///|
priv struct Parser {
tokens : Array[Token]
mut i : Int32
prefixes : Map[String, String]
limits : Limits
mut depth : Int32
mut template_mode : Bool
mut base_uri : String?
mut anonymous_nodes : Int32
mut iri_units : Int32
}
///|
fn Parser::new(
source : String,
limits : Limits,
base_uri? : String,
) -> Parser raise QueryError {
let tokens = lex(source, limits)
if base_uri is Some(base) {
if base.length() > limits.max_query_chars {
raise Resource("query base characters")
}
check_iri(base)
}
{
tokens,
i: 0,
prefixes: Map([]),
limits,
depth: 0,
template_mode: false,
base_uri,
anonymous_nodes: 0,
iri_units: 0,
}
}
///|
fn Parser::current(self : Parser) -> Lexeme {
self.tokens[self.i].value
}
///|
fn Parser::set_template_mode(self : Parser, enabled : Bool) -> Unit {
self.template_mode = enabled
}
///|
fn Parser::position(self : Parser) -> Int32 {
self.tokens[self.i].position
}
///|
fn Parser::advance(self : Parser) -> Lexeme {
let value = self.current()
if value != EndToken {
self.i += 1
}
value
}
///|
fn Parser::sym(self : Parser, symbol : String) -> Bool {
if self.current() == Symbol(symbol) {
self.i += 1
true
} else {
false
}
}
///|
fn Parser::word(self : Parser, wanted : String) -> Bool {
match self.current() {
Word(w) =>
if w.to_upper() == wanted {
self.i += 1
true
} else {
false
}
_ => false
}
}
///|
fn Parser::expect(self : Parser, symbol : String) -> Unit raise QueryError {
if !self.sym(symbol) {
raise Syntax(self.position(), "expected " + symbol)
}
}
///|
fn Parser::require_word(self : Parser, word : String) -> Unit raise QueryError {
if !self.word(word) {
raise Syntax(self.position(), "expected " + word)
}
}
///|
fn Parser::variable(self : Parser) -> String raise QueryError {
match self.advance() {
Var(name) => name
_ => raise Syntax(self.position(), "expected variable")
}
}
///|
fn Parser::enter(self : Parser) -> Unit raise QueryError {
if self.depth >= self.limits.max_depth {
raise Resource("query nesting")
}
self.depth += 1
}
///|
fn Parser::charge_iri(self : Parser, value : String) -> String raise QueryError {
if value.length() > self.limits.max_query_chars - self.iri_units {
raise Resource("expanded query IRI characters")
}
self.iri_units += value.length()
value
}
///|
fn Parser::prefixes(self : Parser) -> Unit raise QueryError {
while true {
if self.word("BASE") {
match self.advance() {
IriToken(value) =>
self.base_uri = Some(
self.charge_iri(resolve_query_iri(value, self.base_uri)),
)
_ => raise Syntax(self.position(), "expected BASE IRI")
}
continue
}
if !self.word("PREFIX") {
break
}
let prefix = match self.advance() {
Word(w) =>
match w.split_once(":") {
Some((prefix, tail)) =>
if tail.length() == 0 {
prefix.to_owned()
} else {
raise Syntax(self.position(), "prefix must end with colon")
}
None => raise Syntax(self.position(), "expected prefix name")
}
_ => raise Syntax(self.position(), "expected prefix name")
}
let base = match self.advance() {
IriToken(v) => self.charge_iri(resolve_query_iri(v, self.base_uri))
_ => raise Syntax(self.position(), "expected prefix IRI")
}
self.prefixes[prefix] = base
}
}
///|
fn Parser::read_term(self : Parser) -> Term raise QueryError {
let position = self.position()
match self.advance() {
IriToken(v) => iri(self.charge_iri(resolve_query_iri(v, self.base_uri)))
StringToken(v) =>
if self.sym("@") {
let lang = match self.advance() {
Word(l) => l
_ => raise Syntax(self.position(), "expected language tag")
}
language_literal(v, lang)
} else if self.sym("^^") {
let datatype = self.read_term()
if datatype.kind != IriKind {
raise Syntax(position, "datatype must be IRI")
}
typed_literal(v, datatype.lexical)
} else {
literal(v)
}
NumToken(n) => {
if n.length() > self.limits.max_numeric_digits {
raise Resource("numeric token digits")
}
let kind = if n.contains("e") || n.contains("E") {
"double"
} else if n.contains(".") {
"decimal"
} else {
"integer"
}
typed_literal(n, xsd_namespace() + kind)
}
Symbol(sign) if sign == "+" || sign == "-" => {
let value = self.read_term()
if value.datatype != xsd_namespace() + "integer" &&
value.datatype != xsd_namespace() + "decimal" &&
value.datatype != xsd_namespace() + "double" {
raise Syntax(position, "sign requires numeric literal")
}
typed_literal(sign + value.lexical, value.datatype)
}
Word(w) => {
if w.to_lower() == "true" || w.to_lower() == "false" {
return typed_literal(w.to_lower(), xsd_namespace() + "boolean")
}
match w.split_once(":") {
Some((prefix, suffix)) =>
if prefix == "_" {
if !self.template_mode {
raise Unsupported(
position, "blank node query labels; use variables",
)
}
blank(suffix.to_owned())
} else {
let base = match self.prefixes.get(prefix.to_owned()) {
Some(v) => v
None =>
raise Syntax(position, "unknown prefix " + prefix.to_owned())
}
iri(self.charge_iri(base + suffix.to_owned()))
}
None => raise Syntax(position, "expected RDF term")
}
}
_ => raise Syntax(position, "expected RDF term")
}
}
///|
fn Parser::slot(self : Parser) -> Slot raise QueryError {
match self.current() {
Var(_) => Variable(self.variable())
_ => Fixed(self.read_term())
}
}
///|
fn Parser::path_atom(self : Parser) -> Path raise QueryError {
self.enter()
let inverse = self.sym("^")
let mut path = if self.sym("(") {
let inner = self.read_path()
self.expect(")")
inner
} else if self.sym("!") {
self.negated_path()
} else {
Predicate(self.path_predicate())
}
if self.sym("*") {
path = ZeroMore(path)
} else if self.sym("+") {
path = OneMore(path)
} else if self.sym("?") {
path = ZeroOne(path)
}
self.depth -= 1
if inverse {
Inverse(path)
} else {
path
}
}
///|
fn Parser::path_predicate(self : Parser) -> Term raise QueryError {
let term = if self.word("A") {
iri(rdf_namespace() + "type")
} else {
self.read_term()
}
if term.kind != IriKind {
raise Syntax(self.position(), "predicate must be IRI")
}
term
}
///|
fn Parser::negated_path(self : Parser) -> Path raise QueryError {
let forward = []
let reverse = []
let grouped = self.sym("(")
if grouped && self.sym(")") {
return Negated(forward, reverse)
}
while true {
let inverse = self.sym("^")
let term = self.path_predicate()
if inverse {
reverse.push(term)
} else {
forward.push(term)
}
if !grouped || !self.sym("|") {
break
}
}
if grouped {
self.expect(")")
}
Negated(forward, reverse)
}
///|
fn Parser::path_sequence(self : Parser) -> Path raise QueryError {
let mut path = self.path_atom()
while self.sym("/") {
path = Sequence(path, self.path_atom())
}
path
}
///|
fn Parser::read_path(self : Parser) -> Path raise QueryError {
if self.current() is Var(_) {
return PathVar(self.variable())
}
let mut path = self.path_sequence()
while self.sym("|") {
path = Alternative(path, self.path_sequence())
}
path
}