@sap/cds-compiler 6.8.0 → 6.9.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 +42 -0
- package/README.md +4 -0
- package/bin/cdshi.js +1 -0
- package/lib/api/main.js +8 -1
- package/lib/api/options.js +3 -1
- package/lib/base/builtins.js +13 -9
- package/lib/base/csnRefs.js +8 -10
- package/lib/base/message-registry.js +61 -2
- package/lib/base/messages.js +2 -0
- package/lib/base/optionProcessor.js +2 -0
- package/lib/base/specialOptions.js +1 -1
- package/lib/compiler/assert-consistency.js +11 -9
- package/lib/compiler/base.js +5 -1
- package/lib/compiler/define.js +1 -1
- package/lib/compiler/dictionaries.js +2 -3
- package/lib/compiler/extend.js +119 -19
- package/lib/compiler/lsp-api.js +3 -3
- package/lib/compiler/populate.js +4 -5
- package/lib/compiler/resolve.js +50 -35
- package/lib/compiler/shared.js +33 -12
- package/lib/compiler/tweak-assocs.js +2 -2
- package/lib/compiler/utils.js +26 -23
- package/lib/compiler/xpr-rewrite.js +2 -2
- package/lib/edm/EdmPrimitiveTypeDefinitions.js +4 -1
- package/lib/edm/annotations/genericTranslation.js +49 -6
- package/lib/gen/BaseParser.js +59 -97
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +2055 -1969
- package/lib/gen/Dictionary.json +67 -7
- package/lib/json/from-csn.js +7 -12
- package/lib/json/to-csn.js +59 -35
- package/lib/parsers/AstBuildingParser.js +43 -30
- package/lib/render/toCdl.js +46 -27
- package/lib/render/toSql.js +9 -0
- package/lib/render/utils/common.js +3 -2
- package/lib/tool-lib/enrichCsn.js +1 -0
- package/lib/transform/effective/flattening.js +6 -5
- package/lib/transform/effective/main.js +5 -0
- package/lib/transform/forOdata.js +20 -2
- package/lib/transform/forRelationalDB.js +8 -4
- package/lib/transform/tupleExpansion.js +40 -0
- package/package.json +3 -40
package/lib/gen/Dictionary.json
CHANGED
|
@@ -170,6 +170,8 @@
|
|
|
170
170
|
"Type": "Core.Tag"
|
|
171
171
|
},
|
|
172
172
|
"Capabilities.BatchContinueOnErrorSupported": {
|
|
173
|
+
"$deprecated": true,
|
|
174
|
+
"$deprecationText": "Deprecated in favor of the [`ContinueOnErrorSupported`](#BatchSupportType) property from the [`BatchSupport`](#BatchSupport) term",
|
|
173
175
|
"AppliesTo": [
|
|
174
176
|
"EntityContainer"
|
|
175
177
|
],
|
|
@@ -1167,13 +1169,15 @@
|
|
|
1167
1169
|
},
|
|
1168
1170
|
"Communication.IsEmailAddress": {
|
|
1169
1171
|
"AppliesTo": [
|
|
1170
|
-
"Property"
|
|
1172
|
+
"Property",
|
|
1173
|
+
"Parameter"
|
|
1171
1174
|
],
|
|
1172
1175
|
"Type": "Core.Tag"
|
|
1173
1176
|
},
|
|
1174
1177
|
"Communication.IsPhoneNumber": {
|
|
1175
1178
|
"AppliesTo": [
|
|
1176
|
-
"Property"
|
|
1179
|
+
"Property",
|
|
1180
|
+
"Parameter"
|
|
1177
1181
|
],
|
|
1178
1182
|
"Type": "Core.Tag"
|
|
1179
1183
|
},
|
|
@@ -1863,7 +1867,6 @@
|
|
|
1863
1867
|
}
|
|
1864
1868
|
}
|
|
1865
1869
|
},
|
|
1866
|
-
"$experimental": true,
|
|
1867
1870
|
"AppliesTo": [
|
|
1868
1871
|
"Property",
|
|
1869
1872
|
"Parameter"
|
|
@@ -2106,7 +2109,8 @@
|
|
|
2106
2109
|
"UI.Placeholder": {
|
|
2107
2110
|
"AppliesTo": [
|
|
2108
2111
|
"Property",
|
|
2109
|
-
"Parameter"
|
|
2112
|
+
"Parameter",
|
|
2113
|
+
"Record"
|
|
2110
2114
|
],
|
|
2111
2115
|
"Type": "Edm.String"
|
|
2112
2116
|
},
|
|
@@ -2145,7 +2149,11 @@
|
|
|
2145
2149
|
"AppliesTo": [
|
|
2146
2150
|
"EntityType"
|
|
2147
2151
|
],
|
|
2148
|
-
"
|
|
2152
|
+
"DerivedTypeConstraint": [
|
|
2153
|
+
"Edm.ComplexType",
|
|
2154
|
+
"Edm.EntityType"
|
|
2155
|
+
],
|
|
2156
|
+
"Type": "Edm.Untyped"
|
|
2149
2157
|
},
|
|
2150
2158
|
"UI.SelectionFields": {
|
|
2151
2159
|
"AppliesTo": [
|
|
@@ -2847,6 +2855,36 @@
|
|
|
2847
2855
|
"TypecastSegmentSupported": "Edm.Boolean"
|
|
2848
2856
|
}
|
|
2849
2857
|
},
|
|
2858
|
+
"Capabilities.ExpandByKeyRestrictionsBase": {
|
|
2859
|
+
"$kind": "ComplexType",
|
|
2860
|
+
"BaseType": "Capabilities.ExpandRestrictionsBase",
|
|
2861
|
+
"Properties": {
|
|
2862
|
+
"Expandable": "Edm.Boolean",
|
|
2863
|
+
"MaxLevels": "Edm.Int32",
|
|
2864
|
+
"StreamsExpandable": "Edm.Boolean"
|
|
2865
|
+
}
|
|
2866
|
+
},
|
|
2867
|
+
"Capabilities.ExpandByKeyRestrictionsType": {
|
|
2868
|
+
"$kind": "ComplexType",
|
|
2869
|
+
"BaseType": "Capabilities.ExpandByKeyRestrictionsBase",
|
|
2870
|
+
"Properties": {
|
|
2871
|
+
"Expandable": "Edm.Boolean",
|
|
2872
|
+
"MaxLevels": "Edm.Int32",
|
|
2873
|
+
"NonExpandableProperties": "Collection(Edm.NavigationPropertyPath)",
|
|
2874
|
+
"NonExpandableStreamProperties": "Collection(Edm.PropertyPath)",
|
|
2875
|
+
"StreamsExpandable": "Edm.Boolean"
|
|
2876
|
+
}
|
|
2877
|
+
},
|
|
2878
|
+
"Capabilities.ExpandCollectionRestrictionsType": {
|
|
2879
|
+
"$kind": "ComplexType",
|
|
2880
|
+
"BaseType": "Capabilities.ExpandRestrictionsBase",
|
|
2881
|
+
"Properties": {
|
|
2882
|
+
"ExpandByKeyRestrictions": "Capabilities.ExpandByKeyRestrictionsBase",
|
|
2883
|
+
"Expandable": "Edm.Boolean",
|
|
2884
|
+
"MaxLevels": "Edm.Int32",
|
|
2885
|
+
"StreamsExpandable": "Edm.Boolean"
|
|
2886
|
+
}
|
|
2887
|
+
},
|
|
2850
2888
|
"Capabilities.ExpandRestrictionsBase": {
|
|
2851
2889
|
"$kind": "ComplexType",
|
|
2852
2890
|
"Properties": {
|
|
@@ -2857,8 +2895,9 @@
|
|
|
2857
2895
|
},
|
|
2858
2896
|
"Capabilities.ExpandRestrictionsType": {
|
|
2859
2897
|
"$kind": "ComplexType",
|
|
2860
|
-
"BaseType": "Capabilities.
|
|
2898
|
+
"BaseType": "Capabilities.ExpandCollectionRestrictionsType",
|
|
2861
2899
|
"Properties": {
|
|
2900
|
+
"ExpandByKeyRestrictions": "Capabilities.ExpandByKeyRestrictionsBase",
|
|
2862
2901
|
"Expandable": "Edm.Boolean",
|
|
2863
2902
|
"MaxLevels": "Edm.Int32",
|
|
2864
2903
|
"NonExpandableProperties": "Collection(Edm.NavigationPropertyPath)",
|
|
@@ -4463,7 +4502,28 @@
|
|
|
4463
4502
|
"CriticalityRepresentation": "UI.CriticalityRepresentationType",
|
|
4464
4503
|
"IconUrl": "Edm.String",
|
|
4465
4504
|
"Label": "Edm.String",
|
|
4466
|
-
"Value":
|
|
4505
|
+
"Value": {
|
|
4506
|
+
"DerivedTypeConstraint": [
|
|
4507
|
+
"Edm.PrimitiveType",
|
|
4508
|
+
"Collection(Edm.Binary)",
|
|
4509
|
+
"Collection(Edm.Boolean)",
|
|
4510
|
+
"Collection(Edm.Byte)",
|
|
4511
|
+
"Collection(Edm.Date)",
|
|
4512
|
+
"Collection(Edm.DateTimeOffset)",
|
|
4513
|
+
"Collection(Edm.Decimal)",
|
|
4514
|
+
"Collection(Edm.Double)",
|
|
4515
|
+
"Collection(Edm.Duration)",
|
|
4516
|
+
"Collection(Edm.Guid)",
|
|
4517
|
+
"Collection(Edm.Int16)",
|
|
4518
|
+
"Collection(Edm.Int32)",
|
|
4519
|
+
"Collection(Edm.Int64)",
|
|
4520
|
+
"Collection(Edm.SByte)",
|
|
4521
|
+
"Collection(Edm.Single)",
|
|
4522
|
+
"Collection(Edm.String)",
|
|
4523
|
+
"Collection(Edm.TimeOfDay)"
|
|
4524
|
+
],
|
|
4525
|
+
"Type": "Edm.Untyped"
|
|
4526
|
+
}
|
|
4467
4527
|
}
|
|
4468
4528
|
},
|
|
4469
4529
|
"UI.DataFieldAbstract": {
|
package/lib/json/from-csn.js
CHANGED
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
|
|
118
118
|
const { dictAdd } = require('../compiler/dictionaries');
|
|
119
119
|
const { quotedLiteralPatterns } = require('../compiler/builtins');
|
|
120
|
-
const {
|
|
120
|
+
const { exprProperties } = require('../base/builtins');
|
|
121
121
|
const { CompilerAssertion } = require('../base/error');
|
|
122
122
|
const { Location } = require('../base/location');
|
|
123
123
|
const { XsnSource } = require('../compiler/xsn-model');
|
|
@@ -141,11 +141,6 @@ const typeProperties = [
|
|
|
141
141
|
'type', 'length', 'precision', 'scale', 'srid', 'localized', 'notNull', 'default',
|
|
142
142
|
'keys', 'on', // only with 'target'
|
|
143
143
|
];
|
|
144
|
-
const exprProperties = [
|
|
145
|
-
// do not include CSN v0.1.0 properties here:
|
|
146
|
-
'ref', 'xpr', 'list', 'val', '#', 'func', 'SELECT', 'SET', // Core Compiler checks SELECT/SET
|
|
147
|
-
'param', 'literal', 'args', 'cast', // only with 'ref'/'ref'/'val'/'func'
|
|
148
|
-
];
|
|
149
144
|
|
|
150
145
|
// Groups of properties which cannot be used together:
|
|
151
146
|
const xorGroups = {
|
|
@@ -619,16 +614,16 @@ const schema = compileSchema( {
|
|
|
619
614
|
optional: exprProperties,
|
|
620
615
|
},
|
|
621
616
|
where: {
|
|
622
|
-
class: 'condition',
|
|
617
|
+
class: 'condition', inKind: [ 'extend' ],
|
|
623
618
|
},
|
|
624
619
|
groupBy: {
|
|
625
|
-
arrayOf: expr, optional: exprProperties,
|
|
620
|
+
arrayOf: expr, optional: exprProperties, inKind: [ 'extend' ],
|
|
626
621
|
},
|
|
627
622
|
having: {
|
|
628
|
-
class: 'condition',
|
|
623
|
+
class: 'condition', inKind: [ 'extend' ],
|
|
629
624
|
},
|
|
630
625
|
orderBy: {
|
|
631
|
-
arrayOf: expr, optional: [ 'sort', 'nulls', ...exprProperties ],
|
|
626
|
+
arrayOf: expr, optional: [ 'sort', 'nulls', ...exprProperties ], inKind: [ 'extend' ],
|
|
632
627
|
},
|
|
633
628
|
sort: {
|
|
634
629
|
type: stringVal,
|
|
@@ -637,7 +632,7 @@ const schema = compileSchema( {
|
|
|
637
632
|
type: stringVal, // TODO: test for valid ones?
|
|
638
633
|
},
|
|
639
634
|
limit: {
|
|
640
|
-
type: object, requires: 'rows', optional: [ 'rows', 'offset' ],
|
|
635
|
+
type: object, requires: 'rows', optional: [ 'rows', 'offset' ], inKind: [ 'extend' ],
|
|
641
636
|
},
|
|
642
637
|
rows: {
|
|
643
638
|
class: 'expression',
|
|
@@ -1467,7 +1462,7 @@ function annoValue( val, spec ) {
|
|
|
1467
1462
|
++virtualLine;
|
|
1468
1463
|
return r;
|
|
1469
1464
|
}
|
|
1470
|
-
else if (
|
|
1465
|
+
else if (exprProperties.some( prop => val[prop] !== undefined )) {
|
|
1471
1466
|
const s = schema['@'].schema['-expr'];
|
|
1472
1467
|
const r = { location: location() };
|
|
1473
1468
|
Object.assign( r, object( val, s ) );
|
package/lib/json/to-csn.js
CHANGED
|
@@ -234,11 +234,11 @@ function compactModel( model, options = model.options || {} ) {
|
|
|
234
234
|
}
|
|
235
235
|
// 'namespace' for complete model is 'namespace' of first source
|
|
236
236
|
// (not a really useful property at all, avoids XSN inspection by Umbrella)
|
|
237
|
-
|
|
237
|
+
const first = Object.keys( srcDict )[0];
|
|
238
|
+
if (first) {
|
|
238
239
|
const { namespace } = srcDict[first];
|
|
239
240
|
if (namespace?.name?.path)
|
|
240
241
|
csn.namespace = pathName( namespace.name.path );
|
|
241
|
-
break;
|
|
242
242
|
}
|
|
243
243
|
set( 'definitions', csn, model );
|
|
244
244
|
if (Object.keys(model.vocabularies || {}).length > 0)
|
|
@@ -249,15 +249,16 @@ function compactModel( model, options = model.options || {} ) {
|
|
|
249
249
|
set( 'i18n', csn, model );
|
|
250
250
|
set( 'sources', csn, model );
|
|
251
251
|
// Set $location, use $extra properties of first source as resulting $extra properties
|
|
252
|
-
|
|
253
|
-
|
|
252
|
+
const firstSourceName = Object.keys( srcDict )[0];
|
|
253
|
+
if (firstSourceName) {
|
|
254
|
+
const firstSource = srcDict[firstSourceName];
|
|
255
|
+
const loc = firstSource.location;
|
|
254
256
|
if (loc && loc.file) {
|
|
255
257
|
Object.defineProperty( csn, '$location', {
|
|
256
258
|
value: { file: loc.file }, configurable: true, writable: true, enumerable: withLocations,
|
|
257
259
|
} );
|
|
258
260
|
}
|
|
259
|
-
set( '$extra', csn,
|
|
260
|
-
break;
|
|
261
|
+
set( '$extra', csn, firstSource );
|
|
261
262
|
}
|
|
262
263
|
|
|
263
264
|
const srcNames = Object.keys( srcDict );
|
|
@@ -273,7 +274,8 @@ function compactModel( model, options = model.options || {} ) {
|
|
|
273
274
|
const compilerCsnFlavor = flavorMap[options.csnFlavor] ||
|
|
274
275
|
(options.parseCdl ? 'parsed' : 'inferred');
|
|
275
276
|
if (!options.testMode) {
|
|
276
|
-
csn.meta = Object.assign( {}, model.meta, meta,
|
|
277
|
+
csn.meta = Object.assign( {}, model.meta, meta,
|
|
278
|
+
{ creator, compilerVersion, compilerCsnFlavor } );
|
|
277
279
|
csn.$version = csnVersion;
|
|
278
280
|
}
|
|
279
281
|
else if (meta !== undefined) {
|
|
@@ -322,16 +324,11 @@ function usings( srcDict ) {
|
|
|
322
324
|
function extensions( node, csn, model ) {
|
|
323
325
|
if (model.kind && model.kind !== 'source')
|
|
324
326
|
return undefined;
|
|
325
|
-
|
|
326
|
-
if (gensrcFlavor) {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const result = { annotate: Object.create(null) };
|
|
331
|
-
attachAnnotations( result, 'annotate', { [name]: art }, art.$inferred );
|
|
332
|
-
if (result.annotate[name])
|
|
333
|
-
exts.push( { annotate: name, ...result.annotate[name] } );
|
|
334
|
-
}
|
|
327
|
+
let exts = node.map( definition );
|
|
328
|
+
if (gensrcFlavor && model.definitions) {
|
|
329
|
+
const gensrc = { annotate: Object.create( null ), extend: Object.create( null ) };
|
|
330
|
+
attachAnnotations( gensrc, null, model.definitions, null );
|
|
331
|
+
exts = exts.concat( Object.values( gensrc.annotate ), Object.values( gensrc.extend ) );
|
|
335
332
|
}
|
|
336
333
|
return exts.sort( // TODO: really sort with parse.cdl?
|
|
337
334
|
(a, b) => (a.annotate || a.extend).localeCompare( b.annotate || b.extend )
|
|
@@ -378,36 +375,64 @@ function sources( srcDict, csn, model ) {
|
|
|
378
375
|
}
|
|
379
376
|
}
|
|
380
377
|
|
|
381
|
-
|
|
382
|
-
|
|
378
|
+
// create annotate and extend statements for CSN flavor gensrc
|
|
379
|
+
// TODO: rename this function - also refactor
|
|
380
|
+
function attachAnnotations( annotate, prop, dict, inferred, withExtend, insideReturns = false ) {
|
|
381
|
+
const annoDict = prop && Object.create( dictionaryPrototype );
|
|
383
382
|
const names = Object.keys( dict );
|
|
384
383
|
if (strictMode)
|
|
385
384
|
names.sort();
|
|
386
385
|
for (const name of names) {
|
|
387
386
|
const entry = dict[name];
|
|
388
387
|
const inf = inferred || entry.$inferred; // is probably always inferred if parent was
|
|
389
|
-
const
|
|
390
|
-
|
|
388
|
+
const withExt = withExtend || entry.$expand === 'extend';
|
|
389
|
+
const extProp = (entry.$expand === 'extend') ? 'extend' : 'annotate';
|
|
390
|
+
if (withExt && !entry.$inferred && entry.kind === 'enum') {
|
|
391
|
+
// currently just added enum - test for safety
|
|
392
|
+
if (dict[$inferred])
|
|
393
|
+
annoDict[name] = definition( entry );
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
let sub = {};
|
|
397
|
+
if (inf)
|
|
398
|
+
annotationsAndDocComment( entry, sub );
|
|
399
|
+
|
|
400
|
+
if (entry.$expand === 'annotate' || entry.$expand === 'extend') {
|
|
391
401
|
if (entry.actions)
|
|
392
402
|
attachAnnotations( sub, 'actions', entry.actions, inf );
|
|
393
403
|
else if (entry.params)
|
|
394
404
|
attachAnnotations( sub, 'params', entry.params, inf );
|
|
405
|
+
|
|
406
|
+
if (entry.$expand === 'extend' && (sub.actions || sub.params)) {
|
|
407
|
+
// there is only `annotate` with actions/params
|
|
408
|
+
annotate.annotate[name] = { annotate: name, ...sub };
|
|
409
|
+
sub = {};
|
|
410
|
+
}
|
|
411
|
+
|
|
395
412
|
const obj = entry.returns || entry;
|
|
396
|
-
const many = obj.items || obj;
|
|
413
|
+
const many = obj.items || obj; // TODO: many many?
|
|
397
414
|
const elems = (many.targetAspect || many).elements;
|
|
398
|
-
if (elems)
|
|
399
|
-
attachAnnotations( sub, 'elements', elems, inf, entry.returns );
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
415
|
+
if (elems) {
|
|
416
|
+
attachAnnotations( sub, 'elements', elems, inf, withExt, entry.returns );
|
|
417
|
+
}
|
|
418
|
+
else if (many.enum) { // make 'enum' annotations appear in 'elements' annotate
|
|
419
|
+
attachAnnotations( sub, (withExt ? 'enum' : 'elements'),
|
|
420
|
+
many.enum, inf, withExt, entry.returns );
|
|
421
|
+
}
|
|
422
|
+
else if (entry.foreignKeys) { // make 'foreignKeys' annotations appear in 'elements' annotate
|
|
423
|
+
attachAnnotations( sub, 'elements', entry.foreignKeys, inf, withExt );
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (Object.keys( sub ).length > 0) {
|
|
427
|
+
if (prop)
|
|
428
|
+
annoDict[name] = (withExt) ? { kind: 'extend', ...sub } : sub;
|
|
429
|
+
else
|
|
430
|
+
annotate[extProp][name] = { [extProp]: name, ...sub };
|
|
404
431
|
}
|
|
405
|
-
if (Object.keys( sub ).length)
|
|
406
|
-
annoDict[name] = sub;
|
|
407
432
|
}
|
|
408
|
-
if (Object.keys( annoDict ).length) {
|
|
433
|
+
if (prop && Object.keys( annoDict ).length) {
|
|
409
434
|
if (insideReturns)
|
|
410
|
-
annotate.returns = {
|
|
435
|
+
annotate.returns = { [prop]: annoDict };
|
|
411
436
|
else
|
|
412
437
|
annotate[prop] = annoDict;
|
|
413
438
|
}
|
|
@@ -524,7 +549,7 @@ function enumerableQueryElements( select ) {
|
|
|
524
549
|
return (universalCsn && select !== select._main._leadingQuery);
|
|
525
550
|
}
|
|
526
551
|
|
|
527
|
-
// Should we render the elements? (and items?)
|
|
552
|
+
// Should we render the elements? (and enum, items?)
|
|
528
553
|
function keepElements( node, line ) {
|
|
529
554
|
if (universalCsn)
|
|
530
555
|
// $expand = null/undefined: not elements not via expansion
|
|
@@ -562,8 +587,7 @@ function keepElements( node, line ) {
|
|
|
562
587
|
*
|
|
563
588
|
* @param {object} node
|
|
564
589
|
*/
|
|
565
|
-
function annotationsAndDocComment( node ) {
|
|
566
|
-
const csn = {};
|
|
590
|
+
function annotationsAndDocComment( node, csn = {} ) {
|
|
567
591
|
const transformer = transformers['@'];
|
|
568
592
|
const keys = Object.keys( node ).filter( a => a.charAt(0) === '@' ).sort();
|
|
569
593
|
for (const prop of keys) {
|
|
@@ -8,6 +8,7 @@ const { functionsWithoutParentheses } = require('./identifiers');
|
|
|
8
8
|
|
|
9
9
|
const { pathName } = require('../compiler/utils');
|
|
10
10
|
const { quotedLiteralPatterns, specialFunctions } = require('../compiler/builtins');
|
|
11
|
+
const { primaryExprProperties } = require('../base/builtins');
|
|
11
12
|
|
|
12
13
|
const { parseMultiLineStringLiteral } = require('../language/multiLineStringParser');
|
|
13
14
|
const { normalizeNewLine, normalizeNumberString } = require('../language/textUtils');
|
|
@@ -100,11 +101,6 @@ const extensionsCode = {
|
|
|
100
101
|
const PRECEDENCE_OF_EQUAL = 10;
|
|
101
102
|
|
|
102
103
|
class AstBuildingParser extends BaseParser {
|
|
103
|
-
leanConditions = {
|
|
104
|
-
afterBrace: true,
|
|
105
|
-
fail: true,
|
|
106
|
-
};
|
|
107
|
-
|
|
108
104
|
constructor( lexer, keywords, table, options, messageFunctions ) {
|
|
109
105
|
super( lexer, keywords, table ); // lexer has file
|
|
110
106
|
this.options = options;
|
|
@@ -201,7 +197,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
201
197
|
queryOnLeftSloppyAlias( _arg, mode ) {
|
|
202
198
|
if (mode === 'M' || this.isNoKeywordInRuleFollow( _arg, mode ))
|
|
203
199
|
return true;
|
|
204
|
-
// TODO TOOL: have a base parser method for the test
|
|
200
|
+
// TODO TOOL: have a base parser method for the test - or extra mode (recovery)
|
|
205
201
|
if (this.conditionTokenIdx === this.tokenIdx && // tested on same
|
|
206
202
|
this.conditionStackLength == null) // after error recovery
|
|
207
203
|
return false;
|
|
@@ -238,7 +234,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
238
234
|
}
|
|
239
235
|
|
|
240
236
|
prepareSpecialFunction() {
|
|
241
|
-
const func = this.
|
|
237
|
+
const func = this.lb(2).keyword?.toUpperCase();
|
|
242
238
|
// TODO: use lower-case in specialFunctions
|
|
243
239
|
const spec = specialFunctions[func];
|
|
244
240
|
this.dynamic_.call = { func, argPos: 0 };
|
|
@@ -261,7 +257,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
261
257
|
if (generic !== 'expr')
|
|
262
258
|
return (generic === 'intro') ? 'GenericIntro' : type;
|
|
263
259
|
// if both intro and expr: specialFunctions[fn][argPos][token] = 'expr'
|
|
264
|
-
const next = this.
|
|
260
|
+
const next = this.lb(-1);
|
|
265
261
|
if (next && next.type !== ',' && next.type !== ')' &&
|
|
266
262
|
this.dynamic_.generic[next.keyword?.toUpperCase?.()] !== 'separator')
|
|
267
263
|
return 'GenericIntro';
|
|
@@ -309,8 +305,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
309
305
|
this.dynamic_.inSelectItem ??= [];
|
|
310
306
|
this.dynamic_.inSelectItem = [ ...this.dynamic_.inSelectItem ];
|
|
311
307
|
}
|
|
312
|
-
this.dynamic_.inSelectItem.push(arg ||
|
|
313
|
-
(this.tokens[this.tokenIdx - 2].type === '.' ? 'inline' : 'expand'));
|
|
308
|
+
this.dynamic_.inSelectItem.push(arg || (this.lb(2).type === '.' ? 'inline' : 'expand'));
|
|
314
309
|
}
|
|
315
310
|
|
|
316
311
|
/**
|
|
@@ -321,7 +316,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
321
316
|
modifierRestriction() {
|
|
322
317
|
const top = this.dynamic_.inSelectItem?.at(-1);
|
|
323
318
|
const insideExpand = this.dynamic_.inSelectItem?.includes('expand');
|
|
324
|
-
const next = this.
|
|
319
|
+
const next = this.la()?.keyword;
|
|
325
320
|
return insideExpand || (next !== 'key' && top === 'inline');
|
|
326
321
|
}
|
|
327
322
|
modifierRestrictionError( args, offending ) {
|
|
@@ -340,7 +335,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
340
335
|
return false;
|
|
341
336
|
// TODO: it would be best to set this.dynamic_.inSelectItem to null in filters
|
|
342
337
|
// (as <prepare>)
|
|
343
|
-
const next = this.
|
|
338
|
+
const next = this.lb(-1)?.type;
|
|
344
339
|
return next === '*' || next === '{';
|
|
345
340
|
}
|
|
346
341
|
|
|
@@ -357,7 +352,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
357
352
|
// <prec=10, postfix=once> + test that the next token is not `null`; TODO: code
|
|
358
353
|
// completion for `… default 3 not ~;` → currently just `null` but hey
|
|
359
354
|
isNegatedRelation( prec ) {
|
|
360
|
-
return this.
|
|
355
|
+
return this.lb(-1)?.keyword === 'null' ||
|
|
361
356
|
this.precNone_( prec );
|
|
362
357
|
}
|
|
363
358
|
|
|
@@ -372,7 +367,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
372
367
|
}
|
|
373
368
|
|
|
374
369
|
isNamedArg() {
|
|
375
|
-
const type = this.
|
|
370
|
+
const type = this.lb(-1)?.type;
|
|
376
371
|
return type !== ':' && type !== '=>';
|
|
377
372
|
}
|
|
378
373
|
|
|
@@ -522,13 +517,13 @@ class AstBuildingParser extends BaseParser {
|
|
|
522
517
|
}
|
|
523
518
|
|
|
524
519
|
noRepeatedCardinality( _arg, mode ) {
|
|
525
|
-
if (this.
|
|
520
|
+
if (this.lb(2)?.type !== ']')
|
|
526
521
|
return false;
|
|
527
522
|
if (mode === 'M')
|
|
528
523
|
return true;
|
|
529
524
|
// currently just warning if same cardinality provided twice
|
|
530
525
|
const same = { one: '1', many: '*' }[this.la().keyword];
|
|
531
|
-
return this.
|
|
526
|
+
return this.lb(3)?.text !== same;
|
|
532
527
|
}
|
|
533
528
|
noRepeatedCardinalityError( args ) {
|
|
534
529
|
let openIdx = this.tokenIdx - 2;
|
|
@@ -606,7 +601,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
606
601
|
const { arrayAnno } = this.dynamic_;
|
|
607
602
|
if (!arrayAnno[0])
|
|
608
603
|
return arrayAnno[0] == null ? 'duplicate' : arg;
|
|
609
|
-
arrayAnno[0] = this.
|
|
604
|
+
arrayAnno[0] = this.lb(-1)?.keyword;
|
|
610
605
|
}
|
|
611
606
|
else if (arg === 'bracket') { // syntax-invalid-ellipsis
|
|
612
607
|
// closing bracket not allowed if last `...` in array is with `up to
|
|
@@ -628,7 +623,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
628
623
|
}
|
|
629
624
|
|
|
630
625
|
beforeColon() {
|
|
631
|
-
return this.
|
|
626
|
+
return this.lb(-1)?.text !== ':';
|
|
632
627
|
}
|
|
633
628
|
|
|
634
629
|
fail( _arg, mode ) {
|
|
@@ -652,13 +647,13 @@ class AstBuildingParser extends BaseParser {
|
|
|
652
647
|
return;
|
|
653
648
|
// assumes no value < -1:
|
|
654
649
|
const location = (tokenAhead > 0)
|
|
655
|
-
? this.combineLocation( this.la(), this.
|
|
656
|
-
: this.
|
|
650
|
+
? this.combineLocation( this.la(), this.lb(-tokenAhead) )
|
|
651
|
+
: this.lb(-tokenAhead).location;
|
|
657
652
|
this.error( msgId, location, textArgs );
|
|
658
653
|
}
|
|
659
654
|
|
|
660
655
|
warnIfColonFollows( name ) {
|
|
661
|
-
if (this.
|
|
656
|
+
if (this.la().type === ':') {
|
|
662
657
|
this.warning( 'syntax-missing-parens', name,
|
|
663
658
|
{ code: '@‹anno›', op: ':', newcode: '@(‹anno›…)' },
|
|
664
659
|
// eslint-disable-next-line @stylistic/max-len
|
|
@@ -680,7 +675,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
680
675
|
|
|
681
676
|
reportDubiousAnnoSpacing() {
|
|
682
677
|
const at = this.lb();
|
|
683
|
-
const before = this.
|
|
678
|
+
const before = this.lb(2);
|
|
684
679
|
if (before?.type === 'Id' && before.location.endLine === at.location.line &&
|
|
685
680
|
before.location.endCol === at.location.col) {
|
|
686
681
|
this.warning( 'syntax-expecting-anno-space', at.location, { code: '@' },
|
|
@@ -888,7 +883,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
888
883
|
classifyImplicitName( category, ref ) {
|
|
889
884
|
if (!ref || ref.path) { // TODO: func
|
|
890
885
|
const tokenIndex = ref?.path.at(-1)?.location.tokenIndex;
|
|
891
|
-
const token = this.prevTokenWithIndex( tokenIndex ) ?? this.
|
|
886
|
+
const token = this.prevTokenWithIndex( tokenIndex ) ?? this.lb();
|
|
892
887
|
const { parsedAs } = token;
|
|
893
888
|
if (parsedAs && parsedAs !== 'token' && parsedAs !== 'keyword')
|
|
894
889
|
token.parsedAs = category;
|
|
@@ -1097,7 +1092,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
1097
1092
|
|
|
1098
1093
|
// TODO: can we remove `;`/EOF from the expected-set for `annotate Foo with ⎀`?
|
|
1099
1094
|
checkWith( keyword ) {
|
|
1100
|
-
if (this.lb() !== keyword || ![ ';', '}', 'EOF' ].includes( this.
|
|
1095
|
+
if (this.lb() !== keyword || ![ ';', '}', 'EOF' ].includes( this.la().type ))
|
|
1101
1096
|
return;
|
|
1102
1097
|
const tok = this.la();
|
|
1103
1098
|
const docTokenIndex = this.docCommentIndex &&
|
|
@@ -1152,15 +1147,15 @@ class AstBuildingParser extends BaseParser {
|
|
|
1152
1147
|
}
|
|
1153
1148
|
|
|
1154
1149
|
// see also <guard=nestedExpand>
|
|
1155
|
-
reportExpandInline( column, isInline ) {
|
|
1156
|
-
// called before matching `{`
|
|
1150
|
+
reportExpandInline( column, sqlStyle, isInline ) {
|
|
1151
|
+
// called before matching `{` (or `*` with inline)
|
|
1157
1152
|
if (column.value && !column.value.path) {
|
|
1158
1153
|
// improve error location when using "inline" `.{…}` after ref (arguments and
|
|
1159
1154
|
// filters not covered, not worth the effort); after an expression where
|
|
1160
1155
|
// the last token is an identifier, not the `.` is wrong, but the `{`:
|
|
1161
|
-
const token = (isInline && this.
|
|
1162
|
-
? this.lb()
|
|
1163
|
-
: this.la();
|
|
1156
|
+
const token = (isInline && this.lb(2)?.type !== 'Id')
|
|
1157
|
+
? this.lb() // e.g. with `func() .{` → the `.` is wrong
|
|
1158
|
+
: this.la(); // after `current_date` → the `{` is wrong
|
|
1164
1159
|
this.error( 'syntax-unexpected-nested-proj', token,
|
|
1165
1160
|
{ code: isInline ? '.{ ‹inline› }' : '{ ‹expand› }' },
|
|
1166
1161
|
'Unexpected $(CODE); nested projections can only be used after a reference' );
|
|
@@ -1169,6 +1164,15 @@ class AstBuildingParser extends BaseParser {
|
|
|
1169
1164
|
// - no errors for refs inside expand/inline, but for refs in sibling expr
|
|
1170
1165
|
// - think about: reference to these (sub) elements from other view
|
|
1171
1166
|
}
|
|
1167
|
+
else if (sqlStyle && this.la().text === '{') {
|
|
1168
|
+
this.message( 'syntax-invalid-nested-proj', this.la(),
|
|
1169
|
+
{
|
|
1170
|
+
code: isInline ? '.{ ‹inline› }' : '{ ‹expand› }',
|
|
1171
|
+
keyword: 'select from … { … }',
|
|
1172
|
+
},
|
|
1173
|
+
// eslint-disable-next-line @stylistic/max-len
|
|
1174
|
+
'Unexpected $(CODE); nested projections can only be used within a CDL-style $(KEYWORD)' );
|
|
1175
|
+
}
|
|
1172
1176
|
}
|
|
1173
1177
|
|
|
1174
1178
|
reportDuplicateClause( prop, erroneous, chosen, code ) {
|
|
@@ -1198,6 +1202,15 @@ class AstBuildingParser extends BaseParser {
|
|
|
1198
1202
|
}
|
|
1199
1203
|
}
|
|
1200
1204
|
|
|
1205
|
+
checkStructProps( dict ) {
|
|
1206
|
+
const special = primaryExprProperties.find( prop => dict[prop] !== undefined );
|
|
1207
|
+
if (special || dict['=']) {
|
|
1208
|
+
const prop = special || '=';
|
|
1209
|
+
this.message( 'syntax-invalid-anno-struct', dict[prop].name.location,
|
|
1210
|
+
{ '#': (special ? 'std' : 'ref'), prop } );
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1201
1214
|
// TODO: remove the check from the parser; move it to shared.js
|
|
1202
1215
|
checkTypeArgs( art ) {
|
|
1203
1216
|
const args = art.$typeArgs;
|
|
@@ -1212,7 +1225,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
1212
1225
|
locationOfPrevTokens( offset ) {
|
|
1213
1226
|
// TODO: use combined location of lb() and la() and move actions accordingly
|
|
1214
1227
|
// (for error recovery)
|
|
1215
|
-
const { file, line, col } = this.
|
|
1228
|
+
const { file, line, col } = this.lb( offset ).location;
|
|
1216
1229
|
const { endLine, endCol } = this.lb().location;
|
|
1217
1230
|
return {
|
|
1218
1231
|
file,
|