@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.
Files changed (208) hide show
  1. package/dist/cli/cli.js +1145 -1105
  2. package/dist/cli/src/AutoCompleter/AutoCompleter.d.ts +2 -1
  3. package/dist/cli/src/builtin/bindingNode.d.ts +3 -3
  4. package/dist/cli/src/builtin/index.d.ts +0 -1
  5. package/dist/cli/src/builtin/interface.d.ts +3 -4
  6. package/dist/cli/src/builtin/specialExpressionTypes.d.ts +0 -1
  7. package/dist/cli/src/builtin/specialExpressions/and.d.ts +2 -2
  8. package/dist/cli/src/builtin/specialExpressions/array.d.ts +2 -2
  9. package/dist/cli/src/builtin/specialExpressions/block.d.ts +2 -2
  10. package/dist/cli/src/builtin/specialExpressions/cond.d.ts +2 -2
  11. package/dist/cli/src/builtin/specialExpressions/if.d.ts +2 -2
  12. package/dist/cli/src/builtin/specialExpressions/loop.d.ts +2 -2
  13. package/dist/cli/src/builtin/specialExpressions/loops.d.ts +4 -4
  14. package/dist/cli/src/builtin/specialExpressions/object.d.ts +2 -2
  15. package/dist/cli/src/builtin/specialExpressions/or.d.ts +2 -2
  16. package/dist/cli/src/builtin/specialExpressions/qq.d.ts +2 -2
  17. package/dist/cli/src/builtin/specialExpressions/recur.d.ts +2 -2
  18. package/dist/cli/src/builtin/specialExpressions/switch.d.ts +2 -2
  19. package/dist/cli/src/builtin/specialExpressions/throw.d.ts +2 -2
  20. package/dist/cli/src/builtin/specialExpressions/try.d.ts +2 -2
  21. package/dist/cli/src/builtin/specialExpressions/unless.d.ts +2 -2
  22. package/dist/cli/src/builtin/utils.d.ts +2 -2
  23. package/dist/cli/src/evaluator/index.d.ts +2 -2
  24. package/dist/cli/src/evaluator/interface.d.ts +4 -3
  25. package/dist/cli/src/getUndefinedSymbols/index.d.ts +2 -2
  26. package/dist/cli/src/parser/ParserContext.d.ts +20 -0
  27. package/dist/cli/src/parser/helpers.d.ts +19 -0
  28. package/dist/cli/src/parser/index.d.ts +3 -0
  29. package/dist/cli/src/parser/subParsers/getPrecedence.d.ts +3 -0
  30. package/dist/cli/src/parser/subParsers/parseArray.d.ts +3 -0
  31. package/dist/cli/src/parser/subParsers/parseBindingTarget.d.ts +6 -0
  32. package/dist/cli/src/parser/subParsers/parseCond.d.ts +4 -0
  33. package/dist/cli/src/parser/subParsers/parseDo.d.ts +3 -0
  34. package/dist/cli/src/parser/subParsers/parseForOrDoseq.d.ts +4 -0
  35. package/dist/cli/src/parser/subParsers/parseFunction.d.ts +4 -0
  36. package/dist/cli/src/parser/subParsers/parseFunctionCall.d.ts +3 -0
  37. package/dist/cli/src/parser/subParsers/parseIfOrUnless.d.ts +5 -0
  38. package/dist/cli/src/parser/subParsers/parseImplicitBlock.d.ts +5 -0
  39. package/dist/cli/src/parser/subParsers/parseLet.d.ts +4 -0
  40. package/dist/cli/src/parser/subParsers/parseLoop.d.ts +4 -0
  41. package/dist/cli/src/parser/subParsers/parseNumber.d.ts +3 -0
  42. package/dist/cli/src/parser/subParsers/parseObject.d.ts +3 -0
  43. package/dist/cli/src/parser/subParsers/parseOperand.d.ts +3 -0
  44. package/dist/cli/src/parser/subParsers/parseRegexpShorthand.d.ts +3 -0
  45. package/dist/cli/src/parser/subParsers/parseReservedSymbol.d.ts +3 -0
  46. package/dist/cli/src/parser/subParsers/parseString.d.ts +4 -0
  47. package/dist/cli/src/parser/subParsers/parseSwitch.d.ts +4 -0
  48. package/dist/cli/src/parser/subParsers/parseSymbol.d.ts +3 -0
  49. package/dist/cli/src/parser/subParsers/parseTry.d.ts +4 -0
  50. package/dist/cli/src/parser/types.d.ts +19 -26
  51. package/dist/cli/src/testFramework/index.d.ts +3 -2
  52. package/dist/cli/src/tokenizer/token.d.ts +0 -2
  53. package/dist/cli/src/tokenizer/tokenizers.d.ts +2 -2
  54. package/dist/cli/src/typeGuards/astNode.d.ts +18 -19
  55. package/dist/cli/src/typeGuards/index.d.ts +0 -1
  56. package/dist/cli/src/typeGuards/lits.d.ts +0 -1
  57. package/dist/full.esm.js +1 -1
  58. package/dist/full.esm.js.map +1 -1
  59. package/dist/full.js +1 -1
  60. package/dist/full.js.map +1 -1
  61. package/dist/index.esm.js +1 -1
  62. package/dist/index.esm.js.map +1 -1
  63. package/dist/index.js +1 -1
  64. package/dist/index.js.map +1 -1
  65. package/dist/lits.iife.js +1 -1
  66. package/dist/lits.iife.js.map +1 -1
  67. package/dist/modules/assert.esm.js.map +1 -1
  68. package/dist/modules/assert.js.map +1 -1
  69. package/dist/modules/bitwise.esm.js.map +1 -1
  70. package/dist/modules/bitwise.js.map +1 -1
  71. package/dist/modules/collection.esm.js.map +1 -1
  72. package/dist/modules/collection.js.map +1 -1
  73. package/dist/modules/functional.esm.js.map +1 -1
  74. package/dist/modules/functional.js.map +1 -1
  75. package/dist/modules/grid.esm.js.map +1 -1
  76. package/dist/modules/grid.js.map +1 -1
  77. package/dist/modules/linear-algebra.esm.js.map +1 -1
  78. package/dist/modules/linear-algebra.js.map +1 -1
  79. package/dist/modules/matrix.esm.js.map +1 -1
  80. package/dist/modules/matrix.js.map +1 -1
  81. package/dist/modules/number-theory.esm.js.map +1 -1
  82. package/dist/modules/number-theory.js.map +1 -1
  83. package/dist/modules/random.esm.js.map +1 -1
  84. package/dist/modules/random.js.map +1 -1
  85. package/dist/modules/sequence.esm.js.map +1 -1
  86. package/dist/modules/sequence.js.map +1 -1
  87. package/dist/modules/src/AutoCompleter/AutoCompleter.d.ts +2 -1
  88. package/dist/modules/src/builtin/bindingNode.d.ts +3 -3
  89. package/dist/modules/src/builtin/index.d.ts +0 -1
  90. package/dist/modules/src/builtin/interface.d.ts +3 -4
  91. package/dist/modules/src/builtin/specialExpressionTypes.d.ts +0 -1
  92. package/dist/modules/src/builtin/specialExpressions/and.d.ts +2 -2
  93. package/dist/modules/src/builtin/specialExpressions/array.d.ts +2 -2
  94. package/dist/modules/src/builtin/specialExpressions/block.d.ts +2 -2
  95. package/dist/modules/src/builtin/specialExpressions/cond.d.ts +2 -2
  96. package/dist/modules/src/builtin/specialExpressions/if.d.ts +2 -2
  97. package/dist/modules/src/builtin/specialExpressions/loop.d.ts +2 -2
  98. package/dist/modules/src/builtin/specialExpressions/loops.d.ts +4 -4
  99. package/dist/modules/src/builtin/specialExpressions/object.d.ts +2 -2
  100. package/dist/modules/src/builtin/specialExpressions/or.d.ts +2 -2
  101. package/dist/modules/src/builtin/specialExpressions/qq.d.ts +2 -2
  102. package/dist/modules/src/builtin/specialExpressions/recur.d.ts +2 -2
  103. package/dist/modules/src/builtin/specialExpressions/switch.d.ts +2 -2
  104. package/dist/modules/src/builtin/specialExpressions/throw.d.ts +2 -2
  105. package/dist/modules/src/builtin/specialExpressions/try.d.ts +2 -2
  106. package/dist/modules/src/builtin/specialExpressions/unless.d.ts +2 -2
  107. package/dist/modules/src/builtin/utils.d.ts +2 -2
  108. package/dist/modules/src/evaluator/index.d.ts +2 -2
  109. package/dist/modules/src/evaluator/interface.d.ts +4 -3
  110. package/dist/modules/src/getUndefinedSymbols/index.d.ts +2 -2
  111. package/dist/modules/src/parser/ParserContext.d.ts +20 -0
  112. package/dist/modules/src/parser/helpers.d.ts +19 -0
  113. package/dist/modules/src/parser/index.d.ts +3 -0
  114. package/dist/modules/src/parser/subParsers/getPrecedence.d.ts +3 -0
  115. package/dist/modules/src/parser/subParsers/parseArray.d.ts +3 -0
  116. package/dist/modules/src/parser/subParsers/parseBindingTarget.d.ts +6 -0
  117. package/dist/modules/src/parser/subParsers/parseCond.d.ts +4 -0
  118. package/dist/modules/src/parser/subParsers/parseDo.d.ts +3 -0
  119. package/dist/modules/src/parser/subParsers/parseForOrDoseq.d.ts +4 -0
  120. package/dist/modules/src/parser/subParsers/parseFunction.d.ts +4 -0
  121. package/dist/modules/src/parser/subParsers/parseFunctionCall.d.ts +3 -0
  122. package/dist/modules/src/parser/subParsers/parseIfOrUnless.d.ts +5 -0
  123. package/dist/modules/src/parser/subParsers/parseImplicitBlock.d.ts +5 -0
  124. package/dist/modules/src/parser/subParsers/parseLet.d.ts +4 -0
  125. package/dist/modules/src/parser/subParsers/parseLoop.d.ts +4 -0
  126. package/dist/modules/src/parser/subParsers/parseNumber.d.ts +3 -0
  127. package/dist/modules/src/parser/subParsers/parseObject.d.ts +3 -0
  128. package/dist/modules/src/parser/subParsers/parseOperand.d.ts +3 -0
  129. package/dist/modules/src/parser/subParsers/parseRegexpShorthand.d.ts +3 -0
  130. package/dist/modules/src/parser/subParsers/parseReservedSymbol.d.ts +3 -0
  131. package/dist/modules/src/parser/subParsers/parseString.d.ts +4 -0
  132. package/dist/modules/src/parser/subParsers/parseSwitch.d.ts +4 -0
  133. package/dist/modules/src/parser/subParsers/parseSymbol.d.ts +3 -0
  134. package/dist/modules/src/parser/subParsers/parseTry.d.ts +4 -0
  135. package/dist/modules/src/parser/types.d.ts +19 -26
  136. package/dist/modules/src/testFramework/index.d.ts +3 -2
  137. package/dist/modules/src/tokenizer/token.d.ts +0 -2
  138. package/dist/modules/src/tokenizer/tokenizers.d.ts +2 -2
  139. package/dist/modules/src/typeGuards/astNode.d.ts +18 -19
  140. package/dist/modules/src/typeGuards/index.d.ts +0 -1
  141. package/dist/modules/src/typeGuards/lits.d.ts +0 -1
  142. package/dist/modules/string.esm.js.map +1 -1
  143. package/dist/modules/string.js.map +1 -1
  144. package/dist/modules/vector.esm.js.map +1 -1
  145. package/dist/modules/vector.js.map +1 -1
  146. package/dist/src/AutoCompleter/AutoCompleter.d.ts +2 -1
  147. package/dist/src/builtin/bindingNode.d.ts +3 -3
  148. package/dist/src/builtin/index.d.ts +0 -1
  149. package/dist/src/builtin/interface.d.ts +3 -4
  150. package/dist/src/builtin/specialExpressionTypes.d.ts +0 -1
  151. package/dist/src/builtin/specialExpressions/and.d.ts +2 -2
  152. package/dist/src/builtin/specialExpressions/array.d.ts +2 -2
  153. package/dist/src/builtin/specialExpressions/block.d.ts +2 -2
  154. package/dist/src/builtin/specialExpressions/cond.d.ts +2 -2
  155. package/dist/src/builtin/specialExpressions/if.d.ts +2 -2
  156. package/dist/src/builtin/specialExpressions/loop.d.ts +2 -2
  157. package/dist/src/builtin/specialExpressions/loops.d.ts +4 -4
  158. package/dist/src/builtin/specialExpressions/object.d.ts +2 -2
  159. package/dist/src/builtin/specialExpressions/or.d.ts +2 -2
  160. package/dist/src/builtin/specialExpressions/qq.d.ts +2 -2
  161. package/dist/src/builtin/specialExpressions/recur.d.ts +2 -2
  162. package/dist/src/builtin/specialExpressions/switch.d.ts +2 -2
  163. package/dist/src/builtin/specialExpressions/throw.d.ts +2 -2
  164. package/dist/src/builtin/specialExpressions/try.d.ts +2 -2
  165. package/dist/src/builtin/specialExpressions/unless.d.ts +2 -2
  166. package/dist/src/builtin/utils.d.ts +2 -2
  167. package/dist/src/evaluator/index.d.ts +2 -2
  168. package/dist/src/evaluator/interface.d.ts +4 -3
  169. package/dist/src/getUndefinedSymbols/index.d.ts +2 -2
  170. package/dist/src/parser/ParserContext.d.ts +20 -0
  171. package/dist/src/parser/helpers.d.ts +19 -0
  172. package/dist/src/parser/index.d.ts +3 -0
  173. package/dist/src/parser/subParsers/getPrecedence.d.ts +3 -0
  174. package/dist/src/parser/subParsers/parseArray.d.ts +3 -0
  175. package/dist/src/parser/subParsers/parseBindingTarget.d.ts +6 -0
  176. package/dist/src/parser/subParsers/parseCond.d.ts +4 -0
  177. package/dist/src/parser/subParsers/parseDo.d.ts +3 -0
  178. package/dist/src/parser/subParsers/parseForOrDoseq.d.ts +4 -0
  179. package/dist/src/parser/subParsers/parseFunction.d.ts +4 -0
  180. package/dist/src/parser/subParsers/parseFunctionCall.d.ts +3 -0
  181. package/dist/src/parser/subParsers/parseIfOrUnless.d.ts +5 -0
  182. package/dist/src/parser/subParsers/parseImplicitBlock.d.ts +5 -0
  183. package/dist/src/parser/subParsers/parseLet.d.ts +4 -0
  184. package/dist/src/parser/subParsers/parseLoop.d.ts +4 -0
  185. package/dist/src/parser/subParsers/parseNumber.d.ts +3 -0
  186. package/dist/src/parser/subParsers/parseObject.d.ts +3 -0
  187. package/dist/src/parser/subParsers/parseOperand.d.ts +3 -0
  188. package/dist/src/parser/subParsers/parseRegexpShorthand.d.ts +3 -0
  189. package/dist/src/parser/subParsers/parseReservedSymbol.d.ts +3 -0
  190. package/dist/src/parser/subParsers/parseString.d.ts +4 -0
  191. package/dist/src/parser/subParsers/parseSwitch.d.ts +4 -0
  192. package/dist/src/parser/subParsers/parseSymbol.d.ts +3 -0
  193. package/dist/src/parser/subParsers/parseTry.d.ts +4 -0
  194. package/dist/src/parser/types.d.ts +19 -26
  195. package/dist/src/testFramework/index.d.ts +3 -2
  196. package/dist/src/tokenizer/token.d.ts +0 -2
  197. package/dist/src/tokenizer/tokenizers.d.ts +2 -2
  198. package/dist/src/typeGuards/astNode.d.ts +18 -19
  199. package/dist/src/typeGuards/index.d.ts +0 -1
  200. package/dist/src/typeGuards/lits.d.ts +0 -1
  201. package/dist/testFramework.esm.js +1 -1
  202. package/dist/testFramework.esm.js.map +1 -1
  203. package/dist/testFramework.js +1 -1
  204. package/dist/testFramework.js.map +1 -1
  205. package/package.json +1 -1
  206. package/dist/cli/src/parser/Parser.d.ts +0 -58
  207. package/dist/modules/src/parser/Parser.d.ts +0 -58
  208. 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.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 createAccessorNode(left, right, sourceCodeInfo) {
7545
- return withSourceCodeInfo([NodeTypes.NormalExpression, [[NodeTypes.NormalBuiltinSymbol, normalExpressionTypes.get], [left, right]]], sourceCodeInfo);
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
- class Parser {
7593
- tokenStream;
7594
- parseState;
7595
- constructor(tokenStream, parseState) {
7596
- this.tokenStream = tokenStream;
7597
- this.parseState = parseState;
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
- peekAhead(count) {
7607
- return this.tokenStream.tokens[this.parseState.position + count];
7747
+ if (token[1][0] === '\'') {
7748
+ return stringToSymbolNode(stringFromQuotedSymbol(token[1]), token[2]);
7608
7749
  }
7609
- advance() {
7610
- this.parseState.position += 1;
7750
+ else {
7751
+ return stringToSymbolNode(token[1], token[2]);
7611
7752
  }
7612
- parse() {
7613
- this.tokenStream.tokens.forEach((token) => {
7614
- if (token[0] === 'Error') {
7615
- throw new LitsError(token[3], token[2]);
7616
- }
7617
- });
7618
- const nodes = [];
7619
- while (!this.isAtEnd()) {
7620
- nodes.push(this.parseExpression(0, true));
7621
- if (isOperatorToken(this.peek(), ';')) {
7622
- this.advance();
7623
- }
7624
- else {
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 nodes;
7767
+ return withSourceCodeInfo([bindingTargetTypes.symbol, [symbol, defaultValue]], firstToken[2]);
7631
7768
  }
7632
- parseExpression(precedence = 0, moduleScope = false) {
7633
- const firstToken = this.peek();
7634
- let left;
7635
- if (isSymbolToken(firstToken)) {
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
- else if (isReservedSymbolToken(firstToken, 'do')) {
7662
- left = this.parseBlock()[0];
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
- else if (isReservedSymbolToken(firstToken, 'export')) {
7665
- if (!moduleScope) {
7666
- throw new LitsError('export is only allowed in module scope', firstToken[2]);
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
- return this.parseExport(firstToken);
7669
- }
7670
- left ||= this.parseOperand();
7671
- let operator = this.peek();
7672
- while (!this.isAtExpressionEnd()) {
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
- else if (operator?.[1] === '?') {
7704
- if (conditionalOperatorPrecedence <= precedence) {
7705
- break;
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
- else {
7717
- break;
7801
+ elements.push(target);
7802
+ token = ctx.peek();
7803
+ if (!isRBracketToken(token)) {
7804
+ assertOperatorToken(token, ',');
7805
+ ctx.advance();
7718
7806
  }
7719
- operator = this.peek();
7807
+ token = ctx.peek();
7720
7808
  }
7721
- return left;
7722
- }
7723
- asToken(token) {
7724
- if (!token) {
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 token;
7814
+ return withSourceCodeInfo([bindingTargetTypes.array, [elements, defaultValue]], firstToken[2]);
7728
7815
  }
7729
- parseOperand() {
7730
- let operand = this.parseOperandPart();
7731
- let token = this.peek();
7732
- while (isOperatorToken(token, '.') || isLBracketToken(token) || isLParenToken(token)) {
7733
- if (token[1] === '.') {
7734
- this.advance();
7735
- const symbolToken = this.asToken(this.peek());
7736
- if (!isSymbolToken(symbolToken)) {
7737
- throw new LitsError('Expected symbol', this.peekSourceCodeInfo());
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
- return operand;
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
- this.parseState.position = positionBefore;
7771
- this.advance();
7772
- const expression = this.parseExpression();
7773
- if (!isRParenToken(this.peek())) {
7774
- throw new LitsError('Expected closing parenthesis', this.peekSourceCodeInfo());
7775
- }
7776
- this.advance();
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
- return withSourceCodeInfo([NodeTypes.NormalBuiltinSymbol, normalExpressionTypes[operatorName]], token[2]);
7787
- }
7788
- if (operatorName === '->') {
7789
- return this.parseShorthandLambdaFunction();
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
- this.parseState.position = positionBefore;
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
- const token = this.peek();
7838
- if (isStringToken(token)) {
7839
- const stringNode = this.parseString(token);
7840
- params.push(withSourceCodeInfo([NodeTypes.String, stringNode[1]], token[2]));
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
- else if (isLBracketToken(token)) {
7850
- this.advance();
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
- else {
7856
- throw new LitsError('Expected key to be a symbol or a string', this.peekSourceCodeInfo());
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
- assertOperatorToken(this.peek(), ':');
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 (isOperatorToken(nextToken, ',')) {
7867
- this.advance();
7866
+ if (!isRBraceToken(ctx.peek())) {
7867
+ assertOperatorToken(ctx.peek(), ',');
7868
+ ctx.advance();
7868
7869
  }
7870
+ token = ctx.peek();
7869
7871
  }
7870
- assertRBraceToken(this.peek());
7871
- this.advance();
7872
- return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.object, params]], firstToken[2]);
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
- parseArray() {
7875
- const firstToken = asLBracketToken(this.peek());
7876
- this.advance();
7877
- const params = [];
7878
- while (!this.isAtEnd() && !isRBracketToken(this.peek())) {
7879
- if (isOperatorToken(this.peek(), '...')) {
7880
- this.advance();
7881
- params.push(withSourceCodeInfo([NodeTypes.Spread, this.parseExpression()], this.peekSourceCodeInfo()));
7882
- }
7883
- else {
7884
- params.push(this.parseExpression());
7885
- }
7886
- const nextToken = this.peek();
7887
- if (!isOperatorToken(nextToken, ',') && !isRBracketToken(nextToken)) {
7888
- throw new LitsError('Expected comma or closing parenthesis', this.peekSourceCodeInfo());
7889
- }
7890
- if (isOperatorToken(nextToken, ',')) {
7891
- this.advance();
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
- parseFunctionCall(symbol) {
7899
- this.advance();
7900
- const params = [];
7901
- while (!this.isAtEnd() && !isRParenToken(this.peek())) {
7902
- if (isOperatorToken(this.peek(), '...')) {
7903
- this.advance();
7904
- params.push(withSourceCodeInfo([NodeTypes.Spread, this.parseExpression()], this.peekSourceCodeInfo()));
7905
- }
7906
- else {
7907
- params.push(this.parseExpression());
7908
- }
7909
- const nextToken = this.peek();
7910
- if (!isOperatorToken(nextToken, ',') && !isRParenToken(nextToken)) {
7911
- throw new LitsError('Expected comma or closing parenthesis', this.peek()?.[2]);
7912
- }
7913
- if (isOperatorToken(nextToken, ',')) {
7914
- this.advance();
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
- if (!isRParenToken(this.peek())) {
7918
- throw new LitsError('Expected closing parenthesis', this.peekSourceCodeInfo());
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
- this.advance();
7921
- if (isSpecialBuiltinSymbolNode(symbol)) { // Named function
7922
- const specialExpressionType = symbol[1];
7923
- const type = specialExpressionType;
7924
- const specialExpression = builtin.specialExpressions[type];
7925
- assertNumberOfParams(specialExpression.arity, params.length, symbol[2]);
7926
- switch (type) {
7927
- case specialExpressionTypes['||']:
7928
- return withSourceCodeInfo([NodeTypes.SpecialExpression, [type, params]], symbol[2]);
7929
- case specialExpressionTypes['&&']:
7930
- return withSourceCodeInfo([NodeTypes.SpecialExpression, [type, params]], symbol[2]);
7931
- case specialExpressionTypes.recur:
7932
- return withSourceCodeInfo([NodeTypes.SpecialExpression, [type, params]], symbol[2]);
7933
- case specialExpressionTypes.array:
7934
- return withSourceCodeInfo([NodeTypes.SpecialExpression, [type, params]], symbol[2]);
7935
- case specialExpressionTypes.object:
7936
- return withSourceCodeInfo([NodeTypes.SpecialExpression, [type, params]], symbol[2]);
7937
- case specialExpressionTypes['??']:
7938
- return withSourceCodeInfo([NodeTypes.SpecialExpression, [type, params]], symbol[2]);
7939
- case specialExpressionTypes['defined?']: {
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
- else if (isNormalBuiltinSymbolNode(symbol) || isUserDefinedSymbolNode(symbol)) {
7956
- return createNamedNormalExpressionNode(symbol, params, symbol[2]);
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
- return withSourceCodeInfo([NodeTypes.NormalExpression, [symbol, params]], symbol[2]);
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
- parseLambdaFunction() {
7963
- const firstToken = this.asToken(this.peek());
7964
- if (isLParenToken(firstToken)
7965
- && isSymbolToken(this.peekAhead(1))
7966
- && isOperatorToken(this.peekAhead(2), '->')) {
7967
- return null;
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
- try {
7970
- const functionArguments = this.parseFunctionArguments();
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
- catch {
7998
- return null;
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
- parseFunctionArguments() {
8002
- const firstToken = this.peek();
8003
- if (isSymbolToken(firstToken)) {
8004
- return [withSourceCodeInfo([bindingTargetTypes.symbol, [this.parseSymbol(), undefined]], firstToken[2])];
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
- assertLParenToken(firstToken);
8007
- this.advance();
8008
- let rest = false;
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 (!isRParenToken(this.peek())) {
8034
- throw new LitsError('Expected closing parenthesis', this.peekSourceCodeInfo());
8155
+ else if (tab) {
8156
+ return '\t';
8035
8157
  }
8036
- this.advance();
8037
- return functionArguments;
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
- nodes = [this.parseExpression()];
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
- const functionArguments = [];
8075
- for (let i = 1; i <= arity; i += 1) {
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
- const node = withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes['0_lambda'], [
8084
- functionArguments,
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
- throw new LitsError('Expected symbol', this.peekSourceCodeInfo());
8222
- }
8223
- parseLet(token) {
8224
- this.advance();
8225
- const target = this.parseBindingTarget({ requireDefaultValue: true, noRest: true });
8226
- const value = target[1][1];
8227
- target[1][1] = undefined;
8228
- const bindingTarget = withSourceCodeInfo([NodeTypes.Binding, [target, value]], token[2]);
8229
- return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.let, bindingTarget]], token[2]);
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
- parseBlock(allowDocString = false) {
8232
- const token = asReservedSymbolToken(this.peek(), 'do');
8233
- this.advance();
8234
- let docString = '';
8235
- if (allowDocString && isDocStringToken(this.peek())) {
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
- const expressions = [];
8239
- while (!this.isAtEnd() && !isReservedSymbolToken(this.peek(), 'end')) {
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
- parseImplicitBlock(ends) {
8256
- const nodes = [];
8257
- while (!this.isAtEnd() && !this.isImplicitBlockEnd(ends)) {
8258
- if (isOperatorToken(this.peek(), ';')) {
8259
- this.advance();
8260
- }
8261
- else {
8262
- nodes.push(this.parseExpression());
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
- this.assertImplicitBlockEnd(ends);
8266
- if (nodes.length === 0) {
8267
- throw new LitsError('Expected expression', this.peekSourceCodeInfo());
8243
+ else {
8244
+ params.push(ctx.parseExpression());
8268
8245
  }
8269
- return nodes.length === 1
8270
- ? nodes[0]
8271
- : withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.block, nodes]], this.peekSourceCodeInfo());
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
- isImplicitBlockEnd(ends) {
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
- parseLoop(firstToken) {
8287
- this.advance();
8288
- assertLParenToken(this.peek());
8289
- this.advance();
8290
- const bindingNodes = [];
8291
- let token = this.peek();
8292
- while (!this.isAtEnd() && !isRParenToken(token)) {
8293
- const target = this.parseBindingTarget({ requireDefaultValue: true, noRest: true });
8294
- const value = target[1][1];
8295
- target[1][1] = undefined;
8296
- bindingNodes.push(withSourceCodeInfo([NodeTypes.Binding, [target, value]], target[2]));
8297
- if (isOperatorToken(this.peek(), ',')) {
8298
- this.advance();
8299
- }
8300
- token = this.peek();
8301
- }
8302
- if (bindingNodes.length === 0) {
8303
- throw new LitsError('Expected binding', this.peekSourceCodeInfo());
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
- let whenNode;
8376
- let whileNode;
8377
- while (isReservedSymbolToken(token, 'when')
8378
- || isReservedSymbolToken(token, 'while')) {
8379
- this.advance();
8380
- if (token[1] === 'when') {
8381
- modifiers.push('&when');
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
- for (const symbol of symbols) {
8412
- if (symbol === 'let' && isSymbolToken(token, 'let')) {
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 false;
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
- withSourceCodeInfo([bindingTargetTypes.symbol, [name, undefined]], firstToken[2]),
8431
- value,
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
- isAtEnd() {
8493
- return this.parseState.position >= this.tokenStream.tokens.length;
8296
+ catch {
8297
+ return null;
8494
8298
  }
8495
- isAtExpressionEnd() {
8496
- if (this.isAtEnd()) {
8497
- return true;
8498
- }
8499
- const token = this.peek();
8500
- if (isOperatorToken(token)) {
8501
- return [';', ',', ':'].includes(token[1]);
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
- if (isReservedSymbolToken(token)) {
8504
- return ['else', 'when', 'while', 'case', 'catch', 'let', 'then', 'end', 'do'].includes(token[1]);
8314
+ const bindingTarget = parseBindingTarget(ctx);
8315
+ if (bindingTarget[1][1] !== undefined) {
8316
+ defaults = true;
8505
8317
  }
8506
- return false;
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
- else {
8516
- throw new LitsError('Expected let', this.peekSourceCodeInfo());
8321
+ if (defaults && !bindingTarget[1][1]) {
8322
+ throw new LitsError('Default arguments must be last', ctx.peekSourceCodeInfo());
8517
8323
  }
8518
- }
8519
- stringToSymbolNode(value, sourceCodeInfo) {
8520
- if (specialExpressionTypes[value] !== undefined && value !== 'fn' && value !== 'def' && value !== 'defn') {
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 (normalExpressionTypes[value] !== undefined) {
8524
- return withSourceCodeInfo([NodeTypes.NormalBuiltinSymbol, normalExpressionTypes[value]], sourceCodeInfo);
8328
+ if (isOperatorToken(ctx.peek(), ',')) {
8329
+ ctx.advance();
8525
8330
  }
8526
- return withSourceCodeInfo([NodeTypes.UserDefinedSymbol, value], sourceCodeInfo);
8527
8331
  }
8528
- stringFromQuotedSymbol(value) {
8529
- return value.substring(1, value.length - 1)
8530
- .replace(/(\\{2})|(\\')|\\(.)/g, (_, backslash, singleQuote, normalChar) => {
8531
- if (backslash) {
8532
- return '\\';
8533
- }
8534
- if (singleQuote) {
8535
- return '\'';
8536
- }
8537
- return `\\${normalChar}`;
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
- parseSymbol() {
8541
- const token = this.asToken(this.peek());
8542
- this.advance();
8543
- if (!isSymbolToken(token)) {
8544
- throw new LitsError(`Expected symbol token, got ${token[0]}`, token[2]);
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
- if (token[1][0] === '\'') {
8547
- return this.stringToSymbolNode(this.stringFromQuotedSymbol(token[1]), token[2]);
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
- return this.stringToSymbolNode(token[1], token[2]);
8380
+ functionArguments.push(withSourceCodeInfo([bindingTargetTypes.symbol, [[NodeTypes.UserDefinedSymbol, `$${i}`], undefined]], firstToken[2]));
8551
8381
  }
8552
8382
  }
8553
- parseReservedSymbol() {
8554
- const token = asReservedSymbolToken(this.peek());
8555
- this.advance();
8556
- const symbol = token[1];
8557
- if (isNumberReservedSymbol(symbol)) {
8558
- return withSourceCodeInfo([NodeTypes.Number, numberReservedSymbolRecord[symbol]], token[2]);
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
- parseNumber() {
8563
- const token = this.asToken(this.peek());
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
- parseDocString() {
8571
- const token = this.asToken(this.peek());
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
- parseString(token) {
8577
- this.advance();
8578
- const value = token[1].substring(1, token[1].length - 1)
8579
- .replace(/(\\{2})|(\\")|(\\n)|(\\t)|(\\r)|(\\b)|(\\f)|\\(.)/g, (_, backslash, doubleQuote, newline, tab, carriageReturn, backspace, formFeed, normalChar) => {
8580
- // If it's a double escape (\\x), return \x
8581
- if (backslash) {
8582
- return '\\';
8583
- }
8584
- // If it's a special character (\n, \t, \r, \b, \f), return the special character
8585
- else if (newline) {
8586
- return '\n';
8587
- }
8588
- else if (tab) {
8589
- return '\t';
8590
- }
8591
- else if (carriageReturn) {
8592
- return '\r';
8593
- }
8594
- else if (backspace) {
8595
- return '\b';
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 (formFeed) {
8598
- return '\f';
8486
+ else if (isLBracketToken(token)) {
8487
+ ctx.advance();
8488
+ params.push(ctx.parseExpression());
8489
+ assertRBracketToken(ctx.tryPeek());
8490
+ ctx.advance();
8599
8491
  }
8600
- else if (doubleQuote) {
8601
- return '"';
8492
+ else {
8493
+ throw new LitsError('Expected key to be a symbol or a string', ctx.peekSourceCodeInfo());
8602
8494
  }
8603
- return normalChar;
8604
- });
8605
- return withSourceCodeInfo([NodeTypes.String, value], token[2]);
8606
- }
8607
- parseRegexpShorthand() {
8608
- const token = this.asToken(this.peek());
8609
- this.advance();
8610
- const endStringPosition = token[1].lastIndexOf('"');
8611
- const regexpString = token[1].substring(2, endStringPosition);
8612
- const optionsString = token[1].substring(endStringPosition + 1);
8613
- const stringNode = withSourceCodeInfo([NodeTypes.String, regexpString], token[2]);
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
- const litsCommands = new Set([...normalExpressionKeys, ...specialExpressionKeys, ...Object.keys(reservedSymbolRecord)]);
8627
- // TODO: replace with get suggestions function
8628
- class AutoCompleter {
8629
- originalProgram;
8630
- originalPosition;
8631
- prefixProgram = '';
8632
- suffixProgram = '';
8633
- searchString = '';
8634
- suggestions = [];
8635
- suggestionIndex = null;
8636
- constructor(originalProgram, originalPosition, lits, params) {
8637
- this.originalProgram = originalProgram;
8638
- this.originalPosition = originalPosition;
8639
- const partialProgram = this.originalProgram.slice(0, this.originalPosition);
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 (lastToken[0] === 'Error') {
8646
- return;
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
- getAutoCompleteSuggestionResult(suggestion) {
8661
- if (suggestion === null) {
8662
- return null;
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
- return {
8665
- program: this.prefixProgram + suggestion + this.suffixProgram,
8666
- position: this.prefixProgram.length + suggestion.length,
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
- getNextSuggestionSymbol() {
8670
- if (this.suggestions.length === 0) {
8671
- return null;
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 (this.suggestionIndex === null) {
8674
- this.suggestionIndex = 0;
8571
+ if (operatorName === '->') {
8572
+ return parseShorthandLambdaFunction(ctx);
8675
8573
  }
8676
8574
  else {
8677
- this.suggestionIndex += 1;
8678
- if (this.suggestionIndex >= this.suggestions.length) {
8679
- this.suggestionIndex = 0;
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
- return this.suggestions[this.suggestionIndex];
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
- getPreviousSuggestionSymbol() {
8685
- if (this.suggestions.length === 0) {
8686
- return null;
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
- if (this.suggestionIndex === null) {
8689
- this.suggestionIndex = this.suggestions.length - 1;
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
- this.suggestionIndex -= 1;
8693
- if (this.suggestionIndex < 0) {
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
- getSuggestions() {
8700
- return [...this.suggestions];
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
- getSearchString() {
8703
- return this.searchString;
8711
+ else if (isReservedSymbolToken(token, 'do')) {
8712
+ left = parseDo(ctx)[0];
8704
8713
  }
8705
- generateSuggestions(params) {
8706
- const blacklist = new Set(['0_def', '0_defn', '0_lambda']);
8707
- const startsWithCaseSensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.startsWith(this.searchString));
8708
- startsWithCaseSensitive.forEach(suggestion => blacklist.add(suggestion));
8709
- const startsWithCaseInsensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.toLowerCase().startsWith(this.searchString.toLowerCase()));
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
- generateWithPredicate(params, shouldInclude) {
8718
- const suggestions = new Set();
8719
- litsCommands.forEach((suggestion) => {
8720
- if (shouldInclude(suggestion)) {
8721
- suggestions.add(suggestion);
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
- Object.keys(params.globalContext ?? {})
8725
- .filter(shouldInclude)
8726
- .forEach(suggestion => suggestions.add(suggestion));
8727
- params.contexts?.forEach((context) => {
8728
- Object.keys(context)
8729
- .filter(shouldInclude)
8730
- .forEach(suggestion => suggestions.add(suggestion));
8731
- });
8732
- Object.keys(params.jsFunctions ?? {})
8733
- .filter(shouldInclude)
8734
- .forEach(suggestion => suggestions.add(suggestion));
8735
- Object.keys(params.values ?? {})
8736
- .filter(shouldInclude)
8737
- .forEach(suggestion => suggestions.add(suggestion));
8738
- return [...suggestions].sort((a, b) => a.localeCompare(b));
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
- const parseState = {
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) {