@sap/cds-compiler 3.0.0 → 3.1.2

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.
Files changed (79) hide show
  1. package/CHANGELOG.md +104 -9
  2. package/bin/.eslintrc.json +2 -1
  3. package/bin/cdsc.js +28 -16
  4. package/doc/API.md +11 -0
  5. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  6. package/doc/CHANGELOG_BETA.md +24 -2
  7. package/doc/CHANGELOG_DEPRECATED.md +21 -1
  8. package/lib/api/main.js +92 -40
  9. package/lib/api/options.js +2 -3
  10. package/lib/base/keywords.js +64 -1
  11. package/lib/base/message-registry.js +33 -5
  12. package/lib/base/messages.js +54 -65
  13. package/lib/base/model.js +2 -0
  14. package/lib/base/optionProcessorHelper.js +53 -21
  15. package/lib/checks/actionsFunctions.js +8 -7
  16. package/lib/checks/selectItems.js +96 -14
  17. package/lib/checks/types.js +5 -8
  18. package/lib/checks/validator.js +1 -2
  19. package/lib/compiler/assert-consistency.js +65 -13
  20. package/lib/compiler/base.js +6 -4
  21. package/lib/compiler/builtins.js +93 -4
  22. package/lib/compiler/checks.js +1 -1
  23. package/lib/compiler/define.js +28 -23
  24. package/lib/compiler/extend.js +20 -11
  25. package/lib/compiler/finalize-parse-cdl.js +5 -9
  26. package/lib/compiler/index.js +2 -0
  27. package/lib/compiler/populate.js +37 -32
  28. package/lib/compiler/propagator.js +11 -6
  29. package/lib/compiler/resolve.js +15 -19
  30. package/lib/compiler/shared.js +54 -18
  31. package/lib/compiler/tweak-assocs.js +5 -11
  32. package/lib/compiler/utils.js +15 -6
  33. package/lib/edm/annotations/genericTranslation.js +12 -2
  34. package/lib/edm/annotations/preprocessAnnotations.js +18 -15
  35. package/lib/edm/csn2edm.js +18 -17
  36. package/lib/edm/edm.js +22 -13
  37. package/lib/edm/edmAnnoPreprocessor.js +349 -0
  38. package/lib/edm/edmInboundChecks.js +85 -0
  39. package/lib/edm/edmPreprocessor.js +336 -665
  40. package/lib/edm/edmUtils.js +86 -45
  41. package/lib/gen/Dictionary.json +29 -9
  42. package/lib/gen/language.checksum +1 -1
  43. package/lib/gen/language.interp +1 -2
  44. package/lib/gen/languageLexer.js +3 -0
  45. package/lib/gen/languageParser.js +4332 -4496
  46. package/lib/inspect/.eslintrc.json +4 -0
  47. package/lib/inspect/index.js +14 -0
  48. package/lib/inspect/inspectModelStatistics.js +81 -0
  49. package/lib/inspect/inspectPropagation.js +189 -0
  50. package/lib/inspect/inspectUtils.js +44 -0
  51. package/lib/json/from-csn.js +19 -20
  52. package/lib/json/to-csn.js +11 -8
  53. package/lib/language/genericAntlrParser.js +150 -92
  54. package/lib/language/language.g4 +47 -74
  55. package/lib/main.d.ts +1 -0
  56. package/lib/model/api.js +1 -1
  57. package/lib/model/csnRefs.js +56 -29
  58. package/lib/model/csnUtils.js +29 -14
  59. package/lib/model/revealInternalProperties.js +6 -4
  60. package/lib/modelCompare/compare.js +3 -0
  61. package/lib/optionProcessor.js +81 -38
  62. package/lib/render/toCdl.js +57 -32
  63. package/lib/render/toHdbcds.js +1 -1
  64. package/lib/render/toSql.js +31 -11
  65. package/lib/render/utils/common.js +3 -4
  66. package/lib/transform/db/associations.js +43 -35
  67. package/lib/transform/db/cdsPersistence.js +0 -1
  68. package/lib/transform/db/flattening.js +3 -4
  69. package/lib/transform/db/transformExists.js +7 -5
  70. package/lib/transform/draft/db.js +1 -1
  71. package/lib/transform/forHanaNew.js +11 -2
  72. package/lib/transform/forOdataNew.js +4 -4
  73. package/lib/transform/localized.js +15 -11
  74. package/lib/transform/odata/typesExposure.js +14 -5
  75. package/lib/utils/file.js +28 -18
  76. package/lib/utils/moduleResolve.js +0 -1
  77. package/package.json +3 -4
  78. package/share/messages/syntax-expected-integer.md +9 -8
  79. package/lib/checks/unknownMagic.js +0 -41
