@sap/cds-compiler 3.4.4 → 3.5.2

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 (129) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +1 -0
  3. package/bin/cds_update_identifiers.js +5 -5
  4. package/bin/cdsc.js +12 -12
  5. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  6. package/doc/CHANGELOG_BETA.md +9 -1
  7. package/doc/CHANGELOG_DEPRECATED.md +2 -0
  8. package/lib/api/main.js +58 -59
  9. package/lib/api/options.js +4 -2
  10. package/lib/api/validate.js +2 -2
  11. package/lib/base/cleanSymbols.js +2 -3
  12. package/lib/base/dictionaries.js +6 -6
  13. package/lib/base/error.js +2 -2
  14. package/lib/base/keywords.js +6 -6
  15. package/lib/base/location.js +11 -12
  16. package/lib/base/message-registry.js +124 -28
  17. package/lib/base/messages.js +247 -179
  18. package/lib/base/model.js +14 -11
  19. package/lib/base/node-helpers.js +9 -10
  20. package/lib/base/optionProcessorHelper.js +138 -129
  21. package/lib/checks/actionsFunctions.js +5 -5
  22. package/lib/checks/annotationsOData.js +4 -4
  23. package/lib/checks/arrayOfs.js +1 -1
  24. package/lib/checks/cdsPersistence.js +1 -1
  25. package/lib/checks/checkForTypes.js +3 -3
  26. package/lib/checks/defaultValues.js +3 -3
  27. package/lib/checks/elements.js +7 -7
  28. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  29. package/lib/checks/foreignKeys.js +1 -1
  30. package/lib/checks/invalidTarget.js +4 -4
  31. package/lib/checks/managedInType.js +1 -1
  32. package/lib/checks/managedWithoutKeys.js +1 -1
  33. package/lib/checks/nonexpandableStructured.js +5 -3
  34. package/lib/checks/nullableKeys.js +1 -1
  35. package/lib/checks/onConditions.js +5 -6
  36. package/lib/checks/parameters.js +1 -1
  37. package/lib/checks/queryNoDbArtifacts.js +2 -2
  38. package/lib/checks/selectItems.js +4 -4
  39. package/lib/checks/sql-snippets.js +4 -4
  40. package/lib/checks/types.js +7 -7
  41. package/lib/checks/utils.js +4 -4
  42. package/lib/checks/validator.js +16 -13
  43. package/lib/compiler/.eslintrc.json +1 -1
  44. package/lib/compiler/assert-consistency.js +0 -1
  45. package/lib/compiler/builtins.js +1 -1
  46. package/lib/compiler/checks.js +73 -15
  47. package/lib/compiler/define.js +3 -7
  48. package/lib/compiler/extend.js +212 -32
  49. package/lib/compiler/finalize-parse-cdl.js +7 -2
  50. package/lib/compiler/index.js +17 -14
  51. package/lib/compiler/populate.js +2 -5
  52. package/lib/compiler/propagator.js +2 -0
  53. package/lib/compiler/shared.js +23 -12
  54. package/lib/compiler/tweak-assocs.js +5 -6
  55. package/lib/compiler/utils.js +6 -0
  56. package/lib/edm/annotations/genericTranslation.js +553 -319
  57. package/lib/edm/annotations/preprocessAnnotations.js +39 -35
  58. package/lib/edm/csn2edm.js +88 -75
  59. package/lib/edm/edm.js +17 -3
  60. package/lib/edm/edmAnnoPreprocessor.js +5 -5
  61. package/lib/edm/edmPreprocessor.js +106 -76
  62. package/lib/edm/edmUtils.js +41 -2
  63. package/lib/gen/Dictionary.json +34 -0
  64. package/lib/gen/language.checksum +1 -1
  65. package/lib/gen/language.interp +66 -63
  66. package/lib/gen/language.tokens +81 -81
  67. package/lib/gen/languageLexer.interp +4 -10
  68. package/lib/gen/languageLexer.js +854 -869
  69. package/lib/gen/languageLexer.tokens +79 -81
  70. package/lib/gen/languageParser.js +14360 -14146
  71. package/lib/inspect/inspectModelStatistics.js +2 -2
  72. package/lib/inspect/inspectPropagation.js +6 -6
  73. package/lib/inspect/inspectUtils.js +2 -2
  74. package/lib/json/from-csn.js +82 -40
  75. package/lib/json/to-csn.js +82 -157
  76. package/lib/language/.eslintrc.json +1 -4
  77. package/lib/language/genericAntlrParser.js +59 -38
  78. package/lib/language/language.g4 +1508 -1490
  79. package/lib/language/multiLineStringParser.js +1 -1
  80. package/lib/main.js +3 -3
  81. package/lib/model/csnUtils.js +130 -122
  82. package/lib/model/revealInternalProperties.js +1 -1
  83. package/lib/model/sortViews.js +4 -6
  84. package/lib/modelCompare/utils/filter.js +4 -3
  85. package/lib/optionProcessor.js +5 -0
  86. package/lib/render/DuplicateChecker.js +1 -1
  87. package/lib/render/manageConstraints.js +12 -12
  88. package/lib/render/toCdl.js +225 -159
  89. package/lib/render/toHdbcds.js +63 -63
  90. package/lib/render/toRename.js +5 -5
  91. package/lib/render/toSql.js +55 -65
  92. package/lib/render/utils/common.js +20 -37
  93. package/lib/render/utils/delta.js +3 -3
  94. package/lib/render/utils/sql.js +22 -6
  95. package/lib/render/utils/stringEscapes.js +3 -3
  96. package/lib/transform/db/applyTransformations.js +3 -3
  97. package/lib/transform/db/assertUnique.js +13 -12
  98. package/lib/transform/db/associations.js +5 -5
  99. package/lib/transform/db/cdsPersistence.js +10 -8
  100. package/lib/transform/db/constraints.js +14 -14
  101. package/lib/transform/db/expansion.js +20 -22
  102. package/lib/transform/db/flattening.js +24 -42
  103. package/lib/transform/db/groupByOrderBy.js +3 -3
  104. package/lib/transform/db/temporal.js +6 -6
  105. package/lib/transform/db/transformExists.js +23 -23
  106. package/lib/transform/db/views.js +16 -16
  107. package/lib/transform/draft/db.js +10 -10
  108. package/lib/transform/draft/odata.js +2 -2
  109. package/lib/transform/forOdataNew.js +12 -40
  110. package/lib/transform/forRelationalDB.js +17 -7
  111. package/lib/transform/localized.js +2 -2
  112. package/lib/transform/odata/toFinalBaseType.js +41 -27
  113. package/lib/transform/odata/typesExposure.js +106 -62
  114. package/lib/transform/parseExpr.js +209 -106
  115. package/lib/transform/transformUtilsNew.js +2 -2
  116. package/lib/transform/translateAssocsToJoins.js +24 -19
  117. package/lib/transform/universalCsn/coreComputed.js +10 -10
  118. package/lib/transform/universalCsn/universalCsnEnricher.js +26 -26
  119. package/lib/transform/universalCsn/utils.js +3 -3
  120. package/lib/utils/file.js +5 -5
  121. package/lib/utils/moduleResolve.js +13 -13
  122. package/lib/utils/objectUtils.js +6 -6
  123. package/lib/utils/term.js +5 -2
  124. package/lib/utils/timetrace.js +51 -24
  125. package/package.json +5 -7
  126. package/share/messages/check-proper-type-of.md +1 -1
  127. package/share/messages/message-explanations.json +1 -1
  128. package/share/messages/redirected-to-complex.md +4 -4
  129. package/share/messages/{syntax-expecting-integer.md → syntax-expecting-unsigned-int.md} +7 -4
@@ -120,6 +120,8 @@ options {
120
120
  superClass = genericAntlrParser;
121
121
  }
122
122
  tokens {
123
+ ELEMENT, // used with setLocalToken()
124
+ MASKED, // used with setLocalToken()
123
125
  VIRTUAL, // used with setLocalToken()
124
126
  OVER, // used with setLocalTokenIfBefore()
125
127
  HelperToken1, // used with setLocalToken(), does not appear in messages
@@ -132,6 +134,20 @@ tokens {
132
134
  COMPOSITIONofBRACE // via token rewrite in rule typeAssociationBase
133
135
  }
134
136
 
137
+ // Content:
138
+ // - top-level: USING, NAMESPACE, artifactDefOrExtend (start rule: start)
139
+ // - main definitions and annotation def
140
+ // - member definitions
141
+ // - EXTEND and ANNOTATE
142
+ // - type expressions
143
+ // - queries: the main query hierarchy (start rule: queryEOF)
144
+ // - queries: columns and other clauses
145
+ // - conditions and expressions (start rule: conditionEOF)
146
+ // - paths and functions
147
+ // - annotation assignments
148
+ // - literal values and identifiers
149
+ // - Lexer: spaces, literal values, reserved keywords, unreserved keywords, identifier
150
+
135
151
  // Top-Level -----------------------------------------------------------------
136
152
 
137
153
  start returns [ source ] locals [ _sync = 'recover' ]
@@ -140,24 +156,14 @@ start returns [ source ] locals [ _sync = 'recover' ]
140
156
  usingDeclaration[$source]*
141
157
  (
142
158
  namespaceDeclaration[$source]
143
- ( usingDeclaration[$source] | artifactDef[$source] )*
159
+ ( usingDeclaration[$source] | artifactDefOrExtend[$source] )*
144
160
  |
145
- artifactDef[$source]
146
- ( usingDeclaration[$source] | artifactDef[$source] )*
161
+ artifactDefOrExtend[$source]
162
+ ( usingDeclaration[$source] | artifactDefOrExtend[$source] )*
147
163
  )?
148
164
  EOF
149
165
  ;
150
166
 
151
- queryEOF returns [ query ]
152
- :
153
- q=queryExpression { $query = $q.query; } EOF
154
- ;
155
-
156
- conditionEOF returns [ cond ]
157
- :
158
- c=condition { $cond = $c.cond; } EOF
159
- ;
160
-
161
167
  namespaceDeclaration[ source ] locals[ decl = {} ]
162
168
  @after { $source.namespace = this.attachLocation($decl); }
163
169
  :
@@ -173,13 +179,9 @@ usingDeclaration[ source ] locals[ decl = {} ]
173
179
  FROM str=String
174
180
  { $source.dependencies.push( this.quotedLiteral( $str, 'string' ) ); }
175
181
  |
176
- path=externalPath
177
- { this.addItem( $decl, $source, 'usings', 'using' );; $decl.extern = $path.extern; }
178
- ( AS name=ident['Using'] { $decl.name = $name.id; }
179
- | { this.classifyImplicitName( 'Using' ); }
180
- )
182
+ usingProxy[ $source, $decl ]
181
183
  ( FROM str=String
182
- { $source.dependencies.push( $decl.fileDep = this.quotedLiteral( $str, 'string' ) ); }
184
+ { $source.dependencies.push( $decl.fileDep = this.quotedLiteral( $str, 'string' ) ); }
183
185
  )?
184
186
  |
185
187
  { this.addItem( $decl, $source, 'usings', 'using' ); }
@@ -187,9 +189,9 @@ usingDeclaration[ source ] locals[ decl = {} ]
187
189
  // to have some check in the future whether the external artifacts are
188
190
  // really in the FROM source...
189
191
  '{' { $decl.usings = this.createArray(); }
190
- innerUsing[ $decl ]
192
+ usingProxy[ $decl, {} ]
191
193
  ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
192
- innerUsing[ $decl ] )*
194
+ usingProxy[ $decl, {} ] )*
193
195
  '}' { this.finalizeDictOrArray( $decl.usings ); }
