@sap/cds-compiler 5.5.2 → 5.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -1
- package/bin/cdsse.js +4 -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 +170 -143
- 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 +4 -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 +8 -2
- package/lib/edm/edmUtils.js +2 -1
- package/lib/gen/BaseParser.js +142 -103
- package/lib/gen/CdlParser.js +2240 -2201
- package/lib/gen/Dictionary.json +185 -6
- 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 +200 -86
- package/lib/parsers/CdlGrammar.g4 +142 -86
- 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/standardDatabaseFunctions.js +576 -0
- package/lib/transform/addTenantFields.js +2 -1
- package/lib/transform/db/expansion.js +3 -0
- 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/forOdata.js +1 -1
- package/lib/transform/odata/adaptAnnotationRefs.js +79 -0
- package/lib/transform/odata/createForeignKeys.js +25 -9
- 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
|
|
|
@@ -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,8 @@ mixinElementDef[ outer ] locals[ art = new XsnArtifact() ]
|
|
|
507
519
|
( assoc=ASSOCIATION cardinality[ $art ]? TO
|
|
508
520
|
| assoc=COMPOSITION cardinality[ $art ]? OF
|
|
509
521
|
)
|
|
510
|
-
card=ONE/MANY?
|
|
522
|
+
( <guard=noRepeatedCardinality> card=ONE/MANY )?
|
|
523
|
+
target=simplePath
|
|
511
524
|
{ this.setAssocAndComposition( $art, $assoc, $card, $target ); }
|
|
512
525
|
ON expr=condition { $art.on = $expr; }
|
|
513
526
|
;
|
|
@@ -526,7 +539,7 @@ annotateArtifact[ art, outer ]
|
|
|
526
539
|
annotateElementsBlock[ $art ]?
|
|
527
540
|
| // definition annotation
|
|
528
541
|
keyword=WITH?
|
|
529
|
-
// <
|
|
542
|
+
// <guard=noRuleExitAfterWith>), or as rule option,
|
|
530
543
|
// this.noSemicolonHere() had the issues: DocComment, before `}`/EOF
|
|
531
544
|
{ this.addExtension( $art, $outer, 'annotate', $name ); }
|
|
532
545
|
{ this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
@@ -597,7 +610,7 @@ extendService[ art, outer ]
|
|
|
597
610
|
name=namePath[ 'ExtService' ]
|
|
598
611
|
{ $art.name = $name; $outer.extensions.push( $art ); }
|
|
599
612
|
keyword=WITH?
|
|
600
|
-
// <
|
|
613
|
+
// <guard=noRuleExitAfterWith>), or as rule option,
|
|
601
614
|
// this.noSemicolonHere() had the issues: DocComment, before `}`/EOF
|
|
602
615
|
{ this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
603
616
|
artifactsBlock[ $art ]?
|
|
@@ -610,7 +623,7 @@ extendContext[ art, outer ]
|
|
|
610
623
|
name=namePath[ 'ExtContext' ]
|
|
611
624
|
{ $art.name = $name; $outer.extensions.push( $art ); }
|
|
612
625
|
keyword=WITH?
|
|
613
|
-
// <
|
|
626
|
+
// <guard=noRuleExitAfterWith>), or as rule option,
|
|
614
627
|
// this.noSemicolonHere() had the issues: DocComment, before `}`/EOF
|
|
615
628
|
{ this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
616
629
|
artifactsBlock[ $art ]?
|
|
@@ -680,7 +693,7 @@ annotateActionsBlock[ art ]
|
|
|
680
693
|
:
|
|
681
694
|
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
682
695
|
( annotateBoundAction[ $art ]
|
|
683
|
-
( ';' | <exitLoop> | <repeatLoop,
|
|
696
|
+
( ';' | <exitLoop> | <repeatLoop, guard=afterBrace> { this.noAssignmentInSameLine(); } )
|
|
684
697
|
)*
|
|
685
698
|
'}'<prepare=afterBrace>
|
|
686
699
|
{ this.finalizeExtensionsDict( $art.actions ); }
|
|
@@ -731,7 +744,7 @@ annotateElementsBlock[ art ]
|
|
|
731
744
|
( annotateElement[ $art ]
|
|
732
745
|
( ';'
|
|
733
746
|
| <exitLoop>
|
|
734
|
-
| <repeatLoop,
|
|
747
|
+
| <repeatLoop, guard=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); }
|
|
735
748
|
)
|
|
736
749
|
)*
|
|
737
750
|
'}'<prepare=afterBrace>
|
|
@@ -754,7 +767,7 @@ extendElementsBlock[ art, start = undefined ]
|
|
|
754
767
|
( elementDefOrExtend[ $art ]
|
|
755
768
|
( ';'
|
|
756
769
|
| <exitLoop>
|
|
757
|
-
| <repeatLoop,
|
|
770
|
+
| <repeatLoop, guard=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); } )
|
|
758
771
|
)*
|
|
759
772
|
'}'<prepare=afterBrace>
|
|
760
773
|
{ this.finalizeExtensionsDict( $art.elements ); }
|
|
@@ -837,7 +850,7 @@ typeOrIncludesSpec[ art ]
|
|
|
837
850
|
// annotations are attached to the element, the type properties to the line type
|
|
838
851
|
//
|
|
839
852
|
// If used in a definition with additional clauses (currently just `= expr` for
|
|
840
|
-
// elements), these clauses must be guarded with <
|
|
853
|
+
// elements), these clauses must be guarded with <guard=…>.
|
|
841
854
|
//
|
|
842
855
|
// This rule is for element, type, (input and `returns`) parameter and annotation
|
|
843
856
|
// definitions. It is not used when the type expression is restricted: CDL-style
|
|
@@ -848,15 +861,15 @@ typeExpression[ art ]
|
|
|
848
861
|
( typeRefOptArgs[ $art ] | typeTypeOf[ $art ] )
|
|
849
862
|
(<altRuleStart>)
|
|
850
863
|
{ this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
851
|
-
( <
|
|
864
|
+
( <guard=elementRestriction, arg=notNull> nullability[ $art ]
|
|
852
865
|
{ this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
853
866
|
)?
|
|
854
867
|
( enumSymbolsBlock[ $art ] <prepare=elementRestriction, arg=anno>
|
|
855
|
-
( <
|
|
856
|
-
( <
|
|
868
|
+
( <guard=elementRestriction, arg=notNull> nullability[ $art ] )?
|
|
869
|
+
( <guard=elementRestriction, arg=default>
|
|
857
870
|
DEFAULT expr=expression { $art.default = $expr; }
|
|
858
871
|
)?
|
|
859
|
-
( <
|
|
872
|
+
( <guard=elementRestriction, arg=notNull> nullability[ $art ] )?
|
|
860
873
|
| typeProperties[ $art ]
|
|
861
874
|
)?
|
|
862
875
|
|
|
|
@@ -866,11 +879,13 @@ typeExpression[ art ]
|
|
|
866
879
|
typeProperties[ $art ]?
|
|
867
880
|
|
|
|
868
881
|
assoc=ASSOCIATION <prepare=elementRestriction, arg=calc>
|
|
869
|
-
cardinality[ $art ]? TO
|
|
882
|
+
cardinality[ $art ]? TO
|
|
883
|
+
( <guard=noRepeatedCardinality> card=ONE/MANY )?
|
|
870
884
|
typeAssocProperties[ $art, $assoc, $card ]
|
|
871
885
|
|
|
|
872
886
|
assoc=COMPOSITION <prepare=elementRestriction, arg=calc>
|
|
873
|
-
cardinality[ $art ]? OF
|
|
887
|
+
cardinality[ $art ]? OF
|
|
888
|
+
( <guard=noRepeatedCardinality> card=ONE/MANY )?
|
|
874
889
|
( typeAssocProperties[ $art, $assoc, $card ]
|
|
875
890
|
| elementsBlock[ this.setAssocAndComposition( $art, $assoc, $card ) ]
|
|
876
891
|
{ $art.target.location = $art.target.elements[Symbol.for('cds.$location')]; }
|
|
@@ -883,7 +898,7 @@ typeExpression[ art ]
|
|
|
883
898
|
) // no anno assignments, except to end type expression
|
|
884
899
|
(
|
|
885
900
|
( typeRefOptArgs[ $art.items ] | typeTypeOf[ $art.items ] )
|
|
886
|
-
( <
|
|
901
|
+
( <guard=elementRestriction, arg=notNull> nullability[ $art.items ] )?
|
|
887
902
|
( { this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
888
903
|
{ ; } <exitRule> // TODO TOOL: make it work without workaround { ; }
|
|
889
904
|
// TODO TOOL: investigate why simply `{} <exitRule>` is ignored
|
|
@@ -892,7 +907,7 @@ typeExpression[ art ]
|
|
|
892
907
|
|
|
|
893
908
|
elementsBlock[ $art.items ]
|
|
894
909
|
)
|
|
895
|
-
( <
|
|
910
|
+
( <guard=elementRestriction, arg=notNull> nullability[ $art.items ] )?
|
|
896
911
|
|
|
|
897
912
|
elementsBlock[ $art ] <prepare=elementRestriction, arg=calc>
|
|
898
913
|
nullability[ $art ]?
|
|
@@ -915,16 +930,17 @@ typeProperties[ art ]
|
|
|
915
930
|
(
|
|
916
931
|
annoAssignStd[ $art ]
|
|
917
932
|
|
|
|
918
|
-
<
|
|
933
|
+
<guard=elementRestriction, arg=notNull>
|
|
919
934
|
nullability[ $art ] { this.docComment( $art ); }
|
|
920
935
|
|
|
|
921
|
-
<
|
|
936
|
+
<guard=elementRestriction, arg=default>
|
|
922
937
|
DEFAULT expr=expression { $art.default = $expr; this.docComment( $art ); }
|
|
923
938
|
)+
|
|
924
939
|
;
|
|
925
940
|
|
|
926
941
|
|
|
927
942
|
typeTypeOf[ art ] locals[ location ]
|
|
943
|
+
@after{ this.attachLocation( $art.type ); }
|
|
928
944
|
:
|
|
929
945
|
TYPE OF { location = this.locationOfPrevTokens( 2 ); }
|
|
930
946
|
type=simplePath[ 'ref' ] { $art.type = $type; }
|
|
@@ -951,6 +967,7 @@ typeRefOptArgs[ art ] locals[ type = $art.type ]
|
|
|
951
967
|
(
|
|
952
968
|
'.' Id_all['ref'] { $type.path.push( this.identAst() ); }
|
|
953
969
|
)*
|
|
970
|
+
{ this.attachLocation( $art.type ); }
|
|
954
971
|
|
|
|
955
972
|
open='('
|
|
956
973
|
(
|
|
@@ -989,6 +1006,7 @@ typeNamedArgsList[ art ]
|
|
|
989
1006
|
|
|
990
1007
|
typeNamedArg[ art ]
|
|
991
1008
|
:
|
|
1009
|
+
// TODO: or keywords with guards for better code completion?
|
|
992
1010
|
name=Id['typeparamname']
|
|
993
1011
|
':'
|
|
994
1012
|
( Number
|
|
@@ -1054,13 +1072,11 @@ queryEOF returns[ query ]
|
|
|
1054
1072
|
projectionSpec returns[ default query = {} ]
|
|
1055
1073
|
@finally{ this.attachLocation($query); }
|
|
1056
1074
|
:
|
|
1057
|
-
|
|
1058
|
-
|
|
1075
|
+
PROJECTION
|
|
1076
|
+
<prepare=afterBrace, arg=entity> // enable special loop-exit, TODO v6 delete
|
|
1077
|
+
{ $query = { op: this.valueWithLocation( 'SELECT' ) }; }
|
|
1059
1078
|
ON
|
|
1060
1079
|
tab=fromRefWithOptAlias
|
|
1061
|
-
// TODO: this <prepare=afterBrace> is extremely strange... v6 forbid.
|
|
1062
|
-
// Deliberately set this via action (→ interpreter will not accept this)
|
|
1063
|
-
{ this.afterBrace(); }<always>
|
|
1064
1080
|
{ $query.from = tab; }
|
|
1065
1081
|
selectItemsList[ $query ]?
|
|
1066
1082
|
excludingClause[ $query ]?
|
|
@@ -1084,7 +1100,7 @@ queryExpression returns[ default expr = {} ] locals[ op, quantifier ]
|
|
|
1084
1100
|
)
|
|
1085
1101
|
query=queryExpression
|
|
1086
1102
|
// with same op/quantifier: make left-assoc binary to nary:
|
|
1087
|
-
{ 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: ...$
|
|
1088
1104
|
{ $quantifier = undefined; }
|
|
1089
1105
|
{ $expr.args.push( $query ); this.attachLocation( $expr ); }
|
|
1090
1106
|
)*
|
|
@@ -1177,16 +1193,16 @@ tableOrQueryParens returns[ default expr ]
|
|
|
1177
1193
|
'(' <prepare=queryOnLeft>
|
|
1178
1194
|
( <prefer> tableOrQueryParens[ ...$ ]
|
|
1179
1195
|
( tableExpression[ ...$ ]<atAltStart, prepare=queryOnLeft, arg=table>
|
|
1180
|
-
| <
|
|
1196
|
+
| <guard=queryOnLeft> queryExpression[ ...$ ]<atAltStart>
|
|
1181
1197
|
)?
|
|
1182
1198
|
| tableExpression[ ...$ ] <prepare=queryOnLeft, arg=table>
|
|
1183
1199
|
| queryExpression[ ...$ ]
|
|
1184
1200
|
)
|
|
1185
1201
|
')'
|
|
1186
1202
|
{ this.surroundByParens( $expr ); }
|
|
1187
|
-
( <
|
|
1203
|
+
( <guard=queryOnLeft, arg=table> AS Id['FromAlias']
|
|
1188
1204
|
{ $expr = this.taggedIfQuery( $expr ); $expr.name = this.identAst(); }
|
|
1189
|
-
| <
|
|
1205
|
+
| <guard=queryOnLeft, arg=tableWithoutAs> Id_restricted['FromAlias']
|
|
1190
1206
|
// TODO TOOL: shouldn't we have generated `default: this.giR()`?
|
|
1191
1207
|
{ $expr = this.taggedIfQuery( $expr ); $expr.name = this.fragileAlias(); }
|
|
1192
1208
|
)?
|
|
@@ -1225,8 +1241,7 @@ fromRefWithOptAlias returns[ default expr = {} ]
|
|
|
1225
1241
|
(
|
|
1226
1242
|
AS Id['FromAlias'] { $expr.name = this.identAst(); }
|
|
1227
1243
|
|
|
|
1228
|
-
<
|
|
1229
|
-
// TODO: probably not necessary, TOOL already uses `default: this.giR()`
|
|
1244
|
+
// <guard=tableWithoutAs> not necessary, tool uses `default: this.giR()`
|
|
1230
1245
|
Id_restricted['FromAlias']
|
|
1231
1246
|
{ $expr.name = this.fragileAlias(); }
|
|
1232
1247
|
|
|
|
@@ -1241,6 +1256,7 @@ fromPath[ table, category ] locals[ pathItem ]
|
|
|
1241
1256
|
Id[ $category ] { $table.path.push( $pathItem = this.identAst() ); }
|
|
1242
1257
|
( fromArgumentsAndFilter[ $pathItem ] { $pathItem = null; } )?
|
|
1243
1258
|
(
|
|
1259
|
+
<guard=notAfterEntityArgOrFilter> // TODO TOOL: allow <hide=method>
|
|
1244
1260
|
'.' { if (!$pathItem && !$table.scope) {
|
|
1245
1261
|
$table.scope = $table.path.length; $category = 'ref';
|
|
1246
1262
|
this.warning( 'syntax-invalid-path-separator', this.lb(),
|
|
@@ -1274,7 +1290,7 @@ fromNamedArgument[ pathStep ]
|
|
|
1274
1290
|
cardinalityAndFilter[ pathStep ]
|
|
1275
1291
|
:
|
|
1276
1292
|
'['
|
|
1277
|
-
( <
|
|
1293
|
+
( <guard=beforeColon> Number // TODO: only allow `1`?
|
|
1278
1294
|
{ $pathStep.cardinality = { targetMax: this.unsignedIntegerLiteral(), location: this.lb().location }; }
|
|
1279
1295
|
':'
|
|
1280
1296
|
)?
|
|
@@ -1338,7 +1354,7 @@ selectItemsList[ query, start = undefined ]
|
|
|
1338
1354
|
|
|
1339
1355
|
nestedSelectItemsList[ query, clause ]
|
|
1340
1356
|
:
|
|
1341
|
-
'{'<prepare=inSelectItem
|
|
1357
|
+
'{'<prepare=inSelectItem>
|
|
1342
1358
|
{ $query[$clause] = this.createArray(); }
|
|
1343
1359
|
(
|
|
1344
1360
|
( '*' { $query[$clause].push( this.valueWithLocation() ); }
|
|
@@ -1350,64 +1366,104 @@ nestedSelectItemsList[ query, clause ]
|
|
|
1350
1366
|
{ this.finalizeDictOrArray( $query[$clause] ); }
|
|
1351
1367
|
;
|
|
1352
1368
|
|
|
1353
|
-
selectItemDef[ columns ] locals[ art = new XsnArtifact()
|
|
1369
|
+
selectItemDef[ columns ] locals[ art = new XsnArtifact() ]
|
|
1354
1370
|
@finally{ this.attachLocation( $art ); }
|
|
1355
1371
|
:
|
|
1356
1372
|
{ $columns.push( $art ); } // TODO: probably too early
|
|
1357
1373
|
{ this.docComment( $art ); } annoAssignCol[ $art ]*
|
|
1358
|
-
( <
|
|
1359
|
-
{ $art.virtual = this.valueWithLocation( true ); }
|
|
1360
|
-
|
|
1361
|
-
|
|
1374
|
+
( <guard=modifierRestriction> VIRTUAL
|
|
1375
|
+
{ $art.virtual = this.valueWithLocation( true ); }
|
|
1376
|
+
)?
|
|
1377
|
+
{;} <prepare=columnExpr, arg=key> // TOOL TODO: disappears without {;}
|
|
1378
|
+
( <guard=modifierRestriction> KEY
|
|
1379
|
+
{ $art.key = this.valueWithLocation( true ); }
|
|
1380
|
+
)?
|
|
1381
|
+
// TODO: we might have an extra rule for column expression...
|
|
1362
1382
|
(
|
|
1363
|
-
expr=expression { $art.value = $expr; }
|
|
1364
|
-
( as=AS Id['ItemAlias'] { $art.name = this.identAst(); }
|
|
1365
|
-
| Id_restricted['ItemAlias'] { $art.name = this.fragileAlias( true ); }
|
|
1366
|
-
| { $alias = this.classifyImplicitName( 'ItemImplicit', $expr ); }
|
|
1367
|
-
)
|
|
1368
|
-
// TODO: <cond> instead `reportExpandInline` for "expand/inline only w/ ref"
|
|
1369
1383
|
(
|
|
1370
|
-
{
|
|
1371
|
-
|
|
1372
|
-
|
|
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.reportExpandInline( $art, $as || true ); }
|
|
1377
|
-
{ if ($alias) $alias.token.parsedAs = $alias.parsedAs; }
|
|
1389
|
+
<prefer>
|
|
1390
|
+
expr=valuePath { $expr = this.valuePathAst( $expr ); $art.value = $expr; }
|
|
1378
1391
|
(
|
|
1379
|
-
|
|
1380
|
-
|
|
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(); }
|
|
1381
1407
|
|
|
|
1382
|
-
'
|
|
1408
|
+
Id_restricted['ItemAlias'] <prepare=nestedExpand>
|
|
1409
|
+
{ $art.name = this.fragileAlias( true ); }
|
|
1410
|
+
|
|
|
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>
|
|
1383
1427
|
)
|
|
1428
|
+
)
|
|
1429
|
+
// expand is only allowed only after valuePath, but error recovery is poor
|
|
1430
|
+
// if the following is moved up to <prepare=nestedExpand>
|
|
1431
|
+
(
|
|
1432
|
+
// TODO: make guard handle that if valuePath might be function?
|
|
1433
|
+
<guard=nestedExpand>
|
|
1434
|
+
{ if (!this.nestedExpand(true)) this.reportExpandInline( $art, false ); }
|
|
1435
|
+
nestedSelectItemsList[ $art, 'expand' ]
|
|
1436
|
+
excludingClause[ $art ]?
|
|
1384
1437
|
)?
|
|
1385
1438
|
|
|
|
1386
1439
|
nestedSelectItemsList[ $art, 'expand' ]
|
|
1387
1440
|
excludingClause[ $art ]?
|
|
1388
1441
|
AS Id['ItemAlias'] { $art.name = this.identAst(); }
|
|
1389
1442
|
)
|
|
1390
|
-
{ this.docComment( $art ); } annoAssignMid[ $art ]*
|
|
1443
|
+
{ this.docComment( $art ); } <prepare=columnExpr> annoAssignMid[ $art ]*
|
|
1391
1444
|
(
|
|
1392
|
-
':'
|
|
1445
|
+
':' // TODO: guard? currently not with expand ?
|
|
1393
1446
|
(
|
|
1394
1447
|
( typeTypeOf[ $art ]
|
|
1395
1448
|
| ( LOCALIZED { $art.localized = this.valueWithLocation( true ); } )?
|
|
1396
1449
|
typeRefOptArgs[ $art ]
|
|
1397
1450
|
)
|
|
1398
1451
|
|
|
|
1452
|
+
// TODO: guard for ref-only expression ?
|
|
1399
1453
|
REDIRECTED TO target=simplePath { $art.target = $target; }
|
|
1400
1454
|
( ON cond=condition { $art.on = $cond; }
|
|
1401
1455
|
| foreignKeysBlock[ $art ]
|
|
1402
1456
|
)?
|
|
1403
1457
|
|
|
|
1404
|
-
//
|
|
1405
|
-
|
|
1458
|
+
( <guard=columnExpr> // arg=singleId
|
|
1459
|
+
assoc=ASSOCIATION { this.associationInSelectItem( $art ); }
|
|
1406
1460
|
cardinality[ $art ]? TO
|
|
1407
|
-
|
|
|
1461
|
+
| <guard=columnExpr> // arg=singleId
|
|
1462
|
+
assoc=COMPOSITION { this.associationInSelectItem( $art ); }
|
|
1408
1463
|
cardinality[ $art ]? OF
|
|
1409
1464
|
)
|
|
1410
|
-
card=ONE/MANY?
|
|
1465
|
+
( <guard=noRepeatedCardinality> card=ONE/MANY )?
|
|
1466
|
+
target=simplePath
|
|
1411
1467
|
{ this.setAssocAndComposition( $art, $assoc, $card, $target ); }
|
|
1412
1468
|
ON expr=condition { $art.on = $expr; }
|
|
1413
1469
|
)
|
|
@@ -1471,7 +1527,7 @@ valuePath returns[ default expr = { path: [] } ] locals[ pathItem ]
|
|
|
1471
1527
|
( argumentsAndFilter[ $pathItem ] )?
|
|
1472
1528
|
(<altRuleStart>)
|
|
1473
1529
|
(
|
|
1474
|
-
<
|
|
1530
|
+
<guard=isDotForPath> '.'
|
|
1475
1531
|
Id_all['ref'] { $expr.path.push( $pathItem = this.identAst() ); }
|
|
1476
1532
|
( argumentsAndFilter[ $pathItem ] { $pathItem = null; } )?
|
|
1477
1533
|
)*
|
|
@@ -1557,7 +1613,7 @@ expression returns[ default expr = {} ]
|
|
|
1557
1613
|
| <prec=10, postfix=once> IS { $expr = this.applyOpToken( $expr ); }
|
|
1558
1614
|
( NOT { this.pushXprToken( $expr ); } )?
|
|
1559
1615
|
NULL { this.pushXprToken( $expr ); }
|
|
1560
|
-
| ( <arg=10,
|
|
1616
|
+
| ( <arg=10, guard=isNegatedRelation> NOT { $expr = this.applyOpToken( $expr ); }
|
|
1561
1617
|
// TODO: condition, because there might be NOT NULL after DEFAULT expression
|
|
1562
1618
|
| <prec=10, postfix=once>
|
|
1563
1619
|
{ $expr = { op: { val: 'ixpr', location: this.la().location }, args: [ $expr ] }; }
|
|
@@ -1587,7 +1643,7 @@ expressionOrQueryParens returns[ default expr ]
|
|
|
1587
1643
|
( expression[ ...$ ]<atAltStart, prepare=queryOnLeft, arg=expr>
|
|
1588
1644
|
continueExpressionslist[ ...$ ]?
|
|
1589
1645
|
| continueExpressionslist[ ...$ ] <prepare=queryOnLeft, arg=expr>
|
|
1590
|
-
| <
|
|
1646
|
+
| <guard=queryOnLeft> queryExpression[ ...$ ]<atAltStart>
|
|
1591
1647
|
)?
|
|
1592
1648
|
| expression[ ...$ ] <prepare=queryOnLeft, arg=expr>
|
|
1593
1649
|
continueExpressionslist[ ...$ ]?
|
|
@@ -1674,7 +1730,7 @@ options{ minTokensMatched = 1 }
|
|
|
1674
1730
|
// TODO: if we want perfect CC and error recovery, `isNamedArg` would work
|
|
1675
1731
|
// like keyword prediction and also do something similar to the (future)
|
|
1676
1732
|
// "identifier confirmation prediction" (consider argument `(par ‡ : 42)`
|
|
1677
|
-
<
|
|
1733
|
+
<guard=isNamedArg> id=Id_all['paramname']
|
|
1678
1734
|
(
|
|
1679
1735
|
':' { $pathStep.args = this.createDict( $open ); $pathStep.$syntax = ':'; }
|
|
1680
1736
|
expr=expression { this.addNamedArg( $pathStep, $id, $expr ); }
|
|
@@ -1815,7 +1871,7 @@ literalValue returns[ default expr = {} ]
|
|
|
1815
1871
|
:
|
|
1816
1872
|
// TODO: remove from this rule (not in enum! `String enum { foo = #bar }`) ?
|
|
1817
1873
|
'#' { this.reportUnexpectedSpace(); }
|
|
1818
|
-
|
|
1874
|
+
Id_all['enumref']
|
|
1819
1875
|
{ $expr = { literal: 'enum', sym: this.identAst() } }
|
|
1820
1876
|
|
|
|
1821
1877
|
NULL
|
|
@@ -1882,7 +1938,7 @@ annoAssignBase[ art ] locals[ value = {} ]
|
|
|
1882
1938
|
@finally{ this.assignAnnotation( $art, $value, $name || {} ); }
|
|
1883
1939
|
:
|
|
1884
1940
|
name=annoNamePath
|
|
1885
|
-
( <
|
|
1941
|
+
( <guard=annoInSameLine> ':' value=annoValue )?
|
|
1886
1942
|
;
|
|
1887
1943
|
|
|
1888
1944
|
annoNamePath[ category = 'anno' ] returns[ default name = new XsnName() ]
|
|
@@ -1897,7 +1953,7 @@ annoNamePath[ category = 'anno' ] returns[ default name = new XsnName() ]
|
|
|
1897
1953
|
Id_all[ $category ] { $name.path.push( this.identAstWithPrefix( $at ) ); }
|
|
1898
1954
|
)
|
|
1899
1955
|
)*
|
|
1900
|
-
( <
|
|
1956
|
+
( <guard=annoInSameLine> annoPathVariant[ $name ] )?
|
|
1901
1957
|
;
|
|
1902
1958
|
|
|
1903
1959
|
annoPath[ nameOrRef, category = 'annoref' ]
|
|
@@ -1955,7 +2011,7 @@ annoValue returns[ default value = {} ]
|
|
|
1955
2011
|
}
|
|
1956
2012
|
(
|
|
1957
2013
|
// TODO: complain about empty loop if top-level as before
|
|
1958
|
-
// TOOL TODO → allow `<
|
|
2014
|
+
// TOOL TODO → allow `<guard=…> '}'` below where the condition rejects `}`
|
|
1959
2015
|
// after `{` if top-level
|
|
1960
2016
|
sub=annoStructValue
|
|
1961
2017
|
{
|
|
@@ -1965,7 +2021,7 @@ annoValue returns[ default value = {} ]
|
|
|
1965
2021
|
( ',' | <exitLoop> )
|
|
1966
2022
|
)*
|
|
1967
2023
|
// TODO TOOL: allow:
|
|
1968
|
-
// ( <
|
|
2024
|
+
// ( <guard=arrayAnno, …> '}' | <error> ) // TODO TOOL - workaround:
|
|
1969
2025
|
{ this.ec( 'arrayAnno', 'orNotEmpty' ); } '}'
|
|
1970
2026
|
// Do NOT use <prepare=afterBrace> here!
|
|
1971
2027
|
|
|
|
@@ -1975,7 +2031,7 @@ annoValue returns[ default value = {} ]
|
|
|
1975
2031
|
(
|
|
1976
2032
|
( sub=annoValue { $value.val.push( $sub ) }
|
|
1977
2033
|
|
|
|
1978
|
-
<
|
|
2034
|
+
<guard=arrayAnno, arg=ellipsis> ellipsis='...'
|
|
1979
2035
|
( UP TO upTo=annoValue
|
|
1980
2036
|
{ $value.val.push( { literal: 'token', val: '...', location: $ellipsis.location, upTo: $upTo } ); }
|
|
1981
2037
|
| { $value.val.push( { literal: 'token', val: '...', location: $ellipsis.location } ); }
|
|
@@ -1987,7 +2043,7 @@ annoValue returns[ default value = {} ]
|
|
|
1987
2043
|
)
|
|
1988
2044
|
( ',' | <exitLoop> )
|
|
1989
2045
|
)*
|
|
1990
|
-
// TODO TOOL: allow ( <
|
|
2046
|
+
// TODO TOOL: allow ( <guard=arrayAnno, arg=bracket> ']' )
|
|
1991
2047
|
{ this.ec( 'arrayAnno', 'bracket' ); }<always>
|
|
1992
2048
|
']'
|
|
1993
2049
|
|
|
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
|