@mojir/dvala 0.0.7 → 0.0.10

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 (321) hide show
  1. package/dist/cli/cli.js +1622 -1620
  2. package/dist/cli/reference/examples.d.ts +1 -1
  3. package/dist/cli/src/AutoCompleter/AutoCompleter.d.ts +4 -2
  4. package/dist/cli/src/{Dvala/Cache.d.ts → Cache.d.ts} +1 -1
  5. package/dist/cli/src/builtin/specialExpressions/functions.d.ts +1 -1
  6. package/dist/cli/src/createDvala.d.ts +47 -0
  7. package/dist/cli/src/evaluator/ContextStack.d.ts +10 -2
  8. package/dist/cli/src/evaluator/effectTypes.d.ts +24 -6
  9. package/dist/cli/src/evaluator/suspension.d.ts +6 -0
  10. package/dist/cli/src/evaluator/trampoline.d.ts +15 -1
  11. package/dist/cli/src/parser/subParsers/parseDo.d.ts +1 -1
  12. package/dist/cli/src/tokenizer/token.d.ts +2 -4
  13. package/dist/cli/src/tokenizer/tokenize.d.ts +1 -2
  14. package/dist/cli/src/tokenizer/tokenizers.d.ts +2 -3
  15. package/dist/cli/src/tooling.d.ts +51 -0
  16. package/dist/debug.esm.js +1 -1
  17. package/dist/debug.esm.js.map +1 -1
  18. package/dist/debug.js +1 -1
  19. package/dist/debug.js.map +1 -1
  20. package/dist/dvala.iife.js +1 -1
  21. package/dist/dvala.iife.js.map +1 -1
  22. package/dist/full.esm.js +1 -1
  23. package/dist/full.esm.js.map +1 -1
  24. package/dist/full.js +1 -1
  25. package/dist/full.js.map +1 -1
  26. package/dist/index.esm.js +1 -1
  27. package/dist/index.esm.js.map +1 -1
  28. package/dist/index.js +1 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/mcp-server/common/utils.d.ts +2 -0
  31. package/dist/mcp-server/mcp-server/src/server.d.ts +1 -0
  32. package/dist/mcp-server/reference/api.d.ts +73 -0
  33. package/dist/mcp-server/reference/datatype.d.ts +3 -0
  34. package/dist/mcp-server/reference/examples.d.ts +11 -0
  35. package/dist/mcp-server/reference/index.d.ts +195 -0
  36. package/dist/mcp-server/reference/shorthand.d.ts +3 -0
  37. package/dist/mcp-server/server.js +36377 -0
  38. package/dist/mcp-server/src/AutoCompleter/AutoCompleter.d.ts +27 -0
  39. package/dist/{src/Dvala → mcp-server/src}/Cache.d.ts +1 -1
  40. package/dist/mcp-server/src/allModules.d.ts +2 -0
  41. package/dist/mcp-server/src/builtin/bindingNode.d.ts +11 -0
  42. package/dist/mcp-server/src/builtin/core/array.d.ts +2 -0
  43. package/dist/mcp-server/src/builtin/core/assertion.d.ts +2 -0
  44. package/dist/mcp-server/src/builtin/core/bitwise.d.ts +2 -0
  45. package/dist/mcp-server/src/builtin/core/collection.d.ts +2 -0
  46. package/dist/mcp-server/src/builtin/core/functional.d.ts +2 -0
  47. package/dist/mcp-server/src/builtin/core/math.d.ts +2 -0
  48. package/dist/mcp-server/src/builtin/core/meta.d.ts +3 -0
  49. package/dist/mcp-server/src/builtin/core/misc.d.ts +2 -0
  50. package/dist/mcp-server/src/builtin/core/object.d.ts +2 -0
  51. package/dist/mcp-server/src/builtin/core/predicates.d.ts +2 -0
  52. package/dist/mcp-server/src/builtin/core/regexp.d.ts +2 -0
  53. package/dist/mcp-server/src/builtin/core/sequence.d.ts +2 -0
  54. package/dist/mcp-server/src/builtin/core/string.d.ts +2 -0
  55. package/dist/mcp-server/src/builtin/core/vector.d.ts +2 -0
  56. package/dist/mcp-server/src/builtin/index.d.ts +13 -0
  57. package/dist/mcp-server/src/builtin/interface.d.ts +113 -0
  58. package/dist/mcp-server/src/builtin/modules/assertion/docs.d.ts +2 -0
  59. package/dist/mcp-server/src/builtin/modules/assertion/index.d.ts +2 -0
  60. package/dist/mcp-server/src/builtin/modules/bitwise/index.d.ts +2 -0
  61. package/dist/mcp-server/src/builtin/modules/collection/index.d.ts +2 -0
  62. package/dist/mcp-server/src/builtin/modules/convert/index.d.ts +2 -0
  63. package/dist/mcp-server/src/builtin/modules/functional/index.d.ts +2 -0
  64. package/dist/mcp-server/src/builtin/modules/grid/docs.d.ts +2 -0
  65. package/dist/mcp-server/src/builtin/modules/grid/fromArray.d.ts +8 -0
  66. package/dist/mcp-server/src/builtin/modules/grid/index.d.ts +2 -0
  67. package/dist/mcp-server/src/builtin/modules/grid/transpose.d.ts +2 -0
  68. package/dist/mcp-server/src/builtin/modules/interface.d.ts +28 -0
  69. package/dist/mcp-server/src/builtin/modules/linear-algebra/docs.d.ts +2 -0
  70. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/calcFractionalRanks.d.ts +1 -0
  71. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/collinear.d.ts +2 -0
  72. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/corrleation.d.ts +8 -0
  73. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/covariance.d.ts +4 -0
  74. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/dot.d.ts +1 -0
  75. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/gaussJordanElimination.d.ts +7 -0
  76. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/getUnit.d.ts +2 -0
  77. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/isZeroVector.d.ts +1 -0
  78. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/kendallTau.d.ts +10 -0
  79. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/length.d.ts +1 -0
  80. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/pearsonCorr.d.ts +1 -0
  81. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/scale.d.ts +1 -0
  82. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/solve.d.ts +8 -0
  83. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/subtract.d.ts +1 -0
  84. package/dist/mcp-server/src/builtin/modules/linear-algebra/index.d.ts +4 -0
  85. package/dist/mcp-server/src/builtin/modules/math/index.d.ts +2 -0
  86. package/dist/mcp-server/src/builtin/modules/matrix/docs.d.ts +2 -0
  87. package/dist/mcp-server/src/builtin/modules/matrix/helpers/adjugate.d.ts +1 -0
  88. package/dist/mcp-server/src/builtin/modules/matrix/helpers/band.d.ts +9 -0
  89. package/dist/mcp-server/src/builtin/modules/matrix/helpers/cofactor.d.ts +1 -0
  90. package/dist/mcp-server/src/builtin/modules/matrix/helpers/determinant.d.ts +6 -0
  91. package/dist/mcp-server/src/builtin/modules/matrix/helpers/inverse.d.ts +6 -0
  92. package/dist/mcp-server/src/builtin/modules/matrix/helpers/isBanded.d.ts +11 -0
  93. package/dist/mcp-server/src/builtin/modules/matrix/helpers/isDiagonal.d.ts +10 -0
  94. package/dist/mcp-server/src/builtin/modules/matrix/helpers/isIdentity.d.ts +1 -0
  95. package/dist/mcp-server/src/builtin/modules/matrix/helpers/isOrthogonal.d.ts +1 -0
  96. package/dist/mcp-server/src/builtin/modules/matrix/helpers/isSquare.d.ts +1 -0
  97. package/dist/mcp-server/src/builtin/modules/matrix/helpers/isSymetric.d.ts +8 -0
  98. package/dist/mcp-server/src/builtin/modules/matrix/helpers/isTriangular.d.ts +13 -0
  99. package/dist/mcp-server/src/builtin/modules/matrix/helpers/matrixMultiply.d.ts +7 -0
  100. package/dist/mcp-server/src/builtin/modules/matrix/helpers/minor.d.ts +1 -0
  101. package/dist/mcp-server/src/builtin/modules/matrix/helpers/norm1.d.ts +1 -0
  102. package/dist/mcp-server/src/builtin/modules/matrix/helpers/trace.d.ts +8 -0
  103. package/dist/mcp-server/src/builtin/modules/matrix/index.d.ts +4 -0
  104. package/dist/mcp-server/src/builtin/modules/number-theory/binomialCefficient.d.ts +1 -0
  105. package/dist/mcp-server/src/builtin/modules/number-theory/combinations.d.ts +2 -0
  106. package/dist/mcp-server/src/builtin/modules/number-theory/derangements.d.ts +2 -0
  107. package/dist/mcp-server/src/builtin/modules/number-theory/divisors.d.ts +4 -0
  108. package/dist/mcp-server/src/builtin/modules/number-theory/docs.d.ts +2 -0
  109. package/dist/mcp-server/src/builtin/modules/number-theory/factorial.d.ts +3 -0
  110. package/dist/mcp-server/src/builtin/modules/number-theory/index.d.ts +4 -0
  111. package/dist/mcp-server/src/builtin/modules/number-theory/partitions.d.ts +2 -0
  112. package/dist/mcp-server/src/builtin/modules/number-theory/permutations.d.ts +2 -0
  113. package/dist/mcp-server/src/builtin/modules/number-theory/powerSet.d.ts +2 -0
  114. package/dist/mcp-server/src/builtin/modules/number-theory/primeFactors.d.ts +11 -0
  115. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/abundant.d.ts +2 -0
  116. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/arithmetic.d.ts +2 -0
  117. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/bell.d.ts +1 -0
  118. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/bernoulli.d.ts +2 -0
  119. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/catalan.d.ts +1 -0
  120. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/collatz.d.ts +2 -0
  121. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/composite.d.ts +3 -0
  122. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/deficient.d.ts +2 -0
  123. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/factorial.d.ts +1 -0
  124. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/fibonacci.d.ts +1 -0
  125. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/geometric.d.ts +2 -0
  126. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/golomb.d.ts +2 -0
  127. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/happy.d.ts +2 -0
  128. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/index.d.ts +27 -0
  129. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/juggler.d.ts +2 -0
  130. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/lookAndSay.d.ts +2 -0
  131. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/lucas.d.ts +1 -0
  132. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/lucky.d.ts +2 -0
  133. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/mersenne.d.ts +1 -0
  134. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/padovan.d.ts +2 -0
  135. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/partition.d.ts +1 -0
  136. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/pell.d.ts +1 -0
  137. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/perfect.d.ts +1 -0
  138. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/perfectCube.d.ts +2 -0
  139. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/perfectPower.d.ts +10 -0
  140. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/perfectSquare.d.ts +2 -0
  141. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/poligonal.d.ts +2 -0
  142. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/prime.d.ts +3 -0
  143. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/recaman.d.ts +9 -0
  144. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/sylvester.d.ts +1 -0
  145. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/thueMorse.d.ts +2 -0
  146. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/tribonacci.d.ts +1 -0
  147. package/dist/mcp-server/src/builtin/modules/sequence/index.d.ts +2 -0
  148. package/dist/mcp-server/src/builtin/modules/string/index.d.ts +2 -0
  149. package/dist/mcp-server/src/builtin/modules/vector/bincount.d.ts +9 -0
  150. package/dist/mcp-server/src/builtin/modules/vector/calcMad.d.ts +1 -0
  151. package/dist/mcp-server/src/builtin/modules/vector/calcMean.d.ts +1 -0
  152. package/dist/mcp-server/src/builtin/modules/vector/calcMedad.d.ts +1 -0
  153. package/dist/mcp-server/src/builtin/modules/vector/calcMedian.d.ts +1 -0
  154. package/dist/mcp-server/src/builtin/modules/vector/calcStdDev.d.ts +2 -0
  155. package/dist/mcp-server/src/builtin/modules/vector/calcVariance.d.ts +2 -0
  156. package/dist/mcp-server/src/builtin/modules/vector/docs.d.ts +2 -0
  157. package/dist/mcp-server/src/builtin/modules/vector/entropy.d.ts +8 -0
  158. package/dist/mcp-server/src/builtin/modules/vector/histogram.d.ts +9 -0
  159. package/dist/mcp-server/src/builtin/modules/vector/index.d.ts +2 -0
  160. package/dist/mcp-server/src/builtin/modules/vector/mode.d.ts +6 -0
  161. package/dist/mcp-server/src/builtin/modules/vector/outliers.d.ts +7 -0
  162. package/dist/mcp-server/src/builtin/modules/vector/percentile.d.ts +7 -0
  163. package/dist/mcp-server/src/builtin/modules/vector/quartiles.d.ts +1 -0
  164. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/entropy.d.ts +2 -0
  165. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/giniCoefficient.d.ts +2 -0
  166. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/index.d.ts +13 -0
  167. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/iqr.d.ts +2 -0
  168. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/kurtosis.d.ts +5 -0
  169. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/mad.d.ts +2 -0
  170. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/mean.d.ts +4 -0
  171. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/medad.d.ts +2 -0
  172. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/median.d.ts +2 -0
  173. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/prod.d.ts +2 -0
  174. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/rms.d.ts +2 -0
  175. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/skewness.d.ts +3 -0
  176. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/span.d.ts +2 -0
  177. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/standardDeviation.d.ts +3 -0
  178. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/sum.d.ts +2 -0
  179. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/variance.d.ts +3 -0
  180. package/dist/mcp-server/src/builtin/normalExpressions/index.d.ts +9 -0
  181. package/dist/mcp-server/src/builtin/normalExpressions/initCoreDvala.d.ts +1 -0
  182. package/dist/mcp-server/src/builtin/specialExpressionTypes.d.ts +24 -0
  183. package/dist/mcp-server/src/builtin/specialExpressions/and.d.ts +6 -0
  184. package/dist/mcp-server/src/builtin/specialExpressions/array.d.ts +6 -0
  185. package/dist/mcp-server/src/builtin/specialExpressions/block.d.ts +7 -0
  186. package/dist/mcp-server/src/builtin/specialExpressions/cond.d.ts +6 -0
  187. package/dist/mcp-server/src/builtin/specialExpressions/defined.d.ts +5 -0
  188. package/dist/mcp-server/src/builtin/specialExpressions/effect.d.ts +5 -0
  189. package/dist/mcp-server/src/builtin/specialExpressions/functions.d.ts +6 -0
  190. package/dist/mcp-server/src/builtin/specialExpressions/if.d.ts +6 -0
  191. package/dist/mcp-server/src/builtin/specialExpressions/import.d.ts +6 -0
  192. package/dist/mcp-server/src/builtin/specialExpressions/let.d.ts +6 -0
  193. package/dist/mcp-server/src/builtin/specialExpressions/loop.d.ts +6 -0
  194. package/dist/mcp-server/src/builtin/specialExpressions/loops.d.ts +9 -0
  195. package/dist/mcp-server/src/builtin/specialExpressions/match.d.ts +7 -0
  196. package/dist/mcp-server/src/builtin/specialExpressions/object.d.ts +6 -0
  197. package/dist/mcp-server/src/builtin/specialExpressions/or.d.ts +6 -0
  198. package/dist/mcp-server/src/builtin/specialExpressions/parallel.d.ts +6 -0
  199. package/dist/mcp-server/src/builtin/specialExpressions/perform.d.ts +6 -0
  200. package/dist/mcp-server/src/builtin/specialExpressions/qq.d.ts +6 -0
  201. package/dist/mcp-server/src/builtin/specialExpressions/race.d.ts +6 -0
  202. package/dist/mcp-server/src/builtin/specialExpressions/recur.d.ts +5 -0
  203. package/dist/mcp-server/src/builtin/specialExpressions/unless.d.ts +6 -0
  204. package/dist/mcp-server/src/builtin/utils.d.ts +6 -0
  205. package/dist/mcp-server/src/bundler/interface.d.ts +15 -0
  206. package/dist/mcp-server/src/constants/constants.d.ts +19 -0
  207. package/dist/mcp-server/src/createDvala.d.ts +47 -0
  208. package/dist/mcp-server/src/errors.d.ts +24 -0
  209. package/dist/mcp-server/src/evaluator/ContextStack.d.ts +70 -0
  210. package/dist/mcp-server/src/evaluator/contentHash.d.ts +21 -0
  211. package/dist/mcp-server/src/evaluator/dedupSubTrees.d.ts +37 -0
  212. package/dist/mcp-server/src/evaluator/effectRef.d.ts +27 -0
  213. package/dist/mcp-server/src/evaluator/effectTypes.d.ts +199 -0
  214. package/dist/mcp-server/src/evaluator/frames.d.ts +513 -0
  215. package/dist/mcp-server/src/evaluator/interface.d.ts +14 -0
  216. package/dist/mcp-server/src/evaluator/standardEffects.d.ts +50 -0
  217. package/dist/mcp-server/src/evaluator/step.d.ts +175 -0
  218. package/dist/mcp-server/src/evaluator/suspension.d.ts +92 -0
  219. package/dist/mcp-server/src/evaluator/trampoline.d.ts +138 -0
  220. package/dist/mcp-server/src/getUndefinedSymbols/index.d.ts +7 -0
  221. package/dist/mcp-server/src/initReferenceData.d.ts +1 -0
  222. package/dist/mcp-server/src/interface.d.ts +7 -0
  223. package/dist/mcp-server/src/parser/ParserContext.d.ts +20 -0
  224. package/dist/mcp-server/src/parser/getPrecedence.d.ts +3 -0
  225. package/dist/mcp-server/src/parser/helpers.d.ts +19 -0
  226. package/dist/mcp-server/src/parser/index.d.ts +5 -0
  227. package/dist/mcp-server/src/parser/subParsers/parseArray.d.ts +3 -0
  228. package/dist/mcp-server/src/parser/subParsers/parseBindingTarget.d.ts +8 -0
  229. package/dist/mcp-server/src/parser/subParsers/parseCond.d.ts +4 -0
  230. package/dist/mcp-server/src/parser/subParsers/parseDo.d.ts +3 -0
  231. package/dist/mcp-server/src/parser/subParsers/parseExpression.d.ts +5 -0
  232. package/dist/mcp-server/src/parser/subParsers/parseForOrDoseq.d.ts +4 -0
  233. package/dist/mcp-server/src/parser/subParsers/parseFunction.d.ts +4 -0
  234. package/dist/mcp-server/src/parser/subParsers/parseFunctionCall.d.ts +3 -0
  235. package/dist/mcp-server/src/parser/subParsers/parseIfOrUnless.d.ts +5 -0
  236. package/dist/mcp-server/src/parser/subParsers/parseImplicitBlock.d.ts +5 -0
  237. package/dist/mcp-server/src/parser/subParsers/parseLet.d.ts +4 -0
  238. package/dist/mcp-server/src/parser/subParsers/parseLoop.d.ts +4 -0
  239. package/dist/mcp-server/src/parser/subParsers/parseMatch.d.ts +4 -0
  240. package/dist/mcp-server/src/parser/subParsers/parseNumber.d.ts +3 -0
  241. package/dist/mcp-server/src/parser/subParsers/parseObject.d.ts +3 -0
  242. package/dist/mcp-server/src/parser/subParsers/parseOperand.d.ts +3 -0
  243. package/dist/mcp-server/src/parser/subParsers/parseRegexpShorthand.d.ts +3 -0
  244. package/dist/mcp-server/src/parser/subParsers/parseReservedSymbol.d.ts +3 -0
  245. package/dist/mcp-server/src/parser/subParsers/parseString.d.ts +4 -0
  246. package/dist/mcp-server/src/parser/subParsers/parseSymbol.d.ts +3 -0
  247. package/dist/mcp-server/src/parser/types.d.ts +128 -0
  248. package/dist/mcp-server/src/tokenizer/minifyTokenStream.d.ts +4 -0
  249. package/dist/mcp-server/src/tokenizer/operators.d.ts +12 -0
  250. package/dist/mcp-server/src/tokenizer/reservedNames.d.ts +65 -0
  251. package/dist/mcp-server/src/tokenizer/token.d.ts +82 -0
  252. package/dist/mcp-server/src/tokenizer/tokenize.d.ts +7 -0
  253. package/dist/mcp-server/src/tokenizer/tokenizers.d.ts +13 -0
  254. package/dist/mcp-server/src/tooling.d.ts +51 -0
  255. package/dist/mcp-server/src/transformer/index.d.ts +2 -0
  256. package/dist/mcp-server/src/typeGuards/annotatedCollections.d.ts +16 -0
  257. package/dist/mcp-server/src/typeGuards/array.d.ts +9 -0
  258. package/dist/mcp-server/src/typeGuards/astNode.d.ts +19 -0
  259. package/dist/mcp-server/src/typeGuards/dvala.d.ts +26 -0
  260. package/dist/mcp-server/src/typeGuards/dvalaFunction.d.ts +9 -0
  261. package/dist/mcp-server/src/typeGuards/index.d.ts +7 -0
  262. package/dist/mcp-server/src/typeGuards/number.d.ts +66 -0
  263. package/dist/mcp-server/src/typeGuards/string.d.ts +15 -0
  264. package/dist/mcp-server/src/untokenizer/index.d.ts +2 -0
  265. package/dist/mcp-server/src/utils/arity.d.ts +10 -0
  266. package/dist/mcp-server/src/utils/debug/debugTools.d.ts +1 -0
  267. package/dist/mcp-server/src/utils/debug/getCodeMarker.d.ts +2 -0
  268. package/dist/mcp-server/src/utils/debug/getSourceCodeInfo.d.ts +2 -0
  269. package/dist/mcp-server/src/utils/docString/generateDocString.d.ts +4 -0
  270. package/dist/mcp-server/src/utils/getAssertionError.d.ts +3 -0
  271. package/dist/mcp-server/src/utils/index.d.ts +14 -0
  272. package/dist/mcp-server/src/utils/maybePromise.d.ts +54 -0
  273. package/dist/mcp-server/src/utils/symbols.d.ts +3 -0
  274. package/dist/modules/grid.esm.js +1 -1
  275. package/dist/modules/grid.esm.js.map +1 -1
  276. package/dist/modules/grid.js +1 -1
  277. package/dist/modules/grid.js.map +1 -1
  278. package/dist/modules/reference/index.d.ts +136 -136
  279. package/dist/modules/src/AutoCompleter/AutoCompleter.d.ts +4 -2
  280. package/dist/modules/src/{Dvala/Cache.d.ts → Cache.d.ts} +1 -1
  281. package/dist/modules/src/builtin/specialExpressions/functions.d.ts +1 -1
  282. package/dist/modules/src/createDvala.d.ts +47 -0
  283. package/dist/modules/src/evaluator/ContextStack.d.ts +10 -2
  284. package/dist/modules/src/evaluator/effectTypes.d.ts +24 -6
  285. package/dist/modules/src/evaluator/suspension.d.ts +6 -0
  286. package/dist/modules/src/evaluator/trampoline.d.ts +15 -1
  287. package/dist/modules/src/index.d.ts +10 -5
  288. package/dist/modules/src/parser/subParsers/parseDo.d.ts +1 -1
  289. package/dist/modules/src/resume.d.ts +41 -0
  290. package/dist/modules/src/retrigger.d.ts +39 -0
  291. package/dist/modules/src/tokenizer/token.d.ts +2 -4
  292. package/dist/modules/src/tokenizer/tokenize.d.ts +1 -2
  293. package/dist/modules/src/tokenizer/tokenizers.d.ts +2 -3
  294. package/dist/modules/src/tooling.d.ts +51 -0
  295. package/dist/reference/index.d.ts +136 -136
  296. package/dist/src/AutoCompleter/AutoCompleter.d.ts +4 -2
  297. package/dist/src/Cache.d.ts +16 -0
  298. package/dist/src/builtin/specialExpressions/functions.d.ts +1 -1
  299. package/dist/src/createDvala.d.ts +47 -0
  300. package/dist/src/evaluator/ContextStack.d.ts +10 -2
  301. package/dist/src/evaluator/effectTypes.d.ts +24 -6
  302. package/dist/src/evaluator/suspension.d.ts +6 -0
  303. package/dist/src/evaluator/trampoline.d.ts +15 -1
  304. package/dist/src/index.d.ts +10 -5
  305. package/dist/src/parser/subParsers/parseDo.d.ts +1 -1
  306. package/dist/src/resume.d.ts +41 -0
  307. package/dist/src/retrigger.d.ts +39 -0
  308. package/dist/src/tokenizer/token.d.ts +2 -4
  309. package/dist/src/tokenizer/tokenize.d.ts +1 -2
  310. package/dist/src/tokenizer/tokenizers.d.ts +2 -3
  311. package/dist/src/tooling.d.ts +51 -0
  312. package/dist/testFramework.esm.js +1 -1
  313. package/dist/testFramework.esm.js.map +1 -1
  314. package/dist/testFramework.js +1 -1
  315. package/dist/testFramework.js.map +1 -1
  316. package/package.json +17 -6
  317. package/dist/cli/src/Dvala/Dvala.d.ts +0 -63
  318. package/dist/modules/src/Dvala/Dvala.d.ts +0 -63
  319. package/dist/modules/src/effects.d.ts +0 -110
  320. package/dist/src/Dvala/Dvala.d.ts +0 -63
  321. package/dist/src/effects.d.ts +0 -110
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 = "0.0.7";
10
+ var version = "0.0.10";
11
11
 
