A tab, line feed or carriage return refused as blank space: JSONPath queries in OjG
A JSONPath filter written over two lines did not parse in OjG, a Go library for JSON. RFC 9535 section 2.1.1 defines blank space as a space, tab, line feed or carriage return; the parser took only the space. I fixed it in PR #256, merged about two hours and twenty minutes after I opened it.
When it happens
_, err := jp.ParseString("$[?\n@.a]")
fmt.Printf("%q\n", err)
// "'' is not a value or function at 4 in $[?\n@.a]"
It should parse as $[?(@.a)], as $[? @.a] already does. Blank space in brackets and around a filter operator failed too: "abc[\t-2\t].def" and "$[?\n@.x\n==\n1\n]".
How blank space is skipped
skipSpace and nextNonSpace in jp/parse.go stepped over spaces and stopped at any other byte. afterBracket and readUnion tested for ' ' themselves before they called skipSpace.
Why the gap existed
The parser came in June 2020 with all four of those checks, and its blank-space tests used only spaces, as in abc[ -2 ].def.
The fix
spaceMap is a 256-byte string constant marking a space, tab, line feed and carriage return. skipSpace, nextNonSpace and readUnion look the byte up in it. afterBracket switches on the byte, so its case lists the four. A form feed and a blank right after a dot, as in "$.\ta", stay errors.
Trimmed jp/parse.go diff, hunk headers cut to @@, without context lines, the table’s two comment lines or its six all-dot rows. After the table the hunks are afterBracket, readUnion, skipSpace and nextNonSpace.
@@
+ spaceMap = "" +
+ ".........ss..s.................." + // 0x00
+ "s..............................." + // 0x20
@@
- case ' ':
+ case ' ', '\t', '\n', '\r':
@@
- if b == ' ' {
+ if spaceMap[b] == 's' {
@@
- if b != ' ' {
+ if spaceMap[b] != 's' {
@@
- if b2 != ' ' {
+ if spaceMap[b2] != 's' {
The test
TestParse gained ten rows. Eight hold blank space: a tab, a line feed and a carriage return in brackets, one union row, one slice row and three filter rows. Two are error rows, "$.\ta" and "$[\f'a']". Run one at a time without the fix, the eight fail and the two pass. go test ./jp -run 'TestParse$' fails without the fix and passes with it.
What the maintainer did
@ohler55 approved it with no comment and squash-merged it into develop. The merged files match my branch.
The lesson
- Search for the literal,
' ', and not only the helper. RevertafterBracketalone andabc[\t-2\t].deffails; revertreadUnionalone and the union row fails (mutant runs). - Give each member of a set a row of its own, so a failure names it. Without carriage return in the fix,
abc[\r-2\r].defis the first row to fail.
If a parser of yours tests for a space alone, reply on X.