@sap/cds-compiler 2.11.4 → 2.12.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 +58 -1
- package/bin/cds_update_identifiers.js +7 -7
- package/bin/cdsc.js +9 -10
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +12 -0
- package/lib/api/main.js +2 -0
- package/lib/api/options.js +2 -2
- package/lib/base/message-registry.js +31 -2
- package/lib/base/model.js +1 -0
- package/lib/base/optionProcessorHelper.js +97 -69
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/sql-snippets.js +93 -0
- package/lib/checks/validator.js +8 -0
- package/lib/compiler/assert-consistency.js +5 -3
- package/lib/compiler/base.js +0 -1
- package/lib/compiler/checks.js +32 -9
- package/lib/compiler/definer.js +25 -4
- package/lib/compiler/index.js +1 -1
- package/lib/compiler/propagator.js +3 -2
- package/lib/compiler/resolver.js +97 -6
- package/lib/compiler/shared.js +12 -1
- package/lib/compiler/utils.js +7 -0
- package/lib/edm/annotations/genericTranslation.js +34 -17
- package/lib/edm/annotations/preprocessAnnotations.js +1 -1
- package/lib/edm/csn2edm.js +1 -1
- package/lib/edm/edm.js +8 -8
- package/lib/edm/edmPreprocessor.js +30 -23
- package/lib/edm/edmUtils.js +11 -12
- package/lib/gen/Dictionary.json +82 -40
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/language.tokens +15 -14
- package/lib/gen/languageLexer.interp +9 -1
- package/lib/gen/languageLexer.js +830 -779
- package/lib/gen/languageLexer.tokens +7 -6
- package/lib/gen/languageParser.js +2401 -2282
- package/lib/json/from-csn.js +47 -16
- package/lib/json/to-csn.js +17 -5
- package/lib/language/antlrParser.js +3 -3
- package/lib/language/docCommentParser.js +1 -1
- package/lib/language/genericAntlrParser.js +68 -51
- package/lib/language/language.g4 +128 -74
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +5 -3
- package/lib/main.js +3 -2
- package/lib/model/csnRefs.js +116 -68
- package/lib/model/csnUtils.js +40 -48
- package/lib/model/enrichCsn.js +30 -14
- package/lib/optionProcessor.js +3 -3
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +193 -79
- package/lib/render/toHdbcds.js +179 -95
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +57 -40
- package/lib/render/utils/common.js +24 -5
- package/lib/render/utils/sql.js +6 -4
- package/lib/transform/braceExpression.js +4 -2
- package/lib/transform/db/associations.js +389 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +6 -4
- package/lib/transform/db/draft.js +3 -2
- package/lib/transform/db/expansion.js +4 -5
- package/lib/transform/db/flattening.js +5 -6
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +36 -23
- package/lib/transform/forHanaNew.js +35 -626
- package/lib/transform/forOdataNew.js +5 -4
- package/lib/transform/localized.js +3 -14
- package/lib/transform/odata/generateForeignKeyElements.js +2 -2
- package/lib/transform/transformUtilsNew.js +13 -13
- package/lib/transform/translateAssocsToJoins.js +8 -8
- package/lib/transform/universalCsnEnricher.js +217 -47
- package/lib/utils/file.js +2 -1
- package/lib/utils/timetrace.js +8 -2
- package/package.json +1 -1
package/lib/json/from-csn.js
CHANGED
|
@@ -705,7 +705,7 @@ let csnFilename = '';
|
|
|
705
705
|
let virtualLine = 1;
|
|
706
706
|
/** @type {CSN.Location[]} */
|
|
707
707
|
let dollarLocations = [];
|
|
708
|
-
let
|
|
708
|
+
let arrayLevelCount = 0;
|
|
709
709
|
|
|
710
710
|
/**
|
|
711
711
|
* @param {Object.<string, SchemaSpec>} specs
|
|
@@ -1164,6 +1164,12 @@ function symbol( id, spec, xsn ) { // for CSN property '#'
|
|
|
1164
1164
|
xsn.sym = { id, location: location() };
|
|
1165
1165
|
}
|
|
1166
1166
|
|
|
1167
|
+
// returns: false = no "...", true = "..." without UP TO, 'upTo' = "..." with UP TO
|
|
1168
|
+
function isEllipsis( val ) {
|
|
1169
|
+
return val && typeof val === 'object' && '...' in val && Object.keys(val).length === 1 &&
|
|
1170
|
+
(val['...'] === true || 'upTo');
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1167
1173
|
function annoValue( val, spec ) {
|
|
1168
1174
|
if (val == null) // TODO: reject undefined
|
|
1169
1175
|
return { val, literal: 'null', location: location() };
|
|
@@ -1171,22 +1177,39 @@ function annoValue( val, spec ) {
|
|
|
1171
1177
|
if (lit !== 'object')
|
|
1172
1178
|
return { val, literal: lit, location: location() };
|
|
1173
1179
|
if (Array.isArray( val )) {
|
|
1174
|
-
|
|
1175
|
-
if (
|
|
1176
|
-
|
|
1177
|
-
|
|
1180
|
+
let seenEllipsis = false;
|
|
1181
|
+
if (arrayLevelCount > 0) { // TODO: also inside structure (possible in CSN!)
|
|
1182
|
+
if (val.some( isEllipsis )) {
|
|
1183
|
+
error( 'syntax-csn-unexpected-ellipsis', location(true), { code: '...' },
|
|
1184
|
+
'Unexpected $(CODE) in nested array' );
|
|
1185
|
+
}
|
|
1178
1186
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1187
|
+
else {
|
|
1188
|
+
for (const item of val) {
|
|
1189
|
+
if (seenEllipsis !== true) {
|
|
1190
|
+
seenEllipsis = isEllipsis( item ) || seenEllipsis;
|
|
1191
|
+
}
|
|
1192
|
+
else if (isEllipsis( item )) { // with or without UP TO
|
|
1193
|
+
// error position at the beginning of the array, but that is fine
|
|
1194
|
+
error( 'syntax-csn-duplicate-ellipsis', location(true), { code: '...' },
|
|
1195
|
+
'Expected no more than one $(CODE)' );
|
|
1196
|
+
break;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1182
1199
|
}
|
|
1183
|
-
|
|
1200
|
+
arrayLevelCount++;
|
|
1184
1201
|
const retval = {
|
|
1185
1202
|
location: location(),
|
|
1186
1203
|
val: arrayOf( annoValue )( val, spec ),
|
|
1187
1204
|
literal: 'array',
|
|
1188
1205
|
};
|
|
1189
|
-
|
|
1206
|
+
arrayLevelCount--;
|
|
1207
|
+
if (seenEllipsis === 'upTo') {
|
|
1208
|
+
error( 'syntax-csn-expecting-ellipsis', location(true), // at closing bracket
|
|
1209
|
+
{ code: '... up to', newcode: '...' },
|
|
1210
|
+
// TODO: should we be more CSN specific in the message?
|
|
1211
|
+
'Expecting an array item $(NEWCODE) after an item with $(CODE)' );
|
|
1212
|
+
}
|
|
1190
1213
|
return retval;
|
|
1191
1214
|
}
|
|
1192
1215
|
if (typeof val['#'] === 'string') {
|
|
@@ -1205,12 +1228,19 @@ function annoValue( val, spec ) {
|
|
|
1205
1228
|
return refSplit( val['='], '=' );
|
|
1206
1229
|
}
|
|
1207
1230
|
}
|
|
1208
|
-
else if (val['...'] && Object.keys(val).length === 1) {
|
|
1209
|
-
|
|
1231
|
+
else if (val['...'] && Object.keys( val ).length === 1) {
|
|
1232
|
+
// TODO: only if not nested - see error above
|
|
1233
|
+
++virtualLine;
|
|
1234
|
+
const ell = val['...'];
|
|
1235
|
+
const r = {
|
|
1210
1236
|
val: '...',
|
|
1211
1237
|
literal: 'token',
|
|
1212
1238
|
location: location(),
|
|
1213
1239
|
};
|
|
1240
|
+
if (ell !== true)
|
|
1241
|
+
r.upTo = annoValue( ell, schema['@'] );
|
|
1242
|
+
++virtualLine;
|
|
1243
|
+
return r;
|
|
1214
1244
|
}
|
|
1215
1245
|
const struct = Object.create(null);
|
|
1216
1246
|
++virtualLine;
|
|
@@ -1354,12 +1384,11 @@ function exprArgs( cond, spec, xsn, csn ) {
|
|
|
1354
1384
|
|
|
1355
1385
|
function condition( cond, spec ) {
|
|
1356
1386
|
const loc = location();
|
|
1357
|
-
|
|
1387
|
+
return {
|
|
1358
1388
|
op: { val: 'xpr', location: loc },
|
|
1359
1389
|
args: exprArgs( cond, spec ),
|
|
1360
1390
|
location: loc,
|
|
1361
1391
|
};
|
|
1362
|
-
return x;
|
|
1363
1392
|
}
|
|
1364
1393
|
|
|
1365
1394
|
function vZeroValue( obj, spec, xsn ) {
|
|
@@ -1666,8 +1695,8 @@ function pushLocation( obj ) {
|
|
|
1666
1695
|
error( 'syntax-csn-expected-object', location(true), { prop: '$location' } );
|
|
1667
1696
|
}
|
|
1668
1697
|
// hidden feature: string $location
|
|
1669
|
-
const m = /:(\d+)(?::(\d+)
|
|
1670
|
-
if (!m) {
|
|
1698
|
+
const m = /:(\d+)(?::(\d+))?$/.exec( loc ); // extra '^'s at end deliberately left out
|
|
1699
|
+
if (!m) { // without location or with '^'s: do not use
|
|
1671
1700
|
dollarLocations.push( null );
|
|
1672
1701
|
}
|
|
1673
1702
|
else {
|
|
@@ -1710,8 +1739,10 @@ function toXsn( csn, filename, options, messageFunctions ) {
|
|
|
1710
1739
|
csnFilename = filename;
|
|
1711
1740
|
virtualLine = 1;
|
|
1712
1741
|
dollarLocations = [];
|
|
1742
|
+
arrayLevelCount = 0;
|
|
1713
1743
|
inExtensions = null;
|
|
1714
1744
|
vocabInDefinitions = null;
|
|
1745
|
+
|
|
1715
1746
|
const xsn = { $frontend: 'json' };
|
|
1716
1747
|
|
|
1717
1748
|
// eslint-disable-next-line object-curly-newline
|
package/lib/json/to-csn.js
CHANGED
|
@@ -100,7 +100,7 @@ const transformers = {
|
|
|
100
100
|
offset: expression,
|
|
101
101
|
on: onCondition,
|
|
102
102
|
// definitions, extensions, members ----------------------------------------
|
|
103
|
-
returns
|
|
103
|
+
returns, // storing the return type of actions
|
|
104
104
|
notNull: value,
|
|
105
105
|
default: expression,
|
|
106
106
|
// targetElement: ignore, // special display of foreign key, renameTo: select
|
|
@@ -556,7 +556,7 @@ function sources( srcDict, csn ) {
|
|
|
556
556
|
}
|
|
557
557
|
}
|
|
558
558
|
|
|
559
|
-
function attachAnnotations( annotate, prop, dict, inferred,
|
|
559
|
+
function attachAnnotations( annotate, prop, dict, inferred, insideReturns = false ) {
|
|
560
560
|
const annoDict = Object.create( dictionaryPrototype );
|
|
561
561
|
for (const name in dict) {
|
|
562
562
|
const elem = dict[name];
|
|
@@ -574,7 +574,7 @@ function attachAnnotations( annotate, prop, dict, inferred, returns = false ) {
|
|
|
574
574
|
annoDict[name] = sub;
|
|
575
575
|
}
|
|
576
576
|
if (Object.keys( annoDict ).length) {
|
|
577
|
-
if (
|
|
577
|
+
if (insideReturns)
|
|
578
578
|
annotate.returns = { elements: annoDict };
|
|
579
579
|
else
|
|
580
580
|
annotate[prop] = annoDict;
|
|
@@ -674,7 +674,8 @@ function enumDict( dict, csn, node ) {
|
|
|
674
674
|
// no 'elements' with SELECT or inferred elements with gensrc;
|
|
675
675
|
// hidden or visible 'elements' will be set in query()
|
|
676
676
|
return undefined;
|
|
677
|
-
if (universalCsn && node.type && !node.type.$inferred && node.$expand === 'annotate'
|
|
677
|
+
if (universalCsn && node.type && !node.type.$inferred && node.$expand === 'annotate' &&
|
|
678
|
+
node.type._artifact && !node.type._artifact.builtin)
|
|
678
679
|
// derived type of enum type with individual annotations: also set $origin
|
|
679
680
|
csn.$origin = originRef( node.type._artifact );
|
|
680
681
|
return insertOrderDict( dict );
|
|
@@ -834,6 +835,14 @@ function foreignKeys( dict, csn, node ) {
|
|
|
834
835
|
csn.keys = keys;
|
|
835
836
|
}
|
|
836
837
|
|
|
838
|
+
function returns( art, csn, _node, prop ) {
|
|
839
|
+
// TODO: currently, the `returns` structure might just have been created by the propagator
|
|
840
|
+
// if that is the case, there should be no reason to store it in universal CSN
|
|
841
|
+
if (universalCsn && art.$inferred === 'proxy')
|
|
842
|
+
return undefined;
|
|
843
|
+
return definition( art, csn, _node, prop );
|
|
844
|
+
}
|
|
845
|
+
|
|
837
846
|
function definition( art, _csn, _node, prop ) {
|
|
838
847
|
if (!art || typeof art !== 'object')
|
|
839
848
|
return undefined; // TODO: complain with strict
|
|
@@ -1163,7 +1172,7 @@ function value( node ) {
|
|
|
1163
1172
|
if (node.literal === 'array')
|
|
1164
1173
|
return node.val.map( value );
|
|
1165
1174
|
if (node.literal === 'token' && node.val === '...')
|
|
1166
|
-
return extra( { '...':
|
|
1175
|
+
return extra( { '...': !node.upTo || value( node.upTo ) } );
|
|
1167
1176
|
if (node.literal !== 'struct')
|
|
1168
1177
|
// no val (undefined) as true only for annotation values (and struct elem values)
|
|
1169
1178
|
return node.name && !('val' in node) || node.val;
|
|
@@ -1516,6 +1525,9 @@ function compactExpr( e ) { // TODO: options
|
|
|
1516
1525
|
return e && expression( e, true );
|
|
1517
1526
|
}
|
|
1518
1527
|
|
|
1528
|
+
/**
|
|
1529
|
+
* @param {CSN.Options} options
|
|
1530
|
+
*/
|
|
1519
1531
|
function initModuleVars( options = { csnFlavor: 'gensrc' } ) {
|
|
1520
1532
|
gensrcFlavor = options.parseCdl || options.csnFlavor === 'gensrc' ||
|
|
1521
1533
|
options.toCsn && options.toCsn.flavor === 'gensrc';
|
|
@@ -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 = {}, messageFunctions, rule = 'cdl' ) {
|
|
115
|
+
function parse( source, filename = '<undefined>.cds', options = {}, messageFunctions = null, rule = 'cdl' ) {
|
|
116
116
|
const lexer = new Lexer( new antlr4.InputStream(source) );
|
|
117
117
|
const tokenStream = new RewriteTypeTokenStream(lexer);
|
|
118
118
|
/** @type {object} */
|
|
@@ -166,8 +166,8 @@ function parse( source, filename = '<undefined>.cds', options = {}, messageFunct
|
|
|
166
166
|
// Do not warn if docComments are explicitly disabled.
|
|
167
167
|
if (options.docComment !== false) {
|
|
168
168
|
for (const token of tokenStream.tokens) {
|
|
169
|
-
if (token.
|
|
170
|
-
messageFunctions.info('syntax-ignoring-doc-comment', parser.
|
|
169
|
+
if (token.type === parser.constructor.DocComment && !token.isUsed) {
|
|
170
|
+
messageFunctions.info('syntax-ignoring-doc-comment', parser.tokenLocation(token), {},
|
|
171
171
|
"Ignoring doc-comment as it does not belong to any artifact");
|
|
172
172
|
}
|
|
173
173
|
}
|
|
@@ -11,14 +11,17 @@ const { ATNState } = require('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
|
+
const { parseMultiLineStringLiteral } = require('./multiLineStringParser');
|
|
14
15
|
const { functionsWithoutParens, specialFunctions } = require('../compiler/builtins');
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
// Push message `msg` with location `loc` to array of errors:
|
|
18
19
|
function _message( parser, severity, id, loc, ...args ) {
|
|
19
20
|
const msg = parser.$messageFunctions[severity]; // set in antlrParser.js
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
if (loc instanceof antlr4.CommonToken) {
|
|
22
|
+
loc = parser.tokenLocation(loc);
|
|
23
|
+
}
|
|
24
|
+
return msg( id, loc, ...args );
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
// Class which is to be used as grammar option with
|
|
@@ -49,7 +52,7 @@ GenericAntlrParser.prototype = Object.assign(
|
|
|
49
52
|
attachLocation,
|
|
50
53
|
startLocation,
|
|
51
54
|
tokenLocation,
|
|
52
|
-
|
|
55
|
+
valueWithTokenLocation,
|
|
53
56
|
previousTokenAtLocation,
|
|
54
57
|
combinedLocation,
|
|
55
58
|
surroundByParens,
|
|
@@ -84,6 +87,7 @@ GenericAntlrParser.prototype = Object.assign(
|
|
|
84
87
|
isStraightBefore,
|
|
85
88
|
meltKeywordToIdentifier,
|
|
86
89
|
prepareGenericKeywords,
|
|
90
|
+
parseMultiLineStringLiteral,
|
|
87
91
|
constructor: GenericAntlrParser, // keep this last
|
|
88
92
|
}
|
|
89
93
|
);
|
|
@@ -290,62 +294,66 @@ function startLocation( token = this._ctx.start ) {
|
|
|
290
294
|
*
|
|
291
295
|
* @param {object} token
|
|
292
296
|
* @param {object} endToken
|
|
293
|
-
* @
|
|
297
|
+
* @return {CSN.Location}
|
|
294
298
|
*/
|
|
295
|
-
function tokenLocation( token, endToken
|
|
299
|
+
function tokenLocation( token, endToken = null ) {
|
|
296
300
|
if (!token)
|
|
297
301
|
return undefined;
|
|
298
302
|
if (!endToken) // including null
|
|
299
303
|
endToken = token;
|
|
304
|
+
|
|
300
305
|
/** @type {CSN.Location} */
|
|
301
|
-
const
|
|
306
|
+
const loc = {
|
|
302
307
|
file: this.filename,
|
|
303
308
|
line: token.line,
|
|
304
309
|
col: token.column + 1,
|
|
305
|
-
//
|
|
310
|
+
// Default for single line tokens
|
|
306
311
|
endLine: endToken.line,
|
|
307
312
|
endCol: endToken.stop - endToken.start + endToken.column + 2, // after the last char (special for EOF?)
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// This check is done for performance reason. No need to access a token's
|
|
316
|
+
// data if we know that it spans only one single line.
|
|
317
|
+
const isMultiLineToken = (
|
|
318
|
+
endToken.type === this.constructor.DocComment ||
|
|
319
|
+
endToken.type === this.constructor.String ||
|
|
320
|
+
endToken.type === this.constructor.UnterminatedLiteral
|
|
321
|
+
);
|
|
322
|
+
if (isMultiLineToken) {
|
|
323
|
+
// Count the number of newlines in the token.
|
|
324
|
+
const source = endToken.source[1].data;
|
|
325
|
+
let newLineCount = 0;
|
|
326
|
+
let lastNewlineIndex = endToken.start;
|
|
327
|
+
for (let i = endToken.start; i < endToken.stop; i++) {
|
|
328
|
+
// Note: We do NOT check for CR, LS, and PS (/[\r\u2028\u2029]/)
|
|
329
|
+
// because ANTLR only uses LF for line break detection.
|
|
330
|
+
if (source[i] === 10) { // code point of '\n'
|
|
331
|
+
newLineCount++;
|
|
332
|
+
lastNewlineIndex = i;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (newLineCount > 0) {
|
|
336
|
+
loc.endLine = endToken.line + newLineCount;
|
|
337
|
+
loc.endCol = endToken.stop - lastNewlineIndex + 1;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return loc;
|
|
312
342
|
}
|
|
313
343
|
|
|
314
344
|
/**
|
|
315
|
-
* Return location of `token`.
|
|
316
|
-
*
|
|
345
|
+
* Return `val` with the location of `token`. If `endToken` is provided, use its end
|
|
346
|
+
* location as end location in the result.
|
|
347
|
+
*
|
|
348
|
+
* @param {object} startToken
|
|
349
|
+
* @param {object} endToken
|
|
350
|
+
* @param {any} val
|
|
317
351
|
*/
|
|
318
|
-
function
|
|
319
|
-
if (!
|
|
352
|
+
function valueWithTokenLocation( val, startToken, endToken = null ) {
|
|
353
|
+
if (!startToken)
|
|
320
354
|
return undefined;
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
const source = token.source[1].data;
|
|
324
|
-
let newLineCount = 0;
|
|
325
|
-
let lastNewlineIndex = token.start;
|
|
326
|
-
for (let i = token.start; i < token.stop; i++) {
|
|
327
|
-
// Note: We do NOT check for CR, LS, and PS (/[\r\u2028\u2029]/)
|
|
328
|
-
// because ANTLR only uses LF for line break detection.
|
|
329
|
-
if (source[i] === 10) { // ASCII code for '\n'
|
|
330
|
-
newLineCount++;
|
|
331
|
-
lastNewlineIndex = i;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
if (newLineCount === 0)
|
|
335
|
-
// endCol calculation below requires at least one newLine.
|
|
336
|
-
return this.tokenLocation(token, token, val);
|
|
337
|
-
|
|
338
|
-
/** @type {CSN.Location} */
|
|
339
|
-
const r = {
|
|
340
|
-
file: this.filename,
|
|
341
|
-
line: token.line,
|
|
342
|
-
col: token.column + 1,
|
|
343
|
-
endLine: token.line + newLineCount,
|
|
344
|
-
endCol: token.stop - lastNewlineIndex + 1, // after the last char (special for EOF?)
|
|
345
|
-
};
|
|
346
|
-
if (val !== undefined)
|
|
347
|
-
return { location: r, val };
|
|
348
|
-
return r;
|
|
355
|
+
const loc = this.tokenLocation( startToken, endToken );
|
|
356
|
+
return { location: loc, val };
|
|
349
357
|
}
|
|
350
358
|
|
|
351
359
|
function previousTokenAtLocation( location ) {
|
|
@@ -406,7 +414,7 @@ function docComment( node ) {
|
|
|
406
414
|
this.warning( 'syntax-duplicate-doc-comment', token, {},
|
|
407
415
|
'Repeated doc comment - previous doc is replaced' );
|
|
408
416
|
}
|
|
409
|
-
node.doc = this.
|
|
417
|
+
node.doc = this.valueWithTokenLocation( parseDocComment( token.text ), token );
|
|
410
418
|
}
|
|
411
419
|
|
|
412
420
|
// Classify token (identifier category) for implicit names,
|
|
@@ -507,7 +515,7 @@ function valuePathAst( ref ) {
|
|
|
507
515
|
// If a '-' is directly before an unsigned number, consider it part of the number;
|
|
508
516
|
// otherwise (including for '+'), represent it as extra unary prefix operator.
|
|
509
517
|
function signedExpression( signToken, expr ) {
|
|
510
|
-
const sign = this.
|
|
518
|
+
const sign = this.valueWithTokenLocation( signToken.text, signToken );
|
|
511
519
|
const nval =
|
|
512
520
|
(signToken.text === '-' &&
|
|
513
521
|
expr && // expr may be null if `-` rule can't be parsed
|
|
@@ -557,8 +565,16 @@ function numberLiteral( token, sign, text = token.text ) {
|
|
|
557
565
|
function quotedLiteral( token, literal ) {
|
|
558
566
|
/** @type {CSN.Location} */
|
|
559
567
|
const location = this.tokenLocation( token );
|
|
560
|
-
|
|
561
|
-
|
|
568
|
+
let pos;
|
|
569
|
+
let val;
|
|
570
|
+
|
|
571
|
+
if (token.text.startsWith('`')) {
|
|
572
|
+
val = this.parseMultiLineStringLiteral(token);
|
|
573
|
+
literal = 'string';
|
|
574
|
+
} else {
|
|
575
|
+
pos = token.text.search( '\'' ) + 1; // pos of char after quote
|
|
576
|
+
val = token.text.slice( pos, -1 ).replace( /''/g, '\'' );
|
|
577
|
+
}
|
|
562
578
|
|
|
563
579
|
if (!literal)
|
|
564
580
|
literal = token.text.slice( 0, pos - 1 ).toLowerCase();
|
|
@@ -588,6 +604,7 @@ function quotedLiteral( token, literal ) {
|
|
|
588
604
|
};
|
|
589
605
|
|
|
590
606
|
function atChar(i) {
|
|
607
|
+
// Is only used with single-line strings.
|
|
591
608
|
return location.col + pos + i;
|
|
592
609
|
}
|
|
593
610
|
}
|
|
@@ -730,7 +747,7 @@ function assignProps( target, annos = [], props = null, location = null) {
|
|
|
730
747
|
for (const key in props) {
|
|
731
748
|
let val = props[key];
|
|
732
749
|
if (val instanceof antlr4.CommonToken)
|
|
733
|
-
val = this.
|
|
750
|
+
val = this.valueWithTokenLocation( true, val);
|
|
734
751
|
// only copy properties which are not undefined, null, {} or []
|
|
735
752
|
if (val != null &&
|
|
736
753
|
(typeof val !== 'object' ||
|
|
@@ -744,15 +761,15 @@ function assignProps( target, annos = [], props = null, location = null) {
|
|
|
744
761
|
|
|
745
762
|
// Create AST node for prefix operator `op` and arguments `args`
|
|
746
763
|
function createPrefixOp( token, args ) {
|
|
747
|
-
const op = this.
|
|
764
|
+
const op = this.valueWithTokenLocation( token.text.toLowerCase(), token );
|
|
748
765
|
return { op, args, location: this.combinedLocation( op, args[args.length - 1] ) };
|
|
749
766
|
}
|
|
750
767
|
|
|
751
768
|
// Create AST node for binary operator `op` and arguments `args`
|
|
752
769
|
function leftAssocBinaryOp( left, opToken, eToken, right, extraProp = 'quantifier' ) {
|
|
753
|
-
const op = this.
|
|
770
|
+
const op = this.valueWithTokenLocation( opToken.text.toLowerCase() , opToken);
|
|
754
771
|
const extra = eToken
|
|
755
|
-
? this.
|
|
772
|
+
? this.valueWithTokenLocation( eToken.text.toLowerCase(), eToken )
|
|
756
773
|
: undefined;
|
|
757
774
|
if (!left.$parens &&
|
|
758
775
|
(left.op && left.op.val) === (op && op.val) &&
|