12
12
  function getCodeMarker(sourceCodeInfo) {
13
13
  if (!sourceCodeInfo.position || !sourceCodeInfo.code)
@@ -82,31 +82,6 @@ class UndefinedSymbolError extends DvalaError {
82
82
  }
83
83
  }
84
84
 
85
- const specialExpressionTypes = {
86
- '??': 0,
87
- '&&': 1,
88
- '||': 2,
89
- 'array': 3,
90
- 'cond': 4,
91
- 'defined?': 5,
92
- 'block': 6,
93
- 'doseq': 7,
94
- '0_lambda': 8,
95
- 'for': 9,
96
- 'if': 10,
97
- 'let': 11,
98
- 'loop': 12,
99
- 'object': 13,
100
- 'recur': 14,
101
- 'match': 15,
102
- 'unless': 16,
103
- 'import': 17,
104
- 'effect': 18,
105
- 'perform': 19,
106
- 'parallel': 20,
107
- 'race': 21,
108
- };
109
-
110
85
  const NodeTypes = {
111
86
  Number: 1,
112
87
  String: 2,
@@ -185,109 +160,6 @@ function getAssertionError(typeName, value, sourceCodeInfo) {
185
160
  return new DvalaError(`Expected ${typeName}, got ${valueToString(value)}.`, getSourceCodeInfo(value, sourceCodeInfo));
186
161
  }
187
162
 
188
- function isSymbolNode(node) {
189
- const nodeType = node[0];
190
- return NodeTypes.UserDefinedSymbol === nodeType
191
- || NodeTypes.NormalBuiltinSymbol === nodeType
192
- || NodeTypes.SpecialBuiltinSymbol === nodeType;
193
- }
194
- function isUserDefinedSymbolNode(node) {
195
- return NodeTypes.UserDefinedSymbol === node[0];
196
- }
197
- function asUserDefinedSymbolNode(node, sourceCodeInfo) {
198
- assertUserDefinedSymbolNode(node, sourceCodeInfo);
199
- return node;
200
- }
201
- function assertUserDefinedSymbolNode(node, sourceCodeInfo) {
202
- if (!isUserDefinedSymbolNode(node))
203
- throw getAssertionError('UserDefinedSymbolNode', node, sourceCodeInfo);
204
- }
205
- function isNormalBuiltinSymbolNode(node) {
206
- return NodeTypes.NormalBuiltinSymbol === node[0];
207
- }
208
- function isSpecialBuiltinSymbolNode(node) {
209
- return NodeTypes.SpecialBuiltinSymbol === node[0];
210
- }
211
- function isNormalExpressionNode(node) {
212
- return node[0] === NodeTypes.NormalExpression;
213
- }
214
- function isNormalExpressionNodeWithName(node) {
215
- if (!isNormalExpressionNode(node)) {
216
- return false;
217
- }
218
- return isSymbolNode(node[1][0]);
219
- }
220
- function isSpreadNode(node) {
221
- return node[0] === NodeTypes.Spread;
222
- }
223
-
224
- const getUndefinedSymbols = (ast, contextStack, builtin, evaluateNode) => {
225
- const nodes = Array.isArray(ast)
226
- ? ast
227
- : [[NodeTypes.SpecialExpression, [specialExpressionTypes.block, ast.body, undefined]]];
228
- const unresolvedSymbols = new Set();
229
- for (const subNode of nodes) {
230
- findUnresolvedSymbolsInNode(subNode, contextStack, builtin, evaluateNode)
231
- ?.forEach(symbol => unresolvedSymbols.add(symbol));
232
- }
233
- return unresolvedSymbols;
234
- };
235
- function findUnresolvedSymbolsInNode(node, contextStack, builtin, evaluateNode) {
236
- const nodeType = node[0];
237
- switch (nodeType) {
238
- case NodeTypes.UserDefinedSymbol: {
239
- const symbolNode = node;
240
- const lookUpResult = contextStack.lookUp(symbolNode);
241
- if (lookUpResult === null)
242
- return new Set([symbolNode[1]]);
243
- return null;
244
- }
245
- case NodeTypes.NormalBuiltinSymbol:
246
- case NodeTypes.SpecialBuiltinSymbol:
247
- case NodeTypes.String:
248
- case NodeTypes.Number:
249
- case NodeTypes.ReservedSymbol:
250
- case NodeTypes.Binding:
251
- return null;
252
- case NodeTypes.NormalExpression: {
253
- const normalExpressionNode = node;
254
- const unresolvedSymbols = new Set();
255
- if (isNormalExpressionNodeWithName(normalExpressionNode)) {
256
- const [, [symbolNode]] = normalExpressionNode;
257
- if (isUserDefinedSymbolNode(symbolNode)) {
258
- const lookUpResult = contextStack.lookUp(symbolNode);
259
- if (lookUpResult === null)
260
- unresolvedSymbols.add(symbolNode[1]);
261
- }
262
- }
263
- else {
264
- const [, [expressionNode]] = normalExpressionNode;
265
- findUnresolvedSymbolsInNode(expressionNode, contextStack, builtin, evaluateNode)?.forEach(symbol => unresolvedSymbols.add(symbol));
266
- }
267
- for (const subNode of normalExpressionNode[1][1]) {
268
- findUnresolvedSymbolsInNode(subNode, contextStack, builtin, evaluateNode)?.forEach(symbol => unresolvedSymbols.add(symbol));
269
- }
270
- return unresolvedSymbols;
271
- }
272
- case NodeTypes.SpecialExpression: {
273
- const specialExpressionNode = node;
274
- const specialExpressionType = specialExpressionNode[1][0];
275
- const specialExpression = builtin.specialExpressions[specialExpressionType];
276
- const castedGetUndefinedSymbols = specialExpression.getUndefinedSymbols;
277
- return castedGetUndefinedSymbols(specialExpressionNode, contextStack, {
278
- getUndefinedSymbols,
279
- builtin,
280
- evaluateNode,
281
- });
282
- }
283
- case NodeTypes.Spread:
284
- return findUnresolvedSymbolsInNode(node[1], contextStack, builtin, evaluateNode);
285
- /* v8 ignore next 2 */
286
- default:
287
- throw new DvalaError(`Unhandled node type: ${nodeType}`, node[2]);
288
- }
289
- }
290
-
291
163
  function isNonUndefined(value) {
292
164
  return value !== undefined;
293
165
  }
@@ -4875,7 +4747,7 @@ function argStrings(reference) {
4875
4747
 
4876
4748
  function getMetaNormalExpression(normalExpressionReference, effectReference) {
4877
4749
  return {
4878
- doc: {
4750
+ 'doc': {
4879
4751
  evaluate: ([value], sourceCodeInfo) => {
4880
4752
  assertNonUndefined(normalExpressionReference);
4881
4753
  // Handle effects
@@ -4905,28 +4777,45 @@ function getMetaNormalExpression(normalExpressionReference, effectReference) {
4905
4777
  args: { value: { type: ['function', 'effect'] } },
4906
4778
  variants: [{ argumentNames: ['value'] }],
4907
4779
  description: 'Returns documentation string of the $value. Works on functions and effects.',
4908
- seeAlso: ['arity'],
4780
+ seeAlso: ['arity', 'with-doc'],
4909
4781
  examples: [
4910
4782
  'doc(+)',
4911
4783
  'doc(effect(dvala.io.println))',
4912
- `
4913
- let add = (x, y) -> do
4914
- """
4915
- Adds two numbers.
4916
- Args:
4917
- x: First number.
4918
- y: Second number.
4919
- Returns:
4920
- Sum of x and y.
4921
- """;
4922
- x + y;
4923
- end;
4924
-
4925
- doc(add)`,
4784
+ 'let add = (x, y) -> x + y with-doc "Adds two numbers.";\ndoc(add)',
4785
+ ],
4786
+ },
4787
+ },
4788
+ 'with-doc': {
4789
+ evaluate: ([fn, docString], sourceCodeInfo) => {
4790
+ assertFunctionLike(fn, sourceCodeInfo);
4791
+ assertString(docString, sourceCodeInfo);
4792
+ if (!isDvalaFunction(fn) || fn.functionType !== 'UserDefined') {
4793
+ throw new Error('with-doc can only be used with user-defined functions');
4794
+ }
4795
+ return {
4796
+ ...fn,
4797
+ [FUNCTION_SYMBOL]: true,
4798
+ docString,
4799
+ };
4800
+ },
4801
+ arity: toFixedArity(2),
4802
+ docs: {
4803
+ category: 'meta',
4804
+ returns: { type: 'function' },
4805
+ args: {
4806
+ a: { type: 'function' },
4807
+ b: { type: 'string' },
4808
+ },
4809
+ variants: [{ argumentNames: ['a', 'b'] }],
4810
+ description: 'Returns a new function with the documentation string $b attached. The original function is not modified.',
4811
+ seeAlso: ['doc'],
4812
+ examples: [
4813
+ '((x, y) -> x + y) with-doc "Adds two numbers."',
4814
+ 'let add = (x, y) -> x + y;\nadd with-doc "Adds x and y."',
4926
4815
  ],
4927
4816
  },
4928
4817
  },
4929
- arity: {
4818
+ 'arity': {
4930
4819
  evaluate: ([value], sourceCodeInfo) => {
4931
4820
  // Handle effects
4932
4821
  if (isEffect(value)) {
@@ -6293,6 +6182,31 @@ const raceSpecialExpression = {
6293
6182
  },
6294
6183
  };
6295
6184
 
6185
+ const specialExpressionTypes = {
6186
+ '??': 0,
6187
+ '&&': 1,
6188
+ '||': 2,
6189
+ 'array': 3,
6190
+ 'cond': 4,
6191
+ 'defined?': 5,
6192
+ 'block': 6,
6193
+ 'doseq': 7,
6194
+ '0_lambda': 8,
6195
+ 'for': 9,
6196
+ 'if': 10,
6197
+ 'let': 11,
6198
+ 'loop': 12,
6199
+ 'object': 13,
6200
+ 'recur': 14,
6201
+ 'match': 15,
6202
+ 'unless': 16,
6203
+ 'import': 17,
6204
+ 'effect': 18,
6205
+ 'perform': 19,
6206
+ 'parallel': 20,
6207
+ 'race': 21,
6208
+ };
6209
+
6296
6210
  const specialExpressions = [
6297
6211
  qqSpecialExpression,
6298
6212
  andSpecialExpression,
@@ -6394,119 +6308,1000 @@ function isSymbolicOperator(operator) {
6394
6308
  return symbolicOperatorSet.has(operator);
6395
6309
  }
6396
6310
 
6397
- function isSymbolToken(token, symbolName) {
6398
- if (token?.[0] !== 'Symbol') {
6399
- return false;
6311
+ const nonNumberReservedSymbolRecord = {
6312
+ true: true,
6313
+ false: false,
6314
+ null: null,
6315
+ do: null,
6316
+ else: null,
6317
+ case: null,
6318
+ each: null,
6319
+ in: null,
6320
+ when: null,
6321
+ while: null,
6322
+ function: null,
6323
+ as: null,
6324
+ then: null,
6325
+ end: null,
6326
+ with: null,
6327
+ _: null,
6328
+ };
6329
+ const phi = (1 + Math.sqrt(5)) / 2;
6330
+ const numberReservedSymbolRecord = {
6331
+ 'E': Math.E,
6332
+ '-E': -Math.E,
6333
+ 'ε': Math.E,
6334
+ '-ε': -Math.E,
6335
+ 'PI': Math.PI,
6336
+ '-PI': -Math.PI,
6337
+ 'π': Math.PI,
6338
+ '-π': -Math.PI,
6339
+ 'PHI': phi,
6340
+ '-PHI': -phi,
6341
+ 'φ': phi,
6342
+ '-φ': -phi,
6343
+ 'POSITIVE_INFINITY': Number.POSITIVE_INFINITY,
6344
+ '∞': Number.POSITIVE_INFINITY,
6345
+ 'NEGATIVE_INFINITY': Number.NEGATIVE_INFINITY,
6346
+ '-∞': Number.NEGATIVE_INFINITY,
6347
+ 'MAX_SAFE_INTEGER': Number.MAX_SAFE_INTEGER,
6348
+ 'MIN_SAFE_INTEGER': Number.MIN_SAFE_INTEGER,
6349
+ 'MAX_VALUE': Number.MAX_VALUE,
6350
+ 'MIN_VALUE': Number.MIN_VALUE,
6351
+ 'NaN': Number.NaN,
6352
+ };
6353
+ const reservedSymbolRecord = {
6354
+ ...nonNumberReservedSymbolRecord,
6355
+ ...numberReservedSymbolRecord,
6356
+ };
6357
+ function isNumberReservedSymbol(symbol) {
6358
+ return symbol in numberReservedSymbolRecord;
6359
+ }
6360
+
6361
+ const illegalSymbolCharacters = [
6362
+ '(',
6363
+ ')',
6364
+ '[',
6365
+ ']',
6366
+ '{',
6367
+ '}',
6368
+ '\'',
6369
+ '"',
6370
+ '`',
6371
+ ',',
6372
+ '.',
6373
+ ';',
6374
+ ' ',
6375
+ '\n',
6376
+ '\r',
6377
+ '\t',
6378
+ ];
6379
+ const illegalFirstSymbolCharacters = [
6380
+ '0',
6381
+ '1',
6382
+ '2',
6383
+ '3',
6384
+ '4',
6385
+ '5',
6386
+ '6',
6387
+ '7',
6388
+ '8',
6389
+ '9',
6390
+ ...illegalSymbolCharacters,
6391
+ ];
6392
+ const illegalSymbolCharacterSet = new Set(illegalSymbolCharacters);
6393
+ const illegalFirstSymbolCharacterSet = new Set(illegalFirstSymbolCharacters);
6394
+ const whitespaceRegExp = /\s/;
6395
+ const NO_MATCH = [0];
6396
+ const tokenizeLParen = (input, position) => tokenizeToken('LParen', '(', input, position);
6397
+ const tokenizeRParen = (input, position) => tokenizeToken('RParen', ')', input, position);
6398
+ const tokenizeLBracket = (input, position) => tokenizeToken('LBracket', '[', input, position);
6399
+ const tokenizeRBracket = (input, position) => tokenizeToken('RBracket', ']', input, position);
6400
+ const tokenizeLBrace = (input, position) => tokenizeToken('LBrace', '{', input, position);
6401
+ const tokenizeRBrace = (input, position) => tokenizeToken('RBrace', '}', input, position);
6402
+ const tokenizeString = (input, position) => {
6403
+ if (input[position] !== '"')
6404
+ return NO_MATCH;
6405
+ let value = '"';
6406
+ let length = 1;
6407
+ let char = input[position + length];
6408
+ let escaping = false;
6409
+ while (char && (char !== '"' || escaping)) {
6410
+ length += 1;
6411
+ if (escaping) {
6412
+ escaping = false;
6413
+ value += char;
6414
+ }
6415
+ else {
6416
+ if (char === '\\') {
6417
+ escaping = true;
6418
+ }
6419
+ value += char;
6420
+ }
6421
+ char = input[position + length];
6400
6422
  }
6401
- if (symbolName && token[1] !== symbolName) {
6402
- return false;
6423
+ if (!char) {
6424
+ return [length, ['Error', value, undefined, `Unclosed string at position ${position}`]];
6403
6425
  }
6404
- return true;
6405
- }
6406
- function assertSymbolToken(token, symbolName) {
6407
- if (!isSymbolToken(token, symbolName)) {
6408
- throwUnexpectedToken('Symbol', undefined, token);
6426
+ value += '"'; // closing quote
6427
+ return [length + 1, ['string', value]];
6428
+ };
6429
+ const tokenizeRegexpShorthand = (input, position) => {
6430
+ if (input[position] !== '#')
6431
+ return NO_MATCH;
6432
+ const [stringLength, token] = tokenizeString(input, position + 1);
6433
+ if (!token)
6434
+ return NO_MATCH;
6435
+ if (token[0] === 'Error') {
6436
+ const errorToken = ['Error', `#${token[1]}`, undefined, `Unclosed regexp at position ${position}`];
6437
+ return [stringLength + 1, errorToken];
6409
6438
  }
6439
+ position += stringLength + 1;
6440
+ let length = stringLength + 1;
6441
+ let options = '';
6442
+ while (input[position] === 'g' || input[position] === 'i') {
6443
+ options += input[position];
6444
+ length += 1;
6445
+ position += 1;
6446
+ if (options.includes(input[position])) {
6447
+ return [length, ['Error', `#${token[1]}${options}`, undefined, `Duplicated regexp option "${input[position]}"`]];
6448
+ }
6449
+ }
6450
+ return [length, ['RegexpShorthand', `#${token[1]}${options}`]];
6451
+ };
6452
+ function tokenizeToken(type, value, input, position) {
6453
+ if (value === input.slice(position, position + value.length))
6454
+ return [value.length, [type, value]];
6455
+ else
6456
+ return NO_MATCH;
6410
6457
  }
6411
- function asSymbolToken(token, symbolName) {
6412
- assertSymbolToken(token, symbolName);
6413
- return token;
6414
- }
6415
- function isReservedSymbolToken(token, symbolName) {
6416
- if (token?.[0] !== 'ReservedSymbol') {
6417
- return false;
6458
+ const tokenizeWhitespace = (input, position) => {
6459
+ let char = input[position];
6460
+ if (!char || !whitespaceRegExp.test(char)) {
6461
+ return NO_MATCH;
6418
6462
  }
6419
- if (symbolName && token[1] !== symbolName) {
6420
- return false;
6463
+ let value = char;
6464
+ position += 1;
6465
+ char = input[position];
6466
+ while (char && whitespaceRegExp.test(char)) {
6467
+ value += char;
6468
+ position += 1;
6469
+ char = input[position];
6421
6470
  }
6422
- return true;
6423
- }
6424
- function assertReservedSymbolToken(token, symbolName) {
6425
- if (!isReservedSymbolToken(token, symbolName)) {
6426
- throwUnexpectedToken('ReservedSymbol', symbolName, token);
6471
+ return [value.length, ['Whitespace', value]];
6472
+ };
6473
+ const decimalNumberRegExp = /\d/;
6474
+ const octalNumberRegExp = /[0-7]/;
6475
+ const hexNumberRegExp = /[0-9a-f]/i;
6476
+ const binaryNumberRegExp = /[01]/;
6477
+ const postNumberRegExp = /[\s)\]}(,;]/;
6478
+ const tokenizeNumber = (input, position) => {
6479
+ let i;
6480
+ const negate = input[position] === '-';
6481
+ const plusPrefix = input[position] === '+';
6482
+ const start = negate || plusPrefix ? position + 1 : position;
6483
+ let hasDecimalPoint = false;
6484
+ let hasExponent = false;
6485
+ for (i = start; i < input.length; i += 1) {
6486
+ const char = input[i];
6487
+ if (char === '_') {
6488
+ if (!decimalNumberRegExp.test(input[i - 1]) || !decimalNumberRegExp.test(input[i + 1])) {
6489
+ if (i === start) {
6490
+ return NO_MATCH;
6491
+ }
6492
+ return [i - position + 1, ['Error', input.substring(position, i + 1), undefined, `Invalid number format at position ${i + 1}`]];
6493
+ }
6494
+ }
6495
+ else if (char === '.') {
6496
+ if (i === start) {
6497
+ return NO_MATCH;
6498
+ }
6499
+ if (hasDecimalPoint || hasExponent) {
6500
+ return [i - position + 1, ['Error', input.substring(position, i + 1), undefined, `Invalid number format at position ${i + 1}`]];
6501
+ }
6502
+ hasDecimalPoint = true;
6503
+ }
6504
+ else if (char === 'e' || char === 'E') {
6505
+ if (i === start) {
6506
+ return NO_MATCH;
6507
+ }
6508
+ if (hasExponent) {
6509
+ return [i - position + 1, ['Error', input.substring(position, i + 1), undefined, `Invalid number format at position ${i + 1}`]];
6510
+ }
6511
+ if (input[i - 1] === '.' || input[i - 1] === '+' || input[i - 1] === '-') {
6512
+ return [i - position + 1, ['Error', input.substring(position, i + 1), undefined, `Invalid number format at position ${i + 1}`]];
6513
+ }
6514
+ if (input[i + 1] === '+' || input[i + 1] === '-') {
6515
+ i += 1;
6516
+ }
6517
+ hasExponent = true;
6518
+ }
6519
+ else if (!decimalNumberRegExp.test(char)) {
6520
+ break;
6521
+ }
6427
6522
  }
6428
- }
6429
- function asReservedSymbolToken(token, symbolName) {
6430
- assertReservedSymbolToken(token, symbolName);
6431
- return token;
6432
- }
6433
- function isShebangToken(token) {
6434
- return token?.[0] === 'Shebang';
6435
- }
6436
- function isSingleLineCommentToken(token) {
6437
- return token?.[0] === 'SingleLineComment';
6438
- }
6439
- function isMultiLineCommentToken(token) {
6440
- return token?.[0] === 'MultiLineComment';
6441
- }
6442
- function isOperatorToken(token, operatorName) {
6443
- if (token?.[0] !== 'Operator') {
6444
- return false;
6523
+ if ((negate || plusPrefix) && i === start) {
6524
+ return NO_MATCH;
6445
6525
  }
6446
- if (operatorName && token[1] !== operatorName) {
6447
- return false;
6526
+ const length = i - position;
6527
+ if (length === 0) {
6528
+ return NO_MATCH;
6448
6529
  }
6449
- return true;
6450
- }
6451
- function assertOperatorToken(token, operatorName) {
6452
- if (!isOperatorToken(token, operatorName)) {
6453
- throwUnexpectedToken('Operator', operatorName, token);
6530
+ const nextChar = input[i];
6531
+ if (nextChar && nextChar !== ':' && !postNumberRegExp.test(nextChar)) {
6532
+ return [i - position + 1, ['Error', input.substring(position, i + 1), undefined, `Invalid number format at position ${i + 1}`]];
6454
6533
  }
6455
- }
6456
- function isWhitespaceToken(token) {
6457
- return token?.[0] === 'Whitespace';
6458
- }
6459
- function isNumberToken(token) {
6460
- return token?.[0] === 'Number';
6461
- }
6462
- function isBasePrefixedNumberToken(token) {
6463
- return token?.[0] === 'BasePrefixedNumber';
6464
- }
6465
- function isLParenToken(token) {
6466
- return token?.[0] === 'LParen';
6467
- }
6468
- function assertLParenToken(token) {
6469
- if (!isLParenToken(token)) {
6470
- throwUnexpectedToken('LParen', undefined, token);
6534
+ return [length, ['Number', input.substring(position, i)]];
6535
+ };
6536
+ const tokenizeBasePrefixedNumber = (input, position) => {
6537
+ if (input[position] !== '0') {
6538
+ return NO_MATCH;
6471
6539
  }
6472
- }
6473
- function isRParenToken(token) {
6474
- return token?.[0] === 'RParen';
6475
- }
6476
- function assertRParenToken(token) {
6477
- if (!isRParenToken(token)) {
6478
- throwUnexpectedToken('RParen', undefined, token);
6540
+ const baseChar = input[position + 1];
6541
+ const type = baseChar === 'b' || baseChar === 'B'
6542
+ ? 'binary'
6543
+ : baseChar === 'o' || baseChar === 'O'
6544
+ ? 'octal'
6545
+ : baseChar === 'x' || baseChar === 'X'
6546
+ ? 'hex'
6547
+ : null;
6548
+ if (type === null) {
6549
+ return NO_MATCH;
6479
6550
  }
6480
- }
6481
- function isLBracketToken(token) {
6482
- return token?.[0] === 'LBracket';
6483
- }
6484
- function assertLBracketToken(token) {
6485
- if (!isLBracketToken(token)) {
6486
- throwUnexpectedToken('LBracket', undefined, token);
6551
+ let i;
6552
+ for (i = position + 2; i < input.length; i += 1) {
6553
+ const char = input[i];
6554
+ if (type === 'binary' && !binaryNumberRegExp.test(char)) {
6555
+ break;
6556
+ }
6557
+ if (type === 'octal' && !octalNumberRegExp.test(char)) {
6558
+ break;
6559
+ }
6560
+ if (type === 'hex' && !hexNumberRegExp.test(char)) {
6561
+ break;
6562
+ }
6487
6563
  }
6488
- }
6489
- function asLBracketToken(token) {
6490
- assertLBracketToken(token);
6491
- return token;
6492
- }
6493
- function isRBracketToken(token) {
6494
- return token?.[0] === 'RBracket';
6495
- }
6496
- function assertRBracketToken(token) {
6497
- if (!isRBracketToken(token)) {
6498
- throwUnexpectedToken('RBracket', undefined, token);
6564
+ const length = i - position;
6565
+ if (length <= 2) {
6566
+ return NO_MATCH;
6499
6567
  }
6500
- }
6501
- function isLBraceToken(token) {
6502
- return token?.[0] === 'LBrace';
6503
- }
6504
- function assertLBraceToken(token) {
6505
- if (!isLBraceToken(token)) {
6506
- throwUnexpectedToken('LBrace', undefined, token);
6568
+ const nextChar = input[i];
6569
+ if (nextChar && !postNumberRegExp.test(nextChar)) {
6570
+ return NO_MATCH;
6507
6571
  }
6508
- }
6509
- function asLBraceToken(token) {
6572
+ return [length, ['BasePrefixedNumber', input.substring(position, i)]];
6573
+ };
6574
+ const tokenizeSymbol = (input, position) => {
6575
+ let value = input[position];
6576
+ if (value === '\'') {
6577
+ let length = 1;
6578
+ let char = input[position + length];
6579
+ let escaping = false;
6580
+ while (char !== '\'' || escaping) {
6581
+ if (char === undefined)
6582
+ return [length, ['Error', value, undefined, `Unclosed quoted symbol at position ${position}`]];
6583
+ length += 1;
6584
+ if (escaping) {
6585
+ escaping = false;
6586
+ value += char;
6587
+ }
6588
+ else {
6589
+ if (char === '\\') {
6590
+ escaping = true;
6591
+ }
6592
+ value += char;
6593
+ }
6594
+ char = input[position + length];
6595
+ }
6596
+ value += '\''; // closing quote
6597
+ return [length + 1, ['Symbol', value]];
6598
+ }
6599
+ if (!illegalFirstSymbolCharacterSet.has(value)) {
6600
+ const initialPosition = position;
6601
+ position += 1;
6602
+ let char = input[position];
6603
+ while (char && !illegalSymbolCharacterSet.has(char)) {
6604
+ value += char;
6605
+ position += 1;
6606
+ char = input[position];
6607
+ }
6608
+ // : can be used as symbol character, but it must not be the last character
6609
+ return value.endsWith(':')
6610
+ ? [position - initialPosition - 1, ['Symbol', value.slice(0, -1)]]
6611
+ : [position - initialPosition, ['Symbol', value]];
6612
+ }
6613
+ return NO_MATCH;
6614
+ };
6615
+ const tokenizeReservedSymbolToken = (input, position) => {
6616
+ const symbolMeta = tokenizeSymbol(input, position);
6617
+ if (symbolMeta[0] === 0 || !symbolMeta[1]) {
6618
+ return NO_MATCH;
6619
+ }
6620
+ let symbolName = symbolMeta[1][1];
6621
+ symbolName = symbolName.startsWith('\'') ? symbolName.slice(1, symbolName.length - 1) : symbolName;
6622
+ const info = reservedSymbolRecord[symbolName];
6623
+ if (info === undefined) {
6624
+ return NO_MATCH;
6625
+ }
6626
+ return [symbolMeta[0], ['ReservedSymbol', symbolName]];
6627
+ };
6628
+ const tokenizeOperator = (input, position) => {
6629
+ const threeChars = input.slice(position, position + 3);
6630
+ if (position + 2 < input.length && isSymbolicOperator(threeChars)) {
6631
+ return [3, ['Operator', threeChars]];
6632
+ }
6633
+ const twoChars = input.slice(position, position + 2);
6634
+ if (position + 1 < input.length && isSymbolicOperator(twoChars)) {
6635
+ return [2, ['Operator', twoChars]];
6636
+ }
6637
+ const oneChar = input[position] ?? '';
6638
+ if (isSymbolicOperator(oneChar)) {
6639
+ return [1, ['Operator', oneChar]];
6640
+ }
6641
+ return NO_MATCH;
6642
+ };
6643
+ const tokenizeMultiLineComment = (input, position) => {
6644
+ if (input[position] === '/' && input[position + 1] === '*') {
6645
+ let length = 2;
6646
+ let value = '/*';
6647
+ while ((input[position + length] !== '*' || input[position + length + 1] !== '/') && position + length + 1 < input.length) {
6648
+ value += input[position + length];
6649
+ length += 1;
6650
+ }
6651
+ if (position + length + 1 >= input.length) {
6652
+ return [length, ['Error', value, undefined, `Unclosed multi-line comment at position ${position}`]];
6653
+ }
6654
+ value += '*/';
6655
+ length += 2;
6656
+ return [length, ['MultiLineComment', value]];
6657
+ }
6658
+ return NO_MATCH;
6659
+ };
6660
+ const tokenizeShebang = (input, position) => {
6661
+ if (input[position] === '#' && input[position + 1] === '!') {
6662
+ let length = 2;
6663
+ let value = '#!';
6664
+ while (input[position + length] !== '\n' && position + length < input.length) {
6665
+ value += input[position + length];
6666
+ length += 1;
6667
+ }
6668
+ return [length, ['SingleLineComment', value]];
6669
+ }
6670
+ return NO_MATCH;
6671
+ };
6672
+ const tokenizeSingleLineComment = (input, position) => {
6673
+ if (input[position] === '/' && input[position + 1] === '/') {
6674
+ let length = 2;
6675
+ let value = '//';
6676
+ while (input[position + length] !== '\n' && position + length < input.length) {
6677
+ value += input[position + length];
6678
+ length += 1;
6679
+ }
6680
+ return [length, ['SingleLineComment', value]];
6681
+ }
6682
+ return NO_MATCH;
6683
+ };
6684
+ // All tokenizers, order matters!
6685
+ const tokenizers = [
6686
+ tokenizeWhitespace,
6687
+ tokenizeMultiLineComment,
6688
+ tokenizeSingleLineComment,
6689
+ tokenizeReservedSymbolToken,
6690
+ tokenizeLParen,
6691
+ tokenizeRParen,
6692
+ tokenizeLBracket,
6693
+ tokenizeRBracket,
6694
+ tokenizeLBrace,
6695
+ tokenizeRBrace,
6696
+ tokenizeString,
6697
+ tokenizeRegexpShorthand,
6698
+ tokenizeBasePrefixedNumber,
6699
+ tokenizeNumber,
6700
+ tokenizeOperator,
6701
+ tokenizeSymbol,
6702
+ ];
6703
+
6704
+ function tokenize(input, debug, filePath) {
6705
+ let position = 0;
6706
+ const tokenStream = {
6707
+ tokens: [],
6708
+ filePath,
6709
+ hasDebugData: debug,
6710
+ };
6711
+ while (position < input.length) {
6712
+ const sourceCodeInfo = debug
6713
+ ? createSourceCodeInfo(input, position, filePath)
6714
+ : undefined;
6715
+ const tokenDescriptor = getCurrentToken(input, position);
6716
+ const [count, token] = tokenDescriptor;
6717
+ position += count;
6718
+ if (token) {
6719
+ if (sourceCodeInfo) {
6720
+ token[2] = sourceCodeInfo;
6721
+ }
6722
+ tokenStream.tokens.push(token);
6723
+ }
6724
+ }
6725
+ return tokenStream;
6726
+ }
6727
+ function getSourceCodeLine(input, lineNbr) {
6728
+ return input.split(/\r\n|\r|\n/)[lineNbr];
6729
+ }
6730
+ function createSourceCodeInfo(input, position, filePath) {
6731
+ const lines = input.substring(0, position + 1).split(/\r\n|\r|\n/);
6732
+ const lastLine = lines[lines.length - 1];
6733
+ const code = getSourceCodeLine(input, lines.length - 1);
6734
+ const line = lines.length;
6735
+ const column = lastLine.length;
6736
+ return {
6737
+ code,
6738
+ position: {
6739
+ line,
6740
+ column,
6741
+ },
6742
+ filePath,
6743
+ };
6744
+ }
6745
+ function getCurrentToken(input, position) {
6746
+ const initialPosition = position;
6747
+ if (position === 0) {
6748
+ const [nbrOfCharacters, token] = tokenizeShebang(input, position);
6749
+ position += nbrOfCharacters;
6750
+ if (nbrOfCharacters > 0) {
6751
+ return [position - initialPosition, token];
6752
+ }
6753
+ }
6754
+ for (const tokenizer of tokenizers) {
6755
+ const [nbrOfCharacters, token] = tokenizer(input, position);
6756
+ position += nbrOfCharacters;
6757
+ if (nbrOfCharacters === 0) {
6758
+ continue;
6759
+ }
6760
+ return [position - initialPosition, token];
6761
+ }
6762
+ return [1, ['Error', input[initialPosition], undefined, 'Unrecognized character']];
6763
+ }
6764
+
6765
+ const dvalaCommands = new Set([...normalExpressionKeys, ...specialExpressionKeys, ...Object.keys(reservedSymbolRecord)]);
6766
+ // TODO: replace with get suggestions function
6767
+ class AutoCompleter {
6768
+ originalProgram;
6769
+ originalPosition;
6770
+ prefixProgram = '';
6771
+ suffixProgram = '';
6772
+ searchString = '';
6773
+ suggestions = [];
6774
+ suggestionIndex = null;
6775
+ constructor(originalProgram, originalPosition, params = {}) {
6776
+ this.originalProgram = originalProgram;
6777
+ this.originalPosition = originalPosition;
6778
+ const partialProgram = this.originalProgram.slice(0, this.originalPosition);
6779
+ const tokenStream = tokenize(partialProgram, false, undefined);
6780
+ const lastToken = tokenStream.tokens.at(-1);
6781
+ if (!lastToken) {
6782
+ return;
6783
+ }
6784
+ if (lastToken[0] === 'Error') {
6785
+ return;
6786
+ }
6787
+ this.searchString = lastToken[1];
6788
+ this.prefixProgram = this.originalProgram.slice(0, this.originalPosition - this.searchString.length);
6789
+ this.suffixProgram = this.originalProgram.slice(this.prefixProgram.length + this.searchString.length);
6790
+ this.originalProgram.slice(this.prefixProgram.length + this.searchString.length);
6791
+ this.suggestions = this.generateSuggestions(params);
6792
+ }
6793
+ getNextSuggestion() {
6794
+ return this.getAutoCompleteSuggestionResult(this.getNextSuggestionSymbol());
6795
+ }
6796
+ getPreviousSuggestion() {
6797
+ return this.getAutoCompleteSuggestionResult(this.getPreviousSuggestionSymbol());
6798
+ }
6799
+ getAutoCompleteSuggestionResult(suggestion) {
6800
+ if (suggestion === null) {
6801
+ return null;
6802
+ }
6803
+ return {
6804
+ program: this.prefixProgram + suggestion + this.suffixProgram,
6805
+ position: this.prefixProgram.length + suggestion.length,
6806
+ };
6807
+ }
6808
+ getNextSuggestionSymbol() {
6809
+ if (this.suggestions.length === 0) {
6810
+ return null;
6811
+ }
6812
+ if (this.suggestionIndex === null) {
6813
+ this.suggestionIndex = 0;
6814
+ }
6815
+ else {
6816
+ this.suggestionIndex += 1;
6817
+ if (this.suggestionIndex >= this.suggestions.length) {
6818
+ this.suggestionIndex = 0;
6819
+ }
6820
+ }
6821
+ return this.suggestions[this.suggestionIndex];
6822
+ }
6823
+ getPreviousSuggestionSymbol() {
6824
+ if (this.suggestions.length === 0) {
6825
+ return null;
6826
+ }
6827
+ if (this.suggestionIndex === null) {
6828
+ this.suggestionIndex = this.suggestions.length - 1;
6829
+ }
6830
+ else {
6831
+ this.suggestionIndex -= 1;
6832
+ if (this.suggestionIndex < 0) {
6833
+ this.suggestionIndex = this.suggestions.length - 1;
6834
+ }
6835
+ }
6836
+ return this.suggestions[this.suggestionIndex];
6837
+ }
6838
+ getSuggestions() {
6839
+ return [...this.suggestions];
6840
+ }
6841
+ getSearchString() {
6842
+ return this.searchString;
6843
+ }
6844
+ generateSuggestions(params) {
6845
+ const blacklist = new Set(['0_defn', '0_lambda']);
6846
+ const startsWithCaseSensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.startsWith(this.searchString));
6847
+ startsWithCaseSensitive.forEach(suggestion => blacklist.add(suggestion));
6848
+ const startsWithCaseInsensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.toLowerCase().startsWith(this.searchString.toLowerCase()));
6849
+ startsWithCaseInsensitive.forEach(suggestion => blacklist.add(suggestion));
6850
+ const includesCaseSensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.includes(this.searchString));
6851
+ includesCaseSensitive.forEach(suggestion => blacklist.add(suggestion));
6852
+ const includesCaseInsensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.includes(this.searchString.toLowerCase()));
6853
+ includesCaseInsensitive.forEach(suggestion => blacklist.add(suggestion));
6854
+ return [...startsWithCaseSensitive, ...startsWithCaseInsensitive, ...includesCaseSensitive, ...includesCaseInsensitive];
6855
+ }
6856
+ generateWithPredicate(params, shouldInclude) {
6857
+ const suggestions = new Set();
6858
+ dvalaCommands.forEach((suggestion) => {
6859
+ if (shouldInclude(suggestion)) {
6860
+ suggestions.add(suggestion);
6861
+ }
6862
+ });
6863
+ Object.keys(params.bindings ?? {})
6864
+ .filter(shouldInclude)
6865
+ .forEach(suggestion => suggestions.add(suggestion));
6866
+ return [...suggestions].sort((a, b) => a.localeCompare(b));
6867
+ }
6868
+ }
6869
+
6870
+ function isSymbolNode(node) {
6871
+ const nodeType = node[0];
6872
+ return NodeTypes.UserDefinedSymbol === nodeType
6873
+ || NodeTypes.NormalBuiltinSymbol === nodeType
6874
+ || NodeTypes.SpecialBuiltinSymbol === nodeType;
6875
+ }
6876
+ function isUserDefinedSymbolNode(node) {
6877
+ return NodeTypes.UserDefinedSymbol === node[0];
6878
+ }
6879
+ function asUserDefinedSymbolNode(node, sourceCodeInfo) {
6880
+ assertUserDefinedSymbolNode(node, sourceCodeInfo);
6881
+ return node;
6882
+ }
6883
+ function assertUserDefinedSymbolNode(node, sourceCodeInfo) {
6884
+ if (!isUserDefinedSymbolNode(node))
6885
+ throw getAssertionError('UserDefinedSymbolNode', node, sourceCodeInfo);
6886
+ }
6887
+ function isNormalBuiltinSymbolNode(node) {
6888
+ return NodeTypes.NormalBuiltinSymbol === node[0];
6889
+ }
6890
+ function isSpecialBuiltinSymbolNode(node) {
6891
+ return NodeTypes.SpecialBuiltinSymbol === node[0];
6892
+ }
6893
+ function isNormalExpressionNode(node) {
6894
+ return node[0] === NodeTypes.NormalExpression;
6895
+ }
6896
+ function isNormalExpressionNodeWithName(node) {
6897
+ if (!isNormalExpressionNode(node)) {
6898
+ return false;
6899
+ }
6900
+ return isSymbolNode(node[1][0]);
6901
+ }
6902
+ function isSpreadNode(node) {
6903
+ return node[0] === NodeTypes.Spread;
6904
+ }
6905
+
6906
+ function isContextEntry(value) {
6907
+ return isUnknownRecord(value) && value.value !== undefined;
6908
+ }
6909
+
6910
+ class ContextStackImpl {
6911
+ _contexts;
6912
+ globalContext;
6913
+ values;
6914
+ modules;
6915
+ valueModules;
6916
+ pure;
6917
+ constructor({ contexts, values: hostValues, modules, valueModules, pure, }) {
6918
+ this.globalContext = asNonUndefined(contexts[0]);
6919
+ this._contexts = contexts;
6920
+ this.values = hostValues;
6921
+ this.modules = modules ?? new Map();
6922
+ this.valueModules = valueModules ?? new Map();
6923
+ this.pure = pure ?? false;
6924
+ }
6925
+ // -- Serialization support (Phase 4) --
6926
+ /** Get the raw context chain for serialization. */
6927
+ getContextsRaw() {
6928
+ return this._contexts;
6929
+ }
6930
+ /** Get host values (plain bindings passed at creation). */
6931
+ getHostValues() {
6932
+ return this.values;
6933
+ }
6934
+ /** Get the top-level module scope as plain key→value bindings. */
6935
+ getModuleScopeBindings() {
6936
+ const scope = this._contexts[0];
6937
+ const result = {};
6938
+ for (const [k, v] of Object.entries(scope))
6939
+ result[k] = v.value;
6940
+ return result;
6941
+ }
6942
+ /**
6943
+ * Find the index of globalContext in the _contexts array.
6944
+ * Returns -1 if not found (should not happen in valid state).
6945
+ */
6946
+ getGlobalContextIndex() {
6947
+ return this._contexts.indexOf(this.globalContext);
6948
+ }
6949
+ /**
6950
+ * Create a ContextStack from deserialized data.
6951
+ * `contexts` is the restored context chain (already resolved).
6952
+ * `globalContextIndex` identifies which element is the globalContext.
6953
+ * Host bindings (`values`, `modules`) come from resume options.
6954
+ */
6955
+ static fromDeserialized(params) {
6956
+ const cs = new ContextStackImpl({
6957
+ contexts: params.contexts,
6958
+ values: params.values,
6959
+ modules: params.modules,
6960
+ pure: params.pure,
6961
+ });
6962
+ if (params.globalContextIndex >= 0 && params.globalContextIndex < params.contexts.length) {
6963
+ cs.globalContext = params.contexts[params.globalContextIndex];
6964
+ }
6965
+ return cs;
6966
+ }
6967
+ /**
6968
+ * Replace the contexts array and globalContext. Used during deserialization
6969
+ * to fill in resolved context data after circular references are handled.
6970
+ */
6971
+ setContextsFromDeserialized(contexts, globalContextIndex) {
6972
+ this._contexts = contexts;
6973
+ if (globalContextIndex >= 0 && globalContextIndex < contexts.length) {
6974
+ this.globalContext = contexts[globalContextIndex];
6975
+ }
6976
+ }
6977
+ getModule(name) {
6978
+ return this.modules.get(name);
6979
+ }
6980
+ getValueModule(name) {
6981
+ if (this.valueModules.has(name)) {
6982
+ return { value: this.valueModules.get(name), found: true };
6983
+ }
6984
+ return { value: undefined, found: false };
6985
+ }
6986
+ registerValueModule(name, value) {
6987
+ this.valueModules.set(name, value);
6988
+ }
6989
+ create(context) {
6990
+ const globalContext = this.globalContext;
6991
+ const contextStack = new ContextStackImpl({
6992
+ contexts: [context, ...this._contexts],
6993
+ values: this.values,
6994
+ modules: this.modules,
6995
+ valueModules: this.valueModules,
6996
+ pure: this.pure,
6997
+ });
6998
+ contextStack.globalContext = globalContext;
6999
+ return contextStack;
7000
+ }
7001
+ new(context) {
7002
+ const contexts = [{}, context];
7003
+ return new ContextStackImpl({ contexts, modules: this.modules, valueModules: this.valueModules, pure: this.pure });
7004
+ }
7005
+ addValues(values, sourceCodeInfo) {
7006
+ const currentContext = this._contexts[0];
7007
+ for (const [name, value] of Object.entries(values)) {
7008
+ if (currentContext[name]) {
7009
+ throw new DvalaError(`Cannot redefine value "${name}"`, sourceCodeInfo);
7010
+ }
7011
+ const shadowedName = getShadowedBuiltinName(name);
7012
+ if (shadowedName) {
7013
+ throw new DvalaError(`Cannot shadow ${shadowedName}`, sourceCodeInfo);
7014
+ }
7015
+ currentContext[name] = { value: toAny(value) };
7016
+ }
7017
+ }
7018
+ getValue(name) {
7019
+ for (const context of this._contexts) {
7020
+ const contextEntry = context[name];
7021
+ if (contextEntry)
7022
+ return contextEntry.value;
7023
+ }
7024
+ return this.values?.[name];
7025
+ }
7026
+ lookUp(node) {
7027
+ const value = node[1];
7028
+ for (const context of this._contexts) {
7029
+ const contextEntry = context[value];
7030
+ if (contextEntry)
7031
+ return contextEntry;
7032
+ }
7033
+ const hostValue = this.values?.[value];
7034
+ if (hostValue !== undefined) {
7035
+ return {
7036
+ value: toAny(hostValue),
7037
+ };
7038
+ }
7039
+ return null;
7040
+ }
7041
+ evaluateSymbol(node) {
7042
+ if (isSpecialBuiltinSymbolNode(node)) {
7043
+ const functionType = node[1];
7044
+ switch (functionType) {
7045
+ case specialExpressionTypes['&&']:
7046
+ case specialExpressionTypes['||']:
7047
+ case specialExpressionTypes.array:
7048
+ case specialExpressionTypes.object:
7049
+ case specialExpressionTypes['defined?']:
7050
+ case specialExpressionTypes.recur:
7051
+ case specialExpressionTypes['??']: {
7052
+ const specialExpression = asNonUndefined(builtin.specialExpressions[functionType], node[2]);
7053
+ return {
7054
+ [FUNCTION_SYMBOL]: true,
7055
+ functionType: 'SpecialBuiltin',
7056
+ specialBuiltinSymbolType: functionType,
7057
+ sourceCodeInfo: node[2],
7058
+ arity: specialExpression.arity,
7059
+ };
7060
+ }
7061
+ default:
7062
+ throw new DvalaError(`Unknown special builtin symbol type: ${functionType}`, node[2]);
7063
+ }
7064
+ }
7065
+ if (isNormalBuiltinSymbolNode(node)) {
7066
+ const type = node[1];
7067
+ const normalExpression = allNormalExpressions[type];
7068
+ const name = normalExpression.name;
7069
+ return {
7070
+ [FUNCTION_SYMBOL]: true,
7071
+ functionType: 'Builtin',
7072
+ normalBuiltinSymbolType: type,
7073
+ sourceCodeInfo: node[2],
7074
+ arity: normalExpression.arity,
7075
+ name,
7076
+ };
7077
+ }
7078
+ const lookUpResult = this.lookUp(node);
7079
+ if (isContextEntry(lookUpResult))
7080
+ return lookUpResult.value;
7081
+ throw new UndefinedSymbolError(node[1], node[2]);
7082
+ }
7083
+ }
7084
+ function getShadowedBuiltinName(name) {
7085
+ if (specialExpressionKeys.includes(name))
7086
+ return `special expression "${name}"`;
7087
+ if (normalExpressionKeys.includes(name))
7088
+ return `builtin function "${name}"`;
7089
+ if (name === 'self')
7090
+ return `builtin value "${name}"`;
7091
+ return null;
7092
+ }
7093
+ function assertNotShadowingBuiltin(name) {
7094
+ const shadowedName = getShadowedBuiltinName(name);
7095
+ if (shadowedName) {
7096
+ throw new DvalaError(`Cannot shadow ${shadowedName}`, undefined);
7097
+ }
7098
+ }
7099
+ function createContextStack(params = {}, modules, pure) {
7100
+ const globalContext = params.globalContext ?? {};
7101
+ // Contexts are checked from left to right
7102
+ const contexts = params.contexts ? [globalContext, ...params.contexts] : [globalContext];
7103
+ let hostValues;
7104
+ if (params.bindings) {
7105
+ for (const [identifier, entry] of Object.entries(params.bindings)) {
7106
+ if (identifier.includes('.')) {
7107
+ throw new DvalaError(`Dots are not allowed in binding keys: "${identifier}"`, undefined);
7108
+ }
7109
+ assertNotShadowingBuiltin(identifier);
7110
+ if (!hostValues) {
7111
+ hostValues = {};
7112
+ }
7113
+ hostValues[identifier] = entry;
7114
+ }
7115
+ }
7116
+ const contextStack = new ContextStackImpl({
7117
+ contexts,
7118
+ values: hostValues,
7119
+ modules,
7120
+ pure,
7121
+ });
7122
+ return params.globalModuleScope ? contextStack : contextStack.create({});
7123
+ }
7124
+
7125
+ const getUndefinedSymbols$1 = (ast, contextStack, builtin, evaluateNode) => {
7126
+ const nodes = Array.isArray(ast)
7127
+ ? ast
7128
+ : [[NodeTypes.SpecialExpression, [specialExpressionTypes.block, ast.body, undefined]]];
7129
+ const unresolvedSymbols = new Set();
7130
+ for (const subNode of nodes) {
7131
+ findUnresolvedSymbolsInNode(subNode, contextStack, builtin, evaluateNode)
7132
+ ?.forEach(symbol => unresolvedSymbols.add(symbol));
7133
+ }
7134
+ return unresolvedSymbols;
7135
+ };
7136
+ function findUnresolvedSymbolsInNode(node, contextStack, builtin, evaluateNode) {
7137
+ const nodeType = node[0];
7138
+ switch (nodeType) {
7139
+ case NodeTypes.UserDefinedSymbol: {
7140
+ const symbolNode = node;
7141
+ const lookUpResult = contextStack.lookUp(symbolNode);
7142
+ if (lookUpResult === null)
7143
+ return new Set([symbolNode[1]]);
7144
+ return null;
7145
+ }
7146
+ case NodeTypes.NormalBuiltinSymbol:
7147
+ case NodeTypes.SpecialBuiltinSymbol:
7148
+ case NodeTypes.String:
7149
+ case NodeTypes.Number:
7150
+ case NodeTypes.ReservedSymbol:
7151
+ case NodeTypes.Binding:
7152
+ return null;
7153
+ case NodeTypes.NormalExpression: {
7154
+ const normalExpressionNode = node;
7155
+ const unresolvedSymbols = new Set();
7156
+ if (isNormalExpressionNodeWithName(normalExpressionNode)) {
7157
+ const [, [symbolNode]] = normalExpressionNode;
7158
+ if (isUserDefinedSymbolNode(symbolNode)) {
7159
+ const lookUpResult = contextStack.lookUp(symbolNode);
7160
+ if (lookUpResult === null)
7161
+ unresolvedSymbols.add(symbolNode[1]);
7162
+ }
7163
+ }
7164
+ else {
7165
+ const [, [expressionNode]] = normalExpressionNode;
7166
+ findUnresolvedSymbolsInNode(expressionNode, contextStack, builtin, evaluateNode)?.forEach(symbol => unresolvedSymbols.add(symbol));
7167
+ }
7168
+ for (const subNode of normalExpressionNode[1][1]) {
7169
+ findUnresolvedSymbolsInNode(subNode, contextStack, builtin, evaluateNode)?.forEach(symbol => unresolvedSymbols.add(symbol));
7170
+ }
7171
+ return unresolvedSymbols;
7172
+ }
7173
+ case NodeTypes.SpecialExpression: {
7174
+ const specialExpressionNode = node;
7175
+ const specialExpressionType = specialExpressionNode[1][0];
7176
+ const specialExpression = builtin.specialExpressions[specialExpressionType];
7177
+ const castedGetUndefinedSymbols = specialExpression.getUndefinedSymbols;
7178
+ return castedGetUndefinedSymbols(specialExpressionNode, contextStack, {
7179
+ getUndefinedSymbols: getUndefinedSymbols$1,
7180
+ builtin,
7181
+ evaluateNode,
7182
+ });
7183
+ }
7184
+ case NodeTypes.Spread:
7185
+ return findUnresolvedSymbolsInNode(node[1], contextStack, builtin, evaluateNode);
7186
+ /* v8 ignore next 2 */
7187
+ default:
7188
+ throw new DvalaError(`Unhandled node type: ${nodeType}`, node[2]);
7189
+ }
7190
+ }
7191
+
7192
+ function isSymbolToken(token, symbolName) {
7193
+ if (token?.[0] !== 'Symbol') {
7194
+ return false;
7195
+ }
7196
+ if (symbolName && token[1] !== symbolName) {
7197
+ return false;
7198
+ }
7199
+ return true;
7200
+ }
7201
+ function assertSymbolToken(token, symbolName) {
7202
+ if (!isSymbolToken(token, symbolName)) {
7203
+ throwUnexpectedToken('Symbol', undefined, token);
7204
+ }
7205
+ }
7206
+ function asSymbolToken(token, symbolName) {
7207
+ assertSymbolToken(token, symbolName);
7208
+ return token;
7209
+ }
7210
+ function isReservedSymbolToken(token, symbolName) {
7211
+ if (token?.[0] !== 'ReservedSymbol') {
7212
+ return false;
7213
+ }
7214
+ if (symbolName && token[1] !== symbolName) {
7215
+ return false;
7216
+ }
7217
+ return true;
7218
+ }
7219
+ function assertReservedSymbolToken(token, symbolName) {
7220
+ if (!isReservedSymbolToken(token, symbolName)) {
7221
+ throwUnexpectedToken('ReservedSymbol', symbolName, token);
7222
+ }
7223
+ }
7224
+ function asReservedSymbolToken(token, symbolName) {
7225
+ assertReservedSymbolToken(token, symbolName);
7226
+ return token;
7227
+ }
7228
+ function isShebangToken(token) {
7229
+ return token?.[0] === 'Shebang';
7230
+ }
7231
+ function isSingleLineCommentToken(token) {
7232
+ return token?.[0] === 'SingleLineComment';
7233
+ }
7234
+ function isMultiLineCommentToken(token) {
7235
+ return token?.[0] === 'MultiLineComment';
7236
+ }
7237
+ function isOperatorToken(token, operatorName) {
7238
+ if (token?.[0] !== 'Operator') {
7239
+ return false;
7240
+ }
7241
+ if (operatorName && token[1] !== operatorName) {
7242
+ return false;
7243
+ }
7244
+ return true;
7245
+ }
7246
+ function assertOperatorToken(token, operatorName) {
7247
+ if (!isOperatorToken(token, operatorName)) {
7248
+ throwUnexpectedToken('Operator', operatorName, token);
7249
+ }
7250
+ }
7251
+ function isWhitespaceToken(token) {
7252
+ return token?.[0] === 'Whitespace';
7253
+ }
7254
+ function isNumberToken(token) {
7255
+ return token?.[0] === 'Number';
7256
+ }
7257
+ function isBasePrefixedNumberToken(token) {
7258
+ return token?.[0] === 'BasePrefixedNumber';
7259
+ }
7260
+ function isLParenToken(token) {
7261
+ return token?.[0] === 'LParen';
7262
+ }
7263
+ function assertLParenToken(token) {
7264
+ if (!isLParenToken(token)) {
7265
+ throwUnexpectedToken('LParen', undefined, token);
7266
+ }
7267
+ }
7268
+ function isRParenToken(token) {
7269
+ return token?.[0] === 'RParen';
7270
+ }
7271
+ function assertRParenToken(token) {
7272
+ if (!isRParenToken(token)) {
7273
+ throwUnexpectedToken('RParen', undefined, token);
7274
+ }
7275
+ }
7276
+ function isLBracketToken(token) {
7277
+ return token?.[0] === 'LBracket';
7278
+ }
7279
+ function assertLBracketToken(token) {
7280
+ if (!isLBracketToken(token)) {
7281
+ throwUnexpectedToken('LBracket', undefined, token);
7282
+ }
7283
+ }
7284
+ function asLBracketToken(token) {
7285
+ assertLBracketToken(token);
7286
+ return token;
7287
+ }
7288
+ function isRBracketToken(token) {
7289
+ return token?.[0] === 'RBracket';
7290
+ }
7291
+ function assertRBracketToken(token) {
7292
+ if (!isRBracketToken(token)) {
7293
+ throwUnexpectedToken('RBracket', undefined, token);
7294
+ }
7295
+ }
7296
+ function isLBraceToken(token) {
7297
+ return token?.[0] === 'LBrace';
7298
+ }
7299
+ function assertLBraceToken(token) {
7300
+ if (!isLBraceToken(token)) {
7301
+ throwUnexpectedToken('LBrace', undefined, token);
7302
+ }
7303
+ }
7304
+ function asLBraceToken(token) {
6510
7305
  assertLBraceToken(token);
6511
7306
  return token;
6512
7307
  }
@@ -6521,9 +7316,6 @@ function assertRBraceToken(token) {
6521
7316
  function isStringToken(token) {
6522
7317
  return token?.[0] === 'string';
6523
7318
  }
6524
- function isDocStringToken(token) {
6525
- return token?.[0] === 'DocString';
6526
- }
6527
7319
  function isA_BinaryOperatorToken(token) {
6528
7320
  return token?.[0] === 'Operator' && isBinaryOperator(token[1]);
6529
7321
  }
@@ -6799,49 +7591,9 @@ function parseCond(ctx, token) {
6799
7591
  return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.cond, params]], token[2]);
6800
7592
  }
6801
7593
 
6802
- function parseString(ctx, token) {
6803
- ctx.advance();
6804
- const value = token[1].substring(1, token[1].length - 1)
6805
- .replace(/(\\{2})|(\\")|(\\n)|(\\t)|(\\r)|(\\b)|(\\f)|\\(.)/g, (_, backslash, doubleQuote, newline, tab, carriageReturn, backspace, formFeed, normalChar) => {
6806
- // If it's a double escape (\\x), return \x
6807
- if (backslash) {
6808
- return '\\';
6809
- }
6810
- // If it's a special character (\n, \t, \r, \b, \f), return the special character
6811
- else if (newline) {
6812
- return '\n';
6813
- }
6814
- else if (tab) {
6815
- return '\t';
6816
- }
6817
- else if (carriageReturn) {
6818
- return '\r';
6819
- }
6820
- else if (backspace) {
6821
- return '\b';
6822
- }
6823
- else if (formFeed) {
6824
- return '\f';
6825
- }
6826
- else if (doubleQuote) {
6827
- return '"';
6828
- }
6829
- return normalChar;
6830
- });
6831
- return withSourceCodeInfo([NodeTypes.String, value], token[2]);
6832
- }
6833
-
6834
- function parseDo(ctx, allowDocString = false) {
7594
+ function parseDo(ctx) {
6835
7595
  const token = asReservedSymbolToken(ctx.tryPeek(), 'do');
6836
7596
  ctx.advance();
6837
- let docString = '';
6838
- if (allowDocString && isDocStringToken(ctx.tryPeek())) {
6839
- docString = parseDocString(ctx);
6840
- if (!ctx.isAtEnd() && !isReservedSymbolToken(ctx.tryPeek(), 'end') && !isReservedSymbolToken(ctx.tryPeek(), 'with')) {
6841
- assertOperatorToken(ctx.tryPeek(), ';');
6842
- ctx.advance();
6843
- }
6844
- }
6845
7597
  const expressions = [];
6846
7598
  while (!ctx.isAtEnd() && !isReservedSymbolToken(ctx.tryPeek(), 'end') && !isReservedSymbolToken(ctx.tryPeek(), 'with')) {
6847
7599
  expressions.push(ctx.parseExpression());
@@ -6869,16 +7621,7 @@ function parseDo(ctx, allowDocString = false) {
6869
7621
  }
6870
7622
  assertReservedSymbolToken(ctx.tryPeek(), 'end');
6871
7623
  ctx.advance();
6872
- return [
6873
- withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.block, expressions, withHandlers]], token[2]),
6874
- docString,
6875
- ];
6876
- }
6877
- function parseDocString(ctx) {
6878
- const token = ctx.peek();
6879
- const stringToken = token[2] ? ['string', token[1].slice(2, -2), token[2]] : ['string', token[1].slice(2, -2)];
6880
- const stringNode = parseString(ctx, stringToken);
6881
- return smartTrim(stringNode[1]); // Extract the string value from the StringNode
7624
+ return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.block, expressions, withHandlers]], token[2]);
6882
7625
  }
6883
7626
 
6884
7627
  function parseSymbol(ctx) {
@@ -6895,6 +7638,38 @@ function parseSymbol(ctx) {
6895
7638
  }
6896
7639
  }
6897
7640
 
7641
+ function parseString(ctx, token) {
7642
+ ctx.advance();
7643
+ const value = token[1].substring(1, token[1].length - 1)
7644
+ .replace(/(\\{2})|(\\")|(\\n)|(\\t)|(\\r)|(\\b)|(\\f)|\\(.)/g, (_, backslash, doubleQuote, newline, tab, carriageReturn, backspace, formFeed, normalChar) => {
7645
+ // If it's a double escape (\\x), return \x
7646
+ if (backslash) {
7647
+ return '\\';
7648
+ }
7649
+ // If it's a special character (\n, \t, \r, \b, \f), return the special character
7650
+ else if (newline) {
7651
+ return '\n';
7652
+ }
7653
+ else if (tab) {
7654
+ return '\t';
7655
+ }
7656
+ else if (carriageReturn) {
7657
+ return '\r';
7658
+ }
7659
+ else if (backspace) {
7660
+ return '\b';
7661
+ }
7662
+ else if (formFeed) {
7663
+ return '\f';
7664
+ }
7665
+ else if (doubleQuote) {
7666
+ return '"';
7667
+ }
7668
+ return normalChar;
7669
+ });
7670
+ return withSourceCodeInfo([NodeTypes.String, value], token[2]);
7671
+ }
7672
+
6898
7673
  function parseNumber(ctx) {
6899
7674
  const token = ctx.peek();
6900
7675
  ctx.advance();
@@ -7268,56 +8043,6 @@ function parseRegexpShorthand(ctx) {
7268
8043
  return node;
7269
8044
  }
7270
8045
 
7271
- const nonNumberReservedSymbolRecord = {
7272
- true: true,
7273
- false: false,
7274
- null: null,
7275
- do: null,
7276
- else: null,
7277
- case: null,
7278
- each: null,
7279
- in: null,
7280
- when: null,
7281
- while: null,
7282
- function: null,
7283
- as: null,
7284
- then: null,
7285
- end: null,
7286
- with: null,
7287
- _: null,
7288
- };
7289
- const phi = (1 + Math.sqrt(5)) / 2;
7290
- const numberReservedSymbolRecord = {
7291
- 'E': Math.E,
7292
- '-E': -Math.E,
7293
- 'ε': Math.E,
7294
- '-ε': -Math.E,
7295
- 'PI': Math.PI,
7296
- '-PI': -Math.PI,
7297
- 'π': Math.PI,
7298
- '-π': -Math.PI,
7299
- 'PHI': phi,
7300
- '-PHI': -phi,
7301
- 'φ': phi,
7302
- '-φ': -phi,
7303
- 'POSITIVE_INFINITY': Number.POSITIVE_INFINITY,
7304
- '∞': Number.POSITIVE_INFINITY,
7305
- 'NEGATIVE_INFINITY': Number.NEGATIVE_INFINITY,
7306
- '-∞': Number.NEGATIVE_INFINITY,
7307
- 'MAX_SAFE_INTEGER': Number.MAX_SAFE_INTEGER,
7308
- 'MIN_SAFE_INTEGER': Number.MIN_SAFE_INTEGER,
7309
- 'MAX_VALUE': Number.MAX_VALUE,
7310
- 'MIN_VALUE': Number.MIN_VALUE,
7311
- 'NaN': Number.NaN,
7312
- };
7313
- const reservedSymbolRecord = {
7314
- ...nonNumberReservedSymbolRecord,
7315
- ...numberReservedSymbolRecord,
7316
- };
7317
- function isNumberReservedSymbol(symbol) {
7318
- return symbol in numberReservedSymbolRecord;
7319
- }
7320
-
7321
8046
  function parseReservedSymbol(ctx) {
7322
8047
  const token = asReservedSymbolToken(ctx.tryPeek());
7323
8048
  ctx.advance();
@@ -7369,11 +8094,8 @@ function parseLambdaFunction(ctx) {
7369
8094
  }
7370
8095
  ctx.advance();
7371
8096
  let nodes;
7372
- let docString = '';
7373
8097
  if (isReservedSymbolToken(ctx.peek(), 'do')) {
7374
- const parsedDo = parseDo(ctx, true);
7375
- docString = parsedDo[1];
7376
- const doNode = parsedDo[0];
8098
+ const doNode = parseDo(ctx);
7377
8099
  const withHandlers = doNode[1][2];
7378
8100
  if (withHandlers && withHandlers.length > 0) {
7379
8101
  // do...with...end: preserve the full DoNode as a single expression so
@@ -7396,7 +8118,6 @@ function parseLambdaFunction(ctx) {
7396
8118
  functionArguments,
7397
8119
  nodes,
7398
8120
  ],
7399
- docString,
7400
8121
  ],
7401
8122
  ], firstToken[2]);
7402
8123
  }
@@ -7448,11 +8169,8 @@ function parseShorthandLambdaFunction(ctx) {
7448
8169
  // TODO, do not like this...
7449
8170
  const startPos = ctx.getPosition();
7450
8171
  let nodes;
7451
- let docString = '';
7452
8172
  if (isReservedSymbolToken(ctx.peek(), 'do')) {
7453
- const parsedDo = parseDo(ctx, true);
7454
- docString = parsedDo[1];
7455
- const doNode = parsedDo[0];
8173
+ const doNode = parseDo(ctx);
7456
8174
  const withHandlers = doNode[1][2];
7457
8175
  if (withHandlers && withHandlers.length > 0) {
7458
8176
  // do...with...end: preserve the full DoNode so the with-handlers are not lost.
@@ -7499,7 +8217,7 @@ function parseShorthandLambdaFunction(ctx) {
7499
8217
  const node = withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes['0_lambda'], [
7500
8218
  functionArguments,
7501
8219
  nodes,
7502
- ], docString]], firstToken[2]);
8220
+ ]]], firstToken[2]);
7503
8221
  return node;
7504
8222
  }
7505
8223
 
@@ -7780,575 +8498,141 @@ function parseMatch(ctx, token) {
7780
8498
  ctx.advance();
7781
8499
  guard = ctx.parseExpression();
7782
8500
  }
7783
- assertReservedSymbolToken(ctx.tryPeek(), 'then');
7784
- ctx.advance();
7785
- const thenExpression = parseImplicitBlock(ctx, ['case', 'end']);
7786
- params.push([pattern, thenExpression, guard]);
7787
- if (isReservedSymbolToken(ctx.tryPeek(), 'end')) {
7788
- break;
7789
- }
7790
- }
7791
- assertReservedSymbolToken(ctx.tryPeek(), 'end');
7792
- ctx.advance();
7793
- return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.match, valueExpression, params]], token[2]);
7794
- }
7795
-
7796
- function createParserContext(tokenStream) {
7797
- const ctx = new ParserContext(tokenStream);
7798
- ctx.parseExpression = (precedence = 0) => parseExpression(ctx, precedence);
7799
- return ctx;
7800
- }
7801
- function parseExpression(ctx, precedence = 0) {
7802
- const token = ctx.tryPeek();
7803
- let left;
7804
- if (isSymbolToken(token)) {
7805
- switch (token[1]) {
7806
- case 'let':
7807
- return parseLet(ctx, token);
7808
- case 'if':
7809
- case 'unless':
7810
- left = parseIfOrUnless(ctx, token);
7811
- break;
7812
- case 'cond':
7813
- left = parseCond(ctx, token);
7814
- break;
7815
- case 'match':
7816
- left = parseMatch(ctx, token);
7817
- break;
7818
- case 'for':
7819
- case 'doseq':
7820
- left = parseForOrDoseq(ctx, token);
7821
- break;
7822
- case 'loop':
7823
- left = parseLoop(ctx, token);
7824
- break;
7825
- }
7826
- }
7827
- else if (isReservedSymbolToken(token, 'do')) {
7828
- left = parseDo(ctx)[0];
7829
- }
7830
- left ||= parseOperand(ctx);
7831
- let operator = ctx.tryPeek();
7832
- while (!isAtExpressionEnd(ctx)) {
7833
- if (isA_BinaryOperatorToken(operator)) {
7834
- const name = operator[1];
7835
- const newPrecedece = getPrecedence(name, operator[2]);
7836
- if (newPrecedece <= precedence
7837
- // ^ (exponentiation) is right associative
7838
- && !(newPrecedece === exponentiationPrecedence && precedence === exponentiationPrecedence)) {
7839
- break;
7840
- }
7841
- const symbol = specialExpressionTypes[name]
7842
- ? withSourceCodeInfo([NodeTypes.SpecialBuiltinSymbol, specialExpressionTypes[name]], operator[2])
7843
- : withSourceCodeInfo([NodeTypes.NormalBuiltinSymbol, normalExpressionTypes[name]], operator[2]);
7844
- ctx.advance();
7845
- const right = parseExpression(ctx, newPrecedece);
7846
- left = fromBinaryOperatorToNode(operator, symbol, left, right, operator[2]);
7847
- }
7848
- else if (isSymbolToken(operator)) {
7849
- if (!isFunctionOperator(operator[1])) {
7850
- break;
7851
- }
7852
- const newPrecedence = binaryFunctionalOperatorPrecedence;
7853
- if (newPrecedence <= precedence) {
7854
- break;
7855
- }
7856
- const operatorSymbol = parseSymbol(ctx);
7857
- const right = parseExpression(ctx, newPrecedence);
7858
- if (isSpecialBuiltinSymbolNode(operatorSymbol)) {
7859
- throw new DvalaError('Special expressions are not allowed in binary functional operators', operatorSymbol[2]);
7860
- }
7861
- left = createNamedNormalExpressionNode(operatorSymbol, [left, right], operator[2]);
7862
- }
7863
- else if (operator?.[1] === '?') {
7864
- if (conditionalOperatorPrecedence <= precedence) {
7865
- break;
7866
- }
7867
- ctx.advance();
7868
- const trueNode = parseExpression(ctx);
7869
- if (!isOperatorToken(ctx.tryPeek(), ':')) {
7870
- throw new DvalaError('Expected :', ctx.peekSourceCodeInfo());
7871
- }
7872
- ctx.advance();
7873
- const falseNode = parseExpression(ctx);
7874
- left = withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.if, [left, trueNode, falseNode]]], left[2]);
7875
- }
7876
- else {
7877
- break;
7878
- }
7879
- operator = ctx.tryPeek();
7880
- }
7881
- return left;
7882
- }
7883
-
7884
- function parse(tokenStream) {
7885
- tokenStream.tokens.forEach((token) => {
7886
- if (token[0] === 'Error') {
7887
- throw new DvalaError(token[3], token[2]);
7888
- }
7889
- });
7890
- const nodes = [];
7891
- const ctx = createParserContext(tokenStream);
7892
- while (!ctx.isAtEnd()) {
7893
- nodes.push(parseExpression(ctx, 0));
7894
- if (isOperatorToken(ctx.tryPeek(), ';')) {
7895
- ctx.advance();
7896
- }
7897
- else {
7898
- if (!ctx.isAtEnd()) {
7899
- throw new DvalaError('Expected ;', ctx.peekSourceCodeInfo());
7900
- }
7901
- }
7902
- }
7903
- return nodes;
7904
- }
7905
-
7906
- function minifyTokenStream(tokenStream, { removeWhiteSpace }) {
7907
- const tokens = tokenStream.tokens
7908
- .filter((token) => {
7909
- if (isSingleLineCommentToken(token)
7910
- || isMultiLineCommentToken(token)
7911
- || isShebangToken(token)
7912
- || (removeWhiteSpace && isWhitespaceToken(token))) {
7913
- return false;
7914
- }
7915
- return true;
7916
- });
7917
- return { ...tokenStream, tokens };
7918
- }
7919
-
7920
- const illegalSymbolCharacters = [
7921
- '(',
7922
- ')',
7923
- '[',
7924
- ']',
7925
- '{',
7926
- '}',
7927
- '\'',
7928
- '"',
7929
- '`',
7930
- ',',
7931
- '.',
7932
- ';',
7933
- ' ',
7934
- '\n',
7935
- '\r',
7936
- '\t',
7937
- ];
7938
- const illegalFirstSymbolCharacters = [
7939
- '0',
7940
- '1',
7941
- '2',
7942
- '3',
7943
- '4',
7944
- '5',
7945
- '6',
7946
- '7',
7947
- '8',
7948
- '9',
7949
- ...illegalSymbolCharacters,
7950
- ];
7951
- const illegalSymbolCharacterSet = new Set(illegalSymbolCharacters);
7952
- const illegalFirstSymbolCharacterSet = new Set(illegalFirstSymbolCharacters);
7953
- const whitespaceRegExp = /\s/;
7954
- const NO_MATCH = [0];
7955
- const tokenizeLParen = (input, position) => tokenizeToken('LParen', '(', input, position);
7956
- const tokenizeRParen = (input, position) => tokenizeToken('RParen', ')', input, position);
7957
- const tokenizeLBracket = (input, position) => tokenizeToken('LBracket', '[', input, position);
7958
- const tokenizeRBracket = (input, position) => tokenizeToken('RBracket', ']', input, position);
7959
- const tokenizeLBrace = (input, position) => tokenizeToken('LBrace', '{', input, position);
7960
- const tokenizeRBrace = (input, position) => tokenizeToken('RBrace', '}', input, position);
7961
- const tokenizeDocString = (input, position) => {
7962
- if (input[position] !== '"' || input[position + 1] !== '"' || input[position + 2] !== '"')
7963
- return NO_MATCH;
7964
- let value = '"""';
7965
- let length = 3;
7966
- let char = input[position + length];
7967
- let nextThreeChars = input.slice(position + length, position + length + 3);
7968
- let escaping = false;
7969
- while (char && (nextThreeChars !== '"""' || escaping)) {
7970
- length += 1;
7971
- if (escaping) {
7972
- escaping = false;
7973
- value += char;
7974
- }
7975
- else {
7976
- if (char === '\\') {
7977
- escaping = true;
7978
- }
7979
- value += char;
7980
- }
7981
- char = input[position + length];
7982
- nextThreeChars = input.slice(position + length, position + length + 3);
7983
- }
7984
- if (!char) {
7985
- return [length, ['Error', value, undefined, `Unclosed doc string at position ${position}`]];
7986
- }
7987
- value += '"""'; // closing quote
7988
- return [length + 3, ['DocString', value]];
7989
- };
7990
- const tokenizeString = (input, position) => {
7991
- if (input[position] !== '"')
7992
- return NO_MATCH;
7993
- let value = '"';
7994
- let length = 1;
7995
- let char = input[position + length];
7996
- let escaping = false;
7997
- while (char && (char !== '"' || escaping)) {
7998
- length += 1;
7999
- if (escaping) {
8000
- escaping = false;
8001
- value += char;
8002
- }
8003
- else {
8004
- if (char === '\\') {
8005
- escaping = true;
8006
- }
8007
- value += char;
8008
- }
8009
- char = input[position + length];
8010
- }
8011
- if (!char) {
8012
- return [length, ['Error', value, undefined, `Unclosed string at position ${position}`]];
8013
- }
8014
- value += '"'; // closing quote
8015
- return [length + 1, ['string', value]];
8016
- };
8017
- const tokenizeRegexpShorthand = (input, position) => {
8018
- if (input[position] !== '#')
8019
- return NO_MATCH;
8020
- const [stringLength, token] = tokenizeString(input, position + 1);
8021
- if (!token)
8022
- return NO_MATCH;
8023
- if (token[0] === 'Error') {
8024
- const errorToken = ['Error', `#${token[1]}`, undefined, `Unclosed regexp at position ${position}`];
8025
- return [stringLength + 1, errorToken];
8026
- }
8027
- position += stringLength + 1;
8028
- let length = stringLength + 1;
8029
- let options = '';
8030
- while (input[position] === 'g' || input[position] === 'i') {
8031
- options += input[position];
8032
- length += 1;
8033
- position += 1;
8034
- if (options.includes(input[position])) {
8035
- return [length, ['Error', `#${token[1]}${options}`, undefined, `Duplicated regexp option "${input[position]}"`]];
8036
- }
8037
- }
8038
- return [length, ['RegexpShorthand', `#${token[1]}${options}`]];
8039
- };
8040
- function tokenizeToken(type, value, input, position) {
8041
- if (value === input.slice(position, position + value.length))
8042
- return [value.length, [type, value]];
8043
- else
8044
- return NO_MATCH;
8045
- }
8046
- const tokenizeWhitespace = (input, position) => {
8047
- let char = input[position];
8048
- if (!char || !whitespaceRegExp.test(char)) {
8049
- return NO_MATCH;
8050
- }
8051
- let value = char;
8052
- position += 1;
8053
- char = input[position];
8054
- while (char && whitespaceRegExp.test(char)) {
8055
- value += char;
8056
- position += 1;
8057
- char = input[position];
8058
- }
8059
- return [value.length, ['Whitespace', value]];
8060
- };
8061
- const decimalNumberRegExp = /\d/;
8062
- const octalNumberRegExp = /[0-7]/;
8063
- const hexNumberRegExp = /[0-9a-f]/i;
8064
- const binaryNumberRegExp = /[01]/;
8065
- const postNumberRegExp = /[\s)\]}(,;]/;
8066
- const tokenizeNumber = (input, position) => {
8067
- let i;
8068
- const negate = input[position] === '-';
8069
- const plusPrefix = input[position] === '+';
8070
- const start = negate || plusPrefix ? position + 1 : position;
8071
- let hasDecimalPoint = false;
8072
- let hasExponent = false;
8073
- for (i = start; i < input.length; i += 1) {
8074
- const char = input[i];
8075
- if (char === '_') {
8076
- if (!decimalNumberRegExp.test(input[i - 1]) || !decimalNumberRegExp.test(input[i + 1])) {
8077
- if (i === start) {
8078
- return NO_MATCH;
8079
- }
8080
- return [i - position + 1, ['Error', input.substring(position, i + 1), undefined, `Invalid number format at position ${i + 1}`]];
8081
- }
8082
- }
8083
- else if (char === '.') {
8084
- if (i === start) {
8085
- return NO_MATCH;
8086
- }
8087
- if (hasDecimalPoint || hasExponent) {
8088
- return [i - position + 1, ['Error', input.substring(position, i + 1), undefined, `Invalid number format at position ${i + 1}`]];
8089
- }
8090
- hasDecimalPoint = true;
8091
- }
8092
- else if (char === 'e' || char === 'E') {
8093
- if (i === start) {
8094
- return NO_MATCH;
8095
- }
8096
- if (hasExponent) {
8097
- return [i - position + 1, ['Error', input.substring(position, i + 1), undefined, `Invalid number format at position ${i + 1}`]];
8098
- }
8099
- if (input[i - 1] === '.' || input[i - 1] === '+' || input[i - 1] === '-') {
8100
- return [i - position + 1, ['Error', input.substring(position, i + 1), undefined, `Invalid number format at position ${i + 1}`]];
8101
- }
8102
- if (input[i + 1] === '+' || input[i + 1] === '-') {
8103
- i += 1;
8104
- }
8105
- hasExponent = true;
8106
- }
8107
- else if (!decimalNumberRegExp.test(char)) {
8108
- break;
8109
- }
8110
- }
8111
- if ((negate || plusPrefix) && i === start) {
8112
- return NO_MATCH;
8113
- }
8114
- const length = i - position;
8115
- if (length === 0) {
8116
- return NO_MATCH;
8117
- }
8118
- const nextChar = input[i];
8119
- if (nextChar && nextChar !== ':' && !postNumberRegExp.test(nextChar)) {
8120
- return [i - position + 1, ['Error', input.substring(position, i + 1), undefined, `Invalid number format at position ${i + 1}`]];
8121
- }
8122
- return [length, ['Number', input.substring(position, i)]];
8123
- };
8124
- const tokenizeBasePrefixedNumber = (input, position) => {
8125
- if (input[position] !== '0') {
8126
- return NO_MATCH;
8127
- }
8128
- const baseChar = input[position + 1];
8129
- const type = baseChar === 'b' || baseChar === 'B'
8130
- ? 'binary'
8131
- : baseChar === 'o' || baseChar === 'O'
8132
- ? 'octal'
8133
- : baseChar === 'x' || baseChar === 'X'
8134
- ? 'hex'
8135
- : null;
8136
- if (type === null) {
8137
- return NO_MATCH;
8138
- }
8139
- let i;
8140
- for (i = position + 2; i < input.length; i += 1) {
8141
- const char = input[i];
8142
- if (type === 'binary' && !binaryNumberRegExp.test(char)) {
8143
- break;
8144
- }
8145
- if (type === 'octal' && !octalNumberRegExp.test(char)) {
8146
- break;
8147
- }
8148
- if (type === 'hex' && !hexNumberRegExp.test(char)) {
8501
+ assertReservedSymbolToken(ctx.tryPeek(), 'then');
8502
+ ctx.advance();
8503
+ const thenExpression = parseImplicitBlock(ctx, ['case', 'end']);
8504
+ params.push([pattern, thenExpression, guard]);
8505
+ if (isReservedSymbolToken(ctx.tryPeek(), 'end')) {
8149
8506
  break;
8150
8507
  }
8151
8508
  }
8152
- const length = i - position;
8153
- if (length <= 2) {
8154
- return NO_MATCH;
8509
+ assertReservedSymbolToken(ctx.tryPeek(), 'end');
8510
+ ctx.advance();
8511
+ return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.match, valueExpression, params]], token[2]);
8512
+ }
8513
+
8514
+ function createParserContext(tokenStream) {
8515
+ const ctx = new ParserContext(tokenStream);
8516
+ ctx.parseExpression = (precedence = 0) => parseExpression(ctx, precedence);
8517
+ return ctx;
8518
+ }
8519
+ function parseExpression(ctx, precedence = 0) {
8520
+ const token = ctx.tryPeek();
8521
+ let left;
8522
+ if (isSymbolToken(token)) {
8523
+ switch (token[1]) {
8524
+ case 'let':
8525
+ return parseLet(ctx, token);
8526
+ case 'if':
8527
+ case 'unless':
8528
+ left = parseIfOrUnless(ctx, token);
8529
+ break;
8530
+ case 'cond':
8531
+ left = parseCond(ctx, token);
8532
+ break;
8533
+ case 'match':
8534
+ left = parseMatch(ctx, token);
8535
+ break;
8536
+ case 'for':
8537
+ case 'doseq':
8538
+ left = parseForOrDoseq(ctx, token);
8539
+ break;
8540
+ case 'loop':
8541
+ left = parseLoop(ctx, token);
8542
+ break;
8543
+ }
8155
8544
  }
8156
- const nextChar = input[i];
8157
- if (nextChar && !postNumberRegExp.test(nextChar)) {
8158
- return NO_MATCH;
8545
+ else if (isReservedSymbolToken(token, 'do')) {
8546
+ left = parseDo(ctx);
8159
8547
  }
8160
- return [length, ['BasePrefixedNumber', input.substring(position, i)]];
8161
- };
8162
- const tokenizeSymbol = (input, position) => {
8163
- let value = input[position];
8164
- if (value === '\'') {
8165
- let length = 1;
8166
- let char = input[position + length];
8167
- let escaping = false;
8168
- while (char !== '\'' || escaping) {
8169
- if (char === undefined)
8170
- return [length, ['Error', value, undefined, `Unclosed quoted symbol at position ${position}`]];
8171
- length += 1;
8172
- if (escaping) {
8173
- escaping = false;
8174
- value += char;
8175
- }
8176
- else {
8177
- if (char === '\\') {
8178
- escaping = true;
8179
- }
8180
- value += char;
8548
+ left ||= parseOperand(ctx);
8549
+ let operator = ctx.tryPeek();
8550
+ while (!isAtExpressionEnd(ctx)) {
8551
+ if (isA_BinaryOperatorToken(operator)) {
8552
+ const name = operator[1];
8553
+ const newPrecedece = getPrecedence(name, operator[2]);
8554
+ if (newPrecedece <= precedence
8555
+ // ^ (exponentiation) is right associative
8556
+ && !(newPrecedece === exponentiationPrecedence && precedence === exponentiationPrecedence)) {
8557
+ break;
8181
8558
  }
8182
- char = input[position + length];
8559
+ const symbol = specialExpressionTypes[name]
8560
+ ? withSourceCodeInfo([NodeTypes.SpecialBuiltinSymbol, specialExpressionTypes[name]], operator[2])
8561
+ : withSourceCodeInfo([NodeTypes.NormalBuiltinSymbol, normalExpressionTypes[name]], operator[2]);
8562
+ ctx.advance();
8563
+ const right = parseExpression(ctx, newPrecedece);
8564
+ left = fromBinaryOperatorToNode(operator, symbol, left, right, operator[2]);
8183
8565
  }
8184
- value += '\''; // closing quote
8185
- return [length + 1, ['Symbol', value]];
8186
- }
8187
- if (!illegalFirstSymbolCharacterSet.has(value)) {
8188
- const initialPosition = position;
8189
- position += 1;
8190
- let char = input[position];
8191
- while (char && !illegalSymbolCharacterSet.has(char)) {
8192
- value += char;
8193
- position += 1;
8194
- char = input[position];
8566
+ else if (isSymbolToken(operator)) {
8567
+ if (!isFunctionOperator(operator[1])) {
8568
+ break;
8569
+ }
8570
+ const newPrecedence = binaryFunctionalOperatorPrecedence;
8571
+ if (newPrecedence <= precedence) {
8572
+ break;
8573
+ }
8574
+ const operatorSymbol = parseSymbol(ctx);
8575
+ const right = parseExpression(ctx, newPrecedence);
8576
+ if (isSpecialBuiltinSymbolNode(operatorSymbol)) {
8577
+ throw new DvalaError('Special expressions are not allowed in binary functional operators', operatorSymbol[2]);
8578
+ }
8579
+ left = createNamedNormalExpressionNode(operatorSymbol, [left, right], operator[2]);
8195
8580
  }
8196
- // : can be used as symbol character, but it must not be the last character
8197
- return value.endsWith(':')
8198
- ? [position - initialPosition - 1, ['Symbol', value.slice(0, -1)]]
8199
- : [position - initialPosition, ['Symbol', value]];
8200
- }
8201
- return NO_MATCH;
8202
- };
8203
- const tokenizeReservedSymbolToken = (input, position) => {
8204
- const symbolMeta = tokenizeSymbol(input, position);
8205
- if (symbolMeta[0] === 0 || !symbolMeta[1]) {
8206
- return NO_MATCH;
8207
- }
8208
- let symbolName = symbolMeta[1][1];
8209
- symbolName = symbolName.startsWith('\'') ? symbolName.slice(1, symbolName.length - 1) : symbolName;
8210
- const info = reservedSymbolRecord[symbolName];
8211
- if (info === undefined) {
8212
- return NO_MATCH;
8213
- }
8214
- return [symbolMeta[0], ['ReservedSymbol', symbolName]];
8215
- };
8216
- const tokenizeOperator = (input, position) => {
8217
- const threeChars = input.slice(position, position + 3);
8218
- if (position + 2 < input.length && isSymbolicOperator(threeChars)) {
8219
- return [3, ['Operator', threeChars]];
8220
- }
8221
- const twoChars = input.slice(position, position + 2);
8222
- if (position + 1 < input.length && isSymbolicOperator(twoChars)) {
8223
- return [2, ['Operator', twoChars]];
8224
- }
8225
- const oneChar = input[position] ?? '';
8226
- if (isSymbolicOperator(oneChar)) {
8227
- return [1, ['Operator', oneChar]];
8228
- }
8229
- return NO_MATCH;
8230
- };
8231
- const tokenizeMultiLineComment = (input, position) => {
8232
- if (input[position] === '/' && input[position + 1] === '*') {
8233
- let length = 2;
8234
- let value = '/*';
8235
- while ((input[position + length] !== '*' || input[position + length + 1] !== '/') && position + length + 1 < input.length) {
8236
- value += input[position + length];
8237
- length += 1;
8581
+ else if (operator?.[1] === '?') {
8582
+ if (conditionalOperatorPrecedence <= precedence) {
8583
+ break;
8584
+ }
8585
+ ctx.advance();
8586
+ const trueNode = parseExpression(ctx);
8587
+ if (!isOperatorToken(ctx.tryPeek(), ':')) {
8588
+ throw new DvalaError('Expected :', ctx.peekSourceCodeInfo());
8589
+ }
8590
+ ctx.advance();
8591
+ const falseNode = parseExpression(ctx);
8592
+ left = withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.if, [left, trueNode, falseNode]]], left[2]);
8238
8593
  }
8239
- if (position + length + 1 >= input.length) {
8240
- return [length, ['Error', value, undefined, `Unclosed multi-line comment at position ${position}`]];
8594
+ else {
8595
+ break;
8241
8596
  }
8242
- value += '*/';
8243
- length += 2;
8244
- return [length, ['MultiLineComment', value]];
8597
+ operator = ctx.tryPeek();
8245
8598
  }
8246
- return NO_MATCH;
8247
- };
8248
- const tokenizeShebang = (input, position) => {
8249
- if (input[position] === '#' && input[position + 1] === '!') {
8250
- let length = 2;
8251
- let value = '#!';
8252
- while (input[position + length] !== '\n' && position + length < input.length) {
8253
- value += input[position + length];
8254
- length += 1;
8599
+ return left;
8600
+ }
8601
+
8602
+ function parse(tokenStream) {
8603
+ tokenStream.tokens.forEach((token) => {
8604
+ if (token[0] === 'Error') {
8605
+ throw new DvalaError(token[3], token[2]);
8255
8606
  }
8256
- return [length, ['SingleLineComment', value]];
8257
- }
8258
- return NO_MATCH;
8259
- };
8260
- const tokenizeSingleLineComment = (input, position) => {
8261
- if (input[position] === '/' && input[position + 1] === '/') {
8262
- let length = 2;
8263
- let value = '//';
8264
- while (input[position + length] !== '\n' && position + length < input.length) {
8265
- value += input[position + length];
8266
- length += 1;
8607
+ });
8608
+ const nodes = [];
8609
+ const ctx = createParserContext(tokenStream);
8610
+ while (!ctx.isAtEnd()) {
8611
+ nodes.push(parseExpression(ctx, 0));
8612
+ if (isOperatorToken(ctx.tryPeek(), ';')) {
8613
+ ctx.advance();
8267
8614
  }
8268
- return [length, ['SingleLineComment', value]];
8269
- }
8270
- return NO_MATCH;
8271
- };
8272
- // All tokenizers, order matters!
8273
- const tokenizers = [
8274
- tokenizeWhitespace,
8275
- tokenizeMultiLineComment,
8276
- tokenizeSingleLineComment,
8277
- tokenizeReservedSymbolToken,
8278
- tokenizeLParen,
8279
- tokenizeRParen,
8280
- tokenizeLBracket,
8281
- tokenizeRBracket,
8282
- tokenizeLBrace,
8283
- tokenizeRBrace,
8284
- tokenizeDocString,
8285
- tokenizeString,
8286
- tokenizeRegexpShorthand,
8287
- tokenizeBasePrefixedNumber,
8288
- tokenizeNumber,
8289
- tokenizeOperator,
8290
- tokenizeSymbol,
8291
- ];
8292
-
8293
- function tokenize(input, debug, filePath) {
8294
- let position = 0;
8295
- const tokenStream = {
8296
- tokens: [],
8297
- filePath,
8298
- hasDebugData: debug,
8299
- };
8300
- while (position < input.length) {
8301
- const sourceCodeInfo = debug
8302
- ? createSourceCodeInfo(input, position, filePath)
8303
- : undefined;
8304
- const tokenDescriptor = getCurrentToken(input, position);
8305
- const [count, token] = tokenDescriptor;
8306
- position += count;
8307
- if (token) {
8308
- if (sourceCodeInfo) {
8309
- token[2] = sourceCodeInfo;
8615
+ else {
8616
+ if (!ctx.isAtEnd()) {
8617
+ throw new DvalaError('Expected ;', ctx.peekSourceCodeInfo());
8310
8618
  }
8311
- tokenStream.tokens.push(token);
8312
8619
  }
8313
8620
  }
8314
- return tokenStream;
8315
- }
8316
- function getSourceCodeLine(input, lineNbr) {
8317
- return input.split(/\r\n|\r|\n/)[lineNbr];
8318
- }
8319
- function createSourceCodeInfo(input, position, filePath) {
8320
- const lines = input.substring(0, position + 1).split(/\r\n|\r|\n/);
8321
- const lastLine = lines[lines.length - 1];
8322
- const code = getSourceCodeLine(input, lines.length - 1);
8323
- const line = lines.length;
8324
- const column = lastLine.length;
8325
- return {
8326
- code,
8327
- position: {
8328
- line,
8329
- column,
8330
- },
8331
- filePath,
8332
- };
8621
+ return nodes;
8333
8622
  }
8334
- function getCurrentToken(input, position) {
8335
- const initialPosition = position;
8336
- if (position === 0) {
8337
- const [nbrOfCharacters, token] = tokenizeShebang(input, position);
8338
- position += nbrOfCharacters;
8339
- if (nbrOfCharacters > 0) {
8340
- return [position - initialPosition, token];
8341
- }
8342
- }
8343
- for (const tokenizer of tokenizers) {
8344
- const [nbrOfCharacters, token] = tokenizer(input, position);
8345
- position += nbrOfCharacters;
8346
- if (nbrOfCharacters === 0) {
8347
- continue;
8623
+
8624
+ function minifyTokenStream(tokenStream, { removeWhiteSpace }) {
8625
+ const tokens = tokenStream.tokens
8626
+ .filter((token) => {
8627
+ if (isSingleLineCommentToken(token)
8628
+ || isMultiLineCommentToken(token)
8629
+ || isShebangToken(token)
8630
+ || (removeWhiteSpace && isWhitespaceToken(token))) {
8631
+ return false;
8348
8632
  }
8349
- return [position - initialPosition, token];
8350
- }
8351
- return [1, ['Error', input[initialPosition], undefined, 'Unrecognized character']];
8633
+ return true;
8634
+ });
8635
+ return { ...tokenStream, tokens };
8352
8636
  }
8353
8637
 
8354
8638
  /**
@@ -8397,9 +8681,8 @@ function effectNameMatchesPattern(effectName, pattern) {
8397
8681
  return effectName === pattern;
8398
8682
  }
8399
8683
  /**
8400
- * Find all matching handlers for an effect name, in registration order.
8401
- * Returns an array of `[pattern, handler]` pairs — the first entry is
8402
- * the "most specific" by registration order, not by pattern specificity.
8684
+ * Find all matching async handlers for an effect name, in registration order.
8685
+ * Returns an array of `[pattern, handler]` pairs.
8403
8686
  */
8404
8687
  function findMatchingHandlers(effectName, handlers) {
8405
8688
  if (!handlers) {
@@ -8425,6 +8708,8 @@ class SuspensionSignal {
8425
8708
  snapshots;
8426
8709
  nextSnapshotIndex;
8427
8710
  meta;
8711
+ effectName;
8712
+ effectArgs;
8428
8713
  _brand = 'SuspensionSignal';
8429
8714
  constructor(
8430
8715
  /** The captured continuation stack at the point of suspension. */
@@ -8434,11 +8719,17 @@ class SuspensionSignal {
8434
8719
  /** High-water mark for snapshot indices at the point of suspension. */
8435
8720
  nextSnapshotIndex,
8436
8721
  /** Optional domain metadata passed through to RunResult. */
8437
- meta) {
8722
+ meta,
8723
+ /** The effect name being handled when suspend() was called. */
8724
+ effectName,
8725
+ /** The effect arguments being handled when suspend() was called. */
8726
+ effectArgs) {
8438
8727
  this.k = k;
8439
8728
  this.snapshots = snapshots;
8440
8729
  this.nextSnapshotIndex = nextSnapshotIndex;
8441
8730
  this.meta = meta;
8731
+ this.effectName = effectName;
8732
+ this.effectArgs = effectArgs;
8442
8733
  }
8443
8734
  }
8444
8735
  function isSuspensionSignal(value) {
@@ -8508,217 +8799,6 @@ function getEffectRef(name) {
8508
8799
  return ref;
8509
8800
  }
8510
8801
 
8511
- function isContextEntry(value) {
8512
- return isUnknownRecord(value) && value.value !== undefined;
8513
- }
8514
-
8515
- class ContextStackImpl {
8516
- _contexts;
8517
- globalContext;
8518
- values;
8519
- modules;
8520
- valueModules;
8521
- pure;
8522
- constructor({ contexts, values: hostValues, modules, valueModules, pure, }) {
8523
- this.globalContext = asNonUndefined(contexts[0]);
8524
- this._contexts = contexts;
8525
- this.values = hostValues;
8526
- this.modules = modules ?? new Map();
8527
- this.valueModules = valueModules ?? new Map();
8528
- this.pure = pure ?? false;
8529
- }
8530
- // -- Serialization support (Phase 4) --
8531
- /** Get the raw context chain for serialization. */
8532
- getContextsRaw() {
8533
- return this._contexts;
8534
- }
8535
- /** Get host values (plain bindings passed at creation). */
8536
- getHostValues() {
8537
- return this.values;
8538
- }
8539
- /**
8540
- * Find the index of globalContext in the _contexts array.
8541
- * Returns -1 if not found (should not happen in valid state).
8542
- */
8543
- getGlobalContextIndex() {
8544
- return this._contexts.indexOf(this.globalContext);
8545
- }
8546
- /**
8547
- * Create a ContextStack from deserialized data.
8548
- * `contexts` is the restored context chain (already resolved).
8549
- * `globalContextIndex` identifies which element is the globalContext.
8550
- * Host bindings (`values`, `modules`) come from resume options.
8551
- */
8552
- static fromDeserialized(params) {
8553
- const cs = new ContextStackImpl({
8554
- contexts: params.contexts,
8555
- values: params.values,
8556
- modules: params.modules,
8557
- pure: params.pure,
8558
- });
8559
- if (params.globalContextIndex >= 0 && params.globalContextIndex < params.contexts.length) {
8560
- cs.globalContext = params.contexts[params.globalContextIndex];
8561
- }
8562
- return cs;
8563
- }
8564
- /**
8565
- * Replace the contexts array and globalContext. Used during deserialization
8566
- * to fill in resolved context data after circular references are handled.
8567
- */
8568
- setContextsFromDeserialized(contexts, globalContextIndex) {
8569
- this._contexts = contexts;
8570
- if (globalContextIndex >= 0 && globalContextIndex < contexts.length) {
8571
- this.globalContext = contexts[globalContextIndex];
8572
- }
8573
- }
8574
- getModule(name) {
8575
- return this.modules.get(name);
8576
- }
8577
- getValueModule(name) {
8578
- if (this.valueModules.has(name)) {
8579
- return { value: this.valueModules.get(name), found: true };
8580
- }
8581
- return { value: undefined, found: false };
8582
- }
8583
- registerValueModule(name, value) {
8584
- this.valueModules.set(name, value);
8585
- }
8586
- create(context) {
8587
- const globalContext = this.globalContext;
8588
- const contextStack = new ContextStackImpl({
8589
- contexts: [context, ...this._contexts],
8590
- values: this.values,
8591
- modules: this.modules,
8592
- valueModules: this.valueModules,
8593
- pure: this.pure,
8594
- });
8595
- contextStack.globalContext = globalContext;
8596
- return contextStack;
8597
- }
8598
- new(context) {
8599
- const contexts = [{}, context];
8600
- return new ContextStackImpl({ contexts, modules: this.modules, valueModules: this.valueModules, pure: this.pure });
8601
- }
8602
- addValues(values, sourceCodeInfo) {
8603
- const currentContext = this._contexts[0];
8604
- for (const [name, value] of Object.entries(values)) {
8605
- if (currentContext[name]) {
8606
- throw new DvalaError(`Cannot redefine value "${name}"`, sourceCodeInfo);
8607
- }
8608
- const shadowedName = getShadowedBuiltinName(name);
8609
- if (shadowedName) {
8610
- throw new DvalaError(`Cannot shadow ${shadowedName}`, sourceCodeInfo);
8611
- }
8612
- currentContext[name] = { value: toAny(value) };
8613
- }
8614
- }
8615
- getValue(name) {
8616
- for (const context of this._contexts) {
8617
- const contextEntry = context[name];
8618
- if (contextEntry)
8619
- return contextEntry.value;
8620
- }
8621
- return this.values?.[name];
8622
- }
8623
- lookUp(node) {
8624
- const value = node[1];
8625
- for (const context of this._contexts) {
8626
- const contextEntry = context[value];
8627
- if (contextEntry)
8628
- return contextEntry;
8629
- }
8630
- const hostValue = this.values?.[value];
8631
- if (hostValue !== undefined) {
8632
- return {
8633
- value: toAny(hostValue),
8634
- };
8635
- }
8636
- return null;
8637
- }
8638
- evaluateSymbol(node) {
8639
- if (isSpecialBuiltinSymbolNode(node)) {
8640
- const functionType = node[1];
8641
- switch (functionType) {
8642
- case specialExpressionTypes['&&']:
8643
- case specialExpressionTypes['||']:
8644
- case specialExpressionTypes.array:
8645
- case specialExpressionTypes.object:
8646
- case specialExpressionTypes['defined?']:
8647
- case specialExpressionTypes.recur:
8648
- case specialExpressionTypes['??']: {
8649
- const specialExpression = asNonUndefined(builtin.specialExpressions[functionType], node[2]);
8650
- return {
8651
- [FUNCTION_SYMBOL]: true,
8652
- functionType: 'SpecialBuiltin',
8653
- specialBuiltinSymbolType: functionType,
8654
- sourceCodeInfo: node[2],
8655
- arity: specialExpression.arity,
8656
- };
8657
- }
8658
- default:
8659
- throw new DvalaError(`Unknown special builtin symbol type: ${functionType}`, node[2]);
8660
- }
8661
- }
8662
- if (isNormalBuiltinSymbolNode(node)) {
8663
- const type = node[1];
8664
- const normalExpression = allNormalExpressions[type];
8665
- const name = normalExpression.name;
8666
- return {
8667
- [FUNCTION_SYMBOL]: true,
8668
- functionType: 'Builtin',
8669
- normalBuiltinSymbolType: type,
8670
- sourceCodeInfo: node[2],
8671
- arity: normalExpression.arity,
8672
- name,
8673
- };
8674
- }
8675
- const lookUpResult = this.lookUp(node);
8676
- if (isContextEntry(lookUpResult))
8677
- return lookUpResult.value;
8678
- throw new UndefinedSymbolError(node[1], node[2]);
8679
- }
8680
- }
8681
- function getShadowedBuiltinName(name) {
8682
- if (specialExpressionKeys.includes(name))
8683
- return `special expression "${name}"`;
8684
- if (normalExpressionKeys.includes(name))
8685
- return `builtin function "${name}"`;
8686
- if (name === 'self')
8687
- return `builtin value "${name}"`;
8688
- return null;
8689
- }
8690
- function assertNotShadowingBuiltin(name) {
8691
- const shadowedName = getShadowedBuiltinName(name);
8692
- if (shadowedName) {
8693
- throw new DvalaError(`Cannot shadow ${shadowedName}`, undefined);
8694
- }
8695
- }
8696
- function createContextStack(params = {}, modules, pure) {
8697
- const globalContext = params.globalContext ?? {};
8698
- // Contexts are checked from left to right
8699
- const contexts = params.contexts ? [globalContext, ...params.contexts] : [globalContext];
8700
- let hostValues;
8701
- if (params.bindings) {
8702
- for (const [identifier, entry] of Object.entries(params.bindings)) {
8703
- if (identifier.includes('.')) {
8704
- throw new DvalaError(`Dots are not allowed in binding keys: "${identifier}"`, undefined);
8705
- }
8706
- assertNotShadowingBuiltin(identifier);
8707
- if (!hostValues) {
8708
- hostValues = {};
8709
- }
8710
- hostValues[identifier] = entry;
8711
- }
8712
- }
8713
- const contextStack = new ContextStackImpl({
8714
- contexts,
8715
- values: hostValues,
8716
- modules,
8717
- pure,
8718
- });
8719
- return params.globalModuleScope ? contextStack : contextStack.create({});
8720
- }
8721
-
8722
8802
  /**
8723
8803
  * Content-addressable hashing for JSON-compatible value trees.
8724
8804
  *
@@ -10239,7 +10319,7 @@ function evaluateFunction(fn, contextStack) {
10239
10319
  });
10240
10320
  return ctx;
10241
10321
  }, {});
10242
- const undefinedSymbols = getUndefinedSymbols(fn[1], contextStack.new(context), builtin, evaluateNodeRecursive);
10322
+ const undefinedSymbols = getUndefinedSymbols$1(fn[1], contextStack.new(context), builtin, evaluateNodeRecursive);
10243
10323
  undefinedSymbols.forEach((name) => {
10244
10324
  const value = contextStack.getValue(name);
10245
10325
  if (isAny(value)) {
@@ -10642,7 +10722,6 @@ function stepSpecialExpression(node, env, k) {
10642
10722
  // --- lambda (fn / ->) ---
10643
10723
  case specialExpressionTypes['0_lambda']: {
10644
10724
  const fn = node[1][1];
10645
- const docString = (node[1][2] ?? '');
10646
10725
  const evaluatedFunc = evaluateFunction(fn, env);
10647
10726
  const min = evaluatedFunc[0].filter(arg => arg[0] !== bindingTargetTypes.rest && arg[1][1] === undefined).length;
10648
10727
  const max = evaluatedFunc[0].some(arg => arg[0] === bindingTargetTypes.rest) ? undefined : evaluatedFunc[0].length;
@@ -10654,7 +10733,7 @@ function stepSpecialExpression(node, env, k) {
10654
10733
  name: undefined,
10655
10734
  evaluatedfunction: evaluatedFunc,
10656
10735
  arity,
10657
- docString,
10736
+ docString: '',
10658
10737
  };
10659
10738
  return { type: 'Value', value: dvalaFunction, k };
10660
10739
  }
@@ -11812,129 +11891,161 @@ function dispatchPerform(effect, args, k, sourceCodeInfo, handlers, signal, snap
11812
11891
  * shape. If no more handlers remain after `next()`, the effect is unhandled.
11813
11892
  *
11814
11893
  * Each handler must call exactly one of `resume`, `suspend`, `fail`, or
11815
- * `next` before its promise resolves.
11894
+ * `next` before its promise resolves (async) or before returning (sync).
11816
11895
  *
11817
11896
  * - `resume(value)` — resolves with a `ValueStep` that continues evaluation.
11818
- * - `suspend(meta?)` — rejects with a `SuspensionSignal`.
11897
+ * - `suspend(meta?)` — throws a `SuspensionSignal`.
11819
11898
  * - `fail(msg?)` — produces an `ErrorStep` routed through `dvala.error`.
11820
11899
  * - `next()` — pass to the next matching handler in the chain.
11900
+ *
11901
+ * Handlers may return `void` (synchronous) or `Promise<void>` (async).
11902
+ * When all handlers in a chain are synchronous, this function returns
11903
+ * a `Step` synchronously, allowing use from the sync trampoline.
11821
11904
  */
11822
11905
  function dispatchHostHandler(effectName, matchingHandlers, args, k, signal, sourceCodeInfo, snapshotState) {
11823
11906
  const effectSignal = signal ?? new AbortController().signal;
11824
11907
  const argsArray = Array.from(args);
11908
+ // If the abort signal already fired before the handler was called, auto-suspend immediately.
11909
+ // This happens when a parallel group aborts (e.g. another branch suspended) before this
11910
+ // branch's dispatchHostHandler runs.
11911
+ if (effectSignal.aborted) {
11912
+ throwSuspension(k, undefined, effectName, argsArray);
11913
+ }
11914
+ function resolveOutcome(o, nextIndex) {
11915
+ switch (o.kind) {
11916
+ case 'step': return o.step;
11917
+ case 'asyncResume': return o.promise.then((v) => ({ type: 'Value', value: v, k }), (e) => ({ type: 'Error', error: e instanceof DvalaError ? e : new DvalaError(e instanceof Error ? e : `${e}`, sourceCodeInfo), k }));
11918
+ case 'throw': throw o.error;
11919
+ case 'next': return tryHandler(nextIndex);
11920
+ }
11921
+ }
11825
11922
  // Recursive helper: try handler at `index`, with `next()` advancing to `index + 1`.
11826
11923
  function tryHandler(index) {
11827
11924
  if (index >= matchingHandlers.length) {
11828
- // No more handlers — effect is unhandled.
11829
- // Return rejected Promises (not synchronous throws) so that
11830
- // next() can propagate via .then(resolve, reject).
11831
11925
  if (effectName === 'dvala.error') {
11832
11926
  const message = typeof argsArray[0] === 'string' ? argsArray[0] : String(argsArray[0] ?? 'Unknown error');
11833
- return Promise.reject(new UserDefinedError(message, sourceCodeInfo));
11927
+ throw new UserDefinedError(message, sourceCodeInfo);
11834
11928
  }
11835
- return Promise.reject(new DvalaError(`Unhandled effect: '${effectName}'`, sourceCodeInfo));
11929
+ // dvala.checkpoint resolves to null when all handlers call next() without resuming.
11930
+ if (effectName === 'dvala.checkpoint') {
11931
+ return { type: 'Value', value: null, k };
11932
+ }
11933
+ throw new DvalaError(`Unhandled effect: '${effectName}'`, sourceCodeInfo);
11836
11934
  }
11837
- const [, handler] = matchingHandlers[index];
11838
- return new Promise((resolve, reject) => {
11839
- let settled = false;
11840
- function assertNotSettled(operation) {
11841
- if (settled) {
11842
- throw new DvalaError(`Effect handler called ${operation}() after already calling another operation`, sourceCodeInfo);
11843
- }
11844
- settled = true;
11935
+ const [pattern, handler] = matchingHandlers[index];
11936
+ // Before trying a "*" catch-all, fall back to standard effects.
11937
+ if (pattern === '*') {
11938
+ const standardHandler = getStandardEffectHandler(effectName);
11939
+ if (standardHandler) {
11940
+ return standardHandler(args, k, sourceCodeInfo);
11845
11941
  }
11846
- const ctx = {
11847
- effectName,
11848
- args: argsArray,
11849
- signal: effectSignal,
11850
- resume: (value) => {
11851
- assertNotSettled('resume');
11852
- if (value instanceof Promise) {
11853
- value.then((v) => {
11854
- resolve({ type: 'Value', value: v, k });
11855
- }, (e) => {
11856
- resolve({
11857
- type: 'Error',
11858
- error: e instanceof DvalaError ? e : new DvalaError(e instanceof Error ? e : `${e}`, sourceCodeInfo),
11859
- k,
11860
- });
11861
- });
11862
- }
11863
- else {
11864
- resolve({ type: 'Value', value, k });
11865
- }
11866
- },
11867
- fail: (msg) => {
11868
- assertNotSettled('fail');
11869
- const errorMsg = msg ?? `Effect handler failed for '${effectName}'`;
11870
- resolve({
11871
- type: 'Error',
11872
- error: new DvalaError(errorMsg, sourceCodeInfo),
11873
- k,
11874
- });
11875
- },
11876
- suspend: (meta) => {
11877
- assertNotSettled('suspend');
11878
- reject(new SuspensionSignal(k, snapshotState ? snapshotState.snapshots : [], snapshotState ? snapshotState.nextSnapshotIndex : 0, meta));
11879
- },
11880
- next: () => {
11881
- assertNotSettled('next');
11882
- // Advance to the next handler in the chain.
11883
- tryHandler(index + 1).then(resolve, reject);
11884
- },
11885
- get snapshots() { return snapshotState ? [...snapshotState.snapshots] : []; },
11886
- checkpoint: (meta) => {
11887
- if (!snapshotState) {
11888
- throw new DvalaError('checkpoint is not available outside effect-enabled execution', sourceCodeInfo);
11889
- }
11890
- const continuation = serializeToObject(k);
11891
- const snapshot = {
11892
- continuation,
11893
- timestamp: Date.now(),
11894
- index: snapshotState.nextSnapshotIndex++,
11895
- runId: snapshotState.runId,
11896
- ...(meta !== undefined ? { meta } : {}),
11897
- };
11898
- snapshotState.snapshots.push(snapshot);
11899
- if (snapshotState.maxSnapshots !== undefined && snapshotState.snapshots.length > snapshotState.maxSnapshots) {
11900
- snapshotState.snapshots.shift();
11901
- }
11902
- return snapshot;
11903
- },
11904
- resumeFrom: (snapshot, value) => {
11905
- if (settled) {
11906
- throw new DvalaError('Effect handler called resumeFrom() after already calling another operation', sourceCodeInfo);
11907
- }
11908
- if (!snapshotState) {
11909
- throw new DvalaError('resumeFrom is not available outside effect-enabled execution', sourceCodeInfo);
11910
- }
11911
- const found = snapshotState.snapshots.find(s => s.index === snapshot.index && s.runId === snapshot.runId);
11912
- if (!found) {
11913
- throw new DvalaError(`Invalid snapshot: no snapshot with index ${snapshot.index} found in current run`, sourceCodeInfo);
11914
- }
11915
- settled = true;
11916
- reject(new ResumeFromSignal(found.continuation, value, found.index));
11917
- },
11918
- };
11919
- handler(ctx).catch((e) => {
11942
+ }
11943
+ let outcome;
11944
+ let settled = false;
11945
+ function assertNotSettled(operation) {
11946
+ if (settled) {
11947
+ throw new DvalaError(`Effect handler called ${operation}() after already calling another operation`, sourceCodeInfo);
11948
+ }
11949
+ settled = true;
11950
+ }
11951
+ const ctx = {
11952
+ effectName,
11953
+ args: argsArray,
11954
+ signal: effectSignal,
11955
+ resume: (value) => {
11956
+ assertNotSettled('resume');
11957
+ if (value instanceof Promise) {
11958
+ outcome = { kind: 'asyncResume', promise: value };
11959
+ }
11960
+ else {
11961
+ outcome = { kind: 'step', step: { type: 'Value', value, k } };
11962
+ }
11963
+ },
11964
+ fail: (msg) => {
11965
+ assertNotSettled('fail');
11966
+ const errorMsg = msg ?? `Effect handler failed for '${effectName}'`;
11967
+ outcome = { kind: 'step', step: { type: 'Error', error: new DvalaError(errorMsg, sourceCodeInfo), k } };
11968
+ },
11969
+ suspend: (meta) => {
11970
+ assertNotSettled('suspend');
11971
+ outcome = {
11972
+ kind: 'throw',
11973
+ error: new SuspensionSignal(k, snapshotState ? snapshotState.snapshots : [], snapshotState ? snapshotState.nextSnapshotIndex : 0, meta, effectName, argsArray),
11974
+ };
11975
+ },
11976
+ next: () => {
11977
+ assertNotSettled('next');
11978
+ outcome = { kind: 'next' };
11979
+ },
11980
+ get snapshots() { return snapshotState ? [...snapshotState.snapshots] : []; },
11981
+ checkpoint: (meta) => {
11982
+ if (!snapshotState) {
11983
+ throw new DvalaError('checkpoint is not available outside effect-enabled execution', sourceCodeInfo);
11984
+ }
11985
+ const continuation = serializeToObject(k);
11986
+ const snapshot = {
11987
+ continuation,
11988
+ timestamp: Date.now(),
11989
+ index: snapshotState.nextSnapshotIndex++,
11990
+ runId: snapshotState.runId,
11991
+ ...(meta !== undefined ? { meta } : {}),
11992
+ };
11993
+ snapshotState.snapshots.push(snapshot);
11994
+ if (snapshotState.maxSnapshots !== undefined && snapshotState.snapshots.length > snapshotState.maxSnapshots) {
11995
+ snapshotState.snapshots.shift();
11996
+ }
11997
+ return snapshot;
11998
+ },
11999
+ resumeFrom: (snapshot, value) => {
11920
12000
  if (settled) {
11921
- // Handler already resolved via resume/suspend/fail/next ignore late errors
11922
- return;
12001
+ throw new DvalaError('Effect handler called resumeFrom() after already calling another operation', sourceCodeInfo);
11923
12002
  }
11924
- settled = true;
11925
- if (isSuspensionSignal(e) || isResumeFromSignal(e)) {
11926
- reject(e);
12003
+ if (!snapshotState) {
12004
+ throw new DvalaError('resumeFrom is not available outside effect-enabled execution', sourceCodeInfo);
11927
12005
  }
11928
- else {
11929
- // Handler itself threw — produce an ErrorStep so tick() can route
11930
- // through dvala.error.
11931
- resolve({
11932
- type: 'Error',
11933
- error: e instanceof DvalaError ? e : new DvalaError(e instanceof Error ? e : `${e}`, sourceCodeInfo),
11934
- k,
11935
- });
12006
+ const found = snapshotState.snapshots.find(s => s.index === snapshot.index && s.runId === snapshot.runId);
12007
+ if (!found) {
12008
+ throw new DvalaError(`Invalid snapshot: no snapshot with index ${snapshot.index} found in current run`, sourceCodeInfo);
11936
12009
  }
11937
- });
12010
+ settled = true;
12011
+ outcome = { kind: 'throw', error: new ResumeFromSignal(found.continuation, value, found.index) };
12012
+ },
12013
+ };
12014
+ const handlerResult = handler(ctx);
12015
+ if (!(handlerResult instanceof Promise)) {
12016
+ // Synchronous handler — outcome must already be set
12017
+ if (!outcome) {
12018
+ throw new DvalaError(`Effect handler for '${effectName}' did not call resume(), fail(), suspend(), or next()`, sourceCodeInfo);
12019
+ }
12020
+ return resolveOutcome(outcome, index + 1);
12021
+ }
12022
+ // Async handler
12023
+ if (outcome) {
12024
+ // Handler settled synchronously before the async part
12025
+ handlerResult.catch(() => { }); // suppress unhandled rejection
12026
+ return resolveOutcome(outcome, index + 1);
12027
+ }
12028
+ // Not yet settled — wait for the handler's promise
12029
+ return handlerResult.then(() => {
12030
+ if (!outcome) {
12031
+ throw new DvalaError(`Effect handler for '${effectName}' did not call resume(), fail(), suspend(), or next()`, sourceCodeInfo);
12032
+ }
12033
+ return resolveOutcome(outcome, index + 1);
12034
+ }, (e) => {
12035
+ if (outcome) {
12036
+ // Already settled — return that result, ignore the rejection
12037
+ return resolveOutcome(outcome, index + 1);
12038
+ }
12039
+ if (isSuspensionSignal(e) || isResumeFromSignal(e)) {
12040
+ // eslint-disable-next-line ts/no-throw-literal -- SuspensionSignal/ResumeFromSignal is a signaling mechanism
12041
+ throw e;
12042
+ }
12043
+ const errorStep = {
12044
+ type: 'Error',
12045
+ error: e instanceof DvalaError ? e : new DvalaError(e instanceof Error ? e : `${e}`, sourceCodeInfo),
12046
+ k,
12047
+ };
12048
+ return errorStep;
11938
12049
  });
11939
12050
  }
11940
12051
  return tryHandler(0);
@@ -11946,9 +12057,20 @@ function dispatchHostHandler(effectName, matchingHandlers, args, k, signal, sour
11946
12057
  * Throw a SuspensionSignal. Factored out to a helper so ESLint's
11947
12058
  * `only-throw-literal` rule can be suppressed in one place.
11948
12059
  */
11949
- function throwSuspension(k, meta) {
12060
+ /** Combine two AbortSignals: aborts when either fires (or already aborted). */
12061
+ function combineSignals(a, b) {
12062
+ const controller = new AbortController();
12063
+ if (a.aborted || b.aborted) {
12064
+ controller.abort();
12065
+ return controller.signal;
12066
+ }
12067
+ a.addEventListener('abort', () => controller.abort(), { once: true });
12068
+ b.addEventListener('abort', () => controller.abort(), { once: true });
12069
+ return controller.signal;
12070
+ }
12071
+ function throwSuspension(k, meta, effectName, effectArgs) {
11950
12072
  // eslint-disable-next-line ts/no-throw-literal -- SuspensionSignal is a signaling mechanism, not an error
11951
- throw new SuspensionSignal(k, [], 0, meta);
12073
+ throw new SuspensionSignal(k, [], 0, meta, effectName, effectArgs);
11952
12074
  }
11953
12075
  /**
11954
12076
  * Run a single trampoline branch to completion with effect handler support.
@@ -11978,31 +12100,41 @@ async function runBranch(node, env, handlers, signal) {
11978
12100
  * but errors take priority)
11979
12101
  */
11980
12102
  async function executeParallelBranches(branches, env, k, handlers, signal) {
11981
- const effectSignal = signal ?? new AbortController().signal;
11982
- // Run all branches concurrently
11983
- const branchPromises = branches.map(branch => runBranch(branch, env, handlers, effectSignal));
12103
+ // AbortController for this parallel group — aborted when any branch suspends,
12104
+ // which signals remaining effect handlers to auto-suspend via ctx.signal.
12105
+ const parallelAbort = new AbortController();
12106
+ const effectSignal = signal
12107
+ ? combineSignals(signal, parallelAbort.signal)
12108
+ : parallelAbort.signal;
12109
+ // Run all branches concurrently; abort the group when a branch suspends
12110
+ const branchPromises = branches.map(async (branch, i) => {
12111
+ const result = await runBranch(branch, env, handlers, effectSignal);
12112
+ if (result.type === 'suspended') {
12113
+ parallelAbort.abort();
12114
+ }
12115
+ return { index: i, result };
12116
+ });
11984
12117
  const results = await Promise.allSettled(branchPromises);
11985
12118
  // Collect outcomes
11986
12119
  const completedBranches = [];
11987
12120
  const suspendedBranches = [];
11988
12121
  const errors = [];
11989
- for (let i = 0; i < results.length; i++) {
11990
- const result = results[i];
11991
- if (result.status === 'rejected') {
11992
- // runEffectLoop should never reject, but handle defensively
11993
- errors.push(new DvalaError(`${result.reason}`, undefined));
12122
+ for (const settled of results) {
12123
+ if (settled.status === 'rejected') {
12124
+ // branchPromises should never reject, but handle defensively
12125
+ errors.push(new DvalaError(`${settled.reason}`, undefined));
11994
12126
  }
11995
12127
  else {
11996
- const r = result.value;
11997
- switch (r.type) {
12128
+ const { index, result } = settled.value;
12129
+ switch (result.type) {
11998
12130
  case 'completed':
11999
- completedBranches.push({ index: i, value: r.value });
12131
+ completedBranches.push({ index, value: result.value });
12000
12132
  break;
12001
12133
  case 'suspended':
12002
- suspendedBranches.push({ index: i, snapshot: r.snapshot });
12134
+ suspendedBranches.push({ index, snapshot: result.snapshot });
12003
12135
  break;
12004
12136
  case 'error':
12005
- errors.push(r.error);
12137
+ errors.push(result.error);
12006
12138
  break;
12007
12139
  }
12008
12140
  }
@@ -12021,9 +12153,9 @@ async function executeParallelBranches(branches, env, k, handlers, signal) {
12021
12153
  suspendedBranches: suspendedBranches.slice(1), // remaining after the first
12022
12154
  };
12023
12155
  const resumeK = [parallelResumeFrame, ...k];
12024
- // Throw SuspensionSignal with the first suspended branch's meta
12156
+ // Throw SuspensionSignal with the first suspended branch's meta and effect info
12025
12157
  const firstSuspended = suspendedBranches[0];
12026
- return throwSuspension(resumeK, firstSuspended.snapshot.meta);
12158
+ return throwSuspension(resumeK, firstSuspended.snapshot.meta, firstSuspended.snapshot.effectName, firstSuspended.snapshot.effectArgs);
12027
12159
  }
12028
12160
  // All branches completed — build the result array in original order
12029
12161
  const resultArray = Array.from({ length: branches.length });
@@ -12176,7 +12308,7 @@ function handleParallelResume(step, _handlers, _signal) {
12176
12308
  suspendedBranches: remaining,
12177
12309
  };
12178
12310
  const resumeK = [parallelResumeFrame, ...k];
12179
- return throwSuspension(resumeK, nextSuspended.snapshot.meta);
12311
+ return throwSuspension(resumeK, nextSuspended.snapshot.meta, nextSuspended.snapshot.effectName, nextSuspended.snapshot.effectArgs);
12180
12312
  }
12181
12313
  // All branches now completed — build the result array in original order
12182
12314
  const resultArray = Array.from({ length: branchCount });
@@ -12441,7 +12573,7 @@ function tick(step, handlers, signal, snapshotState) {
12441
12573
  * Throws if any step produces a Promise (i.e., an async operation was
12442
12574
  * encountered in a synchronous context).
12443
12575
  */
12444
- function runSyncTrampoline(initial) {
12576
+ function runSyncTrampoline(initial, effectHandlers) {
12445
12577
  let step = initial;
12446
12578
  for (;;) {
12447
12579
  if (step instanceof Promise) {
@@ -12450,7 +12582,7 @@ function runSyncTrampoline(initial) {
12450
12582
  if (step.type === 'Value' && step.k.length === 0) {
12451
12583
  return step.value;
12452
12584
  }
12453
- step = tick(step);
12585
+ step = tick(step, effectHandlers);
12454
12586
  }
12455
12587
  }
12456
12588
  /**
@@ -12512,16 +12644,6 @@ function evaluate(ast, contextStack) {
12512
12644
  throw error;
12513
12645
  }
12514
12646
  }
12515
- /**
12516
- * Evaluate an AST using the async trampoline directly.
12517
- * Use this when the caller knows that async operations may be involved
12518
- * (e.g., from Dvala.async.run) to avoid the sync-first-then-retry pattern
12519
- * which can cause side effects to be executed twice.
12520
- */
12521
- function evaluateAsync(ast, contextStack) {
12522
- const initial = buildInitialStep(ast.body, contextStack);
12523
- return runAsyncTrampoline(initial);
12524
- }
12525
12647
  /**
12526
12648
  * Evaluate a single AST node using the trampoline.
12527
12649
  * Used as the `evaluateNode` callback passed to `getUndefinedSymbols`
@@ -12563,6 +12685,27 @@ async function evaluateWithEffects(ast, contextStack, handlers, maxSnapshots, de
12563
12685
  const initial = buildInitialStep(ast.body, contextStack);
12564
12686
  return runEffectLoop(initial, handlers, signal, undefined, maxSnapshots, deserializeOptions);
12565
12687
  }
12688
+ /**
12689
+ * Evaluate an AST synchronously with effect handler support.
12690
+ *
12691
+ * Uses the sync trampoline with `effectHandlers` threaded through `tick`.
12692
+ * Throws if an async operation is encountered (e.g., an async handler
12693
+ * is used). Handlers may call `resume(value)`, `fail(msg?)`, or `next()`.
12694
+ * Calling `suspend()` will throw a runtime error.
12695
+ */
12696
+ function evaluateWithSyncEffects(ast, contextStack, effectHandlers) {
12697
+ const initial = buildInitialStep(ast.body, contextStack);
12698
+ try {
12699
+ return runSyncTrampoline(initial, effectHandlers);
12700
+ }
12701
+ catch (error) {
12702
+ if (error instanceof DvalaError && error.message.includes('Unexpected async operation')) {
12703
+ const freshInitial = buildInitialStep(ast.body, contextStack);
12704
+ return runSyncTrampoline(freshInitial, effectHandlers);
12705
+ }
12706
+ throw error;
12707
+ }
12708
+ }
12566
12709
  /**
12567
12710
  * Shared effect trampoline loop used by both `evaluateWithEffects` and
12568
12711
  * `resumeWithEffects`. Runs the trampoline to completion, suspension, or error.
@@ -12580,7 +12723,7 @@ async function runEffectLoop(initial, handlers, signal, initialSnapshotState, ma
12580
12723
  snapshots: [],
12581
12724
  nextSnapshotIndex: 0,
12582
12725
  runId: generateRunId(),
12583
- ...({}),
12726
+ ...(maxSnapshots !== undefined ? { maxSnapshots } : {}),
12584
12727
  };
12585
12728
  let step = initial;
12586
12729
  for (;;) {
@@ -12628,6 +12771,8 @@ async function runEffectLoop(initial, handlers, signal, initialSnapshotState, ma
12628
12771
  index: snapshotState.nextSnapshotIndex++,
12629
12772
  runId: snapshotState.runId,
12630
12773
  meta: error.meta,
12774
+ effectName: error.effectName,
12775
+ effectArgs: error.effectArgs,
12631
12776
  };
12632
12777
  return { type: 'suspended', snapshot };
12633
12778
  }
@@ -12639,141 +12784,6 @@ async function runEffectLoop(initial, handlers, signal, initialSnapshotState, ma
12639
12784
  }
12640
12785
  }
12641
12786
 
12642
- function transformSymbolTokens(tokenStram, transformer) {
12643
- return {
12644
- ...tokenStram,
12645
- tokens: tokenStram.tokens.map(token => isSymbolToken(token)
12646
- ? [token[0], transformer(token[1])]
12647
- : token),
12648
- };
12649
- }
12650
-
12651
- function untokenize(tokenStream) {
12652
- return tokenStream.tokens.reduce((acc, token) => {
12653
- return `${acc}${token[1]}`;
12654
- }, '');
12655
- }
12656
-
12657
- const dvalaCommands = new Set([...normalExpressionKeys, ...specialExpressionKeys, ...Object.keys(reservedSymbolRecord)]);
12658
- // TODO: replace with get suggestions function
12659
- class AutoCompleter {
12660
- originalProgram;
12661
- originalPosition;
12662
- prefixProgram = '';
12663
- suffixProgram = '';
12664
- searchString = '';
12665
- suggestions = [];
12666
- suggestionIndex = null;
12667
- constructor(originalProgram, originalPosition, dvala, params) {
12668
- this.originalProgram = originalProgram;
12669
- this.originalPosition = originalPosition;
12670
- const partialProgram = this.originalProgram.slice(0, this.originalPosition);
12671
- const tokenStream = dvala.tokenize(partialProgram);
12672
- const lastToken = tokenStream.tokens.at(-1);
12673
- if (!lastToken) {
12674
- return;
12675
- }
12676
- if (lastToken[0] === 'Error') {
12677
- return;
12678
- }
12679
- this.searchString = lastToken[1];
12680
- this.prefixProgram = this.originalProgram.slice(0, this.originalPosition - this.searchString.length);
12681
- this.suffixProgram = this.originalProgram.slice(this.prefixProgram.length + this.searchString.length);
12682
- this.originalProgram.slice(this.prefixProgram.length + this.searchString.length);
12683
- this.suggestions = this.generateSuggestions(params);
12684
- }
12685
- getNextSuggestion() {
12686
- return this.getAutoCompleteSuggestionResult(this.getNextSuggestionSymbol());
12687
- }
12688
- getPreviousSuggestion() {
12689
- return this.getAutoCompleteSuggestionResult(this.getPreviousSuggestionSymbol());
12690
- }
12691
- getAutoCompleteSuggestionResult(suggestion) {
12692
- if (suggestion === null) {
12693
- return null;
12694
- }
12695
- return {
12696
- program: this.prefixProgram + suggestion + this.suffixProgram,
12697
- position: this.prefixProgram.length + suggestion.length,
12698
- };
12699
- }
12700
- getNextSuggestionSymbol() {
12701
- if (this.suggestions.length === 0) {
12702
- return null;
12703
- }
12704
- if (this.suggestionIndex === null) {
12705
- this.suggestionIndex = 0;
12706
- }
12707
- else {
12708
- this.suggestionIndex += 1;
12709
- if (this.suggestionIndex >= this.suggestions.length) {
12710
- this.suggestionIndex = 0;
12711
- }
12712
- }
12713
- return this.suggestions[this.suggestionIndex];
12714
- }
12715
- getPreviousSuggestionSymbol() {
12716
- if (this.suggestions.length === 0) {
12717
- return null;
12718
- }
12719
- if (this.suggestionIndex === null) {
12720
- this.suggestionIndex = this.suggestions.length - 1;
12721
- }
12722
- else {
12723
- this.suggestionIndex -= 1;
12724
- if (this.suggestionIndex < 0) {
12725
- this.suggestionIndex = this.suggestions.length - 1;
12726
- }
12727
- }
12728
- return this.suggestions[this.suggestionIndex];
12729
- }
12730
- getSuggestions() {
12731
- return [...this.suggestions];
12732
- }
12733
- getSearchString() {
12734
- return this.searchString;
12735
- }
12736
- generateSuggestions(params) {
12737
- const blacklist = new Set(['0_defn', '0_lambda']);
12738
- const startsWithCaseSensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.startsWith(this.searchString));
12739
- startsWithCaseSensitive.forEach(suggestion => blacklist.add(suggestion));
12740
- const startsWithCaseInsensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.toLowerCase().startsWith(this.searchString.toLowerCase()));
12741
- startsWithCaseInsensitive.forEach(suggestion => blacklist.add(suggestion));
12742
- const includesCaseSensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.includes(this.searchString));
12743
- includesCaseSensitive.forEach(suggestion => blacklist.add(suggestion));
12744
- const includesCaseInsensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.includes(this.searchString.toLowerCase()));
12745
- includesCaseInsensitive.forEach(suggestion => blacklist.add(suggestion));
12746
- return [...startsWithCaseSensitive, ...startsWithCaseInsensitive, ...includesCaseSensitive, ...includesCaseInsensitive];
12747
- }
12748
- generateWithPredicate(params, shouldInclude) {
12749
- const suggestions = new Set();
12750
- dvalaCommands.forEach((suggestion) => {
12751
- if (shouldInclude(suggestion)) {
12752
- suggestions.add(suggestion);
12753
- }
12754
- });
12755
- Object.keys(params.globalContext ?? {})
12756
- .filter(shouldInclude)
12757
- .forEach(suggestion => suggestions.add(suggestion));
12758
- params.contexts?.forEach((context) => {
12759
- Object.keys(context)
12760
- .filter(shouldInclude)
12761
- .forEach(suggestion => suggestions.add(suggestion));
12762
- });
12763
- Object.keys(params.bindings ?? {})
12764
- .filter(shouldInclude)
12765
- .forEach(suggestion => suggestions.add(suggestion));
12766
- return [...suggestions].sort((a, b) => a.localeCompare(b));
12767
- }
12768
- }
12769
-
12770
- function isDvalaBundle(value) {
12771
- return (typeof value === 'object'
12772
- && value !== null
12773
- && typeof value.program === 'string'
12774
- && Array.isArray(value.fileModules));
12775
- }
12776
-
12777
12787
  var collectionSource = "{\n map: (first-coll, ...args) -> do\n let fn = last(args);\n let other-colls = slice(args, 0, count(args) - 1);\n let all-colls = [first-coll, ...other-colls];\n\n cond\n case object?(first-coll) then do\n let expected-keys = keys(first-coll) |> sort;\n doseq (obj in other-colls) -> do\n if not(object?(obj)) then\n perform(effect(dvala.error), \"Expected object\")\n end;\n let obj-keys = keys(obj) |> sort;\n if not(obj-keys == expected-keys) then\n perform(effect(dvala.error), ++(\n \"All objects must have the same keys. Expected: \",\n join(expected-keys, \", \"),\n \". Found: \",\n join(obj-keys, \", \")\n ))\n end\n end;\n reduce(keys(first-coll), (acc, k) -> do\n let a = for (coll in all-colls) -> coll(k);\n assoc(acc, k, apply(fn, a))\n end, {})\n end\n\n case string?(first-coll) then do\n doseq (s in other-colls) -> do\n if not(string?(s)) then\n perform(effect(dvala.error), \"Expected string\")\n end\n end;\n let len = reduce(other-colls, (m, s) -> min(m, count(s)), count(first-coll));\n let mapped = for (i in range(len)) -> do\n let a = for (coll in all-colls) -> nth(coll, i);\n apply(fn, a)\n end;\n reduce(mapped, (acc, ch) -> do\n if not(string?(ch)) then\n perform(effect(dvala.error), \"Expected string\")\n end;\n ++(acc, ch)\n end, \"\")\n end\n\n case true then do\n doseq (x in other-colls) -> do\n if not(array?(x)) then\n perform(effect(dvala.error), \"Expected array\")\n end\n end;\n let len = reduce(other-colls, (m, x) -> min(m, count(x)), count(first-coll));\n for (i in range(len)) -> do\n let a = for (coll in all-colls) -> nth(coll, i);\n apply(fn, a)\n end\n end\n end\n end,\n\n filter: (coll, fn) -> do\n cond\n case array?(coll) then\n reduce(coll, (acc, elem) -> if fn(elem) then [...acc, elem] else acc end, [])\n\n case string?(coll) then\n reduce(coll, (acc, ch) -> if fn(ch) then ++(acc, ch) else acc end, \"\")\n\n case object?(coll) then\n reduce(keys(coll), (acc, k) -> do\n if fn(coll(k)) then\n assoc(acc, k, coll(k))\n else\n acc\n end\n end, {})\n\n case true then\n perform(effect(dvala.error), \"Expected collection\")\n end\n end,\n\n reduce: (coll, fn, initial) -> do\n cond\n case string?(coll) then\n loop (acc = initial, i = 0) -> do\n if i >= count(coll) then\n acc\n else\n recur(fn(acc, nth(coll, i)), i + 1)\n end\n end\n\n case array?(coll) then\n loop (acc = initial, i = 0) -> do\n if i >= count(coll) then\n acc\n else\n recur(fn(acc, nth(coll, i)), i + 1)\n end\n end\n\n case object?(coll) then do\n let values = vals(coll);\n loop (acc = initial, i = 0) -> do\n if i >= count(values) then\n acc\n else\n recur(fn(acc, nth(values, i)), i + 1)\n end\n end\n end\n\n case true then\n perform(effect(dvala.error), \"Expected collection\")\n end\n end\n}";
12778
12788
 
12779
12789
  var sequenceSource = "{\n some: (seq, fn) -> do\n if null?(seq) then null\n else do\n loop (i = 0) -> do\n if i >= count(seq) then\n null\n else do\n let elem = nth(seq, i);\n if fn(elem) then elem\n else recur(i + 1)\n end\n end\n end\n end\n end\n end\n end,\n\n take-while: (seq, fn) -> do\n let is-str = string?(seq);\n let len = count(seq);\n let idx = loop (i = 0) -> do\n if i >= len then\n len\n else if fn(nth(seq, i)) then\n recur(i + 1)\n else\n i\n end\n end\n end;\n slice(seq, 0, idx)\n end,\n\n drop-while: (seq, fn) -> do\n let is-str = string?(seq);\n let len = count(seq);\n let idx = loop (i = 0) -> do\n if i >= len then\n len\n else if fn(nth(seq, i)) then\n recur(i + 1)\n else\n i\n end\n end\n end;\n slice(seq, idx)\n end,\n\n sort: (seq, ...args) -> do\n let cmp = if count(args) == 0 then compare else first(args) end;\n let is-str = string?(seq);\n let arr = if is-str then split(seq, \"\") else seq end;\n let len = count(arr);\n\n // merge two sorted arrays\n let merge-arrays = (left, right) -> do\n let left-len = count(left);\n let right-len = count(right);\n loop (result = [], li = 0, ri = 0) -> do\n if li >= left-len then\n ++(result, slice(right, ri))\n else if ri >= right-len then\n ++(result, slice(left, li))\n else do\n let l = nth(left, li);\n let r = nth(right, ri);\n if cmp(l, r) <= 0 then\n recur(push(result, l), li + 1, ri)\n else\n recur(push(result, r), li, ri + 1)\n end\n end\n end\n end\n end\n end;\n\n // recursive merge-sort\n let merge-sort = (a) -> do\n let n = count(a);\n if n <= 1 then a\n else do\n let mid = floor(n / 2);\n let left = merge-sort(slice(a, 0, mid));\n let right = merge-sort(slice(a, mid));\n merge-arrays(left, right)\n end\n end\n end;\n\n let sorted = merge-sort(arr);\n if is-str then join(sorted, \"\") else sorted end\n end\n}\n";
@@ -12873,202 +12883,202 @@ class Cache {
12873
12883
  }
12874
12884
  }
12875
12885
 
12876
- class Dvala {
12877
- astCache;
12878
- astCacheSize;
12879
- debug;
12880
- modules;
12881
- constructor(config = {}) {
12882
- initCoreDvalaSources();
12883
- this.debug = config.debug ?? false;
12884
- this.astCacheSize = config.astCacheSize ?? null;
12885
- if (this.astCacheSize) {
12886
- this.astCache = new Cache(this.astCacheSize);
12887
- const initialCache = config.initialCache ?? {};
12888
- for (const cacheEntry of Object.keys(initialCache))
12889
- this.astCache.set(cacheEntry, initialCache[cacheEntry]);
12890
- }
12891
- else {
12892
- this.astCache = null;
12893
- }
12894
- const nsList = config.modules ?? [];
12895
- this.modules = new Map(nsList.map(ns => [ns.name, ns]));
12896
- }
12897
- getRuntimeInfo() {
12898
- return {
12899
- astCacheSize: this.astCacheSize,
12900
- astCache: this.astCache,
12901
- debug: this.debug,
12902
- };
12903
- }
12904
- async = {
12905
- run: async (programOrBundle, params = {}) => {
12906
- assertSerializableBindings(params.bindings);
12907
- if (isDvalaBundle(programOrBundle)) {
12908
- return this.runBundle(programOrBundle, params);
12909
- }
12910
- const ast = this.generateAst(programOrBundle, params);
12911
- if (params.handlers) {
12912
- const contextStack = createContextStack(params, this.modules, params.pure);
12913
- const result = await evaluateWithEffects(ast, contextStack, params.handlers);
12914
- if (result.type === 'completed')
12915
- return result.value;
12916
- if (result.type === 'error')
12917
- throw result.error;
12918
- throw new TypeError('Unexpected suspension in Dvala.async.run(). Use the standalone effects API for suspend/resume.');
12919
- }
12920
- return this.evaluateAsync(ast, params);
12921
- },
12922
- apply: async (fn, fnParams, params = {}) => {
12923
- return this.apply(fn, fnParams, params);
12924
- },
12925
- };
12926
- run(programOrBundle, params = {}) {
12927
- assertSerializableBindings(params.bindings);
12928
- if (isDvalaBundle(programOrBundle)) {
12929
- return this.runBundle(programOrBundle, params);
12930
- }
12931
- const ast = this.generateAst(programOrBundle, params);
12932
- const result = this.evaluate(ast, params);
12933
- if (result instanceof Promise) {
12934
- throw new TypeError('Unexpected async result in synchronous run(). Use dvala.async.run() for async operations.');
12935
- }
12936
- return result;
12937
- }
12938
- runBundle(bundle, params = {}) {
12939
- const contextStack = createContextStack(params, this.modules, params.pure);
12940
- // Evaluate file modules in dependency order and register as value modules.
12941
- // Each file module is evaluated in its own scope so local bindings don't leak.
12942
- // File modules are always evaluated in pure mode to ensure deterministic,
12943
- // side-effect-free initialization regardless of the caller's pure setting.
12944
- const savedPure = contextStack.pure;
12945
- contextStack.pure = true;
12946
- for (const [name, source] of bundle.fileModules) {
12947
- const ast = this.generateAst(source, params);
12948
- const moduleContextStack = contextStack.create({});
12949
- const result = evaluate(ast, moduleContextStack);
12950
- // TODO: When async functions in file modules are able to mark themselves as pure and
12951
- // are returning a Promise, uncomment the following check, and make sure a test is verifying the behaviour.
12952
- // if (result instanceof Promise) {
12953
- // throw new TypeError('Unexpected async result in synchronous runBundle(). Use dvala.async.run() for async operations.')
12954
- // }
12955
- contextStack.registerValueModule(name, result);
12956
- }
12957
- contextStack.pure = savedPure;
12958
- // Parse and evaluate the main program
12959
- const ast = this.generateAst(bundle.program, params);
12960
- const result = evaluate(ast, contextStack);
12961
- if (result instanceof Promise) {
12962
- throw new TypeError('Unexpected async result in synchronous runBundle(). Use dvala.async.run() for async operations.');
12963
- }
12964
- return result;
12965
- }
12966
- getUndefinedSymbols(programOrAst, params = {}) {
12967
- const ast = typeof programOrAst === 'string' ? this.generateAst(programOrAst, params) : programOrAst;
12968
- const contextStack = createContextStack(params, this.modules);
12969
- return getUndefinedSymbols(ast, contextStack, builtin, evaluateNode);
12970
- }
12971
- tokenize(program, tokenizeParams = {}) {
12972
- const tokenStream = tokenize(program, this.debug, tokenizeParams.filePath);
12973
- return tokenizeParams.minify ? minifyTokenStream(tokenStream, { removeWhiteSpace: false }) : tokenStream;
12974
- }
12975
- parse(tokenStream) {
12976
- tokenStream = minifyTokenStream(tokenStream, { removeWhiteSpace: true });
12977
- const ast = {
12978
- body: [],
12979
- hasDebugData: tokenStream.hasDebugData,
12980
- };
12981
- ast.body = parse(tokenStream);
12982
- return ast;
12983
- }
12984
- evaluate(ast, params) {
12985
- const contextStack = createContextStack(params, this.modules, params.pure);
12986
- return evaluate(ast, contextStack);
12987
- }
12988
- evaluateAsync(ast, params) {
12989
- const contextStack = createContextStack(params, this.modules, params.pure);
12990
- return evaluateAsync(ast, contextStack);
12991
- }
12992
- transformSymbols(tokenStream, transformer) {
12993
- return transformSymbolTokens(tokenStream, transformer);
12994
- }
12995
- untokenize(tokenStream) {
12996
- return untokenize(tokenStream);
12997
- }
12998
- apply(fn, fnParams, params = {}) {
12999
- const fnName = 'FN_2eb7b316_471c_5bfa_90cb_d3dfd9164a59';
13000
- const program = this.generateApplyFunctionCall(fnName, fnParams);
13001
- const ast = this.generateAst(program, params);
13002
- const hostValues = fnParams.reduce((result, param, index) => {
13003
- result[`${fnName}_${index}`] = param;
13004
- return result;
13005
- }, { [fnName]: fn });
13006
- params.bindings = { ...params.bindings, ...hostValues };
13007
- return this.evaluate(ast, params);
13008
- }
13009
- generateApplyFunctionCall(fnName, fnParams) {
13010
- const paramsString = fnParams
13011
- .map((_, index) => {
13012
- return `${fnName}_${index}`;
13013
- })
13014
- .join(', ');
13015
- return `${fnName}(${paramsString})`;
13016
- }
13017
- generateAst(program, params) {
13018
- if (this.astCache) {
13019
- const cachedAst = this.astCache.get(program);
13020
- if (cachedAst)
13021
- return cachedAst;
13022
- }
13023
- const tokenStream = this.tokenize(program, {
13024
- filePath: params.filePath,
13025
- });
13026
- const ast = this.parse(tokenStream);
13027
- this.astCache?.set(program, ast);
13028
- return ast;
13029
- }
13030
- getAutoCompleter(program, position, params = {}) {
13031
- return new AutoCompleter(program, position, this, params);
13032
- }
12886
+ function isDvalaBundle(value) {
12887
+ return (typeof value === 'object'
12888
+ && value !== null
12889
+ && typeof value.program === 'string'
12890
+ && Array.isArray(value.fileModules));
12891
+ }
12892
+
12893
+ /**
12894
+ * Standalone tooling functions for tokenizing, parsing, and analysis.
12895
+ *
12896
+ * These are thin wrappers around internal utilities that do not require
12897
+ * a Dvala instance.
12898
+ */
12899
+ /**
12900
+ * Get all undefined symbols in a Dvala program.
12901
+ *
12902
+ * @param source - Dvala source code
12903
+ * @param options - optional context to treat as defined
12904
+ * @param options.bindings - host bindings to treat as defined
12905
+ * @param options.modules - modules to treat as available
12906
+ */
12907
+ function getUndefinedSymbols(source, options) {
12908
+ const modulesMap = options?.modules
12909
+ ? new Map(options.modules.map(m => [m.name, m]))
12910
+ : undefined;
12911
+ const contextStack = createContextStack({ bindings: options?.bindings }, modulesMap);
12912
+ const tokenStream = tokenize(source, false, undefined);
12913
+ const minified = minifyTokenStream(tokenStream, { removeWhiteSpace: true });
12914
+ const ast = { body: parse(minified), hasDebugData: false };
12915
+ return getUndefinedSymbols$1(ast, contextStack, builtin, evaluateNode);
13033
12916
  }
12917
+
13034
12918
  function assertSerializableBindings(bindings) {
13035
12919
  if (!bindings)
13036
12920
  return;
13037
- for (const [key, value] of Object.entries(bindings)) {
13038
- assertSerializable(value, `bindings["${key}"]`);
13039
- }
12921
+ for (const [key, val] of Object.entries(bindings))
12922
+ assertSerializable(val, `bindings["${key}"]`);
13040
12923
  }
13041
- function assertSerializable(value, path) {
13042
- if (value === null || value === undefined)
12924
+ function assertSerializable(val, path) {
12925
+ if (val === null || val === undefined)
13043
12926
  return;
13044
- if (typeof value === 'boolean' || typeof value === 'string')
12927
+ if (typeof val === 'boolean' || typeof val === 'string')
13045
12928
  return;
13046
- if (typeof value === 'number') {
13047
- if (!Number.isFinite(value))
13048
- throw new TypeError(`${path} is not serializable (${value})`);
12929
+ if (typeof val === 'number') {
12930
+ if (!Number.isFinite(val))
12931
+ throw new TypeError(`${path} is not serializable (${val})`);
13049
12932
  return;
13050
12933
  }
13051
- if (typeof value === 'function')
12934
+ if (typeof val === 'function')
13052
12935
  throw new TypeError(`${path} is not serializable (function)`);
13053
- if (typeof value === 'object') {
13054
- // Dvala values (functions, regexps, effects) are valid
13055
- if (FUNCTION_SYMBOL in value) {
12936
+ if (typeof val === 'object') {
12937
+ if (FUNCTION_SYMBOL in val || REGEXP_SYMBOL in val || EFFECT_SYMBOL in val)
13056
12938
  return;
13057
- }
13058
- if (REGEXP_SYMBOL in value || EFFECT_SYMBOL in value)
13059
- return;
13060
- if (Array.isArray(value)) {
13061
- value.forEach((item, i) => assertSerializable(item, `${path}[${i}]`));
12939
+ if (Array.isArray(val)) {
12940
+ val.forEach((item, i) => assertSerializable(item, `${path}[${i}]`));
13062
12941
  return;
13063
12942
  }
13064
- if (Object.getPrototypeOf(value) !== Object.prototype)
12943
+ if (Object.getPrototypeOf(val) !== Object.prototype)
13065
12944
  throw new TypeError(`${path} is not serializable (not a plain object)`);
13066
- for (const [k, v] of Object.entries(value))
12945
+ for (const [k, v] of Object.entries(val))
13067
12946
  assertSerializable(v, `${path}.${k}`);
13068
12947
  return;
13069
12948
  }
13070
12949
  throw new TypeError(`${path} is not serializable`);
13071
12950
  }
12951
+ function createDvala(options) {
12952
+ initCoreDvalaSources();
12953
+ const modules = options?.modules
12954
+ ? new Map(options.modules.map(m => [m.name, m]))
12955
+ : undefined;
12956
+ const factoryBindings = options?.bindings;
12957
+ const factoryEffectHandlers = options?.effectHandlers;
12958
+ const debug = options?.debug ?? false;
12959
+ const cache = options?.cache ? new Cache(options.cache) : null;
12960
+ function buildAst(source, filePath) {
12961
+ if (!filePath && cache) {
12962
+ const cached = cache.get(source);
12963
+ if (cached)
12964
+ return cached;
12965
+ }
12966
+ const tokenStream = tokenize(source, debug, filePath);
12967
+ const minified = minifyTokenStream(tokenStream, { removeWhiteSpace: true });
12968
+ const ast = { body: parse(minified), hasDebugData: debug };
12969
+ if (!filePath)
12970
+ cache?.set(source, ast);
12971
+ return ast;
12972
+ }
12973
+ function mergeBindings(runBindings) {
12974
+ if (!factoryBindings && !runBindings)
12975
+ return undefined;
12976
+ return { ...factoryBindings, ...runBindings };
12977
+ }
12978
+ function mergeEffectHandlers(runEffectHandlers) {
12979
+ if (!factoryEffectHandlers && !runEffectHandlers)
12980
+ return undefined;
12981
+ // Run handlers first (checked first), factory handlers fill in the rest.
12982
+ // For same key, run overrides factory.
12983
+ const result = { ...runEffectHandlers };
12984
+ if (factoryEffectHandlers) {
12985
+ for (const [k, v] of Object.entries(factoryEffectHandlers)) {
12986
+ if (!(k in result))
12987
+ result[k] = v;
12988
+ }
12989
+ }
12990
+ return result;
12991
+ }
12992
+ function assertNotPureWithHandlers(pure, effectHandlers) {
12993
+ if (!pure)
12994
+ return;
12995
+ const hasEffectHandlers = effectHandlers && Object.keys(effectHandlers).length > 0;
12996
+ if (hasEffectHandlers) {
12997
+ throw new TypeError('Cannot use pure mode with effect handlers');
12998
+ }
12999
+ }
13000
+ return {
13001
+ run(source, runOptions) {
13002
+ assertSerializableBindings(runOptions?.bindings);
13003
+ const bindings = mergeBindings(runOptions?.bindings);
13004
+ const effectHandlers = mergeEffectHandlers(runOptions?.effectHandlers);
13005
+ const pure = runOptions?.pure ?? false;
13006
+ assertNotPureWithHandlers(pure, effectHandlers);
13007
+ const contextStack = createContextStack({ bindings }, modules, pure);
13008
+ if (isDvalaBundle(source)) {
13009
+ const savedPure = contextStack.pure;
13010
+ contextStack.pure = true;
13011
+ for (const [name, fileSource] of source.fileModules) {
13012
+ const fileAst = buildAst(fileSource);
13013
+ const moduleContextStack = contextStack.create({});
13014
+ contextStack.registerValueModule(name, evaluate(fileAst, moduleContextStack));
13015
+ }
13016
+ contextStack.pure = savedPure;
13017
+ const ast = buildAst(source.program);
13018
+ const result = evaluate(ast, contextStack);
13019
+ if (result instanceof Promise)
13020
+ throw new TypeError('Unexpected async result in run(). Use runAsync() for async operations.');
13021
+ return result;
13022
+ }
13023
+ const ast = buildAst(source, runOptions?.filePath);
13024
+ if (effectHandlers) {
13025
+ return evaluateWithSyncEffects(ast, contextStack, effectHandlers);
13026
+ }
13027
+ const result = evaluate(ast, contextStack);
13028
+ if (result instanceof Promise) {
13029
+ throw new TypeError('Unexpected async result in run(). Use runAsync() for async operations.');
13030
+ }
13031
+ return result;
13032
+ },
13033
+ async runAsync(source, runOptions) {
13034
+ assertSerializableBindings(runOptions?.bindings);
13035
+ const bindings = mergeBindings(runOptions?.bindings);
13036
+ const effectHandlers = mergeEffectHandlers(runOptions?.effectHandlers);
13037
+ const pure = runOptions?.pure ?? false;
13038
+ assertNotPureWithHandlers(pure, effectHandlers);
13039
+ try {
13040
+ const contextStack = createContextStack({ bindings }, modules, pure);
13041
+ if (isDvalaBundle(source)) {
13042
+ const savedPure = contextStack.pure;
13043
+ contextStack.pure = true;
13044
+ for (const [name, fileSource] of source.fileModules) {
13045
+ const fileAst = buildAst(fileSource);
13046
+ const moduleContextStack = contextStack.create({});
13047
+ contextStack.registerValueModule(name, evaluate(fileAst, moduleContextStack));
13048
+ }
13049
+ contextStack.pure = savedPure;
13050
+ }
13051
+ const programSource = isDvalaBundle(source) ? source.program : source;
13052
+ const ast = buildAst(programSource);
13053
+ const result = await evaluateWithEffects(ast, contextStack, effectHandlers, runOptions?.maxSnapshots, {
13054
+ values: bindings,
13055
+ modules,
13056
+ });
13057
+ if (result.type === 'completed') {
13058
+ return { ...result, definedBindings: contextStack.getModuleScopeBindings() };
13059
+ }
13060
+ return result;
13061
+ }
13062
+ catch (error) {
13063
+ if (error instanceof DvalaError) {
13064
+ return { type: 'error', error };
13065
+ }
13066
+ if (error instanceof TypeError) {
13067
+ throw error;
13068
+ }
13069
+ return { type: 'error', error: new DvalaError(`${error}`, undefined) };
13070
+ }
13071
+ },
13072
+ getUndefinedSymbols(source) {
13073
+ const modulesList = modules ? [...modules.values()] : undefined;
13074
+ return getUndefinedSymbols(source, { bindings: factoryBindings, modules: modulesList });
13075
+ },
13076
+ getAutoCompleter(program, position) {
13077
+ const params = { bindings: factoryBindings };
13078
+ return new AutoCompleter(program, position, params);
13079
+ },
13080
+ };
13081
+ }
13072
13082
 
13073
13083
  var assertionModuleSource = "{}";
13074
13084
 
@@ -14448,7 +14458,7 @@ const assertModule = {
14448
14458
  docs: moduleDocs$5,
14449
14459
  };
14450
14460
 
14451
- var gridModuleSource = "do\nlet _transpose = (g) ->\n map(range(count(first(g))), (i) ->\n map(range(count(g)), (j) -> nth(nth(g, j), i))\n );\n{\n \"cell-every?\": (grid, predicate) -> do\n let cells = flatten(grid, 1);\n loop(i = 0) ->\n cond\n case i >= count(cells) then true\n case not(predicate(nth(cells, i))) then false\n case true then recur(i + 1)\n end\n end,\n\n \"some?\": (grid, predicate) -> do\n let cells = flatten(grid, 1);\n loop(i = 0) ->\n cond\n case i >= count(cells) then false\n case predicate(nth(cells, i)) then true\n case true then recur(i + 1)\n end\n end,\n\n \"every-row?\": (grid, predicate) ->\n loop(i = 0) ->\n cond\n case i >= count(grid) then true\n case not(predicate(nth(grid, i))) then false\n case true then recur(i + 1)\n end,\n\n \"some-row?\": (grid, predicate) ->\n loop(i = 0) ->\n cond\n case i >= count(grid) then false\n case predicate(nth(grid, i)) then true\n case true then recur(i + 1)\n end,\n\n \"every-col?\": (grid, predicate) -> do\n let cols = _transpose(grid);\n loop(i = 0) ->\n cond\n case i >= count(cols) then true\n case not(predicate(nth(cols, i))) then false\n case true then recur(i + 1)\n end\n end,\n\n \"some-col?\": (grid, predicate) -> do\n let cols = _transpose(grid);\n loop(i = 0) ->\n cond\n case i >= count(cols) then false\n case predicate(nth(cols, i)) then true\n case true then recur(i + 1)\n end\n end,\n\n \"generate\": (rows, cols, generator) ->\n map(range(rows), (i) -> map(range(cols), (j) -> generator(i, j))),\n\n \"cell-map\": (...params) -> do\n let fn = last(params);\n let grids = drop-last(params, 1);\n let rows = count(first(grids));\n let cols = count(first(first(grids)));\n map(range(rows), (i) ->\n map(range(cols), (j) ->\n apply(fn, map(grids, (g) -> nth(nth(g, i), j)))\n )\n )\n end,\n\n \"cell-mapi\": (grid, fn) -> do\n let rows = count(grid);\n let cols = count(first(grid));\n map(range(rows), (i) ->\n map(range(cols), (j) ->\n fn(nth(nth(grid, i), j), i, j)\n )\n )\n end,\n\n \"cell-reduce\": (grid, fn, initial-value) ->\n reduce(flatten(grid, 1), fn, initial-value),\n\n \"cell-reducei\": (grid, fn, initial-value) -> do\n let rows = count(grid);\n let cols = count(first(grid));\n loop(acc = initial-value, i = 0, j = 0) ->\n cond\n case i >= rows then acc\n case j >= cols then recur(acc, i + 1, 0)\n case true then recur(fn(acc, nth(nth(grid, i), j), i, j), i, j + 1)\n end\n end\n}\nend\n";
14461
+ var gridModuleSource = "do\nlet _transpose = (g) ->\n map(range(count(first(g))), (i) ->\n map(range(count(g)), (j) -> nth(nth(g, j), i))\n );\n{\n \"cell-every?\": (grid, predicate) -> do\n let cells = flatten(grid, 1);\n loop(i = 0) ->\n cond\n case i >= count(cells) then true\n case not(predicate(nth(cells, i))) then false\n case true then recur(i + 1)\n end\n end,\n \"some?\": (grid, predicate) -> do\n let cells = flatten(grid, 1);\n loop(i = 0) ->\n cond\n case i >= count(cells) then false\n case predicate(nth(cells, i)) then true\n case true then recur(i + 1)\n end\n end,\n\n \"every-row?\": (grid, predicate) ->\n loop(i = 0) ->\n cond\n case i >= count(grid) then true\n case not(predicate(nth(grid, i))) then false\n case true then recur(i + 1)\n end,\n\n \"some-row?\": (grid, predicate) ->\n loop(i = 0) ->\n cond\n case i >= count(grid) then false\n case predicate(nth(grid, i)) then true\n case true then recur(i + 1)\n end,\n\n \"every-col?\": (grid, predicate) -> do\n let cols = _transpose(grid);\n loop(i = 0) ->\n cond\n case i >= count(cols) then true\n case not(predicate(nth(cols, i))) then false\n case true then recur(i + 1)\n end\n end,\n\n \"some-col?\": (grid, predicate) -> do\n let cols = _transpose(grid);\n loop(i = 0) ->\n cond\n case i >= count(cols) then false\n case predicate(nth(cols, i)) then true\n case true then recur(i + 1)\n end\n end,\n\n \"generate\": (rows, cols, generator) ->\n map(range(rows), (i) -> map(range(cols), (j) -> generator(i, j))),\n\n \"cell-map\": (...params) -> do\n let fn = last(params);\n let grids = drop-last(params, 1);\n let rows = count(first(grids));\n let cols = count(first(first(grids)));\n map(range(rows), (i) ->\n map(range(cols), (j) ->\n apply(fn, map(grids, (g) -> nth(nth(g, i), j)))\n )\n )\n end,\n\n \"cell-mapi\": (grid, fn) -> do\n let rows = count(grid);\n let cols = count(first(grid));\n map(range(rows), (i) ->\n map(range(cols), (j) ->\n fn(nth(nth(grid, i), j), i, j)\n )\n )\n end,\n\n \"cell-reduce\": (grid, fn, initial-value) ->\n reduce(flatten(grid, 1), fn, initial-value),\n\n \"cell-reducei\": (grid, fn, initial-value) -> do\n let rows = count(grid);\n let cols = count(first(grid));\n loop(acc = initial-value, i = 0, j = 0) ->\n cond\n case i >= rows then acc\n case j >= cols then recur(acc, i + 1, 0)\n case true then recur(fn(acc, nth(nth(grid, i), j), i, j), i, j + 1)\n end\n end\n}\nend;";
14452
14462
 
14453
14463
  const moduleDocs$4 = {
14454
14464
  'cell-every?': {
@@ -34681,7 +34691,7 @@ function runTest({ testPath: filePath, testNamePattern }) {
34681
34691
  }
34682
34692
  else {
34683
34693
  try {
34684
- const dvala = new Dvala({ debug: true, modules: allBuiltinModules });
34694
+ const dvala = createDvala({ debug: true, modules: allBuiltinModules });
34685
34695
  const bindings = getBindings(includedFilePaths, dvala);
34686
34696
  dvala.run(testChunkProgram.program, {
34687
34697
  bindings,
@@ -35156,7 +35166,7 @@ function specialExpressionDocsToReference() {
35156
35166
  }
35157
35167
  const specialExpressionsReference = specialExpressionDocsToReference();
35158
35168
  function isFunctionReference(ref) {
35159
- return 'returns' in ref && 'args' in ref && 'variants' in ref;
35169
+ return 'returns' in ref && 'args' in ref && 'variants' in ref && !('effect' in ref);
35160
35170
  }
35161
35171
  const normalExpressionReference = {
35162
35172
  // Core categories — all derived from co-located docs
@@ -37064,7 +37074,7 @@ function getArgumentInfo(fmt, reference) {
37064
37074
  }
37065
37075
 
37066
37076
  /* eslint-disable no-console */
37067
- const dvala = new Dvala({ debug: false });
37077
+ const dvala = createDvala({ debug: false });
37068
37078
  function getCliFunctionExamples(fmt, reference) {
37069
37079
  const { examples } = reference;
37070
37080
  return examples
@@ -37671,19 +37681,15 @@ const helpRegExp = new RegExp(`^\`help\\s+(${polishSymbolFirstCharacterClass}${p
37671
37681
  const expressions = [...normalExpressionKeys, ...specialExpressionKeys];
37672
37682
  const config = processArguments(process.argv.slice(2));
37673
37683
  const cliModules = getCliModules();
37674
- function createDvala(context, pure) {
37675
- const _dvala = new Dvala({ debug: true, modules: [...allBuiltinModules, ...cliModules] });
37684
+ function makeDvala(bindings, pure) {
37685
+ const runner = createDvala({ debug: true, modules: [...allBuiltinModules, ...cliModules], bindings });
37676
37686
  return {
37677
- run: (program) => _dvala.run(program, {
37678
- globalContext: context,
37679
- globalModuleScope: true,
37680
- pure,
37681
- }),
37687
+ run: (program) => runner.run(program, { pure }),
37682
37688
  };
37683
37689
  }
37684
37690
  switch (config.subcommand) {
37685
37691
  case 'run': {
37686
- const dvala = createDvala(config.context, config.pure);
37692
+ const dvala = makeDvala(config.context, config.pure);
37687
37693
  try {
37688
37694
  const content = fs.readFileSync(config.filename, { encoding: 'utf-8' });
37689
37695
  const result = dvala.run(content);
@@ -37699,7 +37705,7 @@ switch (config.subcommand) {
37699
37705
  break;
37700
37706
  }
37701
37707
  case 'run-bundle': {
37702
- const dvala = createDvala(config.context, config.pure);
37708
+ const dvala = makeDvala(config.context, config.pure);
37703
37709
  try {
37704
37710
  const content = fs.readFileSync(config.filename, { encoding: 'utf-8' });
37705
37711
  let parsed;
@@ -37727,7 +37733,7 @@ switch (config.subcommand) {
37727
37733
  break;
37728
37734
  }
37729
37735
  case 'eval': {
37730
- const dvala = createDvala(config.context, config.pure);
37736
+ const dvala = makeDvala(config.context, config.pure);
37731
37737
  try {
37732
37738
  const result = dvala.run(config.expression);
37733
37739
  if (config.printResult) {
@@ -37767,12 +37773,12 @@ switch (config.subcommand) {
37767
37773
  }
37768
37774
  case 'repl': {
37769
37775
  if (config.loadFilename) {
37770
- const dvala = createDvala(config.context, false);
37776
+ const dvala = makeDvala(config.context, false);
37771
37777
  const content = fs.readFileSync(config.loadFilename, { encoding: 'utf-8' });
37772
37778
  const result = dvala.run(content);
37773
37779
  if (result !== null && typeof result === 'object' && !Array.isArray(result)) {
37774
37780
  for (const [key, value] of Object.entries(result)) {
37775
- config.context[key] = { value: asAny(value) };
37781
+ config.context[key] = value;
37776
37782
  }
37777
37783
  }
37778
37784
  }
@@ -37803,13 +37809,12 @@ function runDvalaTest(testPath, testNamePattern) {
37803
37809
  if (!success)
37804
37810
  process.exit(1);
37805
37811
  }
37806
- async function execute(expression, context, readLine) {
37807
- const _dvala = new Dvala({ debug: true, modules: [...allBuiltinModules, ...cliModules] });
37812
+ async function execute(expression, bindings, readLine) {
37813
+ const _dvala = createDvala({ debug: true, modules: [...allBuiltinModules, ...cliModules] });
37808
37814
  try {
37809
- const result = await _dvala.async.run(expression, {
37810
- globalContext: context,
37811
- globalModuleScope: true,
37812
- handlers: {
37815
+ const runResult = await _dvala.runAsync(expression, {
37816
+ bindings,
37817
+ effectHandlers: {
37813
37818
  'dvala.io.read-line': async ({ args, resume }) => {
37814
37819
  const message = typeof args[0] === 'string' ? args[0] : '';
37815
37820
  const answer = await readLine(message);
@@ -37817,18 +37822,21 @@ async function execute(expression, context, readLine) {
37817
37822
  },
37818
37823
  },
37819
37824
  });
37825
+ if (runResult.type === 'error')
37826
+ throw runResult.error;
37827
+ const result = runResult.type === 'completed' ? runResult.value : null;
37820
37828
  historyResults.unshift(result);
37821
37829
  if (historyResults.length > 9) {
37822
37830
  historyResults.length = 9;
37823
37831
  }
37824
- setReplHistoryVariables(context);
37832
+ const newBindings = { ...bindings, ...(runResult.type === 'completed' ? runResult.definedBindings : {}) };
37833
+ setReplHistoryVariables(newBindings);
37825
37834
  console.log(stringifyValue(result, false));
37826
- return true;
37835
+ return newBindings;
37827
37836
  }
37828
37837
  catch (error) {
37829
37838
  printErrorMessage(`${error}`);
37830
- context['*e*'] = { value: getErrorMessage(error) };
37831
- return false;
37839
+ return { ...bindings, '*e*': getErrorMessage(error) };
37832
37840
  }
37833
37841
  }
37834
37842
  function getErrorMessage(error) {
@@ -37836,18 +37844,11 @@ function getErrorMessage(error) {
37836
37844
  return error.message;
37837
37845
  return 'Unknown error';
37838
37846
  }
37839
- function setReplHistoryVariables(context) {
37840
- delete context['*1*'];
37841
- delete context['*2*'];
37842
- delete context['*3*'];
37843
- delete context['*4*'];
37844
- delete context['*5*'];
37845
- delete context['*6*'];
37846
- delete context['*7*'];
37847
- delete context['*8*'];
37848
- delete context['*9*'];
37847
+ function setReplHistoryVariables(bindings) {
37848
+ for (let i = 1; i <= 9; i++)
37849
+ delete bindings[`*${i}*`];
37849
37850
  historyResults.forEach((value, i) => {
37850
- context[`*${i + 1}*`] = { value: asAny(value) };
37851
+ bindings[`*${i + 1}*`] = value;
37851
37852
  });
37852
37853
  }
37853
37854
  function parseOption(args, i) {
@@ -37890,7 +37891,7 @@ function parseContextOptions(args, startIndex) {
37890
37891
  }
37891
37892
  try {
37892
37893
  Object.entries(JSON.parse(parsed.argument)).forEach(([key, value]) => {
37893
- options.context[key] = { value: asAny(value) };
37894
+ options.context[key] = value;
37894
37895
  });
37895
37896
  }
37896
37897
  catch (e) {
@@ -37908,7 +37909,7 @@ function parseContextOptions(args, startIndex) {
37908
37909
  try {
37909
37910
  const contextString = fs.readFileSync(parsed.argument, { encoding: 'utf-8' });
37910
37911
  Object.entries(JSON.parse(contextString)).forEach(([key, value]) => {
37911
- options.context[key] = { value: asAny(value) };
37912
+ options.context[key] = value;
37912
37913
  });
37913
37914
  }
37914
37915
  catch (e) {
@@ -38140,9 +38141,10 @@ function processArguments(args) {
38140
38141
  }
38141
38142
  }
38142
38143
  }
38143
- function runREPL(context) {
38144
+ function runREPL(initialBindings) {
38144
38145
  console.log(`Welcome to Dvala v${version}.
38145
38146
  Type ${fmt.italic('`help')} for more information.`);
38147
+ let bindings = initialBindings;
38146
38148
  const rl = createReadlineInterface({
38147
38149
  completer,
38148
38150
  historySize: HIST_SIZE,
@@ -38169,7 +38171,7 @@ Type ${fmt.italic('`help')} for more information.`);
38169
38171
  printHelp();
38170
38172
  break;
38171
38173
  case '`context':
38172
- printContext(context);
38174
+ printContext(bindings);
38173
38175
  break;
38174
38176
  case '`quit':
38175
38177
  rl.close();
@@ -38179,7 +38181,7 @@ Type ${fmt.italic('`help')} for more information.`);
38179
38181
  }
38180
38182
  }
38181
38183
  else if (line) {
38182
- await execute(line, context, readLine);
38184
+ bindings = await execute(line, bindings, readLine);
38183
38185
  }
38184
38186
  rl.prompt();
38185
38187
  }).on('close', () => {
@@ -38248,14 +38250,14 @@ Global options:
38248
38250
  With no subcommand, starts an interactive REPL.
38249
38251
  `.trim());
38250
38252
  }
38251
- function printContext(context) {
38252
- const keys = Object.keys(context);
38253
+ function printContext(bindings) {
38254
+ const keys = Object.keys(bindings);
38253
38255
  if (keys.length === 0) {
38254
38256
  console.log('[empty]\n');
38255
38257
  }
38256
38258
  else {
38257
38259
  keys.sort().forEach((x) => {
38258
- console.log(`${x} = ${formatValue(stringifyValue(context[x].value))}`);
38260
+ console.log(`${x} = ${formatValue(stringifyValue(bindings[x]))}`);
38259
38261
  });
38260
38262
  console.log();
38261
38263
  }
@@ -38270,8 +38272,8 @@ function completer(line) {
38270
38272
  if (expressionMatch)
38271
38273
  return [expressions.filter(c => c.startsWith(expressionMatch[2])).map(c => `${expressionMatch[1]}${c} `), line];
38272
38274
  // TODO, add reserved names
38273
- const context = config.context ?? {};
38274
- const names = Array.from(new Set([...Object.keys(context)]));
38275
+ const replBindings = config.context ?? {};
38276
+ const names = Array.from(new Set([...Object.keys(replBindings)]));
38275
38277
  const nameMatch = nameRegExp.exec(line);
38276
38278
  if (nameMatch)
38277
38279
  return [names.filter(c => c.startsWith(nameMatch[2])).map(c => `${nameMatch[1]}${c} `), line];