@sap/cds-compiler 3.0.0 → 3.1.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 +104 -9
- package/bin/.eslintrc.json +2 -1
- package/bin/cdsc.js +28 -16
- package/doc/API.md +11 -0
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +24 -2
- package/doc/CHANGELOG_DEPRECATED.md +21 -1
- package/lib/api/main.js +92 -40
- package/lib/api/options.js +2 -3
- package/lib/base/keywords.js +64 -1
- package/lib/base/message-registry.js +33 -5
- package/lib/base/messages.js +54 -65
- package/lib/base/model.js +2 -0
- package/lib/base/optionProcessorHelper.js +53 -21
- package/lib/checks/actionsFunctions.js +8 -7
- package/lib/checks/selectItems.js +96 -14
- package/lib/checks/types.js +5 -8
- package/lib/checks/validator.js +1 -2
- package/lib/compiler/assert-consistency.js +65 -13
- package/lib/compiler/base.js +6 -4
- package/lib/compiler/builtins.js +93 -4
- package/lib/compiler/checks.js +1 -1
- package/lib/compiler/define.js +28 -23
- package/lib/compiler/extend.js +20 -11
- package/lib/compiler/finalize-parse-cdl.js +5 -9
- package/lib/compiler/index.js +2 -0
- package/lib/compiler/populate.js +37 -32
- package/lib/compiler/propagator.js +11 -6
- package/lib/compiler/resolve.js +15 -19
- package/lib/compiler/shared.js +54 -18
- package/lib/compiler/tweak-assocs.js +5 -11
- package/lib/compiler/utils.js +15 -6
- package/lib/edm/annotations/genericTranslation.js +12 -2
- package/lib/edm/annotations/preprocessAnnotations.js +18 -15
- package/lib/edm/csn2edm.js +18 -17
- package/lib/edm/edm.js +22 -13
- package/lib/edm/edmAnnoPreprocessor.js +349 -0
- package/lib/edm/edmInboundChecks.js +85 -0
- package/lib/edm/edmPreprocessor.js +336 -665
- package/lib/edm/edmUtils.js +86 -45
- package/lib/gen/Dictionary.json +29 -9
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -2
- package/lib/gen/languageLexer.js +3 -0
- package/lib/gen/languageParser.js +4332 -4496
- package/lib/inspect/.eslintrc.json +4 -0
- package/lib/inspect/index.js +14 -0
- package/lib/inspect/inspectModelStatistics.js +81 -0
- package/lib/inspect/inspectPropagation.js +189 -0
- package/lib/inspect/inspectUtils.js +44 -0
- package/lib/json/from-csn.js +19 -20
- package/lib/json/to-csn.js +11 -8
- package/lib/language/genericAntlrParser.js +150 -92
- package/lib/language/language.g4 +47 -74
- package/lib/main.d.ts +1 -0
- package/lib/model/api.js +1 -1
- package/lib/model/csnRefs.js +56 -29
- package/lib/model/csnUtils.js +29 -14
- package/lib/model/revealInternalProperties.js +6 -4
- package/lib/modelCompare/compare.js +3 -0
- package/lib/optionProcessor.js +81 -38
- package/lib/render/toCdl.js +57 -32
- package/lib/render/toHdbcds.js +1 -1
- package/lib/render/toSql.js +31 -11
- package/lib/render/utils/common.js +3 -4
- package/lib/transform/db/associations.js +43 -35
- package/lib/transform/db/cdsPersistence.js +0 -1
- package/lib/transform/db/flattening.js +3 -4
- package/lib/transform/db/transformExists.js +7 -5
- package/lib/transform/draft/db.js +1 -1
- package/lib/transform/forHanaNew.js +11 -2
- package/lib/transform/forOdataNew.js +4 -4
- package/lib/transform/localized.js +15 -11
- package/lib/transform/odata/typesExposure.js +14 -5
- package/lib/utils/file.js +28 -18
- package/lib/utils/moduleResolve.js +0 -1
- package/package.json +3 -4
- package/share/messages/syntax-expected-integer.md +9 -8
- package/lib/checks/unknownMagic.js +0 -41
package/lib/language/language.g4
CHANGED
|
@@ -385,10 +385,9 @@ artifactDef[ outer, defOnly = false ] locals[ art = {} ] // cannot use `parent`
|
|
|
385
385
|
}
|
|
386
386
|
// #ATN: EXTEND elem, while CONTEXT, ENTITY etc are not reserved
|
|
387
387
|
( extendContext[ $art, $outer ]
|
|
388
|
-
| extendEntity[ $art, $outer ]
|
|
388
|
+
| extendEntity[ $art, $outer ] // or aspect
|
|
389
389
|
| extendProjection[ $art, $outer ]
|
|
390
390
|
| extendType[ $art, $outer ]
|
|
391
|
-
| extendAspect[ $art, $outer ]
|
|
392
391
|
// Streamlined Syntax
|
|
393
392
|
| extendArtifact[ $art, $outer ]
|
|
394
393
|
)
|
|
@@ -398,6 +397,7 @@ artifactDef[ outer, defOnly = false ] locals[ art = {} ] // cannot use `parent`
|
|
|
398
397
|
this.error( 'syntax-extend-context', $annotate,
|
|
399
398
|
{ code: 'ANNOTATE artifact', kind: defOnly },
|
|
400
399
|
'No $(CODE) within $(KIND) extensions' );
|
|
400
|
+
if (!$outer.extensions) $outer.extensions = [];
|
|
401
401
|
this.meltKeywordToIdentifier();
|
|
402
402
|
}
|
|
403
403
|
annotateArtifact[ $art, $outer ] // not kind-specific
|
|
@@ -543,11 +543,12 @@ projectionExclusion[ outer ] locals[ art = {} ]
|
|
|
543
543
|
{ this.addDef( $art, $outer, 'excludingDict', '', $name.id ); }
|
|
544
544
|
;
|
|
545
545
|
|
|
546
|
+
// also used for aspect
|
|
546
547
|
extendEntity[ art, outer ] locals[ name = {} ]
|
|
547
548
|
@after { /* #ATN 1 */ this.attachLocation( $art ); }
|
|
548
549
|
:
|
|
549
|
-
ENTITY simplePath[ $name, 'Extend' ]
|
|
550
|
-
{ $art.expectedKind =
|
|
550
|
+
kind=(ASPECT | ENTITY) simplePath[ $name, 'Extend' ]
|
|
551
|
+
{ $art.expectedKind = $kind.text.toLowerCase(); $art.name = $name;
|
|
551
552
|
this.addItem( $art, $outer, 'extensions', 'extend' );
|
|
552
553
|
}
|
|
553
554
|
(
|
|
@@ -555,7 +556,7 @@ extendEntity[ art, outer ] locals[ name = {} ]
|
|
|
555
556
|
annotationAssignment_ll1[ $art ]*
|
|
556
557
|
// ATN: the ref can start with ACTIONS
|
|
557
558
|
(
|
|
558
|
-
includeRef[ $art ]
|
|
559
|
+
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
559
560
|
requiredSemi
|
|
560
561
|
|
|
|
561
562
|
extendForEntity[ $art ]
|
|
@@ -753,17 +754,6 @@ extendType[ art, outer ] locals[ name = {} ]
|
|
|
753
754
|
extendWithOptElements[ $art, $art ]
|
|
754
755
|
;
|
|
755
756
|
|
|
756
|
-
extendAspect[ art, outer ] locals[ name = {} ]
|
|
757
|
-
@after { this.attachLocation( $art ); }
|
|
758
|
-
:
|
|
759
|
-
// aspects are types, i.e. kind is 'type' for aspects
|
|
760
|
-
ASPECT simplePath[ $name, 'Extend' ]
|
|
761
|
-
{ $art.expectedKind = 'aspect'; $art.name = $name;
|
|
762
|
-
this.addItem( $art, $outer, 'extensions', 'extend' );
|
|
763
|
-
}
|
|
764
|
-
extendWithOptElements[ $art, $art ]
|
|
765
|
-
;
|
|
766
|
-
|
|
767
757
|
annotationDef[ art, outer ] locals[ name = {} ]
|
|
768
758
|
@after { this.attachLocation( $art ); }
|
|
769
759
|
:
|
|
@@ -793,6 +783,7 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
793
783
|
'{' { $art.elements = this.createDict(); }
|
|
794
784
|
elementDefOrExtend[ $art ]*
|
|
795
785
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
786
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
796
787
|
optionalSemi
|
|
797
788
|
|
|
|
798
789
|
requiredSemi
|
|
@@ -803,12 +794,13 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
803
794
|
// #ATN: DEFINITIONS, COLUMNS, ACTIONS etc are not reserved and could be identifiers (ref).
|
|
804
795
|
// TODO: exclude "expected" according to disallowElementExtension()
|
|
805
796
|
(
|
|
806
|
-
includeRef[ $art ]
|
|
797
|
+
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
807
798
|
requiredSemi
|
|
808
799
|
|
|
|
809
800
|
'{' { $art.elements = this.createDict(); }
|
|
810
801
|
elementDefOrExtend[ $art ]*
|
|
811
802
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
803
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
812
804
|
optionalSemi
|
|
813
805
|
|
|
|
814
806
|
requiredSemi
|
|
@@ -834,17 +826,18 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
834
826
|
|
|
|
835
827
|
{ this.disallowElementExtension( $elemName, $outer, 'actions' ); }
|
|
836
828
|
ACTIONS '{' { $art.actions = this.createDict(); }
|
|
837
|
-
actionFunctionDef[ $art ]*
|
|
829
|
+
actionFunctionDef[ $art ]* // TODO: no EXTEND in actions? (ok, would just allow annos)
|
|
838
830
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
839
831
|
optionalSemi
|
|
840
832
|
|
|
|
841
833
|
ELEMENTS '{' { $art.elements = this.createDict(); }
|
|
842
834
|
elementDefOrExtend[ $art ]*
|
|
843
835
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
836
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
844
837
|
optionalSemi
|
|
845
838
|
|
|
|
846
839
|
ENUM '{' { $art.enum = this.createDict(); }
|
|
847
|
-
enumSymbolDef[ $art ]*
|
|
840
|
+
enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
|
|
848
841
|
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
849
842
|
optionalSemi
|
|
850
843
|
)
|
|
@@ -856,12 +849,13 @@ extendWithOptElements[ art ]
|
|
|
856
849
|
WITH { this.noSemicolonHere(); this.docComment( $art ); }
|
|
857
850
|
annotationAssignment_ll1[ $art ]*
|
|
858
851
|
(
|
|
859
|
-
includeRef[ $art ]
|
|
852
|
+
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
860
853
|
requiredSemi
|
|
861
854
|
|
|
|
862
855
|
'{' { $art.elements = this.createDict(); }
|
|
863
856
|
elementDefOrExtend[ $art ]*
|
|
864
857
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
858
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
865
859
|
optionalSemi
|
|
866
860
|
|
|
|
867
861
|
requiredSemi
|
|
@@ -873,6 +867,7 @@ extendWithOptElements[ art ]
|
|
|
873
867
|
'{' { $art.elements = this.createDict(); }
|
|
874
868
|
elementDefOrExtend[ $art ]*
|
|
875
869
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
870
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
876
871
|
optionalSemi
|
|
877
872
|
|
|
|
878
873
|
requiredSemi
|
|
@@ -892,11 +887,13 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
892
887
|
'{' { $art.elements = this.createDict(); }
|
|
893
888
|
annotateElement[ $art ]*
|
|
894
889
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
890
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
895
891
|
(
|
|
896
892
|
ACTIONS
|
|
897
893
|
'{' { $art.actions = this.createDict(); }
|
|
898
894
|
annotateAction[ $art ]*
|
|
899
895
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
896
|
+
{ this.checkExtensionDict( $art.actions ); }
|
|
900
897
|
)?
|
|
901
898
|
optionalSemi
|
|
902
899
|
|
|
|
@@ -904,6 +901,7 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
904
901
|
'{' { $art.actions = this.createDict(); }
|
|
905
902
|
annotateAction[ $art ]*
|
|
906
903
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
904
|
+
{ this.checkExtensionDict( $art.actions ); }
|
|
907
905
|
optionalSemi
|
|
908
906
|
|
|
|
909
907
|
'(' { $art.params = this.createDict(); }
|
|
@@ -912,11 +910,13 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
912
910
|
annotateParam[ $art ]
|
|
913
911
|
)*
|
|
914
912
|
')' { this.finalizeDictOrArray( $art.params ); }
|
|
913
|
+
{ this.checkExtensionDict( $art.params ); }
|
|
915
914
|
(
|
|
916
915
|
RETURNS { $art['$'+'syntax'] = 'returns'; }
|
|
917
916
|
'{' { $art.elements = this.createDict(); }
|
|
918
917
|
annotateElement[ $art ]*
|
|
919
918
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
919
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
920
920
|
optionalSemi
|
|
921
921
|
|
|
|
922
922
|
requiredSemi
|
|
@@ -926,6 +926,7 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
926
926
|
'{' { $art.elements = this.createDict(); }
|
|
927
927
|
annotateElement[ $art ]*
|
|
928
928
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
929
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
929
930
|
optionalSemi
|
|
930
931
|
|
|
931
932
|
|
|
|
@@ -946,6 +947,7 @@ annotateElement[ outer ] locals[ art = {} ]
|
|
|
946
947
|
'{' { $art.elements = this.createDict(); }
|
|
947
948
|
annotateElement[ $art ]*
|
|
948
949
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
950
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
949
951
|
optionalSemi
|
|
950
952
|
|
|
|
951
953
|
requiredSemi
|
|
@@ -968,11 +970,13 @@ annotateAction [ outer ] locals [ art = {} ]
|
|
|
968
970
|
annotateParam[ $art ]
|
|
969
971
|
)*
|
|
970
972
|
')' { this.finalizeDictOrArray( $art.params ); }
|
|
973
|
+
{ this.checkExtensionDict( $art.params ); }
|
|
971
974
|
)?
|
|
972
975
|
(
|
|
973
976
|
RETURNS '{' { $art.elements = this.createDict(); }
|
|
974
977
|
annotateElement[ $art ]*
|
|
975
978
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
979
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
976
980
|
optionalSemi
|
|
977
981
|
|
|
|
978
982
|
requiredSemi
|
|
@@ -1543,46 +1547,14 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1543
1547
|
|
|
|
1544
1548
|
// alt lookahead includes MANY '{'
|
|
1545
1549
|
{ $art.type = {}; }
|
|
1550
|
+
// Can't use typeRefOptArgs because of clash with include rule below (ATN would change)
|
|
1546
1551
|
simplePath[ $art.type, 'artref' ]
|
|
1547
1552
|
(
|
|
1548
|
-
typeRefArgs[ $art ]
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
enumSymbolDef[ $art ]*
|
|
1554
|
-
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
1555
|
-
(
|
|
1556
|
-
optionalSemi
|
|
1557
|
-
|
|
|
1558
|
-
defaultValue[ $art ]
|
|
1559
|
-
requiredSemi
|
|
1560
|
-
)
|
|
1561
|
-
|
|
|
1562
|
-
defaultValue[ $art ]?
|
|
1563
|
-
requiredSemi
|
|
1564
|
-
)
|
|
1565
|
-
|
|
|
1566
|
-
':' // with element, e.g. `type T : E:elem enum { ... }`
|
|
1567
|
-
{ $art.type.scope = $art.type.path.length; }
|
|
1568
|
-
simplePath[ $art.type, 'ref']
|
|
1569
|
-
{ this.docComment( $art ); }
|
|
1570
|
-
annotationAssignment_ll1[ $art ]*
|
|
1571
|
-
(
|
|
1572
|
-
ENUM '{' { $art.enum = this.createDict(); }
|
|
1573
|
-
enumSymbolDef[ $art ]*
|
|
1574
|
-
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
1575
|
-
(
|
|
1576
|
-
optionalSemi
|
|
1577
|
-
|
|
|
1578
|
-
defaultValue[ $art ]
|
|
1579
|
-
requiredSemi
|
|
1580
|
-
)
|
|
1581
|
-
|
|
|
1582
|
-
defaultValue[ $art ]?
|
|
1583
|
-
requiredSemi
|
|
1584
|
-
)
|
|
1585
|
-
|
|
|
1553
|
+
( typeRefArgs[ $art ]
|
|
1554
|
+
| ':' // with element, e.g. `type T : E:elem enum { ... }`
|
|
1555
|
+
{ $art.type.scope = $art.type.path.length; }
|
|
1556
|
+
simplePath[ $art.type, 'ref']
|
|
1557
|
+
)?
|
|
1586
1558
|
{ this.docComment( $art ); }
|
|
1587
1559
|
annotationAssignment_ll1[ $art ]*
|
|
1588
1560
|
(
|
|
@@ -1831,7 +1803,7 @@ typeNamedArg[ art ] locals[ arg = '' ]
|
|
|
1831
1803
|
:
|
|
1832
1804
|
name=ident['paramname']
|
|
1833
1805
|
':'
|
|
1834
|
-
{ if (this.checkTypeFacet( $art, $name.id ))
|
|
1806
|
+
{ if ($name.id && this.checkTypeFacet( $art, $name.id ))
|
|
1835
1807
|
$arg = $name.id.id;
|
|
1836
1808
|
}
|
|
1837
1809
|
(
|
|
@@ -1868,10 +1840,10 @@ queryExpression returns[ query ] // QLSubqueryComplex, SubqueryComplex
|
|
|
1868
1840
|
| op=MINUS q=DISTINCT?
|
|
1869
1841
|
)
|
|
1870
1842
|
qt=queryTerm
|
|
1871
|
-
{ $query = this.leftAssocBinaryOp( $query, $op, $q, $qt.query );; $ctx.q = null; }
|
|
1843
|
+
{ if ($qt.query) $query = this.leftAssocBinaryOp( $query, $op, $q, $qt.query );; $ctx.q = null; }
|
|
1872
1844
|
)*
|
|
1873
|
-
( ob=orderByClause[ $query ] { $query = $ob.query; } ) ?
|
|
1874
|
-
( lc=limitClause[ $query ] { $query = $lc.query; } ) ?
|
|
1845
|
+
( ob=orderByClause[ $query ] { if ($ob.query) $query = $ob.query; } ) ?
|
|
1846
|
+
( lc=limitClause[ $query ] { if ($lc.query) $query = $lc.query; } ) ?
|
|
1875
1847
|
;
|
|
1876
1848
|
|
|
1877
1849
|
orderByClause[ inQuery ] returns [ query ]
|
|
@@ -2075,7 +2047,7 @@ tableExpression returns[ table ] // TableOrJoin
|
|
|
2075
2047
|
ON cond=condition { $table.on = $cond.cond; }
|
|
2076
2048
|
|
|
|
2077
2049
|
crj=CROSS jn=JOIN tt=tableTerm
|
|
2078
|
-
{ $table = this.leftAssocBinaryOp( $table, $jn, $crj, $tt.table, 'join' ); }
|
|
2050
|
+
{ if (!$table) { $table = {}; } $table = this.leftAssocBinaryOp( $table, $jn, $crj, $tt.table, 'join' ); }
|
|
2079
2051
|
)*
|
|
2080
2052
|
;
|
|
2081
2053
|
|
|
@@ -2257,9 +2229,11 @@ conditionTerm returns [ cond ]
|
|
|
2257
2229
|
|
|
|
2258
2230
|
{ $cond = { args: [ $expr.expr ] }; }
|
|
2259
2231
|
NOT predicate[ $cond, true ]
|
|
2232
|
+
{ if (!$cond.op) $cond = null; } // predicate failed to parse, avoid subseqential errors
|
|
2260
2233
|
|
|
|
2261
2234
|
{ $cond = { args: [ $expr.expr ] }; }
|
|
2262
2235
|
predicate[ $cond, false ]
|
|
2236
|
+
{ if (!$cond.op) $cond = null; } // predicate failed to parse, avoid subseqential errors
|
|
2263
2237
|
)? // optional: for conditions in parentheses
|
|
2264
2238
|
;
|
|
2265
2239
|
|
|
@@ -2464,7 +2438,12 @@ pathArguments[ pathStep, considerSpecial ]
|
|
|
2464
2438
|
'(' // dict or array, see below
|
|
2465
2439
|
// Make sure that we do not introduce A:B paths in expressions!
|
|
2466
2440
|
// Need to avoid adaptPredict(), otherwise Generic keywords won't work in funcExpression
|
|
2467
|
-
|
|
2441
|
+
//
|
|
2442
|
+
// For code completion, we need to handle generic tokens directly after the
|
|
2443
|
+
// '('. To avoid invalidating an assoc `trim` to an entity with parameter
|
|
2444
|
+
// `leading` (ok, a bit constructed), we do not do it with named parameters.
|
|
2445
|
+
{ if (!this.setLocalTokenForId( { ':': 'HelperToken1', '=>': 'HelperToken2' } ))
|
|
2446
|
+
this.prepareGenericKeywords( $considerSpecial ); }
|
|
2468
2447
|
(
|
|
2469
2448
|
{ $pathStep.args = this.createDict(); $pathStep['$'+'syntax'] = ':'; }
|
|
2470
2449
|
id=HelperToken1 ':'
|
|
@@ -2687,10 +2666,7 @@ annoValueBase[ assignment ] locals [ seenEllipsis = false ]
|
|
|
2687
2666
|
flattenedValue[ assignment ] locals[ val = { name: {} } ]
|
|
2688
2667
|
:
|
|
2689
2668
|
at='@'? annotationPath[ $val.name, 'name', $at ]
|
|
2690
|
-
(
|
|
2691
|
-
'#' { this.meltKeywordToIdentifier(); }
|
|
2692
|
-
variant=ident['variant'] { $val.name.variant = $variant.id; }
|
|
2693
|
-
)?
|
|
2669
|
+
( annotationPathVariant[ $val.name ] )?
|
|
2694
2670
|
(
|
|
2695
2671
|
':' { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
|
|
2696
2672
|
annoValue[ $val ]
|
|
@@ -2739,10 +2715,7 @@ annoSubValue returns[ val = {} ]
|
|
|
2739
2715
|
{ Object.assign( $val, this.numberLiteral( $num, $plus||$min ) ); }
|
|
2740
2716
|
|
|
|
2741
2717
|
at='@'? annotationPath[ $val, 'ref', $at ]
|
|
2742
|
-
(
|
|
2743
|
-
'#' { this.meltKeywordToIdentifier(); }
|
|
2744
|
-
variant=ident['variant'] { $val.variant = $variant.id; }
|
|
2745
|
-
)?
|
|
2718
|
+
( annotationPathVariant[ $val ] )?
|
|
2746
2719
|
;
|
|
2747
2720
|
|
|
2748
2721
|
literalValue returns[ val ] locals[ tok ]
|
|
@@ -2800,12 +2773,12 @@ annotationPath[ art, category, headat = null ] locals[ _sync = 'nop' ]
|
|
|
2800
2773
|
)*
|
|
2801
2774
|
;
|
|
2802
2775
|
|
|
2803
|
-
annotationPathVariant[ art ]
|
|
2776
|
+
annotationPathVariant[ art ] locals[ variant = {} ]
|
|
2804
2777
|
@after { this.attachLocation($art); }
|
|
2805
2778
|
:
|
|
2806
2779
|
// TODO: warning for space after '#'
|
|
2807
2780
|
'#' { this.meltKeywordToIdentifier(); }
|
|
2808
|
-
variant
|
|
2781
|
+
simplePath[ $variant, 'variant' ] { $art.variant = $variant; }
|
|
2809
2782
|
;
|
|
2810
2783
|
|
|
2811
2784
|
// Identifier and non-reserved keywords --------------------------------------
|
package/lib/main.d.ts
CHANGED
package/lib/model/api.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* Each function in `userFunctions` and `defaultFunctions` is called with:
|
|
12
12
|
* - `userFunctions`
|
|
13
13
|
* - the current CSN node, i.e. ‹parent node›.‹property name›
|
|
14
|
-
* - the
|
|
14
|
+
* - the ‹parent node›
|
|
15
15
|
* - the ‹property name› (might be useful if the same function is used for several props)
|
|
16
16
|
*/
|
|
17
17
|
const defaultFunctions = {
|
package/lib/model/csnRefs.js
CHANGED
|
@@ -210,8 +210,11 @@ const referenceSemantics = {
|
|
|
210
210
|
ref_where: { lexical: justDollar , dynamic: 'ref-target'}, // ...using baseEnv
|
|
211
211
|
on: { lexical: justDollar, dynamic: 'query' }, // assoc defs, redirected to
|
|
212
212
|
// there are also 'on_join' and 'on_mixin' with default semantics
|
|
213
|
-
|
|
214
|
-
|
|
213
|
+
orderBy_ref: { lexical: query => query, dynamic: 'query' },
|
|
214
|
+
orderBy_expr: { lexical: query => query, dynamic: 'source' }, // ref in ORDER BY expression
|
|
215
|
+
orderBy_set_ref: { lexical: query => query.$next, dynamic: 'query' }, // to outer SELECT (from UNION)
|
|
216
|
+
// refs in ORDER BY expr in UNION not really allowed - only with table alias (of outer queries) or $self
|
|
217
|
+
orderBy_set_expr: { lexical: query => query.$next, dynamic: false },
|
|
215
218
|
// default: { lexical: query => query, dynamic: 'source' }
|
|
216
219
|
}
|
|
217
220
|
|
|
@@ -551,37 +554,39 @@ function csnRefs( csn, universalReady ) {
|
|
|
551
554
|
}
|
|
552
555
|
}
|
|
553
556
|
// now the dynamic environment: ------------------------------------------
|
|
554
|
-
if (semantics.dynamic
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
557
|
+
if (semantics.dynamic !== false) {
|
|
558
|
+
if (semantics.dynamic === 'target') { // ref in keys
|
|
559
|
+
const target = assocTarget( parent, refCtx );
|
|
560
|
+
return resolvePath( path, target.elements[head], target, 'target' );
|
|
561
|
+
}
|
|
562
|
+
if (baseEnv) // ref-target (filter condition), expand, inline
|
|
563
|
+
return resolvePath( path, baseEnv.elements[head], baseEnv, semantics.dynamic );
|
|
564
|
+
if (!query) { // outside queries - TODO: items?
|
|
565
|
+
let art = parent.elements[head];
|
|
566
|
+
// Ref to up_ in anonymous aspect
|
|
567
|
+
if (!art && head === 'up_') {
|
|
568
|
+
const up = getCache( parent, '_parent' );
|
|
569
|
+
const target = up && typeof up.target === 'string' && csn.definitions[up.target];
|
|
570
|
+
if (target && target.elements) {
|
|
571
|
+
initDefinition( target );
|
|
572
|
+
art = target.elements.up_;
|
|
573
|
+
}
|
|
569
574
|
}
|
|
575
|
+
return resolvePath( path, art, parent, 'parent' );
|
|
570
576
|
}
|
|
571
|
-
return resolvePath( path, art, parent, 'parent' );
|
|
572
|
-
}
|
|
573
577
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
578
|
+
if (semantics.dynamic === 'query')
|
|
579
|
+
// TODO: for ON condition in expand, would need to use cached _element
|
|
580
|
+
return resolvePath( path, qcache.elements[head], null, 'query' );
|
|
581
|
+
for (const name in qcache.$aliases) {
|
|
582
|
+
const alias = qcache.$aliases[name];
|
|
583
|
+
const found = alias.elements[head];
|
|
584
|
+
if (found)
|
|
585
|
+
return resolvePath( path, found, alias._ref, 'source', name )
|
|
586
|
+
}
|
|
582
587
|
}
|
|
583
588
|
// console.log(query.SELECT,qcache,qcache.$next,main)
|
|
584
|
-
throw new ModelError ( `Path item
|
|
589
|
+
throw new ModelError ( `Path item 0=${ head } refers to nothing, refCtx: ${ refCtx }` );
|
|
585
590
|
}
|
|
586
591
|
|
|
587
592
|
/**
|
|
@@ -907,6 +912,7 @@ function startCsnPath( csnPath, csn ) {
|
|
|
907
912
|
/**
|
|
908
913
|
* @param {CSN.Path} csnPath
|
|
909
914
|
* @param {CSN.Model} csn
|
|
915
|
+
* @param {any} resolve
|
|
910
916
|
*/
|
|
911
917
|
function analyseCsnPath( csnPath, csn, resolve ) {
|
|
912
918
|
/** @type {object} */
|
|
@@ -932,6 +938,15 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
932
938
|
parent = art;
|
|
933
939
|
art = obj[prop];
|
|
934
940
|
}
|
|
941
|
+
else if (refCtx === 'orderBy') {
|
|
942
|
+
const isSelect = isSelectQuery( query );
|
|
943
|
+
// use _query_ elements with direct refs (consider sub-optimal CSN,
|
|
944
|
+
// representation of the CAST function), otherwise source elements:
|
|
945
|
+
if (obj[prop].ref && !obj[prop].cast)
|
|
946
|
+
refCtx = (isSelect ? 'orderBy_ref' : 'orderBy_set_ref');
|
|
947
|
+
else
|
|
948
|
+
refCtx = (isSelect ? 'orderBy_expr' : 'orderBy_set_expr');
|
|
949
|
+
}
|
|
935
950
|
isName = false;
|
|
936
951
|
}
|
|
937
952
|
else if (artifactProperties.includes( String(prop) )) {
|
|
@@ -982,7 +997,7 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
982
997
|
refCtx = prop;
|
|
983
998
|
}
|
|
984
999
|
else if (prop === 'orderBy') {
|
|
985
|
-
refCtx =
|
|
1000
|
+
refCtx = 'orderBy';
|
|
986
1001
|
}
|
|
987
1002
|
else if (prop !== 'xpr') {
|
|
988
1003
|
refCtx = prop;
|
|
@@ -995,6 +1010,18 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
995
1010
|
return resolve( obj, refCtx, main, query, parent, baseEnv );
|
|
996
1011
|
}
|
|
997
1012
|
|
|
1013
|
+
// A SELECT which is (unnecessarily) put into parentheses, the CSN
|
|
1014
|
+
// representation uses SET without `op` and args of length 1:
|
|
1015
|
+
function isSelectQuery( query ) {
|
|
1016
|
+
while (query.SET) {
|
|
1017
|
+
const { args } = query.SET;
|
|
1018
|
+
if (args.length !== 1)
|
|
1019
|
+
return false;
|
|
1020
|
+
query = args[0];
|
|
1021
|
+
}
|
|
1022
|
+
return true;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
998
1025
|
module.exports = {
|
|
999
1026
|
csnRefs,
|
|
1000
1027
|
traverseQuery,
|
package/lib/model/csnUtils.js
CHANGED
|
@@ -492,15 +492,18 @@ function getUtils(model, universalReady) {
|
|
|
492
492
|
|
|
493
493
|
/**
|
|
494
494
|
* Resolve to the final type of a type, that means follow type chains, references, etc.
|
|
495
|
-
* Input is a type name, i.e. string, or type ref, i.e. `{ ref: [...] }`.
|
|
495
|
+
* Input is a fully qualified type name, i.e. string, or type ref, i.e. `{ ref: [...] }`.
|
|
496
496
|
*
|
|
497
497
|
* Returns `null` if the type can't be resolved or if the referenced element has no type,
|
|
498
498
|
* e.g. `typeof V:calculated`.
|
|
499
499
|
* Otherwise, if scalar, returns an object that has a `type` property and all collected type
|
|
500
500
|
* properties, or the type object with `elements` or `items` property if structured/arrayed.
|
|
501
501
|
*
|
|
502
|
-
*
|
|
503
|
-
*
|
|
502
|
+
* Notes:
|
|
503
|
+
* - Caches type lookups. If the CSN changes drastically, you will need to re-call
|
|
504
|
+
* `csnUtils()` and use the newly returned `getFinalBaseTypeWithProps()`.
|
|
505
|
+
* - Does _not_ return the underlying type definition! It is an object with all relevant
|
|
506
|
+
* type properties collected while traversing the type chain!
|
|
504
507
|
*
|
|
505
508
|
* @param {string|object} type Type as string or type ref, i.e. `{ ref: [...] }`
|
|
506
509
|
* @returns {object|null}
|
|
@@ -517,7 +520,7 @@ function getUtils(model, universalReady) {
|
|
|
517
520
|
|
|
518
521
|
// We differentiate between ref and type to avoid collisions due to dict key.
|
|
519
522
|
// Delimiter chosen arbitrarily; just one that is rarely used.
|
|
520
|
-
const resolvedKey = (typeof type === 'object') ? `ref:${type.ref.join('\\')}` : `type:${type}`;
|
|
523
|
+
const resolvedKey = (typeof type === 'object') ? `ref[${type.ref.length}]:${type.ref.join('\\')}` : `type:${type}`;
|
|
521
524
|
|
|
522
525
|
if (finalBaseTypeCache[resolvedKey]) {
|
|
523
526
|
if (finalBaseTypeCache[resolvedKey] === true)
|
|
@@ -786,10 +789,12 @@ function forEachGeneric( construct, prop, callback, path = [], iterateOptions =
|
|
|
786
789
|
executeCallbacks( dictObj, name );
|
|
787
790
|
}
|
|
788
791
|
function executeCallbacks(o, name ) {
|
|
792
|
+
const p = iterateOptions.pathWithoutProp ? [ name ] : [ prop, name ];
|
|
793
|
+
|
|
789
794
|
if (Array.isArray(callback))
|
|
790
|
-
callback.forEach(cb => cb( o, name, prop, path.concat(
|
|
795
|
+
callback.forEach(cb => cb( o, name, prop, path.concat(p), construct ));
|
|
791
796
|
else
|
|
792
|
-
callback( o, name, prop, path.concat(
|
|
797
|
+
callback( o, name, prop, path.concat(p), construct )
|
|
793
798
|
}
|
|
794
799
|
}
|
|
795
800
|
|
|
@@ -1056,7 +1061,7 @@ function isValidMappingDialectCombi(sqlDialect, sqlMapping) {
|
|
|
1056
1061
|
*/
|
|
1057
1062
|
// eslint-disable-next-line no-unused-vars
|
|
1058
1063
|
function getElementDatabaseNameOf(elemName, sqlMapping, sqlDialect='plain') {
|
|
1059
|
-
isValidMappingDialectCombi(
|
|
1064
|
+
isValidMappingDialectCombi(sqlDialect, sqlMapping)
|
|
1060
1065
|
if (sqlMapping === 'hdbcds') {
|
|
1061
1066
|
return elemName;
|
|
1062
1067
|
}
|
|
@@ -1280,19 +1285,29 @@ function copyAnnotationsAndDoc(fromNode, toNode, overwrite = false) {
|
|
|
1280
1285
|
* @todo Does _not_ apply param/action/... annotations.
|
|
1281
1286
|
*
|
|
1282
1287
|
* @param {CSN.Model} csn
|
|
1283
|
-
* @param {{
|
|
1288
|
+
* @param {{notFound?: (name: string, index: number) => void, override?: boolean, filter?: (name: string) => boolean}} config
|
|
1289
|
+
* notFound: Function that is called if the referenced definition can't be found.
|
|
1290
|
+
* Second argument is index in `csn.extensions` array.
|
|
1291
|
+
* override: Whether to ignore existing annotations.
|
|
1292
|
+
* filter: Positive filter. If it returns true, annotations for the referenced artifact
|
|
1293
|
+
* will be applied.
|
|
1284
1294
|
*/
|
|
1285
1295
|
function applyAnnotationsFromExtensions(csn, config) {
|
|
1286
1296
|
if (!csn.extensions)
|
|
1287
1297
|
return;
|
|
1288
1298
|
|
|
1289
1299
|
const filter = config.filter || ((_name) => true);
|
|
1290
|
-
for (
|
|
1300
|
+
for (let i = 0; i < csn.extensions.length; ++i) {
|
|
1301
|
+
const ext = csn.extensions[i];
|
|
1291
1302
|
const name = ext.annotate || ext.extend;
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1303
|
+
if (name && filter(name)) {
|
|
1304
|
+
const def = csn.definitions[name];
|
|
1305
|
+
if (def) {
|
|
1306
|
+
copyAnnotationsAndDoc(ext, def, config.override);
|
|
1307
|
+
applyAnnotationsToElements(ext, def);
|
|
1308
|
+
} else if (config.notFound) {
|
|
1309
|
+
config.notFound(name, i);
|
|
1310
|
+
}
|
|
1296
1311
|
}
|
|
1297
1312
|
}
|
|
1298
1313
|
|
|
@@ -1309,7 +1324,7 @@ function applyAnnotationsFromExtensions(csn, config) {
|
|
|
1309
1324
|
forEach(ext.elements, (key, sourceElem) => {
|
|
1310
1325
|
const targetElem = def.elements[key];
|
|
1311
1326
|
if (targetElem) {
|
|
1312
|
-
copyAnnotationsAndDoc(sourceElem, targetElem, config.
|
|
1327
|
+
copyAnnotationsAndDoc(sourceElem, targetElem, config.override);
|
|
1313
1328
|
applyAnnotationsToElements(sourceElem, targetElem);
|
|
1314
1329
|
}
|
|
1315
1330
|
});
|
|
@@ -58,7 +58,6 @@ function tableAliasAsLink( art, parent, name ) {
|
|
|
58
58
|
*
|
|
59
59
|
* @param {XSN.Model} model
|
|
60
60
|
* @param {string} [nameOrPath]
|
|
61
|
-
* @returns {string}
|
|
62
61
|
*/
|
|
63
62
|
function revealInternalProperties( model, nameOrPath ) {
|
|
64
63
|
const transformers = {
|
|
@@ -131,7 +130,10 @@ function revealInternalProperties( model, nameOrPath ) {
|
|
|
131
130
|
|
|
132
131
|
path = path.split('/');
|
|
133
132
|
if (path.length === 1) {
|
|
134
|
-
|
|
133
|
+
const def = xsn.definitions?.[path[0]] || xsn.vocabularies?.[path[0]];
|
|
134
|
+
if (!def)
|
|
135
|
+
throw new Error(`reveal xsn: Unknown definition: “${path[0]}”`)
|
|
136
|
+
return reveal( def );
|
|
135
137
|
}
|
|
136
138
|
|
|
137
139
|
// with the code below, we might miss the right transformer function
|
|
@@ -296,8 +298,8 @@ function revealInternalProperties( model, nameOrPath ) {
|
|
|
296
298
|
function array( node, fn ) {
|
|
297
299
|
const r = node.map( n => fn( n, node ) );
|
|
298
300
|
if (node[$location])
|
|
299
|
-
r.push( { $location: locationString( node[$location] ) } );
|
|
300
|
-
return r;
|
|
301
|
+
r.push( { '[$location]': locationString( node[$location] ) } );
|
|
302
|
+
return (node.$prefix) ? [ { $prefix: node.$prefix }, ...node ] : r;
|
|
301
303
|
}
|
|
302
304
|
|
|
303
305
|
function artifactIdentifier( node, parent ) {
|
|
@@ -169,6 +169,9 @@ function getElementComparator(otherArtifact, addedElementsDict = null, changedEl
|
|
|
169
169
|
}
|
|
170
170
|
if (relevantTypeChange(element.type, otherElement.type) || typeParametersChanged(element, otherElement)) {
|
|
171
171
|
// Type or parameters, e.g. association target, changed.
|
|
172
|
+
if(otherElement.notNull && element.notNull === undefined) {
|
|
173
|
+
element.$notNull = false; // Explictily set notNull to the implicit default so we render the correct ALTER
|
|
174
|
+
}
|
|
172
175
|
changedElementsDict[name] = changedElement(element, otherElement);
|
|
173
176
|
}
|
|
174
177
|
|