@matdata/yasqe 4.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +121 -0
- package/build/ts/grammar/tokenizer.d.ts +37 -0
- package/build/ts/src/CodeMirror.d.ts +21 -0
- package/build/ts/src/autocompleters/classes.d.ts +3 -0
- package/build/ts/src/autocompleters/index.d.ts +39 -0
- package/build/ts/src/autocompleters/prefixes.d.ts +3 -0
- package/build/ts/src/autocompleters/properties.d.ts +3 -0
- package/build/ts/src/autocompleters/variables.d.ts +3 -0
- package/build/ts/src/defaults.d.ts +75 -0
- package/build/ts/src/imgs.d.ts +7 -0
- package/build/ts/src/index.d.ts +303 -0
- package/build/ts/src/prefixFold.d.ts +8 -0
- package/build/ts/src/prefixUtils.d.ts +9 -0
- package/build/ts/src/sparql.d.ts +20 -0
- package/build/ts/src/tokenUtils.d.ts +4 -0
- package/build/ts/src/tooltip.d.ts +2 -0
- package/build/ts/src/trie.d.ts +13 -0
- package/build/yasqe.html +108 -0
- package/build/yasqe.min.css +2 -0
- package/build/yasqe.min.css.map +1 -0
- package/build/yasqe.min.js +3 -0
- package/build/yasqe.min.js.LICENSE.txt +3 -0
- package/build/yasqe.min.js.map +1 -0
- package/grammar/README.md +12 -0
- package/grammar/_tokenizer-table.js +4776 -0
- package/grammar/build.sh +2 -0
- package/grammar/sparql11-grammar.pl +834 -0
- package/grammar/sparqljs-browser-min.js +4535 -0
- package/grammar/tokenizer.ts +729 -0
- package/grammar/util/gen_ll1.pl +37 -0
- package/grammar/util/gen_sparql11.pl +11 -0
- package/grammar/util/ll1.pl +175 -0
- package/grammar/util/output_to_javascript.pl +75 -0
- package/grammar/util/prune.pl +49 -0
- package/grammar/util/rewrite.pl +104 -0
- package/package.json +40 -0
- package/src/CodeMirror.ts +54 -0
- package/src/autocompleters/classes.ts +32 -0
- package/src/autocompleters/index.ts +346 -0
- package/src/autocompleters/prefixes.ts +130 -0
- package/src/autocompleters/properties.ts +28 -0
- package/src/autocompleters/show-hint.scss +38 -0
- package/src/autocompleters/variables.ts +52 -0
- package/src/defaults.ts +149 -0
- package/src/imgs.ts +14 -0
- package/src/index.ts +1089 -0
- package/src/prefixFold.ts +93 -0
- package/src/prefixUtils.ts +65 -0
- package/src/scss/buttons.scss +275 -0
- package/src/scss/codemirrorMods.scss +36 -0
- package/src/scss/yasqe.scss +89 -0
- package/src/sparql.ts +215 -0
- package/src/tokenUtils.ts +121 -0
- package/src/tooltip.ts +31 -0
- package/src/trie.ts +238 -0
|
@@ -0,0 +1,729 @@
|
|
|
1
|
+
import CodeMirror from "codemirror";
|
|
2
|
+
export interface State {
|
|
3
|
+
tokenize: (stream: CodeMirror.StringStream, state: State) => string;
|
|
4
|
+
inLiteral: "SINGLE" | "DOUBLE" | undefined;
|
|
5
|
+
errorStartPos: number | undefined;
|
|
6
|
+
errorEndPos: number | undefined;
|
|
7
|
+
queryType:
|
|
8
|
+
| "SELECT"
|
|
9
|
+
| "CONSTRUCT"
|
|
10
|
+
| "ASK"
|
|
11
|
+
| "DESCRIBE"
|
|
12
|
+
| "INSERT"
|
|
13
|
+
| "DELETE"
|
|
14
|
+
| "LOAD"
|
|
15
|
+
| "CLEAR"
|
|
16
|
+
| "CREATE"
|
|
17
|
+
| "DROP"
|
|
18
|
+
| "COPY"
|
|
19
|
+
| "MOVE"
|
|
20
|
+
| "ADD"
|
|
21
|
+
| undefined;
|
|
22
|
+
inPrefixDecl: boolean;
|
|
23
|
+
allowVars: boolean;
|
|
24
|
+
allowBnodes: boolean;
|
|
25
|
+
storeProperty: boolean;
|
|
26
|
+
OK: boolean;
|
|
27
|
+
possibleCurrent: string[];
|
|
28
|
+
possibleNext: string[];
|
|
29
|
+
stack: any[];
|
|
30
|
+
variables: { [varName: string]: string };
|
|
31
|
+
prefixes: { [prefLabel: string]: string };
|
|
32
|
+
complete: boolean;
|
|
33
|
+
lastProperty: string;
|
|
34
|
+
lastPropertyIndex: number;
|
|
35
|
+
errorMsg: string | undefined;
|
|
36
|
+
lastPredicateOffset: number;
|
|
37
|
+
currentPnameNs: string | undefined;
|
|
38
|
+
possibleFullIri: boolean;
|
|
39
|
+
}
|
|
40
|
+
export interface Token {
|
|
41
|
+
quotePos: "end" | "start" | "content" | undefined;
|
|
42
|
+
cat: string;
|
|
43
|
+
style: string;
|
|
44
|
+
string: string;
|
|
45
|
+
start: number;
|
|
46
|
+
}
|
|
47
|
+
export default function (config: CodeMirror.EditorConfiguration): CodeMirror.Mode<State> {
|
|
48
|
+
const grammar = require("./_tokenizer-table.js");
|
|
49
|
+
const ll1_table = grammar.table;
|
|
50
|
+
|
|
51
|
+
const IRI_REF = '<[^<>"`|{}^\\\x00-\x20]*>';
|
|
52
|
+
const PN_CHARS_BASE =
|
|
53
|
+
"[A-Za-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD]";
|
|
54
|
+
const PN_CHARS_U = PN_CHARS_BASE + "|_";
|
|
55
|
+
|
|
56
|
+
const PN_CHARS = "(" + PN_CHARS_U + "|-|[0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040])";
|
|
57
|
+
const VARNAME = "(" + PN_CHARS_U + "|[0-9])" + "(" + PN_CHARS_U + "|[0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040])*";
|
|
58
|
+
const VAR1 = "\\?" + VARNAME;
|
|
59
|
+
const VAR2 = "\\$" + VARNAME;
|
|
60
|
+
|
|
61
|
+
const PN_PREFIX = "(" + PN_CHARS_BASE + ")(((" + PN_CHARS + ")|\\.)*(" + PN_CHARS + "))?";
|
|
62
|
+
|
|
63
|
+
const HEX = "[0-9A-Fa-f]";
|
|
64
|
+
const PERCENT = "(%" + HEX + HEX + ")";
|
|
65
|
+
const PN_LOCAL_ESC = "(\\\\[_~\\.\\-!\\$&'\\(\\)\\*\\+,;=/\\?#@%])";
|
|
66
|
+
const PLX = "(" + PERCENT + "|" + PN_LOCAL_ESC + ")";
|
|
67
|
+
const PN_LOCAL =
|
|
68
|
+
"(" + PN_CHARS_U + "|:|[0-9]|" + PLX + ")((" + PN_CHARS + "|\\.|:|" + PLX + ")*(" + PN_CHARS + "|:|" + PLX + "))?";
|
|
69
|
+
const BLANK_NODE_LABEL = "_:(" + PN_CHARS_U + "|[0-9])((" + PN_CHARS + "|\\.)*" + PN_CHARS + ")?";
|
|
70
|
+
const PNAME_NS = "(" + PN_PREFIX + ")?:";
|
|
71
|
+
const PNAME_LN = PNAME_NS + PN_LOCAL;
|
|
72
|
+
const LANGTAG = "@[a-zA-Z]+(-[a-zA-Z0-9]+)*";
|
|
73
|
+
|
|
74
|
+
const EXPONENT = "[eE][\\+-]?[0-9]+";
|
|
75
|
+
const INTEGER = "[0-9]+";
|
|
76
|
+
const DECIMAL = "[0-9]*\\.[0-9]+";
|
|
77
|
+
const DOUBLE = "(([0-9]+\\.[0-9]*" + EXPONENT + ")|(\\.[0-9]+" + EXPONENT + ")|([0-9]+" + EXPONENT + "))";
|
|
78
|
+
|
|
79
|
+
const INTEGER_POSITIVE = "\\+" + INTEGER;
|
|
80
|
+
const DECIMAL_POSITIVE = "\\+" + DECIMAL;
|
|
81
|
+
const DOUBLE_POSITIVE = "\\+" + DOUBLE;
|
|
82
|
+
const INTEGER_NEGATIVE = "-" + INTEGER;
|
|
83
|
+
const DECIMAL_NEGATIVE = "-" + DECIMAL;
|
|
84
|
+
const DOUBLE_NEGATIVE = "-" + DOUBLE;
|
|
85
|
+
|
|
86
|
+
const ECHAR = "\\\\[tbnrf\\\\\"']";
|
|
87
|
+
|
|
88
|
+
//IMPORTANT: this unicode rule is not in the official grammar.
|
|
89
|
+
//Reason: https://github.com/YASGUI/YASQE/issues/49
|
|
90
|
+
//unicode escape sequences (which the sparql spec considers part of the pre-processing of sparql queries)
|
|
91
|
+
//are marked as invalid. We have little choice (other than adding a layer of complixity) than to modify the grammar accordingly
|
|
92
|
+
//however, for now only allow these escape sequences in literals (where actually, this should be allows in e.g. prefixes as well)
|
|
93
|
+
const hex4 = HEX + "{4}";
|
|
94
|
+
const unicode = "(\\\\u" + hex4 + "|\\\\U00(10|0" + HEX + ")" + hex4 + ")";
|
|
95
|
+
const STRING_LITERAL1 = "'(([^\\x27\\x5C\\x0A\\x0D])|" + ECHAR + "|" + unicode + ")*'";
|
|
96
|
+
const STRING_LITERAL2 = '"(([^\\x22\\x5C\\x0A\\x0D])|' + ECHAR + "|" + unicode + ')*"';
|
|
97
|
+
|
|
98
|
+
const STRING_LITERAL_LONG: {
|
|
99
|
+
[key: string]: {
|
|
100
|
+
CAT: string;
|
|
101
|
+
QUOTES: string;
|
|
102
|
+
CONTENTS: string;
|
|
103
|
+
COMPLETE?: string;
|
|
104
|
+
};
|
|
105
|
+
} = {
|
|
106
|
+
SINGLE: {
|
|
107
|
+
CAT: "STRING_LITERAL_LONG1",
|
|
108
|
+
QUOTES: "'''",
|
|
109
|
+
CONTENTS: "(('|'')?([^'\\\\]|" + ECHAR + "|" + unicode + "))*",
|
|
110
|
+
},
|
|
111
|
+
DOUBLE: {
|
|
112
|
+
CAT: "STRING_LITERAL_LONG2",
|
|
113
|
+
QUOTES: '"""',
|
|
114
|
+
CONTENTS: '(("|"")?([^"\\\\]|' + ECHAR + "|" + unicode + "))*",
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
for (const key in STRING_LITERAL_LONG) {
|
|
118
|
+
STRING_LITERAL_LONG[key].COMPLETE =
|
|
119
|
+
STRING_LITERAL_LONG[key].QUOTES + STRING_LITERAL_LONG[key].CONTENTS + STRING_LITERAL_LONG[key].QUOTES;
|
|
120
|
+
}
|
|
121
|
+
//some regular expressions not used in regular terminals, because this is used accross lines
|
|
122
|
+
interface LiteralRegex {
|
|
123
|
+
name: string;
|
|
124
|
+
regex: RegExp;
|
|
125
|
+
style: string;
|
|
126
|
+
}
|
|
127
|
+
var stringLiteralLongRegex: {
|
|
128
|
+
[k: string]: {
|
|
129
|
+
complete: LiteralRegex;
|
|
130
|
+
contents: LiteralRegex;
|
|
131
|
+
closing: LiteralRegex;
|
|
132
|
+
quotes: LiteralRegex;
|
|
133
|
+
};
|
|
134
|
+
} = {};
|
|
135
|
+
for (const key in STRING_LITERAL_LONG) {
|
|
136
|
+
stringLiteralLongRegex[key] = {
|
|
137
|
+
complete: {
|
|
138
|
+
name: "STRING_LITERAL_LONG_" + key,
|
|
139
|
+
regex: new RegExp("^" + STRING_LITERAL_LONG[key].COMPLETE),
|
|
140
|
+
style: "string",
|
|
141
|
+
},
|
|
142
|
+
contents: {
|
|
143
|
+
name: "STRING_LITERAL_LONG_" + key,
|
|
144
|
+
regex: new RegExp("^" + STRING_LITERAL_LONG[key].CONTENTS),
|
|
145
|
+
style: "string",
|
|
146
|
+
},
|
|
147
|
+
closing: {
|
|
148
|
+
name: "STRING_LITERAL_LONG_" + key,
|
|
149
|
+
regex: new RegExp("^" + STRING_LITERAL_LONG[key].CONTENTS + STRING_LITERAL_LONG[key].QUOTES),
|
|
150
|
+
style: "string",
|
|
151
|
+
},
|
|
152
|
+
quotes: {
|
|
153
|
+
name: "STRING_LITERAL_LONG_QUOTES_" + key,
|
|
154
|
+
regex: new RegExp("^" + STRING_LITERAL_LONG[key].QUOTES),
|
|
155
|
+
style: "string",
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const WS = "[\\x20\\x09\\x0D\\x0A]";
|
|
161
|
+
// Careful! Code mirror feeds one line at a time with no \n
|
|
162
|
+
// ... but otherwise comment is terminated by \n
|
|
163
|
+
const COMMENT = "#([^\\n\\r]*[\\n\\r]|[^\\n\\r]*$)";
|
|
164
|
+
const WS_OR_COMMENT_STAR = "(" + WS + "|(" + COMMENT + "))*";
|
|
165
|
+
const NIL = "\\(" + WS_OR_COMMENT_STAR + "\\)";
|
|
166
|
+
const ANON = "\\[" + WS_OR_COMMENT_STAR + "\\]";
|
|
167
|
+
const terminals = [
|
|
168
|
+
{
|
|
169
|
+
name: "WS",
|
|
170
|
+
regex: new RegExp("^" + WS + "+"),
|
|
171
|
+
style: "ws",
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
{
|
|
175
|
+
name: "COMMENT",
|
|
176
|
+
regex: new RegExp("^" + COMMENT),
|
|
177
|
+
style: "comment",
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
{
|
|
181
|
+
name: "IRI_REF",
|
|
182
|
+
regex: new RegExp("^" + IRI_REF),
|
|
183
|
+
style: "variable-3",
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
{
|
|
187
|
+
name: "VAR1",
|
|
188
|
+
regex: new RegExp("^" + VAR1),
|
|
189
|
+
style: "atom",
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
{
|
|
193
|
+
name: "VAR2",
|
|
194
|
+
regex: new RegExp("^" + VAR2),
|
|
195
|
+
style: "atom",
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
{
|
|
199
|
+
name: "LANGTAG",
|
|
200
|
+
regex: new RegExp("^" + LANGTAG),
|
|
201
|
+
style: "meta",
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
{
|
|
205
|
+
name: "DOUBLE",
|
|
206
|
+
regex: new RegExp("^" + DOUBLE),
|
|
207
|
+
style: "number",
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
{
|
|
211
|
+
name: "DECIMAL",
|
|
212
|
+
regex: new RegExp("^" + DECIMAL),
|
|
213
|
+
style: "number",
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
{
|
|
217
|
+
name: "INTEGER",
|
|
218
|
+
regex: new RegExp("^" + INTEGER),
|
|
219
|
+
style: "number",
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
{
|
|
223
|
+
name: "DOUBLE_POSITIVE",
|
|
224
|
+
regex: new RegExp("^" + DOUBLE_POSITIVE),
|
|
225
|
+
style: "number",
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
{
|
|
229
|
+
name: "DECIMAL_POSITIVE",
|
|
230
|
+
regex: new RegExp("^" + DECIMAL_POSITIVE),
|
|
231
|
+
style: "number",
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
{
|
|
235
|
+
name: "INTEGER_POSITIVE",
|
|
236
|
+
regex: new RegExp("^" + INTEGER_POSITIVE),
|
|
237
|
+
style: "number",
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
{
|
|
241
|
+
name: "DOUBLE_NEGATIVE",
|
|
242
|
+
regex: new RegExp("^" + DOUBLE_NEGATIVE),
|
|
243
|
+
style: "number",
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
{
|
|
247
|
+
name: "DECIMAL_NEGATIVE",
|
|
248
|
+
regex: new RegExp("^" + DECIMAL_NEGATIVE),
|
|
249
|
+
style: "number",
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
{
|
|
253
|
+
name: "INTEGER_NEGATIVE",
|
|
254
|
+
regex: new RegExp("^" + INTEGER_NEGATIVE),
|
|
255
|
+
style: "number",
|
|
256
|
+
},
|
|
257
|
+
// stringLiteralLongRegex.SINGLE.complete,
|
|
258
|
+
// stringLiteralLongRegex.DOUBLE.complete,
|
|
259
|
+
// stringLiteralLongRegex.SINGLE.quotes,
|
|
260
|
+
// stringLiteralLongRegex.DOUBLE.quotes,
|
|
261
|
+
|
|
262
|
+
{
|
|
263
|
+
name: "STRING_LITERAL1",
|
|
264
|
+
regex: new RegExp("^" + STRING_LITERAL1),
|
|
265
|
+
style: "string",
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
{
|
|
269
|
+
name: "STRING_LITERAL2",
|
|
270
|
+
regex: new RegExp("^" + STRING_LITERAL2),
|
|
271
|
+
style: "string",
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
// Enclosed comments won't be highlighted
|
|
275
|
+
{
|
|
276
|
+
name: "NIL",
|
|
277
|
+
regex: new RegExp("^" + NIL),
|
|
278
|
+
style: "punc",
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
// Enclosed comments won't be highlighted
|
|
282
|
+
{
|
|
283
|
+
name: "ANON",
|
|
284
|
+
regex: new RegExp("^" + ANON),
|
|
285
|
+
style: "punc",
|
|
286
|
+
},
|
|
287
|
+
|
|
288
|
+
{
|
|
289
|
+
name: "PNAME_LN",
|
|
290
|
+
regex: new RegExp("^" + PNAME_LN),
|
|
291
|
+
style: "string-2",
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
{
|
|
295
|
+
name: "PNAME_NS",
|
|
296
|
+
regex: new RegExp("^" + PNAME_NS),
|
|
297
|
+
style: "string-2",
|
|
298
|
+
},
|
|
299
|
+
|
|
300
|
+
{
|
|
301
|
+
name: "BLANK_NODE_LABEL",
|
|
302
|
+
regex: new RegExp("^" + BLANK_NODE_LABEL),
|
|
303
|
+
style: "string-2",
|
|
304
|
+
},
|
|
305
|
+
];
|
|
306
|
+
|
|
307
|
+
function getPossibles(symbol: string) {
|
|
308
|
+
var possibles = [],
|
|
309
|
+
possiblesOb = ll1_table[symbol];
|
|
310
|
+
if (possiblesOb != undefined) {
|
|
311
|
+
for (const property in possiblesOb) {
|
|
312
|
+
possibles.push(property.toString());
|
|
313
|
+
}
|
|
314
|
+
} else {
|
|
315
|
+
possibles.push(symbol);
|
|
316
|
+
}
|
|
317
|
+
return possibles;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function tokenBase(stream: CodeMirror.StringStream, state: State) {
|
|
321
|
+
function nextToken(): Token {
|
|
322
|
+
let consumed: string[];
|
|
323
|
+
if (state.inLiteral) {
|
|
324
|
+
var closingQuotes = false;
|
|
325
|
+
//multi-line literal. try to parse contents.
|
|
326
|
+
consumed = stream.match(stringLiteralLongRegex[state.inLiteral].contents.regex as any, true, false) as any;
|
|
327
|
+
if (consumed && consumed[0].length == 0) {
|
|
328
|
+
//try seeing whether we can consume closing quotes, to avoid stopping
|
|
329
|
+
consumed = stream.match(stringLiteralLongRegex[state.inLiteral].closing.regex as any, true, false) as any;
|
|
330
|
+
closingQuotes = true;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (consumed && consumed[0].length > 0) {
|
|
334
|
+
//some string content here.
|
|
335
|
+
const returnObj: Token = {
|
|
336
|
+
quotePos: closingQuotes ? "end" : "content",
|
|
337
|
+
cat: STRING_LITERAL_LONG[state.inLiteral].CAT,
|
|
338
|
+
style: stringLiteralLongRegex[state.inLiteral].complete.style,
|
|
339
|
+
string: consumed[0],
|
|
340
|
+
start: stream.start,
|
|
341
|
+
};
|
|
342
|
+
if (closingQuotes) state.inLiteral = undefined;
|
|
343
|
+
return returnObj;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
//Multiline literals
|
|
348
|
+
for (const quoteType in stringLiteralLongRegex) {
|
|
349
|
+
consumed = stream.match(stringLiteralLongRegex[quoteType].quotes.regex as any, true, false) as any;
|
|
350
|
+
if (consumed) {
|
|
351
|
+
var quotePos: Token["quotePos"];
|
|
352
|
+
if (state.inLiteral) {
|
|
353
|
+
//end of literal. everything is fine
|
|
354
|
+
state.inLiteral = undefined;
|
|
355
|
+
quotePos = "end";
|
|
356
|
+
} else {
|
|
357
|
+
state.inLiteral = <any>quoteType;
|
|
358
|
+
quotePos = "start";
|
|
359
|
+
}
|
|
360
|
+
return {
|
|
361
|
+
cat: STRING_LITERAL_LONG[quoteType].CAT,
|
|
362
|
+
style: stringLiteralLongRegex[quoteType].quotes.style,
|
|
363
|
+
string: consumed[0],
|
|
364
|
+
quotePos: quotePos,
|
|
365
|
+
start: stream.start,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Tokens defined by individual regular expressions
|
|
371
|
+
for (var i = 0; i < terminals.length; ++i) {
|
|
372
|
+
consumed = stream.match(terminals[i].regex as any, true, false) as any;
|
|
373
|
+
if (consumed) {
|
|
374
|
+
return {
|
|
375
|
+
cat: terminals[i].name,
|
|
376
|
+
style: terminals[i].style,
|
|
377
|
+
string: consumed[0],
|
|
378
|
+
start: stream.start,
|
|
379
|
+
quotePos: undefined,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Keywords
|
|
385
|
+
consumed = stream.match(grammar.keywords, true, false) as any;
|
|
386
|
+
if (consumed)
|
|
387
|
+
return {
|
|
388
|
+
cat: stream.current().toUpperCase(),
|
|
389
|
+
style: "keyword",
|
|
390
|
+
string: consumed[0],
|
|
391
|
+
start: stream.start,
|
|
392
|
+
quotePos: undefined,
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
// Punctuation
|
|
396
|
+
consumed = stream.match(grammar.punct, true, false) as any;
|
|
397
|
+
if (consumed)
|
|
398
|
+
return {
|
|
399
|
+
cat: stream.current(),
|
|
400
|
+
style: "punc",
|
|
401
|
+
string: consumed[0],
|
|
402
|
+
start: stream.start,
|
|
403
|
+
quotePos: undefined,
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
// Token is invalid
|
|
407
|
+
// better consume something anyway, or else we're stuck
|
|
408
|
+
consumed = stream.match(/^.[A-Za-z0-9]*/ as any, true, false) as any;
|
|
409
|
+
return {
|
|
410
|
+
cat: "<invalid_token>",
|
|
411
|
+
style: "error",
|
|
412
|
+
string: consumed[0],
|
|
413
|
+
start: stream.start,
|
|
414
|
+
quotePos: undefined,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function recordFailurePos() {
|
|
419
|
+
// tokenOb.style= "sp-invalid";
|
|
420
|
+
const col = stream.column();
|
|
421
|
+
state.errorStartPos = col;
|
|
422
|
+
state.errorEndPos = col + tokenOb.string.length;
|
|
423
|
+
}
|
|
424
|
+
function setQueryType(s: string) {
|
|
425
|
+
if (state.queryType == null) {
|
|
426
|
+
switch (s) {
|
|
427
|
+
case "SELECT":
|
|
428
|
+
case "CONSTRUCT":
|
|
429
|
+
case "ASK":
|
|
430
|
+
case "DESCRIBE":
|
|
431
|
+
case "INSERT":
|
|
432
|
+
case "DELETE":
|
|
433
|
+
case "LOAD":
|
|
434
|
+
case "CLEAR":
|
|
435
|
+
case "CREATE":
|
|
436
|
+
case "DROP":
|
|
437
|
+
case "COPY":
|
|
438
|
+
case "MOVE":
|
|
439
|
+
case "ADD":
|
|
440
|
+
state.queryType = s;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Some fake non-terminals are just there to have side-effect on state
|
|
446
|
+
// - i.e. allow or disallow variables and bnodes in certain non-nesting
|
|
447
|
+
// contexts
|
|
448
|
+
function setSideConditions(topSymbol: string) {
|
|
449
|
+
if (topSymbol === "prefixDecl") {
|
|
450
|
+
state.inPrefixDecl = true;
|
|
451
|
+
} else {
|
|
452
|
+
state.inPrefixDecl = false;
|
|
453
|
+
}
|
|
454
|
+
switch (topSymbol) {
|
|
455
|
+
case "disallowVars":
|
|
456
|
+
state.allowVars = false;
|
|
457
|
+
break;
|
|
458
|
+
case "allowVars":
|
|
459
|
+
state.allowVars = true;
|
|
460
|
+
break;
|
|
461
|
+
case "disallowBnodes":
|
|
462
|
+
state.allowBnodes = false;
|
|
463
|
+
break;
|
|
464
|
+
case "allowBnodes":
|
|
465
|
+
state.allowBnodes = true;
|
|
466
|
+
break;
|
|
467
|
+
case "storeProperty":
|
|
468
|
+
state.storeProperty = true;
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function checkSideConditions(topSymbol: string) {
|
|
474
|
+
return (
|
|
475
|
+
(state.allowVars || topSymbol != "var") &&
|
|
476
|
+
(state.allowBnodes ||
|
|
477
|
+
(topSymbol != "blankNode" &&
|
|
478
|
+
topSymbol != "blankNodePropertyList" &&
|
|
479
|
+
topSymbol != "blankNodePropertyListPath"))
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// CodeMirror works with one line at a time,
|
|
484
|
+
// but newline should behave like whitespace
|
|
485
|
+
// - i.e. a definite break between tokens (for autocompleter)
|
|
486
|
+
if (stream.pos == 0) state.possibleCurrent = state.possibleNext;
|
|
487
|
+
|
|
488
|
+
const tokenOb = nextToken();
|
|
489
|
+
|
|
490
|
+
if (tokenOb.cat == "<invalid_token>") {
|
|
491
|
+
// set error state, and
|
|
492
|
+
if (state.OK == true) {
|
|
493
|
+
state.OK = false;
|
|
494
|
+
recordFailurePos();
|
|
495
|
+
}
|
|
496
|
+
state.complete = false;
|
|
497
|
+
// alert("Invalid:"+tokenOb.text);
|
|
498
|
+
return tokenOb.style;
|
|
499
|
+
}
|
|
500
|
+
if (tokenOb.cat === "WS" || tokenOb.cat === "COMMENT" || (tokenOb.quotePos && tokenOb.quotePos != "end")) {
|
|
501
|
+
state.possibleCurrent = state.possibleNext;
|
|
502
|
+
state.possibleFullIri = false;
|
|
503
|
+
return tokenOb.style;
|
|
504
|
+
}
|
|
505
|
+
// Otherwise, run the parser until the token is digested
|
|
506
|
+
// or failure
|
|
507
|
+
var finished = false;
|
|
508
|
+
var topSymbol;
|
|
509
|
+
const tokenCat = tokenOb.cat;
|
|
510
|
+
if (state.possibleFullIri && tokenOb.string === ">") {
|
|
511
|
+
state.possibleFullIri = false;
|
|
512
|
+
}
|
|
513
|
+
if (!state.possibleFullIri && tokenOb.string === "<") {
|
|
514
|
+
state.possibleFullIri = true;
|
|
515
|
+
}
|
|
516
|
+
if (!tokenOb.quotePos || tokenOb.quotePos == "end") {
|
|
517
|
+
// Incremental LL1 parse
|
|
518
|
+
while (state.stack.length > 0 && tokenCat && state.OK && !finished) {
|
|
519
|
+
topSymbol = state.stack.pop();
|
|
520
|
+
if (topSymbol === "var" && tokenOb.string) state.variables[tokenOb.string] = tokenOb.string;
|
|
521
|
+
if (!ll1_table[topSymbol]) {
|
|
522
|
+
// Top symbol is a terminal
|
|
523
|
+
if (topSymbol == tokenCat) {
|
|
524
|
+
if (state.inPrefixDecl) {
|
|
525
|
+
if (topSymbol === "PNAME_NS" && tokenOb.string.length > 0) {
|
|
526
|
+
state.currentPnameNs = tokenOb.string.slice(0, -1);
|
|
527
|
+
} else if (typeof state.currentPnameNs === "string" && tokenOb.string.length > 1) {
|
|
528
|
+
state.prefixes[state.currentPnameNs] = tokenOb.string.slice(1, -1);
|
|
529
|
+
//reset current pname ns
|
|
530
|
+
state.currentPnameNs = undefined;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
// Matching terminals
|
|
534
|
+
// - consume token from input stream
|
|
535
|
+
finished = true;
|
|
536
|
+
setQueryType(topSymbol);
|
|
537
|
+
// Check whether $ (end of input token) is poss next
|
|
538
|
+
// for everything on stack
|
|
539
|
+
var allNillable = true;
|
|
540
|
+
for (var sp = state.stack.length; sp > 0; --sp) {
|
|
541
|
+
const item = ll1_table[state.stack[sp - 1]];
|
|
542
|
+
if (!item || !item["$"]) allNillable = false;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
state.complete = allNillable;
|
|
546
|
+
if (state.storeProperty && tokenCat != "punc") {
|
|
547
|
+
state.lastProperty = tokenOb.string;
|
|
548
|
+
state.lastPropertyIndex = tokenOb.start;
|
|
549
|
+
state.storeProperty = false;
|
|
550
|
+
} else if (tokenCat === "." || tokenCat === ";") {
|
|
551
|
+
// the last property wont be relevant for what follows, so we reset it
|
|
552
|
+
state.lastProperty = "";
|
|
553
|
+
state.lastPropertyIndex = 0;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
//check whether a used prefix is actually defined
|
|
557
|
+
if (!state.inPrefixDecl && (tokenCat === "PNAME_NS" || tokenCat === "PNAME_LN")) {
|
|
558
|
+
const colonIndex = tokenOb.string.indexOf(":");
|
|
559
|
+
if (colonIndex >= 0) {
|
|
560
|
+
const prefNs = tokenOb.string.slice(0, colonIndex);
|
|
561
|
+
if (state.prefixes[prefNs] === undefined) {
|
|
562
|
+
state.OK = false;
|
|
563
|
+
recordFailurePos();
|
|
564
|
+
state.errorMsg = "Prefix '" + prefNs + "' is not defined";
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
} else {
|
|
569
|
+
state.OK = false;
|
|
570
|
+
state.complete = false;
|
|
571
|
+
recordFailurePos();
|
|
572
|
+
}
|
|
573
|
+
} else {
|
|
574
|
+
// topSymbol is nonterminal
|
|
575
|
+
// - see if there is an entry for topSymbol
|
|
576
|
+
// and nextToken in table
|
|
577
|
+
const nextSymbols = ll1_table[topSymbol][tokenCat];
|
|
578
|
+
if (nextSymbols != undefined && checkSideConditions(topSymbol)) {
|
|
579
|
+
// Match - copy RHS of rule to stack
|
|
580
|
+
for (var i = nextSymbols.length - 1; i >= 0; --i) {
|
|
581
|
+
state.stack.push(nextSymbols[i]);
|
|
582
|
+
}
|
|
583
|
+
// Peform any non-grammatical side-effects
|
|
584
|
+
setSideConditions(topSymbol);
|
|
585
|
+
} else {
|
|
586
|
+
// No match in table - fail
|
|
587
|
+
state.OK = false;
|
|
588
|
+
state.complete = false;
|
|
589
|
+
recordFailurePos();
|
|
590
|
+
state.stack.push(topSymbol); // Shove topSymbol back on stack
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
if (!finished && state.OK) {
|
|
596
|
+
state.OK = false;
|
|
597
|
+
state.complete = false;
|
|
598
|
+
recordFailurePos();
|
|
599
|
+
}
|
|
600
|
+
if (state.possibleNext.indexOf("a") >= 0) {
|
|
601
|
+
//only store last pred offset when this prop isnt part of a property path
|
|
602
|
+
//see #https://github.com/TriplyDB/YASGUI.YASQE/issues/105
|
|
603
|
+
const line = stream.string;
|
|
604
|
+
for (let i = tokenOb.start; i >= 0; i--) {
|
|
605
|
+
if (line[i - 1] === " ") {
|
|
606
|
+
//continue search
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
if (line[i - 1] === "|" || line[i - 1] === "/") {
|
|
610
|
+
//part of property path, not setting this val
|
|
611
|
+
} else if (tokenOb.style === "punc") {
|
|
612
|
+
//we've moved past the property path, and reached punctuation. This can happens when the object is a literal
|
|
613
|
+
//So, simply not set this val (i.e. use value that was defined before this token)
|
|
614
|
+
} else {
|
|
615
|
+
state.lastPredicateOffset = tokenOb.start;
|
|
616
|
+
}
|
|
617
|
+
break;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
state.possibleCurrent = state.possibleNext;
|
|
622
|
+
|
|
623
|
+
state.possibleNext = getPossibles(state.stack[state.stack.length - 1]);
|
|
624
|
+
|
|
625
|
+
return tokenOb.style;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
const indentTop: { [symbol: string]: number } = {
|
|
629
|
+
"*[,, object]": 3,
|
|
630
|
+
"*[(,),object]": 3,
|
|
631
|
+
"*[(,),objectPath]": 3,
|
|
632
|
+
"*[/,pathEltOrInverse]": 2,
|
|
633
|
+
object: 2,
|
|
634
|
+
objectPath: 2,
|
|
635
|
+
objectList: 2,
|
|
636
|
+
objectListPath: 2,
|
|
637
|
+
storeProperty: 2,
|
|
638
|
+
pathMod: 2,
|
|
639
|
+
"?pathMod": 2,
|
|
640
|
+
propertyListNotEmpty: 1,
|
|
641
|
+
propertyList: 1,
|
|
642
|
+
propertyListPath: 1,
|
|
643
|
+
propertyListPathNotEmpty: 1,
|
|
644
|
+
"?[verb,objectList]": 1,
|
|
645
|
+
// "?[or([verbPath, verbSimple]),objectList]": 1,
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
const indentTable: { [char: string]: number } = {
|
|
649
|
+
"}": 1,
|
|
650
|
+
"]": 1,
|
|
651
|
+
")": 1,
|
|
652
|
+
"{": -1,
|
|
653
|
+
"(": -1,
|
|
654
|
+
"[": -1,
|
|
655
|
+
// "*[;,?[or([verbPath,verbSimple]),objectList]]": 1,
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
function indent(state: State, textAfter: string) {
|
|
659
|
+
//just avoid we don't indent multi-line literals
|
|
660
|
+
if (state.inLiteral) return 0;
|
|
661
|
+
if (
|
|
662
|
+
state.lastPredicateOffset !== undefined &&
|
|
663
|
+
state.stack.length &&
|
|
664
|
+
state.stack[state.stack.length - 1] == "?[or([verbPath,verbSimple]),objectListPath]"
|
|
665
|
+
) {
|
|
666
|
+
//we are after a semi-colon. I.e., nicely align this line with predicate position of previous line
|
|
667
|
+
return state.lastPredicateOffset;
|
|
668
|
+
} else {
|
|
669
|
+
var n = 0; // indent level
|
|
670
|
+
var i = state.stack.length - 1;
|
|
671
|
+
if (/^[\}\]\)]/.test(textAfter)) {
|
|
672
|
+
// Skip stack items until after matching bracket
|
|
673
|
+
const closeBracket = textAfter.substr(0, 1);
|
|
674
|
+
for (; i >= 0; --i) {
|
|
675
|
+
if (state.stack[i] == closeBracket) {
|
|
676
|
+
--i;
|
|
677
|
+
break;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
} else {
|
|
681
|
+
// Consider nullable non-terminals if at top of stack
|
|
682
|
+
let dn = indentTop[state.stack[i]];
|
|
683
|
+
if (dn) {
|
|
684
|
+
n += dn;
|
|
685
|
+
--i;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
for (; i >= 0; --i) {
|
|
689
|
+
let dn = indentTable[state.stack[i]];
|
|
690
|
+
if (dn) {
|
|
691
|
+
n += dn;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
return n * (config.indentUnit ?? 2);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
return {
|
|
699
|
+
token: tokenBase,
|
|
700
|
+
startState: function (): State {
|
|
701
|
+
return {
|
|
702
|
+
tokenize: tokenBase,
|
|
703
|
+
OK: true,
|
|
704
|
+
complete: grammar.acceptEmpty,
|
|
705
|
+
errorStartPos: undefined,
|
|
706
|
+
errorEndPos: undefined,
|
|
707
|
+
queryType: undefined,
|
|
708
|
+
possibleCurrent: getPossibles(grammar.startSymbol),
|
|
709
|
+
possibleNext: getPossibles(grammar.startSymbol),
|
|
710
|
+
allowVars: true,
|
|
711
|
+
allowBnodes: true,
|
|
712
|
+
storeProperty: false,
|
|
713
|
+
lastProperty: "",
|
|
714
|
+
lastPropertyIndex: 0,
|
|
715
|
+
inLiteral: undefined,
|
|
716
|
+
stack: [grammar.startSymbol],
|
|
717
|
+
lastPredicateOffset: config.indentUnit || 2,
|
|
718
|
+
prefixes: {},
|
|
719
|
+
variables: {},
|
|
720
|
+
currentPnameNs: undefined,
|
|
721
|
+
errorMsg: undefined,
|
|
722
|
+
inPrefixDecl: false,
|
|
723
|
+
possibleFullIri: false,
|
|
724
|
+
};
|
|
725
|
+
},
|
|
726
|
+
indent: indent,
|
|
727
|
+
electricChars: "}])",
|
|
728
|
+
};
|
|
729
|
+
}
|