@sap/cds-compiler 5.6.0 → 5.7.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.
- package/CHANGELOG.md +38 -0
- package/bin/cdsse.js +1 -0
- package/bin/cdsv2m.js +2 -1
- package/doc/Versioning.md +4 -4
- package/lib/api/options.js +1 -0
- package/lib/base/builtins.js +2 -2
- package/lib/base/dictionaries.js +1 -2
- package/lib/base/keywords.js +3 -1
- package/lib/base/lazyload.js +1 -1
- package/lib/base/message-registry.js +169 -144
- package/lib/base/messages.js +69 -59
- package/lib/base/model.js +3 -3
- package/lib/base/node-helpers.js +17 -16
- package/lib/base/optionProcessorHelper.js +13 -14
- package/lib/base/shuffle.js +4 -1
- package/lib/checks/structuredAnnoExpressions.js +1 -1
- package/lib/compiler/assert-consistency.js +1 -1
- package/lib/compiler/builtins.js +2 -1
- package/lib/compiler/extend.js +20 -5
- package/lib/compiler/resolve.js +45 -9
- package/lib/compiler/shared.js +1 -0
- package/lib/edm/annotations/edmJson.js +3 -3
- package/lib/edm/annotations/genericTranslation.js +5 -1
- package/lib/edm/annotations/vocabularyDefinitions.js +2 -2
- package/lib/edm/edmUtils.js +2 -1
- package/lib/gen/BaseParser.js +32 -32
- package/lib/gen/CdlParser.js +1526 -1488
- package/lib/json/from-csn.js +2 -0
- package/lib/json/to-csn.js +13 -4
- package/lib/language/docCommentParser.js +11 -5
- package/lib/language/errorStrategy.js +3 -3
- package/lib/language/genericAntlrParser.js +2 -0
- package/lib/model/csnUtils.js +6 -1
- package/lib/optionProcessor.js +5 -1
- package/lib/parsers/AstBuildingParser.js +161 -73
- package/lib/parsers/CdlGrammar.g4 +129 -85
- package/lib/parsers/Lexer.js +5 -3
- package/lib/parsers/index.js +1 -1
- package/lib/render/toCdl.js +6 -5
- package/lib/render/toHdbcds.js +1 -1
- package/lib/render/toSql.js +5 -3
- package/lib/render/utils/common.js +19 -6
- package/lib/render/utils/delta.js +1 -3
- package/lib/render/utils/standardDatabaseFunctions.js +576 -0
- package/lib/transform/addTenantFields.js +2 -1
- package/lib/transform/db/flattening.js +18 -77
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/rewriteCalculatedElements.js +14 -19
- package/lib/transform/db/temporal.js +2 -1
- package/lib/transform/odata/adaptAnnotationRefs.js +79 -0
- package/lib/transform/odata/createForeignKeys.js +4 -71
- package/lib/transform/odata/flattening.js +11 -1
- package/lib/transform/transformUtils.js +20 -85
- package/package.json +2 -1
- package/bin/cds_update_annotations.js +0 -180
|
@@ -29,6 +29,7 @@ tokens{ // reserved words
|
|
|
29
29
|
ALL, ANY, AS,
|
|
30
30
|
BY,
|
|
31
31
|
CASE, CAST,
|
|
32
|
+
DISTINCT, // not entirely necessary
|
|
32
33
|
EXISTS,
|
|
33
34
|
FALSE, FROM,
|
|
34
35
|
IN,
|
|
@@ -37,7 +38,7 @@ tokens{ // reserved words
|
|
|
37
38
|
OF, ON,
|
|
38
39
|
SELECT, SOME,
|
|
39
40
|
TRUE,
|
|
40
|
-
WHERE, WITH,
|
|
41
|
+
WHEN, WHERE, WITH,
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
// Top-level: USING, NAMESPACE, artifactDefOrExtend (start rule: start) ---------
|
|
@@ -45,22 +46,22 @@ tokens{ // reserved words
|
|
|
45
46
|
start returns[ source = new XsnSource( 'cdl' ) ]
|
|
46
47
|
:
|
|
47
48
|
(
|
|
48
|
-
( <
|
|
49
|
+
( <guard=namespaceRestriction> namespaceDeclaration[ $source ]
|
|
49
50
|
| usingDeclaration[ $source ]
|
|
50
51
|
| artifactDefOrExtend[ $source ] <prepare=namespaceRestriction>
|
|
51
52
|
)
|
|
52
|
-
( ';' | <exitLoop> | <repeatLoop,
|
|
53
|
+
( ';' | <exitLoop> | <repeatLoop, guard=afterBrace> { this.noAssignmentInSameLine(); } )
|
|
53
54
|
)*
|
|
54
55
|
EOF { this.docComment( null ); }
|
|
55
56
|
;
|
|
56
57
|
|
|
57
58
|
artifactsBlock[ art, start = undefined ]
|
|
58
59
|
:
|
|
59
|
-
'{'
|
|
60
|
+
'{'
|
|
60
61
|
{ $art.artifacts = this.createDict( $start ); $art.extensions = []; }
|
|
61
62
|
(
|
|
62
63
|
artifactDefOrExtend[ $art ]
|
|
63
|
-
( ';' | <exitLoop> | <repeatLoop,
|
|
64
|
+
( ';' | <exitLoop> | <repeatLoop, guard=afterBrace> { this.noAssignmentInSameLine(); } )
|
|
64
65
|
)*
|
|
65
66
|
'}'<prepare=afterBrace>
|
|
66
67
|
{ this.finalizeDictOrArray( $art.artifacts ); }
|
|
@@ -74,7 +75,7 @@ artifactDefOrExtend[ outer ] locals[ art = new XsnArtifact() ]
|
|
|
74
75
|
DEFINE?
|
|
75
76
|
( serviceDef[ $art, $outer ]
|
|
76
77
|
| contextDef[ $art, $outer ]
|
|
77
|
-
| <
|
|
78
|
+
| <guard=vocabularyRestriction> annotationDef[ $art, $outer ]
|
|
78
79
|
| typeDef[ $art, $outer ]
|
|
79
80
|
| aspectDef[ $art, $outer ]
|
|
80
81
|
| entityDef[ $art, $outer ]
|
|
@@ -84,7 +85,7 @@ artifactDefOrExtend[ outer ] locals[ art = new XsnArtifact() ]
|
|
|
84
85
|
| functionMainDef[ $art, $outer ]
|
|
85
86
|
)
|
|
86
87
|
|
|
|
87
|
-
<
|
|
88
|
+
<guard=extensionRestriction> EXTEND { $art.kind = 'extend'; }
|
|
88
89
|
( extendArtifact[ $art, $outer ]
|
|
89
90
|
| extendService[ $art, $outer ]
|
|
90
91
|
// Non-streamlined Syntax; we would neither add new clauses to them, nor
|
|
@@ -95,7 +96,7 @@ artifactDefOrExtend[ outer ] locals[ art = new XsnArtifact() ]
|
|
|
95
96
|
| <hide> extendProjection[ $art, $outer ]
|
|
96
97
|
)
|
|
97
98
|
|
|
|
98
|
-
<
|
|
99
|
+
<guard=extensionRestriction> ANNOTATE annotateArtifact[ $art, $outer ]
|
|
99
100
|
)
|
|
100
101
|
;
|
|
101
102
|
|
|
@@ -127,7 +128,7 @@ usingDeclaration[ source ] locals[ decl = { kind: 'using' } ] // TODO: XsnArtifa
|
|
|
127
128
|
'{' { $decl.usings = this.createArray(); }
|
|
128
129
|
( usingProxy[ $decl, { kind: 'using' } ]
|
|
129
130
|
( ',' | <exitLoop> )
|
|
130
|
-
)
|
|
131
|
+
)*
|
|
131
132
|
'}'<prepare=afterBrace>
|
|
132
133
|
{ this.finalizeDictOrArray( $decl.usings ); }
|
|
133
134
|
( FROM String
|
|
@@ -170,7 +171,8 @@ simplePath[ category = 'artref' ] returns[ default ref = {} ]
|
|
|
170
171
|
serviceDef[ art, outer ]
|
|
171
172
|
@finally{ this.attachLocation( $art ); }
|
|
172
173
|
:
|
|
173
|
-
SERVICE
|
|
174
|
+
SERVICE <prepare=vocabularyRestriction>
|
|
175
|
+
name=namePath[ 'Service' ]
|
|
174
176
|
{ this.addDef( $art, $outer, 'artifacts', 'service', $name ); }
|
|
175
177
|
{ this.docComment( $art ); } annoAssignMid[ $art ]*
|
|
176
178
|
artifactsBlock[ $art ]?
|
|
@@ -179,7 +181,8 @@ serviceDef[ art, outer ]
|
|
|
179
181
|
contextDef[ art, outer ]
|
|
180
182
|
@finally{ this.attachLocation( $art ); }
|
|
181
183
|
:
|
|
182
|
-
CONTEXT
|
|
184
|
+
CONTEXT <prepare=vocabularyRestriction>
|
|
185
|
+
name=namePath[ 'Context' ]
|
|
183
186
|
{ this.addDef( $art, $outer, 'artifacts', 'context', $name ); }
|
|
184
187
|
{ this.docComment( $art ); } annoAssignMid[ $art ]*
|
|
185
188
|
artifactsBlock[ $art ]?
|
|
@@ -189,7 +192,7 @@ annotationDef[ art, outer ]
|
|
|
189
192
|
@finally{ this.attachLocation( $art ); }
|
|
190
193
|
:
|
|
191
194
|
ANNOTATION name=namePath[ 'AnnoDef' ]
|
|
192
|
-
// make it also work with ignored <
|
|
195
|
+
// make it also work with ignored <guard=vocabularyRestriction>:
|
|
193
196
|
{ this.addDef( $art, $outer, ($outer.kind === 'source' ? 'vocabularies' : 'artifacts'), 'annotation', $name ); }
|
|
194
197
|
{ this.docComment( $art ); } annoAssignMid[ $art ]*
|
|
195
198
|
typeOrIncludesSpec[ $art ]
|
|
@@ -232,7 +235,8 @@ aspectDef[ art, outer ]
|
|
|
232
235
|
entityDef[ art, outer ]
|
|
233
236
|
@finally{ this.attachLocation( $art ); }
|
|
234
237
|
:
|
|
235
|
-
ENTITY
|
|
238
|
+
ENTITY<prepare=afterBrace> // enable special
|
|
239
|
+
name=namePath[ 'Entity' ]
|
|
236
240
|
{ this.addDef( $art, $outer, 'artifacts', 'entity', $name ); }
|
|
237
241
|
{ this.docComment( $art ); } annoAssignMid[ $art ]*
|
|
238
242
|
paramsList[ $art ]?
|
|
@@ -254,6 +258,14 @@ entityDef[ art, outer ]
|
|
|
254
258
|
{ $art.query = $query; $art.$syntax = 'projection'; }
|
|
255
259
|
whereGroupByHaving[ $query ]?
|
|
256
260
|
orderByLimitOffset[ $query ]?
|
|
261
|
+
{ if (this.lb().type !== '}' && ![ ';', '}', 'EOF' ].includes( this.l() ) && this.la().keyword !== 'actions') {
|
|
262
|
+
// not worth to nicer location here - is standard error in v6
|
|
263
|
+
this.warning( 'syntax-missing-proj-semicolon', this.la(),
|
|
264
|
+
{ expecting: [ "';'" ], offending: this.antlrName( this.la() ) },
|
|
265
|
+
'Missing $(EXPECTING) before $(OFFENDING)');
|
|
266
|
+
} }
|
|
267
|
+
<prepare=afterBrace> // disable special loop-exit, allow no `;`
|
|
268
|
+
// TODO v6: these <prepare=afterBrace>s are extremely strange
|
|
257
269
|
)
|
|
258
270
|
)
|
|
259
271
|
actionsBlock[ $art ]?
|
|
@@ -336,7 +348,7 @@ actionsBlock[ art ]
|
|
|
336
348
|
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
337
349
|
(
|
|
338
350
|
boundActionFunctionDef[ $art ]
|
|
339
|
-
( ';' | <exitLoop> | <repeatLoop,
|
|
351
|
+
( ';' | <exitLoop> | <repeatLoop, guard=afterBrace> { this.noAssignmentInSameLine(); } )
|
|
340
352
|
)*
|
|
341
353
|
'}'<prepare=afterBrace>
|
|
342
354
|
{ this.finalizeDictOrArray( $art.actions ); }
|
|
@@ -402,7 +414,7 @@ elementsBlock[ art ]
|
|
|
402
414
|
( elementDef[ $art ]
|
|
403
415
|
( ';'
|
|
404
416
|
| <exitLoop>
|
|
405
|
-
| <repeatLoop,
|
|
417
|
+
| <repeatLoop, guard=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); }
|
|
406
418
|
)
|
|
407
419
|
)*
|
|
408
420
|
'}'<prepare=afterBrace>
|
|
@@ -429,14 +441,14 @@ elementDef[ outer, art = undefined ]
|
|
|
429
441
|
':' typeExpression[ $art ] // includes DEFAULT
|
|
430
442
|
)?
|
|
431
443
|
(
|
|
432
|
-
<
|
|
444
|
+
<guard=elementRestriction, arg=calc> '='
|
|
433
445
|
// TODO TOOL: add to "expected set" if failing here? Or have some "do not
|
|
434
446
|
// consider for rule exit if condition failure on `=`"?
|
|
435
447
|
expr=expression { $art.value = $expr; }
|
|
436
448
|
( STORED { $art.value.stored = this.valueWithLocation( true ); } )?
|
|
437
449
|
// TODO: why have `stored` as property of the value?
|
|
438
|
-
{ if (this.elementRestriction( true, 'anno' )) this.docComment( $art ); }
|
|
439
|
-
( <
|
|
450
|
+
{ if (!this.elementRestriction( true, 'anno' )) this.docComment( $art ); }
|
|
451
|
+
( <guard=elementRestriction, arg=anno> annoAssignStd[ $art ] )*
|
|
440
452
|
)?
|
|
441
453
|
;
|
|
442
454
|
|
|
@@ -507,7 +519,7 @@ mixinElementDef[ outer ] locals[ art = new XsnArtifact() ]
|
|
|
507
519
|
( assoc=ASSOCIATION cardinality[ $art ]? TO
|
|
508
520
|
| assoc=COMPOSITION cardinality[ $art ]? OF
|
|
509
521
|
)
|
|
510
|
-
( <
|
|
522
|
+
( <guard=noRepeatedCardinality> card=ONE/MANY )?
|
|
511
523
|
target=simplePath
|
|
512
524
|
{ this.setAssocAndComposition( $art, $assoc, $card, $target ); }
|
|
513
525
|
ON expr=condition { $art.on = $expr; }
|
|
@@ -527,7 +539,7 @@ annotateArtifact[ art, outer ]
|
|
|
527
539
|
annotateElementsBlock[ $art ]?
|
|
528
540
|
| // definition annotation
|
|
529
541
|
keyword=WITH?
|
|
530
|
-
// <
|
|
542
|
+
// <guard=noRuleExitAfterWith>), or as rule option,
|
|
531
543
|
// this.noSemicolonHere() had the issues: DocComment, before `}`/EOF
|
|
532
544
|
{ this.addExtension( $art, $outer, 'annotate', $name ); }
|
|
533
545
|
{ this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
@@ -598,7 +610,7 @@ extendService[ art, outer ]
|
|
|
598
610
|
name=namePath[ 'ExtService' ]
|
|
599
611
|
{ $art.name = $name; $outer.extensions.push( $art ); }
|
|
600
612
|
keyword=WITH?
|
|
601
|
-
// <
|
|
613
|
+
// <guard=noRuleExitAfterWith>), or as rule option,
|
|
602
614
|
// this.noSemicolonHere() had the issues: DocComment, before `}`/EOF
|
|
603
615
|
{ this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
604
616
|
artifactsBlock[ $art ]?
|
|
@@ -611,7 +623,7 @@ extendContext[ art, outer ]
|
|
|
611
623
|
name=namePath[ 'ExtContext' ]
|
|
612
624
|
{ $art.name = $name; $outer.extensions.push( $art ); }
|
|
613
625
|
keyword=WITH?
|
|
614
|
-
// <
|
|
626
|
+
// <guard=noRuleExitAfterWith>), or as rule option,
|
|
615
627
|
// this.noSemicolonHere() had the issues: DocComment, before `}`/EOF
|
|
616
628
|
{ this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
617
629
|
artifactsBlock[ $art ]?
|
|
@@ -681,7 +693,7 @@ annotateActionsBlock[ art ]
|
|
|
681
693
|
:
|
|
682
694
|
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
683
695
|
( annotateBoundAction[ $art ]
|
|
684
|
-
( ';' | <exitLoop> | <repeatLoop,
|
|
696
|
+
( ';' | <exitLoop> | <repeatLoop, guard=afterBrace> { this.noAssignmentInSameLine(); } )
|
|
685
697
|
)*
|
|
686
698
|
'}'<prepare=afterBrace>
|
|
687
699
|
{ this.finalizeExtensionsDict( $art.actions ); }
|
|
@@ -732,7 +744,7 @@ annotateElementsBlock[ art ]
|
|
|
732
744
|
( annotateElement[ $art ]
|
|
733
745
|
( ';'
|
|
734
746
|
| <exitLoop>
|
|
735
|
-
| <repeatLoop,
|
|
747
|
+
| <repeatLoop, guard=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); }
|
|
736
748
|
)
|
|
737
749
|
)*
|
|
738
750
|
'}'<prepare=afterBrace>
|
|
@@ -755,7 +767,7 @@ extendElementsBlock[ art, start = undefined ]
|
|
|
755
767
|
( elementDefOrExtend[ $art ]
|
|
756
768
|
( ';'
|
|
757
769
|
| <exitLoop>
|
|
758
|
-
| <repeatLoop,
|
|
770
|
+
| <repeatLoop, guard=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); } )
|
|
759
771
|
)*
|
|
760
772
|
'}'<prepare=afterBrace>
|
|
761
773
|
{ this.finalizeExtensionsDict( $art.elements ); }
|
|
@@ -838,7 +850,7 @@ typeOrIncludesSpec[ art ]
|
|
|
838
850
|
// annotations are attached to the element, the type properties to the line type
|
|
839
851
|
//
|
|
840
852
|
// If used in a definition with additional clauses (currently just `= expr` for
|
|
841
|
-
// elements), these clauses must be guarded with <
|
|
853
|
+
// elements), these clauses must be guarded with <guard=…>.
|
|
842
854
|
//
|
|
843
855
|
// This rule is for element, type, (input and `returns`) parameter and annotation
|
|
844
856
|
// definitions. It is not used when the type expression is restricted: CDL-style
|
|
@@ -849,15 +861,15 @@ typeExpression[ art ]
|
|
|
849
861
|
( typeRefOptArgs[ $art ] | typeTypeOf[ $art ] )
|
|
850
862
|
(<altRuleStart>)
|
|
851
863
|
{ this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
852
|
-
( <
|
|
864
|
+
( <guard=elementRestriction, arg=notNull> nullability[ $art ]
|
|
853
865
|
{ this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
854
866
|
)?
|
|
855
867
|
( enumSymbolsBlock[ $art ] <prepare=elementRestriction, arg=anno>
|
|
856
|
-
( <
|
|
857
|
-
( <
|
|
868
|
+
( <guard=elementRestriction, arg=notNull> nullability[ $art ] )?
|
|
869
|
+
( <guard=elementRestriction, arg=default>
|
|
858
870
|
DEFAULT expr=expression { $art.default = $expr; }
|
|
859
871
|
)?
|
|
860
|
-
( <
|
|
872
|
+
( <guard=elementRestriction, arg=notNull> nullability[ $art ] )?
|
|
861
873
|
| typeProperties[ $art ]
|
|
862
874
|
)?
|
|
863
875
|
|
|
|
@@ -868,12 +880,12 @@ typeExpression[ art ]
|
|
|
868
880
|
|
|
|
869
881
|
assoc=ASSOCIATION <prepare=elementRestriction, arg=calc>
|
|
870
882
|
cardinality[ $art ]? TO
|
|
871
|
-
( <
|
|
883
|
+
( <guard=noRepeatedCardinality> card=ONE/MANY )?
|
|
872
884
|
typeAssocProperties[ $art, $assoc, $card ]
|
|
873
885
|
|
|
|
874
886
|
assoc=COMPOSITION <prepare=elementRestriction, arg=calc>
|
|
875
887
|
cardinality[ $art ]? OF
|
|
876
|
-
( <
|
|
888
|
+
( <guard=noRepeatedCardinality> card=ONE/MANY )?
|
|
877
889
|
( typeAssocProperties[ $art, $assoc, $card ]
|
|
878
890
|
| elementsBlock[ this.setAssocAndComposition( $art, $assoc, $card ) ]
|
|
879
891
|
{ $art.target.location = $art.target.elements[Symbol.for('cds.$location')]; }
|
|
@@ -886,7 +898,7 @@ typeExpression[ art ]
|
|
|
886
898
|
) // no anno assignments, except to end type expression
|
|
887
899
|
(
|
|
888
900
|
( typeRefOptArgs[ $art.items ] | typeTypeOf[ $art.items ] )
|
|
889
|
-
( <
|
|
901
|
+
( <guard=elementRestriction, arg=notNull> nullability[ $art.items ] )?
|
|
890
902
|
( { this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
891
903
|
{ ; } <exitRule> // TODO TOOL: make it work without workaround { ; }
|
|
892
904
|
// TODO TOOL: investigate why simply `{} <exitRule>` is ignored
|
|
@@ -895,7 +907,7 @@ typeExpression[ art ]
|
|
|
895
907
|
|
|
|
896
908
|
elementsBlock[ $art.items ]
|
|
897
909
|
)
|
|
898
|
-
( <
|
|
910
|
+
( <guard=elementRestriction, arg=notNull> nullability[ $art.items ] )?
|
|
899
911
|
|
|
|
900
912
|
elementsBlock[ $art ] <prepare=elementRestriction, arg=calc>
|
|
901
913
|
nullability[ $art ]?
|
|
@@ -918,16 +930,17 @@ typeProperties[ art ]
|
|
|
918
930
|
(
|
|
919
931
|
annoAssignStd[ $art ]
|
|
920
932
|
|
|
|
921
|
-
<
|
|
933
|
+
<guard=elementRestriction, arg=notNull>
|
|
922
934
|
nullability[ $art ] { this.docComment( $art ); }
|
|
923
935
|
|
|
|
924
|
-
<
|
|
936
|
+
<guard=elementRestriction, arg=default>
|
|
925
937
|
DEFAULT expr=expression { $art.default = $expr; this.docComment( $art ); }
|
|
926
938
|
)+
|
|
927
939
|
;
|
|
928
940
|
|
|
929
941
|
|
|
930
942
|
typeTypeOf[ art ] locals[ location ]
|
|
943
|
+
@after{ this.attachLocation( $art.type ); }
|
|
931
944
|
:
|
|
932
945
|
TYPE OF { location = this.locationOfPrevTokens( 2 ); }
|
|
933
946
|
type=simplePath[ 'ref' ] { $art.type = $type; }
|
|
@@ -954,6 +967,7 @@ typeRefOptArgs[ art ] locals[ type = $art.type ]
|
|
|
954
967
|
(
|
|
955
968
|
'.' Id_all['ref'] { $type.path.push( this.identAst() ); }
|
|
956
969
|
)*
|
|
970
|
+
{ this.attachLocation( $art.type ); }
|
|
957
971
|
|
|
|
958
972
|
open='('
|
|
959
973
|
(
|
|
@@ -1058,12 +1072,11 @@ queryEOF returns[ query ]
|
|
|
1058
1072
|
projectionSpec returns[ default query = {} ]
|
|
1059
1073
|
@finally{ this.attachLocation($query); }
|
|
1060
1074
|
:
|
|
1061
|
-
|
|
1062
|
-
|
|
1075
|
+
PROJECTION
|
|
1076
|
+
<prepare=afterBrace, arg=entity> // enable special loop-exit, TODO v6 delete
|
|
1063
1077
|
{ $query = { op: this.valueWithLocation( 'SELECT' ) }; }
|
|
1064
1078
|
ON
|
|
1065
|
-
tab=fromRefWithOptAlias
|
|
1066
|
-
// TODO: this <prepare=afterBrace> is extremely strange... v6 forbid.
|
|
1079
|
+
tab=fromRefWithOptAlias
|
|
1067
1080
|
{ $query.from = tab; }
|
|
1068
1081
|
selectItemsList[ $query ]?
|
|
1069
1082
|
excludingClause[ $query ]?
|
|
@@ -1087,7 +1100,7 @@ queryExpression returns[ default expr = {} ] locals[ op, quantifier ]
|
|
|
1087
1100
|
)
|
|
1088
1101
|
query=queryExpression
|
|
1089
1102
|
// with same op/quantifier: make left-assoc binary to nary:
|
|
1090
|
-
{ if ($expr.$parens || $op.val !== $expr.op.val || $quantifier?.val !== $expr.quantifier?.val) $expr = { op, args: [$expr], quantifier }; }
|
|
1103
|
+
{ if ($expr.$parens || $op.val !== $expr.op.val || $quantifier?.val !== $expr.quantifier?.val) $expr = { op, args: [$expr], quantifier, location: { ...$.expr.location } }; } // TODO: ...$
|
|
1091
1104
|
{ $quantifier = undefined; }
|
|
1092
1105
|
{ $expr.args.push( $query ); this.attachLocation( $expr ); }
|
|
1093
1106
|
)*
|
|
@@ -1180,16 +1193,16 @@ tableOrQueryParens returns[ default expr ]
|
|
|
1180
1193
|
'(' <prepare=queryOnLeft>
|
|
1181
1194
|
( <prefer> tableOrQueryParens[ ...$ ]
|
|
1182
1195
|
( tableExpression[ ...$ ]<atAltStart, prepare=queryOnLeft, arg=table>
|
|
1183
|
-
| <
|
|
1196
|
+
| <guard=queryOnLeft> queryExpression[ ...$ ]<atAltStart>
|
|
1184
1197
|
)?
|
|
1185
1198
|
| tableExpression[ ...$ ] <prepare=queryOnLeft, arg=table>
|
|
1186
1199
|
| queryExpression[ ...$ ]
|
|
1187
1200
|
)
|
|
1188
1201
|
')'
|
|
1189
1202
|
{ this.surroundByParens( $expr ); }
|
|
1190
|
-
( <
|
|
1203
|
+
( <guard=queryOnLeft, arg=table> AS Id['FromAlias']
|
|
1191
1204
|
{ $expr = this.taggedIfQuery( $expr ); $expr.name = this.identAst(); }
|
|
1192
|
-
| <
|
|
1205
|
+
| <guard=queryOnLeft, arg=tableWithoutAs> Id_restricted['FromAlias']
|
|
1193
1206
|
// TODO TOOL: shouldn't we have generated `default: this.giR()`?
|
|
1194
1207
|
{ $expr = this.taggedIfQuery( $expr ); $expr.name = this.fragileAlias(); }
|
|
1195
1208
|
)?
|
|
@@ -1228,7 +1241,7 @@ fromRefWithOptAlias returns[ default expr = {} ]
|
|
|
1228
1241
|
(
|
|
1229
1242
|
AS Id['FromAlias'] { $expr.name = this.identAst(); }
|
|
1230
1243
|
|
|
|
1231
|
-
// <
|
|
1244
|
+
// <guard=tableWithoutAs> not necessary, tool uses `default: this.giR()`
|
|
1232
1245
|
Id_restricted['FromAlias']
|
|
1233
1246
|
{ $expr.name = this.fragileAlias(); }
|
|
1234
1247
|
|
|
|
@@ -1243,7 +1256,7 @@ fromPath[ table, category ] locals[ pathItem ]
|
|
|
1243
1256
|
Id[ $category ] { $table.path.push( $pathItem = this.identAst() ); }
|
|
1244
1257
|
( fromArgumentsAndFilter[ $pathItem ] { $pathItem = null; } )?
|
|
1245
1258
|
(
|
|
1246
|
-
<
|
|
1259
|
+
<guard=notAfterEntityArgOrFilter> // TODO TOOL: allow <hide=method>
|
|
1247
1260
|
'.' { if (!$pathItem && !$table.scope) {
|
|
1248
1261
|
$table.scope = $table.path.length; $category = 'ref';
|
|
1249
1262
|
this.warning( 'syntax-invalid-path-separator', this.lb(),
|
|
@@ -1277,7 +1290,7 @@ fromNamedArgument[ pathStep ]
|
|
|
1277
1290
|
cardinalityAndFilter[ pathStep ]
|
|
1278
1291
|
:
|
|
1279
1292
|
'['
|
|
1280
|
-
( <
|
|
1293
|
+
( <guard=beforeColon> Number // TODO: only allow `1`?
|
|
1281
1294
|
{ $pathStep.cardinality = { targetMax: this.unsignedIntegerLiteral(), location: this.lb().location }; }
|
|
1282
1295
|
':'
|
|
1283
1296
|
)?
|
|
@@ -1341,7 +1354,7 @@ selectItemsList[ query, start = undefined ]
|
|
|
1341
1354
|
|
|
1342
1355
|
nestedSelectItemsList[ query, clause ]
|
|
1343
1356
|
:
|
|
1344
|
-
'{'<prepare=inSelectItem
|
|
1357
|
+
'{'<prepare=inSelectItem>
|
|
1345
1358
|
{ $query[$clause] = this.createArray(); }
|
|
1346
1359
|
(
|
|
1347
1360
|
( '*' { $query[$clause].push( this.valueWithLocation() ); }
|
|
@@ -1353,43 +1366,72 @@ nestedSelectItemsList[ query, clause ]
|
|
|
1353
1366
|
{ this.finalizeDictOrArray( $query[$clause] ); }
|
|
1354
1367
|
;
|
|
1355
1368
|
|
|
1356
|
-
selectItemDef[ columns ] locals[ art = new XsnArtifact()
|
|
1369
|
+
selectItemDef[ columns ] locals[ art = new XsnArtifact() ]
|
|
1357
1370
|
@finally{ this.attachLocation( $art ); }
|
|
1358
1371
|
:
|
|
1359
1372
|
{ $columns.push( $art ); } // TODO: probably too early
|
|
1360
1373
|
{ this.docComment( $art ); } annoAssignCol[ $art ]*
|
|
1361
|
-
( <
|
|
1374
|
+
( <guard=modifierRestriction> VIRTUAL
|
|
1362
1375
|
{ $art.virtual = this.valueWithLocation( true ); }
|
|
1363
1376
|
)?
|
|
1364
1377
|
{;} <prepare=columnExpr, arg=key> // TOOL TODO: disappears without {;}
|
|
1365
|
-
( <
|
|
1378
|
+
( <guard=modifierRestriction> KEY
|
|
1366
1379
|
{ $art.key = this.valueWithLocation( true ); }
|
|
1367
1380
|
)?
|
|
1381
|
+
// TODO: we might have an extra rule for column expression...
|
|
1368
1382
|
(
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1383
|
+
(
|
|
1384
|
+
expr=expression { $art.value = $expr; }
|
|
1385
|
+
( AS Id['ItemAlias'] { $art.name = this.identAst(); }
|
|
1386
|
+
| Id_restricted['ItemAlias'] { $art.name = this.fragileAlias( true ); }
|
|
1387
|
+
)?
|
|
1373
1388
|
|
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
{ this.reportUnexpectedSpace( this.lb(), this.la().location, true ); } // TODO: no ERR
|
|
1377
|
-
{ this.reportExpandInline( $art, $as || true ); }
|
|
1378
|
-
// no extra 'syntax-unexpected-alias' anymore,
|
|
1379
|
-
// 'syntax-unexpected-anno' reported in define.js
|
|
1380
|
-
{ if ($alias) $alias.token.parsedAs = $alias.parsedAs; }
|
|
1389
|
+
<prefer>
|
|
1390
|
+
expr=valuePath { $expr = this.valuePathAst( $expr ); $art.value = $expr; }
|
|
1381
1391
|
(
|
|
1382
|
-
|
|
1383
|
-
|
|
1392
|
+
(
|
|
1393
|
+
OVER { this.pushXprToken( $expr.suffix = [] ); }
|
|
1394
|
+
overClause[ $expr.suffix ]
|
|
1395
|
+
( e=expression[ ...{ expr: $expr } ]<atAltStart>
|
|
1396
|
+
{ Object.assign( $e.location, $expr.location ); $art.value = this.attachLocation( $e )}
|
|
1397
|
+
)?
|
|
1398
|
+
|
|
|
1399
|
+
e=expression[ ...{ expr: $expr } ]<atAltStart>
|
|
1400
|
+
{ Object.assign( $e.location, $expr.location ); $art.value = this.attachLocation( $e )}
|
|
1401
|
+
)
|
|
1402
|
+
( AS Id['ItemAlias'] { $art.name = this.identAst(); }
|
|
1403
|
+
| Id_restricted['ItemAlias'] { $art.name = this.fragileAlias( true ); }
|
|
1404
|
+
)?
|
|
1405
|
+
| // includes empty
|
|
1406
|
+
AS Id['ItemAlias'] <prepare=nestedExpand> { $art.name = this.identAst(); }
|
|
1407
|
+
|
|
|
1408
|
+
Id_restricted['ItemAlias'] <prepare=nestedExpand>
|
|
1409
|
+
{ $art.name = this.fragileAlias( true ); }
|
|
1384
1410
|
|
|
|
1385
|
-
|
|
1411
|
+
{ this.classifyImplicitName( 'ItemImplicit', $expr ); } <prepare=nestedExpand>
|
|
1412
|
+
// TODO TOOL: action in default does not work in embedded action
|
|
1413
|
+
|
|
|
1414
|
+
// TODO: guard instead reportExpandInline if valuePath is function?
|
|
1415
|
+
'.'
|
|
1416
|
+
{ this.reportUnexpectedSpace( this.lb(), this.la().location, true ); } // TODO: no ERR
|
|
1417
|
+
{ this.reportExpandInline( $art, true ); }
|
|
1418
|
+
// no extra 'syntax-unexpected-alias' anymore,
|
|
1419
|
+
// 'syntax-unexpected-anno' reported in define.js
|
|
1420
|
+
(
|
|
1421
|
+
nestedSelectItemsList[ $art, 'inline' ]
|
|
1422
|
+
excludingClause[ $art ]?
|
|
1423
|
+
|
|
|
1424
|
+
'*' { $art.inline = [ this.valueWithLocation() ]; }
|
|
1425
|
+
)
|
|
1426
|
+
<exitRule>
|
|
1386
1427
|
)
|
|
1387
|
-
<exitRule>
|
|
1388
1428
|
)
|
|
1389
|
-
//
|
|
1429
|
+
// expand is only allowed only after valuePath, but error recovery is poor
|
|
1430
|
+
// if the following is moved up to <prepare=nestedExpand>
|
|
1390
1431
|
(
|
|
1391
|
-
// TODO: guard
|
|
1392
|
-
|
|
1432
|
+
// TODO: make guard handle that if valuePath might be function?
|
|
1433
|
+
<guard=nestedExpand>
|
|
1434
|
+
{ if (!this.nestedExpand(true)) this.reportExpandInline( $art, false ); }
|
|
1393
1435
|
nestedSelectItemsList[ $art, 'expand' ]
|
|
1394
1436
|
excludingClause[ $art ]?
|
|
1395
1437
|
)?
|
|
@@ -1400,7 +1442,7 @@ selectItemDef[ columns ] locals[ art = new XsnArtifact(), alias ]
|
|
|
1400
1442
|
)
|
|
1401
1443
|
{ this.docComment( $art ); } <prepare=columnExpr> annoAssignMid[ $art ]*
|
|
1402
1444
|
(
|
|
1403
|
-
':'
|
|
1445
|
+
':' // TODO: guard? currently not with expand ?
|
|
1404
1446
|
(
|
|
1405
1447
|
( typeTypeOf[ $art ]
|
|
1406
1448
|
| ( LOCALIZED { $art.localized = this.valueWithLocation( true ); } )?
|
|
@@ -1413,14 +1455,14 @@ selectItemDef[ columns ] locals[ art = new XsnArtifact(), alias ]
|
|
|
1413
1455
|
| foreignKeysBlock[ $art ]
|
|
1414
1456
|
)?
|
|
1415
1457
|
|
|
|
1416
|
-
( <
|
|
1458
|
+
( <guard=columnExpr> // arg=singleId
|
|
1417
1459
|
assoc=ASSOCIATION { this.associationInSelectItem( $art ); }
|
|
1418
1460
|
cardinality[ $art ]? TO
|
|
1419
|
-
| <
|
|
1461
|
+
| <guard=columnExpr> // arg=singleId
|
|
1420
1462
|
assoc=COMPOSITION { this.associationInSelectItem( $art ); }
|
|
1421
1463
|
cardinality[ $art ]? OF
|
|
1422
1464
|
)
|
|
1423
|
-
( <
|
|
1465
|
+
( <guard=noRepeatedCardinality> card=ONE/MANY )?
|
|
1424
1466
|
target=simplePath
|
|
1425
1467
|
{ this.setAssocAndComposition( $art, $assoc, $card, $target ); }
|
|
1426
1468
|
ON expr=condition { $art.on = $expr; }
|
|
@@ -1485,7 +1527,7 @@ valuePath returns[ default expr = { path: [] } ] locals[ pathItem ]
|
|
|
1485
1527
|
( argumentsAndFilter[ $pathItem ] )?
|
|
1486
1528
|
(<altRuleStart>)
|
|
1487
1529
|
(
|
|
1488
|
-
<
|
|
1530
|
+
<guard=isDotForPath> '.'
|
|
1489
1531
|
Id_all['ref'] { $expr.path.push( $pathItem = this.identAst() ); }
|
|
1490
1532
|
( argumentsAndFilter[ $pathItem ] { $pathItem = null; } )?
|
|
1491
1533
|
)*
|
|
@@ -1571,7 +1613,7 @@ expression returns[ default expr = {} ]
|
|
|
1571
1613
|
| <prec=10, postfix=once> IS { $expr = this.applyOpToken( $expr ); }
|
|
1572
1614
|
( NOT { this.pushXprToken( $expr ); } )?
|
|
1573
1615
|
NULL { this.pushXprToken( $expr ); }
|
|
1574
|
-
| ( <arg=10,
|
|
1616
|
+
| ( <arg=10, guard=isNegatedRelation> NOT { $expr = this.applyOpToken( $expr ); }
|
|
1575
1617
|
// TODO: condition, because there might be NOT NULL after DEFAULT expression
|
|
1576
1618
|
| <prec=10, postfix=once>
|
|
1577
1619
|
{ $expr = { op: { val: 'ixpr', location: this.la().location }, args: [ $expr ] }; }
|
|
@@ -1601,7 +1643,7 @@ expressionOrQueryParens returns[ default expr ]
|
|
|
1601
1643
|
( expression[ ...$ ]<atAltStart, prepare=queryOnLeft, arg=expr>
|
|
1602
1644
|
continueExpressionslist[ ...$ ]?
|
|
1603
1645
|
| continueExpressionslist[ ...$ ] <prepare=queryOnLeft, arg=expr>
|
|
1604
|
-
| <
|
|
1646
|
+
| <guard=queryOnLeft> queryExpression[ ...$ ]<atAltStart>
|
|
1605
1647
|
)?
|
|
1606
1648
|
| expression[ ...$ ] <prepare=queryOnLeft, arg=expr>
|
|
1607
1649
|
continueExpressionslist[ ...$ ]?
|
|
@@ -1688,7 +1730,7 @@ options{ minTokensMatched = 1 }
|
|
|
1688
1730
|
// TODO: if we want perfect CC and error recovery, `isNamedArg` would work
|
|
1689
1731
|
// like keyword prediction and also do something similar to the (future)
|
|
1690
1732
|
// "identifier confirmation prediction" (consider argument `(par ‡ : 42)`
|
|
1691
|
-
<
|
|
1733
|
+
<guard=isNamedArg> id=Id_all['paramname']
|
|
1692
1734
|
(
|
|
1693
1735
|
':' { $pathStep.args = this.createDict( $open ); $pathStep.$syntax = ':'; }
|
|
1694
1736
|
expr=expression { this.addNamedArg( $pathStep, $id, $expr ); }
|
|
@@ -1829,7 +1871,7 @@ literalValue returns[ default expr = {} ]
|
|
|
1829
1871
|
:
|
|
1830
1872
|
// TODO: remove from this rule (not in enum! `String enum { foo = #bar }`) ?
|
|
1831
1873
|
'#' { this.reportUnexpectedSpace(); }
|
|
1832
|
-
|
|
1874
|
+
Id_all['enumref']
|
|
1833
1875
|
{ $expr = { literal: 'enum', sym: this.identAst() } }
|
|
1834
1876
|
|
|
|
1835
1877
|
NULL
|
|
@@ -1887,6 +1929,7 @@ annoAssignParen[ art ]
|
|
|
1887
1929
|
:
|
|
1888
1930
|
'('<prepare=annoInSameLine>
|
|
1889
1931
|
( annoAssignBase[ $art ]
|
|
1932
|
+
//( annoAssignErrorRecoveryHelper )?
|
|
1890
1933
|
( ',' | <exitLoop> )
|
|
1891
1934
|
)*
|
|
1892
1935
|
')'
|
|
@@ -1896,7 +1939,7 @@ annoAssignBase[ art ] locals[ value = {} ]
|
|
|
1896
1939
|
@finally{ this.assignAnnotation( $art, $value, $name || {} ); }
|
|
1897
1940
|
:
|
|
1898
1941
|
name=annoNamePath
|
|
1899
|
-
( <
|
|
1942
|
+
( <guard=annoInSameLine> ':' value=annoValue )?
|
|
1900
1943
|
;
|
|
1901
1944
|
|
|
1902
1945
|
annoNamePath[ category = 'anno' ] returns[ default name = new XsnName() ]
|
|
@@ -1911,7 +1954,7 @@ annoNamePath[ category = 'anno' ] returns[ default name = new XsnName() ]
|
|
|
1911
1954
|
Id_all[ $category ] { $name.path.push( this.identAstWithPrefix( $at ) ); }
|
|
1912
1955
|
)
|
|
1913
1956
|
)*
|
|
1914
|
-
( <
|
|
1957
|
+
( <guard=annoInSameLine> annoPathVariant[ $name ] )?
|
|
1915
1958
|
;
|
|
1916
1959
|
|
|
1917
1960
|
annoPath[ nameOrRef, category = 'annoref' ]
|
|
@@ -1969,17 +2012,18 @@ annoValue returns[ default value = {} ]
|
|
|
1969
2012
|
}
|
|
1970
2013
|
(
|
|
1971
2014
|
// TODO: complain about empty loop if top-level as before
|
|
1972
|
-
// TOOL TODO → allow `<
|
|
2015
|
+
// TOOL TODO → allow `<guard=…> '}'` below where the condition rejects `}`
|
|
1973
2016
|
// after `{` if top-level
|
|
1974
2017
|
sub=annoStructValue
|
|
1975
2018
|
{
|
|
1976
2019
|
if ($value.$flatten) $value.$flatten.push( $sub );
|
|
1977
2020
|
else this.addDef( $sub, $value, 'struct', null, $sub.name );
|
|
1978
2021
|
}
|
|
1979
|
-
( ',' | <exitLoop> )
|
|
2022
|
+
( ',' | <exitLoop> | <guard=fail, repeatLoop, restrict=Id> )
|
|
2023
|
+
// <guard=fail, repeatLoop>` for better error recovery for input `foo@bar`
|
|
1980
2024
|
)*
|
|
1981
2025
|
// TODO TOOL: allow:
|
|
1982
|
-
// ( <
|
|
2026
|
+
// ( <guard=arrayAnno, …> '}' | <error> ) // TODO TOOL - workaround:
|
|
1983
2027
|
{ this.ec( 'arrayAnno', 'orNotEmpty' ); } '}'
|
|
1984
2028
|
// Do NOT use <prepare=afterBrace> here!
|
|
1985
2029
|
|
|
|
@@ -1989,7 +2033,7 @@ annoValue returns[ default value = {} ]
|
|
|
1989
2033
|
(
|
|
1990
2034
|
( sub=annoValue { $value.val.push( $sub ) }
|
|
1991
2035
|
|
|
|
1992
|
-
<
|
|
2036
|
+
<guard=arrayAnno, arg=ellipsis> ellipsis='...'
|
|
1993
2037
|
( UP TO upTo=annoValue
|
|
1994
2038
|
{ $value.val.push( { literal: 'token', val: '...', location: $ellipsis.location, upTo: $upTo } ); }
|
|
1995
2039
|
| { $value.val.push( { literal: 'token', val: '...', location: $ellipsis.location } ); }
|
|
@@ -2001,7 +2045,7 @@ annoValue returns[ default value = {} ]
|
|
|
2001
2045
|
)
|
|
2002
2046
|
( ',' | <exitLoop> )
|
|
2003
2047
|
)*
|
|
2004
|
-
// TODO TOOL: allow ( <
|
|
2048
|
+
// TODO TOOL: allow ( <guard=arrayAnno, arg=bracket> ']' )
|
|
2005
2049
|
{ this.ec( 'arrayAnno', 'bracket' ); }<always>
|
|
2006
2050
|
']'
|
|
2007
2051
|
|
|
package/lib/parsers/Lexer.js
CHANGED
|
@@ -95,7 +95,7 @@ class Lexer {
|
|
|
95
95
|
tokenIndex: parser.tokens.length + parser.docComments.length + parser.comments.length,
|
|
96
96
|
};
|
|
97
97
|
let keyword;
|
|
98
|
-
if (typeof type !== 'function' ||
|
|
98
|
+
if (typeof type !== 'function' || // eslint-disable-next-line sonarjs/no-nested-assignment
|
|
99
99
|
([ type, text, keyword ] = type( text, this, parser, pos )) && type) {
|
|
100
100
|
parser.tokens.push( {
|
|
101
101
|
__proto__: Token.prototype,
|
|
@@ -200,7 +200,8 @@ function string( text, lexer, parser, beg ) {
|
|
|
200
200
|
// TODO: set parsedAs to 0 → no further error if string is not expected?
|
|
201
201
|
prefix = null; // no combination with date/time/…
|
|
202
202
|
}
|
|
203
|
-
|
|
203
|
+
rulesRegexp.lastIndex = lastIndex || lexer.input.length;
|
|
204
|
+
adaptEndLocation( lexer, rulesRegexp.lastIndex );
|
|
204
205
|
|
|
205
206
|
if (!prefix)
|
|
206
207
|
return [ 'String', lexer.input.substring( beg, rulesRegexp.lastIndex ), keyword ];
|
|
@@ -233,7 +234,8 @@ function ident( text, lexer, parser, beg ) {
|
|
|
233
234
|
keyword = 0;
|
|
234
235
|
// TODO: set parsedAs to 0 → no further error if string is not expected?
|
|
235
236
|
}
|
|
236
|
-
|
|
237
|
+
rulesRegexp.lastIndex = lastIndex || lexer.input.length;
|
|
238
|
+
adaptEndLocation( lexer, rulesRegexp.lastIndex );
|
|
237
239
|
return [ 'Id', lexer.input.substring( beg, rulesRegexp.lastIndex ), keyword ];
|
|
238
240
|
}
|
|
239
241
|
|
package/lib/parsers/index.js
CHANGED
|
@@ -21,7 +21,7 @@ function parseCdl( source, filename = '<undefined>.cds',
|
|
|
21
21
|
options = {}, messageFunctions = null,
|
|
22
22
|
rule = 'cdl' ) {
|
|
23
23
|
const rulespec = rules[rule];
|
|
24
|
-
if (!options.newParser)
|
|
24
|
+
if (!options.newParser) // TODO: (options.newParser === false)
|
|
25
25
|
return parseWithAntlr( source, filename, options, messageFunctions, rulespec );
|
|
26
26
|
const { CdlParser } = gen;
|
|
27
27
|
if (CdlParser.tracingParser) // tracing → direct console output of message
|