///|
fn binary_precedence(value : Lexeme) -> Int32 {
match value {
Symbol("||") => 1
Symbol("&&") => 2
Symbol("=" | "!=" | "<" | ">" | "<=" | ">=") => 3
Symbol("+" | "-") => 4
Symbol("*" | "/") => 5
_ => -1
}
}
///|
fn aggregate_name(name : String) -> Bool {
["COUNT", "SUM", "AVG", "MIN", "MAX", "SAMPLE"].contains(name)
}
///|
fn supported_function(name : String) -> Bool {
[
"BOUND", "STR", "LANG", "DATATYPE", "ISIRI", "ISURI", "ISBLANK", "ISLITERAL",
"ISNUMERIC", "SAMETERM", "STRLEN", "CONTAINS", "STRSTARTS", "STRENDS", "LCASE",
"UCASE", "CONCAT", "COALESCE", "IF", "LANGMATCHES", "ABS", "CEIL", "FLOOR", "ROUND",
"STRDT", "STRLANG",
].contains(name)
}
///|
fn Parser::function_call(self : Parser, name : String) -> Expr raise QueryError {
let name = name.to_upper()
if !aggregate_name(name) && !supported_function(name) {
raise Unsupported(self.position(), "function " + name)
}
self.expect("(")
if aggregate_name(name) {
let distinct = self.word("DISTINCT")
let value = if name == "COUNT" && self.sym("*") {
None
} else {
Some(self.expression())
}
self.expect(")")
Aggregate(name, value, distinct)
} else {
let arguments = []
if !self.sym(")") {
arguments.push(self.expression())
while self.sym(",") {
arguments.push(self.expression())
}
self.expect(")")
}
Call(name, arguments)
}
}
///|
fn Parser::primary(self : Parser) -> Expr raise QueryError {
if self.sym("(") {
let expression = self.expression()
self.expect(")")
expression
} else if self.sym("!") {
Unary("!", self.expression(min_precedence=6))
} else if self.sym("+") {
Unary("+", self.expression(min_precedence=6))
} else if self.sym("-") {
Unary("-", self.expression(min_precedence=6))
} else {
match self.current() {
Var(_) => Reference(self.variable())
Word(name) if self.tokens.get(self.i + 1)
is Some({ value: Symbol("("), .. }) => {
ignore(self.advance())
self.function_call(name)
}
_ => Constant(self.read_term())
}
}
}
///|
fn Parser::expression(
self : Parser,
min_precedence~ : Int32 = 0,
) -> Expr raise QueryError {
self.enter()
let mut left = self.primary()
while true {
if min_precedence <= 3 {
let negative = self.word("NOT")
if negative || self.word("IN") {
if negative {
self.require_word("IN")
}
self.expect("(")
let args = [left]
if !self.sym(")") {
args.push(self.expression())
while self.sym(",") {
args.push(self.expression())
}
self.expect(")")
}
left = if negative {
Unary("!", Call("IN", args))
} else {
Call("IN", args)
}
continue
}
}
let precedence = binary_precedence(self.current())
if precedence < min_precedence {
break
}
let op = match self.advance() {
Symbol(op) => op
_ => raise Syntax(self.position(), "expected expression operator")
}
left = Binary(op, left, self.expression(min_precedence=precedence + 1))
}
self.depth -= 1
left
}