featurebase/pql/pql.peg
Seebs e84d2d2a59 refactor number parsing a bit
There are subtle inconsistencies, like "01" being a valid decimal but not
a valid integer, which vaguely bug me. Cleaning this up, and the corresponding
parser logic.

A number can't have leading spaces because the grammar doesn't
put spaces in them in the first place, so stop accepting them in the
number syntax. This should never have any impact on anything,
it's just simpler.

Update a couple of test cases to reflect this -- no longer testing
that trailing spaces are okay, now testing that they're not, for
instance.
2020-10-19 13:37:21 -05:00

79 lines
3.6 KiB
Text

package pql
type PQL Peg {
Query
}
# All input queries consist of a sequence of calls, at the top level.
Calls <- sp (Call sp)* !.
Call <- "Set" {p.startCall("Set")} open col comma args (comma timestamp)? close {p.endCall()}
/ "SetRowAttrs" {p.startCall("SetRowAttrs")} open posfield comma row comma args close {p.endCall()}
/ "SetColumnAttrs" {p.startCall("SetColumnAttrs")} open col comma args close {p.endCall()}
/ "Clear" {p.startCall("Clear")} open col comma args close {p.endCall()}
/ "ClearRow" {p.startCall("ClearRow")} open arg close {p.endCall()}
/ "Store" {p.startCall("Store")} open Call comma arg close {p.endCall()}
/ "TopN" {p.startCall("TopN")} open posfield (comma allargs)? close {p.endCall()}
/ "Rows" {p.startCall("Rows")} open posfield (comma allargs)? close {p.endCall()}
/ "Range" {p.startCall("Range")} open field eq value comma 'from='? {p.addField("from")} timestampfmt {p.addVal(text)} comma 'to='? sp {p.addField("to")} timestampfmt {p.addVal(text)} close {p.endCall()}
/ < IDENT > { p.startCall(text) } open allargs comma? close { p.endCall() }
allargs <- Call (comma Call)* (comma args)? / args / sp
args <- arg (comma args)? sp
arg <- field eq value
/ field sp COND sp value
/ conditional
COND <- '><' { p.addBTWN() }
/ '<=' { p.addLTE() }
/ '>=' { p.addGTE() }
/ '==' { p.addEQ() }
/ '!=' { p.addNEQ() }
/ '<' { p.addLT() }
/ '>' { p.addGT() }
conditional <- {p.startConditional()} condint condLT condfield condLT condint {p.endConditional()}
condint <- < decimal > sp {p.condAdd(text)}
condLT <- <('<=' / '<')> sp {p.condAdd(text)}
condfield <- <fieldExpr> sp {p.condAdd(text)}
value <- item
/ lbrack { p.startList() } items rbrack { p.endList() }
items <- item (comma items)?
item <- 'null' &(comma / close) { p.addVal(nil) }
/ 'true' &(comma / close) { p.addVal(true) }
/ 'false' &(comma / close) { p.addVal(false) }
/ timestampfmt { p.addVal(text) }
/ < decimal > { p.addNumVal(text) }
/ < IDENT > { p.startCall(text) } open allargs comma? close { p.addVal(p.endCall()) }
/ < ([[A-Z]] / [0-9] / '-' / '_' / ':')+ > { p.addVal(text) }
/ < '"' doublequotedstring '"' > { p.addVal(text) }
/ < '\'' singlequotedstring '\'' > { p.addVal(text) }
doublequotedstring <- ( '\\"' / '\\\\' / '\\n' / '\\t' / [^"\\] )*
singlequotedstring <- ( '\\\'' / '\\\\' / '\\n' / '\\t' / [^'\\] )*
fieldExpr <- ( [[A-Z]] / '_' ) ( [[A-Z]] / [0-9] / '_' / '-' )*
field <- <fieldExpr / reserved> { p.addField(text) }
reserved <- '_row' / '_col' / '_start' / '_end' / '_timestamp' / '_field'
posfield <- <fieldExpr> { p.addPosStr("_field", text) }
col <- < digits > {p.addPosNum("_col", text)}
/ < '\'' singlequotedstring '\'' > {p.addPosStr("_col", text)}
/ < '"' doublequotedstring '"' > {p.addPosStr("_col", text)}
row <- < digits > {p.addPosNum("_row", text)}
/ < '\'' singlequotedstring '\'' > {p.addPosStr("_row", text)}
/ < '"' doublequotedstring '"' > {p.addPosStr("_row", text)}
open <- '(' sp
close <- sp ')' sp
sp <- [ \t\n]*
eq <- sp '=' sp
comma <- sp ',' sp
lbrack <- '[' sp
rbrack <- sp ']' sp
IDENT <- [[A-Z]] ([[A-Z]] / [0-9])*
digits <- [0-9]+
signedDigits <- '-'? digits
decimal <- signedDigits ('.' digits?)?
/ '-'? '.' digits
timestampbasicfmt <- [0-9][0-9][0-9][0-9]'-'[01][0-9]'-'[0-3][0-9]'T'[0-9][0-9]':'[0-9][0-9]
timestampfmt <- '"' <timestampbasicfmt> '"' / '\'' <timestampbasicfmt> '\'' / <timestampbasicfmt>
timestamp <- <timestampfmt> {p.addPosStr("_timestamp", text)}