@@ -0,0 +1,4 @@
1
+ {
2
+ "root": true,
3
+ "extends": "../../.eslintrc-ydkjsi.json"
4
+ }
@@ -0,0 +1,14 @@
1
+ // cds-compiler Inspect Module
2
+ // Used by `cdsc inspect` to gather details about the model such as statistics, etc.
3
+
4
+ 'use strict';
5
+
6
+ const { inspectModelStatistics } = require('./inspectModelStatistics');
7
+ const { inspectPropagation } = require('./inspectPropagation');
8
+ const { stringRefToPath } = require('./inspectUtils');
9
+
10
+ module.exports = {
11
+ inspectModelStatistics,
12
+ inspectPropagation,
13
+ stringRefToPath,
14
+ };
@@ -0,0 +1,81 @@
1
+ 'use strict';
2
+
3
+ const { forEach } = require('../utils/objectUtils');
4
+ const { term } = require('../utils/term');
5
+
6
+ /**
7
+ * Return a string representation of the inspected results.
8
+ *
9
+ * @param {XSN.Model} xsn
10
+ * @param {CSN.Options} options
11
+ * @returns {string}
12
+ */
13
+ function inspectModelStatistics(xsn, options) {
14
+ let result = '';
15
+
16
+ // Default color mode is 'auto'
17
+ const color = term(options.color || 'auto');
18
+
19
+ const defCount = countDefinitionKinds(xsn);
20
+ const sources = {
21
+ cdl: Object.keys(xsn.sources).filter(name => xsn.sources[name].$frontend === 'cdl').length,
22
+ csn: Object.keys(xsn.sources).filter(name => xsn.sources[name].$frontend === 'csn').length,
23
+ };
24
+
25
+ result += `cds-compiler model statistics:
26
+
27
+ ${ color.underline('files') }: ${ Object.keys(xsn.sources).length }
28
+ cdl sources: ${ sources.cdl }
29
+ csn sources: ${ sources.csn }
30
+
31
+ ${ color.underline('definitions') }: ${ defCount.definitions }
32
+ entities: ${ defCount.entity }
33
+ queries: ${ defCount.view }
34
+ aspects: ${ defCount.aspect }
35
+ events: ${ defCount.event }
36
+ types: ${ defCount.type }
37
+ services: ${ defCount.service }
38
+ context: ${ defCount.context }
39
+ actions: ${ defCount.action }
40
+ functions: ${ defCount.function }
41
+ namespaces: ${ defCount.namespace } (explicitly in CDL)
42
+
43
+ ${ color.underline('vocabularies') }: ${ Object.keys(xsn.vocabularies || {}).length }
44
+ `;
45
+ return result;
46
+ }
47
+
48
+ function countDefinitionKinds(xsn) {
49
+ const result = {
50
+ definitions: 0,
51
+ entity: 0,
52
+ action: 0,
53
+ function: 0,
54
+ aspect: 0,
55
+ event: 0,
56
+ type: 0,
57
+ service: 0,
58
+ context: 0,
59
+ namespace: 0,
60
+ // non-kind
61
+ view: 0,
62
+ };
63
+ forEach(xsn.definitions || {}, (name, def) => {
64
+ if (def.builtin)
65
+ return;
66
+ ++result.definitions;
67
+
68
+ if (def.query || def.projection)
69
+ ++result.view;
70
+ else if (result[def.kind] !== undefined)
71
+ ++result[def.kind];
72
+ else
73
+ throw new Error(`Unhandled kind: ${ def.kind } for ${ name }`);
74
+ });
75
+ return result;
76
+ }
77
+
78
+
79
+ module.exports = {
80
+ inspectModelStatistics,
81
+ };
@@ -0,0 +1,189 @@
1
+ 'use strict';
2
+
3
+ const { createMessageFunctions } = require('../base/messages');
4
+ const { locationString } = require('../base/location');
5
+ const { findArtifact, stringRefToPath } = require('./inspectUtils');
6
+ const { term } = require('../utils/term');
7
+
8
+ /**
9
+ * @param {XSN.Model} xsn
10
+ * @param {CSN.Options} options
11
+ * @param {string} artifactName
12
+ * @returns {string|null}
13
+ */
14
+ function inspectPropagation(xsn, options, artifactName) {
15
+ const { error } = createMessageFunctions(options, 'inspect', xsn);
16
+ const result = [];
17
+
18
+ // Default color mode is 'auto'
19
+ const color = term(options.color || 'auto');
20
+
21
+ const path = stringRefToPath(artifactName);
22
+ if (!path) {
23
+ error(null, null, { name: artifactName },
24
+ 'Artifact $(NAME) is not a valid path; expected format `<def>[:element]`');
25
+ return null;
26
+ }
27
+
28
+ const artifactXsn = findArtifact(xsn, path);
29
+
30
+ if (!artifactXsn) {
31
+ error(null, null, { name: artifactName },
32
+ // eslint-disable-next-line max-len
33
+ 'Artifact $(NAME) not found, only top-level artifacts and their elements are supported for now');
34
+ return null;
35
+ }
36
+ result.push(color.underline('analyzing propagation for artifact:'));
37
+ result.push(` name: ${ artifactXsn.name.id }`);
38
+ result.push(` kind: ${ artifactXsn.kind }`);
39
+
40
+ if (artifactXsn.$inferred)
41
+ result.push(` inferred: ${ artifactXsn.$inferred }`);
42
+
43
+ result.push('');
44
+ result.push(` ${ color.underline('annotation propagation:') }`);
45
+ result.push(..._indent(_inspectAnnotations(artifactXsn)));
46
+
47
+ result.push('');
48
+ result.push(` ${ color.underline('element propagation:') }`);
49
+ result.push(..._indent(_inspectElements(artifactXsn)));
50
+
51
+ return result.join('\n');
52
+ }
53
+
54
+ /**
55
+ @param {string[]} lines
56
+ @param {string} indent
57
+ * @returns {string[]}
58
+ */
59
+ function _indent(lines, indent = ' ') {
60
+ return lines.map(str => `${ indent }${ str }`);
61
+ }
62
+
63
+ /**
64
+ * @param {XSN.Artifact} artifactXsn
65
+ * @returns {string[]}
66
+ * @private
67
+ */
68
+ function _inspectAnnotations(artifactXsn) {
69
+ const result = [];
70
+ const annos = Object.keys(artifactXsn).filter(str => str.startsWith('@')).sort();
71
+
72
+ if (annos.length === 0)
73
+ return [ 'no annotations' ];
74
+
75
+ let maxAnnoLength = 30; // chosen arbitrarily, hopefully average
76
+ for (const anno of annos) {
77
+ const annoXsn = artifactXsn[anno];
78
+ const loc = locationString(annoXsn.name.location);
79
+ let origin;
80
+ switch (annoXsn.$priority) {
81
+ case false:
82
+ if (annoXsn.$inferred)
83
+ origin = 'propagation';
84
+ else
85
+ origin = 'direct';
86
+ break;
87
+
88
+ case 'extend':
89
+ case 'annotate':
90
+ origin = annoXsn.$priority;
91
+ break;
92
+
93
+ case undefined:
94
+ if (annoXsn.$inferred === '$generated') {
95
+ origin = 'generated';
96
+ break;
97
+ }
98
+ // fallthrough
99
+ default:
100
+ throw new Error(`inspect anno: Unhandled Case: ${ annoXsn.$priority }`);
101
+ }
102
+
103
+ maxAnnoLength = Math.max(maxAnnoLength, anno.length);
104
+
105
+ // origin: assume max length 11 of 'propagation'
106
+ // anno: use max length of all annotations till now
107
+ result.push([ origin.padStart(11), anno.padEnd(maxAnnoLength), loc ].join(' | '));
108
+ }
109
+ return result;
110
+ }
111
+
112
+ /**
113
+ * @param {XSN.Artifact} artifactXsn
114
+ * @returns {string[]}
115
+ * @private
116
+ */
117
+ function _inspectElements(artifactXsn) {
118
+ if (!artifactXsn.elements)
119
+ return [ 'does not have elements' ];
120
+
121
+ const result = [];
122
+ const elements = Object.keys(artifactXsn.elements);
123
+
124
+ let maxElemLength = 12;
125
+
126
+ const inferredNiceOutput = {
127
+ '*': 'wildcard',
128
+ 'expand-element': 'expanded',
129
+ 'expand-param': 'expanded',
130
+ 'aspect-composition': 'composition',
131
+ };
132
+
133
+ for (const element of elements) {
134
+ const elementXsn = artifactXsn.elements[element];
135
+ const loc = locationString(_origin(elementXsn).name.location);
136
+ let origin;
137
+
138
+ if (elementXsn.$inferred) {
139
+ // Use nice(r) output for known $inferred
140
+ if (inferredNiceOutput[elementXsn.$inferred])
141
+ origin = inferredNiceOutput[elementXsn.$inferred];
142
+ else
143
+ origin = elementXsn.$inferred;
144
+ }
145
+ else if (!isContainedInParentLocation(elementXsn, artifactXsn)) {
146
+ // just a heuristic
147
+ origin = 'extend';
148
+ }
149
+ else {
150
+ origin = 'direct';
151
+ }
152
+
153
+ maxElemLength = Math.max(maxElemLength, element.length);
154
+
155
+ // origin: assume max length 11 of 'composition'
156
+ // element: assume average length of 30, chosen randomly
157
+ result.push([ origin.padStart(11), element.padEnd(maxElemLength), loc ].join(' | '));
158
+ }
159
+ return result;
160
+ }
161
+
162
+ function _origin(elementXsn) {
163
+ while (elementXsn._origin)
164
+ elementXsn = elementXsn._origin;
165
+ return elementXsn;
166
+ }
167
+
168
+ /**
169
+ * Returns true if `art` is contained in `parent` according to its location.
170
+ *
171
+ * @param art
172
+ * @param parent
173
+ * @returns {boolean}
174
+ */
175
+ function isContainedInParentLocation(art, parent) {
176
+ const artLoc = art.location;
177
+ const parentLoc = parent.location;
178
+ if (artLoc.file !== parentLoc.file)
179
+ return false;
180
+ if (artLoc.line < parentLoc.line || artLoc.line > parentLoc.endLine)
181
+ return false;
182
+ // Good enough for now
183
+ // TODO: Check columns
184
+ return true;
185
+ }
186
+
187
+ module.exports = {
188
+ inspectPropagation,
189
+ };
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Reference (string) to path (array) that can be used to identify an artifact
5
+ * @param str
6
+ * @returns {*[]|*}
7
+ */
8
+ function stringRefToPath(str) {
9
+ // e.g. `ns.service.E:sub.elem.structured`
10
+ const path = str.split(':');
11
+ if (path.length === 1)
12
+ return path;
13
+ if (path.length > 2)
14
+ return null;
15
+ return [ path[0], ...path[1].split('.') ];
16
+ }
17
+
18
+ /**
19
+ * @param {XSN.Model} xsn
20
+ * @param {string} path
21
+ * @private
22
+ */
23
+ function findArtifact(xsn, path) {
24
+ const segments = [ ...path ];
25
+ const topLevelName = segments[0];
26
+ let art = (xsn.definitions && xsn.definitions[topLevelName]) ||
27
+ (xsn.vocabularies && xsn.vocabularies[topLevelName]);
28
+ if (!art)
29
+ return null;
30
+ segments.shift();
31
+ if (segments.length === 0)
32
+ return art;
33
+ while (segments.length && art) {
34
+ const segment = segments.shift();
35
+ art = (art.items?.elements || art.elements)?.[segment];
36
+ }
37
+ return art || null;
38
+ }
39
+
40
+
41
+ module.exports = {
42
+ stringRefToPath,
43
+ findArtifact,
44
+ };
@@ -88,6 +88,7 @@
88
88
  */
89
89
 
90
90
  const { dictAdd } = require('../base/dictionaries');
91
+ const { quotedLiteralPatterns } = require('../compiler/builtins');
91
92
 
92
93
  const $location = Symbol.for('cds.$location');
93
94
 
@@ -102,6 +103,7 @@ const ourpropsRegex = /^[_$]?[a-zA-Z]+[0-9]*$/;
102
103
  const typeProperties = [
103
104
  // do not include CSN v0.1.0 properties here:
104
105
  'target', 'elements', 'enum', 'items',
106
+ 'cardinality', // for association publishing in views
105
107
  'type', 'length', 'precision', 'scale', 'srid', 'localized', 'notNull',
106
108
  'keys', 'on', // only with 'target'
107
109
  ];
@@ -227,7 +229,7 @@ const schema = compileSchema( {
227
229
  dictionaryOf: definition,
228
230
  defaultKind: 'action',
229
231
  validKinds: [ 'action', 'function' ],
230
- inKind: [ 'entity', 'annotate', 'extend' ],
232
+ inKind: [ 'entity', 'aspect', 'annotate', 'extend' ],
231
233
  },
232
234
  params: {
233
235
  dictionaryOf: definition,
@@ -682,15 +684,6 @@ const topLevelSpec = {
682
684
  schema,
683
685
  };
684
686
 
685
- const validLiteralsExtra = Object.assign( Object.create(null), {
686
- // TODO: should we use quotedLiteralPatterns from genericAntlrParser?
687
- number: 'string',
688
- x: 'string',
689
- time: 'string',
690
- date: 'string',
691
- timestamp: 'string',
692
- } );
693
-
694
687
  // Module variables, schema compilation, and functors ------------------------
695
688
 
696
689
  /** @type {(id, location, textOrArguments, texts?) => void} */
@@ -1288,16 +1281,22 @@ function value( val, spec, xsn ) { // for CSN property 'val'
1288
1281
  return ignore( val );
1289
1282
  }
1290
1283
 
1291
- function literal( val, spec, xsn, csn ) {
1284
+ function literal( lit, spec, xsn, csn ) {
1292
1285
  // TODO: general: requires other property (here: 'val')
1293
1286
  const type = (csn.val == null) ? 'null' : typeof csn.val;
1294
- if (val === type) // also for 'object' which is an error for 'val'
1295
- return val;
1296
- if (typeof val === 'string' && validLiteralsExtra[val] === type)
1297
- return val;
1287
+ if (lit === type) // also for 'object' which is an error for 'val'
1288
+ return lit;
1289
+ if (typeof lit === 'string' && quotedLiteralPatterns[lit]?.json_type === type) {
1290
+ const p = quotedLiteralPatterns[lit];
1291
+ if (p && p.test_fn && !p.test_fn(csn.val))
1292
+ warning( 'syntax-invalid-literal', location(), { '#': p.test_variant } );
1293
+ return lit;
1294
+ }
1295
+ if (lit === 'number' && type === 'string') // special case, not a quoted literal in CDL
1296
+ return lit;
1298
1297
  error( 'syntax-expected-valid', location(true), { prop: spec.msgProp },
1299
1298
  'Expected valid string for property $(PROP)' );
1300
- return ignore( val );
1299
+ return ignore( lit );
1301
1300
  }
1302
1301
 
1303
1302
  function func( val, spec, xsn ) {
@@ -1535,7 +1534,7 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
1535
1534
  const zero = s.vZeroFor;
1536
1535
  if (zero) { // (potential) CSN v0.1.0 property
1537
1536
  const group = s.xorGroup;
1538
- if (zero && expected( zero, schema[zero] ) && !(group && xor[group])) {
1537
+ if (expected( zero, schema[zero] ) && !(group && xor[group])) {
1539
1538
  replaceZeroProp( prop, zero );
1540
1539
  if (group)
1541
1540
  xor[group] = prop;
@@ -1555,7 +1554,7 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
1555
1554
  std: 'CSN property $(PROP) is not expected in $(OTHERPROP)',
1556
1555
  top: 'CSN property $(PROP) is not expected top-level',
1557
1556
  def: 'CSN property $(PROP) is not expected by a definition of kind $(KIND)',
1558
- extend: 'CSN property $(PROP) is not expected by an extend in $(OTHERPROP))',
1557
+ extend: 'CSN property $(PROP) is not expected by an extend in $(OTHERPROP)',
1559
1558
  annotate: 'CSN property $(PROP) is not expected by an annotate in $(OTHERPROP)',
1560
1559
  } );
1561
1560
  // TODO: or still augment it? (but then also handle xorGroup)
@@ -1783,7 +1782,7 @@ function toXsn( csn, filename, options, messageFunctions ) {
1783
1782
  }
1784
1783
 
1785
1784
 
1786
- function augment( csn, filename = 'csn.json', options = {}, messageFunctions ) {
1785
+ function augment( csn, filename = 'csn.json', options = {}, messageFunctions = {} ) {
1787
1786
  try {
1788
1787
  return toXsn( csn, filename, options, messageFunctions );
1789
1788
  }
@@ -1793,7 +1792,7 @@ function augment( csn, filename = 'csn.json', options = {}, messageFunctions ) {
1793
1792
  }
1794
1793
  }
1795
1794
 
1796
- function parse( source, filename = 'csn.json', options = {}, messageFunctions ) {
1795
+ function parse( source, filename = 'csn.json', options = {}, messageFunctions = {} ) {
1797
1796
  try {
1798
1797
  return augment( JSON.parse(source), filename, options, messageFunctions );
1799
1798
  }
@@ -13,6 +13,7 @@
13
13
 
14
14
  const { locationString } = require('../base/messages');
15
15
  const { isBetaEnabled, isDeprecatedEnabled } = require('../base/model');
16
+ const { pathName } = require('../compiler/utils');
16
17
 
17
18
  const compilerVersion = require('../../package.json').version;
18
19
  const creator = `CDS Compiler v${ compilerVersion }`;
@@ -126,9 +127,6 @@ const transformers = {
126
127
  // location is not renamed to $location as the name is well established in
127
128
  // XSN and too many places (also outside the compiler) had to be adapted
128
129
  location, // non-enumerable $location in CSN
129
- $a2j: (e, csn) => { // on artifact level
130
- Object.assign( csn, e );
131
- },
132
130
  $extra: (e, csn) => {
133
131
  Object.assign( csn, e );
134
132
  },
@@ -184,9 +182,11 @@ const propertyOrder = (function orderPositions() {
184
182
  }());
185
183
 
186
184
  // sync with definition in from-csn.js:
185
+ // Note: Order here is also the property order in CSN.
187
186
  const typeProperties = [
188
- 'target', 'elements', 'enum', 'items', // TODO: notNull?
189
- 'type', 'length', 'precision', 'scale', 'srid', 'localized',
187
+ 'target', 'elements', 'enum', 'items',
188
+ 'cardinality', // for association publishing in views
189
+ 'type', 'length', 'precision', 'scale', 'srid', 'localized', // TODO: notNull?
190
190
  'foreignKeys', 'on', // for explicit ON/keys with REDIRECTED
191
191
  ];
192
192
 
@@ -1218,8 +1218,8 @@ function value( node ) {
1218
1218
  if (node.$inferred && gensrcFlavor)
1219
1219
  return undefined;
1220
1220
  if (node.path) {
1221
- const ref = node.path.map( id => id.id ).join('.');
1222
- return extra( { '=': node.variant ? `${ ref }#${ node.variant.id }` : ref }, node );
1221
+ const ref = pathName( node.path );
1222
+ return extra( { '=': node.variant ? `${ ref }#${ pathName(node.variant.path) }` : ref }, node );
1223
1223
  }
1224
1224
  if (node.literal === 'enum')
1225
1225
  return extra( { '#': node.sym.id }, node );
@@ -1275,8 +1275,11 @@ function expression( node, dollarExtra ) {
1275
1275
  return { ref: [ node.param.val ], param: true }; // CDL rule for runtimes
1276
1276
  }
1277
1277
  if (node.path) {
1278
+ const ref = node.path.map( pathItem );
1279
+ if (node.path.$prefix)
1280
+ ref.unshift( node.path.$prefix );
1278
1281
  // we would need to consider node.global here if we introduce that
1279
- return extra( { ref: node.path.map( pathItem ) }, dollarExtraNode );
1282
+ return extra( { ref }, dollarExtraNode );
1280
1283
  }
1281
1284
  if (node.literal) {
1282
1285
  if (typeof node.val === node.literal || node.val === null)