@mojir/dvala 0.0.7 → 0.0.9

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 (316) hide show
  1. package/dist/cli/cli.js +1553 -1599
  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 +5 -5
  9. package/dist/cli/src/evaluator/trampoline.d.ts +10 -1
  10. package/dist/cli/src/parser/subParsers/parseDo.d.ts +1 -1
  11. package/dist/cli/src/tokenizer/token.d.ts +2 -4
  12. package/dist/cli/src/tokenizer/tokenize.d.ts +1 -2
  13. package/dist/cli/src/tokenizer/tokenizers.d.ts +2 -3
  14. package/dist/cli/src/tooling.d.ts +51 -0
  15. package/dist/debug.esm.js +1 -1
  16. package/dist/debug.esm.js.map +1 -1
  17. package/dist/debug.js +1 -1
  18. package/dist/debug.js.map +1 -1
  19. package/dist/dvala.iife.js +1 -1
  20. package/dist/dvala.iife.js.map +1 -1
  21. package/dist/full.esm.js +1 -1
  22. package/dist/full.esm.js.map +1 -1
  23. package/dist/full.js +1 -1
  24. package/dist/full.js.map +1 -1
  25. package/dist/index.esm.js +1 -1
  26. package/dist/index.esm.js.map +1 -1
  27. package/dist/index.js +1 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/mcp-server/common/utils.d.ts +2 -0
  30. package/dist/mcp-server/mcp-server/src/server.d.ts +1 -0
  31. package/dist/mcp-server/reference/api.d.ts +73 -0
  32. package/dist/mcp-server/reference/datatype.d.ts +3 -0
  33. package/dist/mcp-server/reference/examples.d.ts +11 -0
  34. package/dist/mcp-server/reference/index.d.ts +195 -0
  35. package/dist/mcp-server/reference/shorthand.d.ts +3 -0
  36. package/dist/mcp-server/server.js +36330 -0
  37. package/dist/mcp-server/src/AutoCompleter/AutoCompleter.d.ts +27 -0
  38. package/dist/{src/Dvala → mcp-server/src}/Cache.d.ts +1 -1
  39. package/dist/mcp-server/src/allModules.d.ts +2 -0
  40. package/dist/mcp-server/src/builtin/bindingNode.d.ts +11 -0
  41. package/dist/mcp-server/src/builtin/core/array.d.ts +2 -0
  42. package/dist/mcp-server/src/builtin/core/assertion.d.ts +2 -0
  43. package/dist/mcp-server/src/builtin/core/bitwise.d.ts +2 -0
  44. package/dist/mcp-server/src/builtin/core/collection.d.ts +2 -0
  45. package/dist/mcp-server/src/builtin/core/functional.d.ts +2 -0
  46. package/dist/mcp-server/src/builtin/core/math.d.ts +2 -0
  47. package/dist/mcp-server/src/builtin/core/meta.d.ts +3 -0
  48. package/dist/mcp-server/src/builtin/core/misc.d.ts +2 -0
  49. package/dist/mcp-server/src/builtin/core/object.d.ts +2 -0
  50. package/dist/mcp-server/src/builtin/core/predicates.d.ts +2 -0
  51. package/dist/mcp-server/src/builtin/core/regexp.d.ts +2 -0
  52. package/dist/mcp-server/src/builtin/core/sequence.d.ts +2 -0
  53. package/dist/mcp-server/src/builtin/core/string.d.ts +2 -0
  54. package/dist/mcp-server/src/builtin/core/vector.d.ts +2 -0
  55. package/dist/mcp-server/src/builtin/index.d.ts +13 -0
  56. package/dist/mcp-server/src/builtin/interface.d.ts +113 -0
  57. package/dist/mcp-server/src/builtin/modules/assertion/docs.d.ts +2 -0
  58. package/dist/mcp-server/src/builtin/modules/assertion/index.d.ts +2 -0
  59. package/dist/mcp-server/src/builtin/modules/bitwise/index.d.ts +2 -0
  60. package/dist/mcp-server/src/builtin/modules/collection/index.d.ts +2 -0
  61. package/dist/mcp-server/src/builtin/modules/convert/index.d.ts +2 -0
  62. package/dist/mcp-server/src/builtin/modules/functional/index.d.ts +2 -0
  63. package/dist/mcp-server/src/builtin/modules/grid/docs.d.ts +2 -0
  64. package/dist/mcp-server/src/builtin/modules/grid/fromArray.d.ts +8 -0
  65. package/dist/mcp-server/src/builtin/modules/grid/index.d.ts +2 -0
  66. package/dist/mcp-server/src/builtin/modules/grid/transpose.d.ts +2 -0
  67. package/dist/mcp-server/src/builtin/modules/interface.d.ts +28 -0
  68. package/dist/mcp-server/src/builtin/modules/linear-algebra/docs.d.ts +2 -0
  69. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/calcFractionalRanks.d.ts +1 -0
  70. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/collinear.d.ts +2 -0
  71. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/corrleation.d.ts +8 -0
  72. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/covariance.d.ts +4 -0
  73. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/dot.d.ts +1 -0
  74. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/gaussJordanElimination.d.ts +7 -0
  75. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/getUnit.d.ts +2 -0
  76. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/isZeroVector.d.ts +1 -0
  77. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/kendallTau.d.ts +10 -0
  78. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/length.d.ts +1 -0
  79. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/pearsonCorr.d.ts +1 -0
  80. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/scale.d.ts +1 -0
  81. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/solve.d.ts +8 -0
  82. package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/subtract.d.ts +1 -0
  83. package/dist/mcp-server/src/builtin/modules/linear-algebra/index.d.ts +4 -0
  84. package/dist/mcp-server/src/builtin/modules/math/index.d.ts +2 -0
  85. package/dist/mcp-server/src/builtin/modules/matrix/docs.d.ts +2 -0
  86. package/dist/mcp-server/src/builtin/modules/matrix/helpers/adjugate.d.ts +1 -0
  87. package/dist/mcp-server/src/builtin/modules/matrix/helpers/band.d.ts +9 -0
  88. package/dist/mcp-server/src/builtin/modules/matrix/helpers/cofactor.d.ts +1 -0
  89. package/dist/mcp-server/src/builtin/modules/matrix/helpers/determinant.d.ts +6 -0
  90. package/dist/mcp-server/src/builtin/modules/matrix/helpers/inverse.d.ts +6 -0
  91. package/dist/mcp-server/src/builtin/modules/matrix/helpers/isBanded.d.ts +11 -0
  92. package/dist/mcp-server/src/builtin/modules/matrix/helpers/isDiagonal.d.ts +10 -0
  93. package/dist/mcp-server/src/builtin/modules/matrix/helpers/isIdentity.d.ts +1 -0
  94. package/dist/mcp-server/src/builtin/modules/matrix/helpers/isOrthogonal.d.ts +1 -0
  95. package/dist/mcp-server/src/builtin/modules/matrix/helpers/isSquare.d.ts +1 -0
  96. package/dist/mcp-server/src/builtin/modules/matrix/helpers/isSymetric.d.ts +8 -0
  97. package/dist/mcp-server/src/builtin/modules/matrix/helpers/isTriangular.d.ts +13 -0
  98. package/dist/mcp-server/src/builtin/modules/matrix/helpers/matrixMultiply.d.ts +7 -0
  99. package/dist/mcp-server/src/builtin/modules/matrix/helpers/minor.d.ts +1 -0
  100. package/dist/mcp-server/src/builtin/modules/matrix/helpers/norm1.d.ts +1 -0
  101. package/dist/mcp-server/src/builtin/modules/matrix/helpers/trace.d.ts +8 -0
  102. package/dist/mcp-server/src/builtin/modules/matrix/index.d.ts +4 -0
  103. package/dist/mcp-server/src/builtin/modules/number-theory/binomialCefficient.d.ts +1 -0
  104. package/dist/mcp-server/src/builtin/modules/number-theory/combinations.d.ts +2 -0
  105. package/dist/mcp-server/src/builtin/modules/number-theory/derangements.d.ts +2 -0
  106. package/dist/mcp-server/src/builtin/modules/number-theory/divisors.d.ts +4 -0
  107. package/dist/mcp-server/src/builtin/modules/number-theory/docs.d.ts +2 -0
  108. package/dist/mcp-server/src/builtin/modules/number-theory/factorial.d.ts +3 -0
  109. package/dist/mcp-server/src/builtin/modules/number-theory/index.d.ts +4 -0
  110. package/dist/mcp-server/src/builtin/modules/number-theory/partitions.d.ts +2 -0
  111. package/dist/mcp-server/src/builtin/modules/number-theory/permutations.d.ts +2 -0
  112. package/dist/mcp-server/src/builtin/modules/number-theory/powerSet.d.ts +2 -0
  113. package/dist/mcp-server/src/builtin/modules/number-theory/primeFactors.d.ts +11 -0
  114. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/abundant.d.ts +2 -0
  115. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/arithmetic.d.ts +2 -0
  116. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/bell.d.ts +1 -0
  117. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/bernoulli.d.ts +2 -0
  118. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/catalan.d.ts +1 -0
  119. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/collatz.d.ts +2 -0
  120. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/composite.d.ts +3 -0
  121. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/deficient.d.ts +2 -0
  122. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/factorial.d.ts +1 -0
  123. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/fibonacci.d.ts +1 -0
  124. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/geometric.d.ts +2 -0
  125. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/golomb.d.ts +2 -0
  126. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/happy.d.ts +2 -0
  127. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/index.d.ts +27 -0
  128. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/juggler.d.ts +2 -0
  129. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/lookAndSay.d.ts +2 -0
  130. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/lucas.d.ts +1 -0
  131. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/lucky.d.ts +2 -0
  132. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/mersenne.d.ts +1 -0
  133. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/padovan.d.ts +2 -0
  134. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/partition.d.ts +1 -0
  135. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/pell.d.ts +1 -0
  136. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/perfect.d.ts +1 -0
  137. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/perfectCube.d.ts +2 -0
  138. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/perfectPower.d.ts +10 -0
  139. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/perfectSquare.d.ts +2 -0
  140. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/poligonal.d.ts +2 -0
  141. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/prime.d.ts +3 -0
  142. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/recaman.d.ts +9 -0
  143. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/sylvester.d.ts +1 -0
  144. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/thueMorse.d.ts +2 -0
  145. package/dist/mcp-server/src/builtin/modules/number-theory/sequences/tribonacci.d.ts +1 -0
  146. package/dist/mcp-server/src/builtin/modules/sequence/index.d.ts +2 -0
  147. package/dist/mcp-server/src/builtin/modules/string/index.d.ts +2 -0
  148. package/dist/mcp-server/src/builtin/modules/vector/bincount.d.ts +9 -0
  149. package/dist/mcp-server/src/builtin/modules/vector/calcMad.d.ts +1 -0
  150. package/dist/mcp-server/src/builtin/modules/vector/calcMean.d.ts +1 -0
  151. package/dist/mcp-server/src/builtin/modules/vector/calcMedad.d.ts +1 -0
  152. package/dist/mcp-server/src/builtin/modules/vector/calcMedian.d.ts +1 -0
  153. package/dist/mcp-server/src/builtin/modules/vector/calcStdDev.d.ts +2 -0
  154. package/dist/mcp-server/src/builtin/modules/vector/calcVariance.d.ts +2 -0
  155. package/dist/mcp-server/src/builtin/modules/vector/docs.d.ts +2 -0
  156. package/dist/mcp-server/src/builtin/modules/vector/entropy.d.ts +8 -0
  157. package/dist/mcp-server/src/builtin/modules/vector/histogram.d.ts +9 -0
  158. package/dist/mcp-server/src/builtin/modules/vector/index.d.ts +2 -0
  159. package/dist/mcp-server/src/builtin/modules/vector/mode.d.ts +6 -0
  160. package/dist/mcp-server/src/builtin/modules/vector/outliers.d.ts +7 -0
  161. package/dist/mcp-server/src/builtin/modules/vector/percentile.d.ts +7 -0
  162. package/dist/mcp-server/src/builtin/modules/vector/quartiles.d.ts +1 -0
  163. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/entropy.d.ts +2 -0
  164. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/giniCoefficient.d.ts +2 -0
  165. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/index.d.ts +13 -0
  166. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/iqr.d.ts +2 -0
  167. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/kurtosis.d.ts +5 -0
  168. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/mad.d.ts +2 -0
  169. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/mean.d.ts +4 -0
  170. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/medad.d.ts +2 -0
  171. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/median.d.ts +2 -0
  172. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/prod.d.ts +2 -0
  173. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/rms.d.ts +2 -0
  174. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/skewness.d.ts +3 -0
  175. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/span.d.ts +2 -0
  176. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/standardDeviation.d.ts +3 -0
  177. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/sum.d.ts +2 -0
  178. package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/variance.d.ts +3 -0
  179. package/dist/mcp-server/src/builtin/normalExpressions/index.d.ts +9 -0
  180. package/dist/mcp-server/src/builtin/normalExpressions/initCoreDvala.d.ts +1 -0
  181. package/dist/mcp-server/src/builtin/specialExpressionTypes.d.ts +24 -0
  182. package/dist/mcp-server/src/builtin/specialExpressions/and.d.ts +6 -0
  183. package/dist/mcp-server/src/builtin/specialExpressions/array.d.ts +6 -0
  184. package/dist/mcp-server/src/builtin/specialExpressions/block.d.ts +7 -0
  185. package/dist/mcp-server/src/builtin/specialExpressions/cond.d.ts +6 -0
  186. package/dist/mcp-server/src/builtin/specialExpressions/defined.d.ts +5 -0
  187. package/dist/mcp-server/src/builtin/specialExpressions/effect.d.ts +5 -0
  188. package/dist/mcp-server/src/builtin/specialExpressions/functions.d.ts +6 -0
  189. package/dist/mcp-server/src/builtin/specialExpressions/if.d.ts +6 -0
  190. package/dist/mcp-server/src/builtin/specialExpressions/import.d.ts +6 -0
  191. package/dist/mcp-server/src/builtin/specialExpressions/let.d.ts +6 -0
  192. package/dist/mcp-server/src/builtin/specialExpressions/loop.d.ts +6 -0
  193. package/dist/mcp-server/src/builtin/specialExpressions/loops.d.ts +9 -0
  194. package/dist/mcp-server/src/builtin/specialExpressions/match.d.ts +7 -0
  195. package/dist/mcp-server/src/builtin/specialExpressions/object.d.ts +6 -0
  196. package/dist/mcp-server/src/builtin/specialExpressions/or.d.ts +6 -0
  197. package/dist/mcp-server/src/builtin/specialExpressions/parallel.d.ts +6 -0
  198. package/dist/mcp-server/src/builtin/specialExpressions/perform.d.ts +6 -0
  199. package/dist/mcp-server/src/builtin/specialExpressions/qq.d.ts +6 -0
  200. package/dist/mcp-server/src/builtin/specialExpressions/race.d.ts +6 -0
  201. package/dist/mcp-server/src/builtin/specialExpressions/recur.d.ts +5 -0
  202. package/dist/mcp-server/src/builtin/specialExpressions/unless.d.ts +6 -0
  203. package/dist/mcp-server/src/builtin/utils.d.ts +6 -0
  204. package/dist/mcp-server/src/bundler/interface.d.ts +15 -0
  205. package/dist/mcp-server/src/constants/constants.d.ts +19 -0
  206. package/dist/mcp-server/src/createDvala.d.ts +47 -0
  207. package/dist/mcp-server/src/errors.d.ts +24 -0
  208. package/dist/mcp-server/src/evaluator/ContextStack.d.ts +70 -0
  209. package/dist/mcp-server/src/evaluator/contentHash.d.ts +21 -0
  210. package/dist/mcp-server/src/evaluator/dedupSubTrees.d.ts +37 -0
  211. package/dist/mcp-server/src/evaluator/effectRef.d.ts +27 -0
  212. package/dist/mcp-server/src/evaluator/effectTypes.d.ts +181 -0
  213. package/dist/mcp-server/src/evaluator/frames.d.ts +513 -0
  214. package/dist/mcp-server/src/evaluator/interface.d.ts +14 -0
  215. package/dist/mcp-server/src/evaluator/standardEffects.d.ts +50 -0
  216. package/dist/mcp-server/src/evaluator/step.d.ts +175 -0
  217. package/dist/mcp-server/src/evaluator/suspension.d.ts +86 -0
  218. package/dist/mcp-server/src/evaluator/trampoline.d.ts +133 -0
  219. package/dist/mcp-server/src/getUndefinedSymbols/index.d.ts +7 -0
  220. package/dist/mcp-server/src/initReferenceData.d.ts +1 -0
  221. package/dist/mcp-server/src/interface.d.ts +7 -0
  222. package/dist/mcp-server/src/parser/ParserContext.d.ts +20 -0
  223. package/dist/mcp-server/src/parser/getPrecedence.d.ts +3 -0
  224. package/dist/mcp-server/src/parser/helpers.d.ts +19 -0
  225. package/dist/mcp-server/src/parser/index.d.ts +5 -0
  226. package/dist/mcp-server/src/parser/subParsers/parseArray.d.ts +3 -0
  227. package/dist/mcp-server/src/parser/subParsers/parseBindingTarget.d.ts +8 -0
  228. package/dist/mcp-server/src/parser/subParsers/parseCond.d.ts +4 -0
  229. package/dist/mcp-server/src/parser/subParsers/parseDo.d.ts +3 -0
  230. package/dist/mcp-server/src/parser/subParsers/parseExpression.d.ts +5 -0
  231. package/dist/mcp-server/src/parser/subParsers/parseForOrDoseq.d.ts +4 -0
  232. package/dist/mcp-server/src/parser/subParsers/parseFunction.d.ts +4 -0
  233. package/dist/mcp-server/src/parser/subParsers/parseFunctionCall.d.ts +3 -0
  234. package/dist/mcp-server/src/parser/subParsers/parseIfOrUnless.d.ts +5 -0
  235. package/dist/mcp-server/src/parser/subParsers/parseImplicitBlock.d.ts +5 -0
  236. package/dist/mcp-server/src/parser/subParsers/parseLet.d.ts +4 -0
  237. package/dist/mcp-server/src/parser/subParsers/parseLoop.d.ts +4 -0
  238. package/dist/mcp-server/src/parser/subParsers/parseMatch.d.ts +4 -0
  239. package/dist/mcp-server/src/parser/subParsers/parseNumber.d.ts +3 -0
  240. package/dist/mcp-server/src/parser/subParsers/parseObject.d.ts +3 -0
  241. package/dist/mcp-server/src/parser/subParsers/parseOperand.d.ts +3 -0
  242. package/dist/mcp-server/src/parser/subParsers/parseRegexpShorthand.d.ts +3 -0
  243. package/dist/mcp-server/src/parser/subParsers/parseReservedSymbol.d.ts +3 -0
  244. package/dist/mcp-server/src/parser/subParsers/parseString.d.ts +4 -0
  245. package/dist/mcp-server/src/parser/subParsers/parseSymbol.d.ts +3 -0
  246. package/dist/mcp-server/src/parser/types.d.ts +128 -0
  247. package/dist/mcp-server/src/tokenizer/minifyTokenStream.d.ts +4 -0
  248. package/dist/mcp-server/src/tokenizer/operators.d.ts +12 -0
  249. package/dist/mcp-server/src/tokenizer/reservedNames.d.ts +65 -0
  250. package/dist/mcp-server/src/tokenizer/token.d.ts +82 -0
  251. package/dist/mcp-server/src/tokenizer/tokenize.d.ts +7 -0
  252. package/dist/mcp-server/src/tokenizer/tokenizers.d.ts +13 -0
  253. package/dist/mcp-server/src/tooling.d.ts +51 -0
  254. package/dist/mcp-server/src/transformer/index.d.ts +2 -0
  255. package/dist/mcp-server/src/typeGuards/annotatedCollections.d.ts +16 -0
  256. package/dist/mcp-server/src/typeGuards/array.d.ts +9 -0
  257. package/dist/mcp-server/src/typeGuards/astNode.d.ts +19 -0
  258. package/dist/mcp-server/src/typeGuards/dvala.d.ts +26 -0
  259. package/dist/mcp-server/src/typeGuards/dvalaFunction.d.ts +9 -0
  260. package/dist/mcp-server/src/typeGuards/index.d.ts +7 -0
  261. package/dist/mcp-server/src/typeGuards/number.d.ts +66 -0
  262. package/dist/mcp-server/src/typeGuards/string.d.ts +15 -0
  263. package/dist/mcp-server/src/untokenizer/index.d.ts +2 -0
  264. package/dist/mcp-server/src/utils/arity.d.ts +10 -0
  265. package/dist/mcp-server/src/utils/debug/debugTools.d.ts +1 -0
  266. package/dist/mcp-server/src/utils/debug/getCodeMarker.d.ts +2 -0
  267. package/dist/mcp-server/src/utils/debug/getSourceCodeInfo.d.ts +2 -0
  268. package/dist/mcp-server/src/utils/docString/generateDocString.d.ts +4 -0
  269. package/dist/mcp-server/src/utils/getAssertionError.d.ts +3 -0
  270. package/dist/mcp-server/src/utils/index.d.ts +14 -0
  271. package/dist/mcp-server/src/utils/maybePromise.d.ts +54 -0
  272. package/dist/mcp-server/src/utils/symbols.d.ts +3 -0
  273. package/dist/modules/grid.esm.js +1 -1
  274. package/dist/modules/grid.esm.js.map +1 -1
  275. package/dist/modules/grid.js +1 -1
  276. package/dist/modules/grid.js.map +1 -1
  277. package/dist/modules/reference/index.d.ts +136 -136
  278. package/dist/modules/src/AutoCompleter/AutoCompleter.d.ts +4 -2
  279. package/dist/modules/src/{Dvala/Cache.d.ts → Cache.d.ts} +1 -1
  280. package/dist/modules/src/builtin/specialExpressions/functions.d.ts +1 -1
  281. package/dist/modules/src/createDvala.d.ts +47 -0
  282. package/dist/modules/src/evaluator/ContextStack.d.ts +10 -2
  283. package/dist/modules/src/evaluator/effectTypes.d.ts +5 -5
  284. package/dist/modules/src/evaluator/trampoline.d.ts +10 -1
  285. package/dist/modules/src/index.d.ts +8 -5
  286. package/dist/modules/src/parser/subParsers/parseDo.d.ts +1 -1
  287. package/dist/modules/src/resume.d.ts +41 -0
  288. package/dist/modules/src/tokenizer/token.d.ts +2 -4
  289. package/dist/modules/src/tokenizer/tokenize.d.ts +1 -2
  290. package/dist/modules/src/tokenizer/tokenizers.d.ts +2 -3
  291. package/dist/modules/src/tooling.d.ts +51 -0
  292. package/dist/reference/index.d.ts +136 -136
  293. package/dist/src/AutoCompleter/AutoCompleter.d.ts +4 -2
  294. package/dist/src/Cache.d.ts +16 -0
  295. package/dist/src/builtin/specialExpressions/functions.d.ts +1 -1
  296. package/dist/src/createDvala.d.ts +47 -0
  297. package/dist/src/evaluator/ContextStack.d.ts +10 -2
  298. package/dist/src/evaluator/effectTypes.d.ts +5 -5
  299. package/dist/src/evaluator/trampoline.d.ts +10 -1
  300. package/dist/src/index.d.ts +8 -5
  301. package/dist/src/parser/subParsers/parseDo.d.ts +1 -1
  302. package/dist/src/resume.d.ts +41 -0
  303. package/dist/src/tokenizer/token.d.ts +2 -4
  304. package/dist/src/tokenizer/tokenize.d.ts +1 -2
  305. package/dist/src/tokenizer/tokenizers.d.ts +2 -3
  306. package/dist/src/tooling.d.ts +51 -0
  307. package/dist/testFramework.esm.js +1 -1
  308. package/dist/testFramework.esm.js.map +1 -1
  309. package/dist/testFramework.js +1 -1
  310. package/dist/testFramework.js.map +1 -1
  311. package/package.json +17 -6
  312. package/dist/cli/src/Dvala/Dvala.d.ts +0 -63
  313. package/dist/modules/src/Dvala/Dvala.d.ts +0 -63
  314. package/dist/modules/src/effects.d.ts +0 -110
  315. package/dist/src/Dvala/Dvala.d.ts +0 -63
  316. 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.9";
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) {
@@ -8508,217 +8791,6 @@ function getEffectRef(name) {
8508
8791
  return ref;
8509
8792
  }
8510
8793
 
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
8794
  /**
8723
8795
  * Content-addressable hashing for JSON-compatible value trees.
8724
8796
  *
@@ -10239,7 +10311,7 @@ function evaluateFunction(fn, contextStack) {
10239
10311
  });
10240
10312
  return ctx;
10241
10313
  }, {});
10242
- const undefinedSymbols = getUndefinedSymbols(fn[1], contextStack.new(context), builtin, evaluateNodeRecursive);
10314
+ const undefinedSymbols = getUndefinedSymbols$1(fn[1], contextStack.new(context), builtin, evaluateNodeRecursive);
10243
10315
  undefinedSymbols.forEach((name) => {
10244
10316
  const value = contextStack.getValue(name);
10245
10317
  if (isAny(value)) {
@@ -10642,7 +10714,6 @@ function stepSpecialExpression(node, env, k) {
10642
10714
  // --- lambda (fn / ->) ---
10643
10715
  case specialExpressionTypes['0_lambda']: {
10644
10716
  const fn = node[1][1];
10645
- const docString = (node[1][2] ?? '');
10646
10717
  const evaluatedFunc = evaluateFunction(fn, env);
10647
10718
  const min = evaluatedFunc[0].filter(arg => arg[0] !== bindingTargetTypes.rest && arg[1][1] === undefined).length;
10648
10719
  const max = evaluatedFunc[0].some(arg => arg[0] === bindingTargetTypes.rest) ? undefined : evaluatedFunc[0].length;
@@ -10654,7 +10725,7 @@ function stepSpecialExpression(node, env, k) {
10654
10725
  name: undefined,
10655
10726
  evaluatedfunction: evaluatedFunc,
10656
10727
  arity,
10657
- docString,
10728
+ docString: '',
10658
10729
  };
10659
10730
  return { type: 'Value', value: dvalaFunction, k };
10660
10731
  }
@@ -11812,129 +11883,144 @@ function dispatchPerform(effect, args, k, sourceCodeInfo, handlers, signal, snap
11812
11883
  * shape. If no more handlers remain after `next()`, the effect is unhandled.
11813
11884
  *
11814
11885
  * Each handler must call exactly one of `resume`, `suspend`, `fail`, or
11815
- * `next` before its promise resolves.
11886
+ * `next` before its promise resolves (async) or before returning (sync).
11816
11887
  *
11817
11888
  * - `resume(value)` — resolves with a `ValueStep` that continues evaluation.
11818
- * - `suspend(meta?)` — rejects with a `SuspensionSignal`.
11889
+ * - `suspend(meta?)` — throws a `SuspensionSignal`.
11819
11890
  * - `fail(msg?)` — produces an `ErrorStep` routed through `dvala.error`.
11820
11891
  * - `next()` — pass to the next matching handler in the chain.
11892
+ *
11893
+ * Handlers may return `void` (synchronous) or `Promise<void>` (async).
11894
+ * When all handlers in a chain are synchronous, this function returns
11895
+ * a `Step` synchronously, allowing use from the sync trampoline.
11821
11896
  */
11822
11897
  function dispatchHostHandler(effectName, matchingHandlers, args, k, signal, sourceCodeInfo, snapshotState) {
11823
11898
  const effectSignal = signal ?? new AbortController().signal;
11824
11899
  const argsArray = Array.from(args);
11900
+ function resolveOutcome(o, nextIndex) {
11901
+ switch (o.kind) {
11902
+ case 'step': return o.step;
11903
+ 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 }));
11904
+ case 'throw': throw o.error;
11905
+ case 'next': return tryHandler(nextIndex);
11906
+ }
11907
+ }
11825
11908
  // Recursive helper: try handler at `index`, with `next()` advancing to `index + 1`.
