@sap/cds-compiler 2.5.0 → 2.10.4
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 +191 -9
- package/bin/cdsc.js +2 -2
- package/doc/CHANGELOG_BETA.md +33 -3
- package/lib/api/main.js +29 -101
- package/lib/api/options.js +15 -11
- package/lib/api/validate.js +12 -8
- package/lib/backends.js +0 -81
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +63 -9
- package/lib/base/messages.js +63 -21
- package/lib/base/model.js +2 -3
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/foreignKeys.js +0 -6
- package/lib/checks/managedWithoutKeys.js +17 -0
- package/lib/checks/nonexpandableStructured.js +38 -0
- package/lib/checks/onConditions.js +9 -45
- package/lib/checks/queryNoDbArtifacts.js +25 -7
- package/lib/checks/selectItems.js +25 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +38 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +16 -7
- package/lib/compiler/builtins.js +2 -0
- package/lib/compiler/checks.js +6 -4
- package/lib/compiler/definer.js +99 -42
- package/lib/compiler/index.js +73 -27
- package/lib/compiler/resolver.js +288 -157
- package/lib/compiler/shared.js +31 -11
- package/lib/edm/annotations/genericTranslation.js +182 -186
- package/lib/edm/csn2edm.js +103 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +361 -114
- package/lib/edm/edmUtils.js +103 -33
- package/lib/gen/Dictionary.json +22 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +12 -1
- package/lib/gen/language.tokens +57 -53
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +770 -744
- package/lib/gen/languageLexer.tokens +49 -46
- package/lib/gen/languageParser.js +4713 -4279
- package/lib/json/from-csn.js +103 -45
- package/lib/json/to-csn.js +296 -117
- package/lib/language/antlrParser.js +4 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +21 -12
- package/lib/language/language.g4 +99 -31
- package/lib/main.d.ts +81 -3
- package/lib/main.js +30 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +329 -142
- package/lib/model/csnUtils.js +235 -58
- package/lib/model/enrichCsn.js +18 -1
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/modelCompare/compare.js +37 -20
- package/lib/optionProcessor.js +9 -3
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +8 -5
- package/lib/render/toCdl.js +112 -33
- package/lib/render/toHdbcds.js +134 -64
- package/lib/render/toSql.js +91 -38
- package/lib/render/utils/common.js +8 -13
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/assertUnique.js +5 -6
- package/lib/transform/db/constraints.js +29 -13
- package/lib/transform/db/draft.js +8 -6
- package/lib/transform/db/expansion.js +582 -0
- package/lib/transform/db/flattening.js +325 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +284 -63
- package/lib/transform/forHanaNew.js +98 -381
- package/lib/transform/forOdataNew.js +21 -22
- package/lib/transform/localized.js +37 -10
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +60 -39
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +19 -18
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +134 -78
- package/lib/transform/translateAssocsToJoins.js +17 -14
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +0 -11
- package/lib/utils/moduleResolve.js +6 -8
- package/package.json +1 -1
- package/lib/json/walker.js +0 -26
- package/lib/transform/sqlite +0 -0
- package/lib/utils/string.js +0 -17
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
const antlr4 = require('antlr4');
|
|
12
12
|
|
|
13
|
-
const {
|
|
13
|
+
const { CompileMessage } = require('../base/messages');
|
|
14
14
|
const errorStrategy = require('./errorStrategy');
|
|
15
15
|
|
|
16
16
|
const Parser = require('../gen/languageParser').languageParser;
|
|
@@ -112,7 +112,7 @@ const rules = {
|
|
|
112
112
|
expr: { func: 'conditionEOF', returns: 'cond' }, // yes, condition
|
|
113
113
|
};
|
|
114
114
|
|
|
115
|
-
function parse( source, filename = '<undefined>.cds', options = {}, rule = 'cdl' ) {
|
|
115
|
+
function parse( source, filename = '<undefined>.cds', options = {}, messageFunctions, rule = 'cdl' ) {
|
|
116
116
|
const lexer = new Lexer( new antlr4.InputStream(source) );
|
|
117
117
|
const tokenStream = new RewriteTypeTokenStream(lexer);
|
|
118
118
|
/** @type {object} */
|
|
@@ -121,7 +121,7 @@ function parse( source, filename = '<undefined>.cds', options = {}, rule = 'cdl'
|
|
|
121
121
|
|
|
122
122
|
parser.filename = filename;
|
|
123
123
|
parser.options = options;
|
|
124
|
-
parser.$
|
|
124
|
+
parser.$messageFunctions = messageFunctions;
|
|
125
125
|
|
|
126
126
|
initTokenRewrite( parser, tokenStream );
|
|
127
127
|
// comment the following 2 lines if you want to output the parser errors directly:
|
|
@@ -162,6 +162,7 @@ function parse( source, filename = '<undefined>.cds', options = {}, rule = 'cdl'
|
|
|
162
162
|
if (rulespec.$frontend)
|
|
163
163
|
ast.$frontend = rulespec.$frontend;
|
|
164
164
|
|
|
165
|
+
// TODO: clarify with LSP colleagues: still necessary?
|
|
165
166
|
if (parser.messages) {
|
|
166
167
|
Object.defineProperty( ast, 'messages',
|
|
167
168
|
{ value: parser.messages, configurable: true, writable: true } );
|
|
@@ -13,6 +13,14 @@ const locUtils = require('../base/location');
|
|
|
13
13
|
const { parseDocComment } = require('./docCommentParser');
|
|
14
14
|
const { functionsWithoutParens, specialFunctions } = require('../compiler/builtins');
|
|
15
15
|
|
|
16
|
+
|
|
17
|
+
// Push message `msg` with location `loc` to array of errors:
|
|
18
|
+
function _message( parser, severity, id, loc, ...args ) {
|
|
19
|
+
const msg = parser.$messageFunctions[severity]; // set in antlrParser.js
|
|
20
|
+
return msg( id,
|
|
21
|
+
(loc instanceof antlr4.CommonToken) ? parser.tokenLocation(loc) : loc, ...args );
|
|
22
|
+
}
|
|
23
|
+
|
|
16
24
|
// Class which is to be used as grammar option with
|
|
17
25
|
// grammar <name> options { superclass = genericAntlrParser; }
|
|
18
26
|
//
|
|
@@ -34,10 +42,10 @@ function GenericAntlrParser( ...args ) {
|
|
|
34
42
|
|
|
35
43
|
GenericAntlrParser.prototype = Object.assign(
|
|
36
44
|
Object.create( antlr4.Parser.prototype ), {
|
|
37
|
-
message: function(...args) { return
|
|
38
|
-
error: function(...args) { return
|
|
39
|
-
warning: function(...args) { return
|
|
40
|
-
info: function(...args) { return
|
|
45
|
+
message: function(...args) { return _message( this, 'message', ...args ); },
|
|
46
|
+
error: function(...args) { return _message( this, 'error', ...args ); },
|
|
47
|
+
warning: function(...args) { return _message( this, 'warning', ...args ); },
|
|
48
|
+
info: function(...args) { return _message( this, 'info', ...args ); },
|
|
41
49
|
attachLocation,
|
|
42
50
|
startLocation,
|
|
43
51
|
tokenLocation,
|
|
@@ -69,6 +77,7 @@ GenericAntlrParser.prototype = Object.assign(
|
|
|
69
77
|
noAssignmentInSameLine,
|
|
70
78
|
noSemicolonHere,
|
|
71
79
|
setLocalToken,
|
|
80
|
+
setLocalTokenIfBefore,
|
|
72
81
|
excludeExpected,
|
|
73
82
|
isStraightBefore,
|
|
74
83
|
meltKeywordToIdentifier,
|
|
@@ -110,14 +119,6 @@ const quotedLiteralPatterns = {
|
|
|
110
119
|
},
|
|
111
120
|
};
|
|
112
121
|
|
|
113
|
-
|
|
114
|
-
// Push message `msg` with location `loc` to array of errors:
|
|
115
|
-
function message( severity, id, loc, ...args ) {
|
|
116
|
-
const msg = this.$message[severity];
|
|
117
|
-
return msg( id, // function $message is set in antlrParser.js
|
|
118
|
-
(loc instanceof antlr4.CommonToken) ? this.tokenLocation(loc) : loc, ...args );
|
|
119
|
-
}
|
|
120
|
-
|
|
121
122
|
// Use the following function for language constructs which we (currently)
|
|
122
123
|
// just being able to parse, in able to run tests from HANA CDS. As soon as we
|
|
123
124
|
// create ASTs for the language construct and put it into a CSN, a
|
|
@@ -183,6 +184,14 @@ function setLocalToken( string, tokenName, notBefore, inSameLine ) {
|
|
|
183
184
|
ll1.type = this.constructor[tokenName];
|
|
184
185
|
}
|
|
185
186
|
|
|
187
|
+
function setLocalTokenIfBefore( string, tokenName, before, inSameLine ) {
|
|
188
|
+
const ll1 = this.getCurrentToken();
|
|
189
|
+
if (ll1.text.toUpperCase() === string &&
|
|
190
|
+
(!inSameLine || this._input.LT(-1).line === ll1.line) &&
|
|
191
|
+
(!before || before && before.test( this._input.LT(2).text )))
|
|
192
|
+
ll1.type = this.constructor[tokenName];
|
|
193
|
+
}
|
|
194
|
+
|
|
186
195
|
// // Special function for rule `requiredSemi` before return $ctx
|
|
187
196
|
// function braceForSemi() {
|
|
188
197
|
// if (RBRACE == null)
|
package/lib/language/language.g4
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
// allow integers at certain places), one token type for non-quoted and
|
|
21
21
|
// quoted identifiers.
|
|
22
22
|
//
|
|
23
|
-
// * Keep the number of keywords as small as
|
|
23
|
+
// * Keep the number of keywords as small as possible. Thus, built-ins is a
|
|
24
24
|
// topic for the semantic analysis, not the grammar. Examples: no keywords
|
|
25
25
|
// for built-in types or built-in SQL functions. This also avoids noise in
|
|
26
26
|
// the grammar and a huge/slow generated parser.
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
// is not enough for a decision), write a comment starting with `#ATN:`
|
|
44
44
|
// which describes the ambiguity. Additionally, put a comment `/* #ATN n
|
|
45
45
|
// */` INSIDE an (`@after`) action of a rule if the corresponding function
|
|
46
|
-
// in '../gen/
|
|
46
|
+
// in '../gen/languageParser.js' contains `n` occurrences of
|
|
47
47
|
// `adaptivePredict` calls. This is checked in 'test/testCompiler.js',
|
|
48
48
|
// which also counts the total number of `adaptivePredict` occurrences.
|
|
49
49
|
// └─────────────────────────────────────────────────────────────────────────┘
|
|
@@ -55,6 +55,10 @@
|
|
|
55
55
|
// good idea anyway to avoid calls to `adaptivePredict`, see the rules
|
|
56
56
|
// starting with `annotationAssignment_`.
|
|
57
57
|
//
|
|
58
|
+
// * Factoring out a sub rule into a named rule influences the error recovery:
|
|
59
|
+
// the parser tries to consume all tokens which are neither in the follow
|
|
60
|
+
// set of loops and named rules. So be careful.
|
|
61
|
+
//
|
|
58
62
|
// * Do not use actions in the lexer. Examples: de-quote string literals not
|
|
59
63
|
// in the lexer, but in the parser; do not throw errors, but produce error
|
|
60
64
|
// tokens if necessary.
|
|
@@ -106,6 +110,9 @@
|
|
|
106
110
|
//
|
|
107
111
|
// * The ANTLR error "missing attribute access on rule reference c in $c" can
|
|
108
112
|
// be solved with using $ctx.c instead of $c
|
|
113
|
+
//
|
|
114
|
+
// * If you want to set a property starting with '$' like $syntax, use
|
|
115
|
+
// obj['$'+'syntax'] as the ANTLR tool would replace $syntax by $ctx.syntax
|
|
109
116
|
|
|
110
117
|
grammar language;
|
|
111
118
|
options {
|
|
@@ -114,6 +121,7 @@ options {
|
|
|
114
121
|
}
|
|
115
122
|
tokens {
|
|
116
123
|
VIRTUAL, // used with setLocalToken()
|
|
124
|
+
OVER, // used with setLocalTokenIfBefore()
|
|
117
125
|
HelperToken1, // used with setLocalToken(), does not appear in messages
|
|
118
126
|
HelperToken2, // used with setLocalToken(), does not appear in messages
|
|
119
127
|
HideAlternatives, // hide alternative tokens (no token seq!)
|
|
@@ -818,7 +826,7 @@ annotateArtifact[ outer, loc, annos ] locals[ art, name = {} ]
|
|
|
818
826
|
)*
|
|
819
827
|
')'
|
|
820
828
|
(
|
|
821
|
-
RETURNS '{'
|
|
829
|
+
RETURNS '{' { $art['$'+'syntax'] = 'returns'; }
|
|
822
830
|
annotateElement[ $art ]*
|
|
823
831
|
'}'
|
|
824
832
|
optionalSemi
|
|
@@ -826,7 +834,7 @@ annotateArtifact[ outer, loc, annos ] locals[ art, name = {} ]
|
|
|
826
834
|
requiredSemi
|
|
827
835
|
)
|
|
828
836
|
|
|
|
829
|
-
RETURNS '{'
|
|
837
|
+
RETURNS '{' { $art['$'+'syntax'] = 'returns'; }
|
|
830
838
|
annotateElement[ $art ]*
|
|
831
839
|
'}'
|
|
832
840
|
optionalSemi
|
|
@@ -1124,13 +1132,7 @@ bracedSelectItemListDef[ query ]
|
|
|
1124
1132
|
'{'
|
|
1125
1133
|
{ if (!$query.columns) $query.columns = []; } // set it early to avoid "wildcard" errors
|
|
1126
1134
|
(
|
|
1127
|
-
|
|
1128
|
-
{
|
|
1129
|
-
$query.columns = [ this.tokenLocation( $star, undefined, '*' ) ];
|
|
1130
|
-
}
|
|
1131
|
-
|
|
|
1132
|
-
selectItemDef[ $query.columns ]
|
|
1133
|
-
)
|
|
1135
|
+
selectItemDef[ $query.columns ]
|
|
1134
1136
|
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
1135
1137
|
selectItemDef[ $query.columns ]
|
|
1136
1138
|
)*
|
|
@@ -1139,8 +1141,11 @@ bracedSelectItemListDef[ query ]
|
|
|
1139
1141
|
;
|
|
1140
1142
|
|
|
1141
1143
|
selectItemDef[ outer ] locals[ annos = [] ]
|
|
1142
|
-
@after{ this.attachLocation($art.art); }
|
|
1144
|
+
@after{ if ($ctx.art) this.attachLocation($art.art); }
|
|
1143
1145
|
:
|
|
1146
|
+
star='*'
|
|
1147
|
+
{ $outer.push( this.tokenLocation( $star, undefined, '*' ) ); }
|
|
1148
|
+
|
|
|
1144
1149
|
{ this.docComment( $annos ); }
|
|
1145
1150
|
annotationAssignment_atn[ $annos ]*
|
|
1146
1151
|
// VIRTUAL is keyword, except if before the following tokens texts:
|
|
@@ -1159,6 +1164,8 @@ selectItemDefBody[ outer, annos ] returns[ art = {} ]
|
|
|
1159
1164
|
:
|
|
1160
1165
|
(
|
|
1161
1166
|
e=expression
|
|
1167
|
+
// we cannot use 'condition' instead, as long as we allow aliases without
|
|
1168
|
+
// AS (using rule 'ident' instead of 'identNoKeyword') -> ambiguities
|
|
1162
1169
|
{
|
|
1163
1170
|
$art = this.addItem( $outer, null, null, $annos, { value: $e.expr } );
|
|
1164
1171
|
}
|
|
@@ -1218,11 +1225,7 @@ selectItemInlineList[ art, clause ]
|
|
|
1218
1225
|
'{'
|
|
1219
1226
|
{ $art[$clause] = []; }
|
|
1220
1227
|
(
|
|
1221
|
-
|
|
1222
|
-
{ $art[$clause].push( this.tokenLocation( $star, undefined, '*' ) ); }
|
|
1223
|
-
|
|
|
1224
|
-
selectItemInlineDef[ $art[$clause] ]
|
|
1225
|
-
)
|
|
1228
|
+
selectItemInlineDef[ $art[$clause] ]
|
|
1226
1229
|
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
1227
1230
|
selectItemInlineDef[ $art[$clause] ]
|
|
1228
1231
|
)*
|
|
@@ -1231,8 +1234,11 @@ selectItemInlineList[ art, clause ]
|
|
|
1231
1234
|
;
|
|
1232
1235
|
|
|
1233
1236
|
selectItemInlineDef[ outer ] locals[ annos = [] ]
|
|
1234
|
-
@after{ this.attachLocation($art.art); }
|
|
1237
|
+
@after{ if ($ctx.art) this.attachLocation($art.art); }
|
|
1235
1238
|
:
|
|
1239
|
+
star='*'
|
|
1240
|
+
{ $outer.push( this.tokenLocation( $star, undefined, '*' ) ); }
|
|
1241
|
+
|
|
|
1236
1242
|
{ this.docComment( $annos ); }
|
|
1237
1243
|
annotationAssignment_atn[ $annos ]*
|
|
1238
1244
|
art=selectItemDefBody[ $outer, $annos ]
|
|
@@ -1407,6 +1413,8 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
|
|
|
1407
1413
|
|
|
|
1408
1414
|
':'
|
|
1409
1415
|
// #ATN: typeRefOptArgs can start with ARRAY or MANY or ASSOCIATION or TYPE or LOCALIZED
|
|
1416
|
+
// Nevertheless, MANY '{' is handled by local token rewrite:
|
|
1417
|
+
{ this.setLocalToken( 'MANY', 'HelperToken1', /^[^\{]/ ); }
|
|
1410
1418
|
(
|
|
1411
1419
|
typeStruct[ $art ]
|
|
1412
1420
|
optionalSemi
|
|
@@ -1416,6 +1424,12 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
|
|
|
1416
1424
|
( typeToMany[ $art ] | typeToOne[ $art ] | simplePath[ $art.target, 'artref' ] )
|
|
1417
1425
|
typeAssociationCont[ $art ]?
|
|
1418
1426
|
requiredSemi // and if its the ';'...
|
|
1427
|
+
|
|
|
1428
|
+
many=HelperToken1 // rewritten MANY before '{'
|
|
1429
|
+
{ $art.items = { location: this.tokenLocation( $many ) };}
|
|
1430
|
+
typeStruct[ $art.items ]
|
|
1431
|
+
nullability[ $art.items ]?
|
|
1432
|
+
optionalSemi
|
|
1419
1433
|
|
|
|
1420
1434
|
(
|
|
1421
1435
|
array=ARRAY of=OF
|
|
@@ -1455,6 +1469,7 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
|
|
|
1455
1469
|
annotationAssignment_ll1[ $annos ]*
|
|
1456
1470
|
requiredSemi
|
|
1457
1471
|
|
|
|
1472
|
+
// alt lookahead includes MANY '{'
|
|
1458
1473
|
{ $art.type = {}; }
|
|
1459
1474
|
simplePath[ $art.type, 'artref' ]
|
|
1460
1475
|
(
|
|
@@ -1462,8 +1477,20 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
|
|
|
1462
1477
|
head=Number
|
|
1463
1478
|
{ $art['$'+'typeArgs'] = [ this.numberLiteral( $head ) ]; }
|
|
1464
1479
|
( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
1465
|
-
|
|
1466
|
-
|
|
1480
|
+
(
|
|
1481
|
+
v=VARIABLE
|
|
1482
|
+
{ $art['$'+'typeArgs'].push(
|
|
1483
|
+
{ literal: 'string', val: 'variable', location: this.tokenLocation($v) } );
|
|
1484
|
+
}
|
|
1485
|
+
|
|
|
1486
|
+
f=FLOATING
|
|
1487
|
+
{ $art['$'+'typeArgs'].push(
|
|
1488
|
+
{ literal: 'string', val: 'floating', location: this.tokenLocation($f) } );
|
|
1489
|
+
}
|
|
1490
|
+
|
|
|
1491
|
+
tail=Number
|
|
1492
|
+
{ $art['$'+'typeArgs'].push( this.numberLiteral( $tail ) ); }
|
|
1493
|
+
)
|
|
1467
1494
|
)*
|
|
1468
1495
|
')'
|
|
1469
1496
|
{ this.docComment( $annos ); }
|
|
@@ -1687,8 +1714,20 @@ typeRefOptArgs[ art ]
|
|
|
1687
1714
|
head=Number
|
|
1688
1715
|
{ $art['$'+'typeArgs'] = [ this.numberLiteral( $head ) ]; }
|
|
1689
1716
|
( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
1690
|
-
|
|
1691
|
-
|
|
1717
|
+
(
|
|
1718
|
+
v=VARIABLE
|
|
1719
|
+
{ $art['$'+'typeArgs'].push(
|
|
1720
|
+
{ literal: 'string', val: 'variable', location: this.tokenLocation($v) } );
|
|
1721
|
+
}
|
|
1722
|
+
|
|
|
1723
|
+
f=FLOATING
|
|
1724
|
+
{ $art['$'+'typeArgs'].push(
|
|
1725
|
+
{ literal: 'string', val: 'floating', location: this.tokenLocation($f) } );
|
|
1726
|
+
}
|
|
1727
|
+
|
|
|
1728
|
+
tail=Number
|
|
1729
|
+
{ $art['$'+'typeArgs'].push( this.numberLiteral( $tail ) ); }
|
|
1730
|
+
)
|
|
1692
1731
|
)*
|
|
1693
1732
|
')'
|
|
1694
1733
|
|
|
|
@@ -1723,6 +1762,30 @@ orderByClause[ inQuery ] returns [ query ]
|
|
|
1723
1762
|
( ',' obn=orderBySpec { $query.orderBy.push( $obn.ob ); } )*
|
|
1724
1763
|
;
|
|
1725
1764
|
|
|
1765
|
+
overOrderByClause returns [ expr ]
|
|
1766
|
+
:
|
|
1767
|
+
o=ORDER b=BY { $expr = { op: this.tokenLocation($o, $b, 'orderBy' ) , args: [] }}
|
|
1768
|
+
ob1=orderBySpec { $expr.args.push( $ob1.ob ); }
|
|
1769
|
+
( ',' obn=orderBySpec { $expr.args.push( $obn.ob ); } )*
|
|
1770
|
+
;
|
|
1771
|
+
|
|
1772
|
+
partitionByClause returns [ expr ]
|
|
1773
|
+
:
|
|
1774
|
+
p=PARTITION b=BY { $expr = { op: this.tokenLocation($p, $b, 'partitionBy' ) , args: [] }}
|
|
1775
|
+
e1=expression { $expr.args.push( $e1.expr ); }
|
|
1776
|
+
( ',' en=expression { $expr.args.push( $en.expr ); } )*
|
|
1777
|
+
;
|
|
1778
|
+
|
|
1779
|
+
overClause returns [ over ]
|
|
1780
|
+
@after { this.attachLocation($over); }
|
|
1781
|
+
:
|
|
1782
|
+
o=OVER { $over = { op: this.tokenLocation( $o, null, 'over' ) , args: [] } }
|
|
1783
|
+
'('
|
|
1784
|
+
( pb=partitionByClause { $over.args.push( $pb.expr ); } )?
|
|
1785
|
+
( ob=overOrderByClause { $over.args.push( $ob.expr ); } )?
|
|
1786
|
+
')'
|
|
1787
|
+
;
|
|
1788
|
+
|
|
1726
1789
|
limitClause[ inQuery ] returns [ query ]
|
|
1727
1790
|
:
|
|
1728
1791
|
limkw=LIMIT { $query = this.unaryOpForParens( $inQuery, '$'+'query' ); }
|
|
@@ -1781,13 +1844,7 @@ queryPrimary returns[ query = {} ]
|
|
|
1781
1844
|
{ $query.quantifier = this.tokenLocation( $ad, undefined, $ad.text.toLowerCase() ); }
|
|
1782
1845
|
)?
|
|
1783
1846
|
{ $query.columns = []; } // set it early to avoid "wildcard" errors
|
|
1784
|
-
|
|
1785
|
-
{
|
|
1786
|
-
$query.columns = [ this.tokenLocation( $star, undefined, '*' ) ];
|
|
1787
|
-
}
|
|
1788
|
-
|
|
|
1789
|
-
selectItemDef[ $query.columns ]
|
|
1790
|
-
)
|
|
1847
|
+
selectItemDef[ $query.columns ]
|
|
1791
1848
|
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
1792
1849
|
selectItemDef[ $query.columns ]
|
|
1793
1850
|
)*
|
|
@@ -1945,7 +2002,7 @@ fromPath[ qp, idkind ]
|
|
|
1945
2002
|
condition returns [ cond ] locals [ args = [], orl = [] ]
|
|
1946
2003
|
@after{
|
|
1947
2004
|
$cond = ($args.length == 1)
|
|
1948
|
-
? $args[0]
|
|
2005
|
+
? this.attachLocation( $args[0] )
|
|
1949
2006
|
: this.attachLocation({ op: $orl[0], args: $args });
|
|
1950
2007
|
}
|
|
1951
2008
|
:
|
|
@@ -2106,6 +2163,10 @@ expressionTerm returns [ expr ] locals [ op, args = [] ]
|
|
|
2106
2163
|
this.notSupportedYet( $ne ); }
|
|
2107
2164
|
|
|
|
2108
2165
|
vp=valuePath[ 'ref', null ] { $expr = this.valuePathAst( $vp.qp ); }
|
|
2166
|
+
{ this.setLocalTokenIfBefore( 'OVER', 'OVER', /^\($/i ); }
|
|
2167
|
+
(
|
|
2168
|
+
over=overClause { $expr.suffix = [ $over.over ] }
|
|
2169
|
+
)?
|
|
2109
2170
|
|
|
|
2110
2171
|
':'
|
|
2111
2172
|
( vp=valuePath[ 'paramref', this.startLocation() ]
|
|
@@ -2306,7 +2367,7 @@ optionalCardinality[ pathStep ]
|
|
|
2306
2367
|
// completion just produces `:`after having inserted a Number - TODO.
|
|
2307
2368
|
{ if (this._input.LT(2).text !== ':') return $ctx; }
|
|
2308
2369
|
( trgMax=Number ':'
|
|
2309
|
-
{ if ($pathStep) $pathStep.cardinality = { targetMax: this.numberLiteral( $trgMax ) } }
|
|
2370
|
+
{ if ($pathStep) $pathStep.cardinality = { targetMax: this.numberLiteral( $trgMax ), location: this.startLocation() }; }
|
|
2310
2371
|
)
|
|
2311
2372
|
;
|
|
2312
2373
|
|
|
@@ -2557,6 +2618,7 @@ ident[ category ] returns[ id ]
|
|
|
2557
2618
|
| EXCLUDING
|
|
2558
2619
|
| EXTEND
|
|
2559
2620
|
| FIRST
|
|
2621
|
+
| FLOATING
|
|
2560
2622
|
| FULL
|
|
2561
2623
|
| FUNCTION
|
|
2562
2624
|
| GROUP
|
|
@@ -2587,6 +2649,7 @@ ident[ category ] returns[ id ]
|
|
|
2587
2649
|
| ORDER
|
|
2588
2650
|
| OUTER
|
|
2589
2651
|
| PARAMETERS
|
|
2652
|
+
| PARTITION
|
|
2590
2653
|
| PROJECTION
|
|
2591
2654
|
| REDIRECTED
|
|
2592
2655
|
| RETURNS
|
|
@@ -2599,6 +2662,7 @@ ident[ category ] returns[ id ]
|
|
|
2599
2662
|
| TO
|
|
2600
2663
|
| TYPE
|
|
2601
2664
|
| USING
|
|
2665
|
+
| VARIABLE
|
|
2602
2666
|
| VIEW
|
|
2603
2667
|
| YEAR
|
|
2604
2668
|
;
|
|
@@ -2715,6 +2779,7 @@ EXCEPT : [eE][xX][cC][eE][pP][tT] ;
|
|
|
2715
2779
|
EXCLUDING : [eE][xX][cC][lL][uU][dD][iI][nN][gG] ;
|
|
2716
2780
|
EXTEND : [eE][xX][tT][eE][nN][dD] ;
|
|
2717
2781
|
FIRST : [fF][iI][rR][sS][tT] ;
|
|
2782
|
+
FLOATING : [fF][lL][oO][aA][tT][iI][nN][gG] ;
|
|
2718
2783
|
FULL : [fF][uU][lL][lL] ;
|
|
2719
2784
|
FUNCTION : [fF][uU][nN][cC][tT][iI][oO][nN] ;
|
|
2720
2785
|
GROUP : [gG][rR][oO][uU][pP] ;
|
|
@@ -2744,7 +2809,9 @@ ONE : [oO][nN][eE] ;
|
|
|
2744
2809
|
OR : [oO][rR] ;
|
|
2745
2810
|
ORDER : [oO][rR][dD][eE][rR] ;
|
|
2746
2811
|
OUTER : [oO][uU][tT][eE][rR] ;
|
|
2812
|
+
// OVER : [oO][vV][eE][rR] ;
|
|
2747
2813
|
PARAMETERS : [pP][aA][rR][aA][mM][eE][tT][eE][rR][sS] ;
|
|
2814
|
+
PARTITION: [pP][aA][rR][tT][iI][tT][iI][oO][nN] ;
|
|
2748
2815
|
PROJECTION : [pP][rR][oO][jJ][eE][cC][tT][iI][oO][nN] ;
|
|
2749
2816
|
REDIRECTED : [rR][eE][dD][iI][rR][eE][cC][tT][eE][dD] ;
|
|
2750
2817
|
RETURNS : [rR][eE][tT][uU][rR][nN][sS] ;
|
|
@@ -2757,6 +2824,7 @@ TO : [tT][oO] ; // or make reserved? (is in SQL-92)
|
|
|
2757
2824
|
TYPE : [tT][yY][pP][eE] ;
|
|
2758
2825
|
UNION : [uU][nN][iI][oO][nN] ;
|
|
2759
2826
|
USING : [uU][sS][iI][nN][gG] ;
|
|
2827
|
+
VARIABLE : [vV][aA][rR][iI][aA][bB][lL][eE] ;
|
|
2760
2828
|
VIEW : [vV][iI][eE][wW] ;
|
|
2761
2829
|
// VIRTUAL: [vV][iI][rR][tT][uU][aA][lL] ; see tokens {}
|
|
2762
2830
|
YEAR : [yY][eE][aA][rR] ;
|
package/lib/main.d.ts
CHANGED
|
@@ -107,12 +107,81 @@ declare namespace compiler {
|
|
|
107
107
|
constructor(messages: any, model: any, text: any, ...args);
|
|
108
108
|
messages: any[];
|
|
109
109
|
toString(): string;
|
|
110
|
+
/**
|
|
111
|
+
* If `options.attachValidNames` is set, this non-enumerable property holds the CSN model.
|
|
112
|
+
* @internal
|
|
113
|
+
*/
|
|
114
|
+
model?: CSN;
|
|
115
|
+
/**
|
|
116
|
+
* Used by `cdsc` to indicate whether the message was already printed to stderr.
|
|
117
|
+
* @private
|
|
118
|
+
*/
|
|
119
|
+
hasBeenReported: boolean;
|
|
110
120
|
}
|
|
111
121
|
|
|
112
|
-
|
|
113
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Sort the given messages according to their location. Messages are sorted
|
|
124
|
+
* in ascending order according to their:
|
|
125
|
+
*
|
|
126
|
+
* - file name
|
|
127
|
+
* - start line
|
|
128
|
+
* - start column
|
|
129
|
+
* - end line
|
|
130
|
+
* - end column
|
|
131
|
+
* - semantic location (“home”)
|
|
132
|
+
* - message text
|
|
133
|
+
*
|
|
134
|
+
* If both messages do not have a location, they are sorted by their semantic
|
|
135
|
+
* location and then by their message text. If only one message has a file
|
|
136
|
+
* location, that message is sorted prior to those that don't have one.
|
|
137
|
+
*
|
|
138
|
+
* _Note_: Sorting is done in-place.
|
|
139
|
+
*
|
|
140
|
+
* Example of sorted messages:
|
|
141
|
+
* ```txt
|
|
142
|
+
* A.cds:1:11: Info id-3: First message text (in entity:“E”/element:“c”)
|
|
143
|
+
* A.cds:8:11: Error id-5: Another message text (in entity:“C”/element:“g”)
|
|
144
|
+
* B.cds:3:10: Debug id-7: First message text (in entity:“B”/element:“e”)
|
|
145
|
+
* B.cds:3:12: Warning id-4: Message text (in entity:“B”/element:“d”)
|
|
146
|
+
* B.cds:3:12: Error id-4: Message text (in entity:“B”/element:“e”)
|
|
147
|
+
* ```
|
|
148
|
+
*
|
|
149
|
+
* If you also want to sort according to message's severity,
|
|
150
|
+
* see {@link sortMessagesSeverityAware}.
|
|
151
|
+
*
|
|
152
|
+
* @returns The same messages array as the input parameter.
|
|
153
|
+
*/
|
|
154
|
+
export function sortMessages(messages: CompileMessage[]): CompileMessage[];
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Sort the given messages in severity aware order. Messages are sorted first
|
|
158
|
+
* by severity where 'Error' comes first, then 'Warning' and so forth.
|
|
159
|
+
* Messages of the same severity are sorted the same as by {@link sortMessages}.
|
|
160
|
+
*
|
|
161
|
+
* _Note_: Sorting is done in-place.
|
|
162
|
+
*
|
|
163
|
+
* @returns The same messages array as the input parameter.
|
|
164
|
+
*/
|
|
165
|
+
export function sortMessagesSeverityAware(messages: CompileMessage[]): CompileMessage[];
|
|
114
166
|
|
|
115
|
-
|
|
167
|
+
/**
|
|
168
|
+
* Removes duplicate messages from the given messages array without destroying
|
|
169
|
+
* references to the array, i.e. removes them in-place.
|
|
170
|
+
*
|
|
171
|
+
* _Note_: Does NOT keep the original order!
|
|
172
|
+
*
|
|
173
|
+
* Two messages are the same if they have the same message hash (see below).
|
|
174
|
+
* If one of the two is more precise, then it replaces the other.
|
|
175
|
+
* A message is more precise if it is contained in the other or if
|
|
176
|
+
* the first does not have an `endLine`/`endCol`.
|
|
177
|
+
*
|
|
178
|
+
* A “message hash” is the string representation of the message. If the
|
|
179
|
+
* message does not have a semantic location (“home”), the message hash
|
|
180
|
+
* is the result of {@link messageString}. If the message has a semantic
|
|
181
|
+
* location, the file location is stripped before being passed to
|
|
182
|
+
* {@link messageString}.
|
|
183
|
+
*/
|
|
184
|
+
export function deduplicateMessages(messages: CompileMessage[]): void;
|
|
116
185
|
|
|
117
186
|
/**
|
|
118
187
|
* Returns a message string with file- and semantic location if present in compact
|
|
@@ -205,6 +274,15 @@ declare namespace compiler {
|
|
|
205
274
|
export function preparedCsnToEdmx(csn: CSN, service: string, options: any): any;
|
|
206
275
|
|
|
207
276
|
export namespace parse {
|
|
277
|
+
/**
|
|
278
|
+
* Parse the given CDL in parseCdl mode and return its corresponding CSN representation.
|
|
279
|
+
*
|
|
280
|
+
* @param cdl CDL source as string.
|
|
281
|
+
* @param filename Filename to be used in compiler messages.
|
|
282
|
+
* @param options Compiler options. Note that if `options.messages` is not set, messages will be printed to stderr.
|
|
283
|
+
*/
|
|
284
|
+
function cdl(cdl: string, filename: string, options?: Options): any;
|
|
285
|
+
|
|
208
286
|
/**
|
|
209
287
|
* Parse the given CQL and return its corresponding CSN representation.
|
|
210
288
|
*
|
package/lib/main.js
CHANGED
|
@@ -16,10 +16,12 @@
|
|
|
16
16
|
const backends = require('./backends');
|
|
17
17
|
const { odata, cdl, sql, hdi, hdbcds, edm, edmx } = require('./api/main');
|
|
18
18
|
const { getArtifactDatabaseNameOf, getElementDatabaseNameOf } = require('./model/csnUtils');
|
|
19
|
-
const {
|
|
19
|
+
const { traverseCsn } = require('./model/api');
|
|
20
|
+
const { createMessageFunctions, sortMessages, sortMessagesSeverityAware, deduplicateMessages } = require('./base/messages');
|
|
20
21
|
|
|
21
22
|
const parseLanguage = require('./language/antlrParser');
|
|
22
23
|
const { parseX, compileX, compileSyncX, compileSourcesX, InvocationError } = require('./compiler');
|
|
24
|
+
const { define } = require('./compiler/definer');
|
|
23
25
|
|
|
24
26
|
// The compiler version (taken from package.json)
|
|
25
27
|
function version() {
|
|
@@ -31,7 +33,6 @@ const {
|
|
|
31
33
|
messageString,
|
|
32
34
|
messageStringMultiline,
|
|
33
35
|
messageContext,
|
|
34
|
-
handleMessages,
|
|
35
36
|
hasErrors,
|
|
36
37
|
explainMessage,
|
|
37
38
|
hasMessageExplanation
|
|
@@ -39,15 +40,34 @@ const {
|
|
|
39
40
|
|
|
40
41
|
const { compactModel, compactQuery, compactExpr } = require('./json/to-csn')
|
|
41
42
|
|
|
43
|
+
function parseCdl( cdl, filename, options = {} ) {
|
|
44
|
+
options = Object.assign( {}, options, { parseCdl: true } );
|
|
45
|
+
const sources = Object.create(null);
|
|
46
|
+
const model = { sources, options };
|
|
47
|
+
const messageFunctions = createMessageFunctions( options, 'parse', model );
|
|
48
|
+
model.$messageFunctions = messageFunctions;
|
|
49
|
+
|
|
50
|
+
const xsn = parseLanguage( cdl, filename, Object.assign( { parseOnly: true }, options ),
|
|
51
|
+
messageFunctions );
|
|
52
|
+
sources[filename] = xsn;
|
|
53
|
+
define( model );
|
|
54
|
+
messageFunctions.throwWithError();
|
|
55
|
+
return compactModel( model );
|
|
56
|
+
}
|
|
57
|
+
|
|
42
58
|
function parseCql( cdl, filename = '<query>.cds', options = {} ) {
|
|
43
|
-
|
|
44
|
-
|
|
59
|
+
const messageFunctions = createMessageFunctions( options, 'parse' );
|
|
60
|
+
const xsn = parseLanguage( cdl, filename, Object.assign( { parseOnly: true }, options ),
|
|
61
|
+
messageFunctions, 'query' );
|
|
62
|
+
messageFunctions.throwWithError();
|
|
45
63
|
return compactQuery( xsn );
|
|
46
64
|
}
|
|
47
65
|
|
|
48
66
|
function parseExpr( cdl, filename = '<expr>.cds', options = {} ) {
|
|
49
|
-
|
|
50
|
-
|
|
67
|
+
const messageFunctions = createMessageFunctions( options, 'parse' );
|
|
68
|
+
const xsn = parseLanguage( cdl, filename, Object.assign( { parseOnly: true }, options ),
|
|
69
|
+
messageFunctions, 'expr' );
|
|
70
|
+
messageFunctions.throwWithError();
|
|
51
71
|
return compactExpr( xsn );
|
|
52
72
|
}
|
|
53
73
|
|
|
@@ -79,7 +99,7 @@ module.exports = {
|
|
|
79
99
|
preparedCsnToEdm : (csn, service, options) => { return backends.preparedCsnToEdm(csn, service, options).edmj},
|
|
80
100
|
|
|
81
101
|
// additional API:
|
|
82
|
-
parse: { cql: parseCql, expr: parseExpr }, // preferred names
|
|
102
|
+
parse: { cdl: parseCdl, cql: parseCql, expr: parseExpr }, // preferred names
|
|
83
103
|
/**
|
|
84
104
|
* @deprecated Use parse.cql instead
|
|
85
105
|
*/
|
|
@@ -92,6 +112,9 @@ module.exports = {
|
|
|
92
112
|
getArtifactCdsPersistenceName: getArtifactDatabaseNameOf,
|
|
93
113
|
getElementCdsPersistenceName: getElementDatabaseNameOf,
|
|
94
114
|
|
|
115
|
+
// Other API functions:
|
|
116
|
+
traverseCsn,
|
|
117
|
+
|
|
95
118
|
// INTERNAL functions for the cds-lsp package and friends - before you use
|
|
96
119
|
// it, you MUST talk with us - there can be potential incompatibilities with
|
|
97
120
|
// new releases (even having the same major version):
|