@sap/cds-compiler 5.4.4 → 5.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -1
- package/bin/cds_remove_invalid_whitespace.js +4 -4
- package/bin/cds_update_annotations.js +3 -3
- package/bin/cds_update_identifiers.js +3 -3
- package/lib/api/main.js +18 -30
- package/lib/api/validate.js +6 -1
- package/lib/base/lazyload.js +28 -0
- package/lib/base/location.js +1 -0
- package/lib/base/message-registry.js +47 -11
- package/lib/base/messages.js +17 -3
- package/lib/checks/cdsMap.js +27 -0
- package/lib/checks/{dbFeatureFlags.js → featureFlags.js} +1 -1
- package/lib/checks/parameters.js +61 -4
- package/lib/checks/validator.js +17 -7
- package/lib/compiler/define.js +1 -0
- package/lib/compiler/index.js +7 -7
- package/lib/gen/BaseParser.js +345 -235
- package/lib/gen/CdlParser.js +4438 -4492
- package/lib/gen/Dictionary.json +2 -2
- package/lib/language/antlrParser.js +2 -111
- package/lib/main.js +16 -37
- package/lib/model/cloneCsn.js +1 -5
- package/lib/modelCompare/utils/filter.js +47 -21
- package/lib/parsers/AstBuildingParser.js +92 -73
- package/lib/parsers/CdlGrammar.g4 +110 -137
- package/lib/parsers/index.js +123 -0
- package/lib/render/toSql.js +8 -2
- package/lib/render/utils/delta.js +33 -1
- package/lib/transform/db/{transformExists.js → assocsToQueries/transformExists.js} +12 -407
- package/lib/transform/db/assocsToQueries/utils.js +440 -0
- package/lib/transform/db/expansion.js +2 -2
- package/lib/transform/draft/db.js +14 -3
- package/lib/transform/effective/annotations.js +3 -3
- package/lib/transform/effective/main.js +5 -7
- package/lib/transform/featureFlags.js +5 -0
- package/lib/transform/forRelationalDB.js +125 -192
- package/lib/transform/transformUtils.js +0 -51
- package/lib/utils/objectUtils.js +13 -0
- package/package.json +2 -2
- package/lib/transform/db/featureFlags.js +0 -5
|
@@ -9,6 +9,9 @@ options {
|
|
|
9
9
|
const { XsnSource, XsnArtifact, XsnName } = require( '../compiler/xsn-model' );
|
|
10
10
|
const AstBuildingParser = require('../parsers/AstBuildingParser');
|
|
11
11
|
}
|
|
12
|
+
@footer{
|
|
13
|
+
module.exports = { CdlParser }; // make it work with lazyload()
|
|
14
|
+
}
|
|
12
15
|
|
|
13
16
|
// Content:
|
|
14
17
|
// - top-level: USING, NAMESPACE, artifactDefOrExtend (start rule: start)
|
|
@@ -205,7 +208,7 @@ aspectDef[ art, outer ]
|
|
|
205
208
|
@finally{ this.attachLocation( $art ); }
|
|
206
209
|
:
|
|
207
210
|
( ASPECT
|
|
208
|
-
| <hide> ABSTRACT { this.warning( 'syntax-deprecated-abstract', this.lb().
|
|
211
|
+
| <hide> ABSTRACT { this.warning( 'syntax-deprecated-abstract', this.combineLocation( this.lb(), this.la() ) ); }
|
|
209
212
|
ENTITY
|
|
210
213
|
)
|
|
211
214
|
name=namePath[ 'Type' ] // TODO: Type?
|
|
@@ -213,23 +216,17 @@ aspectDef[ art, outer ]
|
|
|
213
216
|
{ this.docComment( $art ); } annoAssignMid[ $art ]*
|
|
214
217
|
(
|
|
215
218
|
elementsBlock[ $art ]
|
|
216
|
-
actionsBlock[ $art ]?
|
|
217
219
|
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
(
|
|
226
|
-
incl=simplePath { $art.includes ??= []; $art.includes.push( $incl ); }
|
|
227
|
-
( ',' | <exitLoop> )
|
|
228
|
-
)*
|
|
229
|
-
(
|
|
220
|
+
<exitRule>
|
|
221
|
+
|
|
|
222
|
+
':'
|
|
223
|
+
(
|
|
224
|
+
incl=simplePath { $art.includes ??= []; $art.includes.push( $incl ); }
|
|
225
|
+
( ',' | <exitLoop> | <exitRule> )
|
|
226
|
+
)*
|
|
230
227
|
elementsBlock[ $art ]
|
|
231
|
-
|
|
232
|
-
|
|
228
|
+
)
|
|
229
|
+
actionsBlock[ $art ]?
|
|
233
230
|
;
|
|
234
231
|
|
|
235
232
|
entityDef[ art, outer ]
|
|
@@ -419,9 +416,9 @@ elementDef[ outer, art = undefined ]
|
|
|
419
416
|
{ this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
420
417
|
( VIRTUAL { $art.virtual = this.valueWithLocation( true ); } )?
|
|
421
418
|
( KEY { $art.key = this.valueWithLocation( true ); } )?
|
|
422
|
-
( MASKED { $art.masked = this.valueWithLocation( true ); }
|
|
419
|
+
( <hide> MASKED { $art.masked = this.valueWithLocation( true ); }
|
|
423
420
|
{ this.message( 'syntax-unsupported-masked', this.lb(), { keyword: 'masked' } ); } )?
|
|
424
|
-
( ELEMENT { $art.$syntax = 'element'; } )?
|
|
421
|
+
( <hide> ELEMENT { $art.$syntax = 'element'; } )?
|
|
425
422
|
Id['Element'] <prepare=elementRestriction, arg=elem>
|
|
426
423
|
{ this.addDef( $art, $outer, 'elements', 'element', this.identAst() ); }
|
|
427
424
|
{ this.docComment( $art ); } annoAssignMid[ $art ]*
|
|
@@ -429,7 +426,7 @@ elementDef[ outer, art = undefined ]
|
|
|
429
426
|
elementsBlock[ $art ]
|
|
430
427
|
nullability[ $art ]?
|
|
431
428
|
|
|
|
432
|
-
':' typeExpression[ $art ] //
|
|
429
|
+
':' typeExpression[ $art ] // includes DEFAULT
|
|
433
430
|
)?
|
|
434
431
|
(
|
|
435
432
|
<cond=elementRestriction, arg=calc> '='
|
|
@@ -440,9 +437,7 @@ elementDef[ outer, art = undefined ]
|
|
|
440
437
|
// TODO: why have `stored` as property of the value?
|
|
441
438
|
{ if (this.elementRestriction( true, 'anno' )) this.docComment( $art ); }
|
|
442
439
|
( <cond=elementRestriction, arg=anno> annoAssignStd[ $art ] )*
|
|
443
|
-
|
|
444
|
-
{ this.docComment( $art, 'type' ); } // delayed from type expression
|
|
445
|
-
)
|
|
440
|
+
)?
|
|
446
441
|
;
|
|
447
442
|
|
|
448
443
|
enumSymbolsBlock[ art ]
|
|
@@ -464,10 +459,10 @@ enumSymbolDef[ outer ] locals[ art = new XsnArtifact() ]
|
|
|
464
459
|
{ this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
465
460
|
( '='
|
|
466
461
|
(
|
|
467
|
-
<
|
|
462
|
+
<prefer> String
|
|
468
463
|
{ $art.value = this.quotedLiteral(); }
|
|
469
464
|
|
|
|
470
|
-
<
|
|
465
|
+
<prefer> Number
|
|
471
466
|
{ $art.value = this.numberLiteral(); }
|
|
472
467
|
|
|
|
473
468
|
sign='+'/'-' Number
|
|
@@ -520,7 +515,7 @@ mixinElementDef[ outer ] locals[ art = new XsnArtifact() ]
|
|
|
520
515
|
// Annotate and Extend: main definitions ----------------------------------------
|
|
521
516
|
|
|
522
517
|
annotateArtifact[ art, outer ]
|
|
523
|
-
@finally{ this.
|
|
518
|
+
@finally{ this.attachLocation( $art ); }
|
|
524
519
|
:
|
|
525
520
|
name=namePath[ 'Ext' ]
|
|
526
521
|
( // direct element annotation:
|
|
@@ -543,10 +538,11 @@ annotateArtifact[ art, outer ]
|
|
|
543
538
|
annotateActionsBlock[ $art ]?
|
|
544
539
|
)
|
|
545
540
|
)
|
|
541
|
+
{ this.checkWith( $keyword ); }
|
|
546
542
|
;
|
|
547
543
|
|
|
548
544
|
extendArtifact[ art, outer ]
|
|
549
|
-
@finally{ this.
|
|
545
|
+
@finally{ this.attachLocation( $art ); }
|
|
550
546
|
:
|
|
551
547
|
name=namePath[ 'Ext' ]
|
|
552
548
|
( // direct element annotation:
|
|
@@ -591,6 +587,7 @@ extendArtifact[ art, outer ]
|
|
|
591
587
|
DEFINITIONS artifactsBlock[ $art, this.lb() ]
|
|
592
588
|
)?
|
|
593
589
|
)
|
|
590
|
+
{ this.checkWith( $keyword ); }
|
|
594
591
|
;
|
|
595
592
|
|
|
596
593
|
extendService[ art, outer ]
|
|
@@ -803,7 +800,7 @@ typeOrIncludesSpec[ art ]
|
|
|
803
800
|
(
|
|
804
801
|
typeExpression[ $art ]
|
|
805
802
|
|
|
|
806
|
-
<
|
|
803
|
+
<prefer>
|
|
807
804
|
ref=simplePath { $art.type = $ref; }
|
|
808
805
|
(
|
|
809
806
|
// <default> does not work here
|
|
@@ -830,122 +827,103 @@ typeOrIncludesSpec[ art ]
|
|
|
830
827
|
)
|
|
831
828
|
;
|
|
832
829
|
|
|
833
|
-
// Type expression (after the `:`)
|
|
834
|
-
//
|
|
835
|
-
//
|
|
830
|
+
// Type expression (after the `:`), including `null`/`not null` and `default`;
|
|
831
|
+
// the latter is forbidden in the `returns` type.
|
|
832
|
+
//
|
|
833
|
+
// This rule also parses annotation assignments and doc comments after the
|
|
834
|
+
// type/target reference and after each type property; exceptions are:
|
|
835
|
+
// - not after element and `enum` blocks (would interfere with optional `;`)
|
|
836
|
+
// - no further type property after `many …` @assignment`, because the
|
|
837
|
+
// annotations are attached to the element, the type properties to the line type
|
|
836
838
|
//
|
|
837
839
|
// If used in a definition with additional clauses (currently just `= expr` for
|
|
838
840
|
// elements), these clauses must be guarded with <cond=…>.
|
|
839
841
|
//
|
|
840
|
-
//
|
|
841
|
-
//
|
|
842
|
-
//
|
|
843
|
-
// the keyword `enum` (only allowed if not after `many`/`array of`).
|
|
844
|
-
//
|
|
845
|
-
// This rule is not used when the type expression is restricted: CDL-style cast in
|
|
846
|
-
// `select` items, `cast` function, `mixin` definition.
|
|
842
|
+
// This rule is for element, type, (input and `returns`) parameter and annotation
|
|
843
|
+
// definitions. It is not used when the type expression is restricted: CDL-style
|
|
844
|
+
// cast in `select` items, `cast` function, `mixin` definition.
|
|
847
845
|
|
|
848
846
|
typeExpression[ art ]
|
|
849
|
-
// TODO: really introduce <exitRule>
|
|
850
847
|
:
|
|
851
|
-
elementsBlock[ $art ] <prepare=elementRestriction, arg=calc>
|
|
852
|
-
nullability[ $art ]?
|
|
853
|
-
|
|
|
854
848
|
( typeRefOptArgs[ $art ] | typeTypeOf[ $art ] )
|
|
855
849
|
(<altRuleStart>)
|
|
856
|
-
|
|
857
|
-
(
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
{
|
|
864
|
-
|
|
|
865
|
-
// We could still have the calc expression after this → delay
|
|
866
|
-
{ this.docComment( $art, 'elem' ); }
|
|
867
|
-
)
|
|
868
|
-
|
|
|
869
|
-
{ this.docComment( $art, 'elem' ); }
|
|
870
|
-
|
|
|
871
|
-
{ this.docComment( $art ); } annoAssignStd[ $art ]+ // +!
|
|
872
|
-
( enumSymbolsBlock[ $art ] <prepare=elementRestriction, arg=anno>
|
|
873
|
-
// TODO TOOL: written?
|
|
874
|
-
nullabilityAndDefault[ $art ]?
|
|
850
|
+
{ this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
851
|
+
( <cond=elementRestriction, arg=notNull> nullability[ $art ]
|
|
852
|
+
{ this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
853
|
+
)?
|
|
854
|
+
( enumSymbolsBlock[ $art ] <prepare=elementRestriction, arg=anno>
|
|
855
|
+
( <cond=elementRestriction, arg=notNull> nullability[ $art ] )?
|
|
856
|
+
( <cond=elementRestriction, arg=default>
|
|
857
|
+
DEFAULT expr=expression { $art.default = $expr; }
|
|
875
858
|
)?
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
nullabilityAndDefault[ $art ]?
|
|
880
|
-
)
|
|
859
|
+
( <cond=elementRestriction, arg=notNull> nullability[ $art ] )?
|
|
860
|
+
| typeProperties[ $art ]
|
|
861
|
+
)?
|
|
881
862
|
|
|
|
882
|
-
LOCALIZED
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
// TODO TOOL: with <exitRule>, we could move the following to the end
|
|
887
|
-
(
|
|
888
|
-
{ this.elementRestriction( false, 'calc' ); }
|
|
889
|
-
// <prepare=elementRestriction, arg=calc>
|
|
890
|
-
{ this.docComment( $art ); } annoAssignStd[ $art ]+
|
|
891
|
-
|
|
|
892
|
-
{ this.docComment( $art, 'elem' ); }
|
|
893
|
-
)
|
|
863
|
+
LOCALIZED { $art.localized = this.valueWithLocation( true ); }
|
|
864
|
+
typeRefOptArgs[ $art ] // no TYPE OF
|
|
865
|
+
{ this.docComment( $art ); }
|
|
866
|
+
typeProperties[ $art ]?
|
|
894
867
|
|
|
|
895
868
|
assoc=ASSOCIATION <prepare=elementRestriction, arg=calc>
|
|
896
869
|
cardinality[ $art ]? TO card=ONE/MANY?
|
|
897
|
-
|
|
898
|
-
( ON cond=condition { $art.on = $cond; }
|
|
899
|
-
| foreignKeysBlock[ $art ]?
|
|
900
|
-
nullabilityAndDefault[ $art ]?
|
|
901
|
-
// scalar default ? - hm..., what about calc expression?
|
|
902
|
-
)
|
|
903
|
-
// final anno assignments allowed also with fkeys (no auto-`;` after them):
|
|
904
|
-
// (the "TODO TOOL" code for doc/annos would also be fine)
|
|
905
|
-
{ this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
870
|
+
typeAssocProperties[ $art, $assoc, $card ]
|
|
906
871
|
|
|
|
907
872
|
assoc=COMPOSITION <prepare=elementRestriction, arg=calc>
|
|
908
873
|
cardinality[ $art ]? OF card=ONE/MANY?
|
|
909
|
-
(
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
| foreignKeysBlock[ $art ]?
|
|
913
|
-
nullabilityAndDefault[ $art ]?
|
|
914
|
-
)
|
|
915
|
-
// final anno assignments allowed also with fkeys (no auto-`;` after them):
|
|
916
|
-
// (the "TODO TOOL" code for doc/annos would also be fine)
|
|
917
|
-
{ this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
918
|
-
|
|
|
919
|
-
{ $target = { location: this.startLocation( this.la() ) };
|
|
920
|
-
this.setAssocAndComposition( $art, $assoc, $card, $target ); }
|
|
921
|
-
elementsBlock[ $target ]
|
|
922
|
-
{ $target.location = $target.elements[Symbol.for('cds.$location')]; }
|
|
874
|
+
( typeAssocProperties[ $art, $assoc, $card ]
|
|
875
|
+
| elementsBlock[ this.setAssocAndComposition( $art, $assoc, $card ) ]
|
|
876
|
+
{ $art.target.location = $art.target.elements[Symbol.for('cds.$location')]; }
|
|
923
877
|
)
|
|
924
878
|
|
|
|
925
879
|
( ARRAY <prepare=elementRestriction, arg=calc>
|
|
926
880
|
OF { $art.items = { location: this.locationOfPrevTokens( 2 ) }; }
|
|
927
881
|
| MANY <prepare=elementRestriction, arg=calc>
|
|
928
882
|
{ $art.items = { location: this.lb().location }; }
|
|
929
|
-
)
|
|
883
|
+
) // no anno assignments, except to end type expression
|
|
930
884
|
(
|
|
931
|
-
elementsBlock[ $art.items ]
|
|
932
|
-
nullability[ $art.items ]?
|
|
933
|
-
|
|
|
934
885
|
( typeRefOptArgs[ $art.items ] | typeTypeOf[ $art.items ] )
|
|
935
|
-
nullability[ $art.items ]?
|
|
936
|
-
(
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
{ this.elementRestriction( false, 'calc' ); }
|
|
941
|
-
// <prepare=elementRestriction, arg=calc>
|
|
942
|
-
{ this.docComment( $art ); } annoAssignStd[ $art ]+
|
|
943
|
-
|
|
|
944
|
-
{ this.docComment( $art, 'elem' ); }
|
|
886
|
+
( <cond=elementRestriction, arg=notNull> nullability[ $art.items ] )?
|
|
887
|
+
( { this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
888
|
+
{ ; } <exitRule> // TODO TOOL: make it work without workaround { ; }
|
|
889
|
+
// TODO TOOL: investigate why simply `{} <exitRule>` is ignored
|
|
890
|
+
| enumSymbolsBlock[ $art.items ]
|
|
945
891
|
)
|
|
892
|
+
|
|
|
893
|
+
elementsBlock[ $art.items ]
|
|
946
894
|
)
|
|
895
|
+
( <cond=elementRestriction, arg=notNull> nullability[ $art.items ] )?
|
|
896
|
+
|
|
|
897
|
+
elementsBlock[ $art ] <prepare=elementRestriction, arg=calc>
|
|
898
|
+
nullability[ $art ]?
|
|
947
899
|
;
|
|
948
900
|
|
|
901
|
+
typeAssocProperties[ art, assoc, card ]
|
|
902
|
+
:
|
|
903
|
+
target=simplePath { this.setAssocAndComposition( $art, $assoc, $card, $target ); }
|
|
904
|
+
{ this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
905
|
+
( ON cond=condition { $art.on = $cond; }
|
|
906
|
+
{ this.docComment( $art ); } annoAssignStd[ $art ]*
|
|
907
|
+
| foreignKeysBlock[ $art ] { this.docComment( $art ); } typeProperties[ $art ]?
|
|
908
|
+
// remark: no auto-`;` after foreign keys → anno assignment after it possible
|
|
909
|
+
| typeProperties[ $art ]
|
|
910
|
+
)?
|
|
911
|
+
;
|
|
912
|
+
|
|
913
|
+
typeProperties[ art ]
|
|
914
|
+
:
|
|
915
|
+
(
|
|
916
|
+
annoAssignStd[ $art ]
|
|
917
|
+
|
|
|
918
|
+
<cond=elementRestriction, arg=notNull>
|
|
919
|
+
nullability[ $art ] { this.docComment( $art ); }
|
|
920
|
+
|
|
|
921
|
+
<cond=elementRestriction, arg=default>
|
|
922
|
+
DEFAULT expr=expression { $art.default = $expr; this.docComment( $art ); }
|
|
923
|
+
)+
|
|
924
|
+
;
|
|
925
|
+
|
|
926
|
+
|
|
949
927
|
typeTypeOf[ art ] locals[ location ]
|
|
950
928
|
:
|
|
951
929
|
TYPE OF { location = this.locationOfPrevTokens( 2 ); }
|
|
@@ -1049,7 +1027,7 @@ targetCardinality[ card, atAlt = false ]
|
|
|
1049
1027
|
'*' { $card.targetMax = this.valueWithLocation(); }
|
|
1050
1028
|
|
|
|
1051
1029
|
Number { $card.targetMax = this.unsignedIntegerLiteral(); }
|
|
1052
|
-
(
|
|
1030
|
+
(<altRuleStart>) // TODO TOOL: robust error when moved to after '('
|
|
1053
1031
|
(
|
|
1054
1032
|
'..' { $card.targetMin = $card.targetMax; }
|
|
1055
1033
|
( '*' { $card.targetMax = this.valueWithLocation(); }
|
|
@@ -1059,20 +1037,6 @@ targetCardinality[ card, atAlt = false ]
|
|
|
1059
1037
|
)
|
|
1060
1038
|
;
|
|
1061
1039
|
|
|
1062
|
-
nullabilityAndDefault[ art ]
|
|
1063
|
-
:
|
|
1064
|
-
nullability[ $art ]
|
|
1065
|
-
( <cond=elementRestriction, arg=default> DEFAULT expr=expression
|
|
1066
|
-
{ $art.default = $expr; }
|
|
1067
|
-
)?
|
|
1068
|
-
|
|
|
1069
|
-
<cond=elementRestriction, arg=default> DEFAULT expr=expression
|
|
1070
|
-
{ $art.default = $expr; }
|
|
1071
|
-
nullability[ $art ]?
|
|
1072
|
-
// TODO TOOL: when `followUnion` does not contain `Id`, `RuleEnd_` does not
|
|
1073
|
-
// need to induce prediction (here for `default`).
|
|
1074
|
-
;
|
|
1075
|
-
|
|
1076
1040
|
nullability[ art ]
|
|
1077
1041
|
:
|
|
1078
1042
|
NULL { this.setNullability( $art, false ); }
|
|
@@ -1153,6 +1117,8 @@ selectQuery returns[ default query = {} ]
|
|
|
1153
1117
|
excludingClause[ $query ]?
|
|
1154
1118
|
|
|
|
1155
1119
|
( ALL/DISTINCT { $query.quantifier = this.valueWithLocation(); } )?
|
|
1120
|
+
// TODO TOOL: move <prepare> to all branches if "simple", or with special <…,attach>
|
|
1121
|
+
{;} <prepare=inSelectItem, arg=sqlStyle>
|
|
1156
1122
|
( '*' { $query.columns = [ this.valueWithLocation() ]; }
|
|
1157
1123
|
| selectItemDef[ ($query.columns = []) ]
|
|
1158
1124
|
)
|
|
@@ -1209,7 +1175,7 @@ tableExpression returns[ default expr = {} ] // TableOrJoin
|
|
|
1209
1175
|
tableOrQueryParens returns[ default expr ]
|
|
1210
1176
|
:
|
|
1211
1177
|
'(' <prepare=queryOnLeft>
|
|
1212
|
-
( <
|
|
1178
|
+
( <prefer> tableOrQueryParens[ ...$ ]
|
|
1213
1179
|
( tableExpression[ ...$ ]<atAltStart, prepare=queryOnLeft, arg=table>
|
|
1214
1180
|
| <cond=queryOnLeft> queryExpression[ ...$ ]<atAltStart>
|
|
1215
1181
|
)?
|
|
@@ -1498,8 +1464,6 @@ condition returns[ default expr ]
|
|
|
1498
1464
|
expression[ ...$ ]
|
|
1499
1465
|
;
|
|
1500
1466
|
|
|
1501
|
-
// TODO TOOL: sort rules if a rule with <altRuleStart> uses rule with <altRuleStart>
|
|
1502
|
-
// at least issue error if no user-sorted
|
|
1503
1467
|
valuePath returns[ default expr = { path: [] } ] locals[ pathItem ]
|
|
1504
1468
|
@finally{ this.attachLocation( $expr ); }
|
|
1505
1469
|
:
|
|
@@ -1619,7 +1583,7 @@ expression returns[ default expr = {} ]
|
|
|
1619
1583
|
expressionOrQueryParens returns[ default expr ]
|
|
1620
1584
|
:
|
|
1621
1585
|
'(' <prepare=queryOnLeft>
|
|
1622
|
-
( <
|
|
1586
|
+
( <prefer> expressionOrQueryParens[ ...$ ]
|
|
1623
1587
|
( expression[ ...$ ]<atAltStart, prepare=queryOnLeft, arg=expr>
|
|
1624
1588
|
continueExpressionslist[ ...$ ]?
|
|
1625
1589
|
| continueExpressionslist[ ...$ ] <prepare=queryOnLeft, arg=expr>
|
|
@@ -1732,7 +1696,7 @@ options{ minTokensMatched = 1 }
|
|
|
1732
1696
|
)*
|
|
1733
1697
|
)
|
|
1734
1698
|
)
|
|
1735
|
-
')'
|
|
1699
|
+
')' { this.finalizeDictOrArray( $pathStep.args ); }
|
|
1736
1700
|
)?
|
|
1737
1701
|
// TODO: not with function!
|
|
1738
1702
|
cardinalityAndFilter[ ...$ ]?
|
|
@@ -2000,7 +1964,8 @@ annoValue returns[ default value = {} ]
|
|
|
2000
1964
|
}
|
|
2001
1965
|
( ',' | <exitLoop> )
|
|
2002
1966
|
)*
|
|
2003
|
-
//
|
|
1967
|
+
// TODO TOOL: allow:
|
|
1968
|
+
// ( <cond=arrayAnno, …> '}' | <error> ) // TODO TOOL - workaround:
|
|
2004
1969
|
{ this.ec( 'arrayAnno', 'orNotEmpty' ); } '}'
|
|
2005
1970
|
// Do NOT use <prepare=afterBrace> here!
|
|
2006
1971
|
|
|
|
@@ -2011,11 +1976,19 @@ annoValue returns[ default value = {} ]
|
|
|
2011
1976
|
( sub=annoValue { $value.val.push( $sub ) }
|
|
2012
1977
|
|
|
|
2013
1978
|
<cond=arrayAnno, arg=ellipsis> ellipsis='...'
|
|
2014
|
-
( UP TO upTo=annoValue
|
|
2015
|
-
|
|
1979
|
+
( UP TO upTo=annoValue
|
|
1980
|
+
{ $value.val.push( { literal: 'token', val: '...', location: $ellipsis.location, upTo: $upTo } ); }
|
|
1981
|
+
| { $value.val.push( { literal: 'token', val: '...', location: $ellipsis.location } ); }
|
|
1982
|
+
)
|
|
1983
|
+
// TODO TOOL: if at last good state the command is ['g'],resume after the
|
|
1984
|
+
// gotos, do not execute its actions - ?
|
|
1985
|
+
// ( UP TO upTo=annoValue | { $upTo = undefined; } )
|
|
1986
|
+
// { $value.val.push( { literal: 'token', val: '...', location: $ellipsis.location, upTo: $upTo } ); }
|
|
2016
1987
|
)
|
|
2017
1988
|
( ',' | <exitLoop> )
|
|
2018
1989
|
)*
|
|
1990
|
+
// TODO TOOL: allow ( <cond=arrayAnno, arg=bracket> ']' )
|
|
1991
|
+
{ this.ec( 'arrayAnno', 'bracket' ); }<always>
|
|
2019
1992
|
']'
|
|
2020
1993
|
|
|
|
2021
1994
|
'(' $value=condition ')' { $value.$tokenTexts = this.ruleTokensText(); }
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// Entry point to parsers
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const lazyload = require('../base/lazyload')( module );
|
|
6
|
+
const { CompilerAssertion } = require( '../base/error' );
|
|
7
|
+
const { createMessageFunctions } = require( '../base/messages' );
|
|
8
|
+
const { XsnSource } = require('../compiler/xsn-model');
|
|
9
|
+
|
|
10
|
+
const parseWithAntlr = lazyload('../language/antlrParser');
|
|
11
|
+
const CdlLexer = require( './Lexer' );
|
|
12
|
+
const gen = lazyload( '../gen/CdlParser' );
|
|
13
|
+
|
|
14
|
+
const rules = {
|
|
15
|
+
cdl: { func: 'start', returns: 'source', $frontend: 'cdl' },
|
|
16
|
+
query: { func: 'queryEOF', returns: 'query' },
|
|
17
|
+
expr: { func: 'conditionEOF', returns: 'cond' }, // yes, condition
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function parseCdl( source, filename = '<undefined>.cds',
|
|
21
|
+
options = {}, messageFunctions = null,
|
|
22
|
+
rule = 'cdl' ) {
|
|
23
|
+
const rulespec = rules[rule];
|
|
24
|
+
if (!options.newParser)
|
|
25
|
+
return parseWithAntlr( source, filename, options, messageFunctions, rulespec );
|
|
26
|
+
const { CdlParser } = gen;
|
|
27
|
+
if (CdlParser.tracingParser) // tracing → direct console output of message
|
|
28
|
+
messageFunctions = createMessageFunctions( {}, 'parse', {} );
|
|
29
|
+
|
|
30
|
+
const lexer = new CdlLexer( filename, source );
|
|
31
|
+
const parser = new CdlParser( lexer, options, messageFunctions ).init();
|
|
32
|
+
parser.filename = filename; // LSP compatibility
|
|
33
|
+
|
|
34
|
+
// For LSP:
|
|
35
|
+
const { parseListener, attachTokens } = options;
|
|
36
|
+
if (parseListener || attachTokens)
|
|
37
|
+
setTokenStream( parser, lexer );
|
|
38
|
+
if (parseListener)
|
|
39
|
+
setParseListener( parser, parseListener );
|
|
40
|
+
|
|
41
|
+
const result = {};
|
|
42
|
+
try {
|
|
43
|
+
parser[rulespec.func]( result );
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
if (!(e instanceof RangeError && /Maximum.*exceeded$/i.test( e.message )))
|
|
47
|
+
throw e;
|
|
48
|
+
messageFunctions.error( 'syntax-invalid-source', { file: filename },
|
|
49
|
+
{ '#': 'cdl-stackoverflow' } );
|
|
50
|
+
result[rulespec.returns] = undefined;
|
|
51
|
+
}
|
|
52
|
+
const ast = result[rulespec?.returns] || (rule === 'cdl' ? new XsnSource( 'cdl' ) : {} );
|
|
53
|
+
ast.options = options;
|
|
54
|
+
if (attachTokens === true || attachTokens === filename)
|
|
55
|
+
ast.tokenStream = parser._input;
|
|
56
|
+
return ast;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function setTokenStream( parser, lexer ) {
|
|
60
|
+
const combined = [];
|
|
61
|
+
const { tokens, comments, docComments } = parser;
|
|
62
|
+
const length = tokens.length + comments.length + docComments.length;
|
|
63
|
+
let tokenIdx = 0;
|
|
64
|
+
let commentIdx = 0;
|
|
65
|
+
let docCommentIdx = 0;
|
|
66
|
+
for (let index = 0; index < length; ++index) {
|
|
67
|
+
if (tokens[tokenIdx].location.tokenIndex === index) // EOF has largest tokenIndex
|
|
68
|
+
combined.push( tokens[tokenIdx++] );
|
|
69
|
+
else if (comments[commentIdx]?.location.tokenIndex === index)
|
|
70
|
+
combined.push( comments[commentIdx++] );
|
|
71
|
+
else
|
|
72
|
+
combined.push( docComments[docCommentIdx++] );
|
|
73
|
+
}
|
|
74
|
+
if (!combined.at( -1 ))
|
|
75
|
+
throw new CompilerAssertion( 'Invalid values for `tokenIndex`' );
|
|
76
|
+
for (const tok of combined)
|
|
77
|
+
tok.start = lexer.characterPos( tok.location.line, tok.location.col );
|
|
78
|
+
|
|
79
|
+
parser._input = { tokens: combined, lexer }; // lexer for characterPos() in cdshi.js
|
|
80
|
+
parser.getTokenStream = function getTokenStream() {
|
|
81
|
+
return this._input;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function setParseListener( parser, parseListener ) {
|
|
86
|
+
const { CdlParser } = gen;
|
|
87
|
+
parser.rule_ = function rule_( ...args ) {
|
|
88
|
+
// TODO: can we use `super` here?
|
|
89
|
+
CdlParser.prototype.rule_.apply( this, args );
|
|
90
|
+
let state = this.s;
|
|
91
|
+
while (typeof this.table[--state] !== 'string')
|
|
92
|
+
;
|
|
93
|
+
const $ctx = { // ANTLR-like context, TODO LSP: more to add?
|
|
94
|
+
parser: this, // set in generated ANTLR parser for each rule context
|
|
95
|
+
ruleName: this.table[state], // instead of ruleIndex
|
|
96
|
+
start: this.la(), // set in Parser#enterRule
|
|
97
|
+
stop: null,
|
|
98
|
+
};
|
|
99
|
+
parser.stack.at( -1 ).$ctx = $ctx;
|
|
100
|
+
parseListener.enterEveryRule( $ctx );
|
|
101
|
+
};
|
|
102
|
+
parser.exit_ = function exit_( ...args ) {
|
|
103
|
+
const { $ctx } = parser.stack.at( -1 );
|
|
104
|
+
// TODO: what should we do in case of errors?
|
|
105
|
+
$ctx.stop = this.lb();
|
|
106
|
+
parseListener.exitEveryRule( $ctx );
|
|
107
|
+
return CdlParser.prototype.exit_.apply( this, args );
|
|
108
|
+
};
|
|
109
|
+
parser.c = function c( ...args ) { // consume
|
|
110
|
+
const symbol = this.la();
|
|
111
|
+
const result = CdlParser.prototype.c.apply( this, args );
|
|
112
|
+
if (result)
|
|
113
|
+
parseListener.visitTerminal( { symbol } );
|
|
114
|
+
return result;
|
|
115
|
+
};
|
|
116
|
+
parser.skipToken_ = function skipToken_( ...args ) { // skip token in error recovery
|
|
117
|
+
const symbol = this.la();
|
|
118
|
+
CdlParser.prototype.skipToken_.apply( this, args ); // = `++this.tokenIdx`
|
|
119
|
+
parseListener.visitErrorNode( { symbol } );
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = { parseCdl };
|
package/lib/render/toSql.js
CHANGED
|
@@ -559,13 +559,19 @@ function toSqlDdl( csn, options, messageFunctions ) {
|
|
|
559
559
|
const add = def.new.target
|
|
560
560
|
? render.addAssociations(artifactName, { [eltName]: def.new }, env)
|
|
561
561
|
: render.addColumnsFromElementsObj(artifactName, { [eltName]: def.new }, env);
|
|
562
|
-
addMigration(resultObj, artifactName, true, render.concat(...drop, ...add).map(s => (def.lossy && options.src !== 'hdi' ? `-- [WARNING] this statement could be lossy\n${s}` : s)));
|
|
562
|
+
addMigration(resultObj, artifactName, true, render.concat(...drop, ...add).map(s => (def.lossy !== undefined && options.src !== 'hdi' ? `-- [WARNING] this statement could ${def.lossy ? 'be lossy' : 'fail'}: ${def.details}\n${s}` : s)));
|
|
563
563
|
}
|
|
564
564
|
else { // Lossless change: no associations directly affected, no size reduction.
|
|
565
|
-
addMigration(resultObj, artifactName, false, render.alterColumns(artifactName, sqlId, def, eltStrNew, eltName, activateAlterMode(env, 'migration')).map(s => (def.lossy ? `-- [WARNING] this statement could be lossy\n${s}` : s)));
|
|
565
|
+
addMigration(resultObj, artifactName, false, render.alterColumns(artifactName, sqlId, def, eltStrNew, eltName, activateAlterMode(env, 'migration')).map(s => (def.lossy !== undefined ? `-- [WARNING] this statement could ${def.lossy ? 'be lossy' : 'fail'}: ${def.details}\n${s}` : s)));
|
|
566
566
|
}
|
|
567
567
|
}
|
|
568
568
|
}
|
|
569
|
+
|
|
570
|
+
if (render.getConsolidatedAlterColumn) {
|
|
571
|
+
const consolidated = render.getConsolidatedAlterColumn(artifactName);
|
|
572
|
+
if (consolidated)
|
|
573
|
+
addMigration(resultObj, artifactName, false, consolidated);
|
|
574
|
+
}
|
|
569
575
|
}
|
|
570
576
|
|
|
571
577
|
/**
|
|
@@ -72,6 +72,15 @@ class DeltaRenderer {
|
|
|
72
72
|
* Render column modifications as SQL.
|
|
73
73
|
*/
|
|
74
74
|
alterColumns(artifactName, columnName, delta, definitionsStr, _eltName, _env) {
|
|
75
|
+
if (Array.isArray(definitionsStr)) {
|
|
76
|
+
const prefix = `ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} ALTER (`;
|
|
77
|
+
let padding = '';
|
|
78
|
+
for (let i = 0; i < prefix.length; i++)
|
|
79
|
+
padding += ' ';
|
|
80
|
+
const body = definitionsStr.map(s => padding + s).join(',\n').slice(padding.length); // no padding for first part
|
|
81
|
+
const postfix = ');';
|
|
82
|
+
return [ prefix + body + postfix ];
|
|
83
|
+
}
|
|
75
84
|
return [ `ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} ALTER (${definitionsStr});` ];
|
|
76
85
|
}
|
|
77
86
|
|
|
@@ -123,6 +132,29 @@ class DeltaRenderer {
|
|
|
123
132
|
}
|
|
124
133
|
|
|
125
134
|
class DeltaRendererHana extends DeltaRenderer {
|
|
135
|
+
#alters = [];
|
|
136
|
+
#details = [];
|
|
137
|
+
|
|
138
|
+
getConsolidatedAlterColumn(artifactName) {
|
|
139
|
+
if (this.#alters.length === 0)
|
|
140
|
+
return null;
|
|
141
|
+
const result = [ ...this.#details, ...super.alterColumns(artifactName, null, null, this.#alters) ];
|
|
142
|
+
this.#alters = [];
|
|
143
|
+
this.#details = [];
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Render column modifications as SQL.
|
|
149
|
+
*/
|
|
150
|
+
alterColumns(artifactName, columnName, delta, definitionsStr, _eltName, _env) {
|
|
151
|
+
if (delta.details)
|
|
152
|
+
this.#details.push(`-- [WARNING] this statement could ${delta.lossy ? 'be lossy' : 'fail'}: ${delta.details}`);
|
|
153
|
+
|
|
154
|
+
this.#alters.push(definitionsStr);
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
|
|
126
158
|
/**
|
|
127
159
|
* Render column additions as HANA SQL. Checks for duplicate elements.
|
|
128
160
|
*/
|
|
@@ -230,7 +262,7 @@ class DeltaRendererH2 extends DeltaRenderer {
|
|
|
230
262
|
/**
|
|
231
263
|
* Render column modifications as H2 SQL - no ().
|
|
232
264
|
*/
|
|
233
|
-
alterColumns(artifactName, columnName, delta, definitionsStr) {
|
|
265
|
+
alterColumns(artifactName, columnName, delta, definitionsStr, _eltName, _env) {
|
|
234
266
|
return [ `ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} ALTER ${definitionsStr};` ];
|
|
235
267
|
}
|
|
236
268
|
}
|