11826
11909
  function tryHandler(index) {
11827
11910
  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
11911
  if (effectName === 'dvala.error') {
11832
11912
  const message = typeof argsArray[0] === 'string' ? argsArray[0] : String(argsArray[0] ?? 'Unknown error');
11833
- return Promise.reject(new UserDefinedError(message, sourceCodeInfo));
11913
+ throw new UserDefinedError(message, sourceCodeInfo);
11834
11914
  }
11835
- return Promise.reject(new DvalaError(`Unhandled effect: '${effectName}'`, sourceCodeInfo));
11915
+ throw new DvalaError(`Unhandled effect: '${effectName}'`, sourceCodeInfo);
11836
11916
  }
11837
11917
  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);
11918
+ let outcome;
11919
+ let settled = false;
11920
+ function assertNotSettled(operation) {
11921
+ if (settled) {
11922
+ throw new DvalaError(`Effect handler called ${operation}() after already calling another operation`, sourceCodeInfo);
11923
+ }
11924
+ settled = true;
11925
+ }
11926
+ const ctx = {
11927
+ effectName,
11928
+ args: argsArray,
11929
+ signal: effectSignal,
11930
+ resume: (value) => {
11931
+ assertNotSettled('resume');
11932
+ if (value instanceof Promise) {
11933
+ outcome = { kind: 'asyncResume', promise: value };
11843
11934
  }
11844
- settled = true;
11845
- }
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) => {
11935
+ else {
11936
+ outcome = { kind: 'step', step: { type: 'Value', value, k } };
11937
+ }
11938
+ },
11939
+ fail: (msg) => {
11940
+ assertNotSettled('fail');
11941
+ const errorMsg = msg ?? `Effect handler failed for '${effectName}'`;
11942
+ outcome = { kind: 'step', step: { type: 'Error', error: new DvalaError(errorMsg, sourceCodeInfo), k } };
11943
+ },
11944
+ suspend: (meta) => {
11945
+ assertNotSettled('suspend');
11946
+ outcome = {
11947
+ kind: 'throw',
11948
+ error: new SuspensionSignal(k, snapshotState ? snapshotState.snapshots : [], snapshotState ? snapshotState.nextSnapshotIndex : 0, meta),
11949
+ };
11950
+ },
11951
+ next: () => {
11952
+ assertNotSettled('next');
11953
+ outcome = { kind: 'next' };
11954
+ },
11955
+ get snapshots() { return snapshotState ? [...snapshotState.snapshots] : []; },
11956
+ checkpoint: (meta) => {
11957
+ if (!snapshotState) {
11958
+ throw new DvalaError('checkpoint is not available outside effect-enabled execution', sourceCodeInfo);
11959
+ }
11960
+ const continuation = serializeToObject(k);
11961
+ const snapshot = {
11962
+ continuation,
11963
+ timestamp: Date.now(),
11964
+ index: snapshotState.nextSnapshotIndex++,
11965
+ runId: snapshotState.runId,
11966
+ ...(meta !== undefined ? { meta } : {}),
11967
+ };
11968
+ snapshotState.snapshots.push(snapshot);
11969
+ if (snapshotState.maxSnapshots !== undefined && snapshotState.snapshots.length > snapshotState.maxSnapshots) {
11970
+ snapshotState.snapshots.shift();
11971
+ }
11972
+ return snapshot;
11973
+ },
11974
+ resumeFrom: (snapshot, value) => {
11920
11975
  if (settled) {
11921
- // Handler already resolved via resume/suspend/fail/next ignore late errors
11922
- return;
11976
+ throw new DvalaError('Effect handler called resumeFrom() after already calling another operation', sourceCodeInfo);
11923
11977
  }
11924
- settled = true;
11925
- if (isSuspensionSignal(e) || isResumeFromSignal(e)) {
11926
- reject(e);
11978
+ if (!snapshotState) {
11979
+ throw new DvalaError('resumeFrom is not available outside effect-enabled execution', sourceCodeInfo);
11927
11980
  }
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
- });
11981
+ const found = snapshotState.snapshots.find(s => s.index === snapshot.index && s.runId === snapshot.runId);
11982
+ if (!found) {
11983
+ throw new DvalaError(`Invalid snapshot: no snapshot with index ${snapshot.index} found in current run`, sourceCodeInfo);
11936
11984
  }
