@sap/cds-compiler 2.15.8 → 3.1.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 +102 -1590
- package/bin/.eslintrc.json +2 -1
- package/bin/cdsc.js +61 -46
- package/doc/API.md +11 -0
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +26 -5
- package/doc/CHANGELOG_DEPRECATED.md +55 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +282 -156
- package/lib/api/options.js +17 -88
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +280 -110
- package/lib/base/message-registry.js +85 -25
- package/lib/base/messages.js +119 -89
- package/lib/base/model.js +46 -2
- package/lib/base/optionProcessorHelper.js +53 -21
- package/lib/checks/actionsFunctions.js +15 -12
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +101 -15
- package/lib/checks/types.js +7 -8
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +3 -3
- package/lib/compiler/assert-consistency.js +78 -21
- package/lib/compiler/base.js +6 -4
- package/lib/compiler/builtins.js +177 -10
- package/lib/compiler/checks.js +1 -1
- package/lib/compiler/define.js +28 -23
- package/lib/compiler/extend.js +75 -18
- package/lib/compiler/finalize-parse-cdl.js +25 -18
- package/lib/compiler/index.js +27 -11
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +26 -39
- package/lib/compiler/propagator.js +12 -7
- package/lib/compiler/resolve.js +207 -236
- package/lib/compiler/shared.js +100 -93
- package/lib/compiler/tweak-assocs.js +13 -20
- package/lib/compiler/utils.js +20 -6
- package/lib/edm/annotations/preprocessAnnotations.js +12 -13
- package/lib/edm/csn2edm.js +35 -37
- 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 +338 -689
- package/lib/edm/edmUtils.js +97 -67
- package/lib/gen/Dictionary.json +29 -9
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +8 -31
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +892 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20629 -22474
- 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 +74 -69
- package/lib/json/to-csn.js +17 -14
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +61 -38
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +424 -292
- package/lib/language/language.g4 +604 -687
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +28 -42
- package/lib/main.js +104 -81
- package/lib/model/api.js +1 -1
- package/lib/model/csnRefs.js +57 -30
- package/lib/model/csnUtils.js +189 -287
- package/lib/model/revealInternalProperties.js +32 -10
- package/lib/model/sortViews.js +32 -31
- package/lib/modelCompare/compare.js +3 -0
- package/lib/optionProcessor.js +91 -57
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +387 -367
- package/lib/render/toHdbcds.js +20 -16
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +81 -59
- package/lib/render/utils/common.js +16 -3
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +3 -2
- package/lib/transform/db/associations.js +43 -35
- package/lib/transform/db/cdsPersistence.js +5 -16
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +7 -6
- package/lib/transform/db/flattening.js +16 -18
- package/lib/transform/db/transformExists.js +7 -5
- package/lib/transform/db/views.js +3 -3
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +30 -24
- package/lib/transform/forOdataNew.js +14 -16
- package/lib/transform/localized.js +35 -25
- package/lib/transform/odata/toFinalBaseType.js +10 -10
- package/lib/transform/odata/typesExposure.js +17 -8
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +63 -77
- package/lib/transform/translateAssocsToJoins.js +2 -2
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +11 -6
- package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
- package/lib/utils/file.js +31 -21
- package/lib/utils/moduleResolve.js +0 -1
- package/lib/utils/timetrace.js +20 -21
- package/package.json +34 -4
- package/share/messages/syntax-expected-integer.md +9 -8
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/checks/unknownMagic.js +0 -41
- package/lib/fix_antlr4-8_warning.js +0 -56
|
@@ -7,12 +7,13 @@
|
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
9
|
const antlr4 = require('antlr4');
|
|
10
|
-
const { ATNState } = require('antlr4/atn/ATNState');
|
|
10
|
+
const { ATNState } = require('antlr4/src/antlr4/atn/ATNState');
|
|
11
11
|
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 { functionsWithoutParens, specialFunctions } = require('../compiler/builtins');
|
|
15
|
+
const { functionsWithoutParens, specialFunctions, quotedLiteralPatterns } = require('../compiler/builtins');
|
|
16
|
+
const { pathName } = require("../compiler/utils");
|
|
16
17
|
|
|
17
18
|
const $location = Symbol.for('cds.$location');
|
|
18
19
|
|
|
@@ -32,118 +33,91 @@ function _message( parser, severity, id, loc, ...args ) {
|
|
|
32
33
|
// this.<function>(...)
|
|
33
34
|
// in the actions inside the grammar.
|
|
34
35
|
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
// - `unexpected_msg`: error message which is issued if `unexpected_char` matches
|
|
121
|
-
// - `unexpected_char`: regular expression matching an illegal character in `value`,
|
|
122
|
-
// the error location is only correct for a literal <prefix>'<value>'
|
|
123
|
-
// - `literal`: the value which is used instead of `prefix` in the AST
|
|
124
|
-
// - `normalized`: function called with argument `value`, return value is used
|
|
125
|
-
// instead of `value` in the AST
|
|
126
|
-
// TODO: think about laxer regexp for date/time/timestamp - normalization?
|
|
127
|
-
const quotedLiteralPatterns = {
|
|
128
|
-
x: {
|
|
129
|
-
test_msg: 'A binary literal must have an even number of characters',
|
|
130
|
-
test_fn: (str => Number.isInteger(str.length / 2)),
|
|
131
|
-
unexpected_msg: 'A binary literal must only contain characters 0-9, a-f and A-F',
|
|
132
|
-
unexpected_char: /[^0-9a-f]/i,
|
|
133
|
-
},
|
|
134
|
-
time: {
|
|
135
|
-
test_msg: 'Expected time\'HH:MM:SS\' where H, M and S are numbers and \':SS\' is optional',
|
|
136
|
-
test_re: /^[0-9]{1,2}:[0-9]{1,2}(:[0-9]{1,2})?$/,
|
|
137
|
-
},
|
|
138
|
-
date: {
|
|
139
|
-
test_msg: 'Expected date\'YYYY-MM-DD\' where Y, M and D are numbers',
|
|
140
|
-
test_re: /^[0-9]{1,4}-[0-9]{1,2}-[0-9]{1,2}$/,
|
|
141
|
-
},
|
|
142
|
-
timestamp: {
|
|
143
|
-
test_msg: 'Expected timestamp\'YYYY-MM-DD HH:MM:SS.u…u\' where Y, M, D, H, S and u are numbers (optional 1-7×u)',
|
|
144
|
-
test_re: /^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}(:[0-9]{2}(\.[0-9]{1,7})?)?$/,
|
|
145
|
-
},
|
|
146
|
-
};
|
|
36
|
+
class GenericAntlrParser extends antlr4.Parser {
|
|
37
|
+
constructor( ...args ) {
|
|
38
|
+
// ANTLR restriction: we cannot add parameters to the constructor.
|
|
39
|
+
super( ...args );
|
|
40
|
+
this.buildParseTrees = false;
|
|
41
|
+
|
|
42
|
+
// Common properties.
|
|
43
|
+
// We set them here so that they are available in the prototype.
|
|
44
|
+
// This improved performance by 25% for certain scenario tests.
|
|
45
|
+
// Probably because there was no need to look up the prototype chain anymore.
|
|
46
|
+
this.$adaptExpectedToken = null;
|
|
47
|
+
this.$adaptExpectedExcludes = [ ];
|
|
48
|
+
this.$nextTokensToken = null;
|
|
49
|
+
this.$nextTokensContext = null;
|
|
50
|
+
|
|
51
|
+
this.options = {};
|
|
52
|
+
|
|
53
|
+
this.genericFunctionsStack = [];
|
|
54
|
+
this.$genericKeywords = specialFunctions[''][1];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// TODO: Use actual methods.
|
|
59
|
+
Object.assign(GenericAntlrParser.prototype, {
|
|
60
|
+
message: function(...args) { return _message( this, 'message', ...args ); },
|
|
61
|
+
error: function(...args) { return _message( this, 'error', ...args ); },
|
|
62
|
+
warning: function(...args) { return _message( this, 'warning', ...args ); },
|
|
63
|
+
info: function(...args) { return _message( this, 'info', ...args ); },
|
|
64
|
+
attachLocation,
|
|
65
|
+
assignAnnotation,
|
|
66
|
+
addAnnotation,
|
|
67
|
+
checkExtensionDict,
|
|
68
|
+
handleDuplicateExtension,
|
|
69
|
+
startLocation,
|
|
70
|
+
tokenLocation,
|
|
71
|
+
isMultiLineToken,
|
|
72
|
+
fixMultiLineTokenEndLocation,
|
|
73
|
+
valueWithTokenLocation,
|
|
74
|
+
previousTokenAtLocation,
|
|
75
|
+
combinedLocation,
|
|
76
|
+
surroundByParens,
|
|
77
|
+
unaryOpForParens,
|
|
78
|
+
leftAssocBinaryOp,
|
|
79
|
+
classifyImplicitName,
|
|
80
|
+
fragileAlias,
|
|
81
|
+
identAst,
|
|
82
|
+
functionAst,
|
|
83
|
+
setLastAsXpr,
|
|
84
|
+
xprToken,
|
|
85
|
+
valuePathAst,
|
|
86
|
+
signedExpression,
|
|
87
|
+
numberLiteral,
|
|
88
|
+
quotedLiteral,
|
|
89
|
+
pathName,
|
|
90
|
+
docComment,
|
|
91
|
+
addDef,
|
|
92
|
+
addItem,
|
|
93
|
+
addExtension,
|
|
94
|
+
createSource,
|
|
95
|
+
createDict,
|
|
96
|
+
createArray,
|
|
97
|
+
finalizeDictOrArray,
|
|
98
|
+
createPrefixOp,
|
|
99
|
+
setOnce,
|
|
100
|
+
setMaxCardinality,
|
|
101
|
+
pushIdent,
|
|
102
|
+
handleComposition,
|
|
103
|
+
associationInSelectItem,
|
|
104
|
+
reportExpandInline,
|
|
105
|
+
checkTypeFacet,
|
|
106
|
+
notSupportedYet,
|
|
107
|
+
csnParseOnly,
|
|
108
|
+
disallowElementExtension,
|
|
109
|
+
noAssignmentInSameLine,
|
|
110
|
+
noSemicolonHere,
|
|
111
|
+
setLocalToken,
|
|
112
|
+
setLocalTokenIfBefore,
|
|
113
|
+
setLocalTokenForId,
|
|
114
|
+
excludeExpected,
|
|
115
|
+
isStraightBefore,
|
|
116
|
+
meltKeywordToIdentifier,
|
|
117
|
+
prepareGenericKeywords,
|
|
118
|
+
reportErrorForGenericKeyword,
|
|
119
|
+
parseMultiLineStringLiteral,
|
|
120
|
+
});
|
|
147
121
|
|
|
148
122
|
// Use the following function for language constructs which we (currently)
|
|
149
123
|
// just being able to parse, in able to run tests from HANA CDS. As soon as we
|
|
@@ -163,8 +137,8 @@ function notSupportedYet( text, ...tokens ) {
|
|
|
163
137
|
}
|
|
164
138
|
|
|
165
139
|
// Use the following function for language constructs which we (currently) do
|
|
166
|
-
// not really compile, just use to produce a CSN for functions
|
|
167
|
-
//
|
|
140
|
+
// not really compile, just use to produce a CSN for functions parse.cql() and
|
|
141
|
+
// parse.expr().
|
|
168
142
|
function csnParseOnly( text, ...tokens ) {
|
|
169
143
|
if (!text || this.options.parseOnly)
|
|
170
144
|
return;
|
|
@@ -218,6 +192,15 @@ function setLocalTokenIfBefore( string, tokenName, before, inSameLine ) {
|
|
|
218
192
|
ll1.type = this.constructor[tokenName];
|
|
219
193
|
}
|
|
220
194
|
|
|
195
|
+
function setLocalTokenForId( tokenNameMap ) {
|
|
196
|
+
const tokenName = tokenNameMap[ this._input.LT(2).text || '' ];
|
|
197
|
+
const ll1 = this.getCurrentToken();
|
|
198
|
+
if (tokenName &&
|
|
199
|
+
(ll1.type === this.constructor.Identifier || /^[a-zA-Z_]+$/.test( ll1.text )))
|
|
200
|
+
ll1.type = this.constructor[tokenName];
|
|
201
|
+
return !!tokenName;
|
|
202
|
+
}
|
|
203
|
+
|
|
221
204
|
// // Special function for rule `requiredSemi` before return $ctx
|
|
222
205
|
// function braceForSemi() {
|
|
223
206
|
// if (RBRACE == null)
|
|
@@ -275,24 +258,47 @@ function meltKeywordToIdentifier( exceptTrueFalseNull = false ) {
|
|
|
275
258
|
token.type = Identifier;
|
|
276
259
|
}
|
|
277
260
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
this.$genericKeywords
|
|
261
|
+
const genericTokenTypes = {
|
|
262
|
+
expr: 'GenericExpr',
|
|
263
|
+
separator: 'GenericSeparator',
|
|
264
|
+
intro: 'GenericIntro',
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
function prepareGenericKeywords( pathItem, expected = null) {
|
|
268
|
+
const length = pathItem?.args?.length || 0;
|
|
269
|
+
const argPos = (expected ? length -1 : length);
|
|
270
|
+
const func = pathItem?.id && specialFunctions[pathItem.id.toUpperCase()];
|
|
271
|
+
const spec = func && func[argPos] || specialFunctions[''][argPos ? 1 : 0];
|
|
272
|
+
this.$genericKeywords = spec;
|
|
290
273
|
// @ts-ignore
|
|
291
274
|
const token = this.getCurrentToken() || { text: '' };
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
275
|
+
const text = token.text.toUpperCase();
|
|
276
|
+
let generic = spec[text];
|
|
277
|
+
// console.log('PGK:',token.text,generic,expected,spec,func,argPos)
|
|
278
|
+
if (expected) { // 'separator' or 'expr' (after 'separator')
|
|
279
|
+
if (generic !== expected)
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
else if (!generic || generic === 'separator') {
|
|
283
|
+
// Mismatch at beginning (or just an expression): keep token type
|
|
284
|
+
// (if not expression, issue error and consider the token to be an
|
|
285
|
+
// expression replacement, like ALL)
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
else if (generic === 'expr' && spec.intro && spec.intro.includes( text )) {
|
|
289
|
+
// token is both an intro and an expression, like LEADING for TRIM
|
|
290
|
+
const next = this._input.LT(2).text;
|
|
291
|
+
if (!next || // followed by EOF -> consider it to be 'intro', better for CC
|
|
292
|
+
next !== ',' && next !== ')' && spec[next.toUpperCase()] !== 'separator')
|
|
293
|
+
generic = 'intro'; // is intro if next token is not separator, not ',', ')'
|
|
295
294
|
}
|
|
295
|
+
// @ts-ignore
|
|
296
|
+
token.type = this.constructor[genericTokenTypes[generic]];
|
|
297
|
+
}
|
|
298
|
+
// To be called before having matched ( HideAlternatives | … )
|
|
299
|
+
function reportErrorForGenericKeyword() {
|
|
300
|
+
this._errHandler.reportUnwantedToken( this );
|
|
301
|
+
//this._errHandler.reportInputMismatch( this, { offending: this._input.LT(1) }, null );
|
|
296
302
|
}
|
|
297
303
|
|
|
298
304
|
// Attach location matched by current rule to node `art`. If a location is
|
|
@@ -302,14 +308,133 @@ function prepareGenericKeywords( pathItem ) {
|
|
|
302
308
|
function attachLocation( art ) {
|
|
303
309
|
if (!art || art.$parens)
|
|
304
310
|
return art;
|
|
305
|
-
if (!art.location)
|
|
306
|
-
art.location = this.
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
311
|
+
if (!art.location) {
|
|
312
|
+
art.location = this.tokenLocation(this._ctx.start, this._ctx.stop);
|
|
313
|
+
return art;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// The last token (this._ctx.stop) may be a multi-line string literal, in which
|
|
317
|
+
// case we can't rely on `this._ctx.stop.line`.
|
|
318
|
+
if (this.isMultiLineToken(this._ctx.stop)) {
|
|
319
|
+
this.fixMultiLineTokenEndLocation(this._ctx.stop, art.location);
|
|
320
|
+
|
|
321
|
+
} else {
|
|
322
|
+
const { stop } = this._ctx;
|
|
323
|
+
art.location.endLine = stop.line;
|
|
324
|
+
art.location.endCol = stop.stop - stop.start + stop.column + 2; // after the last char (special for EOF?)
|
|
325
|
+
}
|
|
326
|
+
|
|
310
327
|
return art;
|
|
311
328
|
}
|
|
312
329
|
|
|
330
|
+
function assignAnnotation( art, anno, prefix = '', iHaveVariant = false ) {
|
|
331
|
+
const { name, $flatten } = anno;
|
|
332
|
+
const { path } = name;
|
|
333
|
+
if (path.broken || !path[path.length - 1].id)
|
|
334
|
+
return;
|
|
335
|
+
const pathname = pathName( path );
|
|
336
|
+
let absolute = '';
|
|
337
|
+
if (name.variant) {
|
|
338
|
+
const variant = pathName( name.variant.path );
|
|
339
|
+
absolute = `${ prefix }${ pathname }#${ variant }`;
|
|
340
|
+
if (iHaveVariant) { // TODO: do we really care in the parser / core compiler?
|
|
341
|
+
this.error( 'anno-duplicate-variant', [ name.variant.location ],
|
|
342
|
+
{}, // TODO: params
|
|
343
|
+
'Annotation variant has been already provided' );
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
else if (!prefix || pathname !== '$value') {
|
|
347
|
+
absolute = `${ prefix }${ pathname }`;
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
absolute = prefix.slice( 0, -1 );
|
|
351
|
+
}
|
|
352
|
+
if ($flatten) {
|
|
353
|
+
for (const a of $flatten)
|
|
354
|
+
this.assignAnnotation( art, a, `${ absolute }.`, iHaveVariant || name.variant);
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
name.absolute = absolute;
|
|
358
|
+
this.addAnnotation( art, '@' + absolute, anno );
|
|
359
|
+
}
|
|
360
|
+
if (!prefix) { // set deprecated $annotations for cds-lsp
|
|
361
|
+
if (!art.$annotations)
|
|
362
|
+
art.$annotations = [];
|
|
363
|
+
const location = locUtils.combinedLocation( anno.name, anno );
|
|
364
|
+
art.$annotations.push( { value: anno, location } );
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function addAnnotation( art, prop, anno ) {
|
|
369
|
+
dictAddArray( art, prop, anno, (n, location, a) => {
|
|
370
|
+
// if we would make it a warning, we would still need to keep it an error
|
|
371
|
+
// with '...'; otherwise parse.cdl would have to split annotate statements
|
|
372
|
+
this.error( 'syntax-duplicate-anno', [ location ], { anno: n },
|
|
373
|
+
'Duplicate assignment with $(ANNO)' );
|
|
374
|
+
a.$errorReported = 'syntax-duplicate-anno';
|
|
375
|
+
// do not report again later as anno-duplicate-xyz
|
|
376
|
+
} );
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const extensionDicts = { elements: true, enum: true, params: true, returns: true };
|
|
380
|
+
|
|
381
|
+
function checkExtensionDict( dict ) {
|
|
382
|
+
for (const name in dict) {
|
|
383
|
+
const def = dict[name];
|
|
384
|
+
if (!def.$duplicates)
|
|
385
|
+
continue;
|
|
386
|
+
|
|
387
|
+
if (def.kind !== 'annotate') {
|
|
388
|
+
const numDefines =
|
|
389
|
+
def.$duplicates.reduce( addOneForDefinition, addOneForDefinition( 0, def ) );
|
|
390
|
+
this.handleDuplicateExtension( def, name, numDefines );
|
|
391
|
+
for (const dup of def.$duplicates)
|
|
392
|
+
this.handleDuplicateExtension( dup, name, numDefines );
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
// move annotations, 'doc' and 'elements' etc to main member
|
|
396
|
+
for (const dup of def.$duplicates) {
|
|
397
|
+
for (const prop of Object.keys( dup )) {
|
|
398
|
+
if (prop.charAt(0) === '@') {
|
|
399
|
+
this.addAnnotation( def, prop, dup[prop] )
|
|
400
|
+
}
|
|
401
|
+
else if (prop === 'doc') {
|
|
402
|
+
if (def.doc)
|
|
403
|
+
this.warning( 'syntax-duplicate-doc-comment', def.doc.location, {},
|
|
404
|
+
'Doc comment is overwritten by another one below' );
|
|
405
|
+
def.doc = dup.doc;
|
|
406
|
+
}
|
|
407
|
+
else if (extensionDicts[prop]) {
|
|
408
|
+
if (def[prop])
|
|
409
|
+
this.message( 'syntax-duplicate-annotate', [ def.name.location ], { name, prop } );
|
|
410
|
+
def[prop] = dup[prop]; // continuation semantics: last wins
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
def.$duplicates = null;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function addOneForDefinition( count, ext ) {
|
|
419
|
+
return (ext.kind === 'extend') ? count : count + 1;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Handle duplicate extensions. Does not handle `annotate`.
|
|
424
|
+
*
|
|
425
|
+
* @param {XSN.Extension} ext
|
|
426
|
+
* @param {string} name
|
|
427
|
+
* @param {number} numDefines
|
|
428
|
+
*/
|
|
429
|
+
function handleDuplicateExtension( ext, name, numDefines ) {
|
|
430
|
+
if (ext.kind === 'extend')
|
|
431
|
+
this.error( 'syntax-duplicate-extend', [ ext.name.location ],
|
|
432
|
+
{ name, '#': (numDefines ? 'define' : 'extend') } );
|
|
433
|
+
else if (numDefines === 1)
|
|
434
|
+
ext.$errorReported = 'syntax-duplicate-extend'; // a definition, but not duplicate
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
|
|
313
438
|
/**
|
|
314
439
|
* Return start location of `token`, or the first token matched by the current
|
|
315
440
|
* rule if `token` is undefined
|
|
@@ -350,31 +475,49 @@ function tokenLocation( token, endToken = null ) {
|
|
|
350
475
|
|
|
351
476
|
// This check is done for performance reason. No need to access a token's
|
|
352
477
|
// data if we know that it spans only one single line.
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
478
|
+
if (this.isMultiLineToken(token))
|
|
479
|
+
this.fixMultiLineTokenEndLocation(token, loc);
|
|
480
|
+
|
|
481
|
+
return loc;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function isMultiLineToken(token) {
|
|
485
|
+
return (
|
|
486
|
+
token.type === this.constructor.DocComment ||
|
|
487
|
+
token.type === this.constructor.String ||
|
|
488
|
+
token.type === this.constructor.UnterminatedLiteral
|
|
357
489
|
);
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Adapt end location of `location` according to `token`, assuming that `token` is a multi-line
|
|
494
|
+
* token such as a multi-line string or doc comment.
|
|
495
|
+
*
|
|
496
|
+
* Sets `endLine`/`endCol`, respecting newline characters in the token.
|
|
497
|
+
*
|
|
498
|
+
* @param token
|
|
499
|
+
* @param {CSN.Location} location
|
|
500
|
+
*/
|
|
501
|
+
function fixMultiLineTokenEndLocation( token, location ) {
|
|
502
|
+
// Count the number of newlines in the token.
|
|
503
|
+
const source = token.source[1].data;
|
|
504
|
+
let newLineCount = 0;
|
|
505
|
+
let lastNewlineIndex = token.start;
|
|
506
|
+
for (let i = token.start; i < token.stop; i++) {
|
|
507
|
+
// Note: We do NOT check for CR, LS, and PS (/[\r\u2028\u2029]/)
|
|
508
|
+
// because ANTLR only uses LF for line break detection.
|
|
509
|
+
if (source[i] === 10) { // code point of '\n'
|
|
510
|
+
newLineCount++;
|
|
511
|
+
lastNewlineIndex = i;
|
|
374
512
|
}
|
|
375
513
|
}
|
|
376
|
-
|
|
377
|
-
|
|
514
|
+
if (newLineCount > 0) {
|
|
515
|
+
location.endLine = token.line + newLineCount;
|
|
516
|
+
location.endCol = token.stop - lastNewlineIndex + 1;
|
|
517
|
+
} else {
|
|
518
|
+
location.endLine = token.line;
|
|
519
|
+
location.endCol = token.stop - token.start + token.column + 2; // after the last char (special for EOF?)
|
|
520
|
+
}
|
|
378
521
|
}
|
|
379
522
|
|
|
380
523
|
/**
|
|
@@ -409,20 +552,6 @@ function combinedLocation( start, end ) {
|
|
|
409
552
|
return locUtils.combinedLocation( start, end );
|
|
410
553
|
}
|
|
411
554
|
|
|
412
|
-
function createDict( location = null ) {
|
|
413
|
-
const dict = Object.create(null);
|
|
414
|
-
dict[$location] = location || this.startLocation( this._input.LT(-1) );
|
|
415
|
-
return dict;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
function setDictEndLocation( dict ) {
|
|
419
|
-
const stop = this._input.LT(-1);
|
|
420
|
-
Object.assign( dict[$location], {
|
|
421
|
-
endLine: stop.line,
|
|
422
|
-
endCol: stop.stop - stop.start + stop.column + 2,
|
|
423
|
-
} );
|
|
424
|
-
}
|
|
425
|
-
|
|
426
555
|
function surroundByParens( expr, open, close, asQuery = false ) {
|
|
427
556
|
if (!expr)
|
|
428
557
|
return expr;
|
|
@@ -435,7 +564,7 @@ function surroundByParens( expr, open, close, asQuery = false ) {
|
|
|
435
564
|
}
|
|
436
565
|
|
|
437
566
|
function unaryOpForParens( query, val ) {
|
|
438
|
-
const parens = query
|
|
567
|
+
const parens = query?.$parens;
|
|
439
568
|
if (!parens)
|
|
440
569
|
return query;
|
|
441
570
|
const location = parens[parens.length - 1];
|
|
@@ -461,8 +590,8 @@ function docComment( node ) {
|
|
|
461
590
|
if (!this.options.docComment)
|
|
462
591
|
return;
|
|
463
592
|
if (node.doc) {
|
|
464
|
-
this.warning( 'syntax-duplicate-doc-comment',
|
|
465
|
-
'
|
|
593
|
+
this.warning( 'syntax-duplicate-doc-comment', node.doc.location, {},
|
|
594
|
+
'Doc comment is overwritten by another one below' );
|
|
466
595
|
}
|
|
467
596
|
node.doc = this.valueWithTokenLocation( parseDocComment( token.text ), token );
|
|
468
597
|
}
|
|
@@ -488,10 +617,11 @@ function fragileAlias( ast, safe = false ) {
|
|
|
488
617
|
}
|
|
489
618
|
|
|
490
619
|
// Return AST for identifier token `token`. Also check that identifer is not empty.
|
|
491
|
-
function identAst( token, category ) {
|
|
620
|
+
function identAst( token, category, noTokenTypeCheck = false ) {
|
|
492
621
|
token.isIdentifier = category;
|
|
493
622
|
let id = token.text;
|
|
494
|
-
if (
|
|
623
|
+
if (!noTokenTypeCheck &&
|
|
624
|
+
token.type !== this.constructor.Identifier && !/^[a-zA-Z_]+$/.test( id ))
|
|
495
625
|
id = '';
|
|
496
626
|
if (token.text[0] === '!') {
|
|
497
627
|
id = id.slice( 2, -1 ).replace( /]]/g, ']' );
|
|
@@ -518,11 +648,11 @@ function identAst( token, category ) {
|
|
|
518
648
|
return { id, $delimited: true, location: this.tokenLocation( token ) };
|
|
519
649
|
}
|
|
520
650
|
|
|
521
|
-
function functionAst( token,
|
|
651
|
+
function functionAst( token, xpr ) { // only for special function TRIM and EXTRACT
|
|
522
652
|
// TODO: XSN func cleanup
|
|
523
653
|
const location = this.tokenLocation( token );
|
|
524
|
-
const args =
|
|
525
|
-
? [ { op: { location, val: '
|
|
654
|
+
const args = xpr
|
|
655
|
+
? [ { op: { location, val: 'ixpr' }, args: [], location: this.tokenLocation( xpr ) } ]
|
|
526
656
|
: [];
|
|
527
657
|
return {
|
|
528
658
|
op: { location, val: 'call' },
|
|
@@ -532,6 +662,21 @@ function functionAst( token, xprToken ) {
|
|
|
532
662
|
};
|
|
533
663
|
}
|
|
534
664
|
|
|
665
|
+
function setLastAsXpr( args ) {
|
|
666
|
+
const pos = args.length - 1;
|
|
667
|
+
const last = args[pos];
|
|
668
|
+
if (!last || last?.op?.val === 'ixpr') // actuall an internal xpr, which is always flattened
|
|
669
|
+
return last;
|
|
670
|
+
const location = { ...last.location };
|
|
671
|
+
args[pos] = { op: { location, val: 'ixpr' }, args: [ last ], location };
|
|
672
|
+
return args[pos].args;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
function xprToken() {
|
|
676
|
+
const token = this._input.LT(-1);
|
|
677
|
+
return { location: this.tokenLocation( token ), val: token.text, literal: 'token' }
|
|
678
|
+
}
|
|
679
|
+
|
|
535
680
|
function valuePathAst( ref ) {
|
|
536
681
|
// TODO: XSN representation of functions is a bit strange - rework if methods
|
|
537
682
|
// are introduced
|
|
@@ -630,21 +775,19 @@ function quotedLiteral( token, literal ) {
|
|
|
630
775
|
literal = token.text.slice( 0, pos - 1 ).toLowerCase();
|
|
631
776
|
const p = quotedLiteralPatterns[literal] || {};
|
|
632
777
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
!this.options.parseOnly)
|
|
636
|
-
this.error( null, location, p.test_msg ); // TODO: message id
|
|
778
|
+
if (p.test_fn && !p.test_fn(val) && !this.options.parseOnly)
|
|
779
|
+
this.warning( 'syntax-invalid-literal', location, { '#': p.test_variant } );
|
|
637
780
|
|
|
638
781
|
if (p.unexpected_char) {
|
|
639
782
|
const idx = val.search(p.unexpected_char);
|
|
640
783
|
if (~idx) {
|
|
641
|
-
this.
|
|
784
|
+
this.warning( 'syntax-invalid-literal', {
|
|
642
785
|
file: location.file,
|
|
643
786
|
line: location.line,
|
|
644
787
|
endLine: location.line,
|
|
645
788
|
col: atChar(idx),
|
|
646
789
|
endCol: atChar( idx + (val[idx] === '\'' ? 2 : 1) ),
|
|
647
|
-
}, p.
|
|
790
|
+
}, { '#': p.unexpected_variant } );
|
|
648
791
|
}
|
|
649
792
|
}
|
|
650
793
|
return {
|
|
@@ -659,10 +802,6 @@ function quotedLiteral( token, literal ) {
|
|
|
659
802
|
}
|
|
660
803
|
}
|
|
661
804
|
|
|
662
|
-
function pathName( path, brokenName ) {
|
|
663
|
-
return (path && !path.broken) ? path.map( id => id.id ).join('.') : brokenName;
|
|
664
|
-
}
|
|
665
|
-
|
|
666
805
|
function pushIdent( path, ident, prefix ) {
|
|
667
806
|
if (!ident) {
|
|
668
807
|
path.broken = true;
|
|
@@ -691,61 +830,62 @@ function pushIdent( path, ident, prefix ) {
|
|
|
691
830
|
}
|
|
692
831
|
}
|
|
693
832
|
|
|
694
|
-
// Add new definition to dictionary property `env` of node `parent
|
|
695
|
-
//
|
|
696
|
-
// - `name`: argument `name`, which is used as key in the dictionary
|
|
697
|
-
// - `kind`: argument `kind` if that is truthy
|
|
698
|
-
// - `location`: argument `location` or the start location of source matched by
|
|
699
|
-
// current rule
|
|
700
|
-
// - properties in argument `props` which are no empty (undefined, null, {},
|
|
701
|
-
// []), ANTLR tokens are replaced by their locations
|
|
833
|
+
// Add new definition `art` to dictionary property `env` of node `parent`.
|
|
834
|
+
// Return `art`.
|
|
702
835
|
//
|
|
703
|
-
//
|
|
704
|
-
//
|
|
705
|
-
// `
|
|
706
|
-
|
|
836
|
+
// If argument `kind` is provided, set `art.kind` to that value.
|
|
837
|
+
// If argument `name` is provided, set `art.name`:
|
|
838
|
+
// - if `name` is an array, the name consist of the ID of the last path item
|
|
839
|
+
// (for elements via columns, foreign keys, table aliases)
|
|
840
|
+
// - if `name` is an object, the name consist of the IDs of all path items
|
|
841
|
+
// (for main artifact definitions)
|
|
842
|
+
function addDef( art, parent, env, kind, name ) {
|
|
707
843
|
if (Array.isArray(name)) {
|
|
708
844
|
// XSN TODO: clearly say: definitions have name.path, members have name.id
|
|
709
845
|
const last = name.length && name[name.length - 1];
|
|
710
846
|
if (last && last.id) { // // A.B.C -> 'C'
|
|
711
|
-
name = {
|
|
847
|
+
art.name = {
|
|
712
848
|
id: last.id, location: last.location, $inferred: 'as',
|
|
713
849
|
};
|
|
714
850
|
}
|
|
715
851
|
}
|
|
716
|
-
else if (name
|
|
717
|
-
name
|
|
852
|
+
else if (name) {
|
|
853
|
+
art.name = name;
|
|
854
|
+
if (name.id == null)
|
|
855
|
+
name.id = pathName( name.path ); // A.B.C -> 'A.B.C'
|
|
856
|
+
// TODO: get rid of setting `id`, only use for named values in structs
|
|
718
857
|
}
|
|
719
|
-
|
|
858
|
+
|
|
720
859
|
if (kind)
|
|
721
860
|
art.kind = kind;
|
|
722
|
-
if (!parent[env]) // TODO: dump with --test-mode, env !== 'artifacts'
|
|
723
|
-
parent[env] = env === 'args' ? Object.create(null) : this.createDict( { ...location } );
|
|
724
861
|
if (!art.name || art.name.id == null) {
|
|
725
862
|
// no id was parsed, but with error recovery: no further error
|
|
726
863
|
// TODO: add to parent[env]['']
|
|
727
864
|
// which could be tested in name search (then no undefined-ref error)
|
|
865
|
+
// console.log( kind+': !!', art, parent, env, name )
|
|
728
866
|
return art;
|
|
729
867
|
}
|
|
730
|
-
|
|
868
|
+
// console.log( kind+':', art, parent, env, name )
|
|
869
|
+
|
|
870
|
+
if (env === 'artifacts' || env === 'vocabularies') {
|
|
731
871
|
dictAddArray( parent[env], art.name.id, art );
|
|
732
872
|
}
|
|
733
873
|
else if (kind || this.options.parseOnly) {
|
|
734
874
|
dictAdd( parent[env], art.name.id, art );
|
|
735
875
|
}
|
|
736
876
|
else {
|
|
737
|
-
dictAdd( parent[env], art.name.id, art, (
|
|
877
|
+
dictAdd( parent[env], art.name.id, art, ( duplicateName, loc ) => {
|
|
738
878
|
// do not use function(), otherwise `this` is wrong:
|
|
739
879
|
if (kind === 0) {
|
|
740
|
-
this.error( 'duplicate-argument', loc, { name },
|
|
880
|
+
this.error( 'duplicate-argument', loc, { name: duplicateName },
|
|
741
881
|
'Duplicate value for parameter $(NAME)' );
|
|
742
882
|
}
|
|
743
883
|
else if (kind === '') {
|
|
744
|
-
this.error( 'duplicate-excluding', loc, { name, keyword: 'excluding' },
|
|
884
|
+
this.error( 'duplicate-excluding', loc, { name: duplicateName, keyword: 'excluding' },
|
|
745
885
|
'Duplicate $(NAME) in the $(KEYWORD) clause' );
|
|
746
886
|
}
|
|
747
887
|
else {
|
|
748
|
-
this.error( 'duplicate-prop', loc, { name },
|
|
888
|
+
this.error( 'duplicate-prop', loc, { name: duplicateName },
|
|
749
889
|
'Duplicate value for structure property $(NAME)' );
|
|
750
890
|
}
|
|
751
891
|
} );
|
|
@@ -753,85 +893,78 @@ function addDef( parent, env, kind, name, annos, props, location ) {
|
|
|
753
893
|
return art;
|
|
754
894
|
}
|
|
755
895
|
|
|
756
|
-
// Add new definition to array property `env` of node `parent
|
|
757
|
-
//
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
// - properties in argument `props` which are no empty (undefined, null, {},
|
|
762
|
-
// []); ANTLR tokens are replaced by their locations
|
|
763
|
-
//
|
|
764
|
-
// Hack: if argument `location` is exactly `true`, do not set `location`
|
|
765
|
-
// (except if part of `props`), but also include the empty properties of
|
|
766
|
-
// `props`.
|
|
767
|
-
function addItem( parent, env, kind, annos, props, location ) {
|
|
768
|
-
const art = this.assignProps( {}, annos, props, location );
|
|
769
|
-
if (kind)
|
|
770
|
-
art.kind = kind;
|
|
771
|
-
if (!env)
|
|
772
|
-
parent.push( art );
|
|
773
|
-
else if (!parent[env])
|
|
774
|
-
parent[env] = [ art ];
|
|
775
|
-
else
|
|
776
|
-
parent[env].push( art );
|
|
896
|
+
// Add new definition `art` to array property `env` of node `parent`.
|
|
897
|
+
// Also set `kind`. Returns `art`.
|
|
898
|
+
function addItem( art, parent, env, kind ) {
|
|
899
|
+
art.kind = kind;
|
|
900
|
+
parent[env].push( art );
|
|
777
901
|
return art;
|
|
778
902
|
}
|
|
779
|
-
|
|
780
903
|
/**
|
|
781
|
-
*
|
|
782
|
-
*
|
|
904
|
+
* Add `annotate/extend Main.Artifact:elem.sub` to `‹xsn›.extensions`:
|
|
905
|
+
* - the array item is an extend/annotate for `Main.Artifact`,
|
|
906
|
+
* - for each path item in `elem.sub`, we add an `elements` property containing
|
|
907
|
+
* one extend/annotate for the corresponding element
|
|
908
|
+
* - The deepest extend/annotate is the object which is to be extended
|
|
783
909
|
*
|
|
784
|
-
* @param {
|
|
785
|
-
* @param {object}
|
|
786
|
-
* @param {
|
|
787
|
-
* @param {object
|
|
788
|
-
* @param {XSN.
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
if (!Array.isArray(
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
910
|
+
* @param {object} ext The object containing the location and annotations for the extension.
|
|
911
|
+
* @param {object} parent The parent containing the `extensions` property, i.e. the source.
|
|
912
|
+
* @param {string} kind Either `annotate` or `extend`.
|
|
913
|
+
* @param {object} artName The "name object" for `Main.Artifact`.
|
|
914
|
+
* @param {XSN.Path} elemPath Path as returned by `simplePath` rule.
|
|
915
|
+
*/
|
|
916
|
+
function addExtension( ext, parent, kind, artName, elemPath ) {
|
|
917
|
+
const { location } = ext;
|
|
918
|
+
if (!Array.isArray( elemPath ) || !elemPath.length || elemPath.broken) {
|
|
919
|
+
ext.name = artName;
|
|
920
|
+
this.addItem( ext, parent, 'extensions', kind );
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
// Note: the element extensions share a common `location`, also with the
|
|
924
|
+
// extension of the main artifact; its end location will usually set later
|
|
925
|
+
parent = this.addItem( { name: artName, location }, parent, 'extensions', kind );
|
|
926
|
+
|
|
927
|
+
const last = elemPath[elemPath.length - 1];
|
|
928
|
+
for (const seg of elemPath) {
|
|
929
|
+
parent.elements = Object.create(null); // no dict location → no createDict()
|
|
930
|
+
parent = this.addDef( (seg === last ? ext : { location }),
|
|
931
|
+
parent, 'elements', kind, seg );
|
|
798
932
|
}
|
|
799
|
-
const last = elementPath[elementPath.length - 1];
|
|
800
|
-
artifact = this.addDef( artifact, 'elements', kind,
|
|
801
|
-
{ path: [ last ], location: last.location }, annos, {}, artifactLocation );
|
|
802
|
-
return artifact;
|
|
803
933
|
}
|
|
804
934
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
935
|
+
// must be in action directly after having parsed '{' or '(`
|
|
936
|
+
function createDict() {
|
|
937
|
+
const dict = Object.create(null);
|
|
938
|
+
dict[$location] = this.startLocation( this._input.LT(-1) );
|
|
939
|
+
return dict;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// must be in action directly after having parsed '[' or '(` or `{`
|
|
943
|
+
function createArray() {
|
|
944
|
+
const array = [];
|
|
945
|
+
array[$location] = this.startLocation( this._input.LT(-1) );
|
|
946
|
+
return array;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// must be in action directly after having parsed '}' or ')`
|
|
950
|
+
function finalizeDictOrArray( dict ) {
|
|
951
|
+
const loc = dict[$location];
|
|
952
|
+
if (!loc)
|
|
953
|
+
return;
|
|
954
|
+
const stop = this._input.LT(-1);
|
|
955
|
+
loc.endLine = stop.line;
|
|
956
|
+
loc.endCol = stop.stop - stop.start + stop.column + 2;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
function createSource() {
|
|
960
|
+
return {
|
|
961
|
+
kind: 'source',
|
|
962
|
+
usings: [],
|
|
963
|
+
dependencies: [],
|
|
964
|
+
artifacts: Object.create(null),
|
|
965
|
+
// vocabularies: Object.create(null),
|
|
966
|
+
extensions: [],
|
|
967
|
+
};
|
|
835
968
|
}
|
|
836
969
|
|
|
837
970
|
// Create AST node for prefix operator `op` and arguments `args`
|
|
@@ -879,14 +1012,12 @@ function setOnce( target, prop, value, ...tokens ) {
|
|
|
879
1012
|
target[prop] = value;
|
|
880
1013
|
}
|
|
881
1014
|
|
|
882
|
-
function setMaxCardinality( art, token, max
|
|
1015
|
+
function setMaxCardinality( art, token, max ) {
|
|
883
1016
|
const location = this.tokenLocation( token );
|
|
884
1017
|
if (!art.cardinality) {
|
|
885
1018
|
art.cardinality = { targetMax: Object.assign( { location }, max ), location };
|
|
886
|
-
if (inferred)
|
|
887
|
-
art.cardinality.$inferred = inferred;
|
|
888
1019
|
}
|
|
889
|
-
else
|
|
1020
|
+
else {
|
|
890
1021
|
this.warning( 'syntax-repeated-cardinality', location, { keyword: token.text },
|
|
891
1022
|
'The target cardinality has already been specified - ignored $(KEYWORD)' );
|
|
892
1023
|
}
|
|
@@ -905,6 +1036,9 @@ function handleComposition( cardinality, isComposition ) {
|
|
|
905
1036
|
}
|
|
906
1037
|
|
|
907
1038
|
function associationInSelectItem( art ) {
|
|
1039
|
+
if (!art.value) // e.g. `expand` without value (for new structures)
|
|
1040
|
+
return;
|
|
1041
|
+
|
|
908
1042
|
const isPath = art.value.path && art.value.path.length
|
|
909
1043
|
const isIdentifier = isPath && art.value.path.length === 1;
|
|
910
1044
|
if (isIdentifier) {
|
|
@@ -958,6 +1092,4 @@ function checkTypeFacet( art, argIdent ) {
|
|
|
958
1092
|
}
|
|
959
1093
|
}
|
|
960
1094
|
|
|
961
|
-
module.exports =
|
|
962
|
-
genericAntlrParser: GenericAntlrParser,
|
|
963
|
-
};
|
|
1095
|
+
module.exports = GenericAntlrParser;
|