@sap/cds-compiler 3.3.2 → 3.4.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.
- package/CHANGELOG.md +33 -0
- package/bin/cdsc.js +3 -1
- package/doc/CHANGELOG_BETA.md +17 -0
- package/lib/api/main.js +147 -18
- package/lib/api/validate.js +8 -3
- package/lib/base/dictionaries.js +6 -6
- package/lib/base/keywords.js +104 -0
- package/lib/base/message-registry.js +137 -68
- package/lib/base/messages.js +59 -48
- package/lib/base/model.js +1 -0
- package/lib/checks/actionsFunctions.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -1
- package/lib/checks/checkForTypes.js +13 -8
- package/lib/checks/defaultValues.js +3 -1
- package/lib/checks/elements.js +1 -1
- package/lib/checks/parameters.js +4 -2
- package/lib/checks/queryNoDbArtifacts.js +1 -1
- package/lib/checks/sql-snippets.js +12 -10
- package/lib/checks/validator.js +14 -4
- package/lib/compiler/assert-consistency.js +8 -7
- package/lib/compiler/checks.js +30 -20
- package/lib/compiler/define.js +89 -25
- package/lib/compiler/extend.js +33 -28
- package/lib/compiler/finalize-parse-cdl.js +14 -9
- package/lib/compiler/populate.js +30 -8
- package/lib/compiler/propagator.js +23 -28
- package/lib/compiler/resolve.js +11 -5
- package/lib/compiler/shared.js +66 -48
- package/lib/compiler/tweak-assocs.js +2 -3
- package/lib/compiler/utils.js +11 -0
- package/lib/edm/annotations/genericTranslation.js +7 -4
- package/lib/edm/csn2edm.js +1 -1
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +3565 -3544
- package/lib/json/csnVersion.js +13 -13
- package/lib/json/from-csn.js +140 -158
- package/lib/json/to-csn.js +23 -5
- package/lib/language/.eslintrc.json +4 -0
- package/lib/language/antlrParser.js +7 -10
- package/lib/language/docCommentParser.js +1 -2
- package/lib/language/errorStrategy.js +54 -27
- package/lib/language/genericAntlrParser.js +115 -84
- package/lib/language/language.g4 +29 -25
- package/lib/language/multiLineStringParser.js +75 -63
- package/lib/main.js +1 -0
- package/lib/model/csnRefs.js +4 -3
- package/lib/model/csnUtils.js +39 -7
- package/lib/model/sortViews.js +7 -3
- package/lib/modelCompare/compare.js +49 -15
- package/lib/modelCompare/filter.js +83 -0
- package/lib/optionProcessor.js +5 -1
- package/lib/render/manageConstraints.js +9 -5
- package/lib/render/toCdl.js +120 -62
- package/lib/render/toHdbcds.js +1 -1
- package/lib/render/toSql.js +6 -2
- package/lib/render/utils/common.js +7 -0
- package/lib/sql-identifier.js +7 -0
- package/lib/transform/db/assertUnique.js +27 -38
- package/lib/transform/db/expansion.js +11 -4
- package/lib/transform/db/temporal.js +3 -1
- package/lib/transform/db/transformExists.js +7 -1
- package/lib/transform/db/views.js +42 -13
- package/lib/transform/draft/db.js +2 -2
- package/lib/transform/forOdataNew.js +7 -3
- package/lib/transform/forRelationalDB.js +12 -6
- package/lib/transform/localized.js +1 -1
- package/lib/transform/odata/typesExposure.js +2 -1
- package/lib/transform/parseExpr.js +245 -0
- package/lib/transform/transformUtilsNew.js +23 -14
- package/lib/transform/translateAssocsToJoins.js +12 -12
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
- package/lib/utils/term.js +5 -5
- package/package.json +2 -2
- package/share/messages/message-explanations.json +1 -1
- package/share/messages/{syntax-expected-integer.md → syntax-expecting-integer.md} +1 -1
package/lib/json/to-csn.js
CHANGED
|
@@ -186,7 +186,7 @@ const propertyOrder = (function orderPositions() {
|
|
|
186
186
|
const typeProperties = [
|
|
187
187
|
'target', 'elements', 'enum', 'items',
|
|
188
188
|
'cardinality', // for association publishing in views
|
|
189
|
-
'type', 'length', 'precision', 'scale', 'srid', 'localized',
|
|
189
|
+
'type', 'length', 'precision', 'scale', 'srid', 'localized', 'notNull',
|
|
190
190
|
'foreignKeys', 'on', // for explicit ON/keys with REDIRECTED
|
|
191
191
|
];
|
|
192
192
|
|
|
@@ -882,7 +882,24 @@ function definition( art, _csn, _node, prop ) {
|
|
|
882
882
|
return c;
|
|
883
883
|
}
|
|
884
884
|
|
|
885
|
-
|
|
885
|
+
/**
|
|
886
|
+
* Create $origin specification for query/projection.
|
|
887
|
+
*/
|
|
888
|
+
function queryOrigin( xsn ) {
|
|
889
|
+
const source = xsn._from[0]._origin;
|
|
890
|
+
let $origin;
|
|
891
|
+
if (xsn.includes)
|
|
892
|
+
// includesOrigin() does originRef() on the first include.
|
|
893
|
+
// Use it to behave the same as entity includes.
|
|
894
|
+
$origin = includesOrigin( [ { _artifact: source }, ...xsn.includes ], xsn );
|
|
895
|
+
else
|
|
896
|
+
$origin = originRef( source );
|
|
897
|
+
return $origin;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Create $origin specification for `includes` of `art`.
|
|
902
|
+
*/
|
|
886
903
|
function includesOrigin( includes, art ) {
|
|
887
904
|
const $origin = originRef( includes[0]._artifact );
|
|
888
905
|
if (includes.length === 1)
|
|
@@ -922,7 +939,7 @@ function addOrigin( csn, xsn, node ) {
|
|
|
922
939
|
}
|
|
923
940
|
if (xsn._from) {
|
|
924
941
|
const source = xsn._from[0]._origin;
|
|
925
|
-
csn.$origin =
|
|
942
|
+
csn.$origin = queryOrigin( xsn );
|
|
926
943
|
if (source.params && !xsn.params)
|
|
927
944
|
csn.params = null; // discontinue `params` inheritance
|
|
928
945
|
if (source.actions && !xsn.actions)
|
|
@@ -1059,10 +1076,11 @@ function getOrigin( art ) {
|
|
|
1059
1076
|
return (_origin.kind === 'builtin') ? undefined : _origin; // not $dollarVariable
|
|
1060
1077
|
if (hasExplicitProp( art.type, 'cast' ))
|
|
1061
1078
|
return art.type._artifact;
|
|
1062
|
-
if (art.includes)
|
|
1063
|
-
return art.includes[0]._artifact;
|
|
1064
1079
|
if (art._from)
|
|
1065
1080
|
return art._from[0]._origin;
|
|
1081
|
+
// must come after _from, since queries can have includes as well.
|
|
1082
|
+
if (art.includes)
|
|
1083
|
+
return art.includes[0]._artifact;
|
|
1066
1084
|
return undefined;
|
|
1067
1085
|
}
|
|
1068
1086
|
|
|
@@ -18,22 +18,17 @@ const Lexer = require('../gen/languageLexer').default;
|
|
|
18
18
|
|
|
19
19
|
// Error listener used for ANTLR4-generated parser
|
|
20
20
|
class ErrorListener extends antlr4.error.ErrorListener {
|
|
21
|
-
constructor(...args) {
|
|
22
|
-
super(...args);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
21
|
// method which is called by generated parser with --trace-parser[-amg]:
|
|
22
|
+
// eslint-disable-next-line class-methods-use-this
|
|
26
23
|
syntaxError( recognizer, offendingSymbol, line, column, msg, e ) {
|
|
27
24
|
if (!(e instanceof CompileMessage)) // not already reported
|
|
25
|
+
// Ignore warning, because only relevant for --trace-parser
|
|
26
|
+
// eslint-disable-next-line cds-compiler/message-call-format
|
|
28
27
|
recognizer.error( null, offendingSymbol, {}, msg );
|
|
29
28
|
}
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
class RewriteTypeTokenStream extends antlr4.CommonTokenStream {
|
|
33
|
-
constructor(...args) {
|
|
34
|
-
super(...args);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
32
|
LT( k ) {
|
|
38
33
|
const t = super.LT(k);
|
|
39
34
|
if (!t || !t.type)
|
|
@@ -112,7 +107,9 @@ const rules = {
|
|
|
112
107
|
expr: { func: 'conditionEOF', returns: 'cond' }, // yes, condition
|
|
113
108
|
};
|
|
114
109
|
|
|
115
|
-
function parse( source, filename = '<undefined>.cds',
|
|
110
|
+
function parse( source, filename = '<undefined>.cds',
|
|
111
|
+
options = {}, messageFunctions = null,
|
|
112
|
+
rule = 'cdl' ) {
|
|
116
113
|
const lexer = new Lexer( new antlr4.InputStream(source) );
|
|
117
114
|
const tokenStream = new RewriteTypeTokenStream(lexer);
|
|
118
115
|
/** @type {object} */
|
|
@@ -168,7 +165,7 @@ function parse( source, filename = '<undefined>.cds', options = {}, messageFunct
|
|
|
168
165
|
for (const token of tokenStream.tokens) {
|
|
169
166
|
if (token.type === parser.constructor.DocComment && !token.isUsed) {
|
|
170
167
|
messageFunctions.info('syntax-ignoring-doc-comment', parser.tokenLocation(token), {},
|
|
171
|
-
|
|
168
|
+
'Ignoring doc comment as it does not belong to any artifact');
|
|
172
169
|
}
|
|
173
170
|
}
|
|
174
171
|
}
|
|
@@ -93,9 +93,8 @@ function stripCommentIndentation(lines, ignoreFirstLine) {
|
|
|
93
93
|
|
|
94
94
|
let count = 0;
|
|
95
95
|
const length = Math.min(min, line.length);
|
|
96
|
-
while (count < length && isWhitespaceCharacterNoNewline(line[count]))
|
|
96
|
+
while (count < length && isWhitespaceCharacterNoNewline(line[count]))
|
|
97
97
|
count++;
|
|
98
|
-
}
|
|
99
98
|
return Math.min(min, count);
|
|
100
99
|
}, Number.MAX_SAFE_INTEGER);
|
|
101
100
|
|
|
@@ -29,10 +29,13 @@
|
|
|
29
29
|
'use strict';
|
|
30
30
|
|
|
31
31
|
const antlr4 = require('antlr4');
|
|
32
|
-
|
|
32
|
+
// eslint-disable-next-line camelcase
|
|
33
|
+
const Antlr4LL1Analyzer = require('antlr4/src/antlr4/LL1Analyzer');
|
|
33
34
|
const { DefaultErrorStrategy } = require('antlr4/src/antlr4/error/ErrorStrategy');
|
|
34
35
|
const { InputMismatchException } = require('antlr4/src/antlr4/error/Errors');
|
|
35
|
-
const {
|
|
36
|
+
const {
|
|
37
|
+
predictionContextFromRuleContext: predictionContext,
|
|
38
|
+
} = require('antlr4/src/antlr4/PredictionContext');
|
|
36
39
|
const { ATNState } = require('antlr4/src/antlr4/atn/ATNState');
|
|
37
40
|
const { IntervalSet, Interval } = require('antlr4/src/antlr4/IntervalSet');
|
|
38
41
|
|
|
@@ -53,8 +56,7 @@ function match( ttype ) {
|
|
|
53
56
|
if (token.type === identType || !keywordRegexp.test( token.text ))
|
|
54
57
|
return antlr4.Parser.prototype.match.call( this, ttype );
|
|
55
58
|
|
|
56
|
-
this.message( 'syntax-fragile-ident', token, { id: token.text, delimited: token.text }
|
|
57
|
-
'$(ID) is a reserved name here - write $(DELIMITED) instead if you want to use it' );
|
|
59
|
+
this.message( 'syntax-fragile-ident', token, { id: token.text, delimited: token.text } );
|
|
58
60
|
this._errHandler.reportMatch(this);
|
|
59
61
|
this.consume();
|
|
60
62
|
return token;
|
|
@@ -107,7 +109,8 @@ function sync( recognizer ) {
|
|
|
107
109
|
const s = recognizer._interp.atn.states[recognizer.state];
|
|
108
110
|
// try cheaper subset first; might get lucky. seems to shave a wee bit off
|
|
109
111
|
const nextTokens = recognizer.atn.nextTokens(s);
|
|
110
|
-
// console.log('SYNC:', recognizer._ctx._sync, s.stateType, token.text,
|
|
112
|
+
// console.log('SYNC:', recognizer._ctx._sync, s.stateType, token.text,
|
|
113
|
+
// intervalSetToArray( recognizer, nextTokens ))
|
|
111
114
|
|
|
112
115
|
if (nextTokens.contains(token.type)) { // we are sure the token matches
|
|
113
116
|
if (token.text === '}' && recognizer.$nextTokensToken !== token &&
|
|
@@ -124,7 +127,8 @@ function sync( recognizer ) {
|
|
|
124
127
|
// when exiting a (innermost) rule, remember the state to make
|
|
125
128
|
// getExpectedTokensForMessage() calculate the full "expected set"
|
|
126
129
|
if (recognizer.$nextTokensToken !== token) {
|
|
127
|
-
// console.log('SET:',token.type,recognizer.state,recognizer.$nextTokensToken &&
|
|
130
|
+
// console.log('SET:',token.type,recognizer.state,recognizer.$nextTokensToken &&
|
|
131
|
+
// recognizer.$nextTokensToken.type)
|
|
128
132
|
recognizer.$nextTokensToken = token;
|
|
129
133
|
recognizer.$nextTokensState = recognizer.state;
|
|
130
134
|
recognizer.$nextTokensContext = recognizer._ctx;
|
|
@@ -142,8 +146,7 @@ function sync( recognizer ) {
|
|
|
142
146
|
// interpreter to see behind EPSILON.
|
|
143
147
|
const identType = recognizer.constructor.Identifier;
|
|
144
148
|
if (keywordRegexp.test( token.text ) && nextTokens.contains( identType )) {
|
|
145
|
-
recognizer.message( 'syntax-fragile-ident', token, { id: token.text, delimited: token.text }
|
|
146
|
-
'$(ID) is a reserved name here - write $(DELIMITED) instead if you want to use it' );
|
|
149
|
+
recognizer.message( 'syntax-fragile-ident', token, { id: token.text, delimited: token.text } );
|
|
147
150
|
token.type = identType; // make next ANTLR decision assume identifier
|
|
148
151
|
return;
|
|
149
152
|
}
|
|
@@ -187,7 +190,8 @@ function sync( recognizer ) {
|
|
|
187
190
|
|
|
188
191
|
// Report `NoViableAltException e` signalled by parser `recognizer`
|
|
189
192
|
function reportNoViableAlternative( recognizer, e ) {
|
|
190
|
-
// console.log('NOV:',this.getTokenErrorDisplay(e.startToken),
|
|
193
|
+
// console.log('NOV:',this.getTokenErrorDisplay(e.startToken),
|
|
194
|
+
// this.getTokenErrorDisplay(e.offendingToken))
|
|
191
195
|
if (e.startToken === e.offendingToken) { // mismatch at LA(1)
|
|
192
196
|
this.reportInputMismatch( recognizer, e );
|
|
193
197
|
}
|
|
@@ -262,6 +266,7 @@ function reportIgnoredWith( recognizer, t ) {
|
|
|
262
266
|
const expecting = this.getExpectedTokensForMessage( recognizer, t );
|
|
263
267
|
const m = recognizer.warning( 'syntax-ignored-with', t,
|
|
264
268
|
{ offending: "';'", expecting },
|
|
269
|
+
// eslint-disable-next-line max-len
|
|
265
270
|
'Unexpected $(OFFENDING), expecting $(EXPECTING) - ignored previous WITH' );
|
|
266
271
|
m.expectedTokens = expecting;
|
|
267
272
|
}
|
|
@@ -279,7 +284,8 @@ function consumeUntil( recognizer, set ) {
|
|
|
279
284
|
}
|
|
280
285
|
else if (set.contains(SEMI)) { // do not check for RBRACE here!
|
|
281
286
|
this._super.consumeUntil.call( this, recognizer, set );
|
|
282
|
-
// console.log('CONSUMED-ORIG:',s,this.getTokenDisplay( recognizer.getCurrentToken(),
|
|
287
|
+
// console.log('CONSUMED-ORIG:',s,this.getTokenDisplay( recognizer.getCurrentToken(),
|
|
288
|
+
// recognizer ),recognizer.getCurrentToken().line,intervalSetToArray( recognizer, set ));
|
|
283
289
|
}
|
|
284
290
|
else {
|
|
285
291
|
// DO NOT modify input param `set`, as the set might be cached in the ATN
|
|
@@ -300,7 +306,8 @@ function consumeUntil( recognizer, set ) {
|
|
|
300
306
|
recognizer.consume();
|
|
301
307
|
this.reportMatch(recognizer); // we know current token is correct
|
|
302
308
|
}
|
|
303
|
-
// console.log('CONSUMED:',s,this.getTokenDisplay( recognizer.getCurrentToken(),
|
|
309
|
+
// console.log('CONSUMED:',s,this.getTokenDisplay( recognizer.getCurrentToken(),
|
|
310
|
+
// recognizer ),recognizer.getCurrentToken().line);
|
|
304
311
|
// throw new Error('Sync')
|
|
305
312
|
}
|
|
306
313
|
}
|
|
@@ -321,8 +328,7 @@ function recoverInline( recognizer ) {
|
|
|
321
328
|
if (!keywordRegexp.test( token.text ))
|
|
322
329
|
return this._super.recoverInline.call( this, recognizer );
|
|
323
330
|
|
|
324
|
-
recognizer.message( 'syntax-fragile-ident', token, { id: token.text, delimited: token.text }
|
|
325
|
-
'$(ID) is a reserved name here - write $(DELIMITED) instead if you want to use it' );
|
|
331
|
+
recognizer.message( 'syntax-fragile-ident', token, { id: token.text, delimited: token.text } );
|
|
326
332
|
this.reportMatch(recognizer); // we know current token is correct
|
|
327
333
|
recognizer.consume();
|
|
328
334
|
return token;
|
|
@@ -362,7 +368,8 @@ function intervalSetToArray( recognizer, expected, excludesForNextToken ) {
|
|
|
362
368
|
}
|
|
363
369
|
}
|
|
364
370
|
// The parser method excludeExpected() additionally removes some tokens from the message:
|
|
365
|
-
if (recognizer.$adaptExpectedToken &&
|
|
371
|
+
if (recognizer.$adaptExpectedToken &&
|
|
372
|
+
recognizer.$nextTokensToken === recognizer.$adaptExpectedToken) {
|
|
366
373
|
const excludes = (excludesForNextToken && Array.isArray(recognizer.$adaptExpectedExcludes[0]))
|
|
367
374
|
? recognizer.$adaptExpectedExcludes[0]
|
|
368
375
|
: recognizer.$adaptExpectedExcludes;
|
|
@@ -381,13 +388,32 @@ function intervalSetToArray( recognizer, expected, excludesForNextToken ) {
|
|
|
381
388
|
const token1sort = {
|
|
382
389
|
// 0: Identifier, Number, ...
|
|
383
390
|
// 1: separators:
|
|
384
|
-
',': 1,
|
|
391
|
+
',': 1,
|
|
392
|
+
'.': 1,
|
|
393
|
+
':': 1,
|
|
394
|
+
';': 1,
|
|
385
395
|
// 2: parentheses:
|
|
386
|
-
'(': 2,
|
|
396
|
+
'(': 2,
|
|
397
|
+
')': 2,
|
|
398
|
+
'[': 2,
|
|
399
|
+
']': 2,
|
|
400
|
+
'{': 2,
|
|
401
|
+
'}': 2,
|
|
387
402
|
// 3: special:
|
|
388
|
-
'!': 3,
|
|
403
|
+
'!': 3,
|
|
404
|
+
'#': 3,
|
|
405
|
+
$: 3,
|
|
406
|
+
'?': 3,
|
|
407
|
+
'@': 3,
|
|
389
408
|
// 4: operators:
|
|
390
|
-
'*': 4,
|
|
409
|
+
'*': 4,
|
|
410
|
+
'+': 4,
|
|
411
|
+
'-': 4,
|
|
412
|
+
'/': 4,
|
|
413
|
+
'<': 4,
|
|
414
|
+
'=': 4,
|
|
415
|
+
'>': 4,
|
|
416
|
+
'|': 4,
|
|
391
417
|
// 8: KEYWORD
|
|
392
418
|
// 9: <EOF>
|
|
393
419
|
};
|
|
@@ -425,7 +451,7 @@ function getExpectedTokensForMessage( recognizer, offendingToken, deadEnds ) {
|
|
|
425
451
|
if (recognizer.state < 0)
|
|
426
452
|
return [];
|
|
427
453
|
if (recognizer.state >= atn.states.length) {
|
|
428
|
-
throw ( `Invalid state number ${ recognizer.state } for ${
|
|
454
|
+
throw new Error( `Invalid state number ${ recognizer.state } for ${
|
|
429
455
|
this.getTokenErrorDisplay( offendingToken ) }`);
|
|
430
456
|
}
|
|
431
457
|
|
|
@@ -435,10 +461,10 @@ function getExpectedTokensForMessage( recognizer, offendingToken, deadEnds ) {
|
|
|
435
461
|
if (!identType || !beforeUnreserved || beforeUnreserved + 2 > identType)
|
|
436
462
|
return intervalSetToArray( recognizer, this._super.getExpectedTokens.call( this, recognizer ) );
|
|
437
463
|
|
|
438
|
-
const ll1 = new
|
|
464
|
+
const ll1 = new Antlr4LL1Analyzer(atn);
|
|
439
465
|
const expected = new IntervalSet();
|
|
440
|
-
const
|
|
441
|
-
const
|
|
466
|
+
const origAddInterval = expected.addInterval;
|
|
467
|
+
const origAddSet = expected.addSet;
|
|
442
468
|
expected.addInterval = addInterval;
|
|
443
469
|
expected.addSet = addSet;
|
|
444
470
|
const lookBusy = new antlr4.Utils.Set();
|
|
@@ -466,23 +492,24 @@ function getExpectedTokensForMessage( recognizer, offendingToken, deadEnds ) {
|
|
|
466
492
|
predictionContext( atn, recognizer._ctx ),
|
|
467
493
|
expected, lookBusy, calledRules, true, true );
|
|
468
494
|
}
|
|
469
|
-
// console.log(state, recognizer.$nextTokensState,
|
|
495
|
+
// console.log(state, recognizer.$nextTokensState,
|
|
496
|
+
// expected.toString(recognizer.literalNames, recognizer.symbolicNames));
|
|
470
497
|
return intervalSetToArray( recognizer, expected );
|
|
471
498
|
|
|
472
499
|
function addSet(other) {
|
|
473
500
|
if (!other.contains( hideAltsType ))
|
|
474
|
-
|
|
501
|
+
origAddSet.call( this, other );
|
|
475
502
|
}
|
|
476
503
|
|
|
477
504
|
// Add an interval `v` to the IntervalSet `this`. If `v` contains the token
|
|
478
505
|
// type `Identifier`, do not add non-reserved keywords in `v`.
|
|
479
506
|
function addInterval(v) {
|
|
480
507
|
if (v.stop <= identType) {
|
|
481
|
-
|
|
508
|
+
origAddInterval.call(this, v);
|
|
482
509
|
}
|
|
483
510
|
else if (v.start >= identType) {
|
|
484
511
|
if (v.stop === identType + 1 || !recognizer.tokenRewrite) {
|
|
485
|
-
|
|
512
|
+
origAddInterval.call(this, v);
|
|
486
513
|
}
|
|
487
514
|
else {
|
|
488
515
|
for (let j = v.start; j < v.stop; j++)
|
|
@@ -497,7 +524,7 @@ function getExpectedTokensForMessage( recognizer, offendingToken, deadEnds ) {
|
|
|
497
524
|
}
|
|
498
525
|
|
|
499
526
|
function addRange( interval, start, stop ) {
|
|
500
|
-
|
|
527
|
+
origAddInterval.call( interval, new Interval( start, stop || start + 1 ) );
|
|
501
528
|
}
|
|
502
529
|
}
|
|
503
530
|
|