194
196
  ( FROM str=String
195
197
  { $source.dependencies.push( $decl.fileDep = this.quotedLiteral( $str, 'string' ) ); }
@@ -198,170 +200,27 @@ usingDeclaration[ source ] locals[ decl = {} ]
198
200
  ';'
199
201
  ;
200
202
 
201
- innerUsing[ using ] locals[ decl = {} ]
202
- @after { this.attachLocation($decl); }
203
+ usingProxy[ outer, proxy ]
204
+ @after { this.attachLocation($proxy); }
203
205
  :
204
- { $decl.location = this.startLocation(); }
205
- path=externalPath
206
- { this.addItem( $decl, $using, 'usings', 'using' );; $decl.extern = $path.extern; }
207
- ( AS name=ident['Using'] { $decl.name = $name.id; }
206
+ { if (!$proxy.location) $proxy.location = this.startLocation();
207
+ $proxy.extern = {}; }
208
+ simplePath[ $proxy.extern, 'global' ]
209
+ { this.addItem( $proxy, $outer, 'usings', 'using' ); }
210
+ ( AS name=ident['Using'] { $proxy.name = $name.id; }
208
211
  | { this.classifyImplicitName( 'Using' ); }
209
212
  )
210
213
  ;
211
214
 
212
- externalPath returns [ extern = {} ]
213
- :
214
- simplePath[ $extern, 'global' ]
215
- ;
216
-
217
- // We have two versions of the annotation assignment rule, because we do not
218
- // want to let the ambiguity in select items (solution: "either" possibility)
219
- // creep into all annotation assignments:
220
- // view V(p) as select from E { // either: anno value "ref p", select item -x
221
- // @anno :p - x as x; // or: anno value true, select item :p-x
222
- // }
223
-
224
- annotationAssignment_1[ art ] locals[ assignment = { name: {} } ]
225
- @after { this.assignAnnotation( $art, $assignment ); }
226
- :
227
- annotationPath[ $assignment.name, 'anno' ]
228
- annotationPathVariant[ $assignment.name ]?
229
- (
230
- ':' { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
231
- val=annoValue[ $assignment ]
232
- )?
233
- ;
234
-
235
- annotationAssignment_paren[ art ]
236
- :
237
- '('
238
- // allow completely useless `@()` with a warning, do not offer it for completion
239
- {
240
- this.meltKeywordToIdentifier();
241
- if (this.isStraightBefore(')')) {
242
- // TODO: or should we simple accept this without warning? (but still no CC)
243
- this.warning( 'syntax-unexpected-right-paren',
244
- this.tokenLocation( this.getCurrentToken() ),
245
- { offending: "')'", expecting: ['Identifier'], code: '@()' },
246
- 'Unexpected $(OFFENDING), expecting $(EXPECTING); ignoring $(CODE)' );
247
- this.matchWildcard(); // we know it is the ')' - we do not reach the final match
248
- return $ctx;
249
- }
250
- }
251
- annotationAssignment_1[ $art ]
252
- ( ','
253
- {
254
- this.meltKeywordToIdentifier();
255
- if (this.isStraightBefore(')')) break; // allow ',' before ')'
256
- }
257
- annotationAssignment_1[ $art ]
258
- )*
259
- ')'
260
- ;
261
-
262
- annotationAssignment_fix[ art ] locals[ assignment ]
263
- // value outside @(...)
264
- @after {
265
- if ($assignment) {
266
- this.assignAnnotation( $art, $assignment );
267
- this.docComment( $art );
268
- }
269
- } :
270
- '@'
271
- (
272
- annotationAssignment_paren[ $art ]
273
- |
274
- { $assignment = { name: {} }; }
275
- annotationPath[ $assignment.name, 'anno' ]
276
- annotationPathVariant[ $assignment.name ]?
277
- { this.warnIfColonFollows( $assignment ); }
278
- )
279
- ;
280
-
281
- annotationAssignment_ll1[ art ] locals[ assignment ]
282
- @after {
283
- if ($assignment) {
284
- this.assignAnnotation( $art, $assignment );
285
- this.docComment( $art );
286
- }
287
- } :
288
- '@'
289
- (
290
- annotationAssignment_paren[ $art ]
291
- |
292
- { $assignment = { name: {} }; }
293
- annotationPath[ $assignment.name, 'anno' ]
294
- annotationPathVariant[ $assignment.name ]?
295
- (
296
- ':' { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
297
- val=annoValue[ $assignment ]
298
- )?
299
- )
300
- ;
301
-
302
- // Has previously used ATN, now via local token rewrite
303
- annotationAssignment_atn[ art ] locals[ assignment ]
304
- @after {
305
- if ($assignment) {
306
- this.assignAnnotation( $art, $assignment );
307
- this.docComment( $art );
308
- }
309
- } :
310
- '@'
311
- (
312
- annotationAssignment_paren[ $art ]
313
- |
314
- { $assignment = { name: {} }; }
315
- annotationPath[ $assignment.name, 'anno' ]
316
- // '#' is in the follow set of this rule, as it is used in rule "selectItemDef"
317
- // before an "expression" which can start with a '#' for an enum value
318
- // -> used to introduce variant name if and only if in same line as previous token
319
- { this.setLocalToken( '#', 'HelperToken1', null, true ); }
320
- (
321
- HelperToken1 { this.meltKeywordToIdentifier(); }
322
- variant=ident['variant'] { $assignment.name.variant = $variant.id; }
323
- )?
324
- // ':' is in the follow set of this rule, as it is used in rule "selectItemDef"
325
- // before an "expression" which can start with a ':' for a parameter reference
326
- // -> used to introduce assignment value if and only if in same line as previous token
327
- { this.setLocalToken( ':', 'HelperToken2', null, true ); }
328
- ( HelperToken2 // ':'
329
- { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
330
- (
331
- val=annoValueBase[ $assignment ]
332
- |
333
- at='@'? annotationPath[ $assignment, 'ref', $at ]
334
- { this.setLocalToken( '#', 'HelperToken1', null, true ); } // see above
335
- (
336
- HelperToken1 { this.meltKeywordToIdentifier(); }
337
- variant=ident['variant'] { $assignment.variant = $variant.id; }
338
- )?
339
- )
340
- )?
341
- )
342
- ;
343
-
344
- // Main artifact definitions -------------------------------------------------
345
-
346
- requiredSemi
347
- : ';'
348
- | { return $ctx; } // do not actually parse the closing brace
349
- '}'
350
- ;
351
-
352
- optionalSemi
353
- : { this.noAssignmentInSameLine(); } // issue warning for } @Anno \n? NextDef
354
- ';'?
355
- ;
356
-
357
- artifactDef[ outer, defOnly = false ] locals[ art = {} ] // cannot use `parent` as parameter name!
215
+ artifactDefOrExtend[ outer, defOnly = false ] locals[ art = {} ] // cannot use `parent` as parameter name!
358
216
  @after{ /* #ATN 1 */ }
359
217
  :
360
218
  { $art.location = this.startLocation(); this.docComment( $art ); }
361
219
  annotationAssignment_ll1[ $art ]*
362
220
  (
363
221
  DEFINE?
364
- ( contextDef[ $art, $outer, defOnly ]
222
+ ( serviceDef[ $art, $outer, defOnly ]
223
+ | contextDef[ $art, $outer, defOnly ]
365
224
  | entityDef[ $art, $outer ]
366
225
  | typeDef[ $art, $outer ]
367
226
  | aspectDef[ $art, $outer ]
@@ -372,26 +231,27 @@ artifactDef[ outer, defOnly = false ] locals[ art = {} ] // cannot use `parent`
372
231
  )
373
232
  |
374
233
  extend=EXTEND
375
- { if (defOnly)
234
+ { if (defOnly) // TODO: nicer message text
376
235
  this.error( 'syntax-extend-context', $extend,
377
- { code: 'EXTEND artifact', kind: defOnly },
378
- 'No $(CODE) within $(KIND) extensions' );
236
+ { code: 'EXTEND artifact', meta: defOnly },
237
+ 'No $(CODE) within $(META) extensions' );
379
238
  if (!$outer.extensions) $outer.extensions = [];
380
239
  }
381
- // #ATN: EXTEND elem, while CONTEXT, ENTITY etc are not reserved
382
- ( extendContext[ $art, $outer ]
383
- | extendEntity[ $art, $outer ] // or aspect
240
+ // #ATN: EXTEND art, while CONTEXT, ENTITY etc are not reserved
241
+ ( extendService[ $art, $outer ]
242
+ | extendContext[ $art, $outer ]
243
+ | extendEntityOrAspect[ $art, $outer ] // or aspect
384
244
  | extendProjection[ $art, $outer ]
385
245
  | extendType[ $art, $outer ]
386
- // Streamlined Syntax
246
+ // Streamlined Syntax; we won't add more kinds of the non-streamlined variants:
387
247
  | extendArtifact[ $art, $outer ]
388
248
  )
389
249
  |
390
250
  annotate=ANNOTATE
391
- { if (defOnly)
251
+ { if (defOnly) // TODO: improve message text
392
252
  this.error( 'syntax-extend-context', $annotate,
393
- { code: 'ANNOTATE artifact', kind: defOnly },
394
- 'No $(CODE) within $(KIND) extensions' );
253
+ { code: 'ANNOTATE artifact', meta: defOnly },
254
+ 'No $(CODE) within $(META) extensions' );
395
255
  if (!$outer.extensions) $outer.extensions = [];
396
256
  this.meltKeywordToIdentifier();
397
257
  }
@@ -399,16 +259,14 @@ artifactDef[ outer, defOnly = false ] locals[ art = {} ] // cannot use `parent`
399
259
  )
400
260
  ;
401
261
 
402
- contextDef[ art, outer, defOnly = false ] locals[ name = {} ]
262
+ optArtifactsBlock[ art, defOnly = false ]
403
263
  @after { this.attachLocation( $art ); }
404
264
  :
405
- ( CONTEXT | service=SERVICE ) simplePath[ $name, $service ? 'Service' : 'Context' ]
406
- { this.addDef( $art, $outer, 'artifacts', $service ? 'service' : 'context', $name );
407
- this.docComment( $art ); }
265
+ { this.docComment( $art ); }
408
266
  annotationAssignment_fix[ $art ]*
409
267
  (
410
268
  '{' { $art.artifacts = this.createDict(); $art.extensions = []; }
411
- artifactDef[ $art, defOnly ]*
269
+ artifactDefOrExtend[ $art, defOnly ]*
412
270
  '}' { this.finalizeDictOrArray( $art.artifacts ); }
413
271
  optionalSemi
414
272
  |
@@ -416,25 +274,110 @@ contextDef[ art, outer, defOnly = false ] locals[ name = {} ]
416
274
  )
417
275
  ;
418
276
 
419
- extendContext[ art, outer ] locals[ name = {} ]
277
+ requiredSemi
278
+ : ';'
279
+ | { return $ctx; } // do not actually parse the closing brace
280
+ '}'
281
+ ;
282
+
283
+ optionalSemi
284
+ : { this.noAssignmentInSameLine(); } // issue warning for } @Anno \n? NextDef
285
+ ';'?
286
+ ;
287
+
288
+ // Annotation def and main definitions ------------------------------------------
289
+
290
+ annotationDef[ art, outer ] locals[ name = {} ]
420
291
  @after { this.attachLocation( $art ); }
421
292
  :
422
- ( CONTEXT { $art.expectedKind = 'context'; } | service=SERVICE { $art.expectedKind = 'service'; })
423
- simplePath[ $name, $service ? 'Service' : 'Context' ] // not 'Extend' here
424
- { $art.name = $name; this.addItem( $art, $outer, 'extensions', 'extend' ); }
425
- ( WITH { this.noSemicolonHere(); } )?
426
- { this.docComment( $art ); }
427
- annotationAssignment_ll1[ $art ]*
293
+ annotation=ANNOTATION simplePath[ $name, 'AnnoDef' ]
294
+ { if ($outer.kind !== 'source') { // this is a syntax restriction to avoid confusion
295
+ this.error( 'syntax-unexpected-vocabulary', $annotation, { '#': $outer.kind } );
296
+ $art = {}; }
297
+ else {
298
+ if (!$outer.vocabularies) $outer.vocabularies = Object.create(null);
299
+ this.addDef( $art, $outer, 'vocabularies', 'annotation', $name );
300
+ }
301
+ this.docComment( $art ); }
302
+ annotationAssignment_fix[ $art ]*
303
+ typeSpecSemi[ $art ] // also 'includes'...
304
+ ;
305
+
306
+ serviceDef[ art, outer, defOnly = false ] locals[ name = {} ]
307
+ @after { this.attachLocation( $art ); }
308
+ :
309
+ SERVICE simplePath[ $name, 'Service' ]
310
+ { this.addDef( $art, $outer, 'artifacts', 'service', $name ); }
311
+ optArtifactsBlock[ $art, defOnly ]
312
+ ;
313
+
314
+ contextDef[ art, outer, defOnly = false ] locals[ name = {} ]
315
+ @after { this.attachLocation( $art ); }
316
+ :
317
+ CONTEXT simplePath[ $name, 'Context' ]
318
+ { this.addDef( $art, $outer, 'artifacts', 'context', $name ); }
319
+ optArtifactsBlock[ $art, defOnly ]
320
+ ;
321
+
322
+ eventDef[ art, outer ] locals[ name = {} ]
323
+ @after { /* #ATN 1 */ this.attachLocation( $art ); }
324
+ :
325
+ EVENT simplePath[ $name, 'Event' ]
326
+ { this.addDef( $art, $outer, 'artifacts', 'event', $name );
327
+ this.docComment( $art ); }
328
+ annotationAssignment_fix[ $art ]*
428
329
  (
429
- '{' { $art.artifacts = this.createDict(); }
430
- artifactDef[ $art, $service ? 'SERVICE' : 'CONTEXT' ]*
431
- '}' { this.finalizeDictOrArray( $art.artifacts ); }
432
- optionalSemi
330
+ typeStruct[ $art ] optionalSemi
433
331
  |
434
- requiredSemi
332
+ ':'
333
+ // #ATN: includeRef can be / start with PROJECTION
334
+ (
335
+ { $art.type = {}; }
336
+ simplePath[ $art.type, 'artref' ]
337
+ (
338
+ { $art.includes = [ $art.type ]; delete $art.type; }
339
+ ( ',' { if (this.isStraightBefore('{')) break; } // allow ',' before '{' // }}
340
+ includeRef[ $art ]
341
+ )*
342
+ typeStruct[ $art ] optionalSemi
343
+ |
344
+ { this.docComment( $art ); }
345
+ annotationAssignment_ll1[ $art ]*
346
+ requiredSemi
347
+ )
348
+ |
349
+ typeStruct[ $art ] optionalSemi
350
+ |
351
+ qp=projectionSpec
352
+ { $art.query = $qp.query; $art['$'+'syntax'] = 'projection'; }
353
+ optionalSemi // TODO: not fully correct without columns or excluding
354
+ )
435
355
  )
436
356
  ;
437
357
 
358
+ viewDef[ art, outer ] locals[ name = {} ]
359
+ @after { this.attachLocation( $art ); }
360
+ :
361
+ v=VIEW simplePath[ $name, 'Entity' ]
362
+ { $art['$'+'syntax'] = 'view';
363
+ this.addDef( $art, $outer, 'artifacts', 'entity', $name );
364
+ this.docComment( $art ); }
365
+ annotationAssignment_fix[ $art ]*
366
+ (
367
+ parameterListDef[ $art ]
368
+ |
369
+ // TODO: warning deprecated?
370
+ ( HideAlternatives | WITH ) { $art.params = this.createDict(); }
371
+ PARAMETERS
372
+ parameterDef[ $art ]
373
+ ( ',' parameterDef[ $art ] )* // no optional final ',' here
374
+ { this.finalizeDictOrArray( $art.params ); }
375
+ )?
376
+ AS qe=queryExpression { $art.query = $qe.query; }
377
+ // TODO check ANTLR: bad msg with 'view V as'<eof> but 'view V as FOO' is fine
378
+ requiredSemi
379
+ ;
380
+
438
381
  entityDef[ art, outer ] locals[ name = {} ]
439
382
  @after { this.attachLocation( $art ); }
440
383
  :
@@ -453,7 +396,6 @@ entityDef[ art, outer ] locals[ name = {} ]
453
396
  '{' { $art.elements = this.createDict(); }
454
397
  elementDef[ $art ]*
455
398
  '}' { this.finalizeDictOrArray( $art.elements ); }
456
- // TODO: action definitions in a specific section?
457
399
  (
458
400
  ACTIONS '{' { $art.actions = this.createDict(); }
459
401
  actionFunctionDef[ $art ]*
@@ -484,161 +426,48 @@ entityDef[ art, outer ] locals[ name = {} ]
484
426
  )
485
427
  ;
486
428
 
487
- projectionSpec returns[ query ] locals[ src ]
488
- @after { this.attachLocation($query); }
489
- :
490
- proj=PROJECTION ON // FIXME: First draft only, details unclear/unspecified
491
- // now a simplified `tableTerm`:
492
- {
493
- $src = { path: [], scope: 0 };
494
- $query = { op: this.valueWithTokenLocation( 'SELECT', $proj ), from: $src, location: this.startLocation() };
495
- }
496
- fromPath[ $src, 'artref']
497
- ( ':'
498
- { $src.scope = $src.path.length; }
499
- fromPath[ $src, 'ref']
500
- )?
501
- ( AS aliasName=ident['FromAlias'] { $src.name = $aliasName.id } )?
502
- // ANTLR errors are better if we use ( A )? instead of ( A | ):
503
- { if (!$src.name) this.classifyImplicitName( $src.scope ? 'FromAlias' : 'Without' ); }
504
- bracedSelectItemListDef[ $query, 'columns' ]?
505
- excludingClause[ $query ]?
506
- ;
507
-
508
- projectionClauses[ query ]
509
- @after { this.attachLocation($query); }
510
- :
511
- ( WHERE cond=condition { $query.where = $cond.cond; } )?
512
- (
513
- GROUP BY
514
- e1=expression { $query.groupBy = [ $e1.expr ]; }
515
- ( ',' en=expression { $query.groupBy.push( $en.expr ); } )*
516
- )?
517
- ( HAVING having=condition { $query.having = $having.cond; } )?
518
- ( ob=orderByClause[ $query ] { $query = $ob.query; } ) ?
519
- ( lc=limitClause[ $query ] { $query = $lc.query; } ) ?
520
- ;
521
-
522
- excludingClause[ query ]
523
- :
524
- // syntax is less than ideal - EXCLUDING is only useful for `*` - with
525
- // this syntax, people wonder what happens with explicit select items
526
- EXCLUDING '{' { $query.excludingDict = this.createDict(); }
527
- projectionExclusion[ $query ]
528
- ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
529
- projectionExclusion[ $query ]
530
- )*
531
- '}' { this.finalizeDictOrArray( $query.excludingDict ); }
532
- ;
533
-
534
- projectionExclusion[ outer ] locals[ art = {} ]
535
- @after { this.attachLocation($art); }
536
- :
537
- name=ident['ref']
538
- { this.addDef( $art, $outer, 'excludingDict', '', $name.id ); }
539
- ;
540
-
541
- // also used for aspect
542
- extendEntity[ art, outer ] locals[ name = {} ]
543
- @after { /* #ATN 1 */ this.attachLocation( $art ); }
544
- :
545
- kind=(ASPECT | ENTITY) simplePath[ $name, 'Extend' ]
546
- { $art.expectedKind = $kind.text.toLowerCase(); $art.name = $name;
547
- this.addItem( $art, $outer, 'extensions', 'extend' );
548
- }
549
- (
550
- WITH { this.noSemicolonHere(); this.docComment( $art ); }
551
- annotationAssignment_ll1[ $art ]*
552
- // ATN: the ref can start with ACTIONS
553
- (
554
- includeRef[ $art ] ( ',' includeRef[ $art ] )*
555
- requiredSemi
556
- |
557
- extendForEntity[ $art ]
558
- )
559
- |
560
- { this.docComment( $art ); }
561
- annotationAssignment_ll1[ $art ]*
562
- extendForEntity[ $art ]
563
- )
564
- ;
565
-
566
- extendForEntity[ art ]
567
- :
568
- '{' { $art.elements = this.createDict(); }
569
- elementDefOrExtend[ $art ]*
570
- '}' { this.finalizeDictOrArray( $art.elements ); }
571
- (
572
- ACTIONS '{' { $art.actions = this.createDict(); }
573
- actionFunctionDef[ $art ]*
574
- '}' { this.finalizeDictOrArray( $art.actions ); }
575
- )?
576
- optionalSemi
577
- |
578
- ACTIONS '{' { $art.actions = this.createDict(); }
579
- actionFunctionDef[ $art ]*
580
- '}' { this.finalizeDictOrArray( $art.actions ); }
581
- optionalSemi
582
- |
583
- requiredSemi
584
- ;
585
-
586
- extendProjection[ art, outer ] locals[ name = {} ]
429
+ aspectDef[ art, outer ] locals[ name = {} ]
587
430
  @after { this.attachLocation( $art ); }
588
431
  :
589
- expected=PROJECTION simplePath[ $name, 'Extend' ]
590
- { $art.expectedKind = 'entity'; $art.name = $name;
591
- this.addItem( $art, $outer, 'extensions', 'extend' );
592
- }
593
- ( WITH { this.noSemicolonHere(); } )?
594
- { this.docComment( $art ); }
595
- annotationAssignment_ll1[ $art ]*
596
- (
597
- '{' { $art.columns = []; }
432
+ ( ASPECT | ( abs=ABSTRACT | HideAlternatives ) ent=ENTITY )
433
+ simplePath[ $name, 'Type' ]
434
+ { this.addDef( $art, $outer, 'artifacts', 'aspect', $name );
435
+ if ($ent)
436
+ this.warning( 'syntax-deprecated-abstract', this.tokenLocation( $abs, $ent ) );
437
+ this.docComment( $art ); }
438
+ annotationAssignment_fix[ $art ]*
439
+ ( ':'
598
440
  (
599
- selectItemDef[ $art.columns ]
600
- ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
601
- selectItemDef[ $art.columns ]
441
+ includeRef[ $art ]
442
+ ( ',' { if (this.isStraightBefore('{')) break; } // allow ',' before '{' // }}
443
+ includeRef[ $art ]
602
444
  )*
603
445
  )?
604
- '}'
446
+ )?
447
+ ( // `aspect MyAspect {};`
448
+ '{' { $art.elements = this.createDict(); }
449
+ ( elementDef[ $art ]* )
450
+ '}' { this.finalizeDictOrArray( $art.elements ); }
605
451
  (
606
452
  ACTIONS '{' { $art.actions = this.createDict(); }
607
453
  actionFunctionDef[ $art ]*
608
454
  '}' { this.finalizeDictOrArray( $art.actions ); }
609
455
  )?
610
456
  optionalSemi
611
- |
612
- ACTIONS '{' { $art.actions = this.createDict(); }
613
- actionFunctionDef[ $art ]*
614
- '}' { this.finalizeDictOrArray( $art.actions ); }
615
- optionalSemi
616
- |
457
+ | // `aspect MyAspect;`, e.g. for annotation aspects.
458
+ { this.aspectWithoutElements( $art ); }
617
459
  requiredSemi
618
460
  )
619
461
  ;
620
462
 
621
- // TODO: no action extension?
622
- actionFunctionDef[ outer ] locals[ art = {} ]
463
+ typeDef[ art, outer ] locals[ name = {} ]
623
464
  @after { this.attachLocation( $art ); }
624
465
  :
625
- { $art.location = this.startLocation();; this.docComment( $art ); }
626
- annotationAssignment_ll1[ $art ]*
627
- (
628
- ACTION name=ident['BoundAction']
629
- { this.addDef( $art, $outer, 'actions', 'action', $name.id );
630
- this.docComment( $art ); }
631
- annotationAssignment_fix[ $art ]*
632
- parameterListDef[ $art ]
633
- ( returnTypeSpec[ $art ] | requiredSemi )
634
- |
635
- FUNCTION name=ident['BoundAction']
636
- { this.addDef( $art, $outer, 'actions', 'function', $name.id );
637
- this.docComment( $art ); }
638
- annotationAssignment_fix[ $art ]*
639
- parameterListDef[ $art ]
640
- returnTypeSpec[ $art ]
641
- )
466
+ TYPE simplePath[ $name, 'Type' ]
467
+ { this.addDef( $art, $outer, 'artifacts', 'type', $name );
468
+ this.docComment( $art ); }
469
+ annotationAssignment_fix[ $art ]*
470
+ typeSpecSemi[ $art ]
642
471
  ;
643
472
 
644
473
  actionFunctionMainDef[ art, outer ] locals[ name = {} ]
@@ -659,137 +488,254 @@ actionFunctionMainDef[ art, outer ] locals[ name = {} ]
659
488
  returnTypeSpec[ $art ]
660
489
  ;
661
490
 
662
- eventDef[ art, outer ] locals[ name = {} ]
663
- @after { /* #ATN 1 */ this.attachLocation( $art ); }
491
+ // Member definitions: actions, elements, enums, parameters: --------------------
492
+
493
+ actionFunctionDef[ outer ] locals[ art = {} ]
494
+ @after { this.attachLocation( $art ); }
664
495
  :
665
- EVENT simplePath[ $name, 'Event' ]
666
- { this.addDef( $art, $outer, 'artifacts', 'event', $name );
667
- this.docComment( $art ); }
668
- annotationAssignment_fix[ $art ]*
496
+ { $art.location = this.startLocation();; this.docComment( $art ); }
497
+ annotationAssignment_ll1[ $art ]*
669
498
  (
670
- typeStruct[ $art ] optionalSemi
499
+ ACTION name=ident['BoundAction']
500
+ { this.addDef( $art, $outer, 'actions', 'action', $name.id );
501
+ this.docComment( $art ); }
502
+ annotationAssignment_fix[ $art ]*
503
+ parameterListDef[ $art ]
504
+ ( returnTypeSpec[ $art ] | requiredSemi )
671
505
  |
672
- ':'
673
- // #ATN: includeRef can be / start with PROJECTION
674
- (
675
- { $art.type = {}; }
676
- simplePath[ $art.type, 'artref' ]
677
- (
678
- { $art.includes = [ $art.type ]; delete $art.type; }
679
- ( ',' { if (this.isStraightBefore('{')) break; } // allow ',' before '{' // }}
680
- includeRef[ $art ]
681
- )*
682
- typeStruct[ $art ] optionalSemi
683
- |
684
- { this.docComment( $art ); }
685
- annotationAssignment_ll1[ $art ]*
686
- requiredSemi
687
- )
688
- |
689
- typeStruct[ $art ] optionalSemi
690
- |
691
- qp=projectionSpec
692
- { $art.query = $qp.query; $art['$'+'syntax'] = 'projection'; }
693
- optionalSemi // TODO: not fully correct without columns or excluding
694
-
695
- )
506
+ FUNCTION name=ident['BoundAction']
507
+ { this.addDef( $art, $outer, 'actions', 'function', $name.id );
508
+ this.docComment( $art ); }
509
+ annotationAssignment_fix[ $art ]*
510
+ parameterListDef[ $art ]
511
+ returnTypeSpec[ $art ]
696
512
  )
697
513
  ;
698
514
 
699
- aspectDef[ art, outer ] locals[ name = {} ]
515
+ parameterDef[ outer ] locals[ art = {} ]
700
516
  @after { this.attachLocation( $art ); }
701
517
  :
702
- ( ASPECT | ( abs=ABSTRACT | HideAlternatives ) ent=ENTITY )
703
- simplePath[ $name, 'Type' ]
704
- { this.addDef( $art, $outer, 'artifacts', 'aspect', $name );
705
- // backends do not like ['$'+'syntax']: ($ent ? 'entity' : 'aspect')
706
- if ($ent)
707
- this.warning( 'syntax-deprecated-abstract', this.tokenLocation( $abs, $ent ) );
518
+ { this.docComment( $art ); }
519
+ annotationAssignment_ll1[ $art ]*
520
+ name=ident['Param']
521
+ { this.addDef( $art, $outer, 'params', 'param', $name.id );
708
522
  this.docComment( $art ); }
709
523
  annotationAssignment_fix[ $art ]*
710
- ( ':'
711
- (
712
- includeRef[ $art ]
713
- ( ',' { if (this.isStraightBefore('{')) break; } // allow ',' before '{' // }}
714
- includeRef[ $art ]
715
- )*
716
- )?
524
+ typeSpec[ $art ]
525
+ defaultValue[ $art ]?
526
+ { this.docComment( $art ); }
527
+ annotationAssignment_ll1[ $art ]*
528
+ ;
529
+
530
+ parameterListDef[ art ]
531
+ :
532
+ '(' { $art.params = this.createDict(); }
533
+ // also empty param list (we might do some hacking later to allow reserved words)
534
+ // see annotationAssignment_paren
535
+ (
536
+ parameterDef[ $art ]
537
+ ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
538
+ parameterDef[ $art ]
539
+ )*
717
540
  )?
718
- ( // `aspect MyAspect {};`
719
- '{' { $art.elements = this.createDict(); }
720
- ( elementDef[ $art ]* )
721
- '}' { this.finalizeDictOrArray( $art.elements ); }
722
- // TODO: action definitions in a specific section?
723
- (
724
- ACTIONS '{' { $art.actions = this.createDict(); }
725
- actionFunctionDef[ $art ]*
726
- '}' { this.finalizeDictOrArray( $art.actions ); }
727
- )?
728
- optionalSemi
729
- | // `aspect MyAspect;`, e.g. for annotation aspects.
730
- { this.aspectWithoutElements( $art ); }
731
- requiredSemi
732
- )
541
+ ')' { this.finalizeDictOrArray( $art.params ); }
733
542
  ;
734
543
 
735
- typeDef[ art, outer ] locals[ name = {} ]
544
+ enumSymbolDef[ outer ] locals[ art = {} ]
736
545
  @after { this.attachLocation( $art ); }
737
546
  :
738
- TYPE simplePath[ $name, 'Type' ]
739
- { this.addDef( $art, $outer, 'artifacts', 'type', $name );
547
+ { $art.location = this.startLocation();; this.docComment( $art ); }
548
+ annotationAssignment_ll1[ $art ]*
549
+ name=ident['Enum']
550
+ { this.addDef( $art, $outer, 'enum', 'enum', $name.id );
551
+ this.docComment( $art ); }
552
+ annotationAssignment_ll1[ $art ]*
553
+ ( '='
554
+ { this.excludeExpected( ['Boolean', 'QuotedLiteral', "'#'", 'NULL'] ); }
555
+ (
556
+ val=literalValue
557
+ { $art.value = $val.val; }
558
+ |
559
+ ( plus='+' | min='-' ) num=Number
560
+ { $art.value = this.numberLiteral( $num, $plus||$min ); }
561
+ )
562
+ { this.docComment( $art ); }
563
+ annotationAssignment_ll1[ $art ]*
564
+ )?
565
+ requiredSemi
566
+ ;
567
+
568
+ elementDef[ outer ] locals[ $art = {} ]
569
+ :
570
+ { $art.location = this.startLocation();; this.docComment( $art ); }
571
+ annotationAssignment_ll1[ $art ]*
572
+ elementDefInner[ $art, $outer, false ]
573
+ ;
574
+
575
+ elementDefInner[ art, outer, allowEq ]
576
+ @after{ this.attachLocation( $art ); }
577
+ :
578
+ // VIRTUAL is keyword, except if before the following tokens texts:
579
+ { this.setLocalToken( 'VIRTUAL', 'VIRTUAL', /^[:{@=}]$/ ); }
580
+ ( virtual=VIRTUAL { $art.virtual = this.valueWithTokenLocation( true, $virtual ); } )?
581
+ ( key=KEY { $art.key = this.valueWithTokenLocation( true, $key ); } )?
582
+ { this.setLocalToken( 'MASKED', 'MASKED', /^[:{@=}]$/ ); }
583
+ ( masked=MASKED
584
+ {
585
+ $art.masked = this.valueWithTokenLocation( true, $masked ) ;
586
+ this.message( 'syntax-unsupported-masked', $masked, { keyword: 'masked' } );
587
+ }
588
+ )?
589
+ { this.setLocalToken( 'ELEMENT', 'ELEMENT', /^[:{@=}]$/ ); }
590
+ ELEMENT?
591
+ name=ident['Element']
592
+ { this.addDef( $art, $outer, 'elements', 'element', $name.id );
740
593
  this.docComment( $art ); }
741
594
  annotationAssignment_fix[ $art ]*
742
- typeSpecSemi[ $art ]
595
+ // TODO: we can think of making the typeSpec optional and do checks instead:
596
+ // type optional with '=', type required otherwise
597
+ (
598
+ typeStruct[ $art ]
599
+ ( nullability[ $art ]
600
+ requiredSemi
601
+ | optionalSemi // NOT and NULL are reserved...
602
+ )
603
+ |
604
+ ':'
605
+ elementType[ $art ]
606
+ |
607
+ // this is also called for enum symbols (in EXTEND)
608
+ eq='=' e=expression // never introduce AS as syntax variant of '='
609
+ {
610
+ if (!$allowEq || $e.expr && !$e.expr.literal )
611
+ this.error( 'syntax-unsupported-calc-elem', $eq );
612
+ else if ($e.expr)
613
+ $art.value = $e.expr;
614
+ }
615
+ { this.docComment( $art ); }
616
+ annotationAssignment_ll1[ $art ]* // for enum symbol def via EXTEND
617
+ requiredSemi
618
+ )
743
619
  ;
744
620
 
745
- extendType[ art, outer ] locals[ name = {} ]
746
- @after { this.attachLocation( $art ); }
621
+ elementType[ art ] // TODO: split this monster rule
622
+ @after{ /* #ATN 3 */ this.attachLocation( $art ); }
747
623
  :
748
- TYPE simplePath[ $name, 'Extend' ]
749
- { $art.expectedKind = 'type'; $art.name = $name;
750
- this.addItem( $art, $outer, 'extensions', 'extend' );
751
- }
752
- // extendWithOptElementsOrType + includeRef:
624
+ // #ATN: referenced type name can be ASSOCIATION or ARRAY or TYPE or LOCALIZED
625
+ typeStruct[ $art ]
626
+ nullability[ $art ]?
627
+ requiredSemi
628
+ |
629
+ typeAssociationBase[ $art, true ]
630
+ // #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
753
631
  (
754
- extendWithOptElementsNoWith[ art ]
632
+ typeStruct[ $art.target, true ] optionalSemi
755
633
  |
756
- WITH { this.noSemicolonHere(); this.docComment( $art ); }
634
+ one=ONE
635
+ { this.setMaxCardinality( $art, $one, this.numberLiteral( $one, null, '1' ) ); }
636
+ typeCompoStruct[ $art.target ] optionalSemi
637
+ |
638
+ many=MANY
639
+ { this.setMaxCardinality( $art, $many, { literal: 'string', val: '*' } ); }
640
+ typeCompoStruct[ $art.target ] optionalSemi
641
+ |
642
+ // we do not support `Composition of many { e }` - ambiguity ad-hoc target versus foreign keys!
643
+ typeToMany[ $art ] typeAssociationElementCont[ $art ]
644
+ |
645
+ typeToOne[ $art ] typeAssociationElementCont[ $art ]
646
+ |
647
+ simplePath[ $art.target, 'artref' ] typeAssociationElementCont[ $art ]
648
+ )
649
+ |
650
+ (
651
+ array=ARRAY of=OF
652
+ { $art.items = { location: this.tokenLocation( $array, $of ) }; }
653
+ | many=MANY
654
+ { $art.items = { location: this.tokenLocation( $many ) };}
655
+ )
656
+ ( typeStruct[ $art.items ]
657
+ nullability[ $art.items ]?
658
+ | // #ATN: typeRefOptArgs/typeTypeOf can start with TYPE
659
+ ( typeTypeOf[ $art.items ] | typeRefOptArgs[ $art.items ] )
660
+ nullability[ $art.items ]? // only if not followed by `enum`
661
+ { this.docComment( $art ); }
757
662
  annotationAssignment_ll1[ $art ]*
758
663
  (
759
- '{' { $art.elements = this.createDict(); }
760
- elementDefOrExtend[ $art ]*
761
- '}' { this.finalizeDictOrArray( $art.elements ); }
762
- { this.checkExtensionDict( $art.elements ); }
763
- optionalSemi
764
- |
765
- // extend type|element Art with (length: 10);
766
- typeNamedArgList[ $art ]
767
- requiredSemi
768
- |
769
- requiredSemi
770
- |
771
- includeRef[ $art ] ( ',' includeRef[ $art ] )*
772
- requiredSemi
773
- )
664
+ { if ($art.items.notNull) {
665
+ this.message( 'syntax-unexpected-null', $art.items.notNull.location,
666
+ { keyword: $art.items.notNull.val ? 'not null' : 'null' } );
667
+ }
668
+ }
669
+ ENUM '{' { $art.items.enum = this.createDict(); }
670
+ enumSymbolDef[ $art.items ]*
671
+ '}' { this.finalizeDictOrArray( $art.items.enum ); }
672
+ nullability[ $art.items ]?
673
+ )?
774
674
  )
675
+ requiredSemi // also req after struct/enum
676
+ |
677
+ l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
678
+ typeRefOptArgs[ $art ]
679
+ { this.docComment( $art ); }
680
+ annotationAssignment_ll1[ $art ]*
681
+ ( elementProperties[ $art ]
682
+ { this.docComment( $art ); }
683
+ annotationAssignment_ll1[ $art ]*
684
+ )?
685
+ requiredSemi
686
+ |
687
+ typeTypeOf[ $art ] // Note: Same as the typeRefOptArgs rule below
688
+ { this.docComment( $art ); }
689
+ annotationAssignment_ll1[ $art ]*
690
+ (
691
+ ENUM '{' { $art.enum = this.createDict(); }
692
+ enumSymbolDef[ $art ]*
693
+ '}' { this.finalizeDictOrArray( $art.enum ); }
694
+ elementProperties[ $art ]?
695
+ |
696
+ elementProperties[ $art ]
697
+ { this.docComment( $art ); }
698
+ annotationAssignment_ll1[ $art ]*
699
+ )?
700
+ requiredSemi // also req after foreign key spec
701
+ |
702
+ typeRefOptArgs[ $art ] // Note: Same as the typeTypeOf rule above
703
+ { this.docComment( $art ); }
704
+ annotationAssignment_ll1[ $art ]*
705
+ (
706
+ ENUM '{' { $art.enum = this.createDict(); }
707
+ enumSymbolDef[ $art ]*
708
+ '}' { this.finalizeDictOrArray( $art.enum ); }
709
+ elementProperties[ $art ]?
710
+ |
711
+ elementProperties[ $art ]
712
+ { this.docComment( $art ); }
713
+ annotationAssignment_ll1[ $art ]*
714
+ )?
715
+ requiredSemi // also req after enum spec
775
716
  ;
776
717
 
777
- annotationDef[ art, outer ] locals[ name = {} ]
778
- @after { this.attachLocation( $art ); }
718
+ elementProperties[ elem ]
779
719
  :
780
- annotation=ANNOTATION simplePath[ $name, 'AnnoDef' ]
781
- { if ($outer.kind !== 'source') { // this is a syntax restriction to avoid confusion
782
- this.error( 'syntax-unexpected-vocabulary', $annotation, { '#': $outer.kind } );
783
- $art = {}; }
784
- else {
785
- if (!$outer.vocabularies) $outer.vocabularies = Object.create(null);
786
- this.addDef( $art, $outer, 'vocabularies', 'annotation', $name );
787
- }
788
- this.docComment( $art ); }
789
- annotationAssignment_fix[ $art ]*
790
- typeSpecSemi[ $art ] // also 'includes'...
720
+ nullability[ $elem ]
721
+ defaultValue[ $elem ]?
722
+ |
723
+ defaultValue[ $elem ]
724
+ nullability[ $elem ]?
725
+ |
726
+ eq='='
727
+ { this.error( 'syntax-unsupported-calc-elem', $eq ); }
728
+ e=expression { $elem.value = $e.expr; }
729
+ ;
730
+
731
+ defaultValue[ art ] locals[ elem, elements = {} ]
732
+ :
733
+ // TODO: We may support structured default values here.
734
+ DEFAULT expr=expression { $art.default = $expr.expr; }
791
735
  ;
792
736
 
737
+ // Extend and annotate ----------------------------------------------------------
738
+
793
739
  extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
794
740
  @after{ /* #ATN 1 */ this.attachLocation( $art ); }
795
741
  :
@@ -830,6 +776,7 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
830
776
  |
831
777
  // extend Art with (length: 10);
832
778
  // `with` is required, or we could have `extend String(length:10);`.
779
+ // future `extend Action with (param: Type)` now has ambiguity
833
780
  typeNamedArgList[ $art ]
834
781
  requiredSemi
835
782
  |
@@ -839,7 +786,7 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
839
786
  |
840
787
  DEFINITIONS
841
788
  '{' { $art.artifacts = this.createDict(); }
842
- artifactDef[ $art, true ]*
789
+ artifactDefOrExtend[ $art, true ]*
843
790
  '}' { this.finalizeDictOrArray( $art.artifacts ); }
844
791
  optionalSemi
845
792
  |
@@ -859,71 +806,229 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
859
806
  '}' { this.finalizeDictOrArray( $art.actions ); }
860
807
  optionalSemi
861
808
  )
809
+ // TODO: what about adding both ELEMENTS and ACTIONS? (TODO: csn input test & to-cdl)
862
810
  )
863
811
  ;
864
812
 
865
- extendWithOptElementsOrType[ art ]
813
+ extendService[ art, outer ] locals[ name = {} ]
814
+ @after { this.attachLocation( $art ); }
866
815
  :
867
- extendWithOptElementsNoWith[ art ]
868
- |
869
- WITH { this.noSemicolonHere(); this.docComment( $art ); }
870
- annotationAssignment_ll1[ $art ]*
871
- (
872
- '{' { $art.elements = this.createDict(); }
873
- elementDefOrExtend[ $art ]*
874
- '}' { this.finalizeDictOrArray( $art.elements ); }
875
- { this.checkExtensionDict( $art.elements ); }
876
- optionalSemi
877
- |
878
- ELEMENTS '{' { $art.elements = this.createDict(); }
879
- elementDefOrExtend[ $art ]*
880
- '}' { this.finalizeDictOrArray( $art.elements ); }
881
- { this.checkExtensionDict( $art.elements ); }
882
- optionalSemi
883
- |
884
- ENUM '{' { $art.enum = this.createDict(); }
885
- enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
886
- '}' { this.finalizeDictOrArray( $art.enum ); }
887
- optionalSemi
888
- |
889
- // extend type|element Art with (length: 10);
890
- typeNamedArgList[ $art ]
891
- requiredSemi
892
- |
893
- requiredSemi
894
- )
816
+ SERVICE { $art.expectedKind = 'service'; }
817
+ simplePath[ $name, 'Service' ]
818
+ { $art.name = $name; this.addItem( $art, $outer, 'extensions', 'extend' ); }
819
+ ( WITH { this.noSemicolonHere(); } )?
820
+ optArtifactsBlock[ art, 'SERVICE' ]
895
821
  ;
896
822
 
897
- extendWithOptElementsNoWith[ art ]
823
+ extendContext[ art, outer ] locals[ name = {} ]
824
+ @after { this.attachLocation( $art ); }
898
825
  :
899
- { this.docComment( $art ); }
900
- annotationAssignment_ll1[ $art ]*
826
+ CONTEXT { $art.expectedKind = 'context'; }
827
+ simplePath[ $name, 'Context' ]
828
+ { $art.name = $name; this.addItem( $art, $outer, 'extensions', 'extend' ); }
829
+ ( WITH { this.noSemicolonHere(); } )?
830
+ optArtifactsBlock[ art, 'CONTEXT' ]
831
+ ;
832
+
833
+ extendEntityOrAspect[ art, outer ] locals[ name = {} ]
834
+ @after { /* #ATN 1 */ this.attachLocation( $art ); }
835
+ :
836
+ kind=(ASPECT | ENTITY) simplePath[ $name, 'Extend' ]
837
+ { $art.expectedKind = $kind.text.toLowerCase(); $art.name = $name;
838
+ this.addItem( $art, $outer, 'extensions', 'extend' );
839
+ }
901
840
  (
902
- '{' { $art.elements = this.createDict(); }
903
- elementDefOrExtend[ $art ]*
904
- '}' { this.finalizeDictOrArray( $art.elements ); }
905
- { this.checkExtensionDict( $art.elements ); }
906
- optionalSemi
841
+ WITH { this.noSemicolonHere(); this.docComment( $art ); }
842
+ annotationAssignment_ll1[ $art ]*
843
+ // ATN: the ref can start with ACTIONS
844
+ (
845
+ includeRef[ $art ] ( ',' includeRef[ $art ] )*
846
+ requiredSemi
847
+ |
848
+ extendForEntity[ $art ]
849
+ )
907
850
  |
908
- requiredSemi
851
+ { this.docComment( $art ); }
852
+ annotationAssignment_ll1[ $art ]*
853
+ extendForEntity[ $art ]
909
854
  )
910
855
  ;
911
856
 
912
- annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
913
- @after { this.attachLocation( $art ); }
857
+ extendForEntity[ art ]
914
858
  :
915
- simplePath[ $name, 'Annotate' ]
916
- ( ':' simplePath[ $elemName, 'Element'] )?
917
- { this.addExtension( $art, $outer, 'annotate', $name, $elemName.path ); }
918
- ( WITH { this.noSemicolonHere(); } )?
919
- { this.docComment( $art ); }
920
- annotationAssignment_ll1[ $art ]*
859
+ '{' { $art.elements = this.createDict(); }
860
+ elementDefOrExtend[ $art ]*
861
+ '}' { this.finalizeDictOrArray( $art.elements ); }
921
862
  (
922
- '{' { $art.elements = this.createDict(); }
923
- annotateElement[ $art ]*
924
- '}' { this.finalizeDictOrArray( $art.elements ); }
925
- { this.checkExtensionDict( $art.elements ); }
926
- (
863
+ ACTIONS '{' { $art.actions = this.createDict(); }
864
+ actionFunctionDef[ $art ]*
865
+ '}' { this.finalizeDictOrArray( $art.actions ); }
866
+ )?
867
+ optionalSemi
868
+ |
869
+ ACTIONS '{' { $art.actions = this.createDict(); }
870
+ actionFunctionDef[ $art ]*
871
+ '}' { this.finalizeDictOrArray( $art.actions ); }
872
+ optionalSemi
873
+ |
874
+ requiredSemi
875
+ ;
876
+
877
+ extendProjection[ art, outer ] locals[ name = {} ]
878
+ @after { this.attachLocation( $art ); }
879
+ :
880
+ expected=PROJECTION simplePath[ $name, 'Extend' ]
881
+ { $art.expectedKind = 'entity'; $art.name = $name;
882
+ this.addItem( $art, $outer, 'extensions', 'extend' );
883
+ }
884
+ ( WITH { this.noSemicolonHere(); } )?
885
+ { this.docComment( $art ); }
886
+ annotationAssignment_ll1[ $art ]*
887
+ (
888
+ '{' { $art.columns = []; }
889
+ (
890
+ selectItemDef[ $art.columns ]
891
+ ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
892
+ selectItemDef[ $art.columns ]
893
+ )*
894
+ )?
895
+ '}'
896
+ (
897
+ ACTIONS '{' { $art.actions = this.createDict(); }
898
+ actionFunctionDef[ $art ]*
899
+ '}' { this.finalizeDictOrArray( $art.actions ); }
900
+ )?
901
+ optionalSemi
902
+ |
903
+ ACTIONS '{' { $art.actions = this.createDict(); }
904
+ actionFunctionDef[ $art ]*
905
+ '}' { this.finalizeDictOrArray( $art.actions ); }
906
+ optionalSemi
907
+ |
908
+ requiredSemi
909
+ )
910
+ ;
911
+
912
+ extendType[ art, outer ] locals[ name = {} ]
913
+ @after { this.attachLocation( $art ); }
914
+ :
915
+ TYPE simplePath[ $name, 'Extend' ]
916
+ { $art.expectedKind = 'type'; $art.name = $name;
917
+ this.addItem( $art, $outer, 'extensions', 'extend' );
918
+ }
919
+ // extendWithOptElementsOrType + includeRef:
920
+ (
921
+ extendWithOptElementsNoWith[ art ]
922
+ |
923
+ WITH { this.noSemicolonHere(); this.docComment( $art ); }
924
+ annotationAssignment_ll1[ $art ]*
925
+ (
926
+ '{' { $art.elements = this.createDict(); }
927
+ elementDefOrExtend[ $art ]*
928
+ '}' { this.finalizeDictOrArray( $art.elements ); }
929
+ { this.checkExtensionDict( $art.elements ); }
930
+ optionalSemi
931
+ |
932
+ // extend type|element Art with (length: 10);
933
+ typeNamedArgList[ $art ]
934
+ requiredSemi
935
+ |
936
+ requiredSemi
937
+ |
938
+ includeRef[ $art ] ( ',' includeRef[ $art ] )*
939
+ requiredSemi
940
+ )
941
+ )
942
+ ;
943
+
944
+ extendWithOptElementsOrType[ art ]
945
+ :
946
+ extendWithOptElementsNoWith[ art ]
947
+ |
948
+ WITH { this.noSemicolonHere(); this.docComment( $art ); }
949
+ annotationAssignment_ll1[ $art ]*
950
+ (
951
+ '{' { $art.elements = this.createDict(); }
952
+ elementDefOrExtend[ $art ]*
953
+ '}' { this.finalizeDictOrArray( $art.elements ); }
954
+ { this.checkExtensionDict( $art.elements ); }
955
+ optionalSemi
956
+ |
957
+ ELEMENTS '{' { $art.elements = this.createDict(); }
958
+ elementDefOrExtend[ $art ]*
959
+ '}' { this.finalizeDictOrArray( $art.elements ); }
960
+ { this.checkExtensionDict( $art.elements ); }
961
+ optionalSemi
962
+ |
963
+ ENUM '{' { $art.enum = this.createDict(); }
964
+ enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
965
+ '}' { this.finalizeDictOrArray( $art.enum ); }
966
+ optionalSemi
967
+ |
968
+ // extend type|element Art with (length: 10);
969
+ typeNamedArgList[ $art ]
970
+ requiredSemi
971
+ |
972
+ requiredSemi
973
+ )
974
+ ;
975
+
976
+ extendWithOptElementsNoWith[ art ]
977
+ :
978
+ { this.docComment( $art ); }
979
+ annotationAssignment_ll1[ $art ]*
980
+ (
981
+ '{' { $art.elements = this.createDict(); }
982
+ elementDefOrExtend[ $art ]*
983
+ '}' { this.finalizeDictOrArray( $art.elements ); }
984
+ { this.checkExtensionDict( $art.elements ); }
985
+ optionalSemi
986
+ |
987
+ requiredSemi
988
+ )
989
+ ;
990
+
991
+ // For `extend … with elements` or `extend entity … with`, `extend aspect … with`,
992
+ // i.e. definitions in { … } are never enums
993
+ elementDefOrExtend[ outer ] locals[ art = {} ]
994
+ @after { /* #ATN 1 */ } // if ($art) this.attachLocation( $art ); }
995
+ :
996
+ { $art.location = this.startLocation();; this.docComment( $art ); }
997
+ annotationAssignment_ll1[ $art ]*
998
+ // #ATN: element name for definition can be EXTEND
999
+ (
1000
+ EXTEND
1001
+ extendElement[ $art, $outer ]
1002
+ |
1003
+ elementDefInner[ $art, $outer, true ]
1004
+ )
1005
+ ;
1006
+
1007
+ extendElement[ art, outer ]
1008
+ @after{ this.attachLocation( $art ); }
1009
+ :
1010
+ { this.setLocalToken( 'ELEMENT', 'ELEMENT', /^([:{@=}()]|WITH)$/i ); }
1011
+ ( expected=ELEMENT { $art.expectedKind = 'element'; } )?
1012
+ name=ident['Element']
1013
+ { this.addDef( $art, $outer, 'elements', 'extend', $name.id ); }
1014
+ extendWithOptElementsOrType[ $art, $art ]
1015
+ ;
1016
+
1017
+ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
1018
+ @after { this.attachLocation( $art ); }
1019
+ :
1020
+ simplePath[ $name, 'Annotate' ]
1021
+ ( ':' simplePath[ $elemName, 'Element'] )?
1022
+ { this.addExtension( $art, $outer, 'annotate', $name, $elemName.path ); }
1023
+ ( WITH { this.noSemicolonHere(); } )?
1024
+ { this.docComment( $art ); }
1025
+ annotationAssignment_ll1[ $art ]*
1026
+ (
1027
+ '{' { $art.elements = this.createDict(); }
1028
+ annotateElement[ $art ]*
1029
+ '}' { this.finalizeDictOrArray( $art.elements ); }
1030
+ { this.checkExtensionDict( $art.elements ); }
1031
+ (
927
1032
  ACTIONS
928
1033
  '{' { $art.actions = this.createDict(); }
929
1034
  annotateAction[ $art ]*
@@ -1029,482 +1134,68 @@ annotateParam [ outer ] locals [ art = {} ]
1029
1134
  annotationAssignment_ll1[ $art ]*
1030
1135
  ;
1031
1136
 
1032
- // Element definition and its helpers ----------------------------------------
1137
+ // Type expressions -------------------------------------------------------------
1033
1138
 
1034
- enumSymbolDef[ outer ] locals[ art = {} ]
1035
- @after { this.attachLocation( $art ); }
1139
+ includeRef[ art ] locals[ incl = {} ]
1036
1140
  :
1037
- { $art.location = this.startLocation();; this.docComment( $art ); }
1038
- annotationAssignment_ll1[ $art ]*
1039
- name=ident['Enum']
1040
- { this.addDef( $art, $outer, 'enum', 'enum', $name.id );
1041
- this.docComment( $art ); }
1042
- annotationAssignment_ll1[ $art ]*
1043
- ( '='
1044
- { this.excludeExpected( ['Boolean', 'QuotedLiteral', "'#'", 'NULL'] ); }
1045
- (
1046
- val=literalValue
1047
- { $art.value = $val.val; }
1048
- |
1049
- ( plus='+' | min='-' ) num=Number
1050
- { $art.value = this.numberLiteral( $num, $plus||$min ); }
1051
- )
1052
- { this.docComment( $art ); }
1053
- annotationAssignment_ll1[ $art ]*
1054
- )?
1055
- requiredSemi
1141
+ simplePath[ $incl, 'artref' ]
1142
+ { if ($art.includes) $art.includes.push($incl); else $art.includes = [$incl]; }
1056
1143
  ;
1057
1144
 
1058
- defaultValue[ art ] locals[ elem, elements = {} ]
1145
+ typeSpec[ art ] // for parameterDef
1146
+ @after{ /* #ATN 1 */ }
1059
1147
  :
1060
- // TODO: We may support structured default values here.
1061
- DEFAULT expr=expression { $art.default = $expr.expr; }
1148
+ typeStruct[ $art ]
1149
+ |
1150
+ ':'
1151
+ // #ATN: typeSimple can start with ARRAY or TYPE
1152
+ ( typeStruct[ $art ]
1153
+ nullability[ $art ]?
1154
+ | typeArray[ $art ] // nullability is set in typeArray
1155
+ | typeTypeOf[ $art ]
1156
+ nullability[ $art ]?
1157
+ (
1158
+ ENUM '{' { $art.enum = this.createDict(); }
1159
+ enumSymbolDef[ $art ]*
1160
+ '}' { this.finalizeDictOrArray( $art.enum ); }
1161
+ )?
1162
+ // TODO: no LOCALIZED ?
1163
+ | typeRefOptArgs[ $art ]
1164
+ nullability[ $art ]?
1165
+ (
1166
+ ENUM '{' { $art.enum = this.createDict(); }
1167
+ enumSymbolDef[ $art ]*
1168
+ '}' { this.finalizeDictOrArray( $art.enum ); }
1169
+ )?
1170
+ )
1062
1171
  ;
1063
1172
 
1064
- elementDefOrExtend[ outer ] locals[ art = {} ]
1065
- @after { /* #ATN 1 */ } // if ($art) this.attachLocation( $art ); }
1173
+ returnTypeSpec[ art ]
1174
+ @after{ /* #ATN 1 */ }
1066
1175
  :
1067
- { $art.location = this.startLocation();; this.docComment( $art ); }
1068
- annotationAssignment_ll1[ $art ]*
1069
- // #ATN: element name for definition can be EXTEND
1070
- (
1071
- EXTEND
1072
- extendElement[ $art, $outer ]
1073
- |
1074
- elementDefInner[ $art, $outer, true ]
1176
+ ret=RETURNS { $art.returns = { location: this.tokenLocation( $ret ), kind: 'param' }; }
1177
+ // #ATN: typeSimple can start with ARRAY or TYPE
1178
+ ( typeStruct[ $art.returns ]
1179
+ nullability[ $art.returns ]?
1180
+ | typeArray[ $art.returns ] // nullability is set in typeArray
1181
+ | typeTypeOf[ $art.returns ]
1182
+ nullability[ $art.returns ]?
1183
+ (
1184
+ ENUM '{' { $art.returns.enum = this.createDict(); }
1185
+ enumSymbolDef[ $art.returns ]*
1186
+ '}' { this.finalizeDictOrArray( $art.returns.enum ); }
1187
+ )?
1188
+ // TODO: no LOCALIZED ?
1189
+ | typeRefOptArgs[ $art.returns ]
1190
+ nullability[ $art.returns ]?
1191
+ (
1192
+ ENUM '{' { $art.returns.enum = this.createDict(); }
1193
+ enumSymbolDef[ $art.returns ]*
1194
+ '}' { this.finalizeDictOrArray( $art.returns.enum ); }
1195
+ )?
1075
1196
  )
1076
- ;
1077
1197
 
1078
- elementDef[ outer ] locals[ $art = {} ]
1079
- :
1080
- { $art.location = this.startLocation();; this.docComment( $art ); }
1081
- annotationAssignment_ll1[ $art ]*
1082
- elementDefInner[ $art, $outer, false ]
1083
- ;
1084
-
1085
- // Actually, this is a subset if elementDefInner...
1086
- // TODO: the corresponding restrictions must also be checked in the core
1087
- // compiler, as the mixin element could come via CSN
1088
- mixinElementDef[ outer ] locals[ art = {} ]
1089
- @after { /* #ATN 2 */ this.attachLocation($art); }
1090
- :
1091
- name=ident['Mixin']
1092
- { this.addDef( $art, $outer, 'mixin', 'mixin', $name.id ); }
1093
- (
1094
- ':'
1095
- // #ATN: referenced type name can be ASSOCIATION or COMPOSITION
1096
- (
1097
- typeAssociationBase[ $art, false ]
1098
- // #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
1099
- ( typeToMany[ $art ] | typeToOne[ $art ] | simplePath[ $art.target, 'artref' ] )
1100
- typeAssociationCont[ $art ]?
1101
- |
1102
- typeRefOptArgs[ $art ]
1103
- ( as='=' expression
1104
- { this.error( 'syntax-unsupported-calc-field', $as ); }
1105
- )?
1106
- )
1107
- |
1108
- as='=' expression
1109
- { this.error( 'syntax-unsupported-calc-field', $as ); }
1110
- )
1111
- requiredSemi
1112
- ;
1113
-
1114
- elementDefInner[ art, outer, allowEq ]
1115
- @after{ /* #ATN 5 */ this.attachLocation( $art ); }
1116
- :
1117
- // TODO: it would be excellent to remove ELEMENT...
1118
- // or have a special ident rule without the ELEMENT
1119
- // Reason: it would be good for error recovery to start a major block without LL1 ambiguity
1120
- // VIRTUAL is keyword, except if before the following tokens texts:
1121
- { this.setLocalToken( 'VIRTUAL', 'VIRTUAL', /^[:{@=}]$/ ); }
1122
- ( virtual=VIRTUAL { $art.virtual = this.valueWithTokenLocation( true, $virtual ); } )?
1123
- ( key=KEY { $art.key = this.valueWithTokenLocation( true, $key ); } )?
1124
- // #ATN: element name can be MASKED or ELEMENT (2x)
1125
- ( masked=MASKED
1126
- {
1127
- $art.masked = this.valueWithTokenLocation( true, $masked ) ;
1128
- this.message( 'syntax-unsupported-masked', $masked, { keyword: 'masked' } );
1129
- }
1130
- )?
1131
- // TODO: order?
1132
- ELEMENT?
1133
- name=ident['Element']
1134
- { this.addDef( $art, $outer, 'elements', 'element', $name.id );
1135
- this.docComment( $art ); }
1136
- annotationAssignment_fix[ $art ]*
1137
- // TODO: we can think of making the typeSpec optional and do checks instead:
1138
- // type optional with '=', type required otherwise
1139
- (
1140
- typeStruct[ $art ]
1141
- ( nullability[ $art ]
1142
- requiredSemi
1143
- | optionalSemi // NOT and NULL are reserved...
1144
- )
1145
- |
1146
- ':'
1147
- // #ATN: referenced type name can be ASSOCIATION or ARRAY or TYPE or LOCALIZED
1148
- (
1149
- typeStruct[ $art ]
1150
- nullability[ $art ]?
1151
- requiredSemi
1152
- |
1153
- typeAssociationBase[ $art, true ]
1154
- // #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
1155
- (
1156
- typeStruct[ $art.target, true ] optionalSemi
1157
- |
1158
- one=ONE
1159
- { this.setMaxCardinality( $art, $one, this.numberLiteral( $one, null, '1' ) ); }
1160
- typeCompoStruct[ $art.target ] optionalSemi
1161
- |
1162
- many=MANY
1163
- { this.setMaxCardinality( $art, $many, { literal: 'string', val: '*' } ); }
1164
- typeCompoStruct[ $art.target ] optionalSemi
1165
- |
1166
- // we do not support `Composition of many { e }` - ambiguity ad-hoc target versus foreign keys!
1167
- typeToMany[ $art ] typeAssociationElementCont[ $art ]
1168
- |
1169
- typeToOne[ $art ] typeAssociationElementCont[ $art ]
1170
- |
1171
- simplePath[ $art.target, 'artref' ] typeAssociationElementCont[ $art ]
1172
- )
1173
- |
1174
- (
1175
- array=ARRAY of=OF
1176
- { $art.items = { location: this.tokenLocation( $array, $of ) }; }
1177
- | many=MANY
1178
- { $art.items = { location: this.tokenLocation( $many ) };}
1179
- )
1180
- // #ATN: typeRefOptArgs can start with TYPE
1181
- ( typeStruct[ $art.items ]
1182
- nullability[ $art.items ]?
1183
- | typeTypeOf[ $art.items ]
1184
- nullability[ $art.items ]?
1185
- { this.docComment( $art ); }
1186
- annotationAssignment_ll1[ $art ]*
1187
- | typeRefOptArgs[ $art.items ]
1188
- nullability[ $art.items ]? // only if not followed by `enum`
1189
- { this.docComment( $art ); }
1190
- annotationAssignment_ll1[ $art ]*
1191
- (
1192
- { if ($art.items.notNull) {
1193
- this.message( 'syntax-unexpected-null', $art.items.notNull.location,
1194
- { keyword: $art.items.notNull.val ? 'not null' : 'null' } );
1195
- }
1196
- }
1197
- ENUM '{' { $art.items.enum = this.createDict(); }
1198
- enumSymbolDef[ $art.items ]*
1199
- '}' { this.finalizeDictOrArray( $art.items.enum ); }
1200
- nullability[ $art.items ]?
1201
- )?
1202
- )
1203
- requiredSemi // also req after struct/enum
1204
- |
1205
- typeTypeOf[ $art ] elementProperties[ $art ]?
1206
- { this.docComment( $art ); }
1207
- annotationAssignment_ll1[ $art ]*
1208
- requiredSemi // also req after foreign key spec
1209
- |
1210
- l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
1211
- typeRefOptArgs[ $art ]
1212
- { this.docComment( $art ); }
1213
- annotationAssignment_ll1[ $art ]*
1214
- ( elementProperties[ $art ]
1215
- { this.docComment( $art ); }
1216
- annotationAssignment_ll1[ $art ]*
1217
- )?
1218
- requiredSemi
1219
- |
1220
- typeRefOptArgs[ $art ]
1221
- { this.docComment( $art ); }
1222
- annotationAssignment_ll1[ $art ]*
1223
- (
1224
- ENUM '{' { $art.enum = this.createDict(); }
1225
- enumSymbolDef[ $art ]*
1226
- '}' { this.finalizeDictOrArray( $art.enum ); }
1227
- elementProperties[ $art ]?
1228
- |
1229
- elementProperties[ $art ]
1230
- { this.docComment( $art ); }
1231
- annotationAssignment_ll1[ $art ]*
1232
- )?
1233
- requiredSemi // also req after enum spec
1234
- )
1235
- |
1236
- // this is also called for enum symbols (in EXTEND)
1237
- eq='=' e=expression // never introduce AS as syntax variant of '='
1238
- {
1239
- if (!$allowEq || $e.expr && !$e.expr.literal )
1240
- this.error( 'syntax-unsupported-calc-field', $eq );
1241
- else if ($e.expr)
1242
- $art.value = $e.expr;
1243
- }
1244
- { this.docComment( $art ); }
1245
- annotationAssignment_ll1[ $art ]* // for enum symbol def via EXTEND
1246
- requiredSemi
1247
- )
1248
- ;
1249
-
1250
- extendElement[ art, outer ]
1251
- @after{ /* #ATN 1 */ this.attachLocation( $art ); }
1252
- :
1253
- // #ATN: element name can be ELEMENT
1254
- ( expected=ELEMENT { $art.expectedKind = 'element'; } )?
1255
- name=ident['Element']
1256
- { this.addDef( $art, $outer, 'elements', 'extend', $name.id ); }
1257
- extendWithOptElementsOrType[ $art, $art ]
1258
- ;
1259
-
1260
- selectItemDef[ outer ] locals[ art ]
1261
- @after{ if ($art) this.attachLocation( $art ); }
1262
- :
1263
- star='*'
1264
- { $outer.push( this.valueWithTokenLocation( '*', $star ) ); }
1265
- |
1266
- { $art = {};; this.docComment( $art ); }
1267
- annotationAssignment_atn[ $art ]*
1268
- // VIRTUAL is keyword, except if before the following tokens texts:
1269
- { this.setLocalToken( 'VIRTUAL', 'VIRTUAL', /^([,.:\[@]|as)$/i ) ; } // not '{'
1270
- ( virtual=VIRTUAL { $art.virtual = this.valueWithTokenLocation( true, $virtual ); } )?
1271
- ( key=KEY { $art.key = this.valueWithTokenLocation( true, $key ); } )?
1272
- selectItemDefBody[ $art, $outer ]
1273
- ;
1274
-
1275
- selectItemDefBody[ art, outer ] locals[ assoc ]
1276
- @after{ /* #ATN 2 */ }
1277
- :
1278
- { $outer.push( $art ); }
1279
- (
1280
- e=expression { $art.value = $e.expr; }
1281
- // we cannot use 'condition' instead, as long as we allow aliases without
1282
- // AS (using rule 'ident' instead of 'identNoKeyword') -> ambiguities
1283
- ( as=AS n1=ident['Item'] { $art.name = $n1.id }
1284
- | n2=ident['Item'] { $art.name = this.fragileAlias( $n2.id, true ); }
1285
- | { this.classifyImplicitName( 'Item', $e.expr ); }
1286
- )
1287
- { if ($art.value && !$art.value.path) this.excludeExpected( ["'.'", "'{'"] );
1288
- else if ($art.name) this.excludeExpected( ["'.'"] );
1289
- }
1290
- (
1291
- { this.reportExpandInline( $art, false ); }
1292
- selectItemInlineList[ $art, 'expand' ]
1293
- excludingClause[ $art ]?
1294
- // TODO: we might alternatively allow AS here
1295
- |
1296
- { this.reportExpandInline( $art, $as || this._input.LT(-1) ); }
1297
- DOTbeforeBRACE // ...orASTERISK
1298
- (
1299
- selectItemInlineList[ $art, 'inline' ]
1300
- excludingClause[ $art ]?
1301
- |
1302
- star='*'
1303
- { $art.inline = [ this.valueWithTokenLocation( '*', $star ) ]; }
1304
- )
1305
- )?
1306
- |
1307
- selectItemInlineList[ $art, 'expand' ]
1308
- excludingClause[ $art ]?
1309
- AS n1=ident['Item'] { $art.name = $n1.id }
1310
- )
1311
- { this.docComment( $art ); }
1312
- annotationAssignment_fix[ $art ]*
1313
- ( ':'
1314
- // #ATN: typeRefOptArgs can start with TYPE, REDIRECTED, ASSOCIATION
1315
- ( re=REDIRECTED to=TO
1316
- { $art.target = {}; }
1317
- simplePath[ $art.target, 'artref' ]
1318
- (
1319
- typeAssociationCont[ $art ]
1320
- |
1321
- { this.docComment( $art ); }
1322
- annotationAssignment_ll1[ $art ]*
1323
- )
1324
- | typeTypeOf[ $art ]
1325
- { this.docComment( $art ); }
1326
- annotationAssignment_ll1[ $art ]*
1327
- | l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
1328
- typeRefOptArgs[ $art ]
1329
- { this.docComment( $art ); }
1330
- annotationAssignment_ll1[ $art ]*
1331
- | typeRefOptArgs[ $art ]
1332
- { this.docComment( $art ); }
1333
- annotationAssignment_ll1[ $art ]*
1334
- |
1335
- { $assoc = this.associationInSelectItem( $art ); }
1336
- typeAssociationBase[ $assoc, false ]
1337
- // #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
1338
- ( typeToMany[ $assoc ] | typeToOne[ $assoc ] | simplePath[ $assoc.target, 'artref' ] )
1339
- ON cond=condition
1340
- { $assoc.on=$cond.cond; }
1341
- )
1342
- )?
1343
- ;
1344
-
1345
- bracedSelectItemListDef[ query ]
1346
- :
1347
- '{' { $query.columns = this.createArray(); }
1348
- (
1349
- selectItemDef[ $query.columns ]
1350
- ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
1351
- selectItemDef[ $query.columns ]
1352
- )*
1353
- )?
1354
- '}' { this.finalizeDictOrArray( $query.columns ); }
1355
- ;
1356
-
1357
- selectItemInlineList[ art, clause ]
1358
- :
1359
- '{' { $art[$clause] = this.createArray(); }
1360
- (
1361
- selectItemInlineDef[ $art[$clause] ]
1362
- ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
1363
- selectItemInlineDef[ $art[$clause] ]
1364
- )*
1365
- )?
1366
- '}' { this.finalizeDictOrArray( $art[$clause] ); }
1367
- ;
1368
-
1369
- selectItemInlineDef[ outer ] locals[ art ]
1370
- @after{ if ($art) this.attachLocation( $art ); }
1371
- :
1372
- star='*'
1373
- { $outer.push( this.valueWithTokenLocation( '*', $star ) ); }
1374
- |
1375
- { $art = {};; this.docComment( $art ); }
1376
- annotationAssignment_atn[ $art ]*
1377
- selectItemDefBody[ $art, $outer ]
1378
- ;
1379
-
1380
- parameterListDef[ art ]
1381
- :
1382
- '(' { $art.params = this.createDict(); }
1383
- // also empty param list (we might do some hacking later to allow reserved words)
1384
- // see annotationAssignment_paren
1385
- (
1386
- parameterDef[ $art ]
1387
- ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1388
- parameterDef[ $art ]
1389
- )*
1390
- )?
1391
- ')' { this.finalizeDictOrArray( $art.params ); }
1392
- ;
1393
-
1394
- parameterDef[ outer ] locals[ art = {} ]
1395
- @after { this.attachLocation( $art ); }
1396
- :
1397
- { this.docComment( $art ); }
1398
- annotationAssignment_ll1[ $art ]*
1399
- name=ident['Param']
1400
- { this.addDef( $art, $outer, 'params', 'param', $name.id );
1401
- this.docComment( $art ); }
1402
- annotationAssignment_fix[ $art ]*
1403
- typeSpec[ $art ]
1404
- defaultValue[ $art ]?
1405
- { this.docComment( $art ); }
1406
- annotationAssignment_ll1[ $art ]*
1407
- ;
1408
-
1409
- nullability[ art ]
1410
- :
1411
- not=NOT n1=NULL
1412
- { $art.notNull = this.valueWithTokenLocation( true, $not, $n1 ); }
1413
- |
1414
- n2=NULL
1415
- { $art.notNull = this.valueWithTokenLocation( false, $n2 ); }
1416
- ;
1417
-
1418
- elementProperties[ elem ]
1419
- :
1420
- nullability[ $elem ]
1421
- defaultValue[ $elem ]?
1422
- |
1423
- defaultValue[ $elem ]
1424
- nullability[ $elem ]?
1425
- |
1426
- eq='='
1427
- { this.error( 'syntax-unsupported-calc-field', $eq ); }
1428
- expression
1429
- ;
1430
-
1431
- // View definitions ----------------------------------------------------------
1432
-
1433
- viewDef[ art, outer ] locals[ name = {} ]
1434
- @after { this.attachLocation( $art ); }
1435
- :
1436
- v=VIEW simplePath[ $name, 'Entity' ]
1437
- { $art['$'+'syntax'] = 'view';
1438
- this.addDef( $art, $outer, 'artifacts', 'entity', $name );
1439
- this.docComment( $art ); }
1440
- annotationAssignment_fix[ $art ]*
1441
- (
1442
- parameterListDef[ $art ]
1443
- |
1444
- // TODO: warning deprecated?
1445
- ( HideAlternatives | WITH ) { $art.params = this.createDict(); }
1446
- PARAMETERS
1447
- parameterDef[ $art ]
1448
- ( ',' parameterDef[ $art ] )* // no optional final ',' here
1449
- { this.finalizeDictOrArray( $art.params ); }
1450
- )?
1451
- AS qe=queryExpression { $art.query = $qe.query; }
1452
- // TODO check ANTLR: bad msg with 'view V as'<eof> but 'view V as FOO' is fine
1453
- requiredSemi
1454
- ;
1455
-
1456
- // Type references -----------------------------------------------------------
1457
-
1458
- includeRef[ art ] locals[ incl = {} ]
1459
- :
1460
- simplePath[ $incl, 'artref' ]
1461
- { if ($art.includes) $art.includes.push($incl); else $art.includes = [$incl]; }
1462
- ;
1463
-
1464
- typeSpec[ art ] // for params
1465
- @after{ /* #ATN 1 */ }
1466
- :
1467
- typeStruct[ $art ]
1468
- |
1469
- ':'
1470
- // #ATN: typeSimple can start with ARRAY or TYPE
1471
- ( typeStruct[ $art ]
1472
- nullability[ $art ]?
1473
- | typeArray[ $art ] // nullability is set in typeArray
1474
- | typeTypeOf[ $art ]
1475
- nullability[ $art ]?
1476
- // TODO: no LOCALIZED ?
1477
- | typeRefOptArgs[ $art ]
1478
- nullability[ $art ]?
1479
- (
1480
- ENUM '{' { $art.enum = this.createDict(); }
1481
- enumSymbolDef[ $art ]*
1482
- '}' { this.finalizeDictOrArray( $art.enum ); }
1483
- )?
1484
- )
1485
- ;
1486
-
1487
- returnTypeSpec[ art ]
1488
- @after{ /* #ATN 1 */ }
1489
- :
1490
- ret=RETURNS { $art.returns = { location: this.tokenLocation( $ret ), kind: 'param' }; }
1491
- // #ATN: typeSimple can start with ARRAY or TYPE
1492
- ( typeStruct[ $art.returns ]
1493
- nullability[ $art.returns ]?
1494
- | typeArray[ $art.returns ] // nullability is set in typeArray
1495
- | typeTypeOf[ $art.returns ]
1496
- nullability[ $art.returns ]?
1497
- // TODO: no LOCALIZED ?
1498
- | typeRefOptArgs[ $art.returns ]
1499
- nullability[ $art.returns ]?
1500
- (
1501
- ENUM '{' { $art.returns.enum = this.createDict(); }
1502
- enumSymbolDef[ $art.returns ]*
1503
- '}' { this.finalizeDictOrArray( $art.returns.enum ); }
1504
- )?
1505
- )
1506
-
1507
- requiredSemi // currently for all - might change if we get rid of the misplaced annos (TODO: Now removed)
1198
+ requiredSemi // currently for all - might change if we get rid of the misplaced annos (TODO: Now removed)
1508
1199
  ;
1509
1200
 
1510
1201
 
@@ -1653,6 +1344,11 @@ typeArray[ art ]
1653
1344
  nullability[ $art.items ]?
1654
1345
  | typeTypeOf[ $art.items ]
1655
1346
  nullability[ $art.items ]?
1347
+ (
1348
+ ENUM '{' { $art.items.enum = this.createDict(); }
1349
+ enumSymbolDef[ $art.items ]*
1350
+ '}' { this.finalizeDictOrArray( $art.items.enum ); }
1351
+ )?
1656
1352
  | typeRefOptArgs[ $art.items ]
1657
1353
  nullability[ $art.items ]?
1658
1354
  (
@@ -1771,6 +1467,15 @@ cardinality[ art ] locals[ card = {} ]
1771
1467
  ']'
1772
1468
  ;
1773
1469
 
1470
+ nullability[ art ]
1471
+ :
1472
+ not=NOT n1=NULL
1473
+ { $art.notNull = this.valueWithTokenLocation( true, $not, $n1 ); }
1474
+ |
1475
+ n2=NULL
1476
+ { $art.notNull = this.valueWithTokenLocation( false, $n2 ); }
1477
+ ;
1478
+
1774
1479
  foreignKey[ outer ] locals[ art = {}, elem = {} ]
1775
1480
  @after { this.attachLocation($art); }
1776
1481
  :
@@ -1793,210 +1498,465 @@ typeTypeOf[ art ] locals[ _sync = 'nop' ]
1793
1498
  )?
1794
1499
  ;
1795
1500
 
1796
- typeRefOptArgs[ art ]
1797
- @init { $art.type = {}; }
1501
+ typeRefOptArgs[ art ]
1502
+ @init { $art.type = {}; }
1503
+ :
1504
+ simplePath[ $art.type, 'artref' ]
1505
+ (
1506
+ typeRefArgs[ $art ]
1507
+ |
1508
+ ':'
1509
+ { $art.type.scope = $art.type.path.length; }
1510
+ simplePath[ $art.type, 'ref']
1511
+ )?
1512
+ ;
1513
+
1514
+ typeRefArgs[ art ]
1515
+ :
1516
+ paren='(' { $art['$'+'typeArgs'] = this.createArray(); }
1517
+ (
1518
+ // unnamed arguments
1519
+ head=Number
1520
+ { $art['$'+'typeArgs'].push( this.numberLiteral( $head ) ); }
1521
+ ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1522
+ (
1523
+ v=VARIABLE
1524
+ { $art['$'+'typeArgs'].push(
1525
+ { literal: 'string', val: 'variable', location: this.tokenLocation($v) } );
1526
+ }
1527
+ |
1528
+ f=FLOATING
1529
+ { $art['$'+'typeArgs'].push(
1530
+ { literal: 'string', val: 'floating', location: this.tokenLocation($f) } );
1531
+ }
1532
+ |
1533
+ tail=Number
1534
+ { $art['$'+'typeArgs'].push( this.numberLiteral( $tail ) ); }
1535
+ )
1536
+ )*
1537
+ |
1538
+ // named arguments
1539
+ typeNamedArg[ $art ]
1540
+ ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1541
+ typeNamedArg[ $art ]
1542
+ )*
1543
+ )
1544
+ ')'{ this.finalizeDictOrArray( $art['$'+'typeArgs']); }
1545
+ ;
1546
+
1547
+ typeNamedArgList[ art ]
1548
+ :
1549
+ paren='('
1550
+ typeNamedArg[ $art ]
1551
+ ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1552
+ typeNamedArg[ $art ]
1553
+ )*
1554
+ ')'
1555
+ ;
1556
+
1557
+ typeNamedArg[ art ] locals[ arg = '' ]
1558
+ :
1559
+ name=ident['paramname']
1560
+ ':'
1561
+ { if ($name.id && this.checkTypeFacet( $art, $name.id ))
1562
+ $arg = $name.id.id;
1563
+ }
1564
+ (
1565
+ val=Number
1566
+ { if ($arg && $art && $name.id) {
1567
+ $art[$arg] = this.numberLiteral( $val );
1568
+ }
1569
+ }
1570
+ |
1571
+ v=VARIABLE
1572
+ { if ($arg && $art && $name.id) {
1573
+ $art[$arg] = { literal: 'string', val: 'variable', location: this.tokenLocation($v) };
1574
+ }
1575
+ }
1576
+ |
1577
+ f=FLOATING
1578
+ { if ($arg && $art && $name.id) {
1579
+ $art[$arg] = { literal: 'string', val: 'floating', location: this.tokenLocation($f) };
1580
+ }
1581
+ }
1582
+ )
1583
+ ;
1584
+
1585
+ // Queries: the main query structure --------------------------------------------
1586
+
1587
+ queryEOF returns [ query ]
1588
+ :
1589
+ q=queryExpression { $query = $q.query; } EOF
1590
+ ;
1591
+
1592
+ projectionSpec returns[ query ] locals[ src ]
1593
+ @after { this.attachLocation($query); }
1594
+ :
1595
+ proj=PROJECTION ON
1596
+ // now a simplified `tableTerm`:
1597
+ {
1598
+ $src = { path: [], scope: 0 };
1599
+ $query = { op: this.valueWithTokenLocation( 'SELECT', $proj ), from: $src, location: this.startLocation() };
1600
+ }
1601
+ fromPath[ $src, 'artref']
1602
+ ( ':'
1603
+ { $src.scope = $src.path.length; }
1604
+ fromPath[ $src, 'ref']
1605
+ )?
1606
+ ( AS aliasName=ident['FromAlias'] { $src.name = $aliasName.id } )?
1607
+ // ANTLR errors are better if we use ( A )? instead of ( A | ):
1608
+ { if (!$src.name) this.classifyImplicitName( $src.scope ? 'FromAlias' : 'Without' ); }
1609
+ bracedSelectItemListDef[ $query, 'columns' ]?
1610
+ excludingClause[ $query ]?
1611
+ ;
1612
+
1613
+ projectionClauses[ query ]
1614
+ @after { this.attachLocation($query); }
1615
+ :
1616
+ ( WHERE cond=condition { $query.where = $cond.cond; } )?
1617
+ (
1618
+ GROUP BY
1619
+ e1=expression { $query.groupBy = [ $e1.expr ]; }
1620
+ ( ',' en=expression { $query.groupBy.push( $en.expr ); } )*
1621
+ )?
1622
+ ( HAVING having=condition { $query.having = $having.cond; } )?
1623
+ ( ob=orderByClause[ $query ] { $query = $ob.query; } ) ?
1624
+ ( lc=limitClause[ $query ] { $query = $lc.query; } ) ?
1625
+ ;
1626
+
1627
+ queryExpression returns[ query ] // QLSubqueryComplex, SubqueryComplex
1628
+ @after{ this.attachLocation($query); }
1629
+ :
1630
+ qt1=queryTerm { $query = $qt1.query; }
1631
+ (
1632
+ ( op=UNION q=( DISTINCT | ALL )?
1633
+ | op=EXCEPT q=DISTINCT?
1634
+ | op=MINUS q=DISTINCT?
1635
+ )
1636
+ qt=queryTerm
1637
+ { if ($qt.query) $query = this.leftAssocBinaryOp( $query, $op, $q, $qt.query );; $ctx.q = null; }
1638
+ )*
1639
+ ( ob=orderByClause[ $query ] { if ($ob.query) $query = $ob.query; } ) ?
1640
+ ( lc=limitClause[ $query ] { if ($lc.query) $query = $lc.query; } ) ?
1641
+ ;
1642
+
1643
+ queryTerm returns[ query ]
1644
+ @after{ this.attachLocation($query); }
1645
+ :
1646
+ qt1=queryPrimary { $query = $qt1.query; }
1647
+ (
1648
+ intersect=INTERSECT quantifier=DISTINCT?
1649
+ qt=queryPrimary
1650
+ { $query = this.leftAssocBinaryOp( $query, $intersect, $quantifier, $qt.query );
1651
+ $ctx.quantifier = null; } // reset for loop
1652
+ )*
1653
+ ;
1654
+
1655
+ queryPrimary returns[ query = {} ]
1656
+ @after { this.attachLocation($query); }
1657
+ :
1658
+ open='(' qe=queryExpression close=')'
1659
+ { $query = this.surroundByParens( $qe.query, $open, $close ); }
1660
+ |
1661
+ select=SELECT
1662
+ { $query = { op: this.valueWithTokenLocation( 'SELECT', $select ), location: this.startLocation() }; }
1663
+ (
1664
+ FROM querySource[ $query ]
1665
+ (
1666
+ mixin=MIXIN '{' { $query.mixin = this.createDict(); }
1667
+ mixinElementDef[ $query ]*
1668
+ '}' { this.finalizeDictOrArray( $query.mixin ); }
1669
+ INTO
1670
+ )?
1671
+ ( ad=( ALL | DISTINCT ) // TODO: or directly after SELECT ?
1672
+ { $query.quantifier = this.valueWithTokenLocation( $ad.text.toLowerCase(), $ad ); }
1673
+ )?
1674
+ bracedSelectItemListDef[ $query, 'columns' ]?
1675
+ excludingClause[ $query ]?
1676
+ |
1677
+ ( ad=( ALL | DISTINCT ) // TODO: or directly after SELECT ?
1678
+ { $query.quantifier = this.valueWithTokenLocation( $ad.text.toLowerCase(), $ad ); }
1679
+ )?
1680
+ { $query.columns = []; } // set it early to avoid "wildcard" errors
1681
+ selectItemDef[ $query.columns ]
1682
+ ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
1683
+ selectItemDef[ $query.columns ]
1684
+ )*
1685
+ FROM querySource[ $query ]
1686
+ )
1687
+ ( WHERE cond=condition { $query.where = $cond.cond; } )?
1688
+ (
1689
+ GROUP BY
1690
+ e1=expression { $query.groupBy = [ $e1.expr ]; }
1691
+ ( ',' en=expression { $query.groupBy.push( $en.expr ); } )*
1692
+ )?
1693
+ ( HAVING having=condition { $query.having = $having.cond; } )?
1694
+ ;
1695
+
1696
+ querySource[ query ]
1697
+ @after { this.attachLocation($query.from); }
1798
1698
  :
1799
- simplePath[ $art.type, 'artref' ]
1699
+ t1=tableExpression { $query.from = $t1.table; }
1800
1700
  (
1801
- typeRefArgs[ $art ]
1802
- |
1803
- ':'
1804
- { $art.type.scope = $art.type.path.length; }
1805
- simplePath[ $art.type, 'ref']
1701
+ { const location = this.tokenLocation( this.getCurrentToken() );
1702
+ $query.from = { op: { val: 'join', location },
1703
+ join: { val: 'cross', location },
1704
+ args: [$t1.table] }; }
1705
+ ( ',' tn=tableExpression { if ($tn.table) $query.from.args.push( $tn.table ); } )+
1806
1706
  )?
1807
1707
  ;
1808
1708
 
1809
- typeRefArgs[ art ]
1709
+ tableExpression returns[ table ] // TableOrJoin
1710
+ @after { this.attachLocation($table); }
1810
1711
  :
1811
- paren='(' { $art['$'+'typeArgs'] = this.createArray(); }
1712
+ qt=tableTerm { $table = $qt.table; }
1812
1713
  (
1813
- // unnamed arguments
1814
- head=Number
1815
- { $art['$'+'typeArgs'].push( this.numberLiteral( $head ) ); }
1816
- ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1817
- (
1818
- v=VARIABLE
1819
- { $art['$'+'typeArgs'].push(
1820
- { literal: 'string', val: 'variable', location: this.tokenLocation($v) } );
1821
- }
1822
- |
1823
- f=FLOATING
1824
- { $art['$'+'typeArgs'].push(
1825
- { literal: 'string', val: 'floating', location: this.tokenLocation($f) } );
1826
- }
1827
- |
1828
- tail=Number
1829
- { $art['$'+'typeArgs'].push( this.numberLiteral( $tail ) ); }
1830
- )
1831
- )*
1714
+ join=joinOp[ $table ] { $table = $join.table; }
1715
+ te=tableExpression
1716
+ { if (!$table) { $table = {}; } else if ($te.table) $table.args.push( $te.table ); }
1717
+ ON cond=condition { $table.on = $cond.cond; }
1832
1718
  |
1833
- // named arguments
1834
- typeNamedArg[ $art ]
1835
- ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1836
- typeNamedArg[ $art ]
1837
- )*
1838
- )
1839
- ')'{ this.finalizeDictOrArray( $art['$'+'typeArgs']); }
1840
- ;
1841
-
1842
- typeNamedArgList[ art ]
1843
- :
1844
- paren='('
1845
- typeNamedArg[ $art ]
1846
- ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1847
- typeNamedArg[ $art ]
1719
+ crj=CROSS jn=JOIN tt=tableTerm
1720
+ { if (!$table) { $table = {}; } $table = this.leftAssocBinaryOp( $table, $jn, $crj, $tt.table, 'join' ); }
1848
1721
  )*
