@sap/cds-compiler 3.1.2 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +101 -3
- package/bin/cdsc.js +4 -2
- package/doc/CHANGELOG_BETA.md +35 -0
- package/lib/api/main.js +153 -29
- package/lib/api/validate.js +8 -3
- package/lib/base/dictionaries.js +6 -6
- package/lib/base/error.js +2 -2
- package/lib/base/keywords.js +106 -24
- package/lib/base/message-registry.js +177 -79
- package/lib/base/messages.js +78 -57
- package/lib/base/model.js +2 -1
- package/lib/checks/actionsFunctions.js +1 -1
- package/lib/checks/annotationsOData.js +2 -2
- package/lib/checks/arrayOfs.js +15 -7
- package/lib/checks/cdsPersistence.js +1 -1
- package/lib/checks/checkForTypes.js +53 -0
- package/lib/checks/defaultValues.js +4 -2
- package/lib/checks/elements.js +81 -6
- package/lib/checks/foreignKeys.js +12 -13
- package/lib/checks/invalidTarget.js +10 -11
- package/lib/checks/managedInType.js +21 -15
- package/lib/checks/nullableKeys.js +1 -1
- package/lib/checks/onConditions.js +9 -9
- package/lib/checks/parameters.js +23 -0
- package/lib/checks/queryNoDbArtifacts.js +1 -1
- package/lib/checks/selectItems.js +1 -1
- package/lib/checks/sql-snippets.js +12 -10
- package/lib/checks/types.js +2 -2
- package/lib/checks/utils.js +17 -7
- package/lib/checks/validator.js +36 -14
- package/lib/compiler/assert-consistency.js +21 -13
- package/lib/compiler/builtins.js +8 -0
- package/lib/compiler/checks.js +57 -40
- package/lib/compiler/define.js +139 -69
- package/lib/compiler/extend.js +319 -50
- package/lib/compiler/finalize-parse-cdl.js +14 -9
- package/lib/compiler/kick-start.js +2 -35
- package/lib/compiler/populate.js +111 -68
- package/lib/compiler/propagator.js +5 -3
- package/lib/compiler/resolve.js +71 -108
- package/lib/compiler/shared.js +82 -54
- package/lib/compiler/tweak-assocs.js +26 -14
- package/lib/compiler/utils.js +13 -2
- package/lib/edm/annotations/genericTranslation.js +10 -7
- package/lib/edm/csn2edm.js +11 -11
- package/lib/edm/edm.js +17 -9
- package/lib/edm/edmPreprocessor.js +53 -30
- package/lib/edm/edmUtils.js +7 -2
- package/lib/gen/Dictionary.json +14 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -2
- package/lib/gen/languageParser.js +4312 -4186
- package/lib/inspect/inspectModelStatistics.js +1 -1
- package/lib/inspect/inspectPropagation.js +23 -9
- package/lib/json/csnVersion.js +13 -13
- package/lib/json/from-csn.js +161 -172
- package/lib/json/to-csn.js +70 -10
- package/lib/language/.eslintrc.json +4 -0
- package/lib/language/antlrParser.js +8 -11
- package/lib/language/docCommentParser.js +1 -2
- package/lib/language/errorStrategy.js +54 -27
- package/lib/language/genericAntlrParser.js +140 -93
- package/lib/language/language.g4 +57 -33
- package/lib/language/multiLineStringParser.js +75 -63
- package/lib/main.d.ts +3 -6
- package/lib/main.js +1 -0
- package/lib/model/.eslintrc.json +13 -0
- package/lib/model/api.js +4 -2
- package/lib/model/csnRefs.js +78 -50
- package/lib/model/csnUtils.js +272 -222
- package/lib/model/enrichCsn.js +41 -31
- package/lib/model/revealInternalProperties.js +61 -57
- package/lib/model/sortViews.js +35 -31
- package/lib/modelCompare/compare.js +52 -18
- package/lib/modelCompare/filter.js +83 -0
- package/lib/optionProcessor.js +10 -1
- package/lib/render/manageConstraints.js +11 -7
- package/lib/render/toCdl.js +151 -106
- package/lib/render/toHdbcds.js +8 -6
- package/lib/render/toRename.js +4 -4
- package/lib/render/toSql.js +17 -7
- package/lib/render/utils/common.js +27 -9
- package/lib/render/utils/sql.js +5 -5
- package/lib/sql-identifier.js +7 -0
- package/lib/transform/db/applyTransformations.js +32 -3
- package/lib/transform/db/assertUnique.js +27 -38
- package/lib/transform/db/expansion.js +92 -41
- package/lib/transform/db/flattening.js +1 -1
- package/lib/transform/db/temporal.js +3 -1
- package/lib/transform/db/transformExists.js +8 -2
- package/lib/transform/db/views.js +42 -13
- package/lib/transform/draft/db.js +2 -2
- package/lib/transform/forOdataNew.js +10 -7
- package/lib/transform/{forHanaNew.js → forRelationalDB.js} +18 -12
- package/lib/transform/localized.js +29 -20
- package/lib/transform/odata/toFinalBaseType.js +8 -11
- package/lib/transform/odata/typesExposure.js +2 -1
- package/lib/transform/parseExpr.js +245 -0
- package/lib/transform/transformUtilsNew.js +122 -51
- package/lib/transform/translateAssocsToJoins.js +17 -16
- package/lib/utils/moduleResolve.js +5 -5
- package/lib/utils/objectUtils.js +3 -3
- package/lib/utils/term.js +5 -5
- package/package.json +2 -2
- package/share/messages/anno-duplicate-unrelated-layer.md +6 -6
- package/share/messages/check-proper-type-of.md +4 -4
- package/share/messages/check-proper-type.md +2 -2
- package/share/messages/duplicate-autoexposed.md +4 -4
- package/share/messages/extend-repeated-intralayer.md +4 -5
- package/share/messages/extend-unrelated-layer.md +4 -4
- package/share/messages/message-explanations.json +3 -1
- package/share/messages/redirected-to-ambiguous.md +7 -6
- package/share/messages/redirected-to-complex.md +63 -0
- package/share/messages/redirected-to-unrelated.md +6 -5
- package/share/messages/rewrite-not-supported.md +4 -4
- package/share/messages/{syntax-expected-integer.md → syntax-expecting-integer.md} +4 -4
- package/share/messages/wildcard-excluding-one.md +37 -0
|
@@ -12,17 +12,21 @@ const { dictAdd, dictAddArray } = require('../base/dictionaries');
|
|
|
12
12
|
const locUtils = require('../base/location');
|
|
13
13
|
const { parseDocComment } = require('./docCommentParser');
|
|
14
14
|
const { parseMultiLineStringLiteral } = require('./multiLineStringParser');
|
|
15
|
-
const {
|
|
16
|
-
|
|
15
|
+
const {
|
|
16
|
+
functionsWithoutParens,
|
|
17
|
+
specialFunctions,
|
|
18
|
+
quotedLiteralPatterns,
|
|
19
|
+
} = require('../compiler/builtins');
|
|
20
|
+
const { pathName } = require('../compiler/utils');
|
|
21
|
+
const { isBetaEnabled } = require('../base/model');
|
|
17
22
|
|
|
18
23
|
const $location = Symbol.for('cds.$location');
|
|
19
24
|
|
|
20
25
|
// Push message `msg` with location `loc` to array of errors:
|
|
21
26
|
function _message( parser, severity, id, loc, ...args ) {
|
|
22
27
|
const msg = parser.$messageFunctions[severity]; // set in antlrParser.js
|
|
23
|
-
if (loc instanceof antlr4.CommonToken)
|
|
28
|
+
if (loc instanceof antlr4.CommonToken)
|
|
24
29
|
loc = parser.tokenLocation(loc);
|
|
25
|
-
}
|
|
26
30
|
return msg( id, loc, ...args );
|
|
27
31
|
}
|
|
28
32
|
|
|
@@ -57,10 +61,18 @@ class GenericAntlrParser extends antlr4.Parser {
|
|
|
57
61
|
|
|
58
62
|
// TODO: Use actual methods.
|
|
59
63
|
Object.assign(GenericAntlrParser.prototype, {
|
|
60
|
-
message
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
message(...args) {
|
|
65
|
+
return _message( this, 'message', ...args );
|
|
66
|
+
},
|
|
67
|
+
error(...args) {
|
|
68
|
+
return _message( this, 'error', ...args );
|
|
69
|
+
},
|
|
70
|
+
warning(...args) {
|
|
71
|
+
return _message( this, 'warning', ...args );
|
|
72
|
+
},
|
|
73
|
+
info(...args) {
|
|
74
|
+
return _message( this, 'info', ...args );
|
|
75
|
+
},
|
|
64
76
|
attachLocation,
|
|
65
77
|
assignAnnotation,
|
|
66
78
|
addAnnotation,
|
|
@@ -91,6 +103,7 @@ Object.assign(GenericAntlrParser.prototype, {
|
|
|
91
103
|
addDef,
|
|
92
104
|
addItem,
|
|
93
105
|
addExtension,
|
|
106
|
+
aspectWithoutElements,
|
|
94
107
|
createSource,
|
|
95
108
|
createDict,
|
|
96
109
|
createArray,
|
|
@@ -103,7 +116,6 @@ Object.assign(GenericAntlrParser.prototype, {
|
|
|
103
116
|
associationInSelectItem,
|
|
104
117
|
reportExpandInline,
|
|
105
118
|
checkTypeFacet,
|
|
106
|
-
notSupportedYet,
|
|
107
119
|
csnParseOnly,
|
|
108
120
|
disallowElementExtension,
|
|
109
121
|
noAssignmentInSameLine,
|
|
@@ -126,30 +138,19 @@ Object.assign(GenericAntlrParser.prototype, {
|
|
|
126
138
|
// language construct can come from a CSN as source.
|
|
127
139
|
// TODO: this is not completely done this way
|
|
128
140
|
|
|
129
|
-
function notSupportedYet( text, ...tokens ) {
|
|
130
|
-
if (!text)
|
|
131
|
-
return;
|
|
132
|
-
if (typeof text !== 'string') {
|
|
133
|
-
tokens = [ text, ...tokens ];
|
|
134
|
-
text = `${ tokens.map( t => t.text.toUpperCase() ).join(' ') } is not supported`;
|
|
135
|
-
}
|
|
136
|
-
this.error( null, this.tokenLocation( tokens[0], tokens[tokens.length - 1] ), text );
|
|
137
|
-
}
|
|
138
|
-
|
|
139
141
|
// Use the following function for language constructs which we (currently) do
|
|
140
142
|
// not really compile, just use to produce a CSN for functions parse.cql() and
|
|
141
143
|
// parse.expr().
|
|
142
|
-
function
|
|
143
|
-
|
|
144
|
+
// This function has a similar interface to our message functions on purpose!
|
|
145
|
+
// (tokens ~= location)
|
|
146
|
+
function csnParseOnly( msgId, tokens, textArgs ) {
|
|
147
|
+
if (!msgId || this.options.parseOnly)
|
|
144
148
|
return;
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
text = `${ tokens.map( t => t.text.toUpperCase() ).join(' ') } is not supported`;
|
|
148
|
-
}
|
|
149
|
-
this.error( null, this.tokenLocation( tokens[0], tokens[tokens.length - 1] ), text );
|
|
149
|
+
const loc = this.tokenLocation( tokens[0], tokens[tokens.length - 1] );
|
|
150
|
+
this.error( msgId, loc, textArgs );
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
|
|
153
|
+
/** @this {object} */
|
|
153
154
|
function noSemicolonHere() {
|
|
154
155
|
const handler = this._errHandler;
|
|
155
156
|
const t = this.getCurrentToken();
|
|
@@ -193,7 +194,7 @@ function setLocalTokenIfBefore( string, tokenName, before, inSameLine ) {
|
|
|
193
194
|
}
|
|
194
195
|
|
|
195
196
|
function setLocalTokenForId( tokenNameMap ) {
|
|
196
|
-
const tokenName = tokenNameMap[
|
|
197
|
+
const tokenName = tokenNameMap[this._input.LT(2).text || ''];
|
|
197
198
|
const ll1 = this.getCurrentToken();
|
|
198
199
|
if (tokenName &&
|
|
199
200
|
(ll1.type === this.constructor.Identifier || /^[a-zA-Z_]+$/.test( ll1.text )))
|
|
@@ -227,8 +228,10 @@ function setLocalTokenForId( tokenNameMap ) {
|
|
|
227
228
|
*/
|
|
228
229
|
function disallowElementExtension(elemName, outer, extensionVariant) {
|
|
229
230
|
if (elemName.path) {
|
|
230
|
-
|
|
231
|
-
|
|
231
|
+
const loc = this.tokenLocation(this.getCurrentToken());
|
|
232
|
+
this.error( 'syntax-invalid-extend', loc, { kind: extensionVariant } );
|
|
233
|
+
// remove last, i.e. new extension
|
|
234
|
+
outer.extensions.pop();
|
|
232
235
|
}
|
|
233
236
|
}
|
|
234
237
|
|
|
@@ -266,7 +269,7 @@ const genericTokenTypes = {
|
|
|
266
269
|
|
|
267
270
|
function prepareGenericKeywords( pathItem, expected = null) {
|
|
268
271
|
const length = pathItem?.args?.length || 0;
|
|
269
|
-
const argPos = (expected ? length -1 : length);
|
|
272
|
+
const argPos = (expected ? length - 1 : length);
|
|
270
273
|
const func = pathItem?.id && specialFunctions[pathItem.id.toUpperCase()];
|
|
271
274
|
const spec = func && func[argPos] || specialFunctions[''][argPos ? 1 : 0];
|
|
272
275
|
this.$genericKeywords = spec;
|
|
@@ -298,7 +301,7 @@ function prepareGenericKeywords( pathItem, expected = null) {
|
|
|
298
301
|
// To be called before having matched ( HideAlternatives | … )
|
|
299
302
|
function reportErrorForGenericKeyword() {
|
|
300
303
|
this._errHandler.reportUnwantedToken( this );
|
|
301
|
-
//this._errHandler.reportInputMismatch( this, { offending: this._input.LT(1) }, null );
|
|
304
|
+
// this._errHandler.reportInputMismatch( this, { offending: this._input.LT(1) }, null );
|
|
302
305
|
}
|
|
303
306
|
|
|
304
307
|
// Attach location matched by current rule to node `art`. If a location is
|
|
@@ -317,11 +320,12 @@ function attachLocation( art ) {
|
|
|
317
320
|
// case we can't rely on `this._ctx.stop.line`.
|
|
318
321
|
if (this.isMultiLineToken(this._ctx.stop)) {
|
|
319
322
|
this.fixMultiLineTokenEndLocation(this._ctx.stop, art.location);
|
|
320
|
-
|
|
321
|
-
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
322
325
|
const { stop } = this._ctx;
|
|
323
326
|
art.location.endLine = stop.line;
|
|
324
|
-
|
|
327
|
+
// after the last char (special for EOF?)
|
|
328
|
+
art.location.endCol = stop.stop - stop.start + stop.column + 2;
|
|
325
329
|
}
|
|
326
330
|
|
|
327
331
|
return art;
|
|
@@ -355,7 +359,7 @@ function assignAnnotation( art, anno, prefix = '', iHaveVariant = false ) {
|
|
|
355
359
|
}
|
|
356
360
|
else {
|
|
357
361
|
name.absolute = absolute;
|
|
358
|
-
this.addAnnotation( art,
|
|
362
|
+
this.addAnnotation( art, `@${ absolute }`, anno );
|
|
359
363
|
}
|
|
360
364
|
if (!prefix) { // set deprecated $annotations for cds-lsp
|
|
361
365
|
if (!art.$annotations)
|
|
@@ -366,6 +370,7 @@ function assignAnnotation( art, anno, prefix = '', iHaveVariant = false ) {
|
|
|
366
370
|
}
|
|
367
371
|
|
|
368
372
|
function addAnnotation( art, prop, anno ) {
|
|
373
|
+
// TODO: just overwrite after having reported the error
|
|
369
374
|
dictAddArray( art, prop, anno, (n, location, a) => {
|
|
370
375
|
// if we would make it a warning, we would still need to keep it an error
|
|
371
376
|
// with '...'; otherwise parse.cdl would have to split annotate statements
|
|
@@ -376,7 +381,9 @@ function addAnnotation( art, prop, anno ) {
|
|
|
376
381
|
} );
|
|
377
382
|
}
|
|
378
383
|
|
|
379
|
-
const extensionDicts = {
|
|
384
|
+
const extensionDicts = {
|
|
385
|
+
elements: true, enum: true, params: true, returns: true,
|
|
386
|
+
};
|
|
380
387
|
|
|
381
388
|
function checkExtensionDict( dict ) {
|
|
382
389
|
for (const name in dict) {
|
|
@@ -385,8 +392,8 @@ function checkExtensionDict( dict ) {
|
|
|
385
392
|
continue;
|
|
386
393
|
|
|
387
394
|
if (def.kind !== 'annotate') {
|
|
388
|
-
const numDefines
|
|
389
|
-
def.$duplicates.reduce( addOneForDefinition, addOneForDefinition( 0, def ) );
|
|
395
|
+
const numDefines
|
|
396
|
+
= def.$duplicates.reduce( addOneForDefinition, addOneForDefinition( 0, def ) );
|
|
390
397
|
this.handleDuplicateExtension( def, name, numDefines );
|
|
391
398
|
for (const dup of def.$duplicates)
|
|
392
399
|
this.handleDuplicateExtension( dup, name, numDefines );
|
|
@@ -396,12 +403,13 @@ function checkExtensionDict( dict ) {
|
|
|
396
403
|
for (const dup of def.$duplicates) {
|
|
397
404
|
for (const prop of Object.keys( dup )) {
|
|
398
405
|
if (prop.charAt(0) === '@') {
|
|
399
|
-
this.addAnnotation( def, prop, dup[prop] )
|
|
406
|
+
this.addAnnotation( def, prop, dup[prop] );
|
|
400
407
|
}
|
|
401
408
|
else if (prop === 'doc') {
|
|
402
|
-
if (def.doc)
|
|
409
|
+
if (def.doc) {
|
|
403
410
|
this.warning( 'syntax-duplicate-doc-comment', def.doc.location, {},
|
|
404
411
|
'Doc comment is overwritten by another one below' );
|
|
412
|
+
}
|
|
405
413
|
def.doc = dup.doc;
|
|
406
414
|
}
|
|
407
415
|
else if (extensionDicts[prop]) {
|
|
@@ -427,11 +435,13 @@ function addOneForDefinition( count, ext ) {
|
|
|
427
435
|
* @param {number} numDefines
|
|
428
436
|
*/
|
|
429
437
|
function handleDuplicateExtension( ext, name, numDefines ) {
|
|
430
|
-
if (ext.kind === 'extend')
|
|
438
|
+
if (ext.kind === 'extend') {
|
|
431
439
|
this.error( 'syntax-duplicate-extend', [ ext.name.location ],
|
|
432
440
|
{ name, '#': (numDefines ? 'define' : 'extend') } );
|
|
433
|
-
|
|
434
|
-
|
|
441
|
+
}
|
|
442
|
+
else if (numDefines === 1) {
|
|
443
|
+
ext.$errorReported = 'syntax-duplicate-extend';
|
|
444
|
+
} // a definition, but not duplicate
|
|
435
445
|
}
|
|
436
446
|
|
|
437
447
|
|
|
@@ -470,8 +480,9 @@ function tokenLocation( token, endToken = null ) {
|
|
|
470
480
|
col: token.column + 1,
|
|
471
481
|
// Default for single line tokens
|
|
472
482
|
endLine: endToken.line,
|
|
473
|
-
|
|
474
|
-
|
|
483
|
+
// after the last char (special for EOF?)
|
|
484
|
+
endCol: endToken.stop - endToken.start + endToken.column + 2,
|
|
485
|
+
};
|
|
475
486
|
|
|
476
487
|
// This check is done for performance reason. No need to access a token's
|
|
477
488
|
// data if we know that it spans only one single line.
|
|
@@ -514,9 +525,11 @@ function fixMultiLineTokenEndLocation( token, location ) {
|
|
|
514
525
|
if (newLineCount > 0) {
|
|
515
526
|
location.endLine = token.line + newLineCount;
|
|
516
527
|
location.endCol = token.stop - lastNewlineIndex + 1;
|
|
517
|
-
}
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
518
530
|
location.endLine = token.line;
|
|
519
|
-
|
|
531
|
+
// after the last char (special for EOF?)
|
|
532
|
+
location.endCol = token.stop - token.start + token.column + 2;
|
|
520
533
|
}
|
|
521
534
|
}
|
|
522
535
|
|
|
@@ -568,7 +581,7 @@ function unaryOpForParens( query, val ) {
|
|
|
568
581
|
if (!parens)
|
|
569
582
|
return query;
|
|
570
583
|
const location = parens[parens.length - 1];
|
|
571
|
-
return { op: { val, location }, location, args: [query] };
|
|
584
|
+
return { op: { val, location }, location, args: [ query ] };
|
|
572
585
|
}
|
|
573
586
|
|
|
574
587
|
// If the token before the current one is a doc comment (ignoring other tokens
|
|
@@ -607,12 +620,16 @@ function classifyImplicitName( category, ref ) {
|
|
|
607
620
|
}
|
|
608
621
|
|
|
609
622
|
function fragileAlias( ast, safe = false ) {
|
|
610
|
-
if (
|
|
623
|
+
if (this.getCurrentToken().text === '.')
|
|
624
|
+
return ast;
|
|
625
|
+
if (safe || ast.$delimited || !/^[a-zA-Z][a-zA-Z_]+$/.test( ast.id )) {
|
|
611
626
|
this.warning( 'syntax-sloppy-alias', ast.location, { keyword: 'as' },
|
|
612
627
|
'Please add the keyword $(KEYWORD) in front of the alias name' );
|
|
613
|
-
|
|
628
|
+
}
|
|
629
|
+
else { // configurable error
|
|
614
630
|
this.message( 'syntax-fragile-alias', ast.location, { keyword: 'as' },
|
|
615
631
|
'Please add the keyword $(KEYWORD) in front of the alias name' );
|
|
632
|
+
}
|
|
616
633
|
return ast;
|
|
617
634
|
}
|
|
618
635
|
|
|
@@ -643,6 +660,7 @@ function identAst( token, category, noTokenTypeCheck = false ) {
|
|
|
643
660
|
}
|
|
644
661
|
else {
|
|
645
662
|
this.message( 'syntax-deprecated-ident', token, { delimited: id },
|
|
663
|
+
// eslint-disable-next-line max-len
|
|
646
664
|
'Deprecated delimited identifier syntax, use $(DELIMITED) - strings are delimited by single quotes' );
|
|
647
665
|
}
|
|
648
666
|
return { id, $delimited: true, location: this.tokenLocation( token ) };
|
|
@@ -674,7 +692,7 @@ function setLastAsXpr( args ) {
|
|
|
674
692
|
|
|
675
693
|
function xprToken() {
|
|
676
694
|
const token = this._input.LT(-1);
|
|
677
|
-
return { location: this.tokenLocation( token ), val: token.text, literal: 'token' }
|
|
695
|
+
return { location: this.tokenLocation( token ), val: token.text, literal: 'token' };
|
|
678
696
|
}
|
|
679
697
|
|
|
680
698
|
function valuePathAst( ref ) {
|
|
@@ -687,7 +705,7 @@ function valuePathAst( ref ) {
|
|
|
687
705
|
const item = path.find( i => i.args && i.$syntax !== ':' );
|
|
688
706
|
if (!item)
|
|
689
707
|
return ref;
|
|
690
|
-
this.error( 'syntax-not-supported', item.location,
|
|
708
|
+
this.error( 'syntax-not-supported', item.location, {},
|
|
691
709
|
'Methods in expressions are not supported yet' );
|
|
692
710
|
path.broken = true;
|
|
693
711
|
path.length = 1;
|
|
@@ -703,7 +721,9 @@ function valuePathAst( ref ) {
|
|
|
703
721
|
implicit.isIdentifier = 'func';
|
|
704
722
|
const op = { location, val: 'call' };
|
|
705
723
|
return (args)
|
|
706
|
-
? {
|
|
724
|
+
? {
|
|
725
|
+
op, func: ref, location: ref.location, args,
|
|
726
|
+
}
|
|
707
727
|
: { op, func: ref, location: ref.location };
|
|
708
728
|
}
|
|
709
729
|
|
|
@@ -711,15 +731,15 @@ function valuePathAst( ref ) {
|
|
|
711
731
|
// otherwise (including for '+'), represent it as extra unary prefix operator.
|
|
712
732
|
function signedExpression( signToken, expr ) {
|
|
713
733
|
const sign = this.valueWithTokenLocation( signToken.text, signToken );
|
|
714
|
-
const nval
|
|
715
|
-
(signToken.text === '-' &&
|
|
734
|
+
const nval
|
|
735
|
+
= (signToken.text === '-' &&
|
|
716
736
|
expr && // expr may be null if `-` rule can't be parsed
|
|
717
737
|
expr.literal === 'number' &&
|
|
718
738
|
sign.location.endLine === expr.location.line &&
|
|
719
739
|
sign.location.endCol === expr.location.col &&
|
|
720
740
|
(typeof expr.val === 'number'
|
|
721
|
-
|
|
722
|
-
|
|
741
|
+
? expr.val >= 0 && -expr.val
|
|
742
|
+
: !expr.val.startsWith('-') && `-${ expr.val }`)) || false;
|
|
723
743
|
if (nval === false)
|
|
724
744
|
return { op: sign, args: expr ? [ expr ] : [] };
|
|
725
745
|
expr.val = nval;
|
|
@@ -745,9 +765,11 @@ function numberLiteral( token, sign, text = token.text ) {
|
|
|
745
765
|
const num = Number.parseFloat( text || '0' ); // not Number.parseInt() !
|
|
746
766
|
if (!Number.isSafeInteger(num)) {
|
|
747
767
|
if (sign == null) {
|
|
748
|
-
this.error( 'syntax-
|
|
768
|
+
this.error( 'syntax-expecting-integer', token,
|
|
769
|
+
{ '#': !text.match(/^[0-9]*$/) ? 'normal' : 'unsafe' } );
|
|
749
770
|
}
|
|
750
|
-
|
|
771
|
+
|
|
772
|
+
else if (text !== `${ num }`) {
|
|
751
773
|
return { literal: 'number', val: text, location };
|
|
752
774
|
}
|
|
753
775
|
}
|
|
@@ -766,7 +788,8 @@ function quotedLiteral( token, literal ) {
|
|
|
766
788
|
if (token.text.startsWith('`')) {
|
|
767
789
|
val = this.parseMultiLineStringLiteral(token);
|
|
768
790
|
literal = 'string';
|
|
769
|
-
}
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
770
793
|
pos = token.text.search( '\'' ) + 1; // pos of char after quote
|
|
771
794
|
val = token.text.slice( pos, -1 ).replace( /''/g, '\'' );
|
|
772
795
|
}
|
|
@@ -780,7 +803,7 @@ function quotedLiteral( token, literal ) {
|
|
|
780
803
|
|
|
781
804
|
if (p.unexpected_char) {
|
|
782
805
|
const idx = val.search(p.unexpected_char);
|
|
783
|
-
if (
|
|
806
|
+
if (idx > -1) {
|
|
784
807
|
this.warning( 'syntax-invalid-literal', {
|
|
785
808
|
file: location.file,
|
|
786
809
|
line: location.line,
|
|
@@ -932,6 +955,15 @@ function addExtension( ext, parent, kind, artName, elemPath ) {
|
|
|
932
955
|
}
|
|
933
956
|
}
|
|
934
957
|
|
|
958
|
+
function aspectWithoutElements( art ) {
|
|
959
|
+
// Empty dictionary to allow element extensions.
|
|
960
|
+
art.elements = this.createDict();
|
|
961
|
+
if (!isBetaEnabled( this.options, 'aspectWithoutElements' )) {
|
|
962
|
+
this.error( null, [ art.name.location ], {},
|
|
963
|
+
'Aspects without elements are not supported, yet' );
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
935
967
|
// must be in action directly after having parsed '{' or '(`
|
|
936
968
|
function createDict() {
|
|
937
969
|
const dict = Object.create(null);
|
|
@@ -975,10 +1007,10 @@ function createPrefixOp( token, args ) {
|
|
|
975
1007
|
|
|
976
1008
|
// Create AST node for binary operator `op` and arguments `args`
|
|
977
1009
|
function leftAssocBinaryOp( left, opToken, eToken, right, extraProp = 'quantifier' ) {
|
|
978
|
-
const op = this.valueWithTokenLocation( opToken.text.toLowerCase()
|
|
1010
|
+
const op = this.valueWithTokenLocation( opToken.text.toLowerCase(), opToken);
|
|
979
1011
|
const extra = eToken
|
|
980
|
-
|
|
981
|
-
|
|
1012
|
+
? this.valueWithTokenLocation( eToken.text.toLowerCase(), eToken )
|
|
1013
|
+
: undefined;
|
|
982
1014
|
if (!left.$parens &&
|
|
983
1015
|
(left.op && left.op.val) === (op && op.val) &&
|
|
984
1016
|
(left[extraProp] && left[extraProp].val) === (extra && extra.val)) {
|
|
@@ -986,11 +1018,12 @@ function leftAssocBinaryOp( left, opToken, eToken, right, extraProp = 'quantifie
|
|
|
986
1018
|
return left;
|
|
987
1019
|
}
|
|
988
1020
|
else if (extra) {
|
|
989
|
-
return {
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
return { op, args: [ left, right ], location: left.location };
|
|
1021
|
+
return {
|
|
1022
|
+
op, [extraProp]: extra, args: [ left, right ], location: left.location,
|
|
1023
|
+
};
|
|
993
1024
|
}
|
|
1025
|
+
|
|
1026
|
+
return { op, args: [ left, right ], location: left.location };
|
|
994
1027
|
}
|
|
995
1028
|
|
|
996
1029
|
// Set property `prop` of `target` to value `value`. Issue error if that
|
|
@@ -1001,7 +1034,7 @@ function setOnce( target, prop, value, ...tokens ) {
|
|
|
1001
1034
|
const prev = target[prop];
|
|
1002
1035
|
if (prev) {
|
|
1003
1036
|
this.error( 'syntax-repeated-option', loc, { option: prev.option },
|
|
1004
|
-
|
|
1037
|
+
'Option $(OPTION) has already been specified' );
|
|
1005
1038
|
}
|
|
1006
1039
|
if (typeof value === 'boolean') {
|
|
1007
1040
|
if (!value)
|
|
@@ -1039,57 +1072,71 @@ function associationInSelectItem( art ) {
|
|
|
1039
1072
|
if (!art.value) // e.g. `expand` without value (for new structures)
|
|
1040
1073
|
return;
|
|
1041
1074
|
|
|
1042
|
-
const isPath = art.value.path && art.value.path.length
|
|
1075
|
+
const isPath = art.value.path && art.value.path.length;
|
|
1043
1076
|
const isIdentifier = isPath && art.value.path.length === 1;
|
|
1044
1077
|
if (isIdentifier) {
|
|
1045
1078
|
if (!art.name) {
|
|
1046
1079
|
art.name = art.value.path[0];
|
|
1047
|
-
}
|
|
1080
|
+
}
|
|
1081
|
+
else {
|
|
1048
1082
|
// Use alias if provided, i.e. ignore art.value.path.
|
|
1049
1083
|
this.error( 'query-unexpected-alias', art.name.location, {},
|
|
1050
|
-
|
|
1084
|
+
'Unexpected alias for association' );
|
|
1051
1085
|
}
|
|
1052
1086
|
delete art.value;
|
|
1053
|
-
}
|
|
1087
|
+
}
|
|
1088
|
+
else {
|
|
1054
1089
|
const loc = isPath ? art.value.path[1].location : art.value.location;
|
|
1055
1090
|
// If neither path nor alias are present, `query-req-name` is emitted in `populate.js`.
|
|
1056
1091
|
if (isPath || art.name) {
|
|
1057
1092
|
this.error( 'query-expected-identifier', loc, { '#': 'assoc' } );
|
|
1058
|
-
if (isPath)
|
|
1093
|
+
if (isPath)
|
|
1059
1094
|
art.name = art.value.path[art.value.path.length - 1];
|
|
1060
|
-
|
|
1095
|
+
|
|
1061
1096
|
delete art.value;
|
|
1062
1097
|
}
|
|
1063
1098
|
}
|
|
1064
1099
|
}
|
|
1065
1100
|
|
|
1066
|
-
function reportExpandInline(
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
token
|
|
1073
|
-
|
|
1074
|
-
|
|
1101
|
+
function reportExpandInline( column, isInline ) {
|
|
1102
|
+
const { name } = column;
|
|
1103
|
+
if (column.value && !column.value.path) {
|
|
1104
|
+
let token = this.getCurrentToken();
|
|
1105
|
+
// improve error location when using "inline" `.{…}` after ref (arguments and
|
|
1106
|
+
// filters not covered, not worth the effort); after an expression where
|
|
1107
|
+
// the last token is an identifier, not the `.` is wrong, but the `{`:
|
|
1108
|
+
if (isInline && !name && this._input.LT(-1).type >= this.constructor.Identifier)
|
|
1109
|
+
token = this._input.LT(2);
|
|
1110
|
+
this.error( 'syntax-unexpected-nested-proj', token,
|
|
1111
|
+
{ prop: isInline ? 'inline' : 'expand' },
|
|
1112
|
+
{ std: 'Unexpected nested $(PROP), can only be used after a reference' } );
|
|
1113
|
+
// continuation semantics:
|
|
1114
|
+
// - add elements anyway (could lead to duplicate errors as usual)
|
|
1115
|
+
// - no errors for refs inside expand/inline, but for refs in sibling expr
|
|
1116
|
+
// - think about: reference to these (sub) elements from other view
|
|
1117
|
+
}
|
|
1118
|
+
if (isInline && name) {
|
|
1119
|
+
const location = this.tokenLocation( isInline, this._input.LT(-1) );
|
|
1120
|
+
this.error( 'syntax-unexpected-alias', location, { prop: 'inline' },
|
|
1121
|
+
'Unexpected alias name before nested $(PROP)' );
|
|
1122
|
+
// continuation semantics: ignore AS
|
|
1123
|
+
}
|
|
1075
1124
|
}
|
|
1076
1125
|
|
|
1077
1126
|
function checkTypeFacet( art, argIdent ) {
|
|
1078
|
-
const id = argIdent
|
|
1127
|
+
const { id } = argIdent;
|
|
1079
1128
|
if (id === 'length' || id === 'scale' || id === 'precision' || id === 'srid') {
|
|
1080
1129
|
if (art[id] !== undefined) {
|
|
1081
1130
|
this.error( 'syntax-duplicate-argument', argIdent.location,
|
|
1082
|
-
|
|
1131
|
+
{ '#': 'duplicate', code: id } );
|
|
1083
1132
|
this.error( 'syntax-duplicate-argument', art[id].location,
|
|
1084
|
-
|
|
1133
|
+
{ '#': 'duplicate', code: id } );
|
|
1085
1134
|
}
|
|
1086
1135
|
return true;
|
|
1087
|
-
|
|
1088
|
-
} else {
|
|
1089
|
-
this.error( 'syntax-duplicate-argument', argIdent.location,
|
|
1090
|
-
{ '#': 'unknown', code: id } );
|
|
1091
|
-
return false;
|
|
1092
1136
|
}
|
|
1137
|
+
this.error( 'syntax-duplicate-argument', argIdent.location,
|
|
1138
|
+
{ '#': 'unknown', code: id } );
|
|
1139
|
+
return false;
|
|
1093
1140
|
}
|
|
1094
1141
|
|
|
1095
1142
|
module.exports = GenericAntlrParser;
|