@sap/cds-compiler 2.5.0 → 2.10.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 +191 -9
- package/bin/cdsc.js +2 -2
- package/doc/CHANGELOG_BETA.md +33 -3
- package/lib/api/main.js +29 -101
- package/lib/api/options.js +15 -11
- package/lib/api/validate.js +12 -8
- package/lib/backends.js +0 -81
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +63 -9
- package/lib/base/messages.js +63 -21
- package/lib/base/model.js +2 -3
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/foreignKeys.js +0 -6
- package/lib/checks/managedWithoutKeys.js +17 -0
- package/lib/checks/nonexpandableStructured.js +38 -0
- package/lib/checks/onConditions.js +9 -45
- package/lib/checks/queryNoDbArtifacts.js +25 -7
- package/lib/checks/selectItems.js +25 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +38 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +16 -7
- package/lib/compiler/builtins.js +2 -0
- package/lib/compiler/checks.js +6 -4
- package/lib/compiler/definer.js +99 -42
- package/lib/compiler/index.js +73 -27
- package/lib/compiler/resolver.js +288 -157
- package/lib/compiler/shared.js +31 -11
- package/lib/edm/annotations/genericTranslation.js +182 -186
- package/lib/edm/csn2edm.js +103 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +361 -114
- package/lib/edm/edmUtils.js +103 -33
- package/lib/gen/Dictionary.json +22 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +12 -1
- package/lib/gen/language.tokens +57 -53
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +770 -744
- package/lib/gen/languageLexer.tokens +49 -46
- package/lib/gen/languageParser.js +4713 -4279
- package/lib/json/from-csn.js +103 -45
- package/lib/json/to-csn.js +296 -117
- package/lib/language/antlrParser.js +4 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +21 -12
- package/lib/language/language.g4 +99 -31
- package/lib/main.d.ts +81 -3
- package/lib/main.js +30 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +329 -142
- package/lib/model/csnUtils.js +235 -58
- package/lib/model/enrichCsn.js +18 -1
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/modelCompare/compare.js +37 -20
- package/lib/optionProcessor.js +9 -3
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +8 -5
- package/lib/render/toCdl.js +112 -33
- package/lib/render/toHdbcds.js +134 -64
- package/lib/render/toSql.js +91 -38
- package/lib/render/utils/common.js +8 -13
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/assertUnique.js +5 -6
- package/lib/transform/db/constraints.js +29 -13
- package/lib/transform/db/draft.js +8 -6
- package/lib/transform/db/expansion.js +582 -0
- package/lib/transform/db/flattening.js +325 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +284 -63
- package/lib/transform/forHanaNew.js +98 -381
- package/lib/transform/forOdataNew.js +21 -22
- package/lib/transform/localized.js +37 -10
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +60 -39
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +19 -18
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +134 -78
- package/lib/transform/translateAssocsToJoins.js +17 -14
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +0 -11
- package/lib/utils/moduleResolve.js +6 -8
- package/package.json +1 -1
- package/lib/json/walker.js +0 -26
- package/lib/transform/sqlite +0 -0
- package/lib/utils/string.js +0 -17
package/lib/compiler/definer.js
CHANGED
|
@@ -107,9 +107,7 @@
|
|
|
107
107
|
|
|
108
108
|
'use strict';
|
|
109
109
|
|
|
110
|
-
const {
|
|
111
|
-
makeMessageFunction, searchName, weakLocation,
|
|
112
|
-
} = require('../base/messages');
|
|
110
|
+
const { searchName, weakLocation } = require('../base/messages');
|
|
113
111
|
const {
|
|
114
112
|
isDeprecatedEnabled, isBetaEnabled,
|
|
115
113
|
setProp, forEachGeneric, forEachInOrder,
|
|
@@ -155,7 +153,7 @@ function getDefinerFunctions( model ) {
|
|
|
155
153
|
// Get simplified "resolve" functionality and the message function:
|
|
156
154
|
const {
|
|
157
155
|
message, error, warning, info, messages,
|
|
158
|
-
} =
|
|
156
|
+
} = model.$messageFunctions;
|
|
159
157
|
const {
|
|
160
158
|
resolveUncheckedPath,
|
|
161
159
|
resolvePath,
|
|
@@ -206,7 +204,7 @@ function getDefinerFunctions( model ) {
|
|
|
206
204
|
|
|
207
205
|
mergeI18nBlocks( model );
|
|
208
206
|
|
|
209
|
-
if (
|
|
207
|
+
if (options.parseCdl) {
|
|
210
208
|
initExtensionsWithoutApplying();
|
|
211
209
|
// Check for redefinitions
|
|
212
210
|
Object.keys( model.definitions ).forEach( preProcessArtifact );
|
|
@@ -218,7 +216,8 @@ function getDefinerFunctions( model ) {
|
|
|
218
216
|
applyExtensions();
|
|
219
217
|
|
|
220
218
|
Object.keys( model.definitions ).forEach( preProcessArtifact );
|
|
221
|
-
const commonLanguagesEntity
|
|
219
|
+
const commonLanguagesEntity // TODO: remove beta after a grace period
|
|
220
|
+
= (options.addTextsLanguageAssoc || isBetaEnabled( options, 'addTextsLanguageAssoc' )) &&
|
|
222
221
|
model.definitions['sap.common.Languages'];
|
|
223
222
|
addTextsLanguageAssoc = !!(commonLanguagesEntity && commonLanguagesEntity.elements &&
|
|
224
223
|
commonLanguagesEntity.elements.code);
|
|
@@ -494,7 +493,7 @@ function getDefinerFunctions( model ) {
|
|
|
494
493
|
initDollarSelf( art ); // $self
|
|
495
494
|
if (art.params)
|
|
496
495
|
initParams( art ); // $parameters
|
|
497
|
-
if (art.includes && !(art.name.absolute in extensionsDict))
|
|
496
|
+
if (art.includes && !(art.name.absolute in extensionsDict)) // TODO: in next phase?
|
|
498
497
|
extensionsDict[art.name.absolute] = []; // structure with includes must be "extended"
|
|
499
498
|
|
|
500
499
|
if (!art.query)
|
|
@@ -609,14 +608,7 @@ function getDefinerFunctions( model ) {
|
|
|
609
608
|
if (query.on)
|
|
610
609
|
initExprForQuery( query.on, query );
|
|
611
610
|
// TODO: MIXIN with name = ...subquery (not yet supported anyway)
|
|
612
|
-
|
|
613
|
-
if (elem && (elem.value || elem.expand)) {
|
|
614
|
-
setProp( elem, '_block', query._block );
|
|
615
|
-
defineAnnotations( elem, elem, query._block );
|
|
616
|
-
initExprForQuery( elem.value, query );
|
|
617
|
-
initExpandInline( elem );
|
|
618
|
-
}
|
|
619
|
-
}
|
|
611
|
+
initSelectItems( query, query.columns );
|
|
620
612
|
if (query.where)
|
|
621
613
|
initExprForQuery( query.where, query );
|
|
622
614
|
if (query.having)
|
|
@@ -624,22 +616,41 @@ function getDefinerFunctions( model ) {
|
|
|
624
616
|
initMembers( query, query, query._block );
|
|
625
617
|
}
|
|
626
618
|
|
|
627
|
-
function
|
|
628
|
-
// TODO: forbid with :param, global:true, in ref-where, outside queries (CSN), ...
|
|
629
|
-
|
|
630
|
-
|
|
619
|
+
function initSelectItems( parent, columns ) {
|
|
620
|
+
// TODO: forbid expand/inline with :param, global:true, in ref-where, outside queries (CSN), ...
|
|
621
|
+
let wildcard = null;
|
|
622
|
+
for (const col of columns || parent.expand || parent.inline || []) {
|
|
623
|
+
if (!col) // parse error
|
|
631
624
|
continue;
|
|
632
|
-
if (
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
625
|
+
if (!columns) {
|
|
626
|
+
if (parent.value)
|
|
627
|
+
setProp( col, '_pathHead', parent ); // also set for '*' in expand/inline
|
|
628
|
+
else if (parent._pathHead)
|
|
629
|
+
setProp( col, '_pathHead', parent._pathHead );
|
|
630
|
+
}
|
|
631
|
+
if (col.val === '*') {
|
|
632
|
+
if (!wildcard) {
|
|
633
|
+
wildcard = col;
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
// a late syntax error (this code also runs with parse-cdl), i.e.
|
|
637
|
+
// no semantic loc (wouldn't be available for expand/inline anyway)
|
|
638
|
+
error( 'syntax-duplicate-clause', [ col.location, null ],
|
|
639
|
+
{ prop: '*', line: wildcard.location.line, col: wildcard.location.col },
|
|
640
|
+
'You have provided a $(PROP) already at line $(LINE), column $(COL)' );
|
|
641
|
+
// TODO: extra text variants for expand/inline? - probably not
|
|
642
|
+
col.val = null; // do not consider it for expandWildcard()
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
else if (col.value || col.expand) {
|
|
646
|
+
setProp( col, '_block', parent._block );
|
|
647
|
+
defineAnnotations( col, col, parent._block ); // TODO: complain with inline
|
|
648
|
+
// TODO: allow sub queries? at least in top-level expand without parallel ref
|
|
649
|
+
if (columns)
|
|
650
|
+
initExprForQuery( col.value, parent );
|
|
651
|
+
initSelectItems( col );
|
|
640
652
|
}
|
|
641
653
|
}
|
|
642
|
-
// TODO: allow sub queries in top-level expand without parallel ref
|
|
643
654
|
}
|
|
644
655
|
|
|
645
656
|
function initExprForQuery( expr, query ) {
|
|
@@ -658,9 +669,38 @@ function getDefinerFunctions( model ) {
|
|
|
658
669
|
}
|
|
659
670
|
else if (expr.path && expr.$expected === 'exists') {
|
|
660
671
|
expr.$expected = 'approved-exists';
|
|
672
|
+
approveExistsInChildren(expr);
|
|
661
673
|
}
|
|
662
674
|
}
|
|
663
675
|
|
|
676
|
+
/**
|
|
677
|
+
* If we have a valid top-level exists, exists in filters of sub-expressions can be translated,
|
|
678
|
+
* since we will have a top-level subquery after exists-processing in the forHanaNew.
|
|
679
|
+
*
|
|
680
|
+
* Recursively drill down into:
|
|
681
|
+
* - the .path
|
|
682
|
+
* - the .args
|
|
683
|
+
* - the .where.args
|
|
684
|
+
*
|
|
685
|
+
* Any $expected === 'exists' encountered along the way are turned into 'approved-exists'
|
|
686
|
+
*
|
|
687
|
+
* working: exists toE[exists toE] -> select from E where exists toE
|
|
688
|
+
* not working: toE[exists toE] -> we don't support subqueries in filters
|
|
689
|
+
*
|
|
690
|
+
* @param {object} exprOrPathElement starts w/ an expr but then subelem from .path or .where.args
|
|
691
|
+
*/
|
|
692
|
+
function approveExistsInChildren(exprOrPathElement) {
|
|
693
|
+
if (exprOrPathElement.$expected === 'exists')
|
|
694
|
+
exprOrPathElement.$expected = 'approved-exists';
|
|
695
|
+
// Drill down
|
|
696
|
+
if (exprOrPathElement.args)
|
|
697
|
+
exprOrPathElement.args.forEach(elem => approveExistsInChildren(elem));
|
|
698
|
+
else if (exprOrPathElement.where && exprOrPathElement.where.args)
|
|
699
|
+
exprOrPathElement.where.args.forEach(elem => approveExistsInChildren(elem));
|
|
700
|
+
else if (exprOrPathElement.path)
|
|
701
|
+
exprOrPathElement.path.forEach(elem => approveExistsInChildren(elem));
|
|
702
|
+
}
|
|
703
|
+
|
|
664
704
|
// table is table expression in FROM, becomes an alias
|
|
665
705
|
function initTableExpression( table, query, joinParents ) {
|
|
666
706
|
if (!table) // parse error
|
|
@@ -708,6 +748,8 @@ function getDefinerFunctions( model ) {
|
|
|
708
748
|
// ? ta._joinParent.args[ta.$joinArgsIndex] // in JOIN
|
|
709
749
|
// : ta._parent.from ) // directly in FROM
|
|
710
750
|
// Note for --raw-output: _joinParent pointing to CROSS JOIN node has not name
|
|
751
|
+
if (!tab) // parse error; time for #6241
|
|
752
|
+
return; // (parser method to only add non-null to array)
|
|
711
753
|
setProp( tab, '_joinParent', table );
|
|
712
754
|
tab.$joinArgsIndex = index;
|
|
713
755
|
initTableExpression( tab, query, joinParents );
|
|
@@ -847,7 +889,7 @@ function getDefinerFunctions( model ) {
|
|
|
847
889
|
// TODO: error if CSN has both target.elements and targetAspect.elements -> delete target
|
|
848
890
|
return true;
|
|
849
891
|
}
|
|
850
|
-
if (elem.targetAspect || !isDirectComposition( elem ))
|
|
892
|
+
if (elem.targetAspect || options.parseCdl || !isDirectComposition( elem ))
|
|
851
893
|
return false;
|
|
852
894
|
const name = resolveUncheckedPath( target, 'compositionTarget', elem );
|
|
853
895
|
const aspect = name && model.definitions[name];
|
|
@@ -861,6 +903,7 @@ function getDefinerFunctions( model ) {
|
|
|
861
903
|
* (which is basically the component name of the `parent` element plus a dot).
|
|
862
904
|
*/
|
|
863
905
|
function initMembers( construct, parent, block, initExtensions = false ) {
|
|
906
|
+
// TODO: split extend from init
|
|
864
907
|
const isQueryExtension = kindProperties[construct.kind].isExtension &&
|
|
865
908
|
(parent._main || parent).query;
|
|
866
909
|
let obj = construct;
|
|
@@ -916,7 +959,7 @@ function getDefinerFunctions( model ) {
|
|
|
916
959
|
forEachInOrder( construct, 'params', init );
|
|
917
960
|
const { returns } = construct;
|
|
918
961
|
if (returns) {
|
|
919
|
-
returns.kind = 'param';
|
|
962
|
+
returns.kind = (kindProperties[construct.kind].isExtension) ? construct.kind : 'param';
|
|
920
963
|
init( returns, '' ); // '' is special name for returns parameter
|
|
921
964
|
}
|
|
922
965
|
return;
|
|
@@ -985,6 +1028,7 @@ function getDefinerFunctions( model ) {
|
|
|
985
1028
|
}
|
|
986
1029
|
|
|
987
1030
|
function checkDefinitions( construct, parent, prop, dict = construct[prop] ) {
|
|
1031
|
+
// TODO: do differently, see also annotateMembers() in resolver
|
|
988
1032
|
// To have been checked by parsers:
|
|
989
1033
|
// - artifacts (CDL-only anyway) only inside [extend] context|service
|
|
990
1034
|
if (!dict)
|
|
@@ -1269,7 +1313,7 @@ function getDefinerFunctions( model ) {
|
|
|
1269
1313
|
}
|
|
1270
1314
|
|
|
1271
1315
|
function createTargetEntity( target, elem, keys, entityName, base ) {
|
|
1272
|
-
const location = elem.
|
|
1316
|
+
const { location } = elem.targetAspect || elem.target || elem;
|
|
1273
1317
|
elem.on = {
|
|
1274
1318
|
location,
|
|
1275
1319
|
op: { val: '=', location },
|
|
@@ -1314,7 +1358,7 @@ function getDefinerFunctions( model ) {
|
|
|
1314
1358
|
// If 'up_' shall be rendered unmanaged, infer the parent
|
|
1315
1359
|
// primary keys and add the ON condition
|
|
1316
1360
|
if (isDeprecatedEnabled( options, 'unmanagedUpInComponent' )) {
|
|
1317
|
-
addProxyElements( art, keys, 'aspect-composition', location,
|
|
1361
|
+
addProxyElements( art, keys, 'aspect-composition', target.name && location,
|
|
1318
1362
|
'up__', '@odata.containment.ignore' );
|
|
1319
1363
|
up.on = augmentEqual( location, 'up_', Object.values( keys ), 'up__' );
|
|
1320
1364
|
}
|
|
@@ -1328,7 +1372,7 @@ function getDefinerFunctions( model ) {
|
|
|
1328
1372
|
setLink( art, '_base', base._base || base );
|
|
1329
1373
|
|
|
1330
1374
|
dictAdd( art.elements, 'up_', up);
|
|
1331
|
-
addProxyElements( art, target.elements, 'aspect-composition', location );
|
|
1375
|
+
addProxyElements( art, target.elements, 'aspect-composition', target.name && location );
|
|
1332
1376
|
|
|
1333
1377
|
setLink( art, '_block', model.$internal );
|
|
1334
1378
|
model.definitions[entityName] = art;
|
|
@@ -1375,7 +1419,7 @@ function getDefinerFunctions( model ) {
|
|
|
1375
1419
|
const exts = model.$lateExtensions[name];
|
|
1376
1420
|
if (art && art.kind !== 'namespace') {
|
|
1377
1421
|
if (art.builtin) {
|
|
1378
|
-
for (const ext
|
|
1422
|
+
for (const ext of exts)
|
|
1379
1423
|
info( 'anno-builtin', [ ext.name.location, ext ] );
|
|
1380
1424
|
}
|
|
1381
1425
|
// created texts entity, autoexposed entity
|
|
@@ -1423,6 +1467,12 @@ function getDefinerFunctions( model ) {
|
|
|
1423
1467
|
|
|
1424
1468
|
model.extensions.push(annotationArtifact);
|
|
1425
1469
|
extendArtifact( exts, annotationArtifact ); // also sets _artifact link in extensions
|
|
1470
|
+
// if one of the annotate statement mentions 'returns', assume it
|
|
1471
|
+
// TODO: with warning/info?
|
|
1472
|
+
for (const ext of exts) {
|
|
1473
|
+
if (ext.$syntax === 'returns')
|
|
1474
|
+
annotationArtifact.$syntax = 'returns';
|
|
1475
|
+
}
|
|
1426
1476
|
}
|
|
1427
1477
|
}
|
|
1428
1478
|
}
|
|
@@ -1706,6 +1756,7 @@ function getDefinerFunctions( model ) {
|
|
|
1706
1756
|
}
|
|
1707
1757
|
|
|
1708
1758
|
function extendMembers( extensions, art, noExtend ) {
|
|
1759
|
+
// TODO: do the whole extension stuff lazily if the elements are requested
|
|
1709
1760
|
const elemExtensions = [];
|
|
1710
1761
|
extensions.sort( compareLayer );
|
|
1711
1762
|
for (const ext of extensions) {
|
|
@@ -1751,7 +1802,12 @@ function getDefinerFunctions( model ) {
|
|
|
1751
1802
|
[ 'elements', 'actions' ].forEach( (prop) => {
|
|
1752
1803
|
const dict = art._extend && art._extend[prop];
|
|
1753
1804
|
for (const name in dict) {
|
|
1754
|
-
|
|
1805
|
+
let obj = art;
|
|
1806
|
+
if (obj.targetAspect)
|
|
1807
|
+
obj = obj.targetAspect;
|
|
1808
|
+
while (obj.items)
|
|
1809
|
+
obj = obj.items;
|
|
1810
|
+
const validDict = obj[prop] || prop === 'elements' && obj.enum;
|
|
1755
1811
|
const member = validDict[name];
|
|
1756
1812
|
if (!member)
|
|
1757
1813
|
extendNothing( dict[name], prop, name, art, validDict );
|
|
@@ -1989,8 +2045,9 @@ function getDefinerFunctions( model ) {
|
|
|
1989
2045
|
}
|
|
1990
2046
|
|
|
1991
2047
|
if (isKey && isLocalized) { // key with localized is wrong - ignore localized
|
|
1992
|
-
|
|
1993
|
-
|
|
2048
|
+
const errpos = elem.localized || elem.type || elem.name;
|
|
2049
|
+
warning( 'localized-key', [ errpos.location, elem ], { keyword: 'localized' },
|
|
2050
|
+
'Keyword $(KEYWORD) is ignored for primary keys' );
|
|
1994
2051
|
}
|
|
1995
2052
|
}
|
|
1996
2053
|
if (textElems.length <= keys)
|
|
@@ -2132,9 +2189,9 @@ function getDefinerFunctions( model ) {
|
|
|
2132
2189
|
});
|
|
2133
2190
|
}
|
|
2134
2191
|
}
|
|
2135
|
-
|
|
2192
|
+
if (hasTruthyProp( orig, 'localized' )) { // use location of LOCALIZED keyword
|
|
2136
2193
|
const localized = orig.localized || orig.type || orig.name;
|
|
2137
|
-
elem.localized = { val:
|
|
2194
|
+
elem.localized = { val: null, $inferred: 'localized', location: localized.location };
|
|
2138
2195
|
}
|
|
2139
2196
|
}
|
|
2140
2197
|
if (fioriEnabled)
|
|
@@ -2250,9 +2307,9 @@ function mergeI18nBlocks( model ) {
|
|
|
2250
2307
|
model.i18n[langKey][textKey] = sourceVal;
|
|
2251
2308
|
}
|
|
2252
2309
|
else if (modelVal.val !== sourceVal.val) {
|
|
2253
|
-
|
|
2254
|
-
warning('i18n-different-value',
|
|
2255
|
-
|
|
2310
|
+
// TODO: put mergeI18nBlocks() into main function instead
|
|
2311
|
+
model.$messageFunctions.warning( 'i18n-different-value', sourceVal.location,
|
|
2312
|
+
{ prop: textKey, otherprop: langKey } );
|
|
2256
2313
|
}
|
|
2257
2314
|
}
|
|
2258
2315
|
}
|
package/lib/compiler/index.js
CHANGED
|
@@ -15,7 +15,7 @@ const check = require('./checks');
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
const { emptyWeakLocation } = require('../base/location');
|
|
18
|
-
const {
|
|
18
|
+
const { createMessageFunctions, deduplicateMessages } = require('../base/messages');
|
|
19
19
|
const { promiseAllDoNotRejectImmediately } = require('../base/node-helpers');
|
|
20
20
|
const { cdsFs } = require('../utils/file');
|
|
21
21
|
|
|
@@ -50,25 +50,27 @@ class ArgumentError extends Error {
|
|
|
50
50
|
* @param {string} source Source code of the file.
|
|
51
51
|
* @param {string} filename Filename including its extension, e.g. "file.cds"
|
|
52
52
|
* @param {object} options Compile options
|
|
53
|
+
* @param {object} messageFunctions If not provided, parse errors will not lead to an exception
|
|
53
54
|
*/
|
|
54
|
-
function parseX( source, filename, options = {} ) {
|
|
55
|
+
function parseX( source, filename, options = {}, messageFunctions ) {
|
|
56
|
+
if (!messageFunctions)
|
|
57
|
+
messageFunctions = createMessageFunctions( options, 'parse' );
|
|
55
58
|
const ext = path.extname( filename ).toLowerCase();
|
|
56
59
|
if ([ '.json', '.csn' ].includes(ext) || options.fallbackParser === 'csn!')
|
|
57
|
-
return parseCsn.parse( source, filename, options );
|
|
60
|
+
return parseCsn.parse( source, filename, options, messageFunctions );
|
|
58
61
|
if ([ '.cds', '.hdbcds', '.hdbdd', '.cdl' ].includes(ext))
|
|
59
|
-
return parseLanguage( source, filename, options );
|
|
62
|
+
return parseLanguage( source, filename, options, messageFunctions );
|
|
60
63
|
if (options.fallbackParser === 'csn')
|
|
61
|
-
return parseCsn.parse( source, filename, options );
|
|
64
|
+
return parseCsn.parse( source, filename, options, messageFunctions );
|
|
62
65
|
if (options.fallbackParser) // any other value: like 'cdl' (historic reasons)
|
|
63
|
-
return parseLanguage( source, filename, options );
|
|
66
|
+
return parseLanguage( source, filename, options, messageFunctions );
|
|
64
67
|
|
|
65
68
|
const model = { location: { file: filename } };
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
} );
|
|
69
|
+
messageFunctions.error( 'file-unknown-ext', emptyWeakLocation(filename),
|
|
70
|
+
{ file: ext && ext.slice(1), '#': !ext && 'none' }, {
|
|
71
|
+
std: 'Unknown file extension $(FILE)',
|
|
72
|
+
none: 'No file extension',
|
|
73
|
+
} );
|
|
72
74
|
return model;
|
|
73
75
|
}
|
|
74
76
|
|
|
@@ -111,6 +113,7 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
|
|
|
111
113
|
a.fileContentDict = Object.create(null);
|
|
112
114
|
|
|
113
115
|
const model = { sources: a.sources, options };
|
|
116
|
+
model.$messageFunctions = createMessageFunctions( options, 'compile', model );
|
|
114
117
|
let all = promiseAllDoNotRejectImmediately( a.files.map(readAndParse) );
|
|
115
118
|
|
|
116
119
|
all = all
|
|
@@ -147,7 +150,7 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
|
|
|
147
150
|
else {
|
|
148
151
|
try {
|
|
149
152
|
a.fileContentDict[filename] = source;
|
|
150
|
-
const ast = parseX( source, rel, options );
|
|
153
|
+
const ast = parseX( source, rel, options, model.$messageFunctions );
|
|
151
154
|
a.sources[filename] = ast;
|
|
152
155
|
ast.location = { file: rel };
|
|
153
156
|
ast.dirname = path.dirname( filename );
|
|
@@ -187,8 +190,10 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
|
|
|
187
190
|
}
|
|
188
191
|
// create promises after all usingFroms have been collected, as the
|
|
189
192
|
// Promise executor is called immediately with `new`:
|
|
190
|
-
for (const module in dependencies)
|
|
191
|
-
promises.push( resolveModule( dependencies[module], fileCache, options
|
|
193
|
+
for (const module in dependencies) {
|
|
194
|
+
promises.push( resolveModule( dependencies[module], fileCache, options,
|
|
195
|
+
model.$messageFunctions ) );
|
|
196
|
+
}
|
|
192
197
|
}
|
|
193
198
|
if (!promises.length)
|
|
194
199
|
return [];
|
|
@@ -218,6 +223,7 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
|
|
|
218
223
|
a.fileContentDict = Object.create(null);
|
|
219
224
|
|
|
220
225
|
const model = { sources: a.sources, options };
|
|
226
|
+
model.$messageFunctions = createMessageFunctions( options, 'compile', model );
|
|
221
227
|
|
|
222
228
|
let asts = [];
|
|
223
229
|
const errors = [];
|
|
@@ -276,7 +282,7 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
|
|
|
276
282
|
else {
|
|
277
283
|
try {
|
|
278
284
|
a.fileContentDict[filename] = source;
|
|
279
|
-
const ast = parseX( source, rel, options );
|
|
285
|
+
const ast = parseX( source, rel, options, model.$messageFunctions );
|
|
280
286
|
a.sources[filename] = ast;
|
|
281
287
|
ast.location = { file: rel };
|
|
282
288
|
ast.dirname = path.dirname( filename );
|
|
@@ -307,8 +313,10 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
|
|
|
307
313
|
}
|
|
308
314
|
// create promises after all usingFroms have been collected, as the
|
|
309
315
|
// Promise executor is called immediately with `new`:
|
|
310
|
-
for (const module in dependencies)
|
|
311
|
-
fileNames.push( resolveModuleSync( dependencies[module], fileCache, options
|
|
316
|
+
for (const module in dependencies) {
|
|
317
|
+
fileNames.push( resolveModuleSync( dependencies[module], fileCache, options,
|
|
318
|
+
model.$messageFunctions ) );
|
|
319
|
+
}
|
|
312
320
|
}
|
|
313
321
|
if (!fileNames.length)
|
|
314
322
|
return [];
|
|
@@ -341,11 +349,12 @@ function compileSourcesX( sourcesDict, options = {} ) {
|
|
|
341
349
|
sourcesDict = { '<stdin>.cds': sourcesDict };
|
|
342
350
|
const sources = Object.create(null);
|
|
343
351
|
const model = { sources, options };
|
|
352
|
+
model.$messageFunctions = createMessageFunctions( options, 'compile', model );
|
|
344
353
|
|
|
345
354
|
for (const filename in sourcesDict) {
|
|
346
355
|
const source = sourcesDict[filename];
|
|
347
356
|
if (typeof source === 'string') {
|
|
348
|
-
const ast = parseX( source, filename, options );
|
|
357
|
+
const ast = parseX( source, filename, options, model.$messageFunctions );
|
|
349
358
|
sources[filename] = ast;
|
|
350
359
|
ast.location = { file: filename };
|
|
351
360
|
assertConsistency( ast, options );
|
|
@@ -359,6 +368,38 @@ function compileSourcesX( sourcesDict, options = {} ) {
|
|
|
359
368
|
return compileDoX( model );
|
|
360
369
|
}
|
|
361
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Recompile the given CSN
|
|
373
|
+
*
|
|
374
|
+
* @param {object} csn Input CSN to recompile to XSN
|
|
375
|
+
* @param {object} options Options
|
|
376
|
+
* @returns {object} XSN
|
|
377
|
+
*
|
|
378
|
+
* TODO: probaby issue message api-recompiled-csn there.
|
|
379
|
+
*/
|
|
380
|
+
function recompileX( csn, options ) {
|
|
381
|
+
options = { ...options, parseCdl: false, $recompile: true };
|
|
382
|
+
// TODO: $recompile: true should be enough
|
|
383
|
+
// Explicitly set parseCdl to false because backends cannot handle it
|
|
384
|
+
// Explicitly delete all toCsn options:
|
|
385
|
+
delete options.toCsn;
|
|
386
|
+
|
|
387
|
+
const file = csn.$location && csn.$location.file &&
|
|
388
|
+
csn.$location.file.replace(/[.]cds$/, '.cds.csn') || '<recompile>.csn';
|
|
389
|
+
|
|
390
|
+
const sources = Object.create(null);
|
|
391
|
+
const model = { sources, options };
|
|
392
|
+
model.$messageFunctions = createMessageFunctions( options, 'compile', model );
|
|
393
|
+
// TODO: or use module which invokes the recompilation?
|
|
394
|
+
|
|
395
|
+
sources[file] = parseCsn.augment( csn, file, options, model.$messageFunctions );
|
|
396
|
+
moduleLayers.setLayers( sources );
|
|
397
|
+
const compiled = compileDoX( model ); // calls throwWithError()
|
|
398
|
+
if (options.messages) // does not help with exception in compileDoX()
|
|
399
|
+
deduplicateMessages(options.messages); // TODO: do better
|
|
400
|
+
return compiled;
|
|
401
|
+
}
|
|
402
|
+
|
|
362
403
|
/**
|
|
363
404
|
* On the given model (AST like CSN) run the definer, resolver as well as semantic checks.
|
|
364
405
|
* Creates an augmented CSN (XSN) and returns it.
|
|
@@ -368,24 +409,28 @@ function compileSourcesX( sourcesDict, options = {} ) {
|
|
|
368
409
|
*/
|
|
369
410
|
function compileDoX( model ) {
|
|
370
411
|
const { options } = model;
|
|
412
|
+
const { throwWithError } = model.$messageFunctions;
|
|
371
413
|
if (!options.testMode)
|
|
372
414
|
model.meta = {}; // provide initial central meta object
|
|
373
|
-
if (options.parseOnly)
|
|
374
|
-
|
|
375
|
-
|
|
415
|
+
if (options.parseOnly) {
|
|
416
|
+
throwWithError();
|
|
417
|
+
return model;
|
|
418
|
+
}
|
|
376
419
|
define( model );
|
|
377
420
|
// do not run the resolver in parse-cdl mode or we get duplicate annotations, etc.
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
421
|
+
// TODO: do not use this function for parseCdl anyway…
|
|
422
|
+
if (options.parseCdl) {
|
|
423
|
+
throwWithError();
|
|
424
|
+
return model;
|
|
425
|
+
}
|
|
381
426
|
resolve( model );
|
|
382
427
|
assertConsistency( model );
|
|
383
|
-
|
|
428
|
+
throwWithError();
|
|
384
429
|
if (options.lintMode)
|
|
385
430
|
return model;
|
|
386
431
|
|
|
387
432
|
check(model);
|
|
388
|
-
|
|
433
|
+
throwWithError();
|
|
389
434
|
return propagator.propagate( model );
|
|
390
435
|
}
|
|
391
436
|
|
|
@@ -433,5 +478,6 @@ module.exports = {
|
|
|
433
478
|
compileX,
|
|
434
479
|
compileSyncX,
|
|
435
480
|
compileSourcesX,
|
|
481
|
+
recompileX,
|
|
436
482
|
InvocationError, // TODO: make it no error if same file name is provided twice
|
|
437
483
|
};
|