1849
- ')'
1850
1722
  ;
1851
1723
 
1852
- typeNamedArg[ art ] locals[ arg = '' ]
1724
+ tableTerm returns [ table ]
1725
+ @after{ /* #ATN 1 */ this.attachLocation($table); }
1853
1726
  :
1854
- name=ident['paramname']
1855
- ':'
1856
- { if ($name.id && this.checkTypeFacet( $art, $name.id ))
1857
- $arg = $name.id.id;
1858
- }
1727
+ { $table = { path: [], scope: 0 }; }
1728
+ fromPath[ $table, 'artref']
1729
+ ( ':'
1730
+ { $table.scope = $table.path.length; }
1731
+ fromPath[ $table, 'ref']
1732
+ )?
1733
+ ( AS n1=ident['FromAlias'] { $table.name = $n1.id }
1734
+ | n2=identNoKeyword['FromAlias'] { $table.name = this.fragileAlias( $n2.id ); }
1735
+ // if we would use rule `ident`, we would either had to make all JOIN
1736
+ // kinds reserved or introduce ATN
1737
+ )?
1738
+ // ANTLR errors are better if we use ( A | B )? instead of ( A | B | ):
1739
+ { if (!$table.name) this.classifyImplicitName( $table.scope ? 'FromAlias' : 'Without' ); }
1740
+ |
1741
+ open='('
1742
+ // #ATN: The following alternative is not LL1, because both can start with
1743
+ // left-paren, but queryExpression has SELECT after initial left-parens
1859
1744
  (
1860
- val=Number
1861
- { if ($arg && $art && $name.id) {
1862
- $art[$arg] = this.numberLiteral( $val );
1863
- }
1864
- }
1865
- |
1866
- v=VARIABLE
1867
- { if ($arg && $art && $name.id) {
1868
- $art[$arg] = { literal: 'string', val: 'variable', location: this.tokenLocation($v) };
1869
- }
1870
- }
1745
+ qe=queryExpression close=')'
1746
+ { $table = this.surroundByParens( $qe.query, $open, $close, true ); }
1747
+ ( AS a1=ident['FromAlias'] { $table.name = $a1.id } // for defining table aliass
1748
+ | a2=identNoKeyword['FromAlias'] { $table.name = this.fragileAlias( $a2.id, true ); }
1749
+ // not using ident` to have a similar behavior to above
1750
+ )
1871
1751
  |
1872
- f=FLOATING
1873
- { if ($arg && $art && $name.id) {
1874
- $art[$arg] = { literal: 'string', val: 'floating', location: this.tokenLocation($f) };
1875
- }
1876
- }
1752
+ te=tableExpression close=')'
1753
+ { $table = this.surroundByParens( $te.table, $open, $close ); }
1877
1754
  )
1878
1755
  ;
1879
1756
 
1880
-
1881
- // Queries -------------------------------------------------------------------
1882
-
1883
- queryExpression returns[ query ] // QLSubqueryComplex, SubqueryComplex
1884
- @after{ this.attachLocation($query); }
1757
+ fromPath[ qp, idkind ]
1758
+ @after{ this.attachLocation($qp); }
1885
1759
  :
1886
- qt1=queryTerm { $query = $qt1.query; }
1760
+ id=ident[$idkind] { this.pushIdent( $qp.path, $id.id ); }
1761
+ ( fromArguments[ $id.id ] cardinalityAndFilter[ $id.id ]?
1762
+ | cardinalityAndFilter[ $id.id ]
1763
+ )?
1887
1764
  (
1888
- ( op=UNION q=( DISTINCT | ALL )?
1889
- | op=EXCEPT q=DISTINCT?
1890
- | op=MINUS q=DISTINCT?
1891
- )
1892
- qt=queryTerm
1893
- { if ($qt.query) $query = this.leftAssocBinaryOp( $query, $op, $q, $qt.query );; $ctx.q = null; }
1765
+ '.' id=ident[$idkind] { this.pushIdent( $qp.path, $id.id ); }
1766
+ ( fromArguments[ $id.id ] cardinalityAndFilter[ $id.id ]?
1767
+ | cardinalityAndFilter[ $id.id ]
1768
+ )?
1894
1769
  )*
