@sap/cds-compiler 4.2.4 → 4.3.0

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.
Files changed (66) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/bin/cdsc.js +8 -0
  3. package/bin/cdshi.js +3 -3
  4. package/doc/CHANGELOG_BETA.md +7 -0
  5. package/lib/api/main.js +19 -0
  6. package/lib/base/location.js +16 -0
  7. package/lib/base/message-registry.js +47 -16
  8. package/lib/base/messages.js +49 -38
  9. package/lib/base/model.js +1 -1
  10. package/lib/checks/checkPathsInStoredCalcElement.js +83 -0
  11. package/lib/checks/existsExpressionsOnlyForeignKeys.js +71 -0
  12. package/lib/checks/existsMustEndInAssoc.js +27 -0
  13. package/lib/checks/onConditions.js +47 -1
  14. package/lib/checks/validator.js +10 -1
  15. package/lib/compiler/assert-consistency.js +23 -15
  16. package/lib/compiler/base.js +31 -14
  17. package/lib/compiler/builtins.js +21 -20
  18. package/lib/compiler/checks.js +36 -49
  19. package/lib/compiler/define.js +71 -91
  20. package/lib/compiler/extend.js +27 -25
  21. package/lib/compiler/finalize-parse-cdl.js +1 -1
  22. package/lib/compiler/generate.js +67 -87
  23. package/lib/compiler/kick-start.js +7 -5
  24. package/lib/compiler/populate.js +32 -30
  25. package/lib/compiler/propagator.js +2 -0
  26. package/lib/compiler/resolve.js +29 -25
  27. package/lib/compiler/shared.js +57 -31
  28. package/lib/compiler/tweak-assocs.js +203 -22
  29. package/lib/compiler/utils.js +0 -18
  30. package/lib/gen/Dictionary.json +10 -4
  31. package/lib/gen/language.checksum +1 -1
  32. package/lib/gen/languageParser.js +3 -3
  33. package/lib/inspect/inspectPropagation.js +2 -1
  34. package/lib/json/from-csn.js +63 -28
  35. package/lib/json/to-csn.js +23 -13
  36. package/lib/language/antlrParser.js +1 -1
  37. package/lib/language/errorStrategy.js +5 -1
  38. package/lib/language/genericAntlrParser.js +67 -61
  39. package/lib/main.d.ts +26 -1
  40. package/lib/main.js +2 -1
  41. package/lib/model/csnRefs.js +1 -0
  42. package/lib/model/csnUtils.js +28 -0
  43. package/lib/model/revealInternalProperties.js +3 -9
  44. package/lib/optionProcessor.js +17 -1
  45. package/lib/render/toCdl.js +1 -1
  46. package/lib/transform/db/associations.js +3 -4
  47. package/lib/transform/db/backlinks.js +293 -0
  48. package/lib/transform/db/expansion.js +9 -7
  49. package/lib/transform/db/flattening.js +3 -2
  50. package/lib/transform/db/rewriteCalculatedElements.js +1 -67
  51. package/lib/transform/db/transformExists.js +3 -58
  52. package/lib/transform/db/views.js +8 -14
  53. package/lib/transform/effective/.eslintrc.json +4 -0
  54. package/lib/transform/effective/associations.js +101 -0
  55. package/lib/transform/effective/main.js +88 -0
  56. package/lib/transform/effective/misc.js +61 -0
  57. package/lib/transform/effective/queries.js +42 -0
  58. package/lib/transform/effective/types.js +121 -0
  59. package/lib/transform/forRelationalDB.js +12 -235
  60. package/lib/transform/localized.js +22 -3
  61. package/lib/transform/parseExpr.js +7 -3
  62. package/lib/transform/transformUtils.js +5 -22
  63. package/lib/transform/translateAssocsToJoins.js +42 -38
  64. package/lib/transform/universalCsn/universalCsnEnricher.js +17 -1
  65. package/package.json +1 -2
  66. package/lib/language/language.g4 +0 -3260
