@mojir/lits 2.2.2 → 2.2.3
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/dist/cli/cli.js +1145 -1105
- package/dist/cli/src/AutoCompleter/AutoCompleter.d.ts +2 -1
- package/dist/cli/src/builtin/bindingNode.d.ts +3 -3
- package/dist/cli/src/builtin/index.d.ts +0 -1
- package/dist/cli/src/builtin/interface.d.ts +3 -4
- package/dist/cli/src/builtin/specialExpressionTypes.d.ts +0 -1
- package/dist/cli/src/builtin/specialExpressions/and.d.ts +2 -2
- package/dist/cli/src/builtin/specialExpressions/array.d.ts +2 -2
- package/dist/cli/src/builtin/specialExpressions/block.d.ts +2 -2
- package/dist/cli/src/builtin/specialExpressions/cond.d.ts +2 -2
- package/dist/cli/src/builtin/specialExpressions/if.d.ts +2 -2
- package/dist/cli/src/builtin/specialExpressions/loop.d.ts +2 -2
- package/dist/cli/src/builtin/specialExpressions/loops.d.ts +4 -4
- package/dist/cli/src/builtin/specialExpressions/object.d.ts +2 -2
- package/dist/cli/src/builtin/specialExpressions/or.d.ts +2 -2
- package/dist/cli/src/builtin/specialExpressions/qq.d.ts +2 -2
- package/dist/cli/src/builtin/specialExpressions/recur.d.ts +2 -2
- package/dist/cli/src/builtin/specialExpressions/switch.d.ts +2 -2
- package/dist/cli/src/builtin/specialExpressions/throw.d.ts +2 -2
- package/dist/cli/src/builtin/specialExpressions/try.d.ts +2 -2
- package/dist/cli/src/builtin/specialExpressions/unless.d.ts +2 -2
- package/dist/cli/src/builtin/utils.d.ts +2 -2
- package/dist/cli/src/evaluator/index.d.ts +2 -2
- package/dist/cli/src/evaluator/interface.d.ts +4 -3
- package/dist/cli/src/getUndefinedSymbols/index.d.ts +2 -2
- package/dist/cli/src/parser/ParserContext.d.ts +20 -0
- package/dist/cli/src/parser/helpers.d.ts +19 -0
- package/dist/cli/src/parser/index.d.ts +3 -0
- package/dist/cli/src/parser/subParsers/getPrecedence.d.ts +3 -0
- package/dist/cli/src/parser/subParsers/parseArray.d.ts +3 -0
- package/dist/cli/src/parser/subParsers/parseBindingTarget.d.ts +6 -0
- package/dist/cli/src/parser/subParsers/parseCond.d.ts +4 -0
- package/dist/cli/src/parser/subParsers/parseDo.d.ts +3 -0
- package/dist/cli/src/parser/subParsers/parseForOrDoseq.d.ts +4 -0
- package/dist/cli/src/parser/subParsers/parseFunction.d.ts +4 -0
- package/dist/cli/src/parser/subParsers/parseFunctionCall.d.ts +3 -0
- package/dist/cli/src/parser/subParsers/parseIfOrUnless.d.ts +5 -0
- package/dist/cli/src/parser/subParsers/parseImplicitBlock.d.ts +5 -0
- package/dist/cli/src/parser/subParsers/parseLet.d.ts +4 -0
- package/dist/cli/src/parser/subParsers/parseLoop.d.ts +4 -0
- package/dist/cli/src/parser/subParsers/parseNumber.d.ts +3 -0
- package/dist/cli/src/parser/subParsers/parseObject.d.ts +3 -0
- package/dist/cli/src/parser/subParsers/parseOperand.d.ts +3 -0
- package/dist/cli/src/parser/subParsers/parseRegexpShorthand.d.ts +3 -0
- package/dist/cli/src/parser/subParsers/parseReservedSymbol.d.ts +3 -0
- package/dist/cli/src/parser/subParsers/parseString.d.ts +4 -0
- package/dist/cli/src/parser/subParsers/parseSwitch.d.ts +4 -0
- package/dist/cli/src/parser/subParsers/parseSymbol.d.ts +3 -0
- package/dist/cli/src/parser/subParsers/parseTry.d.ts +4 -0
- package/dist/cli/src/parser/types.d.ts +19 -26
- package/dist/cli/src/testFramework/index.d.ts +3 -2
- package/dist/cli/src/tokenizer/token.d.ts +0 -2
- package/dist/cli/src/tokenizer/tokenizers.d.ts +2 -2
- package/dist/cli/src/typeGuards/astNode.d.ts +18 -19
- package/dist/cli/src/typeGuards/index.d.ts +0 -1
- package/dist/cli/src/typeGuards/lits.d.ts +0 -1
- package/dist/full.esm.js +1 -1
- package/dist/full.esm.js.map +1 -1
- package/dist/full.js +1 -1
- package/dist/full.js.map +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lits.iife.js +1 -1
- package/dist/lits.iife.js.map +1 -1
- package/dist/modules/assert.esm.js.map +1 -1
- package/dist/modules/assert.js.map +1 -1
- package/dist/modules/bitwise.esm.js.map +1 -1
- package/dist/modules/bitwise.js.map +1 -1
- package/dist/modules/collection.esm.js.map +1 -1
- package/dist/modules/collection.js.map +1 -1
- package/dist/modules/functional.esm.js.map +1 -1
- package/dist/modules/functional.js.map +1 -1
- package/dist/modules/grid.esm.js.map +1 -1
- package/dist/modules/grid.js.map +1 -1
- package/dist/modules/linear-algebra.esm.js.map +1 -1
- package/dist/modules/linear-algebra.js.map +1 -1
- package/dist/modules/matrix.esm.js.map +1 -1
- package/dist/modules/matrix.js.map +1 -1
- package/dist/modules/number-theory.esm.js.map +1 -1
- package/dist/modules/number-theory.js.map +1 -1
- package/dist/modules/random.esm.js.map +1 -1
- package/dist/modules/random.js.map +1 -1
- package/dist/modules/sequence.esm.js.map +1 -1
- package/dist/modules/sequence.js.map +1 -1
- package/dist/modules/src/AutoCompleter/AutoCompleter.d.ts +2 -1
- package/dist/modules/src/builtin/bindingNode.d.ts +3 -3
- package/dist/modules/src/builtin/index.d.ts +0 -1
- package/dist/modules/src/builtin/interface.d.ts +3 -4
- package/dist/modules/src/builtin/specialExpressionTypes.d.ts +0 -1
- package/dist/modules/src/builtin/specialExpressions/and.d.ts +2 -2
- package/dist/modules/src/builtin/specialExpressions/array.d.ts +2 -2
- package/dist/modules/src/builtin/specialExpressions/block.d.ts +2 -2
- package/dist/modules/src/builtin/specialExpressions/cond.d.ts +2 -2
- package/dist/modules/src/builtin/specialExpressions/if.d.ts +2 -2
- package/dist/modules/src/builtin/specialExpressions/loop.d.ts +2 -2
- package/dist/modules/src/builtin/specialExpressions/loops.d.ts +4 -4
- package/dist/modules/src/builtin/specialExpressions/object.d.ts +2 -2
- package/dist/modules/src/builtin/specialExpressions/or.d.ts +2 -2
- package/dist/modules/src/builtin/specialExpressions/qq.d.ts +2 -2
- package/dist/modules/src/builtin/specialExpressions/recur.d.ts +2 -2
- package/dist/modules/src/builtin/specialExpressions/switch.d.ts +2 -2
- package/dist/modules/src/builtin/specialExpressions/throw.d.ts +2 -2
- package/dist/modules/src/builtin/specialExpressions/try.d.ts +2 -2
- package/dist/modules/src/builtin/specialExpressions/unless.d.ts +2 -2
- package/dist/modules/src/builtin/utils.d.ts +2 -2
- package/dist/modules/src/evaluator/index.d.ts +2 -2
- package/dist/modules/src/evaluator/interface.d.ts +4 -3
- package/dist/modules/src/getUndefinedSymbols/index.d.ts +2 -2
- package/dist/modules/src/parser/ParserContext.d.ts +20 -0
- package/dist/modules/src/parser/helpers.d.ts +19 -0
- package/dist/modules/src/parser/index.d.ts +3 -0
- package/dist/modules/src/parser/subParsers/getPrecedence.d.ts +3 -0
- package/dist/modules/src/parser/subParsers/parseArray.d.ts +3 -0
- package/dist/modules/src/parser/subParsers/parseBindingTarget.d.ts +6 -0
- package/dist/modules/src/parser/subParsers/parseCond.d.ts +4 -0
- package/dist/modules/src/parser/subParsers/parseDo.d.ts +3 -0
- package/dist/modules/src/parser/subParsers/parseForOrDoseq.d.ts +4 -0
- package/dist/modules/src/parser/subParsers/parseFunction.d.ts +4 -0
- package/dist/modules/src/parser/subParsers/parseFunctionCall.d.ts +3 -0
- package/dist/modules/src/parser/subParsers/parseIfOrUnless.d.ts +5 -0
- package/dist/modules/src/parser/subParsers/parseImplicitBlock.d.ts +5 -0
- package/dist/modules/src/parser/subParsers/parseLet.d.ts +4 -0
- package/dist/modules/src/parser/subParsers/parseLoop.d.ts +4 -0
- package/dist/modules/src/parser/subParsers/parseNumber.d.ts +3 -0
- package/dist/modules/src/parser/subParsers/parseObject.d.ts +3 -0
- package/dist/modules/src/parser/subParsers/parseOperand.d.ts +3 -0
- package/dist/modules/src/parser/subParsers/parseRegexpShorthand.d.ts +3 -0
- package/dist/modules/src/parser/subParsers/parseReservedSymbol.d.ts +3 -0
- package/dist/modules/src/parser/subParsers/parseString.d.ts +4 -0
- package/dist/modules/src/parser/subParsers/parseSwitch.d.ts +4 -0
- package/dist/modules/src/parser/subParsers/parseSymbol.d.ts +3 -0
- package/dist/modules/src/parser/subParsers/parseTry.d.ts +4 -0
- package/dist/modules/src/parser/types.d.ts +19 -26
- package/dist/modules/src/testFramework/index.d.ts +3 -2
- package/dist/modules/src/tokenizer/token.d.ts +0 -2
- package/dist/modules/src/tokenizer/tokenizers.d.ts +2 -2
- package/dist/modules/src/typeGuards/astNode.d.ts +18 -19
- package/dist/modules/src/typeGuards/index.d.ts +0 -1
- package/dist/modules/src/typeGuards/lits.d.ts +0 -1
- package/dist/modules/string.esm.js.map +1 -1
- package/dist/modules/string.js.map +1 -1
- package/dist/modules/vector.esm.js.map +1 -1
- package/dist/modules/vector.js.map +1 -1
- package/dist/src/AutoCompleter/AutoCompleter.d.ts +2 -1
- package/dist/src/builtin/bindingNode.d.ts +3 -3
- package/dist/src/builtin/index.d.ts +0 -1
- package/dist/src/builtin/interface.d.ts +3 -4
- package/dist/src/builtin/specialExpressionTypes.d.ts +0 -1
- package/dist/src/builtin/specialExpressions/and.d.ts +2 -2
- package/dist/src/builtin/specialExpressions/array.d.ts +2 -2
- package/dist/src/builtin/specialExpressions/block.d.ts +2 -2
- package/dist/src/builtin/specialExpressions/cond.d.ts +2 -2
- package/dist/src/builtin/specialExpressions/if.d.ts +2 -2
- package/dist/src/builtin/specialExpressions/loop.d.ts +2 -2
- package/dist/src/builtin/specialExpressions/loops.d.ts +4 -4
- package/dist/src/builtin/specialExpressions/object.d.ts +2 -2
- package/dist/src/builtin/specialExpressions/or.d.ts +2 -2
- package/dist/src/builtin/specialExpressions/qq.d.ts +2 -2
- package/dist/src/builtin/specialExpressions/recur.d.ts +2 -2
- package/dist/src/builtin/specialExpressions/switch.d.ts +2 -2
- package/dist/src/builtin/specialExpressions/throw.d.ts +2 -2
- package/dist/src/builtin/specialExpressions/try.d.ts +2 -2
- package/dist/src/builtin/specialExpressions/unless.d.ts +2 -2
- package/dist/src/builtin/utils.d.ts +2 -2
- package/dist/src/evaluator/index.d.ts +2 -2
- package/dist/src/evaluator/interface.d.ts +4 -3
- package/dist/src/getUndefinedSymbols/index.d.ts +2 -2
- package/dist/src/parser/ParserContext.d.ts +20 -0
- package/dist/src/parser/helpers.d.ts +19 -0
- package/dist/src/parser/index.d.ts +3 -0
- package/dist/src/parser/subParsers/getPrecedence.d.ts +3 -0
- package/dist/src/parser/subParsers/parseArray.d.ts +3 -0
- package/dist/src/parser/subParsers/parseBindingTarget.d.ts +6 -0
- package/dist/src/parser/subParsers/parseCond.d.ts +4 -0
- package/dist/src/parser/subParsers/parseDo.d.ts +3 -0
- package/dist/src/parser/subParsers/parseForOrDoseq.d.ts +4 -0
- package/dist/src/parser/subParsers/parseFunction.d.ts +4 -0
- package/dist/src/parser/subParsers/parseFunctionCall.d.ts +3 -0
- package/dist/src/parser/subParsers/parseIfOrUnless.d.ts +5 -0
- package/dist/src/parser/subParsers/parseImplicitBlock.d.ts +5 -0
- package/dist/src/parser/subParsers/parseLet.d.ts +4 -0
- package/dist/src/parser/subParsers/parseLoop.d.ts +4 -0
- package/dist/src/parser/subParsers/parseNumber.d.ts +3 -0
- package/dist/src/parser/subParsers/parseObject.d.ts +3 -0
- package/dist/src/parser/subParsers/parseOperand.d.ts +3 -0
- package/dist/src/parser/subParsers/parseRegexpShorthand.d.ts +3 -0
- package/dist/src/parser/subParsers/parseReservedSymbol.d.ts +3 -0
- package/dist/src/parser/subParsers/parseString.d.ts +4 -0
- package/dist/src/parser/subParsers/parseSwitch.d.ts +4 -0
- package/dist/src/parser/subParsers/parseSymbol.d.ts +3 -0
- package/dist/src/parser/subParsers/parseTry.d.ts +4 -0
- package/dist/src/parser/types.d.ts +19 -26
- package/dist/src/testFramework/index.d.ts +3 -2
- package/dist/src/tokenizer/token.d.ts +0 -2
- package/dist/src/tokenizer/tokenizers.d.ts +2 -2
- package/dist/src/typeGuards/astNode.d.ts +18 -19
- package/dist/src/typeGuards/index.d.ts +0 -1
- package/dist/src/typeGuards/lits.d.ts +0 -1
- package/dist/testFramework.esm.js +1 -1
- package/dist/testFramework.esm.js.map +1 -1
- package/dist/testFramework.js +1 -1
- package/dist/testFramework.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/src/parser/Parser.d.ts +0 -58
- package/dist/modules/src/parser/Parser.d.ts +0 -58
- package/dist/src/parser/Parser.d.ts +0 -58
package/dist/cli/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ var readline = require('node:readline');
|
|
|
7
7
|
var os = require('node:os');
|
|
8
8
|
var process$1 = require('node:process');
|
|
9
9
|
|
|
10
|
-
var version = "2.2.
|
|
10
|
+
var version = "2.2.3";
|
|
11
11
|
|
|
12
12
|
function getCodeMarker(sourceCodeInfo) {
|
|
13
13
|
if (!sourceCodeInfo.position || !sourceCodeInfo.code)
|
|
@@ -6212,7 +6212,6 @@ const builtin = {
|
|
|
6212
6212
|
};
|
|
6213
6213
|
const normalExpressionKeys = Object.keys(normalExpressions);
|
|
6214
6214
|
const specialExpressionKeys = Object.keys(specialExpressionTypes);
|
|
6215
|
-
new Set(specialExpressionKeys);
|
|
6216
6215
|
|
|
6217
6216
|
const nonNumberReservedSymbolRecord = {
|
|
6218
6217
|
true: true,
|
|
@@ -7453,6 +7452,201 @@ function untokenize(tokenStream) {
|
|
|
7453
7452
|
}, '');
|
|
7454
7453
|
}
|
|
7455
7454
|
|
|
7455
|
+
const litsCommands = new Set([...normalExpressionKeys, ...specialExpressionKeys, ...Object.keys(reservedSymbolRecord)]);
|
|
7456
|
+
// TODO: replace with get suggestions function
|
|
7457
|
+
class AutoCompleter {
|
|
7458
|
+
originalProgram;
|
|
7459
|
+
originalPosition;
|
|
7460
|
+
prefixProgram = '';
|
|
7461
|
+
suffixProgram = '';
|
|
7462
|
+
searchString = '';
|
|
7463
|
+
suggestions = [];
|
|
7464
|
+
suggestionIndex = null;
|
|
7465
|
+
constructor(originalProgram, originalPosition, lits, params) {
|
|
7466
|
+
this.originalProgram = originalProgram;
|
|
7467
|
+
this.originalPosition = originalPosition;
|
|
7468
|
+
const partialProgram = this.originalProgram.slice(0, this.originalPosition);
|
|
7469
|
+
const tokenStream = lits.tokenize(partialProgram);
|
|
7470
|
+
const lastToken = tokenStream.tokens.at(-1);
|
|
7471
|
+
if (!lastToken) {
|
|
7472
|
+
return;
|
|
7473
|
+
}
|
|
7474
|
+
if (lastToken[0] === 'Error') {
|
|
7475
|
+
return;
|
|
7476
|
+
}
|
|
7477
|
+
this.searchString = lastToken[1];
|
|
7478
|
+
this.prefixProgram = this.originalProgram.slice(0, this.originalPosition - this.searchString.length);
|
|
7479
|
+
this.suffixProgram = this.originalProgram.slice(this.prefixProgram.length + this.searchString.length);
|
|
7480
|
+
this.originalProgram.slice(this.prefixProgram.length + this.searchString.length);
|
|
7481
|
+
this.suggestions = this.generateSuggestions(params);
|
|
7482
|
+
}
|
|
7483
|
+
getNextSuggestion() {
|
|
7484
|
+
return this.getAutoCompleteSuggestionResult(this.getNextSuggestionSymbol());
|
|
7485
|
+
}
|
|
7486
|
+
getPreviousSuggestion() {
|
|
7487
|
+
return this.getAutoCompleteSuggestionResult(this.getPreviousSuggestionSymbol());
|
|
7488
|
+
}
|
|
7489
|
+
getAutoCompleteSuggestionResult(suggestion) {
|
|
7490
|
+
if (suggestion === null) {
|
|
7491
|
+
return null;
|
|
7492
|
+
}
|
|
7493
|
+
return {
|
|
7494
|
+
program: this.prefixProgram + suggestion + this.suffixProgram,
|
|
7495
|
+
position: this.prefixProgram.length + suggestion.length,
|
|
7496
|
+
};
|
|
7497
|
+
}
|
|
7498
|
+
getNextSuggestionSymbol() {
|
|
7499
|
+
if (this.suggestions.length === 0) {
|
|
7500
|
+
return null;
|
|
7501
|
+
}
|
|
7502
|
+
if (this.suggestionIndex === null) {
|
|
7503
|
+
this.suggestionIndex = 0;
|
|
7504
|
+
}
|
|
7505
|
+
else {
|
|
7506
|
+
this.suggestionIndex += 1;
|
|
7507
|
+
if (this.suggestionIndex >= this.suggestions.length) {
|
|
7508
|
+
this.suggestionIndex = 0;
|
|
7509
|
+
}
|
|
7510
|
+
}
|
|
7511
|
+
return this.suggestions[this.suggestionIndex];
|
|
7512
|
+
}
|
|
7513
|
+
getPreviousSuggestionSymbol() {
|
|
7514
|
+
if (this.suggestions.length === 0) {
|
|
7515
|
+
return null;
|
|
7516
|
+
}
|
|
7517
|
+
if (this.suggestionIndex === null) {
|
|
7518
|
+
this.suggestionIndex = this.suggestions.length - 1;
|
|
7519
|
+
}
|
|
7520
|
+
else {
|
|
7521
|
+
this.suggestionIndex -= 1;
|
|
7522
|
+
if (this.suggestionIndex < 0) {
|
|
7523
|
+
this.suggestionIndex = this.suggestions.length - 1;
|
|
7524
|
+
}
|
|
7525
|
+
}
|
|
7526
|
+
return this.suggestions[this.suggestionIndex];
|
|
7527
|
+
}
|
|
7528
|
+
getSuggestions() {
|
|
7529
|
+
return [...this.suggestions];
|
|
7530
|
+
}
|
|
7531
|
+
getSearchString() {
|
|
7532
|
+
return this.searchString;
|
|
7533
|
+
}
|
|
7534
|
+
generateSuggestions(params) {
|
|
7535
|
+
const blacklist = new Set(['0_def', '0_defn', '0_lambda']);
|
|
7536
|
+
const startsWithCaseSensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.startsWith(this.searchString));
|
|
7537
|
+
startsWithCaseSensitive.forEach(suggestion => blacklist.add(suggestion));
|
|
7538
|
+
const startsWithCaseInsensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.toLowerCase().startsWith(this.searchString.toLowerCase()));
|
|
7539
|
+
startsWithCaseInsensitive.forEach(suggestion => blacklist.add(suggestion));
|
|
7540
|
+
const includesCaseSensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.includes(this.searchString));
|
|
7541
|
+
includesCaseSensitive.forEach(suggestion => blacklist.add(suggestion));
|
|
7542
|
+
const includesCaseInsensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.includes(this.searchString.toLowerCase()));
|
|
7543
|
+
includesCaseInsensitive.forEach(suggestion => blacklist.add(suggestion));
|
|
7544
|
+
return [...startsWithCaseSensitive, ...startsWithCaseInsensitive, ...includesCaseSensitive, ...includesCaseInsensitive];
|
|
7545
|
+
}
|
|
7546
|
+
generateWithPredicate(params, shouldInclude) {
|
|
7547
|
+
const suggestions = new Set();
|
|
7548
|
+
litsCommands.forEach((suggestion) => {
|
|
7549
|
+
if (shouldInclude(suggestion)) {
|
|
7550
|
+
suggestions.add(suggestion);
|
|
7551
|
+
}
|
|
7552
|
+
});
|
|
7553
|
+
Object.keys(params.globalContext ?? {})
|
|
7554
|
+
.filter(shouldInclude)
|
|
7555
|
+
.forEach(suggestion => suggestions.add(suggestion));
|
|
7556
|
+
params.contexts?.forEach((context) => {
|
|
7557
|
+
Object.keys(context)
|
|
7558
|
+
.filter(shouldInclude)
|
|
7559
|
+
.forEach(suggestion => suggestions.add(suggestion));
|
|
7560
|
+
});
|
|
7561
|
+
Object.keys(params.jsFunctions ?? {})
|
|
7562
|
+
.filter(shouldInclude)
|
|
7563
|
+
.forEach(suggestion => suggestions.add(suggestion));
|
|
7564
|
+
Object.keys(params.values ?? {})
|
|
7565
|
+
.filter(shouldInclude)
|
|
7566
|
+
.forEach(suggestion => suggestions.add(suggestion));
|
|
7567
|
+
return [...suggestions].sort((a, b) => a.localeCompare(b));
|
|
7568
|
+
}
|
|
7569
|
+
}
|
|
7570
|
+
|
|
7571
|
+
class ParserContext {
|
|
7572
|
+
tokens;
|
|
7573
|
+
position;
|
|
7574
|
+
storedPosition = 0;
|
|
7575
|
+
parseExpression;
|
|
7576
|
+
constructor(tokenStream) {
|
|
7577
|
+
this.tokens = tokenStream.tokens;
|
|
7578
|
+
this.position = 0;
|
|
7579
|
+
}
|
|
7580
|
+
advance() {
|
|
7581
|
+
this.position += 1;
|
|
7582
|
+
}
|
|
7583
|
+
tryPeek() {
|
|
7584
|
+
return this.tokens[this.position];
|
|
7585
|
+
}
|
|
7586
|
+
peek() {
|
|
7587
|
+
const token = this.tokens[this.position];
|
|
7588
|
+
if (!token) {
|
|
7589
|
+
const lastToken = this.tokens.at(-1);
|
|
7590
|
+
const sourceCodeInfo = lastToken ? lastToken[2] : undefined;
|
|
7591
|
+
throw new LitsError('Unexpected end of input', sourceCodeInfo);
|
|
7592
|
+
}
|
|
7593
|
+
return token;
|
|
7594
|
+
}
|
|
7595
|
+
isAtEnd() {
|
|
7596
|
+
return this.position >= this.tokens.length;
|
|
7597
|
+
}
|
|
7598
|
+
// TODO rename to getSourceCodeInfo
|
|
7599
|
+
peekSourceCodeInfo() {
|
|
7600
|
+
const currentToken = this.tryPeek();
|
|
7601
|
+
return currentToken ? currentToken[2] : this.tokens.at(-1)?.[2];
|
|
7602
|
+
}
|
|
7603
|
+
storePosition() {
|
|
7604
|
+
return this.storedPosition = this.position;
|
|
7605
|
+
}
|
|
7606
|
+
restorePosition() {
|
|
7607
|
+
this.position = this.storedPosition;
|
|
7608
|
+
}
|
|
7609
|
+
peekAhead(count) {
|
|
7610
|
+
return this.tokens[this.position + count];
|
|
7611
|
+
}
|
|
7612
|
+
getPosition() {
|
|
7613
|
+
return this.position;
|
|
7614
|
+
}
|
|
7615
|
+
getTokenAt(pos) {
|
|
7616
|
+
return this.tokens[pos];
|
|
7617
|
+
}
|
|
7618
|
+
}
|
|
7619
|
+
|
|
7620
|
+
const exponentiationPrecedence = 12;
|
|
7621
|
+
const binaryFunctionalOperatorPrecedence = 3;
|
|
7622
|
+
const conditionalOperatorPrecedence = 1;
|
|
7623
|
+
function withSourceCodeInfo(node, sourceCodeInfo) {
|
|
7624
|
+
if (sourceCodeInfo) {
|
|
7625
|
+
node[2] = sourceCodeInfo;
|
|
7626
|
+
}
|
|
7627
|
+
return node;
|
|
7628
|
+
}
|
|
7629
|
+
function stringToSymbolNode(value, sourceCodeInfo) {
|
|
7630
|
+
if (specialExpressionTypes[value] !== undefined && value !== 'fn' && value !== 'def' && value !== 'defn') {
|
|
7631
|
+
return withSourceCodeInfo([NodeTypes.SpecialBuiltinSymbol, specialExpressionTypes[value]], sourceCodeInfo);
|
|
7632
|
+
}
|
|
7633
|
+
if (normalExpressionTypes[value] !== undefined) {
|
|
7634
|
+
return withSourceCodeInfo([NodeTypes.NormalBuiltinSymbol, normalExpressionTypes[value]], sourceCodeInfo);
|
|
7635
|
+
}
|
|
7636
|
+
return withSourceCodeInfo([NodeTypes.UserDefinedSymbol, value], sourceCodeInfo);
|
|
7637
|
+
}
|
|
7638
|
+
function stringFromQuotedSymbol(value) {
|
|
7639
|
+
return value.substring(1, value.length - 1)
|
|
7640
|
+
.replace(/(\\{2})|(\\')|\\(.)/g, (_, backslash, singleQuote, normalChar) => {
|
|
7641
|
+
if (backslash) {
|
|
7642
|
+
return '\\';
|
|
7643
|
+
}
|
|
7644
|
+
if (singleQuote) {
|
|
7645
|
+
return '\'';
|
|
7646
|
+
}
|
|
7647
|
+
return `\\${normalChar}`;
|
|
7648
|
+
});
|
|
7649
|
+
}
|
|
7456
7650
|
// Reverse lookup tables for getting symbol names from builtin types
|
|
7457
7651
|
const normalExpressionNames = Object.entries(normalExpressionTypes).reduce((acc, [name, index]) => {
|
|
7458
7652
|
acc[index] = name;
|
|
@@ -7478,62 +7672,6 @@ function getSymbolName(symbol) {
|
|
|
7478
7672
|
// SpecialBuiltinSymbolNode
|
|
7479
7673
|
return specialExpressionNames[symbol[1]];
|
|
7480
7674
|
}
|
|
7481
|
-
const exponentiationPrecedence = 12;
|
|
7482
|
-
const binaryFunctionalOperatorPrecedence = 3;
|
|
7483
|
-
const conditionalOperatorPrecedence = 1;
|
|
7484
|
-
const maxShorthandLambdaArity = 20;
|
|
7485
|
-
const placeholderRegexp = /^\$([1-9]\d?)?$/;
|
|
7486
|
-
function withSourceCodeInfo(node, sourceCodeInfo) {
|
|
7487
|
-
if (sourceCodeInfo) {
|
|
7488
|
-
node[2] = sourceCodeInfo;
|
|
7489
|
-
}
|
|
7490
|
-
return node;
|
|
7491
|
-
}
|
|
7492
|
-
function getPrecedence(operatorSign, sourceCodeInfo) {
|
|
7493
|
-
switch (operatorSign) {
|
|
7494
|
-
case '^': // exponentiation
|
|
7495
|
-
return exponentiationPrecedence;
|
|
7496
|
-
case '*': // multiplication
|
|
7497
|
-
case '/': // division
|
|
7498
|
-
case '%': // remainder
|
|
7499
|
-
return 11;
|
|
7500
|
-
case '+': // addition
|
|
7501
|
-
case '-': // subtraction
|
|
7502
|
-
return 10;
|
|
7503
|
-
case '<<': // left shift
|
|
7504
|
-
case '>>': // signed right shift
|
|
7505
|
-
case '>>>': // unsigned right shift
|
|
7506
|
-
return 9;
|
|
7507
|
-
case '++': // string concatenation
|
|
7508
|
-
return 8;
|
|
7509
|
-
case '<': // less than
|
|
7510
|
-
case '<=': // less than or equal
|
|
7511
|
-
case '≤': // less than or equal
|
|
7512
|
-
case '>': // greater than
|
|
7513
|
-
case '>=': // greater than or equal
|
|
7514
|
-
case '≥': // greater than or equal
|
|
7515
|
-
return 7;
|
|
7516
|
-
case '==': // equal
|
|
7517
|
-
case '!=': // not equal
|
|
7518
|
-
case '≠': // not equal
|
|
7519
|
-
return 6;
|
|
7520
|
-
case '&': // bitwise AND
|
|
7521
|
-
case 'xor': // bitwise XOR
|
|
7522
|
-
case '|': // bitwise OR
|
|
7523
|
-
return 5;
|
|
7524
|
-
case '&&': // logical AND
|
|
7525
|
-
case '||': // logical OR
|
|
7526
|
-
case '??': // nullish coalescing
|
|
7527
|
-
return 4;
|
|
7528
|
-
// leave room for binaryFunctionalOperatorPrecedence = 3
|
|
7529
|
-
case '|>': // pipe
|
|
7530
|
-
return 2;
|
|
7531
|
-
// leave room for conditionalOperatorPrecedence = 1
|
|
7532
|
-
/* v8 ignore next 2 */
|
|
7533
|
-
default:
|
|
7534
|
-
throw new LitsError(`Unknown binary operator: ${operatorSign}`, sourceCodeInfo);
|
|
7535
|
-
}
|
|
7536
|
-
}
|
|
7537
7675
|
function createNamedNormalExpressionNode(symbolNode, params, sourceCodeInfo) {
|
|
7538
7676
|
const node = withSourceCodeInfo([NodeTypes.NormalExpression, [symbolNode, params]], sourceCodeInfo);
|
|
7539
7677
|
if (isNormalBuiltinSymbolNode(symbolNode)) {
|
|
@@ -7541,8 +7679,18 @@ function createNamedNormalExpressionNode(symbolNode, params, sourceCodeInfo) {
|
|
|
7541
7679
|
}
|
|
7542
7680
|
return node;
|
|
7543
7681
|
}
|
|
7544
|
-
function
|
|
7545
|
-
|
|
7682
|
+
function isAtExpressionEnd(ctx) {
|
|
7683
|
+
if (ctx.isAtEnd()) {
|
|
7684
|
+
return true;
|
|
7685
|
+
}
|
|
7686
|
+
const token = ctx.tryPeek();
|
|
7687
|
+
if (isOperatorToken(token)) {
|
|
7688
|
+
return [';', ',', ':'].includes(token[1]);
|
|
7689
|
+
}
|
|
7690
|
+
if (isReservedSymbolToken(token)) {
|
|
7691
|
+
return ['else', 'when', 'while', 'case', 'catch', 'let', 'then', 'end', 'do'].includes(token[1]);
|
|
7692
|
+
}
|
|
7693
|
+
return false;
|
|
7546
7694
|
}
|
|
7547
7695
|
function fromBinaryOperatorToNode(operator, symbolNode, left, right, sourceCodeInfo) {
|
|
7548
7696
|
const operatorName = operator[1];
|
|
@@ -7589,1153 +7737,1048 @@ function fromBinaryOperatorToNode(operator, symbolNode, left, right, sourceCodeI
|
|
|
7589
7737
|
throw new LitsError(`Unknown binary operator: ${operatorName}`, sourceCodeInfo);
|
|
7590
7738
|
}
|
|
7591
7739
|
}
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7598
|
-
}
|
|
7599
|
-
peek() {
|
|
7600
|
-
return this.tokenStream.tokens[this.parseState.position];
|
|
7601
|
-
}
|
|
7602
|
-
peekSourceCodeInfo() {
|
|
7603
|
-
const currentToken = this.peek();
|
|
7604
|
-
return currentToken ? currentToken[2] : this.tokenStream.tokens.at(-1)?.[2];
|
|
7740
|
+
|
|
7741
|
+
function parseSymbol(ctx) {
|
|
7742
|
+
const token = ctx.peek();
|
|
7743
|
+
ctx.advance();
|
|
7744
|
+
if (!isSymbolToken(token)) {
|
|
7745
|
+
throw new LitsError(`Expected symbol token, got ${token[0]}`, token[2]);
|
|
7605
7746
|
}
|
|
7606
|
-
|
|
7607
|
-
return
|
|
7747
|
+
if (token[1][0] === '\'') {
|
|
7748
|
+
return stringToSymbolNode(stringFromQuotedSymbol(token[1]), token[2]);
|
|
7608
7749
|
}
|
|
7609
|
-
|
|
7610
|
-
|
|
7750
|
+
else {
|
|
7751
|
+
return stringToSymbolNode(token[1], token[2]);
|
|
7611
7752
|
}
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7617
|
-
|
|
7618
|
-
const
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
if (!this.isAtEnd()) {
|
|
7626
|
-
throw new LitsError('Expected ;', this.peekSourceCodeInfo());
|
|
7627
|
-
}
|
|
7628
|
-
}
|
|
7753
|
+
}
|
|
7754
|
+
|
|
7755
|
+
function parseBindingTarget(ctx, { requireDefaultValue, noRest } = {}) {
|
|
7756
|
+
const firstToken = ctx.tryPeek();
|
|
7757
|
+
// Symbol
|
|
7758
|
+
if (isSymbolToken(firstToken)) {
|
|
7759
|
+
const symbol = parseSymbol(ctx);
|
|
7760
|
+
if (!isUserDefinedSymbolNode(symbol)) {
|
|
7761
|
+
throw new LitsError('Expected user defined symbol', firstToken[2]);
|
|
7762
|
+
}
|
|
7763
|
+
const defaultValue = parseOptionalDefaulValue(ctx);
|
|
7764
|
+
if (requireDefaultValue && !defaultValue) {
|
|
7765
|
+
throw new LitsError('Expected assignment', ctx.peekSourceCodeInfo());
|
|
7629
7766
|
}
|
|
7630
|
-
return
|
|
7767
|
+
return withSourceCodeInfo([bindingTargetTypes.symbol, [symbol, defaultValue]], firstToken[2]);
|
|
7631
7768
|
}
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
switch (firstToken[1]) {
|
|
7637
|
-
case 'let':
|
|
7638
|
-
return this.parseLet(firstToken);
|
|
7639
|
-
case 'if':
|
|
7640
|
-
case 'unless':
|
|
7641
|
-
left = this.parseIfOrUnless(firstToken);
|
|
7642
|
-
break;
|
|
7643
|
-
case 'cond':
|
|
7644
|
-
left = this.parseCond(firstToken);
|
|
7645
|
-
break;
|
|
7646
|
-
case 'switch':
|
|
7647
|
-
left = this.parseSwitch(firstToken);
|
|
7648
|
-
break;
|
|
7649
|
-
case 'for':
|
|
7650
|
-
case 'doseq':
|
|
7651
|
-
left = this.parseForOrDoseq(firstToken);
|
|
7652
|
-
break;
|
|
7653
|
-
case 'loop':
|
|
7654
|
-
left = this.parseLoop(firstToken);
|
|
7655
|
-
break;
|
|
7656
|
-
case 'try':
|
|
7657
|
-
left = this.parseTry(firstToken);
|
|
7658
|
-
break;
|
|
7659
|
-
}
|
|
7769
|
+
// Rest
|
|
7770
|
+
if (isOperatorToken(firstToken, '...')) {
|
|
7771
|
+
if (noRest) {
|
|
7772
|
+
throw new LitsError('Rest element not allowed', firstToken[2]);
|
|
7660
7773
|
}
|
|
7661
|
-
|
|
7662
|
-
|
|
7774
|
+
ctx.advance();
|
|
7775
|
+
const symbol = asUserDefinedSymbolNode(parseSymbol(ctx));
|
|
7776
|
+
if (isOperatorToken(ctx.tryPeek(), '=')) {
|
|
7777
|
+
throw new LitsError('Rest argument can not have default value', ctx.peekSourceCodeInfo());
|
|
7663
7778
|
}
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
|
|
7779
|
+
return withSourceCodeInfo([bindingTargetTypes.rest, [symbol[1], undefined]], firstToken[2]);
|
|
7780
|
+
}
|
|
7781
|
+
// Array
|
|
7782
|
+
if (isLBracketToken(firstToken)) {
|
|
7783
|
+
ctx.advance();
|
|
7784
|
+
const elements = [];
|
|
7785
|
+
let token = ctx.peek();
|
|
7786
|
+
let rest = false;
|
|
7787
|
+
while (!isRBracketToken(token)) {
|
|
7788
|
+
if (rest) {
|
|
7789
|
+
throw new LitsError('Rest argument must be last', token[2]);
|
|
7667
7790
|
}
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
if (isA_BinaryOperatorToken(operator)) {
|
|
7674
|
-
const name = operator[1];
|
|
7675
|
-
const newPrecedece = getPrecedence(name, operator[2]);
|
|
7676
|
-
if (newPrecedece <= precedence
|
|
7677
|
-
// ^ (exponentiation) is right associative
|
|
7678
|
-
&& !(newPrecedece === exponentiationPrecedence && precedence === exponentiationPrecedence)) {
|
|
7679
|
-
break;
|
|
7680
|
-
}
|
|
7681
|
-
const symbol = specialExpressionTypes[name]
|
|
7682
|
-
? withSourceCodeInfo([NodeTypes.SpecialBuiltinSymbol, specialExpressionTypes[name]], operator[2])
|
|
7683
|
-
: withSourceCodeInfo([NodeTypes.NormalBuiltinSymbol, normalExpressionTypes[name]], operator[2]);
|
|
7684
|
-
this.advance();
|
|
7685
|
-
const right = this.parseExpression(newPrecedece);
|
|
7686
|
-
left = fromBinaryOperatorToNode(operator, symbol, left, right, operator[2]);
|
|
7687
|
-
}
|
|
7688
|
-
else if (isSymbolToken(operator)) {
|
|
7689
|
-
if (!isFunctionOperator(operator[1])) {
|
|
7690
|
-
break;
|
|
7691
|
-
}
|
|
7692
|
-
const newPrecedece = binaryFunctionalOperatorPrecedence;
|
|
7693
|
-
if (newPrecedece <= precedence) {
|
|
7694
|
-
break;
|
|
7695
|
-
}
|
|
7696
|
-
const operatorSymbol = this.parseSymbol();
|
|
7697
|
-
const right = this.parseExpression(newPrecedece);
|
|
7698
|
-
if (isSpecialBuiltinSymbolNode(operatorSymbol)) {
|
|
7699
|
-
throw new LitsError('Special expressions are not allowed in binary functional operators', operatorSymbol[2]);
|
|
7700
|
-
}
|
|
7701
|
-
left = createNamedNormalExpressionNode(operatorSymbol, [left, right], operator[2]);
|
|
7791
|
+
if (isOperatorToken(token, ',')) {
|
|
7792
|
+
elements.push(null);
|
|
7793
|
+
ctx.advance();
|
|
7794
|
+
token = ctx.peek();
|
|
7795
|
+
continue;
|
|
7702
7796
|
}
|
|
7703
|
-
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
}
|
|
7707
|
-
this.advance();
|
|
7708
|
-
const trueNode = this.parseExpression();
|
|
7709
|
-
if (!isOperatorToken(this.peek(), ':')) {
|
|
7710
|
-
throw new LitsError('Expected :', this.peekSourceCodeInfo());
|
|
7711
|
-
}
|
|
7712
|
-
this.advance();
|
|
7713
|
-
const falseNode = this.parseExpression();
|
|
7714
|
-
left = withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.if, [left, trueNode, falseNode]]], left[2]);
|
|
7797
|
+
const target = parseBindingTarget(ctx);
|
|
7798
|
+
if (target[0] === bindingTargetTypes.rest) {
|
|
7799
|
+
rest = true;
|
|
7715
7800
|
}
|
|
7716
|
-
|
|
7717
|
-
|
|
7801
|
+
elements.push(target);
|
|
7802
|
+
token = ctx.peek();
|
|
7803
|
+
if (!isRBracketToken(token)) {
|
|
7804
|
+
assertOperatorToken(token, ',');
|
|
7805
|
+
ctx.advance();
|
|
7718
7806
|
}
|
|
7719
|
-
|
|
7807
|
+
token = ctx.peek();
|
|
7720
7808
|
}
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
throw new LitsError('Unexpected end of input', this.peekSourceCodeInfo());
|
|
7809
|
+
ctx.advance();
|
|
7810
|
+
const defaultValue = parseOptionalDefaulValue(ctx);
|
|
7811
|
+
if (requireDefaultValue && !defaultValue) {
|
|
7812
|
+
throw new LitsError('Expected assignment', ctx.peekSourceCodeInfo());
|
|
7726
7813
|
}
|
|
7727
|
-
return
|
|
7814
|
+
return withSourceCodeInfo([bindingTargetTypes.array, [elements, defaultValue]], firstToken[2]);
|
|
7728
7815
|
}
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
7735
|
-
|
|
7736
|
-
|
|
7737
|
-
|
|
7738
|
-
}
|
|
7739
|
-
const stringNode = withSourceCodeInfo([NodeTypes.String, symbolToken[1]], symbolToken[2]);
|
|
7740
|
-
operand = createAccessorNode(operand, stringNode, token[2]);
|
|
7741
|
-
this.advance();
|
|
7742
|
-
token = this.peek();
|
|
7743
|
-
}
|
|
7744
|
-
else if (isLBracketToken(token)) {
|
|
7745
|
-
this.advance();
|
|
7746
|
-
const expression = this.parseExpression();
|
|
7747
|
-
if (!isRBracketToken(this.peek())) {
|
|
7748
|
-
throw new LitsError('Expected closing bracket', this.peekSourceCodeInfo());
|
|
7749
|
-
}
|
|
7750
|
-
operand = createAccessorNode(operand, expression, token[2]);
|
|
7751
|
-
this.advance();
|
|
7752
|
-
token = this.peek();
|
|
7753
|
-
}
|
|
7754
|
-
else if (isLParenToken(token)) {
|
|
7755
|
-
operand = this.parseFunctionCall(operand);
|
|
7756
|
-
token = this.peek();
|
|
7816
|
+
// Object
|
|
7817
|
+
if (isLBraceToken(firstToken)) {
|
|
7818
|
+
ctx.advance();
|
|
7819
|
+
const elements = {};
|
|
7820
|
+
let token = ctx.peek();
|
|
7821
|
+
let rest = false;
|
|
7822
|
+
while (!isRBraceToken(token)) {
|
|
7823
|
+
if (rest) {
|
|
7824
|
+
throw new LitsError('Rest argument must be last', token[2]);
|
|
7757
7825
|
}
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
|
|
7761
|
-
parseOperandPart() {
|
|
7762
|
-
const token = this.asToken(this.peek());
|
|
7763
|
-
// Parentheses
|
|
7764
|
-
if (isLParenToken(token)) {
|
|
7765
|
-
const positionBefore = this.parseState.position;
|
|
7766
|
-
const lamdaFunction = this.parseLambdaFunction();
|
|
7767
|
-
if (lamdaFunction) {
|
|
7768
|
-
return lamdaFunction;
|
|
7826
|
+
if (isOperatorToken(token, '...')) {
|
|
7827
|
+
rest = true;
|
|
7828
|
+
ctx.advance();
|
|
7769
7829
|
}
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
const
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
return expression;
|
|
7778
|
-
}
|
|
7779
|
-
else if (isOperatorToken(token)) {
|
|
7780
|
-
const operatorName = token[1];
|
|
7781
|
-
if (isBinaryOperator(operatorName)) {
|
|
7782
|
-
this.advance();
|
|
7783
|
-
if (specialExpressionTypes[operatorName] !== undefined) {
|
|
7784
|
-
return withSourceCodeInfo([NodeTypes.SpecialBuiltinSymbol, specialExpressionTypes[operatorName]], token[2]);
|
|
7830
|
+
// Parse the key symbol - can be any symbol type (including builtins) when using 'as' alias
|
|
7831
|
+
const keySymbol = parseSymbol(ctx);
|
|
7832
|
+
const keyName = getSymbolName(keySymbol);
|
|
7833
|
+
token = ctx.peek();
|
|
7834
|
+
if (isReservedSymbolToken(token, 'as')) {
|
|
7835
|
+
if (rest) {
|
|
7836
|
+
throw new LitsError('Rest argument can not have alias', token[2]);
|
|
7785
7837
|
}
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
}
|
|
7791
|
-
else {
|
|
7792
|
-
throw new LitsError(`Illegal operator: ${operatorName}`, token[2]);
|
|
7793
|
-
}
|
|
7794
|
-
}
|
|
7795
|
-
// Object litteral, e.g. {a: 1, b: 2}
|
|
7796
|
-
if (isLBraceToken(token)) {
|
|
7797
|
-
return this.parseObject();
|
|
7798
|
-
}
|
|
7799
|
-
// Array litteral, e.g. [1, 2]
|
|
7800
|
-
if (isLBracketToken(token)) {
|
|
7801
|
-
return this.parseArray();
|
|
7802
|
-
}
|
|
7803
|
-
const tokenType = token[0];
|
|
7804
|
-
switch (tokenType) {
|
|
7805
|
-
case 'Number':
|
|
7806
|
-
case 'BasePrefixedNumber':
|
|
7807
|
-
return this.parseNumber();
|
|
7808
|
-
case 'string':
|
|
7809
|
-
return this.parseString(token);
|
|
7810
|
-
case 'Symbol': {
|
|
7811
|
-
const positionBefore = this.parseState.position;
|
|
7812
|
-
const lamdaFunction = this.parseLambdaFunction();
|
|
7813
|
-
if (lamdaFunction) {
|
|
7814
|
-
return lamdaFunction;
|
|
7838
|
+
ctx.advance();
|
|
7839
|
+
const name = asUserDefinedSymbolNode(parseSymbol(ctx));
|
|
7840
|
+
if (elements[name[1]]) {
|
|
7841
|
+
throw new LitsError(`Duplicate binding name: ${name}`, token[2]);
|
|
7815
7842
|
}
|
|
7816
|
-
|
|
7817
|
-
return this.parseSymbol();
|
|
7818
|
-
}
|
|
7819
|
-
case 'ReservedSymbol':
|
|
7820
|
-
return this.parseReservedSymbol();
|
|
7821
|
-
case 'RegexpShorthand':
|
|
7822
|
-
return this.parseRegexpShorthand();
|
|
7823
|
-
default:
|
|
7824
|
-
throw new LitsError(`Unknown token type: ${tokenType}`, token[2]);
|
|
7825
|
-
}
|
|
7826
|
-
}
|
|
7827
|
-
parseObject() {
|
|
7828
|
-
const firstToken = asLBraceToken(this.peek());
|
|
7829
|
-
this.advance();
|
|
7830
|
-
const params = [];
|
|
7831
|
-
while (!this.isAtEnd() && !isRBraceToken(this.peek())) {
|
|
7832
|
-
if (isOperatorToken(this.peek(), '...')) {
|
|
7833
|
-
this.advance();
|
|
7834
|
-
params.push(withSourceCodeInfo([NodeTypes.Spread, this.parseExpression()], this.peekSourceCodeInfo()));
|
|
7843
|
+
elements[keyName] = withSourceCodeInfo([bindingTargetTypes.symbol, [name, parseOptionalDefaulValue(ctx)]], firstToken[2]);
|
|
7835
7844
|
}
|
|
7836
|
-
else {
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
}
|
|
7842
|
-
else if (isSymbolToken(token)) {
|
|
7843
|
-
const value = token[1].startsWith('\'')
|
|
7844
|
-
? this.stringFromQuotedSymbol(token[1])
|
|
7845
|
-
: token[1];
|
|
7846
|
-
params.push(withSourceCodeInfo([NodeTypes.String, value], token[2]));
|
|
7847
|
-
this.advance();
|
|
7845
|
+
else if (isRBraceToken(token) || isOperatorToken(token, ',') || isOperatorToken(token, '=')) {
|
|
7846
|
+
// Without 'as' alias, the key becomes the binding name - must be user-defined symbol
|
|
7847
|
+
const key = asUserDefinedSymbolNode(keySymbol, keySymbol[2]);
|
|
7848
|
+
if (elements[key[1]]) {
|
|
7849
|
+
throw new LitsError(`Duplicate binding name: ${key}`, token[2]);
|
|
7848
7850
|
}
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
params.push(this.parseExpression());
|
|
7852
|
-
assertRBracketToken(this.peek());
|
|
7853
|
-
this.advance();
|
|
7851
|
+
if (rest && isOperatorToken(ctx.tryPeek(), '=')) {
|
|
7852
|
+
throw new LitsError('Rest argument can not have default value', ctx.peekSourceCodeInfo());
|
|
7854
7853
|
}
|
|
7855
|
-
|
|
7856
|
-
|
|
7854
|
+
elements[key[1]] = rest
|
|
7855
|
+
? withSourceCodeInfo([bindingTargetTypes.rest, [key[1], parseOptionalDefaulValue(ctx)]], firstToken[2])
|
|
7856
|
+
: withSourceCodeInfo([bindingTargetTypes.symbol, [key, parseOptionalDefaulValue(ctx)]], firstToken[2]);
|
|
7857
|
+
}
|
|
7858
|
+
else if (isOperatorToken(token, ':')) {
|
|
7859
|
+
ctx.advance();
|
|
7860
|
+
token = ctx.peek();
|
|
7861
|
+
if (!isLBraceToken(token) && !isLBracketToken(token)) {
|
|
7862
|
+
throw new LitsError('Expected object or array', token[2]);
|
|
7857
7863
|
}
|
|
7858
|
-
|
|
7859
|
-
this.advance();
|
|
7860
|
-
params.push(this.parseExpression());
|
|
7861
|
-
}
|
|
7862
|
-
const nextToken = this.peek();
|
|
7863
|
-
if (!isOperatorToken(nextToken, ',') && !isRBraceToken(nextToken)) {
|
|
7864
|
-
throw new LitsError('Expected comma or closing brace', this.peekSourceCodeInfo());
|
|
7864
|
+
elements[keyName] = parseBindingTarget(ctx);
|
|
7865
7865
|
}
|
|
7866
|
-
if (
|
|
7867
|
-
|
|
7866
|
+
if (!isRBraceToken(ctx.peek())) {
|
|
7867
|
+
assertOperatorToken(ctx.peek(), ',');
|
|
7868
|
+
ctx.advance();
|
|
7868
7869
|
}
|
|
7870
|
+
token = ctx.peek();
|
|
7869
7871
|
}
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
|
|
7872
|
+
ctx.advance();
|
|
7873
|
+
token = ctx.peek();
|
|
7874
|
+
const defaultValue = parseOptionalDefaulValue(ctx);
|
|
7875
|
+
if (requireDefaultValue && !defaultValue) {
|
|
7876
|
+
throw new LitsError('Expected assignment', token[2]);
|
|
7877
|
+
}
|
|
7878
|
+
return withSourceCodeInfo([bindingTargetTypes.object, [elements, defaultValue]], firstToken[2]);
|
|
7873
7879
|
}
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
|
|
7884
|
-
|
|
7885
|
-
|
|
7886
|
-
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
|
|
7891
|
-
|
|
7892
|
-
|
|
7880
|
+
throw new LitsError('Expected symbol', ctx.peekSourceCodeInfo());
|
|
7881
|
+
}
|
|
7882
|
+
function parseOptionalDefaulValue(ctx) {
|
|
7883
|
+
if (isOperatorToken(ctx.tryPeek(), '=')) {
|
|
7884
|
+
ctx.advance();
|
|
7885
|
+
return ctx.parseExpression();
|
|
7886
|
+
}
|
|
7887
|
+
return undefined;
|
|
7888
|
+
}
|
|
7889
|
+
|
|
7890
|
+
function parseLet(ctx, token) {
|
|
7891
|
+
ctx.advance();
|
|
7892
|
+
const target = parseBindingTarget(ctx, { requireDefaultValue: true, noRest: true });
|
|
7893
|
+
const value = target[1][1];
|
|
7894
|
+
target[1][1] = undefined;
|
|
7895
|
+
const bindingTarget = withSourceCodeInfo([NodeTypes.Binding, [target, value]], token[2]);
|
|
7896
|
+
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.let, bindingTarget]], token[2]);
|
|
7897
|
+
}
|
|
7898
|
+
|
|
7899
|
+
function parseImplicitBlock(ctx, ends) {
|
|
7900
|
+
const nodes = [];
|
|
7901
|
+
while (!ctx.isAtEnd() && !isImplicitBlockEnd(ctx, ends)) {
|
|
7902
|
+
if (isOperatorToken(ctx.tryPeek(), ';')) {
|
|
7903
|
+
ctx.advance();
|
|
7904
|
+
}
|
|
7905
|
+
else {
|
|
7906
|
+
nodes.push(ctx.parseExpression());
|
|
7893
7907
|
}
|
|
7894
|
-
assertRBracketToken(this.peek());
|
|
7895
|
-
this.advance();
|
|
7896
|
-
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.array, params]], firstToken[2]);
|
|
7897
7908
|
}
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
7902
|
-
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
}
|
|
7909
|
+
assertImplicitBlockEnd(ctx, ends);
|
|
7910
|
+
if (nodes.length === 0) {
|
|
7911
|
+
throw new LitsError('Expected expression', ctx.peekSourceCodeInfo());
|
|
7912
|
+
}
|
|
7913
|
+
return nodes.length === 1
|
|
7914
|
+
? nodes[0]
|
|
7915
|
+
: withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.block, nodes]], ctx.peekSourceCodeInfo());
|
|
7916
|
+
}
|
|
7917
|
+
function assertImplicitBlockEnd(ctx, ends) {
|
|
7918
|
+
if (!isImplicitBlockEnd(ctx, ends)) {
|
|
7919
|
+
throw new LitsError(`Expected ${ends.map(e => e[1]).join(' or ')}`, ctx.peekSourceCodeInfo());
|
|
7920
|
+
}
|
|
7921
|
+
}
|
|
7922
|
+
function isImplicitBlockEnd(ctx, ends) {
|
|
7923
|
+
for (const end of ends) {
|
|
7924
|
+
if (isReservedSymbolToken(ctx.tryPeek(), end)) {
|
|
7925
|
+
return true;
|
|
7916
7926
|
}
|
|
7917
|
-
|
|
7918
|
-
|
|
7927
|
+
}
|
|
7928
|
+
return false;
|
|
7929
|
+
}
|
|
7930
|
+
|
|
7931
|
+
function parseIfOrUnless(ctx, token) {
|
|
7932
|
+
const isUnless = token[1] === 'unless';
|
|
7933
|
+
ctx.advance();
|
|
7934
|
+
const condition = ctx.parseExpression();
|
|
7935
|
+
assertReservedSymbolToken(ctx.tryPeek(), 'then');
|
|
7936
|
+
ctx.advance();
|
|
7937
|
+
const thenExpression = parseImplicitBlock(ctx, ['else', 'end']);
|
|
7938
|
+
let elseExpression;
|
|
7939
|
+
if (isReservedSymbolToken(ctx.tryPeek(), 'else')) {
|
|
7940
|
+
ctx.advance();
|
|
7941
|
+
elseExpression = parseImplicitBlock(ctx, ['end']);
|
|
7942
|
+
}
|
|
7943
|
+
ctx.advance();
|
|
7944
|
+
return isUnless
|
|
7945
|
+
? withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.unless, [condition, thenExpression, elseExpression]]], token[2])
|
|
7946
|
+
: withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.if, [condition, thenExpression, elseExpression]]], token[2]);
|
|
7947
|
+
}
|
|
7948
|
+
|
|
7949
|
+
function parseCond(ctx, token) {
|
|
7950
|
+
ctx.advance();
|
|
7951
|
+
const params = [];
|
|
7952
|
+
while (!ctx.isAtEnd() && !isReservedSymbolToken(ctx.tryPeek(), 'end')) {
|
|
7953
|
+
assertReservedSymbolToken(ctx.tryPeek(), 'case');
|
|
7954
|
+
ctx.advance();
|
|
7955
|
+
const caseExpression = ctx.parseExpression();
|
|
7956
|
+
assertReservedSymbolToken(ctx.tryPeek(), 'then');
|
|
7957
|
+
ctx.advance();
|
|
7958
|
+
const thenExpression = parseImplicitBlock(ctx, ['case', 'end']);
|
|
7959
|
+
params.push([caseExpression, thenExpression]);
|
|
7960
|
+
if (isReservedSymbolToken(ctx.tryPeek(), 'end')) {
|
|
7961
|
+
break;
|
|
7919
7962
|
}
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
const [param] = params;
|
|
7941
|
-
return withSourceCodeInfo([NodeTypes.SpecialExpression, [type, param]], symbol[2]);
|
|
7942
|
-
}
|
|
7943
|
-
case specialExpressionTypes.throw: {
|
|
7944
|
-
const [param] = params;
|
|
7945
|
-
return withSourceCodeInfo([NodeTypes.SpecialExpression, [type, param]], symbol[2]);
|
|
7946
|
-
}
|
|
7947
|
-
case specialExpressionTypes['0_lambda']:
|
|
7948
|
-
case specialExpressionTypes['0_def']:
|
|
7949
|
-
throw new LitsError(`${type} is not allowed`, symbol[2]);
|
|
7950
|
-
/* v8 ignore next 2 */
|
|
7951
|
-
default:
|
|
7952
|
-
throw new LitsError(`Unknown special expression: ${type}`, symbol[2]);
|
|
7953
|
-
}
|
|
7963
|
+
}
|
|
7964
|
+
assertReservedSymbolToken(ctx.tryPeek());
|
|
7965
|
+
ctx.advance();
|
|
7966
|
+
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.cond, params]], token[2]);
|
|
7967
|
+
}
|
|
7968
|
+
|
|
7969
|
+
function parseSwitch(ctx, token) {
|
|
7970
|
+
ctx.advance();
|
|
7971
|
+
const valueExpression = ctx.parseExpression();
|
|
7972
|
+
const params = [];
|
|
7973
|
+
while (!ctx.isAtEnd() && !isReservedSymbolToken(ctx.tryPeek(), 'end')) {
|
|
7974
|
+
assertReservedSymbolToken(ctx.tryPeek(), 'case');
|
|
7975
|
+
ctx.advance();
|
|
7976
|
+
const caseExpression = ctx.parseExpression();
|
|
7977
|
+
assertReservedSymbolToken(ctx.tryPeek(), 'then');
|
|
7978
|
+
ctx.advance();
|
|
7979
|
+
const thenExpression = parseImplicitBlock(ctx, ['case', 'end']);
|
|
7980
|
+
params.push([caseExpression, thenExpression]);
|
|
7981
|
+
if (isReservedSymbolToken(ctx.tryPeek(), 'end')) {
|
|
7982
|
+
break;
|
|
7954
7983
|
}
|
|
7955
|
-
|
|
7956
|
-
|
|
7984
|
+
}
|
|
7985
|
+
assertReservedSymbolToken(ctx.tryPeek(), 'end');
|
|
7986
|
+
ctx.advance();
|
|
7987
|
+
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.switch, valueExpression, params]], token[2]);
|
|
7988
|
+
}
|
|
7989
|
+
|
|
7990
|
+
function parseForOrDoseq(ctx, firstToken) {
|
|
7991
|
+
const isDoseq = firstToken[1] === 'doseq';
|
|
7992
|
+
ctx.advance();
|
|
7993
|
+
assertLParenToken(ctx.tryPeek());
|
|
7994
|
+
ctx.advance();
|
|
7995
|
+
const forLoopBindings = [];
|
|
7996
|
+
while (!ctx.isAtEnd() && !isRParenToken(ctx.tryPeek())) {
|
|
7997
|
+
const loopBinding = parseForLoopBinding(ctx);
|
|
7998
|
+
const existingBoundNames = forLoopBindings.flatMap(b => Object.keys(getAllBindingTargetNames(b[0][1][0])));
|
|
7999
|
+
const newBoundNames = getAllBindingTargetNames(loopBinding[0][1][0]);
|
|
8000
|
+
if (Object.keys(newBoundNames).some(n => existingBoundNames.includes(n))) {
|
|
8001
|
+
throw new LitsError('Duplicate binding', loopBinding[0][2]);
|
|
8002
|
+
}
|
|
8003
|
+
forLoopBindings.push(loopBinding);
|
|
8004
|
+
if (isOperatorToken(ctx.tryPeek(), ',')) {
|
|
8005
|
+
ctx.advance();
|
|
8006
|
+
}
|
|
8007
|
+
}
|
|
8008
|
+
assertRParenToken(ctx.tryPeek());
|
|
8009
|
+
ctx.advance();
|
|
8010
|
+
assertOperatorToken(ctx.tryPeek(), '->');
|
|
8011
|
+
ctx.advance();
|
|
8012
|
+
const expression = ctx.parseExpression();
|
|
8013
|
+
return isDoseq
|
|
8014
|
+
? withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.doseq, forLoopBindings, expression]], firstToken[2])
|
|
8015
|
+
: withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.for, forLoopBindings, expression]], firstToken[2]);
|
|
8016
|
+
}
|
|
8017
|
+
function parseForLoopBinding(ctx) {
|
|
8018
|
+
const bindingNode = parseBinding(ctx);
|
|
8019
|
+
const modifiers = [];
|
|
8020
|
+
let token = ctx.peek();
|
|
8021
|
+
assertInternalLoopBindingDelimiter(token, ['let', 'when', 'while']);
|
|
8022
|
+
const letBindings = [];
|
|
8023
|
+
if (token[1] === 'let') {
|
|
8024
|
+
modifiers.push('&let');
|
|
8025
|
+
while (isSymbolToken(token, 'let')) {
|
|
8026
|
+
const letNode = parseLet(ctx, token);
|
|
8027
|
+
const existingBoundNames = letBindings.flatMap(b => Object.keys(getAllBindingTargetNames(b[1][0])));
|
|
8028
|
+
const newBoundNames = Object.keys(getAllBindingTargetNames(letNode[1][1][1][0]));
|
|
8029
|
+
if (newBoundNames.some(n => existingBoundNames.includes(n))) {
|
|
8030
|
+
throw new LitsError('Duplicate binding', letNode[1][1][2]);
|
|
8031
|
+
}
|
|
8032
|
+
letBindings.push(letNode[1][1]);
|
|
8033
|
+
token = ctx.peek();
|
|
8034
|
+
assertInternalLoopBindingDelimiter(token, ['let', 'when', 'while']);
|
|
8035
|
+
token = ctx.peek();
|
|
8036
|
+
}
|
|
8037
|
+
}
|
|
8038
|
+
let whenNode;
|
|
8039
|
+
let whileNode;
|
|
8040
|
+
while (isReservedSymbolToken(token, 'when')
|
|
8041
|
+
|| isReservedSymbolToken(token, 'while')) {
|
|
8042
|
+
ctx.advance();
|
|
8043
|
+
if (token[1] === 'when') {
|
|
8044
|
+
modifiers.push('&when');
|
|
8045
|
+
whenNode = ctx.parseExpression();
|
|
7957
8046
|
}
|
|
7958
8047
|
else {
|
|
7959
|
-
|
|
7960
|
-
|
|
8048
|
+
modifiers.push('&while');
|
|
8049
|
+
whileNode = ctx.parseExpression();
|
|
8050
|
+
}
|
|
8051
|
+
token = ctx.peek();
|
|
8052
|
+
const symbols = modifiers.includes('&when') && modifiers.includes('&while')
|
|
8053
|
+
? []
|
|
8054
|
+
: modifiers.includes('&when')
|
|
8055
|
+
? ['while']
|
|
8056
|
+
: ['when'];
|
|
8057
|
+
assertInternalLoopBindingDelimiter(token, symbols);
|
|
8058
|
+
token = ctx.peek();
|
|
8059
|
+
}
|
|
8060
|
+
assertInternalLoopBindingDelimiter(token, []);
|
|
8061
|
+
return [bindingNode, letBindings, whenNode, whileNode];
|
|
8062
|
+
}
|
|
8063
|
+
function parseBinding(ctx) {
|
|
8064
|
+
const firstToken = asSymbolToken(ctx.tryPeek());
|
|
8065
|
+
const name = asUserDefinedSymbolNode(parseSymbol(ctx));
|
|
8066
|
+
assertReservedSymbolToken(ctx.tryPeek(), 'in');
|
|
8067
|
+
ctx.advance();
|
|
8068
|
+
const value = ctx.parseExpression();
|
|
8069
|
+
const node = withSourceCodeInfo([
|
|
8070
|
+
NodeTypes.Binding,
|
|
8071
|
+
[
|
|
8072
|
+
withSourceCodeInfo([bindingTargetTypes.symbol, [name, undefined]], firstToken[2]),
|
|
8073
|
+
value,
|
|
8074
|
+
],
|
|
8075
|
+
], firstToken[2]);
|
|
8076
|
+
return node;
|
|
8077
|
+
}
|
|
8078
|
+
function assertInternalLoopBindingDelimiter(token, symbols) {
|
|
8079
|
+
if (!isInternalLoopBindingDelimiter(token, symbols)) {
|
|
8080
|
+
const symbolsString = `${[...symbols, ','].map(symbol => `"${symbol}"`).join(', ')} or ")"`;
|
|
8081
|
+
throw new LitsError(`Expected symbol ${symbolsString}`, token[2]);
|
|
7961
8082
|
}
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
|
|
7967
|
-
|
|
8083
|
+
}
|
|
8084
|
+
function isInternalLoopBindingDelimiter(token, symbols) {
|
|
8085
|
+
// end of loop binding
|
|
8086
|
+
if (isOperatorToken(token, ',') || isRParenToken(token)) {
|
|
8087
|
+
return true;
|
|
8088
|
+
}
|
|
8089
|
+
for (const symbol of symbols) {
|
|
8090
|
+
if (symbol === 'let' && isSymbolToken(token, 'let')) {
|
|
8091
|
+
return true;
|
|
7968
8092
|
}
|
|
7969
|
-
|
|
7970
|
-
|
|
7971
|
-
if (!isOperatorToken(this.peek(), '->')) {
|
|
7972
|
-
return null;
|
|
7973
|
-
}
|
|
7974
|
-
this.advance();
|
|
7975
|
-
let nodes;
|
|
7976
|
-
let docString = '';
|
|
7977
|
-
if (isReservedSymbolToken(this.peek(), 'do')) {
|
|
7978
|
-
const parsedBlock = this.parseBlock(true);
|
|
7979
|
-
docString = parsedBlock[1];
|
|
7980
|
-
nodes = parsedBlock[0][1][1];
|
|
7981
|
-
}
|
|
7982
|
-
else {
|
|
7983
|
-
nodes = [this.parseExpression()];
|
|
7984
|
-
}
|
|
7985
|
-
return withSourceCodeInfo([
|
|
7986
|
-
NodeTypes.SpecialExpression,
|
|
7987
|
-
[
|
|
7988
|
-
specialExpressionTypes['0_lambda'],
|
|
7989
|
-
[
|
|
7990
|
-
functionArguments,
|
|
7991
|
-
nodes,
|
|
7992
|
-
],
|
|
7993
|
-
docString,
|
|
7994
|
-
],
|
|
7995
|
-
], firstToken[2]);
|
|
8093
|
+
if (['when', 'while'].includes(symbol) && isReservedSymbolToken(token, symbol)) {
|
|
8094
|
+
return true;
|
|
7996
8095
|
}
|
|
7997
|
-
|
|
7998
|
-
|
|
8096
|
+
}
|
|
8097
|
+
return false;
|
|
8098
|
+
}
|
|
8099
|
+
|
|
8100
|
+
function parseLoop(ctx, firstToken) {
|
|
8101
|
+
ctx.advance();
|
|
8102
|
+
assertLParenToken(ctx.tryPeek());
|
|
8103
|
+
ctx.advance();
|
|
8104
|
+
const bindingNodes = [];
|
|
8105
|
+
let token = ctx.tryPeek();
|
|
8106
|
+
while (!ctx.isAtEnd() && !isRParenToken(token)) {
|
|
8107
|
+
const target = parseBindingTarget(ctx, { requireDefaultValue: true, noRest: true });
|
|
8108
|
+
const value = target[1][1];
|
|
8109
|
+
target[1][1] = undefined;
|
|
8110
|
+
bindingNodes.push(withSourceCodeInfo([NodeTypes.Binding, [target, value]], target[2]));
|
|
8111
|
+
if (isOperatorToken(ctx.tryPeek(), ',')) {
|
|
8112
|
+
ctx.advance();
|
|
7999
8113
|
}
|
|
8114
|
+
token = ctx.tryPeek();
|
|
8115
|
+
}
|
|
8116
|
+
if (bindingNodes.length === 0) {
|
|
8117
|
+
throw new LitsError('Expected binding', ctx.peekSourceCodeInfo());
|
|
8000
8118
|
}
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
|
|
8119
|
+
assertRParenToken(token);
|
|
8120
|
+
ctx.advance();
|
|
8121
|
+
assertOperatorToken(ctx.tryPeek(), '->');
|
|
8122
|
+
ctx.advance();
|
|
8123
|
+
const expression = ctx.parseExpression();
|
|
8124
|
+
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.loop, bindingNodes, expression]], firstToken[2]);
|
|
8125
|
+
}
|
|
8126
|
+
|
|
8127
|
+
function parseTry(ctx, token) {
|
|
8128
|
+
ctx.advance();
|
|
8129
|
+
const tryExpression = parseImplicitBlock(ctx, ['catch']);
|
|
8130
|
+
ctx.advance();
|
|
8131
|
+
let errorSymbol;
|
|
8132
|
+
if (isLParenToken(ctx.tryPeek())) {
|
|
8133
|
+
ctx.advance();
|
|
8134
|
+
errorSymbol = parseSymbol(ctx);
|
|
8135
|
+
assertRParenToken(ctx.tryPeek());
|
|
8136
|
+
ctx.advance();
|
|
8137
|
+
}
|
|
8138
|
+
const catchExpression = parseImplicitBlock(ctx, ['end']);
|
|
8139
|
+
ctx.advance();
|
|
8140
|
+
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.try, tryExpression, errorSymbol, catchExpression]], token[2]);
|
|
8141
|
+
}
|
|
8142
|
+
|
|
8143
|
+
function parseString(ctx, token) {
|
|
8144
|
+
ctx.advance();
|
|
8145
|
+
const value = token[1].substring(1, token[1].length - 1)
|
|
8146
|
+
.replace(/(\\{2})|(\\")|(\\n)|(\\t)|(\\r)|(\\b)|(\\f)|\\(.)/g, (_, backslash, doubleQuote, newline, tab, carriageReturn, backspace, formFeed, normalChar) => {
|
|
8147
|
+
// If it's a double escape (\\x), return \x
|
|
8148
|
+
if (backslash) {
|
|
8149
|
+
return '\\';
|
|
8005
8150
|
}
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
8009
|
-
let defaults = false;
|
|
8010
|
-
const functionArguments = [];
|
|
8011
|
-
while (!this.isAtEnd() && !isRParenToken(this.peek()) && !isSymbolToken(this.peek(), 'let')) {
|
|
8012
|
-
if (rest) {
|
|
8013
|
-
throw new LitsError('Rest argument must be last', this.peekSourceCodeInfo());
|
|
8014
|
-
}
|
|
8015
|
-
const bindingTarget = this.parseBindingTarget();
|
|
8016
|
-
if (bindingTarget[1][1] !== undefined) {
|
|
8017
|
-
defaults = true;
|
|
8018
|
-
}
|
|
8019
|
-
if (bindingTarget[0] === bindingTargetTypes.rest) {
|
|
8020
|
-
rest = true;
|
|
8021
|
-
}
|
|
8022
|
-
if (defaults && !bindingTarget[1][1]) {
|
|
8023
|
-
throw new LitsError('Default arguments must be last', this.peekSourceCodeInfo());
|
|
8024
|
-
}
|
|
8025
|
-
functionArguments.push(bindingTarget);
|
|
8026
|
-
if (!isOperatorToken(this.peek(), ',') && !isRParenToken(this.peek()) && !isSymbolToken(this.peek(), 'let')) {
|
|
8027
|
-
throw new LitsError('Expected comma or closing parenthesis', this.peekSourceCodeInfo());
|
|
8028
|
-
}
|
|
8029
|
-
if (isOperatorToken(this.peek(), ',')) {
|
|
8030
|
-
this.advance();
|
|
8031
|
-
}
|
|
8151
|
+
// If it's a special character (\n, \t, \r, \b, \f), return the special character
|
|
8152
|
+
else if (newline) {
|
|
8153
|
+
return '\n';
|
|
8032
8154
|
}
|
|
8033
|
-
if (
|
|
8034
|
-
|
|
8155
|
+
else if (tab) {
|
|
8156
|
+
return '\t';
|
|
8035
8157
|
}
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
}
|
|
8039
|
-
parseShorthandLambdaFunction() {
|
|
8040
|
-
const firstToken = this.asToken(this.peek());
|
|
8041
|
-
this.advance();
|
|
8042
|
-
const startPos = this.parseState.position;
|
|
8043
|
-
let nodes;
|
|
8044
|
-
let docString = '';
|
|
8045
|
-
if (isReservedSymbolToken(this.peek(), 'do')) {
|
|
8046
|
-
const parsedBlock = this.parseBlock(true);
|
|
8047
|
-
docString = parsedBlock[1];
|
|
8048
|
-
nodes = parsedBlock[0][1][1];
|
|
8158
|
+
else if (carriageReturn) {
|
|
8159
|
+
return '\r';
|
|
8049
8160
|
}
|
|
8050
|
-
else {
|
|
8051
|
-
|
|
8052
|
-
}
|
|
8053
|
-
const endPos = this.parseState.position - 1;
|
|
8054
|
-
let arity = 0;
|
|
8055
|
-
let dollar1 = 'NOT_SET'; // referring to argument bindings. $ = NAKED, $1, $2, $3, etc = WITH_1
|
|
8056
|
-
for (let pos = startPos; pos <= endPos; pos += 1) {
|
|
8057
|
-
const token = this.tokenStream.tokens[pos];
|
|
8058
|
-
if (isSymbolToken(token)) {
|
|
8059
|
-
const match = placeholderRegexp.exec(token[1]);
|
|
8060
|
-
if (match) {
|
|
8061
|
-
const number = match[1] ?? '1';
|
|
8062
|
-
if (number === '1') {
|
|
8063
|
-
const mixedPercent1 = (!match[1] && dollar1 === 'WITH_1') || (match[1] && dollar1 === 'NAKED');
|
|
8064
|
-
if (mixedPercent1)
|
|
8065
|
-
throw new LitsError('Please make up your mind, either use $ or $1', firstToken[2]);
|
|
8066
|
-
dollar1 = match[1] ? 'WITH_1' : 'NAKED';
|
|
8067
|
-
}
|
|
8068
|
-
arity = Math.max(arity, Number(number));
|
|
8069
|
-
if (arity > maxShorthandLambdaArity)
|
|
8070
|
-
throw new LitsError('Can\'t specify more than 20 arguments', firstToken[2]);
|
|
8071
|
-
}
|
|
8072
|
-
}
|
|
8161
|
+
else if (backspace) {
|
|
8162
|
+
return '\b';
|
|
8073
8163
|
}
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
if (i === 1 && dollar1 === 'NAKED') {
|
|
8077
|
-
functionArguments.push(withSourceCodeInfo([bindingTargetTypes.symbol, [[NodeTypes.UserDefinedSymbol, '$'], undefined]], firstToken[2]));
|
|
8078
|
-
}
|
|
8079
|
-
else {
|
|
8080
|
-
functionArguments.push(withSourceCodeInfo([bindingTargetTypes.symbol, [[NodeTypes.UserDefinedSymbol, `$${i}`], undefined]], firstToken[2]));
|
|
8081
|
-
}
|
|
8164
|
+
else if (formFeed) {
|
|
8165
|
+
return '\f';
|
|
8082
8166
|
}
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
nodes,
|
|
8086
|
-
], docString]], firstToken[2]);
|
|
8087
|
-
return node;
|
|
8088
|
-
}
|
|
8089
|
-
parseOptionalDefaulValue() {
|
|
8090
|
-
if (isOperatorToken(this.peek(), '=')) {
|
|
8091
|
-
this.advance();
|
|
8092
|
-
return this.parseExpression();
|
|
8093
|
-
}
|
|
8094
|
-
return undefined;
|
|
8095
|
-
}
|
|
8096
|
-
parseBindingTarget({ requireDefaultValue, noRest } = {}) {
|
|
8097
|
-
const firstToken = this.peek();
|
|
8098
|
-
// Symbol
|
|
8099
|
-
if (isSymbolToken(firstToken)) {
|
|
8100
|
-
const symbol = this.parseSymbol();
|
|
8101
|
-
if (!isUserDefinedSymbolNode(symbol)) {
|
|
8102
|
-
throw new LitsError('Expected user defined symbol', firstToken[2]);
|
|
8103
|
-
}
|
|
8104
|
-
const defaultValue = this.parseOptionalDefaulValue();
|
|
8105
|
-
if (requireDefaultValue && !defaultValue) {
|
|
8106
|
-
throw new LitsError('Expected assignment', this.peekSourceCodeInfo());
|
|
8107
|
-
}
|
|
8108
|
-
return withSourceCodeInfo([bindingTargetTypes.symbol, [symbol, defaultValue]], firstToken[2]);
|
|
8109
|
-
}
|
|
8110
|
-
// Rest
|
|
8111
|
-
if (isOperatorToken(firstToken, '...')) {
|
|
8112
|
-
if (noRest) {
|
|
8113
|
-
throw new LitsError('Rest element not allowed', firstToken[2]);
|
|
8114
|
-
}
|
|
8115
|
-
this.advance();
|
|
8116
|
-
const symbol = asUserDefinedSymbolNode(this.parseSymbol());
|
|
8117
|
-
if (isOperatorToken(this.peek(), '=')) {
|
|
8118
|
-
throw new LitsError('Rest argument can not have default value', this.peekSourceCodeInfo());
|
|
8119
|
-
}
|
|
8120
|
-
return withSourceCodeInfo([bindingTargetTypes.rest, [symbol[1], undefined]], firstToken[2]);
|
|
8121
|
-
}
|
|
8122
|
-
// Array
|
|
8123
|
-
if (isLBracketToken(firstToken)) {
|
|
8124
|
-
this.advance();
|
|
8125
|
-
const elements = [];
|
|
8126
|
-
let token = this.asToken(this.peek());
|
|
8127
|
-
let rest = false;
|
|
8128
|
-
while (!isRBracketToken(token)) {
|
|
8129
|
-
if (rest) {
|
|
8130
|
-
throw new LitsError('Rest argument must be last', token[2]);
|
|
8131
|
-
}
|
|
8132
|
-
if (isOperatorToken(token, ',')) {
|
|
8133
|
-
elements.push(null);
|
|
8134
|
-
this.advance();
|
|
8135
|
-
token = this.asToken(this.peek());
|
|
8136
|
-
continue;
|
|
8137
|
-
}
|
|
8138
|
-
const target = this.parseBindingTarget();
|
|
8139
|
-
if (target[0] === bindingTargetTypes.rest) {
|
|
8140
|
-
rest = true;
|
|
8141
|
-
}
|
|
8142
|
-
elements.push(target);
|
|
8143
|
-
token = this.asToken(this.peek());
|
|
8144
|
-
if (!isRBracketToken(token)) {
|
|
8145
|
-
assertOperatorToken(token, ',');
|
|
8146
|
-
this.advance();
|
|
8147
|
-
}
|
|
8148
|
-
token = this.asToken(this.peek());
|
|
8149
|
-
}
|
|
8150
|
-
this.advance();
|
|
8151
|
-
const defaultValue = this.parseOptionalDefaulValue();
|
|
8152
|
-
if (requireDefaultValue && !defaultValue) {
|
|
8153
|
-
throw new LitsError('Expected assignment', this.peekSourceCodeInfo());
|
|
8154
|
-
}
|
|
8155
|
-
return withSourceCodeInfo([bindingTargetTypes.array, [elements, defaultValue]], firstToken[2]);
|
|
8156
|
-
}
|
|
8157
|
-
// Object
|
|
8158
|
-
if (isLBraceToken(firstToken)) {
|
|
8159
|
-
this.advance();
|
|
8160
|
-
const elements = {};
|
|
8161
|
-
let token = this.asToken(this.peek());
|
|
8162
|
-
let rest = false;
|
|
8163
|
-
while (!isRBraceToken(token)) {
|
|
8164
|
-
if (rest) {
|
|
8165
|
-
throw new LitsError('Rest argument must be last', token[2]);
|
|
8166
|
-
}
|
|
8167
|
-
if (isOperatorToken(token, '...')) {
|
|
8168
|
-
rest = true;
|
|
8169
|
-
this.advance();
|
|
8170
|
-
}
|
|
8171
|
-
// Parse the key symbol - can be any symbol type (including builtins) when using 'as' alias
|
|
8172
|
-
const keySymbol = this.parseSymbol();
|
|
8173
|
-
const keyName = getSymbolName(keySymbol);
|
|
8174
|
-
token = this.asToken(this.peek());
|
|
8175
|
-
if (isReservedSymbolToken(token, 'as')) {
|
|
8176
|
-
if (rest) {
|
|
8177
|
-
throw new LitsError('Rest argument can not have alias', token[2]);
|
|
8178
|
-
}
|
|
8179
|
-
this.advance();
|
|
8180
|
-
const name = asUserDefinedSymbolNode(this.parseSymbol());
|
|
8181
|
-
if (elements[name[1]]) {
|
|
8182
|
-
throw new LitsError(`Duplicate binding name: ${name}`, token[2]);
|
|
8183
|
-
}
|
|
8184
|
-
elements[keyName] = withSourceCodeInfo([bindingTargetTypes.symbol, [name, this.parseOptionalDefaulValue()]], firstToken[2]);
|
|
8185
|
-
}
|
|
8186
|
-
else if (isRBraceToken(token) || isOperatorToken(token, ',') || isOperatorToken(token, '=')) {
|
|
8187
|
-
// Without 'as' alias, the key becomes the binding name - must be user-defined symbol
|
|
8188
|
-
const key = asUserDefinedSymbolNode(keySymbol, keySymbol[2]);
|
|
8189
|
-
if (elements[key[1]]) {
|
|
8190
|
-
throw new LitsError(`Duplicate binding name: ${key}`, token[2]);
|
|
8191
|
-
}
|
|
8192
|
-
if (rest && isOperatorToken(this.peek(), '=')) {
|
|
8193
|
-
throw new LitsError('Rest argument can not have default value', this.peekSourceCodeInfo());
|
|
8194
|
-
}
|
|
8195
|
-
elements[key[1]] = rest
|
|
8196
|
-
? withSourceCodeInfo([bindingTargetTypes.rest, [key[1], this.parseOptionalDefaulValue()]], firstToken[2])
|
|
8197
|
-
: withSourceCodeInfo([bindingTargetTypes.symbol, [key, this.parseOptionalDefaulValue()]], firstToken[2]);
|
|
8198
|
-
}
|
|
8199
|
-
else if (isOperatorToken(token, ':')) {
|
|
8200
|
-
this.advance();
|
|
8201
|
-
token = this.asToken(this.peek());
|
|
8202
|
-
if (!isLBraceToken(token) && !isLBracketToken(token)) {
|
|
8203
|
-
throw new LitsError('Expected object or array', token[2]);
|
|
8204
|
-
}
|
|
8205
|
-
elements[keyName] = this.parseBindingTarget();
|
|
8206
|
-
}
|
|
8207
|
-
if (!isRBraceToken(this.peek())) {
|
|
8208
|
-
assertOperatorToken(this.peek(), ',');
|
|
8209
|
-
this.advance();
|
|
8210
|
-
}
|
|
8211
|
-
token = this.asToken(this.peek());
|
|
8212
|
-
}
|
|
8213
|
-
this.advance();
|
|
8214
|
-
token = this.asToken(this.peek());
|
|
8215
|
-
const defaultValue = this.parseOptionalDefaulValue();
|
|
8216
|
-
if (requireDefaultValue && !defaultValue) {
|
|
8217
|
-
throw new LitsError('Expected assignment', token[2]);
|
|
8218
|
-
}
|
|
8219
|
-
return withSourceCodeInfo([bindingTargetTypes.object, [elements, defaultValue]], firstToken[2]);
|
|
8167
|
+
else if (doubleQuote) {
|
|
8168
|
+
return '"';
|
|
8220
8169
|
}
|
|
8221
|
-
|
|
8222
|
-
}
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
|
|
8170
|
+
return normalChar;
|
|
8171
|
+
});
|
|
8172
|
+
return withSourceCodeInfo([NodeTypes.String, value], token[2]);
|
|
8173
|
+
}
|
|
8174
|
+
|
|
8175
|
+
function parseDo(ctx, allowDocString = false) {
|
|
8176
|
+
const token = asReservedSymbolToken(ctx.tryPeek(), 'do');
|
|
8177
|
+
ctx.advance();
|
|
8178
|
+
let docString = '';
|
|
8179
|
+
if (allowDocString && isDocStringToken(ctx.tryPeek())) {
|
|
8180
|
+
docString = parseDocString(ctx);
|
|
8230
8181
|
}
|
|
8231
|
-
|
|
8232
|
-
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
|
|
8236
|
-
docString = this.parseDocString();
|
|
8182
|
+
const expressions = [];
|
|
8183
|
+
while (!ctx.isAtEnd() && !isReservedSymbolToken(ctx.tryPeek(), 'end')) {
|
|
8184
|
+
expressions.push(ctx.parseExpression());
|
|
8185
|
+
if (isOperatorToken(ctx.tryPeek(), ';')) {
|
|
8186
|
+
ctx.advance();
|
|
8237
8187
|
}
|
|
8238
|
-
|
|
8239
|
-
|
|
8240
|
-
expressions.push(this.parseExpression());
|
|
8241
|
-
if (isOperatorToken(this.peek(), ';')) {
|
|
8242
|
-
this.advance();
|
|
8243
|
-
}
|
|
8244
|
-
else if (!isReservedSymbolToken(this.peek(), 'end')) {
|
|
8245
|
-
throw new LitsError('Expected end', this.peekSourceCodeInfo());
|
|
8246
|
-
}
|
|
8188
|
+
else if (!isReservedSymbolToken(ctx.tryPeek(), 'end')) {
|
|
8189
|
+
throw new LitsError('Expected end', ctx.peekSourceCodeInfo());
|
|
8247
8190
|
}
|
|
8248
|
-
assertReservedSymbolToken(this.peek(), 'end');
|
|
8249
|
-
this.advance();
|
|
8250
|
-
return [
|
|
8251
|
-
withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.block, expressions]], token[2]),
|
|
8252
|
-
docString,
|
|
8253
|
-
];
|
|
8254
8191
|
}
|
|
8255
|
-
|
|
8256
|
-
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
8263
|
-
|
|
8192
|
+
assertReservedSymbolToken(ctx.tryPeek(), 'end');
|
|
8193
|
+
ctx.advance();
|
|
8194
|
+
return [
|
|
8195
|
+
withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.block, expressions]], token[2]),
|
|
8196
|
+
docString,
|
|
8197
|
+
];
|
|
8198
|
+
}
|
|
8199
|
+
function parseDocString(ctx) {
|
|
8200
|
+
const token = ctx.peek();
|
|
8201
|
+
const stringToken = token[2] ? ['string', token[1].slice(2, -2), token[2]] : ['string', token[1].slice(2, -2)];
|
|
8202
|
+
const stringNode = parseString(ctx, stringToken);
|
|
8203
|
+
return smartTrim(stringNode[1]); // Extract the string value from the StringNode
|
|
8204
|
+
}
|
|
8205
|
+
|
|
8206
|
+
function parseRegexpShorthand(ctx) {
|
|
8207
|
+
const token = ctx.peek();
|
|
8208
|
+
ctx.advance();
|
|
8209
|
+
const endStringPosition = token[1].lastIndexOf('"');
|
|
8210
|
+
const regexpString = token[1].substring(2, endStringPosition);
|
|
8211
|
+
const optionsString = token[1].substring(endStringPosition + 1);
|
|
8212
|
+
const stringNode = withSourceCodeInfo([NodeTypes.String, regexpString], token[2]);
|
|
8213
|
+
const optionsNode = withSourceCodeInfo([NodeTypes.String, optionsString], token[2]);
|
|
8214
|
+
const node = withSourceCodeInfo([
|
|
8215
|
+
NodeTypes.NormalExpression,
|
|
8216
|
+
[
|
|
8217
|
+
withSourceCodeInfo([NodeTypes.NormalBuiltinSymbol, normalExpressionTypes.regexp], token[2]),
|
|
8218
|
+
[stringNode, optionsNode],
|
|
8219
|
+
],
|
|
8220
|
+
], token[2]);
|
|
8221
|
+
return node;
|
|
8222
|
+
}
|
|
8223
|
+
|
|
8224
|
+
function parseReservedSymbol(ctx) {
|
|
8225
|
+
const token = asReservedSymbolToken(ctx.tryPeek());
|
|
8226
|
+
ctx.advance();
|
|
8227
|
+
const symbol = token[1];
|
|
8228
|
+
if (isNumberReservedSymbol(symbol)) {
|
|
8229
|
+
return withSourceCodeInfo([NodeTypes.Number, numberReservedSymbolRecord[symbol]], token[2]);
|
|
8230
|
+
}
|
|
8231
|
+
return withSourceCodeInfo([NodeTypes.ReservedSymbol, token[1]], token[2]);
|
|
8232
|
+
}
|
|
8233
|
+
|
|
8234
|
+
function parseArray(ctx) {
|
|
8235
|
+
const firstToken = asLBracketToken(ctx.tryPeek());
|
|
8236
|
+
ctx.advance();
|
|
8237
|
+
const params = [];
|
|
8238
|
+
while (!ctx.isAtEnd() && !isRBracketToken(ctx.tryPeek())) {
|
|
8239
|
+
if (isOperatorToken(ctx.tryPeek(), '...')) {
|
|
8240
|
+
ctx.advance();
|
|
8241
|
+
params.push(withSourceCodeInfo([NodeTypes.Spread, ctx.parseExpression()], ctx.peekSourceCodeInfo()));
|
|
8264
8242
|
}
|
|
8265
|
-
|
|
8266
|
-
|
|
8267
|
-
throw new LitsError('Expected expression', this.peekSourceCodeInfo());
|
|
8243
|
+
else {
|
|
8244
|
+
params.push(ctx.parseExpression());
|
|
8268
8245
|
}
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
}
|
|
8273
|
-
assertImplicitBlockEnd(ends) {
|
|
8274
|
-
if (!this.isImplicitBlockEnd(ends)) {
|
|
8275
|
-
throw new LitsError(`Expected ${ends.map(e => e[1]).join(' or ')}`, this.peekSourceCodeInfo());
|
|
8246
|
+
const nextToken = ctx.tryPeek();
|
|
8247
|
+
if (!isOperatorToken(nextToken, ',') && !isRBracketToken(nextToken)) {
|
|
8248
|
+
throw new LitsError('Expected comma or closing parenthesis', ctx.peekSourceCodeInfo());
|
|
8276
8249
|
}
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
for (const end of ends) {
|
|
8280
|
-
if (isReservedSymbolToken(this.peek(), end)) {
|
|
8281
|
-
return true;
|
|
8282
|
-
}
|
|
8250
|
+
if (isOperatorToken(nextToken, ',')) {
|
|
8251
|
+
ctx.advance();
|
|
8283
8252
|
}
|
|
8284
|
-
return false;
|
|
8285
8253
|
}
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
if (
|
|
8303
|
-
|
|
8304
|
-
}
|
|
8305
|
-
assertRParenToken(token);
|
|
8306
|
-
this.advance();
|
|
8307
|
-
assertOperatorToken(this.peek(), '->');
|
|
8308
|
-
this.advance();
|
|
8309
|
-
const expression = this.parseExpression();
|
|
8310
|
-
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.loop, bindingNodes, expression]], firstToken[2]);
|
|
8311
|
-
}
|
|
8312
|
-
parseTry(token) {
|
|
8313
|
-
this.advance();
|
|
8314
|
-
const tryExpression = this.parseImplicitBlock(['catch']);
|
|
8315
|
-
this.advance();
|
|
8316
|
-
let errorSymbol;
|
|
8317
|
-
if (isLParenToken(this.peek())) {
|
|
8318
|
-
this.advance();
|
|
8319
|
-
errorSymbol = this.parseSymbol();
|
|
8320
|
-
assertRParenToken(this.peek());
|
|
8321
|
-
this.advance();
|
|
8322
|
-
}
|
|
8323
|
-
const catchExpression = this.parseImplicitBlock(['end']);
|
|
8324
|
-
this.advance();
|
|
8325
|
-
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.try, tryExpression, errorSymbol, catchExpression]], token[2]);
|
|
8326
|
-
}
|
|
8327
|
-
parseForOrDoseq(firstToken) {
|
|
8328
|
-
const isDoseq = firstToken[1] === 'doseq';
|
|
8329
|
-
this.advance();
|
|
8330
|
-
assertLParenToken(this.peek());
|
|
8331
|
-
this.advance();
|
|
8332
|
-
const forLoopBindings = [];
|
|
8333
|
-
while (!this.isAtEnd() && !isRParenToken(this.peek())) {
|
|
8334
|
-
const loopBinding = this.parseForLoopBinding();
|
|
8335
|
-
const existingBoundNames = forLoopBindings.flatMap(b => Object.keys(getAllBindingTargetNames(b[0][1][0])));
|
|
8336
|
-
const newBoundNames = getAllBindingTargetNames(loopBinding[0][1][0]);
|
|
8337
|
-
if (Object.keys(newBoundNames).some(n => existingBoundNames.includes(n))) {
|
|
8338
|
-
throw new LitsError('Duplicate binding', loopBinding[0][2]);
|
|
8339
|
-
}
|
|
8340
|
-
forLoopBindings.push(loopBinding);
|
|
8341
|
-
if (isOperatorToken(this.peek(), ',')) {
|
|
8342
|
-
this.advance();
|
|
8343
|
-
}
|
|
8344
|
-
}
|
|
8345
|
-
assertRParenToken(this.peek());
|
|
8346
|
-
this.advance();
|
|
8347
|
-
assertOperatorToken(this.peek(), '->');
|
|
8348
|
-
this.advance();
|
|
8349
|
-
const expression = this.parseExpression();
|
|
8350
|
-
return isDoseq
|
|
8351
|
-
? withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.doseq, forLoopBindings, expression]], firstToken[2])
|
|
8352
|
-
: withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.for, forLoopBindings, expression]], firstToken[2]);
|
|
8353
|
-
}
|
|
8354
|
-
parseForLoopBinding() {
|
|
8355
|
-
const bindingNode = this.parseBinding();
|
|
8356
|
-
const modifiers = [];
|
|
8357
|
-
let token = this.asToken(this.peek());
|
|
8358
|
-
this.assertInternalLoopBindingDelimiter(token, ['let', 'when', 'while']);
|
|
8359
|
-
const letBindings = [];
|
|
8360
|
-
if (token[1] === 'let') {
|
|
8361
|
-
modifiers.push('&let');
|
|
8362
|
-
while (isSymbolToken(token, 'let')) {
|
|
8363
|
-
const letNode = this.parseLet(token);
|
|
8364
|
-
const existingBoundNames = letBindings.flatMap(b => Object.keys(getAllBindingTargetNames(b[1][0])));
|
|
8365
|
-
const newBoundNames = Object.keys(getAllBindingTargetNames(letNode[1][1][1][0]));
|
|
8366
|
-
if (newBoundNames.some(n => existingBoundNames.includes(n))) {
|
|
8367
|
-
throw new LitsError('Duplicate binding', letNode[1][1][2]);
|
|
8368
|
-
}
|
|
8369
|
-
letBindings.push(letNode[1][1]);
|
|
8370
|
-
token = this.asToken(this.peek());
|
|
8371
|
-
this.assertInternalLoopBindingDelimiter(token, ['let', 'when', 'while']);
|
|
8372
|
-
token = this.asToken(this.peek());
|
|
8373
|
-
}
|
|
8254
|
+
assertRBracketToken(ctx.tryPeek());
|
|
8255
|
+
ctx.advance();
|
|
8256
|
+
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.array, params]], firstToken[2]);
|
|
8257
|
+
}
|
|
8258
|
+
|
|
8259
|
+
const placeholderRegexp = /^\$([1-9]\d?)?$/;
|
|
8260
|
+
const maxShorthandLambdaArity = 20;
|
|
8261
|
+
function parseLambdaFunction(ctx) {
|
|
8262
|
+
const firstToken = ctx.peek();
|
|
8263
|
+
if (isLParenToken(firstToken)
|
|
8264
|
+
&& isSymbolToken(ctx.peekAhead(1))
|
|
8265
|
+
&& isOperatorToken(ctx.peekAhead(2), '->')) {
|
|
8266
|
+
return null;
|
|
8267
|
+
}
|
|
8268
|
+
try {
|
|
8269
|
+
const functionArguments = parseFunctionArguments(ctx);
|
|
8270
|
+
if (!isOperatorToken(ctx.peek(), '->')) {
|
|
8271
|
+
return null;
|
|
8374
8272
|
}
|
|
8375
|
-
|
|
8376
|
-
let
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
whenNode = this.parseExpression();
|
|
8383
|
-
}
|
|
8384
|
-
else {
|
|
8385
|
-
modifiers.push('&while');
|
|
8386
|
-
whileNode = this.parseExpression();
|
|
8387
|
-
}
|
|
8388
|
-
token = this.asToken(this.peek());
|
|
8389
|
-
const symbols = modifiers.includes('&when') && modifiers.includes('&while')
|
|
8390
|
-
? []
|
|
8391
|
-
: modifiers.includes('&when')
|
|
8392
|
-
? ['while']
|
|
8393
|
-
: ['when'];
|
|
8394
|
-
this.assertInternalLoopBindingDelimiter(token, symbols);
|
|
8395
|
-
token = this.asToken(this.peek());
|
|
8396
|
-
}
|
|
8397
|
-
this.assertInternalLoopBindingDelimiter(token, []);
|
|
8398
|
-
return [bindingNode, letBindings, whenNode, whileNode];
|
|
8399
|
-
}
|
|
8400
|
-
assertInternalLoopBindingDelimiter(token, symbols) {
|
|
8401
|
-
if (!this.isInternalLoopBindingDelimiter(token, symbols)) {
|
|
8402
|
-
const symbolsString = `${[...symbols, ','].map(symbol => `"${symbol}"`).join(', ')} or ")"`;
|
|
8403
|
-
throw new LitsError(`Expected symbol ${symbolsString}`, token[2]);
|
|
8404
|
-
}
|
|
8405
|
-
}
|
|
8406
|
-
isInternalLoopBindingDelimiter(token, symbols) {
|
|
8407
|
-
// end of loop binding
|
|
8408
|
-
if (isOperatorToken(token, ',') || isRParenToken(token)) {
|
|
8409
|
-
return true;
|
|
8273
|
+
ctx.advance();
|
|
8274
|
+
let nodes;
|
|
8275
|
+
let docString = '';
|
|
8276
|
+
if (isReservedSymbolToken(ctx.peek(), 'do')) {
|
|
8277
|
+
const parsedDo = parseDo(ctx, true);
|
|
8278
|
+
docString = parsedDo[1];
|
|
8279
|
+
nodes = parsedDo[0][1][1];
|
|
8410
8280
|
}
|
|
8411
|
-
|
|
8412
|
-
|
|
8413
|
-
return true;
|
|
8414
|
-
}
|
|
8415
|
-
if (['when', 'while'].includes(symbol) && isReservedSymbolToken(token, symbol)) {
|
|
8416
|
-
return true;
|
|
8417
|
-
}
|
|
8281
|
+
else {
|
|
8282
|
+
nodes = [ctx.parseExpression()];
|
|
8418
8283
|
}
|
|
8419
|
-
return
|
|
8420
|
-
|
|
8421
|
-
parseBinding() {
|
|
8422
|
-
const firstToken = asSymbolToken(this.peek());
|
|
8423
|
-
const name = asUserDefinedSymbolNode(this.parseSymbol());
|
|
8424
|
-
assertReservedSymbolToken(this.peek(), 'in');
|
|
8425
|
-
this.advance();
|
|
8426
|
-
const value = this.parseExpression();
|
|
8427
|
-
const node = withSourceCodeInfo([
|
|
8428
|
-
NodeTypes.Binding,
|
|
8284
|
+
return withSourceCodeInfo([
|
|
8285
|
+
NodeTypes.SpecialExpression,
|
|
8429
8286
|
[
|
|
8430
|
-
|
|
8431
|
-
|
|
8287
|
+
specialExpressionTypes['0_lambda'],
|
|
8288
|
+
[
|
|
8289
|
+
functionArguments,
|
|
8290
|
+
nodes,
|
|
8291
|
+
],
|
|
8292
|
+
docString,
|
|
8432
8293
|
],
|
|
8433
8294
|
], firstToken[2]);
|
|
8434
|
-
return node;
|
|
8435
|
-
}
|
|
8436
|
-
parseIfOrUnless(token) {
|
|
8437
|
-
const isUnless = token[1] === 'unless';
|
|
8438
|
-
this.advance();
|
|
8439
|
-
const condition = this.parseExpression();
|
|
8440
|
-
assertReservedSymbolToken(this.peek(), 'then');
|
|
8441
|
-
this.advance();
|
|
8442
|
-
const thenExpression = this.parseImplicitBlock(['else', 'end']);
|
|
8443
|
-
let elseExpression;
|
|
8444
|
-
if (isReservedSymbolToken(this.peek(), 'else')) {
|
|
8445
|
-
this.advance();
|
|
8446
|
-
elseExpression = this.parseImplicitBlock(['end']);
|
|
8447
|
-
}
|
|
8448
|
-
this.advance();
|
|
8449
|
-
return isUnless
|
|
8450
|
-
? withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.unless, [condition, thenExpression, elseExpression]]], token[2])
|
|
8451
|
-
: withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.if, [condition, thenExpression, elseExpression]]], token[2]);
|
|
8452
|
-
}
|
|
8453
|
-
parseCond(token) {
|
|
8454
|
-
this.advance();
|
|
8455
|
-
const params = [];
|
|
8456
|
-
while (!this.isAtEnd() && !isReservedSymbolToken(this.peek(), 'end')) {
|
|
8457
|
-
assertReservedSymbolToken(this.peek(), 'case');
|
|
8458
|
-
this.advance();
|
|
8459
|
-
const caseExpression = this.parseExpression();
|
|
8460
|
-
assertReservedSymbolToken(this.peek(), 'then');
|
|
8461
|
-
this.advance();
|
|
8462
|
-
const thenExpression = this.parseImplicitBlock(['case', 'end']);
|
|
8463
|
-
params.push([caseExpression, thenExpression]);
|
|
8464
|
-
if (isReservedSymbolToken(this.peek(), 'end')) {
|
|
8465
|
-
break;
|
|
8466
|
-
}
|
|
8467
|
-
}
|
|
8468
|
-
assertReservedSymbolToken(this.peek());
|
|
8469
|
-
this.advance();
|
|
8470
|
-
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.cond, params]], token[2]);
|
|
8471
|
-
}
|
|
8472
|
-
parseSwitch(token) {
|
|
8473
|
-
this.advance();
|
|
8474
|
-
const valueExpression = this.parseExpression();
|
|
8475
|
-
const params = [];
|
|
8476
|
-
while (!this.isAtEnd() && !isReservedSymbolToken(this.peek(), 'end')) {
|
|
8477
|
-
assertReservedSymbolToken(this.peek(), 'case');
|
|
8478
|
-
this.advance();
|
|
8479
|
-
const caseExpression = this.parseExpression();
|
|
8480
|
-
assertReservedSymbolToken(this.peek(), 'then');
|
|
8481
|
-
this.advance();
|
|
8482
|
-
const thenExpression = this.parseImplicitBlock(['case', 'end']);
|
|
8483
|
-
params.push([caseExpression, thenExpression]);
|
|
8484
|
-
if (isReservedSymbolToken(this.peek(), 'end')) {
|
|
8485
|
-
break;
|
|
8486
|
-
}
|
|
8487
|
-
}
|
|
8488
|
-
assertReservedSymbolToken(this.peek(), 'end');
|
|
8489
|
-
this.advance();
|
|
8490
|
-
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.switch, valueExpression, params]], token[2]);
|
|
8491
8295
|
}
|
|
8492
|
-
|
|
8493
|
-
return
|
|
8296
|
+
catch {
|
|
8297
|
+
return null;
|
|
8494
8298
|
}
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8299
|
+
}
|
|
8300
|
+
function parseFunctionArguments(ctx) {
|
|
8301
|
+
const firstToken = ctx.peek();
|
|
8302
|
+
if (isSymbolToken(firstToken)) {
|
|
8303
|
+
return [withSourceCodeInfo([bindingTargetTypes.symbol, [parseSymbol(ctx), undefined]], firstToken[2])];
|
|
8304
|
+
}
|
|
8305
|
+
assertLParenToken(firstToken);
|
|
8306
|
+
ctx.advance();
|
|
8307
|
+
let rest = false;
|
|
8308
|
+
let defaults = false;
|
|
8309
|
+
const functionArguments = [];
|
|
8310
|
+
while (!ctx.isAtEnd() && !isRParenToken(ctx.peek()) && !isSymbolToken(ctx.peek(), 'let')) {
|
|
8311
|
+
if (rest) {
|
|
8312
|
+
throw new LitsError('Rest argument must be last', ctx.peekSourceCodeInfo());
|
|
8502
8313
|
}
|
|
8503
|
-
|
|
8504
|
-
|
|
8314
|
+
const bindingTarget = parseBindingTarget(ctx);
|
|
8315
|
+
if (bindingTarget[1][1] !== undefined) {
|
|
8316
|
+
defaults = true;
|
|
8505
8317
|
}
|
|
8506
|
-
|
|
8507
|
-
|
|
8508
|
-
parseExport(exportToken) {
|
|
8509
|
-
this.advance();
|
|
8510
|
-
const token = this.peek();
|
|
8511
|
-
if (isSymbolToken(token, 'let')) {
|
|
8512
|
-
const letNode = this.parseLet(asSymbolToken(token));
|
|
8513
|
-
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes['0_def'], letNode[1][1]]], exportToken[2]);
|
|
8318
|
+
if (bindingTarget[0] === bindingTargetTypes.rest) {
|
|
8319
|
+
rest = true;
|
|
8514
8320
|
}
|
|
8515
|
-
|
|
8516
|
-
throw new LitsError('
|
|
8321
|
+
if (defaults && !bindingTarget[1][1]) {
|
|
8322
|
+
throw new LitsError('Default arguments must be last', ctx.peekSourceCodeInfo());
|
|
8517
8323
|
}
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
return withSourceCodeInfo([NodeTypes.SpecialBuiltinSymbol, specialExpressionTypes[value]], sourceCodeInfo);
|
|
8324
|
+
functionArguments.push(bindingTarget);
|
|
8325
|
+
if (!isOperatorToken(ctx.peek(), ',') && !isRParenToken(ctx.peek()) && !isSymbolToken(ctx.peek(), 'let')) {
|
|
8326
|
+
throw new LitsError('Expected comma or closing parenthesis', ctx.peekSourceCodeInfo());
|
|
8522
8327
|
}
|
|
8523
|
-
if (
|
|
8524
|
-
|
|
8328
|
+
if (isOperatorToken(ctx.peek(), ',')) {
|
|
8329
|
+
ctx.advance();
|
|
8525
8330
|
}
|
|
8526
|
-
return withSourceCodeInfo([NodeTypes.UserDefinedSymbol, value], sourceCodeInfo);
|
|
8527
8331
|
}
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
|
|
8538
|
-
|
|
8332
|
+
if (!isRParenToken(ctx.peek())) {
|
|
8333
|
+
throw new LitsError('Expected closing parenthesis', ctx.peekSourceCodeInfo());
|
|
8334
|
+
}
|
|
8335
|
+
ctx.advance();
|
|
8336
|
+
return functionArguments;
|
|
8337
|
+
}
|
|
8338
|
+
function parseShorthandLambdaFunction(ctx) {
|
|
8339
|
+
const firstToken = ctx.peek();
|
|
8340
|
+
ctx.advance();
|
|
8341
|
+
// TODO, do not like this...
|
|
8342
|
+
const startPos = ctx.getPosition();
|
|
8343
|
+
let nodes;
|
|
8344
|
+
let docString = '';
|
|
8345
|
+
if (isReservedSymbolToken(ctx.peek(), 'do')) {
|
|
8346
|
+
const parsedDo = parseDo(ctx, true);
|
|
8347
|
+
docString = parsedDo[1];
|
|
8348
|
+
nodes = parsedDo[0][1][1];
|
|
8539
8349
|
}
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8350
|
+
else {
|
|
8351
|
+
nodes = [ctx.parseExpression()];
|
|
8352
|
+
}
|
|
8353
|
+
const endPos = ctx.getPosition() - 1;
|
|
8354
|
+
let arity = 0;
|
|
8355
|
+
let dollar1 = 'NOT_SET'; // referring to argument bindings. $ = NAKED, $1, $2, $3, etc = WITH_1
|
|
8356
|
+
for (let pos = startPos; pos <= endPos; pos += 1) {
|
|
8357
|
+
const token = ctx.getTokenAt(pos);
|
|
8358
|
+
if (isSymbolToken(token)) {
|
|
8359
|
+
const match = placeholderRegexp.exec(token[1]);
|
|
8360
|
+
if (match) {
|
|
8361
|
+
const number = match[1] ?? '1';
|
|
8362
|
+
if (number === '1') {
|
|
8363
|
+
const mixedPercent1 = (!match[1] && dollar1 === 'WITH_1') || (match[1] && dollar1 === 'NAKED');
|
|
8364
|
+
if (mixedPercent1)
|
|
8365
|
+
throw new LitsError('Please make up your mind, either use $ or $1', firstToken[2]);
|
|
8366
|
+
dollar1 = match[1] ? 'WITH_1' : 'NAKED';
|
|
8367
|
+
}
|
|
8368
|
+
arity = Math.max(arity, Number(number));
|
|
8369
|
+
if (arity > maxShorthandLambdaArity)
|
|
8370
|
+
throw new LitsError('Can\'t specify more than 20 arguments', firstToken[2]);
|
|
8371
|
+
}
|
|
8545
8372
|
}
|
|
8546
|
-
|
|
8547
|
-
|
|
8373
|
+
}
|
|
8374
|
+
const functionArguments = [];
|
|
8375
|
+
for (let i = 1; i <= arity; i += 1) {
|
|
8376
|
+
if (i === 1 && dollar1 === 'NAKED') {
|
|
8377
|
+
functionArguments.push(withSourceCodeInfo([bindingTargetTypes.symbol, [[NodeTypes.UserDefinedSymbol, '$'], undefined]], firstToken[2]));
|
|
8548
8378
|
}
|
|
8549
8379
|
else {
|
|
8550
|
-
|
|
8380
|
+
functionArguments.push(withSourceCodeInfo([bindingTargetTypes.symbol, [[NodeTypes.UserDefinedSymbol, `$${i}`], undefined]], firstToken[2]));
|
|
8551
8381
|
}
|
|
8552
8382
|
}
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
8558
|
-
|
|
8383
|
+
const node = withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes['0_lambda'], [
|
|
8384
|
+
functionArguments,
|
|
8385
|
+
nodes,
|
|
8386
|
+
], docString]], firstToken[2]);
|
|
8387
|
+
return node;
|
|
8388
|
+
}
|
|
8389
|
+
|
|
8390
|
+
function parseFunctionCall(ctx, symbol) {
|
|
8391
|
+
ctx.advance();
|
|
8392
|
+
const params = [];
|
|
8393
|
+
while (!ctx.isAtEnd() && !isRParenToken(ctx.tryPeek())) {
|
|
8394
|
+
if (isOperatorToken(ctx.tryPeek(), '...')) {
|
|
8395
|
+
ctx.advance();
|
|
8396
|
+
params.push(withSourceCodeInfo([NodeTypes.Spread, ctx.parseExpression()], ctx.peekSourceCodeInfo()));
|
|
8397
|
+
}
|
|
8398
|
+
else {
|
|
8399
|
+
params.push(ctx.parseExpression());
|
|
8400
|
+
}
|
|
8401
|
+
const nextToken = ctx.tryPeek();
|
|
8402
|
+
if (!isOperatorToken(nextToken, ',') && !isRParenToken(nextToken)) {
|
|
8403
|
+
throw new LitsError('Expected comma or closing parenthesis', ctx.tryPeek()?.[2]);
|
|
8404
|
+
}
|
|
8405
|
+
if (isOperatorToken(nextToken, ',')) {
|
|
8406
|
+
ctx.advance();
|
|
8407
|
+
}
|
|
8408
|
+
}
|
|
8409
|
+
if (!isRParenToken(ctx.tryPeek())) {
|
|
8410
|
+
throw new LitsError('Expected closing parenthesis', ctx.peekSourceCodeInfo());
|
|
8411
|
+
}
|
|
8412
|
+
ctx.advance();
|
|
8413
|
+
if (isSpecialBuiltinSymbolNode(symbol)) { // Named function
|
|
8414
|
+
const specialExpressionType = symbol[1];
|
|
8415
|
+
const type = specialExpressionType;
|
|
8416
|
+
const specialExpression = builtin.specialExpressions[type];
|
|
8417
|
+
assertNumberOfParams(specialExpression.arity, params.length, symbol[2]);
|
|
8418
|
+
switch (type) {
|
|
8419
|
+
case specialExpressionTypes['||']:
|
|
8420
|
+
return withSourceCodeInfo([NodeTypes.SpecialExpression, [type, params]], symbol[2]);
|
|
8421
|
+
case specialExpressionTypes['&&']:
|
|
8422
|
+
return withSourceCodeInfo([NodeTypes.SpecialExpression, [type, params]], symbol[2]);
|
|
8423
|
+
case specialExpressionTypes.recur:
|
|
8424
|
+
return withSourceCodeInfo([NodeTypes.SpecialExpression, [type, params]], symbol[2]);
|
|
8425
|
+
case specialExpressionTypes.array:
|
|
8426
|
+
return withSourceCodeInfo([NodeTypes.SpecialExpression, [type, params]], symbol[2]);
|
|
8427
|
+
case specialExpressionTypes.object:
|
|
8428
|
+
return withSourceCodeInfo([NodeTypes.SpecialExpression, [type, params]], symbol[2]);
|
|
8429
|
+
case specialExpressionTypes['??']:
|
|
8430
|
+
return withSourceCodeInfo([NodeTypes.SpecialExpression, [type, params]], symbol[2]);
|
|
8431
|
+
case specialExpressionTypes['defined?']: {
|
|
8432
|
+
const [param] = params;
|
|
8433
|
+
return withSourceCodeInfo([NodeTypes.SpecialExpression, [type, param]], symbol[2]);
|
|
8434
|
+
}
|
|
8435
|
+
case specialExpressionTypes.throw: {
|
|
8436
|
+
const [param] = params;
|
|
8437
|
+
return withSourceCodeInfo([NodeTypes.SpecialExpression, [type, param]], symbol[2]);
|
|
8438
|
+
}
|
|
8439
|
+
case specialExpressionTypes['0_lambda']:
|
|
8440
|
+
case specialExpressionTypes['0_def']:
|
|
8441
|
+
throw new LitsError(`${type} is not allowed`, symbol[2]);
|
|
8442
|
+
/* v8 ignore next 2 */
|
|
8443
|
+
default:
|
|
8444
|
+
throw new LitsError(`Unknown special expression: ${type}`, symbol[2]);
|
|
8559
8445
|
}
|
|
8560
|
-
return withSourceCodeInfo([NodeTypes.ReservedSymbol, token[1]], token[2]);
|
|
8561
8446
|
}
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
this.advance();
|
|
8565
|
-
const value = token[1];
|
|
8566
|
-
const negative = value[0] === '-';
|
|
8567
|
-
const numberString = (negative ? value.substring(1) : value).replace(/_/g, '');
|
|
8568
|
-
return withSourceCodeInfo([NodeTypes.Number, negative ? -Number(numberString) : Number(numberString)], token[2]);
|
|
8447
|
+
else if (isNormalBuiltinSymbolNode(symbol) || isUserDefinedSymbolNode(symbol)) {
|
|
8448
|
+
return createNamedNormalExpressionNode(symbol, params, symbol[2]);
|
|
8569
8449
|
}
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
const stringToken = token[2] ? ['string', token[1].slice(2, -2), token[2]] : ['string', token[1].slice(2, -2)];
|
|
8573
|
-
const stringNode = this.parseString(stringToken);
|
|
8574
|
-
return smartTrim(stringNode[1]); // Extract the string value from the StringNode
|
|
8450
|
+
else {
|
|
8451
|
+
return withSourceCodeInfo([NodeTypes.NormalExpression, [symbol, params]], symbol[2]);
|
|
8575
8452
|
}
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8595
|
-
|
|
8453
|
+
}
|
|
8454
|
+
|
|
8455
|
+
function parseNumber(ctx) {
|
|
8456
|
+
const token = ctx.peek();
|
|
8457
|
+
ctx.advance();
|
|
8458
|
+
const value = token[1];
|
|
8459
|
+
const negative = value[0] === '-';
|
|
8460
|
+
const numberString = (negative ? value.substring(1) : value).replace(/_/g, '');
|
|
8461
|
+
return withSourceCodeInfo([NodeTypes.Number, negative ? -Number(numberString) : Number(numberString)], token[2]);
|
|
8462
|
+
}
|
|
8463
|
+
|
|
8464
|
+
function parseObject(ctx) {
|
|
8465
|
+
const firstToken = asLBraceToken(ctx.tryPeek());
|
|
8466
|
+
ctx.advance();
|
|
8467
|
+
const params = [];
|
|
8468
|
+
while (!ctx.isAtEnd() && !isRBraceToken(ctx.tryPeek())) {
|
|
8469
|
+
if (isOperatorToken(ctx.tryPeek(), '...')) {
|
|
8470
|
+
ctx.advance();
|
|
8471
|
+
params.push(withSourceCodeInfo([NodeTypes.Spread, ctx.parseExpression()], ctx.peekSourceCodeInfo()));
|
|
8472
|
+
}
|
|
8473
|
+
else {
|
|
8474
|
+
const token = ctx.tryPeek();
|
|
8475
|
+
if (isStringToken(token)) {
|
|
8476
|
+
const stringNode = parseString(ctx, token);
|
|
8477
|
+
params.push(withSourceCodeInfo([NodeTypes.String, stringNode[1]], token[2]));
|
|
8478
|
+
}
|
|
8479
|
+
else if (isSymbolToken(token)) {
|
|
8480
|
+
const value = token[1].startsWith('\'')
|
|
8481
|
+
? stringFromQuotedSymbol(token[1])
|
|
8482
|
+
: token[1];
|
|
8483
|
+
params.push(withSourceCodeInfo([NodeTypes.String, value], token[2]));
|
|
8484
|
+
ctx.advance();
|
|
8596
8485
|
}
|
|
8597
|
-
else if (
|
|
8598
|
-
|
|
8486
|
+
else if (isLBracketToken(token)) {
|
|
8487
|
+
ctx.advance();
|
|
8488
|
+
params.push(ctx.parseExpression());
|
|
8489
|
+
assertRBracketToken(ctx.tryPeek());
|
|
8490
|
+
ctx.advance();
|
|
8599
8491
|
}
|
|
8600
|
-
else
|
|
8601
|
-
|
|
8492
|
+
else {
|
|
8493
|
+
throw new LitsError('Expected key to be a symbol or a string', ctx.peekSourceCodeInfo());
|
|
8602
8494
|
}
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
const optionsNode = withSourceCodeInfo([NodeTypes.String, optionsString], token[2]);
|
|
8615
|
-
const node = withSourceCodeInfo([
|
|
8616
|
-
NodeTypes.NormalExpression,
|
|
8617
|
-
[
|
|
8618
|
-
withSourceCodeInfo([NodeTypes.NormalBuiltinSymbol, normalExpressionTypes.regexp], token[2]),
|
|
8619
|
-
[stringNode, optionsNode],
|
|
8620
|
-
],
|
|
8621
|
-
], token[2]);
|
|
8622
|
-
return node;
|
|
8495
|
+
assertOperatorToken(ctx.tryPeek(), ':');
|
|
8496
|
+
ctx.advance();
|
|
8497
|
+
params.push(ctx.parseExpression());
|
|
8498
|
+
}
|
|
8499
|
+
const nextToken = ctx.tryPeek();
|
|
8500
|
+
if (!isOperatorToken(nextToken, ',') && !isRBraceToken(nextToken)) {
|
|
8501
|
+
throw new LitsError('Expected comma or closing brace', ctx.peekSourceCodeInfo());
|
|
8502
|
+
}
|
|
8503
|
+
if (isOperatorToken(nextToken, ',')) {
|
|
8504
|
+
ctx.advance();
|
|
8505
|
+
}
|
|
8623
8506
|
}
|
|
8507
|
+
assertRBraceToken(ctx.tryPeek());
|
|
8508
|
+
ctx.advance();
|
|
8509
|
+
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.object, params]], firstToken[2]);
|
|
8624
8510
|
}
|
|
8625
8511
|
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
|
|
8632
|
-
|
|
8633
|
-
|
|
8634
|
-
|
|
8635
|
-
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
const tokenStream = lits.tokenize(partialProgram);
|
|
8641
|
-
const lastToken = tokenStream.tokens.at(-1);
|
|
8642
|
-
if (!lastToken) {
|
|
8643
|
-
return;
|
|
8512
|
+
function parseOperand(ctx) {
|
|
8513
|
+
let operand = parseOperandPart(ctx);
|
|
8514
|
+
let token = ctx.tryPeek();
|
|
8515
|
+
while (isOperatorToken(token, '.') || isLBracketToken(token) || isLParenToken(token)) {
|
|
8516
|
+
if (token[1] === '.') {
|
|
8517
|
+
ctx.advance();
|
|
8518
|
+
const symbolToken = ctx.tryPeek();
|
|
8519
|
+
if (!isSymbolToken(symbolToken)) {
|
|
8520
|
+
throw new LitsError('Expected symbol', ctx.peekSourceCodeInfo());
|
|
8521
|
+
}
|
|
8522
|
+
const stringNode = withSourceCodeInfo([NodeTypes.String, symbolToken[1]], symbolToken[2]);
|
|
8523
|
+
operand = createAccessorNode(operand, stringNode, token[2]);
|
|
8524
|
+
ctx.advance();
|
|
8525
|
+
token = ctx.tryPeek();
|
|
8644
8526
|
}
|
|
8645
|
-
if (
|
|
8646
|
-
|
|
8527
|
+
else if (isLBracketToken(token)) {
|
|
8528
|
+
ctx.advance();
|
|
8529
|
+
const expression = ctx.parseExpression();
|
|
8530
|
+
if (!isRBracketToken(ctx.tryPeek())) {
|
|
8531
|
+
throw new LitsError('Expected closing bracket', ctx.peekSourceCodeInfo());
|
|
8532
|
+
}
|
|
8533
|
+
operand = createAccessorNode(operand, expression, token[2]);
|
|
8534
|
+
ctx.advance();
|
|
8535
|
+
token = ctx.tryPeek();
|
|
8536
|
+
}
|
|
8537
|
+
else if (isLParenToken(token)) {
|
|
8538
|
+
operand = parseFunctionCall(ctx, operand);
|
|
8539
|
+
token = ctx.tryPeek();
|
|
8647
8540
|
}
|
|
8648
|
-
this.searchString = lastToken[1];
|
|
8649
|
-
this.prefixProgram = this.originalProgram.slice(0, this.originalPosition - this.searchString.length);
|
|
8650
|
-
this.suffixProgram = this.originalProgram.slice(this.prefixProgram.length + this.searchString.length);
|
|
8651
|
-
this.originalProgram.slice(this.prefixProgram.length + this.searchString.length);
|
|
8652
|
-
this.suggestions = this.generateSuggestions(params);
|
|
8653
|
-
}
|
|
8654
|
-
getNextSuggestion() {
|
|
8655
|
-
return this.getAutoCompleteSuggestionResult(this.getNextSuggestionSymbol());
|
|
8656
|
-
}
|
|
8657
|
-
getPreviousSuggestion() {
|
|
8658
|
-
return this.getAutoCompleteSuggestionResult(this.getPreviousSuggestionSymbol());
|
|
8659
8541
|
}
|
|
8660
|
-
|
|
8661
|
-
|
|
8662
|
-
|
|
8542
|
+
return operand;
|
|
8543
|
+
}
|
|
8544
|
+
function parseOperandPart(ctx) {
|
|
8545
|
+
const token = ctx.peek();
|
|
8546
|
+
// Parentheses
|
|
8547
|
+
if (isLParenToken(token)) {
|
|
8548
|
+
ctx.storePosition();
|
|
8549
|
+
const lamdaFunction = parseLambdaFunction(ctx);
|
|
8550
|
+
if (lamdaFunction) {
|
|
8551
|
+
return lamdaFunction;
|
|
8663
8552
|
}
|
|
8664
|
-
|
|
8665
|
-
|
|
8666
|
-
|
|
8667
|
-
|
|
8553
|
+
ctx.restorePosition();
|
|
8554
|
+
ctx.advance();
|
|
8555
|
+
const expression = ctx.parseExpression();
|
|
8556
|
+
if (!isRParenToken(ctx.peek())) {
|
|
8557
|
+
throw new LitsError('Expected closing parenthesis', ctx.peekSourceCodeInfo());
|
|
8558
|
+
}
|
|
8559
|
+
ctx.advance();
|
|
8560
|
+
return expression;
|
|
8668
8561
|
}
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8562
|
+
else if (isOperatorToken(token)) {
|
|
8563
|
+
const operatorName = token[1];
|
|
8564
|
+
if (isBinaryOperator(operatorName)) {
|
|
8565
|
+
ctx.advance();
|
|
8566
|
+
if (specialExpressionTypes[operatorName] !== undefined) {
|
|
8567
|
+
return withSourceCodeInfo([NodeTypes.SpecialBuiltinSymbol, specialExpressionTypes[operatorName]], token[2]);
|
|
8568
|
+
}
|
|
8569
|
+
return withSourceCodeInfo([NodeTypes.NormalBuiltinSymbol, normalExpressionTypes[operatorName]], token[2]);
|
|
8672
8570
|
}
|
|
8673
|
-
if (
|
|
8674
|
-
|
|
8571
|
+
if (operatorName === '->') {
|
|
8572
|
+
return parseShorthandLambdaFunction(ctx);
|
|
8675
8573
|
}
|
|
8676
8574
|
else {
|
|
8677
|
-
|
|
8678
|
-
|
|
8679
|
-
|
|
8575
|
+
throw new LitsError(`Illegal operator: ${operatorName}`, token[2]);
|
|
8576
|
+
}
|
|
8577
|
+
}
|
|
8578
|
+
// Object litteral, e.g. {a: 1, b: 2}
|
|
8579
|
+
if (isLBraceToken(token)) {
|
|
8580
|
+
return parseObject(ctx);
|
|
8581
|
+
}
|
|
8582
|
+
// Array litteral, e.g. [1, 2]
|
|
8583
|
+
if (isLBracketToken(token)) {
|
|
8584
|
+
return parseArray(ctx);
|
|
8585
|
+
}
|
|
8586
|
+
const tokenType = token[0];
|
|
8587
|
+
switch (tokenType) {
|
|
8588
|
+
case 'Number':
|
|
8589
|
+
case 'BasePrefixedNumber':
|
|
8590
|
+
return parseNumber(ctx);
|
|
8591
|
+
case 'string':
|
|
8592
|
+
return parseString(ctx, token);
|
|
8593
|
+
case 'Symbol': {
|
|
8594
|
+
ctx.storePosition();
|
|
8595
|
+
const lamdaFunction = parseLambdaFunction(ctx);
|
|
8596
|
+
if (lamdaFunction) {
|
|
8597
|
+
return lamdaFunction;
|
|
8680
8598
|
}
|
|
8599
|
+
ctx.restorePosition();
|
|
8600
|
+
return parseSymbol(ctx);
|
|
8681
8601
|
}
|
|
8682
|
-
|
|
8602
|
+
case 'ReservedSymbol':
|
|
8603
|
+
return parseReservedSymbol(ctx);
|
|
8604
|
+
case 'RegexpShorthand':
|
|
8605
|
+
return parseRegexpShorthand(ctx);
|
|
8606
|
+
default:
|
|
8607
|
+
throw new LitsError(`Unknown token type: ${tokenType}`, token[2]);
|
|
8683
8608
|
}
|
|
8684
|
-
|
|
8685
|
-
|
|
8686
|
-
|
|
8609
|
+
}
|
|
8610
|
+
function createAccessorNode(left, right, sourceCodeInfo) {
|
|
8611
|
+
return withSourceCodeInfo([NodeTypes.NormalExpression, [[NodeTypes.NormalBuiltinSymbol, normalExpressionTypes.get], [left, right]]], sourceCodeInfo);
|
|
8612
|
+
}
|
|
8613
|
+
|
|
8614
|
+
function getPrecedence(operatorSign, sourceCodeInfo) {
|
|
8615
|
+
switch (operatorSign) {
|
|
8616
|
+
case '^': // exponentiation
|
|
8617
|
+
return exponentiationPrecedence;
|
|
8618
|
+
case '*': // multiplication
|
|
8619
|
+
case '/': // division
|
|
8620
|
+
case '%': // remainder
|
|
8621
|
+
return 11;
|
|
8622
|
+
case '+': // addition
|
|
8623
|
+
case '-': // subtraction
|
|
8624
|
+
return 10;
|
|
8625
|
+
case '<<': // left shift
|
|
8626
|
+
case '>>': // signed right shift
|
|
8627
|
+
case '>>>': // unsigned right shift
|
|
8628
|
+
return 9;
|
|
8629
|
+
case '++': // string concatenation
|
|
8630
|
+
return 8;
|
|
8631
|
+
case '<': // less than
|
|
8632
|
+
case '<=': // less than or equal
|
|
8633
|
+
case '≤': // less than or equal
|
|
8634
|
+
case '>': // greater than
|
|
8635
|
+
case '>=': // greater than or equal
|
|
8636
|
+
case '≥': // greater than or equal
|
|
8637
|
+
return 7;
|
|
8638
|
+
case '==': // equal
|
|
8639
|
+
case '!=': // not equal
|
|
8640
|
+
case '≠': // not equal
|
|
8641
|
+
return 6;
|
|
8642
|
+
case '&': // bitwise AND
|
|
8643
|
+
case 'xor': // bitwise XOR
|
|
8644
|
+
case '|': // bitwise OR
|
|
8645
|
+
return 5;
|
|
8646
|
+
case '&&': // logical AND
|
|
8647
|
+
case '||': // logical OR
|
|
8648
|
+
case '??': // nullish coalescing
|
|
8649
|
+
return 4;
|
|
8650
|
+
// leave room for binaryFunctionalOperatorPrecedence = 3
|
|
8651
|
+
case '|>': // pipe
|
|
8652
|
+
return 2;
|
|
8653
|
+
// leave room for conditionalOperatorPrecedence = 1
|
|
8654
|
+
/* v8 ignore next 2 */
|
|
8655
|
+
default:
|
|
8656
|
+
throw new LitsError(`Unknown binary operator: ${operatorSign}`, sourceCodeInfo);
|
|
8657
|
+
}
|
|
8658
|
+
}
|
|
8659
|
+
|
|
8660
|
+
function parse(tokenStream) {
|
|
8661
|
+
tokenStream.tokens.forEach((token) => {
|
|
8662
|
+
if (token[0] === 'Error') {
|
|
8663
|
+
throw new LitsError(token[3], token[2]);
|
|
8687
8664
|
}
|
|
8688
|
-
|
|
8689
|
-
|
|
8665
|
+
});
|
|
8666
|
+
const nodes = [];
|
|
8667
|
+
const ctx = new ParserContext(tokenStream);
|
|
8668
|
+
ctx.parseExpression = (precedence = 0, moduleScope = false) => parseExpression(ctx, precedence, moduleScope);
|
|
8669
|
+
while (!ctx.isAtEnd()) {
|
|
8670
|
+
nodes.push(parseExpression(ctx, 0, true));
|
|
8671
|
+
if (isOperatorToken(ctx.tryPeek(), ';')) {
|
|
8672
|
+
ctx.advance();
|
|
8690
8673
|
}
|
|
8691
8674
|
else {
|
|
8692
|
-
|
|
8693
|
-
|
|
8694
|
-
this.suggestionIndex = this.suggestions.length - 1;
|
|
8675
|
+
if (!ctx.isAtEnd()) {
|
|
8676
|
+
throw new LitsError('Expected ;', ctx.peekSourceCodeInfo());
|
|
8695
8677
|
}
|
|
8696
8678
|
}
|
|
8697
|
-
return this.suggestions[this.suggestionIndex];
|
|
8698
8679
|
}
|
|
8699
|
-
|
|
8700
|
-
|
|
8680
|
+
return nodes;
|
|
8681
|
+
}
|
|
8682
|
+
function parseExpression(ctx, precedence = 0, moduleScope = false) {
|
|
8683
|
+
const token = ctx.tryPeek();
|
|
8684
|
+
let left;
|
|
8685
|
+
if (isSymbolToken(token)) {
|
|
8686
|
+
switch (token[1]) {
|
|
8687
|
+
case 'let':
|
|
8688
|
+
return parseLet(ctx, token);
|
|
8689
|
+
case 'if':
|
|
8690
|
+
case 'unless':
|
|
8691
|
+
left = parseIfOrUnless(ctx, token);
|
|
8692
|
+
break;
|
|
8693
|
+
case 'cond':
|
|
8694
|
+
left = parseCond(ctx, token);
|
|
8695
|
+
break;
|
|
8696
|
+
case 'switch':
|
|
8697
|
+
left = parseSwitch(ctx, token);
|
|
8698
|
+
break;
|
|
8699
|
+
case 'for':
|
|
8700
|
+
case 'doseq':
|
|
8701
|
+
left = parseForOrDoseq(ctx, token);
|
|
8702
|
+
break;
|
|
8703
|
+
case 'loop':
|
|
8704
|
+
left = parseLoop(ctx, token);
|
|
8705
|
+
break;
|
|
8706
|
+
case 'try':
|
|
8707
|
+
left = parseTry(ctx, token);
|
|
8708
|
+
break;
|
|
8709
|
+
}
|
|
8701
8710
|
}
|
|
8702
|
-
|
|
8703
|
-
|
|
8711
|
+
else if (isReservedSymbolToken(token, 'do')) {
|
|
8712
|
+
left = parseDo(ctx)[0];
|
|
8704
8713
|
}
|
|
8705
|
-
|
|
8706
|
-
|
|
8707
|
-
|
|
8708
|
-
|
|
8709
|
-
|
|
8710
|
-
startsWithCaseInsensitive.forEach(suggestion => blacklist.add(suggestion));
|
|
8711
|
-
const includesCaseSensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.includes(this.searchString));
|
|
8712
|
-
includesCaseSensitive.forEach(suggestion => blacklist.add(suggestion));
|
|
8713
|
-
const includesCaseInsensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.includes(this.searchString.toLowerCase()));
|
|
8714
|
-
includesCaseInsensitive.forEach(suggestion => blacklist.add(suggestion));
|
|
8715
|
-
return [...startsWithCaseSensitive, ...startsWithCaseInsensitive, ...includesCaseSensitive, ...includesCaseInsensitive];
|
|
8714
|
+
else if (isReservedSymbolToken(token, 'export')) {
|
|
8715
|
+
if (!moduleScope) {
|
|
8716
|
+
throw new LitsError('export is only allowed in module scope', token[2]);
|
|
8717
|
+
}
|
|
8718
|
+
return parseExport(ctx, token);
|
|
8716
8719
|
}
|
|
8717
|
-
|
|
8718
|
-
|
|
8719
|
-
|
|
8720
|
-
|
|
8721
|
-
|
|
8720
|
+
left ||= parseOperand(ctx);
|
|
8721
|
+
let operator = ctx.tryPeek();
|
|
8722
|
+
while (!isAtExpressionEnd(ctx)) {
|
|
8723
|
+
if (isA_BinaryOperatorToken(operator)) {
|
|
8724
|
+
const name = operator[1];
|
|
8725
|
+
const newPrecedece = getPrecedence(name, operator[2]);
|
|
8726
|
+
if (newPrecedece <= precedence
|
|
8727
|
+
// ^ (exponentiation) is right associative
|
|
8728
|
+
&& !(newPrecedece === exponentiationPrecedence && precedence === exponentiationPrecedence)) {
|
|
8729
|
+
break;
|
|
8722
8730
|
}
|
|
8723
|
-
|
|
8724
|
-
|
|
8725
|
-
|
|
8726
|
-
.
|
|
8727
|
-
|
|
8728
|
-
|
|
8729
|
-
|
|
8730
|
-
|
|
8731
|
-
|
|
8732
|
-
|
|
8733
|
-
|
|
8734
|
-
|
|
8735
|
-
|
|
8736
|
-
|
|
8737
|
-
|
|
8738
|
-
|
|
8731
|
+
const symbol = specialExpressionTypes[name]
|
|
8732
|
+
? withSourceCodeInfo([NodeTypes.SpecialBuiltinSymbol, specialExpressionTypes[name]], operator[2])
|
|
8733
|
+
: withSourceCodeInfo([NodeTypes.NormalBuiltinSymbol, normalExpressionTypes[name]], operator[2]);
|
|
8734
|
+
ctx.advance();
|
|
8735
|
+
const right = parseExpression(ctx, newPrecedece);
|
|
8736
|
+
left = fromBinaryOperatorToNode(operator, symbol, left, right, operator[2]);
|
|
8737
|
+
}
|
|
8738
|
+
else if (isSymbolToken(operator)) {
|
|
8739
|
+
if (!isFunctionOperator(operator[1])) {
|
|
8740
|
+
break;
|
|
8741
|
+
}
|
|
8742
|
+
const newPrecedence = binaryFunctionalOperatorPrecedence;
|
|
8743
|
+
if (newPrecedence <= precedence) {
|
|
8744
|
+
break;
|
|
8745
|
+
}
|
|
8746
|
+
const operatorSymbol = parseSymbol(ctx);
|
|
8747
|
+
const right = parseExpression(ctx, newPrecedence);
|
|
8748
|
+
if (isSpecialBuiltinSymbolNode(operatorSymbol)) {
|
|
8749
|
+
throw new LitsError('Special expressions are not allowed in binary functional operators', operatorSymbol[2]);
|
|
8750
|
+
}
|
|
8751
|
+
left = createNamedNormalExpressionNode(operatorSymbol, [left, right], operator[2]);
|
|
8752
|
+
}
|
|
8753
|
+
else if (operator?.[1] === '?') {
|
|
8754
|
+
if (conditionalOperatorPrecedence <= precedence) {
|
|
8755
|
+
break;
|
|
8756
|
+
}
|
|
8757
|
+
ctx.advance();
|
|
8758
|
+
const trueNode = parseExpression(ctx);
|
|
8759
|
+
if (!isOperatorToken(ctx.tryPeek(), ':')) {
|
|
8760
|
+
throw new LitsError('Expected :', ctx.peekSourceCodeInfo());
|
|
8761
|
+
}
|
|
8762
|
+
ctx.advance();
|
|
8763
|
+
const falseNode = parseExpression(ctx);
|
|
8764
|
+
left = withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.if, [left, trueNode, falseNode]]], left[2]);
|
|
8765
|
+
}
|
|
8766
|
+
else {
|
|
8767
|
+
break;
|
|
8768
|
+
}
|
|
8769
|
+
operator = ctx.tryPeek();
|
|
8770
|
+
}
|
|
8771
|
+
return left;
|
|
8772
|
+
}
|
|
8773
|
+
function parseExport(ctx, exportToken) {
|
|
8774
|
+
ctx.advance();
|
|
8775
|
+
const token = ctx.tryPeek();
|
|
8776
|
+
if (isSymbolToken(token, 'let')) {
|
|
8777
|
+
const letNode = parseLet(ctx, asSymbolToken(token));
|
|
8778
|
+
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes['0_def'], letNode[1][1]]], exportToken[2]);
|
|
8779
|
+
}
|
|
8780
|
+
else {
|
|
8781
|
+
throw new LitsError('Expected let', ctx.peekSourceCodeInfo());
|
|
8739
8782
|
}
|
|
8740
8783
|
}
|
|
8741
8784
|
|
|
@@ -8846,10 +8889,7 @@ class Lits {
|
|
|
8846
8889
|
body: [],
|
|
8847
8890
|
hasDebugData: tokenStream.hasDebugData,
|
|
8848
8891
|
};
|
|
8849
|
-
|
|
8850
|
-
position: 0,
|
|
8851
|
-
};
|
|
8852
|
-
ast.body = new Parser(tokenStream, parseState).parse();
|
|
8892
|
+
ast.body = parse(tokenStream);
|
|
8853
8893
|
return ast;
|
|
8854
8894
|
}
|
|
8855
8895
|
evaluate(ast, params) {
|