1895
- ( ob=orderByClause[ $query ] { if ($ob.query) $query = $ob.query; } ) ?
1896
- ( lc=limitClause[ $query ] { if ($lc.query) $query = $lc.query; } ) ?
1897
1770
  ;
1898
1771
 
1899
- orderByClause[ inQuery ] returns [ query ]
1772
+ fromArguments[ pathStep ]
1773
+ @init{ if (!$pathStep) $pathStep = {}; } // grammar robustness, see test/negative/parser/NamedExpression.cds
1900
1774
  :
1901
- ORDER BY { $query = this.unaryOpForParens( $inQuery, '$'+'query' ); }
1902
- ob1=orderBySpec { $query.orderBy = [ $ob1.ob ]; }
1903
- ( ',' obn=orderBySpec { $query.orderBy.push( $obn.ob ); } )*
1775
+ '(' { $pathStep.args = this.createDict(); $pathStep['$'+'syntax'] = ':'; } // necessary?
1776
+ name=ident['paramname'] ':'
1777
+ namedExpression[ $pathStep, $name.id ]
1778
+ ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1779
+ name=ident['paramname'] ':'
1780
+ namedExpression[ $pathStep, $name.id ]
1781
+ )*
1782
+ ')' { this.finalizeDictOrArray( $pathStep.args ); }
1783
+ ;
1784
+
1785
+ // Queries: columns and other clauses -------------------------------------------
1786
+
1787
+ excludingClause[ query ]
1788
+ :
1789
+ // syntax is less than ideal - EXCLUDING is only useful for `*` - with
1790
+ // this syntax, people wonder what happens with explicit select items
1791
+ EXCLUDING '{' { $query.excludingDict = this.createDict(); }
1792
+ projectionExclusion[ $query ]
1793
+ ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
1794
+ projectionExclusion[ $query ]
1795
+ )*
1796
+ '}' { this.finalizeDictOrArray( $query.excludingDict ); }
1904
1797
  ;
1905
1798
 
1906
- // Generic function ORDER BY clause, e.g. `first_value(id order by name)`
1907
- // lhsExpr is the left expression of the ORDER BY clause.
1908
- functionOrderByClause[ lhsExpr ] returns [ expr ]
1909
- @after { this.attachLocation( $expr ); }
1799
+ projectionExclusion[ outer ] locals[ art = {} ]
1800
+ @after { this.attachLocation($art); }
1910
1801
  :
1911
- o=ORDER b=BY { $expr = { op: this.valueWithTokenLocation( 'orderBy', $o, $b ) , args: [ $lhsExpr ] }}
1912
- ob1=orderBySpec { $expr.args.push( $ob1.ob ); }
1913
- ( ',' obn=orderBySpec { $expr.args.push( $obn.ob ); } )*
1802
+ name=ident['ref']
1803
+ { this.addDef( $art, $outer, 'excludingDict', '', $name.id ); }
1914
1804
  ;
1915
1805
 
1916
- overOrderByClause returns [ expr ]
1917
- @after { this.attachLocation( $expr ); }
1806
+ // Actually, this is a subset if elementDefInner...
1807
+ // TODO: the corresponding restrictions must also be checked in the core
1808
+ // compiler, as the mixin element could come via CSN
1809
+ mixinElementDef[ outer ] locals[ art = {} ]
1810
+ @after { /* #ATN 2 */ this.attachLocation($art); }
1918
1811
  :
1919
- o=ORDER b=BY { $expr = { op: this.valueWithTokenLocation( 'overOrderBy', $o, $b ) , args: [] }}
1920
- ob1=orderBySpec { $expr.args.push( $ob1.ob ); }
1921
- ( ',' obn=orderBySpec { $expr.args.push( $obn.ob ); } )*
1812
+ name=ident['Mixin']
1813
+ { this.addDef( $art, $outer, 'mixin', 'mixin', $name.id ); }
1814
+ (
1815
+ ':'
1816
+ // #ATN: referenced type name can be ASSOCIATION or COMPOSITION
1817
+ (
1818
+ typeAssociationBase[ $art, false ]
1819
+ // #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
1820
+ ( typeToMany[ $art ] | typeToOne[ $art ] | simplePath[ $art.target, 'artref' ] )
1821
+ typeAssociationCont[ $art ]?
1822
+ |
1823
+ typeRefOptArgs[ $art ]
1824
+ ( as='=' expression
1825
+ { this.error( 'syntax-unsupported-calc-elem', $as ); }
1826
+ )?
1827
+ )
1828
+ |
1829
+ as='=' expression
1830
+ { this.error( 'syntax-unsupported-calc-elem', $as ); }
1831
+ )
1832
+ requiredSemi
1922
1833
  ;
1923
1834
 
1924
- partitionByClause returns [ expr ]
1925
- @after { this.attachLocation( $expr ); }
1835
+ selectItemDef[ outer ] locals[ art ]
1836
+ @after{ if ($art) this.attachLocation( $art ); }
1926
1837
  :
1927
- p=PARTITION b=BY { $expr = { op: this.valueWithTokenLocation( 'partitionBy', $p, $b ) , args: [] }}
1928
- e1=expression { $expr.args.push( $e1.expr ); }
1929
- ( ',' en=expression { $expr.args.push( $en.expr ); } )*
1838
+ star='*'
1839
+ { $outer.push( this.valueWithTokenLocation( '*', $star ) ); }
1840
+ |
1841
+ { $art = {};; this.docComment( $art ); }
1842
+ annotationAssignment_atn[ $art ]*
1843
+ // VIRTUAL is keyword, except if before the following tokens texts:
1844
+ { this.setLocalToken( 'VIRTUAL', 'VIRTUAL', /^([,.:\[@]|as)$/i ) ; } // not '{'
1845
+ ( virtual=VIRTUAL { $art.virtual = this.valueWithTokenLocation( true, $virtual ); } )?
1846
+ ( key=KEY { $art.key = this.valueWithTokenLocation( true, $key ); } )?
1847
+ selectItemDefBody[ $art, $outer ]
1930
1848
  ;
1931
1849
 
1932
- windowFrameClause returns [ wf ]
1933
- @after { this.attachLocation( $wf ); }
1850
+ selectItemDefBody[ art, outer ] locals[ assoc ]
1851
+ @after{ /* #ATN 2 */ }
1934
1852
  :
1935
- r=ROWS { $wf = { op: this.valueWithTokenLocation( 'rows', $r ) , args: [] }}
1936
- wfe=windowFrameExtentSpec { $wf.args.push( $wfe.wfe ); }
1853
+ { $outer.push( $art ); }
1854
+ (
1855
+ e=expression { $art.value = $e.expr; }
1856
+ // we cannot use 'condition' instead, as long as we allow aliases without
1857
+ // AS (using rule 'ident' instead of 'identNoKeyword') -> ambiguities
1858
+ ( as=AS n1=ident['Item'] { $art.name = $n1.id }
1859
+ | n2=ident['Item'] { $art.name = this.fragileAlias( $n2.id, true ); }
1860
+ | { this.classifyImplicitName( 'Item', $e.expr ); }
1861
+ )
1862
+ { if ($art.value && !$art.value.path) this.excludeExpected( ["'.'", "'{'"] );
1863
+ else if ($art.name) this.excludeExpected( ["'.'"] );
1864
+ }
1865
+ (
1866
+ { this.reportExpandInline( $art, false ); }
1867
+ selectItemInlineList[ $art, 'expand' ]
1868
+ excludingClause[ $art ]?
1869
+ // TODO: we might alternatively allow AS here
1870
+ |
1871
+ { this.reportExpandInline( $art, $as || this._input.LT(-1) ); }
1872
+ DOTbeforeBRACE // ...orASTERISK
1873
+ (
1874
+ selectItemInlineList[ $art, 'inline' ]
1875
+ excludingClause[ $art ]?
1876
+ |
1877
+ star='*'
1878
+ { $art.inline = [ this.valueWithTokenLocation( '*', $star ) ]; }
1879
+ )
1880
+ )?
1881
+ |
1882
+ selectItemInlineList[ $art, 'expand' ]
1883
+ excludingClause[ $art ]?
1884
+ AS n1=ident['Item'] { $art.name = $n1.id }
1885
+ )
1886
+ { this.docComment( $art ); }
1887
+ annotationAssignment_fix[ $art ]*
1888
+ ( ':'
1889
+ // #ATN: typeRefOptArgs can start with TYPE, REDIRECTED, ASSOCIATION
1890
+ ( re=REDIRECTED to=TO
1891
+ { $art.target = {}; }
1892
+ simplePath[ $art.target, 'artref' ]
1893
+ (
1894
+ typeAssociationCont[ $art ]
1895
+ |
1896
+ { this.docComment( $art ); }
1897
+ annotationAssignment_ll1[ $art ]*
1898
+ )
1899
+ | typeTypeOf[ $art ]
1900
+ { this.docComment( $art ); }
1901
+ annotationAssignment_ll1[ $art ]*
1902
+ | l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
1903
+ typeRefOptArgs[ $art ]
1904
+ { this.docComment( $art ); }
1905
+ annotationAssignment_ll1[ $art ]*
1906
+ | typeRefOptArgs[ $art ]
1907
+ { this.docComment( $art ); }
1908
+ annotationAssignment_ll1[ $art ]*
1909
+ |
1910
+ { $assoc = this.associationInSelectItem( $art ); }
1911
+ typeAssociationBase[ $assoc, false ]
1912
+ // #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
1913
+ ( typeToMany[ $assoc ] | typeToOne[ $assoc ] | simplePath[ $assoc.target, 'artref' ] )
1914
+ ON cond=condition
1915
+ { $assoc.on=$cond.cond; }
1916
+ )
1917
+ )?
1937
1918
  ;
1938
1919
 
1939
- windowFrameExtentSpec returns[ wfe ]
1940
- @after { this.attachLocation( $wfe ); }
1920
+ bracedSelectItemListDef[ query ]
1941
1921
  :
1942
- { $wfe = {} }
1943
- windowFrameStartSpec [ $wfe ]
1944
- |
1945
- b=BETWEEN
1946
- { $wfe = { op: this.valueWithTokenLocation( 'frameBetween', $b ), args: [] } }
1947
- wfb1=windowFrameBoundSpec { $wfe.args.push( $wfb1.wfb ); }
1948
- AND
1949
- wfb2=windowFrameBoundSpec { $wfe.args.push( $wfb2.wfb ); }
1922
+ '{' { $query.columns = this.createArray(); }
1923
+ (
1924
+ selectItemDef[ $query.columns ]
1925
+ ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
1926
+ selectItemDef[ $query.columns ]
1927
+ )*
1928
+ )?
1929
+ '}' { this.finalizeDictOrArray( $query.columns ); }
1950
1930
  ;
1951
1931
 
1952
- windowFrameBoundSpec returns [ wfb ]
1953
- @after{ /* #ATN 1 */ }
1932
+ selectItemInlineList[ art, clause ]
1954
1933
  :
1955
- // #ATN: Not ll1 because `UNBOUNDED` could also be part of the windowFrameStartSpec
1956
- // `UNBOUNDED` would then be immediately followed by `PRECEDING`
1957
- u=UNBOUNDED f=FOLLOWING
1958
- { $wfb = { op: this.valueWithTokenLocation( 'unboundedFollowing', $u, $f ), args: []} }
1959
- |
1960
- // #ATN: Not ll1 because `Number` could also be part of the windowFrameStartSpec
1961
- // `Number` would then be immediately followed by `PRECEDING`
1962
- n=Number f=FOLLOWING
1963
- { $wfb = { op: this.valueWithTokenLocation( 'following', $n, $f ), args: [ this.numberLiteral( $n ) ]} }
1964
- |
1965
- { $wfb = {} }
1966
- windowFrameStartSpec [ $wfb ]
1934
+ '{' { $art[$clause] = this.createArray(); }
1935
+ (
1936
+ selectItemInlineDef[ $art[$clause] ]
1937
+ ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
1938
+ selectItemInlineDef[ $art[$clause] ]
1939
+ )*
1940
+ )?
1941
+ '}' { this.finalizeDictOrArray( $art[$clause] ); }
1967
1942
  ;
1968
1943
 
1969
- windowFrameStartSpec [ wf ]
1970
- @after { this.attachLocation( $wf ); }
1944
+ selectItemInlineDef[ outer ] locals[ art ]
1945
+ @after{ if ($art) this.attachLocation( $art ); }
1971
1946
  :
1972
- u=UNBOUNDED p=PRECEDING
1973
- {
1974
- $wf.op = this.valueWithTokenLocation( 'unboundedPreceding', $u, $p );
1975
- $wf.args = [];
1976
- }
1977
- |
1978
- n=Number p=PRECEDING
1979
- {
1980
- $wf.op = this.valueWithTokenLocation( 'preceding', $p );
1981
- $wf.args = [ this.numberLiteral( $n ) ];
1982
- }
1947
+ star='*'
1948
+ { $outer.push( this.valueWithTokenLocation( '*', $star ) ); }
1983
1949
  |
1984
- c=CURRENT r=ROW
1985
- {
1986
- $wf.op = this.valueWithTokenLocation( 'currentRow', $c, $r );
1987
- $wf.args = [];
1988
- }
1950
+ { $art = {};; this.docComment( $art ); }
1951
+ annotationAssignment_atn[ $art ]*
1952
+ selectItemDefBody[ $art, $outer ]
1989
1953
  ;
1990
1954
 
1991
- overClause returns [ over ]
1992
- @after { this.attachLocation( $over ); }
1955
+ orderByClause[ inQuery ] returns [ query ]
1993
1956
  :
1994
- o=OVER { $over = { op: this.valueWithTokenLocation( 'over', $o ) , args: [] } }
1995
- '(' // TODO: check whether an extra location could be useful
1996
- ( pb=partitionByClause { $over.args.push( $pb.expr ); } )?
1997
- ( ob=overOrderByClause { $over.args.push( $ob.expr ); } )?
1998
- ( wf=windowFrameClause { $over.args.push( $wf.wf ); } )?
1999
- ')'
1957
+ ORDER BY { $query = this.unaryOpForParens( $inQuery, '$'+'query' ); }
1958
+ ob1=orderBySpec { $query.orderBy = [ $ob1.ob ]; }
1959
+ ( ',' obn=orderBySpec { $query.orderBy.push( $obn.ob ); } )*
2000
1960
  ;
2001
1961
 
2002
1962
  limitClause[ inQuery ] returns [ query ]
@@ -2020,87 +1980,6 @@ orderBySpec returns[ ob ]
2020
1980
  )?
2021
1981
  ;
2022
1982
 
2023
- queryTerm returns[ query ]
2024
- @after{ this.attachLocation($query); }
2025
- :
2026
- qt1=queryPrimary { $query = $qt1.query; }
2027
- (
2028
- intersect=INTERSECT quantifier=DISTINCT?
2029
- qt=queryPrimary
2030
- { $query = this.leftAssocBinaryOp( $query, $intersect, $quantifier, $qt.query );
2031
- $ctx.quantifier = null; } // reset for loop
2032
- )*
2033
- ;
2034
-
2035
- queryPrimary returns[ query = {} ]
2036
- @after { this.attachLocation($query); }
2037
- :
2038
- open='(' qe=queryExpression close=')'
2039
- { $query = this.surroundByParens( $qe.query, $open, $close ); }
2040
- |
2041
- select=SELECT
2042
- { $query = { op: this.valueWithTokenLocation( 'SELECT', $select ), location: this.startLocation() }; }
2043
- (
2044
- FROM querySource[ $query ]
2045
- (
2046
- mixin=MIXIN '{' { $query.mixin = this.createDict(); }
2047
- mixinElementDef[ $query ]*
2048
- '}' { this.finalizeDictOrArray( $query.mixin ); }
2049
- INTO
2050
- )?
2051
- ( ad=( ALL | DISTINCT ) // TODO: or directly after SELECT ?
2052
- { $query.quantifier = this.valueWithTokenLocation( $ad.text.toLowerCase(), $ad ); }
2053
- )?
2054
- bracedSelectItemListDef[ $query, 'columns' ]?
2055
- excludingClause[ $query ]?
2056
- |
2057
- ( ad=( ALL | DISTINCT ) // TODO: or directly after SELECT ?
2058
- { $query.quantifier = this.valueWithTokenLocation( $ad.text.toLowerCase(), $ad ); }
2059
- )?
2060
- { $query.columns = []; } // set it early to avoid "wildcard" errors
2061
- selectItemDef[ $query.columns ]
2062
- ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
2063
- selectItemDef[ $query.columns ]
2064
- )*
2065
- FROM querySource[ $query ]
2066
- )
2067
- ( WHERE cond=condition { $query.where = $cond.cond; } )?
2068
- (
2069
- GROUP BY
2070
- e1=expression { $query.groupBy = [ $e1.expr ]; }
2071
- ( ',' en=expression { $query.groupBy.push( $en.expr ); } )*
2072
- )?
2073
- ( HAVING having=condition { $query.having = $having.cond; } )?
2074
- ;
2075
-
2076
- querySource[ query ]
2077
- @after { this.attachLocation($query.from); }
2078
- :
2079
- t1=tableExpression { $query.from = $t1.table; }
2080
- (
2081
- { const location = this.tokenLocation( this.getCurrentToken() );
2082
- $query.from = { op: { val: 'join', location },
2083
- join: { val: 'cross', location },
2084
- args: [$t1.table] }; }
2085
- ( ',' tn=tableExpression { if ($tn.table) $query.from.args.push( $tn.table ); } )+
2086
- )?
2087
- ;
2088
-
2089
- tableExpression returns[ table ] // TableOrJoin
2090
- @after { this.attachLocation($table); }
2091
- :
2092
- qt=tableTerm { $table = $qt.table; }
2093
- (
2094
- join=joinOp[ $table ] { $table = $join.table; }
2095
- te=tableExpression
2096
- { if (!$table) { $table = {}; } else if ($te.table) $table.args.push( $te.table ); }
2097
- ON cond=condition { $table.on = $cond.cond; }
2098
- |
2099
- crj=CROSS jn=JOIN tt=tableTerm
2100
- { if (!$table) { $table = {}; } $table = this.leftAssocBinaryOp( $table, $jn, $crj, $tt.table, 'join' ); }
2101
- )*
2102
- ;
2103
-
2104
1983
  joinOp[ left ] returns[ table ] locals [ join ]
2105
1984
  :
2106
1985
  ( op=JOIN { $join = 'inner'; }
@@ -2148,55 +2027,7 @@ joinCardinality returns [ joinCard ]
2148
2027
  )
2149
2028
  ;
2150
2029
 
2151
- tableTerm returns [ table ]
2152
- @after{ /* #ATN 1 */ this.attachLocation($table); }
2153
- :
2154
- { $table = { path: [], scope: 0 }; }
2155
- fromPath[ $table, 'artref']
2156
- ( ':'
2157
- { $table.scope = $table.path.length; }
2158
- fromPath[ $table, 'ref']
2159
- )?
2160
- ( AS n1=ident['FromAlias'] { $table.name = $n1.id }
2161
- | n2=identNoKeyword['FromAlias'] { $table.name = this.fragileAlias( $n2.id ); }
2162
- // if we would use rule `ident`, we would either had to make all JOIN
2163
- // kinds reserved or introduce ATN
2164
- )?
2165
- // ANTLR errors are better if we use ( A | B )? instead of ( A | B | ):
2166
- { if (!$table.name) this.classifyImplicitName( $table.scope ? 'FromAlias' : 'Without' ); }
2167
- |
2168
- open='('
2169
- // #ATN: The following alternative is not LL1, because both can start with
2170
- // left-paren, but queryExpression has SELECT after initial left-parens
2171
- (
2172
- qe=queryExpression close=')'
2173
- { $table = this.surroundByParens( $qe.query, $open, $close, true ); }
2174
- ( AS a1=ident['FromAlias'] { $table.name = $a1.id } // for defining table aliass
2175
- | a2=identNoKeyword['FromAlias'] { $table.name = this.fragileAlias( $a2.id, true ); }
2176
- // not using ident` to have a similar behavior to above
2177
- )
2178
- |
2179
- te=tableExpression close=')'
2180
- { $table = this.surroundByParens( $te.table, $open, $close ); }
2181
- )
2182
- ;
2183
-
2184
- fromPath[ qp, idkind ]
2185
- @after{ this.attachLocation($qp); }
2186
- :
2187
- id=ident[$idkind] { this.pushIdent( $qp.path, $id.id ); }
2188
- ( fromArguments[ $id.id ] cardinalityAndFilter[ $id.id ]?
2189
- | cardinalityAndFilter[ $id.id ]
2190
- )?
2191
- (
2192
- '.' id=ident[$idkind] { this.pushIdent( $qp.path, $id.id ); }
2193
- ( fromArguments[ $id.id ] cardinalityAndFilter[ $id.id ]?
2194
- | cardinalityAndFilter[ $id.id ]
2195
- )?
2196
- )*
2197
- ;
2198
-
2199
- // Conditions and expressions ------------------------------------------------
2030
+ // Conditions and expressions ---------------------------------------------------
2200
2031
 
2201
2032
  // With "separate" `condition` and `expression` rules, we have long LL
2202
2033
  // ambiguities (not so with LALR used in Bison) with initial left parentheses:
@@ -2212,221 +2043,214 @@ fromPath[ qp, idkind ]
2212
2043
  // be then ensured by code (either in actions of the grammar or in a check
2213
2044
  // phase - to be discussed).
2214
2045
  //
2046
+ // We cannot generally allow `condition` where `expression` is written:
2047
+ // - IN is also used in non-standard function `args` and as predicate:
2048
+ // - AND is boolean operator and also used for BETWEEN…AND
2049
+ //
2215
2050
  // ANTLR4s left-recursion feature cannot be used as we will have rule
2216
2051
  // arguments.
2217
2052
 
2218
- condition returns [ cond ] locals [ args = [], orl = [] ]
2219
- @after{
2220
- $cond = ($args.length === 1)
2221
- ? this.attachLocation( $args[0] )
2222
- : this.attachLocation({ op: $orl[0], args: $args });
2223
- }
2053
+ conditionEOF returns [ cond ]
2054
+ :
2055
+ c=condition { $cond = $c.cond; } EOF
2056
+ ;
2057
+
2058
+ condition returns [ cond ] locals[ args = [] ]
2059
+ @after{ $cond = this.argsExpression( $args ); }
2224
2060
  :
2225
2061
  c1=conditionAnd { $args.push($c1.cond); }
2226
- ( or=OR c2=conditionAnd { $args.push($c2.cond); $orl.push(this.valueWithTokenLocation( 'or', $or ))} )*
2062
+ (
2063
+ or=OR { this.pushXprToken( $args ); }
2064
+ c2=conditionAnd { $args.push($c2.cond); }
2065
+ )*
2227
2066
  ;
2228
2067
 
2229
- conditionAnd returns [ cond ] locals [ args = [], andl = [] ]
2230
- @after{
2231
- $cond = ($args.length === 1)
2232
- ? $args[0]
2233
- : this.attachLocation({ op: $andl[0], args: $args });
2234
- }
2068
+ conditionAnd returns [ cond ] locals[ args = [] ]
2069
+ @after{ $cond = this.argsExpression( $args, null, 'and' ); } // 'and' used by A2J and checks
2235
2070
  :
2236
2071
  c1=conditionTerm { $args.push($c1.cond); }
2237
- ( and=AND c2=conditionTerm { $args.push($c2.cond); $andl.push(this.valueWithTokenLocation( 'and', $and )) } )*
2072
+ (
2073
+ and=AND { this.pushXprToken( $args ); }
2074
+ c2=conditionTerm { $args.push($c2.cond); }
2075
+ )*
2238
2076
  ;
2239
2077
 