@@ -1,3260 +0,0 @@
1
- // ANTLR4 grammar to generate Parser and Lexer for CDS-Language
2
-
3
- // To be built the parser by hand, install Java, download the ANTLR4 tool, then
4
- // antlr4 -no-listener -o ../gen language.g4
5
- // Alternatively, install Java, and use
6
- // npm run download && npm run gen
7
- //
8
- // To test the parser in the REPL, see file './lib/language/antlrParser.js'.
9
-
10
- // This grammar is built according to the following guidelines:
11
- //
12
- // * Do not express every syntactical restriction by grammar rules, and do
13
- // not define a grammar which allows every nonsense. We might specify
14
- // syntactical restrictions in a certain form inside actions or semantic
15
- // predicates to have them directly available for IDE code completion.
16
- //
17
- // * Keep the number of token types small. Thus, do not define different
18
- // token types for things which are not distinguished in the parser.
19
- // Examples: one token type for numbers (have a check if you just want to
20
- // allow integers at certain places), one token type for non-quoted and
21
- // quoted identifiers.
22
- //
23
- // * Keep the number of keywords as small as possible. Thus, built-ins is a
24
- // topic for the semantic analysis, not the grammar. Examples: no keywords
25
- // for built-in types or built-in SQL functions. This also avoids noise in
26
- // the grammar and a huge/slow generated parser.
27
- // ┌─────────────────────────────────────────────────────────────────────────┐
28
- // For our adapted ANTLR error strategy concerning (non-reserved) keywords,
29
- // make sure to define non-reserved keywords between the lexer rule `Number`
30
- // and `Identifier`. The latter must be the second last rule, the last is
31
- // `IllegalToken`. Do not rename these three rules. Add each new
32
- // non-reserved keyword to rule `ident`, but check for ambiguities!
33
- // └─────────────────────────────────────────────────────────────────────────┘
34
- //
35
- // * Left-factor the parser grammar if the same initial part covers more than
36
- // one or two tokens. ANTLRs adaptive predication allows to write "natural"
37
- // rules, but slows down parsing, especially if a long lookahead is needed
38
- // to solve an LLk ambiguity. Therefore, try to avoid it in rules which are
39
- // called often. Unfortunately, we cannot use ANTLR3's grammar and subrule
40
- // option 'k' (lookahead depth) anymore... Therefore...
41
- // ┌─────────────────────────────────────────────────────────────────────────┐
42
- // Before each alternative with LL1 ambiguities (looking at the next token
43
- // is not enough for a decision), write a comment starting with `#ATN:`
44
- // which describes the ambiguity. Additionally, put a comment `/* #ATN n
45
- // */` INSIDE an (`@after`) action of a rule if the corresponding function
46
- // in '../gen/languageParser.js' contains `n` occurrences of
47
- // `adaptivePredict` calls. This is checked in 'test/testCompiler.js',
48
- // which also counts the total number of `adaptivePredict` occurrences.
49
- // └─────────────────────────────────────────────────────────────────────────┘
50
- //
51
- // * For fast parsing and lower memory consumption, we use ANTLR4 with SLL
52
- // prediction-mode only. That means that ANTLR does not use the actual call
53
- // stack when deciding which alternative to choose in a rule. You might
54
- // need to copy a rule manually to get less ambiguities - this might be a
55
- // good idea anyway to avoid calls to `adaptivePredict`, see the rules
56
- // starting with `annotationAssignment_`.
57
- //
58
- // * Factoring out a sub rule into a named rule influences the error recovery:
59
- // the parser tries to consume all tokens which are neither in the follow
60
- // set of loops and named rules. So be careful.
61
- //
62
- // * Do not use actions in the lexer. Examples: de-quote string literals not
63
- // in the lexer, but in the parser; do not throw errors, but produce error
64
- // tokens if necessary.
65
- //
66
- // * Use actions in the parser to produce a Augmented CSN model. To have it
67
- // also in the case of syntax errors, produce it by adding sub-nodes to a
68
- // parent node, not by returning the nodes (the latter is fine for secondary
69
- // attachments).
70
- //
71
- // * Action code should be a one-liner (<100 chars); usually, just one action
72
- // is called per alternative (plus the @after action which sets the AST
73
- // location). For more complicated code, define a method in file
74
- // './genericAntlrParser.js'.
75
- //
76
- // * Do not write lexer rules for tokens like ';', use ';' directly in the
77
- // parser rule. Advantage: better error messages; taste: more or less
78
- // readable grammar; disadvantage: debugging in generated code.
79
- //
80
- // * Use all-upper token names for keywords (e.g. CONTEXT), capitalized ones
81
- // (e.g. Number) for others - EOF is the exception (is ANTLR-builtin).
82
- // Remember: parser rule names in ANTLR start with a lower-case letter.
83
- //
84
- // * No useless parentheses in the grammar. There are just two binary grammar
85
- // operators: alternative (`|`) and sequence. It should not be too hard to
86
- // remember that sequence binds stronger than alternative.
87
- //
88
- // * Use the following indentation rules:
89
- // - rule header: indentation 0 + 2* parentheses/braces depth
90
- // - rule colon (':' separating header & body): 2
91
- // - rule body: 4 + 2* parentheses/braces depth, -2 for certain chars at
92
- // beginning of line: '|', ')', ']' or '}'
93
- // - inside action: as for the action language, e.g. function argument
94
- // alignment
95
- // - rule semicolon (';' ending body, before exceptions): 2
96
- // - rule exceptions (not used): 2 + 2* parentheses/braces depth
97
-
98
- // Some practical info:
99
- //
100
- // * The end location for the match of a rule is just available in the @after
101
- // action. Use method `attachLocation` there on the produced AST.
102
- //
103
- // * Be careful with the rule names: the methods in antlr4.Parser, the methods
104
- // in `./antlrParser' and the parser rule names share the same namespace.
105
- // Any shadowing lead to an exception when running 'test/testCompiler.js'.
106
- //
107
- // * Be careful with names for rule arguments, returns, locals and rule
108
- // reference labels: the names `parser`, `parent` and `invokingState` cannot
109
- // be used (these are added by the generator).
110
- //
111
- // * The ANTLR error "missing attribute access on rule reference c in $c" can
112
- // be solved with using $ctx.c instead of $c
113
- //
114
- // * If you want to set a property starting with '$' like $syntax, use
115
- // obj['$'+'syntax'] as the ANTLR tool would replace $syntax by $ctx.syntax
116
- //
117
- // * If you want to use Unicode characters, move the corresponding code to
118
- // ./genericAntlrParser.js; ANTLR or the TypeScript wrapper might destroy
119
- // Unicode characters on certain operating systems.
120
-
121
- grammar language;
122
- options {
123
- language = JavaScript;
124
- superClass = genericAntlrParser;
125
- }
126
- tokens {
127
- ELEMENT, // used with setLocalToken()
128
- MASKED, // used with setLocalToken()
129
- VIRTUAL, // used with setLocalToken()
130
- OVER, // used with setLocalTokenIfBefore()
131
- HelperToken1, // used with setLocalToken(), does not appear in messages
132
- HelperToken2, // used with setLocalToken(), does not appear in messages
133
- HideAlternatives, // hide alternative tokens (no token seq!)
134
- GenericExpr, // via token rewriting according to specialFunctions
135
- GenericSeparator, // via token rewriting according to specialFunctions
136
- GenericIntro, // via token rewriting according to specialFunctions
137
- DOTbeforeBRACE, // via token rewrite
138
- COMPOSITIONofBRACE // via token rewrite in rule typeAssociationBase
139
- }
140
-
141
- // Content:
142
- // - top-level: USING, NAMESPACE, artifactDefOrExtend (start rule: start)
143
- // - main definitions and annotation def
144
- // - member definitions
145
- // - EXTEND and ANNOTATE
146
- // - type expressions
147
- // - queries: the main query hierarchy (start rule: queryEOF)
148
- // - queries: columns and other clauses
149
- // - conditions and expressions (start rule: conditionEOF)
150
- // - paths and functions
151
- // - annotation assignments
152
- // - literal values and identifiers
153
- // - Lexer: spaces, literal values, reserved keywords, unreserved keywords, identifier
154
-
155
- // Top-Level -----------------------------------------------------------------
156
-
157
- start returns [ source ] locals [ _sync = 'recover' ]
158
- @init{ $source = this.createSource(); }
159
- :
160
- usingDeclaration[$source]*
161
- (
162
- namespaceDeclaration[$source]
163
- ( usingDeclaration[$source] | artifactDefOrExtend[$source] )*
164
- |
165
- artifactDefOrExtend[$source]
166
- ( usingDeclaration[$source] | artifactDefOrExtend[$source] )*
167
- )?
168
- EOF
169
- ;
170
-
171
- namespaceDeclaration[ source ] locals[ decl = {} ]
172
- @after { $source.namespace = this.attachLocation($decl); }
173
- :
174
- NAMESPACE simplePath[ $decl, 'Namespace' ] ';'
175
- ;
176
-
177
- usingDeclaration[ source ] locals[ decl = {} ]
178
- @after { this.attachLocation($decl); }
179
- :
180
- { $decl.location = this.startLocation(); }
181
- USING
182
- (
183
- FROM str=String
184
- { $source.dependencies.push( this.quotedLiteral( $str, 'string' ) ); }
185
- |
186
- usingProxy[ $source, $decl ]
187
- ( FROM str=String
188
- { $source.dependencies.push( $decl.fileDep = this.quotedLiteral( $str, 'string' ) ); }
189
- )?
190
- |
191
- { this.addItem( $decl, $source, 'usings', 'using' ); }
192
- // We could just create "independent" USING declaration, but if we want
193
- // to have some check in the future whether the external artifacts are
194
- // really in the FROM source...
195
- '{' { $decl.usings = this.createArray(); }
196
- usingProxy[ $decl, {} ]
197
- ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
198
- usingProxy[ $decl, {} ] )*
199
- '}' { this.finalizeDictOrArray( $decl.usings ); }
200
- ( FROM str=String
201
- { $source.dependencies.push( $decl.fileDep = this.quotedLiteral( $str, 'string' ) ); }
202
- )?
203
- )
204
- ';'
205
- ;
206
-
207
- usingProxy[ outer, proxy ]
208
- @after { this.attachLocation($proxy); }
209
- :
210
- { if (!$proxy.location) $proxy.location = this.startLocation();
211
- $proxy.extern = {}; }
212
- simplePath[ $proxy.extern, 'global' ]
213
- { this.addItem( $proxy, $outer, 'usings', 'using' ); }
214
- ( AS name=ident['Using'] { $proxy.name = $name.id; }
215
- | { this.classifyImplicitName( 'Using' ); }
216
- )
217
- ;
218
-
219
- artifactDefOrExtend[ outer, defOnly = false ] locals[ art = new parser.XsnArtifact() ] // cannot use `parent` as parameter name!
220
- @after{ /* #ATN 1 */ }
221
- :
222
- { $art.location = this.startLocation(); this.docComment( $art ); }
223
- annotationAssignment_ll1[ $art ]*
224
- (
225
- DEFINE?
226
- ( serviceDef[ $art, $outer, $defOnly ]
227
- | contextDef[ $art, $outer, $defOnly ]
228
- | entityDef[ $art, $outer ]
229
- | typeDef[ $art, $outer ]
230
- | aspectDef[ $art, $outer ]
231
- | annotationDef[ $art, $outer ]
232
- | viewDef[ $art, $outer ]
233
- | eventDef[ $art, $outer ]
234
- | actionFunctionMainDef[ $art, $outer ]
235
- )
236
- |
237
- extend=EXTEND
238
- { this.reportUnexpectedExtension( $defOnly, $extend );
239
- if (!$outer.extensions) $outer.extensions = [];
240
- }
241
- // #ATN: EXTEND art, while CONTEXT, ENTITY etc are not reserved
242
- ( extendService[ $art, $outer ]
243
- | extendContext[ $art, $outer ]
244
- | extendEntityOrAspect[ $art, $outer ] // or aspect
245
- | extendProjection[ $art, $outer ]
246
- | extendType[ $art, $outer ]
247
- // Streamlined Syntax; we won't add more kinds of the non-streamlined variants:
248
- | extendArtifact[ $art, $outer ]
249
- )
250
- |
251
- annotate=ANNOTATE
252
- { this.reportUnexpectedExtension( $defOnly, $annotate );
253
- if (!$outer.extensions) $outer.extensions = [];
254
- this.meltKeywordToIdentifier();
255
- }
256
- annotateArtifact[ $art, $outer ] // not kind-specific
257
- )
258
- ;
259
-
260
- optArtifactsBlock[ art, defOnly = false ]
261
- @after { this.attachLocation( $art ); }
262
- :
263
- { this.docComment( $art ); }
264
- annotationAssignment_fix[ $art ]*
265
- (
266
- '{' { $art.artifacts = this.createDict(); $art.extensions = []; }
267
- artifactDefOrExtend[ $art, defOnly ]*
268
- '}' { this.finalizeDictOrArray( $art.artifacts ); }
269
- optionalSemi
270
- |
271
- requiredSemi
272
- )
273
- ;
274
-
275
- requiredSemi
276
- : ';'
277
- | { return $ctx; } // do not actually parse the closing brace
278
- '}'
279
- ;
280
-
281
- optionalSemi
282
- : { this.noAssignmentInSameLine(); } // issue warning for } @Anno \n? NextDef
283
- ';'?
284
- ;
285
-
286
- // Annotation def and main definitions ------------------------------------------
287
-
288
- annotationDef[ art, outer ] locals[ name = new parser.XsnName() ]
289
- @after { this.attachLocation( $art ); }
290
- :
291
- annotation=ANNOTATION simplePath[ $name, 'AnnoDef' ]
292
- { if ($outer.kind !== 'source') { // this is a syntax restriction to avoid confusion
293
- this.error( 'syntax-unexpected-vocabulary', $annotation, { '#': $outer.kind } );
294
- $art = new this.XsnArtifact(); }
295
- else {
296
- if (!$outer.vocabularies) $outer.vocabularies = Object.create(null);
297
- this.addDef( $art, $outer, 'vocabularies', 'annotation', $name );
298
- }
299
- this.docComment( $art ); }
300
- annotationAssignment_fix[ $art ]*
301
- typeSpecSemi[ $art ] // also 'includes'...
302
- ;
303
-
304
- serviceDef[ art, outer, defOnly = false ] locals[ name = new parser.XsnName(); ]
305
- @after { this.attachLocation( $art ); }
306
- :
307
- SERVICE simplePath[ $name, 'Service' ]
308
- { this.addDef( $art, $outer, 'artifacts', 'service', $name ); }
309
- optArtifactsBlock[ $art, defOnly ]
310
- ;
311
-
312
- contextDef[ art, outer, defOnly = false ] locals[ name = new parser.XsnName(); ]
313
- @after { this.attachLocation( $art ); }
314
- :
315
- CONTEXT simplePath[ $name, 'Context' ]
316
- { this.addDef( $art, $outer, 'artifacts', 'context', $name ); }
317
- optArtifactsBlock[ $art, defOnly ]
318
- ;
319
-
320
- eventDef[ art, outer ] locals[ name = new parser.XsnName(); ]
321
- @after { /* #ATN 1 */ this.attachLocation( $art ); }
322
- :
323
- EVENT simplePath[ $name, 'Event' ]
324
- { this.addDef( $art, $outer, 'artifacts', 'event', $name );
325
- this.docComment( $art ); }
326
- annotationAssignment_fix[ $art ]*
327
- (
328
- typeStruct[ $art ] optionalSemi
329
- |
330
- ':'
331
- // #ATN: includeRef can be / start with PROJECTION
332
- (
333
- { $art.type = {}; }
334
- simplePath[ $art.type, 'artref' ]
335
- (
336
- { $art.includes = [ $art.type ]; delete $art.type; }
337
- ( ',' { if (this.isStraightBefore('{')) break; } // allow ',' before '{' // }}
338
- includeRef[ $art ]
339
- )*
340
- typeStruct[ $art ] optionalSemi
341
- |
342
- { this.docComment( $art ); }
343
- annotationAssignment_ll1[ $art ]*
344
- requiredSemi
345
- )
346
- |
347
- typeStruct[ $art ] optionalSemi
348
- |
349
- qp=projectionSpec
350
- { $art.query = $qp.query; $art['$'+'syntax'] = 'projection'; }
351
- optionalSemi // TODO: not fully correct without columns or excluding
352
- )
353
- )
354
- ;
355
-
356
- viewDef[ art, outer ] locals[ name = new parser.XsnName(); ]
357
- @after { this.attachLocation( $art ); }
358
- :
359
- v=VIEW simplePath[ $name, 'Entity' ]
360
- { $art['$'+'syntax'] = 'view';
361
- this.addDef( $art, $outer, 'artifacts', 'entity', $name );
362
- this.docComment( $art ); }
363
- annotationAssignment_fix[ $art ]*
364
- (
365
- parameterListDef[ $art ]
366
- |
367
- // TODO: warning deprecated?
368
- ( HideAlternatives | WITH ) { $art.params = this.createDict(); }
369
- PARAMETERS
370
- parameterDef[ $art ]
371
- ( ',' parameterDef[ $art ] )* // no optional final ',' here
372
- { this.finalizeDictOrArray( $art.params ); }
373
- )?
374
- AS qe=queryExpression { $art.query = $qe.query; }
375
- // TODO check ANTLR: bad msg with 'view V as'<eof> but 'view V as FOO' is fine
376
- requiredSemi
377
- ;
378
-
379
- entityDef[ art, outer ] locals[ name = new parser.XsnName() ]
380
- @after { this.attachLocation( $art ); }
381
- :
382
- ENTITY simplePath[ $name, 'Entity' ]
383
- { this.addDef( $art, $outer, 'artifacts', 'entity', $name );
384
- this.docComment( $art ); }
385
- annotationAssignment_fix[ $art ]*
386
- parameterListDef[ $art ]?
387
- (
388
- ( ':'
389
- includeRef[ $art ]
390
- ( ',' { if (this.isStraightBefore('{')) break; } // allow ',' before '{' // }}
391
- includeRef[ $art ]
392
- )*
393
- )?
394
- '{' { $art.elements = this.createDict(); }
395
- elementDef[ $art ]*
396
- '}' { this.finalizeDictOrArray( $art.elements ); }
397
- (
398
- ACTIONS '{' { $art.actions = this.createDict(); }
399
- actionFunctionDef[ $art ]*
400
- '}' { this.finalizeDictOrArray( $art.actions ); }
401
- )?
402
- optionalSemi
403
- |
404
- AS
405
- ( qe=queryExpression
406
- { $art.query = $qe.query; $art['$'+'syntax'] = 'entity' }
407
- (
408
- ACTIONS '{' { $art.actions = this.createDict(); }
409
- actionFunctionDef[ $art ]*
410
- '}' { this.finalizeDictOrArray( $art.actions ); }
411
- optionalSemi
412
- | requiredSemi
413
- )
414
- | qp=projectionSpec
415
- { $art.query = $qp.query; $art['$'+'syntax'] = 'projection'; }
416
- projectionClauses[ $qp.query ]
417
- (
418
- ACTIONS '{' { $art.actions = this.createDict(); }
419
- actionFunctionDef[ $art ]*
420
- '}' { this.finalizeDictOrArray( $art.actions ); }
421
- )?
422
- optionalSemi // TODO: not fully correct without columns or excluding
423
- )
424
- )
425
- ;
426
-
427
- aspectDef[ art, outer ] locals[ name = new parser.XsnName() ]
428
- @after { this.attachLocation( $art ); }
429
- :
430
- ( ASPECT | ( abs=ABSTRACT | HideAlternatives ) ent=ENTITY )
431
- simplePath[ $name, 'Type' ]
432
- { this.addDef( $art, $outer, 'artifacts', 'aspect', $name );
433
- if ($ent)
434
- this.warning( 'syntax-deprecated-abstract', this.tokenLocation( $abs, $ent ) );
435
- this.docComment( $art ); }
436
- annotationAssignment_fix[ $art ]*
437
- ( ':'
438
- (
439
- includeRef[ $art ]
440
- ( ',' { if (this.isStraightBefore('{')) break; } // allow ',' before '{' // }}
441
- includeRef[ $art ]
442
- )*
443
- )?
444
- )?
445
- ( // `aspect MyAspect {};`
446
- '{' { $art.elements = this.createDict(); }
447
- ( elementDef[ $art ]* )
448
- '}' { this.finalizeDictOrArray( $art.elements ); }
449
- (
450
- ACTIONS '{' { $art.actions = this.createDict(); }
451
- actionFunctionDef[ $art ]*
452
- '}' { this.finalizeDictOrArray( $art.actions ); }
453
- )?
454
- optionalSemi
455
- | // `aspect MyAspect;`, e.g. for annotation aspects.
456
- requiredSemi
457
- )
458
- ;
459
-
460
- typeDef[ art, outer ] locals[ name = new parser.XsnName() ]
461
- @after { this.attachLocation( $art ); }
462
- :
463
- TYPE simplePath[ $name, 'Type' ]
464
- { this.addDef( $art, $outer, 'artifacts', 'type', $name );
465
- this.docComment( $art ); }
466
- annotationAssignment_fix[ $art ]*
467
- typeSpecSemi[ $art ]
468
- ;
469
-
470
- actionFunctionMainDef[ art, outer ] locals[ name = new parser.XsnName() ]
471
- @after { this.attachLocation( $art ); }
472
- :
473
- ACTION simplePath[ $name, 'Action' ]
474
- { this.addDef( $art, $outer, 'artifacts', 'action', $name );
475
- this.docComment( $art ); }
476
- annotationAssignment_fix[ $art ]*
477
- parameterListDef[ $art ]
478
- ( returnTypeSpec[ $art ] | requiredSemi )
479
- |
480
- FUNCTION simplePath[ $name, 'Action' ]
481
- { this.addDef( $art, $outer, 'artifacts', 'function', $name );
482
- this.docComment( $art ); }
483
- annotationAssignment_fix[ $art ]*
484
- parameterListDef[ $art ]
485
- returnTypeSpec[ $art ]
486
- ;
487
-
488
- // Member definitions: actions, elements, enums, parameters: --------------------
489
-
490
- actionFunctionDef[ outer ] locals[ art = new parser.XsnArtifact() ]
491
- @after { this.attachLocation( $art ); }
492
- :
493
- { $art.location = this.startLocation();; this.docComment( $art ); }
494
- annotationAssignment_ll1[ $art ]*
495
- (
496
- ACTION name=ident['BoundAction']
497
- { this.addDef( $art, $outer, 'actions', 'action', $name.id );
498
- this.docComment( $art ); }
499
- annotationAssignment_fix[ $art ]*
500
- parameterListDef[ $art ]
501
- ( returnTypeSpec[ $art ] | requiredSemi )
502
- |
503
- FUNCTION name=ident['BoundAction']
504
- { this.addDef( $art, $outer, 'actions', 'function', $name.id );
505
- this.docComment( $art ); }
506
- annotationAssignment_fix[ $art ]*
507
- parameterListDef[ $art ]
508
- returnTypeSpec[ $art ]
509
- )
510
- ;
511
-
512
- parameterDef[ outer ] locals[ art = new parser.XsnArtifact() ]
513
- @after { this.attachLocation( $art ); }
514
- :
515
- { this.meltKeywordToIdentifier();; this.docComment( $art ); }
516
- ( annotationAssignment_ll1[ $art ]
517
- { this.meltKeywordToIdentifier(); }
518
- )*
519
- name=ident['Param']
520
- { this.addDef( $art, $outer, 'params', 'param', $name.id );
521
- this.docComment( $art ); }
522
- annotationAssignment_fix[ $art ]*
523
- typeSpec[ $art ]
524
- defaultValue[ $art ]?
525
- { this.docComment( $art ); }
526
- annotationAssignment_ll1[ $art ]*
527
- ;
528
-
529
- parameterListDef[ art ]
530
- :
531
- '(' { $art.params = this.createDict(); }
532
- // also empty param list (we might do some hacking later to allow reserved words)
533
- // see annotationAssignment_paren
534
- {
535
- if (this.isStraightBefore(')')) {
536
- this.matchWildcard(); // we know it is the ')' - we do not reach the final match
537
- this.finalizeDictOrArray( $art.params );
538
- return $ctx;
539
- }
540
- }
541
- parameterDef[ $art ]
542
- ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
543
- parameterDef[ $art ]
544
- )*
545
- ')' { this.finalizeDictOrArray( $art.params ); }
546
- ;
547
-
548
- enumSymbolDef[ outer ] locals[ art = new parser.XsnArtifact() ]
549
- @after { this.attachLocation( $art ); }
550
- :
551
- { $art.location = this.startLocation();; this.docComment( $art ); }
552
- annotationAssignment_ll1[ $art ]*
553
- name=ident['Enum']
554
- { this.addDef( $art, $outer, 'enum', 'enum', $name.id );
555
- this.docComment( $art ); }
556
- annotationAssignment_ll1[ $art ]*
557
- ( '='
558
- { this.excludeExpected( ['Boolean', 'QuotedLiteral', "'#'", 'NULL'] ); }
559
- (
560
- val=literalValue
561
- { $art.value = $val.val; }
562
- |
563
- ( plus='+' | min='-' ) num=Number
564
- { $art.value = this.numberLiteral( $num, $plus||$min ); }
565
- )
566
- { this.docComment( $art ); }
567
- annotationAssignment_ll1[ $art ]*
568
- )?
569
- requiredSemi
570
- ;
571
-
572
- elementDef[ outer ] locals[ $art = new parser.XsnArtifact() ]
573
- :
574
- { $art.location = this.startLocation();; this.docComment( $art ); }
575
- annotationAssignment_ll1[ $art ]*
576
- elementDefInner[ $art, $outer ]
577
- ;
578
-
579
- elementDefInner[ art, outer, explicitElement = false ]
580
- @after{ this.attachLocation( $art ); }
581
- :
582
- // VIRTUAL is keyword, except if before the following tokens texts:
583
- { this.setLocalToken( 'VIRTUAL', 'VIRTUAL', /^[;:{@=}]$/ ); }
584
- ( virtual=VIRTUAL { $art.virtual = this.valueWithTokenLocation( true, $virtual ); } )?
585
- ( key=KEY { $art.key = this.valueWithTokenLocation( true, $key ); } )?
586
- { this.setLocalToken( 'MASKED', 'MASKED', /^[;:{@=}]$/ ); }
587
- ( masked=MASKED
588
- {
589
- $art.masked = this.valueWithTokenLocation( true, $masked ) ;
590
- this.message( 'syntax-unsupported-masked', $masked, { keyword: 'masked' } );
591
- }
592
- )?
593
- { this.setLocalToken( 'ELEMENT', 'ELEMENT', /^[;:{@=}]$/ ); }
594
- ( ELEMENT { $explicitElement = true; } )? // auto-recognizable at other places
595
- name=ident['Element']
596
- { this.addDef( $art, $outer, 'elements', 'element', $name.id );
597
- this.docComment( $art ); }
598
- annotationAssignment_fix[ $art ]*
599
- (
600
- typeStruct[ $art ]
601
- ( nullability[ $art ]
602
- requiredSemi
603
- | optionalSemi // NOT and NULL are reserved...
604
- )
605
- |
606
- ':'
607
- elementType[ $art ]
608
- |
609
- eq='=' e=expression // SQL has syntax variant using AS - we DO NOT
610
- stored=STORED?
611
- { $art.value = $e.expr;
612
- // this.setIntroLocation( eq ); -- future
613
- if ($stored)
614
- $art.value.stored = this.valueWithTokenLocation( true, $stored );
615
- if ($explicitElement)
616
- $art['$'+'syntax'] = 'element';
617
- }
618
- { this.docComment( $art ); }
619
- annotationAssignment_ll1[ $art ]* // for enum symbol def via EXTEND
620
- requiredSemi
621
- |
622
- requiredSemi
623
- )
624
- ;
625
-
626
- elementType[ art ] // TODO: split this monster rule
627
- @after{ /* #ATN 3 */ this.attachLocation( $art ); }
628
- :
629
- // #ATN: referenced type name can be ASSOCIATION or ARRAY or TYPE or LOCALIZED
630
- typeStruct[ $art ]
631
- nullability[ $art ]?
632
- requiredSemi
633
- |
634
- typeAssociationBase[ $art, true ]
635
- // #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
636
- (
637
- typeStruct[ $art.target, true ] optionalSemi
638
- |
639
- one=ONE
640
- { this.setMaxCardinality( $art, this.numberLiteral( $one, null, '1' ) ); }
641
- typeCompoStruct[ $art.target ] optionalSemi
642
- |
643
- many=MANY
644
- { this.setMaxCardinality( $art, { literal: 'string', val: '*' }, $many ); }
645
- typeCompoStruct[ $art.target ] optionalSemi
646
- |
647
- // we do not support `Composition of many { e }` - ambiguity ad-hoc target versus foreign keys!
648
- typeToMany[ $art ] typeAssociationElementCont[ $art ]
649
- |
650
- typeToOne[ $art ] typeAssociationElementCont[ $art ]
651
- |
652
- simplePath[ $art.target, 'artref' ] typeAssociationElementCont[ $art ]
653
- )
654
- |
655
- (
656
- array=ARRAY of=OF
657
- { $art.items = { location: this.tokenLocation( $array, $of ) }; }
658
- | many=MANY
659
- { $art.items = { location: this.tokenLocation( $many ) };}
660
- )
661
- ( typeStruct[ $art.items ]
662
- nullability[ $art.items ]?
663
- | // #ATN: typeRefOptArgs/typeTypeOf can start with TYPE
664
- ( typeTypeOf[ $art.items ] | typeRefOptArgs[ $art.items ] )
665
- nullability[ $art.items ]?
666
- { this.docComment( $art ); }
667
- annotationAssignment_ll1[ $art ]*
668
- (
669
- ENUM '{' { $art.items.enum = this.createDict(); }
670
- enumSymbolDef[ $art.items ]*
671
- '}' { this.finalizeDictOrArray( $art.items.enum ); }
672
- nullability[ $art.items ]?
673
- )?
674
- )
675
- requiredSemi // also req after struct/enum
676
- |
677
- l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
678
- typeRefOptArgs[ $art ]
679
- optInvisibleNullability[ $art ]
680
- { this.docComment( $art ); }
681
- annotationAssignment_ll1[ $art ]*
682
- ( elementProperties[ $art ]
683
- { this.docComment( $art ); }
684
- annotationAssignment_ll1[ $art ]*
685
- )?
686
- requiredSemi
687
- |
688
- typeTypeOf[ $art ] // Note: Same as the typeRefOptArgs rule below
689
- optInvisibleNullability[ $art ]
690
- { this.docComment( $art ); }
691
- annotationAssignment_ll1[ $art ]*
692
- (
693
- ENUM '{' { $art.enum = this.createDict(); }
694
- enumSymbolDef[ $art ]*
695
- '}' { this.finalizeDictOrArray( $art.enum ); }
696
- elementProperties[ $art ]?
697
- |
698
- elementProperties[ $art ]
699
- { this.docComment( $art ); }
700
- annotationAssignment_ll1[ $art ]*
701
- )?
702
- requiredSemi // also req after foreign key spec
703
- |
704
- typeRefOptArgs[ $art ] // Note: Same as the typeTypeOf rule above
705
- optInvisibleNullability[ $art ]
706
- { this.docComment( $art ); }
707
- annotationAssignment_ll1[ $art ]*
708
- (
709
- ENUM '{' { $art.enum = this.createDict(); }
710
- enumSymbolDef[ $art ]*
711
- '}' { this.finalizeDictOrArray( $art.enum ); }
712
- elementProperties[ $art ]?
713
- |
714
- elementProperties[ $art ]
715
- { this.docComment( $art ); }
716
- annotationAssignment_ll1[ $art ]*
717
- )?
718
- requiredSemi // also req after enum spec
719
- ;
720
-
721
- elementProperties[ elem ]
722
- :
723
- defaultAndNullability[ $elem ]
724
- |
725
- '=' e=expression
726
- stored=STORED?
727
- { $elem.value = $e.expr;
728
- if ($stored)
729
- $elem.value.stored = this.valueWithTokenLocation( true, $stored );
730
- }
731
- ;
732
-
733
- defaultAndNullability[ elem ]
734
- :
735
- defaultValue[ $elem ]
736
- nullability[ $elem ]? // placement accoring to SQL spec
737
- |
738
- nullability[ $elem ]
739
- defaultValue[ $elem ]?
740
- ;
741
-
742
- defaultValue[ art ] locals[ elem, elements = {} ]
743
- :
744
- // TODO: We may support structured default values here.
745
- DEFAULT expr=expression { $art.default = $expr.expr; }
746
- ;
747
-
748
- // Extend and annotate ----------------------------------------------------------
749
-
750
- extendArtifact[ art, outer ] locals[ name = new parser.XsnName(), elemName = new parser.XsnName() ]
751
- @after{ /* #ATN 1 */ this.attachLocation( $art ); }
752
- :
753
- simplePath[ $name, 'Extend' ]
754
- (
755
- ':' simplePath[ $elemName, 'Element']
756
- { this.addExtension( $art, $outer, 'extend', $name, $elemName.path ); }
757
- extendWithOptElementsOrType[ art ]
758
- |
759
- { this.addExtension( $art, $outer, 'extend', $name ); }
760
- extendWithOptElementsNoWith[ art ]
761
- |
762
- { this.addExtension( $art, $outer, 'extend', $name ); }
763
- WITH { this.noSemicolonHere(); }
764
- { this.docComment( $art ); }
765
- annotationAssignment_ll1[ $art ]*
766
- // #ATN: ELEMENTS, ENUM, DEFINITIONS, COLUMNS, ACTIONS are not reserved and
767
- // could be includeRef
768
- (
769
- // all the alternatives from `extendWithOptElementsOrType` --------------
770
- '{' { $art.elements = this.createDict(); }
771
- elementDefOrExtend[ $art ]*
772
- '}' { this.finalizeDictOrArray( $art.elements ); }
773
- { this.checkExtensionDict( $art.elements ); }
774
- optionalSemi
775
- |
776
- requiredSemi
777
- |
778
- ELEMENTS { $art.elements = this.createDict(); } '{'
779
- elementDefOrExtend[ $art, true ]*
780
- '}' { this.finalizeDictOrArray( $art.elements ); }
781
- { this.checkExtensionDict( $art.elements ); }
782
- optionalSemi
783
- |
784
- ENUM { $art.enum = this.createDict(); } '{'
785
- enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
786
- '}' { this.finalizeDictOrArray( $art.enum ); }
787
- optionalSemi
788
- |
789
- // extend Art with (length: 10);
790
- // `with` is required, or we could have `extend String(length:10);`.
791
- // future `extend Action with (param: Type)` now has ambiguity
792
- typeNamedArgList[ $art ]
793
- requiredSemi
794
- |
795
- // extension alternatives for main definitions --------------------------
796
- includeRef[ $art ] ( ',' includeRef[ $art ] )*
797
- requiredSemi
798
- |
799
- DEFINITIONS { $art.artifacts = this.createDict(); } '{'
800
- artifactDefOrExtend[ $art, 'definitions' ]*
801
- '}' { this.finalizeDictOrArray( $art.artifacts ); }
802
- optionalSemi
803
- |
804
- COLUMNS { $art.columns = this.createArray(); } '{'
805
- (
806
- selectItemDef[ $art.columns ]
807
- ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
808
- selectItemDef[ $art.columns ]
809
- )*
810
- )?
811
- '}' { this.finalizeDictOrArray( $art.columns ); }
812
- optionalSemi
813
- |
814
- ACTIONS { $art.actions = this.createDict(); } '{'
815
- actionFunctionDef[ $art ]* // TODO: no EXTEND in actions? (ok, would just allow annos)
816
- '}' { this.finalizeDictOrArray( $art.actions ); }
817
- optionalSemi
818
- )
819
- // TODO: what about adding both ELEMENTS and ACTIONS? (TODO: csn input test & to-cdl)
820
- )
821
- ;
822
-
823
- extendService[ art, outer ] locals[ name = new parser.XsnName() ]
824
- @after { this.attachLocation( $art ); }
825
- :
826
- SERVICE { $art.expectedKind = this.valueWithTokenLocation(); }
827
- simplePath[ $name, 'Service' ]
828
- { $art.name = $name; this.addItem( $art, $outer, 'extensions', 'extend' ); }
829
- ( WITH { this.noSemicolonHere(); } )?
830
- optArtifactsBlock[ art, 'service' ]
831
- ;
832
-
833
- extendContext[ art, outer ] locals[ name = new parser.XsnName() ]
834
- @after { this.attachLocation( $art ); }
835
- :
836
- CONTEXT { $art.expectedKind = this.valueWithTokenLocation(); }
837
- simplePath[ $name, 'Context' ]
838
- { $art.name = $name; this.addItem( $art, $outer, 'extensions', 'extend' ); }
839
- ( WITH { this.noSemicolonHere(); } )?
840
- optArtifactsBlock[ art, 'context' ]
841
- ;
842
-
843
- extendEntityOrAspect[ art, outer ] locals[ name = new parser.XsnName() ]
844
- @after { /* #ATN 1 */ this.attachLocation( $art ); }
845
- :
846
- (ASPECT | ENTITY) { $art.expectedKind = this.valueWithTokenLocation(); }
847
- simplePath[ $name, 'Extend' ]
848
- { $art.name = $name;
849
- this.addItem( $art, $outer, 'extensions', 'extend' );
850
- }
851
- (
852
- WITH { this.noSemicolonHere(); this.docComment( $art ); }
853
- annotationAssignment_ll1[ $art ]*
854
- // ATN: the ref can start with ACTIONS
855
- (
856
- includeRef[ $art ] ( ',' includeRef[ $art ] )*
857
- requiredSemi
858
- |
859
- extendForEntity[ $art ]
860
- )
861
- |
862
- { this.docComment( $art ); }
863
- annotationAssignment_ll1[ $art ]*
864
- extendForEntity[ $art ]
865
- )
866
- ;
867
-
868
- extendForEntity[ art ]
869
- :
870
- '{' { $art.elements = this.createDict(); }
871
- elementDefOrExtend[ $art ]*
872
- '}' { this.finalizeDictOrArray( $art.elements ); }
873
- (
874
- ACTIONS { $art.actions = this.createDict(); } '{'
875
- actionFunctionDef[ $art ]*
876
- '}' { this.finalizeDictOrArray( $art.actions ); }
877
- )?
878
- optionalSemi
879
- |
880
- ACTIONS { $art.actions = this.createDict(); } '{'
881
- actionFunctionDef[ $art ]*
882
- '}' { this.finalizeDictOrArray( $art.actions ); }
883
- optionalSemi
884
- |
885
- requiredSemi
886
- ;
887
-
888
- extendProjection[ art, outer ] locals[ name = new parser.XsnName() ]
889
- @after { this.attachLocation( $art ); }
890
- :
891
- PROJECTION { $art.expectedKind = this.valueWithTokenLocation( 'entity' ); }
892
- simplePath[ $name, 'Extend' ]
893
- { $art.name = $name;
894
- this.addItem( $art, $outer, 'extensions', 'extend' );
895
- }
896
- ( WITH { this.noSemicolonHere(); } )?
897
- { this.docComment( $art ); }
898
- annotationAssignment_ll1[ $art ]*
899
- (
900
- '{' { $art.columns = this.createArray(); }
901
- (
902
- selectItemDef[ $art.columns ]
903
- ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
904
- selectItemDef[ $art.columns ]
905
- )*
906
- )?
907
- '}' { this.finalizeDictOrArray( $art.columns ); }
908
- (
909
- ACTIONS { $art.actions = this.createDict(); } '{'
910
- actionFunctionDef[ $art ]*
911
- '}' { this.finalizeDictOrArray( $art.actions ); }
912
- )?
913
- optionalSemi
914
- |
915
- ACTIONS { $art.actions = this.createDict(); } '{'
916
- actionFunctionDef[ $art ]*
917
- '}' { this.finalizeDictOrArray( $art.actions ); }
918
- optionalSemi
919
- |
920
- requiredSemi
921
- )
922
- ;
923
-
924
- extendType[ art, outer ] locals[ name = new parser.XsnName() ]
925
- @after { this.attachLocation( $art ); }
926
- :
927
- TYPE { $art.expectedKind = this.valueWithTokenLocation(); }
928
- simplePath[ $name, 'Extend' ]
929
- { $art.name = $name;
930
- this.addItem( $art, $outer, 'extensions', 'extend' );
931
- }
932
- // extendWithOptElementsOrType + includeRef:
933
- (
934
- extendWithOptElementsNoWith[ art ]
935
- |
936
- WITH { this.noSemicolonHere(); this.docComment( $art ); }
937
- annotationAssignment_ll1[ $art ]*
938
- (
939
- '{' { $art.elements = this.createDict(); }
940
- elementDefOrExtend[ $art ]*
941
- '}' { this.finalizeDictOrArray( $art.elements ); }
942
- { this.checkExtensionDict( $art.elements ); }
943
- optionalSemi
944
- |
945
- // extend type Art with (length: 10);
946
- typeNamedArgList[ $art ]
947
- requiredSemi
948
- |
949
- requiredSemi
950
- |
951
- includeRef[ $art ] ( ',' includeRef[ $art ] )*
952
- requiredSemi
953
- )
954
- )
955
- ;
956
-
957
- extendWithOptElementsOrType[ art ]
958
- :
959
- extendWithOptElementsNoWith[ art ]
960
- |
961
- WITH { this.noSemicolonHere(); this.docComment( $art ); }
962
- annotationAssignment_ll1[ $art ]*
963
- (
964
- '{' { $art.elements = this.createDict(); }
965
- elementDefOrExtend[ $art ]*
966
- '}' { this.finalizeDictOrArray( $art.elements ); }
967
- { this.checkExtensionDict( $art.elements ); }
968
- optionalSemi
969
- |
970
- ELEMENTS { $art.elements = this.createDict(); } '{'
971
- elementDefOrExtend[ $art, true ]*
972
- '}' { this.finalizeDictOrArray( $art.elements ); }
973
- { this.checkExtensionDict( $art.elements ); }
974
- optionalSemi
975
- |
976
- ENUM { $art.enum = this.createDict(); } '{'
977
- enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
978
- '}' { this.finalizeDictOrArray( $art.enum ); }
979
- optionalSemi
980
- |
981
- // extend type|element Art with (length: 10);
982
- typeNamedArgList[ $art ]
983
- requiredSemi
984
- |
985
- requiredSemi
986
- )
987
- ;
988
-
989
- extendWithOptElementsNoWith[ art ]
990
- :
991
- { this.docComment( $art ); }
992
- annotationAssignment_ll1[ $art ]*
993
- (
994
- '{' { $art.elements = this.createDict(); }
995
- elementDefOrExtend[ $art ]*
996
- '}' { this.finalizeDictOrArray( $art.elements ); }
997
- { this.checkExtensionDict( $art.elements ); }
998
- optionalSemi
999
- |
1000
- requiredSemi
1001
- )
1002
- ;
1003
-
1004
- // For `extend … with elements` or `extend entity … with`, `extend aspect … with`,
1005
- // i.e. definitions in { … } are never enums
1006
- elementDefOrExtend[ outer, explicitElement = false ] locals[ art = new parser.XsnArtifact() ]
1007
- @after { /* #ATN 1 */ } // if ($art) this.attachLocation( $art ); }
1008
- :
1009
- { $art.location = this.startLocation();; this.docComment( $art ); }
1010
- annotationAssignment_ll1[ $art ]*
1011
- // #ATN: element name for definition can be EXTEND
1012
- (
1013
- EXTEND
1014
- extendElement[ $art, $outer ]
1015
- |
1016
- elementDefInner[ $art, $outer, $explicitElement ]
1017
- )
1018
- ;
1019
-
1020
- extendElement[ art, outer ]
1021
- @after{ this.attachLocation( $art ); }
1022
- :
1023
- { this.setLocalToken( 'ELEMENT', 'ELEMENT', /^([:{@=}()]|WITH)$/i ); }
1024
- ( ELEMENT { $art.expectedKind = this.valueWithTokenLocation(); } )?
1025
- name=ident['Element']
1026
- { this.addDef( $art, $outer, 'elements', 'extend', $name.id ); }
1027
- extendWithOptElementsOrType[ $art, $art ]
1028
- ;
1029
-
1030
- annotateArtifact[ art, outer ] locals[ name = new parser.XsnName(), elemName = new parser.XsnName() ]
1031
- @after { this.attachLocation( $art ); }
1032
- :
1033
- simplePath[ $name, 'Annotate' ]
1034
- ( // Element annotation
1035
- ':' simplePath[ $elemName, 'Element']
1036
- { this.addExtension( $art, $outer, 'annotate', $name, $elemName.path ); }
1037
- ( WITH { this.noSemicolonHere(); } )?
1038
- { this.docComment( $art ); }
1039
- annotationAssignment_ll1[ $art ]*
1040
- (
1041
- '{' { $art.elements = this.createDict(); }
1042
- annotateElement[ $art ]*
1043
- '}' { this.finalizeDictOrArray( $art.elements ); }
1044
- { this.checkExtensionDict( $art.elements ); }
1045
- optionalSemi
1046
- | requiredSemi
1047
- )
1048
- | // Definition annotation
1049
- { this.addExtension( $art, $outer, 'annotate', $name ); }
1050
- ( WITH { this.noSemicolonHere(); } )?
1051
- { this.docComment( $art ); }
1052
- annotationAssignment_ll1[ $art ]*
1053
- (
1054
- '{' { $art.elements = this.createDict(); }
1055
- annotateElement[ $art ]*
1056
- '}' { this.finalizeDictOrArray( $art.elements ); }
1057
- { this.checkExtensionDict( $art.elements ); }
1058
- (
1059
- ACTIONS { $art.actions = this.createDict(); } '{'
1060
- annotateAction[ $art ]*
1061
- '}' { this.finalizeDictOrArray( $art.actions ); }
1062
- { this.checkExtensionDict( $art.actions ); }
1063
- )?
1064
- optionalSemi
1065
- |
1066
- ACTIONS { $art.actions = this.createDict(); } '{'
1067
- annotateAction[ $art ]*
1068
- '}' { this.finalizeDictOrArray( $art.actions ); }
1069
- { this.checkExtensionDict( $art.actions ); }
1070
- optionalSemi
1071
- |
1072
- '(' { $art.params = this.createDict(); }
1073
- annotateParam[ $art ]
1074
- ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1075
- annotateParam[ $art ]
1076
- )*
1077
- ')' { this.finalizeDictOrArray( $art.params ); }
1078
- { this.checkExtensionDict( $art.params ); }
1079
- ( annotateReturns[ $art ]
1080
- | requiredSemi
1081
- )
1082
- |
1083
- annotateReturns[ $art ]
1084
- |
1085
- requiredSemi
1086
- )
1087
- )
1088
- ;
1089
-
1090
- annotateElement[ outer ] locals[ art = new parser.XsnArtifact() ]
1091
- @after{ this.attachLocation( $art ); }
1092
- :
1093
- { $art.location = this.startLocation();; this.docComment( $art ); }
1094
- annotationAssignment_ll1[ $art ]*
1095
- name=ident['Element']
1096
- { this.addDef( $art, $outer, 'elements', 'annotate', $name.id );
1097
- this.docComment( $art ); }
1098
- annotationAssignment_ll1[ $art ]*
1099
- (
1100
- '{' { $art.elements = this.createDict(); }
1101
- annotateElement[ $art ]*
1102
- '}' { this.finalizeDictOrArray( $art.elements ); }
1103
- { this.checkExtensionDict( $art.elements ); }
1104
- optionalSemi
1105
- |
1106
- requiredSemi
1107
- )
1108
- ;
1109
-
1110
- annotateAction [ outer ] locals [ art = new parser.XsnArtifact() ]
1111
- @after{ this.attachLocation( $art ); }
1112
- :
1113
- { $art.location = this.startLocation();; this.docComment( $art ); }
1114
- annotationAssignment_ll1[ $art ]*
1115
- name=ident['BoundAction']
1116
- { this.addDef( $art, $outer, 'actions', 'annotate', $name.id );
1117
- this.docComment( $art ); }
1118
- annotationAssignment_ll1[ $art ]*
1119
- (
1120
- '(' { $art.params = this.createDict(); }
1121
- annotateParam[ $art ]
1122
- ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1123
- annotateParam[ $art ]
1124
- )*
1125
- ')' { this.finalizeDictOrArray( $art.params ); }
1126
- { this.checkExtensionDict( $art.params ); }
1127
- )?
1128
- (
1129
- annotateReturns[ $art ]
1130
- |
1131
- requiredSemi
1132
- )
1133
- ;
1134
-
1135
- annotateReturns[ art ]
1136
- @after{ this.attachLocation( $art.returns ); }
1137
- :
1138
- ret=RETURNS { $art.returns = new this.XsnArtifact();; $art.returns.kind = 'annotate'; }
1139
- { this.docComment( $art.returns ); }
1140
- annotationAssignment_ll1[ $art.returns ]*
1141
- ( '{' { $art.returns.elements = this.createDict(); }
1142
- annotateElement[ $art.returns ]*
1143
- '}' { this.finalizeDictOrArray( $art.returns.elements ); }
1144
- { this.checkExtensionDict( $art.returns.elements ); }
1145
- optionalSemi
1146
- | requiredSemi
1147
- )
1148
- ;
1149
-
1150
- annotateParam [ outer ] locals [ art = new parser.XsnArtifact() ]
1151
- @after{ this.attachLocation( $art ); }
1152
- :
1153
- { $art.location = this.startLocation();; this.docComment( $art ); }
1154
- annotationAssignment_ll1[ $art ]*
1155
- param=ident['Param']
1156
- { this.addDef( $art, $outer, 'params', 'annotate', $param.id );
1157
- this.docComment( $art ); }
1158
- annotationAssignment_ll1[ $art ]*
1159
- ;
1160
-
1161
- // Type expressions -------------------------------------------------------------
1162
-
1163
- includeRef[ art ] locals[ incl = {} ]
1164
- :
1165
- simplePath[ $incl, 'artref' ]
1166
- { if ($art.includes) $art.includes.push($incl); else $art.includes = [$incl]; }
1167
- ;
1168
-
1169
- typeSpec[ art ] // for parameterDef
1170
- @after{ /* #ATN 1 */ }
1171
- :
1172
- typeStruct[ $art ]
1173
- |
1174
- ':'
1175
- // #ATN: typeSimple can start with ARRAY or TYPE
1176
- ( typeStruct[ $art ]
1177
- nullability[ $art ]?
1178
- | typeArray[ $art ] // nullability is set in typeArray
1179
- | typeTypeOf[ $art ]
1180
- nullability[ $art ]?
1181
- (
1182
- ENUM '{' { $art.enum = this.createDict(); }
1183
- enumSymbolDef[ $art ]*
1184
- '}' { this.finalizeDictOrArray( $art.enum ); }
1185
- nullability[ $art ]?
1186
- )?
1187
- // TODO: no LOCALIZED ?
1188
- | typeRefOptArgs[ $art ]
1189
- nullability[ $art ]?
1190
- (
1191
- ENUM '{' { $art.enum = this.createDict(); }
1192
- enumSymbolDef[ $art ]*
1193
- '}' { this.finalizeDictOrArray( $art.enum ); }
1194
- nullability[ $art ]?
1195
- )?
1196
- )
1197
- ;
1198
-
1199
- returnTypeSpec[ art ]
1200
- @after{ /* #ATN 1 */ }
1201
- :
1202
- ret=RETURNS { $art.returns = { location: this.tokenLocation( $ret ), kind: 'param' }; }
1203
- { this.docComment( $art.returns ); }
1204
- annotationAssignment_ll1[ $art.returns ]*
1205
- // #ATN: typeSimple can start with ARRAY or TYPE
1206
- ( typeStruct[ $art.returns ]
1207
- nullability[ $art.returns ]?
1208
- | typeArray[ $art.returns ] // nullability is set in typeArray
1209
- | typeTypeOf[ $art.returns ]
1210
- nullability[ $art.returns ]?
1211
- (
1212
- ENUM '{' { $art.returns.enum = this.createDict(); }
1213
- enumSymbolDef[ $art.returns ]*
1214
- '}' { this.finalizeDictOrArray( $art.returns.enum ); }
1215
- nullability[ $art.returns ]?
1216
- )?
1217
- // TODO: no LOCALIZED ?
1218
- | typeRefOptArgs[ $art.returns ]
1219
- nullability[ $art.returns ]?
1220
- (
1221
- ENUM '{' { $art.returns.enum = this.createDict(); }
1222
- enumSymbolDef[ $art.returns ]*
1223
- '}' { this.finalizeDictOrArray( $art.returns.enum ); }
1224
- nullability[ $art.returns ]?
1225
- )?
1226
- )
1227
-
1228
- requiredSemi // currently for all - might change if we get rid of the misplaced annos (TODO: Now removed)
1229
- ;
1230
-
1231
-
1232
- typeSpecSemi[ art ] // with 'includes', for type and annotation defs
1233
- @after{ /* #ATN 3 */ }
1234
- :
1235
- typeStruct[ $art ]
1236
- ( nullability[ $art ]
1237
- requiredSemi
1238
- | optionalSemi
1239
- )
1240
- |
1241
- ':'
1242
- // #ATN: typeRefOptArgs can start with ARRAY or MANY or ASSOCIATION or TYPE or LOCALIZED
1243
- // Nevertheless, MANY '{' is handled by local token rewrite:
1244
- { this.setLocalToken( 'MANY', 'HelperToken1', /^[^\{]/ ); }
1245
- (
1246
- typeStruct[ $art ]
1247
- ( nullability[ $art ]
1248
- requiredSemi
1249
- | optionalSemi
1250
- )
1251
- |
1252
- typeAssociationBase[ $art, false ]
1253
- // #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
1254
- ( typeToMany[ $art ] | typeToOne[ $art ] | simplePath[ $art.target, 'artref' ] )
1255
- typeAssociationCont[ $art ]?
1256
- requiredSemi // and if its the ';'...
1257
- |
1258
- many=HelperToken1 // rewritten MANY before '{'
1259
- { $art.items = { location: this.tokenLocation( $many ) };}
1260
- typeStruct[ $art.items ]
1261
- ( nullability[ $art.items ]
1262
- requiredSemi
1263
- | optionalSemi
1264
- )
1265
- |
1266
- (
1267
- array=ARRAY of=OF
1268
- { $art.items = { location: this.tokenLocation( $array, $of ) }; }
1269
- | many=MANY
1270
- { $art.items = { location: this.tokenLocation( $many ) };}
1271
- )
1272
- // #ATN: typeRefOptArgs can start with TYPE
1273
- ( typeStruct[ $art.items ]
1274
- ( nullability[ $art.items ]
1275
- requiredSemi
1276
- | optionalSemi
1277
- )
1278
- | ( typeTypeOf[ $art.items ] | typeRefOptArgs[ $art.items ] )
1279
- nullability[ $art.items ]?
1280
- { this.docComment( $art ); }
1281
- annotationAssignment_ll1[ $art ]*
1282
- (
1283
- ENUM '{' { $art.items.enum = this.createDict(); }
1284
- enumSymbolDef[ $art.items ]*
1285
- '}' { this.finalizeDictOrArray( $art.items.enum ); }
1286
- ( nullability[ $art.items ]
1287
- requiredSemi
1288
- | optionalSemi
1289
- )
1290
- | requiredSemi
1291
- )
1292
- )
1293
- |
1294
- typeTypeOf[ $art ]
1295
- defaultAndNullability[ $art ]?
1296
- { this.docComment( $art ); }
1297
- annotationAssignment_ll1[ $art ]*
1298
- requiredSemi
1299
- |
1300
- l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
1301
- typeRefOptArgs[ $art ]
1302
- defaultAndNullability[ $art ]?
1303
- { this.docComment( $art ); }
1304
- annotationAssignment_ll1[ $art ]*
1305
- requiredSemi
1306
- |
1307
- // alt lookahead includes MANY '{'
1308
- { $art.type = {}; }
1309
- // Can't use typeRefOptArgs because of clash with include rule below (ATN would change)
1310
- simplePath[ $art.type, 'artref' ]
1311
- (
1312
- ( typeRefArgs[ $art ]
1313
- | ':' // with element, e.g. `type T : E:elem enum { ... }`
1314
- { $art.type.scope = $art.type.path.length; }
1315
- simplePath[ $art.type, 'ref']
1316
- )?
1317
- optInvisibleNullability[ $art ]
1318
- { this.docComment( $art ); }
1319
- annotationAssignment_ll1[ $art ]*
1320
- (
1321
- ENUM '{' { $art.enum = this.createDict(); }
1322
- enumSymbolDef[ $art ]*
1323
- '}' { this.finalizeDictOrArray( $art.enum ); }
1324
- ( optionalSemi
1325
- | defaultAndNullability[ $art ]
1326
- requiredSemi
1327
- )
1328
- | defaultAndNullability[ $art ]?
1329
- requiredSemi
1330
- )
1331
- |
1332
- // TODO: complain if used in anno def?
1333
- { $art.includes = [ $art.type ]; delete $art.type; }
1334
- ( ',' { if (this.isStraightBefore('{')) break; } // allow ',' before '{' // }}
1335
- includeRef[ $art ]
1336
- )*
1337
- typeStruct[ $art ]
1338
- ( optionalSemi
1339
- | nullability[ $art ]
1340
- requiredSemi
1341
- )
1342
- )
1343
- )
1344
- ;
1345
-
1346
- typeStruct[ art, attachLoc = false ]
1347
- @after { if ($attachLoc) this.attachLocation($art); }
1348
- :
1349
- '{' { $art.elements = this.createDict(); }
1350
- elementDef[ $art ]*
1351
- '}' { this.finalizeDictOrArray( $art.elements ); }
1352
- ;
1353
-
1354
- typeCompoStruct[ art ]
1355
- @after { this.attachLocation($art); }
1356
- :
1357
- COMPOSITIONofBRACE { $art.elements = this.createDict(); }
1358
- elementDef[ $art ]*
1359
- '}' { this.finalizeDictOrArray( $art.elements ); }
1360
- ;
1361
-
1362
- typeArray[ art ]
1363
- @after { /* #ATN 1 */ }
1364
- :
1365
- (
1366
- array=ARRAY of=OF
1367
- { $art.items = { location: this.tokenLocation( $array, $of ) }; }
1368
- | many=MANY
1369
- { $art.items = { location: this.tokenLocation( $many ) };}
1370
- )
1371
- // #ATN: typeRefOptArgs can start with TYPE
1372
- ( typeStruct[ $art.items ]
1373
- nullability[ $art.items ]?
1374
- | typeTypeOf[ $art.items ]
1375
- nullability[ $art.items ]?
1376
- (
1377
- ENUM '{' { $art.items.enum = this.createDict(); }
1378
- enumSymbolDef[ $art.items ]*
1379
- '}' { this.finalizeDictOrArray( $art.items.enum ); }
1380
- nullability[ $art.items ]?
1381
- )?
1382
- | typeRefOptArgs[ $art.items ]
1383
- nullability[ $art.items ]?
1384
- (
1385
- ENUM '{' { $art.items.enum = this.createDict(); }
1386
- enumSymbolDef[ $art.items ]*
1387
- '}' { this.finalizeDictOrArray( $art.items.enum ); }
1388
- nullability[ $art.items ]?
1389
- )?
1390
- )
1391
- ;
1392
-
1393
- typeAssociationBase[ art, handleTypeCompo ] // including Composition
1394
- :
1395
- (
1396
- assoc=ASSOCIATION cardinality[$art]? TO
1397
- {{
1398
- let location = this.tokenLocation($assoc);
1399
- $art.type = { path: [{ id: 'cds.Association', location }], scope: 'global', location };
1400
- this.handleComposition( $art.cardinality, false );
1401
- }}
1402
- |
1403
- compo=COMPOSITION cardinality[$art]? OF
1404
- {{
1405
- let location = this.tokenLocation($compo);
1406
- $art.type = { path: [{ id: 'cds.Composition', location }], scope: 'global', location };
1407
- this.handleComposition( $art.cardinality, handleTypeCompo );
1408
- }}
1409
- )
1410
- { $art.target = {}; }
1411
- ;
1412
-
1413
- typeAssociationCont[ art ]
1414
- :
1415
- (
1416
- '{' { $art.foreignKeys = this.createDict(); }
1417
- (
1418
- foreignKey[ $art ]
1419
- ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
1420
- foreignKey[ $art ]
1421
- )*
1422
- )?
1423
- '}' { this.finalizeDictOrArray( $art.foreignKeys ); }
1424
- defaultAndNullability[ $art ]?
1425
- |
1426
- ON cond=condition
1427
- { $art.on=$cond.cond; }
1428
- |
1429
- defaultAndNullability[ $art ]
1430
- )
1431
- ;
1432
-
1433
- typeAssociationElementCont[ art ] // including Composition
1434
- // optional NULL / NOT NULL for managed association only
1435
- :
1436
- (
1437
- '{' { $art.foreignKeys = this.createDict(); }
1438
- (
1439
- foreignKey[ $art ]
1440
- ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
1441
- foreignKey[ $art ]
1442
- )*
1443
- )?
1444
- '}' { this.finalizeDictOrArray( $art.foreignKeys ); }
1445
- defaultAndNullability[ $art ]?
1446
- |
1447
- ON cond=condition
1448
- { $art.on=$cond.cond; }
1449
- |
1450
- defaultAndNullability[ $art ]
1451
- )?
1452
- { this.docComment( $art ); }
1453
- annotationAssignment_ll1[ $art ]*
1454
- requiredSemi // also req after foreign key spec
1455
- ;
1456
-
1457
- typeToOne[ art ]
1458
- :
1459
- one=ONE
1460
- { this.setMaxCardinality( $art, this.numberLiteral( $one, null, '1' ) ); }
1461
- simplePath[ $art.target, 'artref' ]
1462
- ;
1463
-
1464
- typeToMany[ art ]
1465
- :
1466
- many=MANY
1467
- { this.setMaxCardinality( $art, { literal: 'string', val: '*' }, $many ); }
1468
- simplePath[ $art.target, 'artref' ]
1469
- ;
1470
-
1471
- cardinality[ art ] locals[ card = {} ]
1472
- @after { /* #ATN 2 */ $art.cardinality = this.attachLocation($card); }
1473
- :
1474
- lbrack='['
1475
- { $card.targetMax = { literal: 'string', val: '*',
1476
- location: this.tokenLocation($lbrack) }; }
1477
- (
1478
- // #ATN: simple lookahead behind Number
1479
- (
1480
- srcMax=Number ','
1481
- { $card.sourceMax = this.numberLiteral( $srcMax ); }
1482
- |
1483
- srcMaxStar='*' ','
1484
- { $card.sourceMax = { literal: 'string', val: '*',
1485
- location: this.tokenLocation($srcMaxStar) }; }
1486
- )?
1487
- // #ATN: simple lookahead behind Number
1488
- (
1489
- trgMin=Number '..'
1490
- { $card.targetMin = this.numberLiteral( $trgMin ); }
1491
- )?
1492
- (
1493
- trgMax=Number
1494
- { $card.targetMax = this.numberLiteral( $trgMax ); }
1495
- |
1496
- trgMaxStar='*'
1497
- { $card.targetMax = { literal: 'string', val: '*',
1498
- location: this.tokenLocation($trgMaxStar) }; }
1499
- )
1500
- )?
1501
- ']'
1502
- ;
1503
-
1504
- // TO be used when NOT and NULL are already in the lookahead set:
1505
- optInvisibleNullability[ art ]
1506
- : { this.setLocalTokenForId( 1, { 'NOT': 'HelperToken1', 'NULL': 'HelperToken2' } ); }
1507
- ( n1=HelperToken1 n2=NULL { this.setNullability( $art, $n1, $n2 ); }
1508
- | n1=HelperToken2 { this.setNullability( $art, $n1, null ); }
1509
- )?
1510
- ;
1511
-
1512
- nullability[ art ]
1513
- @after{ this.setNullability( $art, $n1, $n2 ); }
1514
- : n1=NOT n2=NULL
1515
- | n1=NULL
1516
- ;
1517
-
1518
- foreignKey[ outer ] locals[ art = new parser.XsnArtifact(), elem = {} ]
1519
- @after { this.attachLocation($art); }
1520
- :
1521
- simplePath[ $elem, 'ref' ] { $art.targetElement = $elem; }
1522
- ( AS name=ident['Key'] )?
1523
- { this.addDef( $art, $outer, 'foreignKeys', 'key', ($ctx.name) ? $name.id : $elem.path ); }
1524
- ;
1525
-
1526
- typeTypeOf[ art ] locals[ _sync = 'nop' ]
1527
- @after { this.attachLocation($art.type); }
1528
- :
1529
- t=TYPE o=OF
1530
- { $art.type = { scope: 'typeOf' }; }
1531
- simplePath[ $art.type, 'ref' ]
1532
- ( ':'
1533
- // If we have too much time, we could set the category of the simple path
1534
- // before to 'artref'
1535
- { $art.type.scope = $art.type.path.length; }
1536
- simplePath[ $art.type, 'ref']
1537
- )?
1538
- // We do not use (…|) here instead (…)? due to different ANTLR code generation:
1539
- // (…|) would check for follow set, which does not work with local token rewrite
1540
- { if ($art.type.scope === 'typeOf')
1541
- // Better error locations and much simpler code if we consider it as a path breakout:
1542
- $art.type.path.unshift( { id: 'type of', location: this.tokenLocation( $t, $o )} );
1543
- }
1544
- ;
1545
-
1546
- typeRefOptArgs[ art ]
1547
- @init { $art.type = {}; }
1548
- :
1549
- simplePath[ $art.type, 'artref' ]
1550
- (
1551
- typeRefArgs[ $art ]
1552
- |
1553
- ':'
1554
- { $art.type.scope = $art.type.path.length; }
1555
- simplePath[ $art.type, 'ref']
1556
- )?
1557
- ;
1558
-
1559
- typeRefArgs[ art ]
1560
- @after { this.checkTypeArgs($art); }
1561
- :
1562
- paren='(' { $art['$'+'typeArgs'] = this.createArray(); }
1563
- (
1564
- // unnamed arguments
1565
- head=Number
1566
- { $art['$'+'typeArgs'].push( this.numberLiteral( $head ) ); }
1567
- ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1568
- (
1569
- v=VARIABLE
1570
- { $art['$'+'typeArgs'].push(
1571
- { literal: 'string', val: 'variable', location: this.tokenLocation($v) } );
1572
- }
1573
- |
1574
- f=FLOATING
1575
- { $art['$'+'typeArgs'].push(
1576
- { literal: 'string', val: 'floating', location: this.tokenLocation($f) } );
1577
- }
1578
- |
1579
- tail=Number
1580
- { $art['$'+'typeArgs'].push( this.numberLiteral( $tail ) ); }
1581
- )
1582
- )*
1583
- |
1584
- // named arguments
1585
- typeNamedArg[ $art ]
1586
- ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1587
- typeNamedArg[ $art ]
1588
- )*
1589
- )
1590
- ')'{ this.finalizeDictOrArray( $art['$'+'typeArgs']); }
1591
- ;
1592
-
1593
- typeNamedArgList[ art ]
1594
- :
1595
- paren='('
1596
- typeNamedArg[ $art ]
1597
- ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1598
- typeNamedArg[ $art ]
1599
- )*
1600
- ')'
1601
- ;
1602
-
1603
- typeNamedArg[ art ] locals[ arg = '' ]
1604
- :
1605
- name=ident['paramname']
1606
- ':'
1607
- { if ($name.id && this.checkTypeFacet( $art, $name.id ))
1608
- $arg = $name.id.id;
1609
- }
1610
- (
1611
- val=Number
1612
- { if ($arg && $art && $name.id) {
1613
- $art[$arg] = this.numberLiteral( $val );
1614
- }
1615
- }
1616
- |
1617
- v=VARIABLE
1618
- { if ($arg && $art && $name.id) {
1619
- $art[$arg] = { literal: 'string', val: 'variable', location: this.tokenLocation($v) };
1620
- }
1621
- }
1622
- |
1623
- f=FLOATING
1624
- { if ($arg && $art && $name.id) {
1625
- $art[$arg] = { literal: 'string', val: 'floating', location: this.tokenLocation($f) };
1626
- }
1627
- }
1628
- )
1629
- ;
1630
-
1631
- // Queries: the main query structure --------------------------------------------
1632
-
1633
- queryEOF returns [ query ]
1634
- :
1635
- q=queryExpression { $query = $q.query; } EOF
1636
- ;
1637
-
1638
- projectionSpec returns[ query ] locals[ src ]
1639
- @after { this.attachLocation($query); }
1640
- :
1641
- proj=PROJECTION ON
1642
- // now a simplified `tableTerm`:
1643
- {
1644
- $src = { path: [] };
1645
- $query = { op: this.valueWithTokenLocation( 'SELECT', $proj ), from: $src, location: this.startLocation() };
1646
- }
1647
- fromPath[ $src, 'artref']
1648
- ( ':'
1649
- { $src.scope = $src.path.length; }
1650
- fromPath[ $src, 'ref']
1651
- )?
1652
- ( AS aliasName=ident['FromAlias'] { $src.name = $aliasName.id } )?
1653
- // ANTLR errors are better if we use ( A )? instead of ( A | ):
1654
- { if (!$src.name) this.classifyImplicitName( $src.scope ? 'FromAlias' : 'Without' ); }
1655
- bracedSelectItemListDef[ $query, 'columns' ]?
1656
- excludingClause[ $query ]?
1657
- ;
1658
-
1659
- projectionClauses[ query ]
1660
- @after { this.attachLocation($query); }
1661
- :
1662
- ( WHERE cond=condition { $query.where = $cond.cond; } )?
1663
- (
1664
- GROUP BY
1665
- e1=expression { $query.groupBy = [ $e1.expr ]; }
1666
- ( ',' en=expression { $query.groupBy.push( $en.expr ); } )*
1667
- )?
1668
- ( HAVING having=condition { $query.having = $having.cond; } )?
1669
- ( ob=orderByClause[ $query ] { $query = $ob.query; } ) ?
1670
- ( lc=limitClause[ $query ] { $query = $lc.query; } ) ?
1671
- ;
1672
-
1673
- queryExpression returns[ query ] // QLSubqueryComplex, SubqueryComplex
1674
- @after{ this.attachLocation($query); }
1675
- :
1676
- qt1=queryTerm { $query = $qt1.query; }
1677
- (
1678
- ( op=UNION q=( DISTINCT | ALL )?
1679
- | op=EXCEPT q=DISTINCT?
1680
- | op=MINUS q=DISTINCT?
1681
- )
1682
- qt=queryTerm
1683
- { if ($qt.query) $query = this.leftAssocBinaryOp( $query, $op, $q, $qt.query );; $ctx.q = null; }
1684
- )*
1685
- ( ob=orderByClause[ $query ] { if ($ob.query) $query = $ob.query; } ) ?
1686
- ( lc=limitClause[ $query ] { if ($lc.query) $query = $lc.query; } ) ?
1687
- ;
1688
-
1689
- queryTerm returns[ query ]
1690
- @after{ this.attachLocation($query); }
1691
- :
1692
- qt1=queryPrimary { $query = $qt1.query; }
1693
- (
1694
- intersect=INTERSECT quantifier=DISTINCT?
1695
- qt=queryPrimary
1696
- { $query = this.leftAssocBinaryOp( $query, $intersect, $quantifier, $qt.query );
1697
- $ctx.quantifier = null; } // reset for loop
1698
- )*
1699
- ;
1700
-
1701
- queryPrimary returns[ query = {} ]
1702
- @after { this.attachLocation($query); }
1703
- :
1704
- open='(' qe=queryExpression close=')'
1705
- { $query = this.surroundByParens( $qe.query, $open, $close ); }
1706
- |
1707
- select=SELECT
1708
- { $query = { op: this.valueWithTokenLocation( 'SELECT', $select ), location: this.startLocation() }; }
1709
- (
1710
- FROM querySource[ $query ]
1711
- (
1712
- mixin=MIXIN '{' { $query.mixin = this.createDict(); }
1713
- mixinElementDef[ $query ]*
1714
- '}' { this.finalizeDictOrArray( $query.mixin ); }
1715
- INTO
1716
- )?
1717
- ( ad=( ALL | DISTINCT ) // TODO: or directly after SELECT ?
1718
- { $query.quantifier = this.valueWithTokenLocation( $ad.text.toLowerCase(), $ad ); }
1719
- )?
1720
- bracedSelectItemListDef[ $query, 'columns' ]?
1721
- excludingClause[ $query ]?
1722
- |
1723
- ( ad=( ALL | DISTINCT ) // TODO: or directly after SELECT ?
1724
- { $query.quantifier = this.valueWithTokenLocation( $ad.text.toLowerCase(), $ad ); }
1725
- )?
1726
- { $query.columns = []; } // set it early to avoid "wildcard" errors
1727
- selectItemDef[ $query.columns ]
1728
- ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
1729
- selectItemDef[ $query.columns ]
1730
- )*
1731
- FROM querySource[ $query ]
1732
- )
1733
- ( WHERE cond=condition { $query.where = $cond.cond; } )?
1734
- (
1735
- GROUP BY
1736
- e1=expression { $query.groupBy = [ $e1.expr ]; }
1737
- ( ',' en=expression { $query.groupBy.push( $en.expr ); } )*
1738
- )?
1739
- ( HAVING having=condition { $query.having = $having.cond; } )?
1740
- ;
1741
-
1742
- querySource[ query ]
1743
- @after { this.attachLocation($query.from); }
1744
- :
1745
- t1=tableExpression { $query.from = $t1.table; }
1746
- (
1747
- { const location = this.tokenLocation( this.getCurrentToken() );
1748
- $query.from = { op: { val: 'join', location },
1749
- join: { val: 'cross', location },
1750
- args: [$t1.table] }; }
1751
- ( ',' tn=tableExpression { if ($tn.table) $query.from.args.push( $tn.table ); } )+
1752
- )?
1753
- ;
1754
-
1755
- tableExpression returns[ table ] // TableOrJoin
1756
- @after { this.attachLocation($table); }
1757
- :
1758
- qt=tableTerm { $table = $qt.table; }
1759
- (
1760
- join=joinOp[ $table ] { $table = $join.table; }
1761
- te=tableExpression
1762
- { if (!$table) { $table = {}; } else if ($te.table) $table.args.push( $te.table ); }
1763
- ON cond=condition { $table.on = $cond.cond; }
1764
- |
1765
- crj=CROSS jn=JOIN tt=tableTerm
1766
- { if (!$table) { $table = {}; } $table = this.leftAssocBinaryOp( $table, $jn, $crj, $tt.table, 'join' ); }
1767
- )*
1768
- ;
1769
-
1770
- tableTerm returns [ table ]
1771
- @after{ /* #ATN 1 */ this.attachLocation($table); }
1772
- :
1773
- { $table = { path: [] }; }
1774
- f=fromPath[ $table, 'artref']
1775
- { if ($f.dotAfterFilter)
1776
- this.warning( 'syntax-invalid-path-separator', $f.dotAfterFilter,
1777
- { '#': 'dot', code: '.', newcode: ':' } );
1778
- }
1779
- ( { if (!$table.scope)
1780
- $table.scope = $table.path.length;
1781
- else
1782
- this.warning( 'syntax-invalid-path-separator', this.getCurrentToken(),
1783
- { '#': 'colon', code: ':', newcode: '.' } );
1784
- }
1785
- ':' fromPath[ $table, 'ref']
1786
- )?
1787
- ( AS n1=ident['FromAlias'] { $table.name = $n1.id; }
1788
- | n2=identNoKeyword['FromAlias'] { $table.name = this.fragileAlias( $n2.id ); }
1789
- // if we would use rule `ident`, we would either had to make all JOIN
1790
- // kinds reserved or introduce ATN
1791
- )?
1792
- // ANTLR errors are better if we use ( A | B )? instead of ( A | B | ):
1793
- { if (!$table.name) this.classifyImplicitName( $table.scope ? 'FromAlias' : 'Without' ); }
1794
- |
1795
- open='('
1796
- // #ATN: The following alternative is not LL1, because both can start with
1797
- // left-paren, but queryExpression has SELECT after initial left-parens
1798
- (
1799
- qe=queryExpression close=')'
1800
- { $table = this.surroundByParens( $qe.query, $open, $close, true ); }
1801
- ( AS a1=ident['FromAlias'] { $table.name = $a1.id } // for defining table alias
1802
- | a2=identNoKeyword['FromAlias'] { $table.name = this.fragileAlias( $a2.id, true ); }
1803
- // not using ident` to have a similar behavior to above
1804
- )?
1805
- |
1806
- te=tableExpression close=')'
1807
- { $table = this.surroundByParens( $te.table, $open, $close ); }
1808
- )
1809
- ;
1810
-
1811
- fromPath[ qp, idkind ] returns[ dotAfterFilter = null ]
1812
- @after{ this.attachLocation($qp); }
1813
- :
1814
- id=ident[$idkind] { this.pushIdent( $qp.path, $id.id ); }
1815
- ( fromArguments[ $id.id ] cardinalityAndFilter[ $id.id ]?
1816
- { $dotAfterFilter = false; }
1817
- | cardinalityAndFilter[ $id.id ]
1818
- { $dotAfterFilter = false; }
1819
- )?
1820
- (
1821
- { if ($dotAfterFilter === false) {
1822
- $dotAfterFilter = this.getCurrentToken();
1823
- if (!$qp.scope) $qp.scope = $qp.path.length;
1824
- } }
1825
- '.' id=ident[$idkind] { this.pushIdent( $qp.path, $id.id ); }
1826
- ( fromArguments[ $id.id ] cardinalityAndFilter[ $id.id ]?
1827
- { if (!$dotAfterFilter) $dotAfterFilter = false; }
1828
- | cardinalityAndFilter[ $id.id ]
1829
- { if (!$dotAfterFilter) $dotAfterFilter = false; }
1830
- )?
1831
- )*
1832
- ;
1833
-
1834
- fromArguments[ pathStep ]
1835
- @init{ if (!$pathStep) $pathStep = {}; } // grammar robustness, see test/negative/parser/NamedExpression.cds
1836
- :
1837
- '(' { $pathStep.args = this.createDict(); $pathStep['$'+'syntax'] = ':'; } // necessary?
1838
- name=ident['paramname'] ':'
1839
- namedExpression[ $pathStep, $name.id ]
1840
- ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1841
- name=ident['paramname'] ':'
1842
- namedExpression[ $pathStep, $name.id ]
1843
- )*
1844
- ')' { this.finalizeDictOrArray( $pathStep.args ); }
1845
- ;
1846
-
1847
- // Queries: columns and other clauses -------------------------------------------
1848
-
1849
- excludingClause[ query ]
1850
- :
1851
- // syntax is less than ideal - EXCLUDING is only useful for `*` - with
1852
- // this syntax, people wonder what happens with explicit select items
1853
- EXCLUDING '{' { $query.excludingDict = this.createDict(); }
1854
- projectionExclusion[ $query ]
1855
- ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
1856
- projectionExclusion[ $query ]
1857
- )*
1858
- '}' { this.finalizeDictOrArray( $query.excludingDict ); }
1859
- ;
1860
-
1861
- projectionExclusion[ outer ] locals[ art = {} ]
1862
- @after { this.attachLocation($art); }
1863
- :
1864
- name=ident['ref']
1865
- { this.addDef( $art, $outer, 'excludingDict', '', $name.id ); }
1866
- ;
1867
-
1868
- // Actually, this is a subset if elementDefInner...
1869
- // TODO: the corresponding restrictions must also be checked in the core
1870
- // compiler, as the mixin element could come via CSN
1871
- mixinElementDef[ outer ] locals[ art = { target: {} } ]
1872
- @after { /* #ATN 1 */ this.attachLocation($art); }
1873
- :
1874
- name=ident['Mixin'] ':'
1875
- typeAssociationBase[ $art, false ]
1876
- { if ($art.type) this.addDef( $art, $outer, 'mixin', 'mixin', $name.id ); }
1877
- // #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
1878
- ( typeToMany[ $art ] | typeToOne[ $art ] | simplePath[ $art.target, 'artref' ] )
1879
- // TODO CC: exclude every token other than ON
1880
- typeAssociationCont[ $art ]? // better error reporting than simply `ON condition`
1881
- requiredSemi
1882
- ;
1883
-
1884
- selectItemDef[ outer ] locals[ art ]
1885
- @after{ if ($art) this.attachLocation( $art ); }
1886
- :
1887
- star='*'
1888
- { $outer.push( this.valueWithTokenLocation( '*', $star ) ); }
1889
- |
1890
- { $art = new this.XsnArtifact();; this.docComment( $art ); }
1891
- annotationAssignment_atn[ $art ]*
1892
- // VIRTUAL is keyword, except if before the following tokens texts:
1893
- { this.setLocalToken( 'VIRTUAL', 'VIRTUAL', /^([,.:\[@]|as)$/i ) ; } // not '{'
1894
- ( virtual=VIRTUAL { $art.virtual = this.valueWithTokenLocation( true, $virtual ); } )?
1895
- ( key=KEY { $art.key = this.valueWithTokenLocation( true, $key ); } )?
1896
- selectItemDefBody[ $art, $outer ]
1897
- ;
1898
-
1899
- selectItemDefBody[ art, outer ] locals[ assoc ]
1900
- @after{ /* #ATN 2 */ }
1901
- :
1902
- { $outer.push( $art ); }
1903
- (
1904
- e=expression { $art.value = $e.expr; }
1905
- // we cannot use 'condition' instead, as long as we allow aliases without
1906
- // AS (using rule 'ident' instead of 'identNoKeyword') -> ambiguities
1907
- ( as=AS n1=ident['Item'] { $art.name = $n1.id; }
1908
- | n2=ident['Item'] { $art.name = this.fragileAlias( $n2.id, true ); }
1909
- | { this.classifyImplicitName( 'Item', $e.expr ); }
1910
- )
1911
- { if ($art.value && !$art.value.path) this.excludeExpected( ["'.'", "'{'"] );
1912
- else if ($art.name) this.excludeExpected( ["'.'"] );
1913
- }
1914
- (
1915
- { this.reportExpandInline( $art, false ); }
1916
- selectItemInlineList[ $art, 'expand' ]
1917
- excludingClause[ $art ]?
1918
- |
1919
- { this.reportExpandInline( $art, $as || this._input.LT(-1) ); }
1920
- DOTbeforeBRACE // ...orASTERISK
1921
- (
1922
- selectItemInlineList[ $art, 'inline' ]
1923
- excludingClause[ $art ]?
1924
- |
1925
- star='*'
1926
- { $art.inline = [ this.valueWithTokenLocation( '*', $star ) ]; }
1927
- )
1928
- )?
1929
- |
1930
- selectItemInlineList[ $art, 'expand' ]
1931
- excludingClause[ $art ]?
1932
- AS n1=ident['Item'] { $art.name = $n1.id; }
1933
- )
1934
- { this.docComment( $art ); }
1935
- annotationAssignment_fix[ $art ]*
1936
- ( ':'
1937
- // #ATN: typeRefOptArgs can start with TYPE, REDIRECTED, ASSOCIATION
1938
- ( re=REDIRECTED to=TO
1939
- { $art.target = {}; }
1940
- simplePath[ $art.target, 'artref' ]
1941
- (
1942
- typeAssociationCont[ $art ]
1943
- |
1944
- { this.docComment( $art ); }
1945
- annotationAssignment_ll1[ $art ]*
1946
- )
1947
- | typeTypeOf[ $art ]
1948
- { this.docComment( $art ); }
1949
- annotationAssignment_ll1[ $art ]*
1950
- | l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
1951
- typeRefOptArgs[ $art ]
1952
- { this.docComment( $art ); }
1953
- annotationAssignment_ll1[ $art ]*
1954
- | typeRefOptArgs[ $art ]
1955
- { this.docComment( $art ); }
1956
- annotationAssignment_ll1[ $art ]*
1957
- |
1958
- { $assoc = this.associationInSelectItem( $art ); }
1959
- typeAssociationBase[ $assoc, false ]
1960
- // #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
1961
- ( typeToMany[ $assoc ] | typeToOne[ $assoc ] | simplePath[ $assoc.target, 'artref' ] )
1962
- ON cond=condition
1963
- { $assoc.on=$cond.cond; }
1964
- )
1965
- )?
1966
- ;
1967
-
1968
- bracedSelectItemListDef[ query ]
1969
- :
1970
- '{' { $query.columns = this.createArray(); }
1971
- (
1972
- selectItemDef[ $query.columns ]
1973
- ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
1974
- selectItemDef[ $query.columns ]
1975
- )*
1976
- )?
1977
- '}' { this.finalizeDictOrArray( $query.columns ); }
1978
- ;
1979
-
1980
- selectItemInlineList[ art, clause ]
1981
- :
1982
- '{' { $art[$clause] = this.createArray(); }
1983
- (
1984
- selectItemInlineDef[ $art[$clause] ]
1985
- ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
1986
- selectItemInlineDef[ $art[$clause] ]
1987
- )*
1988
- )?
1989
- '}' { this.finalizeDictOrArray( $art[$clause] ); }
1990
- ;
1991
-
1992
- selectItemInlineDef[ outer ] locals[ art ]
1993
- @after{ if ($art) this.attachLocation( $art ); }
1994
- :
1995
- star='*'
1996
- { $outer.push( this.valueWithTokenLocation( '*', $star ) ); }
1997
- |
1998
- { $art = new this.XsnArtifact();; this.docComment( $art ); }
1999
- annotationAssignment_atn[ $art ]*
2000
- selectItemDefBody[ $art, $outer ]
2001
- ;
2002
-
2003
- orderByClause[ inQuery ] returns [ query ]
2004
- :
2005
- ORDER BY { $query = this.unaryOpForParens( $inQuery, '$'+'query' ); }
2006
- ob1=orderBySpec { $query.orderBy = [ $ob1.ob ]; }
2007
- ( ',' obn=orderBySpec { $query.orderBy.push( $obn.ob ); } )*
2008
- ;
2009
-
2010
- limitClause[ inQuery ] returns [ query ]
2011
- :
2012
- limkw=LIMIT { $query = this.unaryOpForParens( $inQuery, '$'+'query' ); }
2013
- lim=expression { $query.limit = { rows: $lim.expr }; }
2014
- ( OFFSET off=expression { $query.limit.offset = $off.expr; } )? // unsigned integer
2015
- ;
2016
-
2017
- orderBySpec returns[ ob ]
2018
- :
2019
- e=expression { $ob = $e.expr; }
2020
- ( asc=ASC { $ob.sort = this.valueWithTokenLocation( 'asc', $asc ); }
2021
- | desc=DESC { $ob.sort = this.valueWithTokenLocation( 'desc', $desc ); }
2022
- )?
2023
- ( nb=NULLS ne=( FIRST | LAST )
2024
- { $ob.nulls = this.valueWithTokenLocation( $ne.text.toLowerCase(), $nb, $ne ); }
2025
- )?
2026
- ;
2027
-
2028
- joinOp[ left ] returns[ table ] locals [ join ]
2029
- :
2030
- ( op=JOIN { $join = 'inner'; }
2031
- | t1=INNER c=joinCardinality? op=JOIN { $join = 'inner' }
2032
- | t1=LEFT t2=OUTER? c=joinCardinality? op=JOIN { $join = 'left' }
2033
- | t1=RIGHT t2=OUTER? c=joinCardinality? op=JOIN { $join = 'right' }
2034
- | t1=FULL t2=OUTER? c=joinCardinality? op=JOIN { $join = 'full' }
2035
- )
2036
- { $table = { op: this.valueWithTokenLocation( 'join', $op ),
2037
- join: this.valueWithTokenLocation( $join, $t1 || $op, $t2 ),
2038
- args: ($left ? [$left] : []),
2039
- location: $left && $left.location };
2040
- if ($ctx.c) $table.cardinality = $c.joinCard; }
2041
- ;
2042
-
2043
- joinCardinality returns [ joinCard ]
2044
- @init { $joinCard = {}; }
2045
- @after { this.attachLocation($joinCard); }
2046
- :
2047
- (
2048
- srcExact=EXACT?
2049
- srcMaxOne=ONE
2050
- { if($srcExact)
2051
- $joinCard.sourceMin = { literal: 'number', val: 1,
2052
- location: this.tokenLocation($srcExact) };
2053
- $joinCard.sourceMax = { literal: 'number', val: 1,
2054
- location: this.tokenLocation($srcMaxOne) }; }
2055
- |
2056
- srcMaxMany=MANY
2057
- { $joinCard.sourceMax = { literal: 'string', val: '*',
2058
- location: this.tokenLocation($srcMaxMany) }; }
2059
- )
2060
- TO
2061
- (
2062
- tgtExact=EXACT? tgtMaxOne=ONE
2063
- { if($tgtExact)
2064
- $joinCard.targetMin = { literal: 'number', val: 1,
2065
- location: this.tokenLocation($tgtExact) };
2066
- $joinCard.targetMax = { literal: 'number', val: 1,
2067
- location: this.tokenLocation($tgtMaxOne) }; }
2068
- |
2069
- tgtMaxMany=MANY
2070
- { $joinCard.targetMax = { literal: 'string', val: '*',
2071
- location: this.tokenLocation($tgtMaxMany) }; }
2072
- )
2073
- ;
2074
-
2075
- // Conditions and expressions ---------------------------------------------------
2076
-
2077
- // With "separate" `condition` and `expression` rules, we have long LL
2078
- // ambiguities (not so with LALR used in Bison) with initial left parentheses:
2079
- // ( ( ( a.b.c + d.e.f
2080
- // ) // now we know: 3rd left-paren for expression
2081
- // = // now we know: 1st and 2nd left-paren for condition
2082
- // 3 ) ) )
2083
- //
2084
- // To avoid expensive parsing, we "combine" both rules, i.e. inside '('…')' of
2085
- // rule `expressionTerm`, we recursively refer to `condition`, not
2086
- // `expression`. With that, the existence of relations/predicates in rule
2087
- // `conditionTerm` must be optional. Correct conditions and expressions must
2088
- // be then ensured by code (either in actions of the grammar or in a check
2089
- // phase - to be discussed).
2090
- //
2091
- // We cannot generally allow `condition` where `expression` is written:
2092
- // - IN is also used in non-standard function `args` and as predicate:
2093
- // - AND is boolean operator and also used for BETWEEN…AND
2094
- //
2095
- // ANTLR4s left-recursion feature cannot be used as we will have rule
2096
- // arguments.
2097
-
2098
- conditionEOF returns [ cond ]
2099
- :
2100
- c=condition { $cond = $c.cond; } EOF
2101
- ;
2102
-
2103
- condition returns [ cond ] locals [ args = [], orl = [] ]
2104
- @after{ $cond = this.argsExpression( $args, $ctx.e2 ? '?:' : true ); }
2105
- :
2106
- c1=conditionOr { $args.push($c1.cond); }
2107
- (
2108
- // '?' is not mentioned in code completion, see errorStrategy.js#intervalSetToArray
2109
- q='?' { this.pushXprToken( $args ); }
2110
- e2=expression { $args.push($e2.expr); }
2111
- colon=':' { this.pushXprToken( $args ); }
2112
- e3=expression { $args.push($e3.expr); }
2113
- )?
2114
- ;
2115
-
2116
- conditionOr returns [ cond ] locals [ args = [], orl = [] ]
2117
- @after{ $cond = this.argsExpression( $args, 'or' ); }
2118
- :
2119
- c1=conditionAnd { $args.push($c1.cond); }
2120
- (
2121
- or=OR { this.pushXprToken( $args ); }
2122
- c2=conditionAnd { $args.push($c2.cond); }
2123
- )*
2124
- ;
2125
-
2126
- conditionAnd returns [ cond ] locals[ args = [] ]
2127
- @after{ $cond = this.argsExpression( $args, 'and' ); } // 'and' used by A2J and checks
2128
- :
2129
- c1=conditionTerm { $args.push($c1.cond); }
2130
- (
2131
- and=AND { this.pushXprToken( $args ); }
2132
- c2=conditionTerm { $args.push($c2.cond); }
2133
- )*
2134
- ;
2135
-
2136
- // Note: New operators need to be added to functionExpressionOperatorsRequireParentheses[] in toCdl.js.
2137
- conditionTerm returns [ cond ] locals[ args = [] ]
2138
- @after{ $cond = this.argsExpression( $args, '=' ); }// op: '=' (not nary) used by A2J and checks
2139
- :
2140
- nt=NOT { this.pushXprToken( $args ); }
2141
- ct=conditionTerm { $args.push( $ct.cond ); }
2142
- |
2143
- EXISTS { this.pushXprToken( $args ); }
2144
- (
2145
- open='(' qe=queryExpression close=')'
2146
- { $args.push( this.surroundByParens( $qe.query, $open, $close, true ) ); }
2147
- |
2148
- qm=( HideAlternatives | '?' )
2149
- { $args.push( { param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' } );
2150
- this.csnParseOnly( 'syntax-unsupported-param', [ $qm ], { '#': 'dynamic', code: '?' } );
2151
- }
2152
- |
2153
- ep=valuePath[ 'ref' ] { $args.push( $ep.qp ); }
2154
- { $ep.qp['$'+'expected'] = 'exists'; } // hm, really in parser? what about CSN input?
2155
- )
2156
- |
2157
- expr=expression { $args.push( $expr.expr ); }
2158
- (
2159
- ( '=' | '<>' | '>' | '>=' | '<' | '<=' | '!=' ) { this.pushXprToken( $args ); }
2160
- ( ( ANY | SOME | ALL ) { this.pushXprToken( $args ); } )?
2161
- e2=expression { $args.push( $e2.expr ); }
2162
- |
2163
- IS { this.pushXprToken( $args ); }
2164
- ( NOT { this.pushXprToken( $args ); } )?
2165
- NULL { this.pushXprToken( $args ); }
2166
- |
2167
- NOT { this.pushXprToken( $args ); }
2168
- predicate[ $args ]
2169
- |
2170
- predicate[ $args ]
2171
- )? // optional: for conditions in parentheses
2172
- ;
2173
-
2174
- // Note: New operators need to be added to functionExpressionOperatorsRequireParentheses[] in toCdl.js.
2175
- predicate[ args ]
2176
- :
2177
- IN { this.pushXprToken( $args ); }
2178
- // TODO: do we really allow it not to start with `(`? - TODO: try with warning
2179
- e1=expression { $args.push( this.secureParens( $e1.expr ) ); } // including ExpressionList
2180
- |
2181
- BETWEEN { this.pushXprToken( $args ); }
2182
- e2=expression { $args.push( $e2.expr ); }
2183
- AND { this.pushXprToken( $args ); }
2184
- e3=expression { $args.push( $e3.expr ); }
2185
- |
2186
- LIKE { this.pushXprToken( $args ); }
2187
- e4=expression { $args.push( $e4.expr ); }
2188
- ( ESCAPE { this.pushXprToken( $args ); }
2189
- e5=expression { $args.push( $e5.expr ); }
2190
- )?
2191
- ;
2192
-
2193
- expression returns [ expr ] locals [ args = [] ]
2194
- @after{ $expr = this.argsExpression( $args, true ); }
2195
- :
2196
- e1=expressionSum { $args.push( $e1.expr ); }
2197
- (
2198
- '||' { this.pushXprToken( $args ); }
2199
- e2=expressionSum { $args.push( $e2.expr ); }
2200
- )*
2201
- ;
2202
-
2203
- expressionSum returns [ expr ] locals [ args = [] ]
2204
- @after{ $expr = this.argsExpression( $args, true ); }
2205
- :
2206
- e1=expressionFactor { $args.push( $e1.expr ); }
2207
- (
2208
- ( '+' | '-' ) { this.pushXprToken( $args ); }
2209
- e2=expressionFactor { $args.push( $e2.expr ); }
2210
- )*
2211
- ;
2212
-
2213
- expressionFactor returns [ expr ] locals [ args = [] ]
2214
- @after{ $expr = this.argsExpression( $args, true ); }
2215
- :
2216
- e1=expressionTerm { $args.push( $e1.expr ); }
2217
- (
2218
- ( '*' | '/' ) { this.pushXprToken( $args ); }
2219
- e2=expressionTerm { $args.push( $e2.expr ); }
2220
- )*
2221
- ;
2222
-
2223
- expressionTerm returns [ expr ] locals [ args = [] ]
2224
- @after{ /* #ATN 1 */ $expr = this.argsExpression( $args, false ); }
2225
- :
2226
- ( '+' | '-' ) { this.pushXprToken( $args ); }
2227
- e1=expressionTerm // prefix op or part of the number
2228
- { this.signedExpression( $args, $e1.expr ); }
2229
- |
2230
- val=literalValue { $args.push( $val.val ); }
2231
- |
2232
- sf=specialFunction { $args.push( $sf.ret ); }
2233
- |
2234
- CASE { this.pushXprToken( $args ); }
2235
- (
2236
- e2=expression { $args.push( $e2.expr ); }
2237
- ( WHEN { this.pushXprToken( $args ); }
2238
- ew=expression { $args.push( $ew.expr ); }
2239
- THEN { this.pushXprToken( $args ); }
2240
- e3=expression { $args.push( $e3.expr ); }
2241
- )+
2242
- |
2243
- (
2244
- WHEN { this.pushXprToken( $args ); }
2245
- c=condition { $args.push( $c.cond ); }
2246
- THEN { this.pushXprToken( $args ); }
2247
- e3=expression { $args.push( $e3.expr ); }
2248
- )+
2249
- )
2250
- (
2251
- ELSE { this.pushXprToken( $args ); }
2252
- e4=expression { $args.push( $e4.expr ); }
2253
- )?
2254
- END { this.pushXprToken( $args ); }
2255
- |
2256
- ne=NEW { this.pushXprToken( $args ); } // token rewrite for NEW
2257
- nqp=valuePath[ 'ref', null ]
2258
- { $args.push( this.valuePathAst( $nqp.qp ) ); }
2259
- { this.fixNewKeywordPlacement( $args ); }
2260
- |
2261
- vp=valuePath[ 'ref', null ] { $args.push( this.valuePathAst( $vp.qp ) ); }
2262
- { this.setLocalTokenIfBefore( 'OVER', 'OVER', /^\($/i ); }
2263
- (
2264
- { $args[0].suffix = []; }
2265
- OVER { this.pushXprToken( $args[0].suffix ); }
2266
- open='(' over=overClause close=')'
2267
- { $args[0].suffix.push( this.surroundByParens( $over.over, $open, $close ) ); }
2268
- )?
2269
- |
2270
- colon=':' { this.reportUnexpectedSpace( $colon ); }
2271
- (
2272
- vp=valuePath[ 'paramref', this.startLocation() ]
2273
- {{ const par = $vp.qp;; par.scope = 'param';; $args.push( par ); }}
2274
- |
2275
- pp=Number
2276
- // TODO: no extra XSN property `param` for this, re-use `val`
2277
- { $args.push( { param: this.numberLiteral( $pp ), scope: 'param' } );
2278
- this.csnParseOnly( 'syntax-unsupported-param', [ $pp ], { '#': 'positional', code: ':' + $pp.text } );
2279
- }
2280
- )
2281
- |
2282
- qm= '?' // is automatically not mentioned as CC candidate
2283
- // if we have an HideAlternatives here, we would block it to use it in
2284
- // parallel to an expression (would produce adaptivePredict() otherwise)
2285
- // TODO: no extra XSN property `param` for this, re-use `val`
2286
- { $args.push( { param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' } );
2287
- this.csnParseOnly( 'syntax-unsupported-param', [ $qm ], { '#': 'dynamic', code: '?' } );
2288
- }
2289
- |
2290
- open='('
2291
- // #ATN: The following alternative is not LL1, because both can start with
2292
- // left-paren, but queryExpression has SELECT after initial left-parens
2293
- (
2294
- qe=queryExpression close=')'
2295
- { $args.push( this.surroundByParens( $qe.query, $open, $close, true ) ); }
2296
- |
2297
- c1=condition { $args = [ $c1.cond ]; }
2298
- ( ',' { if ($args.length > 1 && this.isStraightBefore(')')) break; } // allow ',' before ')'
2299
- cn=expression { if ($cn.expr) $args.push($cn.expr); }
2300
- )*
2301
- close=')'
2302
- {
2303
- if ($args.length > 1)
2304
- $args = [ { op: this.valueWithTokenLocation( 'list', $open ), args: $args,
2305
- location: this.tokenLocation( $open, $close ) } ];
2306
- else if ($args[0]) // can be `null` if condition failed to parse
2307
- $args = [ this.surroundByParens( $args[0], $open, $close ) ];
2308
- }
2309
- )
2310
- ;
2311
-
2312
- specialFunction returns [ ret = {} ] locals[ art = new parser.XsnArtifact() ]
2313
- :
2314
- ca=CAST '(' // see createArray() in action
2315
- {
2316
- $ret = {
2317
- op: this.valueWithTokenLocation( 'cast', $ca ),
2318
- args: this.createArray(),
2319
- location: this.tokenLocation( $ca )
2320
- };
2321
- }
2322
- e=expression AS typeRefOptArgs[ $ret ]
2323
- {
2324
- $ret.args.push( $e.expr );
2325
- }
2326
- ')' { this.finalizeDictOrArray( $ret.args ); }
2327
- ;
2328
-
2329
- // Paths and functions: ---------------------------------------------------------
2330
-
2331
- simplePath[ art, category ] locals[ _sync = 'nop' ]
2332
- @after { this.attachLocation($art); }
2333
- // Due to error recovery, rule `ident` can return with value `null`. Set the
2334
- // path as broken in this case.
2335
- :
2336
- head=ident[ $category ]
2337
- { if (!$art.path) $art.path = []; this.pushIdent( $art.path, $head.id ); }
2338
- (
2339
- '.' tail=ident[ $category ] { this.pushIdent( $art.path, $tail.id ); }
2340
- )*
2341
- ;
2342
-
2343
- valuePath[ category, location = null ] returns[ qp = { path: [] } ] locals[ _sync = 'nop' ]
2344
- @init { $qp.location = location || this.startLocation(); }
2345
- @after{ this.attachLocation($qp); }
2346
- :
2347
- id=ident[ $category ]
2348
- { this.pushIdent( $qp.path, $id.id ); }
2349
- ( pathArguments[ $id.id, $id.id ] cardinalityAndFilter[ $id.id ]?
2350
- | cardinalityAndFilter[ $id.id ]
2351
- )?
2352
- (
2353
- '.' id=ident['ref'] // yes 'ref', not $category
2354
- { this.pushIdent( $qp.path, $id.id ); }
2355
- ( pathArguments[ $id.id ] cardinalityAndFilter[ $id.id ]?
2356
- | cardinalityAndFilter[ $id.id ]
2357
- )?
2358
- )*
2359
- ;
2360
-
2361
- pathArguments[ pathStep, considerSpecial ]
2362
- @init{
2363
- if (!$pathStep) $pathStep = {}; // grammar robustness, see test/negative/parser/NamedExpression.cds
2364
- this.genericFunctionsStack.push( this['$'+'genericKeywords'] );
2365
- }
2366
- :
2367
- { this.excludeExpected([ 'ORDER' ]); }
2368
- '(' // dict or array, see below
2369
- // Make sure that we do not introduce A:B paths in expressions!
2370
- // Need to avoid adaptPredict(), otherwise Generic keywords won't work in funcExpression
2371
- //
2372
- // For code completion, we need to handle generic tokens directly after the
2373
- // '('. To avoid invalidating an assoc `trim` to an entity with parameter
2374
- // `leading` (ok, a bit constructed), we do not do it with named parameters.
2375
- { if (!this.setLocalTokenForId( 2, { ':': 'HelperToken1', '=>': 'HelperToken2' } ))
2376
- this.prepareGenericKeywords( $considerSpecial ); }
2377
- (
2378
- { $pathStep.args = this.createDict(); $pathStep['$'+'syntax'] = ':'; }
2379
- id=HelperToken1 ':'
2380
- namedExpression[ $pathStep, this.identAst( $id, 'paramname', true ) ]
2381
- ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
2382
- name=ident['paramname'] ':'
2383
- namedExpression[ $pathStep, $name.id ]
2384
- )*
2385
- |
2386
- { $pathStep.args = this.createDict(); } // TODO: XSN func path cleanup
2387
- id=HelperToken2 '=>'
2388
- namedExpression[ $pathStep, this.identAst( $id, 'paramname', true ) ]
2389
- ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
2390
- name=ident['paramname'] '=>'
2391
- namedExpression[ $pathStep, $name.id ]
2392
- )*
2393
- |
2394
- { $pathStep.args = this.createArray(); }
2395
- funcExpression[ $pathStep, $considerSpecial ]
2396
- ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
2397
- funcExpression[ $pathStep, $considerSpecial ]
2398
- )*
2399
- // Note: We can't move this into funcExpression, or we would increase the ATN count because of `,` amiguity.
2400
- ( ob=funcOrderByClause[ [ $pathStep.args[$pathStep.args.length - 1] ] ]
2401
- // Remove the last entry which was copied to $ob.expr and push $ob.expr:
2402
- { $pathStep.args[$pathStep.args.length - 1] = $ob.expr; }
2403
- )?
2404
- |
2405
- { $pathStep.args = this.createArray(); }
2406
- )
2407
- ')' { this.finalizeDictOrArray( $pathStep.args ); }
2408
- ;
2409
- finally { // see @init
2410
- if (!$pathStep.args) $pathStep.args = [];
2411
- this['$'+'genericKeywords'] = this.genericFunctionsStack.pop();
2412
- }
2413
-
2414
- namedExpression[ pathStep, id ]
2415
- :
2416
- elem=expression
2417
- { if ($pathStep && $id) {
2418
- this.addDef( ($ctx.elem && $elem.expr) ? $elem.expr : { location: $id.location },
2419
- $pathStep, 'args', 0, $id );
2420
- }
2421
- }
2422
- ;
2423
-
2424
- funcExpression[ pathStep, considerSpecial ] locals[ args = [] ]
2425
- @init { this.prepareGenericKeywords( $considerSpecial ); }
2426
- @after{ $pathStep.args.push( this.argsExpression( $args, false ) ); }
2427
- :
2428
- (
2429
- expr=expression
2430
- { $args.push( $expr.expr ); }
2431
- |
2432
- GenericExpr // keyword as replacement for expression, like '*'
2433
- { this.pushXprToken( $args ); }
2434
- |
2435
- GenericIntro // keyword as introduction of expression, like DISTINCT
2436
- { this.pushXprToken( $args ); }
2437
- expr=expression { $args.push( $expr.expr ); }
2438
- |
2439
- // Rule 'pathArguments' makes a decision based on the first two lookahead
2440
- // tokens of this rule → we need to list tokens which would be changed to
2441
- // GenericExpr or GenericIntro, and are not already covered by 'expression'
2442
- { this.reportErrorForGenericKeyword(); }
2443
- ( HideAlternatives | '*' | ALL | DISTINCT ) { this.pushXprToken( $args ); }
2444
- // now continue parsing like GenericExpr:
2445
- )
2446
- (
2447
- (
2448
- { this.prepareGenericKeywords( $considerSpecial, 'separator' ); }
2449
- (
2450
- GenericSeparator
2451
- |
2452
- // For ANTLR's lookahead calculations, we need to list tokens here
2453
- // which could be changed to GenericSeparator. Do not invent a
2454
- // keyword token which is just used here (Identifier does work
2455
- // perfectly)! If we want, we could add all non-reserved keywords
2456
- // except ORDER, and most reserved.
2457
- { this.reportErrorForGenericKeyword(); }
2458
- ( HideAlternatives | Identifier | FROM | IN | WITH | GROUP )
2459
- )
2460
- { this.pushXprToken( $args );
2461
- this.prepareGenericKeywords( $considerSpecial, 'expr' ); }
2462
- (
2463
- expr=expression { $args.push( $expr.expr ); }
2464
- |
2465
- GenericExpr { this.pushXprToken( $args ); }
2466
- |
2467
- { this.reportErrorForGenericKeyword(); }
2468
- // Again, we need to list tokens which could make it to GenericExpr
2469
- // and which do not start an expression
2470
- ( HideAlternatives | ALL ) { this.pushXprToken( $args ); }
2471
- )
2472
- )+
2473
- )?
2474
- ;
2475
-
2476
- overClause returns[ over ] locals[ args = [] ]
2477
- @after{ $over = this.argsExpression( $args, false ); }
2478
- :
2479
- ( PARTITION { this.pushXprToken( $args ); } BY { this.pushXprToken( $args ); }
2480
- pb=partitionByClause { $args.push( $pb.expr ); }
2481
- )?
2482
- ( ORDER { this.pushXprToken( $args ); } BY { this.pushXprToken( $args ); }
2483
- ob=exprOrderByClause { $args.push( $ob.expr ); }
2484
- )?
2485
- ( ROWS { this.pushXprToken( $args ); }
2486
- wf=windowFrameClause { $args.push( $wf.wf ); }
2487
- )?
2488
- ;
2489
-
2490
- partitionByClause returns [ expr ] locals[ args = [] ]
2491
- @after{ $expr = this.argsExpression( $args, false ); }
2492
- :
2493
- e1=expression { $args.push( $e1.expr ); }
2494
- ( ',' { this.pushXprToken( $args ); }
2495
- en=expression { $args.push( $en.expr ); }
2496
- )*
2497
- ;
2498
-
2499
- // ORDER BY clause in generic functions, e.g. `first_value(id order by name)`
2500
- funcOrderByClause[ args ] returns[ expr ]
2501
- @after{ $expr = this.argsExpression( $args, false, $args[0]?.location ); }
2502
- :
2503
- ORDER { this.pushXprToken( $args ); } BY { this.pushXprToken( $args ); }
2504
- ob=exprOrderByClause { $args.push( $ob.expr ); }
2505
- ;
2506
-
2507
- // ORDER BY clause in generic functions or OVER clause
2508
- exprOrderByClause returns[ expr ] locals[ args = [] ]
2509
- @after{ $expr = this.argsExpression( $args, false ); }
2510
- :
2511
- orderBySpecInExpr[ $args ]
2512
- ( ',' { this.pushXprToken( $args ); } orderBySpecInExpr[ $args ] )*
2513
- ;
2514
-
2515
- orderBySpecInExpr[ args ]
2516
- :
2517
- e=expression { $args.push( $e.expr ); }
2518
- ( ASC { this.pushXprToken( $args ); }
2519
- | DESC { this.pushXprToken( $args ); }
2520
- )?
2521
- ( NULLS { this.pushXprToken( $args ); }
2522
- ( FIRST | LAST ) { this.pushXprToken( $args ); }
2523
- )?
2524
- ;
2525
-
2526
- windowFrameClause returns[ wf ] locals[ args = [] ]
2527
- @after{ $wf = this.argsExpression( $args, false ); }
2528
- :
2529
- (
2530
- windowFrameStartSpec[ $args ]
2531
- |
2532
- BETWEEN { this.pushXprToken( $args ); }
2533
- windowFrameBoundSpec[ $args ]
2534
- AND { this.pushXprToken( $args ); }
2535
- windowFrameBoundSpec[ $args ]
2536
- )
2537
- ;
2538
-
2539
- windowFrameBoundSpec[ args ]
2540
- :
2541
- UNBOUNDED { this.pushXprToken( $args ); }
2542
- ( FOLLOWING | PRECEDING ) { this.pushXprToken( $args ); }
2543
- |
2544
- n=Number { $args.push( this.numberLiteral( $n ) ); }
2545
- ( FOLLOWING | PRECEDING ) { this.pushXprToken( $args ); }
2546
- |
2547
- CURRENT { this.pushXprToken( $args ); } ROW { this.pushXprToken( $args ); }
2548
- ;
2549
-
2550
- windowFrameStartSpec[ args = [] ]
2551
- :
2552
- UNBOUNDED { this.pushXprToken( $args ); }
2553
- PRECEDING { this.pushXprToken( $args ); }
2554
- |
2555
- n=Number { $args.push( this.numberLiteral( $n ) ); }
2556
- PRECEDING { this.pushXprToken( $args ); }
2557
- |
2558
- CURRENT { this.pushXprToken( $args ); } ROW { this.pushXprToken( $args ); }
2559
- ;
2560
-
2561
- cardinalityAndFilter[ pathStep ] locals [ _sync = 'nop' ]
2562
- :
2563
- { if (!$pathStep) $pathStep = {}; }
2564
- openFilter='['
2565
- optionalCardinality[ pathStep ]?
2566
-
2567
- filterWhereClause[ $pathStep ] // required, see rule's comment
2568
- (
2569
- group=GROUP by=BY
2570
- e1=expression { $pathStep.groupBy = [ $e1.expr ]; }
2571
- ( ',' en=expression { $pathStep.groupBy.push( $en.expr ); } )*
2572
- { this.csnParseOnly('syntax-unexpected-sql-clause', [ $group, $by ], { keyword: 'GROUP BY' }); }
2573
- )?
2574
- ( hv=HAVING
2575
- having=condition { $pathStep.having = $having.cond; }
2576
- { this.csnParseOnly('syntax-unexpected-sql-clause', [ $hv ], { keyword: 'HAVING' }); }
2577
- )?
2578
- ( { const orderKw = this._input.LT(1); const byKw = this._input.LT(2); }
2579
- ob=orderByClause[ $pathStep ] { $pathStep = $ob.query;; }
2580
- { this.csnParseOnly('syntax-unexpected-sql-clause', [ orderKw, byKw ], { keyword: 'ORDER BY' }); }
2581
- )?
2582
- ( { const limit = this._input.LT(1); }
2583
- lc=limitClause[ $pathStep ] { $pathStep = $lc.query;; }
2584
- { this.csnParseOnly('syntax-unexpected-sql-clause', [ limit ], { keyword: 'LIMIT' }); }
2585
- )?
2586
-
2587
- closeFilter=']'
2588
- ;
2589
-
2590
-
2591
- optionalCardinality[ pathStep ]
2592
- @after { if ($pathStep && $pathStep.cardinality) this.attachLocation($pathStep.cardinality); }
2593
- :
2594
- // Make sure to test second token to allow expressions starting with Number
2595
- // without introducing WHERE - that would be @options{k=2}. The code
2596
- // completion just produces `:` after having inserted a Number - TODO.
2597
- { if (this._input.LT(2).text !== ':') return $ctx; }
2598
- ( trgMax=Number ':'
2599
- { if ($pathStep) $pathStep.cardinality = { targetMax: this.numberLiteral( $trgMax ), location: this.startLocation() }; }
2600
- )
2601
- ;
2602
-
2603
- filterWhereClause[ pathStep ]
2604
- :
2605
- // NOTE: Keep in sync with optionalWhereForFilter!
2606
- //
2607
- // For ANTLR, WHERE is required, but the generated parser may skip `match(WHERE)` in
2608
- // `optionalWhereForFilter`. Because `(WHERE cond)?` would invoke adaptive predict,
2609
- // we use this hack that skips parsing of the condition, if the token is a SQL clause,
2610
- // but makes Antlr assume that it is required.
2611
- {
2612
- const tok = this.getCurrentToken();
2613
- if (tok.type === languageParser.GROUP
2614
- || tok.type === languageParser.ORDER
2615
- || tok.type === languageParser.LIMIT
2616
- || tok.type === languageParser.HAVING)
2617
- return $ctx;
2618
- }
2619
- optionalWhereForFilter cond=condition { if ($pathStep) $pathStep.where = $cond.cond; }
2620
- ;
2621
-
2622
- optionalWhereForFilter
2623
- :
2624
- // NOTE: only call from rule filterWhereClause!
2625
- //
2626
- // For ANTLR, WHERE is required, but we allow the generated parser skipping
2627
- // the call of match(WHERE). This hack requires that sync() at each state in
2628
- // the calling rule does not throw an error if the current token does not match
2629
- // one of the expected ones.
2630
- {
2631
- if (this.getCurrentToken().type !== languageParser.WHERE)
2632
- return $ctx; // TODO: should we somehow add those keywords to $(EXPECTED)?
2633
- }
2634
- WHERE
2635
- ;
2636
-
2637
- // Annotation assignments -------------------------------------------------------
2638
-
2639
- // We have three versions of the annotation assignment rules:
2640
- // - "fix": typically after a name if a ':' could follow
2641
- // - "ll1": typically before keyword+name, after a name if no ':' could follow
2642
- // - "atn": at the beginning of a column definition
2643
- //
2644
- // want to let the ambiguity in select items (solution: "either" possibility)
2645
- //
2646
- // entity E @Anno: Base { … }; // Base is include (chosen w/ warning) or @Anno value?
2647
- // entity V(p) as select from E { // either: anno value "ref p", select item -x
2648
- // @anno :p - x as x; // or: anno value true, select item :p-x
2649
- // }
2650
-
2651
- annotationAssignment_fix[ art ] locals[ assignment ]
2652
- // value outside @(...)
2653
- @after {
2654
- if ($assignment) {
2655
- this.assignAnnotation( $art, $assignment );
2656
- this.docComment( $art );
2657
- }
2658
- } :
2659
- at='@' { this.reportUnexpectedSpace( $at ); }
2660
- (
2661
- annotationAssignment_paren[ $art ]
2662
- |
2663
- { $assignment = { name: new this.XsnName() }; }
2664
- annotationPath[ $assignment.name, 'anno' ]
2665
- ( '#' annotationPathVariant[ $assignment.name ] )?
2666
- { this.warnIfColonFollows( $assignment ); }
2667
- )
2668
- ;
2669
-
2670
- annotationAssignment_ll1[ art ] locals[ assignment ]
2671
- @after {
2672
- if ($assignment) {
2673
- this.assignAnnotation( $art, $assignment );
2674
- this.docComment( $art );
2675
- }
2676
- } :
2677
- at='@' { this.reportUnexpectedSpace( $at ); }
2678
- (
2679
- annotationAssignment_paren[ $art ]
2680
- |
2681
- { $assignment = { name: new this.XsnName() }; }
2682
- annotationPath[ $assignment.name, 'anno' ]
2683
- ( '#' annotationPathVariant[ $assignment.name ] )?
2684
- (
2685
- ':' { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
2686
- val=annoValue[ $assignment ]
2687
- )?
2688
- )
2689
- ;
2690
-
2691
- // Has previously used ATN, now via local token rewrite
2692
- annotationAssignment_atn[ art ] locals[ assignment ]
2693
- @after {
2694
- if ($assignment) {
2695
- this.assignAnnotation( $art, $assignment );
2696
- this.docComment( $art );
2697
- }
2698
- } :
2699
- at='@' { this.reportUnexpectedSpace( $at ); }
2700
- (
2701
- annotationAssignment_paren[ $art ]
2702
- |
2703
- { $assignment = { name: new this.XsnName() }; }
2704
- annotationPath[ $assignment.name, 'anno' ]
2705
- // '#' is in the follow set of this rule, as it is used in rule "selectItemDef"
2706
- // before an "expression" which can start with a '#' for an enum value
2707
- // -> used to introduce variant name if and only if in same line as previous token
2708
- { this.setLocalToken( '#', 'HelperToken1', null, true ); }
2709
- ( hash=HelperToken1 annotationPathVariant[ $assignment.name ] )?
2710
- // ':' is in the follow set of this rule, as it is used in rule "selectItemDef"
2711
- // before an "expression" which can start with a ':' for a parameter reference
2712
- // -> used to introduce assignment value if and only if in same line as previous token
2713
- { this.setLocalToken( ':', 'HelperToken2', null, true ); }
2714
- ( HelperToken2 // ':'
2715
- { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
2716
- (
2717
- val=annoValueBase[ $assignment ]
2718
- |
2719
- atv='@'? annotationPath[ $assignment, 'ref', $atv ]
2720
- { this.setLocalToken( '#', 'HelperToken1', null, true ); } // see above
2721
- ( hash=HelperToken1 annotationPathVariant[ $assignment ] )?
2722
- )
2723
- )?
2724
- )
2725
- ;
2726
-
2727
- annotationAssignment_paren[ art ]
2728
- :
2729
- '('
2730
- // allow completely useless `@()`; no warning anymore - who cares?
2731
- {
2732
- if (this.isStraightBefore(')')) {
2733
- this.matchWildcard(); // we know it is the ')' - we do not reach the final match
2734
- return $ctx;
2735
- }
2736
- this.meltKeywordToIdentifier();
2737
- }
2738
- annotationAssignment_1[ $art ]
2739
- ( ','
2740
- {
2741
- this.meltKeywordToIdentifier();
2742
- if (this.isStraightBefore(')')) break; // allow ',' before ')'
2743
- }
2744
- annotationAssignment_1[ $art ]
2745
- )*
2746
- ')'
2747
- ;
2748
-
2749
- annotationAssignment_1[ art ] locals[ assignment = { name: new parser.XsnName() } ]
2750
- @after { this.assignAnnotation( $art, $assignment ); }
2751
- :
2752
- annotationPath[ $assignment.name, 'anno' ]
2753
- ( '#' annotationPathVariant[ $assignment.name ] )?
2754
- (
2755
- ':' { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
2756
- val=annoValue[ $assignment ]
2757
- )?
2758
- ;
2759
-
2760
- annotationPath[ art, category, headat = null ] locals[ _sync = 'nop' ]
2761
- @after { this.attachLocation($art); }
2762
- // Due to error recovery, rule `ident` can return with value `null`. Set the
2763
- // path as broken in this case.
2764
- :
2765
- head=ident[ $category ]
2766
- { $art.path = []; this.pushIdent( $art.path, $head.id, $headat ); }
2767
- (
2768
- '.' at='@'? tail=ident[ $category ]
2769
- { this.pushIdent( $art.path, $tail.id, $at );
2770
- // Otherwise, $at may continue to be set after one `.@anno` segment.
2771
- $ctx.at = null;
2772
- }
2773
- )*
2774
- ;
2775
-
2776
- // Before calling this rule, match '#'
2777
- annotationPathVariant[ art ] locals[ variant = {} ]
2778
- @after { this.attachLocation($art); }
2779
- :
2780
- { this.reportUnexpectedSpace();; this.meltKeywordToIdentifier(); }
2781
- simplePath[ $variant, 'variant' ] { $art.variant = $variant; }
2782
- ;
2783
-
2784
- annoValue[ assignment ]
2785
- :
2786
- base=annoValueBase[ $assignment ]
2787
- |
2788
- // no docComment() here
2789
- // this alternative is done with token rewrite in rule "annotationAssignment_atn"
2790
- at='@'? annotationPath[ $assignment, 'ref', $at ]
2791
- ( '#' annotationPathVariant[ $assignment ] )?
2792
- ;
2793
-
2794
- annoValueBase[ assignment ] locals [ seenEllipsis = false ]
2795
- @after { this.attachLocation( $assignment ); }
2796
- :
2797
- '{' // no location here, we flatten
2798
- { $assignment['$'+'flatten'] = []; this.meltKeywordToIdentifier(); }
2799
- flattenedValue[ $assignment ]
2800
- (
2801
- ',' {
2802
- this.meltKeywordToIdentifier();
2803
- if (this.isStraightBefore("}")) break; // allow ',' before ')'
2804
- }
2805
- flattenedValue[ $assignment ]
2806
- )*
2807
- '}'
2808
- |
2809
- '[' // no need for createArray() here, $assignment.location is set
2810
- { $assignment.val = []; $assignment.literal = 'array'; }
2811
- { this.meltKeywordToIdentifier(true); }
2812
- (
2813
- (
2814
- head=annoSubValue { $assignment.val.push( $head.val ); }
2815
- |
2816
- e='...' ( UP TO upTo=annoSubValue )?
2817
- {{
2818
- const item = { literal: 'token', val: '...', location: this.tokenLocation($e) };
2819
- $assignment.val.push( item );
2820
- if ($ctx.upTo) item.upTo = $upTo.val;
2821
- $seenEllipsis = !$ctx.upTo || 'upTo';
2822
- }}
2823
- )
2824
- (
2825
- ',' { if (this.isStraightBefore(']')) break; } // allow ',' before ']'
2826
- { this.meltKeywordToIdentifier(true); }
2827
- (
2828
- tail=annoSubValue { $assignment.val.push( $tail.val ); }
2829
- |
2830
- { $ctx.upTo = null; } // is not reset
2831
- e='...' ( UP TO upTo=annoSubValue )?
2832
- {{
2833
- const item = { literal: 'token', val: '...', location: this.tokenLocation($e) };
2834
- if ($ctx.upTo) item.upTo = $upTo.val;
2835
- $assignment.val.push( item );
2836
- if ($seenEllipsis === true)
2837
- this.error( 'syntax-unexpected-ellipsis', $e,
2838
- { '#': 'duplicate', code: '...', keyword: 'up to' } );
2839
- else
2840
- $seenEllipsis = !$ctx.upTo || 'upTo';
2841
- }}
2842
- )
2843
- )*
2844
- )?
2845
- cb=']'
2846
- {
2847
- if ($seenEllipsis === 'upTo')
2848
- this.error( 'syntax-missing-ellipsis', $cb, // at closing bracket
2849
- { code: '... up to', newcode: '...' } );
2850
- }
2851
- |
2852
- v1=literalValue { Object.assign( $assignment, $v1.val ); }
2853
- |
2854
- ( plus='+' | min='-' ) num=Number
2855
- { Object.assign( $assignment, this.numberLiteral( $num, $plus||$min ) ); }
2856
- |
2857
- '('
2858
- cond=condition // 'condition' is also used in 'expression' inside '()'.
2859
- // TODO: (1,2,3) not supported, yet, only ((1,2,3)); we could support it via:
2860
- // (',' condition)*
2861
- { this.expressionAsAnnotationValue( $assignment, $ctx.cond ); }
2862
- ')'
2863
- ;
2864
-
2865
- flattenedValue[ assignment ] locals[ val = { name: new parser.XsnName() } ]
2866
- :
2867
- at='@'? annotationPath[ $val.name, 'name', $at ]
2868
- ( '#' annotationPathVariant[ $val.name ] )?
2869
- (
2870
- ':' { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
2871
- annoValue[ $val ]
2872
- )?
2873
- { $assignment['$'+'flatten'].push( $val ); }
2874
- ;
2875
-
2876
- namedValue[ struct ] locals[ val = { name: new parser.XsnName() } ]
2877
- :
2878
- at='@'? annotationPath[ $val.name, 'name', $at ]
2879
- ( ':' { this.meltKeywordToIdentifier(true); }
2880
- sub=annoSubValue { Object.assign( $val, $sub.val ); } )?
2881
- {
2882
- if (!$val.location) $val.location = $val.name.location;
2883
- this.addDef( $val, $struct, 'struct', null, $val.name ); // TODO: re-check name
2884
- }
2885
- ;
2886
-
2887
- annoSubValue returns[ val = {} ]
2888
- @after { this.attachLocation($val); }
2889
- :
2890
- '{' // no need for createDict() here, $val.location is set
2891
- { $val.struct = Object.create(null); $val.literal = 'struct'; }
2892
- { this.meltKeywordToIdentifier(); }
2893
- namedValue[ $val ]
2894
- ( ','
2895
- {
2896
- this.meltKeywordToIdentifier();
2897
- if (this.isStraightBefore("}")) break; // allow ',' before '}'
2898
- }
2899
- namedValue[ $val ]
2900
- )*
2901
- '}'
2902
- |
2903
- '[' // no need for createArray() here, $val.location is set
2904
- { $val.val = []; $val.literal = 'array'; }
2905
- { this.meltKeywordToIdentifier(true); }
2906
- ( head=annoSubValue { $val.val.push( $head.val ); }
2907
- ( ',' { if (this.isStraightBefore(']')) break; } // allow ',' before ']'
2908
- { this.meltKeywordToIdentifier(true); }
2909
- tail=annoSubValue { $val.val.push( $tail.val ); }
2910
- )*
2911
- )?
2912
- ']'
2913
- |
2914
- v1=literalValue { Object.assign( $val, $v1.val ); }
2915
- |
2916
- ( plus='+' | min='-' ) num=Number
2917
- { Object.assign( $val, this.numberLiteral( $num, $plus||$min ) ); }
2918
- |
2919
- at='@'? annotationPath[ $val, 'ref', $at ]
2920
- ( '#' annotationPathVariant[ $val ] )?
2921
- |
2922
- '('
2923
- cond=condition
2924
- { this.expressionAsAnnotationValue( $val, $ctx.cond ); }
2925
- ')'
2926
- ;
2927
-
2928
- // Literal values and identifiers -----------------------------------------------
2929
-
2930
- literalValue returns[ val ] locals[ tok ]
2931
- @init{ $tok = this.getCurrentToken(); }
2932
- @after { this.attachLocation($val); }
2933
- :
2934
- hash='#' { this.reportUnexpectedSpace( $hash );; this.meltKeywordToIdentifier(); }
2935
- name=ident['enumref'] // TODO v4: remove from this rule (not in enum!)
2936
- { $val = { literal: 'enum', sym: $name.id } }
2937
- |
2938
- NULL
2939
- { $val = { literal: 'null', val: null }; }
2940
- |
2941
- Boolean
2942
- { $val = { literal: 'boolean', val: $tok.text.toLowerCase() != 'false' }; }
2943
- |
2944
- Number
2945
- { $val = this.numberLiteral( $tok, '' ); } // allow float and large number
2946
- |
2947
- String
2948
- { $val = this.quotedLiteral( $tok, 'string' ); }
2949
- |
2950
- QuotedLiteral // x'12', date'...', time'...', timestamp'...'
2951
- { $val = this.quotedLiteral( $tok ); }
2952
- ;
2953
-
2954
- // #IDENT - keep this comment here, used in scripts/linter/lintGrammar.js
2955
-
2956
- identNoKeyword[ category ] returns[ id ] // for aliases without AS
2957
- @after{ $id = this.identAst( $stop, $category ); }
2958
- :
2959
- Identifier
2960
- ;
2961
-
2962
- // The `ident` rule matches `Identifier` and all non-reserved keywords. List
2963
- // all non-reserved keywords directly, do not use an indirection via a rule
2964
- // like `nonReservedKeywords`.
2965
- ident[ category ] returns[ id ]
2966
- @after{ $id = this.identAst( $stop, $category ); }
2967
- :
2968
- Identifier
2969
- | ABSTRACT
2970
- | ACTION
2971
- | ACTIONS
2972
- | AND
2973
- | ANNOTATE
2974
- | ANNOTATION
2975
- | ARRAY
2976
- | ASC
2977
- | ASPECT
2978
- | ASSOCIATION
2979
- | BETWEEN
2980
- | COLUMNS
2981
- | COMPOSITION
2982
- | CONTEXT
2983
- | CROSS
2984
- | CURRENT
2985
- | DEFAULT
2986
- | DEFINE
2987
- | DEFINITIONS
2988
- | DESC
2989
- | ELEMENTS
2990
- | ELSE
2991
- | END
2992
- | ENTITY
2993
- | ENUM
2994
- | ESCAPE
2995
- | EVENT
2996
- | EXACT
2997
- | EXCEPT
2998
- | EXCLUDING
2999
- | EXTEND
3000
- | FIRST
3001
- | FLOATING
3002
- | FOLLOWING
3003
- | FULL
3004
- | FUNCTION
3005
- | GROUP
3006
- | HAVING
3007
- | INNER
3008
- | INTERSECT
3009
- | INTO
3010
- | IS
3011
- | JOIN
3012
- | LAST
3013
- | LEFT
3014
- | LIKE
3015
- | LIMIT
3016
- | LOCALIZED
3017
- | MANY
3018
- | MINUS
3019
- | MIXIN
3020
- | NAMESPACE
3021
- | NULLS
3022
- | OFFSET
3023
- | ONE
3024
- | OR
3025
- | ORDER
3026
- | OUTER
3027
- | PARAMETERS
3028
- | PARTITION
3029
- | PRECEDING
3030
- | PROJECTION
3031
- | REDIRECTED
3032
- | RETURNS
3033
- | RIGHT
3034
- | ROW
3035
- | ROWS
3036
- | STORED
3037
- | SERVICE
3038
- | THEN
3039
- | UNION
3040
- | UP
3041
- | TO
3042
- | TYPE
3043
- | USING
3044
- | UNBOUNDED
3045
- | VARIABLE
3046
- | VIEW
3047
- ;
3048
-
3049
- // LEXER ------------------------------------------------------------------------
3050
-
3051
- WhiteSpace // like \s in JavaScript RegExp
3052
- : // LineTerminator | [\t\f\v\u00A0\uFEFF] | Zs
3053
- [\r\n\u2028\u2029 \t\f\u000B\u00A0\u1680\u180e\u2000-\u200A\u202F\u205F\u3000\uFEFF]+
3054
- -> skip ;
3055
-
3056
- DocComment : '/**' .*? '*/' -> channel(HIDDEN);
3057
-
3058
- Comment : '/*' .*? '*/' -> channel(HIDDEN);
3059
-
3060
- LineComment : '//' ~[\r\n\u2028\u2029]* -> channel(HIDDEN);
3061
-
3062
- // Literal values ---------------------------------------------------------------
3063
-
3064
- // for syntactic code-completion: Combine all three string styles
3065
- // Note: Use rule `string` instead as that also parses escape sequences! (TODO: ???)
3066
- String : SingleLineString
3067
- | MultiLineString
3068
- | MutlLineStringBlock;
3069
-
3070
- fragment SingleLineString
3071
- :
3072
- // \u0027 = '\''
3073
- // \u2028 = LS (Line Separator)
3074
- // \u2029 = PS (Paragraph Separator)
3075
- ( '\'' ~[\u0027\n\r\u2028\u2029]* '\'' )+ //
3076
- ;
3077
-
3078
- fragment MultiLineString
3079
- :
3080
- ('`' ( MultiLineStringContentChar | EscapeSequence )* '`' )
3081
- ;
3082
-
3083
- fragment MutlLineStringBlock
3084
- :
3085
- ('```' ( MultiLineStringContentChar | EscapeSequence )* '```')
3086
- ;
3087
-
3088
- fragment EscapeSequence
3089
- :
3090
- // we could list each escape sequence explicitly, but we already
3091
- // decode them in genericAntlrParser.js, so no need to do work twice.
3092
- '\\' .
3093
- ;
3094
-
3095
- fragment MultiLineStringContentChar
3096
- :
3097
- (~[\u0060\\]) // \u0060 = '`'
3098
- ;
3099
-
3100
- QuotedLiteral
3101
- :
3102
- ( [xX] | [dD][aA][tT][eE] | [tT][iI][mM][eE] ( [sS][tT][aA][mM][pP] )? )
3103
- ( '\'' ~[\u0027\n\r\u2028\u2029]* '\'' )+ // \u0027 = '\''
3104
- ;
3105
-
3106
- // This literal improves error messages for unterminated literals.
3107
- UnterminatedLiteral
3108
- :
3109
- ( [xX] | [dD][aA][tT][eE] | [tT][iI][mM][eE] ( [sS][tT][aA][mM][pP] )? )?
3110
- '\'' ~[\u0027\n\r\u2028\u2029]* // \u0027 = '\''
3111
- |
3112
- ('`' ( MultiLineStringContentChar | EscapeSequence )* )
3113
- |
3114
- ('```' ( MultiLineStringContentChar | EscapeSequence )* )
3115
- ;
3116
-
3117
- UnterminatedDelimitedIdentifier
3118
- :
3119
- '"' ~[\u0022\n\r\u2028\u2029]* ( '""' ~[\u0022\n\r\u2028\u2029]* )* // \u0022 = '"'
3120
- | '![' ~[\u005d\n\r\u2028\u2029]* ( ']]' ~[\u005d\n\r\u2028\u2029]* )* // \u005d = ']'
3121
- // \u005d = ']'
3122
- ;
3123
-
3124
- Boolean // TMP?
3125
- : [tT][rR][uU][eE] | [fF][aA][lL][sS][eE]
3126
- ;
3127
-
3128
- // Reserved keywords (are case-insensitive): ---------------------------------
3129
-
3130
- ALL : [aA][lL][lL] ;
3131
- ANY : [aA][nN][yY] ;
3132
- AS : [aA][sS] ;
3133
- BY : [bB][yY] ;
3134
- CASE : [cC][aA][sS][eE] ;
3135
- CAST : [cC][aA][sS][tT] ;
3136
- DISTINCT : [dD][iI][sS][tT][iI][nN][cC][tT] ;
3137
- EXISTS : [eE][xX][iI][sS][tT][sS] ;
3138
- // FALSE: see Boolean
3139
- FROM : [fF][rR][oO][mM] ;
3140
- IN : [iI][nN] ;
3141
- KEY : [kK][eE][yY] ;
3142
- NEW : [nN][eE][wW] ; // token rewrite for NEW -> not reserved (also not in SQL)
3143
- NOT : [nN][oO][tT] ;
3144
- NULL : [nN][uU][lL][lL] ;
3145
- OF : [oO][fF] ;
3146
- ON : [oO][nN] ;
3147
- SELECT : [sS][eE][lL][eE][cC][tT] ;
3148
- SOME : [sS][oO][mM][eE] ;
3149
- WHEN : [wW][hH][eE][nN] ;
3150
- // TRUE: see Boolean
3151
- WHERE : [wW][hH][eE][rR][eE] ;
3152
- WITH : [wW][iI][tT][hH] ;
3153
-
3154
- // Fixed Token which is defined DIRECTLY BEFORE the unreserved keywords ------
3155
-
3156
- Number // DO NOT RENAME OR MOVE THIS RULE !!!
3157
- : [0-9]+ // no initial sign
3158
- ( '.' [0-9]+ )?
3159
- ( [eE] ('+'|'-')? [0-9]+ )?
3160
- ;
3161
-
3162
- // Unreserved keywords (are case-insensitive): -------------------------------
3163
- // Do not add keywords just for specialFunctions!
3164
-
3165
- ABSTRACT : [aA][bB][sS][tT][rR][aA][cC][tT] ;
3166
- ACTION : [aA][cC][tT][iI][oO][nN] ;
3167
- ACTIONS : [aA][cC][tT][iI][oO][nN][sS] ;
3168
- AND : [aA][nN][dD] ;
3169
- ANNOTATE : [aA][nN][nN][oO][tT][aA][tT][eE] ;
3170
- ANNOTATION : [aA][nN][nN][oO][tT][aA][tT][iI][oO][nN] ;
3171
- ARRAY : [aA][rR][rR][aA][yY] ;
3172
- ASC : [aA][sS][cC] ;
3173
- ASPECT : [aA][sS][pP][eE][cC][tT] ;
3174
- ASSOCIATION : [aA][sS][sS][oO][cC][iI][aA][tT][iI][oO][nN] ;
3175
- BETWEEN : [bB][eE][tT][wW][eE][eE][nN] ;
3176
- COLUMNS : [cC][oO][lL][uU][mM][nN][sS];
3177
- COMPOSITION : [cC][oO][mM][pP][oO][sS][iI][tT][iI][oO][nN] ;
3178
- CONTEXT : [cC][oO][nN][tT][eE][xX][tT] ;
3179
- CROSS : [cC][rR][oO][sS][sS] ;
3180
- CURRENT : [cC][uU][rR][rR][eE][nN][tT] ;
3181
- DEFAULT : [dD][eE][fF][aA][uU][lL][tT] ;
3182
- DEFINE : [dD][eE][fF][iI][nN][eE] ;
3183
- DEFINITIONS : [dD][eE][fF][iI][nN][iI][tT][iI][oO][nN][sS] ;
3184
- DESC : [dD][eE][sS][cC] ;
3185
- // ELEMENT : [eE][lL][eE][mM][eE][nN][tT] ;
3186
- ELEMENTS : [eE][lL][eE][mM][eE][nN][tT][sS] ;
3187
- ELSE : [eE][lL][sS][eE] ;
3188
- END : [eE][nN][dD] ;
3189
- ENTITY : [eE][nN][tT][iI][tT][yY] ;
3190
- ENUM : [eE][nN][uU][mM] ;
3191
- EVENT : [eE][vV][eE][nN][tT] ;
3192
- ESCAPE : [eE][sS][cC][aA][pP][eE] ;
3193
- EXACT : [eE][xX][aA][cC][tT] ;
3194
- EXCEPT : [eE][xX][cC][eE][pP][tT] ;
3195
- EXCLUDING : [eE][xX][cC][lL][uU][dD][iI][nN][gG] ;
3196
- EXTEND : [eE][xX][tT][eE][nN][dD] ;
3197
- FIRST : [fF][iI][rR][sS][tT] ;
3198
- FLOATING : [fF][lL][oO][aA][tT][iI][nN][gG] ;
3199
- FOLLOWING : [fF][oO][lL][lL][oO][wW][iI][nN][gG] ;
3200
- FULL : [fF][uU][lL][lL] ;
3201
- FUNCTION : [fF][uU][nN][cC][tT][iI][oO][nN] ;
3202
- GROUP : [gG][rR][oO][uU][pP] ;
3203
- HAVING : [hH][aA][vV][iI][nN][gG] ;
3204
- INNER : [iI][nN][nN][eE][rR] ;
3205
- INTERSECT : [iI][nN][tT][eE][rR][sS][eE][cC][tT] ;
3206
- INTO : [iI][nN][tT][oO] ;
3207
- IS : [iI][sS] ;
3208
- JOIN : [jJ][oO][iI][nN] ;
3209
- LAST : [lL][aA][sS][tT] ;
3210
- LEFT : [lL][eE][fF][tT] ;
3211
- LIKE : [lL][iI][kK][eE] ;
3212
- LIMIT : [lL][iI][mM][iI][tT] ;
3213
- LOCALIZED: [lL][oO][cC][aA][lL][iI][zZ][eE][dD];
3214
- MANY : [mM][aA][nN][yY] ;
3215
- // MASKED : [mM][aA][sS][kK][eE][dD] ;
3216
- MINUS : [mM][iI][nN][uU][sS] ;
3217
- MIXIN : [mM][iI][xX][iI][nN] ;
3218
- NAMESPACE : [nN][aA][mM][eE][sS][pP][aA][cC][eE] ;
3219
- NULLS : [nN][uU][lL][lL][sS] ;
3220
- OFFSET : [oO][fF][fF][sS][eE][tT] ;
3221
- ONE : [oO][nN][eE] ;
3222
- OR : [oO][rR] ;
3223
- ORDER : [oO][rR][dD][eE][rR] ;
3224
- OUTER : [oO][uU][tT][eE][rR] ;
3225
- // OVER : [oO][vV][eE][rR] ;
3226
- PARAMETERS : [pP][aA][rR][aA][mM][eE][tT][eE][rR][sS] ;
3227
- PARTITION: [pP][aA][rR][tT][iI][tT][iI][oO][nN] ;
3228
- PRECEDING: [pP][rR][eE][cC][eE][dD][iI][nN][gG] ;
3229
- PROJECTION : [pP][rR][oO][jJ][eE][cC][tT][iI][oO][nN] ;
3230
- REDIRECTED : [rR][eE][dD][iI][rR][eE][cC][tT][eE][dD] ;
3231
- RETURNS : [rR][eE][tT][uU][rR][nN][sS] ;
3232
- RIGHT : [rR][iI][gG][hH][tT] ;
3233
- ROW : [rR][oO][wW] ;
3234
- ROWS : [rR][oO][wW][sS] ;
3235
- SERVICE : [sS][eE][rR][vV][iI][cC][eE] ;
3236
- STORED : [sS][tT][oO][rR][eE][dD] ;
3237
- THEN : [tT][hH][eE][nN] ;
3238
- TO : [tT][oO] ; // or make reserved? (is in SQL-92)
3239
- TYPE : [tT][yY][pP][eE] ;
3240
- UNION : [uU][nN][iI][oO][nN] ;
3241
- UNBOUNDED : [uU][nN][bB][oO][uU][nN][dD][eE][dD] ;
3242
- UP : [uU][pP] ;
3243
- USING : [uU][sS][iI][nN][gG] ;
3244
- VARIABLE : [vV][aA][rR][iI][aA][bB][lL][eE] ;
3245
- VIEW : [vV][iI][eE][wW] ;
3246
- // VIRTUAL: [vV][iI][rR][tT][uU][aA][lL] ; see tokens {}
3247
-
3248
- // Identifiers, must BE LAST, DIRECTLY AFTER the unreserved keywords ---------
3249
-
3250
- Identifier // DO NOT RENAME OR MOVE THIS RULE !!!
3251
- : [$_a-zA-Z][$_a-zA-Z0-9]* // i.e. including $param
3252
- | ( '"' ~[\u0022\n\r\u2028\u2029]* '"' )+ // \u0022 = '"'
3253
- | '![' ~[\u005d\n\r\u2028\u2029]* ']' ( ']' ~[\u005d\n\r\u2028\u2029]* ']' )* // \u005d = ']'
3254
- ;
3255
-
3256
- IllegalToken : . ;
3257
-
3258
- // Local Variables:
3259
- // c-basic-offset: 2
3260
- // End: