@sap/cds-compiler 5.9.4 → 6.0.12

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 (114) hide show
  1. package/CHANGELOG.md +117 -319
  2. package/README.md +1 -1
  3. package/bin/cds_update_identifiers.js +3 -5
  4. package/bin/cdsc.js +24 -9
  5. package/bin/cdshi.js +1 -1
  6. package/bin/cdsse.js +4 -4
  7. package/doc/CHANGELOG_BETA.md +11 -0
  8. package/doc/CHANGELOG_DEPRECATED.md +29 -0
  9. package/lib/api/main.js +8 -5
  10. package/lib/api/options.js +12 -10
  11. package/lib/base/builtins.js +1 -0
  12. package/lib/base/message-registry.js +191 -99
  13. package/lib/base/messages.js +35 -21
  14. package/lib/base/model.js +14 -24
  15. package/lib/checks/actionsFunctions.js +10 -20
  16. package/lib/checks/annotationsOData.js +1 -1
  17. package/lib/checks/elements.js +35 -10
  18. package/lib/checks/enums.js +31 -0
  19. package/lib/checks/foreignKeys.js +2 -2
  20. package/lib/checks/hasPersistedElements.js +5 -0
  21. package/lib/checks/invalidTarget.js +1 -1
  22. package/lib/checks/managedWithoutKeys.js +5 -4
  23. package/lib/checks/queryNoDbArtifacts.js +10 -8
  24. package/lib/checks/types.js +5 -5
  25. package/lib/checks/validator.js +6 -4
  26. package/lib/compiler/assert-consistency.js +13 -9
  27. package/lib/compiler/checks.js +20 -52
  28. package/lib/compiler/define.js +31 -6
  29. package/lib/compiler/extend.js +5 -1
  30. package/lib/compiler/generate.js +14 -17
  31. package/lib/compiler/populate.js +8 -31
  32. package/lib/compiler/propagator.js +21 -35
  33. package/lib/compiler/resolve.js +64 -29
  34. package/lib/compiler/shared.js +16 -4
  35. package/lib/compiler/tweak-assocs.js +1 -1
  36. package/lib/compiler/utils.js +1 -1
  37. package/lib/edm/annotations/edmJson.js +23 -20
  38. package/lib/edm/annotations/genericTranslation.js +12 -10
  39. package/lib/edm/csn2edm.js +50 -56
  40. package/lib/edm/edm.js +33 -28
  41. package/lib/edm/edmInboundChecks.js +2 -2
  42. package/lib/edm/edmPreprocessor.js +54 -88
  43. package/lib/edm/edmUtils.js +9 -12
  44. package/lib/gen/BaseParser.js +63 -52
  45. package/lib/gen/CdlGrammar.checksum +1 -1
  46. package/lib/gen/CdlParser.js +1153 -1165
  47. package/lib/gen/Dictionary.json +21 -1
  48. package/lib/json/from-csn.js +70 -43
  49. package/lib/json/to-csn.js +6 -8
  50. package/lib/language/multiLineStringParser.js +3 -2
  51. package/lib/main.d.ts +58 -24
  52. package/lib/model/cloneCsn.js +3 -0
  53. package/lib/model/csnUtils.js +28 -39
  54. package/lib/model/xprAsTree.js +23 -9
  55. package/lib/modelCompare/compare.js +5 -4
  56. package/lib/optionProcessor.js +24 -17
  57. package/lib/parsers/AstBuildingParser.js +81 -25
  58. package/lib/parsers/XprTree.js +57 -3
  59. package/lib/parsers/identifiers.js +1 -1
  60. package/lib/parsers/index.js +0 -3
  61. package/lib/render/manageConstraints.js +25 -25
  62. package/lib/render/toCdl.js +173 -170
  63. package/lib/render/toHdbcds.js +126 -128
  64. package/lib/render/toRename.js +7 -7
  65. package/lib/render/toSql.js +128 -125
  66. package/lib/render/utils/common.js +47 -22
  67. package/lib/render/utils/delta.js +25 -25
  68. package/lib/render/utils/operators.js +2 -2
  69. package/lib/render/utils/pretty.js +5 -5
  70. package/lib/render/utils/sql.js +13 -13
  71. package/lib/render/utils/standardDatabaseFunctions.js +115 -103
  72. package/lib/render/utils/unique.js +4 -4
  73. package/lib/transform/db/applyTransformations.js +1 -1
  74. package/lib/transform/db/assertUnique.js +2 -2
  75. package/lib/transform/db/associations.js +6 -7
  76. package/lib/transform/db/assocsToQueries/utils.js +4 -5
  77. package/lib/transform/db/backlinks.js +12 -9
  78. package/lib/transform/db/cdsPersistence.js +8 -7
  79. package/lib/transform/db/constraints.js +13 -10
  80. package/lib/transform/db/expansion.js +7 -3
  81. package/lib/transform/db/flattening.js +4 -14
  82. package/lib/transform/db/processSqlServices.js +2 -1
  83. package/lib/transform/db/temporal.js +5 -7
  84. package/lib/transform/db/views.js +2 -4
  85. package/lib/transform/draft/db.js +8 -8
  86. package/lib/transform/draft/odata.js +10 -7
  87. package/lib/transform/forOdata.js +10 -5
  88. package/lib/transform/forRelationalDB.js +5 -75
  89. package/lib/transform/localized.js +1 -1
  90. package/lib/transform/odata/createForeignKeys.js +11 -10
  91. package/lib/transform/odata/flattening.js +8 -4
  92. package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +96 -0
  93. package/lib/transform/odata/typesExposure.js +3 -3
  94. package/lib/transform/transformUtils.js +4 -8
  95. package/lib/transform/translateAssocsToJoins.js +14 -7
  96. package/lib/transform/universalCsn/universalCsnEnricher.js +10 -4
  97. package/lib/utils/objectUtils.js +0 -17
  98. package/package.json +10 -13
  99. package/share/messages/def-upcoming-virtual-change.md +1 -1
  100. package/LICENSE +0 -37
  101. package/bin/cds_remove_invalid_whitespace.js +0 -138
  102. package/doc/CHANGELOG_ARCHIVE.md +0 -3604
  103. package/lib/gen/genericAntlrParser.js +0 -3
  104. package/lib/gen/language.checksum +0 -1
  105. package/lib/gen/language.interp +0 -456
  106. package/lib/gen/language.tokens +0 -180
  107. package/lib/gen/languageLexer.interp +0 -439
  108. package/lib/gen/languageLexer.js +0 -1483
  109. package/lib/gen/languageLexer.tokens +0 -167
  110. package/lib/gen/languageParser.js +0 -24941
  111. package/lib/language/antlrParser.js +0 -205
  112. package/lib/language/errorStrategy.js +0 -646
  113. package/lib/language/genericAntlrParser.js +0 -1572
  114. package/lib/parsers/CdlGrammar.g4 +0 -2070
