From fdb92abd85dede6dcde69c8ae615ae1eaffd7192 Mon Sep 17 00:00:00 2001 From: Mathias Vanden Auweele Date: Tue, 29 Sep 2026 22:01:55 +0200 Subject: [PATCH 1/4] fix(yasqe): make grammar generator work with SWI-Prolog >= 8.3 Recent SWI-Prolog versions treat ==> and => as built-in SSU rule operators, so grammar productions were compiled as rules instead of stored as facts, producing an empty table. Store productions as ebnf/2 and bnf/2 facts, emit ESM directly and format with prettier. Regenerating the SPARQL 1.1 table yields an identical file. Co-Authored-By: Claude Opus 5.5 --- packages/yasqe/grammar/build.sh | 9 ++++++- packages/yasqe/grammar/sparql11-grammar.pl | 2 +- packages/yasqe/grammar/util/gen_ll1.pl | 13 ++++++---- packages/yasqe/grammar/util/ll1.pl | 18 +++++++------- .../grammar/util/output_to_javascript.pl | 16 ++++++------- packages/yasqe/grammar/util/prune.pl | 8 +++---- packages/yasqe/grammar/util/rewrite.pl | 24 +++++++++---------- 7 files changed, 50 insertions(+), 40 deletions(-) diff --git a/packages/yasqe/grammar/build.sh b/packages/yasqe/grammar/build.sh index b0ca8b6c..0e2a09c3 100755 --- a/packages/yasqe/grammar/build.sh +++ b/packages/yasqe/grammar/build.sh @@ -1,2 +1,9 @@ #!/bin/bash -swipl -s util/gen_sparql11.pl -t go +# Regenerates _tokenizer-table.js from sparql11-grammar.pl (requires SWI-Prolog) +set -e +GRAMMAR_DIR="$(cd "$(dirname "$0")" && pwd)" +cd "$GRAMMAR_DIR/util" +swipl -s gen_sparql11.pl -t go +mv _tokenizer-table.js ../_tokenizer-table.js +cd "$GRAMMAR_DIR/../../.." +npx prettier --parser babel --write packages/yasqe/grammar/_tokenizer-table.js diff --git a/packages/yasqe/grammar/sparql11-grammar.pl b/packages/yasqe/grammar/sparql11-grammar.pl index 8f3681e6..7a45c44e 100644 --- a/packages/yasqe/grammar/sparql11-grammar.pl +++ b/packages/yasqe/grammar/sparql11-grammar.pl @@ -20,7 +20,7 @@ % However, we do need it to check whether rules *could* precede % end-of-input, so use it with top-level -:-dynamic '==>'/2. + sparql11 ==> [prologue,(queryAll or updateAll), $]. queryUnit ==> [query,$]. diff --git a/packages/yasqe/grammar/util/gen_ll1.pl b/packages/yasqe/grammar/util/gen_ll1.pl index 94847028..3dd7f7c9 100644 --- a/packages/yasqe/grammar/util/gen_ll1.pl +++ b/packages/yasqe/grammar/util/gen_ll1.pl @@ -4,15 +4,20 @@ :-dynamic change/0. :-dynamic m/3. :-dynamic tm/1. -:-dynamic '==>'/2. -:-dynamic '=>'/2. +:-dynamic ebnf/2. % EBNF grammar productions (written as LHS ==> RHS in the grammar file) +:-dynamic bnf/2. % BNF grammar productions (compiled from the EBNF productions) :-op(550,xfy,===>). % Rewrite rules on EBNF expressions :-op(500,xfy,==>). % EBNF grammar productions -:-op(500,xfy,=>). % BNF grammar productions +:-op(500,xfy,=>). % BNF grammar productions (only used when printing) :-op(490,xfy,or). :-op(480,fy,*). :-op(470,xfy,\). +% Since SWI-Prolog 8.3, ==> and => are built-in (SSU) rule operators, so +% grammar clauses written as `LHS ==> RHS.` would be compiled as rules +% instead of stored as facts. Capture them as ebnf/2 facts instead. +term_expansion((LHS ==> RHS), ebnf(LHS,RHS)). + :-reconsult('rewrite.pl'). :-reconsult('ll1.pl'). @@ -32,6 +37,4 @@ output_punct_js, js_vars(Vars), output_vars_js(Vars), - write('}'), told. - diff --git a/packages/yasqe/grammar/util/ll1.pl b/packages/yasqe/grammar/util/ll1.pl index 3515e821..8703d36e 100644 --- a/packages/yasqe/grammar/util/ll1.pl +++ b/packages/yasqe/grammar/util/ll1.pl @@ -31,21 +31,21 @@ iterate_matrix. validate_rules:- - NT=>_, - \+ (_=>RHS, memberchk(NT,RHS) ), + bnf(NT,_), + \+ (bnf(_,RHS), memberchk(NT,RHS) ), format("Warning: unused non-terminal: ~w~n",[NT]), fail. validate_rules:- % (Check the untranslated rules) - LHS ==> RHS, + ebnf(LHS,RHS), \+RHS=[], \+RHS=[_|_], format("Warning: atomic RHS: ~w~n",[LHS=>RHS]), fail. validate_rules:- - _ => RHS, + bnf(_,RHS), member(T,RHS), - \+ T => _, + \+ bnf(T,_), remember(tm(T)), fail. validate_rules:- @@ -96,7 +96,7 @@ first(Tm,Tm):- tm(Tm). first(Nonterm,F):- - Nonterm=>RHS, + bnf(Nonterm,RHS), first_list(RHS,F). first_list([],epsilon). @@ -123,7 +123,7 @@ follow:- - B => RHS, + bnf(B,RHS), follow_list(RHS,B). follow_list([X],B):- @@ -152,13 +152,13 @@ copy_follow(_,_). iterate_matrix:- - A => RHS, + bnf(A,RHS), first_list(RHS,F), F \== epsilon, remember1(m(A,F,A=>RHS)), fail. iterate_matrix:- - A => RHS, + bnf(A,RHS), first_list(RHS,epsilon), fo(A,F), remember1(m(A,F,A=>RHS)), diff --git a/packages/yasqe/grammar/util/output_to_javascript.pl b/packages/yasqe/grammar/util/output_to_javascript.pl index 574edecc..48a2e98c 100644 --- a/packages/yasqe/grammar/util/output_to_javascript.pl +++ b/packages/yasqe/grammar/util/output_to_javascript.pl @@ -2,10 +2,10 @@ */ output_table_js:- - write('module.exports = {table:'),nl, - setof(LHS, RHS^(LHS=>RHS), NTs), + write('export const table = '), + setof(LHS, RHS^bnf(LHS,RHS), NTs), form_table(NTs,'{'), - nl,write('},'). + nl,write('};'),nl. output_terminals_js:- nl,nl,write('var terminal=['),nl, @@ -18,15 +18,15 @@ output_keywords_js:- tm_keywords(Ps), findall(Reg,member(Reg,Ps),Regs), - nl,nl,write('keywords:/^('), + nl,nl,write('export const keywords = /^('), output_as_regex_disj(Regs,''), - write(')/i ,'),nl. + write(')/i;'),nl. output_punct_js:- tm_punct(Ps), findall(Reg,member(_=Reg,Ps),Regs), - nl,write('punct:/^('), + nl,write('export const punct = /^('), output_as_regex_disj(Regs,''), - write(')/ ,'),nl,nl. + write(')/;'),nl,nl. output_top_symbol_js:- start_symbol(TS), format('startSymbol:"~w";~n',[TS]). @@ -42,7 +42,7 @@ output_vars_js([]). output_vars_js([Var=Val|Pairs]):- - format('~w:~w,~n',[Var,Val]), + format('export const ~w = ~w;~n',[Var,Val]), output_vars_js(Pairs). output_as_regex_disj([],_). diff --git a/packages/yasqe/grammar/util/prune.pl b/packages/yasqe/grammar/util/prune.pl index 543e9b12..7cdf3ee1 100644 --- a/packages/yasqe/grammar/util/prune.pl +++ b/packages/yasqe/grammar/util/prune.pl @@ -11,14 +11,14 @@ prune_for_top_symbol:- top_symbol(Top), reachable_syms(Top,NTs,_Ts), - findall(NT,(NT=>_RHS,\+memberchk(NT,NTs)),UnreachableNTs0), + findall(NT,(bnf(NT,_RHS),\+memberchk(NT,NTs)),UnreachableNTs0), sort(UnreachableNTs0,UnreachableNTs), delete_NTs(UnreachableNTs). delete_NTs([]). delete_NTs([NT|NTs]):- format('Removing unreachable nonterminal ~w~n',[NT]), - retractall(NT=>_), + retractall(bnf(NT,_)), delete_NTs(NTs). reachable_syms(Top,NTs,Ts):- @@ -29,13 +29,13 @@ reachable([],_,_):-!. reachable([NT|NTs1],NTs0,Ts):- - once(findall(A,(NT=>RHS,member(A,RHS)),As)), + once(findall(A,(bnf(NT,RHS),member(A,RHS)),As)), includes_rhs(As,NTs0,Ts), reachable(NTs1,NTs0,Ts). includes_rhs([],_,_). includes_rhs([A|As],NTs0,Ts):- - A=>_,!, + bnf(A,_),!, % Non-terminal memberchk(A,NTs0), includes_rhs(As,NTs0,Ts). diff --git a/packages/yasqe/grammar/util/rewrite.pl b/packages/yasqe/grammar/util/rewrite.pl index cff0a130..94b6803a 100644 --- a/packages/yasqe/grammar/util/rewrite.pl +++ b/packages/yasqe/grammar/util/rewrite.pl @@ -8,7 +8,7 @@ ebnf_to_bnf:- - retractall(_=>_), + retractall(bnf(_,_)), remember(change), rewrite_until_stable. @@ -21,9 +21,9 @@ rewrite_until_stable. rewrite_any:- - LHS==>RHS0, + ebnf(LHS,RHS0), rewrite(RHS0,RHS1), - remember(LHS=>RHS1), + remember(bnf(LHS,RHS1)), fail. rewrite_any. @@ -31,29 +31,29 @@ *(A) ===> A_star :- atom(A), format(atom(A_star),"*~w",[A]), - remember(A_star=>[]), - remember(A_star=>[A,A_star]). + remember(bnf(A_star,[])), + remember(bnf(A_star,[A,A_star])). +(A) ===> A_plus :- atom(A), format(atom(A_plus),"+~w",[A]), - remember(A_plus==>[A,*(A)]). % Assert as EBNF - requires further wrangling + remember(ebnf(A_plus,[A,*(A)])). % Assert as EBNF - requires further wrangling ?(A) ===> A_qm :- atom(A), format(atom(A_qm),"?~w",[A]), - remember(A_qm=>[]), - remember(A_qm=>[A]). + remember(bnf(A_qm,[])), + remember(bnf(A_qm,[A])). +A \ B ===> A_or_B :- format(atom(A_or_B),"(~w or ~w)",[A,B]), - remember(A_or_B=>[A]), - remember(A_or_B=>[B]). + remember(bnf(A_or_B,[A])), + remember(bnf(A_or_B,[B])). List ===> ListAtom :- List = [_|_], format(atom(ListAtom),"~w",[List]), - remember(ListAtom=>List). + remember(bnf(ListAtom,List)). OrExpr ===> ListAtom :- OrExpr =.. [or|Args], @@ -62,7 +62,7 @@ map_disjuncts([],_). map_disjuncts([D|Ds],Head):- - remember(Head=>[D]), + remember(bnf(Head,[D])), map_disjuncts(Ds,Head). % Apply rewrite rules starting from inside outwards From 1260b264fd3eedc3d2b2154843f593a03a95969c Mon Sep 17 00:00:00 2001 From: Mathias Vanden Auweele Date: Tue, 29 Sep 2026 22:20:37 +0200 Subject: [PATCH 2/4] feat(yasqe): support SPARQL 1.2 syntax Extend the LL(1) grammar with the SPARQL 1.2 Query Language grammar (W3C Working Draft of 21 September 2026): - VERSION declaration - triple terms <<( s p o )>> in patterns, VALUES and expressions - reified triples << s p o ~ r >>, reifiers and annotation blocks {| |} - language tags with base direction ("x"@en--ltr) - LANGDIR, STRLANGDIR, hasLANG, hasLANGDIR, isTRIPLE, TRIPLE, SUBJECT, PREDICATE and OBJECT; aggregates are built-in calls; '!' applies to a UnaryExpression The tokenizer enforces the grammar note that reifiers and annotations may only follow a simple predicate, not a property path. Also fix PropertyListPath (a blank node property list may be followed by a path) and state.complete, which was always false. Add unit tests with valid and invalid SPARQL 1.2 queries and browser tests for syntax checking and formatting of SPARQL 1.2 queries. Co-Authored-By: Claude Opus 5.5 --- .changeset/sparql-1-2-syntax.md | 14 + packages/yasqe/grammar/README.md | 8 +- packages/yasqe/grammar/_tokenizer-table.js | 950 ++++++++++++++++++--- packages/yasqe/grammar/sparql11-grammar.pl | 159 +++- packages/yasqe/grammar/tokenizer.ts | 78 +- test/fix-esm-imports.mjs | 9 + test/run.ts | 61 ++ test/unit/yasqe-sparql12-grammar-test.ts | 583 +++++++++++++ 8 files changed, 1727 insertions(+), 135 deletions(-) create mode 100644 .changeset/sparql-1-2-syntax.md create mode 100644 test/unit/yasqe-sparql12-grammar-test.ts diff --git a/.changeset/sparql-1-2-syntax.md b/.changeset/sparql-1-2-syntax.md new file mode 100644 index 00000000..859d15cc --- /dev/null +++ b/.changeset/sparql-1-2-syntax.md @@ -0,0 +1,14 @@ +--- +"@matdata/yasqe": minor +--- + +Support SPARQL 1.2 syntax in the query editor, following the grammar of the [SPARQL 1.2 Query Language](https://www.w3.org/TR/sparql12-query/#sparqlGrammar) Working Draft of 21 September 2026. SPARQL 1.2 queries are now highlighted and syntax checked, including: + +- the `VERSION` declaration +- triple terms `<<( :s :p :o )>>` in triple patterns, `VALUES` and expressions +- reified triples `<< :s :p :o ~ :reifier >>`, reifiers (`~`) and annotation blocks `{| ... |}`. Reifiers and annotations after a property path that is not a simple predicate are reported as errors. +- language tags with a base direction, such as `"hello"@en--ltr` +- the new functions `LANGDIR`, `STRLANGDIR`, `hasLANG`, `hasLANGDIR`, `isTRIPLE`, `TRIPLE`, `SUBJECT`, `PREDICATE` and `OBJECT` +- `!` applied to any unary expression, and aggregates wherever a built-in call is allowed + +A blank node property list followed by a property path (`[ :p ?o ] :a/:b ?c`) is no longer reported as a syntax error. diff --git a/packages/yasqe/grammar/README.md b/packages/yasqe/grammar/README.md index d670c4fc..b784c24d 100644 --- a/packages/yasqe/grammar/README.md +++ b/packages/yasqe/grammar/README.md @@ -1,12 +1,16 @@ # Yet Another SPARQL Query Editor (YASQE) grammar +The grammar covers SPARQL 1.1 and SPARQL 1.2 (queries and updates). + ## Prerequisites -- SWI Prolog +- SWI Prolog (`swipl` on the `PATH`) ## How to make modifications to grammar: - Change the Extended Backus-Naur Form (EBNF) grammar in file `sparql11-grammar.pl`. Do not change file `_tokenizer-table.js`. -- Run `./build.sh`. +- Run `./build.sh`. It regenerates `_tokenizer-table.js` and formats it with prettier. + Check its output for `LL(1) clash` messages: the grammar must stay LL(1). - Finally, rebuild YASQE from the YASQE home directory by running `npm run build` (or `npm run dev` to test it locally). +- The grammar is tested by `test/unit/yasqe-sparql12-grammar-test.ts` (`npm run unit-test`). diff --git a/packages/yasqe/grammar/_tokenizer-table.js b/packages/yasqe/grammar/_tokenizer-table.js index cc8a2e52..85c7c51c 100644 --- a/packages/yasqe/grammar/_tokenizer-table.js +++ b/packages/yasqe/grammar/_tokenizer-table.js @@ -16,6 +16,7 @@ export const table = { ".": [], ";": [], "]": [], + "|}": [], "{": [], OPTIONAL: [], MINUS: [], @@ -31,15 +32,9 @@ export const table = { ".": [], ";": [], "]": [], + "|}": [], "}": [], GRAPH: [], - "{": [], - OPTIONAL: [], - MINUS: [], - SERVICE: [], - FILTER: [], - BIND: [], - VALUES: [], }, "*[/,pathEltOrInverse]": { "/": ["[/,pathEltOrInverse]", "*[/,pathEltOrInverse]"], @@ -47,9 +42,11 @@ export const table = { ")": [], "(": [], "[": [], + "<<": [], VAR1: [], VAR2: [], NIL: [], + "<<(": [], IRI_REF: [], TRUE: [], FALSE: [], @@ -75,6 +72,7 @@ export const table = { ";": ["[;,?[or([verbPath,verbSimple]),objectListPath]]", "*[;,?[or([verbPath,verbSimple]),objectListPath]]"], ".": [], "]": [], + "|}": [], "{": [], OPTIONAL: [], MINUS: [], @@ -89,23 +87,19 @@ export const table = { ";": ["[;,?[verb,objectList]]", "*[;,?[verb,objectList]]"], ".": [], "]": [], + "|}": [], "}": [], GRAPH: [], - "{": [], - OPTIONAL: [], - MINUS: [], - SERVICE: [], - FILTER: [], - BIND: [], - VALUES: [], }, "*[UNION,groupGraphPattern]": { UNION: ["[UNION,groupGraphPattern]", "*[UNION,groupGraphPattern]"], VAR1: [], VAR2: [], NIL: [], + "<<(": [], "(": [], "[": [], + "<<": [], IRI_REF: [], TRUE: [], FALSE: [], @@ -161,9 +155,11 @@ export const table = { ")": [], "(": [], "[": [], + "<<": [], VAR1: [], VAR2: [], NIL: [], + "<<(": [], IRI_REF: [], TRUE: [], FALSE: [], @@ -197,6 +193,7 @@ export const table = { IRI_REF: ["dataBlockValue", "*dataBlockValue"], TRUE: ["dataBlockValue", "*dataBlockValue"], FALSE: ["dataBlockValue", "*dataBlockValue"], + "<<(": ["dataBlockValue", "*dataBlockValue"], PNAME_LN: ["dataBlockValue", "*dataBlockValue"], PNAME_NS: ["dataBlockValue", "*dataBlockValue"], STRING_LITERAL1: ["dataBlockValue", "*dataBlockValue"], @@ -235,9 +232,11 @@ export const table = { "*graphNode": { "(": ["graphNode", "*graphNode"], "[": ["graphNode", "*graphNode"], + "<<": ["graphNode", "*graphNode"], VAR1: ["graphNode", "*graphNode"], VAR2: ["graphNode", "*graphNode"], NIL: ["graphNode", "*graphNode"], + "<<(": ["graphNode", "*graphNode"], IRI_REF: ["graphNode", "*graphNode"], TRUE: ["graphNode", "*graphNode"], FALSE: ["graphNode", "*graphNode"], @@ -263,9 +262,11 @@ export const table = { "*graphNodePath": { "(": ["graphNodePath", "*graphNodePath"], "[": ["graphNodePath", "*graphNodePath"], + "<<": ["graphNodePath", "*graphNodePath"], VAR1: ["graphNodePath", "*graphNodePath"], VAR2: ["graphNodePath", "*graphNodePath"], NIL: ["graphNodePath", "*graphNodePath"], + "<<(": ["graphNodePath", "*graphNodePath"], IRI_REF: ["graphNodePath", "*graphNodePath"], TRUE: ["graphNodePath", "*graphNodePath"], FALSE: ["graphNodePath", "*graphNodePath"], @@ -293,6 +294,7 @@ export const table = { STR: ["groupCondition", "*groupCondition"], LANG: ["groupCondition", "*groupCondition"], LANGMATCHES: ["groupCondition", "*groupCondition"], + LANGDIR: ["groupCondition", "*groupCondition"], DATATYPE: ["groupCondition", "*groupCondition"], BOUND: ["groupCondition", "*groupCondition"], IRI: ["groupCondition", "*groupCondition"], @@ -332,6 +334,7 @@ export const table = { COALESCE: ["groupCondition", "*groupCondition"], IF: ["groupCondition", "*groupCondition"], STRLANG: ["groupCondition", "*groupCondition"], + STRLANGDIR: ["groupCondition", "*groupCondition"], STRDT: ["groupCondition", "*groupCondition"], SAMETERM: ["groupCondition", "*groupCondition"], ISIRI: ["groupCondition", "*groupCondition"], @@ -339,8 +342,22 @@ export const table = { ISBLANK: ["groupCondition", "*groupCondition"], ISLITERAL: ["groupCondition", "*groupCondition"], ISNUMERIC: ["groupCondition", "*groupCondition"], + HASLANG: ["groupCondition", "*groupCondition"], + HASLANGDIR: ["groupCondition", "*groupCondition"], + ISTRIPLE: ["groupCondition", "*groupCondition"], + TRIPLE: ["groupCondition", "*groupCondition"], + SUBJECT: ["groupCondition", "*groupCondition"], + PREDICATE: ["groupCondition", "*groupCondition"], + OBJECT: ["groupCondition", "*groupCondition"], VAR1: ["groupCondition", "*groupCondition"], VAR2: ["groupCondition", "*groupCondition"], + COUNT: ["groupCondition", "*groupCondition"], + SUM: ["groupCondition", "*groupCondition"], + MIN: ["groupCondition", "*groupCondition"], + MAX: ["groupCondition", "*groupCondition"], + AVG: ["groupCondition", "*groupCondition"], + SAMPLE: ["groupCondition", "*groupCondition"], + GROUP_CONCAT: ["groupCondition", "*groupCondition"], SUBSTR: ["groupCondition", "*groupCondition"], REPLACE: ["groupCondition", "*groupCondition"], REGEX: ["groupCondition", "*groupCondition"], @@ -362,6 +379,7 @@ export const table = { STR: ["havingCondition", "*havingCondition"], LANG: ["havingCondition", "*havingCondition"], LANGMATCHES: ["havingCondition", "*havingCondition"], + LANGDIR: ["havingCondition", "*havingCondition"], DATATYPE: ["havingCondition", "*havingCondition"], BOUND: ["havingCondition", "*havingCondition"], IRI: ["havingCondition", "*havingCondition"], @@ -401,6 +419,7 @@ export const table = { COALESCE: ["havingCondition", "*havingCondition"], IF: ["havingCondition", "*havingCondition"], STRLANG: ["havingCondition", "*havingCondition"], + STRLANGDIR: ["havingCondition", "*havingCondition"], STRDT: ["havingCondition", "*havingCondition"], SAMETERM: ["havingCondition", "*havingCondition"], ISIRI: ["havingCondition", "*havingCondition"], @@ -408,6 +427,20 @@ export const table = { ISBLANK: ["havingCondition", "*havingCondition"], ISLITERAL: ["havingCondition", "*havingCondition"], ISNUMERIC: ["havingCondition", "*havingCondition"], + HASLANG: ["havingCondition", "*havingCondition"], + HASLANGDIR: ["havingCondition", "*havingCondition"], + ISTRIPLE: ["havingCondition", "*havingCondition"], + TRIPLE: ["havingCondition", "*havingCondition"], + SUBJECT: ["havingCondition", "*havingCondition"], + PREDICATE: ["havingCondition", "*havingCondition"], + OBJECT: ["havingCondition", "*havingCondition"], + COUNT: ["havingCondition", "*havingCondition"], + SUM: ["havingCondition", "*havingCondition"], + MIN: ["havingCondition", "*havingCondition"], + MAX: ["havingCondition", "*havingCondition"], + AVG: ["havingCondition", "*havingCondition"], + SAMPLE: ["havingCondition", "*havingCondition"], + GROUP_CONCAT: ["havingCondition", "*havingCondition"], SUBSTR: ["havingCondition", "*havingCondition"], REPLACE: ["havingCondition", "*havingCondition"], REGEX: ["havingCondition", "*havingCondition"], @@ -503,9 +536,10 @@ export const table = { NOT: [], ";": [], }, - "*or([baseDecl,prefixDecl])": { - BASE: ["or([baseDecl,prefixDecl])", "*or([baseDecl,prefixDecl])"], - PREFIX: ["or([baseDecl,prefixDecl])", "*or([baseDecl,prefixDecl])"], + "*or([baseDecl,prefixDecl,versionDecl])": { + BASE: ["or([baseDecl,prefixDecl,versionDecl])", "*or([baseDecl,prefixDecl,versionDecl])"], + PREFIX: ["or([baseDecl,prefixDecl,versionDecl])", "*or([baseDecl,prefixDecl,versionDecl])"], + VERSION: ["or([baseDecl,prefixDecl,versionDecl])", "*or([baseDecl,prefixDecl,versionDecl])"], $: [], CONSTRUCT: [], DESCRIBE: [], @@ -522,6 +556,35 @@ export const table = { CREATE: [], WITH: [], }, + "*or([pathReifier,annotationBlockPath])": { + "~": ["or([pathReifier,annotationBlockPath])", "*or([pathReifier,annotationBlockPath])"], + "{|": ["or([pathReifier,annotationBlockPath])", "*or([pathReifier,annotationBlockPath])"], + ".": [], + ";": [], + ",": [], + "]": [], + "|}": [], + "{": [], + OPTIONAL: [], + MINUS: [], + GRAPH: [], + SERVICE: [], + FILTER: [], + BIND: [], + VALUES: [], + "}": [], + }, + "*or([reifier,annotationBlock])": { + "~": ["or([reifier,annotationBlock])", "*or([reifier,annotationBlock])"], + "{|": ["or([reifier,annotationBlock])", "*or([reifier,annotationBlock])"], + ".": [], + ";": [], + ",": [], + "]": [], + "|}": [], + "}": [], + GRAPH: [], + }, "*or([var,[(,expression,AS,var,)]])": { "(": ["or([var,[(,expression,AS,var,)]])", "*or([var,[(,expression,AS,var,)]])"], VAR1: ["or([var,[(,expression,AS,var,)]])", "*or([var,[(,expression,AS,var,)]])"], @@ -539,6 +602,7 @@ export const table = { STR: ["orderCondition", "*orderCondition"], LANG: ["orderCondition", "*orderCondition"], LANGMATCHES: ["orderCondition", "*orderCondition"], + LANGDIR: ["orderCondition", "*orderCondition"], DATATYPE: ["orderCondition", "*orderCondition"], BOUND: ["orderCondition", "*orderCondition"], IRI: ["orderCondition", "*orderCondition"], @@ -578,6 +642,7 @@ export const table = { COALESCE: ["orderCondition", "*orderCondition"], IF: ["orderCondition", "*orderCondition"], STRLANG: ["orderCondition", "*orderCondition"], + STRLANGDIR: ["orderCondition", "*orderCondition"], STRDT: ["orderCondition", "*orderCondition"], SAMETERM: ["orderCondition", "*orderCondition"], ISIRI: ["orderCondition", "*orderCondition"], @@ -585,6 +650,20 @@ export const table = { ISBLANK: ["orderCondition", "*orderCondition"], ISLITERAL: ["orderCondition", "*orderCondition"], ISNUMERIC: ["orderCondition", "*orderCondition"], + HASLANG: ["orderCondition", "*orderCondition"], + HASLANGDIR: ["orderCondition", "*orderCondition"], + ISTRIPLE: ["orderCondition", "*orderCondition"], + TRIPLE: ["orderCondition", "*orderCondition"], + SUBJECT: ["orderCondition", "*orderCondition"], + PREDICATE: ["orderCondition", "*orderCondition"], + OBJECT: ["orderCondition", "*orderCondition"], + COUNT: ["orderCondition", "*orderCondition"], + SUM: ["orderCondition", "*orderCondition"], + MIN: ["orderCondition", "*orderCondition"], + MAX: ["orderCondition", "*orderCondition"], + AVG: ["orderCondition", "*orderCondition"], + SAMPLE: ["orderCondition", "*orderCondition"], + GROUP_CONCAT: ["orderCondition", "*orderCondition"], SUBSTR: ["orderCondition", "*orderCondition"], REPLACE: ["orderCondition", "*orderCondition"], REGEX: ["orderCondition", "*orderCondition"], @@ -628,9 +707,11 @@ export const table = { "+graphNode": { "(": ["graphNode", "*graphNode"], "[": ["graphNode", "*graphNode"], + "<<": ["graphNode", "*graphNode"], VAR1: ["graphNode", "*graphNode"], VAR2: ["graphNode", "*graphNode"], NIL: ["graphNode", "*graphNode"], + "<<(": ["graphNode", "*graphNode"], IRI_REF: ["graphNode", "*graphNode"], TRUE: ["graphNode", "*graphNode"], FALSE: ["graphNode", "*graphNode"], @@ -655,9 +736,11 @@ export const table = { "+graphNodePath": { "(": ["graphNodePath", "*graphNodePath"], "[": ["graphNodePath", "*graphNodePath"], + "<<": ["graphNodePath", "*graphNodePath"], VAR1: ["graphNodePath", "*graphNodePath"], VAR2: ["graphNodePath", "*graphNodePath"], NIL: ["graphNodePath", "*graphNodePath"], + "<<(": ["graphNodePath", "*graphNodePath"], IRI_REF: ["graphNodePath", "*graphNodePath"], TRUE: ["graphNodePath", "*graphNodePath"], FALSE: ["graphNodePath", "*graphNodePath"], @@ -684,6 +767,7 @@ export const table = { STR: ["groupCondition", "*groupCondition"], LANG: ["groupCondition", "*groupCondition"], LANGMATCHES: ["groupCondition", "*groupCondition"], + LANGDIR: ["groupCondition", "*groupCondition"], DATATYPE: ["groupCondition", "*groupCondition"], BOUND: ["groupCondition", "*groupCondition"], IRI: ["groupCondition", "*groupCondition"], @@ -723,6 +807,7 @@ export const table = { COALESCE: ["groupCondition", "*groupCondition"], IF: ["groupCondition", "*groupCondition"], STRLANG: ["groupCondition", "*groupCondition"], + STRLANGDIR: ["groupCondition", "*groupCondition"], STRDT: ["groupCondition", "*groupCondition"], SAMETERM: ["groupCondition", "*groupCondition"], ISIRI: ["groupCondition", "*groupCondition"], @@ -730,8 +815,22 @@ export const table = { ISBLANK: ["groupCondition", "*groupCondition"], ISLITERAL: ["groupCondition", "*groupCondition"], ISNUMERIC: ["groupCondition", "*groupCondition"], + HASLANG: ["groupCondition", "*groupCondition"], + HASLANGDIR: ["groupCondition", "*groupCondition"], + ISTRIPLE: ["groupCondition", "*groupCondition"], + TRIPLE: ["groupCondition", "*groupCondition"], + SUBJECT: ["groupCondition", "*groupCondition"], + PREDICATE: ["groupCondition", "*groupCondition"], + OBJECT: ["groupCondition", "*groupCondition"], VAR1: ["groupCondition", "*groupCondition"], VAR2: ["groupCondition", "*groupCondition"], + COUNT: ["groupCondition", "*groupCondition"], + SUM: ["groupCondition", "*groupCondition"], + MIN: ["groupCondition", "*groupCondition"], + MAX: ["groupCondition", "*groupCondition"], + AVG: ["groupCondition", "*groupCondition"], + SAMPLE: ["groupCondition", "*groupCondition"], + GROUP_CONCAT: ["groupCondition", "*groupCondition"], SUBSTR: ["groupCondition", "*groupCondition"], REPLACE: ["groupCondition", "*groupCondition"], REGEX: ["groupCondition", "*groupCondition"], @@ -746,6 +845,7 @@ export const table = { STR: ["havingCondition", "*havingCondition"], LANG: ["havingCondition", "*havingCondition"], LANGMATCHES: ["havingCondition", "*havingCondition"], + LANGDIR: ["havingCondition", "*havingCondition"], DATATYPE: ["havingCondition", "*havingCondition"], BOUND: ["havingCondition", "*havingCondition"], IRI: ["havingCondition", "*havingCondition"], @@ -785,6 +885,7 @@ export const table = { COALESCE: ["havingCondition", "*havingCondition"], IF: ["havingCondition", "*havingCondition"], STRLANG: ["havingCondition", "*havingCondition"], + STRLANGDIR: ["havingCondition", "*havingCondition"], STRDT: ["havingCondition", "*havingCondition"], SAMETERM: ["havingCondition", "*havingCondition"], ISIRI: ["havingCondition", "*havingCondition"], @@ -792,6 +893,20 @@ export const table = { ISBLANK: ["havingCondition", "*havingCondition"], ISLITERAL: ["havingCondition", "*havingCondition"], ISNUMERIC: ["havingCondition", "*havingCondition"], + HASLANG: ["havingCondition", "*havingCondition"], + HASLANGDIR: ["havingCondition", "*havingCondition"], + ISTRIPLE: ["havingCondition", "*havingCondition"], + TRIPLE: ["havingCondition", "*havingCondition"], + SUBJECT: ["havingCondition", "*havingCondition"], + PREDICATE: ["havingCondition", "*havingCondition"], + OBJECT: ["havingCondition", "*havingCondition"], + COUNT: ["havingCondition", "*havingCondition"], + SUM: ["havingCondition", "*havingCondition"], + MIN: ["havingCondition", "*havingCondition"], + MAX: ["havingCondition", "*havingCondition"], + AVG: ["havingCondition", "*havingCondition"], + SAMPLE: ["havingCondition", "*havingCondition"], + GROUP_CONCAT: ["havingCondition", "*havingCondition"], SUBSTR: ["havingCondition", "*havingCondition"], REPLACE: ["havingCondition", "*havingCondition"], REGEX: ["havingCondition", "*havingCondition"], @@ -815,6 +930,7 @@ export const table = { STR: ["orderCondition", "*orderCondition"], LANG: ["orderCondition", "*orderCondition"], LANGMATCHES: ["orderCondition", "*orderCondition"], + LANGDIR: ["orderCondition", "*orderCondition"], DATATYPE: ["orderCondition", "*orderCondition"], BOUND: ["orderCondition", "*orderCondition"], IRI: ["orderCondition", "*orderCondition"], @@ -854,6 +970,7 @@ export const table = { COALESCE: ["orderCondition", "*orderCondition"], IF: ["orderCondition", "*orderCondition"], STRLANG: ["orderCondition", "*orderCondition"], + STRLANGDIR: ["orderCondition", "*orderCondition"], STRDT: ["orderCondition", "*orderCondition"], SAMETERM: ["orderCondition", "*orderCondition"], ISIRI: ["orderCondition", "*orderCondition"], @@ -861,6 +978,20 @@ export const table = { ISBLANK: ["orderCondition", "*orderCondition"], ISLITERAL: ["orderCondition", "*orderCondition"], ISNUMERIC: ["orderCondition", "*orderCondition"], + HASLANG: ["orderCondition", "*orderCondition"], + HASLANGDIR: ["orderCondition", "*orderCondition"], + ISTRIPLE: ["orderCondition", "*orderCondition"], + TRIPLE: ["orderCondition", "*orderCondition"], + SUBJECT: ["orderCondition", "*orderCondition"], + PREDICATE: ["orderCondition", "*orderCondition"], + OBJECT: ["orderCondition", "*orderCondition"], + COUNT: ["orderCondition", "*orderCondition"], + SUM: ["orderCondition", "*orderCondition"], + MIN: ["orderCondition", "*orderCondition"], + MAX: ["orderCondition", "*orderCondition"], + AVG: ["orderCondition", "*orderCondition"], + SAMPLE: ["orderCondition", "*orderCondition"], + GROUP_CONCAT: ["orderCondition", "*orderCondition"], SUBSTR: ["orderCondition", "*orderCondition"], REPLACE: ["orderCondition", "*orderCondition"], REGEX: ["orderCondition", "*orderCondition"], @@ -882,8 +1013,10 @@ export const table = { VAR1: [], VAR2: [], NIL: [], + "<<(": [], "(": [], "[": [], + "<<": [], IRI_REF: [], TRUE: [], FALSE: [], @@ -925,6 +1058,7 @@ export const table = { STR: [], LANG: [], LANGMATCHES: [], + LANGDIR: [], DATATYPE: [], BOUND: [], IRI: [], @@ -964,6 +1098,7 @@ export const table = { COALESCE: [], IF: [], STRLANG: [], + STRLANGDIR: [], STRDT: [], SAMETERM: [], ISIRI: [], @@ -971,8 +1106,16 @@ export const table = { ISBLANK: [], ISLITERAL: [], ISNUMERIC: [], + HASLANG: [], + HASLANGDIR: [], + ISTRIPLE: [], + TRIPLE: [], + SUBJECT: [], + PREDICATE: [], + OBJECT: [], TRUE: [], FALSE: [], + "<<(": [], COUNT: [], SUM: [], MIN: [], @@ -1101,6 +1244,7 @@ export const table = { ";": [], ".": [], "]": [], + "|}": [], "{": [], OPTIONAL: [], MINUS: [], @@ -1142,15 +1286,9 @@ export const table = { ";": [], ".": [], "]": [], + "|}": [], "}": [], GRAPH: [], - "{": [], - OPTIONAL: [], - MINUS: [], - SERVICE: [], - FILTER: [], - BIND: [], - VALUES: [], }, "?argList": { NIL: ["argList"], @@ -1184,8 +1322,10 @@ export const table = { VAR1: ["constructTriples"], VAR2: ["constructTriples"], NIL: ["constructTriples"], + "<<(": ["constructTriples"], "(": ["constructTriples"], "[": ["constructTriples"], + "<<": ["constructTriples"], IRI_REF: ["constructTriples"], TRUE: ["constructTriples"], FALSE: ["constructTriples"], @@ -1259,13 +1399,14 @@ export const table = { VAR1: [], VAR2: [], }, - "?or([LANGTAG,[^^,iriRef]])": { - LANGTAG: ["or([LANGTAG,[^^,iriRef]])"], - "^^": ["or([LANGTAG,[^^,iriRef]])"], + "?or([LANG_DIR,[^^,iriRef]])": { + LANG_DIR: ["or([LANG_DIR,[^^,iriRef]])"], + "^^": ["or([LANG_DIR,[^^,iriRef]])"], UNDEF: [], IRI_REF: [], TRUE: [], FALSE: [], + "<<(": [], PNAME_LN: [], PNAME_NS: [], STRING_LITERAL1: [], @@ -1284,12 +1425,16 @@ export const table = { a: [], VAR1: [], VAR2: [], + ">>": [], + "~": [], + ")>>": [], "^": [], "!": [], "(": [], ".": [], ";": [], ",": [], + "{|": [], AS: [], ")": [], "||": [], @@ -1308,10 +1453,12 @@ export const table = { "/": [], "}": [], "[": [], + "<<": [], NIL: [], BLANK_NODE_LABEL: [], ANON: [], "]": [], + "|}": [], GRAPH: [], "{": [], OPTIONAL: [], @@ -1398,9 +1545,11 @@ export const table = { ")": [], "(": [], "[": [], + "<<": [], VAR1: [], VAR2: [], NIL: [], + "<<(": [], IRI_REF: [], TRUE: [], FALSE: [], @@ -1422,12 +1571,18 @@ export const table = { DECIMAL_NEGATIVE: [], DOUBLE_NEGATIVE: [], }, + "?reifier": { + "~": ["reifier"], + ">>": [], + }, "?triplesBlock": { VAR1: ["triplesBlock"], VAR2: ["triplesBlock"], NIL: ["triplesBlock"], + "<<(": ["triplesBlock"], "(": ["triplesBlock"], "[": ["triplesBlock"], + "<<": ["triplesBlock"], IRI_REF: ["triplesBlock"], TRUE: ["triplesBlock"], FALSE: ["triplesBlock"], @@ -1462,8 +1617,10 @@ export const table = { VAR1: ["triplesTemplate"], VAR2: ["triplesTemplate"], NIL: ["triplesTemplate"], + "<<(": ["triplesTemplate"], "(": ["triplesTemplate"], "[": ["triplesTemplate"], + "<<": ["triplesTemplate"], IRI_REF: ["triplesTemplate"], TRUE: ["triplesTemplate"], FALSE: ["triplesTemplate"], @@ -1487,6 +1644,32 @@ export const table = { "}": [], GRAPH: [], }, + "?varOrReifierId": { + VAR1: ["varOrReifierId"], + VAR2: ["varOrReifierId"], + IRI_REF: ["varOrReifierId"], + BLANK_NODE_LABEL: ["varOrReifierId"], + ANON: ["varOrReifierId"], + PNAME_LN: ["varOrReifierId"], + PNAME_NS: ["varOrReifierId"], + "~": [], + "{|": [], + ">>": [], + ".": [], + ";": [], + ",": [], + "]": [], + "|}": [], + "{": [], + OPTIONAL: [], + MINUS: [], + GRAPH: [], + SERVICE: [], + FILTER: [], + BIND: [], + VALUES: [], + "}": [], + }, "?whereClause": { WHERE: ["whereClause"], "{": ["whereClause"], @@ -1745,6 +1928,10 @@ export const table = { "multiplicativeExpression", "*or([[+,multiplicativeExpression],[-,multiplicativeExpression],[or([numericLiteralPositive,numericLiteralNegative]),?or([[*,unaryExpression],[/,unaryExpression]])]])", ], + LANGDIR: [ + "multiplicativeExpression", + "*or([[+,multiplicativeExpression],[-,multiplicativeExpression],[or([numericLiteralPositive,numericLiteralNegative]),?or([[*,unaryExpression],[/,unaryExpression]])]])", + ], DATATYPE: [ "multiplicativeExpression", "*or([[+,multiplicativeExpression],[-,multiplicativeExpression],[or([numericLiteralPositive,numericLiteralNegative]),?or([[*,unaryExpression],[/,unaryExpression]])]])", @@ -1901,6 +2088,10 @@ export const table = { "multiplicativeExpression", "*or([[+,multiplicativeExpression],[-,multiplicativeExpression],[or([numericLiteralPositive,numericLiteralNegative]),?or([[*,unaryExpression],[/,unaryExpression]])]])", ], + STRLANGDIR: [ + "multiplicativeExpression", + "*or([[+,multiplicativeExpression],[-,multiplicativeExpression],[or([numericLiteralPositive,numericLiteralNegative]),?or([[*,unaryExpression],[/,unaryExpression]])]])", + ], STRDT: [ "multiplicativeExpression", "*or([[+,multiplicativeExpression],[-,multiplicativeExpression],[or([numericLiteralPositive,numericLiteralNegative]),?or([[*,unaryExpression],[/,unaryExpression]])]])", @@ -1929,6 +2120,34 @@ export const table = { "multiplicativeExpression", "*or([[+,multiplicativeExpression],[-,multiplicativeExpression],[or([numericLiteralPositive,numericLiteralNegative]),?or([[*,unaryExpression],[/,unaryExpression]])]])", ], + HASLANG: [ + "multiplicativeExpression", + "*or([[+,multiplicativeExpression],[-,multiplicativeExpression],[or([numericLiteralPositive,numericLiteralNegative]),?or([[*,unaryExpression],[/,unaryExpression]])]])", + ], + HASLANGDIR: [ + "multiplicativeExpression", + "*or([[+,multiplicativeExpression],[-,multiplicativeExpression],[or([numericLiteralPositive,numericLiteralNegative]),?or([[*,unaryExpression],[/,unaryExpression]])]])", + ], + ISTRIPLE: [ + "multiplicativeExpression", + "*or([[+,multiplicativeExpression],[-,multiplicativeExpression],[or([numericLiteralPositive,numericLiteralNegative]),?or([[*,unaryExpression],[/,unaryExpression]])]])", + ], + TRIPLE: [ + "multiplicativeExpression", + "*or([[+,multiplicativeExpression],[-,multiplicativeExpression],[or([numericLiteralPositive,numericLiteralNegative]),?or([[*,unaryExpression],[/,unaryExpression]])]])", + ], + SUBJECT: [ + "multiplicativeExpression", + "*or([[+,multiplicativeExpression],[-,multiplicativeExpression],[or([numericLiteralPositive,numericLiteralNegative]),?or([[*,unaryExpression],[/,unaryExpression]])]])", + ], + PREDICATE: [ + "multiplicativeExpression", + "*or([[+,multiplicativeExpression],[-,multiplicativeExpression],[or([numericLiteralPositive,numericLiteralNegative]),?or([[*,unaryExpression],[/,unaryExpression]])]])", + ], + OBJECT: [ + "multiplicativeExpression", + "*or([[+,multiplicativeExpression],[-,multiplicativeExpression],[or([numericLiteralPositive,numericLiteralNegative]),?or([[*,unaryExpression],[/,unaryExpression]])]])", + ], TRUE: [ "multiplicativeExpression", "*or([[+,multiplicativeExpression],[-,multiplicativeExpression],[or([numericLiteralPositive,numericLiteralNegative]),?or([[*,unaryExpression],[/,unaryExpression]])]])", @@ -1937,6 +2156,10 @@ export const table = { "multiplicativeExpression", "*or([[+,multiplicativeExpression],[-,multiplicativeExpression],[or([numericLiteralPositive,numericLiteralNegative]),?or([[*,unaryExpression],[/,unaryExpression]])]])", ], + "<<(": [ + "multiplicativeExpression", + "*or([[+,multiplicativeExpression],[-,multiplicativeExpression],[or([numericLiteralPositive,numericLiteralNegative]),?or([[*,unaryExpression],[/,unaryExpression]])]])", + ], COUNT: [ "multiplicativeExpression", "*or([[+,multiplicativeExpression],[-,multiplicativeExpression],[or([numericLiteralPositive,numericLiteralNegative]),?or([[*,unaryExpression],[/,unaryExpression]])]])", @@ -2065,6 +2288,41 @@ export const table = { allowVars: { "}": [], }, + annotation: { + "~": ["*or([reifier,annotationBlock])"], + "{|": ["*or([reifier,annotationBlock])"], + ".": ["*or([reifier,annotationBlock])"], + ";": ["*or([reifier,annotationBlock])"], + ",": ["*or([reifier,annotationBlock])"], + "]": ["*or([reifier,annotationBlock])"], + "|}": ["*or([reifier,annotationBlock])"], + "}": ["*or([reifier,annotationBlock])"], + GRAPH: ["*or([reifier,annotationBlock])"], + }, + annotationBlock: { + "{|": ["{|", "propertyListNotEmpty", "|}"], + }, + annotationBlockPath: { + "{|": ["{|", "propertyListPathNotEmpty", "|}"], + }, + annotationPath: { + "~": ["*or([pathReifier,annotationBlockPath])"], + "{|": ["*or([pathReifier,annotationBlockPath])"], + ".": ["*or([pathReifier,annotationBlockPath])"], + ";": ["*or([pathReifier,annotationBlockPath])"], + ",": ["*or([pathReifier,annotationBlockPath])"], + "]": ["*or([pathReifier,annotationBlockPath])"], + "|}": ["*or([pathReifier,annotationBlockPath])"], + "{": ["*or([pathReifier,annotationBlockPath])"], + OPTIONAL: ["*or([pathReifier,annotationBlockPath])"], + MINUS: ["*or([pathReifier,annotationBlockPath])"], + GRAPH: ["*or([pathReifier,annotationBlockPath])"], + SERVICE: ["*or([pathReifier,annotationBlockPath])"], + FILTER: ["*or([pathReifier,annotationBlockPath])"], + BIND: ["*or([pathReifier,annotationBlockPath])"], + VALUES: ["*or([pathReifier,annotationBlockPath])"], + "}": ["*or([pathReifier,annotationBlockPath])"], + }, argList: { NIL: ["NIL"], "(": ["(", "?DISTINCT", "expression", "*[,,expression]", ")"], @@ -2096,9 +2354,17 @@ export const table = { "(": ["(", "expression", ")"], }, builtInCall: { + COUNT: ["aggregate"], + SUM: ["aggregate"], + MIN: ["aggregate"], + MAX: ["aggregate"], + AVG: ["aggregate"], + SAMPLE: ["aggregate"], + GROUP_CONCAT: ["aggregate"], STR: ["STR", "(", "expression", ")"], LANG: ["LANG", "(", "expression", ")"], LANGMATCHES: ["LANGMATCHES", "(", "expression", ",", "expression", ")"], + LANGDIR: ["LANGDIR", "(", "expression", ")"], DATATYPE: ["DATATYPE", "(", "expression", ")"], BOUND: ["BOUND", "(", "var", ")"], IRI: ["IRI", "(", "expression", ")"], @@ -2140,6 +2406,7 @@ export const table = { COALESCE: ["COALESCE", "expressionList"], IF: ["IF", "(", "expression", ",", "expression", ",", "expression", ")"], STRLANG: ["STRLANG", "(", "expression", ",", "expression", ")"], + STRLANGDIR: ["STRLANGDIR", "(", "expression", ",", "expression", ",", "expression", ")"], STRDT: ["STRDT", "(", "expression", ",", "expression", ")"], SAMETERM: ["SAMETERM", "(", "expression", ",", "expression", ")"], ISIRI: ["ISIRI", "(", "expression", ")"], @@ -2147,9 +2414,16 @@ export const table = { ISBLANK: ["ISBLANK", "(", "expression", ")"], ISLITERAL: ["ISLITERAL", "(", "expression", ")"], ISNUMERIC: ["ISNUMERIC", "(", "expression", ")"], + HASLANG: ["HASLANG", "(", "expression", ")"], + HASLANGDIR: ["HASLANGDIR", "(", "expression", ")"], REGEX: ["regexExpression"], EXISTS: ["existsFunc"], NOT: ["notExistsFunc"], + ISTRIPLE: ["ISTRIPLE", "(", "expression", ")"], + TRIPLE: ["TRIPLE", "(", "expression", ",", "expression", ",", "expression", ")"], + SUBJECT: ["SUBJECT", "(", "expression", ")"], + PREDICATE: ["PREDICATE", "(", "expression", ")"], + OBJECT: ["OBJECT", "(", "expression", ")"], }, clear: { CLEAR: ["CLEAR", "?SILENT_2", "graphRefAll"], @@ -2170,6 +2444,7 @@ export const table = { STR: ["valueLogical", "*[&&,valueLogical]"], LANG: ["valueLogical", "*[&&,valueLogical]"], LANGMATCHES: ["valueLogical", "*[&&,valueLogical]"], + LANGDIR: ["valueLogical", "*[&&,valueLogical]"], DATATYPE: ["valueLogical", "*[&&,valueLogical]"], BOUND: ["valueLogical", "*[&&,valueLogical]"], IRI: ["valueLogical", "*[&&,valueLogical]"], @@ -2209,6 +2484,7 @@ export const table = { COALESCE: ["valueLogical", "*[&&,valueLogical]"], IF: ["valueLogical", "*[&&,valueLogical]"], STRLANG: ["valueLogical", "*[&&,valueLogical]"], + STRLANGDIR: ["valueLogical", "*[&&,valueLogical]"], STRDT: ["valueLogical", "*[&&,valueLogical]"], SAMETERM: ["valueLogical", "*[&&,valueLogical]"], ISIRI: ["valueLogical", "*[&&,valueLogical]"], @@ -2216,8 +2492,16 @@ export const table = { ISBLANK: ["valueLogical", "*[&&,valueLogical]"], ISLITERAL: ["valueLogical", "*[&&,valueLogical]"], ISNUMERIC: ["valueLogical", "*[&&,valueLogical]"], + HASLANG: ["valueLogical", "*[&&,valueLogical]"], + HASLANGDIR: ["valueLogical", "*[&&,valueLogical]"], + ISTRIPLE: ["valueLogical", "*[&&,valueLogical]"], + TRIPLE: ["valueLogical", "*[&&,valueLogical]"], + SUBJECT: ["valueLogical", "*[&&,valueLogical]"], + PREDICATE: ["valueLogical", "*[&&,valueLogical]"], + OBJECT: ["valueLogical", "*[&&,valueLogical]"], TRUE: ["valueLogical", "*[&&,valueLogical]"], FALSE: ["valueLogical", "*[&&,valueLogical]"], + "<<(": ["valueLogical", "*[&&,valueLogical]"], COUNT: ["valueLogical", "*[&&,valueLogical]"], SUM: ["valueLogical", "*[&&,valueLogical]"], MIN: ["valueLogical", "*[&&,valueLogical]"], @@ -2257,6 +2541,7 @@ export const table = { STR: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], LANG: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], LANGMATCHES: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], + LANGDIR: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], DATATYPE: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], BOUND: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], IRI: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], @@ -2296,6 +2581,7 @@ export const table = { COALESCE: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], IF: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], STRLANG: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], + STRLANGDIR: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], STRDT: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], SAMETERM: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], ISIRI: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], @@ -2303,8 +2589,16 @@ export const table = { ISBLANK: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], ISLITERAL: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], ISNUMERIC: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], + HASLANG: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], + HASLANGDIR: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], + ISTRIPLE: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], + TRIPLE: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], + SUBJECT: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], + PREDICATE: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], + OBJECT: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], TRUE: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], FALSE: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], + "<<(": ["conditionalAndExpression", "*[||,conditionalAndExpression]"], COUNT: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], SUM: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], MIN: ["conditionalAndExpression", "*[||,conditionalAndExpression]"], @@ -2339,6 +2633,7 @@ export const table = { STR: ["builtInCall"], LANG: ["builtInCall"], LANGMATCHES: ["builtInCall"], + LANGDIR: ["builtInCall"], DATATYPE: ["builtInCall"], BOUND: ["builtInCall"], IRI: ["builtInCall"], @@ -2378,6 +2673,7 @@ export const table = { COALESCE: ["builtInCall"], IF: ["builtInCall"], STRLANG: ["builtInCall"], + STRLANGDIR: ["builtInCall"], STRDT: ["builtInCall"], SAMETERM: ["builtInCall"], ISIRI: ["builtInCall"], @@ -2385,6 +2681,20 @@ export const table = { ISBLANK: ["builtInCall"], ISLITERAL: ["builtInCall"], ISNUMERIC: ["builtInCall"], + HASLANG: ["builtInCall"], + HASLANGDIR: ["builtInCall"], + ISTRIPLE: ["builtInCall"], + TRIPLE: ["builtInCall"], + SUBJECT: ["builtInCall"], + PREDICATE: ["builtInCall"], + OBJECT: ["builtInCall"], + COUNT: ["builtInCall"], + SUM: ["builtInCall"], + MIN: ["builtInCall"], + MAX: ["builtInCall"], + AVG: ["builtInCall"], + SAMPLE: ["builtInCall"], + GROUP_CONCAT: ["builtInCall"], SUBSTR: ["builtInCall"], REPLACE: ["builtInCall"], REGEX: ["builtInCall"], @@ -2407,8 +2717,10 @@ export const table = { VAR1: ["triplesSameSubject", "?[.,?constructTriples]"], VAR2: ["triplesSameSubject", "?[.,?constructTriples]"], NIL: ["triplesSameSubject", "?[.,?constructTriples]"], + "<<(": ["triplesSameSubject", "?[.,?constructTriples]"], "(": ["triplesSameSubject", "?[.,?constructTriples]"], "[": ["triplesSameSubject", "?[.,?constructTriples]"], + "<<": ["triplesSameSubject", "?[.,?constructTriples]"], IRI_REF: ["triplesSameSubject", "?[.,?constructTriples]"], TRUE: ["triplesSameSubject", "?[.,?constructTriples]"], FALSE: ["triplesSameSubject", "?[.,?constructTriples]"], @@ -2462,6 +2774,7 @@ export const table = { TRUE: ["booleanLiteral"], FALSE: ["booleanLiteral"], UNDEF: ["UNDEF"], + "<<(": ["tripleTermData"], }, datasetClause: { FROM: ["FROM", "or([defaultGraphClause,namedGraphClause])"], @@ -2491,8 +2804,10 @@ export const table = { VAR1: [], VAR2: [], NIL: [], + "<<(": [], "(": [], "[": [], + "<<": [], IRI_REF: [], TRUE: [], FALSE: [], @@ -2520,8 +2835,10 @@ export const table = { VAR1: [], VAR2: [], NIL: [], + "<<(": [], "(": [], "[": [], + "<<": [], IRI_REF: [], TRUE: [], FALSE: [], @@ -2549,6 +2866,39 @@ export const table = { existsFunc: { EXISTS: ["EXISTS", "groupGraphPattern"], }, + exprTripleTerm: { + "<<(": ["<<(", "exprTripleTermSubject", "verb", "exprTripleTermObject", ")>>"], + }, + exprTripleTermObject: { + IRI_REF: ["iriRef"], + PNAME_LN: ["iriRef"], + PNAME_NS: ["iriRef"], + STRING_LITERAL1: ["rdfLiteral"], + STRING_LITERAL2: ["rdfLiteral"], + STRING_LITERAL_LONG1: ["rdfLiteral"], + STRING_LITERAL_LONG2: ["rdfLiteral"], + INTEGER: ["numericLiteral"], + DECIMAL: ["numericLiteral"], + DOUBLE: ["numericLiteral"], + INTEGER_POSITIVE: ["numericLiteral"], + DECIMAL_POSITIVE: ["numericLiteral"], + DOUBLE_POSITIVE: ["numericLiteral"], + INTEGER_NEGATIVE: ["numericLiteral"], + DECIMAL_NEGATIVE: ["numericLiteral"], + DOUBLE_NEGATIVE: ["numericLiteral"], + TRUE: ["booleanLiteral"], + FALSE: ["booleanLiteral"], + VAR1: ["var"], + VAR2: ["var"], + "<<(": ["exprTripleTerm"], + }, + exprTripleTermSubject: { + IRI_REF: ["iriRef"], + PNAME_LN: ["iriRef"], + PNAME_NS: ["iriRef"], + VAR1: ["var"], + VAR2: ["var"], + }, expression: { "!": ["conditionalOrExpression"], "+": ["conditionalOrExpression"], @@ -2559,6 +2909,7 @@ export const table = { STR: ["conditionalOrExpression"], LANG: ["conditionalOrExpression"], LANGMATCHES: ["conditionalOrExpression"], + LANGDIR: ["conditionalOrExpression"], DATATYPE: ["conditionalOrExpression"], BOUND: ["conditionalOrExpression"], IRI: ["conditionalOrExpression"], @@ -2598,6 +2949,7 @@ export const table = { COALESCE: ["conditionalOrExpression"], IF: ["conditionalOrExpression"], STRLANG: ["conditionalOrExpression"], + STRLANGDIR: ["conditionalOrExpression"], STRDT: ["conditionalOrExpression"], SAMETERM: ["conditionalOrExpression"], ISIRI: ["conditionalOrExpression"], @@ -2605,8 +2957,16 @@ export const table = { ISBLANK: ["conditionalOrExpression"], ISLITERAL: ["conditionalOrExpression"], ISNUMERIC: ["conditionalOrExpression"], + HASLANG: ["conditionalOrExpression"], + HASLANGDIR: ["conditionalOrExpression"], + ISTRIPLE: ["conditionalOrExpression"], + TRIPLE: ["conditionalOrExpression"], + SUBJECT: ["conditionalOrExpression"], + PREDICATE: ["conditionalOrExpression"], + OBJECT: ["conditionalOrExpression"], TRUE: ["conditionalOrExpression"], FALSE: ["conditionalOrExpression"], + "<<(": ["conditionalOrExpression"], COUNT: ["conditionalOrExpression"], SUM: ["conditionalOrExpression"], MIN: ["conditionalOrExpression"], @@ -2655,6 +3015,7 @@ export const table = { VAR1: ["varOrTerm"], VAR2: ["varOrTerm"], NIL: ["varOrTerm"], + "<<(": ["varOrTerm"], IRI_REF: ["varOrTerm"], TRUE: ["varOrTerm"], FALSE: ["varOrTerm"], @@ -2677,11 +3038,13 @@ export const table = { DOUBLE_NEGATIVE: ["varOrTerm"], "(": ["triplesNode"], "[": ["triplesNode"], + "<<": ["reifiedTriple"], }, graphNodePath: { VAR1: ["varOrTerm"], VAR2: ["varOrTerm"], NIL: ["varOrTerm"], + "<<(": ["varOrTerm"], IRI_REF: ["varOrTerm"], TRUE: ["varOrTerm"], FALSE: ["varOrTerm"], @@ -2704,6 +3067,7 @@ export const table = { DOUBLE_NEGATIVE: ["varOrTerm"], "(": ["triplesNodePath"], "[": ["triplesNodePath"], + "<<": ["reifiedTriple"], }, graphOrDefault: { DEFAULT: ["DEFAULT"], @@ -2761,6 +3125,7 @@ export const table = { STR: ["builtInCall"], LANG: ["builtInCall"], LANGMATCHES: ["builtInCall"], + LANGDIR: ["builtInCall"], DATATYPE: ["builtInCall"], BOUND: ["builtInCall"], IRI: ["builtInCall"], @@ -2800,6 +3165,7 @@ export const table = { COALESCE: ["builtInCall"], IF: ["builtInCall"], STRLANG: ["builtInCall"], + STRLANGDIR: ["builtInCall"], STRDT: ["builtInCall"], SAMETERM: ["builtInCall"], ISIRI: ["builtInCall"], @@ -2807,6 +3173,20 @@ export const table = { ISBLANK: ["builtInCall"], ISLITERAL: ["builtInCall"], ISNUMERIC: ["builtInCall"], + HASLANG: ["builtInCall"], + HASLANGDIR: ["builtInCall"], + ISTRIPLE: ["builtInCall"], + TRIPLE: ["builtInCall"], + SUBJECT: ["builtInCall"], + PREDICATE: ["builtInCall"], + OBJECT: ["builtInCall"], + COUNT: ["builtInCall"], + SUM: ["builtInCall"], + MIN: ["builtInCall"], + MAX: ["builtInCall"], + AVG: ["builtInCall"], + SAMPLE: ["builtInCall"], + GROUP_CONCAT: ["builtInCall"], SUBSTR: ["builtInCall"], REPLACE: ["builtInCall"], REGEX: ["builtInCall"], @@ -2834,8 +3214,10 @@ export const table = { VAR1: ["?triplesBlock", "*[graphPatternNotTriples,?.,?triplesBlock]"], VAR2: ["?triplesBlock", "*[graphPatternNotTriples,?.,?triplesBlock]"], NIL: ["?triplesBlock", "*[graphPatternNotTriples,?.,?triplesBlock]"], + "<<(": ["?triplesBlock", "*[graphPatternNotTriples,?.,?triplesBlock]"], "(": ["?triplesBlock", "*[graphPatternNotTriples,?.,?triplesBlock]"], "[": ["?triplesBlock", "*[graphPatternNotTriples,?.,?triplesBlock]"], + "<<": ["?triplesBlock", "*[graphPatternNotTriples,?.,?triplesBlock]"], IRI_REF: ["?triplesBlock", "*[graphPatternNotTriples,?.,?triplesBlock]"], TRUE: ["?triplesBlock", "*[graphPatternNotTriples,?.,?triplesBlock]"], FALSE: ["?triplesBlock", "*[graphPatternNotTriples,?.,?triplesBlock]"], @@ -2869,6 +3251,7 @@ export const table = { STR: ["constraint"], LANG: ["constraint"], LANGMATCHES: ["constraint"], + LANGDIR: ["constraint"], DATATYPE: ["constraint"], BOUND: ["constraint"], IRI: ["constraint"], @@ -2908,6 +3291,7 @@ export const table = { COALESCE: ["constraint"], IF: ["constraint"], STRLANG: ["constraint"], + STRLANGDIR: ["constraint"], STRDT: ["constraint"], SAMETERM: ["constraint"], ISIRI: ["constraint"], @@ -2915,6 +3299,20 @@ export const table = { ISBLANK: ["constraint"], ISLITERAL: ["constraint"], ISNUMERIC: ["constraint"], + HASLANG: ["constraint"], + HASLANGDIR: ["constraint"], + ISTRIPLE: ["constraint"], + TRIPLE: ["constraint"], + SUBJECT: ["constraint"], + PREDICATE: ["constraint"], + OBJECT: ["constraint"], + COUNT: ["constraint"], + SUM: ["constraint"], + MIN: ["constraint"], + MAX: ["constraint"], + AVG: ["constraint"], + SAMPLE: ["constraint"], + GROUP_CONCAT: ["constraint"], SUBSTR: ["constraint"], REPLACE: ["constraint"], REGEX: ["constraint"], @@ -2991,6 +3389,7 @@ export const table = { STR: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], LANG: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], LANGMATCHES: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], + LANGDIR: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], DATATYPE: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], BOUND: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], IRI: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], @@ -3030,6 +3429,7 @@ export const table = { COALESCE: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], IF: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], STRLANG: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], + STRLANGDIR: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], STRDT: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], SAMETERM: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], ISIRI: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], @@ -3037,8 +3437,16 @@ export const table = { ISBLANK: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], ISLITERAL: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], ISNUMERIC: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], + HASLANG: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], + HASLANGDIR: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], + ISTRIPLE: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], + TRIPLE: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], + SUBJECT: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], + PREDICATE: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], + OBJECT: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], TRUE: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], FALSE: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], + "<<(": ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], COUNT: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], SUM: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], MIN: ["unaryExpression", "*or([[*,unaryExpression],[/,unaryExpression]])"], @@ -3084,6 +3492,7 @@ export const table = { STR: ["additiveExpression"], LANG: ["additiveExpression"], LANGMATCHES: ["additiveExpression"], + LANGDIR: ["additiveExpression"], DATATYPE: ["additiveExpression"], BOUND: ["additiveExpression"], IRI: ["additiveExpression"], @@ -3123,6 +3532,7 @@ export const table = { COALESCE: ["additiveExpression"], IF: ["additiveExpression"], STRLANG: ["additiveExpression"], + STRLANGDIR: ["additiveExpression"], STRDT: ["additiveExpression"], SAMETERM: ["additiveExpression"], ISIRI: ["additiveExpression"], @@ -3130,8 +3540,16 @@ export const table = { ISBLANK: ["additiveExpression"], ISLITERAL: ["additiveExpression"], ISNUMERIC: ["additiveExpression"], + HASLANG: ["additiveExpression"], + HASLANGDIR: ["additiveExpression"], + ISTRIPLE: ["additiveExpression"], + TRIPLE: ["additiveExpression"], + SUBJECT: ["additiveExpression"], + PREDICATE: ["additiveExpression"], + OBJECT: ["additiveExpression"], TRUE: ["additiveExpression"], FALSE: ["additiveExpression"], + "<<(": ["additiveExpression"], COUNT: ["additiveExpression"], SUM: ["additiveExpression"], MIN: ["additiveExpression"], @@ -3188,38 +3606,42 @@ export const table = { DOUBLE: ["DOUBLE"], }, object: { - "(": ["graphNode"], - "[": ["graphNode"], - VAR1: ["graphNode"], - VAR2: ["graphNode"], - NIL: ["graphNode"], - IRI_REF: ["graphNode"], - TRUE: ["graphNode"], - FALSE: ["graphNode"], - BLANK_NODE_LABEL: ["graphNode"], - ANON: ["graphNode"], - PNAME_LN: ["graphNode"], - PNAME_NS: ["graphNode"], - STRING_LITERAL1: ["graphNode"], - STRING_LITERAL2: ["graphNode"], - STRING_LITERAL_LONG1: ["graphNode"], - STRING_LITERAL_LONG2: ["graphNode"], - INTEGER: ["graphNode"], - DECIMAL: ["graphNode"], - DOUBLE: ["graphNode"], - INTEGER_POSITIVE: ["graphNode"], - DECIMAL_POSITIVE: ["graphNode"], - DOUBLE_POSITIVE: ["graphNode"], - INTEGER_NEGATIVE: ["graphNode"], - DECIMAL_NEGATIVE: ["graphNode"], - DOUBLE_NEGATIVE: ["graphNode"], + "(": ["graphNode", "annotation"], + "[": ["graphNode", "annotation"], + "<<": ["graphNode", "annotation"], + VAR1: ["graphNode", "annotation"], + VAR2: ["graphNode", "annotation"], + NIL: ["graphNode", "annotation"], + "<<(": ["graphNode", "annotation"], + IRI_REF: ["graphNode", "annotation"], + TRUE: ["graphNode", "annotation"], + FALSE: ["graphNode", "annotation"], + BLANK_NODE_LABEL: ["graphNode", "annotation"], + ANON: ["graphNode", "annotation"], + PNAME_LN: ["graphNode", "annotation"], + PNAME_NS: ["graphNode", "annotation"], + STRING_LITERAL1: ["graphNode", "annotation"], + STRING_LITERAL2: ["graphNode", "annotation"], + STRING_LITERAL_LONG1: ["graphNode", "annotation"], + STRING_LITERAL_LONG2: ["graphNode", "annotation"], + INTEGER: ["graphNode", "annotation"], + DECIMAL: ["graphNode", "annotation"], + DOUBLE: ["graphNode", "annotation"], + INTEGER_POSITIVE: ["graphNode", "annotation"], + DECIMAL_POSITIVE: ["graphNode", "annotation"], + DOUBLE_POSITIVE: ["graphNode", "annotation"], + INTEGER_NEGATIVE: ["graphNode", "annotation"], + DECIMAL_NEGATIVE: ["graphNode", "annotation"], + DOUBLE_NEGATIVE: ["graphNode", "annotation"], }, objectList: { "(": ["object", "*[,,object]"], "[": ["object", "*[,,object]"], + "<<": ["object", "*[,,object]"], VAR1: ["object", "*[,,object]"], VAR2: ["object", "*[,,object]"], NIL: ["object", "*[,,object]"], + "<<(": ["object", "*[,,object]"], IRI_REF: ["object", "*[,,object]"], TRUE: ["object", "*[,,object]"], FALSE: ["object", "*[,,object]"], @@ -3244,9 +3666,11 @@ export const table = { objectListPath: { "(": ["objectPath", "*[,,objectPath]"], "[": ["objectPath", "*[,,objectPath]"], + "<<": ["objectPath", "*[,,objectPath]"], VAR1: ["objectPath", "*[,,objectPath]"], VAR2: ["objectPath", "*[,,objectPath]"], NIL: ["objectPath", "*[,,objectPath]"], + "<<(": ["objectPath", "*[,,objectPath]"], IRI_REF: ["objectPath", "*[,,objectPath]"], TRUE: ["objectPath", "*[,,objectPath]"], FALSE: ["objectPath", "*[,,objectPath]"], @@ -3269,31 +3693,33 @@ export const table = { DOUBLE_NEGATIVE: ["objectPath", "*[,,objectPath]"], }, objectPath: { - "(": ["graphNodePath"], - "[": ["graphNodePath"], - VAR1: ["graphNodePath"], - VAR2: ["graphNodePath"], - NIL: ["graphNodePath"], - IRI_REF: ["graphNodePath"], - TRUE: ["graphNodePath"], - FALSE: ["graphNodePath"], - BLANK_NODE_LABEL: ["graphNodePath"], - ANON: ["graphNodePath"], - PNAME_LN: ["graphNodePath"], - PNAME_NS: ["graphNodePath"], - STRING_LITERAL1: ["graphNodePath"], - STRING_LITERAL2: ["graphNodePath"], - STRING_LITERAL_LONG1: ["graphNodePath"], - STRING_LITERAL_LONG2: ["graphNodePath"], - INTEGER: ["graphNodePath"], - DECIMAL: ["graphNodePath"], - DOUBLE: ["graphNodePath"], - INTEGER_POSITIVE: ["graphNodePath"], - DECIMAL_POSITIVE: ["graphNodePath"], - DOUBLE_POSITIVE: ["graphNodePath"], - INTEGER_NEGATIVE: ["graphNodePath"], - DECIMAL_NEGATIVE: ["graphNodePath"], - DOUBLE_NEGATIVE: ["graphNodePath"], + "(": ["graphNodePath", "annotationPath"], + "[": ["graphNodePath", "annotationPath"], + "<<": ["graphNodePath", "annotationPath"], + VAR1: ["graphNodePath", "annotationPath"], + VAR2: ["graphNodePath", "annotationPath"], + NIL: ["graphNodePath", "annotationPath"], + "<<(": ["graphNodePath", "annotationPath"], + IRI_REF: ["graphNodePath", "annotationPath"], + TRUE: ["graphNodePath", "annotationPath"], + FALSE: ["graphNodePath", "annotationPath"], + BLANK_NODE_LABEL: ["graphNodePath", "annotationPath"], + ANON: ["graphNodePath", "annotationPath"], + PNAME_LN: ["graphNodePath", "annotationPath"], + PNAME_NS: ["graphNodePath", "annotationPath"], + STRING_LITERAL1: ["graphNodePath", "annotationPath"], + STRING_LITERAL2: ["graphNodePath", "annotationPath"], + STRING_LITERAL_LONG1: ["graphNodePath", "annotationPath"], + STRING_LITERAL_LONG2: ["graphNodePath", "annotationPath"], + INTEGER: ["graphNodePath", "annotationPath"], + DECIMAL: ["graphNodePath", "annotationPath"], + DOUBLE: ["graphNodePath", "annotationPath"], + INTEGER_POSITIVE: ["graphNodePath", "annotationPath"], + DECIMAL_POSITIVE: ["graphNodePath", "annotationPath"], + DOUBLE_POSITIVE: ["graphNodePath", "annotationPath"], + INTEGER_NEGATIVE: ["graphNodePath", "annotationPath"], + DECIMAL_NEGATIVE: ["graphNodePath", "annotationPath"], + DOUBLE_NEGATIVE: ["graphNodePath", "annotationPath"], }, offsetClause: { OFFSET: ["OFFSET", "INTEGER"], @@ -3312,6 +3738,7 @@ export const table = { STR: ["expression"], LANG: ["expression"], LANGMATCHES: ["expression"], + LANGDIR: ["expression"], DATATYPE: ["expression"], BOUND: ["expression"], IRI: ["expression"], @@ -3351,6 +3778,7 @@ export const table = { COALESCE: ["expression"], IF: ["expression"], STRLANG: ["expression"], + STRLANGDIR: ["expression"], STRDT: ["expression"], SAMETERM: ["expression"], ISIRI: ["expression"], @@ -3358,8 +3786,16 @@ export const table = { ISBLANK: ["expression"], ISLITERAL: ["expression"], ISNUMERIC: ["expression"], + HASLANG: ["expression"], + HASLANGDIR: ["expression"], + ISTRIPLE: ["expression"], + TRIPLE: ["expression"], + SUBJECT: ["expression"], + PREDICATE: ["expression"], + OBJECT: ["expression"], TRUE: ["expression"], FALSE: ["expression"], + "<<(": ["expression"], COUNT: ["expression"], SUM: ["expression"], MIN: ["expression"], @@ -3411,8 +3847,8 @@ export const table = { DISTINCT: ["DISTINCT"], REDUCED: ["REDUCED"], }, - "or([LANGTAG,[^^,iriRef]])": { - LANGTAG: ["LANGTAG"], + "or([LANG_DIR,[^^,iriRef]])": { + LANG_DIR: ["LANG_DIR"], "^^": ["[^^,iriRef]"], }, "or([NIL,[(,*var,)]])": { @@ -3483,9 +3919,10 @@ export const table = { INTEGER: ["[integer,or([[,,or([},[integer,}]])],}])]"], ",": ["[,,integer,}]"], }, - "or([baseDecl,prefixDecl])": { + "or([baseDecl,prefixDecl,versionDecl])": { BASE: ["baseDecl"], PREFIX: ["prefixDecl"], + VERSION: ["versionDecl"], }, "or([defaultGraphClause,namedGraphClause])": { IRI_REF: ["defaultGraphClause"], @@ -3519,6 +3956,10 @@ export const table = { DECIMAL_NEGATIVE: ["numericLiteralNegative"], DOUBLE_NEGATIVE: ["numericLiteralNegative"], }, + "or([pathReifier,annotationBlockPath])": { + "~": ["pathReifier"], + "{|": ["annotationBlockPath"], + }, "or([queryAll,updateAll])": { CONSTRUCT: ["queryAll"], DESCRIBE: ["queryAll"], @@ -3536,6 +3977,10 @@ export const table = { WITH: ["updateAll"], $: ["updateAll"], }, + "or([reifier,annotationBlock])": { + "~": ["reifier"], + "{|": ["annotationBlock"], + }, "or([selectQuery,constructQuery,describeQuery,askQuery])": { SELECT: ["selectQuery"], CONSTRUCT: ["constructQuery"], @@ -3555,8 +4000,10 @@ export const table = { VAR1: ["groupGraphPatternSub"], VAR2: ["groupGraphPatternSub"], NIL: ["groupGraphPatternSub"], + "<<(": ["groupGraphPatternSub"], "(": ["groupGraphPatternSub"], "[": ["groupGraphPatternSub"], + "<<": ["groupGraphPatternSub"], IRI_REF: ["groupGraphPatternSub"], TRUE: ["groupGraphPatternSub"], FALSE: ["groupGraphPatternSub"], @@ -3609,6 +4056,7 @@ export const table = { STR: ["constraint"], LANG: ["constraint"], LANGMATCHES: ["constraint"], + LANGDIR: ["constraint"], DATATYPE: ["constraint"], BOUND: ["constraint"], IRI: ["constraint"], @@ -3648,6 +4096,7 @@ export const table = { COALESCE: ["constraint"], IF: ["constraint"], STRLANG: ["constraint"], + STRLANGDIR: ["constraint"], STRDT: ["constraint"], SAMETERM: ["constraint"], ISIRI: ["constraint"], @@ -3655,6 +4104,20 @@ export const table = { ISBLANK: ["constraint"], ISLITERAL: ["constraint"], ISNUMERIC: ["constraint"], + HASLANG: ["constraint"], + HASLANGDIR: ["constraint"], + ISTRIPLE: ["constraint"], + TRIPLE: ["constraint"], + SUBJECT: ["constraint"], + PREDICATE: ["constraint"], + OBJECT: ["constraint"], + COUNT: ["constraint"], + SUM: ["constraint"], + MIN: ["constraint"], + MAX: ["constraint"], + AVG: ["constraint"], + SAMPLE: ["constraint"], + GROUP_CONCAT: ["constraint"], SUBSTR: ["constraint"], REPLACE: ["constraint"], REGEX: ["constraint"], @@ -3730,6 +4193,9 @@ export const table = { "!": ["!", "pathNegatedPropertySet"], "(": ["(", "path", ")"], }, + pathReifier: { + "~": ["reifier"], + }, pathSequence: { "^": ["pathEltOrInverse", "*[/,pathEltOrInverse]"], a: ["pathEltOrInverse", "*[/,pathEltOrInverse]"], @@ -3751,6 +4217,7 @@ export const table = { STR: ["builtInCall"], LANG: ["builtInCall"], LANGMATCHES: ["builtInCall"], + LANGDIR: ["builtInCall"], DATATYPE: ["builtInCall"], BOUND: ["builtInCall"], IRI: ["builtInCall"], @@ -3790,6 +4257,7 @@ export const table = { COALESCE: ["builtInCall"], IF: ["builtInCall"], STRLANG: ["builtInCall"], + STRLANGDIR: ["builtInCall"], STRDT: ["builtInCall"], SAMETERM: ["builtInCall"], ISIRI: ["builtInCall"], @@ -3797,6 +4265,20 @@ export const table = { ISBLANK: ["builtInCall"], ISLITERAL: ["builtInCall"], ISNUMERIC: ["builtInCall"], + HASLANG: ["builtInCall"], + HASLANGDIR: ["builtInCall"], + ISTRIPLE: ["builtInCall"], + TRIPLE: ["builtInCall"], + SUBJECT: ["builtInCall"], + PREDICATE: ["builtInCall"], + OBJECT: ["builtInCall"], + COUNT: ["builtInCall"], + SUM: ["builtInCall"], + MIN: ["builtInCall"], + MAX: ["builtInCall"], + AVG: ["builtInCall"], + SAMPLE: ["builtInCall"], + GROUP_CONCAT: ["builtInCall"], SUBSTR: ["builtInCall"], REPLACE: ["builtInCall"], REGEX: ["builtInCall"], @@ -3822,32 +4304,27 @@ export const table = { FALSE: ["booleanLiteral"], VAR1: ["var"], VAR2: ["var"], - COUNT: ["aggregate"], - SUM: ["aggregate"], - MIN: ["aggregate"], - MAX: ["aggregate"], - AVG: ["aggregate"], - SAMPLE: ["aggregate"], - GROUP_CONCAT: ["aggregate"], + "<<(": ["exprTripleTerm"], }, prologue: { - BASE: ["*or([baseDecl,prefixDecl])"], - PREFIX: ["*or([baseDecl,prefixDecl])"], - $: ["*or([baseDecl,prefixDecl])"], - CONSTRUCT: ["*or([baseDecl,prefixDecl])"], - DESCRIBE: ["*or([baseDecl,prefixDecl])"], - ASK: ["*or([baseDecl,prefixDecl])"], - INSERT: ["*or([baseDecl,prefixDecl])"], - DELETE: ["*or([baseDecl,prefixDecl])"], - SELECT: ["*or([baseDecl,prefixDecl])"], - LOAD: ["*or([baseDecl,prefixDecl])"], - CLEAR: ["*or([baseDecl,prefixDecl])"], - DROP: ["*or([baseDecl,prefixDecl])"], - ADD: ["*or([baseDecl,prefixDecl])"], - MOVE: ["*or([baseDecl,prefixDecl])"], - COPY: ["*or([baseDecl,prefixDecl])"], - CREATE: ["*or([baseDecl,prefixDecl])"], - WITH: ["*or([baseDecl,prefixDecl])"], + BASE: ["*or([baseDecl,prefixDecl,versionDecl])"], + PREFIX: ["*or([baseDecl,prefixDecl,versionDecl])"], + VERSION: ["*or([baseDecl,prefixDecl,versionDecl])"], + $: ["*or([baseDecl,prefixDecl,versionDecl])"], + CONSTRUCT: ["*or([baseDecl,prefixDecl,versionDecl])"], + DESCRIBE: ["*or([baseDecl,prefixDecl,versionDecl])"], + ASK: ["*or([baseDecl,prefixDecl,versionDecl])"], + INSERT: ["*or([baseDecl,prefixDecl,versionDecl])"], + DELETE: ["*or([baseDecl,prefixDecl,versionDecl])"], + SELECT: ["*or([baseDecl,prefixDecl,versionDecl])"], + LOAD: ["*or([baseDecl,prefixDecl,versionDecl])"], + CLEAR: ["*or([baseDecl,prefixDecl,versionDecl])"], + DROP: ["*or([baseDecl,prefixDecl,versionDecl])"], + ADD: ["*or([baseDecl,prefixDecl,versionDecl])"], + MOVE: ["*or([baseDecl,prefixDecl,versionDecl])"], + COPY: ["*or([baseDecl,prefixDecl,versionDecl])"], + CREATE: ["*or([baseDecl,prefixDecl,versionDecl])"], + WITH: ["*or([baseDecl,prefixDecl,versionDecl])"], }, propertyList: { a: ["propertyListNotEmpty"], @@ -3869,12 +4346,15 @@ export const table = { PNAME_NS: ["verb", "objectList", "*[;,?[verb,objectList]]"], }, propertyListPath: { - a: ["propertyListNotEmpty"], - VAR1: ["propertyListNotEmpty"], - VAR2: ["propertyListNotEmpty"], - IRI_REF: ["propertyListNotEmpty"], - PNAME_LN: ["propertyListNotEmpty"], - PNAME_NS: ["propertyListNotEmpty"], + VAR1: ["propertyListPathNotEmpty"], + VAR2: ["propertyListPathNotEmpty"], + "^": ["propertyListPathNotEmpty"], + a: ["propertyListPathNotEmpty"], + "!": ["propertyListPathNotEmpty"], + "(": ["propertyListPathNotEmpty"], + IRI_REF: ["propertyListPathNotEmpty"], + PNAME_LN: ["propertyListPathNotEmpty"], + PNAME_NS: ["propertyListPathNotEmpty"], ".": [], "{": [], OPTIONAL: [], @@ -3914,8 +4394,10 @@ export const table = { VAR1: ["?triplesTemplate", "*[quadsNotTriples,?.,?triplesTemplate]"], VAR2: ["?triplesTemplate", "*[quadsNotTriples,?.,?triplesTemplate]"], NIL: ["?triplesTemplate", "*[quadsNotTriples,?.,?triplesTemplate]"], + "<<(": ["?triplesTemplate", "*[quadsNotTriples,?.,?triplesTemplate]"], "(": ["?triplesTemplate", "*[quadsNotTriples,?.,?triplesTemplate]"], "[": ["?triplesTemplate", "*[quadsNotTriples,?.,?triplesTemplate]"], + "<<": ["?triplesTemplate", "*[quadsNotTriples,?.,?triplesTemplate]"], IRI_REF: ["?triplesTemplate", "*[quadsNotTriples,?.,?triplesTemplate]"], TRUE: ["?triplesTemplate", "*[quadsNotTriples,?.,?triplesTemplate]"], FALSE: ["?triplesTemplate", "*[quadsNotTriples,?.,?triplesTemplate]"], @@ -3948,14 +4430,78 @@ export const table = { SELECT: ["or([selectQuery,constructQuery,describeQuery,askQuery])", "valuesClause"], }, rdfLiteral: { - STRING_LITERAL1: ["string", "?or([LANGTAG,[^^,iriRef]])"], - STRING_LITERAL2: ["string", "?or([LANGTAG,[^^,iriRef]])"], - STRING_LITERAL_LONG1: ["string", "?or([LANGTAG,[^^,iriRef]])"], - STRING_LITERAL_LONG2: ["string", "?or([LANGTAG,[^^,iriRef]])"], + STRING_LITERAL1: ["string", "?or([LANG_DIR,[^^,iriRef]])"], + STRING_LITERAL2: ["string", "?or([LANG_DIR,[^^,iriRef]])"], + STRING_LITERAL_LONG1: ["string", "?or([LANG_DIR,[^^,iriRef]])"], + STRING_LITERAL_LONG2: ["string", "?or([LANG_DIR,[^^,iriRef]])"], }, regexExpression: { REGEX: ["REGEX", "(", "expression", ",", "expression", "?[,,expression]", ")"], }, + reifiedTriple: { + "<<": ["<<", "reifiedTripleSubject", "verb", "reifiedTripleObject", "?reifier", ">>"], + }, + reifiedTripleBlock: { + "<<": ["reifiedTriple", "propertyList"], + }, + reifiedTripleBlockPath: { + "<<": ["reifiedTriple", "propertyListPath"], + }, + reifiedTripleObject: { + VAR1: ["var"], + VAR2: ["var"], + IRI_REF: ["iriRef"], + PNAME_LN: ["iriRef"], + PNAME_NS: ["iriRef"], + STRING_LITERAL1: ["rdfLiteral"], + STRING_LITERAL2: ["rdfLiteral"], + STRING_LITERAL_LONG1: ["rdfLiteral"], + STRING_LITERAL_LONG2: ["rdfLiteral"], + INTEGER: ["numericLiteral"], + DECIMAL: ["numericLiteral"], + DOUBLE: ["numericLiteral"], + INTEGER_POSITIVE: ["numericLiteral"], + DECIMAL_POSITIVE: ["numericLiteral"], + DOUBLE_POSITIVE: ["numericLiteral"], + INTEGER_NEGATIVE: ["numericLiteral"], + DECIMAL_NEGATIVE: ["numericLiteral"], + DOUBLE_NEGATIVE: ["numericLiteral"], + TRUE: ["booleanLiteral"], + FALSE: ["booleanLiteral"], + BLANK_NODE_LABEL: ["blankNode"], + ANON: ["blankNode"], + "<<": ["reifiedTriple"], + "<<(": ["tripleTerm"], + }, + reifiedTripleSubject: { + VAR1: ["var"], + VAR2: ["var"], + IRI_REF: ["iriRef"], + PNAME_LN: ["iriRef"], + PNAME_NS: ["iriRef"], + STRING_LITERAL1: ["rdfLiteral"], + STRING_LITERAL2: ["rdfLiteral"], + STRING_LITERAL_LONG1: ["rdfLiteral"], + STRING_LITERAL_LONG2: ["rdfLiteral"], + INTEGER: ["numericLiteral"], + DECIMAL: ["numericLiteral"], + DOUBLE: ["numericLiteral"], + INTEGER_POSITIVE: ["numericLiteral"], + DECIMAL_POSITIVE: ["numericLiteral"], + DOUBLE_POSITIVE: ["numericLiteral"], + INTEGER_NEGATIVE: ["numericLiteral"], + DECIMAL_NEGATIVE: ["numericLiteral"], + DOUBLE_NEGATIVE: ["numericLiteral"], + TRUE: ["booleanLiteral"], + FALSE: ["booleanLiteral"], + BLANK_NODE_LABEL: ["blankNode"], + ANON: ["blankNode"], + "<<": ["reifiedTriple"], + "<<(": ["tripleTerm"], + }, + reifier: { + "~": ["~", "?varOrReifierId"], + }, relationalExpression: { "!": [ "numericExpression", @@ -3993,6 +4539,10 @@ export const table = { "numericExpression", "?or([[=,numericExpression],[!=,numericExpression],[<,numericExpression],[>,numericExpression],[<=,numericExpression],[>=,numericExpression],[IN,expressionList],[NOT,IN,expressionList]])", ], + LANGDIR: [ + "numericExpression", + "?or([[=,numericExpression],[!=,numericExpression],[<,numericExpression],[>,numericExpression],[<=,numericExpression],[>=,numericExpression],[IN,expressionList],[NOT,IN,expressionList]])", + ], DATATYPE: [ "numericExpression", "?or([[=,numericExpression],[!=,numericExpression],[<,numericExpression],[>,numericExpression],[<=,numericExpression],[>=,numericExpression],[IN,expressionList],[NOT,IN,expressionList]])", @@ -4149,6 +4699,10 @@ export const table = { "numericExpression", "?or([[=,numericExpression],[!=,numericExpression],[<,numericExpression],[>,numericExpression],[<=,numericExpression],[>=,numericExpression],[IN,expressionList],[NOT,IN,expressionList]])", ], + STRLANGDIR: [ + "numericExpression", + "?or([[=,numericExpression],[!=,numericExpression],[<,numericExpression],[>,numericExpression],[<=,numericExpression],[>=,numericExpression],[IN,expressionList],[NOT,IN,expressionList]])", + ], STRDT: [ "numericExpression", "?or([[=,numericExpression],[!=,numericExpression],[<,numericExpression],[>,numericExpression],[<=,numericExpression],[>=,numericExpression],[IN,expressionList],[NOT,IN,expressionList]])", @@ -4177,6 +4731,34 @@ export const table = { "numericExpression", "?or([[=,numericExpression],[!=,numericExpression],[<,numericExpression],[>,numericExpression],[<=,numericExpression],[>=,numericExpression],[IN,expressionList],[NOT,IN,expressionList]])", ], + HASLANG: [ + "numericExpression", + "?or([[=,numericExpression],[!=,numericExpression],[<,numericExpression],[>,numericExpression],[<=,numericExpression],[>=,numericExpression],[IN,expressionList],[NOT,IN,expressionList]])", + ], + HASLANGDIR: [ + "numericExpression", + "?or([[=,numericExpression],[!=,numericExpression],[<,numericExpression],[>,numericExpression],[<=,numericExpression],[>=,numericExpression],[IN,expressionList],[NOT,IN,expressionList]])", + ], + ISTRIPLE: [ + "numericExpression", + "?or([[=,numericExpression],[!=,numericExpression],[<,numericExpression],[>,numericExpression],[<=,numericExpression],[>=,numericExpression],[IN,expressionList],[NOT,IN,expressionList]])", + ], + TRIPLE: [ + "numericExpression", + "?or([[=,numericExpression],[!=,numericExpression],[<,numericExpression],[>,numericExpression],[<=,numericExpression],[>=,numericExpression],[IN,expressionList],[NOT,IN,expressionList]])", + ], + SUBJECT: [ + "numericExpression", + "?or([[=,numericExpression],[!=,numericExpression],[<,numericExpression],[>,numericExpression],[<=,numericExpression],[>=,numericExpression],[IN,expressionList],[NOT,IN,expressionList]])", + ], + PREDICATE: [ + "numericExpression", + "?or([[=,numericExpression],[!=,numericExpression],[<,numericExpression],[>,numericExpression],[<=,numericExpression],[>=,numericExpression],[IN,expressionList],[NOT,IN,expressionList]])", + ], + OBJECT: [ + "numericExpression", + "?or([[=,numericExpression],[!=,numericExpression],[<,numericExpression],[>,numericExpression],[<=,numericExpression],[>=,numericExpression],[IN,expressionList],[NOT,IN,expressionList]])", + ], TRUE: [ "numericExpression", "?or([[=,numericExpression],[!=,numericExpression],[<,numericExpression],[>,numericExpression],[<=,numericExpression],[>=,numericExpression],[IN,expressionList],[NOT,IN,expressionList]])", @@ -4185,6 +4767,10 @@ export const table = { "numericExpression", "?or([[=,numericExpression],[!=,numericExpression],[<,numericExpression],[>,numericExpression],[<=,numericExpression],[>=,numericExpression],[IN,expressionList],[NOT,IN,expressionList]])", ], + "<<(": [ + "numericExpression", + "?or([[=,numericExpression],[!=,numericExpression],[<,numericExpression],[>,numericExpression],[<=,numericExpression],[>=,numericExpression],[IN,expressionList],[NOT,IN,expressionList]])", + ], COUNT: [ "numericExpression", "?or([[=,numericExpression],[!=,numericExpression],[<,numericExpression],[>,numericExpression],[<=,numericExpression],[>=,numericExpression],[IN,expressionList],[NOT,IN,expressionList]])", @@ -4340,6 +4926,7 @@ export const table = { WITH: ["prologue", "or([queryAll,updateAll])", "$"], BASE: ["prologue", "or([queryAll,updateAll])", "$"], PREFIX: ["prologue", "or([queryAll,updateAll])", "$"], + VERSION: ["prologue", "or([queryAll,updateAll])", "$"], }, storeProperty: { VAR1: [], @@ -4364,12 +4951,96 @@ export const table = { substringExpression: { SUBSTR: ["SUBSTR", "(", "expression", ",", "expression", "?[,,expression]", ")"], }, + tripleTerm: { + "<<(": ["<<(", "tripleTermSubject", "verb", "tripleTermObject", ")>>"], + }, + tripleTermData: { + "<<(": ["<<(", "tripleTermDataSubject", "or([iriRef,a])", "tripleTermDataObject", ")>>"], + }, + tripleTermDataObject: { + IRI_REF: ["iriRef"], + PNAME_LN: ["iriRef"], + PNAME_NS: ["iriRef"], + STRING_LITERAL1: ["rdfLiteral"], + STRING_LITERAL2: ["rdfLiteral"], + STRING_LITERAL_LONG1: ["rdfLiteral"], + STRING_LITERAL_LONG2: ["rdfLiteral"], + INTEGER: ["numericLiteral"], + DECIMAL: ["numericLiteral"], + DOUBLE: ["numericLiteral"], + INTEGER_POSITIVE: ["numericLiteral"], + DECIMAL_POSITIVE: ["numericLiteral"], + DOUBLE_POSITIVE: ["numericLiteral"], + INTEGER_NEGATIVE: ["numericLiteral"], + DECIMAL_NEGATIVE: ["numericLiteral"], + DOUBLE_NEGATIVE: ["numericLiteral"], + TRUE: ["booleanLiteral"], + FALSE: ["booleanLiteral"], + "<<(": ["tripleTermData"], + }, + tripleTermDataSubject: { + IRI_REF: ["iriRef"], + PNAME_LN: ["iriRef"], + PNAME_NS: ["iriRef"], + }, + tripleTermObject: { + VAR1: ["var"], + VAR2: ["var"], + IRI_REF: ["iriRef"], + PNAME_LN: ["iriRef"], + PNAME_NS: ["iriRef"], + STRING_LITERAL1: ["rdfLiteral"], + STRING_LITERAL2: ["rdfLiteral"], + STRING_LITERAL_LONG1: ["rdfLiteral"], + STRING_LITERAL_LONG2: ["rdfLiteral"], + INTEGER: ["numericLiteral"], + DECIMAL: ["numericLiteral"], + DOUBLE: ["numericLiteral"], + INTEGER_POSITIVE: ["numericLiteral"], + DECIMAL_POSITIVE: ["numericLiteral"], + DOUBLE_POSITIVE: ["numericLiteral"], + INTEGER_NEGATIVE: ["numericLiteral"], + DECIMAL_NEGATIVE: ["numericLiteral"], + DOUBLE_NEGATIVE: ["numericLiteral"], + TRUE: ["booleanLiteral"], + FALSE: ["booleanLiteral"], + BLANK_NODE_LABEL: ["blankNode"], + ANON: ["blankNode"], + "<<(": ["tripleTerm"], + }, + tripleTermSubject: { + VAR1: ["var"], + VAR2: ["var"], + IRI_REF: ["iriRef"], + PNAME_LN: ["iriRef"], + PNAME_NS: ["iriRef"], + STRING_LITERAL1: ["rdfLiteral"], + STRING_LITERAL2: ["rdfLiteral"], + STRING_LITERAL_LONG1: ["rdfLiteral"], + STRING_LITERAL_LONG2: ["rdfLiteral"], + INTEGER: ["numericLiteral"], + DECIMAL: ["numericLiteral"], + DOUBLE: ["numericLiteral"], + INTEGER_POSITIVE: ["numericLiteral"], + DECIMAL_POSITIVE: ["numericLiteral"], + DOUBLE_POSITIVE: ["numericLiteral"], + INTEGER_NEGATIVE: ["numericLiteral"], + DECIMAL_NEGATIVE: ["numericLiteral"], + DOUBLE_NEGATIVE: ["numericLiteral"], + TRUE: ["booleanLiteral"], + FALSE: ["booleanLiteral"], + BLANK_NODE_LABEL: ["blankNode"], + ANON: ["blankNode"], + "<<(": ["tripleTerm"], + }, triplesBlock: { VAR1: ["triplesSameSubjectPath", "?[.,?triplesBlock]"], VAR2: ["triplesSameSubjectPath", "?[.,?triplesBlock]"], NIL: ["triplesSameSubjectPath", "?[.,?triplesBlock]"], + "<<(": ["triplesSameSubjectPath", "?[.,?triplesBlock]"], "(": ["triplesSameSubjectPath", "?[.,?triplesBlock]"], "[": ["triplesSameSubjectPath", "?[.,?triplesBlock]"], + "<<": ["triplesSameSubjectPath", "?[.,?triplesBlock]"], IRI_REF: ["triplesSameSubjectPath", "?[.,?triplesBlock]"], TRUE: ["triplesSameSubjectPath", "?[.,?triplesBlock]"], FALSE: ["triplesSameSubjectPath", "?[.,?triplesBlock]"], @@ -4403,6 +5074,7 @@ export const table = { VAR1: ["varOrTerm", "propertyListNotEmpty"], VAR2: ["varOrTerm", "propertyListNotEmpty"], NIL: ["varOrTerm", "propertyListNotEmpty"], + "<<(": ["varOrTerm", "propertyListNotEmpty"], IRI_REF: ["varOrTerm", "propertyListNotEmpty"], TRUE: ["varOrTerm", "propertyListNotEmpty"], FALSE: ["varOrTerm", "propertyListNotEmpty"], @@ -4425,11 +5097,13 @@ export const table = { DOUBLE_NEGATIVE: ["varOrTerm", "propertyListNotEmpty"], "(": ["triplesNode", "propertyList"], "[": ["triplesNode", "propertyList"], + "<<": ["reifiedTripleBlock"], }, triplesSameSubjectPath: { VAR1: ["varOrTerm", "propertyListPathNotEmpty"], VAR2: ["varOrTerm", "propertyListPathNotEmpty"], NIL: ["varOrTerm", "propertyListPathNotEmpty"], + "<<(": ["varOrTerm", "propertyListPathNotEmpty"], IRI_REF: ["varOrTerm", "propertyListPathNotEmpty"], TRUE: ["varOrTerm", "propertyListPathNotEmpty"], FALSE: ["varOrTerm", "propertyListPathNotEmpty"], @@ -4452,13 +5126,16 @@ export const table = { DOUBLE_NEGATIVE: ["varOrTerm", "propertyListPathNotEmpty"], "(": ["triplesNodePath", "propertyListPath"], "[": ["triplesNodePath", "propertyListPath"], + "<<": ["reifiedTripleBlockPath"], }, triplesTemplate: { VAR1: ["triplesSameSubject", "?[.,?triplesTemplate]"], VAR2: ["triplesSameSubject", "?[.,?triplesTemplate]"], NIL: ["triplesSameSubject", "?[.,?triplesTemplate]"], + "<<(": ["triplesSameSubject", "?[.,?triplesTemplate]"], "(": ["triplesSameSubject", "?[.,?triplesTemplate]"], "[": ["triplesSameSubject", "?[.,?triplesTemplate]"], + "<<": ["triplesSameSubject", "?[.,?triplesTemplate]"], IRI_REF: ["triplesSameSubject", "?[.,?triplesTemplate]"], TRUE: ["triplesSameSubject", "?[.,?triplesTemplate]"], FALSE: ["triplesSameSubject", "?[.,?triplesTemplate]"], @@ -4481,7 +5158,7 @@ export const table = { DOUBLE_NEGATIVE: ["triplesSameSubject", "?[.,?triplesTemplate]"], }, unaryExpression: { - "!": ["!", "primaryExpression"], + "!": ["!", "unaryExpression"], "+": ["+", "primaryExpression"], "-": ["-", "primaryExpression"], VAR1: ["primaryExpression"], @@ -4490,6 +5167,7 @@ export const table = { STR: ["primaryExpression"], LANG: ["primaryExpression"], LANGMATCHES: ["primaryExpression"], + LANGDIR: ["primaryExpression"], DATATYPE: ["primaryExpression"], BOUND: ["primaryExpression"], IRI: ["primaryExpression"], @@ -4529,6 +5207,7 @@ export const table = { COALESCE: ["primaryExpression"], IF: ["primaryExpression"], STRLANG: ["primaryExpression"], + STRLANGDIR: ["primaryExpression"], STRDT: ["primaryExpression"], SAMETERM: ["primaryExpression"], ISIRI: ["primaryExpression"], @@ -4536,8 +5215,16 @@ export const table = { ISBLANK: ["primaryExpression"], ISLITERAL: ["primaryExpression"], ISNUMERIC: ["primaryExpression"], + HASLANG: ["primaryExpression"], + HASLANGDIR: ["primaryExpression"], + ISTRIPLE: ["primaryExpression"], + TRIPLE: ["primaryExpression"], + SUBJECT: ["primaryExpression"], + PREDICATE: ["primaryExpression"], + OBJECT: ["primaryExpression"], TRUE: ["primaryExpression"], FALSE: ["primaryExpression"], + "<<(": ["primaryExpression"], COUNT: ["primaryExpression"], SUM: ["primaryExpression"], MIN: ["primaryExpression"], @@ -4580,6 +5267,7 @@ export const table = { WITH: ["prologue", "?[update1,?[;,update]]"], BASE: ["prologue", "?[update1,?[;,update]]"], PREFIX: ["prologue", "?[update1,?[;,update]]"], + VERSION: ["prologue", "?[update1,?[;,update]]"], $: ["prologue", "?[update1,?[;,update]]"], }, update1: { @@ -4620,6 +5308,7 @@ export const table = { STR: ["relationalExpression"], LANG: ["relationalExpression"], LANGMATCHES: ["relationalExpression"], + LANGDIR: ["relationalExpression"], DATATYPE: ["relationalExpression"], BOUND: ["relationalExpression"], IRI: ["relationalExpression"], @@ -4659,6 +5348,7 @@ export const table = { COALESCE: ["relationalExpression"], IF: ["relationalExpression"], STRLANG: ["relationalExpression"], + STRLANGDIR: ["relationalExpression"], STRDT: ["relationalExpression"], SAMETERM: ["relationalExpression"], ISIRI: ["relationalExpression"], @@ -4666,8 +5356,16 @@ export const table = { ISBLANK: ["relationalExpression"], ISLITERAL: ["relationalExpression"], ISNUMERIC: ["relationalExpression"], + HASLANG: ["relationalExpression"], + HASLANGDIR: ["relationalExpression"], + ISTRIPLE: ["relationalExpression"], + TRIPLE: ["relationalExpression"], + SUBJECT: ["relationalExpression"], + PREDICATE: ["relationalExpression"], + OBJECT: ["relationalExpression"], TRUE: ["relationalExpression"], FALSE: ["relationalExpression"], + "<<(": ["relationalExpression"], COUNT: ["relationalExpression"], SUM: ["relationalExpression"], MIN: ["relationalExpression"], @@ -4713,6 +5411,15 @@ export const table = { PNAME_LN: ["iriRef"], PNAME_NS: ["iriRef"], }, + varOrReifierId: { + VAR1: ["var"], + VAR2: ["var"], + IRI_REF: ["iriRef"], + PNAME_LN: ["iriRef"], + PNAME_NS: ["iriRef"], + BLANK_NODE_LABEL: ["blankNode"], + ANON: ["blankNode"], + }, varOrTerm: { VAR1: ["var"], VAR2: ["var"], @@ -4737,6 +5444,7 @@ export const table = { INTEGER_NEGATIVE: ["graphTerm"], DECIMAL_NEGATIVE: ["graphTerm"], DOUBLE_NEGATIVE: ["graphTerm"], + "<<(": ["tripleTerm"], }, verb: { VAR1: ["storeProperty", "varOrIRIref"], @@ -4759,6 +5467,13 @@ export const table = { VAR1: ["var"], VAR2: ["var"], }, + versionDecl: { + VERSION: ["VERSION", "versionSpecifier"], + }, + versionSpecifier: { + STRING_LITERAL1: ["STRING_LITERAL1"], + STRING_LITERAL2: ["STRING_LITERAL2"], + }, whereClause: { "{": ["?WHERE", "groupGraphPattern"], WHERE: ["?WHERE", "groupGraphPattern"], @@ -4766,9 +5481,10 @@ export const table = { }; export const keywords = - /^(GROUP_CONCAT|DATATYPE|BASE|PREFIX|SELECT|CONSTRUCT|DESCRIBE|ASK|FROM|NAMED|ORDER|BY|LIMIT|ASC|DESC|OFFSET|DISTINCT|REDUCED|WHERE|GRAPH|OPTIONAL|UNION|FILTER|GROUP|HAVING|AS|VALUES|LOAD|CLEAR|DROP|CREATE|MOVE|COPY|SILENT|INSERT|DELETE|DATA|WITH|TO|USING|NAMED|MINUS|BIND|LANGMATCHES|LANG|BOUND|SAMETERM|ISIRI|ISURI|ISBLANK|ISLITERAL|REGEX|TRUE|FALSE|UNDEF|ADD|DEFAULT|ALL|SERVICE|INTO|IN|NOT|IRI|URI|BNODE|RAND|ABS|CEIL|FLOOR|ROUND|CONCAT|STRLEN|UCASE|LCASE|ENCODE_FOR_URI|CONTAINS|STRSTARTS|STRENDS|STRBEFORE|STRAFTER|YEAR|MONTH|DAY|HOURS|MINUTES|SECONDS|TIMEZONE|TZ|NOW|UUID|STRUUID|MD5|SHA1|SHA256|SHA384|SHA512|COALESCE|IF|STRLANG|STRDT|ISNUMERIC|SUBSTR|REPLACE|EXISTS|COUNT|SUM|MIN|MAX|AVG|SAMPLE|SEPARATOR|STR)/i; + /^(GROUP_CONCAT|DATATYPE|LANGDIR|STRLANGDIR|HASLANGDIR|BASE|PREFIX|VERSION|SELECT|CONSTRUCT|DESCRIBE|ASK|FROM|NAMED|ORDER|BY|LIMIT|ASC|DESC|OFFSET|DISTINCT|REDUCED|WHERE|GRAPH|OPTIONAL|UNION|FILTER|GROUP|HAVING|AS|VALUES|LOAD|CLEAR|DROP|CREATE|MOVE|COPY|SILENT|INSERT|DELETE|DATA|WITH|TO|USING|NAMED|MINUS|BIND|LANGMATCHES|LANG|BOUND|SAMETERM|ISIRI|ISURI|ISBLANK|ISLITERAL|REGEX|TRUE|FALSE|UNDEF|ADD|DEFAULT|ALL|SERVICE|INTO|IN|NOT|IRI|URI|BNODE|RAND|ABS|CEIL|FLOOR|ROUND|CONCAT|STRLEN|UCASE|LCASE|ENCODE_FOR_URI|CONTAINS|STRSTARTS|STRENDS|STRBEFORE|STRAFTER|YEAR|MONTH|DAY|HOURS|MINUTES|SECONDS|TIMEZONE|TZ|NOW|UUID|STRUUID|MD5|SHA1|SHA256|SHA384|SHA512|COALESCE|IF|STRLANG|STRDT|ISNUMERIC|SUBSTR|REPLACE|EXISTS|COUNT|SUM|MIN|MAX|AVG|SAMPLE|SEPARATOR|HASLANG|ISTRIPLE|TRIPLE|SUBJECT|PREDICATE|OBJECT|STR)/i; -export const punct = /^(\*|a|\.|\{|\}|,|\(|\)|;|\[|\]|\|\||&&|=|!=|!|<=|>=|<|>|\+|-|\/|\^\^|\?|\||\^)/; +export const punct = + /^(\*|a|\.|\{\||\|\}|\{|\}|,|\)>>|\(|\)|;|\[|\]|\|\||&&|=|!=|!|<<\(|<<|>>|<=|>=|<|>|\+|-|\/|\^\^|\?|\||\^|~)/; export const startSymbol = "sparql11"; export const acceptEmpty = true; diff --git a/packages/yasqe/grammar/sparql11-grammar.pl b/packages/yasqe/grammar/sparql11-grammar.pl index 7a45c44e..d4897cec 100644 --- a/packages/yasqe/grammar/sparql11-grammar.pl +++ b/packages/yasqe/grammar/sparql11-grammar.pl @@ -3,6 +3,17 @@ SPARQL 1.1 grammar rules based on the Last Call Working Draft of 24/07/2012: http://www.w3.org/TR/2012/WD-sparql11-query-20120724/#sparqlGrammar +Extended with the SPARQL 1.2 grammar (Working Draft of 21/09/2026): + https://www.w3.org/TR/sparql12-query/#sparqlGrammar + - VERSION declaration + - reified triples << s p o ~ r >> and triple terms <<( s p o )>> + - reifiers (~) and annotation blocks {| ... |} + - triple terms in VALUES and in expressions + - base direction in language tags ("x"@en--ltr) + - new built-ins (LANGDIR, STRLANGDIR, hasLANG, hasLANGDIR, isTRIPLE, + TRIPLE, SUBJECT, PREDICATE, OBJECT); aggregates are built-in calls + - '!' applies to a UnaryExpression + Be careful with grammar notation - it is EBNF in prolog syntax! [...] lists always represent sequence. @@ -33,7 +44,7 @@ prologue ==> %[?(baseDecl),*(prefixDecl)]. - [*(baseDecl or prefixDecl)]. + [*(or(baseDecl,prefixDecl,versionDecl))]. baseDecl ==> ['BASE','IRI_REF']. @@ -41,6 +52,13 @@ prefixDecl ==> ['PREFIX','PNAME_NS','IRI_REF']. +% SPARQL 1.2 [7] +versionDecl ==> + ['VERSION',versionSpecifier]. +% SPARQL 1.2 [8] +versionSpecifier ==> ['STRING_LITERAL1']. +versionSpecifier ==> ['STRING_LITERAL2']. + % [7] selectQuery ==> [selectClause,*(datasetClause),whereClause,solutionModifier]. @@ -301,6 +319,14 @@ dataBlockValue ==> [numericLiteral]. dataBlockValue ==> [booleanLiteral]. dataBlockValue ==> ['UNDEF']. +dataBlockValue ==> [tripleTermData]. + +% SPARQL 1.2 [70] +reifier ==> ['~',?(varOrReifierId)]. +% SPARQL 1.2 [71] +varOrReifierId ==> [var]. +varOrReifierId ==> [iriRef]. +varOrReifierId ==> [blankNode]. %[66] minusGraphPattern ==> @@ -340,6 +366,10 @@ [varOrTerm,propertyListNotEmpty]. triplesSameSubject ==> [triplesNode,propertyList]. +triplesSameSubject ==> + [reifiedTripleBlock]. +% SPARQL 1.2 [58] +reifiedTripleBlock ==> [reifiedTriple,propertyList]. %[76] propertyList ==> [propertyListNotEmpty]. propertyList ==> []. @@ -356,12 +386,15 @@ [object,*([',',object])]. %[80] object ==> - [graphNode]. + [graphNode,annotation]. %[81] triplesSameSubjectPath ==> [varOrTerm,propertyListPathNotEmpty]. triplesSameSubjectPath ==> [triplesNodePath,propertyListPath]. +triplesSameSubjectPath ==> [reifiedTripleBlockPath]. +% SPARQL 1.2 [59] +reifiedTripleBlockPath ==> [reifiedTriple,propertyListPath]. %[82] -propertyListPath ==> [propertyListNotEmpty]. +propertyListPath ==> [propertyListPathNotEmpty]. propertyListPath ==> []. %[83] propertyListPathNotEmpty ==> @@ -376,7 +409,7 @@ objectListPath ==> [objectPath,*([',',objectPath])]. %[87] -objectPath ==> [graphNodePath]. +objectPath ==> [graphNodePath,annotationPath]. %[88] path ==> [pathAlternative]. %[89]. @@ -448,15 +481,80 @@ collection ==> ['(',+(graphNode),')']. %[103] collectionPath ==> ['(',+(graphNodePath),')']. +% SPARQL 1.2 [109] +% pathReifier only wraps reifier, so that the tokenizer can reject reifiers +% and annotations after a predicate that is not a simple path. +annotationPath ==> [*(pathReifier or annotationBlockPath)]. +pathReifier ==> [reifier]. +% SPARQL 1.2 [110] +annotationBlockPath ==> ['{|',propertyListPathNotEmpty,'|}']. +% SPARQL 1.2 [111] +annotation ==> [*(reifier or annotationBlock)]. +% SPARQL 1.2 [112] +annotationBlock ==> ['{|',propertyListNotEmpty,'|}']. %[104] graphNode ==> [varOrTerm]. graphNode ==> [triplesNode]. +graphNode ==> [reifiedTriple]. %[105] graphNodePath ==> [varOrTerm]. graphNodePath ==> [triplesNodePath]. +graphNodePath ==> [reifiedTriple]. %[106] varOrTerm ==> [var]. varOrTerm ==> [graphTerm]. +varOrTerm ==> [tripleTerm]. +% SPARQL 1.2 [116] +reifiedTriple ==> + ['<<',reifiedTripleSubject,verb,reifiedTripleObject,?(reifier),'>>']. +% SPARQL 1.2 [117] +reifiedTripleSubject ==> [var]. +reifiedTripleSubject ==> [iriRef]. +reifiedTripleSubject ==> [rdfLiteral]. +reifiedTripleSubject ==> [numericLiteral]. +reifiedTripleSubject ==> [booleanLiteral]. +reifiedTripleSubject ==> [blankNode]. +reifiedTripleSubject ==> [reifiedTriple]. +reifiedTripleSubject ==> [tripleTerm]. +% SPARQL 1.2 [118] +reifiedTripleObject ==> [var]. +reifiedTripleObject ==> [iriRef]. +reifiedTripleObject ==> [rdfLiteral]. +reifiedTripleObject ==> [numericLiteral]. +reifiedTripleObject ==> [booleanLiteral]. +reifiedTripleObject ==> [blankNode]. +reifiedTripleObject ==> [reifiedTriple]. +reifiedTripleObject ==> [tripleTerm]. +% SPARQL 1.2 [119] +tripleTerm ==> + ['<<(',tripleTermSubject,verb,tripleTermObject,')>>']. +% SPARQL 1.2 [120] +tripleTermSubject ==> [var]. +tripleTermSubject ==> [iriRef]. +tripleTermSubject ==> [rdfLiteral]. +tripleTermSubject ==> [numericLiteral]. +tripleTermSubject ==> [booleanLiteral]. +tripleTermSubject ==> [blankNode]. +tripleTermSubject ==> [tripleTerm]. +% SPARQL 1.2 [121] +tripleTermObject ==> [var]. +tripleTermObject ==> [iriRef]. +tripleTermObject ==> [rdfLiteral]. +tripleTermObject ==> [numericLiteral]. +tripleTermObject ==> [booleanLiteral]. +tripleTermObject ==> [blankNode]. +tripleTermObject ==> [tripleTerm]. +% SPARQL 1.2 [122] +tripleTermData ==> + ['<<(',tripleTermDataSubject,iriRef or 'a',tripleTermDataObject,')>>']. +% SPARQL 1.2 [123] +tripleTermDataSubject ==> [iriRef]. +% SPARQL 1.2 [124] +tripleTermDataObject ==> [iriRef]. +tripleTermDataObject ==> [rdfLiteral]. +tripleTermDataObject ==> [numericLiteral]. +tripleTermDataObject ==> [booleanLiteral]. +tripleTermDataObject ==> [tripleTermData]. %[107] varOrIRIref ==> [var]. varOrIRIref ==> [iriRef]. @@ -504,7 +602,7 @@ multiplicativeExpression ==> [unaryExpression,*(['*',unaryExpression] or ['/',unaryExpression])]. %[118] -unaryExpression ==> ['!',primaryExpression]. +unaryExpression ==> ['!',unaryExpression]. unaryExpression ==> ['+',primaryExpression]. unaryExpression ==> ['-',primaryExpression]. unaryExpression ==> [primaryExpression]. @@ -516,13 +614,29 @@ primaryExpression ==> [numericLiteral]. primaryExpression ==> [booleanLiteral]. primaryExpression ==> [var]. -primaryExpression ==> [aggregate]. +primaryExpression ==> [exprTripleTerm]. +% SPARQL 1.2 [137] +exprTripleTerm ==> + ['<<(',exprTripleTermSubject,verb,exprTripleTermObject,')>>']. +% SPARQL 1.2 [138] +exprTripleTermSubject ==> [iriRef]. +exprTripleTermSubject ==> [var]. +% SPARQL 1.2 [139] +exprTripleTermObject ==> [iriRef]. +exprTripleTermObject ==> [rdfLiteral]. +exprTripleTermObject ==> [numericLiteral]. +exprTripleTermObject ==> [booleanLiteral]. +exprTripleTermObject ==> [var]. +exprTripleTermObject ==> [exprTripleTerm]. %[120] brackettedExpression ==> ['(',expression,')']. %[121] +% In SPARQL 1.2, aggregates are built-in calls (rather than primary expressions) +builtInCall ==> [aggregate]. builtInCall ==> ['STR','(',expression,')']. builtInCall ==> ['LANG','(',expression,')']. builtInCall ==> ['LANGMATCHES','(',expression,',',expression,')']. +builtInCall ==> ['LANGDIR','(',expression,')']. builtInCall ==> ['DATATYPE','(',expression,')']. builtInCall ==> ['BOUND','(',var,')']. builtInCall ==> ['IRI','(',expression,')']. @@ -567,6 +681,7 @@ builtInCall ==> ['COALESCE',expressionList]. builtInCall ==> ['IF','(',expression,',',expression,',',expression,')']. builtInCall ==> ['STRLANG','(',expression,',',expression,')']. +builtInCall ==> ['STRLANGDIR','(',expression,',',expression,',',expression,')']. builtInCall ==> ['STRDT','(',expression,',',expression,')']. builtInCall ==> ['SAMETERM','(',expression,',',expression,')']. builtInCall ==> ['ISIRI','(',expression,')']. @@ -574,9 +689,16 @@ builtInCall ==> ['ISBLANK','(',expression,')']. builtInCall ==> ['ISLITERAL','(',expression,')']. builtInCall ==> ['ISNUMERIC','(',expression,')']. +builtInCall ==> ['HASLANG','(',expression,')']. +builtInCall ==> ['HASLANGDIR','(',expression,')']. builtInCall ==> [regexExpression]. builtInCall ==> [existsFunc]. builtInCall ==> [notExistsFunc]. +builtInCall ==> ['ISTRIPLE','(',expression,')']. +builtInCall ==> ['TRIPLE','(',expression,',',expression,',',expression,')']. +builtInCall ==> ['SUBJECT','(',expression,')']. +builtInCall ==> ['PREDICATE','(',expression,')']. +builtInCall ==> ['OBJECT','(',expression,')']. %[122] regexExpression ==> ['REGEX','(',expression,',',expression,?([',',expression]),')']. @@ -608,7 +730,7 @@ %[128] iriRefOrFunction ==> [iriRef,?(argList)]. %[129] -rdfLiteral ==> [string,?('LANGTAG' or ['^^',iriRef])]. +rdfLiteral ==> [string,?('LANG_DIR' or ['^^',iriRef])]. %[130] numericLiteral ==> [numericLiteralUnsigned]. numericLiteral ==> [numericLiteralPositive]. @@ -651,7 +773,7 @@ 'VAR1', 'VAR2', -'LANGTAG', +'LANG_DIR', 'DOUBLE', 'DECIMAL', @@ -680,9 +802,13 @@ 'GROUP_CONCAT', % Must appear before GROUP 'DATATYPE', % Must appear before DATA +'LANGDIR', % Must appear before LANG +'STRLANGDIR', % Must appear before STRLANG +'HASLANGDIR', % Must appear before HASLANG 'BASE', 'PREFIX', +'VERSION', 'SELECT', 'CONSTRUCT', 'DESCRIBE', @@ -792,6 +918,12 @@ 'AVG', 'SAMPLE', 'SEPARATOR', +'HASLANG', +'ISTRIPLE', +'TRIPLE', +'SUBJECT', +'PREDICATE', +'OBJECT', 'STR' ]). @@ -803,13 +935,18 @@ % e.g. ANON, [ % e.g. DOUBLE, DECIMAL, INTEGER % e.g. INTEGER_POSITIVE, PLUS +% e.g. <<(, <<, <=, < and )>>, ) and >>, >=, > +% e.g. {|, { and |}, ||, | tm_punct([ '*'= '\\*', 'a'= 'a', '.'= '\\.', +'{|'= '\\{\\|', +'|}'= '\\|\\}', '{'= '\\{', '}'= '\\}', ','= ',', +')>>'= '\\)>>', '('= '\\(', ')'= '\\)', ';'= ';', @@ -820,6 +957,9 @@ '='= '=', '!='= '!=', '!'= '!', +'<<('= '<<\\(', +'<<'= '<<', +'>>'= '>>', '<='= '<=', '>='= '>=', '<'= '<', @@ -830,5 +970,6 @@ '^^'= '\\^\\^', '?' = '\\?', '|' = '\\|', -'^'= '\\^' +'^'= '\\^', +'~'= '~' ]). diff --git a/packages/yasqe/grammar/tokenizer.ts b/packages/yasqe/grammar/tokenizer.ts index 71861b39..34d850a5 100644 --- a/packages/yasqe/grammar/tokenizer.ts +++ b/packages/yasqe/grammar/tokenizer.ts @@ -43,6 +43,13 @@ export interface State { pendingToken: Token | undefined; bracketStack: Array<{ type: string; level: number }>; bracketLevel: number; + /** + * One entry per predicate (verb) of a triple pattern in a WHERE clause whose object list is still + * being parsed. `complex` is set when the predicate is not a simple path (IRI, 'a' or a variable), + * in which case SPARQL 1.2 disallows reifiers and annotations. `depth` is the parser stack depth + * below the object list, or undefined while the predicate itself is being parsed. + */ + verbPaths: Array<{ complex: boolean; depth: number | undefined }>; } export interface Token { quotePos: "end" | "start" | "content" | undefined; @@ -96,6 +103,7 @@ export function copyState(s: State): State { whereVariables: { ...s.whereVariables }, pendingToken: s.pendingToken ? { ...s.pendingToken } : undefined, bracketStack: s.bracketStack.map((b) => ({ ...b })), + verbPaths: s.verbPaths.map((v) => ({ ...v })), }; } @@ -123,7 +131,8 @@ export default function (config: TokenizerConfig = {}): StreamParser { const BLANK_NODE_LABEL = "_:(" + PN_CHARS_U + "|[0-9])((" + PN_CHARS + "|\\.)*" + PN_CHARS + ")?"; const PNAME_NS = "(" + PN_PREFIX + ")?:"; const PNAME_LN = PNAME_NS + PN_LOCAL; - const LANGTAG = "@[a-zA-Z]+(-[a-zA-Z0-9]+)*"; + // Language tag with optional (SPARQL 1.2) base direction, e.g. @en-US or @ar--rtl + const LANG_DIR = "@[a-zA-Z]+(-[a-zA-Z0-9]+)*(--[a-zA-Z]+)?"; const EXPONENT = "[eE][\\+-]?[0-9]+"; const INTEGER = "[0-9]+"; @@ -250,8 +259,8 @@ export default function (config: TokenizerConfig = {}): StreamParser { }, { - name: "LANGTAG", - regex: new RegExp("^" + LANGTAG), + name: "LANG_DIR", + regex: new RegExp("^" + LANG_DIR), style: "meta", }, @@ -611,10 +620,48 @@ export default function (config: TokenizerConfig = {}): StreamParser { (state.allowBnodes || (topSymbol != "blankNode" && topSymbol != "blankNodePropertyList" && - topSymbol != "blankNodePropertyListPath")) + topSymbol != "blankNodePropertyListPath")) && + !violatesSimplePathCondition(topSymbol) ); } + function currentVerbPath() { + return state.verbPaths[state.verbPaths.length - 1]; + } + + // Call after popping a symbol from the parser stack: once the symbol directly below an object + // list has been popped, that object list (and its predicate) has been fully parsed + function pruneVerbPaths() { + let top = currentVerbPath(); + while (top && top.depth !== undefined && state.stack.length < top.depth) { + state.verbPaths.pop(); + top = currentVerbPath(); + } + } + + // SPARQL 1.2: a reifier or annotation is only allowed after a triple whose predicate is a simple path + function violatesSimplePathCondition(topSymbol: string) { + if (topSymbol !== "pathReifier" && topSymbol !== "annotationBlockPath") return false; + const verbPath = currentVerbPath(); + if (verbPath && verbPath.complex) { + state.errorMsg = + "Reifiers and annotations are only allowed after a triple whose predicate is an IRI, 'a' or a variable, not a property path"; + return true; + } + return false; + } + + // Keep track of predicates in property lists that allow property paths (see State.verbPaths) + function trackVerbPath(topSymbol: string, rhsLength: number) { + if (topSymbol === "verbPath" || topSymbol === "verbSimple") { + state.verbPaths.push({ complex: false, depth: undefined }); + } else if (topSymbol === "objectListPath") { + const verbPath = currentVerbPath(); + if (verbPath && verbPath.depth === undefined) verbPath.depth = state.stack.length - rhsLength; + } + } + const complexPathTokens = ["/", "|", "^", "!", "(", "*", "?", "+", "{"]; + // CodeMirror works with one line at a time, // but newline should behave like whitespace // - i.e. a definite break between tokens (for autocompleter) @@ -659,6 +706,7 @@ export default function (config: TokenizerConfig = {}): StreamParser { // Incremental LL1 parse while (state.stack.length > 0 && tokenCat && state.OK && !finished) { topSymbol = state.stack.pop(); + pruneVerbPaths(); if (topSymbol === "var" && tokenOb.string) { state.variables[tokenOb.string] = tokenOb.string; // Track variables separately for CONSTRUCT template validation @@ -687,10 +735,15 @@ export default function (config: TokenizerConfig = {}): StreamParser { // - consume token from input stream finished = true; setQueryType(topSymbol); + const verbPath = currentVerbPath(); + if (verbPath && verbPath.depth === undefined && complexPathTokens.indexOf(tokenCat) >= 0) { + verbPath.complex = true; + } // Check whether $ (end of input token) is poss next // for everything on stack var allNillable = true; for (var sp = state.stack.length; sp > 0; --sp) { + if (state.stack[sp - 1] === "$") continue; // the end-of-input marker itself const item = ll1_table[state.stack[sp - 1]]; if (!item || !item["$"]) allNillable = false; } @@ -738,6 +791,7 @@ export default function (config: TokenizerConfig = {}): StreamParser { } // Peform any non-grammatical side-effects setSideConditions(topSymbol); + trackVerbPath(topSymbol, nextSymbols.length); } else { // No match in table - fail state.OK = false; @@ -805,12 +859,20 @@ export default function (config: TokenizerConfig = {}): StreamParser { "}": 1, "]": 1, ")": 1, + "|}": 1, + ">>": 1, + ")>>": 1, "{": -1, "(": -1, "[": -1, + "{|": -1, + "<<": -1, + "<<(": -1, // "*[;,?[or([verbPath,verbSimple]),objectList]]": 1, }; + const closingBracketRegex = /^(\|\}|\)>>|>>|[\}\]\)])/; + function indent(state: State, textAfter: string, indentUnit: number) { //just avoid we don't indent multi-line literals if (state.inLiteral) return 0; @@ -824,9 +886,10 @@ export default function (config: TokenizerConfig = {}): StreamParser { } else { var n = 0; // indent level var i = state.stack.length - 1; - if (/^[\}\]\)]/.test(textAfter)) { + const closeMatch = closingBracketRegex.exec(textAfter); + if (closeMatch) { // Skip stack items until after matching bracket - const closeBracket = textAfter.substr(0, 1); + const closeBracket = closeMatch[1]; for (; i >= 0; --i) { if (state.stack[i] == closeBracket) { --i; @@ -859,7 +922,7 @@ export default function (config: TokenizerConfig = {}): StreamParser { indent(state, textAfter, context.unit ?? config.indentUnit ?? 2), languageData: { commentTokens: { line: "#" }, - indentOnInput: /^\s*[\}\]\)]$/, + indentOnInput: /^\s*(\|\}|\)>>|>>|[\}\]\)])$/, closeBrackets: { brackets: ["(", "[", "{", "'", '"'] }, }, startState: function (): State { @@ -893,6 +956,7 @@ export default function (config: TokenizerConfig = {}): StreamParser { pendingToken: undefined, bracketStack: [], bracketLevel: 0, + verbPaths: [], }; }, }; diff --git a/test/fix-esm-imports.mjs b/test/fix-esm-imports.mjs index 55de5c8d..6f35257c 100644 --- a/test/fix-esm-imports.mjs +++ b/test/fix-esm-imports.mjs @@ -41,7 +41,16 @@ function rewriteSource(source, filePath) { ); } +// The SPARQL tokenizer imports its generated LL(1) table, a plain JS file that tsc does not emit +async function copyGrammarTable() { + const grammarDir = path.join("packages", "yasqe", "grammar"); + const destDir = path.join(repoRoot, "build", "test", grammarDir); + if (!(await exists(destDir))) return; + await fs.copyFile(path.join(repoRoot, grammarDir, "_tokenizer-table.js"), path.join(destDir, "_tokenizer-table.js")); +} + async function main() { + await copyGrammarTable(); if (!(await exists(targetDir))) return; const files = await walk(targetDir); diff --git a/test/run.ts b/test/run.ts index 1621b83d..2656fd1a 100644 --- a/test/run.ts +++ b/test/run.ts @@ -98,6 +98,67 @@ select * { }); }); + describe("SPARQL 1.2", function () { + const sparql12Query = `VERSION "1.2" +PREFIX : +PREFIX rdf: +SELECT * WHERE { + ?person :name "Alice"@en--ltr ~ ?r {| :statedBy ?authority |} . + << ?person :jobTitle "Designer" >> :accordingTo ?source . + ?r rdf:reifies <<( ?person :name "Alice"@en--ltr )>> . + FILTER(isTRIPLE(TRIPLE(?person, :name, "Alice")) && hasLANGDIR(?x)) +}`; + + function checkSyntax(query: string) { + return page.evaluate((query) => { + window.yasqe.setValue(query); + window.yasqe.checkSyntax(); + return { + valid: window.yasqe.queryValid, + errorIcons: document.querySelectorAll(".parseErrorIcon").length, + }; + }, query); + } + + it("Should mark a valid SPARQL 1.2 query as valid", async function () { + const result = await checkSyntax(sparql12Query); + expect(result.valid).to.be.true; + expect(result.errorIcons).to.equal(0); + }); + + it("Should mark an annotation after a property path as invalid", async function () { + const result = await checkSyntax(`PREFIX : +SELECT * WHERE { + ?s :p/:q ?o {| :source ?g |} . +}`); + expect(result.valid).to.be.false; + expect(result.errorIcons).to.equal(1); + }); + + for (const formatterType of ["sparql-formatter", "legacy"] as const) { + it(`Should keep a SPARQL 1.2 query valid when formatting with ${formatterType}`, async function () { + const result = await page.evaluate( + (query, formatterType) => { + if (!window.yasqe.persistentConfig) { + window.yasqe.persistentConfig = { query: "", editorHeight: "300px" }; + } + window.yasqe.persistentConfig.formatterType = formatterType; + window.yasqe.setValue(query); + window.yasqe.format(); + window.yasqe.checkSyntax(); + return { valid: window.yasqe.queryValid, value: window.yasqe.getValue() }; + }, + sparql12Query, + formatterType, + ); + expect(result.valid, result.value).to.be.true; + for (const token of ["<<(", ")>>", "<<", ">>", "{|", "|}", "~", "@en--ltr", "VERSION"]) { + expect(result.value).to.contain(token); + } + }); + } + }); + describe("SPARQL Formatter", function () { it("Should format with sparql-formatter", async function () { const value = await page.evaluate(() => { diff --git a/test/unit/yasqe-sparql12-grammar-test.ts b/test/unit/yasqe-sparql12-grammar-test.ts new file mode 100644 index 00000000..cd318184 --- /dev/null +++ b/test/unit/yasqe-sparql12-grammar-test.ts @@ -0,0 +1,583 @@ +import * as chai from "chai"; +import { describe, it } from "mocha"; +import { StringStream } from "@codemirror/language"; + +import sparqlTokenizer, { State } from "../../packages/yasqe/grammar/tokenizer.js"; + +const expect = chai.expect; + +interface ParseResult { + /** No syntax error was detected (this is what Yasqe shows as valid/invalid) */ + ok: boolean; + /** The query may end here */ + complete: boolean; + /** 1-based line of the first syntax error */ + errorLine?: number; + errorMsg?: string; + state: State; +} + +/** Runs the Yasqe tokenizer over a query, line by line, the same way the editor does */ +function parse(query: string): ParseResult { + const parser = sparqlTokenizer(); + const state = parser.startState!(2); + const lines = query.split(/\r?\n/); + for (let i = 0; i < lines.length; i++) { + const stream = new StringStream(lines[i], 4, 2); + while (!stream.eol()) { + stream.start = stream.pos; + parser.token(stream, state); + if (stream.pos <= stream.start) stream.pos = stream.start + 1; + } + if (!state.OK) return { ok: false, complete: false, errorLine: i + 1, errorMsg: state.errorMsg, state }; + } + return { ok: state.OK, complete: state.complete, state }; +} + +/** Returns the [text, style] pairs of all non-whitespace tokens */ +function tokenStyles(query: string): Array<[string, string]> { + const parser = sparqlTokenizer(); + const state = parser.startState!(2); + const result: Array<[string, string]> = []; + for (const line of query.split(/\r?\n/)) { + const stream = new StringStream(line, 4, 2); + while (!stream.eol()) { + stream.start = stream.pos; + const style = parser.token(stream, state) || ""; + if (style !== "ws") result.push([line.slice(stream.start, stream.pos), style]); + } + } + return result; +} + +const PREFIXES = `PREFIX : +PREFIX rdf: +PREFIX xsd: +`; + +function expectValid(query: string) { + const result = parse(query); + expect( + result.ok, + `Unexpected syntax error on line ${result.errorLine} (${result.errorMsg || "no message"}) in:\n${query}`, + ).to.be.true; + expect(result.complete, `Query is valid so far, but incomplete:\n${query}`).to.be.true; +} + +function expectInvalid(query: string, errorLine?: number) { + const result = parse(query); + expect(result.ok, `Expected a syntax error in:\n${query}`).to.be.false; + if (errorLine !== undefined) expect(result.errorLine, "line of the syntax error").to.equal(errorLine); + return result; +} + +interface TestCase { + name: string; + query: string; + errorLine?: number; +} + +const validQueries: TestCase[] = [ + { + name: "VERSION declaration (double quotes)", + query: `VERSION "1.2" +${PREFIXES}SELECT * WHERE { ?s ?p ?o }`, + }, + { + name: "VERSION declaration (single quotes) mixed with BASE and PREFIX", + query: `BASE +PREFIX : +VERSION '1.2' +ASK { ?s :p ?o }`, + }, + { + name: "VERSION declaration in an update", + query: `VERSION "1.2" +${PREFIXES}INSERT DATA { :s :p :o }`, + }, + { + name: "triple term as object", + query: `${PREFIXES}SELECT ?person ?authority { + ?person :familyName "Smith" . + _:anno rdf:reifies <<( ?person :jobTitle "Designer" )>> . + _:anno :accordingTo ?authority . +}`, + }, + { + name: "nested triple terms with all kinds of terms", + query: `${PREFIXES}SELECT * WHERE { + ?r :p <<( :a :b <<( _:c ?d "e"@en )>> )>> . + ?r :q <<( [] a 1.5e3 )>> , <<( "s" :p true )>> , <<( ?s ?p -2 )>> . +}`, + }, + { + name: "triple term in subject position", + query: `${PREFIXES}SELECT * WHERE { <<( :a :b :c )>> :p ?o }`, + }, + { + name: "reified triple as subject", + query: `${PREFIXES}SELECT ?person ?authority { + ?person :familyName "Smith" . + << ?person :jobTitle "Designer" >> :accordingTo ?authority . +}`, + }, + { + name: "reified triple with reifiers of every kind", + query: `${PREFIXES}SELECT * WHERE { + << :a :b :c ~ ?r >> :p ?o1 . + << :a :b :c ~ :r >> :p ?o2 . + << :a :b :c ~ _:r >> :p ?o3 . + << :a :b :c ~ [] >> :p ?o4 . + << :a :b :c ~ >> :p ?o5 . +}`, + }, + { + name: "reified triple as object, and on its own", + query: `${PREFIXES}SELECT * WHERE { + ?who :says << :a :b :c >> . + << :x :y :z ~ :stmt >> . +}`, + }, + { + name: "nested reified triples, triple terms and literal subjects", + query: `${PREFIXES}SELECT * WHERE { + << << :a :b :c >> :d <<( :e :f :g )>> >> :h ?i . + << "x" a :C >> :p 1 . +}`, + }, + { + name: "reified triple followed by a property path", + query: `${PREFIXES}SELECT * WHERE { << :a :b :c >> :p/:q* ?o }`, + }, + { + name: "reified triple spread over multiple lines", + query: `${PREFIXES}SELECT * WHERE { + << + ?s :p ?o + ~ ?r + >> :q ?z . +}`, + }, + { + name: "annotation block", + query: `${PREFIXES}SELECT ?person ?authority { + ?person :name "Alice" {| :statedBy ?authority ; :recorded "2021-07-07"^^xsd:date |} . +}`, + }, + { + name: "reifier and annotation block", + query: `${PREFIXES}SELECT * WHERE { :a :b :c ~ :r {| :p :o |} . }`, + }, + { + name: "multiple reifiers and annotations in object and predicate lists", + query: `${PREFIXES}SELECT * WHERE { + :a :b :c ~ :r1 {| :p 1 |} ~ :r2 {| :q 2 |} , :d {| :s 3 |} ; + :e :f ~ ?r3 . +}`, + }, + { + name: "annotations after a variable predicate and after 'a'", + query: `${PREFIXES}SELECT * WHERE { + ?s ?p ?o {| :source ?g |} . + ?s a :C ~ ?r . +}`, + }, + { + name: "property paths inside an annotation block", + query: `${PREFIXES}SELECT * WHERE { ?s :p ?o {| :source/:name ?n ; ^:q ?z |} }`, + }, + { + name: "annotation after a simple predicate that follows a property path", + query: `${PREFIXES}SELECT * WHERE { + ?s :p/:q ?x ; :r ?y {| :source ?z |} . + ?s :p* ?x , ?w . ?x :r ?y ~ ?r . +}`, + }, + { + name: "annotation in a blank node that is the object of a property path", + query: `${PREFIXES}SELECT * WHERE { ?s :p/:q [ :r ?y {| :source ?z |} ] . }`, + }, + { + name: "reifier inside a reified triple that is the object of a property path", + query: `${PREFIXES}SELECT * WHERE { ?s :p/:q << :a :b :c ~ :r >> . }`, + }, + { + name: "annotations and reified triples in a CONSTRUCT template", + query: `${PREFIXES}CONSTRUCT { + ?s :p ?o {| :source ?g |} . + << ?s :p ?o >> :certainty 0.9 . +} +WHERE { GRAPH ?g { ?s :p ?o } }`, + }, + { + name: "annotation in the short CONSTRUCT WHERE form", + query: `${PREFIXES}CONSTRUCT WHERE { ?s :p ?o {| :q ?z |} }`, + }, + { + name: "triple term functions", + query: `${PREFIXES}SELECT * WHERE { + ?s :p ?o . + BIND(TRIPLE(?s, :p, ?o) AS ?t) + FILTER(isTRIPLE(?t) && SUBJECT(?t) = ?s && PREDICATE(?t) = :p && sameTerm(OBJECT(?t), ?o)) +}`, + }, + { + name: "triple terms in expressions", + query: `${PREFIXES}SELECT * WHERE { + ?s :p ?o . + BIND(<<( ?s :p "x" )>> AS ?t) + FILTER(?t != <<( :a a <<( ?s :d 1 )>> )>>) +}`, + }, + { + name: "triple terms in VALUES", + query: `${PREFIXES}SELECT * WHERE { + VALUES ?t { <<( :a :b :c )>> <<( :a a :C )>> <<( :a :b <<( :c :d "x"@en--ltr )>> )>> UNDEF } + VALUES (?x ?y) { (:a <<( :a :b 1 )>>) (UNDEF <<( :a :b true )>>) } +} +VALUES ?z { <<( :a :b "c"^^xsd:string )>> }`, + }, + { + name: "language tags with base direction and the related functions", + query: `${PREFIXES}SELECT * WHERE { + ?s :label ?l . + FILTER(?l = "hello"@en--ltr || ?l = "مرحبا"@ar--rtl || ?l = "hi"@en-US) + FILTER(LANGDIR(?l) = "ltr" && hasLANG(?l) && hasLANGDIR(?l)) + BIND(STRLANGDIR("abc", "en", "ltr") AS ?d) +}`, + }, + { + name: "language tag with base direction in a triple", + query: `${PREFIXES}SELECT * WHERE { ?s :label "hello"@en-GB--ltr . }`, + }, + { + name: "'!' applied to a unary expression", + query: `${PREFIXES}SELECT * WHERE { ?s ?p ?o FILTER(!!BOUND(?o)) FILTER(!-?o) }`, + }, + { + name: "aggregates are built-in calls (e.g. directly in ORDER BY)", + query: `${PREFIXES}SELECT ?s (COUNT(?o) AS ?c) WHERE { ?s :p ?o } +GROUP BY ?s +ORDER BY DESC(COUNT(?o)) COUNT(?o)`, + }, + { + name: "INSERT DATA with triple terms, reifiers and annotations", + query: `${PREFIXES}INSERT DATA { + :alice :name "Alice" ~ :r1 {| :source :hr |} . + :r2 rdf:reifies <<( :bob :age 42 )>> . + << :carol :age 30 ~ :r3 >> :source :census . + GRAPH :g { :dave :name "Dave"@en--ltr {| :source :hr |} } +}`, + }, + { + name: "DELETE DATA with triple terms and reifier IRIs", + query: `${PREFIXES}DELETE DATA { :r2 rdf:reifies <<( :bob :age 42 )>> . :a :b :c ~ :r {| :p :o |} }`, + }, + { + name: "DELETE/INSERT WHERE with reified triples", + query: `${PREFIXES}DELETE { << ?s :age ?old ~ ?r >> :source ?src } +INSERT { << ?s :age ?new ~ ?r >> :source ?src } +WHERE { + << ?s :age ?old ~ ?r >> :source ?src . + BIND(?old + 1 AS ?new) +}`, + }, + { + name: "DELETE WHERE with a reifier and annotation", + query: `${PREFIXES}DELETE WHERE { ?s :p ?o ~ ?r {| :q ?z |} }`, + }, + { + name: "SPARQL 1.2 syntax inside GRAPH, OPTIONAL, SERVICE and subqueries", + query: `${PREFIXES}SELECT * WHERE { + GRAPH ?g { ?s :p ?o {| :source ?src |} } + OPTIONAL { << ?s :p ?o >> :certainty ?c } + SERVICE { ?x rdf:reifies <<( ?s :p ?o )>> } + { SELECT ?t WHERE { ?r rdf:reifies ?t FILTER(isTRIPLE(?t)) } } +}`, + }, + { + name: "blank node property list followed by a property path (was rejected before)", + query: `${PREFIXES}SELECT * WHERE { [ :p ?o ] :a/:b ?c }`, + }, +]; + +// SPARQL 1.1 queries (and tokens that are close to the new SPARQL 1.2 tokens) must keep working +const sparql11Queries: TestCase[] = [ + { + name: "basic query with FILTER, OPTIONAL, ORDER BY and LIMIT", + query: `${PREFIXES}SELECT DISTINCT ?s (STR(?o) AS ?str) WHERE { + ?s a :C ; :p ?o . + OPTIONAL { ?s :q ?q } + FILTER(?o > 3 && ?o < 10 || !BOUND(?q)) +} ORDER BY DESC(?o) LIMIT 10 OFFSET 5`, + }, + { + name: "comparisons and brackets without whitespace", + query: `${PREFIXES}SELECT * WHERE {?s ?p ?o FILTER(?o>3) FILTER((?o)>(?s)) FILTER(?o>=1||?o<=0) FILTER(?o 1)`, + }, + { + name: "language tags", + query: `${PREFIXES}SELECT * WHERE { ?s :label "chat"@fr, "cat"@en-GB . FILTER(LANG(?l) = "en" && LANGMATCHES(LANG(?l), "EN")) }`, + }, + { + name: "update request", + query: `${PREFIXES}DELETE { ?s :p ?o } INSERT { ?s :q ?o } WHERE { ?s :p ?o } ; +INSERT DATA { :a :b [ :c "d" ] } ; +CLEAR SILENT GRAPH :g`, + }, +]; + +const invalidQueries: TestCase[] = [ + { + name: "VERSION with an IRI", + query: `VERSION +SELECT * WHERE { ?s ?p ?o }`, + errorLine: 1, + }, + { + name: "VERSION after the query", + query: `SELECT * WHERE { ?s ?p ?o } +VERSION "1.2"`, + errorLine: 2, + }, + { + name: "triple term closed with >>", + query: `${PREFIXES}SELECT * WHERE { ?r :p <<( :a :b :c >> . }`, + errorLine: 4, + }, + { + name: "reified triple closed with )>>", + query: `${PREFIXES}SELECT * WHERE { ?r :p << :a :b :c )>> . }`, + errorLine: 4, + }, + { + name: "property path in a reified triple", + query: `${PREFIXES}SELECT * WHERE { << :a :b/:c :d >> :p ?o }`, + errorLine: 4, + }, + { + name: "property path in a triple term", + query: `${PREFIXES}SELECT * WHERE { ?r :p <<( :a :b* :c )>> }`, + errorLine: 4, + }, + { + name: "collection in a reified triple", + query: `${PREFIXES}SELECT * WHERE { << (1 2) :p :o >> :q ?z }`, + errorLine: 4, + }, + { + name: "blank node property list in a triple term", + query: `${PREFIXES}SELECT * WHERE { ?r :p <<( [ :q 1 ] :p :o )>> }`, + errorLine: 4, + }, + { + name: "reifier in a triple term", + query: `${PREFIXES}SELECT * WHERE { ?r :p <<( :a :b :c ~ :r )>> }`, + errorLine: 4, + }, + { + name: "reified triple in a triple term", + query: `${PREFIXES}SELECT * WHERE { ?r :p <<( << :a :b :c >> :p :o )>> }`, + errorLine: 4, + }, + { + name: "collection in a triple term", + query: `${PREFIXES}SELECT * WHERE { ?r :p <<( ?s ?p ( ) )>> }`, + errorLine: 4, + }, + { + name: "reified triple as predicate", + query: `${PREFIXES}SELECT * WHERE { :x << :a :b :c >> :y }`, + errorLine: 4, + }, + { + name: "literal as reifier", + query: `${PREFIXES}SELECT * WHERE { :a :b :c ~ "r" . }`, + errorLine: 4, + }, + { + name: "empty annotation block", + query: `${PREFIXES}SELECT * WHERE { :a :b :c {| |} . }`, + errorLine: 4, + }, + { + name: "unclosed annotation block", + query: `${PREFIXES}SELECT * WHERE { :a :b :c {| :p :o } }`, + errorLine: 4, + }, + { + name: "reifier in a collection", + query: `${PREFIXES}SELECT * WHERE { ?s :p ( :a ~ :r ) }`, + errorLine: 4, + }, + { + name: "annotation after a property path", + query: `${PREFIXES}SELECT * WHERE { + ?s :p/:q ?o {| :source ?g |} . +}`, + errorLine: 5, + }, + { + name: "reifier after a property path with a modifier", + query: `${PREFIXES}SELECT * WHERE { ?s :p* ?o ~ ?r }`, + errorLine: 4, + }, + { + name: "reifier after an inverse path", + query: `${PREFIXES}SELECT * WHERE { ?s ^:p ?o ~ ?r }`, + errorLine: 4, + }, + { + name: "reifier after a parenthesized path", + query: `${PREFIXES}SELECT * WHERE { ?s (:p) ?o ~ ?r }`, + errorLine: 4, + }, + { + name: "annotation after a later object of a property path", + query: `${PREFIXES}SELECT * WHERE { ?s :p|:q ?a , ?b {| :source ?g |} }`, + errorLine: 4, + }, + { + name: "annotation after a property path, after an earlier annotated object", + query: `${PREFIXES}SELECT * WHERE { ?s :p ?o {| :src ?g |} ; :p/:q ?x ~ ?r }`, + errorLine: 4, + }, + { + name: "reifier after a property path inside an annotation block", + query: `${PREFIXES}SELECT * WHERE { ?s :p ?o {| :src/:x ?g ~ ?r |} }`, + errorLine: 4, + }, + { + name: "variable in a triple term in VALUES", + query: `${PREFIXES}SELECT * WHERE { VALUES ?t { <<( ?s :p :o )>> } }`, + errorLine: 4, + }, + { + name: "blank node in a triple term in VALUES", + query: `${PREFIXES}SELECT * WHERE { VALUES ?t { <<( _:b :p :o )>> } }`, + errorLine: 4, + }, + { + name: "literal subject in a triple term in VALUES", + query: `${PREFIXES}SELECT * WHERE { VALUES ?t { <<( "s" :p :o )>> } }`, + errorLine: 4, + }, + { + name: "reified triple in VALUES", + query: `${PREFIXES}SELECT * WHERE { VALUES ?t { << :s :p :o >> } }`, + errorLine: 4, + }, + { + name: "blank node in a triple term expression", + query: `${PREFIXES}SELECT * WHERE { BIND(<<( _:b :p :o )>> AS ?t) }`, + errorLine: 4, + }, + { + name: "literal subject in a triple term expression", + query: `${PREFIXES}SELECT * WHERE { BIND(<<( "s" :p :o )>> AS ?t) }`, + errorLine: 4, + }, + { + name: "variable as reifier in INSERT DATA", + query: `${PREFIXES}INSERT DATA { :a :b :c ~ ?r }`, + errorLine: 4, + }, + { + name: "variable in a reified triple in INSERT DATA", + query: `${PREFIXES}INSERT DATA { << :a :b ?c >> :p :o }`, + errorLine: 4, + }, + { + name: "blank node reifier in DELETE DATA", + query: `${PREFIXES}DELETE DATA { :a :b :c ~ _:r }`, + errorLine: 4, + }, + { + name: "blank node in a reified triple in DELETE WHERE", + query: `${PREFIXES}DELETE WHERE { << _:a :b :c >> :p ?o }`, + errorLine: 4, + }, + { + name: "blank node in a triple term in a DELETE clause", + query: `${PREFIXES}DELETE { ?r rdf:reifies <<( [] :b :c )>> } WHERE { ?r :p ?o }`, + errorLine: 4, + }, + { + name: "TRIPLE with two arguments", + query: `${PREFIXES}SELECT * WHERE { BIND(TRIPLE(?s, ?p) AS ?t) }`, + errorLine: 4, + }, + { + name: "LANGDIR with two arguments", + query: `${PREFIXES}SELECT * WHERE { BIND(LANGDIR(?s, ?p) AS ?t) }`, + errorLine: 4, + }, + { + name: "language tag with an empty base direction", + query: `${PREFIXES}SELECT * WHERE { ?s :label "hello"@en-- . }`, + errorLine: 4, + }, +]; + +describe("Yasqe SPARQL 1.2 grammar", () => { + describe("accepts valid SPARQL 1.2 queries", () => { + validQueries.forEach(({ name, query }) => it(name, () => expectValid(query))); + }); + + describe("keeps accepting SPARQL 1.1 queries", () => { + sparql11Queries.forEach(({ name, query }) => it(name, () => expectValid(query))); + }); + + describe("rejects invalid SPARQL 1.2 queries", () => { + invalidQueries.forEach(({ name, query, errorLine }) => it(name, () => expectInvalid(query, errorLine))); + }); + + it("explains why an annotation after a property path is invalid", () => { + const result = expectInvalid(`${PREFIXES}SELECT * WHERE { ?s :p/:q ?o ~ ?r }`, 4); + expect(result.errorMsg).to.contain("only allowed after a triple whose predicate is an IRI, 'a' or a variable"); + }); + + it("reports an incomplete reified triple as incomplete, not as an error", () => { + const result = parse(`${PREFIXES}SELECT * WHERE { << :a :b :c`); + expect(result.ok).to.be.true; + expect(result.complete).to.be.false; + }); + + it("suggests reifiers and annotations after an object", () => { + const result = parse(`${PREFIXES}SELECT * WHERE { ?s :p ?o `); + expect(result.state.possibleNext).to.include.members(["~", "{|"]); + }); + + it("tokenizes the new SPARQL 1.2 tokens", () => { + const styles = tokenStyles( + `VERSION "1.2" SELECT * { << :a :b "x"@en--ltr ~ :r >> :p <<( :a :b :c )>> {| :q 1 |} FILTER(hasLANGDIR(?x)) }`, + ); + const styleOf = (text: string) => styles.find(([t]) => t === text)?.[1]; + expect(styleOf("VERSION")).to.equal("keyword"); + expect(styleOf("hasLANGDIR")).to.equal("keyword"); + expect(styleOf("@en--ltr")).to.equal("meta"); + for (const punct of ["<<", ">>", "<<(", ")>>", "{|", "|}", "~"]) { + expect( + styles.map(([t]) => t), + `token ${punct}`, + ).to.include(punct); + } + }); +}); From d0efff1b68fdfe5bd469881c65e45897dd256042 Mon Sep 17 00:00:00 2001 From: Mathias Vanden Auweele Date: Tue, 29 Sep 2026 23:22:41 +0200 Subject: [PATCH 3/4] chore(yasqe): give the grammar files version-neutral names The grammar now covers SPARQL 1.1 and 1.2, so rename sparql11-grammar.pl to sparql-grammar.pl and gen_sparql11.pl to gen_sparql.pl, and update build.sh, the README and a code comment. The "sparql11" mode name is public configuration and stays unchanged. Co-Authored-By: Claude Opus 5.5 --- packages/yasqe/grammar/README.md | 2 +- packages/yasqe/grammar/build.sh | 4 ++-- .../yasqe/grammar/{sparql11-grammar.pl => sparql-grammar.pl} | 0 .../yasqe/grammar/util/{gen_sparql11.pl => gen_sparql.pl} | 2 +- packages/yasqe/src/editor/language.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename packages/yasqe/grammar/{sparql11-grammar.pl => sparql-grammar.pl} (100%) rename packages/yasqe/grammar/util/{gen_sparql11.pl => gen_sparql.pl} (78%) diff --git a/packages/yasqe/grammar/README.md b/packages/yasqe/grammar/README.md index b784c24d..40d80377 100644 --- a/packages/yasqe/grammar/README.md +++ b/packages/yasqe/grammar/README.md @@ -9,7 +9,7 @@ The grammar covers SPARQL 1.1 and SPARQL 1.2 (queries and updates). ## How to make modifications to grammar: - Change the Extended Backus-Naur Form (EBNF) grammar in file - `sparql11-grammar.pl`. Do not change file `_tokenizer-table.js`. + `sparql-grammar.pl`. Do not change file `_tokenizer-table.js`. - Run `./build.sh`. It regenerates `_tokenizer-table.js` and formats it with prettier. Check its output for `LL(1) clash` messages: the grammar must stay LL(1). - Finally, rebuild YASQE from the YASQE home directory by running `npm run build` (or `npm run dev` to test it locally). diff --git a/packages/yasqe/grammar/build.sh b/packages/yasqe/grammar/build.sh index 0e2a09c3..b98b7be6 100755 --- a/packages/yasqe/grammar/build.sh +++ b/packages/yasqe/grammar/build.sh @@ -1,9 +1,9 @@ #!/bin/bash -# Regenerates _tokenizer-table.js from sparql11-grammar.pl (requires SWI-Prolog) +# Regenerates _tokenizer-table.js from sparql-grammar.pl (requires SWI-Prolog) set -e GRAMMAR_DIR="$(cd "$(dirname "$0")" && pwd)" cd "$GRAMMAR_DIR/util" -swipl -s gen_sparql11.pl -t go +swipl -s gen_sparql.pl -t go mv _tokenizer-table.js ../_tokenizer-table.js cd "$GRAMMAR_DIR/../../.." npx prettier --parser babel --write packages/yasqe/grammar/_tokenizer-table.js diff --git a/packages/yasqe/grammar/sparql11-grammar.pl b/packages/yasqe/grammar/sparql-grammar.pl similarity index 100% rename from packages/yasqe/grammar/sparql11-grammar.pl rename to packages/yasqe/grammar/sparql-grammar.pl diff --git a/packages/yasqe/grammar/util/gen_sparql11.pl b/packages/yasqe/grammar/util/gen_sparql.pl similarity index 78% rename from packages/yasqe/grammar/util/gen_sparql11.pl rename to packages/yasqe/grammar/util/gen_sparql.pl index 3b3a1426..a9393f0a 100644 --- a/packages/yasqe/grammar/util/gen_sparql11.pl +++ b/packages/yasqe/grammar/util/gen_sparql.pl @@ -8,4 +8,4 @@ ]). :-reconsult(gen_ll1). -:-reconsult('../sparql11-grammar.pl'). +:-reconsult('../sparql-grammar.pl'). diff --git a/packages/yasqe/src/editor/language.ts b/packages/yasqe/src/editor/language.ts index 5e0a4031..ee706049 100644 --- a/packages/yasqe/src/editor/language.ts +++ b/packages/yasqe/src/editor/language.ts @@ -1,5 +1,5 @@ /** - * CodeMirror 6 language definition for SPARQL 1.1, built on top of the + * CodeMirror 6 language definition for SPARQL (1.1 and 1.2), built on top of the * legacy (stream based) LL(1) tokenizer in ../../grammar/tokenizer.ts. * * Every token style emitted by the tokenizer gets its own lezer Tag and a From 4f8f033847b7a286a5306ddc6e2acfb909a0f5cb Mon Sep 17 00:00:00 2001 From: Mathias Vanden Auweele Date: Tue, 29 Sep 2026 23:36:51 +0200 Subject: [PATCH 4/4] docs(yasqe): document '<<' tokenization incompatibility A '<' comparison directly followed by a full IRI (?o<) is now a syntax error, because SPARQL 1.2 tokenizes '<<' as a single token. Mention this in the changeset and add tests for both the rejected form and the spaced form. Co-Authored-By: Claude Opus 5.5 --- .changeset/sparql-1-2-syntax.md | 2 ++ test/unit/yasqe-sparql12-grammar-test.ts | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/.changeset/sparql-1-2-syntax.md b/.changeset/sparql-1-2-syntax.md index 859d15cc..b7eefd55 100644 --- a/.changeset/sparql-1-2-syntax.md +++ b/.changeset/sparql-1-2-syntax.md @@ -12,3 +12,5 @@ Support SPARQL 1.2 syntax in the query editor, following the grammar of the [SPA - `!` applied to any unary expression, and aggregates wherever a built-in call is allowed A blank node property list followed by a property path (`[ :p ?o ] :a/:b ?c`) is no longer reported as a syntax error. + +Known incompatibility: a `<` comparison written without a space before a full IRI, such as `FILTER(?o<)`, is now reported as a syntax error. SPARQL 1.2 reads `<<` as a single token (the longest match wins), so a SPARQL 1.2 parser rejects this query as well. Add a space to fix it: `FILTER(?o < )`. diff --git a/test/unit/yasqe-sparql12-grammar-test.ts b/test/unit/yasqe-sparql12-grammar-test.ts index cd318184..f930c739 100644 --- a/test/unit/yasqe-sparql12-grammar-test.ts +++ b/test/unit/yasqe-sparql12-grammar-test.ts @@ -315,6 +315,10 @@ const sparql11Queries: TestCase[] = [ name: "comparisons and brackets without whitespace", query: `${PREFIXES}SELECT * WHERE {?s ?p ?o FILTER(?o>3) FILTER((?o)>(?s)) FILTER(?o>=1||?o<=0) FILTER(?o) FILTER(>?o) }`, + }, { name: "property paths", query: `${PREFIXES}SELECT * WHERE { @@ -534,6 +538,12 @@ VERSION "1.2"`, query: `${PREFIXES}SELECT * WHERE { ?s :label "hello"@en-- . }`, errorLine: 4, }, + { + // Known incompatibility with SPARQL 1.1: the longest match '<<' is a single token in SPARQL 1.2 + name: "'<' directly followed by a full IRI", + query: `${PREFIXES}SELECT * WHERE { ?s ?p ?o FILTER(?o<) }`, + errorLine: 4, + }, ]; describe("Yasqe SPARQL 1.2 grammar", () => {