@sap/cds-compiler 3.4.0 → 3.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -1
- package/bin/cdsc.js +3 -4
- package/bin/cdshi.js +19 -6
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/lib/api/main.js +6 -3
- package/lib/base/message-registry.js +61 -38
- package/lib/base/messages.js +7 -3
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/compiler/.eslintrc.json +4 -1
- package/lib/compiler/assert-consistency.js +8 -6
- package/lib/compiler/builtins.js +13 -13
- package/lib/compiler/checks.js +50 -33
- package/lib/compiler/define.js +9 -6
- package/lib/compiler/extend.js +83 -55
- package/lib/compiler/finalize-parse-cdl.js +3 -3
- package/lib/compiler/populate.js +16 -5
- package/lib/compiler/propagator.js +22 -29
- package/lib/compiler/resolve.js +2 -15
- package/lib/compiler/shared.js +4 -4
- package/lib/compiler/utils.js +14 -0
- package/lib/edm/annotations/genericTranslation.js +68 -56
- package/lib/edm/csn2edm.js +214 -174
- package/lib/edm/edmAnnoPreprocessor.js +5 -5
- package/lib/edm/edmInboundChecks.js +2 -2
- package/lib/edm/edmPreprocessor.js +1 -1
- package/lib/edm/edmUtils.js +3 -3
- package/lib/gen/Dictionary.json +176 -8
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +2 -1
- package/lib/gen/languageParser.js +4776 -4513
- package/lib/json/from-csn.js +21 -16
- package/lib/json/to-csn.js +37 -41
- package/lib/language/.eslintrc.json +4 -1
- package/lib/language/antlrParser.js +5 -2
- package/lib/language/docCommentParser.js +6 -6
- package/lib/language/errorStrategy.js +43 -23
- package/lib/language/genericAntlrParser.js +54 -95
- package/lib/language/language.g4 +92 -66
- package/lib/language/multiLineStringParser.js +2 -2
- package/lib/language/textUtils.js +2 -2
- package/lib/model/csnRefs.js +5 -0
- package/lib/modelCompare/compare.js +2 -2
- package/lib/modelCompare/utils/.eslintrc.json +22 -0
- package/lib/modelCompare/utils/filter.js +99 -0
- package/lib/render/.eslintrc.json +1 -0
- package/lib/render/toCdl.js +96 -127
- package/lib/render/toHdbcds.js +38 -35
- package/lib/render/toSql.js +75 -161
- package/lib/render/utils/common.js +133 -83
- package/lib/render/utils/delta.js +227 -0
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/draft/.eslintrc.json +1 -35
- package/lib/transform/forOdataNew.js +33 -22
- package/lib/transform/forRelationalDB.js +1 -1
- package/lib/transform/localized.js +9 -8
- package/lib/transform/odata/typesExposure.js +26 -4
- package/lib/transform/transformUtilsNew.js +15 -8
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
- package/package.json +2 -3
- package/lib/modelCompare/filter.js +0 -83
package/lib/json/from-csn.js
CHANGED
|
@@ -961,7 +961,7 @@ function dictionaryOf( elementFct ) {
|
|
|
961
961
|
++virtualLine;
|
|
962
962
|
for (const name of allNames) {
|
|
963
963
|
if (!name) {
|
|
964
|
-
|
|
964
|
+
message( 'syntax-invalid-name', location(true),
|
|
965
965
|
{ '#': 'csn', parentprop: spec.prop } );
|
|
966
966
|
}
|
|
967
967
|
const val = elementFct( dict[name], spec, r, dict, name );
|
|
@@ -1148,8 +1148,11 @@ function natnum( val, spec ) {
|
|
|
1148
1148
|
if (typeof val === 'number' && val >= 0)
|
|
1149
1149
|
// XSN TODO: do not require literal
|
|
1150
1150
|
return { val, literal: 'number', location: location() };
|
|
1151
|
-
|
|
1152
|
-
|
|
1151
|
+
const loc = location(true);
|
|
1152
|
+
if (spec.msgId)
|
|
1153
|
+
error( spec.msgId, loc, { prop: spec.msgProp } );
|
|
1154
|
+
else
|
|
1155
|
+
error( 'syntax-expecting-natnum', loc, { prop: spec.msgProp } );
|
|
1153
1156
|
return ignore( val );
|
|
1154
1157
|
}
|
|
1155
1158
|
|
|
@@ -1456,8 +1459,7 @@ function excluding( array, spec, xsn ) {
|
|
|
1456
1459
|
}
|
|
1457
1460
|
|
|
1458
1461
|
function duplicateExcluding( name, loc ) {
|
|
1459
|
-
error( 'syntax-duplicate-excluding', loc, { name,
|
|
1460
|
-
'Duplicate $(NAME) in the $(KEYWORD) clause' );
|
|
1462
|
+
error( 'syntax-duplicate-excluding', loc, { '#': 'csn', name, prop: 'excluding[]' } );
|
|
1461
1463
|
}
|
|
1462
1464
|
|
|
1463
1465
|
function masked( val, spec ) {
|
|
@@ -1541,14 +1543,14 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
|
|
|
1541
1543
|
const variant = kind && s.inKind
|
|
1542
1544
|
? ([ 'extend', 'annotate' ].includes(kind) ? kind : 'kind')
|
|
1543
1545
|
: (parentSpec.msgProp ? 'prop' : 'top');
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1546
|
+
error( 'syntax-unexpected-property', location(true),
|
|
1547
|
+
{
|
|
1548
|
+
'#': variant,
|
|
1549
|
+
prop,
|
|
1550
|
+
parentprop:
|
|
1551
|
+
parentSpec.msgProp,
|
|
1552
|
+
kind,
|
|
1553
|
+
} );
|
|
1552
1554
|
}
|
|
1553
1555
|
else if (checkAndSetXorGroup( s.xorGroup, s.xorException, prop, xor )) {
|
|
1554
1556
|
onlyWith( s, s.onlyWith, csn, prop, xor, expected );
|
|
@@ -1622,7 +1624,7 @@ function checkAndSetXorGroup( group, exception, prop, xor ) {
|
|
|
1622
1624
|
}
|
|
1623
1625
|
if (siblingprop === exception)
|
|
1624
1626
|
return true;
|
|
1625
|
-
|
|
1627
|
+
error( 'syntax-unexpected-property', location(true), { '#': 'sibling', prop, siblingprop } );
|
|
1626
1628
|
return false;
|
|
1627
1629
|
}
|
|
1628
1630
|
|
|
@@ -1652,8 +1654,11 @@ function isArray( array, spec ) {
|
|
|
1652
1654
|
function isObject( obj, spec ) {
|
|
1653
1655
|
if (obj && typeof obj === 'object' && !Array.isArray( obj ))
|
|
1654
1656
|
return obj;
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
+
const loc = location(true);
|
|
1658
|
+
if (spec.msgId)
|
|
1659
|
+
error( spec.msgId, loc, { prop: spec.msgProp });
|
|
1660
|
+
else
|
|
1661
|
+
error( 'syntax-expecting-object', loc, { prop: spec.msgProp });
|
|
1657
1662
|
return ignore( obj );
|
|
1658
1663
|
}
|
|
1659
1664
|
|
package/lib/json/to-csn.js
CHANGED
|
@@ -23,6 +23,7 @@ const normalizedKind = {
|
|
|
23
23
|
param: 'param',
|
|
24
24
|
action: 'action',
|
|
25
25
|
function: 'action',
|
|
26
|
+
enum: 'enum',
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
/** @type {boolean|string} */
|
|
@@ -429,21 +430,10 @@ function extensions( node, csn, model ) {
|
|
|
429
430
|
}
|
|
430
431
|
else if (gensrcFlavor) {
|
|
431
432
|
// From definitions (without redefinitions) with potential inferred elements:
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
if (art.actions)
|
|
437
|
-
attachAnnotations( annotate, 'actions', art.actions, art.$inferred );
|
|
438
|
-
else if (art.params)
|
|
439
|
-
attachAnnotations( annotate, 'params', art.params, art.$inferred );
|
|
440
|
-
const obj = art.returns || art;
|
|
441
|
-
const elems = (obj.items || obj).elements; // no targetAspect here
|
|
442
|
-
if (elems)
|
|
443
|
-
attachAnnotations( annotate, 'elements', elems, art.$inferred, art.returns );
|
|
444
|
-
}
|
|
445
|
-
if (Object.keys( annotate ).length > 1)
|
|
446
|
-
exts.push( annotate );
|
|
433
|
+
const result = { annotate: Object.create(null) };
|
|
434
|
+
attachAnnotations(result, 'annotate', { [name]: art }, art.$inferred );
|
|
435
|
+
if (result.annotate[name])
|
|
436
|
+
exts.push({ annotate: name, ...result.annotate[name] } );
|
|
447
437
|
}
|
|
448
438
|
}
|
|
449
439
|
|
|
@@ -470,7 +460,7 @@ function extensions( node, csn, model ) {
|
|
|
470
460
|
const par = art.params[name];
|
|
471
461
|
if (!inferredParent && !par.$inferred && par.$expand !== 'annotate')
|
|
472
462
|
continue;
|
|
473
|
-
const render = annotationsAndDocComment( par
|
|
463
|
+
const render = annotationsAndDocComment( par );
|
|
474
464
|
const subElems = par.$expand !== 'origin' && (par.items || par).elements;
|
|
475
465
|
if (subElems) {
|
|
476
466
|
const sub = inferred( subElems, par.$inferred );
|
|
@@ -487,7 +477,7 @@ function extensions( node, csn, model ) {
|
|
|
487
477
|
const par = art.returns;
|
|
488
478
|
if (!inferredParent && !par.$inferred && par.$expand !== 'annotate')
|
|
489
479
|
return;
|
|
490
|
-
const render = annotationsAndDocComment( par
|
|
480
|
+
const render = annotationsAndDocComment( par );
|
|
491
481
|
const subElems = par.$expand !== 'origin' && (par.items || par).elements;
|
|
492
482
|
if (subElems) {
|
|
493
483
|
const sub = inferred( subElems, par.$inferred );
|
|
@@ -508,7 +498,7 @@ function extensions( node, csn, model ) {
|
|
|
508
498
|
const name = art.name.absolute;
|
|
509
499
|
// 'true' because annotations on namespaces and builtins can only
|
|
510
500
|
// happen through extensions.
|
|
511
|
-
const annos = annotationsAndDocComment( art
|
|
501
|
+
const annos = annotationsAndDocComment( art );
|
|
512
502
|
const annotate = Object.assign( { annotate: name }, annos );
|
|
513
503
|
if (Object.keys( annotate ).length > 1) {
|
|
514
504
|
const loc = locationForAnnotationExtension();
|
|
@@ -567,16 +557,21 @@ function sources( srcDict, csn ) {
|
|
|
567
557
|
function attachAnnotations( annotate, prop, dict, inferred, insideReturns = false ) {
|
|
568
558
|
const annoDict = Object.create( dictionaryPrototype );
|
|
569
559
|
for (const name in dict) {
|
|
570
|
-
const
|
|
571
|
-
const inf = inferred ||
|
|
572
|
-
const sub = (inf) ? annotationsAndDocComment(
|
|
573
|
-
if (
|
|
574
|
-
if (
|
|
575
|
-
attachAnnotations( sub, '
|
|
576
|
-
|
|
577
|
-
|
|
560
|
+
const entry = dict[name];
|
|
561
|
+
const inf = inferred || entry.$inferred; // is probably always inferred if parent was
|
|
562
|
+
const sub = (inf) ? annotationsAndDocComment( entry ) : {};
|
|
563
|
+
if (entry.$expand === 'annotate') {
|
|
564
|
+
if (entry.actions)
|
|
565
|
+
attachAnnotations( sub, 'actions', entry.actions, inf );
|
|
566
|
+
else if (entry.params)
|
|
567
|
+
attachAnnotations( sub, 'params', entry.params, inf );
|
|
568
|
+
const obj = entry.returns || entry;
|
|
569
|
+
const many = obj.items || obj;
|
|
570
|
+
const elems = (many.targetAspect || many).elements;
|
|
578
571
|
if (elems)
|
|
579
|
-
attachAnnotations( sub, 'elements', elems, inf,
|
|
572
|
+
attachAnnotations( sub, 'elements', elems, inf, entry.returns );
|
|
573
|
+
if (many.enum)
|
|
574
|
+
attachAnnotations( sub, 'enum', many.enum, inf );
|
|
580
575
|
}
|
|
581
576
|
if (Object.keys( sub ).length)
|
|
582
577
|
annoDict[name] = sub;
|
|
@@ -723,10 +718,14 @@ function keepElements( node, line ) {
|
|
|
723
718
|
return false; // no need to render elements
|
|
724
719
|
}
|
|
725
720
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
721
|
+
/**
|
|
722
|
+
* For gensrcFlavor and namespace/builtin annotation extraction:
|
|
723
|
+
* return annotations from definition and annotations.
|
|
724
|
+
* The call side should check that node.$inferred is truthy.
|
|
725
|
+
*
|
|
726
|
+
* @param {object} node
|
|
727
|
+
*/
|
|
728
|
+
function annotationsAndDocComment( node ) {
|
|
730
729
|
const csn = {};
|
|
731
730
|
const transformer = transformers['@'];
|
|
732
731
|
const keys = Object.keys( node ).filter( a => a.charAt(0) === '@' ).sort();
|
|
@@ -734,15 +733,12 @@ function annotationsAndDocComment( node, annotated ) {
|
|
|
734
733
|
const val = node[prop];
|
|
735
734
|
// val.$priority isn't set for computed annotations like @Core.Computed
|
|
736
735
|
// and @odata.containment.ignore
|
|
737
|
-
//
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
if (sub !== undefined)
|
|
744
|
-
csn[prop] = sub;
|
|
745
|
-
}
|
|
736
|
+
// transformer (= value) takes care to exclude $inferred annotation assignments
|
|
737
|
+
const sub = transformer( val );
|
|
738
|
+
// As value() just has one value, so we do not provide ( val, csn, node, prop )
|
|
739
|
+
// which would be more robust, but makes some JS checks unhappy
|
|
740
|
+
if (sub !== undefined)
|
|
741
|
+
csn[prop] = sub;
|
|
746
742
|
}
|
|
747
743
|
if (node.doc)
|
|
748
744
|
csn.doc = transformers.doc(node.doc);
|
|
@@ -1534,7 +1530,7 @@ function addElementAsColumn( elem, cols ) {
|
|
|
1534
1530
|
if (elem.$inferred === '*')
|
|
1535
1531
|
return;
|
|
1536
1532
|
// only list annotations here which are provided directly with definition
|
|
1537
|
-
const col = (gensrcFlavor) ? annotationsAndDocComment( elem
|
|
1533
|
+
const col = (gensrcFlavor) ? annotationsAndDocComment( elem ) : {};
|
|
1538
1534
|
// with `client` flavor, assignments are available at the element
|
|
1539
1535
|
const gensrcSaved = gensrcFlavor;
|
|
1540
1536
|
|
|
@@ -164,8 +164,11 @@ function parse( source, filename = '<undefined>.cds',
|
|
|
164
164
|
if (options.docComment !== false) {
|
|
165
165
|
for (const token of tokenStream.tokens) {
|
|
166
166
|
if (token.type === parser.constructor.DocComment && !token.isUsed) {
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
// TODO: think of 'syntax-unexpected-doc-comment'
|
|
168
|
+
messageFunctions.info( 'syntax-ignoring-doc-comment', parser.tokenLocation(token), {},
|
|
169
|
+
'Ignoring doc comment as it is not written at a defined position' );
|
|
170
|
+
// this is also for position inside some artifact definition, i.e. previous text
|
|
171
|
+
// "does not belong to any artifact" might be confusing
|
|
169
172
|
}
|
|
170
173
|
}
|
|
171
174
|
}
|
|
@@ -16,7 +16,7 @@ const {
|
|
|
16
16
|
* @returns {string|null} Parsed contents or if the comment has an invalid format or
|
|
17
17
|
* does not have any content, null is returned.
|
|
18
18
|
*/
|
|
19
|
-
function parseDocComment(comment) {
|
|
19
|
+
function parseDocComment( comment ) {
|
|
20
20
|
// Also return "null" for empty doc comments so that doc comment propagation
|
|
21
21
|
// can be stopped.
|
|
22
22
|
if (comment.length <= 5) // at least "/***/"
|
|
@@ -83,7 +83,7 @@ function parseDocComment(comment) {
|
|
|
83
83
|
* @param {string[]} lines String split into lines.
|
|
84
84
|
* @param {boolean} ignoreFirstLine Whether to ignore the first line for indentation counting.
|
|
85
85
|
*/
|
|
86
|
-
function stripCommentIndentation(lines, ignoreFirstLine) {
|
|
86
|
+
function stripCommentIndentation( lines, ignoreFirstLine ) {
|
|
87
87
|
const n = lines.length;
|
|
88
88
|
|
|
89
89
|
const minIndent = lines.reduce((min, line, index) => {
|
|
@@ -114,7 +114,7 @@ function stripCommentIndentation(lines, ignoreFirstLine) {
|
|
|
114
114
|
* @param {string} line
|
|
115
115
|
* @returns {string} line without fence
|
|
116
116
|
*/
|
|
117
|
-
function removeFence(line) {
|
|
117
|
+
function removeFence( line ) {
|
|
118
118
|
return line.replace(/^\s*[*]\s?/, '');
|
|
119
119
|
}
|
|
120
120
|
|
|
@@ -125,7 +125,7 @@ function removeFence(line) {
|
|
|
125
125
|
* @param {string} line
|
|
126
126
|
* @returns {string} Header without fence.
|
|
127
127
|
*/
|
|
128
|
-
function removeHeaderFence(line) {
|
|
128
|
+
function removeHeaderFence( line ) {
|
|
129
129
|
return line.replace(/^\/[*]{2,}\s?/, '');
|
|
130
130
|
}
|
|
131
131
|
|
|
@@ -138,7 +138,7 @@ function removeHeaderFence(line) {
|
|
|
138
138
|
* @param {string} line
|
|
139
139
|
* @returns {string} header without fence
|
|
140
140
|
*/
|
|
141
|
-
function removeFooterFence(line) {
|
|
141
|
+
function removeFooterFence( line ) {
|
|
142
142
|
return line.replace(/\s*[*]+\/$/, '');
|
|
143
143
|
}
|
|
144
144
|
|
|
@@ -148,7 +148,7 @@ function removeFooterFence(line) {
|
|
|
148
148
|
*
|
|
149
149
|
* @param {string[]} lines
|
|
150
150
|
*/
|
|
151
|
-
function isFencedComment(lines) {
|
|
151
|
+
function isFencedComment( lines ) {
|
|
152
152
|
const index = lines.findIndex((line, i) => {
|
|
153
153
|
const exclude = (i === 0 || i === lines.length - 1);
|
|
154
154
|
return !exclude && !(/^\s*[*]/.test(line));
|
|
@@ -55,8 +55,11 @@ function match( ttype ) {
|
|
|
55
55
|
const token = this.getCurrentToken();
|
|
56
56
|
if (token.type === identType || !keywordRegexp.test( token.text ))
|
|
57
57
|
return antlr4.Parser.prototype.match.call( this, ttype );
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
// This is very likely to be dead code: we do not use a simple Identifier
|
|
59
|
+
// without alternatives in the grammar. With alternatives, recoverInline() is
|
|
60
|
+
// the place to go. (But this code should work with a changed grammar…)
|
|
61
|
+
this.message( 'syntax-unexpected-reserved-word', token,
|
|
62
|
+
{ code: token.text, delimited: token.text } );
|
|
60
63
|
this._errHandler.reportMatch(this);
|
|
61
64
|
this.consume();
|
|
62
65
|
return token;
|
|
@@ -72,7 +75,6 @@ class KeywordErrorStrategy extends DefaultErrorStrategy {
|
|
|
72
75
|
super( ...args );
|
|
73
76
|
|
|
74
77
|
this._super = {
|
|
75
|
-
consumeUntil: super.consumeUntil,
|
|
76
78
|
recoverInline: super.recoverInline,
|
|
77
79
|
getExpectedTokens: super.getExpectedTokens,
|
|
78
80
|
};
|
|
@@ -89,6 +91,7 @@ Object.assign( KeywordErrorStrategy.prototype, {
|
|
|
89
91
|
reportIgnoredWith,
|
|
90
92
|
// getErrorRecoverySet,
|
|
91
93
|
consumeUntil,
|
|
94
|
+
consumeAndMarkUntil,
|
|
92
95
|
recoverInline,
|
|
93
96
|
getMissingSymbol,
|
|
94
97
|
getExpectedTokensForMessage,
|
|
@@ -137,16 +140,19 @@ function sync( recognizer ) {
|
|
|
137
140
|
}
|
|
138
141
|
|
|
139
142
|
// Expected token is identifier, current is (reserved) KEYWORD:
|
|
140
|
-
// TODO: do not use this if "close enough" (1 char diff
|
|
143
|
+
// TODO: do not use this if "close enough" (1 char diff or prefix)
|
|
144
|
+
// to a keyword in nextTokens
|
|
141
145
|
//
|
|
142
146
|
// NOTE: it is important to do this only if EPSILON is not in `nextTokens`,
|
|
143
|
-
// which means that we cannot bring the better special syntax-
|
|
147
|
+
// which means that we cannot bring the better special syntax-unexpected-reserved
|
|
144
148
|
// in all cases. Reason: high performance impact of the alternative,
|
|
145
149
|
// i.e. calling method Parser#isExpectedToken() = invoking the ATN
|
|
146
150
|
// interpreter to see behind EPSILON.
|
|
147
151
|
const identType = recognizer.constructor.Identifier;
|
|
148
152
|
if (keywordRegexp.test( token.text ) && nextTokens.contains( identType )) {
|
|
149
|
-
recognizer.message( 'syntax-
|
|
153
|
+
recognizer.message( 'syntax-unexpected-reserved-word', token,
|
|
154
|
+
{ code: token.text, delimited: token.text } );
|
|
155
|
+
// TODO: attach tokens like for 'syntax-unexpected-token'
|
|
150
156
|
token.type = identType; // make next ANTLR decision assume identifier
|
|
151
157
|
return;
|
|
152
158
|
}
|
|
@@ -210,11 +216,11 @@ function reportInputMismatch( recognizer, e, deadEnds ) {
|
|
|
210
216
|
const expecting = deadEnds !== true && // true: cannot compute expecting
|
|
211
217
|
this.getExpectedTokensForMessage( recognizer, e.offendingToken, deadEnds );
|
|
212
218
|
const offending = this.getTokenDisplay( e.offendingToken, recognizer );
|
|
219
|
+
e.offendingToken.$isSkipped = 'offending';
|
|
213
220
|
let err;
|
|
214
221
|
if (expecting && expecting.length) {
|
|
215
|
-
err = recognizer.error( 'syntax-
|
|
216
|
-
{ offending, expecting }
|
|
217
|
-
'Mismatched $(OFFENDING), expecting $(EXPECTING)' );
|
|
222
|
+
err = recognizer.error( 'syntax-unexpected-token', e.offendingToken,
|
|
223
|
+
{ offending, expecting } );
|
|
218
224
|
err.expectedTokens = expecting;
|
|
219
225
|
}
|
|
220
226
|
else { // should not really happen anymore... -> no messageId !
|
|
@@ -232,11 +238,12 @@ function reportUnwantedToken( recognizer ) {
|
|
|
232
238
|
this.beginErrorCondition(recognizer);
|
|
233
239
|
|
|
234
240
|
const token = recognizer.getCurrentToken();
|
|
241
|
+
token.$isSkipped = 'offending';
|
|
235
242
|
const expecting = this.getExpectedTokensForMessage( recognizer, token );
|
|
236
243
|
const offending = this.getTokenDisplay( token, recognizer );
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
'
|
|
244
|
+
// Just text variant, no other message id! Would depend on ANTLR-internals
|
|
245
|
+
const err = recognizer.error( 'syntax-unexpected-token', token,
|
|
246
|
+
{ '#': 'unwanted', offending, expecting } );
|
|
240
247
|
err.expectedTokens = expecting; // TODO: remove next token?
|
|
241
248
|
if (!recognizer.avoidErrorListeners) // with --trace-parser or --trace-parser-ambig
|
|
242
249
|
recognizer.notifyErrorListeners( err.message, token, err );
|
|
@@ -249,9 +256,11 @@ function reportMissingToken( recognizer ) {
|
|
|
249
256
|
this.beginErrorCondition(recognizer);
|
|
250
257
|
|
|
251
258
|
const token = recognizer.getCurrentToken();
|
|
259
|
+
token.$isSkipped = 'offending';
|
|
252
260
|
const expecting = this.getExpectedTokensForMessage( recognizer, token );
|
|
253
261
|
const offending = this.getTokenDisplay( token, recognizer );
|
|
254
262
|
// TODO: if non-reserved keyword will not been parsed as keyword, use Identifier for offending
|
|
263
|
+
// Hopefully not too ANTLR-specific, so extra message id is ok:
|
|
255
264
|
const err = recognizer.error( 'syntax-missing-token', token,
|
|
256
265
|
{ offending, expecting },
|
|
257
266
|
'Missing $(EXPECTING) before $(OFFENDING)' );
|
|
@@ -264,10 +273,10 @@ function reportIgnoredWith( recognizer, t ) {
|
|
|
264
273
|
const next = recognizer._interp.atn.states[recognizer.state].transitions[0].target;
|
|
265
274
|
recognizer.state = next.stateNumber; // previous match() does not set the state
|
|
266
275
|
const expecting = this.getExpectedTokensForMessage( recognizer, t );
|
|
267
|
-
const m = recognizer.warning( 'syntax-
|
|
268
|
-
{ offending: "';'", expecting },
|
|
276
|
+
const m = recognizer.warning( 'syntax-unexpected-semicolon', t,
|
|
277
|
+
{ offending: "';'", expecting, keyword: 'with' },
|
|
269
278
|
// eslint-disable-next-line max-len
|
|
270
|
-
'Unexpected $(OFFENDING), expecting $(EXPECTING) - ignored previous
|
|
279
|
+
'Unexpected $(OFFENDING), expecting $(EXPECTING) - ignored previous $(KEYWORD)' );
|
|
271
280
|
m.expectedTokens = expecting;
|
|
272
281
|
}
|
|
273
282
|
|
|
@@ -280,10 +289,10 @@ function consumeUntil( recognizer, set ) {
|
|
|
280
289
|
|
|
281
290
|
// let s=this.getTokenDisplay( recognizer.getCurrentToken(), recognizer );
|
|
282
291
|
if (SEMI < 1 || RBRACE < 1) {
|
|
283
|
-
this.
|
|
292
|
+
this.consumeAndMarkUntil( recognizer, set );
|
|
284
293
|
}
|
|
285
294
|
else if (set.contains(SEMI)) { // do not check for RBRACE here!
|
|
286
|
-
this.
|
|
295
|
+
this.consumeAndMarkUntil( recognizer, set );
|
|
287
296
|
// console.log('CONSUMED-ORIG:',s,this.getTokenDisplay( recognizer.getCurrentToken(),
|
|
288
297
|
// recognizer ),recognizer.getCurrentToken().line,intervalSetToArray( recognizer, set ));
|
|
289
298
|
}
|
|
@@ -295,9 +304,9 @@ function consumeUntil( recognizer, set ) {
|
|
|
295
304
|
stop.addOne( SEMI );
|
|
296
305
|
// I am not that sure whether to add RBRACE...
|
|
297
306
|
stop.addOne( RBRACE );
|
|
298
|
-
this.
|
|
299
|
-
|
|
300
|
-
|
|
307
|
+
this.consumeAndMarkUntil( recognizer, stop );
|
|
308
|
+
const ttype = recognizer.getTokenStream().LA(1);
|
|
309
|
+
if (ttype === SEMI || ttype === RBRACE && !set.contains(RBRACE)) {
|
|
301
310
|
recognizer.consume();
|
|
302
311
|
this.reportMatch(recognizer); // we know current token is correct
|
|
303
312
|
}
|
|
@@ -312,6 +321,15 @@ function consumeUntil( recognizer, set ) {
|
|
|
312
321
|
}
|
|
313
322
|
}
|
|
314
323
|
|
|
324
|
+
function consumeAndMarkUntil( recognizer, set ) {
|
|
325
|
+
let t = recognizer.getTokenStream().LT(1);
|
|
326
|
+
while (t.type !== antlr4.Token.EOF && !set.contains( t.type )) {
|
|
327
|
+
if (!t.$isSkipped)
|
|
328
|
+
t.$isSkipped = true;
|
|
329
|
+
recognizer.consume();
|
|
330
|
+
t = recognizer.getTokenStream().LT(1);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
315
333
|
|
|
316
334
|
// As the `match` function of the parser `recognizer` does not allow to check
|
|
317
335
|
// against a set of token types, the generated parser code checks against that
|
|
@@ -328,7 +346,9 @@ function recoverInline( recognizer ) {
|
|
|
328
346
|
if (!keywordRegexp.test( token.text ))
|
|
329
347
|
return this._super.recoverInline.call( this, recognizer );
|
|
330
348
|
|
|
331
|
-
|
|
349
|
+
// TODO: attach `Identifier` as valid name to message?
|
|
350
|
+
recognizer.message( 'syntax-unexpected-reserved-word', token,
|
|
351
|
+
{ code: token.text, delimited: token.text } );
|
|
332
352
|
this.reportMatch(recognizer); // we know current token is correct
|
|
333
353
|
recognizer.consume();
|
|
334
354
|
return token;
|
|
@@ -496,14 +516,14 @@ function getExpectedTokensForMessage( recognizer, offendingToken, deadEnds ) {
|
|
|
496
516
|
// expected.toString(recognizer.literalNames, recognizer.symbolicNames));
|
|
497
517
|
return intervalSetToArray( recognizer, expected );
|
|
498
518
|
|
|
499
|
-
function addSet(other) {
|
|
519
|
+
function addSet( other ) {
|
|
500
520
|
if (!other.contains( hideAltsType ))
|
|
501
521
|
origAddSet.call( this, other );
|
|
502
522
|
}
|
|
503
523
|
|
|
504
524
|
// Add an interval `v` to the IntervalSet `this`. If `v` contains the token
|
|
505
525
|
// type `Identifier`, do not add non-reserved keywords in `v`.
|
|
506
|
-
function addInterval(v) {
|
|
526
|
+
function addInterval( v ) {
|
|
507
527
|
if (v.stop <= identType) {
|
|
508
528
|
origAddInterval.call(this, v);
|
|
509
529
|
}
|