11937
- });
11985
+ settled = true;
11986
+ outcome = { kind: 'throw', error: new ResumeFromSignal(found.continuation, value, found.index) };
11987
+ },
11988
+ };
11989
+ const handlerResult = handler(ctx);
11990
+ if (!(handlerResult instanceof Promise)) {
11991
+ // Synchronous handler — outcome must already be set
11992
+ if (!outcome) {
11993
+ throw new DvalaError(`Effect handler for '${effectName}' did not call resume(), fail(), suspend(), or next()`, sourceCodeInfo);
11994
+ }
11995
+ return resolveOutcome(outcome, index + 1);
11996
+ }
11997
+ // Async handler
11998
+ if (outcome) {
11999
+ // Handler settled synchronously before the async part
12000
+ handlerResult.catch(() => { }); // suppress unhandled rejection
12001
+ return resolveOutcome(outcome, index + 1);
12002
+ }
12003
+ // Not yet settled — wait for the handler's promise
12004
+ return handlerResult.then(() => {
12005
+ if (!outcome) {
12006
+ throw new DvalaError(`Effect handler for '${effectName}' did not call resume(), fail(), suspend(), or next()`, sourceCodeInfo);
12007
+ }
12008
+ return resolveOutcome(outcome, index + 1);
12009
+ }, (e) => {
12010
+ if (outcome) {
12011
+ // Already settled — return that result, ignore the rejection
12012
+ return resolveOutcome(outcome, index + 1);
12013
+ }
12014
+ if (isSuspensionSignal(e) || isResumeFromSignal(e)) {
12015
+ // eslint-disable-next-line ts/no-throw-literal -- SuspensionSignal/ResumeFromSignal is a signaling mechanism
12016
+ throw e;
12017
+ }
12018
+ const errorStep = {
12019
+ type: 'Error',
12020
+ error: e instanceof DvalaError ? e : new DvalaError(e instanceof Error ? e : `${e}`, sourceCodeInfo),
12021
+ k,
12022
+ };
12023
+ return errorStep;
11938
12024
  });
