@mojir/dvala 0.0.7 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/cli.js +1622 -1620
- package/dist/cli/reference/examples.d.ts +1 -1
- package/dist/cli/src/AutoCompleter/AutoCompleter.d.ts +4 -2
- package/dist/cli/src/{Dvala/Cache.d.ts → Cache.d.ts} +1 -1
- package/dist/cli/src/builtin/specialExpressions/functions.d.ts +1 -1
- package/dist/cli/src/createDvala.d.ts +47 -0
- package/dist/cli/src/evaluator/ContextStack.d.ts +10 -2
- package/dist/cli/src/evaluator/effectTypes.d.ts +24 -6
- package/dist/cli/src/evaluator/suspension.d.ts +6 -0
- package/dist/cli/src/evaluator/trampoline.d.ts +15 -1
- package/dist/cli/src/parser/subParsers/parseDo.d.ts +1 -1
- package/dist/cli/src/tokenizer/token.d.ts +2 -4
- package/dist/cli/src/tokenizer/tokenize.d.ts +1 -2
- package/dist/cli/src/tokenizer/tokenizers.d.ts +2 -3
- package/dist/cli/src/tooling.d.ts +51 -0
- package/dist/debug.esm.js +1 -1
- package/dist/debug.esm.js.map +1 -1
- package/dist/debug.js +1 -1
- package/dist/debug.js.map +1 -1
- package/dist/dvala.iife.js +1 -1
- package/dist/dvala.iife.js.map +1 -1
- package/dist/full.esm.js +1 -1
- package/dist/full.esm.js.map +1 -1
- package/dist/full.js +1 -1
- package/dist/full.js.map +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-server/common/utils.d.ts +2 -0
- package/dist/mcp-server/mcp-server/src/server.d.ts +1 -0
- package/dist/mcp-server/reference/api.d.ts +73 -0
- package/dist/mcp-server/reference/datatype.d.ts +3 -0
- package/dist/mcp-server/reference/examples.d.ts +11 -0
- package/dist/mcp-server/reference/index.d.ts +195 -0
- package/dist/mcp-server/reference/shorthand.d.ts +3 -0
- package/dist/mcp-server/server.js +36377 -0
- package/dist/mcp-server/src/AutoCompleter/AutoCompleter.d.ts +27 -0
- package/dist/{src/Dvala → mcp-server/src}/Cache.d.ts +1 -1
- package/dist/mcp-server/src/allModules.d.ts +2 -0
- package/dist/mcp-server/src/builtin/bindingNode.d.ts +11 -0
- package/dist/mcp-server/src/builtin/core/array.d.ts +2 -0
- package/dist/mcp-server/src/builtin/core/assertion.d.ts +2 -0
- package/dist/mcp-server/src/builtin/core/bitwise.d.ts +2 -0
- package/dist/mcp-server/src/builtin/core/collection.d.ts +2 -0
- package/dist/mcp-server/src/builtin/core/functional.d.ts +2 -0
- package/dist/mcp-server/src/builtin/core/math.d.ts +2 -0
- package/dist/mcp-server/src/builtin/core/meta.d.ts +3 -0
- package/dist/mcp-server/src/builtin/core/misc.d.ts +2 -0
- package/dist/mcp-server/src/builtin/core/object.d.ts +2 -0
- package/dist/mcp-server/src/builtin/core/predicates.d.ts +2 -0
- package/dist/mcp-server/src/builtin/core/regexp.d.ts +2 -0
- package/dist/mcp-server/src/builtin/core/sequence.d.ts +2 -0
- package/dist/mcp-server/src/builtin/core/string.d.ts +2 -0
- package/dist/mcp-server/src/builtin/core/vector.d.ts +2 -0
- package/dist/mcp-server/src/builtin/index.d.ts +13 -0
- package/dist/mcp-server/src/builtin/interface.d.ts +113 -0
- package/dist/mcp-server/src/builtin/modules/assertion/docs.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/assertion/index.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/bitwise/index.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/collection/index.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/convert/index.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/functional/index.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/grid/docs.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/grid/fromArray.d.ts +8 -0
- package/dist/mcp-server/src/builtin/modules/grid/index.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/grid/transpose.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/interface.d.ts +28 -0
- package/dist/mcp-server/src/builtin/modules/linear-algebra/docs.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/calcFractionalRanks.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/collinear.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/corrleation.d.ts +8 -0
- package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/covariance.d.ts +4 -0
- package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/dot.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/gaussJordanElimination.d.ts +7 -0
- package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/getUnit.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/isZeroVector.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/kendallTau.d.ts +10 -0
- package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/length.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/pearsonCorr.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/scale.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/solve.d.ts +8 -0
- package/dist/mcp-server/src/builtin/modules/linear-algebra/helpers/subtract.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/linear-algebra/index.d.ts +4 -0
- package/dist/mcp-server/src/builtin/modules/math/index.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/matrix/docs.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/matrix/helpers/adjugate.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/matrix/helpers/band.d.ts +9 -0
- package/dist/mcp-server/src/builtin/modules/matrix/helpers/cofactor.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/matrix/helpers/determinant.d.ts +6 -0
- package/dist/mcp-server/src/builtin/modules/matrix/helpers/inverse.d.ts +6 -0
- package/dist/mcp-server/src/builtin/modules/matrix/helpers/isBanded.d.ts +11 -0
- package/dist/mcp-server/src/builtin/modules/matrix/helpers/isDiagonal.d.ts +10 -0
- package/dist/mcp-server/src/builtin/modules/matrix/helpers/isIdentity.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/matrix/helpers/isOrthogonal.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/matrix/helpers/isSquare.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/matrix/helpers/isSymetric.d.ts +8 -0
- package/dist/mcp-server/src/builtin/modules/matrix/helpers/isTriangular.d.ts +13 -0
- package/dist/mcp-server/src/builtin/modules/matrix/helpers/matrixMultiply.d.ts +7 -0
- package/dist/mcp-server/src/builtin/modules/matrix/helpers/minor.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/matrix/helpers/norm1.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/matrix/helpers/trace.d.ts +8 -0
- package/dist/mcp-server/src/builtin/modules/matrix/index.d.ts +4 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/binomialCefficient.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/combinations.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/derangements.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/divisors.d.ts +4 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/docs.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/factorial.d.ts +3 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/index.d.ts +4 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/partitions.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/permutations.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/powerSet.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/primeFactors.d.ts +11 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/abundant.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/arithmetic.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/bell.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/bernoulli.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/catalan.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/collatz.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/composite.d.ts +3 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/deficient.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/factorial.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/fibonacci.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/geometric.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/golomb.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/happy.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/index.d.ts +27 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/juggler.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/lookAndSay.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/lucas.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/lucky.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/mersenne.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/padovan.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/partition.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/pell.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/perfect.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/perfectCube.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/perfectPower.d.ts +10 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/perfectSquare.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/poligonal.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/prime.d.ts +3 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/recaman.d.ts +9 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/sylvester.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/thueMorse.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/number-theory/sequences/tribonacci.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/sequence/index.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/string/index.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/vector/bincount.d.ts +9 -0
- package/dist/mcp-server/src/builtin/modules/vector/calcMad.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/vector/calcMean.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/vector/calcMedad.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/vector/calcMedian.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/vector/calcStdDev.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/vector/calcVariance.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/vector/docs.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/vector/entropy.d.ts +8 -0
- package/dist/mcp-server/src/builtin/modules/vector/histogram.d.ts +9 -0
- package/dist/mcp-server/src/builtin/modules/vector/index.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/vector/mode.d.ts +6 -0
- package/dist/mcp-server/src/builtin/modules/vector/outliers.d.ts +7 -0
- package/dist/mcp-server/src/builtin/modules/vector/percentile.d.ts +7 -0
- package/dist/mcp-server/src/builtin/modules/vector/quartiles.d.ts +1 -0
- package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/entropy.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/giniCoefficient.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/index.d.ts +13 -0
- package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/iqr.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/kurtosis.d.ts +5 -0
- package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/mad.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/mean.d.ts +4 -0
- package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/medad.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/median.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/prod.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/rms.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/skewness.d.ts +3 -0
- package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/span.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/standardDeviation.d.ts +3 -0
- package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/sum.d.ts +2 -0
- package/dist/mcp-server/src/builtin/modules/vector/reductionFunctions/variance.d.ts +3 -0
- package/dist/mcp-server/src/builtin/normalExpressions/index.d.ts +9 -0
- package/dist/mcp-server/src/builtin/normalExpressions/initCoreDvala.d.ts +1 -0
- package/dist/mcp-server/src/builtin/specialExpressionTypes.d.ts +24 -0
- package/dist/mcp-server/src/builtin/specialExpressions/and.d.ts +6 -0
- package/dist/mcp-server/src/builtin/specialExpressions/array.d.ts +6 -0
- package/dist/mcp-server/src/builtin/specialExpressions/block.d.ts +7 -0
- package/dist/mcp-server/src/builtin/specialExpressions/cond.d.ts +6 -0
- package/dist/mcp-server/src/builtin/specialExpressions/defined.d.ts +5 -0
- package/dist/mcp-server/src/builtin/specialExpressions/effect.d.ts +5 -0
- package/dist/mcp-server/src/builtin/specialExpressions/functions.d.ts +6 -0
- package/dist/mcp-server/src/builtin/specialExpressions/if.d.ts +6 -0
- package/dist/mcp-server/src/builtin/specialExpressions/import.d.ts +6 -0
- package/dist/mcp-server/src/builtin/specialExpressions/let.d.ts +6 -0
- package/dist/mcp-server/src/builtin/specialExpressions/loop.d.ts +6 -0
- package/dist/mcp-server/src/builtin/specialExpressions/loops.d.ts +9 -0
- package/dist/mcp-server/src/builtin/specialExpressions/match.d.ts +7 -0
- package/dist/mcp-server/src/builtin/specialExpressions/object.d.ts +6 -0
- package/dist/mcp-server/src/builtin/specialExpressions/or.d.ts +6 -0
- package/dist/mcp-server/src/builtin/specialExpressions/parallel.d.ts +6 -0
- package/dist/mcp-server/src/builtin/specialExpressions/perform.d.ts +6 -0
- package/dist/mcp-server/src/builtin/specialExpressions/qq.d.ts +6 -0
- package/dist/mcp-server/src/builtin/specialExpressions/race.d.ts +6 -0
- package/dist/mcp-server/src/builtin/specialExpressions/recur.d.ts +5 -0
- package/dist/mcp-server/src/builtin/specialExpressions/unless.d.ts +6 -0
- package/dist/mcp-server/src/builtin/utils.d.ts +6 -0
- package/dist/mcp-server/src/bundler/interface.d.ts +15 -0
- package/dist/mcp-server/src/constants/constants.d.ts +19 -0
- package/dist/mcp-server/src/createDvala.d.ts +47 -0
- package/dist/mcp-server/src/errors.d.ts +24 -0
- package/dist/mcp-server/src/evaluator/ContextStack.d.ts +70 -0
- package/dist/mcp-server/src/evaluator/contentHash.d.ts +21 -0
- package/dist/mcp-server/src/evaluator/dedupSubTrees.d.ts +37 -0
- package/dist/mcp-server/src/evaluator/effectRef.d.ts +27 -0
- package/dist/mcp-server/src/evaluator/effectTypes.d.ts +199 -0
- package/dist/mcp-server/src/evaluator/frames.d.ts +513 -0
- package/dist/mcp-server/src/evaluator/interface.d.ts +14 -0
- package/dist/mcp-server/src/evaluator/standardEffects.d.ts +50 -0
- package/dist/mcp-server/src/evaluator/step.d.ts +175 -0
- package/dist/mcp-server/src/evaluator/suspension.d.ts +92 -0
- package/dist/mcp-server/src/evaluator/trampoline.d.ts +138 -0
- package/dist/mcp-server/src/getUndefinedSymbols/index.d.ts +7 -0
- package/dist/mcp-server/src/initReferenceData.d.ts +1 -0
- package/dist/mcp-server/src/interface.d.ts +7 -0
- package/dist/mcp-server/src/parser/ParserContext.d.ts +20 -0
- package/dist/mcp-server/src/parser/getPrecedence.d.ts +3 -0
- package/dist/mcp-server/src/parser/helpers.d.ts +19 -0
- package/dist/mcp-server/src/parser/index.d.ts +5 -0
- package/dist/mcp-server/src/parser/subParsers/parseArray.d.ts +3 -0
- package/dist/mcp-server/src/parser/subParsers/parseBindingTarget.d.ts +8 -0
- package/dist/mcp-server/src/parser/subParsers/parseCond.d.ts +4 -0
- package/dist/mcp-server/src/parser/subParsers/parseDo.d.ts +3 -0
- package/dist/mcp-server/src/parser/subParsers/parseExpression.d.ts +5 -0
- package/dist/mcp-server/src/parser/subParsers/parseForOrDoseq.d.ts +4 -0
- package/dist/mcp-server/src/parser/subParsers/parseFunction.d.ts +4 -0
- package/dist/mcp-server/src/parser/subParsers/parseFunctionCall.d.ts +3 -0
- package/dist/mcp-server/src/parser/subParsers/parseIfOrUnless.d.ts +5 -0
- package/dist/mcp-server/src/parser/subParsers/parseImplicitBlock.d.ts +5 -0
- package/dist/mcp-server/src/parser/subParsers/parseLet.d.ts +4 -0
- package/dist/mcp-server/src/parser/subParsers/parseLoop.d.ts +4 -0
- package/dist/mcp-server/src/parser/subParsers/parseMatch.d.ts +4 -0
- package/dist/mcp-server/src/parser/subParsers/parseNumber.d.ts +3 -0
- package/dist/mcp-server/src/parser/subParsers/parseObject.d.ts +3 -0
- package/dist/mcp-server/src/parser/subParsers/parseOperand.d.ts +3 -0
- package/dist/mcp-server/src/parser/subParsers/parseRegexpShorthand.d.ts +3 -0
- package/dist/mcp-server/src/parser/subParsers/parseReservedSymbol.d.ts +3 -0
- package/dist/mcp-server/src/parser/subParsers/parseString.d.ts +4 -0
- package/dist/mcp-server/src/parser/subParsers/parseSymbol.d.ts +3 -0
- package/dist/mcp-server/src/parser/types.d.ts +128 -0
- package/dist/mcp-server/src/tokenizer/minifyTokenStream.d.ts +4 -0
- package/dist/mcp-server/src/tokenizer/operators.d.ts +12 -0
- package/dist/mcp-server/src/tokenizer/reservedNames.d.ts +65 -0
- package/dist/mcp-server/src/tokenizer/token.d.ts +82 -0
- package/dist/mcp-server/src/tokenizer/tokenize.d.ts +7 -0
- package/dist/mcp-server/src/tokenizer/tokenizers.d.ts +13 -0
- package/dist/mcp-server/src/tooling.d.ts +51 -0
- package/dist/mcp-server/src/transformer/index.d.ts +2 -0
- package/dist/mcp-server/src/typeGuards/annotatedCollections.d.ts +16 -0
- package/dist/mcp-server/src/typeGuards/array.d.ts +9 -0
- package/dist/mcp-server/src/typeGuards/astNode.d.ts +19 -0
- package/dist/mcp-server/src/typeGuards/dvala.d.ts +26 -0
- package/dist/mcp-server/src/typeGuards/dvalaFunction.d.ts +9 -0
- package/dist/mcp-server/src/typeGuards/index.d.ts +7 -0
- package/dist/mcp-server/src/typeGuards/number.d.ts +66 -0
- package/dist/mcp-server/src/typeGuards/string.d.ts +15 -0
- package/dist/mcp-server/src/untokenizer/index.d.ts +2 -0
- package/dist/mcp-server/src/utils/arity.d.ts +10 -0
- package/dist/mcp-server/src/utils/debug/debugTools.d.ts +1 -0
- package/dist/mcp-server/src/utils/debug/getCodeMarker.d.ts +2 -0
- package/dist/mcp-server/src/utils/debug/getSourceCodeInfo.d.ts +2 -0
- package/dist/mcp-server/src/utils/docString/generateDocString.d.ts +4 -0
- package/dist/mcp-server/src/utils/getAssertionError.d.ts +3 -0
- package/dist/mcp-server/src/utils/index.d.ts +14 -0
- package/dist/mcp-server/src/utils/maybePromise.d.ts +54 -0
- package/dist/mcp-server/src/utils/symbols.d.ts +3 -0
- package/dist/modules/grid.esm.js +1 -1
- package/dist/modules/grid.esm.js.map +1 -1
- package/dist/modules/grid.js +1 -1
- package/dist/modules/grid.js.map +1 -1
- package/dist/modules/reference/index.d.ts +136 -136
- package/dist/modules/src/AutoCompleter/AutoCompleter.d.ts +4 -2
- package/dist/modules/src/{Dvala/Cache.d.ts → Cache.d.ts} +1 -1
- package/dist/modules/src/builtin/specialExpressions/functions.d.ts +1 -1
- package/dist/modules/src/createDvala.d.ts +47 -0
- package/dist/modules/src/evaluator/ContextStack.d.ts +10 -2
- package/dist/modules/src/evaluator/effectTypes.d.ts +24 -6
- package/dist/modules/src/evaluator/suspension.d.ts +6 -0
- package/dist/modules/src/evaluator/trampoline.d.ts +15 -1
- package/dist/modules/src/index.d.ts +10 -5
- package/dist/modules/src/parser/subParsers/parseDo.d.ts +1 -1
- package/dist/modules/src/resume.d.ts +41 -0
- package/dist/modules/src/retrigger.d.ts +39 -0
- package/dist/modules/src/tokenizer/token.d.ts +2 -4
- package/dist/modules/src/tokenizer/tokenize.d.ts +1 -2
- package/dist/modules/src/tokenizer/tokenizers.d.ts +2 -3
- package/dist/modules/src/tooling.d.ts +51 -0
- package/dist/reference/index.d.ts +136 -136
- package/dist/src/AutoCompleter/AutoCompleter.d.ts +4 -2
- package/dist/src/Cache.d.ts +16 -0
- package/dist/src/builtin/specialExpressions/functions.d.ts +1 -1
- package/dist/src/createDvala.d.ts +47 -0
- package/dist/src/evaluator/ContextStack.d.ts +10 -2
- package/dist/src/evaluator/effectTypes.d.ts +24 -6
- package/dist/src/evaluator/suspension.d.ts +6 -0
- package/dist/src/evaluator/trampoline.d.ts +15 -1
- package/dist/src/index.d.ts +10 -5
- package/dist/src/parser/subParsers/parseDo.d.ts +1 -1
- package/dist/src/resume.d.ts +41 -0
- package/dist/src/retrigger.d.ts +39 -0
- package/dist/src/tokenizer/token.d.ts +2 -4
- package/dist/src/tokenizer/tokenize.d.ts +1 -2
- package/dist/src/tokenizer/tokenizers.d.ts +2 -3
- package/dist/src/tooling.d.ts +51 -0
- package/dist/testFramework.esm.js +1 -1
- package/dist/testFramework.esm.js.map +1 -1
- package/dist/testFramework.js +1 -1
- package/dist/testFramework.js.map +1 -1
- package/package.json +17 -6
- package/dist/cli/src/Dvala/Dvala.d.ts +0 -63
- package/dist/modules/src/Dvala/Dvala.d.ts +0 -63
- package/dist/modules/src/effects.d.ts +0 -110
- package/dist/src/Dvala/Dvala.d.ts +0 -63
- 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.
|
|
10
|
+
var version = "0.0.10";
|
|
11
11
|
|
|
12
12
|
function getCodeMarker(sourceCodeInfo) {
|
|
13
13
|
if (!sourceCodeInfo.position || !sourceCodeInfo.code)
|
|
@@ -82,31 +82,6 @@ class UndefinedSymbolError extends DvalaError {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
const specialExpressionTypes = {
|
|
86
|
-
'??': 0,
|
|
87
|
-
'&&': 1,
|
|
88
|
-
'||': 2,
|
|
89
|
-
'array': 3,
|
|
90
|
-
'cond': 4,
|
|
91
|
-
'defined?': 5,
|
|
92
|
-
'block': 6,
|
|
93
|
-
'doseq': 7,
|
|
94
|
-
'0_lambda': 8,
|
|
95
|
-
'for': 9,
|
|
96
|
-
'if': 10,
|
|
97
|
-
'let': 11,
|
|
98
|
-
'loop': 12,
|
|
99
|
-
'object': 13,
|
|
100
|
-
'recur': 14,
|
|
101
|
-
'match': 15,
|
|
102
|
-
'unless': 16,
|
|
103
|
-
'import': 17,
|
|
104
|
-
'effect': 18,
|
|
105
|
-
'perform': 19,
|
|
106
|
-
'parallel': 20,
|
|
107
|
-
'race': 21,
|
|
108
|
-
};
|
|
109
|
-
|
|
110
85
|
const NodeTypes = {
|
|
111
86
|
Number: 1,
|
|
112
87
|
String: 2,
|
|
@@ -185,109 +160,6 @@ function getAssertionError(typeName, value, sourceCodeInfo) {
|
|
|
185
160
|
return new DvalaError(`Expected ${typeName}, got ${valueToString(value)}.`, getSourceCodeInfo(value, sourceCodeInfo));
|
|
186
161
|
}
|
|
187
162
|
|
|
188
|
-
function isSymbolNode(node) {
|
|
189
|
-
const nodeType = node[0];
|
|
190
|
-
return NodeTypes.UserDefinedSymbol === nodeType
|
|
191
|
-
|| NodeTypes.NormalBuiltinSymbol === nodeType
|
|
192
|
-
|| NodeTypes.SpecialBuiltinSymbol === nodeType;
|
|
193
|
-
}
|
|
194
|
-
function isUserDefinedSymbolNode(node) {
|
|
195
|
-
return NodeTypes.UserDefinedSymbol === node[0];
|
|
196
|
-
}
|
|
197
|
-
function asUserDefinedSymbolNode(node, sourceCodeInfo) {
|
|
198
|
-
assertUserDefinedSymbolNode(node, sourceCodeInfo);
|
|
199
|
-
return node;
|
|
200
|
-
}
|
|
201
|
-
function assertUserDefinedSymbolNode(node, sourceCodeInfo) {
|
|
202
|
-
if (!isUserDefinedSymbolNode(node))
|
|
203
|
-
throw getAssertionError('UserDefinedSymbolNode', node, sourceCodeInfo);
|
|
204
|
-
}
|
|
205
|
-
function isNormalBuiltinSymbolNode(node) {
|
|
206
|
-
return NodeTypes.NormalBuiltinSymbol === node[0];
|
|
207
|
-
}
|
|
208
|
-
function isSpecialBuiltinSymbolNode(node) {
|
|
209
|
-
return NodeTypes.SpecialBuiltinSymbol === node[0];
|
|
210
|
-
}
|
|
211
|
-
function isNormalExpressionNode(node) {
|
|
212
|
-
return node[0] === NodeTypes.NormalExpression;
|
|
213
|
-
}
|
|
214
|
-
function isNormalExpressionNodeWithName(node) {
|
|
215
|
-
if (!isNormalExpressionNode(node)) {
|
|
216
|
-
return false;
|
|
217
|
-
}
|
|
218
|
-
return isSymbolNode(node[1][0]);
|
|
219
|
-
}
|
|
220
|
-
function isSpreadNode(node) {
|
|
221
|
-
return node[0] === NodeTypes.Spread;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const getUndefinedSymbols = (ast, contextStack, builtin, evaluateNode) => {
|
|
225
|
-
const nodes = Array.isArray(ast)
|
|
226
|
-
? ast
|
|
227
|
-
: [[NodeTypes.SpecialExpression, [specialExpressionTypes.block, ast.body, undefined]]];
|
|
228
|
-
const unresolvedSymbols = new Set();
|
|
229
|
-
for (const subNode of nodes) {
|
|
230
|
-
findUnresolvedSymbolsInNode(subNode, contextStack, builtin, evaluateNode)
|
|
231
|
-
?.forEach(symbol => unresolvedSymbols.add(symbol));
|
|
232
|
-
}
|
|
233
|
-
return unresolvedSymbols;
|
|
234
|
-
};
|
|
235
|
-
function findUnresolvedSymbolsInNode(node, contextStack, builtin, evaluateNode) {
|
|
236
|
-
const nodeType = node[0];
|
|
237
|
-
switch (nodeType) {
|
|
238
|
-
case NodeTypes.UserDefinedSymbol: {
|
|
239
|
-
const symbolNode = node;
|
|
240
|
-
const lookUpResult = contextStack.lookUp(symbolNode);
|
|
241
|
-
if (lookUpResult === null)
|
|
242
|
-
return new Set([symbolNode[1]]);
|
|
243
|
-
return null;
|
|
244
|
-
}
|
|
245
|
-
case NodeTypes.NormalBuiltinSymbol:
|
|
246
|
-
case NodeTypes.SpecialBuiltinSymbol:
|
|
247
|
-
case NodeTypes.String:
|
|
248
|
-
case NodeTypes.Number:
|
|
249
|
-
case NodeTypes.ReservedSymbol:
|
|
250
|
-
case NodeTypes.Binding:
|
|
251
|
-
return null;
|
|
252
|
-
case NodeTypes.NormalExpression: {
|
|
253
|
-
const normalExpressionNode = node;
|
|
254
|
-
const unresolvedSymbols = new Set();
|
|
255
|
-
if (isNormalExpressionNodeWithName(normalExpressionNode)) {
|
|
256
|
-
const [, [symbolNode]] = normalExpressionNode;
|
|
257
|
-
if (isUserDefinedSymbolNode(symbolNode)) {
|
|
258
|
-
const lookUpResult = contextStack.lookUp(symbolNode);
|
|
259
|
-
if (lookUpResult === null)
|
|
260
|
-
unresolvedSymbols.add(symbolNode[1]);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
else {
|
|
264
|
-
const [, [expressionNode]] = normalExpressionNode;
|
|
265
|
-
findUnresolvedSymbolsInNode(expressionNode, contextStack, builtin, evaluateNode)?.forEach(symbol => unresolvedSymbols.add(symbol));
|
|
266
|
-
}
|
|
267
|
-
for (const subNode of normalExpressionNode[1][1]) {
|
|
268
|
-
findUnresolvedSymbolsInNode(subNode, contextStack, builtin, evaluateNode)?.forEach(symbol => unresolvedSymbols.add(symbol));
|
|
269
|
-
}
|
|
270
|
-
return unresolvedSymbols;
|
|
271
|
-
}
|
|
272
|
-
case NodeTypes.SpecialExpression: {
|
|
273
|
-
const specialExpressionNode = node;
|
|
274
|
-
const specialExpressionType = specialExpressionNode[1][0];
|
|
275
|
-
const specialExpression = builtin.specialExpressions[specialExpressionType];
|
|
276
|
-
const castedGetUndefinedSymbols = specialExpression.getUndefinedSymbols;
|
|
277
|
-
return castedGetUndefinedSymbols(specialExpressionNode, contextStack, {
|
|
278
|
-
getUndefinedSymbols,
|
|
279
|
-
builtin,
|
|
280
|
-
evaluateNode,
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
case NodeTypes.Spread:
|
|
284
|
-
return findUnresolvedSymbolsInNode(node[1], contextStack, builtin, evaluateNode);
|
|
285
|
-
/* v8 ignore next 2 */
|
|
286
|
-
default:
|
|
287
|
-
throw new DvalaError(`Unhandled node type: ${nodeType}`, node[2]);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
163
|
function isNonUndefined(value) {
|
|
292
164
|
return value !== undefined;
|
|
293
165
|
}
|
|
@@ -4875,7 +4747,7 @@ function argStrings(reference) {
|
|
|
4875
4747
|
|
|
4876
4748
|
function getMetaNormalExpression(normalExpressionReference, effectReference) {
|
|
4877
4749
|
return {
|
|
4878
|
-
doc: {
|
|
4750
|
+
'doc': {
|
|
4879
4751
|
evaluate: ([value], sourceCodeInfo) => {
|
|
4880
4752
|
assertNonUndefined(normalExpressionReference);
|
|
4881
4753
|
// Handle effects
|
|
@@ -4905,28 +4777,45 @@ function getMetaNormalExpression(normalExpressionReference, effectReference) {
|
|
|
4905
4777
|
args: { value: { type: ['function', 'effect'] } },
|
|
4906
4778
|
variants: [{ argumentNames: ['value'] }],
|
|
4907
4779
|
description: 'Returns documentation string of the $value. Works on functions and effects.',
|
|
4908
|
-
seeAlso: ['arity'],
|
|
4780
|
+
seeAlso: ['arity', 'with-doc'],
|
|
4909
4781
|
examples: [
|
|
4910
4782
|
'doc(+)',
|
|
4911
4783
|
'doc(effect(dvala.io.println))',
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
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
|
-
|
|
6398
|
-
|
|
6399
|
-
|
|
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 (
|
|
6402
|
-
return
|
|
6423
|
+
if (!char) {
|
|
6424
|
+
return [length, ['Error', value, undefined, `Unclosed string at position ${position}`]];
|
|
6403
6425
|
}
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
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
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
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
|
-
|
|
6420
|
-
|
|
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
|
|
6423
|
-
}
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6447
|
-
|
|
6526
|
+
const length = i - position;
|
|
6527
|
+
if (length === 0) {
|
|
6528
|
+
return NO_MATCH;
|
|
6448
6529
|
}
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
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
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
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
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
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
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
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
|
-
|
|
6490
|
-
|
|
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
|
-
|
|
6502
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
]
|
|
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
|
-
|
|
8153
|
-
|
|
8154
|
-
|
|
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
|
-
|
|
8157
|
-
|
|
8158
|
-
return NO_MATCH;
|
|
8545
|
+
else if (isReservedSymbolToken(token, 'do')) {
|
|
8546
|
+
left = parseDo(ctx);
|
|
8159
8547
|
}
|
|
8160
|
-
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
|
|
8164
|
-
|
|
8165
|
-
|
|
8166
|
-
|
|
8167
|
-
|
|
8168
|
-
|
|
8169
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8185
|
-
|
|
8186
|
-
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8192
|
-
|
|
8193
|
-
|
|
8194
|
-
|
|
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
|
-
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
|
|
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
|
-
|
|
8240
|
-
|
|
8594
|
+
else {
|
|
8595
|
+
break;
|
|
8241
8596
|
}
|
|
8242
|
-
|
|
8243
|
-
length += 2;
|
|
8244
|
-
return [length, ['MultiLineComment', value]];
|
|
8597
|
+
operator = ctx.tryPeek();
|
|
8245
8598
|
}
|
|
8246
|
-
return
|
|
8247
|
-
}
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
|
|
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
|
-
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
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
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
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
|
|
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
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
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
|
|
8350
|
-
}
|
|
8351
|
-
return
|
|
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
|
|
8402
|
-
* the "most specific" by registration order, not by pattern specificity.
|
|
8684
|
+
* Find all matching async handlers for an effect name, in registration order.
|
|
8685
|
+
* Returns an array of `[pattern, handler]` pairs.
|
|
8403
8686
|
*/
|
|
8404
8687
|
function findMatchingHandlers(effectName, handlers) {
|
|
8405
8688
|
if (!handlers) {
|
|
@@ -8425,6 +8708,8 @@ class SuspensionSignal {
|
|
|
8425
8708
|
snapshots;
|
|
8426
8709
|
nextSnapshotIndex;
|
|
8427
8710
|
meta;
|
|
8711
|
+
effectName;
|
|
8712
|
+
effectArgs;
|
|
8428
8713
|
_brand = 'SuspensionSignal';
|
|
8429
8714
|
constructor(
|
|
8430
8715
|
/** The captured continuation stack at the point of suspension. */
|
|
@@ -8434,11 +8719,17 @@ class SuspensionSignal {
|
|
|
8434
8719
|
/** High-water mark for snapshot indices at the point of suspension. */
|
|
8435
8720
|
nextSnapshotIndex,
|
|
8436
8721
|
/** Optional domain metadata passed through to RunResult. */
|
|
8437
|
-
meta
|
|
8722
|
+
meta,
|
|
8723
|
+
/** The effect name being handled when suspend() was called. */
|
|
8724
|
+
effectName,
|
|
8725
|
+
/** The effect arguments being handled when suspend() was called. */
|
|
8726
|
+
effectArgs) {
|
|
8438
8727
|
this.k = k;
|
|
8439
8728
|
this.snapshots = snapshots;
|
|
8440
8729
|
this.nextSnapshotIndex = nextSnapshotIndex;
|
|
8441
8730
|
this.meta = meta;
|
|
8731
|
+
this.effectName = effectName;
|
|
8732
|
+
this.effectArgs = effectArgs;
|
|
8442
8733
|
}
|
|
8443
8734
|
}
|
|
8444
8735
|
function isSuspensionSignal(value) {
|
|
@@ -8508,217 +8799,6 @@ function getEffectRef(name) {
|
|
|
8508
8799
|
return ref;
|
|
8509
8800
|
}
|
|
8510
8801
|
|
|
8511
|
-
function isContextEntry(value) {
|
|
8512
|
-
return isUnknownRecord(value) && value.value !== undefined;
|
|
8513
|
-
}
|
|
8514
|
-
|
|
8515
|
-
class ContextStackImpl {
|
|
8516
|
-
_contexts;
|
|
8517
|
-
globalContext;
|
|
8518
|
-
values;
|
|
8519
|
-
modules;
|
|
8520
|
-
valueModules;
|
|
8521
|
-
pure;
|
|
8522
|
-
constructor({ contexts, values: hostValues, modules, valueModules, pure, }) {
|
|
8523
|
-
this.globalContext = asNonUndefined(contexts[0]);
|
|
8524
|
-
this._contexts = contexts;
|
|
8525
|
-
this.values = hostValues;
|
|
8526
|
-
this.modules = modules ?? new Map();
|
|
8527
|
-
this.valueModules = valueModules ?? new Map();
|
|
8528
|
-
this.pure = pure ?? false;
|
|
8529
|
-
}
|
|
8530
|
-
// -- Serialization support (Phase 4) --
|
|
8531
|
-
/** Get the raw context chain for serialization. */
|
|
8532
|
-
getContextsRaw() {
|
|
8533
|
-
return this._contexts;
|
|
8534
|
-
}
|
|
8535
|
-
/** Get host values (plain bindings passed at creation). */
|
|
8536
|
-
getHostValues() {
|
|
8537
|
-
return this.values;
|
|
8538
|
-
}
|
|
8539
|
-
/**
|
|
8540
|
-
* Find the index of globalContext in the _contexts array.
|
|
8541
|
-
* Returns -1 if not found (should not happen in valid state).
|
|
8542
|
-
*/
|
|
8543
|
-
getGlobalContextIndex() {
|
|
8544
|
-
return this._contexts.indexOf(this.globalContext);
|
|
8545
|
-
}
|
|
8546
|
-
/**
|
|
8547
|
-
* Create a ContextStack from deserialized data.
|
|
8548
|
-
* `contexts` is the restored context chain (already resolved).
|
|
8549
|
-
* `globalContextIndex` identifies which element is the globalContext.
|
|
8550
|
-
* Host bindings (`values`, `modules`) come from resume options.
|
|
8551
|
-
*/
|
|
8552
|
-
static fromDeserialized(params) {
|
|
8553
|
-
const cs = new ContextStackImpl({
|
|
8554
|
-
contexts: params.contexts,
|
|
8555
|
-
values: params.values,
|
|
8556
|
-
modules: params.modules,
|
|
8557
|
-
pure: params.pure,
|
|
8558
|
-
});
|
|
8559
|
-
if (params.globalContextIndex >= 0 && params.globalContextIndex < params.contexts.length) {
|
|
8560
|
-
cs.globalContext = params.contexts[params.globalContextIndex];
|
|
8561
|
-
}
|
|
8562
|
-
return cs;
|
|
8563
|
-
}
|
|
8564
|
-
/**
|
|
8565
|
-
* Replace the contexts array and globalContext. Used during deserialization
|
|
8566
|
-
* to fill in resolved context data after circular references are handled.
|
|
8567
|
-
*/
|
|
8568
|
-
setContextsFromDeserialized(contexts, globalContextIndex) {
|
|
8569
|
-
this._contexts = contexts;
|
|
8570
|
-
if (globalContextIndex >= 0 && globalContextIndex < contexts.length) {
|
|
8571
|
-
this.globalContext = contexts[globalContextIndex];
|
|
8572
|
-
}
|
|
8573
|
-
}
|
|
8574
|
-
getModule(name) {
|
|
8575
|
-
return this.modules.get(name);
|
|
8576
|
-
}
|
|
8577
|
-
getValueModule(name) {
|
|
8578
|
-
if (this.valueModules.has(name)) {
|
|
8579
|
-
return { value: this.valueModules.get(name), found: true };
|
|
8580
|
-
}
|
|
8581
|
-
return { value: undefined, found: false };
|
|
8582
|
-
}
|
|
8583
|
-
registerValueModule(name, value) {
|
|
8584
|
-
this.valueModules.set(name, value);
|
|
8585
|
-
}
|
|
8586
|
-
create(context) {
|
|
8587
|
-
const globalContext = this.globalContext;
|
|
8588
|
-
const contextStack = new ContextStackImpl({
|
|
8589
|
-
contexts: [context, ...this._contexts],
|
|
8590
|
-
values: this.values,
|
|
8591
|
-
modules: this.modules,
|
|
8592
|
-
valueModules: this.valueModules,
|
|
8593
|
-
pure: this.pure,
|
|
8594
|
-
});
|
|
8595
|
-
contextStack.globalContext = globalContext;
|
|
8596
|
-
return contextStack;
|
|
8597
|
-
}
|
|
8598
|
-
new(context) {
|
|
8599
|
-
const contexts = [{}, context];
|
|
8600
|
-
return new ContextStackImpl({ contexts, modules: this.modules, valueModules: this.valueModules, pure: this.pure });
|
|
8601
|
-
}
|
|
8602
|
-
addValues(values, sourceCodeInfo) {
|
|
8603
|
-
const currentContext = this._contexts[0];
|
|
8604
|
-
for (const [name, value] of Object.entries(values)) {
|
|
8605
|
-
if (currentContext[name]) {
|
|
8606
|
-
throw new DvalaError(`Cannot redefine value "${name}"`, sourceCodeInfo);
|
|
8607
|
-
}
|
|
8608
|
-
const shadowedName = getShadowedBuiltinName(name);
|
|
8609
|
-
if (shadowedName) {
|
|
8610
|
-
throw new DvalaError(`Cannot shadow ${shadowedName}`, sourceCodeInfo);
|
|
8611
|
-
}
|
|
8612
|
-
currentContext[name] = { value: toAny(value) };
|
|
8613
|
-
}
|
|
8614
|
-
}
|
|
8615
|
-
getValue(name) {
|
|
8616
|
-
for (const context of this._contexts) {
|
|
8617
|
-
const contextEntry = context[name];
|
|
8618
|
-
if (contextEntry)
|
|
8619
|
-
return contextEntry.value;
|
|
8620
|
-
}
|
|
8621
|
-
return this.values?.[name];
|
|
8622
|
-
}
|
|
8623
|
-
lookUp(node) {
|
|
8624
|
-
const value = node[1];
|
|
8625
|
-
for (const context of this._contexts) {
|
|
8626
|
-
const contextEntry = context[value];
|
|
8627
|
-
if (contextEntry)
|
|
8628
|
-
return contextEntry;
|
|
8629
|
-
}
|
|
8630
|
-
const hostValue = this.values?.[value];
|
|
8631
|
-
if (hostValue !== undefined) {
|
|
8632
|
-
return {
|
|
8633
|
-
value: toAny(hostValue),
|
|
8634
|
-
};
|
|
8635
|
-
}
|
|
8636
|
-
return null;
|
|
8637
|
-
}
|
|
8638
|
-
evaluateSymbol(node) {
|
|
8639
|
-
if (isSpecialBuiltinSymbolNode(node)) {
|
|
8640
|
-
const functionType = node[1];
|
|
8641
|
-
switch (functionType) {
|
|
8642
|
-
case specialExpressionTypes['&&']:
|
|
8643
|
-
case specialExpressionTypes['||']:
|
|
8644
|
-
case specialExpressionTypes.array:
|
|
8645
|
-
case specialExpressionTypes.object:
|
|
8646
|
-
case specialExpressionTypes['defined?']:
|
|
8647
|
-
case specialExpressionTypes.recur:
|
|
8648
|
-
case specialExpressionTypes['??']: {
|
|
8649
|
-
const specialExpression = asNonUndefined(builtin.specialExpressions[functionType], node[2]);
|
|
8650
|
-
return {
|
|
8651
|
-
[FUNCTION_SYMBOL]: true,
|
|
8652
|
-
functionType: 'SpecialBuiltin',
|
|
8653
|
-
specialBuiltinSymbolType: functionType,
|
|
8654
|
-
sourceCodeInfo: node[2],
|
|
8655
|
-
arity: specialExpression.arity,
|
|
8656
|
-
};
|
|
8657
|
-
}
|
|
8658
|
-
default:
|
|
8659
|
-
throw new DvalaError(`Unknown special builtin symbol type: ${functionType}`, node[2]);
|
|
8660
|
-
}
|
|
8661
|
-
}
|
|
8662
|
-
if (isNormalBuiltinSymbolNode(node)) {
|
|
8663
|
-
const type = node[1];
|
|
8664
|
-
const normalExpression = allNormalExpressions[type];
|
|
8665
|
-
const name = normalExpression.name;
|
|
8666
|
-
return {
|
|
8667
|
-
[FUNCTION_SYMBOL]: true,
|
|
8668
|
-
functionType: 'Builtin',
|
|
8669
|
-
normalBuiltinSymbolType: type,
|
|
8670
|
-
sourceCodeInfo: node[2],
|
|
8671
|
-
arity: normalExpression.arity,
|
|
8672
|
-
name,
|
|
8673
|
-
};
|
|
8674
|
-
}
|
|
8675
|
-
const lookUpResult = this.lookUp(node);
|
|
8676
|
-
if (isContextEntry(lookUpResult))
|
|
8677
|
-
return lookUpResult.value;
|
|
8678
|
-
throw new UndefinedSymbolError(node[1], node[2]);
|
|
8679
|
-
}
|
|
8680
|
-
}
|
|
8681
|
-
function getShadowedBuiltinName(name) {
|
|
8682
|
-
if (specialExpressionKeys.includes(name))
|
|
8683
|
-
return `special expression "${name}"`;
|
|
8684
|
-
if (normalExpressionKeys.includes(name))
|
|
8685
|
-
return `builtin function "${name}"`;
|
|
8686
|
-
if (name === 'self')
|
|
8687
|
-
return `builtin value "${name}"`;
|
|
8688
|
-
return null;
|
|
8689
|
-
}
|
|
8690
|
-
function assertNotShadowingBuiltin(name) {
|
|
8691
|
-
const shadowedName = getShadowedBuiltinName(name);
|
|
8692
|
-
if (shadowedName) {
|
|
8693
|
-
throw new DvalaError(`Cannot shadow ${shadowedName}`, undefined);
|
|
8694
|
-
}
|
|
8695
|
-
}
|
|
8696
|
-
function createContextStack(params = {}, modules, pure) {
|
|
8697
|
-
const globalContext = params.globalContext ?? {};
|
|
8698
|
-
// Contexts are checked from left to right
|
|
8699
|
-
const contexts = params.contexts ? [globalContext, ...params.contexts] : [globalContext];
|
|
8700
|
-
let hostValues;
|
|
8701
|
-
if (params.bindings) {
|
|
8702
|
-
for (const [identifier, entry] of Object.entries(params.bindings)) {
|
|
8703
|
-
if (identifier.includes('.')) {
|
|
8704
|
-
throw new DvalaError(`Dots are not allowed in binding keys: "${identifier}"`, undefined);
|
|
8705
|
-
}
|
|
8706
|
-
assertNotShadowingBuiltin(identifier);
|
|
8707
|
-
if (!hostValues) {
|
|
8708
|
-
hostValues = {};
|
|
8709
|
-
}
|
|
8710
|
-
hostValues[identifier] = entry;
|
|
8711
|
-
}
|
|
8712
|
-
}
|
|
8713
|
-
const contextStack = new ContextStackImpl({
|
|
8714
|
-
contexts,
|
|
8715
|
-
values: hostValues,
|
|
8716
|
-
modules,
|
|
8717
|
-
pure,
|
|
8718
|
-
});
|
|
8719
|
-
return params.globalModuleScope ? contextStack : contextStack.create({});
|
|
8720
|
-
}
|
|
8721
|
-
|
|
8722
8802
|
/**
|
|
8723
8803
|
* Content-addressable hashing for JSON-compatible value trees.
|
|
8724
8804
|
*
|
|
@@ -10239,7 +10319,7 @@ function evaluateFunction(fn, contextStack) {
|
|
|
10239
10319
|
});
|
|
10240
10320
|
return ctx;
|
|
10241
10321
|
}, {});
|
|
10242
|
-
const undefinedSymbols = getUndefinedSymbols(fn[1], contextStack.new(context), builtin, evaluateNodeRecursive);
|
|
10322
|
+
const undefinedSymbols = getUndefinedSymbols$1(fn[1], contextStack.new(context), builtin, evaluateNodeRecursive);
|
|
10243
10323
|
undefinedSymbols.forEach((name) => {
|
|
10244
10324
|
const value = contextStack.getValue(name);
|
|
10245
10325
|
if (isAny(value)) {
|
|
@@ -10642,7 +10722,6 @@ function stepSpecialExpression(node, env, k) {
|
|
|
10642
10722
|
// --- lambda (fn / ->) ---
|
|
10643
10723
|
case specialExpressionTypes['0_lambda']: {
|
|
10644
10724
|
const fn = node[1][1];
|
|
10645
|
-
const docString = (node[1][2] ?? '');
|
|
10646
10725
|
const evaluatedFunc = evaluateFunction(fn, env);
|
|
10647
10726
|
const min = evaluatedFunc[0].filter(arg => arg[0] !== bindingTargetTypes.rest && arg[1][1] === undefined).length;
|
|
10648
10727
|
const max = evaluatedFunc[0].some(arg => arg[0] === bindingTargetTypes.rest) ? undefined : evaluatedFunc[0].length;
|
|
@@ -10654,7 +10733,7 @@ function stepSpecialExpression(node, env, k) {
|
|
|
10654
10733
|
name: undefined,
|
|
10655
10734
|
evaluatedfunction: evaluatedFunc,
|
|
10656
10735
|
arity,
|
|
10657
|
-
docString,
|
|
10736
|
+
docString: '',
|
|
10658
10737
|
};
|
|
10659
10738
|
return { type: 'Value', value: dvalaFunction, k };
|
|
10660
10739
|
}
|
|
@@ -11812,129 +11891,161 @@ function dispatchPerform(effect, args, k, sourceCodeInfo, handlers, signal, snap
|
|
|
11812
11891
|
* shape. If no more handlers remain after `next()`, the effect is unhandled.
|
|
11813
11892
|
*
|
|
11814
11893
|
* Each handler must call exactly one of `resume`, `suspend`, `fail`, or
|
|
11815
|
-
* `next` before its promise resolves.
|
|
11894
|
+
* `next` before its promise resolves (async) or before returning (sync).
|
|
11816
11895
|
*
|
|
11817
11896
|
* - `resume(value)` — resolves with a `ValueStep` that continues evaluation.
|
|
11818
|
-
* - `suspend(meta?)` —
|
|
11897
|
+
* - `suspend(meta?)` — throws a `SuspensionSignal`.
|
|
11819
11898
|
* - `fail(msg?)` — produces an `ErrorStep` routed through `dvala.error`.
|
|
11820
11899
|
* - `next()` — pass to the next matching handler in the chain.
|
|
11900
|
+
*
|
|
11901
|
+
* Handlers may return `void` (synchronous) or `Promise<void>` (async).
|
|
11902
|
+
* When all handlers in a chain are synchronous, this function returns
|
|
11903
|
+
* a `Step` synchronously, allowing use from the sync trampoline.
|
|
11821
11904
|
*/
|
|
11822
11905
|
function dispatchHostHandler(effectName, matchingHandlers, args, k, signal, sourceCodeInfo, snapshotState) {
|
|
11823
11906
|
const effectSignal = signal ?? new AbortController().signal;
|
|
11824
11907
|
const argsArray = Array.from(args);
|
|
11908
|
+
// If the abort signal already fired before the handler was called, auto-suspend immediately.
|
|
11909
|
+
// This happens when a parallel group aborts (e.g. another branch suspended) before this
|
|
11910
|
+
// branch's dispatchHostHandler runs.
|
|
11911
|
+
if (effectSignal.aborted) {
|
|
11912
|
+
throwSuspension(k, undefined, effectName, argsArray);
|
|
11913
|
+
}
|
|
11914
|
+
function resolveOutcome(o, nextIndex) {
|
|
11915
|
+
switch (o.kind) {
|
|
11916
|
+
case 'step': return o.step;
|
|
11917
|
+
case 'asyncResume': return o.promise.then((v) => ({ type: 'Value', value: v, k }), (e) => ({ type: 'Error', error: e instanceof DvalaError ? e : new DvalaError(e instanceof Error ? e : `${e}`, sourceCodeInfo), k }));
|
|
11918
|
+
case 'throw': throw o.error;
|
|
11919
|
+
case 'next': return tryHandler(nextIndex);
|
|
11920
|
+
}
|
|
11921
|
+
}
|
|
11825
11922
|
// Recursive helper: try handler at `index`, with `next()` advancing to `index + 1`.
|
|
11826
11923
|
function tryHandler(index) {
|
|
11827
11924
|
if (index >= matchingHandlers.length) {
|
|
11828
|
-
// No more handlers — effect is unhandled.
|
|
11829
|
-
// Return rejected Promises (not synchronous throws) so that
|
|
11830
|
-
// next() can propagate via .then(resolve, reject).
|
|
11831
11925
|
if (effectName === 'dvala.error') {
|
|
11832
11926
|
const message = typeof argsArray[0] === 'string' ? argsArray[0] : String(argsArray[0] ?? 'Unknown error');
|
|
11833
|
-
|
|
11927
|
+
throw new UserDefinedError(message, sourceCodeInfo);
|
|
11834
11928
|
}
|
|
11835
|
-
|
|
11929
|
+
// dvala.checkpoint resolves to null when all handlers call next() without resuming.
|
|
11930
|
+
if (effectName === 'dvala.checkpoint') {
|
|
11931
|
+
return { type: 'Value', value: null, k };
|
|
11932
|
+
}
|
|
11933
|
+
throw new DvalaError(`Unhandled effect: '${effectName}'`, sourceCodeInfo);
|
|
11836
11934
|
}
|
|
11837
|
-
const [, handler] = matchingHandlers[index];
|
|
11838
|
-
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
|
|
11843
|
-
}
|
|
11844
|
-
settled = true;
|
|
11935
|
+
const [pattern, handler] = matchingHandlers[index];
|
|
11936
|
+
// Before trying a "*" catch-all, fall back to standard effects.
|
|
11937
|
+
if (pattern === '*') {
|
|
11938
|
+
const standardHandler = getStandardEffectHandler(effectName);
|
|
11939
|
+
if (standardHandler) {
|
|
11940
|
+
return standardHandler(args, k, sourceCodeInfo);
|
|
11845
11941
|
}
|
|
11846
|
-
|
|
11847
|
-
|
|
11848
|
-
|
|
11849
|
-
|
|
11850
|
-
|
|
11851
|
-
|
|
11852
|
-
|
|
11853
|
-
|
|
11854
|
-
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11860
|
-
|
|
11861
|
-
|
|
11862
|
-
}
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
}
|
|
11866
|
-
}
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
|
|
11871
|
-
|
|
11872
|
-
|
|
11873
|
-
|
|
11874
|
-
|
|
11875
|
-
|
|
11876
|
-
|
|
11877
|
-
|
|
11878
|
-
|
|
11879
|
-
|
|
11880
|
-
|
|
11881
|
-
|
|
11882
|
-
|
|
11883
|
-
|
|
11884
|
-
|
|
11885
|
-
|
|
11886
|
-
|
|
11887
|
-
|
|
11888
|
-
|
|
11889
|
-
|
|
11890
|
-
|
|
11891
|
-
|
|
11892
|
-
|
|
11893
|
-
|
|
11894
|
-
|
|
11895
|
-
|
|
11896
|
-
|
|
11897
|
-
|
|
11898
|
-
|
|
11899
|
-
|
|
11900
|
-
|
|
11901
|
-
|
|
11902
|
-
|
|
11903
|
-
|
|
11904
|
-
resumeFrom: (snapshot, value) => {
|
|
11905
|
-
if (settled) {
|
|
11906
|
-
throw new DvalaError('Effect handler called resumeFrom() after already calling another operation', sourceCodeInfo);
|
|
11907
|
-
}
|
|
11908
|
-
if (!snapshotState) {
|
|
11909
|
-
throw new DvalaError('resumeFrom is not available outside effect-enabled execution', sourceCodeInfo);
|
|
11910
|
-
}
|
|
11911
|
-
const found = snapshotState.snapshots.find(s => s.index === snapshot.index && s.runId === snapshot.runId);
|
|
11912
|
-
if (!found) {
|
|
11913
|
-
throw new DvalaError(`Invalid snapshot: no snapshot with index ${snapshot.index} found in current run`, sourceCodeInfo);
|
|
11914
|
-
}
|
|
11915
|
-
settled = true;
|
|
11916
|
-
reject(new ResumeFromSignal(found.continuation, value, found.index));
|
|
11917
|
-
},
|
|
11918
|
-
};
|
|
11919
|
-
handler(ctx).catch((e) => {
|
|
11942
|
+
}
|
|
11943
|
+
let outcome;
|
|
11944
|
+
let settled = false;
|
|
11945
|
+
function assertNotSettled(operation) {
|
|
11946
|
+
if (settled) {
|
|
11947
|
+
throw new DvalaError(`Effect handler called ${operation}() after already calling another operation`, sourceCodeInfo);
|
|
11948
|
+
}
|
|
11949
|
+
settled = true;
|
|
11950
|
+
}
|
|
11951
|
+
const ctx = {
|
|
11952
|
+
effectName,
|
|
11953
|
+
args: argsArray,
|
|
11954
|
+
signal: effectSignal,
|
|
11955
|
+
resume: (value) => {
|
|
11956
|
+
assertNotSettled('resume');
|
|
11957
|
+
if (value instanceof Promise) {
|
|
11958
|
+
outcome = { kind: 'asyncResume', promise: value };
|
|
11959
|
+
}
|
|
11960
|
+
else {
|
|
11961
|
+
outcome = { kind: 'step', step: { type: 'Value', value, k } };
|
|
11962
|
+
}
|
|
11963
|
+
},
|
|
11964
|
+
fail: (msg) => {
|
|
11965
|
+
assertNotSettled('fail');
|
|
11966
|
+
const errorMsg = msg ?? `Effect handler failed for '${effectName}'`;
|
|
11967
|
+
outcome = { kind: 'step', step: { type: 'Error', error: new DvalaError(errorMsg, sourceCodeInfo), k } };
|
|
11968
|
+
},
|
|
11969
|
+
suspend: (meta) => {
|
|
11970
|
+
assertNotSettled('suspend');
|
|
11971
|
+
outcome = {
|
|
11972
|
+
kind: 'throw',
|
|
11973
|
+
error: new SuspensionSignal(k, snapshotState ? snapshotState.snapshots : [], snapshotState ? snapshotState.nextSnapshotIndex : 0, meta, effectName, argsArray),
|
|
11974
|
+
};
|
|
11975
|
+
},
|
|
11976
|
+
next: () => {
|
|
11977
|
+
assertNotSettled('next');
|
|
11978
|
+
outcome = { kind: 'next' };
|
|
11979
|
+
},
|
|
11980
|
+
get snapshots() { return snapshotState ? [...snapshotState.snapshots] : []; },
|
|
11981
|
+
checkpoint: (meta) => {
|
|
11982
|
+
if (!snapshotState) {
|
|
11983
|
+
throw new DvalaError('checkpoint is not available outside effect-enabled execution', sourceCodeInfo);
|
|
11984
|
+
}
|
|
11985
|
+
const continuation = serializeToObject(k);
|
|
11986
|
+
const snapshot = {
|
|
11987
|
+
continuation,
|
|
11988
|
+
timestamp: Date.now(),
|
|
11989
|
+
index: snapshotState.nextSnapshotIndex++,
|
|
11990
|
+
runId: snapshotState.runId,
|
|
11991
|
+
...(meta !== undefined ? { meta } : {}),
|
|
11992
|
+
};
|
|
11993
|
+
snapshotState.snapshots.push(snapshot);
|
|
11994
|
+
if (snapshotState.maxSnapshots !== undefined && snapshotState.snapshots.length > snapshotState.maxSnapshots) {
|
|
11995
|
+
snapshotState.snapshots.shift();
|
|
11996
|
+
}
|
|
11997
|
+
return snapshot;
|
|
11998
|
+
},
|
|
11999
|
+
resumeFrom: (snapshot, value) => {
|
|
11920
12000
|
if (settled) {
|
|
11921
|
-
|
|
11922
|
-
return;
|
|
12001
|
+
throw new DvalaError('Effect handler called resumeFrom() after already calling another operation', sourceCodeInfo);
|
|
11923
12002
|
}
|
|
11924
|
-
|
|
11925
|
-
|
|
11926
|
-
reject(e);
|
|
12003
|
+
if (!snapshotState) {
|
|
12004
|
+
throw new DvalaError('resumeFrom is not available outside effect-enabled execution', sourceCodeInfo);
|
|
11927
12005
|
}
|
|
11928
|
-
|
|
11929
|
-
|
|
11930
|
-
|
|
11931
|
-
resolve({
|
|
11932
|
-
type: 'Error',
|
|
11933
|
-
error: e instanceof DvalaError ? e : new DvalaError(e instanceof Error ? e : `${e}`, sourceCodeInfo),
|
|
11934
|
-
k,
|
|
11935
|
-
});
|
|
12006
|
+
const found = snapshotState.snapshots.find(s => s.index === snapshot.index && s.runId === snapshot.runId);
|
|
12007
|
+
if (!found) {
|
|
12008
|
+
throw new DvalaError(`Invalid snapshot: no snapshot with index ${snapshot.index} found in current run`, sourceCodeInfo);
|
|
11936
12009
|
}
|
|
11937
|
-
|
|
12010
|
+
settled = true;
|
|
12011
|
+
outcome = { kind: 'throw', error: new ResumeFromSignal(found.continuation, value, found.index) };
|
|
12012
|
+
},
|
|
12013
|
+
};
|
|
12014
|
+
const handlerResult = handler(ctx);
|
|
12015
|
+
if (!(handlerResult instanceof Promise)) {
|
|
12016
|
+
// Synchronous handler — outcome must already be set
|
|
12017
|
+
if (!outcome) {
|
|
12018
|
+
throw new DvalaError(`Effect handler for '${effectName}' did not call resume(), fail(), suspend(), or next()`, sourceCodeInfo);
|
|
12019
|
+
}
|
|
12020
|
+
return resolveOutcome(outcome, index + 1);
|
|
12021
|
+
}
|
|
12022
|
+
// Async handler
|
|
12023
|
+
if (outcome) {
|
|
12024
|
+
// Handler settled synchronously before the async part
|
|
12025
|
+
handlerResult.catch(() => { }); // suppress unhandled rejection
|
|
12026
|
+
return resolveOutcome(outcome, index + 1);
|
|
12027
|
+
}
|
|
12028
|
+
// Not yet settled — wait for the handler's promise
|
|
12029
|
+
return handlerResult.then(() => {
|
|
12030
|
+
if (!outcome) {
|
|
12031
|
+
throw new DvalaError(`Effect handler for '${effectName}' did not call resume(), fail(), suspend(), or next()`, sourceCodeInfo);
|
|
12032
|
+
}
|
|
12033
|
+
return resolveOutcome(outcome, index + 1);
|
|
12034
|
+
}, (e) => {
|
|
12035
|
+
if (outcome) {
|
|
12036
|
+
// Already settled — return that result, ignore the rejection
|
|
12037
|
+
return resolveOutcome(outcome, index + 1);
|
|
12038
|
+
}
|
|
12039
|
+
if (isSuspensionSignal(e) || isResumeFromSignal(e)) {
|
|
12040
|
+
// eslint-disable-next-line ts/no-throw-literal -- SuspensionSignal/ResumeFromSignal is a signaling mechanism
|
|
12041
|
+
throw e;
|
|
12042
|
+
}
|
|
12043
|
+
const errorStep = {
|
|
12044
|
+
type: 'Error',
|
|
12045
|
+
error: e instanceof DvalaError ? e : new DvalaError(e instanceof Error ? e : `${e}`, sourceCodeInfo),
|
|
12046
|
+
k,
|
|
12047
|
+
};
|
|
12048
|
+
return errorStep;
|
|
11938
12049
|
});
|
|
11939
12050
|
}
|
|
11940
12051
|
return tryHandler(0);
|
|
@@ -11946,9 +12057,20 @@ function dispatchHostHandler(effectName, matchingHandlers, args, k, signal, sour
|
|
|
11946
12057
|
* Throw a SuspensionSignal. Factored out to a helper so ESLint's
|
|
11947
12058
|
* `only-throw-literal` rule can be suppressed in one place.
|
|
11948
12059
|
*/
|
|
11949
|
-
|
|
12060
|
+
/** Combine two AbortSignals: aborts when either fires (or already aborted). */
|
|
12061
|
+
function combineSignals(a, b) {
|
|
12062
|
+
const controller = new AbortController();
|
|
12063
|
+
if (a.aborted || b.aborted) {
|
|
12064
|
+
controller.abort();
|
|
12065
|
+
return controller.signal;
|
|
12066
|
+
}
|
|
12067
|
+
a.addEventListener('abort', () => controller.abort(), { once: true });
|
|
12068
|
+
b.addEventListener('abort', () => controller.abort(), { once: true });
|
|
12069
|
+
return controller.signal;
|
|
12070
|
+
}
|
|
12071
|
+
function throwSuspension(k, meta, effectName, effectArgs) {
|
|
11950
12072
|
// eslint-disable-next-line ts/no-throw-literal -- SuspensionSignal is a signaling mechanism, not an error
|
|
11951
|
-
throw new SuspensionSignal(k, [], 0, meta);
|
|
12073
|
+
throw new SuspensionSignal(k, [], 0, meta, effectName, effectArgs);
|
|
11952
12074
|
}
|
|
11953
12075
|
/**
|
|
11954
12076
|
* Run a single trampoline branch to completion with effect handler support.
|
|
@@ -11978,31 +12100,41 @@ async function runBranch(node, env, handlers, signal) {
|
|
|
11978
12100
|
* but errors take priority)
|
|
11979
12101
|
*/
|
|
11980
12102
|
async function executeParallelBranches(branches, env, k, handlers, signal) {
|
|
11981
|
-
|
|
11982
|
-
//
|
|
11983
|
-
const
|
|
12103
|
+
// AbortController for this parallel group — aborted when any branch suspends,
|
|
12104
|
+
// which signals remaining effect handlers to auto-suspend via ctx.signal.
|
|
12105
|
+
const parallelAbort = new AbortController();
|
|
12106
|
+
const effectSignal = signal
|
|
12107
|
+
? combineSignals(signal, parallelAbort.signal)
|
|
12108
|
+
: parallelAbort.signal;
|
|
12109
|
+
// Run all branches concurrently; abort the group when a branch suspends
|
|
12110
|
+
const branchPromises = branches.map(async (branch, i) => {
|
|
12111
|
+
const result = await runBranch(branch, env, handlers, effectSignal);
|
|
12112
|
+
if (result.type === 'suspended') {
|
|
12113
|
+
parallelAbort.abort();
|
|
12114
|
+
}
|
|
12115
|
+
return { index: i, result };
|
|
12116
|
+
});
|
|
11984
12117
|
const results = await Promise.allSettled(branchPromises);
|
|
11985
12118
|
// Collect outcomes
|
|
11986
12119
|
const completedBranches = [];
|
|
11987
12120
|
const suspendedBranches = [];
|
|
11988
12121
|
const errors = [];
|
|
11989
|
-
for (
|
|
11990
|
-
|
|
11991
|
-
|
|
11992
|
-
|
|
11993
|
-
errors.push(new DvalaError(`${result.reason}`, undefined));
|
|
12122
|
+
for (const settled of results) {
|
|
12123
|
+
if (settled.status === 'rejected') {
|
|
12124
|
+
// branchPromises should never reject, but handle defensively
|
|
12125
|
+
errors.push(new DvalaError(`${settled.reason}`, undefined));
|
|
11994
12126
|
}
|
|
11995
12127
|
else {
|
|
11996
|
-
const
|
|
11997
|
-
switch (
|
|
12128
|
+
const { index, result } = settled.value;
|
|
12129
|
+
switch (result.type) {
|
|
11998
12130
|
case 'completed':
|
|
11999
|
-
completedBranches.push({ index
|
|
12131
|
+
completedBranches.push({ index, value: result.value });
|
|
12000
12132
|
break;
|
|
12001
12133
|
case 'suspended':
|
|
12002
|
-
suspendedBranches.push({ index
|
|
12134
|
+
suspendedBranches.push({ index, snapshot: result.snapshot });
|
|
12003
12135
|
break;
|
|
12004
12136
|
case 'error':
|
|
12005
|
-
errors.push(
|
|
12137
|
+
errors.push(result.error);
|
|
12006
12138
|
break;
|
|
12007
12139
|
}
|
|
12008
12140
|
}
|
|
@@ -12021,9 +12153,9 @@ async function executeParallelBranches(branches, env, k, handlers, signal) {
|
|
|
12021
12153
|
suspendedBranches: suspendedBranches.slice(1), // remaining after the first
|
|
12022
12154
|
};
|
|
12023
12155
|
const resumeK = [parallelResumeFrame, ...k];
|
|
12024
|
-
// Throw SuspensionSignal with the first suspended branch's meta
|
|
12156
|
+
// Throw SuspensionSignal with the first suspended branch's meta and effect info
|
|
12025
12157
|
const firstSuspended = suspendedBranches[0];
|
|
12026
|
-
return throwSuspension(resumeK, firstSuspended.snapshot.meta);
|
|
12158
|
+
return throwSuspension(resumeK, firstSuspended.snapshot.meta, firstSuspended.snapshot.effectName, firstSuspended.snapshot.effectArgs);
|
|
12027
12159
|
}
|
|
12028
12160
|
// All branches completed — build the result array in original order
|
|
12029
12161
|
const resultArray = Array.from({ length: branches.length });
|
|
@@ -12176,7 +12308,7 @@ function handleParallelResume(step, _handlers, _signal) {
|
|
|
12176
12308
|
suspendedBranches: remaining,
|
|
12177
12309
|
};
|
|
12178
12310
|
const resumeK = [parallelResumeFrame, ...k];
|
|
12179
|
-
return throwSuspension(resumeK, nextSuspended.snapshot.meta);
|
|
12311
|
+
return throwSuspension(resumeK, nextSuspended.snapshot.meta, nextSuspended.snapshot.effectName, nextSuspended.snapshot.effectArgs);
|
|
12180
12312
|
}
|
|
12181
12313
|
// All branches now completed — build the result array in original order
|
|
12182
12314
|
const resultArray = Array.from({ length: branchCount });
|
|
@@ -12441,7 +12573,7 @@ function tick(step, handlers, signal, snapshotState) {
|
|
|
12441
12573
|
* Throws if any step produces a Promise (i.e., an async operation was
|
|
12442
12574
|
* encountered in a synchronous context).
|
|
12443
12575
|
*/
|
|
12444
|
-
function runSyncTrampoline(initial) {
|
|
12576
|
+
function runSyncTrampoline(initial, effectHandlers) {
|
|
12445
12577
|
let step = initial;
|
|
12446
12578
|
for (;;) {
|
|
12447
12579
|
if (step instanceof Promise) {
|
|
@@ -12450,7 +12582,7 @@ function runSyncTrampoline(initial) {
|
|
|
12450
12582
|
if (step.type === 'Value' && step.k.length === 0) {
|
|
12451
12583
|
return step.value;
|
|
12452
12584
|
}
|
|
12453
|
-
step = tick(step);
|
|
12585
|
+
step = tick(step, effectHandlers);
|
|
12454
12586
|
}
|
|
12455
12587
|
}
|
|
12456
12588
|
/**
|
|
@@ -12512,16 +12644,6 @@ function evaluate(ast, contextStack) {
|
|
|
12512
12644
|
throw error;
|
|
12513
12645
|
}
|
|
12514
12646
|
}
|
|
12515
|
-
/**
|
|
12516
|
-
* Evaluate an AST using the async trampoline directly.
|
|
12517
|
-
* Use this when the caller knows that async operations may be involved
|
|
12518
|
-
* (e.g., from Dvala.async.run) to avoid the sync-first-then-retry pattern
|
|
12519
|
-
* which can cause side effects to be executed twice.
|
|
12520
|
-
*/
|
|
12521
|
-
function evaluateAsync(ast, contextStack) {
|
|
12522
|
-
const initial = buildInitialStep(ast.body, contextStack);
|
|
12523
|
-
return runAsyncTrampoline(initial);
|
|
12524
|
-
}
|
|
12525
12647
|
/**
|
|
12526
12648
|
* Evaluate a single AST node using the trampoline.
|
|
12527
12649
|
* Used as the `evaluateNode` callback passed to `getUndefinedSymbols`
|
|
@@ -12563,6 +12685,27 @@ async function evaluateWithEffects(ast, contextStack, handlers, maxSnapshots, de
|
|
|
12563
12685
|
const initial = buildInitialStep(ast.body, contextStack);
|
|
12564
12686
|
return runEffectLoop(initial, handlers, signal, undefined, maxSnapshots, deserializeOptions);
|
|
12565
12687
|
}
|
|
12688
|
+
/**
|
|
12689
|
+
* Evaluate an AST synchronously with effect handler support.
|
|
12690
|
+
*
|
|
12691
|
+
* Uses the sync trampoline with `effectHandlers` threaded through `tick`.
|
|
12692
|
+
* Throws if an async operation is encountered (e.g., an async handler
|
|
12693
|
+
* is used). Handlers may call `resume(value)`, `fail(msg?)`, or `next()`.
|
|
12694
|
+
* Calling `suspend()` will throw a runtime error.
|
|
12695
|
+
*/
|
|
12696
|
+
function evaluateWithSyncEffects(ast, contextStack, effectHandlers) {
|
|
12697
|
+
const initial = buildInitialStep(ast.body, contextStack);
|
|
12698
|
+
try {
|
|
12699
|
+
return runSyncTrampoline(initial, effectHandlers);
|
|
12700
|
+
}
|
|
12701
|
+
catch (error) {
|
|
12702
|
+
if (error instanceof DvalaError && error.message.includes('Unexpected async operation')) {
|
|
12703
|
+
const freshInitial = buildInitialStep(ast.body, contextStack);
|
|
12704
|
+
return runSyncTrampoline(freshInitial, effectHandlers);
|
|
12705
|
+
}
|
|
12706
|
+
throw error;
|
|
12707
|
+
}
|
|
12708
|
+
}
|
|
12566
12709
|
/**
|
|
12567
12710
|
* Shared effect trampoline loop used by both `evaluateWithEffects` and
|
|
12568
12711
|
* `resumeWithEffects`. Runs the trampoline to completion, suspension, or error.
|
|
@@ -12580,7 +12723,7 @@ async function runEffectLoop(initial, handlers, signal, initialSnapshotState, ma
|
|
|
12580
12723
|
snapshots: [],
|
|
12581
12724
|
nextSnapshotIndex: 0,
|
|
12582
12725
|
runId: generateRunId(),
|
|
12583
|
-
...({}),
|
|
12726
|
+
...(maxSnapshots !== undefined ? { maxSnapshots } : {}),
|
|
12584
12727
|
};
|
|
12585
12728
|
let step = initial;
|
|
12586
12729
|
for (;;) {
|
|
@@ -12628,6 +12771,8 @@ async function runEffectLoop(initial, handlers, signal, initialSnapshotState, ma
|
|
|
12628
12771
|
index: snapshotState.nextSnapshotIndex++,
|
|
12629
12772
|
runId: snapshotState.runId,
|
|
12630
12773
|
meta: error.meta,
|
|
12774
|
+
effectName: error.effectName,
|
|
12775
|
+
effectArgs: error.effectArgs,
|
|
12631
12776
|
};
|
|
12632
12777
|
return { type: 'suspended', snapshot };
|
|
12633
12778
|
}
|
|
@@ -12639,141 +12784,6 @@ async function runEffectLoop(initial, handlers, signal, initialSnapshotState, ma
|
|
|
12639
12784
|
}
|
|
12640
12785
|
}
|
|
12641
12786
|
|
|
12642
|
-
function transformSymbolTokens(tokenStram, transformer) {
|
|
12643
|
-
return {
|
|
12644
|
-
...tokenStram,
|
|
12645
|
-
tokens: tokenStram.tokens.map(token => isSymbolToken(token)
|
|
12646
|
-
? [token[0], transformer(token[1])]
|
|
12647
|
-
: token),
|
|
12648
|
-
};
|
|
12649
|
-
}
|
|
12650
|
-
|
|
12651
|
-
function untokenize(tokenStream) {
|
|
12652
|
-
return tokenStream.tokens.reduce((acc, token) => {
|
|
12653
|
-
return `${acc}${token[1]}`;
|
|
12654
|
-
}, '');
|
|
12655
|
-
}
|
|
12656
|
-
|
|
12657
|
-
const dvalaCommands = new Set([...normalExpressionKeys, ...specialExpressionKeys, ...Object.keys(reservedSymbolRecord)]);
|
|
12658
|
-
// TODO: replace with get suggestions function
|
|
12659
|
-
class AutoCompleter {
|
|
12660
|
-
originalProgram;
|
|
12661
|
-
originalPosition;
|
|
12662
|
-
prefixProgram = '';
|
|
12663
|
-
suffixProgram = '';
|
|
12664
|
-
searchString = '';
|
|
12665
|
-
suggestions = [];
|
|
12666
|
-
suggestionIndex = null;
|
|
12667
|
-
constructor(originalProgram, originalPosition, dvala, params) {
|
|
12668
|
-
this.originalProgram = originalProgram;
|
|
12669
|
-
this.originalPosition = originalPosition;
|
|
12670
|
-
const partialProgram = this.originalProgram.slice(0, this.originalPosition);
|
|
12671
|
-
const tokenStream = dvala.tokenize(partialProgram);
|
|
12672
|
-
const lastToken = tokenStream.tokens.at(-1);
|
|
12673
|
-
if (!lastToken) {
|
|
12674
|
-
return;
|
|
12675
|
-
}
|
|
12676
|
-
if (lastToken[0] === 'Error') {
|
|
12677
|
-
return;
|
|
12678
|
-
}
|
|
12679
|
-
this.searchString = lastToken[1];
|
|
12680
|
-
this.prefixProgram = this.originalProgram.slice(0, this.originalPosition - this.searchString.length);
|
|
12681
|
-
this.suffixProgram = this.originalProgram.slice(this.prefixProgram.length + this.searchString.length);
|
|
12682
|
-
this.originalProgram.slice(this.prefixProgram.length + this.searchString.length);
|
|
12683
|
-
this.suggestions = this.generateSuggestions(params);
|
|
12684
|
-
}
|
|
12685
|
-
getNextSuggestion() {
|
|
12686
|
-
return this.getAutoCompleteSuggestionResult(this.getNextSuggestionSymbol());
|
|
12687
|
-
}
|
|
12688
|
-
getPreviousSuggestion() {
|
|
12689
|
-
return this.getAutoCompleteSuggestionResult(this.getPreviousSuggestionSymbol());
|
|
12690
|
-
}
|
|
12691
|
-
getAutoCompleteSuggestionResult(suggestion) {
|
|
12692
|
-
if (suggestion === null) {
|
|
12693
|
-
return null;
|
|
12694
|
-
}
|
|
12695
|
-
return {
|
|
12696
|
-
program: this.prefixProgram + suggestion + this.suffixProgram,
|
|
12697
|
-
position: this.prefixProgram.length + suggestion.length,
|
|
12698
|
-
};
|
|
12699
|
-
}
|
|
12700
|
-
getNextSuggestionSymbol() {
|
|
12701
|
-
if (this.suggestions.length === 0) {
|
|
12702
|
-
return null;
|
|
12703
|
-
}
|
|
12704
|
-
if (this.suggestionIndex === null) {
|
|
12705
|
-
this.suggestionIndex = 0;
|
|
12706
|
-
}
|
|
12707
|
-
else {
|
|
12708
|
-
this.suggestionIndex += 1;
|
|
12709
|
-
if (this.suggestionIndex >= this.suggestions.length) {
|
|
12710
|
-
this.suggestionIndex = 0;
|
|
12711
|
-
}
|
|
12712
|
-
}
|
|
12713
|
-
return this.suggestions[this.suggestionIndex];
|
|
12714
|
-
}
|
|
12715
|
-
getPreviousSuggestionSymbol() {
|
|
12716
|
-
if (this.suggestions.length === 0) {
|
|
12717
|
-
return null;
|
|
12718
|
-
}
|
|
12719
|
-
if (this.suggestionIndex === null) {
|
|
12720
|
-
this.suggestionIndex = this.suggestions.length - 1;
|
|
12721
|
-
}
|
|
12722
|
-
else {
|
|
12723
|
-
this.suggestionIndex -= 1;
|
|
12724
|
-
if (this.suggestionIndex < 0) {
|
|
12725
|
-
this.suggestionIndex = this.suggestions.length - 1;
|
|
12726
|
-
}
|
|
12727
|
-
}
|
|
12728
|
-
return this.suggestions[this.suggestionIndex];
|
|
12729
|
-
}
|
|
12730
|
-
getSuggestions() {
|
|
12731
|
-
return [...this.suggestions];
|
|
12732
|
-
}
|
|
12733
|
-
getSearchString() {
|
|
12734
|
-
return this.searchString;
|
|
12735
|
-
}
|
|
12736
|
-
generateSuggestions(params) {
|
|
12737
|
-
const blacklist = new Set(['0_defn', '0_lambda']);
|
|
12738
|
-
const startsWithCaseSensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.startsWith(this.searchString));
|
|
12739
|
-
startsWithCaseSensitive.forEach(suggestion => blacklist.add(suggestion));
|
|
12740
|
-
const startsWithCaseInsensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.toLowerCase().startsWith(this.searchString.toLowerCase()));
|
|
12741
|
-
startsWithCaseInsensitive.forEach(suggestion => blacklist.add(suggestion));
|
|
12742
|
-
const includesCaseSensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.includes(this.searchString));
|
|
12743
|
-
includesCaseSensitive.forEach(suggestion => blacklist.add(suggestion));
|
|
12744
|
-
const includesCaseInsensitive = this.generateWithPredicate(params, suggestion => !blacklist.has(suggestion) && suggestion.includes(this.searchString.toLowerCase()));
|
|
12745
|
-
includesCaseInsensitive.forEach(suggestion => blacklist.add(suggestion));
|
|
12746
|
-
return [...startsWithCaseSensitive, ...startsWithCaseInsensitive, ...includesCaseSensitive, ...includesCaseInsensitive];
|
|
12747
|
-
}
|
|
12748
|
-
generateWithPredicate(params, shouldInclude) {
|
|
12749
|
-
const suggestions = new Set();
|
|
12750
|
-
dvalaCommands.forEach((suggestion) => {
|
|
12751
|
-
if (shouldInclude(suggestion)) {
|
|
12752
|
-
suggestions.add(suggestion);
|
|
12753
|
-
}
|
|
12754
|
-
});
|
|
12755
|
-
Object.keys(params.globalContext ?? {})
|
|
12756
|
-
.filter(shouldInclude)
|
|
12757
|
-
.forEach(suggestion => suggestions.add(suggestion));
|
|
12758
|
-
params.contexts?.forEach((context) => {
|
|
12759
|
-
Object.keys(context)
|
|
12760
|
-
.filter(shouldInclude)
|
|
12761
|
-
.forEach(suggestion => suggestions.add(suggestion));
|
|
12762
|
-
});
|
|
12763
|
-
Object.keys(params.bindings ?? {})
|
|
12764
|
-
.filter(shouldInclude)
|
|
12765
|
-
.forEach(suggestion => suggestions.add(suggestion));
|
|
12766
|
-
return [...suggestions].sort((a, b) => a.localeCompare(b));
|
|
12767
|
-
}
|
|
12768
|
-
}
|
|
12769
|
-
|
|
12770
|
-
function isDvalaBundle(value) {
|
|
12771
|
-
return (typeof value === 'object'
|
|
12772
|
-
&& value !== null
|
|
12773
|
-
&& typeof value.program === 'string'
|
|
12774
|
-
&& Array.isArray(value.fileModules));
|
|
12775
|
-
}
|
|
12776
|
-
|
|
12777
12787
|
var collectionSource = "{\n map: (first-coll, ...args) -> do\n let fn = last(args);\n let other-colls = slice(args, 0, count(args) - 1);\n let all-colls = [first-coll, ...other-colls];\n\n cond\n case object?(first-coll) then do\n let expected-keys = keys(first-coll) |> sort;\n doseq (obj in other-colls) -> do\n if not(object?(obj)) then\n perform(effect(dvala.error), \"Expected object\")\n end;\n let obj-keys = keys(obj) |> sort;\n if not(obj-keys == expected-keys) then\n perform(effect(dvala.error), ++(\n \"All objects must have the same keys. Expected: \",\n join(expected-keys, \", \"),\n \". Found: \",\n join(obj-keys, \", \")\n ))\n end\n end;\n reduce(keys(first-coll), (acc, k) -> do\n let a = for (coll in all-colls) -> coll(k);\n assoc(acc, k, apply(fn, a))\n end, {})\n end\n\n case string?(first-coll) then do\n doseq (s in other-colls) -> do\n if not(string?(s)) then\n perform(effect(dvala.error), \"Expected string\")\n end\n end;\n let len = reduce(other-colls, (m, s) -> min(m, count(s)), count(first-coll));\n let mapped = for (i in range(len)) -> do\n let a = for (coll in all-colls) -> nth(coll, i);\n apply(fn, a)\n end;\n reduce(mapped, (acc, ch) -> do\n if not(string?(ch)) then\n perform(effect(dvala.error), \"Expected string\")\n end;\n ++(acc, ch)\n end, \"\")\n end\n\n case true then do\n doseq (x in other-colls) -> do\n if not(array?(x)) then\n perform(effect(dvala.error), \"Expected array\")\n end\n end;\n let len = reduce(other-colls, (m, x) -> min(m, count(x)), count(first-coll));\n for (i in range(len)) -> do\n let a = for (coll in all-colls) -> nth(coll, i);\n apply(fn, a)\n end\n end\n end\n end,\n\n filter: (coll, fn) -> do\n cond\n case array?(coll) then\n reduce(coll, (acc, elem) -> if fn(elem) then [...acc, elem] else acc end, [])\n\n case string?(coll) then\n reduce(coll, (acc, ch) -> if fn(ch) then ++(acc, ch) else acc end, \"\")\n\n case object?(coll) then\n reduce(keys(coll), (acc, k) -> do\n if fn(coll(k)) then\n assoc(acc, k, coll(k))\n else\n acc\n end\n end, {})\n\n case true then\n perform(effect(dvala.error), \"Expected collection\")\n end\n end,\n\n reduce: (coll, fn, initial) -> do\n cond\n case string?(coll) then\n loop (acc = initial, i = 0) -> do\n if i >= count(coll) then\n acc\n else\n recur(fn(acc, nth(coll, i)), i + 1)\n end\n end\n\n case array?(coll) then\n loop (acc = initial, i = 0) -> do\n if i >= count(coll) then\n acc\n else\n recur(fn(acc, nth(coll, i)), i + 1)\n end\n end\n\n case object?(coll) then do\n let values = vals(coll);\n loop (acc = initial, i = 0) -> do\n if i >= count(values) then\n acc\n else\n recur(fn(acc, nth(values, i)), i + 1)\n end\n end\n end\n\n case true then\n perform(effect(dvala.error), \"Expected collection\")\n end\n end\n}";
|
|
12778
12788
|
|
|
12779
12789
|
var sequenceSource = "{\n some: (seq, fn) -> do\n if null?(seq) then null\n else do\n loop (i = 0) -> do\n if i >= count(seq) then\n null\n else do\n let elem = nth(seq, i);\n if fn(elem) then elem\n else recur(i + 1)\n end\n end\n end\n end\n end\n end\n end,\n\n take-while: (seq, fn) -> do\n let is-str = string?(seq);\n let len = count(seq);\n let idx = loop (i = 0) -> do\n if i >= len then\n len\n else if fn(nth(seq, i)) then\n recur(i + 1)\n else\n i\n end\n end\n end;\n slice(seq, 0, idx)\n end,\n\n drop-while: (seq, fn) -> do\n let is-str = string?(seq);\n let len = count(seq);\n let idx = loop (i = 0) -> do\n if i >= len then\n len\n else if fn(nth(seq, i)) then\n recur(i + 1)\n else\n i\n end\n end\n end;\n slice(seq, idx)\n end,\n\n sort: (seq, ...args) -> do\n let cmp = if count(args) == 0 then compare else first(args) end;\n let is-str = string?(seq);\n let arr = if is-str then split(seq, \"\") else seq end;\n let len = count(arr);\n\n // merge two sorted arrays\n let merge-arrays = (left, right) -> do\n let left-len = count(left);\n let right-len = count(right);\n loop (result = [], li = 0, ri = 0) -> do\n if li >= left-len then\n ++(result, slice(right, ri))\n else if ri >= right-len then\n ++(result, slice(left, li))\n else do\n let l = nth(left, li);\n let r = nth(right, ri);\n if cmp(l, r) <= 0 then\n recur(push(result, l), li + 1, ri)\n else\n recur(push(result, r), li, ri + 1)\n end\n end\n end\n end\n end\n end;\n\n // recursive merge-sort\n let merge-sort = (a) -> do\n let n = count(a);\n if n <= 1 then a\n else do\n let mid = floor(n / 2);\n let left = merge-sort(slice(a, 0, mid));\n let right = merge-sort(slice(a, mid));\n merge-arrays(left, right)\n end\n end\n end;\n\n let sorted = merge-sort(arr);\n if is-str then join(sorted, \"\") else sorted end\n end\n}\n";
|
|
@@ -12873,202 +12883,202 @@ class Cache {
|
|
|
12873
12883
|
}
|
|
12874
12884
|
}
|
|
12875
12885
|
|
|
12876
|
-
|
|
12877
|
-
|
|
12878
|
-
|
|
12879
|
-
|
|
12880
|
-
|
|
12881
|
-
|
|
12882
|
-
|
|
12883
|
-
|
|
12884
|
-
|
|
12885
|
-
|
|
12886
|
-
|
|
12887
|
-
|
|
12888
|
-
|
|
12889
|
-
|
|
12890
|
-
|
|
12891
|
-
|
|
12892
|
-
|
|
12893
|
-
|
|
12894
|
-
|
|
12895
|
-
|
|
12896
|
-
|
|
12897
|
-
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
|
|
12903
|
-
}
|
|
12904
|
-
|
|
12905
|
-
|
|
12906
|
-
assertSerializableBindings(params.bindings);
|
|
12907
|
-
if (isDvalaBundle(programOrBundle)) {
|
|
12908
|
-
return this.runBundle(programOrBundle, params);
|
|
12909
|
-
}
|
|
12910
|
-
const ast = this.generateAst(programOrBundle, params);
|
|
12911
|
-
if (params.handlers) {
|
|
12912
|
-
const contextStack = createContextStack(params, this.modules, params.pure);
|
|
12913
|
-
const result = await evaluateWithEffects(ast, contextStack, params.handlers);
|
|
12914
|
-
if (result.type === 'completed')
|
|
12915
|
-
return result.value;
|
|
12916
|
-
if (result.type === 'error')
|
|
12917
|
-
throw result.error;
|
|
12918
|
-
throw new TypeError('Unexpected suspension in Dvala.async.run(). Use the standalone effects API for suspend/resume.');
|
|
12919
|
-
}
|
|
12920
|
-
return this.evaluateAsync(ast, params);
|
|
12921
|
-
},
|
|
12922
|
-
apply: async (fn, fnParams, params = {}) => {
|
|
12923
|
-
return this.apply(fn, fnParams, params);
|
|
12924
|
-
},
|
|
12925
|
-
};
|
|
12926
|
-
run(programOrBundle, params = {}) {
|
|
12927
|
-
assertSerializableBindings(params.bindings);
|
|
12928
|
-
if (isDvalaBundle(programOrBundle)) {
|
|
12929
|
-
return this.runBundle(programOrBundle, params);
|
|
12930
|
-
}
|
|
12931
|
-
const ast = this.generateAst(programOrBundle, params);
|
|
12932
|
-
const result = this.evaluate(ast, params);
|
|
12933
|
-
if (result instanceof Promise) {
|
|
12934
|
-
throw new TypeError('Unexpected async result in synchronous run(). Use dvala.async.run() for async operations.');
|
|
12935
|
-
}
|
|
12936
|
-
return result;
|
|
12937
|
-
}
|
|
12938
|
-
runBundle(bundle, params = {}) {
|
|
12939
|
-
const contextStack = createContextStack(params, this.modules, params.pure);
|
|
12940
|
-
// Evaluate file modules in dependency order and register as value modules.
|
|
12941
|
-
// Each file module is evaluated in its own scope so local bindings don't leak.
|
|
12942
|
-
// File modules are always evaluated in pure mode to ensure deterministic,
|
|
12943
|
-
// side-effect-free initialization regardless of the caller's pure setting.
|
|
12944
|
-
const savedPure = contextStack.pure;
|
|
12945
|
-
contextStack.pure = true;
|
|
12946
|
-
for (const [name, source] of bundle.fileModules) {
|
|
12947
|
-
const ast = this.generateAst(source, params);
|
|
12948
|
-
const moduleContextStack = contextStack.create({});
|
|
12949
|
-
const result = evaluate(ast, moduleContextStack);
|
|
12950
|
-
// TODO: When async functions in file modules are able to mark themselves as pure and
|
|
12951
|
-
// are returning a Promise, uncomment the following check, and make sure a test is verifying the behaviour.
|
|
12952
|
-
// if (result instanceof Promise) {
|
|
12953
|
-
// throw new TypeError('Unexpected async result in synchronous runBundle(). Use dvala.async.run() for async operations.')
|
|
12954
|
-
// }
|
|
12955
|
-
contextStack.registerValueModule(name, result);
|
|
12956
|
-
}
|
|
12957
|
-
contextStack.pure = savedPure;
|
|
12958
|
-
// Parse and evaluate the main program
|
|
12959
|
-
const ast = this.generateAst(bundle.program, params);
|
|
12960
|
-
const result = evaluate(ast, contextStack);
|
|
12961
|
-
if (result instanceof Promise) {
|
|
12962
|
-
throw new TypeError('Unexpected async result in synchronous runBundle(). Use dvala.async.run() for async operations.');
|
|
12963
|
-
}
|
|
12964
|
-
return result;
|
|
12965
|
-
}
|
|
12966
|
-
getUndefinedSymbols(programOrAst, params = {}) {
|
|
12967
|
-
const ast = typeof programOrAst === 'string' ? this.generateAst(programOrAst, params) : programOrAst;
|
|
12968
|
-
const contextStack = createContextStack(params, this.modules);
|
|
12969
|
-
return getUndefinedSymbols(ast, contextStack, builtin, evaluateNode);
|
|
12970
|
-
}
|
|
12971
|
-
tokenize(program, tokenizeParams = {}) {
|
|
12972
|
-
const tokenStream = tokenize(program, this.debug, tokenizeParams.filePath);
|
|
12973
|
-
return tokenizeParams.minify ? minifyTokenStream(tokenStream, { removeWhiteSpace: false }) : tokenStream;
|
|
12974
|
-
}
|
|
12975
|
-
parse(tokenStream) {
|
|
12976
|
-
tokenStream = minifyTokenStream(tokenStream, { removeWhiteSpace: true });
|
|
12977
|
-
const ast = {
|
|
12978
|
-
body: [],
|
|
12979
|
-
hasDebugData: tokenStream.hasDebugData,
|
|
12980
|
-
};
|
|
12981
|
-
ast.body = parse(tokenStream);
|
|
12982
|
-
return ast;
|
|
12983
|
-
}
|
|
12984
|
-
evaluate(ast, params) {
|
|
12985
|
-
const contextStack = createContextStack(params, this.modules, params.pure);
|
|
12986
|
-
return evaluate(ast, contextStack);
|
|
12987
|
-
}
|
|
12988
|
-
evaluateAsync(ast, params) {
|
|
12989
|
-
const contextStack = createContextStack(params, this.modules, params.pure);
|
|
12990
|
-
return evaluateAsync(ast, contextStack);
|
|
12991
|
-
}
|
|
12992
|
-
transformSymbols(tokenStream, transformer) {
|
|
12993
|
-
return transformSymbolTokens(tokenStream, transformer);
|
|
12994
|
-
}
|
|
12995
|
-
untokenize(tokenStream) {
|
|
12996
|
-
return untokenize(tokenStream);
|
|
12997
|
-
}
|
|
12998
|
-
apply(fn, fnParams, params = {}) {
|
|
12999
|
-
const fnName = 'FN_2eb7b316_471c_5bfa_90cb_d3dfd9164a59';
|
|
13000
|
-
const program = this.generateApplyFunctionCall(fnName, fnParams);
|
|
13001
|
-
const ast = this.generateAst(program, params);
|
|
13002
|
-
const hostValues = fnParams.reduce((result, param, index) => {
|
|
13003
|
-
result[`${fnName}_${index}`] = param;
|
|
13004
|
-
return result;
|
|
13005
|
-
}, { [fnName]: fn });
|
|
13006
|
-
params.bindings = { ...params.bindings, ...hostValues };
|
|
13007
|
-
return this.evaluate(ast, params);
|
|
13008
|
-
}
|
|
13009
|
-
generateApplyFunctionCall(fnName, fnParams) {
|
|
13010
|
-
const paramsString = fnParams
|
|
13011
|
-
.map((_, index) => {
|
|
13012
|
-
return `${fnName}_${index}`;
|
|
13013
|
-
})
|
|
13014
|
-
.join(', ');
|
|
13015
|
-
return `${fnName}(${paramsString})`;
|
|
13016
|
-
}
|
|
13017
|
-
generateAst(program, params) {
|
|
13018
|
-
if (this.astCache) {
|
|
13019
|
-
const cachedAst = this.astCache.get(program);
|
|
13020
|
-
if (cachedAst)
|
|
13021
|
-
return cachedAst;
|
|
13022
|
-
}
|
|
13023
|
-
const tokenStream = this.tokenize(program, {
|
|
13024
|
-
filePath: params.filePath,
|
|
13025
|
-
});
|
|
13026
|
-
const ast = this.parse(tokenStream);
|
|
13027
|
-
this.astCache?.set(program, ast);
|
|
13028
|
-
return ast;
|
|
13029
|
-
}
|
|
13030
|
-
getAutoCompleter(program, position, params = {}) {
|
|
13031
|
-
return new AutoCompleter(program, position, this, params);
|
|
13032
|
-
}
|
|
12886
|
+
function isDvalaBundle(value) {
|
|
12887
|
+
return (typeof value === 'object'
|
|
12888
|
+
&& value !== null
|
|
12889
|
+
&& typeof value.program === 'string'
|
|
12890
|
+
&& Array.isArray(value.fileModules));
|
|
12891
|
+
}
|
|
12892
|
+
|
|
12893
|
+
/**
|
|
12894
|
+
* Standalone tooling functions for tokenizing, parsing, and analysis.
|
|
12895
|
+
*
|
|
12896
|
+
* These are thin wrappers around internal utilities that do not require
|
|
12897
|
+
* a Dvala instance.
|
|
12898
|
+
*/
|
|
12899
|
+
/**
|
|
12900
|
+
* Get all undefined symbols in a Dvala program.
|
|
12901
|
+
*
|
|
12902
|
+
* @param source - Dvala source code
|
|
12903
|
+
* @param options - optional context to treat as defined
|
|
12904
|
+
* @param options.bindings - host bindings to treat as defined
|
|
12905
|
+
* @param options.modules - modules to treat as available
|
|
12906
|
+
*/
|
|
12907
|
+
function getUndefinedSymbols(source, options) {
|
|
12908
|
+
const modulesMap = options?.modules
|
|
12909
|
+
? new Map(options.modules.map(m => [m.name, m]))
|
|
12910
|
+
: undefined;
|
|
12911
|
+
const contextStack = createContextStack({ bindings: options?.bindings }, modulesMap);
|
|
12912
|
+
const tokenStream = tokenize(source, false, undefined);
|
|
12913
|
+
const minified = minifyTokenStream(tokenStream, { removeWhiteSpace: true });
|
|
12914
|
+
const ast = { body: parse(minified), hasDebugData: false };
|
|
12915
|
+
return getUndefinedSymbols$1(ast, contextStack, builtin, evaluateNode);
|
|
13033
12916
|
}
|
|
12917
|
+
|
|
13034
12918
|
function assertSerializableBindings(bindings) {
|
|
13035
12919
|
if (!bindings)
|
|
13036
12920
|
return;
|
|
13037
|
-
for (const [key,
|
|
13038
|
-
assertSerializable(
|
|
13039
|
-
}
|
|
12921
|
+
for (const [key, val] of Object.entries(bindings))
|
|
12922
|
+
assertSerializable(val, `bindings["${key}"]`);
|
|
13040
12923
|
}
|
|
13041
|
-
function assertSerializable(
|
|
13042
|
-
if (
|
|
12924
|
+
function assertSerializable(val, path) {
|
|
12925
|
+
if (val === null || val === undefined)
|
|
13043
12926
|
return;
|
|
13044
|
-
if (typeof
|
|
12927
|
+
if (typeof val === 'boolean' || typeof val === 'string')
|
|
13045
12928
|
return;
|
|
13046
|
-
if (typeof
|
|
13047
|
-
if (!Number.isFinite(
|
|
13048
|
-
throw new TypeError(`${path} is not serializable (${
|
|
12929
|
+
if (typeof val === 'number') {
|
|
12930
|
+
if (!Number.isFinite(val))
|
|
12931
|
+
throw new TypeError(`${path} is not serializable (${val})`);
|
|
13049
12932
|
return;
|
|
13050
12933
|
}
|
|
13051
|
-
if (typeof
|
|
12934
|
+
if (typeof val === 'function')
|
|
13052
12935
|
throw new TypeError(`${path} is not serializable (function)`);
|
|
13053
|
-
if (typeof
|
|
13054
|
-
|
|
13055
|
-
if (FUNCTION_SYMBOL in value) {
|
|
12936
|
+
if (typeof val === 'object') {
|
|
12937
|
+
if (FUNCTION_SYMBOL in val || REGEXP_SYMBOL in val || EFFECT_SYMBOL in val)
|
|
13056
12938
|
return;
|
|
13057
|
-
|
|
13058
|
-
|
|
13059
|
-
return;
|
|
13060
|
-
if (Array.isArray(value)) {
|
|
13061
|
-
value.forEach((item, i) => assertSerializable(item, `${path}[${i}]`));
|
|
12939
|
+
if (Array.isArray(val)) {
|
|
12940
|
+
val.forEach((item, i) => assertSerializable(item, `${path}[${i}]`));
|
|
13062
12941
|
return;
|
|
13063
12942
|
}
|
|
13064
|
-
if (Object.getPrototypeOf(
|
|
12943
|
+
if (Object.getPrototypeOf(val) !== Object.prototype)
|
|
13065
12944
|
throw new TypeError(`${path} is not serializable (not a plain object)`);
|
|
13066
|
-
for (const [k, v] of Object.entries(
|
|
12945
|
+
for (const [k, v] of Object.entries(val))
|
|
13067
12946
|
assertSerializable(v, `${path}.${k}`);
|
|
13068
12947
|
return;
|
|
13069
12948
|
}
|
|
13070
12949
|
throw new TypeError(`${path} is not serializable`);
|
|
13071
12950
|
}
|
|
12951
|
+
function createDvala(options) {
|
|
12952
|
+
initCoreDvalaSources();
|
|
12953
|
+
const modules = options?.modules
|
|
12954
|
+
? new Map(options.modules.map(m => [m.name, m]))
|
|
12955
|
+
: undefined;
|
|
12956
|
+
const factoryBindings = options?.bindings;
|
|
12957
|
+
const factoryEffectHandlers = options?.effectHandlers;
|
|
12958
|
+
const debug = options?.debug ?? false;
|
|
12959
|
+
const cache = options?.cache ? new Cache(options.cache) : null;
|
|
12960
|
+
function buildAst(source, filePath) {
|
|
12961
|
+
if (!filePath && cache) {
|
|
12962
|
+
const cached = cache.get(source);
|
|
12963
|
+
if (cached)
|
|
12964
|
+
return cached;
|
|
12965
|
+
}
|
|
12966
|
+
const tokenStream = tokenize(source, debug, filePath);
|
|
12967
|
+
const minified = minifyTokenStream(tokenStream, { removeWhiteSpace: true });
|
|
12968
|
+
const ast = { body: parse(minified), hasDebugData: debug };
|
|
12969
|
+
if (!filePath)
|
|
12970
|
+
cache?.set(source, ast);
|
|
12971
|
+
return ast;
|
|
12972
|
+
}
|
|
12973
|
+
function mergeBindings(runBindings) {
|
|
12974
|
+
if (!factoryBindings && !runBindings)
|
|
12975
|
+
return undefined;
|
|
12976
|
+
return { ...factoryBindings, ...runBindings };
|
|
12977
|
+
}
|
|
12978
|
+
function mergeEffectHandlers(runEffectHandlers) {
|
|
12979
|
+
if (!factoryEffectHandlers && !runEffectHandlers)
|
|
12980
|
+
return undefined;
|
|
12981
|
+
// Run handlers first (checked first), factory handlers fill in the rest.
|
|
12982
|
+
// For same key, run overrides factory.
|
|
12983
|
+
const result = { ...runEffectHandlers };
|
|
12984
|
+
if (factoryEffectHandlers) {
|
|
12985
|
+
for (const [k, v] of Object.entries(factoryEffectHandlers)) {
|
|
12986
|
+
if (!(k in result))
|
|
12987
|
+
result[k] = v;
|
|
12988
|
+
}
|
|
12989
|
+
}
|
|
12990
|
+
return result;
|
|
12991
|
+
}
|
|
12992
|
+
function assertNotPureWithHandlers(pure, effectHandlers) {
|
|
12993
|
+
if (!pure)
|
|
12994
|
+
return;
|
|
12995
|
+
const hasEffectHandlers = effectHandlers && Object.keys(effectHandlers).length > 0;
|
|
12996
|
+
if (hasEffectHandlers) {
|
|
12997
|
+
throw new TypeError('Cannot use pure mode with effect handlers');
|
|
12998
|
+
}
|
|
12999
|
+
}
|
|
13000
|
+
return {
|
|
13001
|
+
run(source, runOptions) {
|
|
13002
|
+
assertSerializableBindings(runOptions?.bindings);
|
|
13003
|
+
const bindings = mergeBindings(runOptions?.bindings);
|
|
13004
|
+
const effectHandlers = mergeEffectHandlers(runOptions?.effectHandlers);
|
|
13005
|
+
const pure = runOptions?.pure ?? false;
|
|
13006
|
+
assertNotPureWithHandlers(pure, effectHandlers);
|
|
13007
|
+
const contextStack = createContextStack({ bindings }, modules, pure);
|
|
13008
|
+
if (isDvalaBundle(source)) {
|
|
13009
|
+
const savedPure = contextStack.pure;
|
|
13010
|
+
contextStack.pure = true;
|
|
13011
|
+
for (const [name, fileSource] of source.fileModules) {
|
|
13012
|
+
const fileAst = buildAst(fileSource);
|
|
13013
|
+
const moduleContextStack = contextStack.create({});
|
|
13014
|
+
contextStack.registerValueModule(name, evaluate(fileAst, moduleContextStack));
|
|
13015
|
+
}
|
|
13016
|
+
contextStack.pure = savedPure;
|
|
13017
|
+
const ast = buildAst(source.program);
|
|
13018
|
+
const result = evaluate(ast, contextStack);
|
|
13019
|
+
if (result instanceof Promise)
|
|
13020
|
+
throw new TypeError('Unexpected async result in run(). Use runAsync() for async operations.');
|
|
13021
|
+
return result;
|
|
13022
|
+
}
|
|
13023
|
+
const ast = buildAst(source, runOptions?.filePath);
|
|
13024
|
+
if (effectHandlers) {
|
|
13025
|
+
return evaluateWithSyncEffects(ast, contextStack, effectHandlers);
|
|
13026
|
+
}
|
|
13027
|
+
const result = evaluate(ast, contextStack);
|
|
13028
|
+
if (result instanceof Promise) {
|
|
13029
|
+
throw new TypeError('Unexpected async result in run(). Use runAsync() for async operations.');
|
|
13030
|
+
}
|
|
13031
|
+
return result;
|
|
13032
|
+
},
|
|
13033
|
+
async runAsync(source, runOptions) {
|
|
13034
|
+
assertSerializableBindings(runOptions?.bindings);
|
|
13035
|
+
const bindings = mergeBindings(runOptions?.bindings);
|
|
13036
|
+
const effectHandlers = mergeEffectHandlers(runOptions?.effectHandlers);
|
|
13037
|
+
const pure = runOptions?.pure ?? false;
|
|
13038
|
+
assertNotPureWithHandlers(pure, effectHandlers);
|
|
13039
|
+
try {
|
|
13040
|
+
const contextStack = createContextStack({ bindings }, modules, pure);
|
|
13041
|
+
if (isDvalaBundle(source)) {
|
|
13042
|
+
const savedPure = contextStack.pure;
|
|
13043
|
+
contextStack.pure = true;
|
|
13044
|
+
for (const [name, fileSource] of source.fileModules) {
|
|
13045
|
+
const fileAst = buildAst(fileSource);
|
|
13046
|
+
const moduleContextStack = contextStack.create({});
|
|
13047
|
+
contextStack.registerValueModule(name, evaluate(fileAst, moduleContextStack));
|
|
13048
|
+
}
|
|
13049
|
+
contextStack.pure = savedPure;
|
|
13050
|
+
}
|
|
13051
|
+
const programSource = isDvalaBundle(source) ? source.program : source;
|
|
13052
|
+
const ast = buildAst(programSource);
|
|
13053
|
+
const result = await evaluateWithEffects(ast, contextStack, effectHandlers, runOptions?.maxSnapshots, {
|
|
13054
|
+
values: bindings,
|
|
13055
|
+
modules,
|
|
13056
|
+
});
|
|
13057
|
+
if (result.type === 'completed') {
|
|
13058
|
+
return { ...result, definedBindings: contextStack.getModuleScopeBindings() };
|
|
13059
|
+
}
|
|
13060
|
+
return result;
|
|
13061
|
+
}
|
|
13062
|
+
catch (error) {
|
|
13063
|
+
if (error instanceof DvalaError) {
|
|
13064
|
+
return { type: 'error', error };
|
|
13065
|
+
}
|
|
13066
|
+
if (error instanceof TypeError) {
|
|
13067
|
+
throw error;
|
|
13068
|
+
}
|
|
13069
|
+
return { type: 'error', error: new DvalaError(`${error}`, undefined) };
|
|
13070
|
+
}
|
|
13071
|
+
},
|
|
13072
|
+
getUndefinedSymbols(source) {
|
|
13073
|
+
const modulesList = modules ? [...modules.values()] : undefined;
|
|
13074
|
+
return getUndefinedSymbols(source, { bindings: factoryBindings, modules: modulesList });
|
|
13075
|
+
},
|
|
13076
|
+
getAutoCompleter(program, position) {
|
|
13077
|
+
const params = { bindings: factoryBindings };
|
|
13078
|
+
return new AutoCompleter(program, position, params);
|
|
13079
|
+
},
|
|
13080
|
+
};
|
|
13081
|
+
}
|
|
13072
13082
|
|
|
13073
13083
|
var assertionModuleSource = "{}";
|
|
13074
13084
|
|
|
@@ -14448,7 +14458,7 @@ const assertModule = {
|
|
|
14448
14458
|
docs: moduleDocs$5,
|
|
14449
14459
|
};
|
|
14450
14460
|
|
|
14451
|
-
var gridModuleSource = "do\nlet _transpose = (g) ->\n map(range(count(first(g))), (i) ->\n map(range(count(g)), (j) -> nth(nth(g, j), i))\n );\n{\n \"cell-every?\": (grid, predicate) -> do\n let cells = flatten(grid, 1);\n loop(i = 0) ->\n cond\n case i >= count(cells) then true\n case not(predicate(nth(cells, i))) then false\n case true then recur(i + 1)\n end\n end,\n
|
|
14461
|
+
var gridModuleSource = "do\nlet _transpose = (g) ->\n map(range(count(first(g))), (i) ->\n map(range(count(g)), (j) -> nth(nth(g, j), i))\n );\n{\n \"cell-every?\": (grid, predicate) -> do\n let cells = flatten(grid, 1);\n loop(i = 0) ->\n cond\n case i >= count(cells) then true\n case not(predicate(nth(cells, i))) then false\n case true then recur(i + 1)\n end\n end,\n \"some?\": (grid, predicate) -> do\n let cells = flatten(grid, 1);\n loop(i = 0) ->\n cond\n case i >= count(cells) then false\n case predicate(nth(cells, i)) then true\n case true then recur(i + 1)\n end\n end,\n\n \"every-row?\": (grid, predicate) ->\n loop(i = 0) ->\n cond\n case i >= count(grid) then true\n case not(predicate(nth(grid, i))) then false\n case true then recur(i + 1)\n end,\n\n \"some-row?\": (grid, predicate) ->\n loop(i = 0) ->\n cond\n case i >= count(grid) then false\n case predicate(nth(grid, i)) then true\n case true then recur(i + 1)\n end,\n\n \"every-col?\": (grid, predicate) -> do\n let cols = _transpose(grid);\n loop(i = 0) ->\n cond\n case i >= count(cols) then true\n case not(predicate(nth(cols, i))) then false\n case true then recur(i + 1)\n end\n end,\n\n \"some-col?\": (grid, predicate) -> do\n let cols = _transpose(grid);\n loop(i = 0) ->\n cond\n case i >= count(cols) then false\n case predicate(nth(cols, i)) then true\n case true then recur(i + 1)\n end\n end,\n\n \"generate\": (rows, cols, generator) ->\n map(range(rows), (i) -> map(range(cols), (j) -> generator(i, j))),\n\n \"cell-map\": (...params) -> do\n let fn = last(params);\n let grids = drop-last(params, 1);\n let rows = count(first(grids));\n let cols = count(first(first(grids)));\n map(range(rows), (i) ->\n map(range(cols), (j) ->\n apply(fn, map(grids, (g) -> nth(nth(g, i), j)))\n )\n )\n end,\n\n \"cell-mapi\": (grid, fn) -> do\n let rows = count(grid);\n let cols = count(first(grid));\n map(range(rows), (i) ->\n map(range(cols), (j) ->\n fn(nth(nth(grid, i), j), i, j)\n )\n )\n end,\n\n \"cell-reduce\": (grid, fn, initial-value) ->\n reduce(flatten(grid, 1), fn, initial-value),\n\n \"cell-reducei\": (grid, fn, initial-value) -> do\n let rows = count(grid);\n let cols = count(first(grid));\n loop(acc = initial-value, i = 0, j = 0) ->\n cond\n case i >= rows then acc\n case j >= cols then recur(acc, i + 1, 0)\n case true then recur(fn(acc, nth(nth(grid, i), j), i, j), i, j + 1)\n end\n end\n}\nend;";
|
|
14452
14462
|
|
|
14453
14463
|
const moduleDocs$4 = {
|
|
14454
14464
|
'cell-every?': {
|
|
@@ -34681,7 +34691,7 @@ function runTest({ testPath: filePath, testNamePattern }) {
|
|
|
34681
34691
|
}
|
|
34682
34692
|
else {
|
|
34683
34693
|
try {
|
|
34684
|
-
const dvala =
|
|
34694
|
+
const dvala = createDvala({ debug: true, modules: allBuiltinModules });
|
|
34685
34695
|
const bindings = getBindings(includedFilePaths, dvala);
|
|
34686
34696
|
dvala.run(testChunkProgram.program, {
|
|
34687
34697
|
bindings,
|
|
@@ -35156,7 +35166,7 @@ function specialExpressionDocsToReference() {
|
|
|
35156
35166
|
}
|
|
35157
35167
|
const specialExpressionsReference = specialExpressionDocsToReference();
|
|
35158
35168
|
function isFunctionReference(ref) {
|
|
35159
|
-
return 'returns' in ref && 'args' in ref && 'variants' in ref;
|
|
35169
|
+
return 'returns' in ref && 'args' in ref && 'variants' in ref && !('effect' in ref);
|
|
35160
35170
|
}
|
|
35161
35171
|
const normalExpressionReference = {
|
|
35162
35172
|
// Core categories — all derived from co-located docs
|
|
@@ -37064,7 +37074,7 @@ function getArgumentInfo(fmt, reference) {
|
|
|
37064
37074
|
}
|
|
37065
37075
|
|
|
37066
37076
|
/* eslint-disable no-console */
|
|
37067
|
-
const dvala =
|
|
37077
|
+
const dvala = createDvala({ debug: false });
|
|
37068
37078
|
function getCliFunctionExamples(fmt, reference) {
|
|
37069
37079
|
const { examples } = reference;
|
|
37070
37080
|
return examples
|
|
@@ -37671,19 +37681,15 @@ const helpRegExp = new RegExp(`^\`help\\s+(${polishSymbolFirstCharacterClass}${p
|
|
|
37671
37681
|
const expressions = [...normalExpressionKeys, ...specialExpressionKeys];
|
|
37672
37682
|
const config = processArguments(process.argv.slice(2));
|
|
37673
37683
|
const cliModules = getCliModules();
|
|
37674
|
-
function
|
|
37675
|
-
const
|
|
37684
|
+
function makeDvala(bindings, pure) {
|
|
37685
|
+
const runner = createDvala({ debug: true, modules: [...allBuiltinModules, ...cliModules], bindings });
|
|
37676
37686
|
return {
|
|
37677
|
-
run: (program) =>
|
|
37678
|
-
globalContext: context,
|
|
37679
|
-
globalModuleScope: true,
|
|
37680
|
-
pure,
|
|
37681
|
-
}),
|
|
37687
|
+
run: (program) => runner.run(program, { pure }),
|
|
37682
37688
|
};
|
|
37683
37689
|
}
|
|
37684
37690
|
switch (config.subcommand) {
|
|
37685
37691
|
case 'run': {
|
|
37686
|
-
const dvala =
|
|
37692
|
+
const dvala = makeDvala(config.context, config.pure);
|
|
37687
37693
|
try {
|
|
37688
37694
|
const content = fs.readFileSync(config.filename, { encoding: 'utf-8' });
|
|
37689
37695
|
const result = dvala.run(content);
|
|
@@ -37699,7 +37705,7 @@ switch (config.subcommand) {
|
|
|
37699
37705
|
break;
|
|
37700
37706
|
}
|
|
37701
37707
|
case 'run-bundle': {
|
|
37702
|
-
const dvala =
|
|
37708
|
+
const dvala = makeDvala(config.context, config.pure);
|
|
37703
37709
|
try {
|
|
37704
37710
|
const content = fs.readFileSync(config.filename, { encoding: 'utf-8' });
|
|
37705
37711
|
let parsed;
|
|
@@ -37727,7 +37733,7 @@ switch (config.subcommand) {
|
|
|
37727
37733
|
break;
|
|
37728
37734
|
}
|
|
37729
37735
|
case 'eval': {
|
|
37730
|
-
const dvala =
|
|
37736
|
+
const dvala = makeDvala(config.context, config.pure);
|
|
37731
37737
|
try {
|
|
37732
37738
|
const result = dvala.run(config.expression);
|
|
37733
37739
|
if (config.printResult) {
|
|
@@ -37767,12 +37773,12 @@ switch (config.subcommand) {
|
|
|
37767
37773
|
}
|
|
37768
37774
|
case 'repl': {
|
|
37769
37775
|
if (config.loadFilename) {
|
|
37770
|
-
const dvala =
|
|
37776
|
+
const dvala = makeDvala(config.context, false);
|
|
37771
37777
|
const content = fs.readFileSync(config.loadFilename, { encoding: 'utf-8' });
|
|
37772
37778
|
const result = dvala.run(content);
|
|
37773
37779
|
if (result !== null && typeof result === 'object' && !Array.isArray(result)) {
|
|
37774
37780
|
for (const [key, value] of Object.entries(result)) {
|
|
37775
|
-
config.context[key] =
|
|
37781
|
+
config.context[key] = value;
|
|
37776
37782
|
}
|
|
37777
37783
|
}
|
|
37778
37784
|
}
|
|
@@ -37803,13 +37809,12 @@ function runDvalaTest(testPath, testNamePattern) {
|
|
|
37803
37809
|
if (!success)
|
|
37804
37810
|
process.exit(1);
|
|
37805
37811
|
}
|
|
37806
|
-
async function execute(expression,
|
|
37807
|
-
const _dvala =
|
|
37812
|
+
async function execute(expression, bindings, readLine) {
|
|
37813
|
+
const _dvala = createDvala({ debug: true, modules: [...allBuiltinModules, ...cliModules] });
|
|
37808
37814
|
try {
|
|
37809
|
-
const
|
|
37810
|
-
|
|
37811
|
-
|
|
37812
|
-
handlers: {
|
|
37815
|
+
const runResult = await _dvala.runAsync(expression, {
|
|
37816
|
+
bindings,
|
|
37817
|
+
effectHandlers: {
|
|
37813
37818
|
'dvala.io.read-line': async ({ args, resume }) => {
|
|
37814
37819
|
const message = typeof args[0] === 'string' ? args[0] : '';
|
|
37815
37820
|
const answer = await readLine(message);
|
|
@@ -37817,18 +37822,21 @@ async function execute(expression, context, readLine) {
|
|
|
37817
37822
|
},
|
|
37818
37823
|
},
|
|
37819
37824
|
});
|
|
37825
|
+
if (runResult.type === 'error')
|
|
37826
|
+
throw runResult.error;
|
|
37827
|
+
const result = runResult.type === 'completed' ? runResult.value : null;
|
|
37820
37828
|
historyResults.unshift(result);
|
|
37821
37829
|
if (historyResults.length > 9) {
|
|
37822
37830
|
historyResults.length = 9;
|
|
37823
37831
|
}
|
|
37824
|
-
|
|
37832
|
+
const newBindings = { ...bindings, ...(runResult.type === 'completed' ? runResult.definedBindings : {}) };
|
|
37833
|
+
setReplHistoryVariables(newBindings);
|
|
37825
37834
|
console.log(stringifyValue(result, false));
|
|
37826
|
-
return
|
|
37835
|
+
return newBindings;
|
|
37827
37836
|
}
|
|
37828
37837
|
catch (error) {
|
|
37829
37838
|
printErrorMessage(`${error}`);
|
|
37830
|
-
|
|
37831
|
-
return false;
|
|
37839
|
+
return { ...bindings, '*e*': getErrorMessage(error) };
|
|
37832
37840
|
}
|
|
37833
37841
|
}
|
|
37834
37842
|
function getErrorMessage(error) {
|
|
@@ -37836,18 +37844,11 @@ function getErrorMessage(error) {
|
|
|
37836
37844
|
return error.message;
|
|
37837
37845
|
return 'Unknown error';
|
|
37838
37846
|
}
|
|
37839
|
-
function setReplHistoryVariables(
|
|
37840
|
-
|
|
37841
|
-
|
|
37842
|
-
delete context['*3*'];
|
|
37843
|
-
delete context['*4*'];
|
|
37844
|
-
delete context['*5*'];
|
|
37845
|
-
delete context['*6*'];
|
|
37846
|
-
delete context['*7*'];
|
|
37847
|
-
delete context['*8*'];
|
|
37848
|
-
delete context['*9*'];
|
|
37847
|
+
function setReplHistoryVariables(bindings) {
|
|
37848
|
+
for (let i = 1; i <= 9; i++)
|
|
37849
|
+
delete bindings[`*${i}*`];
|
|
37849
37850
|
historyResults.forEach((value, i) => {
|
|
37850
|
-
|
|
37851
|
+
bindings[`*${i + 1}*`] = value;
|
|
37851
37852
|
});
|
|
37852
37853
|
}
|
|
37853
37854
|
function parseOption(args, i) {
|
|
@@ -37890,7 +37891,7 @@ function parseContextOptions(args, startIndex) {
|
|
|
37890
37891
|
}
|
|
37891
37892
|
try {
|
|
37892
37893
|
Object.entries(JSON.parse(parsed.argument)).forEach(([key, value]) => {
|
|
37893
|
-
options.context[key] =
|
|
37894
|
+
options.context[key] = value;
|
|
37894
37895
|
});
|
|
37895
37896
|
}
|
|
37896
37897
|
catch (e) {
|
|
@@ -37908,7 +37909,7 @@ function parseContextOptions(args, startIndex) {
|
|
|
37908
37909
|
try {
|
|
37909
37910
|
const contextString = fs.readFileSync(parsed.argument, { encoding: 'utf-8' });
|
|
37910
37911
|
Object.entries(JSON.parse(contextString)).forEach(([key, value]) => {
|
|
37911
|
-
options.context[key] =
|
|
37912
|
+
options.context[key] = value;
|
|
37912
37913
|
});
|
|
37913
37914
|
}
|
|
37914
37915
|
catch (e) {
|
|
@@ -38140,9 +38141,10 @@ function processArguments(args) {
|
|
|
38140
38141
|
}
|
|
38141
38142
|
}
|
|
38142
38143
|
}
|
|
38143
|
-
function runREPL(
|
|
38144
|
+
function runREPL(initialBindings) {
|
|
38144
38145
|
console.log(`Welcome to Dvala v${version}.
|
|
38145
38146
|
Type ${fmt.italic('`help')} for more information.`);
|
|
38147
|
+
let bindings = initialBindings;
|
|
38146
38148
|
const rl = createReadlineInterface({
|
|
38147
38149
|
completer,
|
|
38148
38150
|
historySize: HIST_SIZE,
|
|
@@ -38169,7 +38171,7 @@ Type ${fmt.italic('`help')} for more information.`);
|
|
|
38169
38171
|
printHelp();
|
|
38170
38172
|
break;
|
|
38171
38173
|
case '`context':
|
|
38172
|
-
printContext(
|
|
38174
|
+
printContext(bindings);
|
|
38173
38175
|
break;
|
|
38174
38176
|
case '`quit':
|
|
38175
38177
|
rl.close();
|
|
@@ -38179,7 +38181,7 @@ Type ${fmt.italic('`help')} for more information.`);
|
|
|
38179
38181
|
}
|
|
38180
38182
|
}
|
|
38181
38183
|
else if (line) {
|
|
38182
|
-
await execute(line,
|
|
38184
|
+
bindings = await execute(line, bindings, readLine);
|
|
38183
38185
|
}
|
|
38184
38186
|
rl.prompt();
|
|
38185
38187
|
}).on('close', () => {
|
|
@@ -38248,14 +38250,14 @@ Global options:
|
|
|
38248
38250
|
With no subcommand, starts an interactive REPL.
|
|
38249
38251
|
`.trim());
|
|
38250
38252
|
}
|
|
38251
|
-
function printContext(
|
|
38252
|
-
const keys = Object.keys(
|
|
38253
|
+
function printContext(bindings) {
|
|
38254
|
+
const keys = Object.keys(bindings);
|
|
38253
38255
|
if (keys.length === 0) {
|
|
38254
38256
|
console.log('[empty]\n');
|
|
38255
38257
|
}
|
|
38256
38258
|
else {
|
|
38257
38259
|
keys.sort().forEach((x) => {
|
|
38258
|
-
console.log(`${x} = ${formatValue(stringifyValue(
|
|
38260
|
+
console.log(`${x} = ${formatValue(stringifyValue(bindings[x]))}`);
|
|
38259
38261
|
});
|
|
38260
38262
|
console.log();
|
|
38261
38263
|
}
|
|
@@ -38270,8 +38272,8 @@ function completer(line) {
|
|
|
38270
38272
|
if (expressionMatch)
|
|
38271
38273
|
return [expressions.filter(c => c.startsWith(expressionMatch[2])).map(c => `${expressionMatch[1]}${c} `), line];
|
|
38272
38274
|
// TODO, add reserved names
|
|
38273
|
-
const
|
|
38274
|
-
const names = Array.from(new Set([...Object.keys(
|
|
38275
|
+
const replBindings = config.context ?? {};
|
|
38276
|
+
const names = Array.from(new Set([...Object.keys(replBindings)]));
|
|
38275
38277
|
const nameMatch = nameRegExp.exec(line);
|
|
38276
38278
|
if (nameMatch)
|
|
38277
38279
|
return [names.filter(c => c.startsWith(nameMatch[2])).map(c => `${nameMatch[1]}${c} `), line];
|