@@ -1,2070 +0,0 @@
1
- // Grammar for CDS
2
-
3
- parser grammar Cdl;
4
- options {
5
- language = JavaScript;
6
- superClass = AstBuildingParser;
7
- }
8
- @header{
9
- const { XsnSource, XsnArtifact, XsnName } = require( '../compiler/xsn-model' );
10
- const AstBuildingParser = require('../parsers/AstBuildingParser');
11
- }
12
- @footer{
13
- module.exports = { CdlParser }; // make it work with lazyload()
14
- }
15
-
16
- // Content:
17
- // - top-level: USING, NAMESPACE, artifactDefOrExtend (start rule: start)
18
- // - main definitions and annotation def
19
- // - member definitions
20
- // - EXTEND and ANNOTATE
21
- // - type expressions
22
- // - queries: the main query hierarchy (start rule: queryEOF)
23
- // - queries: columns and other clauses
24
- // - conditions and expressions (start rule: conditionEOF)
25
- // - paths and functions
26
- // - annotation assignments
27
-
28
- tokens{ // reserved words
29
- ALL, ANY, AS,
30
- BY,
31
- CASE, CAST,
32
- DISTINCT, // not entirely necessary
33
- EXISTS,
34
- FALSE, FROM,
35
- IN,
36
- KEY,
37
- NOT, NULL,
38
- OF, ON,
39
- SELECT, SOME,
40
- TRUE,
41
- WHEN, WHERE, WITH,
42
- }
43
-
44
- // Top-level: USING, NAMESPACE, artifactDefOrExtend (start rule: start) ---------
45
-
46
- start returns[ source = new XsnSource( 'cdl' ) ]
47
- :
48
- { this.afterBrace( null, 'init' ); }<always> // init sloppy semicolon handling
49
- (
50
- ( <guard=namespaceRestriction> namespaceDeclaration[ $source ]
51
- | usingDeclaration[ $source ]
52
- | artifactDefOrExtend[ $source ] <prepare=namespaceRestriction>
53
- )
54
- ( ';' | <exitLoop> | <repeatLoop, guard=afterBrace> { this.noAssignmentInSameLine(); } )
55
- )*
56
- EOF { this.docComment( null ); }
57
- ;
58
-
59
- artifactsBlock[ art, start = undefined ]
60
- :
61
- '{' <prepare=afterBrace, arg=init>
62
- { $art.artifacts = this.createDict( $start ); $art.extensions = []; }
63
- (
64
- artifactDefOrExtend[ $art ]
65
- ( ';' | <exitLoop> | <repeatLoop, guard=afterBrace> { this.noAssignmentInSameLine(); } )
66
- )*
67
- '}'<prepare=afterBrace>
68
- { this.finalizeDictOrArray( $art.artifacts ); }
69
- ;
70
-
71
- artifactDefOrExtend[ outer ] locals[ art = new XsnArtifact() ]
72
- :
73
- { $art.location = this.startLocation(); }
74
- { this.docComment( $art ); } annoAssignStd[ $art ]*
75
- (
76
- DEFINE?
77
- ( serviceDef[ $art, $outer ]
78
- | contextDef[ $art, $outer ]
79
- | <guard=vocabularyRestriction> annotationDef[ $art, $outer ]
80
- | typeDef[ $art, $outer ]
81
- | aspectDef[ $art, $outer ]
82
- | entityDef[ $art, $outer ]
83
- | <hide> viewDef[ $art, $outer ]
84
- | eventDef[ $art, $outer ]
85
- | actionMainDef[ $art, $outer ]
86
- | functionMainDef[ $art, $outer ]
87
- )
88
- |
89
- <guard=extensionRestriction> EXTEND { $art.kind = 'extend'; }
90
- ( extendArtifact[ $art, $outer ]
91
- | extendService[ $art, $outer ]
92
- // Non-streamlined Syntax; we would neither add new clauses to them, nor
93
- // add more of them (for further `kind`s):
94
- | <hide> extendContext[ $art, $outer ]
95
- | <hide> extendType[ $art, $outer ]
96
- | <hide> extendEntityOrAspect[ $art, $outer ]
97
- | <hide> extendProjection[ $art, $outer ]
98
- )
99
- |
100
- <guard=extensionRestriction> ANNOTATE annotateArtifact[ $art, $outer ]
101
- )
102
- ;
103
-
104
- namespaceDeclaration[ source ]
105
- @finally{ this.attachLocation( $source.namespace ); }
106
- :
107
- NAMESPACE name=namePath[ 'Namespace' ]
108
- { $source.namespace ??= { kind: 'namespace', name: $name }; }
109
- // TODO: XsnArtifact ?
110
- ;
111
-
112
- usingDeclaration[ source ] locals[ decl = { kind: 'using' } ] // TODO: XsnArtifact ?
113
- @finally{ this.attachLocation( $decl ); }
114
- :
115
- USING
116
- (
117
- FROM String
118
- { $source.dependencies.push( this.quotedLiteral() ); }
119
- |
120
- usingProxy[ $source, $decl ]
121
- ( FROM String
122
- { $source.dependencies.push( $decl.fileDep = this.quotedLiteral() ); }
123
- )?
124
- |
125
- { $source.usings.push( $decl ); }
126
- // We could just create "independent" USING declaration, but if we want
127
- // to have some check in the future whether the external artifacts are
128
- // really in the FROM source...
129
- '{' { $decl.usings = this.createArray(); }
130
- ( usingProxy[ $decl, { kind: 'using' } ]
131
- ( ',' | <exitLoop> )
132
- )*
133
- '}'<prepare=afterBrace>
134
- { this.finalizeDictOrArray( $decl.usings ); }
135
- ( FROM String
136
- { $source.dependencies.push( $decl.fileDep = this.quotedLiteral() ); }
137
- )?
138
- )
139
- ;
140
-
141
- usingProxy[ outer, proxy ]
142
- @finally{ this.attachLocation( $proxy ); }
143
- :
144
- extern=simplePath[ 'global' ]
145
- { $proxy.extern = $extern; $outer.usings.push( $proxy ); }
146
- ( AS Id['UsingAlias'] { $proxy.name = this.identAst(); } // TODO: XsnName ?
147
- | { this.classifyImplicitName( 'Using' ); }
148
- )
149
- ;
150
-
151
- namePath[ category ] returns[ default name = new XsnName() ]
152
- @finally{ this.attachLocation( $name ); }
153
- :
154
- Id[ $category ] { $name.path = [ this.identAst() ]; }
155
- (
156
- '.' Id_all[ $category ] { $name.path.push( this.identAst() ); }
157
- )*
158
- ;
159
-
160
- simplePath[ category = 'artref' ] returns[ default ref = {} ]
161
- @finally{ this.attachLocation( $ref ); }
162
- :
163
- Id[ $category ]
164
- { $ref.path = [ this.identAst() ]; }
165
- (
166
- '.' Id_all[ $category ] { $ref.path.push( this.identAst() ); }
167
- )*
168
- ;
169
-
170
- // Annotation def and main definitions ------------------------------------------
171
-
172
- serviceDef[ art, outer ]
173
- @finally{ this.attachLocation( $art ); }
174
- :
175
- SERVICE <prepare=vocabularyRestriction>
176
- name=namePath[ 'Service' ]
177
- { this.addDef( $art, $outer, 'artifacts', 'service', $name ); }
178
- { this.docComment( $art ); } annoAssignMid[ $art ]*
179
- artifactsBlock[ $art ]?
180
- ;
181
-
182
- contextDef[ art, outer ]
183
- @finally{ this.attachLocation( $art ); }
184
- :
185
- CONTEXT <prepare=vocabularyRestriction>
186
- name=namePath[ 'Context' ]
187
- { this.addDef( $art, $outer, 'artifacts', 'context', $name ); }
188
- { this.docComment( $art ); } annoAssignMid[ $art ]*
189
- artifactsBlock[ $art ]?
190
- ;
191
-
192
- annotationDef[ art, outer ]
193
- @finally{ this.attachLocation( $art ); }
194
- :
195
- ANNOTATION name=namePath[ 'AnnoDef' ]
196
- // make it also work with ignored <guard=vocabularyRestriction>:
197
- { this.addDef( $art, $outer, ($outer.kind === 'source' ? 'vocabularies' : 'artifacts'), 'annotation', $name ); }
198
- { this.docComment( $art ); } annoAssignMid[ $art ]*
199
- typeOrIncludesSpec[ $art ]
200
- ;
201
-
202
- typeDef[ art, outer ]
203
- @finally{ this.attachLocation( $art ); }
204
- :
205
- TYPE name=namePath[ 'Type' ]
206
- { this.addDef( $art, $outer, 'artifacts', 'type', $name ); }
207
- { this.docComment( $art ); } annoAssignMid[ $art ]*
208
- typeOrIncludesSpec[ $art ] // TODO: optional
209
- ;
210
-
211
- aspectDef[ art, outer ]
212
- @finally{ this.attachLocation( $art ); }
213
- :
214
- ( ASPECT
215
- | <hide> ABSTRACT { this.warning( 'syntax-deprecated-abstract', this.combineLocation( this.lb(), this.la() ) ); }
216
- ENTITY
217
- )
218
- name=namePath[ 'Type' ] // TODO: Type?
219
- { this.addDef( $art, $outer, 'artifacts', 'aspect', $name ); }
220
- { this.docComment( $art ); } annoAssignMid[ $art ]*
221
- (
222
- elementsBlock[ $art ]
223
- |
224
- <exitRule>
225
- |
226
- ':'
227
- (
228
- incl=simplePath { $art.includes ??= []; $art.includes.push( $incl ); }
229
- ( ',' | <exitLoop> | <exitRule> )
230
- )*
231
- elementsBlock[ $art ]
232
- )
233
- actionsBlock[ $art ]?
234
- ;
235
-
236
- entityDef[ art, outer ]
237
- @finally{ this.attachLocation( $art ); }
238
- :
239
- ENTITY
240
- name=namePath[ 'Entity' ]
241
- { this.addDef( $art, $outer, 'artifacts', 'entity', $name ); }
242
- { this.docComment( $art ); } annoAssignMid[ $art ]*
243
- paramsList[ $art ]?
244
- (
245
- ( ':' { $art.includes ??= []; }
246
- (
247
- incl=simplePath { $art.includes.push( $incl ); }
248
- ( ',' | <exitLoop> )
249
- )+
250
- )?
251
- elementsBlock[ $art ]
252
- |
253
- AS
254
- (
255
- query=queryExpression
256
- { $art.query = $query; $art.$syntax = 'entity'; }
257
- |
258
- <prepare=afterBrace, arg=sloppy> // enable special loop-exit, allow no `;`
259
- query=projectionSpec
260
- { $art.query = $query; $art.$syntax = 'projection'; }
261
- whereGroupByHaving[ $query ]?
262
- orderByLimitOffset[ $query ]?
263
- {;}<prepare=afterBrace, arg=normal> // disable special loop-exit, allow no `;`
264
- // TODO v6: these <prepare=afterBrace>s are extremely strange
265
- )
266
- )
267
- actionsBlock[ $art ]?
268
- ;
269
-
270
- viewDef[ art, outer ]
271
- @finally{ this.attachLocation( $art ); }
272
- :
273
- VIEW name=namePath[ 'Entity' ]
274
- { this.addDef( $art, $outer, 'artifacts', 'entity', $name ); }
275
- { this.docComment( $art ); } annoAssignMid[ $art ]*
276
- (
277
- paramsList[ $art ]
278
- |
279
- <hide> WITH PARAMETERS { $art.params = this.createDict(); }
280
- paramDef[ $art ]
281
- ( ',' paramDef[ $art ] )* // no optional final ',' here
282
- { this.finalizeDictOrArray( $art.params ); }
283
- )?
284
- AS query=queryExpression
285
- { $art.query = $query; $art.$syntax = 'view'; }
286
- ;
287
-
288
- eventDef[ art, outer ]
289
- @finally{ this.attachLocation( $art ); }
290
- :
291
- EVENT
292
- name=namePath[ 'Event' ]
293
- { this.addDef( $art, $outer, 'artifacts', 'event', $name ); }
294
- { this.docComment( $art ); } annoAssignMid[ $art ]*
295
- (
296
- elementsBlock[ $art ]
297
- |
298
- ':'
299
- (
300
- elementsBlock[ $art ]
301
- |
302
- incl=simplePath { $art.type = $incl; }
303
- (
304
- { $art.includes = [ $art.type ]; delete $art.type; }
305
- ( ','
306
- ( incl=simplePath { $art.includes.push( $incl ); }
307
- ( ',' | <exitLoop> )
308
- )*
309
- )?
310
- elementsBlock[ $art ]
311
- |
312
- { this.docComment( $art ); } annoAssignStd[ $art ]*
313
- )
314
- |
315
- query=projectionSpec { $art.query = $query; $art.$syntax = 'projection'; }
316
- )
317
- )
318
- ;
319
-
320
- actionMainDef[ art, outer ]
321
- @finally{ this.attachLocation( $art ); }
322
- :
323
- ACTION name=namePath[ 'Action' ]
324
- { this.addDef( $art, $outer, 'artifacts', 'action', $name ); }
325
- { this.docComment( $art ); } annoAssignMid[ $art ]*
326
- paramsList[ $art ]
327
- returnsSpec[ $art ]?
328
- ;
329
-
330
- functionMainDef[ art, outer ]
331
- @finally{ this.attachLocation( $art ); }
332
- :
333
- FUNCTION name=namePath[ 'Action' ]
334
- { this.addDef( $art, $outer, 'artifacts', 'function', $name ); }
335
- { this.docComment( $art ); } annoAssignMid[ $art ]*
336
- paramsList[ $art ]
337
- returnsSpec[ $art ]
338
- ;
339
-
340
- // Member definitions: actions, parameters, elements, enums: --------------------
341
-
342
- actionsBlock[ art ]
343
- :
344
- ACTIONS { $art.actions = this.createDict(); } '{'
345
- (
346
- boundActionFunctionDef[ $art ]
347
- ( ';' | <exitLoop> | <repeatLoop, guard=afterBrace> { this.noAssignmentInSameLine(); } )
348
- )*
349
- '}'<prepare=afterBrace>
350
- { this.finalizeDictOrArray( $art.actions ); }
351
- ;
352
-
353
- boundActionFunctionDef[ outer ] locals[ art = new XsnArtifact() ]
354
- @finally{ this.attachLocation( $art ); }
355
- :
356
- { this.docComment( $art ); } annoAssignStd[ $art ]*
357
- (
358
- ACTION Id['BoundAction']
359
- { this.addDef( $art, $outer, 'actions', 'action', this.identAst() ); }
360
- { this.docComment( $art ); } annoAssignMid[ $art ]*
361
- paramsList[ $art ]
362
- returnsSpec[ $art ]?
363
- |
364
- FUNCTION Id['BoundAction']
365
- { this.addDef( $art, $outer, 'actions', 'function', this.identAst() ); }
366
- { this.docComment( $art ); } annoAssignMid[ $art ]*
367
- paramsList[ $art ]
368
- returnsSpec[ $art ]
369
- )
370
- ;
371
-
372
- paramsList[ art ]
373
- :
374
- '(' { $art.params = this.createDict(); }
375
- (
376
- paramDef[ $art ]
377
- ( ',' | <exitLoop> )
378
- )*
379
- ')' { this.finalizeDictOrArray( $art.params ); }
380
- ;
381
-
382
- paramDef[ outer ] locals[ art = new XsnArtifact() ]
383
- @finally{ this.attachLocation( $art ); }
384
- :
385
- { this.docComment( $art ); } annoAssignStd[ $art ]*
386
- Id_all['Param']
387
- { this.addDef( $art, $outer, 'params', 'param', this.identAst() ); }
388
- { this.docComment( $art ); } annoAssignMid[ $art ]*
389
- (
390
- elementsBlock[ $art ]
391
- nullability[ $art ]?
392
- |
393
- ':'
394
- typeExpression[ $art ] // was elementType, with NOT? NULL / DEFAULT
395
- )
396
- ;
397
-
398
- returnsSpec[ outer ] locals[ art = new XsnArtifact() ]
399
- @finally{ this.attachLocation( $art ); if ($ret) art.location.tokenIndex = $ret.location.tokenIndex; }
400
- :
401
- ret=RETURNS <prepare=elementRestriction, arg=default>
402
- { $art.kind = 'param'; $outer.returns = $art; }
403
- { this.docComment( $art ); } annoAssignStd[ $art ]*
404
- typeExpression[ $art ]
405
- ;
406
-
407
- elementsBlock[ art ]
408
- :
409
- '{' { $art.elements = this.createDict(); }
410
- ( elementDef[ $art ]
411
- ( ';'
412
- | <exitLoop>
413
- | <repeatLoop, guard=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); }
414
- )
415
- )*
416
- '}'<prepare=afterBrace>
417
- { this.finalizeDictOrArray( $art.elements ); }
418
- ;
419
-
420
- elementDef[ outer, art = undefined ]
421
- @finally{ this.attachLocation( $art ); }
422
- :
423
- { $art ??= new XsnArtifact(); }
424
- { this.docComment( $art ); } annoAssignStd[ $art ]*
425
- ( VIRTUAL { $art.virtual = this.valueWithLocation( true ); } )?
426
- ( KEY { $art.key = this.valueWithLocation( true ); } )?
427
- ( <hide> MASKED { $art.masked = this.valueWithLocation( true ); }
428
- { this.message( 'syntax-unsupported-masked', this.lb(), { keyword: 'masked' } ); } )?
429
- ( <hide> ELEMENT { $art.$syntax = 'element'; } )?
430
- Id['Element'] <prepare=elementRestriction, arg=elem>
431
- { this.addDef( $art, $outer, 'elements', 'element', this.identAst() ); }
432
- { this.docComment( $art ); } annoAssignMid[ $art ]*
433
- (
434
- elementsBlock[ $art ]
435
- nullability[ $art ]?
436
- |
437
- ':' typeExpression[ $art ] // includes DEFAULT
438
- )?
439
- (
440
- <guard=elementRestriction, arg=calc> '='
441
- // TODO TOOL: add to "expected set" if failing here? Or have some "do not
442
- // consider for rule exit if condition failure on `=`"?
443
- expr=expression { $art.value = $expr; }
444
- ( STORED { $art.value.stored = this.valueWithLocation( true ); } )?
445
- // TODO: why have `stored` as property of the value?
446
- { if (!this.elementRestriction( true, 'anno' )) this.docComment( $art ); }
447
- ( <guard=elementRestriction, arg=anno> annoAssignStd[ $art ] )*
448
- )?
449
- ;
450
-
451
- enumSymbolsBlock[ art ]
452
- :
453
- ENUM { $art.enum = this.createDict(); } '{'
454
- ( enumSymbolDef[ $art ]
455
- ( ';' | <exitLoop> )
456
- )*
457
- '}'<prepare=afterBrace>
458
- { this.finalizeDictOrArray( $art.enum ); }
459
- ;
460
-
461
- enumSymbolDef[ outer ] locals[ art = new XsnArtifact() ]
462
- @finally{ this.attachLocation( $art ); }
463
- :
464
- { this.docComment( $art ); } annoAssignStd[ $art ]*
465
- Id['Enum']
466
- { this.addDef( $art, $outer, 'enum', 'enum', this.identAst() ); }
467
- { this.docComment( $art ); } annoAssignStd[ $art ]*
468
- ( '='
469
- (
470
- <prefer> String
471
- { $art.value = this.quotedLiteral(); }
472
- |
473
- <prefer> Number
474
- { $art.value = this.numberLiteral(); }
475
- |
476
- sign='+'/'-' Number
477
- { $art.value = this.numberLiteral( $sign ); }
478
- |
479
- <hide> value=literalValue
480
- { $art.value = $value; }
481
- )
482
- { this.docComment( $art ); } annoAssignStd[ $art ]*
483
- )?
484
- ;
485
-
486
- foreignKeysBlock[ art ]
487
- :
488
- '{' { $art.foreignKeys = this.createDict(); }
489
- ( foreignKeyDef[ $art ]
490
- ( ',' | <exitLoop> )
491
- )*
492
- '}' // DOES NOT SET afterBrace, because we allow annos after { …fks… }
493
- { this.finalizeDictOrArray( $art.foreignKeys ); }
494
- ;
495
-
496
- foreignKeyDef[ outer ] locals[ art = new XsnArtifact(), name ]
497
- @finally{ this.attachLocation($art); }
498
- :
499
- { this.docComment( $art ); } annoAssignStd[ $art ]*
500
- ref=simplePath[ 'ref' ] { $art.targetElement = $ref; }
501
- ( AS name=Id['Key'] { $name = this.identAst(); }
502
- | { this.classifyImplicitName( 'KeyImplicit', $ref ); $name = $ref.path; }
503
- )
504
- { this.addDef( $art, $outer, 'foreignKeys', 'key', $name ); }
505
- // TODO: for a more uniform syntax, we'd allow:
506
- // { this.docComment( $art ); } annoAssignMid[ $art ]*
507
- ;
508
-
509
- mixinElementDef[ outer ] locals[ art = new XsnArtifact() ]
510
- @finally{ this.attachLocation($art); }
511
- :
512
- Id['Mixin']
513
- { this.addDef( $art, $outer, 'mixin', 'mixin', this.identAst() ); }
514
- ':'
515
- ( assoc=ASSOCIATION cardinality[ $art ]? TO
516
- | assoc=COMPOSITION cardinality[ $art ]? OF
517
- )
518
- ( <guard=noRepeatedCardinality> card=ONE/MANY )?
519
- target=simplePath
520
- { this.setAssocAndComposition( $art, $assoc, $card, $target ); }
521
- ON expr=condition { $art.on = $expr; }
522
- ;
523
-
524
- // Annotate and Extend: main definitions ----------------------------------------
525
-
526
- annotateArtifact[ art, outer ]
527
- @finally{ this.attachLocation( $art ); }
528
- :
529
- name=namePath[ 'Ext' ]
530
- ( // direct element annotation:
531
- ':' elemName=namePath[ 'ExtElement']
532
- { this.addExtension( $art, $outer, 'annotate', $name, $elemName.path ); }
533
- keyword=WITH?
534
- { this.docComment( $art ); } annoAssignStd[ $art ]*
535
- annotateElementsBlock[ $art ]?
536
- | // definition annotation
537
- keyword=WITH?
538
- // <guard=noRuleExitAfterWith>), or as rule option,
539
- // this.noSemicolonHere() had the issues: DocComment, before `}`/EOF
540
- { this.addExtension( $art, $outer, 'annotate', $name ); }
541
- { this.docComment( $art ); } annoAssignStd[ $art ]*
542
- annotateParamsBlock[ $art ]?
543
- (
544
- annotateReturns[ $art ]
545
- |
546
- annotateElementsBlock[ $art ]?
547
- annotateActionsBlock[ $art ]?
548
- )
549
- )
550
- { this.checkWith( $keyword ); }
551
- ;
552
-
553
- extendArtifact[ art, outer ]
554
- @finally{ this.attachLocation( $art ); }
555
- :
556
- name=namePath[ 'Ext' ]
557
- ( // direct element annotation:
558
- ':' elemName=namePath[ 'ExtElement']
559
- { this.addExtension( $art, $outer, 'extend', $name, $elemName.path ); }
560
- keyword=WITH?
561
- { this.docComment( $art ); } annoAssignStd[ $art ]*
562
- (
563
- elements=ELEMENTS? extendElementsBlock[ $art, $elements ]
564
- |
565
- enumSymbolsBlock[ $art ] // ENUM …, just define, no extend
566
- |
567
- typeNamedArgsList[ $art ]
568
- )?
569
- |
570
- { this.addExtension( $art, $outer, 'extend', $name ); }
571
- { this.docComment( $art ); } annoAssignStd[ $art ]*
572
- ( extendElementsBlock[ $art ]
573
- actionsBlock[ $art ]?
574
- )?
575
- |
576
- keyword=WITH
577
- { this.addExtension( $art, $outer, 'extend', $name ); }
578
- { this.docComment( $art ); } annoAssignStd[ $art ]*
579
- (
580
- incl=simplePath { $art.includes = [ $incl ]; }
581
- ( ',' incl=simplePath { $art.includes.push( $incl ); } )*
582
- extendElementsBlock[ $art ]?
583
- actionsBlock[ $art ]?
584
- |
585
- elements=ELEMENTS? extendElementsBlock[ $art, $elements ]
586
- actionsBlock[ $art ]?
587
- |
588
- actionsBlock[ $art ]
589
- |
590
- enumSymbolsBlock[ $art ] // ENUM …, just define, no extend
591
- |
592
- typeNamedArgsList[ $art ]
593
- |
594
- COLUMNS selectItemsList[ $art, this.lb() ]
595
- |
596
- DEFINITIONS artifactsBlock[ $art, this.lb() ]
597
- )?
598
- )
599
- { this.checkWith( $keyword ); }
600
- ;
601
-
602
- extendService[ art, outer ]
603
- @finally{ this.checkWith( $keyword ); this.attachLocation( $art ); }
604
- :
605
- SERVICE { $art.expectedKind = this.valueWithLocation(); }
606
- name=namePath[ 'ExtService' ]
607
- { $art.name = $name; $outer.extensions.push( $art ); }
608
- keyword=WITH?
609
- // <guard=noRuleExitAfterWith>), or as rule option,
610
- // this.noSemicolonHere() had the issues: DocComment, before `}`/EOF
611
- { this.docComment( $art ); } annoAssignStd[ $art ]*
612
- artifactsBlock[ $art ]?
613
- ;
614
-
615
- extendContext[ art, outer ]
616
- @finally{ this.checkWith( $keyword ); this.attachLocation( $art ); }
617
- :
618
- CONTEXT { $art.expectedKind = this.valueWithLocation(); }
619
- name=namePath[ 'ExtContext' ]
620
- { $art.name = $name; $outer.extensions.push( $art ); }
621
- keyword=WITH?
622
- // <guard=noRuleExitAfterWith>), or as rule option,
623
- // this.noSemicolonHere() had the issues: DocComment, before `}`/EOF
624
- { this.docComment( $art ); } annoAssignStd[ $art ]*
625
- artifactsBlock[ $art ]?
626
- ;
627
-
628
- extendType[ art, outer ]
629
- @finally{ this.checkWith( $keyword ); this.attachLocation( $art ); }
630
- :
631
- TYPE { $art.expectedKind = this.valueWithLocation(); }
632
- name=namePath[ 'Ext' ]
633
- { $art.name = $name; $outer.extensions.push( $art ); }
634
- (
635
- { this.docComment( $art ); } annoAssignStd[ $art ]*
636
- extendElementsBlock[ $art ]?
637
- |
638
- keyword=WITH
639
- { this.docComment( $art ); } annoAssignStd[ $art ]*
640
- (
641
- incl=simplePath { $art.includes = [ $incl ]; }
642
- ( ',' incl=simplePath { $art.includes.push( $incl ); } )*
643
- extendElementsBlock[ $art ]?
644
- |
645
- elements=ELEMENTS? extendElementsBlock[ $art, $elements ]
646
- |
647
- enumSymbolsBlock[ $art ]
648
- |
649
- typeNamedArgsList[ $art ]
650
- )?
651
- )
652
- ;
653
-
654
- extendEntityOrAspect[ art, outer ]
655
- @finally{ this.checkWith( $keyword ); this.attachLocation( $art ); }
656
- :
657
- ASPECT/ENTITY { $art.expectedKind = this.valueWithLocation(); }
658
- name=namePath[ 'Ext' ]
659
- { $art.name = $name; $outer.extensions.push( $art ); }
660
- (
661
- { this.docComment( $art ); } annoAssignStd[ $art ]*
662
- |
663
- keyword=WITH
664
- { this.docComment( $art ); } annoAssignStd[ $art ]*
665
- (
666
- incl=simplePath { $art.includes = [ $incl ]; }
667
- ( ',' incl=simplePath { $art.includes.push( $incl ); } )*
668
- )?
669
- )
670
- extendElementsBlock[ $art ]?
671
- actionsBlock[ $art ]?
672
- ;
673
-
674
- extendProjection[ art, outer ]
675
- @finally{ this.checkWith( $keyword ); this.attachLocation( $art ); }
676
- :
677
- PROJECTION { $art.expectedKind = this.valueWithLocation(); }
678
- name=namePath[ 'Ext' ]
679
- { $art.name = $name; $outer.extensions.push( $art ); }
680
- keyword=WITH ?
681
- { this.docComment( $art ); } annoAssignStd[ $art ]*
682
- selectItemsList[ $art ]?
683
- actionsBlock[ $art ]?
684
- ;
685
-
686
- // Extend and annotate on members: bound actions, parameters, elements ----------
687
-
688
- annotateActionsBlock[ art ]
689
- :
690
- ACTIONS { $art.actions = this.createDict(); } '{'
691
- ( annotateBoundAction[ $art ]
692
- ( ';' | <exitLoop> | <repeatLoop, guard=afterBrace> { this.noAssignmentInSameLine(); } )
693
- )*
694
- '}'<prepare=afterBrace>
695
- { this.finalizeExtensionsDict( $art.actions ); }
696
- ;
697
-
698
- annotateBoundAction[ outer ] locals[ art = new XsnArtifact() ]
699
- @finally{ this.attachLocation( $art ); }
700
- :
701
- { this.docComment( $art ); } annoAssignStd[ $art ]*
702
- Id['ExtBoundAction']
703
- { this.addDef( $art, $outer, 'actions', 'annotate', this.identAst() ); }
704
- { this.docComment( $art ); } annoAssignStd[ $art ]*
705
- annotateParamsBlock[ $art ]?
706
- annotateReturns[ $art ]?
707
- ;
708
-
709
- annotateParamsBlock[ art ]
710
- :
711
- '(' { $art.params = this.createDict(); }
712
- ( annotateParam[ $art ]
713
- ( ',' | <exitLoop> )
714
- )*
715
- ')'
716
- { this.finalizeExtensionsDict( $art.params ); }
717
- ;
718
-
719
- annotateParam[ outer ] locals[ art = new XsnArtifact() ]
720
- @finally{ this.attachLocation( $art ); }
721
- :
722
- { this.docComment( $art ); } annoAssignStd[ $art ]*
723
- Id['ExtParam']
724
- { this.addDef( $art, $outer, 'params', 'annotate', this.identAst() ); }
725
- { this.docComment( $art ); } annoAssignStd[ $art ]*
726
- // annotateElementsBlock[ $art ]? // TODO: why not
727
- ;
728
-
729
- annotateReturns[ outer ] locals[ art = new XsnArtifact() ]
730
- @finally{ this.attachLocation( $art ); if ($ret) art.location.tokenIndex = $ret.location.tokenIndex; }
731
- :
732
- ret=RETURNS { $outer.returns = $art; $art.kind = 'annotate'; }
733
- { this.docComment( $art ); } annoAssignStd[ $art ]*
734
- annotateElementsBlock[ $art ]?
735
- ;
736
-
737
- annotateElementsBlock[ art ]
738
- :
739
- '{' { $art.elements = this.createDict(); }
740
- ( annotateElement[ $art ]
741
- ( ';'
742
- | <exitLoop>
743
- | <repeatLoop, guard=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); }
744
- )
745
- )*
746
- '}'<prepare=afterBrace>
747
- { this.finalizeExtensionsDict( $art.elements ); }
748
- ;
749
-
750
- annotateElement[ outer ] locals[ art = new XsnArtifact() ]
751
- @finally{ this.attachLocation( $art ); }
752
- :
753
- { this.docComment( $art ); } annoAssignStd[ $art ]*
754
- Id['ExtElement']
755
- { this.addDef( $art, $outer, 'elements', 'annotate', this.identAst() ); }
756
- { this.docComment( $art ); } annoAssignStd[ $art ]*
757
- annotateElementsBlock[ $art ]?
758
- ;
759
-
760
- extendElementsBlock[ art, start = undefined ]
761
- :
762
- '{' { $art.elements = this.createDict( $start ); }
763
- ( elementDefOrExtend[ $art ]
764
- ( ';'
765
- | <exitLoop>
766
- | <repeatLoop, guard=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); } )
767
- )*
768
- '}'<prepare=afterBrace>
769
- { this.finalizeExtensionsDict( $art.elements ); }
770
- ;
771
-
772
- elementDefOrExtend[ outer ] locals[ art = new XsnArtifact() ]
773
- @finally{ this.checkWith( $keyword ); this.attachLocation( $art ); }
774
- :
775
- { $art.location = this.startLocation(); }
776
- { this.docComment( $art ); } annoAssignStd[ $art ]*
777
- (
778
- elementDef[ $outer, $art ]
779
- |
780
- EXTEND
781
- ( ELEMENT { $art.expectedKind = this.valueWithLocation(); } )?
782
- Id['ExtElement']
783
- { this.addDef( $art, $outer, 'elements', 'extend', this.identAst() ); }
784
- (
785
- { this.docComment( $art ); } annoAssignStd[ $art ]*
786
- extendElementsBlock[ $art ]?
787
- |
788
- keyword=WITH
789
- { this.docComment( $art ); } annoAssignStd[ $art ]*
790
- (
791
- elements=ELEMENTS? extendElementsBlock[ art, $elements ]
792
- |
793
- enumSymbolsBlock[ $art ] // ENUM …, just define, no extend
794
- |
795
- typeNamedArgsList[ $art ]
796
- )?
797
- )
798
- )
799
- ;
800
-
801
- // Type expressions -------------------------------------------------------------
802
-
803
- // For `type` and `annotation` definitions:
804
- typeOrIncludesSpec[ art ]
805
- :
806
- elementsBlock[ $art ]
807
- nullability[ $art ]?
808
- |
809
- ':'
810
- (
811
- // Since cds-compiler v5.8; new parser only
812
- query=projectionSpec { $art.query = $query; $art.$syntax = 'projection'; }
813
- |
814
- typeExpression[ $art ]
815
- |
816
- <prefer>
817
- ref=simplePath { $art.type = $ref; }
818
- (
819
- // <default> does not work here
820
- typeRefOptArgs[ $art ]<atAltStart>
821
- ( typeExpression[ $art ]<atAltStart>
822
- | { this.docComment( $art ); }
823
- )
824
- |
825
- typeExpression[ $art ]<atAltStart>
826
- |
827
- { this.docComment( $art ); }
828
- |
829
- { $art.includes = [ $art.type ]; delete $art.type; }
830
- (
831
- ','
832
- (
833
- ref=simplePath { $art.includes.push( $ref ); }
834
- ( ',' | <exitLoop> )
835
- )*
836
- )?
837
- elementsBlock[ $art ]
838
- nullability[ $art ]?
839
- )
840
- )
841
- ;
842
-
843
- // Type expression (after the `:`), including `null`/`not null` and `default`;
844
- // the latter is forbidden in the `returns` type.
845
- //
846
- // This rule also parses annotation assignments and doc comments after the
847
- // type/target reference and after each type property; exceptions are:
848
- // - not after element and `enum` blocks (would interfere with optional `;`)
849
- // - no further type property after `many …` @assignment`, because the
850
- // annotations are attached to the element, the type properties to the line type
851
- //
852
- // If used in a definition with additional clauses (currently just `= expr` for
853
- // elements), these clauses must be guarded with <guard=…>.
854
- //
855
- // This rule is for element, type, (input and `returns`) parameter and annotation
856
- // definitions. It is not used when the type expression is restricted: CDL-style
857
- // cast in `select` items, `cast` function, `mixin` definition.
858
-
859
- typeExpression[ art ]
860
- :
861
- ( typeRefOptArgs[ $art ] | typeTypeOf[ $art ] )
862
- (<altRuleStart>)
863
- { this.docComment( $art ); } annoAssignStd[ $art ]*
864
- ( <guard=elementRestriction, arg=notNull> nullability[ $art ]
865
- { this.docComment( $art ); } annoAssignStd[ $art ]*
866
- )?
867
- ( enumSymbolsBlock[ $art ] <prepare=elementRestriction, arg=anno>
868
- ( <guard=elementRestriction, arg=notNull> nullability[ $art ] )?
869
- ( <guard=elementRestriction, arg=default>
870
- DEFAULT expr=expression { $art.default = $expr; }
871
- )?
872
- ( <guard=elementRestriction, arg=notNull> nullability[ $art ] )?
873
- | typeProperties[ $art ]
874
- )?
875
- |
876
- LOCALIZED { $art.localized = this.valueWithLocation( true ); }
877
- typeRefOptArgs[ $art ] // no TYPE OF
878
- { this.docComment( $art ); }
879
- typeProperties[ $art ]?
880
- |
881
- assoc=ASSOCIATION <prepare=elementRestriction, arg=calc>
882
- cardinality[ $art ]? TO
883
- ( <guard=noRepeatedCardinality> card=ONE/MANY )?
884
- typeAssocProperties[ $art, $assoc, $card ]
885
- |
886
- assoc=COMPOSITION <prepare=elementRestriction, arg=calc>
887
- cardinality[ $art ]? OF
888
- ( <guard=noRepeatedCardinality> card=ONE/MANY )?
889
- ( typeAssocProperties[ $art, $assoc, $card ]
890
- | elementsBlock[ this.setAssocAndComposition( $art, $assoc, $card ) ]
891
- { $art.target.location = $art.target.elements[Symbol.for('cds.$location')]; }
892
- )
893
- |
894
- ( ARRAY <prepare=elementRestriction, arg=calc>
895
- OF { $art.items = { location: this.locationOfPrevTokens( 2 ) }; }
896
- | MANY <prepare=elementRestriction, arg=calc>
897
- { $art.items = { location: this.lb().location }; }
898
- ) // no anno assignments, except to end type expression
899
- (
900
- ( typeRefOptArgs[ $art.items ] | typeTypeOf[ $art.items ] )
901
- ( <guard=elementRestriction, arg=notNull> nullability[ $art.items ] )?
902
- ( { this.docComment( $art ); } annoAssignStd[ $art ]*
903
- { ; } <exitRule> // TODO TOOL: make it work without workaround { ; }
904
- // TODO TOOL: investigate why simply `{} <exitRule>` is ignored
905
- | enumSymbolsBlock[ $art.items ]
906
- )
907
- |
908
- elementsBlock[ $art.items ]
909
- )
910
- ( <guard=elementRestriction, arg=notNull> nullability[ $art.items ] )?
911
- |
912
- elementsBlock[ $art ] <prepare=elementRestriction, arg=calc>
913
- nullability[ $art ]?
914
- ;
915
-
916
- typeAssocProperties[ art, assoc, card ]
917
- :
918
- target=simplePath { this.setAssocAndComposition( $art, $assoc, $card, $target ); }
919
- { this.docComment( $art ); } annoAssignStd[ $art ]*
920
- ( ON cond=condition { $art.on = $cond; }
921
- { this.docComment( $art ); } annoAssignStd[ $art ]*
922
- | foreignKeysBlock[ $art ] { this.docComment( $art ); } typeProperties[ $art ]?
923
- // remark: no auto-`;` after foreign keys → anno assignment after it possible
924
- | typeProperties[ $art ]
925
- )?
926
- ;
927
-
928
- typeProperties[ art ]
929
- :
930
- (
931
- annoAssignStd[ $art ]
932
- |
933
- <guard=elementRestriction, arg=notNull>
934
- nullability[ $art ] { this.docComment( $art ); }
935
- |
936
- <guard=elementRestriction, arg=default>
937
- DEFAULT expr=expression { $art.default = $expr; this.docComment( $art ); }
938
- )+
939
- ;
940
-
941
-
942
- typeTypeOf[ art ] locals[ location ]
943
- @after{ this.attachLocation( $art.type ); }
944
- :
945
- TYPE OF { location = this.locationOfPrevTokens( 2 ); }
946
- type=simplePath[ 'ref' ] { $art.type = $type; }
947
- (
948
- { $type.scope = 'typeOf'; $type.path.unshift( { id: 'type of', location } ); }
949
- |
950
- ':' { $type.scope = $type.path.length; }
951
- // If we have too much time, we could set the category of the simple path
952
- // before to 'artref'; but why use TYPE OF before `Art:elem` anyway?
953
- Id_all['ref'] { $type.path.push( this.identAst() ); }
954
- (
955
- '.' Id_all['ref'] { $type.path.push( this.identAst() ); }
956
- )*
957
- )
958
- ;
959
-
960
- typeRefOptArgs[ art ] locals[ type = $art.type ]
961
- :
962
- type=simplePath { $art.type = $type; }
963
- (<altRuleStart>)
964
- (
965
- ':' { $type.scope = $type.path.length; }
966
- Id_all['ref'] { $type.path.push( this.identAst() ); }
967
- (
968
- '.' Id_all['ref'] { $type.path.push( this.identAst() ); }
969
- )*
970
- { this.attachLocation( $art.type ); }
971
- |
972
- open='('
973
- (
974
- Number { $art.$typeArgs = this.createArray( $open ); }
975
- { $art.$typeArgs.push( this.unsignedIntegerLiteral() ); }
976
- (
977
- ','
978
- ( Number
979
- { $art.$typeArgs.push( this.unsignedIntegerLiteral() ); }
980
- | tok=VARIABLE/FLOATING
981
- { $art.$typeArgs.push( { literal: 'string', val: $tok.keyword, location: $tok.location } ); }
982
- | <exitLoop>
983
- )
984
- )* // TODO: really as loop?
985
- { this.checkTypeArgs( $art ); } // might reset $art.$typeArgs
986
- |
987
- { $art.$typeArgs = this.createDict( $open ); }
988
- (
989
- typeNamedArg[ $art ]
990
- ( ',' | <exitLoop> )
991
- )+ // TODO: really as loop?
992
- )
993
- ')' { if ($art.$typeArgs) this.finalizeDictOrArray( $art.$typeArgs ); }
994
- )?
995
- ;
996
-
997
- typeNamedArgsList[ art ]
998
- :
999
- '(' { $art.$typeArgs = this.createDict(); }
1000
- (
1001
- typeNamedArg[ $art ]
1002
- ( ',' | <exitLoop> )
1003
- )* // TODO: really as loop?
1004
- ')' { this.finalizeDictOrArray( $art.$typeArgs ); }
1005
- ;
1006
-
1007
- typeNamedArg[ art ]
1008
- :
1009
- // TODO: or keywords with guards for better code completion?
1010
- name=Id['typeparamname']
1011
- ':'
1012
- ( Number
1013
- { this.setTypeFacet( $art, $name, this.unsignedIntegerLiteral() ); }
1014
- | tok=VARIABLE/FLOATING
1015
- { this.setTypeFacet( $art, $name, { literal: 'string', val: $tok.keyword, location: $tok.location } ); }
1016
- )
1017
- ;
1018
-
1019
- cardinality[ art ] locals[ card = {} ]
1020
- @finally{ $art.cardinality = this.attachLocation($card); }
1021
- :
1022
- '['
1023
- (
1024
- '*' { $card.targetMax = this.valueWithLocation(); }
1025
- ( ',' targetCardinality[ $card ] )?
1026
- |
1027
- Number { $card.targetMax = this.unsignedIntegerLiteral(); }
1028
- (
1029
- ',' targetCardinality[ $card ]
1030
- |
1031
- targetCardinality[ $card, true ]<atAltStart>
1032
- )?
1033
- |
1034
- { $card.targetMax = this.valueWithLocation( '*' ); }
1035
- ) // TODO: really optional?
1036
- ']'
1037
- ;
1038
-
1039
- targetCardinality[ card, atAlt = false ]
1040
- :
1041
- // TODO TOOL: the following action should not be executed when called
1042
- // <atAltStart> → we can then remove param `atAlt`
1043
- { if (!$atAlt) $card.sourceMax = $card.targetMax; }
1044
- (
1045
- '*' { $card.targetMax = this.valueWithLocation(); }
1046
- |
1047
- Number { $card.targetMax = this.unsignedIntegerLiteral(); }
1048
- (<altRuleStart>) // TODO TOOL: robust error when moved to after '('
1049
- (
1050
- '..' { $card.targetMin = $card.targetMax; }
1051
- ( '*' { $card.targetMax = this.valueWithLocation(); }
1052
- | Number { $card.targetMax = this.unsignedIntegerLiteral(); }
1053
- )
1054
- )?
1055
- )
1056
- ;
1057
-
1058
- nullability[ art ]
1059
- :
1060
- NULL { this.setNullability( $art, false ); }
1061
- |
1062
- NOT NULL { this.setNullability( $art, true, this.locationOfPrevTokens( 2 ) ); }
1063
- ;
1064
-
1065
- // Queries: projections and SELECTs ---------------------------------------------
1066
-
1067
- queryEOF returns[ query ]
1068
- :
1069
- $query=queryExpression ';'? EOF
1070
- ;
1071
-
1072
- projectionSpec returns[ default query = {} ]
1073
- @finally{ this.attachLocation($query); }
1074
- :
1075
- PROJECTION
1076
- { $query = { op: this.valueWithLocation( 'SELECT' ) }; }
1077
- ON
1078
- tab=fromRefWithOptAlias
1079
- { $query.from = tab; }
1080
- selectItemsList[ $query ]?
1081
- excludingClause[ $query ]?
1082
- ;
1083
-
1084
- queryExpression returns[ default expr = {} ] locals[ op, quantifier ]
1085
- @finally{ this.attachLocation( $expr ); }
1086
- :
1087
- ( '(' queryExpression[ ...$ ] ')' { this.surroundByParens( $expr ); }
1088
- | $expr=selectQuery
1089
- )
1090
- (<altRuleStart>)
1091
- (
1092
- // See also `taggedIfQuery`/`queryOps` in AstBuildingParser.js
1093
- ( ( <prec=4> INTERSECT { $op = this.valueWithLocation(); }
1094
- | <prec=2> EXCEPT/MINUS { $op = this.valueWithLocation(); }
1095
- )
1096
- ( DISTINCT { $quantifier = this.valueWithLocation(); } )?
1097
- | <prec=2> UNION { $op = this.valueWithLocation(); }
1098
- ( DISTINCT/ALL { $quantifier = this.valueWithLocation(); } )?
1099
- )
1100
- query=queryExpression
1101
- // with same op/quantifier: make left-assoc binary to nary:
1102
- { if ($expr.$parens || $op.val !== $expr.op?.val || $quantifier?.val !== $expr.quantifier?.val) $expr = { op, args: [$expr], quantifier, location: { ...$.expr.location } }; } // TODO: ...$
1103
- { $quantifier = undefined; }
1104
- { $expr.args.push( $query ); this.attachLocation( $expr ); }
1105
- )*
1106
- ( <prec=0, postfix>
1107
- // TODO: the following action is a bit stupid, but compatible to ANTLR-based
1108
- // parser:
1109
- { if ($expr.$parens) { this.attachLocation( $expr ); $expr = { op: this.valueWithLocation( '$query', this.la() ), args: [ $expr ] }; } }
1110
- orderByLimitOffset[ $expr ]
1111
- )?
1112
- ;
1113
-
1114
- selectQuery returns[ default query = {} ]
1115
- @finally{ this.attachLocation($query); }
1116
- :
1117
- SELECT { $query = { op: this.valueWithLocation( 'SELECT' ) }; }
1118
- (
1119
- FROM querySource[ $query ]
1120
- (
1121
- MIXIN '{' { $query.mixin = this.createDict(); }
1122
- (
1123
- mixinElementDef[ $query ]
1124
- ( ';' | <exitLoop> )
1125
- )*
1126
- '}' { this.finalizeDictOrArray( $query.mixin ); }
1127
- INTO
1128
- )?
1129
- ( ALL/DISTINCT { $query.quantifier = this.valueWithLocation(); } )?
1130
- // TODO: or directly after SELECT ?
1131
- selectItemsList[ $query ]?
1132
- excludingClause[ $query ]?
1133
- |
1134
- ( ALL/DISTINCT { $query.quantifier = this.valueWithLocation(); } )?
1135
- // TODO TOOL: move <prepare> to all branches if "simple", or with special <…,attach>
1136
- {;} <prepare=inSelectItem, arg=sqlStyle>
1137
- ( '*' { $query.columns = [ this.valueWithLocation() ]; }
1138
- | selectItemDef[ ($query.columns = []) ]
1139
- )
1140
- ( ','
1141
- ( '*' { $query.columns.push( this.valueWithLocation() ); }
1142
- | selectItemDef[ $query.columns ]
1143
- )
1144
- )*
1145
- FROM querySource[ $query ]
1146
- )
1147
- whereGroupByHaving[ $query ]?
1148
- ;
1149
-
1150
- querySource[ query ]
1151
- @after { this.attachLocation($query.from); }
1152
- :
1153
- tab=tableExpression { $query.from = $tab; }
1154
- (
1155
- { const { location } = this.la();
1156
- $query.from = { op: { val: 'join', location }, join: { val: 'cross', location }, args: [$tab] };
1157
- }
1158
- ( ',' tab=tableExpression { $query.from.args.push( $tab ); } )+
1159
- )?
1160
- ;
1161
-
1162
- tableExpression returns[ default expr = {} ] // TableOrJoin
1163
- :
1164
- ( tableOrQueryParens[ ...$ ]
1165
- (<altRuleStart> { $expr = this.taggedIfQuery( $expr ); } )
1166
- | fromRefWithOptAlias[ ...$ ]
1167
- )
1168
- (
1169
- join=CROSS JOIN
1170
- { if ($expr?.join?.val !== 'cross' || $expr.$parens) $expr = { op: this.valueWithLocation(), join: this.valueWithLocation( undefined, $join ), args: [ $expr ] }; }
1171
- ( tab=tableOrQueryParens { const r = this.taggedIfQuery( $tab ); if (r) $expr.args.push( r ); }
1172
- { this.attachLocation( $expr ); }
1173
- | tab=fromRefWithOptAlias { if ($tab) $expr.args.push( $tab ); }
1174
- { this.attachLocation( $expr ); }
1175
- )
1176
- |
1177
- ( ( join=INNER | join=LEFT/RIGHT/FULL OUTER? )
1178
- card=joinCardinality? JOIN
1179
- | JOIN { $join = undefined; }
1180
- )
1181
- // TODO TOOL: allow zero-alt in choice in outer alt → JOIN can be moved outside
1182
- { $expr = { op: this.valueWithLocation(), join: this.valueWithLocation( $join?.keyword || 'inner', $join ), args: [ $expr ] }; if ($card) $expr.cardinality = $card; $card = undefined; }
1183
- tab=tableExpression
1184
- { $expr.args.push( $tab ); this.attachLocation( $expr ); }
1185
- ON cond=condition { $expr.on = $cond; }
1186
- { this.attachLocation( $expr ); }
1187
- )*
1188
- ;
1189
-
1190
- tableOrQueryParens returns[ default expr ]
1191
- :
1192
- '(' <prepare=queryOnLeft>
1193
- ( <prefer> tableOrQueryParens[ ...$ ]
1194
- ( tableExpression[ ...$ ]<atAltStart, prepare=queryOnLeft, arg=table>
1195
- | <guard=queryOnLeft> queryExpression[ ...$ ]<atAltStart>
1196
- )?
1197
- | tableExpression[ ...$ ] <prepare=queryOnLeft, arg=table>
1198
- | queryExpression[ ...$ ]
1199
- )
1200
- ')'
1201
- { this.surroundByParens( $expr ); }
1202
- ( <guard=queryOnLeft, arg=table> AS Id['FromAlias']
1203
- { $expr = this.taggedIfQuery( $expr ); $expr.name = this.identAst(); }
1204
- | <guard=queryOnLeft, arg=tableWithoutAs> Id_restricted['FromAlias']
1205
- // TODO TOOL: shouldn't we have generated `default: this.giR()`?
1206
- { $expr = this.taggedIfQuery( $expr ); $expr.name = this.fragileAlias(); }
1207
- )?
1208
- ; // change #10799 for ANTLR-based parser
1209
-
1210
- joinCardinality returns[ sourceMax, targetMax ]
1211
- @finally{ this.attachLocation( $ ); }
1212
- :
1213
- (
1214
- ( EXACT { $.sourceMin = this.valueWithLocation( 1 ); } )?
1215
- ONE { $sourceMax = this.valueWithLocation( 1 ); }
1216
- |
1217
- MANY { $sourceMax = this.valueWithLocation( '*' ); }
1218
- )
1219
- TO
1220
- (
1221
- ( EXACT { $.targetMin = this.valueWithLocation( 1 ); } )?
1222
- ONE { $targetMax = this.valueWithLocation( 1 ); }
1223
- |
1224
- MANY { $targetMax = this.valueWithLocation( '*' ); }
1225
- )
1226
- ;
1227
-
1228
- fromRefWithOptAlias returns[ default expr = {} ]
1229
- @finally{ this.attachLocation( $expr ); }
1230
- :
1231
- { $expr.path = []; }
1232
- fromPath[ $expr, 'artref' ]
1233
- (
1234
- ':' { if (!$expr.scope) $expr.scope = $expr.path.length; else {
1235
- this.warning( 'syntax-invalid-path-separator', this.lb(),
1236
- { '#': 'colon', code: ':', newcode: '.' } );
1237
- } }
1238
- fromPath[ $expr, 'ref']
1239
- )?
1240
- (
1241
- AS Id['FromAlias'] { $expr.name = this.identAst(); }
1242
- |
1243
- // <guard=tableWithoutAs> not necessary, tool uses `default: this.giR()`
1244
- Id_restricted['FromAlias']
1245
- { $expr.name = this.fragileAlias(); }
1246
- |
1247
- <default=fallback>
1248
- { this.classifyImplicitName( $expr.scope ? 'FromElemImplicit' : 'FromImplicit', $expr ); }
1249
- )
1250
- ;
1251
-
1252
- fromPath[ table, category ] locals[ pathItem ]
1253
- @finally{ this.attachLocation( $table.path ); }
1254
- :
1255
- Id[ $category ] { $table.path.push( $pathItem = this.identAst() ); }
1256
- ( fromArgumentsAndFilter[ $pathItem ] { $pathItem = null; } )?
1257
- (
1258
- <guard=notAfterEntityArgOrFilter> // TODO TOOL: allow <hide=method>
1259
- '.' { if (!$pathItem && !$table.scope) {
1260
- $table.scope = $table.path.length; $category = 'ref';
1261
- this.warning( 'syntax-invalid-path-separator', this.lb(),
1262
- { '#': 'dot', code: '.', newcode: ':' } );
1263
- } }
1264
- Id_all[ $category ] { $table.path.push( $pathItem = this.identAst() ); }
1265
- ( fromArgumentsAndFilter[ $pathItem ] { $pathItem = null; } )?
1266
- )*
1267
- ;
1268
-
1269
- fromArgumentsAndFilter[ pathStep ]
1270
- options{ minTokensMatched = 1 }
1271
- :
1272
- (
1273
- '(' { $pathStep.args = this.createDict(); $pathStep.$syntax = ':'; }
1274
- ( fromNamedArgument[ ...$ ]
1275
- ( ',' | <exitLoop> )
1276
- )+
1277
- ')'
1278
- )?
1279
- cardinalityAndFilter[ ...$ ]?
1280
- ;
1281
-
1282
- fromNamedArgument[ pathStep ]
1283
- :
1284
- name=Id['paramname'] ':' expr=expression
1285
- { this.addDef( $expr, $pathStep, 'args', 0, this.identAst( $name ) ); }
1286
- // TODO: or add argument directly after having parsed the name? (for CC)
1287
- ;
1288
-
1289
- cardinalityAndFilter[ pathStep ]
1290
- :
1291
- '['
1292
- ( <guard=beforeColon> Number // TODO: only allow `1`?
1293
- { $pathStep.cardinality = { targetMax: this.unsignedIntegerLiteral(), location: this.lb().location }; }
1294
- ':'
1295
- )?
1296
- filterClauses[ $pathStep ]
1297
- // TODO: why not allowing all clauses to be optional? (then inline rule `filterClauses`)
1298
- ']'
1299
- ;
1300
-
1301
- filterClauses[ pathStep ]
1302
- options{ minTokensMatched = 1 }
1303
- :
1304
- ( WHERE?
1305
- // compare GROUP/HAVING/ORDER/LIMIT w/o prediction (reserved WHERE anyway):
1306
- // <restrict=Id> // TODO TOOL: not yet supported here
1307
- // BTW, why? not necessary anymore…
1308
- cond=condition { $pathStep.where = $cond; }
1309
- )?
1310
- ( <hide>
1311
- { this.csnParseOnly('syntax-unexpected-sql-clause', 1, { keyword: 'group by' }); }
1312
- groupByClause[ $pathStep ]
1313
- )?
1314
- ( <hide> HAVING
1315
- { this.csnParseOnly('syntax-unexpected-sql-clause', -1, { keyword: 'having' }); }
1316
- cond=condition { $pathStep.having = $cond; }
1317
- )?
1318
- ( <hide>
1319
- { if (this.lk() === 'limit') this.csnParseOnly('syntax-unexpected-sql-clause', 0, { keyword: 'limit' } ); else this.csnParseOnly('syntax-unexpected-sql-clause', 1, { keyword: 'order by' } ); }
1320
- // I do not care that there is now only one error msg for both ORDER BY … LIMIT …
1321
- orderByLimitOffset[ $pathStep ]
1322
- )?
1323
- ;
1324
-
1325
- excludingClause[ query ]
1326
- :
1327
- // syntax is less than ideal - EXCLUDING is only useful for `*` - with
1328
- // this syntax, people wonder what happens with explicit select items
1329
- EXCLUDING '{' { $query.excludingDict = this.createDict(); }
1330
- // TODO: better move '{' to after action, but → diff to ANTLR-based parser
1331
- (
1332
- Id_all['ref'] // TODO: different category?
1333
- { this.addDef( { location: this.lb().location }, $query, 'excludingDict', '', this.identAst() ); }
1334
- ( ',' | <exitLoop> )
1335
- )+
1336
- '}'<prepare=afterBrace>
1337
- { this.finalizeDictOrArray( $query.excludingDict ); }
1338
- ;
1339
-
1340
- selectItemsList[ query, start = undefined ]
1341
- :
1342
- '{'<prepare=inSelectItem, arg=top>
1343
- { $query.columns = this.createArray( $start ); }
1344
- (
1345
- ( '*' { $query.columns.push( this.valueWithLocation() ); }
1346
- | selectItemDef[ $query.columns ]
1347
- )
1348
- ( ',' | <exitLoop> )
1349
- )*
1350
- '}'<prepare=afterBrace>
1351
- { this.finalizeDictOrArray( $query.columns ); }
1352
- ;
1353
-
1354
- nestedSelectItemsList[ query, clause ]
1355
- :
1356
- '{'<prepare=inSelectItem>
1357
- { $query[$clause] = this.createArray(); }
1358
- (
1359
- ( '*' { $query[$clause].push( this.valueWithLocation() ); }
1360
- | selectItemDef[ $query[$clause] ]
1361
- )
1362
- ( ',' | <exitLoop> )
1363
- )*
1364
- '}'<prepare=afterBrace>
1365
- { this.finalizeDictOrArray( $query[$clause] ); }
1366
- ;
1367
-
1368
- selectItemDef[ columns ] locals[ art = new XsnArtifact() ]
1369
- @finally{ this.attachLocation( $art ); }
1370
- :
1371
- { $columns.push( $art ); } // TODO: probably too early
1372
- { this.docComment( $art ); } annoAssignCol[ $art ]*
1373
- ( <guard=modifierRestriction> VIRTUAL
1374
- { $art.virtual = this.valueWithLocation( true ); }
1375
- )?
1376
- {;} <prepare=columnExpr, arg=key> // TOOL TODO: disappears without {;}
1377
- ( <guard=modifierRestriction> KEY
1378
- { $art.key = this.valueWithLocation( true ); }
1379
- )?
1380
- // TODO: we might have an extra rule for column expression...
1381
- (
1382
- (
1383
- expr=expression { $art.value = $expr; }
1384
- ( AS Id['ItemAlias'] { $art.name = this.identAst(); }
1385
- | Id_restricted['ItemAlias'] { $art.name = this.fragileAlias( true ); }
1386
- )?
1387
- |
1388
- <prefer>
1389
- expr=valuePath { $expr = this.valuePathAst( $expr ); $art.value = $expr; }
1390
- (
1391
- (
1392
- OVER { this.pushXprToken( $expr.suffix = [] ); }
1393
- overClause[ $expr.suffix ]
1394
- ( e=expression[ ...{ expr: $expr } ]<atAltStart>
1395
- { Object.assign( $e.location || {}, $expr.location ); $art.value = this.attachLocation( $e )}
1396
- )?
1397
- |
1398
- e=expression[ ...{ expr: $expr } ]<atAltStart>
1399
- { Object.assign( $e.location || {}, $expr.location ); $art.value = this.attachLocation( $e )}
1400
- )
1401
- ( AS Id['ItemAlias'] { $art.name = this.identAst(); }
1402
- | Id_restricted['ItemAlias'] { $art.name = this.fragileAlias( true ); }
1403
- )?
1404
- | // includes empty
1405
- AS Id['ItemAlias'] <prepare=nestedExpand> { $art.name = this.identAst(); }
1406
- |
1407
- Id_restricted['ItemAlias'] <prepare=nestedExpand>
1408
- { $art.name = this.fragileAlias( true ); }
1409
- |
1410
- { this.classifyImplicitName( 'ItemImplicit', $expr ); } <prepare=nestedExpand>
1411
- // TODO TOOL: action in default does not work in embedded action
1412
- |
1413
- // TODO: guard instead reportExpandInline if valuePath is function?
1414
- '.'
1415
- { this.reportUnexpectedSpace( this.lb(), this.la().location, true ); } // TODO: no ERR
1416
- { this.reportExpandInline( $art, true ); }
1417
- // no extra 'syntax-unexpected-alias' anymore,
1418
- // 'syntax-unexpected-anno' reported in define.js
1419
- (
1420
- nestedSelectItemsList[ $art, 'inline' ]
1421
- excludingClause[ $art ]?
1422
- |
1423
- '*' { $art.inline = [ this.valueWithLocation() ]; }
1424
- )
1425
- <exitRule>
1426
- )
1427
- )
1428
- // expand is only allowed only after valuePath, but error recovery is poor
1429
- // if the following is moved up to <prepare=nestedExpand>
1430
- (
1431
- // TODO: make guard handle that if valuePath might be function?
1432
- <guard=nestedExpand>
1433
- { if (!this.nestedExpand(true)) this.reportExpandInline( $art, false ); }
1434
- nestedSelectItemsList[ $art, 'expand' ]
1435
- excludingClause[ $art ]?
1436
- )?
1437
- |
1438
- nestedSelectItemsList[ $art, 'expand' ]
1439
- excludingClause[ $art ]?
1440
- AS Id['ItemAlias'] { $art.name = this.identAst(); }
1441
- )
1442
- { this.docComment( $art ); } <prepare=columnExpr> annoAssignMid[ $art ]*
1443
- (
1444
- ':' // TODO: guard? currently not with expand ?
1445
- (
1446
- ( typeTypeOf[ $art ]
1447
- | ( LOCALIZED { $art.localized = this.valueWithLocation( true ); } )?
1448
- typeRefOptArgs[ $art ]
1449
- )
1450
- |
1451
- // TODO: guard for ref-only expression ?
1452
- REDIRECTED TO target=simplePath { $art.target = $target; }
1453
- ( ON cond=condition { $art.on = $cond; }
1454
- | foreignKeysBlock[ $art ]
1455
- )?
1456
- |
1457
- ( <guard=columnExpr> // arg=singleId
1458
- assoc=ASSOCIATION { this.associationInSelectItem( $art ); }
1459
- cardinality[ $art ]? TO
1460
- | <guard=columnExpr> // arg=singleId
1461
- assoc=COMPOSITION { this.associationInSelectItem( $art ); }
1462
- cardinality[ $art ]? OF
1463
- )
1464
- ( <guard=noRepeatedCardinality> card=ONE/MANY )?
1465
- target=simplePath
1466
- { this.setAssocAndComposition( $art, $assoc, $card, $target ); }
1467
- ON expr=condition { $art.on = $expr; }
1468
- )
1469
- // TODO: no nullability here ?
1470
- { this.docComment( $art ); } annoAssignStd[ $art ]*
1471
- )?
1472
- ;
1473
-
1474
- whereGroupByHaving[ query ]
1475
- options{ minTokensMatched = 1 }
1476
- :
1477
- ( WHERE cond=condition { $query.where = $cond; } )?
1478
- groupByClause[ ...$ ]?
1479
- ( HAVING cond=condition { $query.having = $cond; } )?
1480
- ;
1481
-
1482
- groupByClause[ query ]
1483
- :
1484
- GROUP BY expr=expression { $query.groupBy = [ $expr ]; }
1485
- ( ',' expr=expression { $query.groupBy.push( $expr ); } )*
1486
- ;
1487
-
1488
- orderByLimitOffset[ query ]
1489
- options{ minTokensMatched = 1 }
1490
- :
1491
- orderByClause[ ...$ ]?
1492
- ( LIMIT expr=expression { $query.limit = { rows: $expr }; }
1493
- ( OFFSET expr=expression { $query.limit.offset = $expr; } )?
1494
- )?
1495
- ;
1496
-
1497
- orderByClause[ query ]
1498
- :
1499
- ORDER BY expr=orderByExpression { $query.orderBy = [ $expr ]; }
1500
- ( ',' expr=orderByExpression { $query.orderBy.push( $expr ); } )*
1501
- ;
1502
-
1503
- orderByExpression returns[ default expr ]
1504
- :
1505
- expression[ ...$ ]
1506
- ( ASC/DESC { $expr.sort = this.valueWithLocation(); } )?
1507
- ( NULLS FIRST/LAST { $expr.nulls = this.valueWithLocation(); } )?
1508
- ;
1509
-
1510
- // Conditions and expressions ---------------------------------------------------
1511
-
1512
- conditionEOF returns[ cond ]
1513
- :
1514
- $cond=expression EOF
1515
- ;
1516
-
1517
- condition returns[ default expr ]
1518
- :
1519
- expression[ ...$ ]
1520
- ;
1521
-
1522
- valuePath returns[ default expr = { path: [] } ] locals[ pathItem ]
1523
- @finally{ this.attachLocation( $expr ); }
1524
- :
1525
- Id['ref'] { $expr.path.push( $pathItem = this.identAst() ); }
1526
- ( argumentsAndFilter[ $pathItem ] )?
1527
- (<altRuleStart>)
1528
- (
1529
- <guard=isDotForPath> '.'
1530
- Id_all['ref'] { $expr.path.push( $pathItem = this.identAst() ); }
1531
- ( argumentsAndFilter[ $pathItem ] { $pathItem = null; } )?
1532
- )*
1533
- ;
1534
-
1535
- expression returns[ default expr = {} ]
1536
- @finally{ if (this.s == null) this.attachLocation( $expr ); }
1537
- :
1538
- (
1539
- expressionOrQueryParens[ ...$ ]
1540
- (<altRuleStart> { $expr = this.taggedIfQuery( $expr ); })
1541
- |
1542
- literalValue[ ...$ ]
1543
- |
1544
- ':' { this.reportUnexpectedSpace(); }
1545
- (
1546
- Id_all['paramref']
1547
- { $expr = { path: [ this.identAst() ], location: this.startLocation(), scope: 'param' }; }
1548
- ( valuePath[ ...$ ]<atAltStart>
1549
- { $expr = this.valuePathAst( $expr ); }
1550
- | { this.attachLocation( $expr ); }
1551
- )
1552
- |
1553
- <hide> Number // TODO: as user condition
1554
- { this.csnParseOnly( 'syntax-unsupported-param', -1, { '#': 'positional', code: ':' + this.lb().text } ); }
1555
- { $expr = this.attachLocation({ param: this.unsignedIntegerLiteral(), scope: 'param' }); }
1556
- )
1557
- |
1558
- <hide> '?' // TODO: do as user condition
1559
- {this.csnParseOnly( 'syntax-unsupported-param', -1, { '#': 'dynamic', code: '?' } );
1560
- }
1561
- { $expr = this.attachLocation({ param: this.valueWithLocation(), scope: 'param' }); }
1562
- |
1563
- e=valuePath { $expr = this.valuePathAst( $e ); }
1564
- (
1565
- OVER { this.pushXprToken( $expr.suffix = [] ); }
1566
- e=overClause[ $expr.suffix ]
1567
- )?
1568
- { this.attachLocation( $expr ); }
1569
- |
1570
- newAndValuePath[ ...$ ]
1571
- |
1572
- EXISTS { $expr = this.applyOpToken(); }
1573
- ( open='(' e=queryExpression ')'
1574
- { $expr.args.push( this.taggedIfQuery( this.surroundByParens( $e, $open ) ) ); }
1575
- { this.attachLocation( $expr ); }
1576
- | e=valuePath { $e = this.valuePathAst( $e ); $e.$expected = 'exists'; }
1577
- // TODO: re-check whether to really set $expected in parser
1578
- { $expr.args.push( $e ); this.attachLocation( $expr ); }
1579
- | <hide> '?' // TODO: do as user condition
1580
- {this.csnParseOnly( 'syntax-unsupported-param', -1, { '#': 'dynamic', code: '?' } ); }
1581
- { $expr.args.push( { param: this.valueWithLocation(), scope: 'param' } ); this.attachLocation( $expr ); }
1582
- )
1583
- |
1584
- caseExpression[ ...$ ]
1585
- |
1586
- castFunction[ ...$ ]
1587
- |
1588
- ( <prec=30, prefix> '+'/'-' { $expr = this.applyOpToken(); }
1589
- | <prec=8, prefix> NOT { $expr = this.applyOpToken(); }
1590
- )
1591
- e=expression { $expr = this.signedExpression( $expr, $e ); }
1592
- )
1593
- // binary + postfix
1594
- ( (
1595
- ( <prec=24> '*'/'/' { $expr = this.applyOpToken( $expr, 'nary' ); }
1596
- | <prec=22> '+'/'-' { $expr = this.applyOpToken( $expr, 'nary' ); }
1597
- | <prec=20> '||' { $expr = this.applyOpToken( $expr, 'nary' ); }
1598
- | <prec=4> AND { $expr = this.applyOpToken( $expr, 'nary' ); }
1599
- | <prec=2> OR { $expr = this.applyOpToken( $expr, 'nary' ); }
1600
- | <prec=0> '?' { $expr = this.applyOpToken( $expr, '?:' ); }
1601
- e=expression { $expr.args.push( $e ); }
1602
- ':' { this.pushXprToken( $expr ); }
1603
- // -> createXprForOp vs createAstForOp
1604
-
1605
- | <prec=10, assoc=none> '='/'<>'/'>'/'>='/'<'/'<='/'!='
1606
- { $expr = this.applyOpToken( $expr ); }
1607
- ( ANY/SOME/ALL { this.pushXprToken( $expr ); } )?
1608
- | <prec=10, assoc=none> '=='
1609
- { $expr = this.applyOpToken( $expr ); }
1610
- )
1611
- e=expression { $expr.args.push( $e ); }
1612
-
1613
- | <prec=10, postfix=once> IS { $expr = this.applyOpToken( $expr ); }
1614
- ( NOT { this.pushXprToken( $expr ); } )?
1615
- NULL { this.pushXprToken( $expr ); }
1616
- | ( <arg=10, guard=isNegatedRelation> NOT { $expr = this.applyOpToken( $expr ); }
1617
- // TODO: condition, because there might be NOT NULL after DEFAULT expression
1618
- | <prec=10, postfix=once>
1619
- { $expr = { op: { val: 'ixpr', location: this.la().location }, args: [ $expr ] }; }
1620
- )
1621
- (
1622
- BETWEEN { this.pushXprToken( $expr ); }
1623
- e=expression { $expr.args?.push( $e ); }
1624
- AND { this.pushXprToken( $expr ); }
1625
- e=expression { $expr.args?.push( $e ); }
1626
- | IN { this.pushXprToken( $expr ); }
1627
- e=expression { $expr.args?.push( this.secureParens( $e ) ); }
1628
- | LIKE { this.pushXprToken( $expr ); }
1629
- e=expression { $expr.args?.push( $e ); }
1630
- ( ESCAPE { this.pushXprToken( $expr ); }
1631
- e=expression { $expr.args?.push( $e ); }
1632
- )?
1633
- )
1634
- )
1635
- { this.attachLocation( $expr ); }
1636
- )*
1637
- ;
1638
-
1639
- expressionOrQueryParens returns[ default expr ]
1640
- :
1641
- '(' <prepare=queryOnLeft>
1642
- ( <prefer> expressionOrQueryParens[ ...$ ]
1643
- ( expression[ ...$ ]<atAltStart, prepare=queryOnLeft, arg=expr>
1644
- continueExpressionslist[ ...$ ]?
1645
- | continueExpressionslist[ ...$ ] <prepare=queryOnLeft, arg=expr>
1646
- | <guard=queryOnLeft> queryExpression[ ...$ ]<atAltStart>
1647
- )?
1648
- | expression[ ...$ ] <prepare=queryOnLeft, arg=expr>
1649
- continueExpressionslist[ ...$ ]?
1650
- | queryExpression[ ...$ ]
1651
- )
1652
- ')'
1653
- { this.surroundByParens( $expr ); }
1654
- ;
1655
-
1656
- continueExpressionslist[ expr ]
1657
- @finally{ this.attachLocation( $expr ); }
1658
- :
1659
- ',' { $expr = { op: this.valueWithLocation( 'list' ), args: [ $expr ], location: { ... $expr.$parens?.at( -1 ) ?? $expr.location } }; }
1660
- (
1661
- e=expression { $expr.args.push( $e ); }
1662
- ( ',' | <exitLoop> )
1663
- )+
1664
- { this.attachLocation( $expr ); }
1665
- ;
1666
-
1667
- newAndValuePath returns[ default expr ]
1668
- @finally{ this.attachLocation( $expr ); }
1669
- :
1670
- NEW { $expr = this.applyOpToken(); }
1671
- e=valuePath { $e = this.valuePathAst( $e ); }
1672
- { if ($e.op?.val !== 'ixpr') $expr.args.push( $e ); else $expr.args.push( ...e.args ); }
1673
- ;
1674
-
1675
- caseExpression returns[ default expr ]
1676
- @finally{ this.attachLocation( $expr ); }
1677
- :
1678
- CASE { $expr.op = { val: 'ixpr', location: this.lb().location }; $expr.args = []; this.pushXprToken( $expr ); }
1679
- ( e=expression { $expr.args.push( $e ); } )?
1680
- (
1681
- WHEN { this.pushXprToken( $expr ); }
1682
- e=expression { $expr.args.push( $e ); }
1683
- THEN { this.pushXprToken( $expr ); }
1684
- e=expression { $expr.args.push( $e ); }
1685
- )+
1686
- (
1687
- ELSE { this.pushXprToken( $expr ); }
1688
- e=expression { $expr.args.push( $e ); }
1689
- )?
1690
- END { this.pushXprToken( $expr ); }
1691
- ;
1692
-
1693
- castFunction returns[ default expr ]
1694
- @finally{ this.attachLocation( $expr ); }
1695
- :
1696
- CAST { $expr.op = this.valueWithLocation(); }
1697
- '(' { $expr.args = this.createArray(); }
1698
- arg=expression { $expr.args?.push( $arg ); }
1699
- AS typeRefOptArgs[ $expr ]
1700
- ')' { this.finalizeDictOrArray( $expr.args ); }
1701
- ;
1702
-
1703
- argumentsAndFilter[ pathStep ]
1704
- // TODO: what about valuePath with EXISTS, after `:` etc (also in ANTLR-based parser)?
1705
- options{ minTokensMatched = 1 }
1706
- :
1707
- (
1708
- open='(' <prepare=prepareSpecialFunction>
1709
- { $pathStep.args = this.createArray(); }
1710
- // action here, default action won't be executed with failed condition (TODO
1711
- // TOOL? at least msg)
1712
- (
1713
- // TODO: if we want perfect CC and error recovery, `isNamedArg` would not
1714
- // only check the next token for the ':'/'=>', but probably also for the
1715
- // one after that, at least if the next token could not be a valid
1716
- // operator (consider that parameter references look like `:PAR`)
1717
- <guard=isNamedArg> id=Id_all['paramname']
1718
- (
1719
- ':' { $pathStep.args = this.createDict( $open ); $pathStep.$syntax = ':'; }
1720
- expr=expression { this.addNamedArg( $pathStep, $id, $expr ); }
1721
- ( ','
1722
- ( id=Id_all['paramname'] ':'
1723
- expr=expression { this.addNamedArg( $pathStep, $id, $expr ); }
1724
- | <exitLoop>
1725
- )
1726
- )*
1727
- |
1728
- '=>' { $pathStep.args = this.createDict(); }
1729
- // TODO: potentially special expressions for special functions
1730
- expr=expression { this.addNamedArg( $pathStep, $id, $expr ); }
1731
- ( ','
1732
- ( id=Id_all['paramname'] '=>'
1733
- expr=expression { this.addNamedArg( $pathStep, $id, $expr ); }
1734
- | <exitLoop>
1735
- )
1736
- )*
1737
- )
1738
- |
1739
- <default=fallback>
1740
- (
1741
- expr=funcExpression { $pathStep.args.push( $expr ); }
1742
- (
1743
- ','<prepare=nextFunctionArgument>
1744
- ( expr=funcExpression { $pathStep.args.push( $expr ); }
1745
- | <exitLoop, guard=atRightParen>
1746
- // TODO: later allow ')' <exitRule>, or ')'<mock, exitLoop>, or <exitBlock=MainAlt> with ( options{ block=MainAlt }: …)
1747
- )
1748
- )*
1749
- ( // ORDER BY in generic functions, e.g. `first_value(id order by name)`
1750
- ORDER { $expr = $pathStep.args[$pathStep.args.length - 1] = this.applyOpToken( $expr ); }
1751
- BY { this.pushXprToken( $expr ); }
1752
- orderByClauseAsXpr[ $expr.args ]
1753
- { this.attachLocation( $expr ); }
1754
- )?
1755
- )?
1756
- )
1757
- ')' { this.finalizeDictOrArray( $pathStep.args ); }
1758
- )?
1759
- // TODO: not with function!
1760
- cardinalityAndFilter[ ...$ ]?
1761
- ;
1762
-
1763
- funcExpression returns[ default expr ] locals[ args ]
1764
- @finally{ this.attachLocation( $expr ); }
1765
- :
1766
- ( options{ lookahead = lGenericIntroOrExpr; }
1767
- :
1768
- $expr=expression
1769
- |
1770
- tok=GenericExpr // keyword as replacement for expression, like '*'
1771
- { $expr = { val: $tok.keyword ?? $tok.type, location: $tok.location, literal: 'token' }; }
1772
- |
1773
- GenericIntro // keyword as introduction of expression, like DISTINCT
1774
- { $expr = this.applyOpToken(); $args = $expr.args; }
1775
- e=expression { $expr.args.push( $e ); }
1776
- )
1777
- // TODO: some <restrict=Id> here?
1778
- ( options{ lookahead = lGenericSeparator; }
1779
- :
1780
- GenericSeparator
1781
- { if ($args) this.pushXprToken( $args ); else { $expr= this.applyOpToken( $expr ); $args = $expr.args; } }
1782
-
1783
- ( options{ lookahead = lGenericExpr; }
1784
- :
1785
- e=expression { $args.push( $e ); }
1786
- |
1787
- GenericExpr { this.pushXprToken( $args ); }
1788
- )
1789
- )*
1790
- ;
1791
-
1792
- // TODO: check Id_all - necessary if generic token is a reserved word?
1793
- GenericExpr // workaround TODO Runtime: no-skip/exit recovery w/o guards, see #13485
1794
- : Id_all | '*' | DeleteStarFromSet ; // → DeleteStarFromSet to avoid failing on same token
1795
- GenericIntro
1796
- : Id_all ;
1797
- GenericSeparator
1798
- : Id_restricted ; // otherwise expression ops use keyword prediction
1799
- // TODO TOOL: use `<restrict=Id> GenericSeparator` instead and back to Id_all or Id
1800
-
1801
- overClause[ outer ] locals[ over = [] ]
1802
- @finally{ $outer.push( this.surroundByParens( this.ixprAst( $over ) ) ); }
1803
- :
1804
- '('
1805
- ( PARTITION { this.pushXprToken( $over ); } BY { this.pushXprToken( $over ); }
1806
- expressionsAsXpr[ $over ]
1807
- )?
1808
- ( ORDER { this.pushXprToken( $over ); } BY { this.pushXprToken( $over ); }
1809
- orderByClauseAsXpr[ $over ]
1810
- )?
1811
- ( ROWS { this.pushXprToken( $over ); }
1812
- windowFrameClause[ $over ]
1813
- )?
1814
- ')'
1815
- ;
1816
-
1817
- expressionsAsXpr[ outer ] locals[ args = [] ]
1818
- @finally{ $outer.push( this.ixprAst( $args ) ); }
1819
- :
1820
- expr=expression { $args.push( $expr ); }
1821
- ( ',' { this.pushXprToken( $args ); }
1822
- expr=expression { $args.push( $expr ); }
1823
- )*
1824
- ;
1825
-
1826
- orderByClauseAsXpr[ outer ] locals[ args = [] ]
1827
- @finally{ $outer.push( this.ixprAst( $args ) ); }
1828
- :
1829
- orderBySpecAsXpr[ $args ]
1830
- ( ',' { this.pushXprToken( $args ); }
1831
- orderBySpecAsXpr[ $args ]
1832
- )*
1833
- ;
1834
-
1835
- orderBySpecAsXpr[ args ]
1836
- :
1837
- expr=expression { $args.push( $expr ); }
1838
- ( ASC/DESC { this.pushXprToken( $args ); } )?
1839
- ( NULLS { this.pushXprToken( $args ); }
1840
- FIRST/LAST { this.pushXprToken( $args ); }
1841
- )?
1842
- ;
1843
-
1844
- windowFrameClause[ outer ] locals[ args = [] ]
1845
- @finally{ $outer.push( this.ixprAst( $args ) ); }
1846
- :
1847
- ( UNBOUNDED { this.pushXprToken( $args ); }
1848
- | Number { $args.push( this.unsignedIntegerLiteral() ); }
1849
- )
1850
- PRECEDING { this.pushXprToken( $args ); }
1851
- |
1852
- CURRENT { this.pushXprToken( $args ); }
1853
- ROW { this.pushXprToken( $args ); }
1854
- |
1855
- BETWEEN { this.pushXprToken( $args ); }
1856
- windowFrameBoundSpec[ $args ]
1857
- AND { this.pushXprToken( $args ); }
1858
- windowFrameBoundSpec[ $args ]
1859
- ;
1860
-
1861
- windowFrameBoundSpec[ args ]
1862
- :
1863
- ( UNBOUNDED { this.pushXprToken( $args ); }
1864
- | Number { $args.push( this.unsignedIntegerLiteral() ); }
1865
- )
1866
- ( FOLLOWING | PRECEDING ) { this.pushXprToken( $args ); }
1867
- |
1868
- CURRENT { this.pushXprToken( $args ); }
1869
- ROW { this.pushXprToken( $args ); }
1870
- ;
1871
-
1872
- literalValue returns[ default expr = {} ]
1873
- @finally{ this.attachLocation( $expr ); }
1874
- :
1875
- // TODO: remove from this rule (not in enum! `String enum { foo = #bar }`) ?
1876
- '#' { this.reportUnexpectedSpace(); }
1877
- Id_all['enumref']
1878
- { $expr = { literal: 'enum', sym: this.identAst() } }
1879
- |
1880
- NULL
1881
- { $expr = { literal: 'null', val: null }; }
1882
- |
1883
- TRUE/FALSE
1884
- { $expr = { literal: 'boolean', val: this.lb().keyword === 'true' }; }
1885
- |
1886
- Number
1887
- { $expr = this.numberLiteral(); } // allow float and large number
1888
- |
1889
- String
1890
- { $expr = this.quotedLiteral(); }
1891
- |
1892
- QuotedLiteral // x'12', date'...', time'...', timestamp'...'
1893
- { $expr = this.quotedLiteral(); }
1894
- ;
1895
-
1896
- // Annotation assignments -------------------------------------------------------
1897
-
1898
- // We have three versions of the annotation assignment rules:
1899
- // - "…Std": typically before keyword+name, after a name if no ':' could follow
1900
- // - "…Col": at the beginning of a column def, which can start with ':' or '#'
1901
- // - "…Mid": typically after a name if a ':' could follow
1902
-
1903
- annoAssignStd[ art ]
1904
- @finally{ this.docComment( $art ); }
1905
- :
1906
- '@'<prepare=annoInSameLine> { this.reportUnexpectedSpace(); }
1907
- ( annoAssignParen[ ...$ ]
1908
- | annoAssignBase[ ...$ ]
1909
- )
1910
- ;
1911
-
1912
- annoAssignCol[ art ]
1913
- @finally{ this.docComment( $art ); }
1914
- :
1915
- '@' { this.reportUnexpectedSpace(); }
1916
- ( annoAssignParen[ ...$ ]
1917
- | annoAssignBase[ ...$ ]
1918
- )
1919
- ;
1920
-
1921
- annoAssignMid[ art ]
1922
- @finally{ this.docComment( $art ); }
1923
- :
1924
- '@'<prepare=annoInSameLine> { this.reportUnexpectedSpace(); }
1925
- ( annoAssignParen[ ...$ ]
1926
- | name=annoNamePath // !
1927
- { this.assignAnnotation( $art, {}, $name ); this.warnIfColonFollows( $name ); }
1928
- )
1929
- ;
1930
-
1931
- annoAssignParen[ art ]
1932
- :
1933
- '('<prepare=annoInSameLine>
1934
- ( annoAssignBase[ $art ]
1935
- //( annoAssignErrorRecoveryHelper )?
1936
- ( ',' | <exitLoop> )
1937
- )*
1938
- ')'
1939
- ;
1940
-
1941
- annoAssignBase[ art ] locals[ value = {} ]
1942
- @finally{ this.assignAnnotation( $art, $value, $name || {} ); }
1943
- :
1944
- name=annoNamePath
1945
- ( <guard=annoInSameLine> ':' value=annoValue )?
1946
- ;
1947
-
1948
- annoNamePath[ category = 'anno' ] returns[ default name = new XsnName() ]
1949
- @finally{ this.attachLocation( $name ); }
1950
- :
1951
- Id_all[ $category ]
1952
- { $name.path = [ this.identAst() ]; }
1953
- (
1954
- '.'
1955
- ( Id_all[ $category ] { $name.path.push( this.identAst() ); }
1956
- | at='@' // TODO: complain about spaces after?
1957
- Id_all[ $category ] { $name.path.push( this.identAstWithPrefix( $at ) ); }
1958
- )
1959
- )*
1960
- ( <guard=annoInSameLine> annoPathVariant[ $name ] )?
1961
- ;
1962
-
1963
- annoPath[ nameOrRef, category = 'annoref' ]
1964
- @finally{ this.attachLocation( $nameOrRef ); }
1965
- :
1966
- ( Id_all[ $category ] { $nameOrRef.path = [ this.identAst() ]; }
1967
- | at='@' // TODO: complain about spaces after?
1968
- Id_all[ $category ] { $nameOrRef.path = [ this.identAstWithPrefix( $at ) ]; }
1969
- )
1970
- (
1971
- '.'
1972
- ( Id_all[ $category ] { $nameOrRef.path.push( this.identAst() ); }
1973
- | at='@' // TODO: complain about spaces after?
1974
- Id_all[ $category ] { $nameOrRef.path.push( this.identAstWithPrefix( $at ) ); }
1975
- )
1976
- )*
1977
- annoPathVariant[ $nameOrRef ]?
1978
- ;
1979
-
1980
- annoPathVariant[ nameOrRef ]
1981
- @finally{ this.attachLocation( $nameOrRef.variant ); }
1982
- :
1983
- '#' { this.reportUnexpectedSpace(); }
1984
- Id_all['variant'] { $nameOrRef.variant = { path: [ this.identAst() ] }; }
1985
- (
1986
- '.' Id_all['variant'] { $nameOrRef.variant.path.push( this.identAst() ); }
1987
- )*
1988
- ;
1989
-
1990
- annoStructValue returns[ default value = {} ] locals[ name = new XsnName() ]
1991
- @finally{ $value.name = $name; }
1992
- :
1993
- annoPath[ $name, 'name' ] { this.attachLocation( $name ); }
1994
- ( ':'
1995
- $value=annoValue
1996
- |
1997
- { this.attachLocation( $value ); }
1998
- )
1999
- ;
2000
-
2001
- annoValue returns[ default value = {} ]
2002
- @finally{ this.attachLocation( $value ); }
2003
- :
2004
- $value=literalValue { this.adjustAnnoNumber( $value ); }
2005
- |
2006
- sign='+'/'-' Number
2007
- { this.adjustAnnoNumber( $value = this.numberLiteral( $sign ) ); }
2008
- |
2009
- annoPath[ $value, 'annoref' ]
2010
- |
2011
- '{'
2012
- {
2013
- if (!this.dynamic_.arrayAnno) $value.$flatten = [];
2014
- else { $value.struct = Object.create(null); $value.literal = 'struct'; }
2015
- }
2016
- (
2017
- // TOOL TODO → allow `<guard=…> '}'` below where the condition rejects `}`
2018
- // after `{` if top-level
2019
- sub=annoStructValue
2020
- {
2021
- if ($value.$flatten) $value.$flatten.push( $sub );
2022
- else this.addDef( $sub, $value, 'struct', null, $sub.name );
2023
- }
2024
- ( ',' | <exitLoop> | <guard=fail, repeatLoop, restrict=Id> )
2025
- // <guard=fail, repeatLoop>` for better error recovery for input `foo@bar`
2026
- )*
2027
- // TODO TOOL: allow:
2028
- // ( <guard=arrayAnno, …> '}' | <error> ) // TODO TOOL - workaround:
2029
- { this.ec( 'arrayAnno', 'orNotEmpty' ); } '}'
2030
- // Do NOT use <prepare=afterBrace> here!
2031
- |
2032
- '['<prepare=arrayAnno>
2033
- { $value.val = []; $value.literal = 'array' }
2034
- // no need for createArray() here, $value.location is set above
2035
- (
2036
- ( sub=annoValue { $value.val.push( $sub ) }
2037
- |
2038
- <guard=arrayAnno, arg=ellipsis> ellipsis='...'
2039
- ( UP TO upTo=annoValue
2040
- { $value.val.push( { literal: 'token', val: '...', location: $ellipsis.location, upTo: $upTo } ); }
2041
- | { $value.val.push( { literal: 'token', val: '...', location: $ellipsis.location } ); }
2042
- )
2043
- // TODO TOOL: if at last good state the command is ['g'],resume after the
2044
- // gotos, do not execute its actions - ?
2045
- // ( UP TO upTo=annoValue | { $upTo = undefined; } )
2046
- // { $value.val.push( { literal: 'token', val: '...', location: $ellipsis.location, upTo: $upTo } ); }
2047
- )
2048
- ( ',' | <exitLoop> )
2049
- )*
2050
- // TODO TOOL: allow ( <guard=arrayAnno, arg=bracket> ']' )
2051
- { this.ec( 'arrayAnno', 'bracket' ); }<always>
2052
- ']'
2053
- |
2054
- '(' $value=condition ')' { $value.$tokenTexts = this.ruleTokensText(); }
2055
- // TODO: (1,2,3) not supported, yet, only ((1,2,3))
2056
- ;
2057
-
2058
- // Shorten tokens array in this.gr() calls in top-level rules by reducing
2059
- // intersection follow set, see cap/redepage#97:
2060
- ignoredRule
2061
- options{ excludeRuleFrom = Parser }
2062
- :
2063
- usingDeclaration ';'
2064
- artifactDefOrExtend ';'
2065
- boundActionFunctionDef ';'
2066
- elementDef ';'
2067
- annotateBoundAction ';'
2068
- annotateElement ';'
2069
- elementDefOrExtend ';'
2070
- ;