11939
12025
  }
11940
12026
  return tryHandler(0);
@@ -12441,7 +12527,7 @@ function tick(step, handlers, signal, snapshotState) {
12441
12527
  * Throws if any step produces a Promise (i.e., an async operation was
12442
12528
  * encountered in a synchronous context).
12443
12529
  */
12444
- function runSyncTrampoline(initial) {
12530
+ function runSyncTrampoline(initial, effectHandlers) {
12445
12531
  let step = initial;
12446
12532
  for (;;) {
12447
12533
  if (step instanceof Promise) {
@@ -12450,7 +12536,7 @@ function runSyncTrampoline(initial) {
12450
12536
  if (step.type === 'Value' && step.k.length === 0) {
12451
12537
  return step.value;
12452
12538
  }
12453
- step = tick(step);
12539
+ step = tick(step, effectHandlers);
12454
12540
  }
12455
12541
  }
12456
12542
  /**
@@ -12512,16 +12598,6 @@ function evaluate(ast, contextStack) {
12512
12598
  throw error;
12513
12599
  }
12514
12600
  }
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
12601
  /**
12526
12602
  * Evaluate a single AST node using the trampoline.
12527
12603
  * Used as the `evaluateNode` callback passed to `getUndefinedSymbols`
@@ -12563,6 +12639,27 @@ async function evaluateWithEffects(ast, contextStack, handlers, maxSnapshots, de
12563
12639
  const initial = buildInitialStep(ast.body, contextStack);
12564
12640
  return runEffectLoop(initial, handlers, signal, undefined, maxSnapshots, deserializeOptions);
12565
12641
  }
12642
+ /**
12643
+ * Evaluate an AST synchronously with effect handler support.
12644
+ *
12645
+ * Uses the sync trampoline with `effectHandlers` threaded through `tick`.
12646
+ * Throws if an async operation is encountered (e.g., an async handler
12647
+ * is used). Handlers may call `resume(value)`, `fail(msg?)`, or `next()`.
12648
+ * Calling `suspend()` will throw a runtime error.
12649
+ */
12650
+ function evaluateWithSyncEffects(ast, contextStack, effectHandlers) {
12651
+ const initial = buildInitialStep(ast.body, contextStack);
12652
+ try {
12653
+ return runSyncTrampoline(initial, effectHandlers);
12654
+ }
12655
+ catch (error) {
12656
+ if (error instanceof DvalaError && error.message.includes('Unexpected async operation')) {
12657
+ const freshInitial = buildInitialStep(ast.body, contextStack);
12658
+ return runSyncTrampoline(freshInitial, effectHandlers);
12659
+ }
12660
+ throw error;
12661
+ }
12662
+ }
12566
12663
  /**
12567
12664
  * Shared effect trampoline loop used by both `evaluateWithEffects` and
12568
12665
  * `resumeWithEffects`. Runs the trampoline to completion, suspension, or error.
@@ -12580,7 +12677,7 @@ async function runEffectLoop(initial, handlers, signal, initialSnapshotState, ma
12580
12677
  snapshots: [],
12581
12678
  nextSnapshotIndex: 0,
12582
12679
  runId: generateRunId(),
12583
- ...({}),
12680
+ ...(maxSnapshots !== undefined ? { maxSnapshots } : {}),
12584
12681
  };
12585
12682
  let step = initial;
12586
12683
  for (;;) {
@@ -12639,141 +12736,6 @@ async function runEffectLoop(initial, handlers, signal, initialSnapshotState, ma
12639
12736
  }
12640
12737
  }
12641
12738
 
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
12739
  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
12740
 
12779
12741
  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 +12835,202 @@ class Cache {
12873
12835
  }
12874
12836
  }
12875
12837
 
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
- }
12838
+ function isDvalaBundle(value) {
12839
+ return (typeof value === 'object'
12840
+ && value !== null
12841
+ && typeof value.program === 'string'
12842
+ && Array.isArray(value.fileModules));
13033
12843
  }
12844
+
12845
+ /**
12846
+ * Standalone tooling functions for tokenizing, parsing, and analysis.
12847
+ *
12848
+ * These are thin wrappers around internal utilities that do not require
12849
+ * a Dvala instance.
12850
+ */
12851
+ /**
12852
+ * Get all undefined symbols in a Dvala program.
12853
+ *
12854
+ * @param source - Dvala source code
12855
+ * @param options - optional context to treat as defined
12856
+ * @param options.bindings - host bindings to treat as defined
12857
+ * @param options.modules - modules to treat as available
12858
+ */
12859
+ function getUndefinedSymbols(source, options) {
12860
+ const modulesMap = options?.modules
12861
+ ? new Map(options.modules.map(m => [m.name, m]))
12862
+ : undefined;
12863
+ const contextStack = createContextStack({ bindings: options?.bindings }, modulesMap);
12864
+ const tokenStream = tokenize(source, false, undefined);
12865
+ const minified = minifyTokenStream(tokenStream, { removeWhiteSpace: true });
12866
+ const ast = { body: parse(minified), hasDebugData: false };
12867
+ return getUndefinedSymbols$1(ast, contextStack, builtin, evaluateNode);
12868
+ }
12869
+
13034
12870
  function assertSerializableBindings(bindings) {
13035
12871
  if (!bindings)
13036
12872
  return;
13037
- for (const [key, value] of Object.entries(bindings)) {
13038
- assertSerializable(value, `bindings["${key}"]`);
13039
- }
12873
+ for (const [key, val] of Object.entries(bindings))
12874
+ assertSerializable(val, `bindings["${key}"]`);
13040
12875
  }
13041
- function assertSerializable(value, path) {
13042
- if (value === null || value === undefined)
12876
+ function assertSerializable(val, path) {
12877
+ if (val === null || val === undefined)
13043
12878
  return;
13044
- if (typeof value === 'boolean' || typeof value === 'string')
12879
+ if (typeof val === 'boolean' || typeof val === 'string')
13045
12880
  return;
13046
- if (typeof value === 'number') {
13047
- if (!Number.isFinite(value))
13048
- throw new TypeError(`${path} is not serializable (${value})`);
12881
+ if (typeof val === 'number') {
12882
+ if (!Number.isFinite(val))
12883
+ throw new TypeError(`${path} is not serializable (${val})`);
13049
12884
  return;
13050
12885
  }
13051
- if (typeof value === 'function')
12886
+ if (typeof val === 'function')
13052
12887
  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) {
13056
- return;
13057
- }
13058
- if (REGEXP_SYMBOL in value || EFFECT_SYMBOL in value)
12888
+ if (typeof val === 'object') {
12889
+ if (FUNCTION_SYMBOL in val || REGEXP_SYMBOL in val || EFFECT_SYMBOL in val)
13059
12890
  return;
13060
- if (Array.isArray(value)) {
13061
- value.forEach((item, i) => assertSerializable(item, `${path}[${i}]`));
12891
+ if (Array.isArray(val)) {
12892
+ val.forEach((item, i) => assertSerializable(item, `${path}[${i}]`));
13062
12893
  return;
13063
12894
  }
13064
- if (Object.getPrototypeOf(value) !== Object.prototype)
12895
+ if (Object.getPrototypeOf(val) !== Object.prototype)
13065
12896
  throw new TypeError(`${path} is not serializable (not a plain object)`);
13066
- for (const [k, v] of Object.entries(value))
12897
+ for (const [k, v] of Object.entries(val))
13067
12898
  assertSerializable(v, `${path}.${k}`);
13068
12899
  return;
13069
12900
  }
13070
12901
  throw new TypeError(`${path} is not serializable`);
13071
12902
  }
12903
+ function createDvala(options) {
12904
+ initCoreDvalaSources();
12905
+ const modules = options?.modules
12906
+ ? new Map(options.modules.map(m => [m.name, m]))
12907
+ : undefined;
12908
+ const factoryBindings = options?.bindings;
12909
+ const factoryEffectHandlers = options?.effectHandlers;
12910
+ const debug = options?.debug ?? false;
12911
+ const cache = options?.cache ? new Cache(options.cache) : null;
12912
+ function buildAst(source, filePath) {
12913
+ if (!filePath && cache) {
12914
+ const cached = cache.get(source);
12915
+ if (cached)
12916
+ return cached;
12917
+ }
12918
+ const tokenStream = tokenize(source, debug, filePath);
12919
+ const minified = minifyTokenStream(tokenStream, { removeWhiteSpace: true });
12920
+ const ast = { body: parse(minified), hasDebugData: debug };
12921
+ if (!filePath)
12922
+ cache?.set(source, ast);
12923
+ return ast;
12924
+ }
12925
+ function mergeBindings(runBindings) {
12926
+ if (!factoryBindings && !runBindings)
12927
+ return undefined;
12928
+ return { ...factoryBindings, ...runBindings };
12929
+ }
12930
+ function mergeEffectHandlers(runEffectHandlers) {
12931
+ if (!factoryEffectHandlers && !runEffectHandlers)
12932
+ return undefined;
12933
+ // Run handlers first (checked first), factory handlers fill in the rest.
12934
+ // For same key, run overrides factory.
12935
+ const result = { ...runEffectHandlers };
12936
+ if (factoryEffectHandlers) {
12937
+ for (const [k, v] of Object.entries(factoryEffectHandlers)) {
12938
+ if (!(k in result))
12939
+ result[k] = v;
12940
+ }
12941
+ }
12942
+ return result;
12943
+ }
12944
+ function assertNotPureWithHandlers(pure, effectHandlers) {
12945
+ if (!pure)
12946
+ return;
12947
+ const hasEffectHandlers = effectHandlers && Object.keys(effectHandlers).length > 0;
12948
+ if (hasEffectHandlers) {
12949
+ throw new TypeError('Cannot use pure mode with effect handlers');
12950
+ }
12951
+ }
12952
+ return {
12953
+ run(source, runOptions) {
12954
+ assertSerializableBindings(runOptions?.bindings);
12955
+ const bindings = mergeBindings(runOptions?.bindings);
12956
+ const effectHandlers = mergeEffectHandlers(runOptions?.effectHandlers);
12957
+ const pure = runOptions?.pure ?? false;
12958
+ assertNotPureWithHandlers(pure, effectHandlers);
12959
+ const contextStack = createContextStack({ bindings }, modules, pure);
12960
+ if (isDvalaBundle(source)) {
12961
+ const savedPure = contextStack.pure;
12962
+ contextStack.pure = true;
12963
+ for (const [name, fileSource] of source.fileModules) {
12964
+ const fileAst = buildAst(fileSource);
12965
+ const moduleContextStack = contextStack.create({});
12966
+ contextStack.registerValueModule(name, evaluate(fileAst, moduleContextStack));
12967
+ }
12968
+ contextStack.pure = savedPure;
12969
+ const ast = buildAst(source.program);
12970
+ const result = evaluate(ast, contextStack);
12971
+ if (result instanceof Promise)
12972
+ throw new TypeError('Unexpected async result in run(). Use runAsync() for async operations.');
12973
+ return result;
12974
+ }
12975
+ const ast = buildAst(source, runOptions?.filePath);
12976
+ if (effectHandlers) {
12977
+ return evaluateWithSyncEffects(ast, contextStack, effectHandlers);
12978
+ }
12979
+ const result = evaluate(ast, contextStack);
12980
+ if (result instanceof Promise) {
12981
+ throw new TypeError('Unexpected async result in run(). Use runAsync() for async operations.');
12982
+ }
12983
+ return result;
12984
+ },
12985
+ async runAsync(source, runOptions) {
12986
+ assertSerializableBindings(runOptions?.bindings);
12987
+ const bindings = mergeBindings(runOptions?.bindings);
12988
+ const effectHandlers = mergeEffectHandlers(runOptions?.effectHandlers);
12989
+ const pure = runOptions?.pure ?? false;
12990
+ assertNotPureWithHandlers(pure, effectHandlers);
12991
+ try {
12992
+ const contextStack = createContextStack({ bindings }, modules, pure);
12993
+ if (isDvalaBundle(source)) {
12994
+ const savedPure = contextStack.pure;
12995
+ contextStack.pure = true;
12996
+ for (const [name, fileSource] of source.fileModules) {
12997
+ const fileAst = buildAst(fileSource);
12998
+ const moduleContextStack = contextStack.create({});
12999
+ contextStack.registerValueModule(name, evaluate(fileAst, moduleContextStack));
13000
+ }
13001
+ contextStack.pure = savedPure;
13002
+ }
13003
+ const programSource = isDvalaBundle(source) ? source.program : source;
13004
+ const ast = buildAst(programSource);
13005
+ const result = await evaluateWithEffects(ast, contextStack, effectHandlers, runOptions?.maxSnapshots, {
13006
+ values: bindings,
13007
+ modules,
13008
+ });
13009
+ if (result.type === 'completed') {
13010
+ return { ...result, definedBindings: contextStack.getModuleScopeBindings() };
13011
+ }
13012
+ return result;
13013
+ }
13014
+ catch (error) {
13015
+ if (error instanceof DvalaError) {
13016
+ return { type: 'error', error };
13017
+ }
13018
+ if (error instanceof TypeError) {
13019
+ throw error;
13020
+ }
13021
+ return { type: 'error', error: new DvalaError(`${error}`, undefined) };
13022
+ }
13023
+ },
13024
+ getUndefinedSymbols(source) {
13025
+ const modulesList = modules ? [...modules.values()] : undefined;
13026
+ return getUndefinedSymbols(source, { bindings: factoryBindings, modules: modulesList });
13027
+ },
13028
+ getAutoCompleter(program, position) {
13029
+ const params = { bindings: factoryBindings };
13030
+ return new AutoCompleter(program, position, params);
13031
+ },
13032
+ };
13033
+ }
13072
13034
 
13073
13035
  var assertionModuleSource = "{}";
13074
13036
 
@@ -14448,7 +14410,7 @@ const assertModule = {
14448
14410
  docs: moduleDocs$5,
14449
14411
  };
14450
14412
 
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";
14413
+ 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
14414
 
14453
14415
  const moduleDocs$4 = {
14454
14416
  'cell-every?': {
@@ -34681,7 +34643,7 @@ function runTest({ testPath: filePath, testNamePattern }) {
34681
34643
  }
34682
34644
  else {
34683
34645
  try {
34684
- const dvala = new Dvala({ debug: true, modules: allBuiltinModules });
34646
+ const dvala = createDvala({ debug: true, modules: allBuiltinModules });
34685
34647
  const bindings = getBindings(includedFilePaths, dvala);
34686
34648
  dvala.run(testChunkProgram.program, {
34687
34649
  bindings,
@@ -37064,7 +37026,7 @@ function getArgumentInfo(fmt, reference) {
37064
37026
  }
37065
37027
 
37066
37028
  /* eslint-disable no-console */
37067
- const dvala = new Dvala({ debug: false });
37029
+ const dvala = createDvala({ debug: false });
37068
37030
  function getCliFunctionExamples(fmt, reference) {
37069
37031
  const { examples } = reference;
37070
37032
  return examples
@@ -37671,19 +37633,15 @@ const helpRegExp = new RegExp(`^\`help\\s+(${polishSymbolFirstCharacterClass}${p
37671
37633
  const expressions = [...normalExpressionKeys, ...specialExpressionKeys];
37672
37634
  const config = processArguments(process.argv.slice(2));
37673
37635
  const cliModules = getCliModules();
37674
- function createDvala(context, pure) {
37675
- const _dvala = new Dvala({ debug: true, modules: [...allBuiltinModules, ...cliModules] });
37636
+ function makeDvala(bindings, pure) {
37637
+ const runner = createDvala({ debug: true, modules: [...allBuiltinModules, ...cliModules], bindings });
37676
37638
  return {
37677
- run: (program) => _dvala.run(program, {
37678
- globalContext: context,
37679
- globalModuleScope: true,
37680
- pure,
37681
- }),
37639
+ run: (program) => runner.run(program, { pure }),
37682
37640
  };
37683
37641
  }
37684
37642
  switch (config.subcommand) {
37685
37643
  case 'run': {
37686
- const dvala = createDvala(config.context, config.pure);
37644
+ const dvala = makeDvala(config.context, config.pure);
37687
37645
  try {
37688
37646
  const content = fs.readFileSync(config.filename, { encoding: 'utf-8' });
37689
37647
  const result = dvala.run(content);
@@ -37699,7 +37657,7 @@ switch (config.subcommand) {
37699
37657
  break;
37700
37658
  }
37701
37659
  case 'run-bundle': {
37702
- const dvala = createDvala(config.context, config.pure);
37660
+ const dvala = makeDvala(config.context, config.pure);
37703
37661
  try {
37704
37662
  const content = fs.readFileSync(config.filename, { encoding: 'utf-8' });
37705
37663
  let parsed;
@@ -37727,7 +37685,7 @@ switch (config.subcommand) {
37727
37685
  break;
37728
37686
  }
37729
37687
  case 'eval': {
37730
- const dvala = createDvala(config.context, config.pure);
37688
+ const dvala = makeDvala(config.context, config.pure);
37731
37689
  try {
37732
37690
  const result = dvala.run(config.expression);
37733
37691
  if (config.printResult) {
@@ -37767,12 +37725,12 @@ switch (config.subcommand) {
37767
37725
  }
37768
37726
  case 'repl': {
37769
37727
  if (config.loadFilename) {
37770
- const dvala = createDvala(config.context, false);
37728
+ const dvala = makeDvala(config.context, false);
37771
37729
  const content = fs.readFileSync(config.loadFilename, { encoding: 'utf-8' });
37772
37730
  const result = dvala.run(content);
37773
37731
  if (result !== null && typeof result === 'object' && !Array.isArray(result)) {
37774
37732
  for (const [key, value] of Object.entries(result)) {
37775
- config.context[key] = { value: asAny(value) };
37733
+ config.context[key] = value;
37776
37734
  }
37777
37735
  }
37778
37736
  }
@@ -37803,13 +37761,12 @@ function runDvalaTest(testPath, testNamePattern) {
37803
37761
  if (!success)
37804
37762
  process.exit(1);
37805
37763
  }
37806
- async function execute(expression, context, readLine) {
37807
- const _dvala = new Dvala({ debug: true, modules: [...allBuiltinModules, ...cliModules] });
37764
+ async function execute(expression, bindings, readLine) {
37765
+ const _dvala = createDvala({ debug: true, modules: [...allBuiltinModules, ...cliModules] });
37808
37766
  try {
37809
- const result = await _dvala.async.run(expression, {
37810
- globalContext: context,
37811
- globalModuleScope: true,
37812
- handlers: {
37767
+ const runResult = await _dvala.runAsync(expression, {
37768
+ bindings,
37769
+ effectHandlers: {
37813
37770
  'dvala.io.read-line': async ({ args, resume }) => {
37814
37771
  const message = typeof args[0] === 'string' ? args[0] : '';
37815
37772
  const answer = await readLine(message);
@@ -37817,18 +37774,21 @@ async function execute(expression, context, readLine) {
37817
37774
  },
37818
37775
  },
37819
37776
  });
37777
+ if (runResult.type === 'error')
37778
+ throw runResult.error;
37779
+ const result = runResult.type === 'completed' ? runResult.value : null;
37820
37780
  historyResults.unshift(result);
37821
37781
  if (historyResults.length > 9) {
37822
37782
  historyResults.length = 9;
37823
37783
  }
37824
- setReplHistoryVariables(context);
37784
+ const newBindings = { ...bindings, ...(runResult.type === 'completed' ? runResult.definedBindings : {}) };
37785
+ setReplHistoryVariables(newBindings);
37825
37786
  console.log(stringifyValue(result, false));
37826
- return true;
37787
+ return newBindings;
37827
37788
  }
37828
37789
  catch (error) {
37829
37790
  printErrorMessage(`${error}`);
37830
- context['*e*'] = { value: getErrorMessage(error) };
37831
- return false;
37791
+ return { ...bindings, '*e*': getErrorMessage(error) };
37832
37792
  }
37833
37793
  }
37834
37794
  function getErrorMessage(error) {
@@ -37836,18 +37796,11 @@ function getErrorMessage(error) {
37836
37796
  return error.message;
37837
37797
  return 'Unknown error';
37838
37798
  }
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*'];
37799
+ function setReplHistoryVariables(bindings) {
37800
+ for (let i = 1; i <= 9; i++)
37801
+ delete bindings[`*${i}*`];
37849
37802
  historyResults.forEach((value, i) => {
37850
- context[`*${i + 1}*`] = { value: asAny(value) };
37803
+ bindings[`*${i + 1}*`] = value;
37851
37804
  });
37852
37805
  }
37853
37806
  function parseOption(args, i) {
@@ -37890,7 +37843,7 @@ function parseContextOptions(args, startIndex) {
37890
37843
  }
37891
37844
  try {
37892
37845
  Object.entries(JSON.parse(parsed.argument)).forEach(([key, value]) => {
37893
- options.context[key] = { value: asAny(value) };
37846
+ options.context[key] = value;
37894
37847
  });
37895
37848
  }
37896
37849
  catch (e) {
@@ -37908,7 +37861,7 @@ function parseContextOptions(args, startIndex) {
37908
37861
  try {
37909
37862
  const contextString = fs.readFileSync(parsed.argument, { encoding: 'utf-8' });
37910
37863
  Object.entries(JSON.parse(contextString)).forEach(([key, value]) => {
37911
- options.context[key] = { value: asAny(value) };
37864
+ options.context[key] = value;
37912
37865
  });
37913
37866
  }
37914
37867
  catch (e) {
@@ -38140,9 +38093,10 @@ function processArguments(args) {
38140
38093
  }
38141
38094
  }
38142
38095
  }
38143
- function runREPL(context) {
38096
+ function runREPL(initialBindings) {
38144
38097
  console.log(`Welcome to Dvala v${version}.
38145
38098
  Type ${fmt.italic('`help')} for more information.`);
38099
+ let bindings = initialBindings;
38146
38100
  const rl = createReadlineInterface({
38147
38101
  completer,
38148
38102
  historySize: HIST_SIZE,
@@ -38169,7 +38123,7 @@ Type ${fmt.italic('`help')} for more information.`);
38169
38123
  printHelp();
38170
38124
  break;
38171
38125
  case '`context':
38172
- printContext(context);
38126
+ printContext(bindings);
38173
38127
  break;
38174
38128
  case '`quit':
38175
38129
  rl.close();
@@ -38179,7 +38133,7 @@ Type ${fmt.italic('`help')} for more information.`);
38179
38133
  }
38180
38134
  }
38181
38135
  else if (line) {
38182
- await execute(line, context, readLine);
38136
+ bindings = await execute(line, bindings, readLine);
38183
38137
  }
38184
38138
  rl.prompt();
38185
38139
  }).on('close', () => {
@@ -38248,14 +38202,14 @@ Global options:
38248
38202
  With no subcommand, starts an interactive REPL.
38249
38203
  `.trim());
38250
38204
  }
38251
- function printContext(context) {
38252
- const keys = Object.keys(context);
38205
+ function printContext(bindings) {
38206
+ const keys = Object.keys(bindings);
38253
38207
  if (keys.length === 0) {
38254
38208
  console.log('[empty]\n');
38255
38209
  }
38256
38210
  else {
38257
38211
  keys.sort().forEach((x) => {
38258
- console.log(`${x} = ${formatValue(stringifyValue(context[x].value))}`);
38212
+ console.log(`${x} = ${formatValue(stringifyValue(bindings[x]))}`);
38259
38213
  });
38260
38214
  console.log();
38261
38215
  }
@@ -38270,8 +38224,8 @@ function completer(line) {
38270
38224
  if (expressionMatch)
38271
38225
  return [expressions.filter(c => c.startsWith(expressionMatch[2])).map(c => `${expressionMatch[1]}${c} `), line];
38272
38226
  // TODO, add reserved names
38273
- const context = config.context ?? {};
38274
- const names = Array.from(new Set([...Object.keys(context)]));
38227
+ const replBindings = config.context ?? {};
38228
+ const names = Array.from(new Set([...Object.keys(replBindings)]));
38275
38229
  const nameMatch = nameRegExp.exec(line);
38276
38230
  if (nameMatch)
38277
38231
  return [names.filter(c => c.startsWith(nameMatch[2])).map(c => `${nameMatch[1]}${c} `), line];