2240
2078
  // Note: New operators need to be added to functionExpressionOperatorsRequireParentheses[] in toCdl.js.
2241
- conditionTerm returns [ cond ]
2242
- @after{
2243
- if ($cond) { this.attachLocation($cond); } else { $cond = $expr.expr; }
2244
- }
2079
+ conditionTerm returns [ cond ] locals[ args = [] ]
2080
+ @after{ $cond = this.argsExpression( $args, null, '=' ); }// op: '=' used by A2J and checks
2245
2081
  :
2246
- nt=NOT ct=conditionTerm
2247
- { $cond = { op: this.valueWithTokenLocation( 'not', $nt ), args: [ $ct.cond ] }; }
2082
+ nt=NOT { this.pushXprToken( $args ); }
2083
+ ct=conditionTerm { $args.push( $ct.cond ); }
2248
2084
  |
2249
- ex=EXISTS
2085
+ EXISTS { this.pushXprToken( $args ); }
2250
2086
  (
2251
2087
  open='(' qe=queryExpression close=')'
2252
- { $cond = { op: this.valueWithTokenLocation( 'exists', $ex ),
2253
- args: [ this.surroundByParens( $qe.query, $open, $close, true ) ] }; }
2088
+ { $args.push( this.surroundByParens( $qe.query, $open, $close, true ) ); }
2254
2089
  |
2255
2090
  qm=( HideAlternatives | '?' )
2256
- { $cond = { op: this.valueWithTokenLocation( 'exists', $ex ), args: [
2257
- { param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' }
2258
- ] };
2091
+ { $args.push( { param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' } );
2259
2092
  this.csnParseOnly( 'syntax-unsupported-param', [ $qm ], { '#': 'dynamic', code: '?' } );
2260
2093
  }
2261
2094
  |
2262
- ep=valuePath[ 'ref' ]
2263
- { $ep.qp['$'+'expected'] = 'exists';
2264
- $cond = { op: this.valueWithTokenLocation( 'exists', $ex ), args: [ $ep.qp ] };
2265
- }
2095
+ ep=valuePath[ 'ref' ] { $args.push( $ep.qp ); }
2096
+ { $ep.qp['$'+'expected'] = 'exists'; } // hm, really in parser? what about CSN input?
2266
2097
  )
2267
2098
  |
2268
- expr=expression // see @after
2099
+ expr=expression { $args.push( $expr.expr ); }
2269
2100
  (
2270
- rel=( '=' | '<>' | '>' | '>=' | '<' | '<=' | '!=' )
2271
- { $cond = { op: this.valueWithTokenLocation( $rel.text, $rel ), args: [ $expr.expr ] }; }
2272
- ( asa=( ANY | SOME | ALL )
2273
- { $cond.quantifier = this.valueWithTokenLocation( $asa.text.toLowerCase(), $asa ); }
2274
- )?
2275
- e2=expression { $cond.args.push($e2.expr); }
2101
+ ( '=' | '<>' | '>' | '>=' | '<' | '<=' | '!=' ) { this.pushXprToken( $args ); }
2102
+ ( ( ANY | SOME | ALL ) { this.pushXprToken( $args ); } )?
2103
+ e2=expression { $args.push( $e2.expr ); }
2276
2104
  |
2277
- IS ( inn=NOT NULL | innu=NULL )
2278
- { $cond = { op: $inn ? this.valueWithTokenLocation( 'isNotNull', $inn ) : this.valueWithTokenLocation( 'isNull', $innu ), args: [ $expr.expr ] }; }
2105
+ IS { this.pushXprToken( $args ); }
2106
+ ( NOT { this.pushXprToken( $args ); } )?
2107
+ NULL { this.pushXprToken( $args ); }
2279
2108
  |
2280
- { $cond = { args: [ $expr.expr ] }; }
2281
- NOT predicate[ $cond, true ]
2282
- { if (!$cond.op) $cond = null; } // predicate failed to parse, avoid subseqential errors
2109
+ NOT { this.pushXprToken( $args ); }
2110
+ predicate[ $args ]
2283
2111
  |
2284
- { $cond = { args: [ $expr.expr ] }; }
2285
- predicate[ $cond, false ]
2286
- { if (!$cond.op) $cond = null; } // predicate failed to parse, avoid subseqential errors
2112
+ predicate[ $args ]
2287
2113
  )? // optional: for conditions in parentheses
2288
2114
  ;
2289
2115
 
2290
2116
  // Note: New operators need to be added to functionExpressionOperatorsRequireParentheses[] in toCdl.js.
2291
- predicate[ cond, negated ]
2292
- // As an alternative, we could have a `negated` properties for the operations
2293
- // `isNull`(!), `in`, `between` and `like` (or produce the same AST as for
2294
- // NOT (a BETWEEN b AND c)
2117
+ predicate[ args ]
2295
2118
  :
2296
- ino=IN e1=expression // including ExpressionList
2297
- { $cond.op = this.valueWithTokenLocation( (negated) ? 'notIn' : 'in', $ino ); $cond.args.push( $e1.expr ); }
2119
+ IN { this.pushXprToken( $args ); }
2120
+ // TODO: do we really allow it not to start with `(`? - TODO: try with warning
2121
+ e1=expression { $args.push( this.secureParens( $e1.expr ) ); } // including ExpressionList
2298
2122
  |
2299
- bw=BETWEEN e2=expression
2300
- { $cond.op = this.valueWithTokenLocation( (negated) ? 'notBetween' : 'between', $bw ); $cond.args.push( $e2.expr ); }
2301
- AND e3=expression { $cond.args.push( $e3.expr ); }
2123
+ BETWEEN { this.pushXprToken( $args ); }
2124
+ e2=expression { $args.push( $e2.expr ); }
2125
+ AND { this.pushXprToken( $args ); }
2126
+ e3=expression { $args.push( $e3.expr ); }
2302
2127
  |
2303
- lk=LIKE e4=expression
2304
- { $cond.op = this.valueWithTokenLocation( (negated) ? 'notLike' : 'like', $lk ); $cond.args.push( $e4.expr ); }
2305
- ( ESCAPE e5=expression { $cond.args.push( $e5.expr ); } )?
2128
+ LIKE { this.pushXprToken( $args ); }
2129
+ e4=expression { $args.push( $e4.expr ); }
2130
+ ( ESCAPE { this.pushXprToken( $args ); }
2131
+ e5=expression { $args.push( $e5.expr ); }
2132
+ )?
2306
2133
  ;
2307
2134
 
2308
- expression returns [ expr ]
2309
- @after{ if ($expr) { this.attachLocation($expr); } else { $expr = this.attachLocation({});} }
2135
+ expression returns [ expr ] locals [ args = [] ]
2136
+ @after{ $expr = this.argsExpression( $args ); }
2310
2137
  :
2311
- e1=expressionSum { $expr = $e1.expr; }
2138
+ e1=expressionSum { $args.push( $e1.expr ); }
2312
2139
  (
2313
- or='||' e2=expressionSum
2314
- {
2315
- $expr = {
2316
- op: this.valueWithTokenLocation( '||', $or ), args: [$expr, $e2.expr],
2317
- location: this.combinedLocation( $expr, $e2.expr ) };
2318
- }
2140
+ '||' { this.pushXprToken( $args ); }
2141
+ e2=expressionSum { $args.push( $e2.expr ); }
2319
2142
  )*
2320
2143
  ;
2321
2144
 
2322
- expressionSum returns [ expr ]
2323
- @after{ if ($expr) this.attachLocation($expr); }
2145
+ expressionSum returns [ expr ] locals [ args = [] ]
2146
+ @after{ $expr = this.argsExpression( $args ); }
2324
2147
  :
2325
- e1=expressionFactor { $expr = $e1.expr; }
2148
+ e1=expressionFactor { $args.push( $e1.expr ); }
2326
2149
  (
2327
- op=( '+' | '-' ) e2=expressionFactor
2328
- {
2329
- $expr = {
2330
- op: this.valueWithTokenLocation( $op.text, $op ), args: [$expr, $e2.expr],
2331
- location: this.combinedLocation( $expr, $e2.expr ) };
2332
- }
2150
+ ( '+' | '-' ) { this.pushXprToken( $args ); }
2151
+ e2=expressionFactor { $args.push( $e2.expr ); }
2333
2152
  )*
2334
2153
  ;
2335
2154
 
2336
- expressionFactor returns [ expr ]
2337
- @after{ if ($expr) this.attachLocation($expr); }
2155
+ expressionFactor returns [ expr ] locals [ args = [] ]
2156
+ @after{ $expr = this.argsExpression( $args ); }
2338
2157
  :
2339
- e1=expressionTerm { $expr = $e1.expr; }
2158
+ e1=expressionTerm { $args.push( $e1.expr ); }
2340
2159
  (
2341
- op=( '*' | '/' ) e2=expressionTerm
2342
- {
2343
- $expr = {
2344
- op: this.valueWithTokenLocation( $op.text, $op ), args: [$expr, $e2.expr],
2345
- location: this.combinedLocation( $expr, $e2.expr ) };
2346
- }
2160
+ ( '*' | '/' ) { this.pushXprToken( $args ); }
2161
+ e2=expressionTerm { $args.push( $e2.expr ); }
2347
2162
  )*
2348
2163
  ;
2349
2164
 
2350
- expressionTerm returns [ expr ] locals [ op, args = [] ]
2351
- @after{ /* #ATN 1 */ this.attachLocation($expr); }
2165
+ expressionTerm returns [ expr ] locals [ args = [] ]
2166
+ @after{ /* #ATN 1 */ $expr = this.argsExpression( $args ); }
2352
2167
  :
2353
- unary=( '+' | '-' ) e1=expressionTerm // prefix op or part of the number
2354
- { $expr = this.signedExpression( $unary, $e1.expr ); }
2168
+ ( '+' | '-' ) { this.pushXprToken( $args ); }
2169
+ e1=expressionTerm // prefix op or part of the number
2170
+ { this.signedExpression( $args, $e1.expr ); }
2171
+ |
2172
+ val=literalValue { $args.push( $val.val ); }
2355
2173
  |
2174
+ sf=specialFunction { $args.push( $sf.ret ); }
2175
+ |
2176
+ CASE { this.pushXprToken( $args ); }
2356
2177
  (
2357
- val=literalValue
2358
- { $expr = $val.val; }
2359
- |
2360
- sf=specialFunction
2361
- { $expr = $sf.ret; }
2362
- |
2363
- ca=CASE
2364
- { $expr = { op : this.valueWithTokenLocation( 'case', $ca ), args: [] }; }
2365
- (
2366
- e2=expression { $expr.args.push($e2.expr); }
2367
- ( ow=WHEN ew=expression THEN e3=expression
2368
- { $expr.args.push( this.createPrefixOp( $ow, [ $ew.expr, $e3.expr ] ) ); }
2369
- )+
2370
- |
2371
- ( ow=WHEN c=condition THEN e3=expression
2372
- { $expr.args.push( this.createPrefixOp( $ow, [ $c.cond, $e3.expr ] ) ); }
2373
- )+
2374
- )
2375
- ( el=ELSE e4=expression
2376
- { $expr.args.push( this.createPrefixOp( $el, [ $e4.expr ] ) ); }
2377
- )?
2378
- END
2379
- |
2380
- ne=NEW nqp=valuePath[ 'ref', null] // token rewrite for NEW
2381
- // please note: there will be no compiler-supported code completion after NEW
2382
- { $expr = { op: this.valueWithTokenLocation( 'new', $ne ), args: [] };
2383
- this.error( 'syntax-unsupported-new', $ne, { keyword: $ne.text }, '$(KEYWORD) is not supported' ); }
2178
+ e2=expression { $args.push( $e2.expr ); }
2179
+ ( WHEN { this.pushXprToken( $args ); }
2180
+ ew=expression { $args.push( $ew.expr ); }
2181
+ THEN { this.pushXprToken( $args ); }
2182
+ e3=expression { $args.push( $e3.expr ); }
2183
+ )+
2384
2184
  |
2385
- vp=valuePath[ 'ref', null ] { $expr = this.valuePathAst( $vp.qp ); }
2386
- { this.setLocalTokenIfBefore( 'OVER', 'OVER', /^\($/i ); }
2387
2185
  (
2388
- over=overClause { $expr.suffix = [ $over.over ] }
2389
- )?
2390
- |
2391
- ':'
2392
- ( vp=valuePath[ 'paramref', this.startLocation() ]
2393
- { $expr = $vp.qp;; $expr.scope = 'param'; }
2394
- | pp=Number
2395
- { $expr = { param: this.numberLiteral( $pp ), scope: 'param' };
2396
- this.csnParseOnly( 'syntax-unsupported-param', [ $pp ], { '#': 'positional', code: ':' + $pp.text } );
2397
- }
2398
- )
2186
+ WHEN { this.pushXprToken( $args ); }
2187
+ c=condition { $args.push( $c.cond ); }
2188
+ THEN { this.pushXprToken( $args ); }
2189
+ e3=expression { $args.push( $e3.expr ); }
2190
+ )+
2191
+ )
2192
+ (
2193
+ ELSE { this.pushXprToken( $args ); }
2194
+ e4=expression { $args.push( $e4.expr ); }
2195
+ )?
2196
+ END { this.pushXprToken( $args ); }
2197
+ |
2198
+ ne=NEW { this.pushXprToken( $args ); } // token rewrite for NEW
2199
+ // please note: there will be no compiler-supported code completion after NEW
2200
+ { $expr = { op: this.valueWithTokenLocation( 'new', $ne ), args: [] };
2201
+ this.error( 'syntax-unsupported-new', $ne, { keyword: $ne.text }, '$(KEYWORD) is not supported' ); }
2202
+ nqp=valuePath[ 'ref', null ]
2203
+ |
2204
+ vp=valuePath[ 'ref', null ] { $args.push( this.valuePathAst( $vp.qp ) ); }
2205
+ { this.setLocalTokenIfBefore( 'OVER', 'OVER', /^\($/i ); }
2206
+ (
2207
+ { $args[0].suffix = []; }
2208
+ OVER { this.pushXprToken( $args[0].suffix ); }
2209
+ open='(' over=overClause close=')'
2210
+ { $args[0].suffix.push( this.surroundByParens( $over.over, $open, $close ) ); }
2211
+ )?
2212
+ |
2213
+ ':'
2214
+ (
2215
+ vp=valuePath[ 'paramref', this.startLocation() ]
2216
+ {{ const par = $vp.qp;; par.scope = 'param';; $args.push( par ); }}
2399
2217
  |
2400
- qm= '?' // is automatically not mentioned as CC candidate
2401
- // if we have an HideAlternatives here, we would block it to use it in
2402
- // parallel to an expression (would produce adaptivePredict() otherwise)
2403
- { $expr = { param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' };
2404
- this.csnParseOnly( 'syntax-unsupported-param', [ $qm ], { '#': 'dynamic', code: '?' } );
2218
+ pp=Number
2219
+ { $args.push( { param: this.numberLiteral( $pp ), scope: 'param' } );
2220
+ this.csnParseOnly( 'syntax-unsupported-param', [ $pp ], { '#': 'positional', code: ':' + $pp.text } );
2405
2221
  }
2222
+ )
2223
+ |
2224
+ qm= '?' // is automatically not mentioned as CC candidate
2225
+ // if we have an HideAlternatives here, we would block it to use it in
2226
+ // parallel to an expression (would produce adaptivePredict() otherwise)
2227
+ { $args.push( { param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' } );
2228
+ this.csnParseOnly( 'syntax-unsupported-param', [ $qm ], { '#': 'dynamic', code: '?' } );
2229
+ }
2230
+ |
2231
+ open='('
2232
+ // #ATN: The following alternative is not LL1, because both can start with
2233
+ // left-paren, but queryExpression has SELECT after initial left-parens
2234
+ (
2235
+ qe=queryExpression close=')'
2236
+ { $args.push( this.surroundByParens( $qe.query, $open, $close, true ) ); }
2406
2237
  |
2407
- open='('
2408
- // #ATN: The following alternative is not LL1, because both can start with
2409
- // left-paren, but queryExpression has SELECT after initial left-parens
2410
- (
2411
- qe=queryExpression close=')'
2412
- { $expr = this.surroundByParens( $qe.query, $open, $close, true ); }
2413
- |
2414
- c1=condition { $expr = [ $c1.cond ]; }
2415
- ( ',' { if ($expr.length > 1 && this.isStraightBefore(')')) break; } // allow ',' before ')'
2416
- cn=expression { if ($cn.expr) $expr.push($cn.expr); }
2417
- )*
2418
- close=')'
2419
- {
2420
- if ($expr.length > 1)
2421
- $expr = { op: this.valueWithTokenLocation( ',', $open ), args: $expr };
2422
- else if ($expr[0]) // can be `null` if condition failed to parse
2423
- $expr = this.surroundByParens( $expr[0], $open, $close );
2424
- }
2425
- )
2238
+ c1=condition { $args = [ $c1.cond ]; }
2239
+ ( ',' { if ($args.length > 1 && this.isStraightBefore(')')) break; } // allow ',' before ')'
2240
+ cn=expression { if ($cn.expr) $args.push($cn.expr); }
2241
+ )*
2242
+ close=')'
2243
+ {
2244
+ if ($args.length > 1)
2245
+ $args = [ { op: this.valueWithTokenLocation( 'list', $open ), args: $args,
2246
+ location: this.tokenLocation( $open, $close ) } ];
2247
+ else if ($args[0]) // can be `null` if condition failed to parse
2248
+ $args = [ this.surroundByParens( $args[0], $open, $close ) ];
2249
+ }
2426
2250
  )
2427
2251
  ;
2428
2252
 
2429
- specialFunction returns [ ret = { } ] locals[ art = {} ]
2253
+ specialFunction returns [ ret = {} ] locals[ art = {} ]
2430
2254
  :
2431
2255
  ca=CAST '(' // see createArray() in action
2432
2256
  {
@@ -2440,13 +2264,25 @@ specialFunction returns [ ret = { } ] locals[ art = {} ]
2440
2264
  {
2441
2265
  $ret.args.push( $e.expr );
2442
2266
  }
2443
- ')' { this.finalizeDictOrArray( $ret.args ); }
2267
+ ')' { this.finalizeDictOrArray( $ret.args ); }
2268
+ ;
2269
+
2270
+ // Paths and functions: ---------------------------------------------------------
2271
+
2272
+ simplePath[ art, category ] locals[ _sync = 'nop' ]
2273
+ @after { this.attachLocation($art); }
2274
+ // Due to error recovery, rule `ident` can return with value `null`. Set the
2275
+ // path as broken in this case.
2276
+ :
2277
+ head=ident[ $category ]
2278
+ { if (!$art.path) $art.path = []; this.pushIdent( $art.path, $head.id );
2279
+ if ($category === 'artref') $art.scope = 0;
2280
+ }
2281
+ (
2282
+ '.' tail=ident[ $category ] { this.pushIdent( $art.path, $tail.id ); }
2283
+ )*
2444
2284
  ;
2445
2285
 
2446
- // query path includes aggregation:
2447
- // ( COUNT | MIN | MAX | SUM | AVG | STDDEV | VAR )
2448
- // '(' ( '*' | expression | ALL expression | DISTINCT expression_list ) ')'
2449
-
2450
2286
  valuePath[ category, location = null ] returns[ qp = { path: [] } ] locals[ _sync = 'nop' ]
2451
2287
  @init { $qp.location = location || this.startLocation(); }
2452
2288
  @after{ this.attachLocation($qp); }
@@ -2465,19 +2301,6 @@ valuePath[ category, location = null ] returns[ qp = { path: [] } ] locals[ _syn
2465
2301
  )*
2466
2302
  ;
2467
2303
 
2468
- fromArguments[ pathStep ]
2469
- @init{ if (!$pathStep) $pathStep = {}; } // grammar robustness, see test/negative/parser/NamedExpression.cds
2470
- :
2471
- '(' { $pathStep.args = this.createDict(); $pathStep['$'+'syntax'] = ':'; } // necessary?
2472
- name=ident['paramname'] ':'
2473
- namedExpression[ $pathStep, $name.id ]
2474
- ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
2475
- name=ident['paramname'] ':'
2476
- namedExpression[ $pathStep, $name.id ]
2477
- )*
2478
- ')' { this.finalizeDictOrArray( $pathStep.args ); }
2479
- ;
2480
-
2481
2304
  pathArguments[ pathStep, considerSpecial ]
2482
2305
  @init{
2483
2306
  if (!$pathStep) $pathStep = {}; // grammar robustness, see test/negative/parser/NamedExpression.cds
@@ -2517,11 +2340,9 @@ pathArguments[ pathStep, considerSpecial ]
2517
2340
  funcExpression[ $pathStep, $considerSpecial ]
2518
2341
  )*
2519
2342
  // Note: We can't move this into funcExpression, or we would increase the ATN count because of `,` amiguity.
2520
- ( ob=functionOrderByClause[ $pathStep.args[$pathStep.args.length - 1] ]
2521
- {
2522
- // Remove the last entry which was copied to $ob.expr and push $ob.expr.
2523
- $pathStep.args[$pathStep.args.length - 1] = $ob.expr;
2524
- }
2343
+ ( ob=funcOrderByClause[ [ $pathStep.args[$pathStep.args.length - 1] ] ]
2344
+ // Remove the last entry which was copied to $ob.expr and push $ob.expr:
2345
+ { $pathStep.args[$pathStep.args.length - 1] = $ob.expr; }
2525
2346
  )?
2526
2347
  |
2527
2348
  { $pathStep.args = this.createArray(); }
@@ -2543,32 +2364,29 @@ namedExpression[ pathStep, id ]
2543
2364
  }
2544
2365
  ;
2545
2366
 
2546
- funcExpression[ pathStep, considerSpecial ] locals[ args ]
2367
+ funcExpression[ pathStep, considerSpecial ] locals[ args = [] ]
2547
2368
  @init { this.prepareGenericKeywords( $considerSpecial ); }
2369
+ @after{ $pathStep.args.push( this.argsExpression( $args, true ) ); }
2548
2370
  :
2549
2371
  (
2550
2372
  expr=expression
2551
- { $pathStep.args.push( $expr.expr ); }
2373
+ { $args.push( $expr.expr ); }
2552
2374
  |
2553
2375
  GenericExpr // keyword as replacement for expression, like '*'
2554
- { $pathStep.args.push( this.xprToken() ); }
2376
+ { this.pushXprToken( $args ); }
2555
2377
  |
2556
2378
  GenericIntro // keyword as introduction of expression, like DISTINCT
2557
- { $pathStep.args.push( this.xprToken() ); }
2558
- expr=expression
2559
- { $args = this.setLastAsXpr( $pathStep.args );
2560
- $args.push( $expr.expr ); }
2379
+ { this.pushXprToken( $args ); }
2380
+ expr=expression { $args.push( $expr.expr ); }
2561
2381
  |
2562
2382
  // Rule 'pathArguments' makes a decision based on the first two lookahead
2563
2383
  // tokens of this rule → we need to list tokens which would be changed to
2564
2384
  // GenericExpr or GenericIntro, and are not already covered by 'expression'
2565
2385
  { this.reportErrorForGenericKeyword(); }
2566
- ( HideAlternatives | '*' | ALL | DISTINCT )
2386
+ ( HideAlternatives | '*' | ALL | DISTINCT ) { this.pushXprToken( $args ); }
2567
2387
  // now continue parsing like GenericExpr:
2568
- { $pathStep.args.push( this.xprToken() ); }
2569
2388
  )
2570
2389
  (
2571
- { if (!$args) $args = this.setLastAsXpr( $pathStep.args ); }
2572
2390
  (
2573
2391
  { this.prepareGenericKeywords( $considerSpecial, 'separator' ); }
2574
2392
  (
@@ -2582,26 +2400,108 @@ funcExpression[ pathStep, considerSpecial ] locals[ args ]
2582
2400
  { this.reportErrorForGenericKeyword(); }
2583
2401
  ( HideAlternatives | Identifier | FROM | IN | WITH | GROUP )
2584
2402
  )
2585
- { $args.push( this.xprToken() );
2586
- this.prepareGenericKeywords( $considerSpecial, 'expr' );
2587
- }
2403
+ { this.pushXprToken( $args );
2404
+ this.prepareGenericKeywords( $considerSpecial, 'expr' ); }
2588
2405
  (
2589
- expr=expression
2590
- { $args.push( $expr.expr ); }
2406
+ expr=expression { $args.push( $expr.expr ); }
2591
2407
  |
2592
- GenericExpr
2593
- { $args.push( this.xprToken() ); }
2408
+ GenericExpr { this.pushXprToken( $args ); }
2594
2409
  |
2595
2410
  { this.reportErrorForGenericKeyword(); }
2596
2411
  // Again, we need to list tokens which could make it to GenericExpr
2597
2412
  // and which do not start an expression
2598
- ( HideAlternatives | ALL )
2599
- { $args.push( this.xprToken() ); }
2413
+ ( HideAlternatives | ALL ) { this.pushXprToken( $args ); }
2600
2414
  )
2601
2415
  )+
2602
2416
  )?
2603
2417
  ;
2604
2418
 
2419
+ overClause returns[ over ] locals[ args = [] ]
2420
+ @after{ $over = this.argsExpression( $args ); }
2421
+ :
2422
+ // TODO: empty really allowed?
2423
+ ( PARTITION { this.pushXprToken( $args ); } BY { this.pushXprToken( $args ); }
2424
+ pb=partitionByClause { $args.push( $pb.expr ); }
2425
+ )?
2426
+ ( ORDER { this.pushXprToken( $args ); } BY { this.pushXprToken( $args ); }
2427
+ ob=exprOrderByClause { $args.push( $ob.expr ); }
2428
+ )?
2429
+ ( ROWS { this.pushXprToken( $args ); }
2430
+ wf=windowFrameClause { $args.push( $wf.wf ); }
2431
+ )?
2432
+ ;
2433
+
2434
+ partitionByClause returns [ expr ] locals[ args = [] ]
2435
+ @after{ $expr = this.argsExpression( $args ); }
2436
+ :
2437
+ e1=expression { $args.push( $e1.expr ); }
2438
+ ( ',' { this.pushXprToken( $args ); }
2439
+ en=expression { $args.push( $en.expr ); }
2440
+ )*
2441
+ ;
2442
+
2443
+ // ORDER BY clause in generic functions, e.g. `first_value(id order by name)`
2444
+ funcOrderByClause[ args ] returns[ expr ]
2445
+ @after{ $expr = this.argsExpression( args, true ); }
2446
+ :
2447
+ ORDER { this.pushXprToken( $args ); } BY { this.pushXprToken( $args ); }
2448
+ ob=exprOrderByClause { $args.push( $ob.expr ); }
2449
+ ;
2450
+
2451
+ // ORDER BY clause in generic functions or OVER clause
2452
+ exprOrderByClause returns[ expr ] locals[ args = [] ]
2453
+ @after{ $expr = this.argsExpression( $args ); }
2454
+ :
2455
+ orderBySpecInExpr[ $args ]
2456
+ ( ',' { this.pushXprToken( $args ); } orderBySpecInExpr[ $args ] )*
2457
+ ;
2458
+
2459
+ orderBySpecInExpr[ args ]
2460
+ :
2461
+ e=expression { $args.push( $e.expr ); }
2462
+ ( ASC { this.pushXprToken( $args ); }
2463
+ | DESC { this.pushXprToken( $args ); }
2464
+ )?
2465
+ ( NULLS { this.pushXprToken( $args ); }
2466
+ ( FIRST | LAST ) { this.pushXprToken( $args ); }
2467
+ )?
2468
+ ;
2469
+
2470
+ windowFrameClause returns[ wf ] locals[ args = [] ]
2471
+ @after{ $wf = this.argsExpression( $args ); }
2472
+ :
2473
+ (
2474
+ windowFrameStartSpec[ $args ]
2475
+ |
2476
+ BETWEEN { this.pushXprToken( $args ); }
2477
+ windowFrameBoundSpec[ $args ]
2478
+ AND { this.pushXprToken( $args ); }
2479
+ windowFrameBoundSpec[ $args ]
2480
+ )
2481
+ ;
2482
+
2483
+ windowFrameBoundSpec[ args ]
2484
+ :
2485
+ UNBOUNDED { this.pushXprToken( $args ); }
2486
+ ( FOLLOWING | PRECEDING ) { this.pushXprToken( $args ); }
2487
+ |
2488
+ n=Number { $args.push( this.numberLiteral( $n ) ); }
2489
+ ( FOLLOWING | PRECEDING ) { this.pushXprToken( $args ); }
2490
+ |
2491
+ CURRENT { this.pushXprToken( $args ); } ROW { this.pushXprToken( $args ); }
2492
+ ;
2493
+
2494
+ windowFrameStartSpec[ args = [] ]
2495
+ :
2496
+ UNBOUNDED { this.pushXprToken( $args ); }
2497
+ PRECEDING { this.pushXprToken( $args ); }
2498
+ |
2499
+ n=Number { $args.push( this.numberLiteral( $n ) ); }
2500
+ PRECEDING { this.pushXprToken( $args ); }
2501
+ |
2502
+ CURRENT { this.pushXprToken( $args ); } ROW { this.pushXprToken( $args ); }
2503
+ ;
2504
+
2605
2505
  cardinalityAndFilter[ pathStep ] locals [ _sync = 'nop' ]
2606
2506
  :
2607
2507
  '['
@@ -2633,13 +2533,171 @@ optionalWhereForFilter
2633
2533
  // one of the expected ones.
2634
2534
  {
2635
2535
  var text = this.getCurrentToken().text.toUpperCase();
2636
- if (!['WHERE','GROUP','ORDER','LIMIT'].includes( text )) return;
2536
+ if (!['WHERE','GROUP','ORDER','LIMIT'].includes( text )) return $ctx;
2637
2537
  // TODO: should we somehow add those keywords to $(EXPECTED)?
2638
2538
  }
2639
2539
  WHERE
2640
2540
  ;
2641
2541
 
2642
- // Simple paths and values ---------------------------------------------------
2542
+ // Annotation assignments -------------------------------------------------------
2543
+
2544
+ // We have three versions of the annotation assignment rules:
2545
+ // - "fix": typically after a name if a ':' could follow
2546
+ // - "ll1": typically before keyword+name, after a name if no ':' could follow
2547
+ // - "atn": at the beginning of a column definition
2548
+ //
2549
+ // want to let the ambiguity in select items (solution: "either" possibility)
2550
+ //
2551
+ // entity E @Anno: Base { … }; // Base is include (chosen w/ warning) or @Anno value?
2552
+ // entity V(p) as select from E { // either: anno value "ref p", select item -x
2553
+ // @anno :p - x as x; // or: anno value true, select item :p-x
2554
+ // }
2555
+
2556
+ annotationAssignment_fix[ art ] locals[ assignment ]
2557
+ // value outside @(...)
2558
+ @after {
2559
+ if ($assignment) {
2560
+ this.assignAnnotation( $art, $assignment );
2561
+ this.docComment( $art );
2562
+ }
2563
+ } :
2564
+ '@'
2565
+ (
2566
+ annotationAssignment_paren[ $art ]
2567
+ |
2568
+ { $assignment = { name: {} }; }
2569
+ annotationPath[ $assignment.name, 'anno' ]
2570
+ annotationPathVariant[ $assignment.name ]?
2571
+ { this.warnIfColonFollows( $assignment ); }
2572
+ )
2573
+ ;
2574
+
2575
+ annotationAssignment_ll1[ art ] locals[ assignment ]
2576
+ @after {
2577
+ if ($assignment) {
2578
+ this.assignAnnotation( $art, $assignment );
2579
+ this.docComment( $art );
2580
+ }
2581
+ } :
2582
+ '@'
2583
+ (
2584
+ annotationAssignment_paren[ $art ]
2585
+ |
2586
+ { $assignment = { name: {} }; }
2587
+ annotationPath[ $assignment.name, 'anno' ]
2588
+ annotationPathVariant[ $assignment.name ]?
2589
+ (
2590
+ ':' { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
2591
+ val=annoValue[ $assignment ]
2592
+ )?
2593
+ )
2594
+ ;
2595
+
2596
+ // Has previously used ATN, now via local token rewrite
2597
+ annotationAssignment_atn[ art ] locals[ assignment ]
2598
+ @after {
2599
+ if ($assignment) {
2600
+ this.assignAnnotation( $art, $assignment );
2601
+ this.docComment( $art );
2602
+ }
2603
+ } :
2604
+ '@'
2605
+ (
2606
+ annotationAssignment_paren[ $art ]
2607
+ |
2608
+ { $assignment = { name: {} }; }
2609
+ annotationPath[ $assignment.name, 'anno' ]
2610
+ // '#' is in the follow set of this rule, as it is used in rule "selectItemDef"
2611
+ // before an "expression" which can start with a '#' for an enum value
2612
+ // -> used to introduce variant name if and only if in same line as previous token
2613
+ { this.setLocalToken( '#', 'HelperToken1', null, true ); }
2614
+ (
2615
+ HelperToken1 { this.meltKeywordToIdentifier(); }
2616
+ variant=ident['variant'] { $assignment.name.variant = $variant.id; }
2617
+ )?
2618
+ // ':' is in the follow set of this rule, as it is used in rule "selectItemDef"
2619
+ // before an "expression" which can start with a ':' for a parameter reference
2620
+ // -> used to introduce assignment value if and only if in same line as previous token
2621
+ { this.setLocalToken( ':', 'HelperToken2', null, true ); }
2622
+ ( HelperToken2 // ':'
2623
+ { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
2624
+ (
2625
+ val=annoValueBase[ $assignment ]
2626
+ |
2627
+ at='@'? annotationPath[ $assignment, 'ref', $at ]
2628
+ { this.setLocalToken( '#', 'HelperToken1', null, true ); } // see above
2629
+ (
2630
+ HelperToken1 { this.meltKeywordToIdentifier(); }
2631
+ variant=ident['variant'] { $assignment.variant = $variant.id; }
2632
+ )?
2633
+ )
2634
+ )?
2635
+ )
2636
+ ;
2637
+
2638
+ annotationAssignment_paren[ art ]
2639
+ :
2640
+ '('
2641
+ // allow completely useless `@()` with a warning, do not offer it for completion
2642
+ {
2643
+ this.meltKeywordToIdentifier();
2644
+ if (this.isStraightBefore(')')) {
2645
+ // TODO: or should we simple accept this without warning? (but still no CC)
2646
+ this.warning( 'syntax-unexpected-right-paren',
2647
+ this.tokenLocation( this.getCurrentToken() ),
2648
+ { offending: "')'", expecting: ['Identifier'], code: '@()' },
2649
+ 'Unexpected $(OFFENDING), expecting $(EXPECTING); ignoring $(CODE)' );
2650
+ this.matchWildcard(); // we know it is the ')' - we do not reach the final match
2651
+ return $ctx;
2652
+ }
2653
+ }
2654
+ annotationAssignment_1[ $art ]
2655
+ ( ','
2656
+ {
2657
+ this.meltKeywordToIdentifier();
2658
+ if (this.isStraightBefore(')')) break; // allow ',' before ')'
2659
+ }
2660
+ annotationAssignment_1[ $art ]
2661
+ )*
2662
+ ')'
2663
+ ;
2664
+
2665
+ annotationAssignment_1[ art ] locals[ assignment = { name: {} } ]
2666
+ @after { this.assignAnnotation( $art, $assignment ); }
2667
+ :
2668
+ annotationPath[ $assignment.name, 'anno' ]
2669
+ annotationPathVariant[ $assignment.name ]?
2670
+ (
2671
+ ':' { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
2672
+ val=annoValue[ $assignment ]
2673
+ )?
2674
+ ;
2675
+
2676
+ annotationPath[ art, category, headat = null ] locals[ _sync = 'nop' ]
2677
+ @after { this.attachLocation($art); }
2678
+ // Due to error recovery, rule `ident` can return with value `null`. Set the
2679
+ // path as broken in this case.
2680
+ :
2681
+ head=ident[ $category ]
2682
+ { $art.path = []; this.pushIdent( $art.path, $head.id, $headat );
2683
+ if ($category === 'artref') $art.scope = 0;
2684
+ }
2685
+ (
2686
+ '.' at='@'? tail=ident[ $category ]
2687
+ { this.pushIdent( $art.path, $tail.id, $at );
2688
+ // Otherwise, $at may continue to be set after one `.@anno` segment.
2689
+ $ctx.at = null;
2690
+ }
2691
+ )*
2692
+ ;
2693
+
2694
+ annotationPathVariant[ art ] locals[ variant = {} ]
2695
+ @after { this.attachLocation($art); }
2696
+ :
2697
+ // TODO: warning for space after '#'
2698
+ '#' { this.meltKeywordToIdentifier(); }
2699
+ simplePath[ $variant, 'variant' ] { $art.variant = $variant; }
2700
+ ;
2643
2701
 
2644
2702
  annoValue[ assignment ]
2645
2703
  :
@@ -2768,6 +2826,8 @@ annoSubValue returns[ val = {} ]
2768
2826
  ( annotationPathVariant[ $val ] )?
2769
2827
  ;
2770
2828
 
2829
+ // Literal values and identifiers -----------------------------------------------
2830
+
2771
2831
  literalValue returns[ val ] locals[ tok ]
2772
2832
  @init{ $tok = this.getCurrentToken(); }
2773
2833
  @after { this.attachLocation($val); }
@@ -2791,47 +2851,7 @@ literalValue returns[ val ] locals[ tok ]
2791
2851
  { $val = this.quotedLiteral( $tok ); }
2792
2852
  ;
2793
2853
 
2794
- simplePath[ art, category ] locals[ _sync = 'nop' ]
2795
- @after { this.attachLocation($art); }
2796
- // Due to error recovery, rule `ident` can return with value `null`. Set the
2797
- // path as broken in this case.
2798
- :
2799
- head=ident[ $category ]
2800
- { if (!$art.path) $art.path = []; this.pushIdent( $art.path, $head.id );
2801
- if ($category === 'artref') $art.scope = 0;
2802
- }
2803
- (
2804
- '.' tail=ident[ $category ] { this.pushIdent( $art.path, $tail.id ); }
2805
- )*
2806
- ;
2807
-
2808
- annotationPath[ art, category, headat = null ] locals[ _sync = 'nop' ]
2809
- @after { this.attachLocation($art); }
2810
- // Due to error recovery, rule `ident` can return with value `null`. Set the
2811
- // path as broken in this case.
2812
- :
2813
- head=ident[ $category ]
2814
- { $art.path = []; this.pushIdent( $art.path, $head.id, $headat );
2815
- if ($category === 'artref') $art.scope = 0;
2816
- }
2817
- (
2818
- '.' at='@'? tail=ident[ $category ]
2819
- { this.pushIdent( $art.path, $tail.id, $at );
2820
- // Otherwise, $at may continue to be set after one `.@anno` segment.
2821
- $ctx.at = null;
2822
- }
2823
- )*
2824
- ;
2825
-
2826
- annotationPathVariant[ art ] locals[ variant = {} ]
2827
- @after { this.attachLocation($art); }
2828
- :
2829
- // TODO: warning for space after '#'
2830
- '#' { this.meltKeywordToIdentifier(); }
2831
- simplePath[ $variant, 'variant' ] { $art.variant = $variant; }
2832
- ;
2833
-
2834
- // Identifier and non-reserved keywords --------------------------------------
2854
+ // #IDENT - keep this comment here, used in scripts/linter/lintGrammar.js
2835
2855
 
2836
2856
  identNoKeyword[ category ] returns[ id ] // for aliases without AS
2837
2857
  @after{ $id = this.identAst( $stop, $category ); }
@@ -2866,7 +2886,6 @@ ident[ category ] returns[ id ]
2866
2886
  | DEFINE
2867
2887
  | DEFINITIONS
2868
2888
  | DESC
2869
- | ELEMENT
2870
2889
  | ELEMENTS
2871
2890
  | ELSE
2872
2891
  | END
@@ -2896,7 +2915,6 @@ ident[ category ] returns[ id ]
2896
2915
  | LIMIT
2897
2916
  | LOCALIZED
2898
2917
  | MANY
2899
- | MASKED
2900
2918
  | MINUS
2901
2919
  | MIXIN
2902
2920
  | NAMESPACE
@@ -2927,7 +2945,7 @@ ident[ category ] returns[ id ]
2927
2945
  | VIEW
2928
2946
  ;
2929
2947
 
2930
- //----------------------------------------------------------------------------
2948
+ // LEXER ------------------------------------------------------------------------
2931
2949
 
2932
2950
  WhiteSpace // like \s in JavaScript RegExp
2933
2951
  : // LineTerminator | [\t\f\v\u00A0\uFEFF] | Zs
@@ -2940,10 +2958,10 @@ Comment : '/*' .*? '*/' -> channel(HIDDEN);
2940
2958
 
2941
2959
  LineComment : '//' ~[\r\n\u2028\u2029]* -> channel(HIDDEN);
2942
2960
 
2943
- // Values --------------------------------------------------------------------
2961
+ // Literal values ---------------------------------------------------------------
2944
2962
 
2945
2963
  // for syntactic code-completion: Combine all three string styles
2946
- // Note: Use rule `string` instead as that also parses escape sequences!
2964
+ // Note: Use rule `string` instead as that also parses escape sequences! (TODO: ???)
2947
2965
  String : SingleLineString
2948
2966
  | MultiLineString
2949
2967
  | MutlLineStringBlock;
@@ -3063,7 +3081,7 @@ DEFAULT : [dD][eE][fF][aA][uU][lL][tT] ;
3063
3081
  DEFINE : [dD][eE][fF][iI][nN][eE] ;
3064
3082
  DEFINITIONS : [dD][eE][fF][iI][nN][iI][tT][iI][oO][nN][sS] ;
3065
3083
  DESC : [dD][eE][sS][cC] ;
3066
- ELEMENT : [eE][lL][eE][mM][eE][nN][tT] ;
3084
+ // ELEMENT : [eE][lL][eE][mM][eE][nN][tT] ;
3067
3085
  ELEMENTS : [eE][lL][eE][mM][eE][nN][tT][sS] ;
3068
3086
  ELSE : [eE][lL][sS][eE] ;
3069
3087
  END : [eE][nN][dD] ;
@@ -3093,7 +3111,7 @@ LIKE : [lL][iI][kK][eE] ;
3093
3111
  LIMIT : [lL][iI][mM][iI][tT] ;
3094
3112
  LOCALIZED: [lL][oO][cC][aA][lL][iI][zZ][eE][dD];
3095
3113
  MANY : [mM][aA][nN][yY] ;
3096
- MASKED : [mM][aA][sS][kK][eE][dD] ;
3114
+ // MASKED : [mM][aA][sS][kK][eE][dD] ;
3097
3115
  MINUS : [mM][iI][nN][uU][sS] ;
3098
3116
  MIXIN : [mM][iI][xX][iI][nN] ;
3099
3117
  NAMESPACE : [nN][aA][mM][eE][sS][pP][aA][cC][eE] ;