solve-engine 2.2.0 → 2.3.0
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/{Configuration-B6W0-T67.d.cts → Configuration-Culg8H_o.d.cts} +17 -0
- package/dist/{Configuration-B6W0-T67.d.ts → Configuration-Culg8H_o.d.ts} +17 -0
- package/dist/{PackageCompatibility-Cv7zjqSV.d.cts → PackageCompatibility-CN-2CVhu.d.cts} +1 -1
- package/dist/{PackageCompatibility-DBsZ0l2a.d.ts → PackageCompatibility-DbpyKYOe.d.ts} +1 -1
- package/dist/{PackageRegistry-BSGQ6saH.d.cts → PackageRegistry-DXsvmpQN.d.cts} +24 -1
- package/dist/{PackageRegistry-DRbqNJfI.d.ts → PackageRegistry-jqdAsm6U.d.ts} +24 -1
- package/dist/{chunk-7GK3XEZF.js → chunk-7E7NXKQ5.js} +2 -2
- package/dist/chunk-7E7NXKQ5.js.map +1 -0
- package/dist/chunk-AIIPKVKO.js +3 -0
- package/dist/chunk-AIIPKVKO.js.map +1 -0
- package/dist/chunk-BAVZMGTZ.cjs +2 -0
- package/dist/chunk-BAVZMGTZ.cjs.map +1 -0
- package/dist/chunk-ESXQDRP7.js +2 -0
- package/dist/chunk-ESXQDRP7.js.map +1 -0
- package/dist/chunk-JFREB45J.cjs +3 -0
- package/dist/chunk-JFREB45J.cjs.map +1 -0
- package/dist/chunk-L7ZQQ773.cjs +5 -0
- package/dist/chunk-L7ZQQ773.cjs.map +1 -0
- package/dist/{chunk-ZCNBT44X.cjs → chunk-LNZNJSRW.cjs} +2 -2
- package/dist/chunk-LNZNJSRW.cjs.map +1 -0
- package/dist/chunk-NNYMXK6H.js +5 -0
- package/dist/chunk-NNYMXK6H.js.map +1 -0
- package/dist/{chunk-6C6IPUBH.cjs → chunk-PBGPQM3X.cjs} +3 -3
- package/dist/{chunk-6C6IPUBH.cjs.map → chunk-PBGPQM3X.cjs.map} +1 -1
- package/dist/{chunk-7NXAZJDB.js → chunk-PXWOATTF.js} +3 -3
- package/dist/{chunk-7NXAZJDB.js.map → chunk-PXWOATTF.js.map} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.d.cts +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/engine.cjs +1 -1
- package/dist/engine.d.cts +5 -5
- package/dist/engine.d.ts +5 -5
- package/dist/engine.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/language.d.cts +3 -3
- package/dist/language.d.ts +3 -3
- package/dist/packages.cjs +1 -1
- package/dist/packages.d.cts +11 -2
- package/dist/packages.d.ts +11 -2
- package/dist/packages.js +1 -1
- package/dist/resolvers.cjs +1 -1
- package/dist/resolvers.d.cts +14 -0
- package/dist/resolvers.d.ts +14 -0
- package/dist/resolvers.js +1 -1
- package/dist/testing.cjs +2 -2
- package/dist/testing.d.cts +3 -3
- package/dist/testing.d.ts +3 -3
- package/dist/testing.js +1 -1
- package/dist/worker.cjs +2 -2
- package/dist/worker.d.cts +2 -2
- package/dist/worker.d.ts +2 -2
- package/dist/worker.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-7GK3XEZF.js.map +0 -1
- package/dist/chunk-E2Q3PQ2G.js +0 -3
- package/dist/chunk-E2Q3PQ2G.js.map +0 -1
- package/dist/chunk-KVTOV3TZ.js +0 -2
- package/dist/chunk-KVTOV3TZ.js.map +0 -1
- package/dist/chunk-OVKDDTXO.cjs +0 -3
- package/dist/chunk-OVKDDTXO.cjs.map +0 -1
- package/dist/chunk-U2UZJX5W.cjs +0 -5
- package/dist/chunk-U2UZJX5W.cjs.map +0 -1
- package/dist/chunk-X3R7B7XM.cjs +0 -2
- package/dist/chunk-X3R7B7XM.cjs.map +0 -1
- package/dist/chunk-YU3PACLE.js +0 -5
- package/dist/chunk-YU3PACLE.js.map +0 -1
- package/dist/chunk-ZCNBT44X.cjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/packages/arithmetic/parselets/NumberParselet.ts","../src/packages/arithmetic/parselets/PrefixOpParselet.ts","../src/packages/arithmetic/parselets/BinaryOpParselet.ts","../src/packages/arithmetic/parselets/GroupParselet.ts","../src/packages/arithmetic/parselets/ConstantParselet.ts","../src/packages/arithmetic/normalizer/LargeNumberSuffixNormalizerRule.ts","../src/packages/arithmetic/ArithmeticPackage.ts","../src/packages/percentage/parselets/PercentParselet.ts","../src/packages/percentage/parselets/IsWhatParselet.ts","../src/packages/percentage/parselets/OnOffBaseParselet.ts","../src/packages/percentage/normalizer/PercentOnOffNormalizerRule.ts","../src/packages/percentage/parselets/OfParselet.ts","../src/packages/percentage/parselets/OfWhatIsParselet.ts","../src/packages/percentage/parselets/OnOffWhatIsParselet.ts","../src/packages/percentage/parselets/IncreaseDecreaseParselet.ts","../src/packages/percentage/parselets/IncreaseByParselet.ts","../src/packages/percentage/parselets/PercentageChangeParselet.ts","../src/packages/percentage/parselets/UpDownParselet.ts","../src/packages/percentage/normalizer/PercentUpDownNormalizerRule.ts","../src/packages/percentage/PercentagePackage.ts","../src/packages/function/parselets/FunctionCallParselet.ts","../src/packages/function/FunctionPackage.ts","../src/packages/datetime/parselets/NowParselet.ts","../src/packages/datetime/parselets/NextLastParselet.ts","../src/packages/datetime/parselets/UntilSinceParselet.ts","../src/packages/datetime/parselets/WorkdaysInParselet.ts","../src/packages/datetime/parselets/WorkdayOffsetParselet.ts","../src/packages/datetime/parselets/WorkdaysBetweenParselet.ts","../src/packages/datetime/parselets/DateFieldQueryParselet.ts","../src/packages/datetime/parselets/DurationBetweenParselet.ts","../src/packages/datetime/parselets/DayTypePredicateParselet.ts","../src/packages/datetime/parselets/CurrentTimestampParselet.ts","../src/packages/datetime/parselets/ToDateParselet.ts","../src/packages/datetime/parselets/ToTimestampParselet.ts","../src/packages/datetime/parselets/DateLiteralParselet.ts","../src/packages/datetime/Iso8601.ts","../src/packages/datetime/parselets/DatetimeTimestampPluginFunctions.ts","../src/packages/datetime/normalizer/UntilSinceNormalizerRule.ts","../src/packages/datetime/normalizer/BetweenUnitNormalizerRule.ts","../src/packages/datetime/normalizer/WorkdayRateDenominatorNormalizerRule.ts","../src/packages/datetime/normalizer/DateLiteralNormalizerRule.ts","../src/packages/datetime/normalizer/MonthNameDateNormalizerRule.ts","../src/packages/datetime/parselets/DaysInPeriodParselet.ts","../src/packages/datetime/normalizer/DaysInPeriodNormalizerRule.ts","../src/packages/datetime/normalizer/DailyNoteLinkNormalizerRule.ts","../src/packages/datetime/DatetimePackage.ts","../src/packages/time/timezones/CityZones.ts","../src/packages/time/timezones/ZoneMath.ts","../src/packages/time/parselets/shared/ZoneReference.ts","../src/packages/time/parselets/TimezonePluginFunctions.ts","../src/packages/time/parselets/ClockTimeParselet.ts","../src/packages/time/parselets/ClockTimeIntervalParselet.ts","../src/packages/time/parselets/FpsRateParselet.ts","../src/packages/time/parselets/LaptimeParselet.ts","../src/packages/time/parselets/TimeInZoneParselet.ts","../src/packages/time/parselets/TimeDifferenceParselet.ts","../src/packages/time/parselets/VideoTimecodeParselet.ts","../src/packages/time/timecode/TimecodeMath.ts","../src/packages/time/parselets/FrameCountParselet.ts","../src/packages/time/normalizer/ClockTimeNormalizerRule.ts","../src/packages/time/normalizer/ClockTimeIntervalNormalizerRule.ts","../src/packages/time/normalizer/FpsRateNormalizerRule.ts","../src/packages/time/normalizer/LaptimeNormalizerRule.ts","../src/packages/time/normalizer/VideoTimecodeNormalizerRule.ts","../src/packages/time/normalizer/FrameCountNormalizerRule.ts","../src/packages/time/TimespanConverters.ts","../src/packages/time/TimePackage.ts","../src/parser/PhrasePattern.ts","../src/packages/dice/parselets/DiceRollParselet.ts","../src/packages/dice/DicePackage.ts","../src/packages/variables/parselets/VariableParselet.ts","../src/packages/variables/parselets/IdentifierParselet.ts","../src/packages/variables/parselets/GlobalVariableParselet.ts","../src/vm/GlobalVariableAsyncResolver.ts","../src/packages/variables/VariablesPackage.ts","../src/uom/HistoricalCurrency.ts","../src/packages/uom/parselets/UomLiteralParselet.ts","../src/packages/uom/parselets/ConvertParselet.ts","../src/packages/uom/parselets/PossibilitiesParselet.ts","../src/packages/uom/normalizer/PossibilitiesNormalizerRule.ts","../src/packages/uom/normalizer/CompoundQuantityNormalizerRule.ts","../src/packages/uom/normalizer/TwoUnitConversionNormalizerRule.ts","../src/packages/uom/normalizer/DegreeSymbolNormalizerRule.ts","../src/packages/uom/parselets/TwoUnitConversionParselet.ts","../src/packages/uom/normalizer/BareRateDenominatorNormalizerRule.ts","../src/packages/uom/parselets/PerUnitParselet.ts","../src/packages/uom/parselets/AtRateParselet.ts","../src/packages/uom/normalizer/AtRateNormalizerRule.ts","../src/packages/uom/normalizer/ForDurationNormalizerRule.ts","../src/packages/uom/normalizer/ReversedConversionNormalizerRule.ts","../src/packages/uom/data/IngredientDensities.ts","../src/packages/uom/parselets/CookingPluginFunctions.ts","../src/packages/uom/parselets/CookingConversionParselet.ts","../src/packages/uom/normalizer/IngredientNameNormalizerRule.ts","../src/packages/uom/normalizer/MultiWordUnitNormalizerRule.ts","../src/packages/uom/normalizer/CompoundUnitNormalizerRule.ts","../src/packages/uom/UomPackage.ts","../src/packages/currency/parselets/CurrencySymbolParselet.ts","../src/packages/currency/parselets/InParselet.ts","../src/packages/currency/CurrencyPackage.ts","../src/packages/vector/parselets/VectorParselet.ts","../src/packages/vector/parselets/FloatParselet.ts","../src/packages/vector/VectorPackage.ts","../src/packages/matrix/parselets/MatrixLiteralParselet.ts","../src/packages/matrix/parselets/MatrixIndexParselet.ts","../src/packages/matrix/MatrixPackage.ts","../src/packages/mapreduce/MapReduceShared.ts","../src/packages/mapreduce/parselets/MapParselet.ts","../src/packages/mapreduce/parselets/ReduceParselet.ts","../src/packages/mapreduce/parselets/SumParselet.ts","../src/packages/mapreduce/parselets/ProdParselet.ts","../src/packages/mapreduce/normalizer/MapReduceCallNormalizerRule.ts","../src/packages/mapreduce/MapReducePackage.ts","../src/packages/biginteger/parselets/BigIntNumberParselet.ts","../src/packages/biginteger/BigIntPackage.ts","../src/packages/conditionals/parselets/ComparisonParselet.ts","../src/packages/conditionals/parselets/LogicalParselet.ts","../src/packages/conditionals/parselets/BooleanLiteralParselet.ts","../src/packages/conditionals/parselets/IfThenElseParselet.ts","../src/packages/conditionals/ConditionalsPackage.ts","../src/packages/converters/parselets/AsConverterParselet.ts","../src/packages/converters/parselets/RoundingParselets.ts","../src/packages/converters/normalizer/DecimalPlacesNormalizerRule.ts","../src/packages/converters/normalizer/ConverterPrepositionNormalizerRule.ts","../src/packages/converters/ConvertersPackage.ts","../src/packages/mathphrases/parselets/VariadicAggregateParselet.ts","../src/packages/mathphrases/parselets/UnaryPhraseParselet.ts","../src/packages/mathphrases/parselets/RemainderRootParselets.ts","../src/packages/mathphrases/normalizer/FunctionPhraseNormalizerRule.ts","../src/packages/mathphrases/parselets/LargerSmallerParselet.ts","../src/packages/mathphrases/parselets/HalfParselet.ts","../src/packages/mathphrases/parselets/MidpointParselet.ts","../src/packages/mathphrases/parselets/RandomNumberParselet.ts","../src/packages/mathphrases/parselets/ClampParselet.ts","../src/packages/mathphrases/parselets/ProportionParselet.ts","../src/packages/mathphrases/MathPhrasesPackage.ts","../src/packages/finance/parselets/InvestmentParselets.ts","../src/packages/finance/parselets/TermRateClauses.ts","../src/packages/finance/parselets/CompoundInterestParselet.ts","../src/packages/finance/parselets/LoanRepaymentParselet.ts","../src/packages/finance/parselets/BillSplitParselets.ts","../src/packages/finance/parselets/SavingsGoalParselets.ts","../src/packages/finance/parselets/SalesTaxParselet.ts","../src/packages/finance/parselets/InflationQueryParselet.ts","../src/packages/finance/parselets/InflationFutureValueParselet.ts","../src/packages/finance/parselets/InYearDollarsParselet.ts","../src/packages/finance/parselets/InflationPluginFunctions.ts","../src/packages/finance/normalizer/InYearDollarsNormalizerRule.ts","../src/packages/finance/normalizer/RecurringScheduleNormalizerRule.ts","../src/packages/finance/normalizer/BillSplitNormalizerRule.ts","../src/packages/finance/FinancePackage.ts","../src/resolvers/QueryResolver.ts","../src/packages/weather/parselets/WeatherQueryParselet.ts","../src/packages/weather/WmoWeatherCodes.ts","../src/packages/weather/OpenMeteoClient.ts","../src/packages/weather/WeatherPackage.ts","../src/packages/stocks/DatePhrase.ts","../src/packages/stocks/MajorTickers.ts","../src/packages/stocks/normalizer/StockTickerNormalizerRule.ts","../src/packages/stocks/parselets/StockParselet.ts","../src/packages/stocks/StocksPackage.ts","../src/packages/knowledge/parselets/KnowledgeQueryParselet.ts","../src/packages/knowledge/KnowledgePackage.ts","../src/packages/lines/parselets/PrevParselet.ts","../src/packages/lines/parselets/LineRefParselet.ts","../src/packages/lines/parselets/RangeAggregateParselet.ts","../src/packages/lines/parselets/AboveAggregateParselet.ts","../src/packages/lines/normalizer/LineRefNormalizerRule.ts","../src/packages/lines/LinesPluginFunctions.ts","../src/packages/lines/LinesPackage.ts","../src/packages/tables/parselets/ColumnAggregateParselet.ts","../src/packages/tables/TableReader.ts","../src/packages/tables/TablesPluginFunctions.ts","../src/packages/tables/TablesPackage.ts","../src/packages/tags/parselets/TagAggregateParselet.ts","../src/packages/tags/normalizer/TagAggregateNormalizerRule.ts","../src/packages/tags/TagScanner.ts","../src/packages/tags/TagsPluginFunctions.ts","../src/packages/tags/TagsPackage.ts","../src/packages/uncertainty/parselets/UncertaintyParselet.ts","../src/packages/uncertainty/normalizer/AsciiPlusMinusNormalizerRule.ts","../src/packages/uncertainty/UncertaintyPackage.ts","../src/packages/goalseek/GoalSeekPluginFunctions.ts","../src/packages/goalseek/parselets/GoalSeekParselet.ts","../src/packages/goalseek/normalizer/GoalSeekNormalizerRule.ts","../src/packages/goalseek/GoalSeekPackage.ts","../src/packages/colour/parselets/HexColourParselet.ts","../src/packages/colour/ColourPluginFunctions.ts","../src/packages/colour/parselets/ColourCallParselet.ts","../src/packages/colour/normalizer/ColourCallNormalizerRule.ts","../src/packages/colour/ColourPackage.ts","../src/packages/builtins.ts"],"names":["CHAINED_DOT_THOUSANDS_GROUPS","PLAIN_DECIMAL","NumberParselet","parser","token","builder","v","decimalText","raw","ErrorFactory","locale","getLocale","decimalSep","thousandsSep","normalized","PrefixOpParselet","opcode","BinaryOpParselet","bp","rightAssociative","left","GroupParselet","count","ConstantParselet","val","SUFFIX_MAGNITUDE","WORD_MAGNITUDE","scaleDecimalString","magnitude","dotIdx","intPart","fracPart","shiftedInt","remainingFrac","largeNumberSuffixNormalizerRule","priority","tokens","pos","numberToken","suffixToken","adjacent","wordMagnitude","scaled","createFusedToken","ARITHMETIC_PACKAGE","BindingPower","PercentParselet","IsWhatParselet","_left","_token","nextWordIs","preposition","readPreposition","expectWord","LOG_BUILTIN","word","example","OnOffBaseParselet","sign","percentOnOffNormalizerRule","text","OfParselet","OfWhatIsParselet","OnOffWhatIsParselet","IncreaseDecreaseParselet","multiplier","next","IncreaseByParselet","PercentageChangeParselet","nextToken","isKnownUnit","COUNT_WORDS","resolveCount","n","UpDownParselet","repeat","countTok","timesTok","stepTokenAt","i","percentUpDownNormalizerRule","here","step","PERCENTAGE_PACKAGE","builtinNameToIndex","FunctionCallParselet","fnName","fnIdx","argCount","FUNCTION_PACKAGE","NowParselet","dayOffset","WEEKDAY_TOKEN_INDEX","NextLastParselet","direction","dayToken","targetDay","UntilSinceParselet","unit","WorkdaysInParselet","WorkdayOffsetParselet","WorkdaysBetweenParselet","DateFieldQueryParselet","functionName","mode","DurationBetweenParselet","DayTypePredicateParselet","CurrentTimestampParselet","ToDateParselet","ToTimestampParselet","DateLiteralParselet","ISO8601_PATTERN","parseIso8601","s","trimmed","ms","formatIso8601Local","epochMs","d","pad","year","month","day","hours","minutes","seconds","offsetMinutesTotal","absOffset","offsetHours","offsetMinutes","MS_TIMESTAMP_THRESHOLD","unixTimestampToEpochMs","workdaysInDurationHandler","args","totalDays","getMeasure","errorValue","convertUnit","magnitudeDays","fullWeeks","remainderDays","workdays","numberValue","WEEKDAY_NAMES","asEpochMs","value","fieldName","ValueType","weekdayOnDateHandler","stringValue","MONTH_NAMES","monthOnDateHandler","weekOnDateHandler","target","isoDay","yearStart","days","isWeekendDate","isWeekendOnDateHandler","boolValue","isWorkdayOnDateHandler","spanBetweenDatesHandler","uomValue","toDateFromAnyHandler","datetimeValue","toTimestampFromAnyHandler","workdaysInDuration","weekdayOnDate","monthOnDate","weekOnDate","isWeekendOnDate","isWorkdayOnDate","spanBetweenDates","toDateFromAny","toTimestampFromAny","untilSinceNormalizerRule","unitToken","keywordToken","fusedType","betweenUnitNormalizerRule","start","howManyLength","isBetween","isUntilSince","sources","workdayRateDenominatorNormalizerRule","slash","syntheticOne","tokenTypeId","DATETIME_LITERAL_TYPE","DATETIME_LITERAL_TYPE_ID","PLAIN_INTEGER","DOT_DAY_MONTH","DOT_LEADING_YEAR","DAY_OR_MONTH","writtenAsOneRun","resolveYear","digits","yy","buildDateToken","sourceTokens","ruleName","date","first","t","fusedToken","LexerToken","ISO_TIMESTAMP","ISO_TOKEN_TYPES","ISO_MAX_TOKENS","fuseIsoTimestamp","runEnd","window","matched","consumed","covered","dateLiteralNormalizerRule","t0","t1","dayText","monthText","match","timestamp","sep1","sep2","t2","MONTHS","monthOf","looksLikeYear","monthNameDateNormalizerRule","RULE","yearToken","second","comma","DAYS_COUNT_BUILTIN","QUARTERS","daysInMonth","daysInYear","DaysInPeriodParselet","total","quarter","startsPeriod","daysInPeriodNormalizerRule","dailyNoteLinkNormalizerRule","DATETIME_PACKAGE","CITY_TO_IANA_ZONE","MULTI_WORD_CITY_ZONES","ZONE_ABBREVIATION_TO_IANA","ZONE_LOOKUP","FIXED_OFFSET_PREFIX","encodeFixedOffset","isFixedOffset","zoneRef","decodeFixedOffsetMinutes","resolveOffsetMinutes","atMs","dtf","parts","p","asUtcMs","zonedWallClockToUtcMs","month0","hour","minute","naiveUtcMs","formatTimeInZone","offsetMs","formatTimeUtc","formatDateInZone","formatDateUtc","zonedYMD","dayShiftBetweenZones","relativeToZoneRef","a","b","aMs","bMs","zoneLabel","abs","mins","tryConsumeZoneReference","rawText","lowerText","signToken","isNegative","offsetToken","totalMinutes","hourToken","minuteToken","displayName","c","ZONE_CONVERT_FN","TIME_IN_ZONE_FN","DATE_IN_ZONE_FN","TIME_DIFFERENCE_FN","zoneConvertHandler","sourceZoneRef","targetZoneRef","today","utcMs","time","dayShift","timeInZoneHandler","dateInZoneHandler","timeDifferenceHandler","zoneRef1","zoneRef2","label1","label2","now","offset1","diff","ahead","behind","absDiff","ClockTimeParselet","ClockTimeIntervalParselet","startStr","endStr","end","durationMinutes","FpsRateParselet","LaptimeParselet","timeOrDateInZoneParselet","pluginFnName","zone","TimeDifferenceParselet","zone1","zone2","VideoTimecodeParselet","frames","separator","fpsToken","fps","fpsWhole","totalFrames","timecodeUnit","framesWord","framesToTimecodeString","frameCount","totalSeconds","FrameCountParselet","computeTotalMinutes","ampm","isPM","isAmPmToken","clockTimeNormalizerRule","isInsideRangeContext","colonToken","ampmToken","hasAmPm","bareAmPmToken","clockTimeIntervalNormalizerRule","to","fpsRateNormalizerRule","laptimeNormalizerRule","h","c1","m","c2","videoTimecodeNormalizerRule","c3","f","frameCountNormalizerRule","framesToken","TIME_KIND","TIMESPAN_PARTS","durationSeconds","entry","UNIT_TABLE","toTimespanString","negative","remaining","singular","plural","secondsLeft","toLaptimeString","rounded","whole","fraction","base","TIME_PACKAGE","phrase","definePhrasePattern","opts","alt","alternative","expected","captures","slot","DICE_ROLL_BUILTIN","emitDiceRollCall","keywordLedRollPattern","DiceRollParselet","minToken","maxToken","DICE_PACKAGE","VariableParselet","nameToken","varName","IdentifierParselet","GlobalVariableParselet","GlobalVariableAsyncResolver","_tokens","bytecode","packageId","signal","_queryClient","opcodes","strings","len","sharedGlobalVariableStore","nextInstruction","resolver","resolve","unsubscribe","name","VARIABLES_PACKAGE","HISTORICAL_CURRENCY_NS","HISTORICAL_CURRENCY_FN","HISTORICAL_CURRENCY_QUALIFIED","HistoricalCurrencyErrorCodes","FAILURE_COOLDOWN_MS","HISTORICAL_RATE_STALE_TIME_MS","FETCH_TIMEOUT_MS","historicalRateQueryKey","from","isoDate","pad2","tryConsumeCurrencyOnDate","sharedCurrencyExchange","onToken","dateToken","fetchHistoricalRate","provider","queryClient","fromUpper","toUpper","fetchSignal","cleanup","createTimeoutSignal","rate","error","failedValue","key","createHistoricalCurrencyPluginFunction","amount","toArg","dateArg","fault","faultedOperand","getActiveQueryClient","cached","createHistoricalCurrencyResolver","historicalFnIdx","pluginFunctionIndexFor","stringIdxs","op","k","qSignal","resolveUnitAlias","rawUnit","resolveCurrencyAlias","UomLiteralParselet","targetToken","targetUnit","ConvertParselet","PossibilitiesParselet","uomPossibilitiesNormalizerRule","toToken","questionToken","unitEntry","previousKind","spelling","PLAIN_NUMBER","compoundQuantityNormalizerRule","firstNumber","firstUnit","kind","lastRatio","smallestUnit","smallestUnitToken","number","source","isUnit","twoUnitConversionNormalizerRule","major","minor","degreeSymbolNormalizerRule","symbol","TwoUnitConversionParselet","builtinIndex","_parser","PER_WORDS","isCountLabel","bareRateDenominatorNormalizerRule","head","label","after","isSlash","isPerWord","PerUnitParselet","AtRateParselet","hasRateAhead","atRateNormalizerRule","ARTICLES","forDurationNormalizerRule","article","reversedConversionNormalizerRule","third","INGREDIENT_DENSITIES","MAX_INGREDIENT_NAME_WORDS","getIngredientDensity","COOKING_CONVERT_FN","cookingConvertHandler","amountValue","ingredientName","targetUnitText","sourceUnit","sourceMeasure","targetMeasure","isMassOrVolume","density","mL","result","grams","CookingConversionParselet","ingredientNameNormalizerRule","prevToken","wordCount","candidateTokens","afterIdx","inToken","candidate","multiWordUnitNormalizerRule","compoundUnitNormalizerRule","numerator","denominator","UOM_PACKAGE","CurrencySymbolParselet","currency","InParselet","createCurrencyPackage","config","CurrencyAsyncResolver","CURRENCY_PACKAGE","VectorParselet","dimension","FloatParselet","VECTOR_PACKAGE","MatrixLiteralParselet","rows","cols","rowCols","MatrixIndexParselet","isSlice","depth","offset","isRange","MATRIX_PACKAGE","parseTransform","afterNext","builtinIdx","transformBuilder","BytecodeBuilder","program","isZippedCollectionForm","consumeCollectionName","parseCollectionExpr","emitInvoke","transform","paramNames","thirdOperand","idx","MapParselet","ReduceParselet","hasInitial","SumParselet","bodyBuilder","bodyProgram","ProdParselet","WORD_TO_TOKEN_TYPE","looksLikeLineRef","mapReduceCallNormalizerRule","tokenType","MAPREDUCE_PACKAGE","BigIntNumberParselet","bigIntLiteralDigits","BIGINT_PACKAGE","ComparisonParselet","LogicalParselet","bindingPower","BooleanLiteralParselet","IfThenElseParselet","CONDITIONALS_PACKAGE","BUILTIN_CONVERTERS","BASE_CONVERTERS","AsConverterParselet","radixToken","radix","builtinOp","INCREMENT_WORDS","emitRoundToIncrement","increment","emitRoundDirection","builtin","CEIL_BUILTIN","FLOOR_BUILTIN","ROUND_BUILTIN","ROUND_TO_PLACES_BUILTIN","readDirection","RoundedParselet","readIncrement","ToNearestParselet","DecimalPlacesParselet","places","PLACE_WORDS","PLACE_PHRASES","decimalPlacesNormalizerRule","tailWord","FUNC_TYPED_CONVERTERS","converterPrepositionNormalizerRule","CONVERTERS_PACKAGE","VariadicAggregateParselet","unaryPhraseParselet","remainderOfParselet","nthRootParselet","logBaseParselet","emitNaturalLog","functionPhraseNormalizerRule","baseAt","findBase","replacement","index","largerSmallerParselet","halfParselet","midpointParselet","randomNumberParselet","CLAMP_BUILTIN_INDEX","ClampParselet","mid","PROPORTION_BUILTIN_INDEX","ProportionParselet","whatToken","AVERAGE","MEDIAN","TOTAL","COUNT","MIN","MAX","GCD","LCM","SQRT","CBRT","MATHPHRASES_PACKAGE","PERIODS_PER_YEAR","readCompoundingInterval","periods","InvestmentGrowthParselet","annualIndex","intervalIndex","PresentValueParselet","ReturnOnInvestmentParselet","AnnualReturnParselet","parseTermAndRate","termWords","matchTerm","consumeRate","consumeTerm","CompoundInterestParselet","swap","LoanRepaymentParselet","periodsPerYear","SplitBetweenParselet","SplitWaysParselet","SAVINGS_PERIODS","readSavingsPeriod","period","SavingsDurationParselet","SavingsContributionParselet","SalesTaxParselet","INFLATION_ADJUST_BUILTIN_IDX","hasInflationKeyword","InflationQueryParselet","variant","gotType","InflationFutureValueParselet","inflationToken","got","InYearDollarsParselet","yearRangeError","code","badYear","CPI_MIN_YEAR","CPI_MAX_YEAR","inflationFromYearToPresentHandler","fromYear","toYear","adjustForInflation","inflationToYearFromPresentHandler","inflationFutureValueHandler","futureYear","years","worth","inYearDollarsNormalizerRule","dollarsToken","RecurringScheduleErrorCodes","UNIT_PER_YEAR","VALUE_ENDERS","wordOf","unitPerYear","readPeriod","simple","countToken","per","readDuration","unitIndex","wholePayments","perYear","durationYears","nearest","recurringScheduleNormalizerRule","forIndex","duration","NUMERIC","billSplitNormalizerRule","split","ways","before","betweenIndex","peopleIndex","hasPeople","lastIndex","COMPOUND_FV","COMPOUND_INTEREST","LOAN_REPAYMENT","LOAN_INTEREST","COMPOUND_FV_EVERY","COMPOUND_INTEREST_EVERY","PRESENT_VALUE","ROI","ANNUAL_RETURN","TAX_REMOVE","TAX_IN","TAX_ON","SPLIT_EACH","SAVINGS_PAYMENT","FINANCE_PACKAGE","defaultOnError","namespace","query","createQueryResolver","staleTimeMs","timeoutMs","failureCooldownMs","onError","queryKeyFor","fetchAndCache","resolverPromise","consumeCityName","triggerText","words","weatherQueryParselet","city","WMO_WEATHER_DESCRIPTIONS","describeWeatherCode","WeatherErrorCodes","GEOCODING_API_URL","FORECAST_API_URL","geocodeCity","url","response","fetchForecast","lat","lon","params","json","current","daily","cityWeatherCache","CACHE_TTL_MS","fetchCityWeather","promise","geo","forecast","WEATHER_PACKAGE_NAME","WEATHER_FN_NAME","WEATHER_FN_IDX","weatherResolver","weatherPluginFunction","sep","data","WEATHER_PACKAGE","validateAndFormat","monthIndex","requireYear","tryParseDatePhrase","g1","sepTok","sepType","g2","g3","monthTok","yearTok","dayTok","MAJOR_TICKERS","STOCK_TICKER_TYPE","STOCK_TICKER_TYPE_ID","stockTickerNormalizerRule","tryParseDateSuffix","lower","field","onTok","emitStockQuery","ticker","suffix","currentFn","historicalFn","finishStockExpression","sawStar","stockFnParselet","tickerToken","stripQuotes","bareStockTickerParselet","PACKAGE_NAME","CURRENT_FN","HISTORICAL_FN","createStocksPackage","notConfigured","what","currentResolver","currentPluginFunction","quote","historicalResolver","historicalPluginFunction","knowledgeQueryParselet","createKnowledgePackage","packageName","pluginFunction","answer","PrevParselet","LineRefParselet","lineNumber","RangeAggregateParselet","isAverage","fromToken","AboveAggregateParselet","LINE_REF_TYPE_ID","SUM_RANGE_CALL_TYPE_ID","AVERAGE_RANGE_CALL_TYPE_ID","lineRefNormalizerRule","makeLineRefToken","lineNumberText","sourceToken","rangeCallNormalizerRule","typeId","checkLineValue","requireContext","context","prevHandler","_args","ctxError","targetLine","lineRefHandler","aggregateRange","commonUnit","sawUom","err","sumRangeHandler","averageRangeHandler","aggregateAbove","boundaryCheck","totalAboveHandler","averageAboveHandler","LINES_PACKAGE","ColumnAggregateParselet","ADDRESS_WORDS","isTableRow","line","isSeparatorRow","splitTableRow","cells","cell","ch","parseNumericCell","cleaned","findTableAbove","getLineText","fromLine","bottom","top","header","columnIndex","numericColumn","table","colIndex","out","row","TablesErrorCodes","reduce","acc","sorted","aggregateColumn","columnName","names","tableColumnSumHandler","tableColumnAverageHandler","tableColumnMinHandler","tableColumnMaxHandler","tableColumnCountHandler","tableColumnMedianHandler","TABLES_PACKAGE","TagAggregateParselet","AGGREGATE_TOKEN","AGGREGATE_WORDS","tagAggregateNormalizerRule","fused","tag","of","tagStripNormalizerRule","escapeTag","lineCarriesTag","hashIndex","aggregateTagged","getText","getResult","isBoundary","needle","tagSumHandler","tagAverageHandler","tagCountHandler","TAGS_PACKAGE","UncertaintyParselet","asciiPlusMinusNormalizerRule","plus","minus","UNCERTAINTY_PACKAGE","SEARCH_LOWER_BOUND","SEARCH_UPPER_BOUND","FALLBACK_MAX_ITERATIONS","ABSOLUTE_VALUE_TOLERANCE","RELATIVE_VALUE_TOLERANCE","INTERVAL_COLLAPSE_WIDTH","REAL_ROOT_IMAGINARY_TOLERANCE","GOAL_SEEK_FN_NAME","goalSeekHandler","probe","getLineReads","targetArg","reads","symbolic","symbolicValue","varNode","exact","solveClosedForm","maxIterations","bisect","lhs","variable","rhs","constNode","rationalFromNumber","outcome","solveForVariable","candidates","node","simplified","simplifySymbolic","rationalToNumber","root","inDomain","pool","sample","numeric","lo","hi","loSample","hiSample","tolerance","flo","fhi","midSample","fmid","GoalSeekParselet","lineRef","connective","GOAL_SEEK_TYPE_ID","goalSeekNormalizerRule","GOALSEEK_PACKAGE","HexColourParselet","ColourErrorCodes","colourOf","readAmount","badArgs","fn","notAColour","colourParseHandler","arg","parsed","parseColour","colourValue","rgbHandler","clamp255","rgbaHandler","clamp01","hslHandler","r","g","hslToRgb","hslaHandler","adjuster","lightenHandler","lighten","darkenHandler","darken","saturateHandler","saturate","desaturateHandler","desaturate","rotateHandler","rotateHue","complementHandler","complement","grayscaleHandler","grayscale","invertHandler","invert","mixHandler","mix","alphaHandler","withAlpha","contrastHandler","contrastRatio","luminanceHandler","relativeLuminance","extractor","get","redHandler","greenHandler","blueHandler","hueHandler","rgbToHsl","saturationHandler","lightnessHandler","hsvHandler","hsvToRgb","hsvaHandler","hwbHandler","hwbToRgb","tintHandler","tint","shadeHandler","shade","toneHandler","tone","isdarkHandler","isDark","islightHandler","readableHandler","readableColour","readWcagThreshold","WCAG_THRESHOLDS","contrastCompliantHandler","wcagLevelHandler","wcagLevel","COLOUR_FUNCTION_HANDLERS","ColourCallParselet","COLOUR_FUNCTION_NAMES","colourCallNormalizerRule","retag","format","completionItems","COLOUR_PACKAGE","BUILTIN_PACKAGES","SYMBOLIC_PACKAGE"],"mappings":"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","file":"chunk-AIIPKVKO.js","sourcesContent":["import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { getLocale } from \"@solve-js/constants/locales\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/**\n * Matches a CHAINED thousands-grouped integer using \".\" as the group\n * separator, e.g. \"1.234.567\", regardless of the active locale. Two or\n * more repetitions is the key: a genuine decimal literal can never\n * contain more than one \".\", so this shape is unambiguous, unlike a\n * single group (\"1.234\") which is deliberately left alone below (could\n * mean 1234 grouped, or 1.234 as a three-decimal-place fraction, far\n * more commonly the latter in this project's \"en\"-default locale, so\n * changing that interpretation is out of scope here).\n */\nconst CHAINED_DOT_THOUSANDS_GROUPS = /^\\d{1,3}(\\.\\d{3}){2,}$/;\n\n/**\n * A plain fractional literal (digits, one dot, digits), the gate for\n * PUSH_DECIMAL. Mirrors PrecedenceParser's constant of the same name: it\n * refuses scientific notation (\"2.5e-3\"), which stays an ordinary double.\n */\nconst PLAIN_DECIMAL = /^\\d*\\.\\d*$/;\n\n/**\n * NOTE: for real evaluation, this class's `parse()` is dead code.\n * PrecedenceParser's Tier-1 fast-path switch handles NUMBER_ID inline\n * (with its own copy of this exact logic) and always `return`s before\n * ever falling through to the Tier-2 parselet-registry lookup that would\n * invoke this. This class stays registered so the \"matched parselets\"\n * diagnostic view isn't blind to NUMBER tokens, and so direct unit tests\n * of parselet behavior keep working, but any fix here must be mirrored\n * in PrecedenceParser.ts's NUMBER_ID case to actually take effect.\n */\nexport class NumberParselet implements PrefixParselet {\n\treadonly category = \"Arithmetic\";\n\n\tparse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n\t\tlet v: number;\n\t\t// Mirrors PrecedenceParser's Tier-1 NUMBER case: a fractional literal is\n\t\t// pushed as PUSH_DECIMAL so its exact value survives to any money it\n\t\t// meets, everything integer-shaped stays PUSH_NUMBER.\n\t\tlet decimalText: string | null = null;\n\t\tconst raw = token.value;\n\t\tif (raw.startsWith(\"0x\") || raw.startsWith(\"0X\")) {\n\t\t\tv = parseInt(raw, 16);\n\t\t\tif (Number.isNaN(v)) {\n\t\t\t\t// Matches PrecedenceParser.ts's NUMBER_ID fast path (the\n\t\t\t\t// actual production code path this dead-code parselet\n\t\t\t\t// mirrors), a raw Error here would skip\n\t\t\t\t// ThreeTierEvaluator's DAG-preservation enrichment on parse\n\t\t\t\t// failure, which specifically checks for EngineError.\n\t\t\t\tthrow ErrorFactory.parsing(\"INVALID_NUMBER_LITERAL\", `Invalid hex literal: \"${raw}\"`, { raw });\n\t\t\t}\n\t\t} else if (raw.startsWith(\"0b\") || raw.startsWith(\"0B\")) {\n\t\t\tv = parseInt(raw.slice(2), 2);\n\t\t\tif (Number.isNaN(v)) {\n\t\t\t\tthrow ErrorFactory.parsing(\"INVALID_NUMBER_LITERAL\", `Invalid binary literal: \"${raw}\"`, { raw });\n\t\t\t}\n\t\t} else if (raw.startsWith(\"0o\") || raw.startsWith(\"0O\")) {\n\t\t\tv = parseInt(raw.slice(2), 8);\n\t\t\tif (Number.isNaN(v)) {\n\t\t\t\tthrow ErrorFactory.parsing(\"INVALID_NUMBER_LITERAL\", `Invalid octal literal: \"${raw}\"`, { raw });\n\t\t\t}\n\t\t} else if (CHAINED_DOT_THOUSANDS_GROUPS.test(raw)) {\n\t\t\t// The lexer accepts \".\" as a thousands-group separator\n\t\t\t// independent of locale (see ExpressionLexer's number-scanning\n\t\t\t// \"Thousands separators\" block), but the locale-based\n\t\t\t// normalization below only strips the ACTIVE locale's own\n\t\t\t// designated thousandsSeparator character, for \"en\" that's\n\t\t\t// \",\", not \".\", so a chained dot-grouped number like\n\t\t\t// \"1.234.567\" fell through to parseFloat() untouched, which\n\t\t\t// stops at the second \".\" and silently truncated it to 1.234\n\t\t\t// (over 99% of the digits dropped, with no error). Handled as\n\t\t\t// its own case, ahead of the locale-aware path, since it's\n\t\t\t// unambiguous regardless of locale.\n\t\t\tv = parseFloat(raw.split(\".\").join(\"\"));\n\t\t} else {\n\t\t\t// Normalize number based on locale separators\n\t\t\tconst locale = getLocale(parser.getLocaleCode());\n\t\t\tconst decimalSep = locale.display.decimalSeparator;\n\t\t\tconst thousandsSep = locale.display.thousandsSeparator;\n\n\t\t\tlet normalized = raw;\n\t\t// Replace thousands separator with nothing (split+join avoids per-call RegExp compilation)\n\t\tif (thousandsSep) {\n\t\t\tnormalized = normalized.split(thousandsSep).join(\"\");\n\t\t}\n\t\t\t// Replace locale decimal separator with \".\" for JavaScript parsing\n\t\t\tif (decimalSep && decimalSep !== \".\") {\n\t\t\t\tnormalized = normalized.replace(decimalSep, \".\");\n\t\t\t}\n\t\t\tv = parseFloat(normalized);\n\t\t\tif (PLAIN_DECIMAL.test(normalized)) decimalText = normalized;\n\t\t}\n\t\tif (decimalText !== null) {\n\t\t\tbuilder.emitOpcode(OpCode.PUSH_DECIMAL);\n\t\t\tbuilder.emitString(decimalText);\n\t\t\treturn;\n\t\t}\n\t\tbuilder.emitOpcode(OpCode.PUSH_NUMBER);\n\t\tbuilder.emitNumber(v);\n\t}\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * Prefix operator such as unary minus, parameterised by opcode.\n *\n * Parses its operand at prefix binding power, so `-2^2` binds tighter on the\n * exponent and gives -4 rather than 4.\n */\nexport class PrefixOpParselet implements PrefixParselet {\n\treadonly category = \"Arithmetic\";\n\tconstructor(private readonly opcode: OpCode) {}\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(60, builder);\n builder.emitOpcode(this.opcode);\n }\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * Infix operator, parameterised by binding power, opcode and associativity.\n *\n * One class covers every arithmetic operator rather than a class each: the only\n * thing that varies is precedence, which opcode to emit, and whether a chain of\n * the operator groups to the left or the right, all constructor arguments.\n */\nexport class BinaryOpParselet implements InfixParselet {\n\treadonly category = \"Arithmetic\";\n\treadonly bindingPower: number;\n\tprivate readonly rightBindingPower: number;\n\tconstructor(\n bp: number,\n private readonly opcode: OpCode,\n rightAssociative = false\n ) {\n this.bindingPower = bp;\n // The parser's infix loop breaks on `bp <= minBp`, so parsing the right\n // operand at this operator's OWN power stops at the next occurrence of it\n // and the chain groups left. One below keeps consuming, which groups\n // right. `^` is the only right-associative operator here.\n this.rightBindingPower = rightAssociative ? bp - 1 : bp;\n }\n\n parse(parser: Parser, left: Token, token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(this.rightBindingPower, builder);\n builder.emitOpcode(this.opcode);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * Parses a parenthesized prefix expression starting at `(`.\n *\n * `(1 + 2)` is plain grouping for precedence. `(x, y)` / `(x, y, z)` /\n * `(x, y, z, w)` is the bare-tuple vector literal documented as an\n * alternative to `vec2(...)`/`vec3(...)`/`vec4(...)` (wiki:\n * Arithmetic/Vector). Both forms share the LPAREN token, so this one\n * parselet must own both: the Vector package never registers its own\n * LPAREN handler, which would otherwise silently win or lose depending on\n * package registration order (ParseletRegistry has no collision detection).\n */\nexport class GroupParselet implements PrefixParselet {\n\treadonly category = \"Arithmetic\";\n\n\tparse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(0, builder);\n let count = 1;\n while (parser.match(\"COMMA\")) {\n parser.parseExpression(0, builder);\n count++;\n }\n parser.consume(\"RPAREN\");\n if (count > 1) {\n // Legacy bare-tuple vector sugar, a 1xN row-vector Matrix. Mirrors\n // PrecedenceParser.ts's Tier-1 LPAREN_ID case, which is what actually\n // runs in production, keep both in sync (see this file's own class\n // doc comment).\n builder.emitOpcode(OpCode.MAT_NEW);\n builder.emitIndex(1);\n builder.emitIndex(count);\n }\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * Mathematical constants, currently `pi` and `e`.\n *\n * Emits the literal value at parse time rather than a lookup, since neither can\n * be reassigned.\n */\nexport class ConstantParselet implements PrefixParselet {\n\treadonly category = \"Arithmetic\";\n\n\tparse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n const val = token.type === \"PI\" ? Math.PI : Math.E;\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(val);\n }\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\n/**\n * Single-letter magnitude suffixes recognized immediately after a NUMBER\n * literal, and the power-of-ten each one scales by.\n *\n * Case matters. This is deliberately NOT case-insensitive:\n * - `k` (lowercase only) = thousand. Uppercase `K` is left alone (not\n * requested, and unlike the others below there's no existing-unit\n * collision reason to avoid it, just staying narrowly scoped to what\n * was asked).\n * - `M` (uppercase only) = million. Lowercase `m` is the \"meter\" unit\n * (`knownUnits` in `lexer/units.ts`) and must NOT be reinterpreted\n * `5m` stays 5 meters.\n * - `G`/`B` (uppercase only) = billion, accepted as synonyms (`$5B` and\n * `5G` both read as \"5 billion\", SoulverCore-style). Lowercase `g`\n * (gram) and `b` (bit, see `knownUnits`) are untouched.\n * - `T` (uppercase only) = trillion. Lowercase `t` is the \"tonne\" unit.\n *\n * **Known, accepted collision**: bare uppercase `B` is ALSO a registered\n * unit symbol in `knownUnits` (data-storage \"bytes\", e.g. so `5GB`/`5MB`\n * keep working, those lex as a single two-letter `UNIT` token, \"GB\"/\"MB\"\n * completely distinct from the bare one-letter \"B\" this rule matches, so\n * they're unaffected either way). But a BARE `5B` was, before this rule,\n * a valid (if obscure) \"5 bytes\" `Uom` literal, confirmed via a real\n * lexer probe: `\"5B\"` tokenizes as `NUMBER \"5\"` + `UNIT \"B\"`, immediately\n * adjacent. This rule has higher priority than UOM parsing and\n * unconditionally reinterprets that exact shape as \"5 billion\" instead.\n * Deliberate, not an oversight: grepping the full test suite and\n * `uom/UomConverter.ts`'s real conversion table found zero test coverage\n * or real conversion-table entries for a BARE (non-prefixed) byte unit\n * only the compound forms (`KB`/`MB`/`GB`/`TB`) are ever exercised or\n * documented, while \"$5B\"/\"5B\" for \"5 billion\" is the far more common\n * expected reading (this is exactly the SoulverCore-style feature being\n * added here). If a real bare-bytes use case surfaces later, it would\n * need a different, explicit spelling (`5 B` with a space still lexes as\n * two tokens and is untouched, or `5 bytes`).\n *\n * Multi-letter lookalikes (`Mi`, `MB`, `km`, `kg`, ...) are automatically\n * excluded without any extra lookahead: the lexer already tokenizes them\n * as a single, longer `IDENT`/`UNIT` token (confirmed by direct lexer\n * probe), so their `.value` never equals one of the single-character keys\n * below, there's no separate trailing-letter token to accidentally\n * consume.\n */\nconst SUFFIX_MAGNITUDE: Record<string, number> = {\n k: 3,\n M: 6,\n G: 9,\n B: 9,\n T: 12,\n};\n\n/**\n * The same magnitudes written as words, which is how most people write them.\n * `3 million` is the ordinary spelling and only `3M` used to work.\n *\n * Unlike the single letters above these are matched case-insensitively and\n * with the space that normally separates them from the number. The rule only\n * fires directly after a numeric literal, so `:million = 5` and a bare\n * `million` reference are both untouched.\n *\n * Case-insensitivity DOES collide, which the original version of this comment\n * claimed it did not. The two-letter abbreviations lowercase into the newton's\n * metric prefixes: `mN`, `MN` and `TN` are all real units and all match \"mn\",\n * \"mn\" and \"tn\". `5 mN` came back as five million, and no spelling of the\n * millinewton, meganewton or teranewton could reach the unit system at all.\n * The match below therefore refuses a token the lexer typed as a UNIT, which\n * costs nothing here because none of the words in this table is itself a unit\n * spelling.\n *\n * \"m\" is deliberately absent. It is the metre, and `5m` must stay 5 metres.\n */\nconst WORD_MAGNITUDE: Record<string, number> = {\n thousand: 3,\n thousands: 3,\n million: 6,\n millions: 6,\n mn: 6,\n billion: 9,\n billions: 9,\n bn: 9,\n trillion: 12,\n trillions: 12,\n tn: 12,\n};\n\n/**\n * Plain unsigned decimal literal, digits, with at most one \".\", as\n * produced by the lexer's ordinary NUMBER scanning. Deliberately excludes\n * `0x`/`0X` hex and `0b`/`0B` binary literals (also lexed as type\n * `NUMBER`, e.g. `\"0xFF\"`) and the multi-dot chained-thousands form\n * (`\"1.234.567\"`, see `NumberParselet.ts`'s `CHAINED_DOT_THOUSANDS_GROUPS`)\n *, none of those are meaningful inputs to decimal-point shifting, and\n * blindly string-shifting `\"0xFF\"` would silently corrupt it into a\n * different, wrong hex literal instead of erroring.\n */\nconst PLAIN_DECIMAL = /^\\d+(\\.\\d+)?$/;\n\n/**\n * Shift a plain decimal digit-string's decimal point right by `magnitude`\n * places, entirely via string manipulation, NOT `rawValue * 10**magnitude`.\n *\n * Naive floating-point multiplication silently produces wrong results for\n * inputs like `1.005 * 1000` (`1004.9999999999999` in IEEE-754 double\n * precision, not the exact `1005`), which is exactly the class of \"wrong\n * answer with no error\" bug this codebase treats as a real bug elsewhere\n * (see `BytecodeBuilder`'s constant-pool overflow fix). Moving the decimal\n * point through the digit string sidesteps floating-point entirely and is\n * always exact.\n *\n * @param raw Unsigned decimal digit-string as produced by the lexer's\n * NUMBER token (e.g. \"2.5\", \"10\", \"1.005\"), never signed, never\n * locale-separated (this rule reads the raw pre-locale-normalization\n * token text, same as the lexer emits it).\n * @param magnitude Number of decimal places to shift right (3/6/9/12).\n */\nfunction scaleDecimalString(raw: string, magnitude: number): string {\n const dotIdx = raw.indexOf(\".\");\n const intPart = dotIdx === -1 ? raw : raw.slice(0, dotIdx);\n let fracPart = dotIdx === -1 ? \"\" : raw.slice(dotIdx + 1);\n\n if (fracPart.length < magnitude) {\n fracPart = fracPart.padEnd(magnitude, \"0\");\n }\n\n const shiftedInt = intPart + fracPart.slice(0, magnitude);\n const remainingFrac = fracPart.slice(magnitude);\n\n return remainingFrac ? `${shiftedInt}.${remainingFrac}` : shiftedInt;\n}\n\n/**\n * Fuses `NUMBER` + an immediately-adjacent large-number magnitude suffix\n * (`k`/`M`/`G`/`B`/`T`, see {@link SUFFIX_MAGNITUDE}) into a single\n * `NUMBER` token carrying the fully-scaled value, e.g. `2.5k` becomes\n * indistinguishable from typing `2500` directly, all the way down to\n * emitting through the exact same `PUSH_NUMBER` opcode path (see\n * `PrecedenceParser.ts`'s `NUMBER_ID` fast path).\n *\n * Adjacency is required and checked via source offsets, NOT just token\n * order: `2.5 k` (a space before the suffix) must NOT fuse, confirmed by\n * a real lexer probe that `\"2.5k\"` and `\"2.5 k\"` both tokenize as\n * `NUMBER` + `IDENT \"k\"`, differing only in the `IDENT` token's `offset`\n * (immediately following the number's last character vs. one past it).\n * Without this check, a bare \"the letter k as its own word\" a few tokens\n * later could never realistically follow a number directly, but a\n * deliberately-spaced `2.5 k` (e.g. someone starting to type a unit that\n * doesn't exist) would otherwise be silently reinterpreted as 2500.\n */\nexport function largeNumberSuffixNormalizerRule(priority = 65): NormalizerRule {\n return {\n name: \"arithmetic:large-number-suffix\",\n priority,\n match(tokens, pos): NormalizerMatch | null {\n const numberToken = tokens[pos];\n if (numberToken.type !== \"NUMBER\") return null;\n if (!PLAIN_DECIMAL.test(numberToken.text)) return null;\n\n const suffixToken = tokens[pos + 1];\n if (!suffixToken) return null;\n // Suffix must be a word-shaped token (IDENT for k/M/G/T, or UNIT for\n // bare \"B\", already a registered unit symbol, see the collision\n // note on SUFFIX_MAGNITUDE above) sitting immediately adjacent to\n // the number, with no whitespace in between.\n if (suffixToken.type !== \"IDENT\" && suffixToken.type !== \"UNIT\") return null;\n\n const adjacent = numberToken.offset + numberToken.text.length === suffixToken.offset;\n // A word magnitude never comes from a UNIT token. The words are matched\n // case-insensitively (see WORD_MAGNITUDE), so \"mn\"/\"bn\"/\"tn\" otherwise\n // swallow `mN`, `MN` and `TN`, and a written unit has to beat a\n // differently-cased abbreviation of a number word. The single-letter\n // table above is deliberately NOT restricted this way: bare \"B\" is a\n // unit token and claiming it is the collision SUFFIX_MAGNITUDE's own\n // note describes and accepts.\n const wordMagnitude = suffixToken.type === \"UNIT\"\n ? undefined\n : WORD_MAGNITUDE[suffixToken.value.toLowerCase()];\n // A single-letter suffix must touch the number: `5 k` is not 5,000, and\n // more importantly `5 M` next to an unrelated variable M should not be.\n // A word may be separated by the space it is normally written with.\n const magnitude = adjacent\n ? (SUFFIX_MAGNITUDE[suffixToken.value] ?? wordMagnitude)\n : wordMagnitude;\n if (magnitude === undefined) return null;\n\n const scaled = scaleDecimalString(numberToken.text, magnitude);\n return {\n consumed: 2,\n replacement: [createFusedToken(\"NUMBER\", scaled, tokens.slice(pos, pos + 2))],\n ruleName: \"arithmetic:large-number-suffix\",\n };\n },\n };\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { NumberParselet } from \"./parselets/NumberParselet\";\nimport { PrefixOpParselet } from \"./parselets/PrefixOpParselet\";\nimport { BinaryOpParselet } from \"./parselets/BinaryOpParselet\";\nimport { GroupParselet } from \"./parselets/GroupParselet\";\nimport { ConstantParselet } from \"./parselets/ConstantParselet\";\nimport { largeNumberSuffixNormalizerRule } from \"./normalizer/LargeNumberSuffixNormalizerRule\";\n\n/** Core arithmetic: numbers, `()` grouping, `pi`/`e` constants, `+ - * / % ^`, bitwise `<< >> & | ^`, and their `*_by` word forms. */\nexport const ARITHMETIC_PACKAGE: IEnginePackage = {\n name: \"solve-arithmetic\",\n prefixParselets: {\n NUMBER: new NumberParselet(),\n LPAREN: new GroupParselet(),\n PI: new ConstantParselet(),\n E: new ConstantParselet(),\n PLUS: new PrefixOpParselet(OpCode.POS),\n MINUS: new PrefixOpParselet(OpCode.NEG),\n // `~x`. The opcode and the lexer token both already existed; without this\n // registration the token reached the parser and stopped there, so `~5`\n // reported \"no prefix parselet found\" rather than -6.\n BIT_NOT: new PrefixOpParselet(OpCode.BIT_NOT),\n },\n infixParselets: {\n PLUS: new BinaryOpParselet(BindingPower.Sum, OpCode.ADD),\n // The word \"and\", which adds exactly like \"+\" but at a looser binding\n // power so a phrase parselet can use it as a list separator. See\n // Token.ts's AND_CONJ comment for why it is not simply mapped to PLUS.\n AND_CONJ: new BinaryOpParselet(BindingPower.Conjunction, OpCode.ADD),\n MINUS: new BinaryOpParselet(BindingPower.Sum, OpCode.SUB),\n STAR: new BinaryOpParselet(BindingPower.Product, OpCode.MUL),\n SLASH: new BinaryOpParselet(BindingPower.Product, OpCode.DIV),\n MOD: new BinaryOpParselet(BindingPower.Product, OpCode.MOD),\n // `^` is right-associative, so \"2 ^ 3 ^ 2\" is 2^(3^2) = 512. The Tier 1\n // fast path in PrecedenceParser is what actually runs for `^`; this\n // registration has to agree with it or the two tiers describe different\n // languages.\n CARET: new BinaryOpParselet(BindingPower.Exponent, OpCode.EXP, true),\n TIMES_BY: new BinaryOpParselet(BindingPower.Product, OpCode.MUL),\n MULTIPLY_BY: new BinaryOpParselet(BindingPower.Product, OpCode.MUL),\n DIVIDE_BY: new BinaryOpParselet(BindingPower.Product, OpCode.DIV),\n // The shifts and the bitwise trio, at the levels BindingPower gives them\n // and therefore at C and JavaScript precedence. These six are declared\n // twice, here and in BUILTIN_INFIX_BP, and only the Tier 1 numbers are\n // ever read, so the levels must be the SAME names in both places. When\n // they were not, the four that had a fast-path entry ran at one level\n // while `>>>`, which had none, ran at another.\n LSHIFT: new BinaryOpParselet(BindingPower.Shift, OpCode.LSHIFT),\n RSHIFT: new BinaryOpParselet(BindingPower.Shift, OpCode.RSHIFT),\n URSHIFT: new BinaryOpParselet(BindingPower.Shift, OpCode.URSHIFT),\n BIT_AND: new BinaryOpParselet(BindingPower.BitwiseAnd, OpCode.BIT_AND),\n BIT_OR: new BinaryOpParselet(BindingPower.BitwiseOr, OpCode.BIT_OR),\n BIT_XOR: new BinaryOpParselet(BindingPower.BitwiseXor, OpCode.BIT_XOR),\n },\n normalizerRules: [largeNumberSuffixNormalizerRule()],\n};\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * The postfix `%`, producing a Percentage-typed value holding the fraction.\n *\n * Postfix rather than a remainder operator, which is why `17 % 5` is a parse\n * error rather than 2: `17 %` is already complete. Use `mod` for remainder.\n *\n * This used to emit a literal divide-by-100, which made `10%` an ordinary\n * number 0.1 and `200 + 10%` therefore 200.10. Nobody writing that line means\n * 200.10; they mean 220, which is what Soulver answers. A percentage is only\n * meaningful relative to something, so it has to stay distinguishable from the\n * bare fraction long enough for the operator to apply it. The relative\n * behaviour lives in the VM's ADD/SUB, where the other operand is known.\n */\nexport class PercentParselet implements InfixParselet {\n\treadonly category = \"Percentage\";\n\treadonly bindingPower = BindingPower.Prefix;\n\n parse(parser: Parser, left: Token, token: Token, builder: BytecodeBuilder): void {\n // The divide still happens: a Percentage stores its fraction (0.1 for\n // 10%), and TO_PERCENTAGE only re-types what is already on the stack,\n // because it also backs `as %`, where `0.35 as %` is 35% and dividing\n // would be wrong.\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(100);\n builder.emitOpcode(OpCode.DIV);\n builder.emitOpcode(OpCode.TO_PERCENTAGE);\n }\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/**\n * The `<value> is ... what` family: asking for whichever part of a percentage\n * relationship you do not have.\n *\n * 20 is 10% of what 200 the base, given the part and the rate\n * 180 is 10% off what 200 the same, before a discount\n * 220 is 10% on what 200 the same, before a markup\n * 20 is what % of 200 10% the rate, given the part and the base\n * 180 is what % off 200 10% the rate, as a reduction\n * 180 is what % on 150 20% the rate, as an increase\n * 50 is 1/5 of what 250 a fraction reads the same as a rate\n * 81 is 9 to what power 2 the odd one out, kept here because it is\n * the same \"solve for the missing piece\"\n * shape and the same trigger word\n *\n * `5% of what is 6` (the other word order) already existed as\n * `OfWhatIsParselet`. This is the order Soulver documents, and it reads more\n * naturally: you state what you know first.\n *\n * All of it hangs off a bare `is`, which is a real risk this codebase normally\n * avoids taking. It is accepted here because \"is\" cannot be a `:variableName`\n * (it is a verb, and the colon form rejects keywords anyway), because the\n * existing `is to` proportion phrase is fused earlier and so still wins, and\n * because every alternative below is anchored by a second keyword, so a bare\n * `is` with nothing recognisable after it fails loudly rather than silently\n * swallowing the rest of the line.\n *\n * The left operand is already on the stack when this runs, which is why\n * several branches need a SWAP: `BytecodeBuilder` is append-only, so operand\n * order has to be fixed on the stack rather than by emitting differently.\n */\nexport class IsWhatParselet implements InfixParselet {\n\treadonly category = \"Percentage\";\n\t// Below arithmetic, so the whole expression to the left is the value being\n\t// asked about: `10 + 10 is what % of 200` asks about 20.\n\treadonly bindingPower = BindingPower.Conditional;\n\n\tparse(parser: Parser, _left: Token, _token: Token, builder: BytecodeBuilder): void {\n\t\tif (nextWordIs(parser, \"what\")) {\n\t\t\tparser.consume();\n\t\t\tthis.parseWhatRate(parser, builder);\n\t\t\treturn;\n\t\t}\n\t\tthis.parseWhatBase(parser, builder);\n\t}\n\n\t/**\n\t * `<part> is what % of|off|on <base>`, solving for the rate.\n\t */\n\tprivate parseWhatRate(parser: Parser, builder: BytecodeBuilder): void {\n\t\tif (!parser.match(\"PERCENT\")) {\n\t\t\tthrow ErrorFactory.parsing(\n\t\t\t\t\"IS_WHAT_EXPECTED_PERCENT\",\n\t\t\t\t'Expected \"%\" after \"is what\", as in \"20 is what % of 200\"',\n\t\t\t);\n\t\t}\n\n\t\t// `50 to 75 is what %` with no trailing preposition: the left operand is\n\t\t// already the change, and this only asks for it as a percentage.\n\t\tconst preposition = readPreposition(parser);\n\t\tif (preposition === null) {\n\t\t\tbuilder.emitOpcode(OpCode.TO_PERCENTAGE);\n\t\t\treturn;\n\t\t}\n\n\t\tparser.parseExpression(BindingPower.Conditional, builder); // base\n\n\t\t// Every branch starts by dividing, which avoids needing the part and the\n\t\t// base on the stack at the same time in the wrong order. `off` and `on`\n\t\t// then just measure that ratio's distance from 1, in whichever\n\t\t// direction the word implies.\n\t\tbuilder.emitOpcode(OpCode.DIV); // part / base\n\t\tif (preposition === \"off\") {\n\t\t\t// 180/200 is 0.9, and 0.9 is 10% off.\n\t\t\tbuilder.emitOpcode(OpCode.PUSH_NUMBER);\n\t\t\tbuilder.emitNumber(1);\n\t\t\tbuilder.emitOpcode(OpCode.SWAP);\n\t\t\tbuilder.emitOpcode(OpCode.SUB);\n\t\t} else if (preposition === \"on\") {\n\t\t\t// 180/150 is 1.2, and 1.2 is 20% on.\n\t\t\tbuilder.emitOpcode(OpCode.PUSH_NUMBER);\n\t\t\tbuilder.emitNumber(1);\n\t\t\tbuilder.emitOpcode(OpCode.SUB);\n\t\t}\n\t\tbuilder.emitOpcode(OpCode.TO_PERCENTAGE);\n\t}\n\n\t/**\n\t * `<part> is <rate> of|off|on what`, solving for the base.\n\t */\n\tprivate parseWhatBase(parser: Parser, builder: BytecodeBuilder): void {\n\t\tparser.parseExpression(BindingPower.Conditional, builder); // rate\n\n\t\t// `81 is 9 to what power`, the logarithm. Same shape, different question.\n\t\tif (parser.match(\"TO\")) {\n\t\t\texpectWord(parser, \"what\", '\"81 is 9 to what power\"');\n\t\t\texpectWord(parser, \"power\", '\"81 is 9 to what power\"');\n\t\t\t// log(value) / log(base). stack: value, base\n\t\t\tbuilder.emitOpcode(OpCode.CALL_BUILTIN);\n\t\t\tbuilder.emitIndex(LOG_BUILTIN);\n\t\t\tbuilder.emitIndex(1);\n\t\t\tbuilder.emitOpcode(OpCode.SWAP);\n\t\t\tbuilder.emitOpcode(OpCode.CALL_BUILTIN);\n\t\t\tbuilder.emitIndex(LOG_BUILTIN);\n\t\t\tbuilder.emitIndex(1);\n\t\t\tbuilder.emitOpcode(OpCode.SWAP);\n\t\t\tbuilder.emitOpcode(OpCode.DIV);\n\t\t\treturn;\n\t\t}\n\n\t\t// Fused tokens, not \"of\" + \"what\": \"of\" is an infix operator in its own\n\t\t// right (\"10% of 200\"), so parsing the rate above would otherwise\n\t\t// consume it and the trailing \"what\" before this line ran.\n\t\tconst preposition = parser.match(\"OF_WHAT\")\n\t\t\t? \"of\"\n\t\t\t: parser.match(\"OFF_WHAT\")\n\t\t\t\t? \"off\"\n\t\t\t\t: parser.match(\"ON_WHAT\")\n\t\t\t\t\t? \"on\"\n\t\t\t\t\t: null;\n\t\tif (preposition === null) {\n\t\t\tthrow ErrorFactory.parsing(\n\t\t\t\t\"IS_WHAT_EXPECTED_PREPOSITION\",\n\t\t\t\t'Expected \"of what\", \"off what\" or \"on what\", as in \"20 is 10% of what\"',\n\t\t\t);\n\t\t}\n\n\t\t// stack: part, rate\n\t\tif (preposition === \"of\") {\n\t\t\t// 20 is 10% of 200, so the base is simply the part over the rate.\n\t\t\tbuilder.emitOpcode(OpCode.DIV);\n\t\t\treturn;\n\t\t}\n\n\t\t// A discounted or marked-up total: part / (1 -/+ rate). The 1 is\n\t\t// combined with the rate first, leaving the part untouched underneath,\n\t\t// so the final divide has its operands already in the right order.\n\t\tbuilder.emitOpcode(OpCode.PUSH_NUMBER);\n\t\tbuilder.emitNumber(1);\n\t\tif (preposition === \"off\") {\n\t\t\t// 1 - rate, so SUB needs the 1 on the left.\n\t\t\tbuilder.emitOpcode(OpCode.SWAP);\n\t\t\tbuilder.emitOpcode(OpCode.SUB);\n\t\t} else {\n\t\t\tbuilder.emitOpcode(OpCode.ADD);\n\t\t}\n\t\tbuilder.emitOpcode(OpCode.DIV);\n\t}\n}\n\n/** `log` in VMBuiltins.ts, the natural logarithm. */\nconst LOG_BUILTIN = 5;\n\n/** Whether the next token is the given word, without consuming it. */\nfunction nextWordIs(parser: Parser, word: string): boolean {\n\treturn (parser.peek()?.text ?? \"\").toLowerCase() === word;\n}\n\n/** Consumes the expected word, or reports what the phrase should look like. */\nfunction expectWord(parser: Parser, word: string, example: string): void {\n\tif (!nextWordIs(parser, word)) {\n\t\tthrow ErrorFactory.parsing(\n\t\t\t\"IS_WHAT_EXPECTED_WORD\",\n\t\t\t`Expected \"${word}\" here, as in ${example}`,\n\t\t);\n\t}\n\tparser.consume();\n}\n\n/** Reads `of`, `off` or `on`, or `null` when none is present. */\nfunction readPreposition(parser: Parser): \"of\" | \"off\" | \"on\" | null {\n\tif (parser.match(\"OF\")) return \"of\";\n\tconst word = (parser.peek()?.text ?? \"\").toLowerCase();\n\tif (word === \"off\" || word === \"on\") {\n\t\tparser.consume();\n\t\treturn word;\n\t}\n\treturn null;\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `<rate> on <base>` and `<rate> off <base>`: a markup or a discount with the\n * rate stated first.\n *\n * 10% on 200 220\n * 10% off 200 180\n *\n * The same answers as `200 + 10%` and `200 - 10%`, said the other way round.\n * Both orders exist because both get written, and only one of them worked.\n *\n * Binds at `Conditional`, below arithmetic, so the base is the whole\n * expression after it.\n */\nexport class OnOffBaseParselet implements InfixParselet {\n\treadonly category = \"Percentage\";\n\treadonly bindingPower = BindingPower.Conditional;\n\n\t/** @param sign - `1` for a markup (`on`), `-1` for a discount (`off`). */\n\tconstructor(private readonly sign: 1 | -1) {}\n\n\tparse(parser: Parser, _left: Token, _token: Token, builder: BytecodeBuilder): void {\n\t\tparser.parseExpression(BindingPower.Conditional, builder); // base\n\n\t\t// stack: rate, base. Result is base × (1 ± rate), and the rate is\n\t\t// underneath, so the 1 ± rate is built first and then multiplied.\n\t\tbuilder.emitOpcode(OpCode.SWAP); // base, rate\n\t\tbuilder.emitOpcode(OpCode.PUSH_NUMBER);\n\t\tbuilder.emitNumber(1);\n\t\tif (this.sign === 1) {\n\t\t\tbuilder.emitOpcode(OpCode.ADD);\n\t\t} else {\n\t\t\t// 1 - rate needs the 1 on the left of the subtraction.\n\t\t\tbuilder.emitOpcode(OpCode.SWAP);\n\t\t\tbuilder.emitOpcode(OpCode.SUB);\n\t\t}\n\t\tbuilder.emitOpcode(OpCode.MUL);\n\t}\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\n/**\n * Turns the `on`/`off` immediately after a `%` into a markup/discount operator.\n *\n * `10% on 200` is 220 and `10% off 200` is 180, the same answers as\n * `200 + 10%` and `200 - 10%` with the rate stated first. Both orders get\n * written; only one of them worked.\n *\n * Deliberately a normalizer rule keyed on the preceding `%` rather than a\n * keyword mapping for the bare words. \"on\" is already spoken for in two other\n * grammars, `stock(AAPL) on April 12, 2005` and `weekday on March 9, 2024`.\n * Claiming it outright broke both, which is how this rule came to exist.\n * Requiring the `%` immediately before means it can only fire where a\n * percentage is genuinely being applied to something.\n */\nexport function percentOnOffNormalizerRule(priority = 68): NormalizerRule {\n\treturn {\n\t\tname: \"percentage:on-off\",\n\t\tpriority,\n\t\tmatch(tokens, pos): NormalizerMatch | null {\n\t\t\tif (tokens[pos]?.type !== \"PERCENT\") return null;\n\n\t\t\tconst word = tokens[pos + 1];\n\t\t\tif (word?.type !== \"IDENT\") return null;\n\t\t\tconst text = (word.text ?? word.value ?? \"\").toLowerCase();\n\t\t\tif (text !== \"on\" && text !== \"off\") return null;\n\n\t\t\t// `5% off what is 190` is the solve-for-the-base grammar, whose\n\t\t\t// \"off what is\" phrase owns this same \"off\". It is fused later than\n\t\t\t// this rule runs, so the guard has to be here.\n\t\t\tconst after = (tokens[pos + 2]?.text ?? \"\").toLowerCase();\n\t\t\tif (after === \"what\") return null;\n\n\t\t\t// The `%` is kept: it still has to divide by a hundred. Only the\n\t\t\t// following word is retyped.\n\t\t\treturn {\n\t\t\t\tconsumed: 2,\n\t\t\t\treplacement: [\n\t\t\t\t\ttokens[pos],\n\t\t\t\t\tcreateFusedToken(text === \"on\" ? \"PCT_ON\" : \"PCT_OFF\", text, [word]),\n\t\t\t\t],\n\t\t\t\truleName: \"percentage:on-off\",\n\t\t\t};\n\t\t},\n\t};\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * The `of` in `15% of 2400`.\n *\n * A plain multiply once the left side is already a fraction, which is what\n * {@link PercentParselet} leaves on the stack. Product binding power keeps\n * `10% of 200 + 5` reading as `(10% of 200) + 5`.\n */\nexport class OfParselet implements InfixParselet {\n\treadonly category = \"Percentage\";\n\treadonly bindingPower = BindingPower.Product;\n\n parse(parser: Parser, left: Token, token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(this.bindingPower, builder);\n builder.emitOpcode(OpCode.MUL);\n }\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * \"N% of what is X\", solves `N% * base = X` for `base` (base = X / N%)\n * the inverse of the plain \"N% of X\" multiplication `OfParselet` already\n * handles. Numi's syntax reference names this exact form; this codebase\n * had no \"solve for the unknown\" percentage grammar before it.\n *\n * By the time this infix parselet runs, the left-hand percent value (as a\n * plain decimal, e.g. 0.05 for \"5%\") is ALREADY on the bytecode stack\n * that's how Pratt-parser infix continuation works, the left operand's\n * bytecode is emitted before the infix trigger token is even consumed.\n * `BytecodeBuilder` is append-only (no rewriting earlier instructions), so\n * getting `X / percent` instead of the wrong `percent / X` needs an\n * explicit SWAP between parsing X and dividing, not just emitting DIV\n * directly. See OpCode.SWAP's VM.ts case for the exact stack-swap\n * semantics this relies on.\n */\nexport class OfWhatIsParselet implements InfixParselet {\n\treadonly category = \"Percentage\";\n\treadonly bindingPower = BindingPower.Product;\n\n parse(parser: Parser, left: Token, token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(this.bindingPower, builder);\n builder.emitOpcode(OpCode.SWAP);\n builder.emitOpcode(OpCode.DIV);\n }\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * \"N% on what is X\" / \"N% off what is X\", solve for the base value given\n * a percentage INCREASE/DECREASE result, e.g. \"5% on what is 210\" -> 200\n * (200 * 1.05 = 210), \"5% off what is 190\" -> 200 (200 * 0.95 = 190).\n * The sibling of `OfWhatIsParselet` (plain \"N% of what is X\", no +/-1\n * offset). See that file's doc comment for the base SWAP+DIV technique\n * this builds on. Both forms come from Numpad's documented syntax\n * reference (`5% on what is $210` / `5% off what is $190`).\n *\n * `sign` is +1 for \"on\" (X / (1 + percent)) or -1 for \"off\" (X / (1 -\n * percent)). By the time this infix parselet runs, the left-hand percent\n * decimal is already on the stack. See the two bytecode sequences below\n * each independently derived to land the operands in the right order for\n * a plain (non-reversed) `SUB`/`DIV`, since `BytecodeBuilder` is\n * append-only and there's no way to \"undo\" the already-emitted percent\n * value's stack position other than via explicit `SWAP`s.\n */\nexport class OnOffWhatIsParselet implements InfixParselet {\n\treadonly category = \"Percentage\";\n\treadonly bindingPower = BindingPower.Product;\n constructor(private readonly sign: 1 | -1) {}\n\n parse(parser: Parser, left: Token, token: Token, builder: BytecodeBuilder): void {\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(1);\n if (this.sign === 1) {\n // stack: [percent, 1] -> ADD (commutative) -> [1 + percent]\n builder.emitOpcode(OpCode.ADD);\n } else {\n // stack: [percent, 1] -> SWAP -> [1, percent] -> SUB -> [1 - percent]\n builder.emitOpcode(OpCode.SWAP);\n builder.emitOpcode(OpCode.SUB);\n }\n parser.parseExpression(this.bindingPower, builder);\n // stack: [(1±percent), X] -> SWAP -> [X, (1±percent)] -> DIV -> X / (1±percent)\n builder.emitOpcode(OpCode.SWAP);\n builder.emitOpcode(OpCode.DIV);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * Prefix form of increase and decrease, as in `increase 100 by 10%`.\n *\n * The infix form is {@link IncreaseByParselet}. Both exist because the phrase\n * reads naturally either side of the amount.\n */\nexport class IncreaseDecreaseParselet implements PrefixParselet {\n\treadonly category = \"Percentage\";\n\tconstructor(private readonly multiplier: number) {}\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(0, builder);\n const next = parser.peek();\n if (next && (next.type === \"IDENT\" || next.type === \"BY\")) {\n parser.consume();\n }\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(1);\n parser.parseExpression(0, builder);\n if (parser.peek()?.type === \"PERCENT\") parser.consume();\n builder.emitOpcode(this.multiplier > 0 ? OpCode.ADD : OpCode.SUB);\n builder.emitOpcode(OpCode.MUL);\n }\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `increase X by N%` and its decrease counterpart, sharing one implementation.\n *\n * The multiplier passed to the constructor is the only difference between them:\n * +1 grows, -1 shrinks.\n */\nexport class IncreaseByParselet implements InfixParselet {\n\treadonly category = \"Percentage\";\n\treadonly bindingPower = BindingPower.Conditional;\n\n\tconstructor(private readonly multiplier: number) {}\n\n parse(parser: Parser, left: Token, token: Token, builder: BytecodeBuilder): void {\n // left operand (e.g., 100) is already on the VM stack.\n // Parse the right expression (e.g., \"20%\" via PercentParselet -> 0.2).\n parser.parseExpression(this.bindingPower, builder);\n\n // Bytecode: compute left * (1 + right) for increase, left * (1 - right) for decrease.\n // Stack: [left, right] -> PUSH 1 -> [left, right, 1] -> SWAP -> [left, 1, right]\n // -> ADD/SUB -> [left, 1±right] -> MUL -> [left * (1±right)]\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(1);\n builder.emitOpcode(OpCode.SWAP);\n builder.emitOpcode(this.multiplier > 0 ? OpCode.ADD : OpCode.SUB);\n builder.emitOpcode(OpCode.MUL);\n }\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { isKnownUnit } from \"@solve-js/lexer/units\";\n\n/**\n * `100 to 150`, the percentage change between two numbers.\n *\n * Shares the `to` token with unit conversion, so it checks whether the left\n * operand is a UNIT first and yields to the conversion parselet if so. Without\n * that check `5 km to miles` would be read as a percentage change.\n */\nexport class PercentageChangeParselet implements InfixParselet {\n\treadonly category = \"Percentage\";\n\treadonly bindingPower = BindingPower.Conditional;\n\n parse(parser: Parser, left: Token, token: Token, builder: BytecodeBuilder): void {\n // Check if the left operand is a UNIT token\n // If so, this is a UoM conversion, not a percentage change\n if (left.type === \"UNIT\") {\n // This is a UoM conversion, not a percentage change\n // Don't parse the right operand as a percentage change\n // Instead, let the UoM parselet handle it\n return;\n }\n\n // \"TO\" serves two grammars: percentage change (`800 to 1000`) and unit\n // conversion (`<uom-expr> to <unit>`). The `left.type === \"UNIT\"` guard\n // above only catches the literal-number-then-unit case (handled inline\n // by UomLiteralParselet before the Pratt loop ever reaches here), it\n // misses a bare variable/expression on the left (`:total to USD`,\n // `:heightCm to m`), because a variable's runtime type (Uom or plain\n // Number) isn't knowable from its token type at parse time.\n //\n // What IS knowable at parse time is whether the RIGHT-hand token names\n // a real unit or ISO 4217 currency code, a percentage-change target is\n // always numeric, so if the word after \"to\" is a recognized unit name,\n // this must be a conversion, not a percentage change. Mirrors InParselet\n // (same UOM_CONVERT_IN opcode), which has no such ambiguity to resolve\n // because \"in\" has no percentage-change meaning.\n //\n // Target token type is usually UNIT or IDENT and gets verified against\n // isKnownUnit. \"in\" (inches) is a valid unit name that collides with\n // the IN keyword, it lexes as type IN, never UNIT/IDENT, and is\n // deliberately absent from knownUnits (see units.ts) because adding it\n // there confuses the lexer's keyword-vs-unit priority for the \"in\"\n // OPERATOR itself. UomLiteralParselet/ConvertParselet already special-\n // case token type IN by trusting its literal text unconditionally\n // (\"3 ft in in\"); do the same here so `:x to in` doesn't fall through\n // and try to parse the IN keyword as a percentage-change operand\n // (which has no prefix parselet and throws).\n const nextToken = parser.peek();\n if (\n nextToken &&\n (\n ((nextToken.type === \"UNIT\" || nextToken.type === \"IDENT\") && isKnownUnit(nextToken.value)) ||\n nextToken.type === \"IN\"\n )\n ) {\n parser.consume();\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(nextToken.value);\n builder.emitOpcode(OpCode.UOM_CONVERT_IN);\n return;\n }\n\n // Parse the right operand (target value)\n parser.parseExpression(this.bindingPower, builder);\n \n // Calculate percentage change: right / left - 1\n // Stack before: [left, right]\n // After SWAP: [right, left]\n builder.emitOpcode(OpCode.SWAP);\n // After DIV: [right / left]\n builder.emitOpcode(OpCode.DIV);\n // After PUSH_NUMBER 1: [right / left, 1]\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(1);\n // After SUB: [right / left - 1]\n builder.emitOpcode(OpCode.SUB);\n // Convert to percentage type\n builder.emitOpcode(OpCode.TO_PERCENTAGE);\n }\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * Small English count words, so `three times` reads as naturally as `3 times`.\n *\n * Only the counts that plausibly precede `times` in a sentence, a repeat is a\n * literal known at parse time. A digit works too, resolved directly below.\n */\nconst COUNT_WORDS: Record<string, number> = {\n\tone: 1, two: 2, three: 3, four: 4, five: 5, six: 6,\n\tseven: 7, eight: 8, nine: 9, ten: 10, eleven: 11, twelve: 12,\n};\n\n/** The count in a `N times` suffix, or `null` when the token is not one. */\nfunction resolveCount(token: Token): number | null {\n\tif (token.type === \"NUMBER\") {\n\t\tconst n = Number(token.value);\n\t\treturn Number.isInteger(n) && n >= 0 ? n : null;\n\t}\n\tif (token.type === \"IDENT\") {\n\t\treturn COUNT_WORDS[token.value.toLowerCase()] ?? null;\n\t}\n\treturn null;\n}\n\n/**\n * `X up N%` and `X down N%`, a successive percentage change applied to a value,\n * chainable with `then` and repeatable with `N times`.\n *\n * 120 up 10% then down 10% 118.80 (not 120: each change compounds)\n * 50 up 20% 60\n * 80 down 15% 68\n * 100 up 10% three times 133.10\n *\n * One step is `left * (1 ± rate)`, the same arithmetic as `increase X by N%`\n * (see {@link IncreaseByParselet}), so a chain is that step applied to the\n * running total again and again. That is exactly where the intuition fails:\n * `120 up 10% then down 10%` looks like it should return to 120, and returns\n * 118.80 instead, because the 10% down is taken off the larger 132.\n *\n * `up`/`down` become this operator only immediately before a percentage (the\n * PercentUpDownNormalizerRule does the retyping), so prose keeps its words.\n */\nexport class UpDownParselet implements InfixParselet {\n\treadonly category = \"Percentage\";\n\treadonly bindingPower = BindingPower.Conditional;\n\n\t/** @param sign `1` for `up` (grow), `-1` for `down` (shrink). */\n\tconstructor(private readonly sign: 1 | -1) {}\n\n\tparse(parser: Parser, _left: Token, _token: Token, builder: BytecodeBuilder): void {\n\t\t// left operand is already on the VM stack. Parse the rate (\"10%\" -> 0.1).\n\t\tparser.parseExpression(this.bindingPower, builder);\n\n\t\t// Build the factor (1 ± rate) from [left, rate], mirroring IncreaseByParselet:\n\t\t// PUSH 1 -> [left, rate, 1] -> SWAP -> [left, 1, rate] -> ADD/SUB -> [left, 1 ± rate].\n\t\tbuilder.emitOpcode(OpCode.PUSH_NUMBER);\n\t\tbuilder.emitNumber(1);\n\t\tbuilder.emitOpcode(OpCode.SWAP);\n\t\tbuilder.emitOpcode(this.sign > 0 ? OpCode.ADD : OpCode.SUB);\n\n\t\t// Optional `N times`: raise the factor to the Nth power, so the change\n\t\t// lands N times over. `1.1 ^ 3` for \"up 10% three times\".\n\t\tconst repeat = this.tryConsumeRepeat(parser);\n\t\tif (repeat !== null) {\n\t\t\tbuilder.emitOpcode(OpCode.PUSH_NUMBER);\n\t\t\tbuilder.emitNumber(repeat);\n\t\t\tbuilder.emitOpcode(OpCode.EXP);\n\t\t}\n\n\t\t// Apply the factor to the running total.\n\t\tbuilder.emitOpcode(OpCode.MUL);\n\n\t\t// A `then` before the next step is pure connective. Drop it so the\n\t\t// parser's own precedence loop reaches the following `up`/`down` and\n\t\t// chains onto this result. Only a `then` that actually precedes a step\n\t\t// is taken, so `if C then V else W` keeps its own `then`.\n\t\tthis.consumeChainThen(parser);\n\t}\n\n\t/**\n\t * Consume a trailing `N times` (`3 times`, `three times`) if present,\n\t * returning the count, or `null` when there is no repeat here. The word\n\t * \"times\" lexes as the multiply token but keeps its source text, which is\n\t * what tells it apart from a literal `*`.\n\t */\n\tprivate tryConsumeRepeat(parser: Parser): number | null {\n\t\tconst countTok = parser.peek();\n\t\tconst timesTok = parser.peekAt(1);\n\t\tif (!countTok || !timesTok) return null;\n\t\tif (timesTok.type !== \"STAR\" || timesTok.value.toLowerCase() !== \"times\") return null;\n\n\t\tconst count = resolveCount(countTok);\n\t\tif (count === null) return null;\n\n\t\tparser.consume(); // count\n\t\tparser.consume(); // times\n\t\treturn count;\n\t}\n\n\t/** Drop a connective `then` that sits directly before the next step. */\n\tprivate consumeChainThen(parser: Parser): void {\n\t\tif (parser.peek()?.type !== \"THEN\") return;\n\t\tconst next = parser.peekAt(1)?.type;\n\t\tif (next === \"PCT_UP\" || next === \"PCT_DOWN\") {\n\t\t\tparser.consume(); // then\n\t\t}\n\t}\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\n/**\n * The retyped operator token for a `up`/`down` at position `i`, or `null` when\n * that position does not begin a percentage step.\n *\n * A step is the bare word `up`/`down` immediately before a percentage: an IDENT\n * then a NUMBER then a `%`. Requiring the `%` is what keeps the words safe to\n * use in prose, the same guard the `on`/`off` markup rule relies on.\n */\nfunction stepTokenAt(tokens: Token[], i: number): Token | null {\n\tconst word = tokens[i];\n\tif (word?.type !== \"IDENT\") return null;\n\tconst text = (word.text ?? word.value ?? \"\").toLowerCase();\n\tif (text !== \"up\" && text !== \"down\") return null;\n\tif (tokens[i + 1]?.type !== \"NUMBER\") return null;\n\tif (tokens[i + 2]?.type !== \"PERCENT\") return null;\n\treturn createFusedToken(text === \"up\" ? \"PCT_UP\" : \"PCT_DOWN\", text, [word]);\n}\n\n/**\n * Retypes a bare `up`/`down` into the successive-change operator, but only\n * where a percentage immediately follows.\n *\n * 120 up 10% then down 10% 118.80\n * 50 up 20% 60\n * 80 down 15% 68\n *\n * `up` and `down` are ordinary English words, far too common to claim as bare\n * keywords: \"prices are up\", \"scroll down\", a variable named `up`. So the same\n * approach as the `on`/`off` markup rule is used (see\n * PercentOnOffNormalizerRule): the word is recognised only when it sits\n * directly before a percentage (a NUMBER then a `%`), the one place\n * `up 10%`/`down 10%` can only mean a percentage change. Anywhere else the word\n * passes through as an IDENT and stays a name.\n *\n * Two positions can match, at a higher priority than the implicit-multiply\n * rule (50):\n *\n * 1. A NUMBER or a `)` directly before a step. The implicit-multiply rule\n * would otherwise insert a `*` here (`120 up` read as `120 * up`), which\n * strands the operator with no left operand. Winning at this position with\n * a higher priority both retypes the word and suppresses that `*`.\n * 2. The `up`/`down` itself, for every other left operand (a unit like\n * `$300`, a `then` connective, the start of the line), where no implicit\n * `*` is at stake.\n *\n * The arithmetic and the `then`/`N times` chaining live in\n * {@link UpDownParselet}; this rule only marks where a step begins.\n */\nexport function percentUpDownNormalizerRule(priority = 68): NormalizerRule {\n\treturn {\n\t\tname: \"percentage:up-down\",\n\t\tpriority,\n\t\tmatch(tokens, pos): NormalizerMatch | null {\n\t\t\t// Position 1: a value the implicit-multiply rule would glue to the\n\t\t\t// word. Consume both, keep the value, retype the word, no `*`.\n\t\t\tconst here = tokens[pos];\n\t\t\tif (here && (here.type === \"NUMBER\" || here.type === \"RPAREN\")) {\n\t\t\t\tconst step = stepTokenAt(tokens, pos + 1);\n\t\t\t\tif (step) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tconsumed: 2,\n\t\t\t\t\t\treplacement: [here, step],\n\t\t\t\t\t\truleName: \"percentage:up-down\",\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Position 2: the word itself, anywhere the value before it does not\n\t\t\t// trigger an implicit `*`.\n\t\t\tconst step = stepTokenAt(tokens, pos);\n\t\t\tif (step) {\n\t\t\t\treturn {\n\t\t\t\t\tconsumed: 1,\n\t\t\t\t\treplacement: [step],\n\t\t\t\t\truleName: \"percentage:up-down\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn null;\n\t\t},\n\t};\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { PercentParselet } from \"./parselets/PercentParselet\";\nimport { IsWhatParselet } from \"./parselets/IsWhatParselet\";\nimport { OnOffBaseParselet } from \"./parselets/OnOffBaseParselet\";\nimport { percentOnOffNormalizerRule } from \"./normalizer/PercentOnOffNormalizerRule\";\nimport { OfParselet } from \"./parselets/OfParselet\";\nimport { OfWhatIsParselet } from \"./parselets/OfWhatIsParselet\";\nimport { OnOffWhatIsParselet } from \"./parselets/OnOffWhatIsParselet\";\nimport { IncreaseDecreaseParselet } from \"./parselets/IncreaseDecreaseParselet\";\nimport { IncreaseByParselet } from \"./parselets/IncreaseByParselet\";\nimport { PercentageChangeParselet } from \"./parselets/PercentageChangeParselet\";\nimport { UpDownParselet } from \"./parselets/UpDownParselet\";\nimport { percentUpDownNormalizerRule } from \"./normalizer/PercentUpDownNormalizerRule\";\n\n/**\n * Percentage syntax: `50%`, `50% of 200`, `100 to 150` (percentage change),\n * `increase 100 by 10%`/`decrease 100 by 10%` (prefix form), the\n * `100 increase by 10%`/`100 decrease by 10%` infix form (fused from the\n * \"increase by\"/\"decrease by\" phrases by the built-in normalizer. See\n * BuiltinNormalizerRules.BUILTIN_PHRASES), and `5% of what is 6` (solve\n * for the base value. See OfWhatIsParselet.ts's doc comment). \"of what\n * is\" is phrase-fused (not a bare \"what\" keyword) for the same\n * variable-name-collision reason \"total\"/\"average\"/etc. are fused\n * elsewhere in this codebase, \"what\" is common enough to be worth\n * protecting as a `:variableName`.\n *\n * Successive change is `120 up 10% then down 10%` (118.80, not 120),\n * `50 up 20%`, `80 down 15%` and the repeat form `100 up 10% three times`.\n * `up`/`down` are retyped from a bare IDENT only when a percentage follows\n * (percentUpDownNormalizerRule), so prose keeps the words. See\n * UpDownParselet.ts.\n */\nexport const PERCENTAGE_PACKAGE: IEnginePackage = {\n name: \"solve-percentage\",\n phrases: {\n \"of what is\": \"OF_WHAT_IS\",\n // Fused so that parsing the rate cannot swallow the \"of\": it is an infix\n // operator in its own right (\"10% of 200\"), so a sub-expression would\n // consume it and the trailing \"what\" before this parselet ever looked.\n \"of what\": \"OF_WHAT\",\n \"off what\": \"OFF_WHAT\",\n \"on what\": \"ON_WHAT\",\n \"on what is\": \"ON_WHAT_IS\",\n \"off what is\": \"OFF_WHAT_IS\",\n },\n infixParselets: {\n PERCENT: new PercentParselet(),\n IS: new IsWhatParselet(),\n // \"10% on 200\" is 220 and \"10% off 200\" is 180: the percentage comes\n // first, the base second. The reverse of \"200 + 10%\".\n PCT_ON: new OnOffBaseParselet(1),\n PCT_OFF: new OnOffBaseParselet(-1),\n OF: new OfParselet(),\n OF_WHAT_IS: new OfWhatIsParselet(),\n ON_WHAT_IS: new OnOffWhatIsParselet(1),\n OFF_WHAT_IS: new OnOffWhatIsParselet(-1),\n TO: new PercentageChangeParselet(),\n INCREASE_BY: new IncreaseByParselet(1),\n DECREASE_BY: new IncreaseByParselet(-1),\n // Successive change: \"120 up 10% then down 10%\" is 118.80, not 120.\n PCT_UP: new UpDownParselet(1),\n PCT_DOWN: new UpDownParselet(-1),\n },\n prefixParselets: {\n INCREASE: new IncreaseDecreaseParselet(1),\n DECREASE: new IncreaseDecreaseParselet(-1),\n },\n normalizerRules: [\n percentOnOffNormalizerRule(),\n percentUpDownNormalizerRule(),\n ],\n};\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/**\n * Exported so `packages/mapreduce/`'s parselets can resolve a bare\n * function-name argument (`map(cos, ...)`) to its builtin index AT PARSE\n * TIME, the same map this file's own `FunctionCallParselet` uses for\n * ordinary `sqrt(x)`-style calls. A name NOT found here is deferred to\n * runtime as a possible user-defined-function reference instead (resolved\n * dynamically via `vm.getUserFunction()`, mirroring `CALL_USER_FUNCTION`'s\n * own forward-reference philosophy). See `MapParselet`/`ReduceParselet`.\n */\nexport const builtinNameToIndex: Record<string, number> = {\n sqrt: 0, abs: 1, sin: 2, cos: 3, tan: 4, log: 5,\n ceil: 6, floor: 7, round: 8, min: 9, max: 10,\n asin: 11, acos: 12, atan: 13, atan2: 14,\n // Long-form trig-function-inverse aliases (Numi/older-calculator naming\n // convention) -- same indices as their short forms above, not a separate\n // implementation.\n arcsin: 11, arccos: 12, arctan: 13,\n // Degree-taking variants (VMBuiltins.ts 87-92).\n sind: 87, cosd: 88, tand: 89, asind: 90, acosd: 91, atand: 92,\n sinh: 15, cosh: 16, tanh: 17,\n asinh: 18, acosh: 19, atanh: 20,\n cbrt: 21, clz32: 22, expm1: 23, exp: 24,\n fround: 25, hypot: 26, imul: 27,\n log10: 28, log1p: 29, log2: 30,\n pow: 31, random: 32, sign: 33, trunc: 34,\n degtorad: 35, radtodeg: 36,\n // 37 is \"roll\" (dice), emitted directly by the Dice package, not routed\n // through this name map.\n gcd: 38, lcm: 39, permutation: 40, combination: 41,\n // 42-46 (average/median/total/count/proportion) are emitted directly by\n // the MathPhrases package's own phrase parselets, not routed through\n // this name map either.\n // 47 (clamp) is emitted via the CLAMP keyword's dedicated parselet.\n hex: 48, bin: 49, int: 50,\n // Finance (packages/finance/) function-call forms. See VMBuiltins.ts\n // indices 51-59 for the implementations. The phrase-grammar forms\n // (\"compound interest on ...\", \"monthly repayment on ...\", \"tax on ...\")\n // are hand-written parselets in packages/finance/parselets/, not routed\n // through this name map. See FinancePackage.ts.\n compoundinterest: 51, interestearned: 52,\n compoundinterestrate: 53, compoundinterestyears: 54,\n loanrepayment: 55, loaninterest: 56, monthlypayment: 57,\n taxadd: 58, taxremove: 59,\n // Inflation-adjusted value (packages/finance/) -- see VMBuiltins.ts\n // index 60. The present-year-relative phrase forms and the flat-rate\n // future-value projection are pluginFunctions instead, not reachable\n // via this map -- see InflationPluginFunctions.ts.\n inflationadjust: 60,\n // root(n, x) -- the n-th root of x. cbrt(x) above already covers n=3\n // specifically; this is the general form (Numi: `root n (x)`).\n root: 61,\n // fact(n) / factorial(n) -- both names accepted, same implementation.\n fact: 62, factorial: 62,\n // Matrix (packages/matrix/) -- transpose/det/inv/dot. Also reachable via\n // operator syntax (`^T`, `^-1` -- PrecedenceParser.ts's CARET special-\n // casing emits these SAME indices) and `|a|` (abs()'s Matrix branch,\n // index 1 above) -- see VMBuiltins.ts's own comment on indices 63-66.\n transpose: 63, det: 64, inv: 65, dot: 66,\n};\n\n/**\n * A named function call with parenthesised arguments.\n *\n * Resolves the name to a builtin index at parse time rather than dispatching on\n * a string at run time, which is also why the name-to-index map is exported:\n * map and reduce need the same resolution for a bare function argument.\n */\nexport class FunctionCallParselet implements PrefixParselet {\n\treadonly category = \"Function\";\n\tparse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n const fnName = token.value.toLowerCase();\n const fnIdx = builtinNameToIndex[fnName];\n if (fnIdx === undefined) {\n throw ErrorFactory.execution(\n 'UNKNOWN_FUNCTION',\n `Unknown function: ${fnName}`,\n { functionName: fnName }\n );\n }\n\n parser.consume(\"LPAREN\");\n\n let argCount = 0;\n if (parser.peek()?.type !== \"RPAREN\") {\n parser.parseExpression(BindingPower.Lowest, builder);\n argCount++;\n while (parser.match(\"COMMA\")) {\n parser.parseExpression(BindingPower.Lowest, builder);\n argCount++;\n }\n }\n\n parser.consume(\"RPAREN\");\n\n builder.emitOpcode(OpCode.CALL_BUILTIN);\n builder.emitIndex(fnIdx);\n builder.emitIndex(argCount);\n }\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { FunctionCallParselet } from \"./parselets/FunctionCallParselet\";\n\n/** Built-in function call syntax, e.g. `sqrt(2)`, `sin(pi)`, dispatches recognized function names to CALL_BUILTIN opcodes. */\nexport const FUNCTION_PACKAGE: IEnginePackage = {\n name: \"solve-function\",\n prefixParselets: {\n FUNC: new FunctionCallParselet(),\n },\n};\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * `now` / `today` (dayOffset 0), `tomorrow` (+1 day), `yesterday` (-1 day).\n *\n * Previously all four keywords shared one zero-offset implementation\n * \"tomorrow\"/\"yesterday\" silently evaluated to the exact same instant as\n * \"now\" (a real bug: `evaluate(\"tomorrow\") - evaluate(\"now\")` was 0, not\n * ~86400000ms). Existing tests never caught this because they only\n * checked internal consistency (e.g. `\"yesterday + 1 day\" - \"yesterday\"\n * === 1 day`), which holds regardless of what \"yesterday\" itself resolves\n * to. dayOffset must stay applied at evaluation time (ADD on top of\n * DATE_NOW), not baked in at parse time, so \"now\"/\"tomorrow\"/\"yesterday\"\n * stay relative to whenever the bytecode actually runs.\n *\n * The offset is emitted as a duration IN DAYS rather than as its length in\n * milliseconds. It used to be `dayOffset * 24 * 60 * 60 * 1000`, which is only\n * a day long when no daylight-saving transition falls inside it: on the day a\n * zone springs forward \"tomorrow\" was really 23 hours away and on the day it\n * falls back 25, so the answer was an hour out and could name the wrong\n * calendar day outright. Tagging the number with \"days\" hands the decision to\n * the ADD opcode, which steps the calendar day field instead (see\n * `vm/VM.ts`'s shiftDatetime()). The same `PUSH_NUMBER; PUSH_STRING;\n * UOM_CONVERT` trio any unit literal compiles to (see UomLiteralParselet).\n */\nexport class NowParselet implements PrefixParselet {\n\treadonly category = \"Date/Time\";\n\tprivate readonly dayOffset: number;\n\n\tconstructor(dayOffset: number = 0) {\n\t\tthis.dayOffset = dayOffset;\n\t}\n\n\tparse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n builder.emitOpcode(OpCode.DATE_NOW);\n if (this.dayOffset !== 0) {\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(this.dayOffset);\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(\"days\");\n builder.emitOpcode(OpCode.UOM_CONVERT);\n builder.emitOpcode(OpCode.ADD);\n }\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/** Weekday token type → JS Date.getDay() index (0=Sunday..6=Saturday). */\nconst WEEKDAY_TOKEN_INDEX: Record<string, number> = {\n\tSUNDAY: 0, MONDAY: 1, TUESDAY: 2, WEDNESDAY: 3, THURSDAY: 4, FRIDAY: 5, SATURDAY: 6,\n};\n\n/**\n * `next <Weekday>` / `last <Weekday>` (wiki: Datetime, \"next Saturday\"\n * \"last Monday\"), the actual next/previous occurrence of the named day\n * of the week, computed at evaluation time by {@link OpCode.DATE_NEXT_WEEKDAY}/\n * {@link OpCode.DATE_LAST_WEEKDAY} (must stay a VM opcode, not parse-time\n * arithmetic, \"now\" can only be read when the bytecode actually runs).\n */\nexport class NextLastParselet implements PrefixParselet {\n\treadonly category = \"Date/Time\";\n\tconstructor(private readonly direction: \"next\" | \"last\") {}\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n const dayToken = parser.peek();\n const targetDay = dayToken ? WEEKDAY_TOKEN_INDEX[dayToken.type] : undefined;\n if (targetDay === undefined) {\n throw ErrorFactory.parsing(\n \"MISSING_WEEKDAY\",\n `\"${token.value}\" must be followed by a day of the week (e.g. \"${token.value} Saturday\")`,\n { keyword: token.value, actualType: dayToken?.type, actualValue: dayToken?.value }\n );\n }\n parser.consume(); // consume the weekday token\n\n builder.emitOpcode(OpCode.DATE_NOW);\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(targetDay);\n builder.emitOpcode(this.direction === \"next\" ? OpCode.DATE_NEXT_WEEKDAY : OpCode.DATE_LAST_WEEKDAY);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * `<TimeUnit> until <Datetime>` / `<TimeUnit> since <Datetime>` (wiki:\n * Datetime, \"days until 25/12/23\", \"1 year since 1/1/22\"), the signed\n * elapsed span between now and a target datetime, expressed in the given\n * unit. \"until\" is positive for a future target (\"count down to\");\n * \"since\" is positive for a past target (\"count up from\").\n *\n * The fused token this parselet handles is produced by\n * {@link untilSinceNormalizerRule}, which merges a `UNIT` token followed by\n * `UNTIL`/`SINCE` into a single prefix token. This sidesteps the fact\n * that a bare `UNIT` token in prefix position is already claimed by\n * VariablesPackage's IdentifierParselet (`:days = 5`-style variable\n * lookups); fusing at the normalizer stage means that prefix dispatch\n * never sees a bare UNIT here at all, so there's no ParseletRegistry\n * collision between the two packages.\n *\n * Reuses the VM's existing Datetime-Datetime SUB semantics (which already\n * produce a `Uom(\"ms\")` duration. See VM.ts's SUB case) plus\n * UOM_CONVERT_IN to convert that duration into the requested unit, instead\n * of introducing new opcodes.\n */\nexport class UntilSinceParselet implements PrefixParselet {\n\treadonly category = \"Date/Time\";\n\tconstructor(private readonly direction: \"until\" | \"since\") {}\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n const unit = token.value; // the fused UNIT token's original text, e.g. \"days\"\n\n if (this.direction === \"until\") {\n parser.parseExpression(0, builder); // target datetime\n builder.emitOpcode(OpCode.DATE_NOW); // now\n } else {\n builder.emitOpcode(OpCode.DATE_NOW); // now\n parser.parseExpression(0, builder); // target datetime\n }\n // SUB pops (r, l) and computes l - r. Pushing [target, now] for\n // \"until\" makes SUB compute target - now (positive for a future\n // target). Pushing [now, target] for \"since\" makes SUB compute\n // now - target (positive for a past target).\n builder.emitOpcode(OpCode.SUB);\n\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(unit);\n builder.emitOpcode(OpCode.UOM_CONVERT_IN);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\n\n/**\n * `workdays in <duration>` (e.g. \"workdays in 3 weeks\") -> the number of\n * Mon-Fri workdays in that span, as a plain Number.\n *\n * Handles the fused `WORKDAYS_IN` token produced by the package's\n * `phrases` field (`\"workdays in\": \"WORKDAYS_IN\"`), fused as the FULL\n * two-word phrase rather than claiming bare \"workdays\" as its own keyword,\n * since \"workdays\" is a plausible variable name (same reasoning as\n * MathPhrasesPackage.ts's \"average\"/\"total\" note. See that file's doc\n * comment for the full regression story this avoids). The bare word\n * \"workdays\" itself only ever reaches this grammar as a UNIT token (added\n * to lexer/units.ts, for `N workdays` duration literals and `<date> + N\n * workdays` arithmetic. See vm/VM.ts's ADD/SUB special-casing), which\n * VariableParselet.ts already accepts for `:name = expr` syntax, so\n * \":workdays = 5\" is unaffected either way.\n *\n * SCOPE DECISION (see `DatetimeTimestampPluginFunctions.ts`'s\n * `workdaysInDurationHandler` doc comment for the full reasoning): the\n * count is a deterministic ratio (5 workdays per full week, remainder\n * capped at 5), NOT a real calendar walk anchored to \"now\". It is therefore\n * weekends-only and consults no holiday calendar: with no anchor date there\n * is no calendar day to look up. The forms that DO walk a real calendar (the\n * offsets and `between`) skip configured holidays; this counts a span with no\n * position on the calendar. See `DatetimePackage.ts`'s holiday scope note.\n */\nexport class WorkdaysInParselet implements PrefixParselet {\n readonly category = \"Date/Time\";\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(0, builder); // the duration expression, e.g. \"3 weeks\"\n builder.emitPluginCall(\"workdaysInDuration\", 1);\n }\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `N working/business days after|from|before <date>`, e.g. \"5 working days\n * after 20 Dec\" or \"3 business days from today\", the natural-language spelling\n * of a business-day offset.\n *\n * INFIX, not prefix: the count comes first in \"5 working days after ...\", so by\n * the time this runs the Pratt loop has already compiled `N` as the left\n * operand. Handles the fused `WORKDAYS_AFTER`/`WORKDAYS_BEFORE` tokens the\n * package's `phrases` field produces from the full three-word phrase (see\n * `DatetimePackage.ts`). Fusing the whole phrase (rather than claiming bare\n * \"working\"/\"business\") is what lets a note say \"5 working parts\" or\n * `:working = 5` untouched, the same reasoning MathPhrasesPackage.ts gives for\n * \"average\"/\"total\"; the fused phrase is also why the implicit-multiply rule\n * leaves the `N` in front of it alone (its guard skips phrase-start words).\n *\n * Emits `DATE_WORKDAY_OFFSET` with a one-byte direction, which walks the same\n * `addBusinessDays()` as `<date> + N workdays` (vm/VM.ts), so the two spellings\n * and the host holiday calendar can never disagree. \"after\"/\"from\" go forward,\n * \"before\" back; the anchor date is parsed at `Sum`, so a bare date term binds\n * as the anchor and any trailing `+ <duration>` applies to the offset result.\n */\nexport class WorkdayOffsetParselet implements InfixParselet {\n readonly category = \"Date/Time\";\n readonly bindingPower = BindingPower.Conditional;\n\n /** @param direction \"forward\" for after/from, \"backward\" for before. */\n constructor(private readonly direction: \"forward\" | \"backward\") {}\n\n parse(parser: Parser, _left: Token, _token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(BindingPower.Sum, builder); // the anchor date\n builder.emitOpcode(OpCode.DATE_WORKDAY_OFFSET);\n builder.emitByte(this.direction === \"backward\" ? 1 : 0);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `working/business days between <date> and <date>`, the count of working days\n * in that window (\"working days between 1 Jan and 31 Jan\").\n *\n * The working-day sibling of {@link DurationBetweenParselet}: same two-endpoint\n * shape, same \"and\" handling (see that file's note on why \"and\" lexes to\n * `AND_CONJ` and why the first endpoint is parsed at `Product` so it stops\n * before the conjunction), but the result is a plain count of working days\n * rather than a calendar-day span. Handles the fused `WORKDAYS_BETWEEN` token\n * from the package's `phrases` field, which consumes the whole \"working days\n * between\" so the bare `days between` rule never claims the \"days\".\n *\n * Emits `DATE_WORKDAYS_BETWEEN` (see vm/VM.ts), which counts inclusively of\n * both endpoints, order-independently, and skips weekends plus any host-\n * configured holidays. Count, not duration: a working-day total is a whole\n * number that no `Uom` span can carry without re-introducing the weekends it\n * excludes.\n */\nexport class WorkdaysBetweenParselet implements PrefixParselet {\n readonly category = \"Date/Time\";\n\n parse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(BindingPower.Product, builder); // first endpoint, stops before \"and\"\n parser.consume(\"AND_CONJ\"); // \"and\"\n parser.parseExpression(BindingPower.Lowest, builder); // second endpoint\n builder.emitOpcode(OpCode.DATE_WORKDAYS_BETWEEN);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * The natural-question forms over a single date field:\n *\n * - `\"on\"` mode, `what day is it on <date>` / `what month is it on <date>`\n * / `weekday on <date>`, plus the BARE form (`what day is it`) which\n * answers for right now.\n * - `\"in\"` mode, `what day is it in <duration>`, i.e. the field of\n * `now + <duration>`. `what day was it <duration> ago` is NOT this\n * parselet: \"ago\" is a trailing word with no token of its own, so the\n * backwards direction is spelled `what day is it in -30 days`, or\n * `weekday on now - 30 days`.\n *\n * One parameterized class rather than six near-identical ones, the field\n * differs only by which plugin function receives the epoch-ms, mirroring\n * how `NowParselet(offset)` and `NextLastParselet(direction)` already\n * parameterize instead of subclassing.\n *\n * The bare form is handled by peeking rather than by its own token type:\n * `what day is it` and `what day is it on` fuse to the SAME token (the\n * phrase trie is longest-match-wins, so the 4-word phrase claims the \"on\"\n * case), and an absent operand is then just \"no more tokens\".\n */\nexport class DateFieldQueryParselet implements PrefixParselet {\n readonly category = \"Date/Time\";\n\n constructor(\n private readonly functionName: string,\n private readonly mode: \"on\" | \"in\",\n ) {}\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n if (this.mode === \"in\") {\n builder.emitOpcode(OpCode.DATE_NOW);\n parser.parseExpression(0, builder); // the duration\n builder.emitOpcode(OpCode.ADD); // now + duration\n } else if (parser.peek()) {\n parser.parseExpression(0, builder); // the date expression\n } else {\n builder.emitOpcode(OpCode.DATE_NOW); // bare form — \"what day is it\"\n }\n\n builder.emitPluginCall(this.functionName, 1);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `<TimeUnit> between <Datetime> and <Datetime>`, the span between two\n * explicit dates, in the given unit (\"days between today and 25/12/2026\").\n *\n * The sibling of {@link UntilSinceParselet}, which only ever measures\n * against `now`; this is the two-explicit-endpoints form. Result is\n * unsigned, \"between\" has no direction in English the way until/since do\n * so the endpoints are ordered by value rather than by how they were\n * written, and `days between A and B` == `days between B and A`.\n *\n * Handles the fused `BETWEEN_UNIT` token from\n * {@link betweenUnitNormalizerRule}.\n *\n * Note \"and\" lexes to PLUS (`and: \"PLUS\"` in the locale keyword table\n * it's the word form of addition, as in \"1 and 2\"). So the first endpoint\n * must be parsed at a binding power that stops before it, exactly as\n * `ClampParselet` does for `clamp X between Y and Z`.\n */\nexport class DurationBetweenParselet implements PrefixParselet {\n readonly category = \"Date/Time\";\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n const unit = token.value; // the fused UNIT token's original text, e.g. \"days\"\n\n parser.parseExpression(BindingPower.Product, builder); // first endpoint\n parser.consume(\"AND_CONJ\"); // \"and\"\n parser.parseExpression(BindingPower.Lowest, builder); // second endpoint\n\n // An unsigned Uom(\"ms\") span, a plugin function rather than SUB, since\n // there is no ABS opcode and \"between\" must not depend on write order.\n builder.emitPluginCall(\"spanBetweenDates\", 2);\n\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(unit);\n builder.emitOpcode(OpCode.UOM_CONVERT_IN);\n }\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `<date> is a weekend` / `<date> is a workday` / `<date> is a weekday`\n * -> Boolean.\n *\n * A POSTFIX operator, an `InfixParselet` that parses no right operand, so\n * the already-emitted left-hand date is the only thing on the stack when\n * the plugin call runs. Structurally identical to `ToTimestampParselet`,\n * which is the same shape (`<date> to timestamp`) and uses the same\n * binding power.\n *\n * Written as a postfix rather than a prefix `is <date> a weekend` because\n * the prefix form would need \"is\" as a bare keyword, and \"is\" is far too\n * common a word to claim globally, the same objection that makes every\n * other phrase in this package fuse its full text.\n */\nexport class DayTypePredicateParselet implements InfixParselet {\n readonly category = \"Date/Time\";\n readonly bindingPower = BindingPower.Conditional;\n\n constructor(private readonly functionName: string) {}\n\n parse(parser: Parser, left: Token, token: Token, builder: BytecodeBuilder): void {\n builder.emitPluginCall(this.functionName, 1);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * `current timestamp` -> the current Unix timestamp in seconds, as a\n * plain Number.\n *\n * Handles the fused `CURRENT_TIMESTAMP` token, \"current\"/\"timestamp\" are\n * both plausible variable names, so this is fused as the full two-word\n * phrase (via the package's `phrases` field) rather than claiming either\n * bare word as its own keyword, same reasoning as `WorkdaysInParselet.ts`.\n *\n * Emits a fresh `DATE_NOW` (so \"current timestamp\" is always the instant\n * the bytecode actually runs, not a parse-time constant, matches\n * `NowParselet.ts`'s existing \"now\"/\"today\" convention) followed by the\n * SAME `TO_TIMESTAMP_FN_IDX` handler `ToTimestampParselet.ts` uses for\n * `<date> to timestamp`, \"current timestamp\" is exactly \"now to\n * timestamp\" under the hood, so there is no separate conversion logic to\n * duplicate.\n */\nexport class CurrentTimestampParselet implements PrefixParselet {\n readonly category = \"Date/Time\";\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n builder.emitOpcode(OpCode.DATE_NOW);\n builder.emitPluginCall(\"toTimestampFromAny\", 1);\n }\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `<ISO8601 string> to date` / `<unix timestamp> to date` -> a Datetime\n * value.\n *\n * Handles the fused `TO_DATE` token, produced by the package's `phrases`\n * field (`\"to date\": \"TO_DATE\"`), fusing the full \"to date\" phrase means\n * this does NOT compete with the bare `TO` infix token, which\n * `PercentageChangeParselet.ts` already owns (percentage change / UoM\n * conversion), `ParseletRegistry` only allows ONE parselet per token\n * type, so claiming bare `TO` here would silently overwrite that\n * registration (see `ParseletRegistry.ts`'s collision-warning doc\n * comment). Because phrase fusion happens at the normalizer stage, BEFORE\n * parsing ever sees a `TO` token in this position, there's no such\n * collision, \"to date\" is a completely different token by the time the\n * parser runs, at zero risk to \"5 to 10\" or \"$5 to USD\".\n *\n * The left-hand value is already on the stack by the time an infix\n * parselet runs (the Pratt loop parsed it already). This just applies\n * `TO_DATE_FN_IDX`'s runtime type-dispatch (String/Number/Datetime) on\n * top of it. See `DatetimeTimestampPluginFunctions.ts`'s\n * `toDateFromAnyHandler` doc comment for the exact dispatch rules\n * (including the seconds-vs-milliseconds magnitude heuristic).\n */\nexport class ToDateParselet implements InfixParselet {\n readonly category = \"Date/Time\";\n readonly bindingPower = BindingPower.Conditional;\n\n parse(parser: Parser, left: Token, token: Token, builder: BytecodeBuilder): void {\n builder.emitPluginCall(\"toDateFromAny\", 1);\n }\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `<date/time> to timestamp` -> a Unix timestamp in seconds, as a plain\n * Number.\n *\n * Handles the fused `TO_TIMESTAMP` token (`\"to timestamp\": \"TO_TIMESTAMP\"`\n * in the package's `phrases` field). See `ToDateParselet.ts`'s doc\n * comment for why fusing the full phrase (rather than claiming bare `TO`)\n * is required to avoid colliding with `PercentageChangeParselet.ts`'s\n * existing ownership of the bare `TO` token.\n */\nexport class ToTimestampParselet implements InfixParselet {\n readonly category = \"Date/Time\";\n readonly bindingPower = BindingPower.Conditional;\n\n parse(parser: Parser, left: Token, token: Token, builder: BytecodeBuilder): void {\n builder.emitPluginCall(\"toTimestampFromAny\", 1);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * Pushes a datetime literal (25/12/2023, 12-25-2023, 2023-12-25, 25.12.2023)\n * whose epoch-ms was already computed by {@link dateLiteralNormalizerRule}\n * during token fusion. This parselet just emits the constant.\n */\nexport class DateLiteralParselet implements PrefixParselet {\n\treadonly category = \"Date/Time\";\n\tparse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n builder.emitOpcode(OpCode.DATE_LITERAL);\n builder.emitNumber(Number(token.value));\n }\n}\n","/**\n * ISO8601 string <-> epoch-ms helpers backing the datetime package's\n * `<ISO8601 string> to date` and `<date/time> as iso8601` features.\n *\n * SCOPE, revised in 1.0.0: {@link parseIso8601} is now reached by BOTH input\n * shapes. A quoted string literal (`\"2019-04-01T15:30:00+11:00\" to date`)\n * always came here; the bare, unquoted form now does too, fused into one\n * DATETIME_LITERAL by `normalizer/DateLiteralNormalizerRule.ts` before the\n * parser sees it.\n *\n * This file used to record that the bare form was out of scope because it is\n * ambiguous with arithmetic (`2019 - 04 - 01` reads as two subtractions) and\n * would need a dedicated lexer change. The date half of that ambiguity was\n * already settled by the normalizer rule, and the differential run against\n * 1.0.0-beta.6 showed what leaving the other half cost: a bare\n * `2019-04-01T15:30:00+11:00` read its `+11:00` as a clock time being ADDED to\n * the date, and answered TODAY at 11:00. Not the instant, not an error, and\n * formatted as a date, which is the most convincing way to be wrong. The two\n * spellings now answer identically, which is the only defensible arrangement\n * when one of them was always right.\n */\n\n/**\n * Matches a (reasonably) well-formed ISO8601 date or date-time string:\n * `YYYY-MM-DD` optionally followed by `THH:MM[:SS[.fff]][Z|+HH:MM|-HH:MM]`.\n * Deliberately stricter than the native `Date` constructor's very lenient\n * parsing (which also accepts things like `\"April 1, 2019\"`), only a\n * string actually ISO8601-shaped is accepted here, so `\"not a date\" to\n * date` fails loudly instead of silently parsing as `Invalid Date` (or,\n * worse, something the native parser guesses at).\n */\nconst ISO8601_PATTERN =\n /^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:?\\d{2})?)?$/;\n\n/**\n * Parse an ISO8601-shaped string into epoch milliseconds.\n *\n * Delegates the actual parsing to the native `Date` constructor, which\n * fully implements the ISO8601 extended format (including \"Z\"/\"+HH:MM\"\n * offsets and a fractional-seconds component) per the ECMA-262 Date Time\n * String Format spec, no custom parsing logic needed, just a stricter\n * shape gate in front of it (see {@link ISO8601_PATTERN}'s doc comment).\n *\n * A date-only string (`\"2019-04-01\"`) parses as UTC midnight; a date-time\n * with no offset and no \"Z\" (`\"2019-04-01T15:30:00\"`) parses in the HOST\n * SYSTEM's local timezone, both are the native `Date` constructor's own\n * spec-mandated behavior, not something this function adds.\n *\n * @returns epoch ms, or `null` if `s` isn't ISO8601-shaped or doesn't\n * parse to a valid instant.\n */\nexport function parseIso8601(s: string): number | null {\n // A `ValueType.String` Value's `.value` used to retain its original\n // surrounding double-quotes, because `tokenizeString()` set the token's\n // value to the raw source slice and `OpCode.PUSH_STRING` pushed that\n // verbatim. It no longer does: the token's `value` is the payload and its\n // `text` is the quoted slice. The strip below stays because this function\n // is also reachable with text that came from somewhere other than a\n // literal, and stripping a pair that is not there is a no-op.\n let trimmed = s.trim();\n if (trimmed.length >= 2 && trimmed.startsWith('\"') && trimmed.endsWith('\"')) {\n trimmed = trimmed.slice(1, -1);\n }\n if (!ISO8601_PATTERN.test(trimmed)) return null;\n const ms = new Date(trimmed).getTime();\n return Number.isNaN(ms) ? null : ms;\n}\n\n/**\n * Format epoch milliseconds as an ISO8601 string with an explicit\n * `+HH:MM`/`-HH:MM` offset, e.g. `\"2019-04-01T15:30:00+11:00\"`.\n *\n * DESIGN NOTE: uses the HOST SYSTEM's local timezone offset for the\n * offset component. This engine's `Datetime` representation is a bare\n * epoch-ms number with no zone tag (see `packages/time/TimePackage.ts`'s\n * doc comment for the same limitation affecting timezone conversion), so\n * there is no per-expression timezone context to draw on otherwise, the\n * process's own local offset (`Date.getTimezoneOffset()`) is the only\n * notion of \"local\" available. No milliseconds component is emitted,\n * matching the precision of the task's own worked example.\n */\nexport function formatIso8601Local(epochMs: number): string {\n const d = new Date(epochMs);\n const pad = (n: number) => String(n).padStart(2, \"0\");\n\n const year = d.getFullYear();\n const month = pad(d.getMonth() + 1);\n const day = pad(d.getDate());\n const hours = pad(d.getHours());\n const minutes = pad(d.getMinutes());\n const seconds = pad(d.getSeconds());\n\n // getTimezoneOffset() is (UTC - local) in minutes, i.e. the SIGN is\n // inverted relative to a \"+HH:MM ahead of UTC\" display offset.\n const offsetMinutesTotal = -d.getTimezoneOffset();\n const sign = offsetMinutesTotal >= 0 ? \"+\" : \"-\";\n const absOffset = Math.abs(offsetMinutesTotal);\n const offsetHours = pad(Math.floor(absOffset / 60));\n const offsetMinutes = pad(absOffset % 60);\n\n return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}${sign}${offsetHours}:${offsetMinutes}`;\n}\n\n/**\n * Magnitude threshold distinguishing a millisecond Unix timestamp from a\n * second Unix timestamp: values at or above this are treated as\n * milliseconds, below it as seconds.\n *\n * Chosen so that \"now\" in EITHER unit sits comfortably on its own side:\n * seconds-since-epoch for the foreseeable future stays in the ~1.7-2.5\n * billion range (1e9-1e10-ish), nowhere near 1e12, while the SAME\n * instant in milliseconds is ~1000x larger, comfortably past 1e12\n * (e.g. \"2024-12-10\" is ~1.73e9 seconds or ~1.73e12 ms). 1e12 sits well\n * clear of both real-world ranges for at least the next few centuries of\n * second-timestamps, so this is a safe, simple magnitude gate rather than\n * a fragile exact boundary.\n */\nconst MS_TIMESTAMP_THRESHOLD = 1e12;\n\n/**\n * Interpret a bare numeric Unix timestamp as epoch milliseconds, applying\n * the magnitude-based seconds-vs-milliseconds disambiguation described by\n * {@link MS_TIMESTAMP_THRESHOLD}.\n */\nexport function unixTimestampToEpochMs(n: number): number {\n return Math.abs(n) >= MS_TIMESTAMP_THRESHOLD ? n : n * 1000;\n}\n","import { Value, ValueType, numberValue, stringValue, boolValue, uomValue, datetimeValue, errorValue } from \"@solve-js/vm/Value\";\nimport { convertUnit, getMeasure } from \"@solve-js/uom/UomConverter\";\nimport { parseIso8601, unixTimestampToEpochMs } from \"../Iso8601\";\n\n/**\n * `CALL_PLUGIN` handlers backing the datetime package's workdays/weekday/\n * timestamp features (see `DatetimePackage.ts`). Grouped in one file since\n * they're all small, single-purpose Value -> Value functions with no\n * shared state, mirrors `time/parselets/TimezonePluginFunctions.ts`'s\n * organization for the same kind of grouping.\n */\n\n/**\n * `workdays in <duration>` -> the number of Mon-Fri workdays in that span,\n * as a plain Number.\n *\n * SCOPE DECISION (documented here and in `WorkdaysInParselet.ts`): no\n * anchor date is specified by this phrase's grammar at all, \"workdays in\n * 3 weeks\" doesn't say \"starting when\", so this is computed via a pure\n * deterministic ratio (5 workdays per full 7-day week, plus the remainder\n * capped at 5) rather than walking an actual calendar from \"now\". That\n * would make the result depend on which day of the week \"now\" happens to\n * be when evaluated, a non-deterministic, hard-to-test result for the\n * exact same input expression. This IS anchor-independent and exact for\n * any whole-week span (3 weeks = 21 days = exactly 15 workdays, regardless\n * of start day); for a partial-week remainder it's a reasonable capped\n * approximation, not a real calendar walk. Weekends-only by nature: with no\n * anchor date there is no calendar day to test against a holiday calendar, so\n * unlike the offset/`between` forms this consults none. See\n * `DatetimePackage.ts`'s holiday scope note.\n */\nfunction workdaysInDurationHandler(args: Value[]): Value {\n const v = args[0];\n let totalDays: number;\n\n if (v.type === ValueType.Uom && v.unit) {\n const measure = getMeasure(v.unit);\n if (measure !== \"time\") {\n return errorValue(\n \"WORKDAYS_IN_EXPECTED_DURATION\",\n `\"workdays in\" expects a time duration, got unit \"${v.unit}\"`\n );\n }\n totalDays = convertUnit(v.toNumber(), v.unit, \"day\");\n } else {\n // A bare Number is treated as already being a count of days.\n totalDays = v.toNumber();\n }\n\n // Counted on the magnitude, with the sign put back afterwards. Running\n // the ratio directly on a negative day count borrowed a whole week from\n // Math.floor() and then handed the borrowed days back through the\n // remainder, so \"workdays in -3 days\" answered -1 instead of -3 and\n // \"workdays in -10 days\" answered -6 instead of -8. A negative span\n // reaches here whenever the duration came from a variable or a\n // subtraction rather than a typed literal.\n const sign = totalDays < 0 ? -1 : 1;\n const magnitudeDays = Math.abs(totalDays);\n const fullWeeks = Math.floor(magnitudeDays / 7);\n const remainderDays = magnitudeDays - fullWeeks * 7;\n const workdays = sign * (fullWeeks * 5 + Math.min(remainderDays, 5));\n return numberValue(workdays);\n}\n\nconst WEEKDAY_NAMES = [\n \"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\",\n];\n\n/**\n * Guards the date-field extractors below (weekday/month/week/is-a-weekend).\n *\n * The grammar-driven call sites always build a Datetime by construction,\n * but the `as` converter forms don't: `<anything> as week` reaches the same\n * handler, so `90 days as week` would otherwise read a duration's raw ms as\n * if it were an epoch and answer \"week 1\" with a straight face. Returning\n * an error instead is the difference between a wrong answer and a\n * diagnosable one.\n */\nfunction asEpochMs(value: Value, fieldName: string): number | Value {\n if (value.type === ValueType.Datetime) return value.toNumber();\n return errorValue(\n \"DATE_FIELD_EXPECTED_DATE\",\n `\"${fieldName}\" expects a date, got ${ValueType[value.type] ?? \"an unsupported value\"}`\n );\n}\n\n/**\n * `day of the week on <date>` / `what day is it in <duration>` /\n * `<date> as weekday` -> the weekday name (e.g. \"Tuesday\") as a String.\n */\nfunction weekdayOnDateHandler(args: Value[]): Value {\n const epochMs = asEpochMs(args[0], \"weekday\");\n if (typeof epochMs !== \"number\") return epochMs;\n return stringValue(WEEKDAY_NAMES[new Date(epochMs).getDay()]);\n}\n\nconst MONTH_NAMES = [\n \"January\", \"February\", \"March\", \"April\", \"May\", \"June\",\n \"July\", \"August\", \"September\", \"October\", \"November\", \"December\",\n];\n\n/**\n * `what month is it on <date>` / `<date> as month` -> the month name\n * (e.g. \"December\") as a String value.\n *\n * English-only, exactly like {@link WEEKDAY_NAMES} directly above, the\n * locale-aware path is `format/FormatEngine.ts`, which is what renders a\n * whole Datetime; this returns a bare String field extracted from one, and\n * matching the established weekday behaviour beats having the two\n * neighbouring fields disagree about localization.\n */\nfunction monthOnDateHandler(args: Value[]): Value {\n const epochMs = asEpochMs(args[0], \"month\");\n if (typeof epochMs !== \"number\") return epochMs;\n return stringValue(MONTH_NAMES[new Date(epochMs).getMonth()]);\n}\n\n/**\n * `what week is it on <date>` / `<date> as week` -> the ISO-8601 week\n * number (1-53) as a plain Number.\n *\n * ISO weeks start on Monday and week 1 is the one containing the first\n * Thursday of the year, which is why this shifts to the Thursday of the\n * target's week before counting. A naive \"day-of-year / 7\" would disagree\n * with every calendar app for the first and last days of a year.\n */\nfunction weekOnDateHandler(args: Value[]): Value {\n const epochMs = asEpochMs(args[0], \"week\");\n if (typeof epochMs !== \"number\") return epochMs;\n const d = new Date(epochMs);\n // Work in UTC on a date-only copy so a local-time hour can't shift the day.\n const target = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));\n // getUTCDay(): Sunday=0. Map to ISO's Monday=1..Sunday=7, then step to Thursday.\n const isoDay = target.getUTCDay() === 0 ? 7 : target.getUTCDay();\n target.setUTCDate(target.getUTCDate() + 4 - isoDay);\n const yearStart = Date.UTC(target.getUTCFullYear(), 0, 1);\n const days = Math.floor((target.getTime() - yearStart) / 86_400_000);\n return numberValue(Math.floor(days / 7) + 1);\n}\n\n/** True for Saturday/Sunday. */\nfunction isWeekendDate(epochMs: number): boolean {\n const day = new Date(epochMs).getDay();\n return day === 0 || day === 6;\n}\n\n/** `<date> is a weekend` -> Boolean. */\nfunction isWeekendOnDateHandler(args: Value[]): Value {\n const epochMs = asEpochMs(args[0], \"is a weekend\");\n if (typeof epochMs !== \"number\") return epochMs;\n return boolValue(isWeekendDate(epochMs));\n}\n\n/**\n * `<date> is a workday` / `is a weekday` -> Boolean.\n *\n * Mon-Fri only, and deliberately blind to the holiday calendar: this answers\n * \"is this a weekday\", a question about the week's shape, not \"is this a\n * working day in my region\". Keeping it weekend-based means the predicate is a\n * pure function of the date, decidable without any host configuration, and\n * `is a workday` stays the exact complement of `is a weekend`. The working-day\n * ARITHMETIC (offsets and `between`) is what consults the calendar. See\n * `DatetimePackage.ts`'s holiday scope note.\n */\nfunction isWorkdayOnDateHandler(args: Value[]): Value {\n const epochMs = asEpochMs(args[0], \"is a workday\");\n if (typeof epochMs !== \"number\") return epochMs;\n return boolValue(!isWeekendDate(epochMs));\n}\n\n/**\n * `<unit> between <date> and <date>` -> the UNSIGNED span between the two\n * endpoints as a `Uom(\"ms\")`, which the caller then converts into the\n * requested unit via `UOM_CONVERT_IN` (identical to how\n * `UntilSinceParselet` feeds that opcode).\n *\n * A plugin function rather than a `SUB`: \"between\" has no direction in\n * English, so `days between A and B` must equal `days between B and A`,\n * and there is no ABS opcode to apply after a signed subtraction.\n * Arguments arrive in push order, so `args[0]` is the first endpoint as\n * written, though by construction the result doesn't depend on that.\n */\nfunction spanBetweenDatesHandler(args: Value[]): Value {\n return uomValue(Math.abs(args[0].toNumber() - args[1].toNumber()), \"ms\");\n}\n\n/**\n * `<ISO8601 string> to date` / `<unix timestamp> to date` -> a Datetime\n * value. Dispatches on the RUNTIME value type (a `CALL_PLUGIN` handler\n * runs at VM-execution time, after the left-hand expression has already\n * been evaluated to a concrete Value) rather than at parse time, since the\n * left-hand expression could be a string literal, a bare number literal,\n * or an arbitrary variable/expression of either type.\n *\n * Real ambiguity resolved here: a bare Number could be a SECONDS or a\n * MILLISECONDS Unix timestamp for the same real-world date. See\n * `Iso8601.ts`'s `unixTimestampToEpochMs()`/`MS_TIMESTAMP_THRESHOLD` doc\n * comment for the exact magnitude threshold and reasoning.\n */\nfunction toDateFromAnyHandler(args: Value[]): Value {\n const v = args[0];\n\n if (v.type === ValueType.Datetime) {\n return v; // already a date — \"date to date\" is a harmless no-op\n }\n\n if (v.type === ValueType.String) {\n const ms = parseIso8601(v.value as string);\n if (ms === null) {\n return errorValue(\n \"INVALID_ISO8601_STRING\",\n `\"${v.value}\" is not a recognizable ISO8601 date/time string`\n );\n }\n return datetimeValue(ms);\n }\n\n return datetimeValue(unixTimestampToEpochMs(v.toNumber()));\n}\n\n/**\n * `<date/time> to timestamp` / `current timestamp` -> a Unix timestamp in\n * SECONDS (rounded down), as a plain Number.\n *\n * Also accepts a String (parsed as ISO8601 first) for robustness, though\n * the primary grammar this backs (`ToTimestampParselet.ts`) always feeds\n * it an already-Datetime-typed left-hand expression; `CurrentTimestampParselet.ts`\n * feeds it a fresh `DATE_NOW` result via the same opcode, reusing this one\n * handler for both call sites.\n */\nfunction toTimestampFromAnyHandler(args: Value[]): Value {\n const v = args[0];\n\n if (v.type === ValueType.String) {\n const ms = parseIso8601(v.value as string);\n if (ms === null) {\n return errorValue(\n \"INVALID_ISO8601_STRING\",\n `\"${v.value}\" is not a recognizable ISO8601 date/time string`\n );\n }\n return numberValue(Math.floor(ms / 1000));\n }\n\n // Datetime (epoch ms) or a plain Number already representing epoch ms.\n return numberValue(Math.floor(v.toNumber() / 1000));\n}\n\n/**\n * Count the Mon-Fri workdays in a duration.\n *\n * Uses a deterministic ratio rather than walking a calendar, because the\n * phrase carries no anchor date. See the handler for the full reasoning.\n */\nexport const workdaysInDuration = workdaysInDurationHandler;\n/**\n * Day-of-week name for a date, as a String value.\n */\nexport const weekdayOnDate = weekdayOnDateHandler;\n/**\n * Month name for a date, as a String value.\n */\nexport const monthOnDate = monthOnDateHandler;\n/**\n * ISO week number for a date, as a Number value.\n */\nexport const weekOnDate = weekOnDateHandler;\n/**\n * Whether a date falls on a Saturday or Sunday.\n */\nexport const isWeekendOnDate = isWeekendOnDateHandler;\n/**\n * Whether a date falls Monday to Friday. The complement of\n * {@link isWeekendOnDate}, with no holiday calendar involved.\n */\nexport const isWorkdayOnDate = isWorkdayOnDateHandler;\n/**\n * Elapsed time between two dates, as a duration value.\n */\nexport const spanBetweenDates = spanBetweenDatesHandler;\n/**\n * Coerce a Unix timestamp, ISO 8601 string or epoch number to a\n * Datetime.\n */\nexport const toDateFromAny = toDateFromAnyHandler;\n/**\n * Coerce a date to whole Unix seconds. The inverse of\n * {@link toDateFromAny}.\n */\nexport const toTimestampFromAny = toTimestampFromAnyHandler;\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\n/**\n * Fuses `UNIT UNTIL` → a single `UNTIL_UNIT` prefix token, and\n * `UNIT SINCE` → a single `SINCE_UNIT` prefix token, carrying the unit\n * name as the fused token's value (e.g. \"days\").\n *\n * Why fuse instead of registering UNTIL/SINCE as infix parselets: a bare\n * `UNIT` token in prefix position (e.g. the \"days\" in \"days until ...\")\n * is already claimed by VariablesPackage's IdentifierParselet, which\n * treats it as a variable reference. By the time an infix parselet for\n * UNTIL/SINCE would run, the parser has already emitted (wrong) bytecode\n * for that bare UNIT as a variable lookup. Fusing at the normalizer stage\n *, before parsing starts, means prefix dispatch never sees a bare UNIT\n * in this position at all, avoiding any collision between the two\n * packages' UNIT handling.\n */\nexport function untilSinceNormalizerRule(priority = 60): NormalizerRule {\n return {\n name: \"datetime:until-since\",\n priority,\n match(tokens, pos): NormalizerMatch | null {\n if (pos + 1 >= tokens.length) return null;\n const unitToken = tokens[pos];\n const keywordToken = tokens[pos + 1];\n if (unitToken.type !== \"UNIT\") return null;\n if (keywordToken.type !== \"UNTIL\" && keywordToken.type !== \"SINCE\") return null;\n\n const fusedType = keywordToken.type === \"UNTIL\" ? \"UNTIL_UNIT\" : \"SINCE_UNIT\";\n return {\n consumed: 2,\n replacement: [createFusedToken(fusedType, unitToken.value, [unitToken, keywordToken])],\n ruleName: `datetime:${keywordToken.type.toLowerCase()}`,\n };\n },\n };\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\n/**\n * Fuses `UNIT BETWEEN` → a single `BETWEEN_UNIT` prefix token carrying the\n * unit name as its value (e.g. \"days\"), so `days between <a> and <b>`\n * parses.\n *\n * Same rationale as {@link untilSinceNormalizerRule}, which this mirrors\n * exactly: a bare `UNIT` in prefix position is already claimed by\n * VariablesPackage's IdentifierParselet, so the collision has to be\n * removed before parsing starts rather than resolved during it.\n *\n * Also swallows a preceding `how many` (`how many days between ...`,\n * `how many days until ...`). That reads as one phrase to a human but is\n * two ordinary identifier tokens to the lexer, and fusing them here, only\n * ever directly in front of a unit-plus-connector, keeps \"how\" and \"many\"\n * from becoming keywords that would shadow variables named either.\n */\nexport function betweenUnitNormalizerRule(priority = 60): NormalizerRule {\n return {\n name: \"datetime:between-unit\",\n priority,\n match(tokens, pos): NormalizerMatch | null {\n // Optional leading \"how many\".\n let start = pos;\n let howManyLength = 0;\n if (\n tokens[pos]?.value?.toLowerCase() === \"how\" &&\n tokens[pos + 1]?.value?.toLowerCase() === \"many\"\n ) {\n start = pos + 2;\n howManyLength = 2;\n }\n\n const unitToken = tokens[start];\n const keywordToken = tokens[start + 1];\n if (!unitToken || !keywordToken) return null;\n if (unitToken.type !== \"UNIT\") return null;\n\n // \"how many days until X\" reuses the existing UNTIL_UNIT/SINCE_UNIT\n // tokens. This rule only has to drop the \"how many\" for those, since\n // untilSinceNormalizerRule then sees a plain `UNIT UNTIL` pair.\n const isBetween = keywordToken.type === \"BETWEEN\";\n const isUntilSince = keywordToken.type === \"UNTIL\" || keywordToken.type === \"SINCE\";\n if (!isBetween && !isUntilSince) return null;\n if (isUntilSince && howManyLength === 0) return null; // nothing for this rule to do\n\n const sources = tokens.slice(pos, start + 2);\n if (isUntilSince) {\n // Re-emit the pair untouched, minus the \"how many\".\n return {\n consumed: howManyLength + 2,\n replacement: [unitToken, keywordToken],\n ruleName: \"datetime:how-many\",\n };\n }\n\n return {\n consumed: howManyLength + 2,\n replacement: [createFusedToken(\"BETWEEN_UNIT\", unitToken.value, sources)],\n ruleName: \"datetime:between\",\n };\n },\n };\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { tokenTypeId } from \"@solve-js/lexer/Token\";\n\n/**\n * `$500/workday` -> implicitly `$500 / 1 workday`, by inserting a synthetic\n * `NUMBER \"1\"` token between the `/` and a bare `workday`/`workdays` UNIT\n * token.\n *\n * WHY THIS IS NEEDED: a bare unit word with no preceding number (e.g. the\n * \"workday\" in \"$500/workday\") does NOT parse as `Uom(1, \"workday\")` in\n * this codebase today, `UNIT` tokens in PREFIX position (i.e. not\n * immediately preceded by a number) are claimed by VariablesPackage's\n * `IdentifierParselet`, which treats a bare unit word as a variable\n * lookup (`LOAD_VAR \"workday\"`), not an implicit-1 UoM literal. This is a\n * PRE-EXISTING gap in the codebase, not something new to workdays, there\n * is no general \"$X/unit\" bare-denominator Rate literal anywhere yet\n * (confirmed: no existing test or parselet constructs a Rate this way).\n *\n * SCOPE DECISION: this rule deliberately fixes ONLY `/workday` and\n * `/workdays`, the exact syntax this task's \"$500/workday x 4 weeks\"\n * example needs, rather than generalizing to \"$X/<any unit>\". A fully\n * general bare-unit-denominator fix is a separate, broader change (it\n * would need to touch the shared `UNIT`-in-prefix-position dispatch that\n * every package relies on) and is out of scope here.\n *\n * Runs as a normalizer rule (not a phrase, since `phrases` fuses on WORD\n * text and \"/\" is a symbol token) that recognizes `SLASH` immediately\n * followed by a `UNIT` token whose text is \"workday\"/\"workdays\", and\n * REPLACES those 2 tokens with 3 (`SLASH`, a synthetic `NUMBER \"1\"`, the\n * original `UNIT` token unchanged), `NormalizerMatch.replacement` is\n * explicitly allowed to be longer than `consumed` tokens (see\n * `NormalizerRule.ts`'s doc comment: \"expansion/splitting\"). By the time\n * the parser runs, \"$500/workday\" and \"$500/1 workday\" are indistinguishable\n * token streams, so no parser/VM change is needed at all, the existing\n * `DIV` -> `RATE_DIV`-equivalent construction path\n * (`vm/VM.ts`'s `OpCode.DIV` case) handles it exactly like any other\n * explicit \"$X / N unit\" Rate literal.\n */\nexport function workdayRateDenominatorNormalizerRule(priority = 70): NormalizerRule {\n return {\n name: \"datetime:workday-rate-denominator\",\n priority,\n match(tokens, pos): NormalizerMatch | null {\n const slash = tokens[pos];\n if (slash.type !== \"SLASH\") return null;\n\n const unit = tokens[pos + 1];\n if (!unit || unit.type !== \"UNIT\") return null;\n if (unit.value !== \"workday\" && unit.value !== \"workdays\") return null;\n\n // A synthetic NUMBER \"1\" token, positioned/spanned at the unit\n // token's own source location (there's no real source span for a\n // number that was never actually typed), mirrors\n // normalizer/TokenNormalizer.ts's createFusedToken() for how a\n // synthesized token's offset/line/col are derived from an existing\n // one, just without consuming that token in the process (this rule\n // EXPANDS 2 tokens into 3, it doesn't fuse them into fewer).\n const syntheticOne = {\n ...unit,\n type: \"NUMBER\",\n typeId: tokenTypeId(\"NUMBER\"),\n value: \"1\",\n text: \"1\",\n };\n\n return {\n consumed: 2,\n replacement: [slash, syntheticOne, unit],\n ruleName: \"datetime:workday-rate-denominator\",\n };\n },\n };\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport { tokenTypeId } from \"@solve-js/lexer/Token\";\nimport { LexerToken } from \"@solve-js/lexer/ExpressionLexer\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { parseIso8601 } from \"@solve-js/packages/datetime/Iso8601\";\n\n/**\n * Token type a fused date literal is rewritten to.\n *\n * One type covers every supported ordering, so the parselet handles the shape\n * rather than the locale.\n */\nexport const DATETIME_LITERAL_TYPE = \"DATETIME_LITERAL\";\nconst DATETIME_LITERAL_TYPE_ID = tokenTypeId(DATETIME_LITERAL_TYPE);\n\n/** A pure digit string, guards against fusing hex/scientific/bigint-suffixed NUMBER tokens. */\nconst PLAIN_INTEGER = /^\\d+$/;\n\n/** First NUMBER token of a dot-separated literal: the lexer's decimal scanner\n * merges \"DD.MM\" into one float-shaped token (see module doc below). */\nconst DOT_DAY_MONTH = /^\\d{1,2}\\.\\d{1,2}$/;\n\n/** Second NUMBER token of a dot-separated literal: the lexer re-enters\n * tokenizeNumber() AT the second dot, so the year keeps its leading dot. */\nconst DOT_LEADING_YEAR = /^\\.(\\d{2}|\\d{4})$/;\n\n/**\n * Day and month groups of a separator-separated literal, in any of the three\n * orderings below: one or two digits, padded or not.\n *\n * Deliberately NOT two digits exactly. Zero-padding is what ISO 8601 requires\n * of a serialized date, but it is not what people type: \"2024-5-3\" is an\n * ordinary way to write a date and has to keep working. The digit count is\n * still bounded because a group of three or more digits is not a date group at\n * all, and the calendar check in {@link buildDateToken} would not catch it on\n * its own (\"030\" reads back as day 30).\n *\n * What actually separates a date from the arithmetic it is spelled like is\n * adjacency, not padding. See {@link writtenAsOneRun}.\n */\nconst DAY_OR_MONTH = /^\\d{1,2}$/;\n\n/**\n * Whether every token in a run touches the next one in the source text, with\n * no whitespace anywhere between the first character and the last.\n *\n * This is the whole basis for telling a date literal apart from the\n * arithmetic it is spelled identically to. A date is written as one\n * uninterrupted run (\"2024-5-3\"); a subtraction chain is written with spaces\n * around its operators (\"2024 - 5 - 3\"). The lexer records that difference in\n * the token offsets and nowhere else, since the tokens themselves are the same\n * five either way:\n *\n * 2024-5-3 NUMBER@0 MINUS@4 NUMBER@5 MINUS@6 NUMBER@7\n * 2024 - 5 - 3 NUMBER@0 MINUS@5 NUMBER@7 MINUS@9 NUMBER@11\n *\n * The same technique, for the same reason, is what tells \"5k\" from \"5 k\" in\n * `arithmetic/normalizer/LargeNumberSuffixNormalizerRule.ts`.\n *\n * ## Why this replaced a zero-padding requirement on the ISO branch\n * The ISO branch briefly required its month and day groups to be padded to two\n * digits, which did stop \"2024 - 5 - 3\" being answered as \"Friday, May 3,\n * 2024\" but also stopped \"2024-5-3\" being a date at all. Padding was never the\n * real distinction, and the rule's own US branch is the proof: it has always\n * accepted unpadded groups, so \"1-1-2020\" and \"12-25-2023\" are both dates\n * today. Requiring padding on one branch of the two was therefore an\n * inconsistency between two spellings of one rule rather than a policy.\n *\n * The US branch's own guard is a 2-or-4-digit trailing year plus the calendar\n * check, and it is NOT safe against spaced arithmetic on its own: before this\n * check existed, \"12 - 25 - 2023\" answered Christmas 2023 instead of -2036,\n * and \"1 - 1 - 2020\" answered New Year's Day instead of -2020. Applying\n * adjacency to the whole run rather than to one branch fixes that at the same\n * time and leaves both branches resting on the same reason.\n *\n * ## Padded but spaced\n * \"2024 - 05 - 03\" is subtraction under this check, and that is the intended\n * reading. Nobody types the spaces when they mean the date, so the spacing is\n * the clearest signal available about which was meant, and the padding is not:\n * a person subtracting may well write \"05\" for the same reason they write\n * \"05\" anywhere else. Choosing the date there would mean no spelling of that\n * subtraction existed.\n *\n * `sourceEnd` is preferred over `text.length` where it is set, because a token\n * an earlier rule fused carries replacement text rather than source text and\n * only `sourceEnd` still describes the span it covers (see\n * `normalizer/TokenNormalizer.ts`'s `createFusedToken`).\n */\nfunction writtenAsOneRun(tokens: Token[]): boolean {\n for (let i = 0; i < tokens.length - 1; i++) {\n const token = tokens[i];\n const end = token.sourceEnd ?? token.offset + token.text.length;\n if (end !== tokens[i + 1].offset) return false;\n }\n return true;\n}\n\n/**\n * Resolves a year token's raw digit text to a 4-digit year.\n *\n * Accepts exactly 2 or 4 digits (matching the wiki's documented formats).\n * A 2-digit year is windowed using the common glibc `strptime(\"%y\")`\n * convention: 00-68 -> 2000-2068, 69-99 -> 1969-1999. Any other digit\n * count (1 or 3 digits) is not a valid year shape and returns null so the\n * caller can decline the match and fall back to ordinary arithmetic.\n */\nfunction resolveYear(digits: string): number | null {\n if (digits.length === 4) return Number(digits);\n if (digits.length === 2) {\n const yy = Number(digits);\n return yy <= 68 ? 2000 + yy : 1900 + yy;\n }\n return null;\n}\n\n/**\n * Converts a validated day/month/year triple into a fused DATETIME_LITERAL\n * token, or null if the triple isn't a real calendar date (e.g. \"30\" for\n * February), letting the caller fall back to treating the source tokens\n * as ordinary arithmetic instead of a date.\n */\nexport function buildDateToken(\n day: number,\n month: number,\n year: number,\n sourceTokens: Token[],\n ruleName: string,\n): NormalizerMatch | null {\n if (month < 1 || month > 12 || day < 1 || day > 31) return null;\n\n // Local midnight, consistent with formatDatetime()'s toLocaleString()\n // display and DATE_NOW's Date.now() epoch, both of which are local-time.\n const date = new Date(year, month - 1, day);\n // Reject calendar rollover (e.g. day=30 in February) rather than silently\n // normalizing to March 2, a rollover almost always means the tokens were\n // never a date to begin with (e.g. \"2-30-5\" as chained subtraction).\n if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {\n return null;\n }\n\n const first = sourceTokens[0];\n const text = sourceTokens.map((t) => t.value).join(\"\");\n const fusedToken = new LexerToken(\n DATETIME_LITERAL_TYPE,\n DATETIME_LITERAL_TYPE_ID,\n String(date.getTime()),\n text,\n first.offset,\n 0,\n first.line,\n first.col,\n );\n\n return { consumed: sourceTokens.length, replacement: [fusedToken], ruleName };\n}\n\n/**\n * A full ISO 8601 timestamp: an ISO date with a time of day, optionally\n * carrying a UTC offset or the `Z` that means one of zero.\n *\n * Anchored at the start and deliberately greedy about the offset, so\n * `...T15:30:00+11:00` matches through the offset rather than stopping at the\n * seconds and leaving `+11:00` behind as an addition. That is what the bare\n * form used to do: `2019-04-01T15:30:00+11:00` read the offset as a clock time\n * being ADDED to the date on its left, and answered today at 11:00. Both\n * halves of that were wrong and neither was visible, which is worse than the\n * bare form simply not being supported.\n *\n * The time of day is required. Without it this would match what the date-only\n * branches below already handle, and they carry the ambiguity rules\n * (adjacency, group shapes) that keep a subtraction chain a subtraction.\n *\n * Matched against reconstructed source text rather than token by token. The\n * lexer has no idea this is one literal, so it emits nine to fourteen tokens\n * for it with the shape depending on the spelling (`T15` arrives as an\n * identifier, `Z` as another, an offset as a PLUS or MINUS with two more\n * numbers), and enumerating those permutations in token patterns is how a\n * spelling gets missed.\n *\n * `T` and `Z` are uppercase only, matching `Iso8601.ts`'s own pattern, which is\n * what actually parses the match. Accepting a spelling here that the parser\n * then refuses would mean the bare form supported more shapes than the quoted\n * one, for no reason a reader could infer.\n */\nconst ISO_TIMESTAMP = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:?\\d{2})?/;\n\n/**\n * Token types a run of ISO 8601 timestamp text can lex into. Anything else\n * ends the run, so the reconstruction below can never reach across an operator\n * or a word that is not part of the literal.\n */\nconst ISO_TOKEN_TYPES = new Set([\"NUMBER\", \"MINUS\", \"PLUS\", \"COLON\", \"IDENT\"]);\n\n/**\n * How many tokens the longest supported spelling occupies. `2019-04-01T15:30:00.123+11:00`\n * is fourteen; the cap only bounds the walk, since the match itself decides\n * where the literal ends.\n */\nconst ISO_MAX_TOKENS = 16;\n\n/**\n * Fuses a bare (unquoted) ISO 8601 timestamp into one DATETIME_LITERAL.\n *\n * `Iso8601.ts` could always parse this shape, and still does the parsing here;\n * what it said could not be done without \"a much larger, dedicated lexer\n * change\" was recognising the bare form, on the grounds that it is ambiguous\n * with arithmetic. The date half of that ambiguity was settled by the rule this\n * helper sits inside (a date is written as one uninterrupted run, so\n * `2024 - 5 - 3` stays arithmetic), and the time half is not ambiguous at all:\n * no chain of subtractions contains a `T15:30`.\n *\n * Contiguity in the SOURCE is what makes that true, the same test\n * {@link writtenAsOneRun} applies to the date-only orderings. Every token has\n * to begin exactly where the previous one ended, so `2019-04-01 T15:30` with a\n * space in it, or `2019-04-01 + 11:00` with the offset written as an addition,\n * are left alone: the user who typed a space typed two things.\n *\n * A token an earlier normalizer rule already fused ends the run rather than\n * joining it. Its `text` is replacement text rather than source text (see\n * `normalizer/TokenNormalizer.ts`'s `createFusedToken`), so concatenating it\n * would reconstruct something the user never typed, and nothing that has\n * already been fused into a phrase is part of a timestamp anyway.\n *\n * @returns The fused match, or null when the tokens at `pos` are not a\n * timestamp, when the match would end mid-token, or when the shape is\n * ISO-like but names no real instant (`2019-04-01T25:00:00`).\n */\nfunction fuseIsoTimestamp(tokens: Token[], pos: number, ruleName: string): NormalizerMatch | null {\n const first = tokens[pos];\n if (first.sourceEnd !== undefined) return null;\n let text = first.text;\n let runEnd = first.offset + first.text.length;\n const window: Token[] = [first];\n for (let i = pos + 1; i < tokens.length && window.length < ISO_MAX_TOKENS; i++) {\n const next = tokens[i];\n if (next.sourceEnd !== undefined) break;\n if (next.offset !== runEnd || !ISO_TOKEN_TYPES.has(next.type)) break;\n text += next.text;\n runEnd = next.offset + next.text.length;\n window.push(next);\n }\n\n const matched = ISO_TIMESTAMP.exec(text);\n if (matched === null) return null;\n\n // The literal has to end where a token ends. A match that stopped halfway\n // through one would leave the parser holding a token whose text has already\n // been consumed, so decline and let the ordinary rules have it.\n let consumed = 0;\n let covered = 0;\n for (const token of window) {\n covered += token.text.length;\n consumed++;\n if (covered >= matched[0].length) break;\n }\n if (covered !== matched[0].length) return null;\n\n // The instant comes from the same parser the quoted form uses, so the two\n // spellings cannot drift apart: an offset means what it means there, and a\n // timestamp with no offset is local time, exactly as a bare date literal is\n // local midnight.\n const epochMs = parseIso8601(matched[0]);\n if (epochMs === null) return null;\n\n const fusedToken = new LexerToken(\n DATETIME_LITERAL_TYPE,\n DATETIME_LITERAL_TYPE_ID,\n String(epochMs),\n matched[0],\n first.offset,\n 0,\n first.line,\n first.col,\n first.offset + matched[0].length,\n );\n return { consumed, replacement: [fusedToken], ruleName };\n}\n\n/**\n * NormalizerRule that fuses numeric date literals into a single\n * DATETIME_LITERAL token, per the documented \"Datetime Formats\":\n * - European: DD/MM/YYYY (5 tokens: NUMBER SLASH NUMBER SLASH NUMBER)\n * - US: MM-DD-YYYY (5 tokens: NUMBER MINUS NUMBER MINUS NUMBER)\n * - ISO 8601: YYYY-MM-DD (same 5-token shape as US, disambiguated by the\n * first group being exactly 4 digits)\n * - Dot: DD.MM.YYYY (2 tokens. See below)\n *\n * ## Why the dot format is a 2-token window, not 5\n * ExpressionLexer's tokenizeNumber() treats \".\" adjacent to digits on both\n * sides as a decimal point, not a separator. For \"25.12.2023\" the lexer\n * itself emits only two NUMBER tokens: \"25.12\" (day+month merged into one\n * float-shaped literal) and \".2023\" (the lexer re-enters number scanning\n * AT the second dot, so the year keeps a leading dot). There is no 5-token\n * NUMBER-DOT-NUMBER-DOT-NUMBER shape to match against, the fusion has to\n * happen post-hoc from that 2-token shape instead. This only works for the\n * literal typed with no internal whitespace (e.g. \"25.12.2023\"); a spaced\n * variant (\"25 .12 .2023\") produces three separate leading-dot tokens and\n * is intentionally not supported, out of scope for the documented format.\n *\n * ## Ambiguity with existing arithmetic\n * A date-shaped SLASH or MINUS chain (e.g. \"25/12/2023\" or \"12-25-2023\")\n * previously parsed as chained division/subtraction. This rule\n * deliberately reinterprets any such chain that resolves to a real\n * calendar date as a date literal instead, matching the documented\n * behavior. Chains that aren't valid dates (out-of-range day/month, a\n * February 30, a non-2/4-digit year) fall through unchanged.\n *\n * That reinterpretation only ever claims a chain written as one\n * uninterrupted run of characters. \"25/12/2023\" and \"12-25-2023\" are dates;\n * \"25 / 12 / 2023\" and \"12 - 25 - 2023\", written with the spaces an operator\n * normally gets, stay division and subtraction. {@link writtenAsOneRun}\n * carries the full reasoning, including why this is checked instead of\n * requiring an ISO date's zero-padding.\n *\n * ## Relationship to Iso8601.ts / DatePhrase.ts\n * This is the general-purpose \"bare numeric date literal\" implementation\n * those two files' doc comments referenced as landing on a sibling branch\n * (`feat/safety-limits-datetime-literals`), now ported here. It's\n * intentionally independent of both: `Iso8601.ts` only parses QUOTED\n * STRING literals (`\"2019-04-01...\" to date`), and the Stocks package's\n * `DatePhrase.ts` is a narrower, deliberately-diverging grammar (US-style\n * bare SLASH dates, 4-digit-year-only) scoped to stock-history queries\n * neither of those needed to change for this to land.\n */\nexport function dateLiteralNormalizerRule(): NormalizerRule {\n return {\n name: \"datetime:date-literal\",\n priority: 70,\n match(tokens: Token[], pos: number): NormalizerMatch | null {\n const t0 = tokens[pos];\n if (t0.type !== \"NUMBER\") return null;\n\n // ── Dot format: 2-token window (see module doc) ──────────────────\n if (DOT_DAY_MONTH.test(t0.value)) {\n const t1 = tokens[pos + 1];\n if (t1 && t1.type === \"NUMBER\" && DOT_LEADING_YEAR.test(t1.value)) {\n const [dayText, monthText] = t0.value.split(\".\");\n const year = resolveYear(t1.value.slice(1));\n if (year !== null) {\n const match = buildDateToken(\n Number(dayText), Number(monthText), year,\n [t0, t1], \"datetime:date-literal:dot\",\n );\n if (match) return match;\n }\n }\n return null;\n }\n\n // ── ISO 8601 timestamp: as many tokens as the spelling takes ─────\n // Tried before the date-only windows below, which would otherwise fuse\n // the date half of a timestamp and leave the time half to be read as\n // arithmetic. See fuseIsoTimestamp().\n if (t0.value.length === 4 && PLAIN_INTEGER.test(t0.value)) {\n const timestamp = fuseIsoTimestamp(tokens, pos, \"datetime:date-literal:iso-timestamp\");\n if (timestamp) return timestamp;\n }\n\n // ── Slash / minus format: 5-token window ─────────────────────────\n if (!PLAIN_INTEGER.test(t0.value)) return null;\n const sep1 = tokens[pos + 1];\n if (!sep1 || (sep1.type !== \"SLASH\" && sep1.type !== \"MINUS\")) return null;\n const t1 = tokens[pos + 2];\n const sep2 = tokens[pos + 3];\n const t2 = tokens[pos + 4];\n if (!t1 || t1.type !== \"NUMBER\" || !PLAIN_INTEGER.test(t1.value)) return null;\n if (!sep2 || sep2.type !== sep1.type) return null;\n if (!t2 || t2.type !== \"NUMBER\" || !PLAIN_INTEGER.test(t2.value)) return null;\n\n const sourceTokens = [t0, sep1, t1, sep2, t2];\n // The one check that separates every ordering below from the arithmetic\n // it is spelled identically to, and the only one that applies to all\n // three equally. See writtenAsOneRun.\n if (!writtenAsOneRun(sourceTokens)) return null;\n\n if (sep1.type === \"SLASH\") {\n // European: DD/MM/YYYY (always, no ISO-slash variant is documented)\n if (!DAY_OR_MONTH.test(t0.value) || !DAY_OR_MONTH.test(t1.value)) return null;\n const year = resolveYear(t2.value);\n if (year === null) return null;\n return buildDateToken(Number(t0.value), Number(t1.value), year, sourceTokens, \"datetime:date-literal:european\");\n }\n\n // MINUS: ISO (YYYY-MM-DD) if the first group is exactly 4 digits, else US (MM-DD-YYYY)\n if (t0.value.length === 4) {\n if (!DAY_OR_MONTH.test(t1.value) || !DAY_OR_MONTH.test(t2.value)) return null;\n // year=t0, month=t1, day=t2, buildDateToken takes (day, month, year)\n return buildDateToken(Number(t2.value), Number(t1.value), Number(t0.value), sourceTokens, \"datetime:date-literal:iso\");\n }\n if (!DAY_OR_MONTH.test(t0.value) || !DAY_OR_MONTH.test(t1.value)) return null;\n const year = resolveYear(t2.value);\n if (year === null) return null;\n // month=t0, day=t1, year=t2, buildDateToken takes (day, month, year)\n return buildDateToken(Number(t1.value), Number(t0.value), year, sourceTokens, \"datetime:date-literal:us\");\n },\n };\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { buildDateToken } from \"./DateLiteralNormalizerRule\";\n\n/** Month names and the common abbreviations, to their 1-based number. */\nconst MONTHS: Record<string, number> = {\n\tjanuary: 1, jan: 1,\n\tfebruary: 2, feb: 2,\n\tmarch: 3, mar: 3,\n\tapril: 4, apr: 4,\n\tmay: 5,\n\tjune: 6, jun: 6,\n\tjuly: 7, jul: 7,\n\taugust: 8, aug: 8,\n\tseptember: 9, sep: 9, sept: 9,\n\toctober: 10, oct: 10,\n\tnovember: 11, nov: 11,\n\tdecember: 12, dec: 12,\n};\n\n/** The month number for a token, or 0 when it is not a month name. */\nfunction monthOf(token: Token | undefined): number {\n\tif (token === undefined) return 0;\n\t// UNIT as well as IDENT: \"may\" and \"march\" are ordinary words, but \"sept\"\n\t// and friends can lex either way depending on what else is registered.\n\tif (token.type !== \"IDENT\" && token.type !== \"UNIT\") return 0;\n\treturn MONTHS[(token.text ?? token.value ?? \"\").toLowerCase()] ?? 0;\n}\n\n/** A pure digit string, so hex and scientific literals are never fused. */\nconst PLAIN_INTEGER = /^\\d+$/;\n\n/**\n * Whether a plain integer reads as a year rather than a day of the month.\n *\n * Four digits only. A two-digit year would need the same windowing the numeric\n * rule does, and `March 99` is not a real spelling of 1999 anyway; accepting it\n * would mean guessing where guessing is not warranted.\n */\nfunction looksLikeYear(digits: string): boolean {\n\treturn digits.length === 4;\n}\n\n/**\n * Dates written with the month as a word: `March 9, 2024`, `3 March`,\n * `January 24, 1984`.\n *\n * `DateLiteralNormalizerRule` covers the all-numeric orderings and nothing\n * else, so every documented expression built on a spelled-out month failed,\n * and several failed in a way that looked unrelated to dates:\n *\n * weekday on March 9, 2024 Unexpected token after expression: \"9\"\n * days between 3 March and 30 May Expected AND_CONJ but got STAR\n *\n * The second is the interesting one. With no month-name rule, `3 March` fell\n * through to implicit multiplication, so the parser was genuinely looking at\n * `3 * March` and reporting exactly that. The parselets those expressions\n * needed were all present and working: `weekday on 2024-03-09` answered\n * Saturday the whole time. Only the literal was missing.\n *\n * A year of its own (`February 2020`) resolves to the first of that month, so\n * a caller asking about the month as a period has a date inside it to work\n * from. Ambiguity is resolved by width: a number after a month name is a year\n * when it has four digits, otherwise a day.\n */\nexport function monthNameDateNormalizerRule(priority = 64): NormalizerRule {\n\treturn {\n\t\tname: \"datetime:month-name-date\",\n\t\tpriority,\n\t\tmatch(tokens, pos): NormalizerMatch | null {\n\t\t\tconst RULE = \"datetime:month-name-date\";\n\t\t\tconst first = tokens[pos];\n\n\t\t\t// `9 March` and `9 March 2024`.\n\t\t\tif (first?.type === \"NUMBER\" && PLAIN_INTEGER.test(first.text ?? \"\")) {\n\t\t\t\tconst month = monthOf(tokens[pos + 1]);\n\t\t\t\tif (month === 0) return null;\n\t\t\t\tconst day = Number(first.text);\n\t\t\t\tif (day < 1 || day > 31) return null;\n\n\t\t\t\tconst yearToken = tokens[pos + 2];\n\t\t\t\tif (\n\t\t\t\t\tyearToken?.type === \"NUMBER\" &&\n\t\t\t\t\tPLAIN_INTEGER.test(yearToken.text ?? \"\") &&\n\t\t\t\t\tlooksLikeYear(yearToken.text ?? \"\")\n\t\t\t\t) {\n\t\t\t\t\treturn buildDateToken(day, month, Number(yearToken.text), tokens.slice(pos, pos + 3), RULE);\n\t\t\t\t}\n\t\t\t\t// No year given: the current one, matching what the numeric rule\n\t\t\t\t// does for the same shape.\n\t\t\t\treturn buildDateToken(day, month, new Date().getFullYear(), tokens.slice(pos, pos + 2), RULE);\n\t\t\t}\n\n\t\t\tconst month = monthOf(first);\n\t\t\tif (month === 0) return null;\n\n\t\t\tconst second = tokens[pos + 1];\n\t\t\tif (second?.type !== \"NUMBER\" || !PLAIN_INTEGER.test(second.text ?? \"\")) return null;\n\n\t\t\t// `February 2020`, a whole month rather than a day in one.\n\t\t\tif (looksLikeYear(second.text ?? \"\")) {\n\t\t\t\treturn buildDateToken(1, month, Number(second.text), tokens.slice(pos, pos + 2), RULE);\n\t\t\t}\n\n\t\t\tconst day = Number(second.text);\n\n\t\t\t// `March 9, 2024`. The comma is part of the literal, not a separator.\n\t\t\tconst comma = tokens[pos + 2];\n\t\t\tconst yearToken = tokens[pos + 3];\n\t\t\tif (\n\t\t\t\tcomma?.type === \"COMMA\" &&\n\t\t\t\tyearToken?.type === \"NUMBER\" &&\n\t\t\t\tPLAIN_INTEGER.test(yearToken.text ?? \"\") &&\n\t\t\t\tlooksLikeYear(yearToken.text ?? \"\")\n\t\t\t) {\n\t\t\t\treturn buildDateToken(day, month, Number(yearToken.text), tokens.slice(pos, pos + 4), RULE);\n\t\t\t}\n\n\t\t\t// `March 9 2024`, the same without the comma.\n\t\t\tif (\n\t\t\t\tcomma?.type === \"NUMBER\" &&\n\t\t\t\tPLAIN_INTEGER.test(comma.text ?? \"\") &&\n\t\t\t\tlooksLikeYear(comma.text ?? \"\")\n\t\t\t) {\n\t\t\t\treturn buildDateToken(day, month, Number(comma.text), tokens.slice(pos, pos + 3), RULE);\n\t\t\t}\n\n\t\t\t// `March 9`, no year.\n\t\t\treturn buildDateToken(day, month, new Date().getFullYear(), tokens.slice(pos, pos + 2), RULE);\n\t\t},\n\t};\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/** VMBuiltins.ts index for labelling a count as days. */\nconst DAYS_COUNT_BUILTIN = 93;\n\n/** Quarters, as their first and last month numbers. */\nconst QUARTERS: Record<string, readonly [number, number]> = {\n\tq1: [1, 3],\n\tq2: [4, 6],\n\tq3: [7, 9],\n\tq4: [10, 12],\n};\n\n/** Days in a month, honouring leap years. */\nfunction daysInMonth(year: number, month: number): number {\n\treturn new Date(year, month, 0).getDate();\n}\n\n/** Days in a whole year. */\nfunction daysInYear(year: number): number {\n\treturn daysInMonth(year, 2) === 29 ? 366 : 365;\n}\n\n/**\n * `days in <period>`: how long a named stretch of the calendar is.\n *\n * days in February 2020 29 days\n * days in Q3 92 days\n * days in 2024 366 days\n *\n * Distinct from the unit conversion that looks identical. `days in 3 weeks`\n * converts a quantity and is handled by `ReversedConversionNormalizerRule`;\n * this asks how many days a real calendar period contains, which depends on\n * which period it is. February is 28 days or 29, and no conversion factor\n * expresses that.\n *\n * That is also why the reversed-conversion rule deliberately declines a date:\n * rewriting `days in February 2020` to `February 2020 in days` would answer a\n * different question with a plausible-looking number.\n *\n * The count is computed at parse time, because the period is written out\n * literally and there is nothing to defer.\n */\nexport class DaysInPeriodParselet implements PrefixParselet {\n\treadonly category = \"Datetime\";\n\n\tparse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n\t\tconst total = this.readPeriodDays(parser);\n\t\tbuilder.emitOpcode(OpCode.PUSH_NUMBER);\n\t\tbuilder.emitNumber(total);\n\t\t// Labelled, so the answer reads \"92 days\" rather than a bare 92 that\n\t\t// has lost what it was counting.\n\t\tbuilder.emitOpcode(OpCode.CALL_BUILTIN);\n\t\tbuilder.emitIndex(DAYS_COUNT_BUILTIN);\n\t\tbuilder.emitIndex(1);\n\t}\n\n\t/** The number of days in the period written after `days in`. */\n\tprivate readPeriodDays(parser: Parser): number {\n\t\tconst next = parser.peek();\n\t\tconst word = (next?.text ?? next?.value ?? \"\").toLowerCase();\n\n\t\t// `days in Q3`, optionally with a year: `days in Q3 2024`.\n\t\tconst quarter = QUARTERS[word];\n\t\tif (quarter !== undefined) {\n\t\t\tparser.consume();\n\t\t\tconst year = this.readOptionalYear(parser) ?? new Date().getFullYear();\n\t\t\tlet total = 0;\n\t\t\tfor (let month = quarter[0]; month <= quarter[1]; month++) {\n\t\t\t\ttotal += daysInMonth(year, month);\n\t\t\t}\n\t\t\treturn total;\n\t\t}\n\n\t\t// `days in February 2020`, which the month-name rule has already fused\n\t\t// into a date literal pointing at the first of that month.\n\t\tif (next?.type === \"DATETIME_LITERAL\") {\n\t\t\tparser.consume();\n\t\t\tconst date = new Date(Number(next.value));\n\t\t\treturn daysInMonth(date.getFullYear(), date.getMonth() + 1);\n\t\t}\n\n\t\t// `days in 2024`, a bare year.\n\t\tconst year = this.readOptionalYear(parser);\n\t\tif (year !== null) return daysInYear(year);\n\n\t\tthrow ErrorFactory.parsing(\n\t\t\t\"DAYS_IN_EXPECTED_PERIOD\",\n\t\t\t'Expected a month, quarter or year, as in \"days in February 2020\" or \"days in Q3\"',\n\t\t);\n\t}\n\n /** A four-digit year sitting at the cursor, consumed if present. */\n\tprivate readOptionalYear(parser: Parser): number | null {\n\t\tconst token = parser.peek();\n\t\tif (token?.type !== \"NUMBER\") return null;\n\t\tconst text = token.text ?? token.value ?? \"\";\n\t\tif (!/^\\d{4}$/.test(text)) return null;\n\t\tparser.consume();\n\t\treturn Number(text);\n\t}\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\n/** Quarter names, which have no other meaning in this position. */\nconst QUARTERS = new Set([\"q1\", \"q2\", \"q3\", \"q4\"]);\n\n/** Month names and abbreviations, matching MonthNameDateNormalizerRule. */\nconst MONTHS = new Set([\n\t\"january\", \"jan\", \"february\", \"feb\", \"march\", \"mar\", \"april\", \"apr\",\n\t\"may\", \"june\", \"jun\", \"july\", \"jul\", \"august\", \"aug\",\n\t\"september\", \"sep\", \"sept\", \"october\", \"oct\", \"november\", \"nov\",\n\t\"december\", \"dec\",\n]);\n\n/** Whether the token begins a calendar period rather than a quantity. */\nfunction startsPeriod(token: Token | undefined): boolean {\n\tif (token === undefined) return false;\n\tif (token.type === \"DATETIME_LITERAL\") return true;\n\tconst word = (token.text ?? token.value ?? \"\").toLowerCase();\n\tif (token.type === \"IDENT\" || token.type === \"UNIT\") {\n\t\treturn QUARTERS.has(word) || MONTHS.has(word);\n\t}\n\t// A bare four-digit year. A shorter number is a quantity to convert.\n\treturn token.type === \"NUMBER\" && /^\\d{4}$/.test(token.text ?? \"\");\n}\n\n/**\n * Recognises `days in <calendar period>` and nothing else.\n *\n * `days in February 2020` asks how long a real month is; `days in 3 weeks`\n * converts a quantity. The two are spelled identically for the first two\n * words, so this cannot be an ordinary fused phrase: registering `days in`\n * unconditionally claimed the conversion as well and broke it.\n *\n * The lookahead is what separates them. Only a quarter, a month name, a\n * four-digit year, or a date literal the month-name rule has already fused\n * counts as a period; anything else is left for the conversion path.\n */\nexport function daysInPeriodNormalizerRule(priority = 60): NormalizerRule {\n\treturn {\n\t\tname: \"datetime:days-in-period\",\n\t\tpriority,\n\t\tmatch(tokens, pos): NormalizerMatch | null {\n\t\t\tconst days = tokens[pos];\n\t\t\tif (days === undefined) return null;\n\t\t\tif ((days.text ?? days.value ?? \"\").toLowerCase() !== \"days\") return null;\n\t\t\tif (days.type !== \"UNIT\" && days.type !== \"IDENT\") return null;\n\t\t\tif (tokens[pos + 1]?.type !== \"IN\") return null;\n\t\t\tif (!startsPeriod(tokens[pos + 2])) return null;\n\n\t\t\treturn {\n\t\t\t\tconsumed: 2,\n\t\t\t\treplacement: [createFusedToken(\"DAYS_IN_PERIOD\", \"days in\", [days, tokens[pos + 1]])],\n\t\t\t\truleName: \"datetime:days-in-period\",\n\t\t\t};\n\t\t},\n\t};\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { DATETIME_LITERAL_TYPE } from \"./DateLiteralNormalizerRule\";\n\n/**\n * Unwraps `[[<date>]]`, Obsidian's own wikilink syntax, as produced by\n * plugins like Natural Language Dates when inserting a link to a daily\n * note (e.g. `[[2024-01-15]]`), into the bare date literal inside, so\n * `[[2024-01-15]] + 5 days` works exactly like `2024-01-15 + 5 days`.\n * See GitHub issue #67.\n *\n * Deliberately narrow: only fires when the bracket-wrapped content is\n * ALREADY a fused `DATETIME_LITERAL` token (see\n * `DateLiteralNormalizerRule.ts`, DD/MM/YYYY, ISO, US, and dotted\n * formats all fuse to this one token type before this rule ever runs,\n * priority 80 running after that rule's own priority-80 pass converges\n * across the normalizer's multi-pass loop). `[...]`/`[[...]]` have no\n * other meaning ANYWHERE in this grammar today (confirmed, no parselet\n * is registered for `LBRACKET` at all), so this claims no existing\n * behavior; it does NOT unwrap arbitrary bracketed content (a page title,\n * a non-date word, ...), only a bracket pair immediately around a real\n * date, which is the one shape Obsidian's daily-note links actually\n * produce.\n */\nexport function dailyNoteLinkNormalizerRule(): NormalizerRule {\n return {\n name: \"datetime:daily-note-link\",\n priority: 80,\n match(tokens: Token[], pos: number): NormalizerMatch | null {\n if (tokens[pos]?.type !== \"LBRACKET\") return null;\n if (tokens[pos + 1]?.type !== \"LBRACKET\") return null;\n if (tokens[pos + 2]?.type !== DATETIME_LITERAL_TYPE) return null;\n if (tokens[pos + 3]?.type !== \"RBRACKET\") return null;\n if (tokens[pos + 4]?.type !== \"RBRACKET\") return null;\n return {\n consumed: 5,\n replacement: [tokens[pos + 2]],\n ruleName: \"datetime:daily-note-link\",\n };\n },\n };\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { stringValue } from \"@solve-js/vm/Value\";\nimport { NowParselet } from \"./parselets/NowParselet\";\nimport { NextLastParselet } from \"./parselets/NextLastParselet\";\nimport { UntilSinceParselet } from \"./parselets/UntilSinceParselet\";\nimport { WorkdaysInParselet } from \"./parselets/WorkdaysInParselet\";\nimport { WorkdayOffsetParselet } from \"./parselets/WorkdayOffsetParselet\";\nimport { WorkdaysBetweenParselet } from \"./parselets/WorkdaysBetweenParselet\";\nimport { DateFieldQueryParselet } from \"./parselets/DateFieldQueryParselet\";\nimport { DurationBetweenParselet } from \"./parselets/DurationBetweenParselet\";\nimport { DayTypePredicateParselet } from \"./parselets/DayTypePredicateParselet\";\nimport { CurrentTimestampParselet } from \"./parselets/CurrentTimestampParselet\";\nimport { ToDateParselet } from \"./parselets/ToDateParselet\";\nimport { ToTimestampParselet } from \"./parselets/ToTimestampParselet\";\nimport { DateLiteralParselet } from \"./parselets/DateLiteralParselet\";\nimport {\n workdaysInDuration, weekdayOnDate, toDateFromAny, toTimestampFromAny,\n monthOnDate, weekOnDate, isWeekendOnDate, isWorkdayOnDate, spanBetweenDates,\n} from \"./parselets/DatetimeTimestampPluginFunctions\";\nimport { untilSinceNormalizerRule } from \"./normalizer/UntilSinceNormalizerRule\";\nimport { betweenUnitNormalizerRule } from \"./normalizer/BetweenUnitNormalizerRule\";\nimport { workdayRateDenominatorNormalizerRule } from \"./normalizer/WorkdayRateDenominatorNormalizerRule\";\nimport { dateLiteralNormalizerRule } from \"./normalizer/DateLiteralNormalizerRule\";\nimport { monthNameDateNormalizerRule } from \"./normalizer/MonthNameDateNormalizerRule\";\nimport { DaysInPeriodParselet } from \"./parselets/DaysInPeriodParselet\";\nimport { daysInPeriodNormalizerRule } from \"./normalizer/DaysInPeriodNormalizerRule\";\nimport { dailyNoteLinkNormalizerRule } from \"./normalizer/DailyNoteLinkNormalizerRule\";\nimport { formatIso8601Local } from \"./Iso8601\";\n\n/**\n * Date/time keywords: `now`, `today`, `tomorrow`, `yesterday`,\n * `next <Weekday>`/`last <Weekday>`, `<unit> until <Datetime>`/`<unit> since <Datetime>`.\n * Uses the datetime arithmetic opcodes (`DATE_ADD`/`DATE_SUB`/`DATE_NEXT_WEEKDAY`/`DATE_LAST_WEEKDAY`)\n * plus the shared UoM conversion opcode (`UOM_CONVERT_IN`) for until/since.\n *\n * Also: workdays/weekdays and timestamps/ISO8601\n *\n * - `workdays in <duration>`. See `WorkdaysInParselet.ts`.\n * - `<date> + N workdays` / `<date> - N workdays`, plain \"+\"/\"-\"\n * arithmetic, special-cased in `vm/VM.ts`'s ADD/SUB dispatch for the\n * `workday`/`workdays` UNIT (see `lexer/units.ts`) since business-day\n * math needs actual weekend-skipping, not a linear ms conversion.\n * - `N working/business days after|from|before <date>`, the natural-language\n * spelling of the same offset (`5 working days after 20 Dec`,\n * `3 business days from today`). Fused as INFIX phrase tokens\n * (`WORKDAYS_AFTER`/`WORKDAYS_BEFORE`) since the count is the left operand;\n * emits `DATE_WORKDAY_OFFSET`, which walks the very same `addBusinessDays()`\n * as `<date> + N workdays`. See `WorkdayOffsetParselet.ts`.\n * - `working/business days between <date> and <date>`, the COUNT of working\n * days in the inclusive window (`working days between 1 Jan and 31 Jan`), the\n * working-day sibling of `<unit> between` below. Emits\n * `DATE_WORKDAYS_BETWEEN`. See `WorkdaysBetweenParselet.ts`.\n * - `<amount>/workday x <duration>`, a Rate, exactly like `$99/week`\n * already works, using `uom/UomConverter.ts`'s workday<->day shim so\n * `getMeasure()`/`convertUnit()` treat \"workday\" as a Time-measure unit\n * (5 workdays == 7 calendar days). See that file's doc comment. The\n * bare-denominator syntax `$500/workday` (no explicit \"1\") additionally\n * needs `workdayRateDenominatorNormalizerRule`. See its doc comment.\n * - `day of the week on <date>` / `weekday on <date>`, and the\n * natural-question forms over the same three date fields\n * `what day is it`, `what day is it on <date>`, `what day is it in\n * <duration>`, and the `month`/`week` equivalents. All one parselet, see\n * `DateFieldQueryParselet.ts`. The same fields are also available\n * composably as `<date> as weekday` / `as month` / `as week` via\n * `asConverters` below.\n * - `<unit> between <date> and <date>`, the two-explicit-endpoints\n * sibling of `until`/`since`, see `DurationBetweenParselet.ts`. A\n * leading `how many` is accepted for it and for `until`/`since`, see\n * `BetweenUnitNormalizerRule.ts`.\n * - `<date> is a weekend` / `is a workday`, postfix predicates, see\n * `DayTypePredicateParselet.ts`. Mon-Fri only: these answer the week's\n * SHAPE (is this a weekday), which the holiday calendar deliberately does\n * not touch, see the scope note below.\n * - `current timestamp` / `<date/time> to timestamp` / `<ISO8601 string\n * or unix timestamp> to date`. See `CurrentTimestampParselet.ts` /\n * `ToTimestampParselet.ts` / `ToDateParselet.ts` and\n * `DatetimeTimestampPluginFunctions.ts`.\n * - Bare numeric date literals, `25/12/2023` (European DD/MM/YYYY)\n * `12-25-2023` (US MM-DD-YYYY), `2023-12-25` (ISO YYYY-MM-DD), and\n * `25.12.2023` (dot-separated DD.MM.YYYY), fused into a single\n * `DATETIME_LITERAL` token by `dateLiteralNormalizerRule()` and pushed by\n * `DateLiteralParselet`. Ported from the sibling `feat/safety-limits-datetime-literals`\n * branch referenced in `Iso8601.ts`'s and the Stocks package's\n * `DatePhrase.ts`'s doc comments. This is that work, now merged.\n * - `<date/time> as iso8601`, registered below via `asConverters`\n * (the `Converters` package's `<expr> as <type>` extension point, see\n * `api/PackageRegistry.ts`'s doc comment) rather than a new opcode or a\n * `Converters`-package-owned built-in name: `asConverters` already\n * exists precisely for a third-party/domain package to contribute a\n * new `as <name>` target without touching `AsConverterParselet.ts` or\n * `OpCode.ts` at all, and \"iso8601\" is inherently a datetime-package\n * concept (needs `Iso8601.ts`'s formatting, which already lives here)\n * simpler to keep it self-contained in this package than to split the\n * feature across two packages for a marginal \"which package owns the\n * converter-name list\" tidiness gain.\n *\n * SCOPE DECISION (holidays): the business-day WALK and COUNT (the offset forms,\n * `between`, and `<date> + N workdays`) skip weekends always, and public\n * holidays too when the host configures a calendar via `date.holidays` (see\n * `constants/Configuration.ts` and `vm/HolidayCalendar.ts`). No calendar means\n * weekends-only, the honest default: the engine excludes exactly the days it\n * can prove are non-working. Picking a region's holidays and keeping them\n * current is the host's to own, the same \"bring your own data source\" shape\n * stocks and weather use, so this package never guesses a holiday the caller\n * did not supply.\n *\n * Two workday features stay weekends-only by design, and say so: `workdays in\n * <duration>` (a deterministic ratio with no anchor date, so no calendar to\n * consult, see `WorkdaysInParselet.ts`) and the `is a weekend`/`is a workday`\n * predicates (which report the week's Mon-Fri shape, a different question from\n * \"is this a working day here\"). See `vm/VM.ts`'s `addBusinessDays()` for the\n * walk's side of this.\n */\nexport const DATETIME_PACKAGE: IEnginePackage = {\n name: \"solve-datetime\",\n phrases: {\n \"workdays in\": \"WORKDAYS_IN\",\n // Business-day arithmetic in words. \"after\"/\"from\" count forward, \"before\"\n // back; the count is the left operand, so these fuse as INFIX tokens (see\n // WorkdayOffsetParselet). Both \"working\" and \"business\" days, singular and\n // plural, so \"1 working day after\" and \"5 business days from\" both read.\n \"working days after\": \"WORKDAYS_AFTER\",\n \"business days after\": \"WORKDAYS_AFTER\",\n \"working day after\": \"WORKDAYS_AFTER\",\n \"business day after\": \"WORKDAYS_AFTER\",\n \"working days from\": \"WORKDAYS_AFTER\",\n \"business days from\": \"WORKDAYS_AFTER\",\n \"working day from\": \"WORKDAYS_AFTER\",\n \"business day from\": \"WORKDAYS_AFTER\",\n \"working days before\": \"WORKDAYS_BEFORE\",\n \"business days before\": \"WORKDAYS_BEFORE\",\n \"working day before\": \"WORKDAYS_BEFORE\",\n \"business day before\": \"WORKDAYS_BEFORE\",\n // The count form (prefix). Fusing the whole \"working days between\" keeps\n // the bare `days between` rule from claiming the \"days\" first.\n \"working days between\": \"WORKDAYS_BETWEEN\",\n \"business days between\": \"WORKDAYS_BETWEEN\",\n \"day of the week on\": \"WEEKDAY_ON\",\n \"weekday on\": \"WEEKDAY_ON\",\n \"current timestamp\": \"CURRENT_TIMESTAMP\",\n \"to date\": \"TO_DATE\",\n \"to timestamp\": \"TO_TIMESTAMP\",\n // Natural-question forms. The bare \"what X is it\" and the \"... on\"/\n // \"... in\" variants are all registered explicitly; the phrase trie is\n // longest-match-wins, so \"what day is it in\" beats \"what day is it\"\n // whenever a duration follows, and the bare phrase is left to answer\n // for right now (see DateFieldQueryParselet's peek).\n \"what day is it\": \"WEEKDAY_ON\",\n \"what day is it on\": \"WEEKDAY_ON\",\n \"what day will it be on\": \"WEEKDAY_ON\",\n \"what day is it in\": \"WEEKDAY_IN\",\n \"what day will it be in\": \"WEEKDAY_IN\",\n \"what month is it\": \"MONTH_ON\",\n \"what month is it on\": \"MONTH_ON\",\n \"month of\": \"MONTH_ON\",\n \"what month is it in\": \"MONTH_IN\",\n \"what month will it be in\": \"MONTH_IN\",\n \"what week is it\": \"WEEK_ON\",\n \"what week is it on\": \"WEEK_ON\",\n \"week of\": \"WEEK_ON\",\n // The spelling Soulver documents. Same question, same handler.\n \"week number on\": \"WEEK_ON\",\n \"week number of\": \"WEEK_ON\",\n \"week number\": \"WEEK_ON\",\n \"what week is it in\": \"WEEK_IN\",\n \"what week will it be in\": \"WEEK_IN\",\n // Postfix day-type predicates.\n \"is a weekend\": \"IS_WEEKEND\",\n \"is on a weekend\": \"IS_WEEKEND\",\n \"is a workday\": \"IS_WORKDAY\",\n \"is a weekday\": \"IS_WORKDAY\",\n \"is a business day\": \"IS_WORKDAY\",\n },\n prefixParselets: {\n DAYS_IN_PERIOD: new DaysInPeriodParselet(),\n NOW: new NowParselet(0),\n TODAY: new NowParselet(0),\n TOMORROW: new NowParselet(1),\n YESTERDAY: new NowParselet(-1),\n NEXT: new NextLastParselet(\"next\"),\n LAST: new NextLastParselet(\"last\"),\n UNTIL_UNIT: new UntilSinceParselet(\"until\"),\n SINCE_UNIT: new UntilSinceParselet(\"since\"),\n WORKDAYS_IN: new WorkdaysInParselet(),\n WORKDAYS_BETWEEN: new WorkdaysBetweenParselet(),\n WEEKDAY_ON: new DateFieldQueryParselet(\"weekdayOnDate\", \"on\"),\n WEEKDAY_IN: new DateFieldQueryParselet(\"weekdayOnDate\", \"in\"),\n MONTH_ON: new DateFieldQueryParselet(\"monthOnDate\", \"on\"),\n MONTH_IN: new DateFieldQueryParselet(\"monthOnDate\", \"in\"),\n WEEK_ON: new DateFieldQueryParselet(\"weekOnDate\", \"on\"),\n WEEK_IN: new DateFieldQueryParselet(\"weekOnDate\", \"in\"),\n BETWEEN_UNIT: new DurationBetweenParselet(),\n CURRENT_TIMESTAMP: new CurrentTimestampParselet(),\n DATETIME_LITERAL: new DateLiteralParselet(),\n },\n infixParselets: {\n WORKDAYS_AFTER: new WorkdayOffsetParselet(\"forward\"),\n WORKDAYS_BEFORE: new WorkdayOffsetParselet(\"backward\"),\n TO_DATE: new ToDateParselet(),\n TO_TIMESTAMP: new ToTimestampParselet(),\n IS_WEEKEND: new DayTypePredicateParselet(\"isWeekendOnDate\"),\n IS_WORKDAY: new DayTypePredicateParselet(\"isWorkdayOnDate\"),\n },\n normalizerRules: [\n untilSinceNormalizerRule(),\n betweenUnitNormalizerRule(),\n workdayRateDenominatorNormalizerRule(),\n dateLiteralNormalizerRule(),\n // Dates with the month spelled out (\"March 9, 2024\"). Priority 64, just\n // under the numeric rule, so an all-numeric literal is still claimed by\n // the rule that has always claimed it.\n monthNameDateNormalizerRule(),\n daysInPeriodNormalizerRule(),\n dailyNoteLinkNormalizerRule(),\n ],\n pluginFunctions: {\n workdaysInDuration,\n weekdayOnDate,\n toDateFromAny,\n toTimestampFromAny,\n monthOnDate,\n weekOnDate,\n isWeekendOnDate,\n isWorkdayOnDate,\n spanBetweenDates,\n },\n asConverters: {\n iso8601: (value) => stringValue(formatIso8601Local(value.toNumber())),\n // The same three fields the \"what X is it\" questions answer, in the\n // composable form: `next friday + 2 weeks as weekday`. Question phrases\n // only ever take a bare date expression, whereas `as` binds after a\n // whole expression, so these are the general case rather than a\n // shorthand, and they cost one line each, sharing the identical\n // handlers.\n weekday: (value) => weekdayOnDate([value]),\n month: (value) => monthOnDate([value]),\n week: (value) => weekOnDate([value]),\n },\n};\n","/**\n * City/country/abbreviation name -> IANA timezone identifier.\n *\n * Scoped to ~80 major cities, common standard-time abbreviations, and a\n * handful of countries (mapped to their capital's zone, per SoulverCore's\n * own documented convention for countries spanning multiple zones)\n * deliberately not exhaustive. Additive: extend this table as gaps are\n * found, no other code needs to change.\n *\n * Keys are lowercase. Single-word entries are looked up directly against\n * a bare `IDENT` token's text; multi-word entries (see\n * {@link MULTI_WORD_CITY_PHRASES}) are looked up the same way, but only\n * after `TimePackage.ts`'s `phrases` field has fused them into one token.\n *\n * Abbreviations are inherently ambiguous (PST/CST/EST/etc. don't\n * self-describe standard vs. daylight time, and some collide with other\n * meanings, e.g. IST is also used for Irish/Israel Standard Time). This\n * table picks the single most common convention for each, matching how\n * every comparable calculator tool resolves the same ambiguity, not\n * claimed as the only valid reading.\n */\nexport const CITY_TO_IANA_ZONE: Record<string, string> = {\n // ── Oceania ──\n sydney: \"Australia/Sydney\",\n melbourne: \"Australia/Melbourne\",\n brisbane: \"Australia/Brisbane\",\n perth: \"Australia/Perth\",\n adelaide: \"Australia/Adelaide\",\n canberra: \"Australia/Sydney\",\n auckland: \"Pacific/Auckland\",\n wellington: \"Pacific/Auckland\",\n fiji: \"Pacific/Fiji\",\n\n // ── North America ──\n chicago: \"America/Chicago\",\n seattle: \"America/Los_Angeles\",\n denver: \"America/Denver\",\n phoenix: \"America/Phoenix\",\n toronto: \"America/Toronto\",\n montreal: \"America/Toronto\",\n vancouver: \"America/Vancouver\",\n ottawa: \"America/Toronto\",\n honolulu: \"Pacific/Honolulu\",\n anchorage: \"America/Anchorage\",\n miami: \"America/New_York\",\n boston: \"America/New_York\",\n atlanta: \"America/New_York\",\n dallas: \"America/Chicago\",\n houston: \"America/Chicago\",\n\n // ── South America ──\n lima: \"America/Lima\",\n bogota: \"America/Bogota\",\n caracas: \"America/Caracas\",\n santiago: \"America/Santiago\",\n montevideo: \"America/Montevideo\",\n quito: \"America/Guayaquil\",\n\n // ── Europe ──\n london: \"Europe/London\",\n paris: \"Europe/Paris\",\n berlin: \"Europe/Berlin\",\n madrid: \"Europe/Madrid\",\n rome: \"Europe/Rome\",\n amsterdam: \"Europe/Amsterdam\",\n dublin: \"Europe/Dublin\",\n lisbon: \"Europe/Lisbon\",\n vienna: \"Europe/Vienna\",\n zurich: \"Europe/Zurich\",\n geneva: \"Europe/Zurich\",\n moscow: \"Europe/Moscow\",\n istanbul: \"Europe/Istanbul\",\n athens: \"Europe/Athens\",\n stockholm: \"Europe/Stockholm\",\n oslo: \"Europe/Oslo\",\n helsinki: \"Europe/Helsinki\",\n warsaw: \"Europe/Warsaw\",\n prague: \"Europe/Prague\",\n budapest: \"Europe/Budapest\",\n brussels: \"Europe/Brussels\",\n copenhagen: \"Europe/Copenhagen\",\n reykjavik: \"Atlantic/Reykjavik\",\n kiev: \"Europe/Kyiv\",\n kyiv: \"Europe/Kyiv\",\n\n // ── Asia ──\n tokyo: \"Asia/Tokyo\",\n osaka: \"Asia/Tokyo\",\n beijing: \"Asia/Shanghai\",\n shanghai: \"Asia/Shanghai\",\n seoul: \"Asia/Seoul\",\n singapore: \"Asia/Singapore\",\n bangkok: \"Asia/Bangkok\",\n jakarta: \"Asia/Jakarta\",\n manila: \"Asia/Manila\",\n mumbai: \"Asia/Kolkata\",\n delhi: \"Asia/Kolkata\",\n bangalore: \"Asia/Kolkata\",\n dubai: \"Asia/Dubai\",\n abudhabi: \"Asia/Dubai\",\n karachi: \"Asia/Karachi\",\n dhaka: \"Asia/Dhaka\",\n hanoi: \"Asia/Ho_Chi_Minh\",\n taipei: \"Asia/Taipei\",\n islamabad: \"Asia/Karachi\",\n riyadh: \"Asia/Riyadh\",\n telaviv: \"Asia/Jerusalem\",\n jerusalem: \"Asia/Jerusalem\",\n\n // ── Africa ──\n cairo: \"Africa/Cairo\",\n lagos: \"Africa/Lagos\",\n nairobi: \"Africa/Nairobi\",\n johannesburg: \"Africa/Johannesburg\",\n casablanca: \"Africa/Casablanca\",\n accra: \"Africa/Accra\",\n tunis: \"Africa/Tunis\",\n addisababa: \"Africa/Addis_Ababa\",\n\n // ── Countries (capital's zone. See doc comment) ──\n france: \"Europe/Paris\",\n germany: \"Europe/Berlin\",\n spain: \"Europe/Madrid\",\n italy: \"Europe/Rome\",\n japan: \"Asia/Tokyo\",\n china: \"Asia/Shanghai\",\n india: \"Asia/Kolkata\",\n brazil: \"America/Sao_Paulo\",\n russia: \"Europe/Moscow\",\n australia: \"Australia/Sydney\",\n canada: \"America/Toronto\",\n mexico: \"America/Mexico_City\",\n uk: \"Europe/London\",\n usa: \"America/New_York\",\n ireland: \"Europe/Dublin\",\n netherlands: \"Europe/Amsterdam\",\n sweden: \"Europe/Stockholm\",\n norway: \"Europe/Oslo\",\n poland: \"Europe/Warsaw\",\n turkey: \"Europe/Istanbul\",\n egypt: \"Africa/Cairo\",\n nigeria: \"Africa/Lagos\",\n // \"singapore\" (the city entry above) already covers the country too\n // Singapore is a city-state, one zone either way, no separate entry needed.\n};\n\n/**\n * Multi-word city/country names, fused into a single `CITY_NAME` token\n * by {@link TimePackage}'s `phrases` field before parsing, since\n * {@link CITY_TO_IANA_ZONE} lookups only ever see one already-tokenized\n * word. Keys here double as the phrase text registered with the fuser;\n * the resolved zone lives in {@link CITY_TO_IANA_ZONE} under the same\n * (lowercase, space-separated) key.\n */\nexport const MULTI_WORD_CITY_ZONES: Record<string, string> = {\n \"new york\": \"America/New_York\",\n \"los angeles\": \"America/Los_Angeles\",\n \"san francisco\": \"America/Los_Angeles\",\n \"las vegas\": \"America/Los_Angeles\",\n \"mexico city\": \"America/Mexico_City\",\n \"hong kong\": \"Asia/Hong_Kong\",\n \"kuala lumpur\": \"Asia/Kuala_Lumpur\",\n \"sao paulo\": \"America/Sao_Paulo\",\n \"buenos aires\": \"America/Argentina/Buenos_Aires\",\n \"rio de janeiro\": \"America/Sao_Paulo\",\n \"cape town\": \"Africa/Johannesburg\",\n \"new delhi\": \"Asia/Kolkata\",\n \"united kingdom\": \"Europe/London\",\n \"united states\": \"America/New_York\",\n \"south africa\": \"Africa/Johannesburg\",\n};\n\n/** Standard-time-zone abbreviations. See the doc comment above on ambiguity. */\nexport const ZONE_ABBREVIATION_TO_IANA: Record<string, string> = {\n pst: \"America/Los_Angeles\",\n pdt: \"America/Los_Angeles\",\n est: \"America/New_York\",\n edt: \"America/New_York\",\n cst: \"America/Chicago\",\n cdt: \"America/Chicago\",\n mst: \"America/Denver\",\n mdt: \"America/Denver\",\n hst: \"Pacific/Honolulu\",\n bst: \"Europe/London\",\n cet: \"Europe/Paris\",\n cest: \"Europe/Paris\",\n jst: \"Asia/Tokyo\",\n ist: \"Asia/Kolkata\",\n aest: \"Australia/Sydney\",\n aedt: \"Australia/Sydney\",\n};\n\n/** Merged single-word lookup table used by {@link tryConsumeZoneReference}. */\nexport const ZONE_LOOKUP: Record<string, string> = {\n ...CITY_TO_IANA_ZONE,\n ...MULTI_WORD_CITY_ZONES,\n ...ZONE_ABBREVIATION_TO_IANA,\n};\n","/**\n * Timezone math, built entirely on native `Intl.DateTimeFormat`/IANA data\n * zero external dependency, DST-correct, and always as current as the\n * runtime's own tz database (no bundled table to go stale).\n *\n * A \"zone reference\" as used throughout this module is one of:\n * - A real IANA zone identifier (e.g. `\"Australia/Sydney\"`).\n * - A synthetic fixed-offset encoding, `\"UTCOFFSET:<minutes>\"` (e.g.\n * `\"UTCOFFSET:480\"` for GMT+8), for the numeric `GMT+N`/`UTC-N` form\n * which has no IANA identifier of its own and needs no DST awareness\n * (a fixed offset is fixed, by definition).\n *\n * {@link encodeFixedOffset}/{@link isFixedOffset}/{@link decodeFixedOffsetMinutes}\n * are the only code that needs to know this encoding exists, everything\n * else just calls {@link resolveOffsetMinutes} and {@link zoneLabel}.\n */\n\nconst FIXED_OFFSET_PREFIX = \"UTCOFFSET:\";\n\n/** Encode a fixed UTC offset (in minutes, may be negative) as a zone-reference string. */\nexport function encodeFixedOffset(offsetMinutes: number): string {\n return `${FIXED_OFFSET_PREFIX}${offsetMinutes}`;\n}\n\nfunction isFixedOffset(zoneRef: string): boolean {\n return zoneRef.startsWith(FIXED_OFFSET_PREFIX);\n}\n\nfunction decodeFixedOffsetMinutes(zoneRef: string): number {\n return parseInt(zoneRef.slice(FIXED_OFFSET_PREFIX.length), 10);\n}\n\n/**\n * The UTC offset (in minutes, positive = ahead of UTC) a zone reference\n * has AT a given instant. IANA zones are resolved via the standard\n * `Intl.DateTimeFormat` round-trip trick: format the instant using the\n * zone's wall-clock fields, then re-interpret those same field values as\n * if they were UTC, the difference from the original instant IS the\n * zone's offset at that instant (correctly DST-aware, since the format\n * step already applied whatever DST rule is in effect then).\n */\nexport function resolveOffsetMinutes(zoneRef: string, atMs: number): number {\n if (isFixedOffset(zoneRef)) return decodeFixedOffsetMinutes(zoneRef);\n\n const dtf = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: zoneRef,\n hourCycle: \"h23\",\n year: \"numeric\", month: \"2-digit\", day: \"2-digit\",\n hour: \"2-digit\", minute: \"2-digit\", second: \"2-digit\",\n });\n const parts: Record<string, string> = {};\n for (const p of dtf.formatToParts(new Date(atMs))) parts[p.type] = p.value;\n const asUtcMs = Date.UTC(\n +parts.year, +parts.month - 1, +parts.day,\n +parts.hour, +parts.minute, +parts.second,\n );\n return Math.round((asUtcMs - atMs) / 60000);\n}\n\n/**\n * The UTC instant (ms) for a given wall-clock date/time interpreted AS\n * local time in `zoneRef`. Single-pass (not iteratively refined against\n * the DST transition it might land in), the standard, generally-accepted\n * simplification every comparable \"convert this local time to another\n * zone\" tool makes; only matters within the ~1-2 hour window around a\n * DST transition instant, which is inherent to any offset-based approach\n * without a full transition-table walk.\n */\nexport function zonedWallClockToUtcMs(\n year: number, month0: number, day: number, hour: number, minute: number,\n zoneRef: string,\n): number {\n const naiveUtcMs = Date.UTC(year, month0, day, hour, minute, 0);\n const offsetMinutes = resolveOffsetMinutes(zoneRef, naiveUtcMs);\n return naiveUtcMs - offsetMinutes * 60000;\n}\n\n/** Format a UTC instant as `zoneRef`'s local wall-clock time, e.g. \"1:00 AM\". */\nexport function formatTimeInZone(atMs: number, zoneRef: string): string {\n if (isFixedOffset(zoneRef)) {\n const offsetMs = decodeFixedOffsetMinutes(zoneRef) * 60000;\n return formatTimeUtc(new Date(atMs + offsetMs));\n }\n const dtf = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: zoneRef, hour: \"numeric\", minute: \"2-digit\", hour12: true,\n });\n return dtf.format(new Date(atMs));\n}\n\n/** Format a UTC instant as `zoneRef`'s local calendar date, e.g. \"July 31, 2026\". */\nexport function formatDateInZone(atMs: number, zoneRef: string): string {\n if (isFixedOffset(zoneRef)) {\n const offsetMs = decodeFixedOffsetMinutes(zoneRef) * 60000;\n return formatDateUtc(new Date(atMs + offsetMs));\n }\n const dtf = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: zoneRef, year: \"numeric\", month: \"long\", day: \"numeric\",\n });\n return dtf.format(new Date(atMs));\n}\n\n/** The calendar (year, month0, day) `zoneRef` shows for a given instant. */\nexport function zonedYMD(atMs: number, zoneRef: string): { year: number; month0: number; day: number } {\n if (isFixedOffset(zoneRef)) {\n const d = new Date(atMs + decodeFixedOffsetMinutes(zoneRef) * 60000);\n return { year: d.getUTCFullYear(), month0: d.getUTCMonth(), day: d.getUTCDate() };\n }\n const dtf = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: zoneRef, year: \"numeric\", month: \"2-digit\", day: \"2-digit\",\n });\n const parts: Record<string, string> = {};\n for (const p of dtf.formatToParts(new Date(atMs))) parts[p.type] = p.value;\n return { year: +parts.year, month0: +parts.month - 1, day: +parts.day };\n}\n\n/**\n * Signed whole-day difference between the calendar dates `zoneRef` and\n * `relativeToZoneRef` show for the SAME instant, e.g. +1 if `zoneRef`'s\n * date is one day ahead of `relativeToZoneRef`'s. Correctly handles\n * month/year boundaries (unlike naively diffing day-of-month numbers).\n */\nexport function dayShiftBetweenZones(atMs: number, zoneRef: string, relativeToZoneRef: string): number {\n const a = zonedYMD(atMs, zoneRef);\n const b = zonedYMD(atMs, relativeToZoneRef);\n const aMs = Date.UTC(a.year, a.month0, a.day);\n const bMs = Date.UTC(b.year, b.month0, b.day);\n return Math.round((aMs - bMs) / 86400000);\n}\n\nfunction formatTimeUtc(d: Date): string {\n const dtf = new Intl.DateTimeFormat(\"en-US\", { timeZone: \"UTC\", hour: \"numeric\", minute: \"2-digit\", hour12: true });\n return dtf.format(d);\n}\n\nfunction formatDateUtc(d: Date): string {\n const dtf = new Intl.DateTimeFormat(\"en-US\", { timeZone: \"UTC\", year: \"numeric\", month: \"long\", day: \"numeric\" });\n return dtf.format(d);\n}\n\n/**\n * A human-readable label for a zone reference, used in \"time difference\"\n * output. For a real IANA zone, the last path segment with underscores\n * replaced by spaces (e.g. `\"Europe/Moscow\"` -> `\"Moscow\"`,\n * `\"America/Los_Angeles\"` -> `\"Los Angeles\"`). For a fixed offset,\n * `\"UTC+8\"`/`\"UTC-5\"`.\n */\nexport function zoneLabel(zoneRef: string): string {\n if (isFixedOffset(zoneRef)) {\n const minutes = decodeFixedOffsetMinutes(zoneRef);\n const sign = minutes >= 0 ? \"+\" : \"-\";\n const abs = Math.abs(minutes);\n const hours = Math.floor(abs / 60);\n const mins = abs % 60;\n return mins === 0 ? `UTC${sign}${hours}` : `UTC${sign}${hours}:${String(mins).padStart(2, \"0\")}`;\n }\n const lastSegment = zoneRef.split(\"/\").pop() ?? zoneRef;\n return lastSegment.replace(/_/g, \" \");\n}\n","import { Parser } from \"@solve-js/parser/Parser\";\nimport { ZONE_LOOKUP } from \"../../timezones/CityZones\";\nimport { encodeFixedOffset, zoneLabel } from \"../../timezones/ZoneMath\";\n\n/** A resolved zone reference plus the display text the user actually typed. */\nexport interface ZoneReference {\n /** IANA zone identifier, or a fixed-offset encoding. See `ZoneMath.ts`. */\n zoneRef: string;\n /**\n * What to show the user for this zone, e.g. in \"time difference\"\n * output. The user's OWN raw text, title-cased, when they named a\n * city/abbreviation/country (so \"Seattle\" and \"Los Angeles\", both\n * `America/Los_Angeles`, each keep their own label instead of\n * collapsing to whichever one the IANA identifier happens to be named\n * after). Falls back to `zoneLabel(zoneRef)`'s derived label\n * (e.g. \"UTC+8\") for the numeric GMT/UTC-offset form, where there's no\n * more specific name to prefer.\n */\n displayName: string;\n}\n\n/**\n * Consume a \"zone reference\" from the parser if one is present, a city\n * country, or standard-abbreviation name (`ZONE_LOOKUP`, matched by raw\n * token text regardless of whether it's a bare `IDENT` or a phrase-fused\n * `CITY_NAME` token. See `TimePackage.ts`'s `phrases` field for the\n * multi-word cities), or a numeric `GMT+N`/`UTC-N[:MM]` offset (a\n * multi-token form: an IDENT \"gmt\"/\"utc\", then an optional sign +\n * number + optional `:MM`).\n *\n * Returns `null` and consumes NOTHING if the next token doesn't match\n * either shape, callers must check for `null` before assuming a zone\n * reference was found, since (unlike most of this codebase's phrase\n * parsing) this helper is deliberately speculative: it only commits\n * (consumes tokens) once it's confident, so a caller can safely use it to\n * decide \"is a zone-conversion suffix present at all\" without needing\n * backtracking (this parser has none, `BytecodeBuilder` is append-only).\n */\nexport function tryConsumeZoneReference(parser: Parser): ZoneReference | null {\n const token = parser.peek();\n if (!token) return null;\n\n const rawText = token.value;\n const lowerText = rawText.toLowerCase();\n\n if (lowerText === \"gmt\" || lowerText === \"utc\") {\n parser.consume();\n const signToken = parser.peek();\n if (signToken?.type === \"PLUS\" || signToken?.type === \"MINUS\") {\n const isNegative = signToken.type === \"MINUS\";\n parser.consume();\n\n // \"8:30\" between the sign and a following word (e.g. \"in Paris\") has\n // already been fused into a single CLOCK_TIME token by the lexer's\n // clock-time normalizer (see ClockTimeNormalizerRule.ts) before this\n // parselet ever runs, so a bare NUMBER never appears in that case\n // the fused token's value IS already hour*60+minute, matching\n // `totalMinutes` below exactly, so it can be used as-is.\n const offsetToken = parser.peek();\n let totalMinutes: number;\n if (offsetToken?.type === \"CLOCK_TIME\") {\n parser.consume();\n totalMinutes = parseInt(offsetToken.value, 10);\n } else {\n const hourToken = parser.consume(\"NUMBER\");\n totalMinutes = parseInt(hourToken.value, 10) * 60;\n if (parser.peek()?.type === \"COLON\") {\n parser.consume();\n const minuteToken = parser.consume(\"NUMBER\");\n totalMinutes += parseInt(minuteToken.value, 10);\n }\n }\n const zoneRef = encodeFixedOffset(isNegative ? -totalMinutes : totalMinutes);\n return { zoneRef, displayName: zoneLabel(zoneRef) };\n }\n const zoneRef = encodeFixedOffset(0); // bare \"GMT\"/\"UTC\" = zero offset\n return { zoneRef, displayName: zoneLabel(zoneRef) };\n }\n\n const zoneRef = ZONE_LOOKUP[lowerText];\n if (zoneRef) {\n parser.consume();\n const displayName = rawText.replace(/\\b\\w/g, (c) => c.toUpperCase());\n return { zoneRef, displayName };\n }\n\n return null;\n}\n","import { Value, stringValue } from \"@solve-js/vm/Value\";\nimport {\n zonedWallClockToUtcMs, formatTimeInZone, formatDateInZone,\n dayShiftBetweenZones, resolveOffsetMinutes,\n} from \"../timezones/ZoneMath\";\n\n/**\n * Timezone plugin functions, registered via `pluginFunctions` (not\n * `VMBuiltins.ts`'s shared `builtinFunctions` registry), since this logic\n * is genuinely Time-package-specific (city/IANA-zone lookups), unlike the\n * generic math functions (`gcd`, `sqrt`, ...) that belong in the shared\n * registry. Matches `examples/osrs`'s `OsrsParselet.ts` pattern: a stable\n * package-local name per function, `CALL_PLUGIN` emitted by that name\n * (`builder.emitPluginCall(name, argCount)`) in the parselet, the actual\n * handler registered in `TimePackage.ts`'s `pluginFunctions` field.\n */\n\nexport const ZONE_CONVERT_FN = \"zoneConvert\";\n/** Plugin name for `time in <zone>`, the current wall clock there. */\nexport const TIME_IN_ZONE_FN = \"timeInZone\";\n/** Plugin name for `date in <zone>`, which can differ from the local date. */\nexport const DATE_IN_ZONE_FN = \"dateInZone\";\n/** Plugin name for the offset between two zones, as a duration. */\nexport const TIME_DIFFERENCE_FN = \"timeDifference\";\n\n/**\n * `<clock-time> <sourceZone> in <targetZone>` -> targetZone's wall-clock\n * for that instant, e.g. \"6pm Sydney in Chicago\" -> \"1:00 AM (-1 day)\".\n * Anchored to today's (system-local) calendar date, matching\n * `ClockTimeParselet`'s own \"today\" convention for the bare (no zone)\n * form.\n */\nexport function zoneConvertHandler(args: Value[]): Value {\n const totalMinutes = args[0].toNumber();\n const sourceZoneRef = args[1].value as string;\n const targetZoneRef = args[2].value as string;\n\n const today = new Date();\n const utcMs = zonedWallClockToUtcMs(\n today.getFullYear(), today.getMonth(), today.getDate(),\n Math.floor(totalMinutes / 60), totalMinutes % 60,\n sourceZoneRef,\n );\n\n const time = formatTimeInZone(utcMs, targetZoneRef);\n const dayShift = dayShiftBetweenZones(utcMs, targetZoneRef, sourceZoneRef);\n if (dayShift === 0) return stringValue(time);\n const sign = dayShift > 0 ? \"+\" : \"\";\n return stringValue(`${time} (${sign}${dayShift} day${Math.abs(dayShift) === 1 ? \"\" : \"s\"})`);\n}\n\n/** `time in <city>` -> that zone's current wall-clock time, e.g. \"3:45 PM\". */\nexport function timeInZoneHandler(args: Value[]): Value {\n const zoneRef = args[0].value as string;\n return stringValue(formatTimeInZone(Date.now(), zoneRef));\n}\n\n/** `date in <city>` -> that zone's current calendar date, e.g. \"July 31, 2026\". */\nexport function dateInZoneHandler(args: Value[]): Value {\n const zoneRef = args[0].value as string;\n return stringValue(formatDateInZone(Date.now(), zoneRef));\n}\n\n/**\n * `time difference between <city1> and <city2>` -> a directional,\n * human-readable offset delta, e.g. \"Moscow is 8 hours ahead of Seattle\".\n * Computed at the current instant, a zone's offset can shift across a\n * DST transition, so this is a live \"right now\" answer, not a fixed\n * constant.\n *\n * Takes 4 args: [zoneRef1, zoneRef2, displayName1, displayName2]. The\n * display names are the user's OWN typed text (see\n * `ZoneReference.ts`'s `displayName`), not derived from the resolved\n * zone, \"Seattle\" and \"Los Angeles\" both resolve to the same IANA zone\n * (`America/Los_Angeles`), and deriving the label from the zone id alone\n * would silently rename whichever one the user didn't type.\n */\nexport function timeDifferenceHandler(args: Value[]): Value {\n const zoneRef1 = args[0].value as string;\n const zoneRef2 = args[1].value as string;\n const label1 = args[2].value as string;\n const label2 = args[3].value as string;\n const now = Date.now();\n\n const offset1 = resolveOffsetMinutes(zoneRef1, now);\n const offset2 = resolveOffsetMinutes(zoneRef2, now);\n const diff = offset2 - offset1;\n\n if (diff === 0) {\n return stringValue(`${label2} and ${label1} currently share the same UTC offset`);\n }\n const ahead = diff > 0 ? label2 : label1;\n const behind = diff > 0 ? label1 : label2;\n const absDiff = Math.abs(diff);\n const hours = Math.floor(absDiff / 60);\n const minutes = absDiff % 60;\n const parts: string[] = [];\n if (hours > 0) parts.push(`${hours} hour${hours === 1 ? \"\" : \"s\"}`);\n if (minutes > 0) parts.push(`${minutes} minute${minutes === 1 ? \"\" : \"s\"}`);\n return stringValue(`${ahead} is ${parts.join(\" \")} ahead of ${behind}`);\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\nimport { tryConsumeZoneReference } from \"./shared/ZoneReference\";\nimport { ZONE_CONVERT_FN } from \"./TimezonePluginFunctions\";\n\n/**\n * `9:00am` / `16:00` / `4pm`, a clock-time-of-day literal, anchored to\n * today's calendar date (see `OpCode.CLOCK_TIME_TODAY` in `vm/VM.ts`).\n * Handles the fused `CLOCK_TIME` token produced by\n * {@link clockTimeNormalizerRule} (token value = total minutes since\n * midnight, as a decimal string).\n *\n * Also handles the optional timezone-conversion suffix, `<clock-time>\n * <sourceZone> in <targetZone>` (e.g. \"6pm Sydney in Chicago\"). See\n * {@link tryConsumeZoneReference}. The suffix is entirely optional and\n * only consumes tokens once a recognized zone name is actually found\n * immediately after the clock-time literal, so the plain\n * `CLOCK_TIME_TODAY` path below is completely unchanged when no suffix is\n * present. A recognized source zone with no following \"in <target>\" is a\n * parse error (not silently ignored), once a zone name is consumed\n * there's no going back given this parser has no backtracking.\n */\nexport class ClockTimeParselet implements PrefixParselet {\n\treadonly category = \"Time\";\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n const totalMinutes = parseInt(token.value, 10);\n const sourceZoneRef = tryConsumeZoneReference(parser);\n\n if (sourceZoneRef !== null) {\n if (parser.peek()?.type !== \"IN\") {\n throw ErrorFactory.parsing(\n \"TIME_ZONE_EXPECTED_IN\",\n `Expected \"in <city>\" after the zone name (e.g. \"6pm Sydney in Chicago\") but got ${parser.peek() ? `\"${parser.peek()!.value}\"` : \"end of input\"}`,\n );\n }\n parser.consume(); // \"in\"\n const target = tryConsumeZoneReference(parser);\n if (target === null) {\n throw ErrorFactory.parsing(\n \"TIME_ZONE_EXPECTED_TARGET\",\n `Expected a city or zone name after \"in\" (e.g. \"6pm Sydney in Chicago\")`,\n );\n }\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(totalMinutes);\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(sourceZoneRef.zoneRef);\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(target.zoneRef);\n builder.emitPluginCall(ZONE_CONVERT_FN, 3);\n return;\n }\n\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(totalMinutes);\n builder.emitOpcode(OpCode.CLOCK_TIME_TODAY);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * `7:30 to 20:45` / `4pm to 3am`, the duration between two clock times\n * rolling over midnight when the end is earlier than the start (`4pm to\n * 3am` -> 11 hours, not a negative duration). Handles the fused\n * `CLOCK_TIME_INTERVAL` token produced by\n * {@link clockTimeIntervalNormalizerRule} (value = `\"<startMin>:<endMin>\"`).\n *\n * The rollover-aware subtraction happens here at parse time (both\n * endpoints are already known integers baked into the fused token)\n * no VM opcode needed, just plain arithmetic producing a duration `Uom`.\n */\nexport class ClockTimeIntervalParselet implements PrefixParselet {\n\treadonly category = \"Time\";\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n const [startStr, endStr] = token.value.split(\":\");\n const start = parseInt(startStr, 10);\n const end = parseInt(endStr, 10);\n const durationMinutes = end >= start ? end - start : (1440 - start) + end;\n\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(durationMinutes);\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(\"minutes\");\n builder.emitOpcode(OpCode.UOM_CONVERT);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * `30 fps`, a frame-rate literal, constructed directly as a\n * `Rate(N, \"frames\", \"s\")` value (see `vm/Value.ts`'s `rateValue()`) so\n * `30 fps × 3 minutes` -> `5,400 frames` works via the existing\n * `RATE_MUL` opcode with zero fps-specific VM logic. Handles the fused\n * `FPS_RATE` token produced by {@link fpsRateNormalizerRule}.\n */\nexport class FpsRateParselet implements PrefixParselet {\n\treadonly category = \"Time\";\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(parseFloat(token.value));\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(\"frames/s\");\n builder.emitOpcode(OpCode.UOM_CONVERT);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * `03:04:05` / `00:00:01.5`, a lap-time/stopwatch-split duration, in\n * seconds. Handles the fused `LAPTIME` token produced by\n * {@link laptimeNormalizerRule} (value = total seconds, as a decimal\n * string). Represented as `Uom(totalSeconds, \"s\")`, so laptime arithmetic\n * (`03:04:05 + 01:02:03`) works via the existing generic ADD/SUB opcodes\n *, both operands share the exact same unit string, the fast path in\n * `VMConversion.ts`'s `unifyUom()`.\n */\nexport class LaptimeParselet implements PrefixParselet {\n\treadonly category = \"Time\";\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(parseFloat(token.value));\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(\"s\");\n builder.emitOpcode(OpCode.UOM_CONVERT);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\nimport { tryConsumeZoneReference } from \"./shared/ZoneReference\";\n\n/**\n * `time in <city>` -> that zone's current wall-clock time. `date in\n * <city>` -> that zone's current calendar date. One parameterized\n * parselet for both, triggered on the fused `TIME_IN`/`DATE_IN` token\n * (see `TimePackage.ts`'s `phrases` field), \"time\"/\"date\" are common\n * variable names, so the trigger is the full two-word phrase, not the\n * bare word (same reasoning as `MathPhrasesPackage.ts`'s \"average of\"\n * pattern: fusing the phrase means the bare word stays a plain,\n * still-usable `IDENT`).\n */\nexport function timeOrDateInZoneParselet(pluginFnName: string): PrefixParselet {\n return {\n category: \"Time\",\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n const zone = tryConsumeZoneReference(parser);\n if (zone === null) {\n throw ErrorFactory.parsing(\n \"TIME_ZONE_EXPECTED_CITY\",\n `Expected a city or zone name after \"${token.value}\" (e.g. \"time in Paris\")`,\n );\n }\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(zone.zoneRef);\n builder.emitPluginCall(pluginFnName, 1);\n },\n };\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\nimport { tryConsumeZoneReference } from \"./shared/ZoneReference\";\nimport { TIME_DIFFERENCE_FN } from \"./TimezonePluginFunctions\";\n\n/**\n * `time difference between <city1> and <city2>` -> a directional,\n * human-readable UTC-offset delta (see `timeDifferenceHandler` in\n * `TimezonePluginFunctions.ts`).\n *\n * Triggered on the fused `TIME_DIFFERENCE_BETWEEN` token (the full\n * three-word phrase, fused by `TimePackage.ts`'s `phrases` field)\n * \"difference\" alone is a plausible variable name, so (matching this\n * package's other zone-query grammars) the trigger is the complete\n * phrase, not the bare word.\n *\n * \"and\" lexes as `PLUS`, not a literal \"AND\" token. See\n * `ConditionalsPackage.ts`'s doc comment. The first zone reference is\n * read via `tryConsumeZoneReference`, which only ever consumes a single\n * token (or a short, self-contained `GMT+N` sequence), never an\n * open-ended `expr`, so there's no risk of it swallowing the \"and\", the\n * bindingPower guard other packages need for this collision doesn't\n * apply here.\n */\nexport class TimeDifferenceParselet implements PrefixParselet {\n readonly category = \"Time\";\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n const zone1 = tryConsumeZoneReference(parser);\n if (zone1 === null) {\n throw ErrorFactory.parsing(\n \"TIME_DIFFERENCE_EXPECTED_CITY\",\n `Expected a city or zone name after \"time difference between\" (e.g. \"time difference between Seattle and Moscow\")`,\n );\n }\n parser.consume(\"AND_CONJ\"); // \"and\"\n const zone2 = tryConsumeZoneReference(parser);\n if (zone2 === null) {\n throw ErrorFactory.parsing(\n \"TIME_DIFFERENCE_EXPECTED_SECOND_CITY\",\n `Expected a second city or zone name after \"and\" (e.g. \"time difference between Seattle and Moscow\")`,\n );\n }\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(zone1.zoneRef);\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(zone2.zoneRef);\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(zone1.displayName);\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(zone2.displayName);\n builder.emitPluginCall(TIME_DIFFERENCE_FN, 4);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\nimport { timecodeUnit } from \"@solve-js/vm/Value\";\n\n/**\n * `HH:MM:SS:FF at <N> fps` / `HH:MM:SS:FF @ <N> fps`, a video-timecode\n * literal carrying its own fps context, constructed as\n * `Uom(totalFrames, \"timecode@<fps>\")` (see `vm/Value.ts`'s timecode\n * section for the representation, and why storing the TOTAL frame count\n * rather than four separate fields makes carry/borrow arithmetic free).\n *\n * Handles the fused `VIDEO_TIMECODE` token produced by\n * {@link videoTimecodeNormalizerRule} (the raw `HH:MM:SS:FF` shape) and\n * then looks ahead, the same pattern `ClockTimeParselet.ts` uses for its\n * optional trailing timezone suffix, for the REQUIRED frame-rate\n * specifier and an OPTIONAL trailing `in frames` query:\n *\n * - `at`/`@` (lexes as `RATE_AT`/`AT` respectively) followed by an `N fps`\n * literal (already fused to `FPS_RATE` by `fpsRateNormalizerRule` before\n * this parselet ever runs) is REQUIRED, a video timecode has no\n * meaning without a frame rate. Both separators are accepted uniformly;\n * \"@\" activates a previously-dormant lexer token (see\n * `ExpressionLexer.ts`'s `OP_MAP` doc comment) specifically for this\n * feature.\n * - An optional trailing `in frames` re-tags the value as a plain\n * `Uom(totalFrames, \"frames\")` instead of `\"timecode@<fps>\"`, cheap\n * since the NUMERIC value is identical either way; only the unit string\n * differs (no runtime conversion opcode needed for this. See the\n * `UOM_CONVERT` emission below, which just tags a number with a unit,\n * the same \"PUSH_NUMBER; PUSH_STRING; UOM_CONVERT\" pattern\n * `FpsRateParselet.ts`/`LaptimeParselet.ts` already use for their own\n * literal construction).\n *\n * Once \"at\"/\"@\" is consumed, there's no backtracking (this parser doesn't\n * support it. See `ClockTimeParselet.ts`'s own doc comment for the same\n * accepted constraint), a malformed suffix throws rather than silently\n * reinterpreting the tokens some other way.\n */\nexport class VideoTimecodeParselet implements PrefixParselet {\n readonly category = \"Time\";\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n const [hours, minutes, seconds, frames] = token.value.split(\",\").map(Number);\n\n const separator = parser.peek();\n if (!separator || (separator.type !== \"RATE_AT\" && separator.type !== \"AT\")) {\n throw ErrorFactory.parsing(\n \"TIMECODE_EXPECTED_FPS\",\n `A video timecode must specify a frame rate, e.g. \"${token.value.split(\",\").join(\":\")} at 30 fps\" or \"... @ 30 fps\"`\n );\n }\n parser.consume();\n\n const fpsToken = parser.peek();\n if (!fpsToken || fpsToken.type !== \"FPS_RATE\") {\n throw ErrorFactory.parsing(\n \"TIMECODE_EXPECTED_FPS\",\n `Expected \"<number> fps\" after \"${separator.value}\" (e.g. \"at 30 fps\")`\n );\n }\n parser.consume();\n const fps = parseFloat(fpsToken.value);\n\n // Frames per timecode SECOND, which is the rounded frame rate rather\n // than the exact one: non-drop-frame timecode at 29.97 fps still\n // labels thirty frames per second (that mismatch with wall-clock time\n // is the whole reason drop-frame notation exists). This is the same\n // bucketing `framesToTimecodeString()` uses to go the other way, and\n // the two directions have to agree or a timecode does not survive a\n // round trip.\n //\n // Flooring instead, which is what this did, was wrong twice over at a\n // fractional rate: it rejected frame 29 at 29.97 fps as out of range\n // when 0-29 is exactly the valid set, and multiplying the elapsed\n // seconds by the raw 29.97 made \"01:00:00:00 at 29.97 fps\" come back\n // out as \"00:59:56:12\".\n const fpsWhole = Math.round(fps);\n if (frames >= fpsWhole) {\n throw ErrorFactory.parsing(\n \"TIMECODE_FRAME_OUT_OF_RANGE\",\n `Frame number ${frames} is out of range for ${fps} fps (must be 0-${fpsWhole - 1})`\n );\n }\n\n const totalFrames = (hours * 3600 + minutes * 60 + seconds) * fpsWhole + frames;\n\n // Optional trailing \"in frames\", query the total frame count directly\n // rather than tagging the result with its fps context.\n let unit = timecodeUnit(fps);\n if (parser.peek()?.type === \"IN\") {\n parser.consume();\n const framesWord = parser.peek();\n if (!framesWord || framesWord.type !== \"IDENT\" || framesWord.value.toLowerCase() !== \"frames\") {\n throw ErrorFactory.parsing(\n \"TIMECODE_EXPECTED_FRAMES\",\n `Expected \"frames\" after \"in\" (e.g. \"... in frames\") but got ${framesWord ? `\"${framesWord.value}\"` : \"end of input\"}`\n );\n }\n parser.consume();\n unit = \"frames\";\n }\n\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(totalFrames);\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(unit);\n builder.emitOpcode(OpCode.UOM_CONVERT);\n }\n}\n","/**\n * Video-timecode display formatting, converting a total frame count back\n * into `HH:MM:SS:FF` notation. The reverse direction (`HH:MM:SS:FF` ->\n * total frames) is simple arithmetic done inline in\n * `parselets/VideoTimecodeParselet.ts` (`((h*3600 + m*60 + s) * fps) + f`);\n * this file only has the \"put the carry back\" direction, matching\n * `timezones/ZoneMath.ts`'s \"compute then return a formatted String Value\"\n * pattern for the same kind of display-only conversion.\n */\n\n/**\n * Convert a total frame count (at a given fps) into `HH:MM:SS:FF` display\n * notation.\n *\n * SCOPE DECISION: buckets frames-per-second by `Math.round(fps)`\n * regardless of whether fps is a whole number (e.g. NTSC's 29.97fps).\n * This is plain non-drop-frame timecode math, exact for integer fps\n * (24/25/30/60/...) and a reasonable, clearly-scoped-down approximation\n * for fractional fps. It does NOT implement SMPTE drop-frame timecode\n * notation, which periodically skips frame NUMBERS (not actual frames) to\n * keep 29.97fps timecode approximately in sync with wall-clock time\n * that's a real, separate piece of the SMPTE spec this pass doesn't take\n * on, matching this session's established pattern of documenting a\n * deliberate simplification rather than silently guessing.\n */\nexport function framesToTimecodeString(totalFrames: number, fps: number): string {\n const fpsWhole = Math.max(1, Math.round(fps));\n const pad = (n: number) => String(n).padStart(2, \"0\");\n\n const frameCount = Math.trunc(totalFrames);\n // Modulo can return a negative result in JS for a negative dividend\n // the double-modulo pattern keeps the frame field in [0, fpsWhole).\n const frames = ((frameCount % fpsWhole) + fpsWhole) % fpsWhole;\n const totalSeconds = Math.floor(frameCount / fpsWhole);\n\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n\n return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}:${pad(frames)}`;\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\nimport { framesToTimecodeString } from \"../timecode/TimecodeMath\";\n\n/**\n * `<N> frames`, a plain frame-count duration, `Uom(N, \"frames\")`. Also\n * the reverse of {@link VideoTimecodeParselet}: `<N> frames @ <fps>` (or\n * `... at <fps>`) converts the frame count back into `HH:MM:SS:FF`\n * display notation, producing a formatted STRING value (matches\n * `timezones/ZoneMath.ts`'s \"compute then return a formatted String\n * Value\" pattern for the same kind of display-only conversion).\n *\n * Handles the fused `FRAME_COUNT` token produced by\n * {@link frameCountNormalizerRule}. Both `N` and the fps are always\n * parse-time-literal numbers by construction of that fusion rule (mirrors\n * `FpsRateParselet.ts`'s \"NUMBER fps\" fusion, which has the same\n * restriction), so the `HH:MM:SS:FF` string is computed directly at\n * parse time rather than needing a runtime `CALL_PLUGIN` handler.\n */\nexport class FrameCountParselet implements PrefixParselet {\n readonly category = \"Time\";\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n const frameCount = parseFloat(token.value);\n\n const separator = parser.peek();\n if (separator && (separator.type === \"RATE_AT\" || separator.type === \"AT\")) {\n parser.consume();\n const fpsToken = parser.peek();\n if (!fpsToken || fpsToken.type !== \"FPS_RATE\") {\n throw ErrorFactory.parsing(\n \"TIMECODE_EXPECTED_FPS\",\n `Expected \"<number> fps\" after \"${separator.value}\" (e.g. \"@ 30 fps\")`\n );\n }\n parser.consume();\n const fps = parseFloat(fpsToken.value);\n\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(framesToTimecodeString(frameCount, fps));\n return;\n }\n\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(frameCount);\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(\"frames\");\n builder.emitOpcode(OpCode.UOM_CONVERT);\n }\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\nimport { isInsideRangeContext } from \"@solve-js/normalizer/BuiltinNormalizerRules\";\n\n/**\n * Converts an hour[:minute] + optional am/pm marker into total\n * minutes-since-midnight (0-1439), or `null` if the combination is out of\n * range for whichever clock convention applies:\n * - With an am/pm marker: 12-hour clock, hour must be 1-12 (`12am`\n * midnight -> 0, `12pm` noon -> 720).\n * - Without: 24-hour clock, hour must be 0-23.\n */\nfunction computeTotalMinutes(hour: number, minute: number, ampm: string | undefined): number | null {\n if (minute < 0 || minute > 59) return null;\n if (ampm) {\n if (hour < 1 || hour > 12) return null;\n const isPM = ampm.toLowerCase() === \"pm\";\n const hour24 = (hour % 12) + (isPM ? 12 : 0);\n return hour24 * 60 + minute;\n }\n if (hour < 0 || hour > 23) return null;\n return hour * 60 + minute;\n}\n\nfunction isAmPmToken(token: { type: string; value: string } | undefined): token is { type: string; value: string } {\n return !!token && token.type === \"IDENT\" && /^(am|pm)$/i.test(token.value);\n}\n\n/**\n * Fuses a clock-time-of-day literal into a single `CLOCK_TIME` prefix\n * token carrying total minutes-since-midnight as its value (a plain\n * decimal string, e.g. \"540\" for 9:00am).\n *\n * Two source shapes:\n * - `NUMBER COLON NUMBER [am|pm]`, `9:00am`, `9:00 am`, `16:00` (24h\n * no am/pm).\n * - `NUMBER am|pm`, the bare-hour form, `4pm`.\n *\n * \"am\"/\"pm\" are matched as plain `IDENT` tokens (case-insensitively)\n * deliberately NOT added to the locale keywordMap, so this doesn't change\n * lexing for \"am\"/\"pm\" used as ordinary identifiers/variable names\n * anywhere else; the pattern only fires when one immediately follows a\n * number (optionally via `H:MM`), which is a narrow, low-collision-risk\n * trigger. Not a general per-word keyword change like the datetime\n * package's weekday names.\n */\nexport function clockTimeNormalizerRule(priority = 65): NormalizerRule {\n return {\n name: \"time:clock-time\",\n priority,\n match(tokens, pos): NormalizerMatch | null {\n // A clock time inside `[...]` (matrix literal/index/slice) has no\n // legitimate meaning, reserve bare `NUMBER:NUMBER` there for a\n // matrix range instead (see isInsideRangeContext's own doc comment).\n if (isInsideRangeContext(tokens, pos)) return null;\n const hourToken = tokens[pos];\n if (hourToken.type !== \"NUMBER\") return null;\n const hour = parseInt(hourToken.value, 10);\n if (isNaN(hour) || hour < 0 || hour > 23) return null;\n\n // Pattern: NUMBER COLON NUMBER [am|pm]\n const colonToken = tokens[pos + 1];\n const minuteToken = tokens[pos + 2];\n if (colonToken?.type === \"COLON\" && minuteToken?.type === \"NUMBER\") {\n const minute = parseInt(minuteToken.value, 10);\n const ampmToken = tokens[pos + 3];\n const hasAmPm = isAmPmToken(ampmToken);\n const totalMinutes = computeTotalMinutes(hour, minute, hasAmPm ? ampmToken!.value : undefined);\n if (totalMinutes === null) return null;\n const consumed = hasAmPm ? 4 : 3;\n return {\n consumed,\n replacement: [createFusedToken(\"CLOCK_TIME\", String(totalMinutes), tokens.slice(pos, pos + consumed))],\n ruleName: \"time:clock-time\",\n };\n }\n\n // Pattern: NUMBER am|pm (bare hour, e.g. \"4pm\")\n const bareAmPmToken = tokens[pos + 1];\n if (isAmPmToken(bareAmPmToken)) {\n const totalMinutes = computeTotalMinutes(hour, 0, bareAmPmToken.value);\n if (totalMinutes === null) return null;\n return {\n consumed: 2,\n replacement: [createFusedToken(\"CLOCK_TIME\", String(totalMinutes), tokens.slice(pos, pos + 2))],\n ruleName: \"time:clock-time\",\n };\n }\n\n return null;\n },\n };\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\n/**\n * Fuses `CLOCK_TIME TO CLOCK_TIME` (e.g. `7:30 to 20:45`) into a single\n * `CLOCK_TIME_INTERVAL` prefix token carrying both endpoints as\n * `\"<startMinutes>:<endMinutes>\"`.\n *\n * Why fuse instead of an infix parselet on `TO`: `TO` is already claimed\n * by `PercentageChangeParselet` (`800 to 1000` percentage change) and\n * UOM's unit-conversion handling (`10 cm to m`), an infix parselet\n * registered here would silently collide with one of those (last\n * registration wins). Fusing at the normalizer stage, keyed on the\n * specific `CLOCK_TIME TO CLOCK_TIME` token shape, means `TO`'s existing\n * infix dispatch never even sees this case. This relies on\n * {@link clockTimeNormalizerRule} having already run in an earlier\n * normalizer pass to produce the `CLOCK_TIME` tokens this rule looks\n * for, the engine's `TokenNormalizer.normalize()` is multi-pass\n * specifically to support this kind of cascading fusion.\n *\n * Only the unambiguous `to` form is handled, SoulverCore's own docs flag\n * a bare `-` between two clock times as genuinely ambiguous (`5pm - 7pm`\n * read as a range vs. `5pm - 2pm` read as subtraction) and recommend\n * `to`; this package does the same rather than guessing.\n */\nexport function clockTimeIntervalNormalizerRule(priority = 66): NormalizerRule {\n return {\n name: \"time:clock-time-interval\",\n priority,\n match(tokens, pos): NormalizerMatch | null {\n const start = tokens[pos];\n const to = tokens[pos + 1];\n const end = tokens[pos + 2];\n if (start.type !== \"CLOCK_TIME\") return null;\n if (to?.type !== \"TO\") return null;\n if (end?.type !== \"CLOCK_TIME\") return null;\n\n return {\n consumed: 3,\n replacement: [createFusedToken(\"CLOCK_TIME_INTERVAL\", `${start.value}:${end.value}`, tokens.slice(pos, pos + 3))],\n ruleName: \"time:clock-time-interval\",\n };\n },\n };\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\n/**\n * Fuses `NUMBER fps` (e.g. `30 fps`) into a single `FPS_RATE` prefix\n * token carrying the frame rate as its value.\n *\n * \"fps\" is matched as a plain `IDENT` token (case-insensitive), the same\n * approach {@link clockTimeNormalizerRule} uses for \"am\"/\"pm\", NOT\n * added to the locale keywordMap or `lexerVocabulary.units`, so it\n * doesn't change lexing for \"fps\" used elsewhere, and doesn't collide\n * with the shared `UomLiteralParselet` that owns generic `UNIT` tokens\n * (which has no concept of a rate, \"30 fps\" would otherwise become a\n * plain `Uom(30, \"fps\")`, not the `Rate(30, \"frames\", \"s\")` this domain\n * actually needs for `30 fps × 3 minutes` -> `5,400 frames`).\n */\nexport function fpsRateNormalizerRule(priority = 65): NormalizerRule {\n return {\n name: \"time:fps-rate\",\n priority,\n match(tokens, pos): NormalizerMatch | null {\n const numberToken = tokens[pos];\n const fpsToken = tokens[pos + 1];\n if (numberToken.type !== \"NUMBER\") return null;\n if (fpsToken?.type !== \"IDENT\" || fpsToken.value.toLowerCase() !== \"fps\") return null;\n\n return {\n consumed: 2,\n replacement: [createFusedToken(\"FPS_RATE\", numberToken.value, tokens.slice(pos, pos + 2))],\n ruleName: \"time:fps-rate\",\n };\n },\n };\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\nimport { isInsideRangeContext } from \"@solve-js/normalizer/BuiltinNormalizerRules\";\n\n/**\n * Fuses a lap-time / stopwatch-split literal, `HH:MM:SS[.f]`, always\n * TWO colons, into a single `LAPTIME` prefix token carrying total\n * seconds (fractional-precision) as its value.\n *\n * A laptime is a DURATION, not a point in time (unlike clock-time's one\n * colon), SoulverCore's own docs note \"a laptime must include two\n * colons so Soulver can distinguish it from a clock time,\" which this\n * rule mirrors: it must run at HIGHER priority than\n * {@link clockTimeNormalizerRule} so `03:04:05` gets a chance to match\n * all three number groups before the clock-time rule greedily fuses just\n * the first two.\n */\nexport function laptimeNormalizerRule(priority = 70): NormalizerRule {\n return {\n name: \"time:laptime\",\n priority,\n match(tokens, pos): NormalizerMatch | null {\n // See ClockTimeNormalizerRule's identical guard, a laptime inside\n // `[...]` has no legitimate meaning; reserved for matrix ranges.\n if (isInsideRangeContext(tokens, pos)) return null;\n const h = tokens[pos];\n const c1 = tokens[pos + 1];\n const m = tokens[pos + 2];\n const c2 = tokens[pos + 3];\n const s = tokens[pos + 4];\n if (h.type !== \"NUMBER\") return null;\n if (c1?.type !== \"COLON\") return null;\n if (m?.type !== \"NUMBER\") return null;\n if (c2?.type !== \"COLON\") return null;\n if (s?.type !== \"NUMBER\") return null;\n\n const hours = parseInt(h.value, 10);\n const minutes = parseInt(m.value, 10);\n const seconds = parseFloat(s.value);\n if (isNaN(hours) || isNaN(minutes) || isNaN(seconds)) return null;\n if (minutes < 0 || minutes > 59 || seconds < 0 || seconds >= 60) return null;\n\n const totalSeconds = hours * 3600 + minutes * 60 + seconds;\n return {\n consumed: 5,\n replacement: [createFusedToken(\"LAPTIME\", String(totalSeconds), tokens.slice(pos, pos + 5))],\n ruleName: \"time:laptime\",\n };\n },\n };\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\nimport { isInsideRangeContext } from \"@solve-js/normalizer/BuiltinNormalizerRules\";\n\n/**\n * Fuses a video-timecode literal, `HH:MM:SS:FF`, always THREE colons\n * into a single `VIDEO_TIMECODE` prefix token carrying its four numeric\n * fields as a comma-joined value (e.g. \"1,2,3,4\" for \"01:02:03:04\").\n *\n * Must run at HIGHER priority than {@link laptimeNormalizerRule} (a\n * lap-time is `HH:MM:SS`, TWO colons) so a genuine 4-group timecode gets a\n * chance to match all four number groups before the lap-time rule\n * greedily fuses just the first three. Same reasoning\n * `laptimeNormalizerRule`'s own doc comment gives for outranking\n * `clockTimeNormalizerRule`.\n *\n * The frame-rate (\"at 30 fps\" / \"@ 30 fps\") is NOT part of this fusion\n * it's consumed by {@link VideoTimecodeParselet} via lookahead after this\n * token, the same way {@link ClockTimeParselet} optionally consumes a\n * trailing timezone-conversion suffix. Requiring the fps to be specified\n * is enforced at parse time (a video timecode has no meaning without a\n * frame rate, SoulverCore requires it too), not here.\n */\nexport function videoTimecodeNormalizerRule(priority = 75): NormalizerRule {\n return {\n name: \"time:video-timecode\",\n priority,\n match(tokens, pos): NormalizerMatch | null {\n // See ClockTimeNormalizerRule's identical guard, a video timecode\n // inside `[...]` has no legitimate meaning; reserved for matrix ranges.\n if (isInsideRangeContext(tokens, pos)) return null;\n const h = tokens[pos];\n const c1 = tokens[pos + 1];\n const m = tokens[pos + 2];\n const c2 = tokens[pos + 3];\n const s = tokens[pos + 4];\n const c3 = tokens[pos + 5];\n const f = tokens[pos + 6];\n\n if (h?.type !== \"NUMBER\") return null;\n if (c1?.type !== \"COLON\") return null;\n if (m?.type !== \"NUMBER\") return null;\n if (c2?.type !== \"COLON\") return null;\n if (s?.type !== \"NUMBER\") return null;\n if (c3?.type !== \"COLON\") return null;\n if (f?.type !== \"NUMBER\") return null;\n\n const hours = parseInt(h.value, 10);\n const minutes = parseInt(m.value, 10);\n const seconds = parseInt(s.value, 10);\n const frames = parseInt(f.value, 10);\n if ([hours, minutes, seconds, frames].some((n) => isNaN(n))) return null;\n if (minutes < 0 || minutes > 59) return null;\n if (seconds < 0 || seconds > 59) return null;\n if (hours < 0 || frames < 0) return null;\n\n return {\n consumed: 7,\n replacement: [\n createFusedToken(\"VIDEO_TIMECODE\", `${hours},${minutes},${seconds},${frames}`, tokens.slice(pos, pos + 7)),\n ],\n ruleName: \"time:video-timecode\",\n };\n },\n };\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\n/**\n * Fuses `NUMBER frames` (e.g. `10 frames`) into a single `FRAME_COUNT`\n * prefix token carrying the frame count as its value.\n *\n * \"frames\" is matched as a plain `IDENT` token (case-insensitive), the\n * same approach {@link fpsRateNormalizerRule} uses for \"fps\" and\n * {@link clockTimeNormalizerRule} uses for \"am\"/\"pm\": deliberately NOT\n * added to the locale keywordMap or `lexer/units.ts`'s known-unit set, so\n * this doesn't change lexing for \"frames\" used elsewhere (e.g. as a\n * variable name), and the fusion only fires when it immediately follows a\n * number, a narrow, low-collision-risk trigger, not a general keyword\n * change.\n *\n * Backs BOTH the `<N> frames @ <fps>` -> timecode-string conversion (see\n * {@link FrameCountParselet}) AND plain `Uom(N, \"frames\")` duration values\n * used as the right-hand side of timecode arithmetic (`timecode + N\n * frames`. See `vm/VM.ts`'s `combineTimecode()`), since a bare `FRAME_COUNT`\n * with no trailing \"@ fps\" just becomes the latter.\n */\nexport function frameCountNormalizerRule(priority = 65): NormalizerRule {\n return {\n name: \"time:frame-count\",\n priority,\n match(tokens, pos): NormalizerMatch | null {\n const numberToken = tokens[pos];\n const framesToken = tokens[pos + 1];\n if (numberToken.type !== \"NUMBER\") return null;\n if (framesToken?.type !== \"IDENT\" || framesToken.value.toLowerCase() !== \"frames\") return null;\n\n return {\n consumed: 2,\n replacement: [createFusedToken(\"FRAME_COUNT\", numberToken.value, tokens.slice(pos, pos + 2))],\n ruleName: \"time:frame-count\",\n };\n },\n };\n}\n","import { Value, ValueType, stringValue } from \"@solve-js/vm/Value\";\nimport { UNIT_TABLE } from \"@solve-js/uom/generated/UnitTable.generated\";\n\n/**\n * `as timespan` and `as laptime`, the two ways of writing a duration out.\n *\n * 5.5 minutes as timespan 5 min 30 s\n * 72 days as timespan 10 weeks 2 days\n * 5.5 minutes as laptime 00:05:30\n * 03:04:05 as timespan 3 hours 4 minutes 5 seconds\n *\n * The parity audit credited both of these to `packages/time`, where the only\n * occurrence of the word \"timespan\" was a doc comment. Nothing was registered\n * and both returned \"Unknown converter\".\n *\n * They are the inverse of a compound quantity: that rule collapses\n * `3 hours 5 minutes` into one number, and these expand one number back into\n * the parts a person would say.\n */\n\n/** The time measure's kind in UNIT_TABLE. Its base unit is the second. */\nconst TIME_KIND = 14;\n\n/** Descending units a timespan is broken into, with their length in seconds. */\nconst TIMESPAN_PARTS: readonly (readonly [singular: string, plural: string, seconds: number])[] = [\n\t[\"week\", \"weeks\", 604800],\n\t[\"day\", \"days\", 86400],\n\t[\"hour\", \"hours\", 3600],\n\t[\"minute\", \"minutes\", 60],\n\t[\"second\", \"seconds\", 1],\n];\n\n/**\n * The value in seconds, or null when it is not a duration.\n *\n * A plain number is taken as seconds, which is what `5.5 as timespan` has to\n * mean if it means anything. A unit from another measure returns null so the\n * caller can report an honest error rather than inventing a duration.\n */\nfunction durationSeconds(value: Value): number | null {\n\tif (value.type === ValueType.Number) return value.toNumber();\n\tif (value.type !== ValueType.Uom || value.unit === undefined) return null;\n\tconst entry = UNIT_TABLE[value.unit.toLowerCase()] as readonly [number, number] | undefined;\n\tif (entry === undefined || entry[0] !== TIME_KIND) return null;\n\treturn value.toNumber() * entry[1];\n}\n\n/**\n * `5 min 30 s`: the duration as the parts someone would say out loud.\n *\n * Parts that are zero are skipped, so an exact number of hours reads as\n * \"2 hours\" rather than \"2 hours 0 minutes 0 seconds\". Seconds keep a\n * fractional remainder rather than rounding it away, since dropping it would\n * make the conversion lossy in a way the caller cannot see.\n */\nexport function toTimespanString(value: Value): Value {\n\tconst total = durationSeconds(value);\n\tif (total === null) {\n\t\treturn stringValue(`\"as timespan\" needs a duration, got ${value.unit ?? \"a non-duration\"}`);\n\t}\n\n\tconst negative = total < 0;\n\tlet remaining = Math.abs(total);\n\tconst parts: string[] = [];\n\n\tfor (const [singular, plural, seconds] of TIMESPAN_PARTS) {\n\t\tif (seconds === 1) break;\n\t\tconst count = Math.floor(remaining / seconds);\n\t\tif (count > 0) {\n\t\t\tparts.push(`${count} ${count === 1 ? singular : plural}`);\n\t\t\tremaining -= count * seconds;\n\t\t}\n\t}\n\n\t// Whatever is left is seconds, kept even when fractional. Rounded to\n\t// milliseconds so floating-point division does not print a tail of noise.\n\tconst secondsLeft = Math.round(remaining * 1000) / 1000;\n\tif (secondsLeft > 0 || parts.length === 0) {\n\t\tparts.push(`${secondsLeft} ${secondsLeft === 1 ? \"second\" : \"seconds\"}`);\n\t}\n\n\treturn stringValue((negative ? \"-\" : \"\") + parts.join(\" \"));\n}\n\n/**\n * `00:05:30`: the duration as a stopwatch reading.\n *\n * Always `HH:MM:SS`, with hours allowed to exceed 24 rather than wrapping,\n * because a lap time of twenty-six hours is a real thing to measure and\n * wrapping it to two would be silently wrong.\n */\nexport function toLaptimeString(value: Value): Value {\n\tconst total = durationSeconds(value);\n\tif (total === null) {\n\t\treturn stringValue(`\"as laptime\" needs a duration, got ${value.unit ?? \"a non-duration\"}`);\n\t}\n\n\tconst negative = total < 0;\n\tconst rounded = Math.round(Math.abs(total) * 1000) / 1000;\n\tconst hours = Math.floor(rounded / 3600);\n\tconst minutes = Math.floor((rounded % 3600) / 60);\n\tconst seconds = rounded % 60;\n\n\tconst whole = Math.floor(seconds);\n\tconst fraction = Math.round((seconds - whole) * 1000);\n\tconst base = `${pad(hours)}:${pad(minutes)}:${pad(whole)}`;\n\treturn stringValue((negative ? \"-\" : \"\") + (fraction > 0 ? `${base}.${String(fraction).padStart(3, \"0\")}` : base));\n}\n\n/** Two-digit zero padding. */\nfunction pad(n: number): string {\n\treturn n < 10 ? `0${n}` : String(n);\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { ClockTimeParselet } from \"./parselets/ClockTimeParselet\";\nimport { ClockTimeIntervalParselet } from \"./parselets/ClockTimeIntervalParselet\";\nimport { FpsRateParselet } from \"./parselets/FpsRateParselet\";\nimport { LaptimeParselet } from \"./parselets/LaptimeParselet\";\nimport { timeOrDateInZoneParselet } from \"./parselets/TimeInZoneParselet\";\nimport { TimeDifferenceParselet } from \"./parselets/TimeDifferenceParselet\";\nimport { VideoTimecodeParselet } from \"./parselets/VideoTimecodeParselet\";\nimport { FrameCountParselet } from \"./parselets/FrameCountParselet\";\nimport {\n ZONE_CONVERT_FN, TIME_IN_ZONE_FN, DATE_IN_ZONE_FN, TIME_DIFFERENCE_FN,\n zoneConvertHandler, timeInZoneHandler, dateInZoneHandler, timeDifferenceHandler,\n} from \"./parselets/TimezonePluginFunctions\";\nimport { clockTimeNormalizerRule } from \"./normalizer/ClockTimeNormalizerRule\";\nimport { clockTimeIntervalNormalizerRule } from \"./normalizer/ClockTimeIntervalNormalizerRule\";\nimport { fpsRateNormalizerRule } from \"./normalizer/FpsRateNormalizerRule\";\nimport { laptimeNormalizerRule } from \"./normalizer/LaptimeNormalizerRule\";\nimport { videoTimecodeNormalizerRule } from \"./normalizer/VideoTimecodeNormalizerRule\";\nimport { frameCountNormalizerRule } from \"./normalizer/FrameCountNormalizerRule\";\nimport { MULTI_WORD_CITY_ZONES } from \"./timezones/CityZones\";\n\nimport { toTimespanString, toLaptimeString } from \"./TimespanConverters\";\n\n/**\n * Clock-time-of-day arithmetic: `9:00am`, `16:00`, `4pm` (anchored to\n * today's calendar date), `7:30 to 20:45` / `4pm to 3am` interval\n * durations (midnight-rollover aware), `30 fps` frame-rate literals (a\n * `Rate` value. See `vm/Value.ts`), and `03:04:05` / `00:00:01.5`\n * lap-time/stopwatch-split durations (two colons, vs clock-time's one).\n * Distinct from the Datetime package's calendar-date arithmetic. See\n * `packages/time/` vs `packages/datetime/`.\n *\n * Full HH:MM:SS:FF video-timecode literal parsing + fps-aware carry\n * arithmetic: `01:02:03:04 at 30 fps` / `... @ 30 fps` (a `Uom(totalFrames,\n * \"timecode@<fps>\")` value. See `vm/Value.ts`'s timecode section)\n * `timecode + N frames` / `+ <duration>` / `+ timecode` (sum) / `-\n * timecode` (difference), all special-cased in `vm/VM.ts`'s ADD/SUB\n * dispatch (`combineTimecode()`), plus `timecode in frames` and the\n * reverse `<N> frames @ <fps>` -> `HH:MM:SS:FF` string conversion (see\n * `parselets/VideoTimecodeParselet.ts`/`FrameCountParselet.ts` and\n * `timecode/TimecodeMath.ts`). `30 fps × <duration>` frame-count math\n * (below) composes with this unchanged, the fps literal itself is\n * unaffected by any of the above. Likewise, pretty-printing a duration\n * back as `\"3 hours 15 min\"` (`as timespan`) or `\"03:04:05\"` (`as\n * laptime`) is a `converters` package (Phase 1c) concern. This package\n * only produces the underlying `Uom` values.\n *\n * Also: timezone conversion (`6pm Sydney in Chicago`), current\n * time/date-in-a-zone queries (`time in Paris`, `date in Vancouver`), and\n * zone-offset deltas (`time difference between Seattle and Moscow`). See\n * `timezones/CityZones.ts`/`ZoneMath.ts`. Built entirely on native\n * `Intl.DateTimeFormat`/IANA data, no external dependency. Results are\n * `String` values (formatted, human-readable), not `Datetime`. This\n * engine's `Datetime` representation is a bare epoch-ms number with no\n * zone tag, so there's no way to represent \"this instant, but interpreted\n * through zone X\" as anything other than a pre-formatted string; see\n * `ZoneMath.ts`'s doc comment.\n *\n * SCOPE DECISION: the alternate phrasing `<City> time` (e.g. \"Tokyo\n * time\", city first) is deliberately NOT implemented, it would need\n * \"time\" registered as a keyword reachable from a BARE city identifier's\n * infix position, and \"time\" is exactly the kind of common noun this\n * session found real trouble with (see MathPhrasesPackage.ts's \"total\"\n * regression note). `time in <city>` covers the same need unambiguously.\n */\nexport const TIME_PACKAGE: IEnginePackage = {\n name: \"solve-time\",\n asConverters: {\n // The two ways of writing a duration out. See TimespanConverters.ts.\n timespan: toTimespanString,\n laptime: toLaptimeString,\n lap: toLaptimeString,\n },\n phrases: {\n \"time in\": \"TIME_IN\",\n \"date in\": \"DATE_IN\",\n \"time difference between\": \"TIME_DIFFERENCE_BETWEEN\",\n ...Object.fromEntries(Object.keys(MULTI_WORD_CITY_ZONES).map((phrase) => [phrase, \"CITY_NAME\"])),\n },\n prefixParselets: {\n CLOCK_TIME: new ClockTimeParselet(),\n CLOCK_TIME_INTERVAL: new ClockTimeIntervalParselet(),\n FPS_RATE: new FpsRateParselet(),\n LAPTIME: new LaptimeParselet(),\n TIME_IN: timeOrDateInZoneParselet(TIME_IN_ZONE_FN),\n DATE_IN: timeOrDateInZoneParselet(DATE_IN_ZONE_FN),\n TIME_DIFFERENCE_BETWEEN: new TimeDifferenceParselet(),\n VIDEO_TIMECODE: new VideoTimecodeParselet(),\n FRAME_COUNT: new FrameCountParselet(),\n },\n normalizerRules: [\n clockTimeNormalizerRule(),\n clockTimeIntervalNormalizerRule(),\n fpsRateNormalizerRule(),\n laptimeNormalizerRule(),\n videoTimecodeNormalizerRule(),\n frameCountNormalizerRule(),\n ],\n pluginFunctions: {\n [ZONE_CONVERT_FN]: zoneConvertHandler,\n [TIME_IN_ZONE_FN]: timeInZoneHandler,\n [DATE_IN_ZONE_FN]: dateInZoneHandler,\n [TIME_DIFFERENCE_FN]: timeDifferenceHandler,\n },\n};\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/**\n * A single slot in a phrase-grammar alternative.\n *\n * - `keyword`: consumes exactly one token whose type is in `tokenTypes`. Its\n * matched token is recorded in the `captures` array passed to `emit()`\n * (in slot order). This is how an alternative distinguishes \"between X\n * and Y\" from \"from X to Y\" if it needs to.\n * - `expr`: parses a full sub-expression via `parser.parseExpression()`.\n * Its bytecode is emitted inline, in slot order, exactly like a\n * hand-written parselet calling `parseExpression()` directly. Not\n * recorded in `captures` (there's no single token to record).\n */\nexport type PhraseSlot =\n | { kind: \"keyword\"; tokenTypes: string[] }\n | { kind: \"expr\"; bindingPower?: number };\n\n/** A keyword slot's matched token, recorded for `emit()` to inspect. */\nexport interface PhraseCapture {\n type: string;\n value: string;\n}\n\n/**\n * One alternative phrasing of a pattern. The FIRST slot must be a\n * `keyword` slot. This is what lets {@link definePhrasePattern} choose\n * which alternative applies via a single `parser.peek()`, before\n * consuming or emitting anything. Bytecode is append-only\n * ({@link BytecodeBuilder} has no rollback), so an alternative can only be\n * committed to, never speculatively tried and abandoned, every phrase\n * grammar in this codebase is naturally leading-keyword-disambiguated\n * (that's what makes it parseable as a phrase at all), so this is not a\n * real limitation in practice.\n */\nexport interface PhraseAlternative {\n slots: PhraseSlot[];\n /**\n * Called once every slot has been consumed (and every `expr` slot's\n * bytecode already emitted, in slot order). Emit whatever final\n * opcode(s) turn the already-pushed operands into a result here\n * e.g. `builder.emitOpcode(OpCode.CALL_BUILTIN); builder.emitIndex(...)`.\n */\n emit(builder: BytecodeBuilder, captures: PhraseCapture[]): void;\n}\n\n/**\n * Build a {@link PrefixParselet} from a declarative list of phrase\n * alternatives, instead of hand-writing `parser.consume()`/\n * `parser.parseExpression()` call sequences.\n *\n * Covers the \"keyword slot keyword slot keyword\" shape shared by most of\n * this codebase's phrase-grammar parselets (`roll between X and Y`,\n * `clamp X between Y and Z`, `average of X, Y, Z`'s single-item case,\n * etc.) with one well-tested consumption/error-handling core, rather than\n * each parselet re-deriving its own, the class of bug this is meant to\n * prevent (a hand-rolled parselet mishandling an alternative-phrasing or\n * precedence edge case) is exactly what produced real, shipped bugs this\n * session (Dice's bare-hyphen range, Vector's tuple-vs-group ambiguity).\n *\n * @example\n * ```ts\n * // \"roll between X and Y\" / \"roll from X to Y\"\n * definePhrasePattern({\n * category: \"Dice\",\n * alternatives: [\n * {\n * slots: [\n * { kind: \"keyword\", tokenTypes: [\"BETWEEN\"] },\n * { kind: \"expr\" },\n * { kind: \"keyword\", tokenTypes: [\"AND\"] },\n * { kind: \"expr\" },\n * ],\n * emit: (builder) => {\n * builder.emitOpcode(OpCode.CALL_BUILTIN);\n * builder.emitIndex(DICE_ROLL_BUILTIN);\n * builder.emitIndex(2);\n * },\n * },\n * ],\n * });\n * ```\n */\nexport function definePhrasePattern(opts: {\n category: string;\n alternatives: PhraseAlternative[];\n}): PrefixParselet {\n for (const alt of opts.alternatives) {\n const first = alt.slots[0];\n if (!first || first.kind !== \"keyword\") {\n throw ErrorFactory.config(\n \"INVALID_PHRASE_PATTERN\",\n \"Every PhraseAlternative's first slot must be a `keyword` slot — alternatives are chosen by peeking the next token, which only works if the choice is decidable from one leading keyword.\",\n { category: opts.category }\n );\n }\n }\n\n return {\n category: opts.category,\n parse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n const next = parser.peek();\n const alternative = opts.alternatives.find((alt) => {\n const first = alt.slots[0] as { kind: \"keyword\"; tokenTypes: string[] };\n return next !== undefined && first.tokenTypes.includes(next.type);\n });\n\n if (!alternative) {\n const expected = opts.alternatives\n .map((alt) => (alt.slots[0] as { kind: \"keyword\"; tokenTypes: string[] }).tokenTypes.join(\"|\"))\n .join(\", \");\n throw ErrorFactory.parsing(\n \"NO_MATCHING_PHRASE_ALTERNATIVE\",\n `Expected one of [${expected}] but got \"${next?.type ?? \"end of input\"}\" (\"${next?.value ?? \"\"}\")`,\n { category: opts.category, expected, actualType: next?.type, actualValue: next?.value }\n );\n }\n\n const captures: PhraseCapture[] = [];\n for (const slot of alternative.slots) {\n if (slot.kind === \"keyword\") {\n const token = parser.peek();\n if (!token || !slot.tokenTypes.includes(token.type)) {\n throw ErrorFactory.parsing(\n \"PHRASE_KEYWORD_MISMATCH\",\n `Expected one of [${slot.tokenTypes.join(\"|\")}] but got \"${token?.type ?? \"end of input\"}\" (\"${token?.value ?? \"\"}\")`,\n { category: opts.category, expected: slot.tokenTypes, actualType: token?.type, actualValue: token?.value }\n );\n }\n parser.consume();\n captures.push({ type: token.type, value: token.value });\n } else {\n parser.parseExpression(slot.bindingPower ?? 0, builder);\n }\n }\n\n alternative.emit(builder, captures);\n },\n };\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { definePhrasePattern } from \"@solve-js/parser/PhrasePattern\";\n\n/** CALL_BUILTIN index for diceRoll(from, to). Matches VMBuiltins.ts index 37. */\nconst DICE_ROLL_BUILTIN = 37;\n\nfunction emitDiceRollCall(builder: BytecodeBuilder): void {\n builder.emitOpcode(OpCode.CALL_BUILTIN);\n builder.emitIndex(DICE_ROLL_BUILTIN);\n builder.emitIndex(2); // argc = 2\n}\n\n/**\n * `roll between X and Y` / `roll from X to Y` / `roll(X, Y)`, the three\n * forms whose first token unambiguously picks the alternative, so this is\n * the {@link definePhrasePattern} proof-of-concept: same behavior as the\n * original hand-written branches (including the `BindingPower.Product`\n * first-operand binding power, preserved so \"AND\"/\"TO\" never gets\n * misparsed as part of the first expression), now declarative.\n */\nconst keywordLedRollPattern = definePhrasePattern({\n category: \"Dice\",\n alternatives: [\n {\n slots: [\n { kind: \"keyword\", tokenTypes: [\"BETWEEN\"] },\n { kind: \"expr\", bindingPower: BindingPower.Product },\n // The word \"and\" lexes as PLUS, not a dedicated AND token, en.ts's\n // keywordMap maps `and: \"PLUS\"` (a synonym for arithmetic \"+\").\n // The original hand-written parselet never actually checked the\n // separator's token type (a bare `parser.consume()`), so it never\n // surfaced this; matching that real lexer behavior explicitly here\n // rather than the aspirational \"AND\" the old code's comment implied.\n { kind: \"keyword\", tokenTypes: [\"AND_CONJ\"] },\n { kind: \"expr\" },\n ],\n emit: emitDiceRollCall,\n },\n {\n slots: [\n { kind: \"keyword\", tokenTypes: [\"FROM\"] },\n { kind: \"expr\", bindingPower: BindingPower.Product },\n { kind: \"keyword\", tokenTypes: [\"TO\"] },\n { kind: \"expr\" },\n ],\n emit: emitDiceRollCall,\n },\n {\n slots: [\n { kind: \"keyword\", tokenTypes: [\"LPAREN\"] },\n { kind: \"expr\" },\n { kind: \"keyword\", tokenTypes: [\"COMMA\"] },\n { kind: \"expr\" },\n { kind: \"keyword\", tokenTypes: [\"RPAREN\"] },\n ],\n emit: emitDiceRollCall,\n },\n ],\n});\n\n/**\n * Dice notation and its spelled-out ranges.\n *\n * Covers `roll between X and Y`, `roll from X to Y` and `roll(X, Y)`. Each\n * alternative is picked by its first token, which is what makes the declarative\n * phrase-pattern form usable here rather than hand-written lookahead.\n */\nexport class DiceRollParselet implements PrefixParselet {\n\treadonly category = \"Dice\";\n\tparse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n const next = parser.peek();\n if (next && next.type === \"NUMBER\") {\n // Bare hyphen range: \"roll 4-8\" (wiki: Dice, no keyword, no\n // parens). Both bounds must be consumed as plain NUMBER literals,\n // NOT via parseExpression(), that would let the infix MINUS\n // continue an arithmetic expression (\"4 - 8\" = -4) instead of\n // terminating the range's first operand. This means negative\n // bounds aren't supported through this shorthand (`roll -5-5` is\n // genuinely ambiguous to tokenize); use `roll between -5 and 5` /\n // `roll from -5 to 5` for negative ranges, those go through\n // parseExpression() and handle unary minus correctly. Not\n // expressible via definePhrasePattern (its alternatives must be\n // disambiguated by a single leading keyword; this form starts with\n // an expression, not a keyword), so it stays hand-written.\n const minToken = parser.consume(); // NUMBER\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(parseFloat(minToken.value));\n parser.consume(\"MINUS\");\n const maxToken = parser.consume(); // NUMBER\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(parseFloat(maxToken.value));\n emitDiceRollCall(builder);\n return;\n }\n keywordLedRollPattern.parse(parser, token, builder);\n }\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { DiceRollParselet } from \"./parselets/DiceRollParselet\";\n\n/** `roll(min, max)`, returns a random integer in the inclusive `[min, max]` range. */\nexport const DICE_PACKAGE: IEnginePackage = {\n name: \"solve-dice\",\n prefixParselets: {\n ROLL: new DiceRollParselet(),\n },\n};\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/**\n * The `:name` assignment form.\n *\n * Accepts a UNIT token as the name as well as an IDENT, because a variable\n * name may collide with a known unit, as in `:b = 5` where `b` is bits. The\n * leading colon makes the context unambiguous, so the override is safe.\n */\nexport class VariableParselet implements PrefixParselet {\n\treadonly category = \"Variable\";\n\tparse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n // Handle :var syntax, accept IDENT and UNIT tokens as variable names.\n // UNIT tokens occur when the variable name collides with a known unit\n // (e.g. \":b = 5\", \"b\" is a known unit for bits). The colon prefix\n // unambiguously signals a variable definition context, so the token\n // type override is safe and intentional. Deliberately narrow beyond\n // that, though: a word claimed as a keyword (GLOBAL, ROLL, CONVERT,\n // BY, ...) is NOT accepted here even with a colon prefix. See\n // GlobalVariableParselets.spec.ts's \"reserved-keyword regression\" test\n // for why this is intentional, tested policy, not an oversight. A\n // package that wants its trigger word to stay usable as a variable\n // name should fuse a longer phrase (e.g. \"total of\") into its own\n // token at the normalizer stage instead of claiming the bare word as\n // a keyword. See MathPhrasesPackage.ts for the established pattern.\n const nameToken = parser.consume();\n if (nameToken.type !== \"IDENT\" && nameToken.type !== \"UNIT\") {\n throw ErrorFactory.parsing(\n 'EXPECTED_IDENTIFIER',\n `Expected identifier or unit after colon, got ${nameToken.type}`,\n { tokenType: nameToken.type }\n );\n }\n const varName = nameToken.value;\n\n if (parser.peek()?.type === \"EQUALS\") {\n parser.consume(\"EQUALS\");\n parser.parseExpression(0, builder);\n builder.emitOpcode(OpCode.STORE_VAR);\n builder.emitString(varName);\n } else {\n builder.emitOpcode(OpCode.LOAD_VAR);\n builder.emitString(varName);\n }\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * IMPORTANT ASYMMETRY, confirmed by reading `PrecedenceParser.ts` directly\n * rather than assumed: this class is registered for BOTH `IDENT` and `UNIT`\n * token types (see `VariablesPackage.ts`) but is only DEAD CODE for the\n * `IDENT` case. `IDENT` is one of `PrecedenceParser`'s Tier-1 fast-path\n * token types, its inline `IDENT_ID` case always handles a bare `IDENT`\n * and `return`s before ever consulting the `ParseletRegistry`, matching\n * `NumberParselet.ts`'s identical situation for `NUMBER`. `UNIT` has NO\n * Tier-1 case, though, a bare unit-letter used as a variable reference\n * (`a + b` where \"b\" collides with the bits unit) genuinely reaches THIS\n * class's `parse()` for real.\n *\n * No special handling is needed here for user-defined-function parameter\n * names that collide with a unit abbreviation (e.g. `area(w, h) = w * h`,\n * where \"h\" lexes as UNIT). See `parser/BytecodeBuilder.ts`'s\n * `UserFunctionDef` doc comment: parameter resolution happens dynamically\n * at the VM level (the innermost call frame is checked before the flat\n * variable store), so a bare `LOAD_VAR` is correct for EVERY identifier\n * read, parameter or not.\n */\nexport class IdentifierParselet implements PrefixParselet {\n\treadonly category = \"Variable\";\n\tparse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n\t\t// Bare identifier: only support variable reads, not writes.\n\t\t// Assignments must use the :var = value syntax via VariableParselet (COLON prefix).\n\t\tbuilder.emitOpcode(OpCode.LOAD_VAR);\n\t\tbuilder.emitString(token.value);\n\t}\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/**\n * `global :name` (read) / `global :name = expr` (write), a document-\n * spanning variable, backed by the process-wide GlobalVariableStore rather\n * than this VM's own local scope. Registered as the prefix parselet for the\n * `GLOBAL` keyword token (see en.ts's keywordMap), so `_token` here is\n * already-consumed `global`; this parselet manually consumes the rest of\n * its own multi-token grammar, the same shape IncreaseDecreaseParselet\n * uses for \"increase X by Y%\".\n *\n * Reuses VariableParselet's exact name/`=`-detection logic, just emitting\n * the GLOBAL opcodes instead, kept as a sibling class rather than a shared\n * base/parameterization since the two grammars (`:name` vs `global :name`)\n * differ in how many tokens precede the name, and the duplication here is\n * small and unlikely to drift.\n */\nexport class GlobalVariableParselet implements PrefixParselet {\n\treadonly category = \"Variable\";\n\tparse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n\t\t// consume(\"COLON\") throws UNEXPECTED_TOKEN_TYPE itself if the next\n\t\t// token isn't a colon, no separate check needed here.\n\t\tparser.consume(\"COLON\");\n\n\t\t// Same IDENT/UNIT acceptance as VariableParselet, a global variable\n\t\t// named after a known unit (e.g. \"global :b = 5\") is unambiguous once\n\t\t// preceded by \"global :\", same reasoning as the local case.\n\t\tconst nameToken = parser.consume();\n\t\tif (nameToken.type !== \"IDENT\" && nameToken.type !== \"UNIT\") {\n\t\t\tthrow ErrorFactory.parsing(\n\t\t\t\t'EXPECTED_IDENTIFIER',\n\t\t\t\t`Expected identifier after 'global :', got ${nameToken.type}`,\n\t\t\t\t{ tokenType: nameToken.type }\n\t\t\t);\n\t\t}\n\t\tconst varName = nameToken.value;\n\n\t\tif (parser.peek()?.type === \"EQUALS\") {\n\t\t\tparser.consume(\"EQUALS\");\n\t\t\tparser.parseExpression(0, builder);\n\t\t\tbuilder.emitOpcode(OpCode.STORE_GLOBAL_VAR);\n\t\t\tbuilder.emitString(varName);\n\t\t} else {\n\t\t\tbuilder.emitOpcode(OpCode.LOAD_GLOBAL_VAR);\n\t\t\tbuilder.emitString(varName);\n\t\t}\n\t}\n}\n","import type { QueryClient } from \"@tanstack/query-core\";\nimport type { Token } from \"@solve-js/lexer\";\nimport type { BytecodeProgram } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport type { Value } from \"@solve-js/vm/Value\";\nimport type { IAsyncResolver, AsyncCheckResult } from \"@solve-js/resolvers/ResolverRegistry\";\nimport { sharedGlobalVariableStore } from \"@solve-js/vm/GlobalVariableStore\";\nimport { nextInstruction } from \"@solve-js/parser/OperandWidth\";\n\n/**\n * Async resolver for `global :name` reads that aren't yet known, i.e. no\n * currently-loaded document has run `global :name = value` for this name.\n *\n * Plugs into the engine's EXISTING async-resolution pipeline (the same one\n * CurrencyAsyncResolver/OsrsAsyncResolver use for currency rates and OSRS\n * prices): `preflight()` runs BEFORE the VM, and if a referenced global is\n * missing, returns an `AsyncCheckResult`, the engine immediately shows a\n * Pending value and, when the returned promise resolves, re-executes the\n * line via the same batching/DAG/event-stream machinery already built and\n * tested for currency. No new UI wiring is needed: `ExpressionResultWidget`\n * already renders Pending as a spinner.\n *\n * Unlike currency/OSRS, there is no network fetch here and no timeout\n * the promise for a missing name resolves the moment ANY loaded document\n * calls `global :name = value` (via GlobalVariableStore.subscribe()), and\n * simply stays pending forever if nothing ever does. This mirrors how an\n * unresolved external reference in a real spreadsheet behaves, and is the\n * deliberate design choice confirmed for this feature (no artificial\n * \"give up and error\" timer).\n */\nexport class GlobalVariableAsyncResolver implements IAsyncResolver {\n\treadonly namespace = \"global-variables\";\n\n\t/**\n\t * In-flight (not-yet-resolved) promises, keyed by variable name.\n\t *\n\t * Without this, every re-evaluation of a still-pending line (every\n\t * keystroke elsewhere in the document, every scroll, every unrelated\n\t * evaluate() call) would call preflight() again and, without dedup\n\t * create a BRAND NEW GlobalVariableStore subscription each time. Since\n\t * \"never declared\" is an explicitly supported, indefinitely-pending\n\t * outcome (no timeout), those listeners would never naturally clean\n\t * themselves up, growing unboundedly. This cache means repeated\n\t * preflight() calls for the same still-missing name return the exact\n\t * same promise (and therefore the same single subscription) every time.\n\t *\n\t * TanStack Query's fetchQuery() gives currency/OSRS this same dedup\n\t * for free; this resolver doesn't use TanStack Query (its \"cache\" is\n\t * GlobalVariableStore, not a network response), so it needs its own.\n\t */\n\tprivate pending = new Map<string, Promise<Value>>();\n\n\tpreflight(_tokens: Token[], bytecode: BytecodeProgram, packageId: string, signal: AbortSignal, _queryClient: QueryClient): AsyncCheckResult | null {\n\t\tconst { opcodes, strings } = bytecode;\n\t\tconst len = opcodes.length;\n\t\tlet i = 0;\n\n\t\twhile (i < len) {\n\t\t\tconst op = opcodes[i] as OpCode;\n\n\t\t\tif (op === OpCode.LOAD_GLOBAL_VAR) {\n\t\t\t\tconst varName = strings[opcodes[i + 1]];\n\t\t\t\tif (!sharedGlobalVariableStore.has(varName)) {\n\t\t\t\t\treturn this.pendingResultFor(varName, packageId, signal);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Every opcode's operand width, needed to correctly skip operand\n\t\t\t// bytes while scanning, mirrors the identical table already\n\t\t\t// duplicated in CurrencyAsyncResolver/OsrsAsyncResolver (this\n\t\t\t// codebase has no shared bytecode-walking utility yet). Keep\n\t\t\t// this in sync with those two files' switch blocks: an opcode\n\t\t\t// missing here (or there) causes a resolver to misread the next\n\t\t\t// opcode's operand byte as if it were itself an opcode, silently\n\t\t\t// corrupting the rest of that resolver's scan for ANY bytecode\n\t\t\t// that happens to also contain this opcode.\n\t\t\t// Step over this instruction and its operands. Shared table, because\n\t\t\t// three hand-copied versions of this had already drifted.\n\t\t\ti = nextInstruction(opcodes, i);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate pendingResultFor(varName: string, packageId: string, signal: AbortSignal): AsyncCheckResult {\n\t\tlet resolver = this.pending.get(varName);\n\t\tif (!resolver) {\n\t\t\tresolver = new Promise<Value>((resolve) => {\n\t\t\t\tconst unsubscribe = sharedGlobalVariableStore.subscribe((name, value) => {\n\t\t\t\t\tif (name !== varName) return;\n\t\t\t\t\tunsubscribe();\n\t\t\t\t\tthis.pending.delete(varName);\n\t\t\t\t\tresolve(value);\n\t\t\t\t});\n\t\t\t});\n\t\t\tthis.pending.set(varName, resolver);\n\t\t}\n\n\t\treturn {\n\t\t\tqueryKey: `global:${varName}`,\n\t\t\tresolver,\n\t\t\tpackageId,\n\t\t\tsignal,\n\t\t\tmetadata: { varName },\n\t\t};\n\t}\n\n\tdestroy(): void {\n\t\t// The underlying GlobalVariableStore subscriptions self-remove on\n\t\t// resolve; nothing here needs to force-unsubscribe in-flight ones\n\t\t// letting an already-pending global keep waiting even after this\n\t\t// particular resolver instance is torn down (e.g. package\n\t\t// unregister/re-register elsewhere) is harmless, since the\n\t\t// subscription only ever touches sharedGlobalVariableStore itself.\n\t\tthis.pending.clear();\n\t}\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { VariableParselet } from \"./parselets/VariableParselet\";\nimport { IdentifierParselet } from \"./parselets/IdentifierParselet\";\nimport { GlobalVariableParselet } from \"./parselets/GlobalVariableParselet\";\nimport { GlobalVariableAsyncResolver } from \"@solve-js/vm/GlobalVariableAsyncResolver\";\n\n/** Variable read/write: `:name = expr` to define, `name` to read, plus `global :name` for a document-spanning variable backed by the {@link GlobalVariableStore} instead of local VM scope. */\nexport const VARIABLES_PACKAGE: IEnginePackage = {\n name: \"solve-variables\",\n // Resolves `global :name` reads that aren't yet known to any currently-\n // loaded document, shows Pending and re-resolves automatically the\n // instant some document declares it, via the same async pipeline\n // the currency package uses for currency rates.\n asyncResolvers: [new GlobalVariableAsyncResolver()],\n prefixParselets: {\n COLON: new VariableParselet(),\n IDENT: new IdentifierParselet(),\n // UNIT tokens in prefix position (standalone or after operators) are\n // resolved as variable references via LOAD_VAR, same as IDENT tokens.\n // This handles cases like `a + b` where \"b\" is classified as UNIT\n // because it collides with a known unit (e.g., \"b\" = bits).\n UNIT: new IdentifierParselet(),\n // `global :name` (read) / `global :name = expr` (write), a document-\n // spanning variable backed by GlobalVariableStore instead of this VM's\n // own local scope.\n GLOBAL: new GlobalVariableParselet(),\n },\n};\n","/**\n * Historical currency conversion, `<money> in <currency> on <date>`.\n *\n * Live conversion (`100 USD in GBP`) uses whatever the rate is right now and\n * quietly drifts as the market moves, which is wrong for an expense or an\n * invoice: a note that was right when written stops being right. This module\n * adds the date to that resolution, so `100 USD in GBP on 2024-01-15` reports\n * the rate on the day it names and never changes afterwards.\n *\n * **Bring your own data source**, the same shape as stocks and weather. There\n * is no free, keyless historical-FX endpoint the engine can bake in the way\n * Frankfurter backs the live rate, so the HOST supplies a\n * {@link HistoricalRateProvider} via `createCurrencyPackage({ ... })`.\n * Unconfigured, a historical conversion resolves to a clearly worded\n * `HISTORICAL_RATES_NOT_CONFIGURED` error rather than silently falling back to\n * today's rate (see `packages/stocks/StocksPackage.ts` for the same principle,\n * and `uom/CurrencyExchange.ts`'s getRateSync doc for why a made-up number\n * dressed as a real one is worse than an honest failure).\n *\n * **Why one shared plugin function, read at runtime**: both the general\n * UomLiteralParselet (`100 USD in GBP on <date>`) and the currency\n * InParselet (`$100 in GBP on <date>`) emit the SAME `CALL_PLUGIN` by the\n * module-level name {@link HISTORICAL_CURRENCY_FN}, the same way Weather's five\n * phrases share one plugin function. The source currency is read from the\n * amount Value at RUNTIME rather than baked into the opcode stream, because the\n * InParselet's left operand (`$100`, a variable, a subexpression) has no\n * compile-time unit. Preflight fetches the rate ahead of the VM only when it can\n * recover the source currency unambiguously (exactly one distinct currency among\n * the amount's operand strings); a mixed-currency subexpression, where a foreign\n * literal may cancel out, is left to the runtime read of the amount's own unit,\n * so the two never disagree about, or double-fetch, a pair.\n */\nimport type { QueryClient } from \"@tanstack/query-core\";\nimport type { Token } from \"@solve-js/lexer\";\nimport type { Parser } from \"@solve-js/parser/Parser\";\nimport type { BytecodeProgram } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { nextInstruction } from \"@solve-js/parser/OperandWidth\";\nimport { pluginFunctionIndexFor } from \"@solve-js/vm/VMBuiltins\";\nimport { getActiveQueryClient } from \"@solve-js/services/DataQueryService\";\nimport { ValueType, numberValue, uomValue, errorValue, faultedOperand, type Value } from \"@solve-js/vm/Value\";\nimport type { IAsyncResolver, AsyncCheckResult } from \"@solve-js/resolvers/ResolverRegistry\";\nimport { sharedCurrencyExchange } from \"@solve-js/uom/CurrencyExchange\";\nimport { createTimeoutSignal } from \"@solve-js/utilities/TimeoutSignal\";\n\n/**\n * Resolve one historical exchange rate: the value of 1 unit of `from` in `to`\n * on `isoDate` (`YYYY-MM-DD`). Supplied by the host, backed by whichever\n * provider and key THEY have. Receives an `AbortSignal` that fires on caller\n * cancellation or the fetch timeout, pass it to `fetch()`.\n */\nexport type HistoricalRateProvider = (\n\tfrom: string,\n\tto: string,\n\tisoDate: string,\n\tsignal: AbortSignal,\n) => Promise<number>;\n\n/**\n * Cache namespace for historical rates, distinct from the live \"currency\"\n * namespace so a live and a historical rate for the same pair never overwrite\n * each other, and so `ResolverRegistry.unregister(\"currency-historical\")`\n * clears only these entries.\n */\nexport const HISTORICAL_CURRENCY_NS = \"currency-historical\";\n\n/**\n * The package-local name every historical conversion is compiled to. Both the\n * general UOM parselet and the currency InParselet emit a `CALL_PLUGIN` to it by\n * this name (`builder.emitPluginCall(HISTORICAL_CURRENCY_FN, 3)`); the engine\n * assigns the numeric index at registration. Module level, so the general UOM\n * parselets can emit it without importing a per-package instance. See this\n * module's doc.\n */\nexport const HISTORICAL_CURRENCY_FN = \"historicalCurrency\";\n\n/**\n * The qualified name the engine files this function's index under. It is\n * registered by the currency package (`name: \"solve-currency\"`, see\n * CurrencyPackage.ts), so the bytecode-scanning resolver below recovers the\n * runtime index by this name via {@link pluginFunctionIndexFor} rather than\n * owning a hand-allocated one.\n */\nconst HISTORICAL_CURRENCY_QUALIFIED = `solve-currency:${HISTORICAL_CURRENCY_FN}`;\n\n/** Structured error codes this feature produces, co-located per the `errors/ErrorCode.ts` per-package pattern. */\nexport const HistoricalCurrencyErrorCodes = {\n\t/** No {@link HistoricalRateProvider} was supplied, so `on <date>` conversions cannot be answered. NOT a fall back to today's rate. */\n\tNOT_CONFIGURED: \"HISTORICAL_RATES_NOT_CONFIGURED\",\n\t/** The host provider threw or timed out for one pair/date. Transient, evicted after a cooldown so a retry can happen. */\n\tQUERY_FAILED: \"HISTORICAL_RATE_QUERY_FAILED\",\n\t/** The VM reached the conversion before preflight cached its rate, a \"shouldn't happen\" invariant break, not a user error. */\n\tNOT_PREFLIGHTED: \"HISTORICAL_RATE_NOT_PREFLIGHTED\",\n\t/** The amount being converted was not a currency Value (bad bytecode, or a non-currency left operand). */\n\tINVALID_OPERAND: \"HISTORICAL_CURRENCY_INVALID_OPERAND\",\n} as const;\n\n/**\n * How long a FAILED historical fetch's error result stays cached before the\n * next evaluation retries it. A successful rate is kept forever (see\n * {@link HISTORICAL_RATE_STALE_TIME_MS}), so only failures need a bound, without\n * one a transient provider outage would either retry on every keystroke or\n * stick as \"the answer\" permanently. Mirrors `resolvers/QueryResolver.ts`.\n */\nconst FAILURE_COOLDOWN_MS = 30_000;\n\n/**\n * TanStack Query staleTime for a resolved historical rate: `Infinity`, it never\n * goes stale. The rate on a fixed past date is immutable, unlike a live rate\n * that the query cache re-fetches once its short window lapses. This is the\n * whole point of the feature, a cached historical rate is permanently fresh.\n */\nexport const HISTORICAL_RATE_STALE_TIME_MS = Infinity;\n\n/** Hard timeout for one provider call, an unresponsive source must not block re-evaluation indefinitely. */\nconst FETCH_TIMEOUT_MS = 10_000;\n\n/**\n * The TanStack Query key one historical rate is cached under. Currencies are\n * upper-cased so `usd`/`USD` share one entry, the date is already ISO. Both the\n * resolver (writer) and the plugin function (reader) build the key here, so\n * they cannot drift.\n */\nexport function historicalRateQueryKey(from: string, to: string, isoDate: string): [string, string] {\n\treturn [HISTORICAL_CURRENCY_NS, `${from.toUpperCase()}:${to.toUpperCase()}:${isoDate}`];\n}\n\n/** Two digits, zero-padded, for reassembling an ISO date from a Date's local fields. */\nfunction pad2(n: number): string {\n\treturn n < 10 ? `0${n}` : String(n);\n}\n\n/**\n * Consume an `on <date>` suffix for a currency conversion, or leave the parser\n * untouched and return `null`.\n *\n * Returns the ISO date (`YYYY-MM-DD`) only when the target `to` is a currency\n * (and the source `from`, when the caller knows it at compile time, likewise)\n * AND the next two tokens are the word \"on\" followed by a fused\n * `DATETIME_LITERAL` (which the datetime package produces for `2024-01-15` and\n * `15 Jan 2024` alike). Anything else consumes nothing, so `100 km in miles`,\n * `100 USD in GBP` with no date, and a bare `on` that is not a date all fall\n * straight through to the ordinary live conversion. Reading the fused literal\n * rather than re-deriving the date from its parts reuses the datetime package's\n * own calendar validation and its canonical (European/ISO) orderings.\n *\n * `from` is optional because the `$100 in GBP on <date>` form's left operand\n * has no compile-time unit (it is a value the VM produces). There the target\n * check is enough to commit, and the plugin function verifies the amount really\n * is a currency at runtime.\n */\nexport function tryConsumeCurrencyOnDate(parser: Parser, to: string, from?: string): string | null {\n\tif (!sharedCurrencyExchange.isCurrency(to)) return null;\n\tif (from !== undefined && !sharedCurrencyExchange.isCurrency(from)) return null;\n\n\tconst onToken = parser.peek();\n\tif (!onToken || onToken.type !== \"IDENT\" || onToken.value.toLowerCase() !== \"on\") return null;\n\n\tconst dateToken = parser.peekAt(1);\n\tif (!dateToken || dateToken.type !== \"DATETIME_LITERAL\") return null;\n\n\tparser.consume(); // \"on\"\n\tparser.consume(); // the date literal\n\tconst date = new Date(Number(dateToken.value));\n\t// Built with new Date(year, month - 1, day) (local midnight) by the datetime\n\t// normalizer, so it reads back the same way in local fields.\n\treturn `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;\n}\n\n/**\n * Resolve one historical rate to a cached `Value`, an honest error rather than\n * a rejection so a failed lookup surfaces as `ValueType.Error` the reader can\n * see (the same never-throw shape `resolvers/QueryResolver.ts` uses).\n *\n * With no provider the result is `HISTORICAL_RATES_NOT_CONFIGURED` and stays\n * cached (a missing provider is not transient). A provider that throws or times\n * out yields `HISTORICAL_RATE_QUERY_FAILED` and is evicted after\n * {@link FAILURE_COOLDOWN_MS} so a later keystroke retries it.\n */\nasync function fetchHistoricalRate(\n\tprovider: HistoricalRateProvider | undefined,\n\tfrom: string,\n\tto: string,\n\tisoDate: string,\n\tsignal: AbortSignal,\n\tqueryClient: QueryClient,\n): Promise<Value> {\n\tconst fromUpper = from.toUpperCase();\n\tconst toUpper = to.toUpperCase();\n\n\tif (!provider) {\n\t\treturn errorValue(\n\t\t\tHistoricalCurrencyErrorCodes.NOT_CONFIGURED,\n\t\t\t`Historical exchange rates are not configured. Supply historicalRateProvider via createCurrencyPackage({ ... }) to convert ${fromUpper} to ${toUpper} on ${isoDate}.`,\n\t\t);\n\t}\n\n\tconst { signal: fetchSignal, cleanup } = createTimeoutSignal(signal, FETCH_TIMEOUT_MS, \"Historical currency rate fetch\");\n\ttry {\n\t\tconst rate = await provider(fromUpper, toUpper, isoDate, fetchSignal);\n\t\treturn numberValue(rate);\n\t} catch (error) {\n\t\tconst failedValue = errorValue(\n\t\t\tHistoricalCurrencyErrorCodes.QUERY_FAILED,\n\t\t\t`Failed to resolve the historical rate for ${fromUpper} to ${toUpper} on ${isoDate}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t\t// Bound the failure's lifetime separately from the (infinite) success\n\t\t// staleTime, so a transient outage is retried rather than treated as the\n\t\t// permanent answer a real past rate would be.\n\t\tconst key = historicalRateQueryKey(from, to, isoDate);\n\t\tconst cooldownTimer = setTimeout(() => {\n\t\t\tif (queryClient.getQueryData(key) === failedValue) {\n\t\t\t\tqueryClient.removeQueries({ queryKey: key, exact: true });\n\t\t\t}\n\t\t}, FAILURE_COOLDOWN_MS);\n\t\t// unref so a pending eviction timer does not, on its own, keep a Node\n\t\t// process alive (browsers need no equivalent). Same reasoning as\n\t\t// `resolvers/QueryResolver.ts`.\n\t\t(cooldownTimer as unknown as { unref?: () => void }).unref?.();\n\t\treturn failedValue;\n\t} finally {\n\t\tcleanup();\n\t}\n}\n\n/**\n * Build the plugin function every historical conversion dispatches to at runtime.\n *\n * Args are `[amount, toUnit, isoDate]` (CALL_PLUGIN hands them back in push\n * order). The source currency is the amount's own unit, read here rather than\n * passed as an operand so the `$100 in GBP on <date>` form, whose left operand\n * has no compile-time unit, works through the same path.\n *\n * **Two ways the rate arrives, one reader.** For a literal-source conversion\n * (`100 USD in GBP on <date>`, `$100 in GBP on <date>`) preflight recovers the\n * source currency from the bytecode and caches the rate before the VM runs, so\n * this reads it and applies it synchronously. But a source known only at RUNTIME\n * (a variable or subexpression left operand, `x in GBP on <date>`) carries no\n * currency literal for preflight's scan to find, so preflight could not have\n * fetched anything. On that cache miss this fetches the rate itself and returns\n * the Promise: the VM yields a pending result (see EngineContext's\n * PluginFunctionHandler), the engine awaits it, re-evaluates, and this same\n * reader then reads the freshly cached rate. The fetch shares the resolver's\n * query key and function, so a literal and a variable form of the same\n * conversion never fetch twice.\n *\n * @param provider - the host historical-rate provider, closed over so the\n * runtime fetch can reach it; `undefined` surfaces `NOT_CONFIGURED` through the\n * same fetch, never a fall back to today's rate.\n */\nexport function createHistoricalCurrencyPluginFunction(\n\tprovider?: HistoricalRateProvider,\n): (args: Value[]) => Value | Promise<Value> {\n\treturn (args: Value[]): Value | Promise<Value> => {\n\t\tconst amount = args[0];\n\t\tconst toArg = args[1];\n\t\tconst dateArg = args[2];\n\n\t\t// CALL_PLUGIN already refuses a faulted argument before dispatch; this is\n\t\t// the same guard in this function's own terms, in case it is ever called\n\t\t// directly (tests, a future call site).\n\t\tconst fault = faultedOperand(amount);\n\t\tif (fault) return fault;\n\n\t\tif (amount.type !== ValueType.Uom || amount.unit === undefined || !sharedCurrencyExchange.isCurrency(amount.unit)) {\n\t\t\treturn errorValue(\n\t\t\t\tHistoricalCurrencyErrorCodes.INVALID_OPERAND,\n\t\t\t\t`Historical conversion needs an amount in a currency, for example \"100 USD in GBP on 2024-01-15\".`,\n\t\t\t);\n\t\t}\n\n\t\tconst from = amount.unit;\n\t\tconst to = String(toArg.value);\n\t\tconst isoDate = String(dateArg.value);\n\n\t\t// A currency converts to itself at 1 on any date, no rate lookup needed.\n\t\tif (from.toUpperCase() === to.toUpperCase()) {\n\t\t\treturn uomValue(amount.toNumber(), to);\n\t\t}\n\n\t\tconst queryClient = getActiveQueryClient();\n\t\tconst key = historicalRateQueryKey(from, to, isoDate);\n\t\tconst cached = queryClient?.getQueryData(key) as Value | undefined;\n\n\t\tif (cached === undefined) {\n\t\t\t// Preflight caches the rate before the VM for a literal-source\n\t\t\t// conversion, but a source currency known only at runtime (a\n\t\t\t// variable/subexpression left operand) leaves no literal for its scan,\n\t\t\t// so nothing was cached. Fetch it here and return the Promise: the VM\n\t\t\t// makes the line pending, the engine awaits and re-evaluates, and the\n\t\t\t// re-run reads the now-cached rate below. Same query key and function\n\t\t\t// as the resolver, so the two never double-fetch.\n\t\t\tif (!queryClient) {\n\t\t\t\t// No active query client at all (not the engine's normal path):\n\t\t\t\t// nothing can fetch or cache, so this stays the invariant-break\n\t\t\t\t// error it was (mirrors QueryResolver's _NOT_PREFLIGHTED).\n\t\t\t\treturn errorValue(\n\t\t\t\t\tHistoricalCurrencyErrorCodes.NOT_PREFLIGHTED,\n\t\t\t\t\t`No historical rate resolved for ${from.toUpperCase()} to ${to.toUpperCase()} on ${isoDate}.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn queryClient.fetchQuery({\n\t\t\t\tqueryKey: key,\n\t\t\t\tqueryFn: ({ signal }) => fetchHistoricalRate(provider, from, to, isoDate, signal, queryClient),\n\t\t\t\tstaleTime: HISTORICAL_RATE_STALE_TIME_MS,\n\t\t\t});\n\t\t}\n\t\t// A not-configured or failed fetch resolved to an Error Value: surface it\n\t\t// as-is rather than converting against a rate that is not there.\n\t\tif (cached.type === ValueType.Error) return cached;\n\n\t\tconst rate = cached.value as number;\n\t\t// A cross-currency rate is a double, so the result is an ordinary float Uom,\n\t\t// the same as the live path (exact-decimal money holds only within one\n\t\t// currency, see vm/VMConversion.ts's exactMoneyOp).\n\t\treturn uomValue(amount.toNumber() * rate, to);\n\t};\n}\n\n/**\n * The provider-less historical plugin function: reads a rate preflight (or a\n * test) has already cached and applies it, and on a miss resolves to\n * `NOT_CONFIGURED` through the shared fetch rather than a made-up rate. Kept as a\n * standalone export for direct callers; the currency package wires a\n * provider-backed instance through {@link createHistoricalCurrencyPluginFunction}\n * so a runtime-only source currency can still fetch.\n */\nexport const historicalCurrencyPluginFunction = createHistoricalCurrencyPluginFunction();\n\n/**\n * Build the async resolver that fetches historical rates through the host\n * `provider` before the VM runs.\n *\n * Scans compiled bytecode for the historical `CALL_PLUGIN`. `to` and `isoDate`\n * are the last two strings its parselets emit; the strings before them are the\n * amount's operands. It fetches only when those name exactly one distinct\n * currency (an unambiguous source), deferring a mixed-currency subexpression to\n * the runtime plugin, so it never fetches a pair the amount does not resolve to.\n * A same-currency conversion needs no rate and is skipped. A missing provider is\n * discovered by the fetch, not here, so the grammar still recognises `on <date>`\n * and reports the not-configured error plainly.\n */\nexport function createHistoricalCurrencyResolver(provider?: HistoricalRateProvider): IAsyncResolver {\n\treturn {\n\t\tnamespace: HISTORICAL_CURRENCY_NS,\n\n\t\tpreflight(\n\t\t\t_tokens: Token[],\n\t\t\tbytecode: BytecodeProgram,\n\t\t\tpackageId: string,\n\t\t\tsignal: AbortSignal,\n\t\t\tqueryClient: QueryClient,\n\t\t): AsyncCheckResult | null {\n\t\t\tconst { opcodes, strings } = bytecode;\n\t\t\tconst len = opcodes.length;\n\t\t\t// The engine assigns this function's CALL_PLUGIN index at registration;\n\t\t\t// recover it by the same qualified name to scan the compiled bytecode.\n\t\t\tconst historicalFnIdx = pluginFunctionIndexFor(HISTORICAL_CURRENCY_QUALIFIED);\n\t\t\tlet i = 0;\n\n\t\t\t// Pool indices of the PUSH_STRINGs seen since the last CALL_PLUGIN, so\n\t\t\t// each historical conversion is read from its own operand strings.\n\t\t\tlet stringIdxs: number[] = [];\n\n\t\t\twhile (i < len) {\n\t\t\t\tconst op = opcodes[i] as OpCode;\n\n\t\t\t\tif (op === OpCode.PUSH_STRING) {\n\t\t\t\t\tstringIdxs.push(opcodes[i + 1]);\n\t\t\t\t} else if (op === OpCode.CALL_PLUGIN) {\n\t\t\t\t\tconst fnIdx = opcodes[i + 1];\n\t\t\t\t\tconst argCount = opcodes[i + 2];\n\t\t\t\t\tif (fnIdx === historicalFnIdx && argCount === 3 && stringIdxs.length >= 3) {\n\t\t\t\t\t\t// The parselet emits `to` then `isoDate` as the last two\n\t\t\t\t\t\t// strings; everything before them is the amount's operands.\n\t\t\t\t\t\tconst isoDate = strings[stringIdxs[stringIdxs.length - 1]];\n\t\t\t\t\t\tconst to = strings[stringIdxs[stringIdxs.length - 2]];\n\n\t\t\t\t\t\t// Fetch ahead of the VM only when the source currency is\n\t\t\t\t\t\t// UNAMBIGUOUS: exactly one distinct currency among the operand\n\t\t\t\t\t\t// strings. A single literal amount (`100 USD in GBP on\n\t\t\t\t\t\t// <date>`) leaves exactly one, so its rate is fetched here. A\n\t\t\t\t\t\t// mixed-currency subexpression (`(100 USD * (5 JPY / 5 JPY))\n\t\t\t\t\t\t// in GBP on <date>`, where the JPY cancels) leaves more than\n\t\t\t\t\t\t// one, and the bytecode cannot say which the result carries,\n\t\t\t\t\t\t// so this defers to the runtime plugin, which reads the true\n\t\t\t\t\t\t// source off the computed amount (see\n\t\t\t\t\t\t// createHistoricalCurrencyPluginFunction). That keeps the fast\n\t\t\t\t\t\t// literal path while never fetching a currency the amount does\n\t\t\t\t\t\t// not actually resolve to.\n\t\t\t\t\t\tconst sources = new Set<string>();\n\t\t\t\t\t\tfor (let k = 0; k < stringIdxs.length - 2; k++) {\n\t\t\t\t\t\t\tconst s = strings[stringIdxs[k]];\n\t\t\t\t\t\t\tif (sharedCurrencyExchange.isCurrency(s)) sources.add(s.toUpperCase());\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (sources.size === 1) {\n\t\t\t\t\t\t\tconst from = sources.values().next().value as string;\n\t\t\t\t\t\t\t// Same currency needs no rate, the plugin function\n\t\t\t\t\t\t\t// converts it at 1 without ever reading the cache.\n\t\t\t\t\t\t\tif (from !== to.toUpperCase()) {\n\t\t\t\t\t\t\t\tconst key = historicalRateQueryKey(from, to, isoDate);\n\t\t\t\t\t\t\t\tif (queryClient.getQueryData(key) === undefined) {\n\t\t\t\t\t\t\t\t\tconst resolver = queryClient.fetchQuery({\n\t\t\t\t\t\t\t\t\t\tqueryKey: key,\n\t\t\t\t\t\t\t\t\t\tqueryFn: ({ signal: qSignal }) => fetchHistoricalRate(provider, from, to, isoDate, qSignal, queryClient),\n\t\t\t\t\t\t\t\t\t\tstaleTime: HISTORICAL_RATE_STALE_TIME_MS,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\treturn { queryKey: key.join(\":\"), resolver, packageId, signal, metadata: { from, to, isoDate } };\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tstringIdxs = []; // reset for the next conversion\n\t\t\t\t}\n\n\t\t\t\t// Step over this instruction and its operands via the shared width\n\t\t\t\t// table, so a wide opcode never desyncs the scan.\n\t\t\t\ti = nextInstruction(opcodes, i);\n\t\t\t}\n\n\t\t\treturn null;\n\t\t},\n\n\t\tdestroy(): void {\n\t\t\t// Cache cleared by ResolverRegistry.unregister() via removeQueries({ queryKey: [\"currency-historical\"] }).\n\t\t},\n\t};\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { isKnownUnit } from \"@solve-js/lexer/units\";\nimport { resolveCurrencyAlias } from \"@solve-js/uom/CurrencyAliases\";\nimport { tryConsumeCurrencyOnDate, HISTORICAL_CURRENCY_FN } from \"@solve-js/uom/HistoricalCurrency\";\n\n/**\n * Resolve `rawUnit` to its canonical ISO 4217 code if it's a recognized\n * currency WORD alias (e.g. \"euros\" -> \"EUR\"); otherwise return it\n * unchanged. Every other unit (km, grams, workdays, ...) passes straight\n * through untouched. This only ever fires for the specific word list in\n * `uom/CurrencyAliases.ts`'s `CURRENCY_WORD_ALIASES`.\n */\nfunction resolveUnitAlias(rawUnit: string): string {\n return resolveCurrencyAlias(rawUnit) ?? rawUnit;\n}\n\n/**\n * A unit directly following a value, as in `5 km` or `100 cm`.\n *\n * Postfix binding power so the unit attaches to the number beside it rather\n * than to a surrounding expression: `2 * 3 km` is `2 * (3 km)`. Aliases resolve\n * through the shared table, so currency symbols and unit words agree.\n */\nexport class UomLiteralParselet implements InfixParselet {\n\treadonly category = \"UoM\";\n\treadonly bindingPower = BindingPower.Postfix;\n\n parse(parser: Parser, left: Token, token: Token, builder: BytecodeBuilder): void {\n const unit = resolveUnitAlias(token.value);\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(unit);\n\n // Swallow a trailing `to`/`in` inline ONLY when this unit literal is not the\n // right operand of a tighter operator. As the RHS of `*` or `/`, the `in` in\n // `120 km / 2 hours in kph` binds looser than the `/` and belongs to the\n // whole quotient, so it is left for the outer conversion parselet (IN, bound\n // at 35) rather than pulled onto `2 hours` here. See the parser's\n // infixMinBindingPower doc comment.\n const boundInsideProduct = parser.infixMinBindingPower >= BindingPower.Product;\n\n // Check if the next token is \"to\" or \"in\"\n if (!boundInsideProduct && (parser.peek()?.type === \"TO\" || parser.peek()?.type === \"IN\")) {\n parser.consume(); // consume TO or IN\n const targetToken = parser.peek();\n // Accept UNIT or IN (for cases like \"3 ft in in\" where\n // the target unit name collides with the IN keyword).\n if (targetToken?.type === \"UNIT\" || targetToken?.type === \"IN\") {\n parser.consume();\n const targetUnit = resolveUnitAlias(targetToken.value);\n\n // `<money> in <currency> on <date>`: a historical conversion through\n // the host-supplied rate provider, distinct from the live conversion\n // below. Only fires for two currencies followed by `on <date>`, so an\n // ordinary `100 km in miles` or a dateless `100 USD in GBP` consumes\n // nothing here and falls straight through. See uom/HistoricalCurrency.ts.\n const isoDate = tryConsumeCurrencyOnDate(parser, targetUnit, unit);\n if (isoDate !== null) {\n // The source unit was pushed above; fold it and the amount into a\n // currency Uom, then hand [amount, target, date] to the historical\n // plugin, which reads the source currency back off that Uom.\n builder.emitOpcode(OpCode.UOM_CONVERT);\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(targetUnit);\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(isoDate);\n builder.emitPluginCall(HISTORICAL_CURRENCY_FN, 3);\n return;\n }\n\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(targetUnit);\n builder.emitOpcode(OpCode.UOM_CONVERT_TO);\n return;\n }\n // \"sourceUnit to ?\", conversion-possibilities query (wiki:\n // Units-Of-Measurement, \"Explore what units a particular unit can\n // be converted into\"). The source unit name is already on the\n // stack from the PUSH_STRING above.\n if (targetToken?.type === \"QUESTION\") {\n parser.consume();\n builder.emitOpcode(OpCode.UOM_POSSIBILITIES);\n return;\n }\n }\n \n // Check if the next token is \"best\"\n if (parser.peek()?.type === \"BEST\") {\n parser.consume(); // consume BEST\n builder.emitOpcode(OpCode.UOM_BEST);\n return;\n }\n \n builder.emitOpcode(OpCode.UOM_CONVERT);\n }\n}\n\nexport { isKnownUnit };\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { resolveCurrencyAlias } from \"@solve-js/uom/CurrencyAliases\";\n\n/** Resolve a currency WORD alias (e.g. \"euros\" -> \"EUR\") if recognized; otherwise return `rawUnit` unchanged. See uom/CurrencyAliases.ts. */\nfunction resolveUnitAlias(rawUnit: string): string {\n return resolveCurrencyAlias(rawUnit) ?? rawUnit;\n}\n\n/**\n * The explicit `convert` keyword, as in `convert 5 km to miles`.\n *\n * Handles both an explicit unit after the value and a value that already\n * carries one. The bare `5 km to miles` form does not come through here; it is\n * an infix conversion on `to`.\n */\nexport class ConvertParselet implements PrefixParselet {\n\treadonly category = \"UoM\";\n\tparse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(BindingPower.Postfix, builder);\n const nextToken = parser.peek();\n\n // Case 1: convert <number> <unit> [to <target>], explicit unit after value\n if (nextToken && nextToken.type === \"UNIT\") {\n parser.consume();\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(resolveUnitAlias(nextToken.value));\n\n if (parser.peek()?.type === \"TO\") {\n parser.consume(\"TO\");\n const targetToken = parser.consume();\n if (targetToken?.type === \"UNIT\") {\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(resolveUnitAlias(targetToken.value));\n builder.emitOpcode(OpCode.UOM_CONVERT_TO);\n } else if (targetToken?.type === \"BEST\") {\n builder.emitOpcode(OpCode.UOM_BEST);\n }\n } else {\n builder.emitOpcode(OpCode.UOM_CONVERT);\n }\n return;\n }\n\n // Case 2: convert (<expression>) to <target>, parenthesized or complex\n // expression where the expression result already has units on the stack.\n if (nextToken && (nextToken.type === \"TO\" || nextToken.type === \"IN\")) {\n parser.consume(); // consume TO or IN\n const targetToken = parser.peek();\n if (targetToken?.type === \"UNIT\" || targetToken?.type === \"IN\" ||\n targetToken?.type === \"DOLLAR\" || targetToken?.type === \"POUND\" ||\n targetToken?.type === \"EURO\" || targetToken?.type === \"YEN\" ||\n targetToken?.type === \"RUBLE\" || targetToken?.type === \"WON\" ||\n targetToken?.type === \"CURRENCY_SYMBOL\" || targetToken?.type === \"IDENT\") {\n parser.consume();\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(resolveUnitAlias(targetToken.value));\n builder.emitOpcode(OpCode.UOM_CONVERT_IN);\n } else if (targetToken?.type === \"BEST\") {\n parser.consume();\n builder.emitOpcode(OpCode.UOM_BEST);\n }\n return;\n }\n\n // Case 3: convert <expression> (no target), value is already on the stack\n // as a UOM value. Nothing more to emit.\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * `sourceUnit to ?` (wiki: Units-Of-Measurement, \"Conversion\n * Possibilities\"), lists every unit convertible from `sourceUnit`.\n * Handles the fused `UOM_POSSIBILITIES_QUERY` token produced by\n * {@link uomPossibilitiesNormalizerRule} (see that file for why this is a\n * normalizer fusion rather than a plain prefix parselet on UNIT).\n */\nexport class PossibilitiesParselet implements PrefixParselet {\n\treadonly category = \"UoM\";\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(token.value);\n builder.emitOpcode(OpCode.UOM_POSSIBILITIES);\n }\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\n/**\n * Fuses `UNIT TO QUESTION` (e.g. `cm to ?`) into a single\n * `UOM_POSSIBILITIES_QUERY` prefix token carrying the source unit name.\n *\n * Why fuse instead of a plain prefix parselet on UNIT: a bare `UNIT`\n * token in prefix position (the \"cm\" in \"cm to ?\", with no preceding\n * magnitude) is already claimed by VariablesPackage's IdentifierParselet,\n * which treats it as a variable reference. Same collision reasoning as\n * datetime's `UntilSinceNormalizerRule`. Fusing at the normalizer stage\n * means prefix dispatch never sees a bare UNIT in this position.\n */\nexport function uomPossibilitiesNormalizerRule(priority = 60): NormalizerRule {\n return {\n name: \"uom:possibilities\",\n priority,\n match(tokens, pos): NormalizerMatch | null {\n if (pos + 2 >= tokens.length) return null;\n const unitToken = tokens[pos];\n const toToken = tokens[pos + 1];\n const questionToken = tokens[pos + 2];\n if (unitToken.type !== \"UNIT\") return null;\n if (toToken.type !== \"TO\") return null;\n if (questionToken.type !== \"QUESTION\") return null;\n\n return {\n consumed: 3,\n replacement: [createFusedToken(\"UOM_POSSIBILITIES_QUERY\", unitToken.value, [unitToken, toToken, questionToken])],\n ruleName: \"uom:possibilities\",\n };\n },\n };\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\nimport { UNIT_TABLE } from \"@solve-js/uom/generated/UnitTable.generated\";\n\n/** The time measure's kind in UNIT_TABLE. Its base unit is the second. */\nconst TIME_KIND = 14;\n\n/**\n * `[measureKind, ratioToBaseUnit]` for a unit spelling, or undefined.\n *\n * `previousKind` disambiguates the one letter that genuinely collides: `m` is\n * the metre, so `3h 5m 10s` would otherwise be three hours and five metres and\n * be declined. Inside a run that has already established itself as a duration,\n * `m` is minutes, which is the only thing it can be between an `h` and an `s`.\n * A `5m` on its own never reaches this, because a single part is not compound.\n */\nfunction unitEntry(\n\ttoken: Token | undefined,\n\tpreviousKind?: number,\n): { kind: number; ratio: number; spelling: string } | undefined {\n\tif (token === undefined || token.type !== \"UNIT\") return undefined;\n\tconst spelling = (token.value ?? \"\").toLowerCase();\n\t// The corrected spelling is returned, not just the ratio. Emitting the\n\t// original \"m\" would label the result metres: `3h 5m` came out as \"185 m\",\n\t// the right number under the wrong unit, which is worse than not parsing.\n\tif (spelling === \"m\" && previousKind === TIME_KIND) {\n\t\treturn { kind: TIME_KIND, ratio: 60, spelling: \"minutes\" };\n\t}\n\tconst entry = UNIT_TABLE[spelling] as readonly [number, number] | undefined;\n\treturn entry === undefined ? undefined : { kind: entry[0], ratio: entry[1], spelling };\n}\n\n/** A bare unsigned integer or decimal, never hex or scientific. */\nconst PLAIN_NUMBER = /^\\d+(\\.\\d+)?$/;\n\n/**\n * Quantities written as several units at once: `3 hours 5 minutes 10 seconds`,\n * `5 hours 30 minutes`, `3h 5m 10s`.\n *\n * This is how durations are actually written, and none of it parsed. The two\n * parts sat next to each other as separate quantities and the parser reported\n * an unexpected number, which is why `5 hours 30 minutes to seconds`,\n * `16:00 + 3 hours 12 minutes` and the timespan examples all failed at the\n * same place for the same reason.\n *\n * The parts are summed into the smallest unit present, so\n * `3 hours 5 minutes 10 seconds` becomes 11,110 seconds and behaves as one\n * quantity everywhere afterwards: convertible, addable to a clock time, and\n * comparable. The engine renders that total rather than restating the parts,\n * which differs from Soulver's output but not from its arithmetic.\n *\n * Deliberately narrow, because a sequence of number-unit pairs is a shape that\n * ordinary arithmetic also produces:\n *\n * - Every part must belong to the same measure. `3 hours 5 metres` is not a\n * quantity and is left alone.\n * - The units must strictly decrease. `5 minutes 3 hours` is not how anyone\n * writes a duration, and treating it as one would silently reinterpret a\n * multiplication.\n * - Every part after the first must be a bare positive integer with no sign,\n * so `3 hours -5 minutes` stays a subtraction.\n */\nexport function compoundQuantityNormalizerRule(priority = 63): NormalizerRule {\n\treturn {\n\t\tname: \"uom:compound-quantity\",\n\t\tpriority,\n\t\tmatch(tokens, pos): NormalizerMatch | null {\n\t\t\tconst firstNumber = tokens[pos];\n\t\t\tif (firstNumber?.type !== \"NUMBER\" || !PLAIN_NUMBER.test(firstNumber.text ?? \"\")) return null;\n\n\t\t\tconst firstUnit = unitEntry(tokens[pos + 1]);\n\t\t\tif (firstUnit === undefined) return null;\n\n\t\t\tconst kind = firstUnit.kind;\n\t\t\tlet lastRatio = firstUnit.ratio;\n\t\t\tlet total = Number(firstNumber.text) * lastRatio;\n\t\t\tlet smallestUnit = firstUnit.spelling;\n\t\t\tlet smallestUnitToken = tokens[pos + 1];\n\t\t\tlet consumed = 2;\n\n\t\t\t// Each further part has to be a number, then a unit of the same\n\t\t\t// measure that is strictly smaller than the one before it.\n\t\t\tfor (;;) {\n\t\t\t\tconst number = tokens[pos + consumed];\n\t\t\t\tif (number?.type !== \"NUMBER\" || !PLAIN_NUMBER.test(number.text ?? \"\")) break;\n\n\t\t\t\tconst entry = unitEntry(tokens[pos + consumed + 1], kind);\n\t\t\t\tif (entry === undefined) break;\n\t\t\t\tif (entry.kind !== kind || entry.ratio >= lastRatio) break;\n\n\t\t\t\ttotal += Number(number.text) * entry.ratio;\n\t\t\t\tlastRatio = entry.ratio;\n\t\t\t\tsmallestUnit = entry.spelling;\n\t\t\t\tsmallestUnitToken = tokens[pos + consumed + 1];\n\t\t\t\tconsumed += 2;\n\t\t\t}\n\n\t\t\t// A single number and unit is not compound; leave it entirely alone.\n\t\t\tif (consumed === 2) return null;\n\n\t\t\tconst source = tokens.slice(pos, pos + consumed);\n\t\t\t// The total is expressed in the smallest unit that appeared, which\n\t\t\t// keeps it exact for the units people actually combine and reads\n\t\t\t// naturally when converted.\n\t\t\tconst scaled = total / lastRatio;\n\t\t\treturn {\n\t\t\t\tconsumed,\n\t\t\t\treplacement: [\n\t\t\t\t\tcreateFusedToken(\"NUMBER\", String(scaled), source),\n\t\t\t\t\tcreateFusedToken(\"UNIT\", smallestUnit, [smallestUnitToken]),\n\t\t\t\t],\n\t\t\t\truleName: \"uom:compound-quantity\",\n\t\t\t};\n\t\t},\n\t};\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\nimport { UNIT_TABLE } from \"@solve-js/uom/generated/UnitTable.generated\";\n\nfunction isUnit(token: Token | undefined): boolean {\n\tif (token === undefined || token.type !== \"UNIT\") return false;\n\treturn UNIT_TABLE[(token.value ?? \"\").toLowerCase()] !== undefined;\n}\n\n/**\n * Recognises `in <unit> and <unit>`, the two-unit conversion.\n *\n * Fused here rather than handled by the ordinary conversion parselet, so that\n * parselet keeps its single job and does not have to look ahead for an `and`\n * that is almost never there. The two unit names ride on the fused token.\n *\n * Both names must be real units, so `in minutes and pay me` is left alone.\n */\nexport function twoUnitConversionNormalizerRule(priority = 59): NormalizerRule {\n\treturn {\n\t\tname: \"uom:two-unit-conversion\",\n\t\tpriority,\n\t\tmatch(tokens, pos): NormalizerMatch | null {\n\t\t\tconst preposition = tokens[pos];\n\t\t\tif (preposition?.type !== \"IN\" && preposition?.type !== \"TO\") return null;\n\t\t\tif (!isUnit(tokens[pos + 1])) return null;\n\t\t\tif (tokens[pos + 2]?.type !== \"AND_CONJ\") return null;\n\t\t\tif (!isUnit(tokens[pos + 3])) return null;\n\n\t\t\tconst major = tokens[pos + 1].value;\n\t\t\tconst minor = tokens[pos + 3].value;\n\t\t\treturn {\n\t\t\t\tconsumed: 4,\n\t\t\t\treplacement: [\n\t\t\t\t\tcreateFusedToken(\"IN_TWO_UNITS\", `${major} ${minor}`, tokens.slice(pos, pos + 4)),\n\t\t\t\t],\n\t\t\t\truleName: \"uom:two-unit-conversion\",\n\t\t\t};\n\t\t},\n\t};\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\n/**\n * `90°` as ninety degrees.\n *\n * The degree sign is in the unit table but could never reach it: the lexer\n * reads a unit as one run of `[A-Za-z0-9_]`, so a non-ASCII character cannot\n * become a UNIT token however well the converter understands it. `90°` lexed\n * as a number and an identifier, and `sin(90°)` failed with \"Undefined\n * variable: °\".\n *\n * Retyping it here is the whole fix. It needs no lexer change, because the\n * symbol is unambiguous: `°` is degrees and nothing else, so there is no\n * context in which this could be claiming something that was already spoken\n * for.\n *\n * Note this is the angle degree. Temperature is written `°C` and `°F`, which\n * lex as their own units already and are not touched, because the symbol is\n * followed by a letter there rather than standing alone.\n */\nexport function degreeSymbolNormalizerRule(priority = 74): NormalizerRule {\n\treturn {\n\t\tname: \"uom:degree-symbol\",\n\t\tpriority,\n\t\tmatch(tokens, pos): NormalizerMatch | null {\n\t\t\t// Matched from the number rather than from the symbol, so the\n\t\t\t// replacement emits the pair together and the unit lands adjacent to\n\t\t\t// the quantity it belongs to.\n\t\t\tconst number = tokens[pos];\n\t\t\tif (number?.type !== \"NUMBER\") return null;\n\n\t\t\tconst symbol = tokens[pos + 1];\n\t\t\tif (symbol === undefined || symbol.type !== \"IDENT\") return null;\n\t\t\tif ((symbol.text ?? symbol.value ?? \"\") !== \"°\") return null;\n\n\t\t\treturn {\n\t\t\t\tconsumed: 2,\n\t\t\t\treplacement: [number, createFusedToken(\"UNIT\", \"degrees\", [symbol])],\n\t\t\t\truleName: \"uom:degree-symbol\",\n\t\t\t};\n\t\t},\n\t};\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `12.5 minutes in minutes and seconds`, one quantity split across two units.\n *\n * Every other conversion produces a single number in a single unit, so this\n * could not be expressed at all: `in minutes` loses the seconds and\n * `in seconds` loses the readability. Splitting is what makes\n * `4.5 weeks in days and hours` say something a person can act on.\n *\n * Triggered on a fused `IN_TWO_UNITS` token rather than on `in` itself, so the\n * ordinary single-unit conversion is untouched and does not have to look ahead\n * for an `and` that is usually not there. The two unit names ride on the token,\n * separated by a space, since the normalizer has already read them.\n */\nexport class TwoUnitConversionParselet implements InfixParselet {\n\treadonly category = \"Uom\";\n\treadonly bindingPower = BindingPower.Conditional;\n\n\tconstructor(private readonly builtinIndex: number) {}\n\n\tparse(_parser: Parser, _left: Token, token: Token, builder: BytecodeBuilder): void {\n\t\tconst [major, minor] = String(token.value).split(\" \");\n\t\tbuilder.emitOpcode(OpCode.PUSH_STRING);\n\t\tbuilder.emitString(major);\n\t\tbuilder.emitOpcode(OpCode.PUSH_STRING);\n\t\tbuilder.emitString(minor);\n\t\tbuilder.emitOpcode(OpCode.CALL_BUILTIN);\n\t\tbuilder.emitIndex(this.builtinIndex);\n\t\tbuilder.emitIndex(3);\n\t}\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\nimport { UNIT_TABLE } from \"@solve-js/uom/generated/UnitTable.generated\";\n\n/** Whether a token is a unit spelling the engine knows. */\nfunction isUnit(token: Token | undefined): boolean {\n\tif (token === undefined || token.type !== \"UNIT\") return false;\n\treturn UNIT_TABLE[(token.value ?? \"\").toLowerCase()] !== undefined;\n}\n\n/** Words that introduce a rate denominator on their own. */\nconst PER_WORDS = new Set([\"per\", \"a\", \"an\", \"each\", \"every\"]);\n\n// Priority above the implicit-multiplication rule (50), which would\n// otherwise insert a `*` between a bare number and the `per` that follows it\n// and leave the fused token in operand position.\n/**\n * A word that is not a unit, standing where a rate numerator would be.\n *\n * `30 bottles / week` is thirty bottles a week. \"bottles\" is not a unit and\n * never will be, so the only readings available are to drop it, which is what\n * Soulver does, or to keep it as the label it plainly is. Keeping it says more\n * and loses nothing.\n *\n * Only fires when the very next thing is a rate denominator, which is a strong\n * enough signal to distinguish a count noun from a variable. The cost is real\n * and stated: if `bottles` happens to be a defined variable, this reads the\n * word rather than its value. That is asserted in the spec so the trade is\n * visible rather than discovered.\n */\nfunction isCountLabel(token: Token | undefined): boolean {\n\tif (token === undefined || token.type !== \"IDENT\") return false;\n\tconst word = (token.text ?? token.value ?? \"\").toLowerCase();\n\tif (PER_WORDS.has(word)) return false;\n\t// A word, not a symbol or a single letter: single letters are overwhelmingly\n\t// variables (`30 x / week`) and overwhelmingly not count nouns.\n\treturn /^[a-z][a-z_]{2,}$/.test(word) && UNIT_TABLE[word] === undefined;\n}\n\n/**\n * Recognises a rate denominator written with no number: `/ day`, `per week`,\n * `a day`.\n *\n * This is the distinction that makes rates work. `3 hours / day` is a rate;\n * `3 hours / 3 days` is a division that cancels to a plain number. The two are\n * told apart by whether a number was written, which is a fact about the source\n * text and therefore has to be decided here rather than at evaluation time.\n *\n * The earlier attempt at this supplied the missing `1` and let the ordinary\n * division run. It made `$50/week × 12 weeks` work and turned `3 hours / day`\n * into 0.125, because once the denominator has a number the same-measure units\n * cancel. Fusing instead means the rate is constructed directly and no division\n * ever happens.\n *\n * `per`, `a`, `an`, `each` and `every` are only ever consumed when a unit\n * follows, so those very common words are otherwise untouched.\n */\nexport function bareRateDenominatorNormalizerRule(priority = 75): NormalizerRule {\n\treturn {\n\t\tname: \"uom:bare-rate-denominator\",\n\t\tpriority,\n\t\tmatch(tokens, pos): NormalizerMatch | null {\n\t\t\tconst head = tokens[pos];\n\t\t\tif (head === undefined) return null;\n\n\t\t\t// `30 bottles / week`: a count noun in front of the denominator.\n\t\t\t// Retyped as the unit it is acting as, so the rate keeps the label.\n\t\t\tif (head.type === \"NUMBER\" && isCountLabel(tokens[pos + 1])) {\n\t\t\t\tconst label = tokens[pos + 1];\n\t\t\t\tconst after = tokens[pos + 2];\n\t\t\t\tconst introduces =\n\t\t\t\t\tafter?.type === \"SLASH\" ||\n\t\t\t\t\t(after?.type === \"IDENT\" && PER_WORDS.has((after.text ?? after.value ?? \"\").toLowerCase()));\n\t\t\t\tif (introduces && isUnit(tokens[pos + 3])) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tconsumed: 2,\n\t\t\t\t\t\treplacement: [head, createFusedToken(\"UNIT\", label.value, [label])],\n\t\t\t\t\t\truleName: \"uom:bare-rate-denominator\",\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!isUnit(tokens[pos + 1])) return null;\n\n\t\t\tconst isSlash = head.type === \"SLASH\";\n\t\t\tconst word = (head.text ?? head.value ?? \"\").toLowerCase();\n\t\t\tconst isPerWord = head.type === \"IDENT\" && PER_WORDS.has(word);\n\t\t\tif (!isSlash && !isPerWord) return null;\n\n\t\t\t// A number after the unit means this was never a bare denominator:\n\t\t\t// `/ 3 days` is a division and `a day + 2` is not a rate at all.\n\t\t\tconst unitToken = tokens[pos + 1];\n\t\t\treturn {\n\t\t\t\tconsumed: 2,\n\t\t\t\treplacement: [\n\t\t\t\t\tcreateFusedToken(\"PER_UNIT\", unitToken.value, [head, unitToken]),\n\t\t\t\t],\n\t\t\t\truleName: \"uom:bare-rate-denominator\",\n\t\t\t};\n\t\t},\n\t};\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `<quantity> per <unit>`, a rate.\n *\n * Triggered on the fused `PER_UNIT` token, which only exists where a\n * denominator was written with no number. The rate is built directly rather\n * than by dividing, because dividing makes same-measure units cancel and\n * `3 hours / day` would come out as 0.125.\n *\n * Binds at `Product`, the same level division binds at, so a rate sits where a\n * division would have and the surrounding arithmetic is unchanged.\n */\nexport class PerUnitParselet implements InfixParselet {\n\treadonly category = \"Uom\";\n\treadonly bindingPower = BindingPower.Product;\n\n\tconstructor(private readonly builtinIndex: number) {}\n\n\tparse(_parser: Parser, _left: Token, token: Token, builder: BytecodeBuilder): void {\n\t\tbuilder.emitOpcode(OpCode.PUSH_STRING);\n\t\tbuilder.emitString(String(token.value));\n\t\tbuilder.emitOpcode(OpCode.CALL_BUILTIN);\n\t\tbuilder.emitIndex(this.builtinIndex);\n\t\tbuilder.emitIndex(2);\n\t}\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `<quantity> at <rate>`.\n *\n * 30 hours at $30/hour $900\n * $500 at $20/hour 25 hours\n *\n * The same word for opposite operations, told apart by which half of the rate\n * the left side matches. That needs the units, so the decision is made in the\n * builtin where both values are known rather than here.\n *\n * Triggered on `AT_RATE`, which a normalizer produces only when a rate really\n * follows. Registering on the plain `at` token broke the finance grammar,\n * which parses its own rate with the same word; see AtRateNormalizerRule.\n */\nexport class AtRateParselet implements InfixParselet {\n\treadonly category = \"Uom\";\n\treadonly bindingPower = BindingPower.Product;\n\n\tconstructor(private readonly builtinIndex: number) {}\n\n\tparse(parser: Parser, _left: Token, _token: Token, builder: BytecodeBuilder): void {\n\t\t// Below Product, so the rate on the right is parsed whole. At Product\n\t\t// the PER_UNIT that carries the denominator binds at the same level and\n\t\t// is left behind, which produced a rate of nothing per hour.\n\t\tparser.parseExpression(BindingPower.Conditional, builder);\n\t\tbuilder.emitOpcode(OpCode.CALL_BUILTIN);\n\t\tbuilder.emitIndex(this.builtinIndex);\n\t\tbuilder.emitIndex(2);\n\t}\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\nimport { UNIT_TABLE } from \"@solve-js/uom/generated/UnitTable.generated\";\n\n/** Whether a token is a unit spelling the engine knows. */\nfunction isUnit(token: Token | undefined): boolean {\n\tif (token === undefined || token.type !== \"UNIT\") return false;\n\treturn UNIT_TABLE[(token.value ?? \"\").toLowerCase()] !== undefined;\n}\n\n/** Words that introduce a rate denominator, matching the bare-denominator rule. */\nconst PER_WORDS = new Set([\"per\", \"a\", \"an\", \"each\", \"every\"]);\n\n/**\n * Whether a rate denominator appears soon after `from`.\n *\n * Checked against the raw shapes as well as the already-fused `PER_UNIT`, so\n * this does not depend on which normalizer rule ran first.\n */\nfunction hasRateAhead(tokens: readonly Token[], from: number): boolean {\n\tfor (let i = from; i < tokens.length && i < from + 5; i++) {\n\t\tconst token = tokens[i];\n\t\tif (token.type === \"PER_UNIT\") return true;\n\t\tif (token.type === \"EOF\" || token.type === \"COMMA\" || token.type === \"RATE_AT\") return false;\n\t\tconst word = (token.text ?? token.value ?? \"\").toLowerCase();\n\t\tconst introducesDenominator =\n\t\t\ttoken.type === \"SLASH\" || (token.type === \"IDENT\" && PER_WORDS.has(word));\n\t\tif (introducesDenominator && isUnit(tokens[i + 1])) return true;\n\t}\n\treturn false;\n}\n\n/**\n * Retypes `at` to `AT_RATE`, but only when a rate actually follows.\n *\n * 30 hours at $30/hour -> AT_RATE\n * $500 at $20/hour -> AT_RATE\n * over 6 years at 6% -> left alone\n *\n * The lookahead is the entire point. Registering an ordinary infix parselet on\n * `at` was tried and broke every mortgage and investment expression: the\n * finance grammar parses its own rate with the same word, and the infix took\n * the token before those parselets could consume it, so\n * `$1,000 after 3 years at 7%` failed with \"Unexpected end of input\".\n *\n * Looking for a denominator ahead separates them cleanly, because a finance\n * rate is a bare percentage and never has one. The scan is deliberately short\n * and stops at anything that ends a clause, so a `/` much later in the line\n * cannot drag an unrelated `at` into this.\n */\nexport function atRateNormalizerRule(priority = 73): NormalizerRule {\n\treturn {\n\t\tname: \"uom:at-rate\",\n\t\tpriority,\n\t\tmatch(tokens, pos): NormalizerMatch | null {\n\t\t\tif (tokens[pos]?.type !== \"RATE_AT\") return null;\n\t\t\tif (!hasRateAhead(tokens, pos + 1)) return null;\n\n\t\t\treturn {\n\t\t\t\tconsumed: 1,\n\t\t\t\treplacement: [createFusedToken(\"AT_RATE\", \"at\", [tokens[pos]])],\n\t\t\t\truleName: \"uom:at-rate\",\n\t\t\t};\n\t\t},\n\t};\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\nimport { UNIT_TABLE } from \"@solve-js/uom/generated/UnitTable.generated\";\n\n/** Whether a token is a unit spelling the engine knows. */\nfunction isUnit(token: Token | undefined): boolean {\n\tif (token === undefined || token.type !== \"UNIT\") return false;\n\treturn UNIT_TABLE[(token.value ?? \"\").toLowerCase()] !== undefined;\n}\n\n/** Articles meaning \"one\" in this position. */\nconst ARTICLES = new Set([\"a\", \"an\"]);\n\n/**\n * `for a <unit>` as \"times one of them\": `$24 a day for a year` is $8,760.\n *\n * The article is doing two different jobs in that one line. The first (`a day`)\n * introduces a rate denominator; the second (`a year`) is a quantity the rate\n * runs for. Without separating them the second article was read as another\n * denominator and the line failed with the fused token in operand position.\n *\n * Rewritten to an explicit multiplication rather than given its own operator,\n * because that is exactly what it means and the rate machinery already knows\n * how to multiply a rate by a duration.\n *\n * Requires the article. `for 3 years` keeps a number of its own and belongs to\n * the finance grammar (`$1,000 for 3 years at 7%`), which this must not touch.\n */\nexport function forDurationNormalizerRule(priority = 76): NormalizerRule {\n\treturn {\n\t\tname: \"uom:for-duration\",\n\t\tpriority,\n\t\tmatch(tokens, pos): NormalizerMatch | null {\n\t\t\tconst head = tokens[pos];\n\t\t\tif (head?.type !== \"FOR_DURATION\") return null;\n\n\t\t\tconst article = tokens[pos + 1];\n\t\t\tif (article?.type !== \"IDENT\") return null;\n\t\t\tif (!ARTICLES.has((article.text ?? article.value ?? \"\").toLowerCase())) return null;\n\t\t\tif (!isUnit(tokens[pos + 2])) return null;\n\n\t\t\treturn {\n\t\t\t\tconsumed: 3,\n\t\t\t\treplacement: [\n\t\t\t\t\tcreateFusedToken(\"STAR\", \"*\", [head]),\n\t\t\t\t\tcreateFusedToken(\"NUMBER\", \"1\", [article]),\n\t\t\t\t\ttokens[pos + 2],\n\t\t\t\t],\n\t\t\t\truleName: \"uom:for-duration\",\n\t\t\t};\n\t\t},\n\t};\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\nimport { UNIT_TABLE } from \"@solve-js/uom/generated/UnitTable.generated\";\n\n/** Whether a token is a unit spelling the engine knows. */\nfunction isUnit(token: Token | undefined): boolean {\n\tif (token === undefined || token.type !== \"UNIT\") return false;\n\treturn UNIT_TABLE[(token.value ?? \"\").toLowerCase()] !== undefined;\n}\n\n/** Articles that stand in for \"one\" in front of a unit. */\nconst ARTICLES = new Set([\"a\", \"an\"]);\n\n/**\n * Conversions asked the other way round: `meters in 10 km`, `days in 3 weeks`,\n * `seconds in a day`.\n *\n * `10 km in meters` has always worked. Asking \"how many X in Y\" is the same\n * question with the parts swapped, and it failed on the leading unit, which\n * had nothing in front of it to convert.\n *\n * Implemented as a reorder rather than a second conversion grammar, so there\n * is still exactly one path that performs a conversion and this is only a\n * different way of spelling the input.\n *\n * Narrow on purpose. The quantity after `in` must be a plain number and unit,\n * optionally with the article standing in for the one, so:\n *\n * - `days in February 2020` is left alone. That asks how long a named period\n * is, which is a different question with a different answer, and quietly\n * turning it into `February 2020 in days` would answer something nobody\n * asked.\n * - `10 km in m` is untouched, since it does not start with a bare unit.\n */\nexport function reversedConversionNormalizerRule(priority = 61): NormalizerRule {\n\treturn {\n\t\tname: \"uom:reversed-conversion\",\n\t\tpriority,\n\t\tmatch(tokens, pos): NormalizerMatch | null {\n\t\t\t// A bare unit, at the very start of the expression. Anywhere else it\n\t\t\t// is far more likely to be part of something already being parsed.\n\t\t\tif (pos !== 0 || !isUnit(tokens[pos])) return null;\n\t\t\tif (tokens[pos + 1]?.type !== \"IN\") return null;\n\n\t\t\tconst target = tokens[pos];\n\t\t\tconst third = tokens[pos + 2];\n\t\t\tif (third === undefined) return null;\n\n\t\t\t// `days in 3 weeks`\n\t\t\tif (third.type === \"NUMBER\" && isUnit(tokens[pos + 3])) {\n\t\t\t\treturn {\n\t\t\t\t\tconsumed: 4,\n\t\t\t\t\treplacement: [third, tokens[pos + 3], tokens[pos + 1], target],\n\t\t\t\t\truleName: \"uom:reversed-conversion\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// `seconds in a day`\n\t\t\tconst word = (third.text ?? third.value ?? \"\").toLowerCase();\n\t\t\tif (third.type === \"IDENT\" && ARTICLES.has(word) && isUnit(tokens[pos + 3])) {\n\t\t\t\treturn {\n\t\t\t\t\tconsumed: 4,\n\t\t\t\t\treplacement: [\n\t\t\t\t\t\tcreateFusedToken(\"NUMBER\", \"1\", [third]),\n\t\t\t\t\t\ttokens[pos + 3],\n\t\t\t\t\t\ttokens[pos + 1],\n\t\t\t\t\t\ttarget,\n\t\t\t\t\t],\n\t\t\t\t\truleName: \"uom:reversed-conversion\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn null;\n\t\t},\n\t};\n}\n","/**\n * Approximate ingredient densities for cooking/baking mass-volume\n * conversion (e.g. \"300g butter in cups\", \"10 cups olive oil in grams\").\n *\n * DATA ACCURACY NOTE:\n * Density is expressed in grams per millilitre (g/mL), the canonical unit\n * this table stores everything in (mass and volume, since both measures\n * this package already supports -- see uom/UomConverter.ts -- reduce to\n * grams/millilitres internally). Values are APPROXIMATE, general-knowledge\n * estimates for common preparations of each ingredient (e.g. \"flour\"\n * assumes spooned-and-leveled all-purpose flour, \"butter\" assumes standard\n * solid stick butter) -- real density varies with how an ingredient is\n * packed, sifted, its temperature, humidity, and brand. This is an\n * INHERENT approximation of any mass-to-volume cooking conversion\n * (SoulverCore's own bundled density tables carry the same caveat), not a\n * bug in this table -- treat results as close enough for a recipe, not\n * lab-precise.\n *\n * SCOPE:\n * Covers about 70 common cooking/baking ingredients -- a representative\n * subset, NOT the full ~200 SoulverCore ships. Additive and extensible:\n * add more entries here as gaps are found, no other code needs to change\n * (same \"not exhaustive, extend as needed\" pattern already used for\n * packages/time/timezones/CityZones.ts's city table).\n *\n * Multi-word names (e.g. \"olive oil\", \"brown sugar\") are matched via\n * MAX_INGREDIENT_NAME_WORDS-word lookahead in\n * normalizer/IngredientNameNormalizerRule.ts -- no separate phrase\n * registration needed (see that file's doc comment for why this table\n * does NOT go through IEnginePackage.phrases).\n */\nexport const INGREDIENT_DENSITIES: Readonly<Record<string, number>> = {\n water: 1.0,\n milk: 1.03,\n \"whole milk\": 1.03,\n \"skim milk\": 1.035,\n buttermilk: 1.03,\n \"heavy cream\": 1.0,\n \"whipping cream\": 1.0,\n \"sour cream\": 0.96,\n yogurt: 1.03,\n \"greek yogurt\": 1.05,\n \"cream cheese\": 1.01,\n \"condensed milk\": 1.3,\n \"evaporated milk\": 1.06,\n \"coconut milk\": 0.97,\n \"almond milk\": 1.03,\n \"soy milk\": 1.03,\n butter: 0.9595,\n margarine: 0.96,\n \"vegetable oil\": 0.92,\n \"olive oil\": 0.913,\n \"canola oil\": 0.92,\n \"coconut oil\": 0.92,\n \"sunflower oil\": 0.92,\n \"sesame oil\": 0.92,\n mayonnaise: 0.91,\n\n sugar: 0.845,\n \"granulated sugar\": 0.845,\n \"brown sugar\": 0.93,\n \"powdered sugar\": 0.51,\n \"icing sugar\": 0.51,\n honey: 1.42,\n \"maple syrup\": 1.32,\n \"corn syrup\": 1.38,\n molasses: 1.4,\n nutella: 1.14,\n \"peanut butter\": 1.09,\n\n flour: 0.53,\n \"all-purpose flour\": 0.53,\n \"all purpose flour\": 0.53,\n \"bread flour\": 0.54,\n \"cake flour\": 0.47,\n \"whole wheat flour\": 0.55,\n cornstarch: 0.53,\n cornmeal: 0.65,\n semolina: 0.6,\n rice: 0.79,\n \"white rice\": 0.79,\n \"brown rice\": 0.8,\n oats: 0.34,\n \"rolled oats\": 0.38,\n breadcrumbs: 0.43,\n yeast: 0.68,\n \"dry yeast\": 0.68,\n\n salt: 1.2,\n \"table salt\": 1.2,\n \"kosher salt\": 0.96,\n \"baking powder\": 0.9,\n \"baking soda\": 0.99,\n \"cream of tartar\": 0.68,\n cinnamon: 0.56,\n ginger: 0.5,\n \"vanilla extract\": 0.88,\n \"lemon juice\": 1.03,\n \"lime juice\": 1.02,\n \"orange juice\": 1.04,\n vinegar: 1.01,\n\n cocoa: 0.41,\n \"cocoa powder\": 0.41,\n \"chocolate chips\": 0.72,\n \"chocolate chunks\": 0.72,\n coconut: 0.35,\n \"shredded coconut\": 0.35,\n raisins: 0.68,\n\n almonds: 0.55,\n walnuts: 0.5,\n pecans: 0.5,\n pistachios: 0.55,\n peanuts: 0.6,\n\n \"shredded cheese\": 0.4,\n parmesan: 0.44,\n \"tomato paste\": 1.06,\n \"tomato sauce\": 1.02,\n ketchup: 1.14,\n broth: 1.0,\n stock: 1.0,\n wine: 0.99,\n beer: 1.01,\n};\n\n/**\n * Longest ingredient name (in words) this table needs to match -- bounds the\n * lookahead window IngredientNameNormalizerRule tries. Recompute if a\n * longer multi-word entry is ever added above.\n */\nexport const MAX_INGREDIENT_NAME_WORDS = 2;\n\n/** Look up an ingredient's density (g/mL) by its lowercase, space-joined name. */\nexport function getIngredientDensity(name: string): number | undefined {\n return INGREDIENT_DENSITIES[name.toLowerCase().trim()];\n}\n","import { Value, ValueType, uomValue, errorValue } from \"@solve-js/vm/Value\";\nimport { convertUnit, getMeasure } from \"@solve-js/uom/UomConverter\";\nimport { getIngredientDensity } from \"../data/IngredientDensities\";\n\n/**\n * Cooking mass<->volume conversion, the density-aware plugin function\n * backing `CookingConversionParselet.ts`'s `<mass-or-volume> <substance>\n * in <target-unit>` grammar (e.g. \"300g butter in cups\", \"10 cups olive\n * oil in grams\", \"100g nutella in tablespoons\").\n *\n * Registered via `IEnginePackage.pluginFunctions` (collision-safe\n * allocator) rather than a coordinated `VMBuiltins.ts` CALL_BUILTIN index\n * this conversion has no `functionName(args)` call-style form to support\n * (unlike Finance's `inflationAdjust`), so there's no need for\n * `FunctionCallParselet`'s coordinated index space at all.\n *\n * SCOPE DECISION: only ONE volume-unit convention is supported, whatever\n * the underlying `convert` npm package resolves for names like \"cup\"/\n * \"tablespoon\"/\"teaspoon\" (its own generated tables use US customary\n * definitions for these, e.g. 1 cup = 236.588 mL), matching SoulverCore's\n * own stated default. SoulverCore additionally lets a user pick US\n * Customary vs. Imperial vs. Metric cup/tablespoon/pint definitions as a\n * global preference, implementing that region-preference system is a\n * separate, larger feature and is NOT implemented here; an Imperial or\n * Metric-cup reading of \"cup\" is simply not available yet.\n */\nexport const COOKING_CONVERT_FN = \"cookingConvert\";\n\n/**\n * `amount` carries the source unit (e.g. Uom(300, \"g\")); `ingredientName`/\n * `targetUnitText` are the substance name (already lowercase, from the\n * fused `INGREDIENT_NAME` token) and the raw target-unit word typed after\n * \"in\" (e.g. \"cups\", \"grams\", \"tablespoons\", NOT required to already be a\n * recognized lexer `UNIT` token; this handler passes it straight to the\n * `convert` package, which accepts full unit names directly).\n */\nexport function cookingConvertHandler(args: Value[]): Value {\n const amountValue = args[0];\n const ingredientName = args[1].value as string;\n const targetUnitText = args[2].value as string;\n\n if (amountValue.type !== ValueType.Uom || !amountValue.unit) {\n return errorValue(\n \"COOKING_CONVERSION_REQUIRES_UNIT\",\n `Expected a value with a mass or volume unit (e.g. \"300g\", \"10 cups\"), got a plain number`,\n );\n }\n const sourceUnit = amountValue.unit;\n const amount = amountValue.toNumber();\n\n const sourceMeasure = getMeasure(sourceUnit);\n const targetMeasure = getMeasure(targetUnitText);\n const isMassOrVolume = (m: string | undefined) => m === \"mass\" || m === \"volume\";\n\n if (!isMassOrVolume(sourceMeasure)) {\n return errorValue(\n \"COOKING_CONVERSION_UNSUPPORTED_UNIT\",\n `\"${sourceUnit}\" is not a recognized mass or volume unit`,\n );\n }\n if (!isMassOrVolume(targetMeasure)) {\n return errorValue(\n \"COOKING_CONVERSION_UNSUPPORTED_UNIT\",\n `\"${targetUnitText}\" is not a recognized mass or volume unit`,\n );\n }\n\n // Same-measure conversion (e.g. \"300g butter in kg\"), density plays no\n // part; just a normal unit conversion, same as the UoM package's \"to\"/\"in\".\n if (sourceMeasure === targetMeasure) {\n return uomValue(convertUnit(amount, sourceUnit, targetUnitText), targetUnitText);\n }\n\n const density = getIngredientDensity(ingredientName);\n if (density === undefined) {\n return errorValue(\n \"COOKING_UNKNOWN_INGREDIENT\",\n `No density data for \"${ingredientName}\" — cannot convert between mass and volume for this ingredient`,\n );\n }\n\n if (sourceMeasure === \"mass\") {\n const grams = convertUnit(amount, sourceUnit, \"g\");\n const mL = grams / density;\n const result = convertUnit(mL, \"ml\", targetUnitText);\n return uomValue(result, targetUnitText);\n }\n\n // volume -> mass\n const mL = convertUnit(amount, sourceUnit, \"ml\");\n const grams = mL * density;\n const result = convertUnit(grams, \"g\", targetUnitText);\n return uomValue(result, targetUnitText);\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { COOKING_CONVERT_FN } from \"./CookingPluginFunctions\";\n\n/**\n * `<mass-or-volume> <substance> in <target-unit>`, cooking mass<->volume\n * conversion, e.g. \"300g butter in cups\" -> \"1.32 cup\", \"10 cups olive oil\n * in grams\" -> \"~2160 g\", \"100g nutella in tablespoons\".\n *\n * Registered as the INFIX parselet for the fused `INGREDIENT_NAME` token\n * (see `normalizer/IngredientNameNormalizerRule.ts`), the substance name\n * sits BETWEEN the quantity+unit and the target unit, the same shape as\n * `ClampParselet`'s \"clamp X between Y and Z\" (the pivot isn't a leading\n * keyword, so this isn't a `definePhrasePattern`-friendly grammar and is\n * hand-written instead).\n *\n * By the time this parselet fires, the left-hand `<mass-or-volume>` value\n * (e.g. \"300g\") is already a `Uom` value on the bytecode stack, the\n * standard `NUMBER UNIT` literal path (`UomLiteralParselet`, bindingPower\n * `Postfix`=70) already ran and left it there; this parselet's only job is\n * to consume the ingredient name (already fused, `token.value`), the\n * literal \"in\", and the target-unit word, then hand all three off to the\n * density-aware `cookingConvertHandler`.\n *\n * No collision with the currency/UoM package's generic `InParselet` (also\n * registered on bare `IN`, see `InflationQueryParselet.ts`'s doc comment\n * for that collision in a DIFFERENT context): here, `IN` never appears as\n * a bare lookahead token during a sub-expression parse, it's consumed\n * directly by THIS parselet immediately after the fused `INGREDIENT_NAME`\n * token, which itself owns the entire \"ingredient in target-unit\" span.\n */\nexport class CookingConversionParselet implements InfixParselet {\n readonly category = \"UoM\";\n readonly bindingPower = 35;\n\n parse(parser: Parser, _left: Token, token: Token, builder: BytecodeBuilder): void {\n const ingredientName = token.value;\n parser.consume(\"IN\");\n const targetToken = parser.consume();\n\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(ingredientName);\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(targetToken.value);\n builder.emitPluginCall(COOKING_CONVERT_FN, 3);\n }\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\nimport { MAX_INGREDIENT_NAME_WORDS } from \"../data/IngredientDensities\";\n\n/**\n * Fuses a substance name (e.g. \"butter\", \"olive oil\", \"unobtainium\") into a\n * single `INGREDIENT_NAME` token, backs the cooking mass<->volume grammar\n * `<mass-or-volume> <substance> in <target-unit>` (e.g. \"300g butter in\n * cups\"). See `CookingConversionParselet.ts` for the infix parselet that\n * consumes this fused token, and `CookingPluginFunctions.ts` for the\n * density lookup (an unrecognized substance name fuses here just fine but\n * produces a clear `COOKING_UNKNOWN_INGREDIENT` error value at runtime\n * this rule does NOT gate on `IngredientDensities.ts`'s table membership).\n *\n * WHY THIS IS A CONTEXT-SENSITIVE NORMALIZER RULE, NOT `IEnginePackage.phrases`:\n * `phrases` (the `PhraseTrie`) fuses a registered word/phrase EVERYWHERE it\n * appears in a token stream, unconditionally, that's exactly right for a\n * connector phrase like \"tax on\" (see `FinancePackage.ts`), but a substance\n * name here is an ordinary, plausible English word/variable name\n * (\"butter\", \"sugar\", \"milk\", \"flour\", ...) with NO connector to fuse it\n * onto (unlike \"tax on\"/\"interest on\", there's no natural second word that\n * always follows a substance name to disambiguate it from a bare\n * identifier). Registering these as bare phrase-trie entries would repeat\n * this session's exact \"total\"/\"tax\" bare-keyword regression (see\n * `packages/variables/parselets/VariableParselet.ts`'s doc comment) at a\n * much larger scale, `:butter = 4.99`, `:sugar = 2`, `:milk = 3` would\n * all silently break.\n *\n * Instead, this rule only fires in a narrowly-qualified CONTEXT:\n * 1. The token immediately BEFORE the candidate word(s) must be a real\n * `UNIT` token (the amount's mass/volume unit, e.g. \"g\"/\"cups\")\n * never true for a bare `:butter = 5` variable definition.\n * 2. The token(s) immediately AFTER the candidate word(s) must be a\n * literal `IN` keyword followed by a unit-like word (`UNIT` or\n * `IDENT`), never true for a bare identifier reference either.\n * Both conditions must hold, so a plain \"butter\" anywhere else in an\n * expression (variable name, arithmetic operand, whatever) is completely\n * unaffected and stays an ordinary `IDENT` token. Multi-word names (e.g.\n * \"olive oil\") are tried longest-first, up to\n * {@link MAX_INGREDIENT_NAME_WORDS} words -- the \"followed by IN\" check at\n * each candidate length is what correctly sizes the match (not a\n * known-ingredient lookup), so an as-yet-unrecognized multi-word substance\n * name is captured with the same accuracy as a recognized one.\n */\nexport function ingredientNameNormalizerRule(priority = 68): NormalizerRule {\n return {\n name: \"uom-cooking:ingredient-name\",\n priority,\n match(tokens, pos): NormalizerMatch | null {\n const prevToken = tokens[pos - 1];\n if (!prevToken || prevToken.type !== \"UNIT\") return null;\n\n for (let wordCount = MAX_INGREDIENT_NAME_WORDS; wordCount >= 1; wordCount--) {\n if (pos + wordCount > tokens.length) continue;\n\n const candidateTokens = tokens.slice(pos, pos + wordCount);\n if (!candidateTokens.every((t) => t.type === \"IDENT\")) continue;\n\n const afterIdx = pos + wordCount;\n const inToken = tokens[afterIdx];\n if (!inToken || inToken.type !== \"IN\") continue;\n const targetToken = tokens[afterIdx + 1];\n if (!targetToken || (targetToken.type !== \"UNIT\" && targetToken.type !== \"IDENT\")) continue;\n\n const candidate = candidateTokens.map((t) => t.value.toLowerCase()).join(\" \");\n return {\n consumed: wordCount,\n replacement: [createFusedToken(\"INGREDIENT_NAME\", candidate, candidateTokens)],\n ruleName: \"uom-cooking:ingredient-name\",\n };\n }\n return null;\n },\n };\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\nimport { UNIT_TABLE } from \"@solve-js/uom/generated/UnitTable.generated\";\n\n/**\n * Fuses two adjacent unit tokens into the single multi-word unit the table\n * already carries, when that is what the pair spells.\n *\n * The lexer reads a unit as one run of word characters, so a spelling with a\n * space in it arrives as two tokens and the first one wins on its own. That is\n * not merely a missing feature, it produces a wrong answer with no error: `fl`\n * is a lexable spelling of the FEMTOLITRE, so `1 fl oz` was one femtolitre\n * relabelled `oz` and `1 cup in fl oz` reported 236,588,236,500,000. A recipe's\n * sixteen fluid ounces became sixteen ounces of weight. `oz t` landed on tonnes\n * the same way, a factor of thirty thousand out, and `kW h` on hours.\n *\n * Fusing here rather than teaching the lexer about spaces keeps the lexer's one\n * rule about what a unit looks like intact, and keeps the vocabulary in the one\n * place it is already defined: only a pair the table itself spells with a space\n * fuses, so nothing is invented and the rule cannot drift from the table.\n *\n * Deliberately narrow in three ways:\n *\n * - Both tokens must be UNIT tokens, so an identifier that happens to sit\n * after a unit is left alone.\n * - They must be separated by exactly one space, because that is what the\n * table key contains. `1 fl oz` with two spaces is not a spelling the table\n * has, and neither is `1 fl` at the end of one line and `oz` at the start of\n * the next.\n * - The lookup is case-sensitive, like every other read of UNIT_TABLE. `W h`\n * is the watt-hour and `w h` is not a spelling of anything.\n *\n * Seventeen pairs qualify today: the watt-hour and its twelve metric prefixes,\n * `oz t`, `ac ft` and `fl oz`. Each of them was previously read as its second\n * token applied to a quantity of its first, which is wrong in every case.\n */\nexport function multiWordUnitNormalizerRule(priority = 78): NormalizerRule {\n\treturn {\n\t\tname: \"uom:multi-word-unit\",\n\t\tpriority,\n\t\tmatch(tokens, pos): NormalizerMatch | null {\n\t\t\tconst first = tokens[pos];\n\t\t\tconst second = tokens[pos + 1];\n\t\t\tif (first?.type !== \"UNIT\" || second?.type !== \"UNIT\") return null;\n\n\t\t\t// Exactly one space between them, measured from the source offsets\n\t\t\t// rather than assumed from token order.\n\t\t\tif (first.offset + first.text.length + 1 !== second.offset) return null;\n\n\t\t\tconst spelling = `${first.value} ${second.value}`;\n\t\t\tif (UNIT_TABLE[spelling] === undefined) return null;\n\n\t\t\treturn {\n\t\t\t\tconsumed: 2,\n\t\t\t\treplacement: [createFusedToken(\"UNIT\", spelling, tokens.slice(pos, pos + 2))],\n\t\t\t\truleName: \"uom:multi-word-unit\",\n\t\t\t};\n\t\t},\n\t};\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\n/**\n * Fuses a slash-notation compound unit, `UNIT / UNIT`, into one UNIT token\n * whose value is the rate spelling `\"numerator/denominator\"` (e.g. `km/h`,\n * `m/s`, `hours/day`).\n *\n * The lexer reads a unit as one run of word characters and stops at the slash,\n * so `km/h` arrived as three tokens and the parser saw `km` divided by `h`.\n * That is exactly a rate, and the surrounding code already represents a rate as\n * a `\"numerator/denominator\"` unit string, so fusing here produces the same\n * value the bare-denominator path builds for `100 km / h` while ALSO making the\n * compound spelling usable as a conversion target: `100 km/h in mph` needs the\n * whole `km/h` in one token before `in` can see it, and a target like `in km/h`\n * has no number to hang a bare denominator off at all.\n *\n * Deliberately narrow, so it never steals a case from a real division:\n *\n * - Both flanks must be UNIT tokens. `120 km / 2 hours` has a number after the\n * slash, so the slash stays a divide and the result is still a rate built the\n * ordinary way. `a / b` between variables is untouched.\n * - Exactly one slash is fused. `m/s^2` becomes `m/s` followed by `^2`, which\n * is the same value the engine already produced for it, a squared rate rather\n * than an acceleration. Naming a compound derived unit like the newton is left\n * for later; see the pull request for #89.\n *\n * Runs above the bare-denominator rule so the whole spelling is claimed at the\n * numerator's position before the slash can be read on its own.\n */\nexport function compoundUnitNormalizerRule(priority = 77): NormalizerRule {\n\treturn {\n\t\tname: \"uom:compound-unit\",\n\t\tpriority,\n\t\tmatch(tokens, pos): NormalizerMatch | null {\n\t\t\tconst numerator = tokens[pos];\n\t\t\tconst slash = tokens[pos + 1];\n\t\t\tconst denominator = tokens[pos + 2];\n\t\t\tif (numerator?.type !== \"UNIT\") return null;\n\t\t\tif (slash?.type !== \"SLASH\") return null;\n\t\t\tif (denominator?.type !== \"UNIT\") return null;\n\n\t\t\tconst spelling = `${numerator.value}/${denominator.value}`;\n\t\t\treturn {\n\t\t\t\tconsumed: 3,\n\t\t\t\treplacement: [createFusedToken(\"UNIT\", spelling, [numerator, slash, denominator] as Token[])],\n\t\t\t\truleName: \"uom:compound-unit\",\n\t\t\t};\n\t\t},\n\t};\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { UomLiteralParselet } from \"./parselets/UomLiteralParselet\";\nimport { ConvertParselet } from \"./parselets/ConvertParselet\";\nimport { PossibilitiesParselet } from \"./parselets/PossibilitiesParselet\";\nimport { uomPossibilitiesNormalizerRule } from \"./normalizer/PossibilitiesNormalizerRule\";\nimport { compoundQuantityNormalizerRule } from \"./normalizer/CompoundQuantityNormalizerRule\";\nimport { twoUnitConversionNormalizerRule } from \"./normalizer/TwoUnitConversionNormalizerRule\";\nimport { degreeSymbolNormalizerRule } from \"./normalizer/DegreeSymbolNormalizerRule\";\nimport { TwoUnitConversionParselet } from \"./parselets/TwoUnitConversionParselet\";\nimport { bareRateDenominatorNormalizerRule } from \"./normalizer/BareRateDenominatorNormalizerRule\";\nimport { PerUnitParselet } from \"./parselets/PerUnitParselet\";\nimport { AtRateParselet } from \"./parselets/AtRateParselet\";\nimport { atRateNormalizerRule } from \"./normalizer/AtRateNormalizerRule\";\nimport { forDurationNormalizerRule } from \"./normalizer/ForDurationNormalizerRule\";\nimport { reversedConversionNormalizerRule } from \"./normalizer/ReversedConversionNormalizerRule\";\nimport { CookingConversionParselet } from \"./parselets/CookingConversionParselet\";\nimport { COOKING_CONVERT_FN, cookingConvertHandler } from \"./parselets/CookingPluginFunctions\";\nimport { ingredientNameNormalizerRule } from \"./normalizer/IngredientNameNormalizerRule\";\nimport { multiWordUnitNormalizerRule } from \"./normalizer/MultiWordUnitNormalizerRule\";\nimport { compoundUnitNormalizerRule } from \"./normalizer/CompoundUnitNormalizerRule\";\n\n/**\n * Units of measurement: `10 km`, `10 km to miles`, `convert 10 km to miles`,\n * `cm to ?` (conversion-possibilities query). See {@link CURRENCY_PACKAGE}\n * for money, which is a separate package.\n *\n * Also cooking/baking mass<->volume conversion (e.g. \"300g butter in\n * cups\", \"10 cups olive oil in grams\"). See\n * `parselets/CookingConversionParselet.ts`,\n * `normalizer/IngredientNameNormalizerRule.ts`, and\n * `data/IngredientDensities.ts` (the bundled, clearly-labeled-approximate\n * ingredient-density table and its scope/accuracy doc comment) for the\n * full design, including why US Customary is the only volume-unit\n * convention supported so far.\n */\nexport const UOM_PACKAGE: IEnginePackage = {\n name: \"solve-uom\",\n prefixParselets: {\n CONVERT: new ConvertParselet(),\n UOM_POSSIBILITIES_QUERY: new PossibilitiesParselet(),\n },\n infixParselets: {\n IN_TWO_UNITS: new TwoUnitConversionParselet(94),\n PER_UNIT: new PerUnitParselet(95),\n AT_RATE: new AtRateParselet(96),\n UNIT: new UomLiteralParselet(),\n INGREDIENT_NAME: new CookingConversionParselet(),\n },\n normalizerRules: [\n // Highest priority of the unit rules: a two-word spelling has to become one\n // token before anything else reads the first word as a unit on its own.\n multiWordUnitNormalizerRule(),\n // Above the bare-denominator rule (75): a slash-notation compound unit like\n // `km/h` is claimed whole before the slash is read as a bare denominator.\n compoundUnitNormalizerRule(),\n uomPossibilitiesNormalizerRule(),\n compoundQuantityNormalizerRule(),\n twoUnitConversionNormalizerRule(),\n degreeSymbolNormalizerRule(),\n atRateNormalizerRule(),\n forDurationNormalizerRule(),\n bareRateDenominatorNormalizerRule(),\n reversedConversionNormalizerRule(),\n ingredientNameNormalizerRule(),\n ],\n pluginFunctions: {\n [COOKING_CONVERT_FN]: cookingConvertHandler,\n },\n};\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { resolveCurrencyAlias } from \"@solve-js/uom/CurrencyAliases\";\n\n/**\n * Currency symbol before an amount, as in `$100` or `£25`.\n *\n * Resolves the symbol through the shared alias table so that the symbol, word\n * and code forms all produce the same currency, and falls back to the raw text\n * uppercased when the symbol is unknown rather than rejecting the line.\n */\nexport class CurrencySymbolParselet implements PrefixParselet {\n\treadonly category = \"UoM\";\n\tparse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n const currency = resolveCurrencyAlias(token.value) ?? token.value.toUpperCase();\n parser.parseExpression(BindingPower.Prefix, builder);\n if (parser.peek()?.type === \"UNIT\") {\n parser.consume();\n }\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(currency);\n builder.emitOpcode(OpCode.UOM_CONVERT);\n }\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { resolveCurrencyAlias } from \"@solve-js/uom/CurrencyAliases\";\nimport { tryConsumeCurrencyOnDate, HISTORICAL_CURRENCY_FN } from \"@solve-js/uom/HistoricalCurrency\";\n\n/**\n * InParselet, handles the standalone `IN` keyword as a postfix conversion.\n *\n * Enables expressions like `125 USD in GBP`, `(a + b) in minutes`, or\n * `price in EUR` where the left side can be any expression (not just a\n * bare UNIT token).\n *\n * When `IN` directly follows a UNIT token (e.g., `25 USD in GBP`), the\n * UomLiteralParselet handles the conversion inline using UOM_CONVERT_TO.\n * This parselet only fires when `IN` follows non-UNIT expressions (e.g.,\n * variables, subexpressions, parenthesized values).\n *\n * Binding power 35 (between Sum=30 and Product=40):\n * - `100 + 25 in GBP` → `100 + (25 in GBP)` (in binds tighter than +)\n * - `25 in GBP * 2` → `(25 in GBP) * 2` (in binds looser than *)\n */\nexport class InParselet implements InfixParselet {\n\treadonly category = \"UoM\";\n\treadonly bindingPower = 35;\n\n\tparse(parser: Parser, _left: Token, _token: Token, builder: BytecodeBuilder): void {\n\t\tconst targetToken = parser.peek();\n\t\t// Accept UNIT, currency symbols, bare IDENT, or IN (for cases like\n\t\t// \"3 ft in in\" where the target unit is tokenized as a keyword).\n\t\tif (targetToken && (\n\t\t\ttargetToken.type === \"UNIT\" ||\n\t\t\ttargetToken.type === \"DOLLAR\" ||\n\t\t\ttargetToken.type === \"POUND\" ||\n\t\t\ttargetToken.type === \"EURO\" ||\n\t\t\ttargetToken.type === \"YEN\" ||\n\t\t\ttargetToken.type === \"RUBLE\" ||\n\t\t\ttargetToken.type === \"WON\" ||\n\t\t\ttargetToken.type === \"CURRENCY_SYMBOL\" ||\n\t\t\ttargetToken.type === \"IDENT\" ||\n\t\t\ttargetToken.type === \"IN\"\n\t\t)) {\n\t\t\tparser.consume();\n\t\t\tconst targetUnit = resolveCurrencyAlias(targetToken.value) ?? targetToken.value;\n\n\t\t\t// `<money> in <currency> on <date>` where the left side is an\n\t\t\t// expression (`$100`, a variable, a subexpression) rather than a bare\n\t\t\t// UNIT literal. The source currency is unknown until the VM produces\n\t\t\t// the left value, so only the target is checked here; the historical\n\t\t\t// plugin reads the source currency off that Uom at runtime. See\n\t\t\t// uom/HistoricalCurrency.ts. Anything that is not `on <date>` between\n\t\t\t// currencies consumes nothing and falls through to the live path.\n\t\t\tconst isoDate = tryConsumeCurrencyOnDate(parser, targetUnit);\n\t\t\tif (isoDate !== null) {\n\t\t\t\t// The left expression is already a Uom on the stack; hand\n\t\t\t\t// [amount, target, date] to the historical plugin.\n\t\t\t\tbuilder.emitOpcode(OpCode.PUSH_STRING);\n\t\t\t\tbuilder.emitString(targetUnit);\n\t\t\t\tbuilder.emitOpcode(OpCode.PUSH_STRING);\n\t\t\t\tbuilder.emitString(isoDate);\n\t\t\t\tbuilder.emitPluginCall(HISTORICAL_CURRENCY_FN, 3);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tbuilder.emitOpcode(OpCode.PUSH_STRING);\n\t\t\tbuilder.emitString(targetUnit);\n\t\t\tbuilder.emitOpcode(OpCode.UOM_CONVERT_IN);\n\t\t}\n\t\t// If the next token isn't a valid target unit, silently skip.\n\t\t// The left expression remains on the stack unchanged.\n\t}\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { CurrencySymbolParselet } from \"./parselets/CurrencySymbolParselet\";\nimport { InParselet } from \"./parselets/InParselet\";\nimport { CurrencyAsyncResolver } from \"@solve-js/uom/CurrencyResolver\";\nimport {\n HISTORICAL_CURRENCY_FN,\n createHistoricalCurrencyResolver,\n createHistoricalCurrencyPluginFunction,\n} from \"@solve-js/uom/HistoricalCurrency\";\nimport type { CurrencyPackageConfig } from \"./types\";\n\n/**\n * Currency: `$10`, `£10`, `€10`, `¥10`, `₽10`, `₩10`, `₹10`, `₺10`, `₴10`,\n * `₪10`, `₫10`, `₦10`, `₱10`, `10 USD in GBP`, `100 USD in GBP on 2024-01-15`,\n * and word forms like `10 euros`/`10 dollars` (see `uom/CurrencyAliases.ts` for\n * the full symbol/word alias tables and the ambiguity decisions behind them).\n *\n * Live rates are fetched asynchronously (via {@link CurrencyAsyncResolver}) and\n * the expression shows Pending until they resolve. The dated `on <date>` form\n * resolves through a HOST-SUPPLIED {@link CurrencyPackageConfig.historicalRateProvider}\n * instead, unconfigured, it reports `HISTORICAL_RATES_NOT_CONFIGURED` plainly\n * rather than drifting to today's rate (see `uom/HistoricalCurrency.ts`).\n *\n * **A factory, but still a default builtin.** Unlike stocks (no free provider,\n * so excluded from `BUILTIN_PACKAGES`), the LIVE half of currency needs no\n * configuration, so {@link CURRENCY_PACKAGE} = `createCurrencyPackage()` ships\n * as a default. The historical resolver is registered either way, so the\n * grammar always recognises `on <date>` and answers the not-configured case\n * honestly; supplying a provider is what turns that error into a real rate. A\n * host wanting historical conversion calls `createCurrencyPackage({ historicalRateProvider })`\n * and swaps the result in for the default in its `packages` array.\n */\nexport function createCurrencyPackage(config: CurrencyPackageConfig = {}): IEnginePackage {\n return {\n name: \"solve-currency\",\n asyncResolvers: [\n new CurrencyAsyncResolver(),\n createHistoricalCurrencyResolver(config.historicalRateProvider),\n ],\n prefixParselets: {\n DOLLAR: new CurrencySymbolParselet(),\n POUND: new CurrencySymbolParselet(),\n EURO: new CurrencySymbolParselet(),\n YEN: new CurrencySymbolParselet(),\n RUBLE: new CurrencySymbolParselet(),\n WON: new CurrencySymbolParselet(),\n // Every currency symbol added after the original six above shares this\n // one generic token type. See Token.ts's CURRENCY_SYMBOL doc comment.\n CURRENCY_SYMBOL: new CurrencySymbolParselet(),\n },\n infixParselets: {\n IN: new InParselet(),\n },\n pluginFunctions: {\n // Historical conversions (`<money> in <currency> on <date>`) compile to a\n // CALL_PLUGIN at this shared index (see uom/HistoricalCurrency.ts). One\n // index serves every pair and date, the query is the amount plus the\n // target and date strings. The handler carries the same provider as the\n // resolver, so a source currency known only at runtime (`x in GBP on\n // <date>`) can fetch the rate the bytecode scan could not preflight.\n [HISTORICAL_CURRENCY_FN]: createHistoricalCurrencyPluginFunction(config.historicalRateProvider),\n },\n };\n}\n\n/**\n * The default currency package, live rates configured, historical rates NOT.\n *\n * Kept as a named constant so existing imports and {@link BUILTIN_PACKAGES}\n * keep working unchanged, a host wanting historical conversion builds its own\n * via {@link createCurrencyPackage} and substitutes it in.\n */\nexport const CURRENCY_PACKAGE: IEnginePackage = createCurrencyPackage();\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * Vector literals, both bracketed and the bare tuple form.\n *\n * The dimension passed to the constructor pins the expected component count so\n * a two-dimensional literal cannot silently parse as three; zero means accept\n * whatever the literal contains.\n */\nexport class VectorParselet implements PrefixParselet {\n\treadonly category = \"Vector\";\n\tprivate dimension: number;\n\n constructor(dimension = 0) {\n this.dimension = dimension;\n }\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n parser.consume(\"LPAREN\");\n let count = 0;\n if (parser.peek()?.type !== \"RPAREN\") {\n parser.parseExpression(0, builder);\n count++;\n while (parser.match(\"COMMA\")) {\n parser.parseExpression(0, builder);\n count++;\n }\n }\n parser.consume(\"RPAREN\");\n // Legacy vector-constructor sugar, kept working as a 1xN row-vector\n // Matrix (Calca has no equivalent syntax; this codebase's own existing\n // tests exercise vec2/vec3/vec4, so it stays as sugar rather than being\n // removed. See MatrixOps.ts/the bracket-literal matrix syntax for the\n // primary construction path going forward).\n builder.emitOpcode(OpCode.MAT_NEW);\n builder.emitIndex(1);\n builder.emitIndex(this.dimension > 0 ? this.dimension : count);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * A floating-point component inside a vector literal.\n *\n * Separate from the ordinary number parselet because vector components parse\n * in a context where a bare comma separates elements rather than arguments.\n */\nexport class FloatParselet implements PrefixParselet {\n\treadonly category = \"Float\";\n\tparse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n\t\tparser.consume(\"LPAREN\");\n\t\tparser.parseExpression(0, builder);\n\t\tparser.consume(\"RPAREN\");\n\t\t// A 1x1 Matrix. See VectorParselet.ts's comment on why this legacy\n\t\t// sugar emits MAT_NEW rather than being removed.\n\t\tbuilder.emitOpcode(OpCode.MAT_NEW);\n\t\tbuilder.emitIndex(1);\n\t\tbuilder.emitIndex(1);\n\t}\n}","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { VectorParselet } from \"./parselets/VectorParselet\";\nimport { FloatParselet } from \"./parselets/FloatParselet\";\n\n/**\n * Vector literals `vec2(x, y)`/`vec3(x, y, z)`/`vec4(x, y, z, w)` plus float\n * literals, legacy construction sugar kept working after the Calca-parity\n * Matrix rewrite; each just builds a 1xN row-vector `ValueType.Matrix` via\n * `OpCode.MAT_NEW` (see vm/MatrixOps.ts). The bracket literal `[x, y, z]`\n * (packages/matrix/) is the primary, Calca-parity construction syntax going\n * forward.\n */\nexport const VECTOR_PACKAGE: IEnginePackage = {\n name: \"solve-vector\",\n prefixParselets: {\n VEC2: new VectorParselet(2),\n VEC3: new VectorParselet(3),\n VEC4: new VectorParselet(4),\n FLOAT: new FloatParselet(),\n },\n};\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/**\n * Matrix literal, `[1, 2; 3, 4]` (comma-separated values within a row\n * semicolon-separated rows). A single row with no semicolons, `[1, 2, 3]`,\n * is a 1xN row-vector Matrix, Calca's own convention, and this engine's\n * primary construction syntax going forward (see\n * `packages/vector/parselets/VectorParselet.ts`'s doc comment for the\n * legacy `vec2(...)`/bare-tuple sugar this supersedes).\n *\n * Cells may be ANY expression, not just literals (`[foo, bar] * 2`), so\n * each cell is parsed via the ordinary `parseExpression()` recursive\n * descent, exactly like a function-call argument list.\n *\n * Confirmed zero grammar collision: `LBRACKET`/`RBRACKET` have no other\n * prefix/infix parselet registered anywhere in this engine, the only\n * other consumer is `datetime/normalizer/DailyNoteLinkNormalizerRule.ts`'s\n * narrow DOUBLE-bracket `[[2024-01-15]]` daily-note-link unwrap, which\n * only ever matches that exact 5-token shape and never single brackets.\n */\nexport class MatrixLiteralParselet implements PrefixParselet {\n readonly category = \"Matrix\";\n\n parse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n let rows = 0;\n let cols = -1; // -1 = not yet known (set by the first row)\n\n // Empty matrix literal `[]`, reject rather than silently emit a 0x0\n // Matrix, which every downstream consumer (arithmetic, indexing,\n // display) would have to special-case for no real benefit.\n if (parser.peek()?.type === \"RBRACKET\") {\n throw ErrorFactory.parsing(\n \"EMPTY_MATRIX_LITERAL\",\n \"A matrix literal cannot be empty — `[]` has no valid shape.\",\n {},\n );\n }\n\n for (;;) {\n let rowCols = 0;\n parser.parseExpression(0, builder);\n rowCols++;\n while (parser.match(\"COMMA\")) {\n parser.parseExpression(0, builder);\n rowCols++;\n }\n\n if (cols === -1) {\n cols = rowCols;\n } else if (rowCols !== cols) {\n throw ErrorFactory.parsing(\n \"RAGGED_MATRIX_LITERAL\",\n `Matrix literal rows must all have the same number of columns — row ${rows + 1} has ${rowCols}, but a previous row has ${cols}.`,\n { expectedCols: cols, actualCols: rowCols, row: rows + 1 },\n );\n }\n rows++;\n\n if (!parser.match(\"SEMICOLON\")) break;\n }\n\n parser.consume(\"RBRACKET\");\n\n // Cells were parsed/pushed in row-major reading order (matching how\n // `[1,2;3,4]` is textually written), MAT_NEW's own VM handler\n // transposes this into the column-major storage MatrixData actually\n // uses (see vm/MatrixOps.ts's rowMajorToColumnMajor()).\n builder.emitOpcode(OpCode.MAT_NEW);\n builder.emitIndex(rows);\n builder.emitIndex(cols);\n }\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/**\n * Matrix indexing/slicing, `a[i]` (column-major single index, matching\n * `MatrixData.data`'s own storage order directly), `a[row, col]` (two-arg\n * point index, both 0-based), or range-based slicing where either/both\n * arguments use an explicit `min:max` bound (`a[0:1, 1:2]`). Registered as\n * an INFIX parselet on LBRACKET, a separate registry slot from\n * `MatrixLiteralParselet`'s PREFIX registration on the same token: `[`\n * opens a literal in prefix position (right after `=`, `(`, an operator,\n * ...) and opens an index in infix (postfix) position, right after any\n * value-producing expression.\n *\n * The `:` inside `[...]` is hand-consumed directly here (`parser.match\n * (\"COLON\")`), exactly like `packages/lines/parselets/RangeAggregateParselet.ts`'s\n * own `sum(line 1 : line 4)` grammar, NOT via a general registry-wide\n * infix parselet on COLON. That distinction matters: a general COLON\n * operator would also fire for a bare top-level `label: value` line,\n * silently breaking the shipped \"labeled-line fallback\" feature (see\n * ExpressionEngine.ts's `parseExpression()` doc comment, and\n * `__tests__/engine/LabeledLine.spec.ts`'s \"total: 5 + 3\" case, which\n * resolves the \"total\" label ONLY because nothing today gives COLON an\n * infix meaning at the top level). Hand-parsing it here, scoped to\n * exactly this grammar production, gets range-slicing without touching\n * that shared token's meaning anywhere else in the language. A second,\n * independent collision, the Time package's clock-time/laptime/video-\n * timecode normalizer rules fuse ANY bare `NUMBER:NUMBER...` sequence\n * (context-blind, pre-parse), is handled separately via each of those\n * rules' own `isInsideRangeContext()` guard.\n *\n * A plain point index (no colon anywhere in the brackets) takes the\n * ORIGINAL scalar-result path (`MAT_INDEX1`/`MAT_INDEX2`), with bytecode\n * emission bit-for-bit identical to before range-slicing existed. Slicing\n * (`MAT_SLICE`) is a genuinely different operation, it always returns a\n * sub-Matrix, never a bare scalar, even for a single-cell selection like\n * `a[0:0, 0:0]`, so the two paths stay fully separate rather than\n * unified, matching Calca's own point-vs-range distinction. Which path\n * applies is decided via a one-time lookahead ({@link hasTopLevelColon})\n * BEFORE either argument is parsed, so each argument can be wrapped into a\n * Range (or left as a raw value) immediately as it's parsed, with no need\n * to reorder or patch up the stack afterward, deferring that decision\n * and trying to wrap arguments after the fact does NOT work, since\n * wrapping the last-parsed argument first buries any earlier argument's\n * still-unwrapped value(s) under it, with no way to reach back down.\n */\nexport class MatrixIndexParselet implements InfixParselet {\n readonly category = \"Matrix\";\n readonly bindingPower = BindingPower.Postfix;\n\n parse(parser: Parser, _left: Token, _token: Token, builder: BytecodeBuilder): void {\n const isSlice = this.hasTopLevelColon(parser);\n\n this.parseIndexArg(parser, builder, isSlice);\n let argCount = 1;\n if (parser.match(\"COMMA\")) {\n this.parseIndexArg(parser, builder, isSlice);\n argCount = 2;\n }\n parser.consume(\"RBRACKET\");\n\n if (!isSlice) {\n if (argCount === 1) {\n builder.emitOpcode(OpCode.MAT_INDEX1);\n } else {\n builder.emitOpcode(OpCode.MAT_INDEX2);\n }\n return;\n }\n\n if (argCount !== 2) {\n throw ErrorFactory.parsing(\n \"INVALID_MATRIX_SLICE_ARITY\",\n `Range-based matrix slicing needs exactly 2 arguments (\"a[rowRange, colRange]\"), got ${argCount}.`,\n { argCount },\n );\n }\n\n builder.emitOpcode(OpCode.MAT_SLICE);\n }\n\n /**\n * Read-only lookahead (no tokens consumed) from the current position\n * right after the triggering `[`, to whichever `]` matches it, tracking\n * `[`/`(` nesting depth so a colon inside a NESTED bracket/paren (e.g.\n * `a[b[0:1], 2]`'s inner slice, or a function-call argument) is never\n * mistaken for one of THIS bracket's own top-level range separators.\n * Determines the whole bracket's point-vs-slice interpretation upfront,\n * before any argument is actually parsed.\n */\n private hasTopLevelColon(parser: Parser): boolean {\n let depth = 0;\n for (let offset = 0; ; offset++) {\n const t = parser.peekAt(offset);\n if (!t) return false; // unbalanced brackets — the real parse below will throw its own error\n if (t.type === \"LBRACKET\" || t.type === \"LPAREN\") {\n depth++;\n } else if (t.type === \"RBRACKET\" || t.type === \"RPAREN\") {\n if (depth === 0) return false; // this is OUR closing bracket — nothing found before it\n depth--;\n } else if (t.type === \"COLON\" && depth === 0) {\n return true;\n }\n }\n }\n\n /**\n * Parses one index/slice argument: a plain expression, or `expr : expr`\n * for an explicit range. When `isSlice` is true (decided once, upfront,\n * for the WHOLE bracket via {@link hasTopLevelColon}), immediately\n * collapses whatever this argument pushed into a single Range value\n * `RANGE_NEW` directly for an explicit `min:max` pair already on the\n * stack, or `DUP` + `RANGE_NEW` for a lone point (becoming `Range(v,\n * v)`, a single-cell selection along that dimension). When `isSlice` is\n * false, no wrapping happens at all, the argument's raw value is left\n * exactly as `MAT_INDEX1`/`MAT_INDEX2` already expect.\n */\n private parseIndexArg(parser: Parser, builder: BytecodeBuilder, isSlice: boolean): void {\n parser.parseExpression(0, builder);\n let isRange = false;\n if (parser.match(\"COLON\")) {\n parser.parseExpression(0, builder);\n isRange = true;\n }\n if (isSlice) {\n if (!isRange) {\n builder.emitOpcode(OpCode.DUP);\n }\n builder.emitOpcode(OpCode.RANGE_NEW);\n }\n }\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { MatrixLiteralParselet } from \"./parselets/MatrixLiteralParselet\";\nimport { MatrixIndexParselet } from \"./parselets/MatrixIndexParselet\";\n\n/**\n * Matrix literals `[1, 2; 3, 4]` and indexing `a[i]`/`a[row, col]`\n * Calca-parity general matrix support (a vector is just a 1xN or Nx1\n * matrix). See `vm/Value.ts`'s `MatrixData` and `vm/MatrixOps.ts`'s shared\n * column-major storage helpers.\n *\n * `LBRACKET` is registered in BOTH prefix and infix slots, separate\n * registry lookups keyed on parse position, not a collision: a literal\n * opens in prefix position (right after `=`, `(`, an operator, ...) and an\n * index opens in infix/postfix position, right after any value-producing\n * expression.\n */\nexport const MATRIX_PACKAGE: IEnginePackage = {\n name: \"solve-matrix\",\n prefixParselets: {\n LBRACKET: new MatrixLiteralParselet(),\n },\n infixParselets: {\n LBRACKET: new MatrixIndexParselet(),\n },\n};\n","import { Parser } from \"@solve-js/parser/Parser\";\nimport { BytecodeBuilder, type BytecodeProgram } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\nimport { builtinNameToIndex } from \"@solve-js/packages/function/parselets/FunctionCallParselet\";\n\n/**\n * A resolved `map`/`reduce` transform, the first argument to either call.\n * `kind` matches the `MAP_INVOKE`/`REDUCE_INVOKE` opcode's own operand\n * (see `vm/VM.ts`'s handlers): 0 = an inline anonymous body (compiled to\n * its own independent `BytecodeProgram`, registered via\n * `BytecodeBuilder.emitAnonymousBody()`), 1 = a builtin function (resolved\n * at PARSE time against `FunctionCallParselet`'s own name table), 2 = a\n * user-defined function (deferred to RUNTIME, resolved dynamically via\n * `vm.getUserFunction()`, since it may be defined later in the document,\n * mirroring `CALL_USER_FUNCTION`'s own forward-reference philosophy).\n */\nexport type TransformSpec =\n | { kind: 0; program: BytecodeProgram }\n | { kind: 1; builtinIdx: number }\n | { kind: 2; name: string };\n\n/**\n * Parses `map`/`reduce`'s first argument, the \"transform\", disambiguating\n * a bare function-name reference from a genuine inline expression.\n *\n * SCOPE NOTE: a bare single identifier immediately followed by a comma is\n * ALWAYS interpreted as a function-name reference (builtin if known,\n * otherwise deferred as a user-defined-function reference), NEVER as the\n * trivial \"identity\" inline expression that's just that one variable.\n * Every spec example uses a genuine multi-token expression (`10*x`,\n * `acc+x`) for the inline form, so this keeps the two cases unambiguous\n * with no fuzzy runtime fallback heuristics needed. The one real cost:\n * you can't write `map(x, [1,2,3])` to mean an identity map, a vanishingly\n * rare thing to want (why not just use the array directly?), and not\n * something any spec example shows.\n */\nexport function parseTransform(parser: Parser, builder: BytecodeBuilder): TransformSpec {\n const next = parser.peek();\n const afterNext = parser.peekAt(1);\n if (next && (next.type === \"IDENT\" || next.type === \"UNIT\" || next.type === \"FUNC\") && afterNext?.type === \"COMMA\") {\n parser.consume();\n const name = next.value;\n const builtinIdx = builtinNameToIndex[name.toLowerCase()];\n if (builtinIdx !== undefined) {\n return { kind: 1, builtinIdx };\n }\n return { kind: 2, name };\n }\n\n // Genuine inline expression, compiled into its OWN independent\n // BytecodeProgram, exactly like a user-defined function's body\n // (PrecedenceParser.ts's parseUserFunctionDefinition): parseExpression()\n // sets `this.builder` to the isolated builder with NO automatic\n // restore, so setBuilder(builder) explicitly restores it afterward.\n const transformBuilder = new BytecodeBuilder(builder.pluginIndexMap);\n parser.parseExpression(BindingPower.Lowest, transformBuilder);\n parser.setBuilder(builder);\n const program = transformBuilder.build();\n if (program.hasAsync) {\n throw ErrorFactory.parsing(\n \"MAP_REDUCE_TRANSFORM_MUST_BE_SYNCHRONOUS\",\n `map/reduce transform expressions must be synchronous (no weather/stocks/currency calls).`,\n );\n }\n return { kind: 0, program };\n}\n\n/** Whether the upcoming argument uses the explicit zipped `name=collection` form (peek-only, nothing consumed). */\nexport function isZippedCollectionForm(parser: Parser): boolean {\n const next = parser.peek();\n const afterNext = parser.peekAt(1);\n return !!next && (next.type === \"IDENT\" || next.type === \"UNIT\") && afterNext?.type === \"EQUALS\";\n}\n\n/** Consumes a zipped-form collection name (`name` in `name=collection`). */\nexport function consumeCollectionName(parser: Parser): string {\n const token = parser.peek();\n if (token && (token.type === \"IDENT\" || token.type === \"UNIT\")) {\n parser.consume();\n return token.value;\n }\n throw ErrorFactory.parsing(\n \"MAP_REDUCE_EXPECTED_COLLECTION_NAME\",\n `Expected a collection name (e.g. \"x\" in \"x=[1,2,3]\") but got ${token ? `\"${token.value}\"` : \"end of input\"}.`,\n );\n}\n\n/**\n * Parses one \"collection\" argument, a plain expression, or `expr : expr`\n * for a bare Range (`map(f, 0:3)`'s own spec example), hand-consuming the\n * `:` locally exactly like `MatrixIndexParselet` does, for the same\n * reason: a general infix COLON operator would break the shipped\n * labeled-line fallback feature (see that parselet's own doc comment for\n * the full explanation).\n */\nexport function parseCollectionExpr(parser: Parser, builder: BytecodeBuilder): void {\n parser.parseExpression(0, builder);\n if (parser.match(\"COLON\")) {\n parser.parseExpression(0, builder);\n builder.emitOpcode(OpCode.RANGE_NEW);\n }\n}\n\n/**\n * Emits a resolved transform + its `MAP_INVOKE`/`REDUCE_INVOKE` opcode\n * `kind`/`ref` are common to both opcodes; `thirdOperand` is\n * `collectionCount` for `MAP_INVOKE` or `hasInitial` (0|1) for\n * `REDUCE_INVOKE`. Registers an inline (kind 0) body into `builder`'s own\n * `anonymousBodies` side-table only NOW, after every collection argument\n * has already been parsed, so `paramNames` (fixed `[\"acc\",\"x\"]` for\n * reduce, or the zipped-form's declared names for map) is fully known.\n */\nexport function emitInvoke(\n builder: BytecodeBuilder,\n opcode: OpCode.MAP_INVOKE | OpCode.REDUCE_INVOKE,\n transform: TransformSpec,\n paramNames: string[],\n thirdOperand: number,\n): void {\n builder.emitOpcode(opcode);\n builder.emitIndex(transform.kind);\n if (transform.kind === 0) {\n const idx = builder.emitAnonymousBody(paramNames, transform.program);\n builder.emitIndex(idx);\n } else if (transform.kind === 1) {\n builder.emitIndex(transform.builtinIdx);\n } else {\n builder.emitString(transform.name);\n }\n builder.emitIndex(thirdOperand);\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport {\n parseTransform,\n isZippedCollectionForm,\n consumeCollectionName,\n parseCollectionExpr,\n emitInvoke,\n} from \"../MapReduceShared\";\n\n/**\n * `map(transform, collection)` or `map(transform, name1=collection1,\n * name2=collection2, ...)`, applies `transform` to every (zipped)\n * element of one or more collections (a Matrix or a bare Range\n * `map(f, 0:3)`), producing a new 1xN row-vector Matrix.\n *\n * The simple single-collection form implicitly binds the reserved name\n * `x` (`map(10*x, [0,1,500])`); the explicit zipped form declares its own\n * names (`map(10*y+x, x=[1,2], y=[3,4])`). See `MapReduceShared.ts`'s\n * `parseTransform()` for how `transform` itself is disambiguated between\n * an inline expression, a bare builtin name, and a bare user-function\n * name.\n */\nexport class MapParselet implements PrefixParselet {\n readonly category = \"MapReduce\";\n\n parse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n parser.consume(\"LPAREN\");\n\n const transform = parseTransform(parser, builder);\n parser.consume(\"COMMA\");\n\n const paramNames: string[] = [];\n if (isZippedCollectionForm(parser)) {\n do {\n const name = consumeCollectionName(parser);\n parser.consume(\"EQUALS\");\n parseCollectionExpr(parser, builder);\n paramNames.push(name);\n } while (parser.match(\"COMMA\"));\n } else {\n parseCollectionExpr(parser, builder);\n paramNames.push(\"x\");\n }\n\n parser.consume(\"RPAREN\");\n\n emitInvoke(builder, OpCode.MAP_INVOKE, transform, paramNames, paramNames.length);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { parseTransform, parseCollectionExpr, emitInvoke } from \"../MapReduceShared\";\n\n/**\n * `reduce(transform, collection[, initial])`, folds `collection` (a\n * Matrix or a bare Range) into a single value using `transform`, an\n * inline expression using the reserved names `acc`/`x`\n * (`reduce(acc+x, [1,2,3])`) or a bare 2-argument function reference\n * (builtin or user-defined, `reduce(f, [1,2,3])` is `f(f(1,2),3)`).\n *\n * Without `initial`, the collection's own first element seeds the\n * accumulator and folding starts from the second element, an empty\n * collection is then a clear error (there's nothing to seed from). With\n * `initial`, folding starts from `initial` over EVERY element\n * (`reduce(f,[1,2,3],1000)` is `f(f(f(1000,1),2),3)`).\n */\nexport class ReduceParselet implements PrefixParselet {\n readonly category = \"MapReduce\";\n\n parse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n parser.consume(\"LPAREN\");\n\n const transform = parseTransform(parser, builder);\n parser.consume(\"COMMA\");\n\n parseCollectionExpr(parser, builder);\n\n let hasInitial = 0;\n if (parser.match(\"COMMA\")) {\n // A plain expression, an initial accumulator is just a value, no\n // colon-range handling needed here (only the COLLECTION argument\n // accepts a bare Range).\n parser.parseExpression(BindingPower.Lowest, builder);\n hasInitial = 1;\n }\n\n parser.consume(\"RPAREN\");\n\n emitInvoke(builder, OpCode.REDUCE_INVOKE, transform, [\"acc\", \"x\"], hasInitial);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\nimport { parseCollectionExpr, emitInvoke } from \"../MapReduceShared\";\n\n/**\n * `sum(elementExpr, collection)`, parse-time sugar for\n * `reduce(acc+elementExpr, collection)`, no separate runtime\n * implementation. `elementExpr` may reference the reserved name `x` (the\n * current element), `sum(x, c)` is the trivial \"sum of the raw\n * elements\" case; `sum(x^2, c)` would be \"sum of squares\", etc.\n *\n * Built by emitting `LOAD_VAR acc` directly into a fresh builder, then\n * letting the parser continue writing `elementExpr`'s own bytecode onto\n * that SAME builder, then appending `ADD`, no bytecode-splicing API is\n * needed since `parseExpression(minBp, builder)` just keeps emitting\n * whatever comes next onto whichever builder is currently active.\n */\nexport class SumParselet implements PrefixParselet {\n readonly category = \"MapReduce\";\n\n parse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n parser.consume(\"LPAREN\");\n\n const bodyBuilder = new BytecodeBuilder(builder.pluginIndexMap);\n bodyBuilder.emitOpcode(OpCode.LOAD_VAR);\n bodyBuilder.emitString(\"acc\");\n parser.parseExpression(BindingPower.Lowest, bodyBuilder);\n parser.setBuilder(builder);\n bodyBuilder.emitOpcode(OpCode.ADD);\n const bodyProgram = bodyBuilder.build();\n if (bodyProgram.hasAsync) {\n throw ErrorFactory.parsing(\n \"MAP_REDUCE_TRANSFORM_MUST_BE_SYNCHRONOUS\",\n `sum's element expression must be synchronous (no weather/stocks/currency calls).`,\n );\n }\n\n parser.consume(\"COMMA\");\n parseCollectionExpr(parser, builder);\n parser.consume(\"RPAREN\");\n\n // Seed the accumulator with zero rather than letting `reduce` take the\n // collection's first element. Seeding from the first element skips the\n // element expression for that one element, so `sum(x*10, [5])` folded to\n // 5 instead of 50, and `sum(x*2, [1,2,3])` to 1+4+6 = 11 instead of 12.\n // The trivial `sum(x, c)` case is unaffected, which is why the shortfall\n // stayed invisible: with the identity expression the skipped element is\n // its own image.\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(0);\n\n emitInvoke(builder, OpCode.REDUCE_INVOKE, { kind: 0, program: bodyProgram }, [\"acc\", \"x\"], 1);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\nimport { parseCollectionExpr, emitInvoke } from \"../MapReduceShared\";\n\n/**\n * `prod(elementExpr, collection)`, parse-time sugar for\n * `reduce(acc*elementExpr, collection)`. See `SumParselet.ts`'s doc\n * comment for the full construction technique (identical, just `MUL`\n * instead of `ADD`).\n */\nexport class ProdParselet implements PrefixParselet {\n readonly category = \"MapReduce\";\n\n parse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n parser.consume(\"LPAREN\");\n\n const bodyBuilder = new BytecodeBuilder(builder.pluginIndexMap);\n bodyBuilder.emitOpcode(OpCode.LOAD_VAR);\n bodyBuilder.emitString(\"acc\");\n parser.parseExpression(BindingPower.Lowest, bodyBuilder);\n parser.setBuilder(builder);\n bodyBuilder.emitOpcode(OpCode.MUL);\n const bodyProgram = bodyBuilder.build();\n if (bodyProgram.hasAsync) {\n throw ErrorFactory.parsing(\n \"MAP_REDUCE_TRANSFORM_MUST_BE_SYNCHRONOUS\",\n `prod's element expression must be synchronous (no weather/stocks/currency calls).`,\n );\n }\n\n parser.consume(\"COMMA\");\n parseCollectionExpr(parser, builder);\n parser.consume(\"RPAREN\");\n\n // One, the multiplicative identity, for the reason spelled out in\n // `SumParselet.ts`: seeding from the collection's first element skipped\n // the element expression for that element, so `prod(x*2, [2,3,4])` folded\n // to 2*6*8 = 96 rather than 4*6*8 = 192.\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(1);\n\n emitInvoke(builder, OpCode.REDUCE_INVOKE, { kind: 0, program: bodyProgram }, [\"acc\", \"x\"], 1);\n }\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport { tokenTypeId } from \"@solve-js/lexer/Token\";\nimport { LexerToken } from \"@solve-js/lexer/ExpressionLexer\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\n\nconst WORD_TO_TOKEN_TYPE: Record<string, string> = {\n map: \"MAP\",\n reduce: \"REDUCE\",\n sum: \"SUM_FN\",\n prod: \"PROD_FN\",\n};\n\n/**\n * Whether the token at this position looks like it's about to become (or\n * already is) a `LINE_REF`, i.e. this \"sum(\" is actually\n * `packages/lines/`'s own cross-line `sum(line 1 : line 4)` aggregate\n * call, not this package's `sum(elementExpr, collection)`. Mirrors\n * `packages/lines/normalizer/LineRefNormalizerRule.ts`'s own glued/spaced\n * matching exactly, so both rules agree on what counts as a line\n * reference without one importing the other.\n */\nfunction looksLikeLineRef(token: Token | undefined, next: Token | undefined): boolean {\n if (!token) return false;\n if (token.type === \"LINE_REF\") return true;\n if (token.type === \"IDENT\") {\n if (/^line\\d+$/i.test(token.value)) return true;\n if (/^line$/i.test(token.value) && next?.type === \"NUMBER\" && /^\\d+$/.test(next.value)) return true;\n }\n return false;\n}\n\n/**\n * Fuses `map(`/`reduce(`/`sum(`/`prod(`, but ONLY when the bare word is\n * IMMEDIATELY followed by `LPAREN`, into their own dedicated token\n * types. Mirrors `packages/lines/normalizer/LineRefNormalizerRule.ts`'s\n * `rangeCallNormalizerRule()` (same \"conditional-on-LPAREN\" pattern), so\n * `:map = [...]` / `:reduce = ...` / `:sum = 100` / `:prod = 1` all keep\n * working as ordinary variable names when NOT immediately called.\n *\n * `sum(` specifically declines to match (the {@link looksLikeLineRef}\n * guard) when its own arguments look like the lines package's\n * `sum(line 1 : line 4)` cross-line aggregate, a genuine trigger-shape\n * collision between two independently-shipped features (both packages\n * pick \"sum(\" as their trigger). Declining here (returning `null`) lets\n * the normalizer try `rangeCallNormalizerRule` at the same position next\n *, that composition works regardless of the two rules' relative\n * priority, since the normalizer tries every registered rule at a\n * position, in priority order, until one matches.\n */\nexport function mapReduceCallNormalizerRule(priority = 80): NormalizerRule {\n return {\n name: \"mapreduce:call\",\n priority,\n match(tokens: Token[], pos: number): NormalizerMatch | null {\n const token = tokens[pos];\n if (!token || token.type !== \"IDENT\") return null;\n const word = token.value.toLowerCase();\n const tokenType = WORD_TO_TOKEN_TYPE[word];\n if (!tokenType) return null;\n if (tokens[pos + 1]?.type !== \"LPAREN\") return null;\n if (word === \"sum\" && looksLikeLineRef(tokens[pos + 2], tokens[pos + 3])) return null;\n\n return {\n consumed: 1,\n replacement: [\n new LexerToken(tokenType, tokenTypeId(tokenType), token.value, token.value, token.offset, 0, token.line, token.col),\n ],\n ruleName: \"mapreduce:call\",\n };\n },\n };\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { MapParselet } from \"./parselets/MapParselet\";\nimport { ReduceParselet } from \"./parselets/ReduceParselet\";\nimport { SumParselet } from \"./parselets/SumParselet\";\nimport { ProdParselet } from \"./parselets/ProdParselet\";\nimport { mapReduceCallNormalizerRule } from \"./normalizer/MapReduceCallNormalizerRule\";\n\n/**\n * `map`/`reduce`/`sum`/`prod`, Calca-parity collection transforms over a\n * Matrix or a bare Range. See `MapReduceShared.ts` for the shared\n * transform-disambiguation/collection-parsing logic, and `vm/VM.ts`'s\n * `MAP_INVOKE`/`REDUCE_INVOKE` opcode handlers for the runtime semantics.\n *\n * `map`/`reduce`/`sum`/`prod` are NOT bare keywordMap entries. See\n * `MapReduceCallNormalizerRule.ts`, which fuses them into their own token\n * types only when immediately followed by `(`, so `:map = [...]` etc.\n * keep working as ordinary variable names.\n */\nexport const MAPREDUCE_PACKAGE: IEnginePackage = {\n name: \"solve-mapreduce\",\n normalizerRules: [mapReduceCallNormalizerRule()],\n prefixParselets: {\n MAP: new MapParselet(),\n REDUCE: new ReduceParselet(),\n SUM_FN: new SumParselet(),\n PROD_FN: new ProdParselet(),\n },\n};\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { bigIntLiteralDigits } from \"@solve-js/parser/BigIntLiteral\";\n\n/**\n * Arbitrary-precision integer literal, with or without the trailing `n`.\n *\n * The digits are emitted as a string rather than a number, because the whole\n * point of the type is holding values a double cannot represent exactly.\n */\nexport class BigIntNumberParselet implements PrefixParselet {\n\treadonly category = \"BigInt\";\n\tparse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n // Store as string to preserve arbitrary precision (exceeds Float64).\n // The digits come from the same helper the Tier-1 switch in\n // parser/PrecedenceParser.ts uses, so the two tiers cannot read a literal\n // differently. `1.000n` is what that buys: whichever tier parses it, a\n // dot-decimal locale refuses it with the same INVALID_NUMBER_LITERAL and a\n // dot-grouping locale like de reads it as 1000n. Neither tier guesses on\n // its own.\n builder.emitOpcode(OpCode.PUSH_BIGINT);\n builder.emitString(bigIntLiteralDigits(token.value, parser.getLocaleCode()));\n }\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { BigIntNumberParselet } from \"./parselets/BigIntNumberParselet\";\n\n/** Arbitrary-precision integer literals (suffixed, e.g. `123n`) that overflow a normal `number` without losing precision. */\nexport const BIGINT_PACKAGE: IEnginePackage = {\n name: \"solve-bigint\",\n prefixParselets: {\n BIGINT: new BigIntNumberParselet(),\n },\n};\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `==`, `!=`, `<`, `>`, `<=`, `>=`, comparison operators, producing a\n * `ValueType.Boolean` result. The VM opcodes (`EQ`/`NEQ`/`LT`/`LTE`/`GT`/\n * `GTE`, `OpCode` 40-45) already existed and were fully implemented\n * (including UoM-aware equality) before this package, only the\n * lexer/parser front end was missing (confirmed via full-repo grep this\n * session: no parselet claimed these tokens). One parameterized class\n * covers all six; the constructor picks which opcode to emit.\n */\nexport class ComparisonParselet implements InfixParselet {\n\treadonly category = \"Conditionals\";\n\treadonly bindingPower = BindingPower.Conditional;\n\n\tconstructor(private readonly opcode: OpCode) {}\n\n parse(parser: Parser, left: Token, token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(this.bindingPower, builder);\n builder.emitOpcode(this.opcode);\n }\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * `or`, `&&`, `||`, logical OR/AND, producing a `ValueType.Boolean`\n * result (see `OpCode.LOGICAL_AND`/`LOGICAL_OR` in `vm/VM.ts`; both\n * operands are coerced via `isTruthy()`, so plain numeric operands work\n * too, e.g. `x and y > 0`).\n *\n * The word \"and\" is deliberately NOT handled here, it already lexes as\n * `PLUS` (a locale word-synonym for arithmetic \"+\", `en.ts`: `and:\n * \"PLUS\"`), which is a Tier-1 hardcoded infix operator\n * (`parser/BindingPower.ts`'s `BUILTIN_INFIX_BP`) that never reaches this\n * registry-based parselet at all. `OpCode.ADD`'s own VM handler special-\n * cases `Boolean && Boolean` operands instead. See the comment there.\n */\nexport class LogicalParselet implements InfixParselet {\n\treadonly category = \"Conditionals\";\n\n\tconstructor(private readonly opcode: OpCode, readonly bindingPower: number) {}\n\n parse(parser: Parser, left: Token, token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(this.bindingPower, builder);\n builder.emitOpcode(this.opcode);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/** `true` / `false` boolean literals. */\nexport class BooleanLiteralParselet implements PrefixParselet {\n\treadonly category = \"Conditionals\";\n\n\tconstructor(private readonly value: boolean) {}\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n builder.emitOpcode(OpCode.PUSH_BOOLEAN);\n builder.emitByte(this.value ? 1 : 0);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * `if <condition> then <value> else <value>`, an eager ternary (see\n * `OpCode.SELECT` in `vm/VM.ts` for why this VM has no real branching).\n *\n * NOT built on {@link definePhrasePattern}: that builder's alternatives\n * are disambiguated by peeking the NEXT token's leading keyword, a\n * mechanism for one registered trigger fanning out into multiple\n * grammars (e.g. `DiceRollParselet`'s `between`/`from`/`(` forms, all\n * reached via the single \"roll\" registration). `IF` is registered\n * directly as this parselet's own trigger, so by the time `parse()`\n * runs, \"if\" is already consumed and there is exactly one grammar to\n * match, a plain, hand-written sequence is simpler and more honest here\n * than forcing a single-alternative pattern through machinery built for\n * disambiguation it doesn't need.\n */\nexport class IfThenElseParselet implements PrefixParselet {\n\treadonly category = \"Conditionals\";\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(0, builder); // condition\n parser.consume(\"THEN\");\n parser.parseExpression(0, builder); // then-value\n parser.consume(\"ELSE\");\n parser.parseExpression(0, builder); // else-value\n builder.emitOpcode(OpCode.SELECT);\n }\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ComparisonParselet } from \"./parselets/ComparisonParselet\";\nimport { LogicalParselet } from \"./parselets/LogicalParselet\";\nimport { BooleanLiteralParselet } from \"./parselets/BooleanLiteralParselet\";\nimport { IfThenElseParselet } from \"./parselets/IfThenElseParselet\";\n\n/**\n * Comparisons (`==`, `!=`, `<`, `>`, `<=`, `>=`), boolean logic (`true`/\n * `false`, `and`/`or`/`&&`/`||`), and `if <cond> then <val> else <val>`\n * eager-ternary conditionals.\n *\n * The `and`/`or` split is deliberate, not an oversight: \"and\" already\n * lexes as `PLUS` (a pre-existing arithmetic word-synonym, `en.ts`:\n * `and: \"PLUS\"`), a Tier-1 hardcoded infix operator no registry-based\n * parselet can intercept, `OpCode.ADD`'s own VM handler special-cases\n * `Boolean && Boolean` instead (see `vm/VM.ts`). \"or\"/\"&&\"/\"||\" have no\n * such collision and are handled normally here via `LogicalParselet`.\n *\n * KNOWN LIMITATION: because \"and\" is pinned to `PLUS`'s Tier-1 binding\n * power (`Sum`, tighter than comparisons), an unparenthesized `X >= Y and\n * Z < W` does NOT parse as `(X >= Y) and (Z < W)`, \"and\"'s fixed\n * precedence grabs a comparison operand instead. Use `&&` (correct,\n * dedicated `LogicalAnd` precedence, looser than comparisons) for that\n * pattern, or wrap each side in parens if \"and\" is preferred:\n * `(X >= Y) and (Z < W)`. \"and\" alone (no comparisons in the same\n * unparenthesized expression) works fine, e.g. `discount and hasCoupon`.\n *\n * SCOPE DECISION: SoulverCore-style postfix `Y if X` / `Y unless X` (a\n * ternary with no explicit else-branch) is deliberately NOT implemented.\n * This VM's `Value` has no \"empty\"/\"void\" representation for the\n * false-branch case (every expression must produce a concrete typed\n * result), building it properly would mean adding a new sentinel\n * `ValueType` and deciding how every consumer (formatting, DAG\n * propagation, UOM/arithmetic ops) treats it, which is a bigger call than\n * this package should make implicitly. `if X then Y else Z` (both\n * branches required) covers the same need unambiguously today.\n */\nexport const CONDITIONALS_PACKAGE: IEnginePackage = {\n name: \"solve-conditionals\",\n prefixParselets: {\n TRUE: new BooleanLiteralParselet(true),\n FALSE: new BooleanLiteralParselet(false),\n IF: new IfThenElseParselet(),\n },\n infixParselets: {\n EQUALITY: new ComparisonParselet(OpCode.EQ),\n NEQ: new ComparisonParselet(OpCode.NEQ),\n LT: new ComparisonParselet(OpCode.LT),\n GT: new ComparisonParselet(OpCode.GT),\n LTE: new ComparisonParselet(OpCode.LTE),\n GTE: new ComparisonParselet(OpCode.GTE),\n OR: new LogicalParselet(OpCode.LOGICAL_OR, BindingPower.LogicalOr),\n LOGICAL_AND: new LogicalParselet(OpCode.LOGICAL_AND, BindingPower.LogicalAnd),\n LOGICAL_OR: new LogicalParselet(OpCode.LOGICAL_OR, BindingPower.LogicalOr),\n },\n};\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/** Recognized `as <name>` targets that dispatch to a dedicated fast opcode. */\nconst BUILTIN_CONVERTERS: Record<string, OpCode> = {\n percent: OpCode.TO_PERCENTAGE,\n percentage: OpCode.TO_PERCENTAGE,\n decimal: OpCode.TO_NUMBER,\n dec: OpCode.TO_NUMBER,\n number: OpCode.TO_NUMBER,\n hex: OpCode.TO_HEX,\n fraction: OpCode.TO_FRACTION,\n multiplier: OpCode.TO_MULTIPLIER,\n // \"20 to 40 as x\". Only reachable in converter position, immediately after\n // \"as\", so a variable named x is unaffected.\n x: OpCode.TO_MULTIPLIER,\n sci: OpCode.TO_SCI,\n scientific: OpCode.TO_SCI,\n binary: OpCode.TO_BINARY,\n bin: OpCode.TO_BINARY,\n octal: OpCode.TO_OCTAL,\n oct: OpCode.TO_OCTAL,\n};\n\n/**\n * Radixes reachable as \"as base <n>\".\n *\n * The same three the named converters cover, because these are the bases the\n * engine can actually render a literal for. A base-7 renderer would be a\n * different feature, not a missing table entry.\n */\nconst BASE_CONVERTERS: Record<number, OpCode> = {\n 2: OpCode.TO_BINARY,\n 8: OpCode.TO_OCTAL,\n 16: OpCode.TO_HEX,\n};\n\n/**\n * `<expr> as <type>`, general value/display conversion, e.g.\n * `50% as decimal` -> `0.5`, `255 as hex` -> `0xFF`, `0.5 as fraction` ->\n * `\"1/2\"`.\n *\n * The target-name position is read as raw token text, like UOM's `to\n * <unit>` (see `packages/uom/parselets/ConvertParselet.ts`), never parsed\n * as a sub-expression, since \"hex\"/\"fraction\"/etc. are type names, not\n * values. `decimal`/`dec`/`number` are deliberately aliases of the same\n * TO_NUMBER opcode: this VM's Percentage representation already stores\n * the raw decimal fraction (0.5 for 50%, see vm/Value.ts's\n * percentageValue()), so \"as decimal\" and \"as number\" compute the exact\n * same thing, there's no second, distinct meaning to invent.\n *\n * Any name NOT in {@link BUILTIN_CONVERTERS} falls through to\n * `OpCode.CALL_AS_CONVERTER`, resolved at VM-execution time against\n * `vm/VMBuiltins.ts`'s `asConverterRegistry`, the\n * `IEnginePackage.asConverters` SDK extension point. `CONVERTER_NAME`\n * (this package's reserved words, via the locale's keywordMap) and bare\n * `IDENT` tokens are accepted as the target, so a third-party converter\n * name works without needing a locale change.\n *\n * `FUNC` tokens are ALSO accepted here: `hex`/`bin` do double duty as\n * both a converter name (`255 as hex`) and a Python/JS-style callable\n * builtin (`hex(255)`, see `vm/VMBuiltins.ts` indices 48/49), a word can\n * only lex as one token type, and `FunctionCallParselet`'s call-syntax\n * needs `FUNC`, so `hex`/`bin`'s locale keywordMap entry was moved from\n * `CONVERTER_NAME` to `FUNC` and this check widened to match, rather than\n * inventing a second token type for the same two words. The only\n * observable behavior change: `<expr> as <any-other-FUNC-keyword>` (e.g.\n * `5 as sqrt`) now reaches this method instead of failing to lex, it\n * still isn't a real converter, so it now surfaces as a runtime \"unknown\n * as-converter\" error via `CALL_AS_CONVERTER` instead of this method's\n * parse-time `AS_CONVERTER_EXPECTED_NAME` error. No such input was ever\n * valid before or after this change.\n *\n * `UNIT` tokens are accepted for exactly the same reason, added for the\n * Datetime package's `as month` / `as week` converters: \"month\" and \"week\"\n * lex as time UNITs (they have to, `90 days in weeks`), so without this\n * they could never reach the registry. Note `as <unit>` is NOT a unit\n * conversion and never was, `100cm as m` was a parse error before this\n * widening and is an unknown-as-converter error after it; the unit-\n * conversion syntaxes are `to <unit>` and `in <unit>`.\n */\nexport class AsConverterParselet implements InfixParselet {\n readonly category = \"Converters\";\n readonly bindingPower = BindingPower.Conditional;\n\n parse(parser: Parser, left: Token, token: Token, builder: BytecodeBuilder): void {\n const nextToken = parser.peek();\n\n // \"as %\", the bare percent SYMBOL, not the word \"percent\".\n if (nextToken?.type === \"PERCENT\") {\n parser.consume();\n builder.emitOpcode(OpCode.TO_PERCENTAGE);\n return;\n }\n\n if (\n !nextToken ||\n (nextToken.type !== \"CONVERTER_NAME\" &&\n nextToken.type !== \"IDENT\" &&\n nextToken.type !== \"FUNC\" &&\n nextToken.type !== \"UNIT\")\n ) {\n throw ErrorFactory.parsing(\n \"AS_CONVERTER_EXPECTED_NAME\",\n `Expected a converter name after \"as\" (e.g. \"as hex\", \"as decimal\") but got ${nextToken ? `\"${nextToken.value}\"` : \"end of input\"}`,\n );\n }\n parser.consume();\n const name = nextToken.value.toLowerCase();\n\n // \"as base 8\", the radix written as a number rather than a name. Only the\n // three radixes this engine can render are accepted, and an unsupported\n // one says so rather than silently falling through to \"unknown converter\",\n // which would be true but unhelpful.\n if (name === \"base\") {\n const radixToken = parser.peek();\n const radix = radixToken?.type === \"NUMBER\" ? Number(radixToken.value) : NaN;\n const opcode = BASE_CONVERTERS[radix];\n if (opcode === undefined) {\n throw ErrorFactory.parsing(\n \"AS_CONVERTER_UNSUPPORTED_BASE\",\n `\"as base ${radixToken?.value ?? \"?\"}\": only bases ${Object.keys(BASE_CONVERTERS).join(\", \")} can be written out`,\n );\n }\n parser.consume();\n builder.emitOpcode(opcode);\n return;\n }\n\n const builtinOp = BUILTIN_CONVERTERS[name];\n if (builtinOp !== undefined) {\n // \"50 as x of 5\" and \"2 as multiplier of 1\": the multiple is relative to\n // a base rather than to 1, so divide before converting. Only meaningful\n // for the multiplier converters; \"255 as hex of 5\" is not a thing.\n if (builtinOp === OpCode.TO_MULTIPLIER && parser.match(\"OF\")) {\n parser.parseExpression(BindingPower.Conditional, builder);\n builder.emitOpcode(OpCode.DIV);\n }\n builder.emitOpcode(builtinOp);\n return;\n }\n\n // Not a built-in name, defer to the runtime asConverters registry so\n // third-party packages can contribute names without touching this file.\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(name);\n builder.emitOpcode(OpCode.CALL_AS_CONVERTER);\n }\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/**\n * Rounding, written the way it is said.\n *\n * 5.5 rounded 6\n * 5.5 rounded down 5\n * 37 to nearest 10 40\n * $490 rounded to nearest hundred $500\n * 21 rounded up to nearest 5 25\n * 1/3 to 2 dp 0.33\n *\n * None of this parsed before. The engine could round, but only by configuring\n * the formatter, which changes how every answer is displayed rather than\n * rounding one value inside an expression. They are different things: the\n * formatter cannot express `21 rounded up to nearest 5`, and it cannot feed a\n * rounded number into the next line's arithmetic.\n *\n * All three forms are postfix operators on the value to their left, which is\n * why they are infix parselets triggered on the word rather than prefix\n * parselets. They bind at `Conditional`, below arithmetic, so the whole\n * expression to the left is what gets rounded: `1/3 to 2 dp` rounds a third,\n * not the 3.\n */\n\n/**\n * Magnitude words usable as a rounding increment.\n *\n * `to nearest hundred` reads naturally and `to nearest 100` is the same thing.\n * Only the round magnitudes are here, because those are the only ones anyone\n * rounds to.\n */\nconst INCREMENT_WORDS: Record<string, number> = {\n\tten: 10,\n\thundred: 100,\n\tthousand: 1_000,\n\tmillion: 1_000_000,\n\tbillion: 1_000_000_000,\n};\n\n/** Which way an explicit `up`/`down` sends the result. */\ntype Direction = \"nearest\" | \"up\" | \"down\";\n\n/**\n * Emits the rounding of the value already on the stack to `increment`.\n *\n * Rounding to an increment is a division, an ordinary round, and a\n * multiplication back, so it needs no new opcode. The increment is a\n * parse-time number rather than a stack value, which is what lets it be\n * emitted twice without any stack shuffling; see {@link readIncrement} for why\n * that is not a real restriction.\n */\nfunction emitRoundToIncrement(builder: BytecodeBuilder, increment: number, direction: Direction): void {\n\tbuilder.emitOpcode(OpCode.PUSH_NUMBER);\n\tbuilder.emitNumber(increment);\n\tbuilder.emitOpcode(OpCode.DIV);\n\temitRoundDirection(builder, direction);\n\tbuilder.emitOpcode(OpCode.PUSH_NUMBER);\n\tbuilder.emitNumber(increment);\n\tbuilder.emitOpcode(OpCode.MUL);\n}\n\n/** Emits `round`/`ceil`/`floor` for the value on top of the stack. */\nfunction emitRoundDirection(builder: BytecodeBuilder, direction: Direction): void {\n\tconst builtin = direction === \"up\" ? CEIL_BUILTIN : direction === \"down\" ? FLOOR_BUILTIN : ROUND_BUILTIN;\n\tbuilder.emitOpcode(OpCode.CALL_BUILTIN);\n\tbuilder.emitIndex(builtin);\n\tbuilder.emitIndex(1);\n}\n\nconst ROUND_BUILTIN = 8;\nconst CEIL_BUILTIN = 6;\nconst FLOOR_BUILTIN = 7;\nconst ROUND_TO_PLACES_BUILTIN = 97;\n\n/** Reads an optional `up`/`down` immediately after `rounded`. */\nfunction readDirection(parser: Parser): Direction {\n\tconst next = parser.peek();\n\tconst word = (next?.text ?? \"\").toLowerCase();\n\tif (word === \"up\") {\n\t\tparser.consume();\n\t\treturn \"up\";\n\t}\n\tif (word === \"down\") {\n\t\tparser.consume();\n\t\treturn \"down\";\n\t}\n\treturn \"nearest\";\n}\n\n/**\n * `<value> rounded`, `<value> rounded up|down`, and either of those followed\n * by `to nearest <increment>`.\n */\nexport class RoundedParselet implements InfixParselet {\n\treadonly category = \"Converters\";\n\treadonly bindingPower = BindingPower.Conditional;\n\n\tparse(parser: Parser, _left: Token, _token: Token, builder: BytecodeBuilder): void {\n\t\tconst direction = readDirection(parser);\n\n\t\t// The optional \"to nearest <increment>\" tail. Without it, round to a\n\t\t// whole number.\n\t\tif (parser.match(\"TO_NEAREST\")) {\n\t\t\temitRoundToIncrement(builder, readIncrement(parser), direction);\n\t\t\treturn;\n\t\t}\n\t\temitRoundDirection(builder, direction);\n\t}\n}\n\n/** `<value> to nearest <increment>`, with no `rounded` in front. */\nexport class ToNearestParselet implements InfixParselet {\n\treadonly category = \"Converters\";\n\treadonly bindingPower = BindingPower.Conditional;\n\n\tparse(parser: Parser, _left: Token, _token: Token, builder: BytecodeBuilder): void {\n\t\temitRoundToIncrement(builder, readIncrement(parser), \"nearest\");\n\t}\n}\n\n/**\n * Reads the rounding increment: either a number literal or a magnitude word.\n *\n * A literal rather than an expression, deliberately. Every way anyone writes\n * this names a constant, because an increment computed at run time is not a\n * thing people round to, and taking it at parse time is what allows the\n * emitted bytecode to mention it twice without shuffling the stack.\n */\nfunction readIncrement(parser: Parser): number {\n\tconst next = parser.peek();\n\tconst word = (next?.text ?? \"\").toLowerCase();\n\tconst magnitude = INCREMENT_WORDS[word];\n\tif (magnitude !== undefined) {\n\t\tparser.consume();\n\t\treturn magnitude;\n\t}\n\tif (next?.type === \"NUMBER\") {\n\t\tparser.consume();\n\t\tconst value = Number(next.value);\n\t\tif (Number.isFinite(value) && value > 0) return value;\n\t}\n\tthrow ErrorFactory.parsing(\n\t\t\"INVALID_ROUNDING_INCREMENT\",\n\t\t`\"to nearest ${next?.value ?? \"?\"}\": expected a positive number or one of ${Object.keys(INCREMENT_WORDS).join(\", \")}`,\n\t);\n}\n\n/**\n * `<value> to <n> dp` and its spellings, rounding to a number of decimal\n * places.\n *\n * The place count is carried on the token itself, fused by\n * `decimalPlacesNormalizerRule`, because \"to\" is already an infix operator\n * here (percentage change, `100 to 150`) and a second parselet on it would\n * have to guess which grammar it was in from lookahead.\n */\nexport class DecimalPlacesParselet implements InfixParselet {\n\treadonly category = \"Converters\";\n\treadonly bindingPower = BindingPower.Conditional;\n\n\tparse(_parser: Parser, _left: Token, token: Token, builder: BytecodeBuilder): void {\n\t\tconst places = Number(token.value);\n\t\tif (!Number.isFinite(places) || places < 0 || places > 100) {\n\t\t\tthrow ErrorFactory.parsing(\n\t\t\t\t\"INVALID_DECIMAL_PLACES\",\n\t\t\t\t`\"to ${token.value} dp\": expected a place count between 0 and 100`,\n\t\t\t);\n\t\t}\n\t\t// A builtin, rather than the \"× 10^p, round, ÷ 10^p\" this used to\n\t\t// emit. That sequence is exact only while the multiplication is, and\n\t\t// asking for decimal places a number does not have is an ordinary\n\t\t// thing to do: `1e21 to 2 dp` answered 999999999999999900000, having\n\t\t// scaled a whole number up past the point where a double has any\n\t\t// fractional digits at all and back down again. Whether the scaling is\n\t\t// safe is a property of the value, which only the VM knows, so the\n\t\t// decision belongs there. See VMBuiltins.ts's index 97.\n\t\tbuilder.emitOpcode(OpCode.PUSH_NUMBER);\n\t\tbuilder.emitNumber(places);\n\t\tbuilder.emitOpcode(OpCode.CALL_BUILTIN);\n\t\tbuilder.emitIndex(ROUND_TO_PLACES_BUILTIN);\n\t\tbuilder.emitIndex(2);\n\t}\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\nconst PLACE_WORDS = new Set([\"dp\", \"dps\", \"decimals\", \"digits\", \"digit\"]);\n\n/** Two-word spellings whose first word alone means nothing here. */\nconst PLACE_PHRASES = new Set([\"decimal\"]);\n\n/**\n * Fuses `to <n> dp` into one token carrying the place count.\n *\n * `1/3 to 2 dp` and `π to 5 digits` are three tokens where the middle one is\n * the operand. That cannot be a plain infix parselet on \"to\", because \"to\" is\n * already one: `100 to 150` is a percentage change. A second parselet on the\n * same token would have to guess which grammar it was in by looking ahead,\n * and lookahead that decides between two unrelated meanings is how a parser\n * becomes impossible to reason about.\n *\n * Fusing first means the parser sees a single, unambiguous postfix operator,\n * and \"to\" keeps its one meaning. The place count rides on the fused token's\n * value, which is what `sourceEnd` exists for (see Token.ts).\n *\n * Accepted spellings: `dp`, `dps`, `d.p.`, `decimal place(s)`, and `digits`.\n * \"digits\" is included because Soulver documents `π to 5 digits` as 3.14159,\n * which is five decimal places rather than five significant figures.\n */\nexport function decimalPlacesNormalizerRule(priority = 66): NormalizerRule {\n\treturn {\n\t\tname: \"converters:decimal-places\",\n\t\tpriority,\n\t\tmatch(tokens, pos): NormalizerMatch | null {\n\t\t\tif (tokens[pos]?.type !== \"TO\") return null;\n\n\t\t\tconst count = tokens[pos + 1];\n\t\t\tif (count?.type !== \"NUMBER\") return null;\n\n\t\t\tconst unitToken = tokens[pos + 2];\n\t\t\tif (!unitToken) return null;\n\t\t\tconst word = (unitToken.text ?? unitToken.value ?? \"\").toLowerCase();\n\n\t\t\t// \"to 2 decimal places\" is four tokens; the rest are three.\n\t\t\tlet consumed = 3;\n\t\t\tif (PLACE_PHRASES.has(word)) {\n\t\t\t\tconst tail = tokens[pos + 3];\n\t\t\t\tconst tailWord = (tail?.text ?? \"\").toLowerCase();\n\t\t\t\tif (tailWord !== \"place\" && tailWord !== \"places\") return null;\n\t\t\t\tconsumed = 4;\n\t\t\t} else if (!PLACE_WORDS.has(word)) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tconsumed,\n\t\t\t\treplacement: [\n\t\t\t\t\tcreateFusedToken(\"DECIMAL_PLACES\", count.value, tokens.slice(pos, pos + consumed)),\n\t\t\t\t],\n\t\t\t\truleName: \"converters:decimal-places\",\n\t\t\t};\n\t\t},\n\t};\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\n/**\n * Rewrites `in <converter>` and `to <converter>` into `as <converter>`.\n *\n * `256 as hex` has always worked. `99 in binary` and `0x9F31 to decimal` are\n * the same operation said differently, and both failed: `in` belongs to unit\n * conversion, which looked for a unit called \"binary\" and found none, and `to`\n * belongs to percentage change, which tried to parse \"decimal\" as a number.\n *\n * Doing this as a token rewrite rather than by teaching those two parselets\n * about converters keeps each of them with one job. It is also unambiguous in\n * a way a parselet-level fallback would not be: the lexer only produces\n * CONVERTER_NAME for a name that is already in the converter table, so there\n * is no unit and no expression this could be shadowing.\n *\n * `hex` and `bin` are the exception to that, and they are named here by hand.\n * Both used to lex as CONVERTER_NAME, and both were moved to FUNC so that the\n * call spellings `hex(255)` / `bin(255)` could exist at all (a word gets one\n * token type; see the locale's own comment on those two entries).\n * AsConverterParselet was widened to accept FUNC at the same time, which kept\n * `255 as hex` working, but this rule was not, so `255 in hex` and `255 to\n * bin` stopped parsing while `255 in binary` carried on working. Only those\n * two names are listed: widening the check to FUNC in general would rewrite\n * `100 to sqrt(4)`, a percentage change, into a converter nobody asked for.\n */\nconst FUNC_TYPED_CONVERTERS = new Set([\"hex\", \"bin\"]);\n/**\n * The rule itself.\n *\n * @param priority - Where this sits among the normalizer rules. It must run\n * before anything that reads `IN`/`TO` as its own operator, since it rewrites\n * the preposition out from under them.\n * @returns The rule, ready to register on a package.\n */\nexport function converterPrepositionNormalizerRule(priority = 67): NormalizerRule {\n\treturn {\n\t\tname: \"converters:preposition\",\n\t\tpriority,\n\t\tmatch(tokens, pos): NormalizerMatch | null {\n\t\t\tconst preposition = tokens[pos];\n\t\t\tif (preposition?.type !== \"IN\" && preposition?.type !== \"TO\") return null;\n\t\t\tconst target = tokens[pos + 1];\n\t\t\tconst isConverterTarget =\n\t\t\t\ttarget?.type === \"CONVERTER_NAME\" ||\n\t\t\t\t(target?.type === \"FUNC\" && FUNC_TYPED_CONVERTERS.has(target.value.toLowerCase()));\n\t\t\tif (!isConverterTarget) return null;\n\n\t\t\t// Only the preposition is replaced; the converter name is left for\n\t\t\t// AsConverterParselet to read exactly as it would after a real \"as\".\n\t\t\treturn {\n\t\t\t\tconsumed: 1,\n\t\t\t\treplacement: [createFusedToken(\"AS\", \"as\", [preposition])],\n\t\t\t\truleName: \"converters:preposition\",\n\t\t\t};\n\t\t},\n\t};\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { AsConverterParselet } from \"./parselets/AsConverterParselet\";\nimport { RoundedParselet, ToNearestParselet, DecimalPlacesParselet } from \"./parselets/RoundingParselets\";\nimport { decimalPlacesNormalizerRule } from \"./normalizer/DecimalPlacesNormalizerRule\";\nimport { converterPrepositionNormalizerRule } from \"./normalizer/ConverterPrepositionNormalizerRule\";\n\n/**\n * The general `<expr> as <type>` conversion/display mechanism, one\n * unifying grammar point rather than N unrelated per-type features.\n *\n * Ships the built-in set immediately usable (see `BUILTIN_CONVERTERS` in\n * `AsConverterParselet.ts`): `as %`/`as percent`, `as decimal`/`as dec`/\n * `as number`, `as fraction`, `as multiplier`, `as sci`, `as hex`/\n * `as binary`/`as octal`.\n *\n * This is also the other clearly SDK-shaped piece from the SoulverCore\n * feature-parity work (alongside `PhrasePattern`/`createQueryResolver`):\n * `IEnginePackage.asConverters` (see `api/PackageRegistry.ts`) lets a\n * third-party package contribute its own `as <name>` target without\n * touching this package at all, resolved at VM-execution time via\n * `OpCode.CALL_AS_CONVERTER` against `vm/VMBuiltins.ts`'s\n * `asConverterRegistry`.\n *\n * SCOPE DECISION: no `as x` alias for `as multiplier`, \"x\" is too likely\n * to collide with ordinary variable names (algebraic use, `x = 5`) to\n * justify claiming it as a global keyword for one converter's sake.\n */\nexport const CONVERTERS_PACKAGE: IEnginePackage = {\n name: \"solve-converters\",\n phrases: {\n // A phrase, not a bare \"nearest\": \"to\" is already an infix operator\n // (percentage change), so the two words have to arrive as one token for\n // the parser to tell the grammars apart. See RoundingParselets.ts.\n \"to nearest\": \"TO_NEAREST\",\n \"to the nearest\": \"TO_NEAREST\",\n },\n infixParselets: {\n AS: new AsConverterParselet(),\n ROUNDED: new RoundedParselet(),\n TO_NEAREST: new ToNearestParselet(),\n DECIMAL_PLACES: new DecimalPlacesParselet(),\n },\n normalizerRules: [\n decimalPlacesNormalizerRule(),\n converterPrepositionNormalizerRule(),\n ],\n};\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `average of X, Y, Z` / `median of X, Y, Z` / `total of X, Y, Z` /\n * `count of X, Y, Z`, a comma-separated argument list, same shape as a\n * function call but without parens.\n *\n * Triggered on a fused `AVERAGE_OF`/`MEDIAN_OF`/`TOTAL_OF`/`COUNT_OF`\n * token (the literal phrase \"average of\" etc., fused by\n * `MathPhrasesPackage.ts`'s `phrases` field) rather than a bare\n * \"average\"/\"total\"/... keyword. See MathPhrasesPackage.ts's doc comment\n * for why: those are common variable names, and this codebase has a\n * tested policy against claiming common words as bare keywords when a\n * phrase-fused alternative is available. By the time `parse()` runs here,\n * \"average of\" is already fully consumed, only the comma-list remains.\n *\n * Not built on {@link definePhrasePattern}: that builder's slots are a\n * fixed sequence, with no support for a variable-length repeated slot\n * \"X, Y, Z, ...\" is open-ended, exactly like `FunctionCallParselet`'s own\n * comma loop (reused here rather than reinvented).\n */\nexport class VariadicAggregateParselet implements PrefixParselet {\n readonly category = \"MathPhrases\";\n\n constructor(private readonly builtinIndex: number) {}\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n let argCount = 0;\n // Arguments are parsed at `Conjunction`, the level \"and\" itself binds at,\n // rather than at `Lowest`, so that the trailing \"and\" of an English list\n // (\"36, 42, 19 and 81\") ends an argument instead of being consumed as the\n // addition it also is. `+` binds tighter (`Sum`), so \"average of 1 + 2, 3\"\n // still gets 1 + 2 as one argument.\n //\n // Parsing at `Lowest` is what made \"average of 36, 42, 19 and 81\" return\n // 59.33: it read three arguments, the last being 19 + 81, and divided 178\n // by 3. Soulver returns 44.5.\n parser.parseExpression(BindingPower.Conjunction, builder);\n argCount++;\n while (parser.match(\"COMMA\") || parser.match(\"AND_CONJ\")) {\n parser.parseExpression(BindingPower.Conjunction, builder);\n argCount++;\n }\n\n builder.emitOpcode(OpCode.CALL_BUILTIN);\n builder.emitIndex(this.builtinIndex);\n builder.emitIndex(argCount);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `<phrase> of X`, a one-operand phrase front end for a builtin that already\n * exists.\n *\n * Backs `square root of` and `cube root of`, which are just `sqrt` and `cbrt`\n * spelled the way they are said. Soulver documents both spellings, and\n * `sqrt(81)` already worked while `square root of 81` did not.\n *\n * The operand is parsed at `Lowest`, so the phrase takes the whole expression\n * after it: `square root of 3 * 27` is the root of 81, which is how the words\n * read.\n */\nexport function unaryPhraseParselet(builtinIndex: number): PrefixParselet {\n\treturn {\n\t\tcategory: \"MathPhrases\",\n\t\tparse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n\t\t\tparser.parseExpression(BindingPower.Lowest, builder);\n\t\t\tbuilder.emitOpcode(OpCode.CALL_BUILTIN);\n\t\t\tbuilder.emitIndex(builtinIndex);\n\t\t\tbuilder.emitIndex(1);\n\t\t},\n\t};\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/**\n * `remainder of <x> divided by <y>`.\n *\n * The words have to be intercepted rather than parsed as an ordinary\n * expression: `21 divided by 5` already means 4.2, and the remainder is a\n * different operation on the same two numbers. So this consumes the\n * `divided by` itself and emits MOD instead of DIV.\n *\n * The left operand is parsed at `Product`, which is where `divided by` binds,\n * so it stops there rather than performing the division this exists to avoid.\n */\nexport const remainderOfParselet: PrefixParselet = {\n\tcategory: \"MathPhrases\",\n\tparse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n\t\tparser.parseExpression(BindingPower.Product, builder); // dividend\n\t\tif (!parser.match(\"DIVIDE_BY\") && !parser.match(\"SLASH\")) {\n\t\t\tthrow ErrorFactory.parsing(\n\t\t\t\t\"REMAINDER_EXPECTED_DIVIDED_BY\",\n\t\t\t\t'Expected \"divided by\" or \"/\", as in \"remainder of 21 divided by 5\"',\n\t\t\t);\n\t\t}\n\t\tparser.parseExpression(BindingPower.Product, builder); // divisor\n\t\tbuilder.emitOpcode(OpCode.MOD);\n\t},\n};\n\n/**\n * `root <n> of <x>`, the nth root.\n *\n * `square root of` and `cube root of` cover the two common cases as fixed\n * phrases; this is the general one, where the degree is written as a number.\n * Emitted as `x ^ (1/n)` rather than through a builtin, because that is\n * exactly what an nth root is and the exponent opcode already exists.\n */\nexport const nthRootParselet: PrefixParselet = {\n\tcategory: \"MathPhrases\",\n\tparse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n\t\tparser.parseExpression(BindingPower.Product, builder); // degree\n\t\tif (!parser.match(\"OF\")) {\n\t\t\tthrow ErrorFactory.parsing(\n\t\t\t\t\"ROOT_EXPECTED_OF\",\n\t\t\t\t'Expected \"of\", as in \"root 5 of 100\"',\n\t\t\t);\n\t\t}\n\t\tparser.parseExpression(BindingPower.Conditional, builder); // radicand\n\n\t\t// stack: degree, radicand. Wanted: radicand ^ (1 / degree).\n\t\tbuilder.emitOpcode(OpCode.SWAP); // radicand, degree\n\t\tbuilder.emitOpcode(OpCode.PUSH_NUMBER);\n\t\tbuilder.emitNumber(1);\n\t\tbuilder.emitOpcode(OpCode.SWAP); // radicand, 1, degree\n\t\tbuilder.emitOpcode(OpCode.DIV); // radicand, 1/degree\n\t\tbuilder.emitOpcode(OpCode.EXP);\n\t},\n};\n\n/**\n * `log <x> base <n>`, the logarithm to an arbitrary base.\n *\n * `log` is already a FUNC, so `log(20)` goes through the ordinary call path\n * and this cannot. The trigger is the fused phrase `log ... base`, produced by\n * {@link logBaseNormalizerRule}, which is what keeps `log(20)` working\n * untouched.\n *\n * Computed as `ln(x) / ln(n)`, the change-of-base identity.\n */\nexport const logBaseParselet: PrefixParselet = {\n\tcategory: \"MathPhrases\",\n\tparse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n\t\tparser.parseExpression(BindingPower.Conditional, builder); // x\n\t\temitNaturalLog(builder);\n\t\tif (!parser.match(\"LOG_BASE\")) {\n\t\t\tthrow ErrorFactory.parsing(\n\t\t\t\t\"LOG_EXPECTED_BASE\",\n\t\t\t\t'Expected \"base\", as in \"log 20 base 4\"',\n\t\t\t);\n\t\t}\n\t\tparser.parseExpression(BindingPower.Conditional, builder); // n\n\t\temitNaturalLog(builder);\n\t\tbuilder.emitOpcode(OpCode.DIV);\n\t},\n};\n\n/** `log` in VMBuiltins.ts is the natural logarithm. */\nconst LOG_BUILTIN = 5;\n\n/** Replaces the value on top of the stack with its natural logarithm. */\nfunction emitNaturalLog(builder: BytecodeBuilder): void {\n\tbuilder.emitOpcode(OpCode.CALL_BUILTIN);\n\tbuilder.emitIndex(LOG_BUILTIN);\n\tbuilder.emitIndex(1);\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\n/**\n * Retypes `root` and `log` when they are written as phrases rather than calls.\n *\n * root 5 of 100 100 ^ (1/5)\n * log 20 base 4 ln(20) / ln(4)\n *\n * Both words are already `FUNC` tokens, so the ordinary call parselet claims\n * them and demands a `(`. A phrase parselet can only be reached if the token\n * type differs, which is what this does, and only when the next token is not\n * a `(`, so `log(20)` and `root(8, 3)` are untouched.\n *\n * The trailing `base` is retyped at the same time. Doing it here rather than\n * claiming the bare word keeps `base` usable everywhere else, including as\n * `as base 8` in the converters package.\n */\nexport function functionPhraseNormalizerRule(priority = 69): NormalizerRule {\n\treturn {\n\t\tname: \"mathphrases:function-phrase\",\n\t\tpriority,\n\t\tmatch(tokens, pos): NormalizerMatch | null {\n\t\t\tconst head = tokens[pos];\n\t\t\tif (head?.type !== \"FUNC\") return null;\n\n\t\t\tconst word = (head.value ?? \"\").toLowerCase();\n\t\t\tif (word !== \"root\" && word !== \"log\") return null;\n\n\t\t\t// A real call. Leave it entirely alone.\n\t\t\tif (tokens[pos + 1]?.type === \"LPAREN\") return null;\n\n\t\t\tif (word === \"root\") {\n\t\t\t\treturn {\n\t\t\t\t\tconsumed: 1,\n\t\t\t\t\treplacement: [createFusedToken(\"NTH_ROOT\", \"root\", [head])],\n\t\t\t\t\truleName: \"mathphrases:function-phrase\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// `log <x> base <n>`: only a phrase when the \"base\" is actually\n\t\t\t// there. Without it, \"log 20\" is left as it was so it still fails\n\t\t\t// the way it always did rather than in some new way.\n\t\t\tconst baseAt = findBase(tokens, pos + 1);\n\t\t\tif (baseAt === -1) return null;\n\n\t\t\tconst replacement = tokens.slice(pos, baseAt + 1).map((token, index) => {\n\t\t\t\tif (index === 0) return createFusedToken(\"LOG_PHRASE\", \"log\", [token]);\n\t\t\t\tif (index === baseAt - pos) return createFusedToken(\"LOG_BASE\", \"base\", [token]);\n\t\t\t\treturn token;\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tconsumed: baseAt - pos + 1,\n\t\t\t\treplacement,\n\t\t\t\truleName: \"mathphrases:function-phrase\",\n\t\t\t};\n\t\t},\n\t};\n}\n\n/**\n * Index of the `base` keyword belonging to this `log`, or -1.\n *\n * Scans a short way rather than assuming it sits immediately after a single\n * number, so `log 2 * 10 base 4` works. Stops at anything that ends a phrase.\n */\nfunction findBase(tokens: readonly { type: string; text?: string; value?: string }[], from: number): number {\n\tfor (let i = from; i < tokens.length && i < from + 8; i++) {\n\t\tconst token = tokens[i];\n\t\tif (token.type === \"EOF\" || token.type === \"COMMA\" || token.type === \"RPAREN\") return -1;\n\t\tif ((token.text ?? token.value ?? \"\").toLowerCase() === \"base\") return i;\n\t}\n\treturn -1;\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `<phrase> of X and Y`, a two-operand phrase front end for a builtin that\n * already exists. Backs `larger of`/`greater of` and `smaller of`/`lesser of`\n * (min/max, indices 9/10) and `gcd of`/`lcm of` (indices 38/39), reusing those\n * implementations rather than duplicating them.\n *\n * Triggered on a fused `LARGER_OF`/`SMALLER_OF` token (see\n * MathPhrasesPackage.ts's `phrases` field). NOT built on\n * {@link definePhrasePattern}: once \"of\" is fused into the trigger, the\n * next thing is X (an `expr`), not a keyword, the same structural\n * mismatch as `ClampParselet`/`IfThenElseParselet` (see their doc\n * comments), so this is hand-written too.\n *\n * X is parsed at `BindingPower.Conjunction` so it stops at \"and\" without also\n * stopping at \"+\", which is what lets \"larger of 1 + 1 and 3\" be written. See\n * Token.ts's AND_CONJ comment.\n */\nexport function largerSmallerParselet(builtinIndex: number): PrefixParselet {\n return {\n category: \"MathPhrases\",\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(BindingPower.Conjunction, builder); // X\n parser.consume(\"AND_CONJ\"); // \"and\"\n parser.parseExpression(BindingPower.Lowest, builder); // Y\n builder.emitOpcode(OpCode.CALL_BUILTIN);\n builder.emitIndex(builtinIndex);\n builder.emitIndex(2);\n },\n };\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `half of X` -> X / 2. Triggered on the fused `HALF_OF` token (see\n * MathPhrasesPackage.ts's `phrases` field), once \"of\" is fused into the\n * trigger, nothing but X remains, so this is a plain PUSH_NUMBER + DIV,\n * no PhrasePattern/keyword-slot machinery needed at all.\n */\nexport const halfParselet: PrefixParselet = {\n category: \"MathPhrases\",\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(BindingPower.Lowest, builder);\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(2);\n builder.emitOpcode(OpCode.DIV);\n },\n};\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `midpoint between X and Y` -> (X + Y) / 2. Triggered on the fused\n * `MIDPOINT_BETWEEN` token (see MathPhrasesPackage.ts's `phrases` field).\n * Hand-written for the same reason as `LargerSmallerParselet`, once\n * \"between\" is fused into the trigger, X (an `expr`) comes next, not a\n * keyword, which `definePhrasePattern` can't start an alternative with.\n *\n * X is parsed at `BindingPower.Conjunction`, the level \"and\" itself binds at,\n * so that it stops at \"and\" without also stopping at \"+\". This used to be\n * `Product` for the same reason, back when the word \"and\" was the PLUS token\n * itself and nothing weaker could tell them apart; the cost was that\n * \"midpoint between 100 + 50 and 300\" could not be written, because the\n * operand slot gave up at the \"+\". See Token.ts's AND_CONJ comment.\n */\nexport const midpointParselet: PrefixParselet = {\n category: \"MathPhrases\",\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(BindingPower.Conjunction, builder); // X\n parser.consume(\"AND_CONJ\"); // \"and\"\n parser.parseExpression(BindingPower.Lowest, builder); // Y\n builder.emitOpcode(OpCode.ADD);\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(2);\n builder.emitOpcode(OpCode.DIV);\n },\n};\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { definePhrasePattern } from \"@solve-js/parser/PhrasePattern\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `random number between X and Y`, a phrase-form alias of the existing\n * `roll(X, Y)` builtin (CALL_BUILTIN index 37, see VMBuiltins.ts):\n * \"random number\" is deliberately NOT a distinct random-generation\n * primitive, it's the same random-integer-in-range logic under a\n * different, more calculator-natural name.\n *\n * Triggered on the `RANDOM_NUMBER` token, fused from the literal two-word\n * phrase \"random number\" by `MathPhrasesPackage.ts`'s `phrases` field\n * needed because \"random\" alone is already a zero-arg `FUNC` keyword\n * (`random()` -> `Math.random()`); fusing the full phrase up front avoids\n * that token ever reaching prefix dispatch as a bare `FUNC` in this\n * context.\n */\nexport const randomNumberParselet: PrefixParselet = definePhrasePattern({\n category: \"MathPhrases\",\n alternatives: [\n {\n slots: [\n { kind: \"keyword\", tokenTypes: [\"BETWEEN\"] },\n { kind: \"expr\", bindingPower: BindingPower.Product },\n { kind: \"keyword\", tokenTypes: [\"AND_CONJ\"] },\n { kind: \"expr\" },\n ],\n emit: (builder) => {\n builder.emitOpcode(OpCode.CALL_BUILTIN);\n builder.emitIndex(37); // roll(from, to)\n builder.emitIndex(2);\n },\n },\n ],\n});\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\nconst CLAMP_BUILTIN_INDEX = 47;\n\n/**\n * `clamp X between Y and Z` / `clamp X from Y to Z`.\n *\n * NOT built on {@link definePhrasePattern}: that builder requires every\n * alternative's first slot to be a `keyword`, but here the value X comes\n * immediately after the \"clamp\" trigger, there's no keyword to peek at\n * until AFTER X is already parsed. Same structural mismatch as\n * `IfThenElseParselet` (see its doc comment); hand-written for the same\n * reason.\n */\nexport class ClampParselet implements PrefixParselet {\n readonly category = \"MathPhrases\";\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(BindingPower.Lowest, builder); // value\n\n const mid = parser.peek();\n if (mid?.type === \"BETWEEN\") {\n parser.consume();\n parser.parseExpression(BindingPower.Product, builder); // lo — Product bp guards against \"and\" (PLUS) swallowing hi\n parser.consume(\"AND_CONJ\"); // \"and\" — see ConditionalsPackage.ts's doc comment\n parser.parseExpression(BindingPower.Lowest, builder); // hi\n } else if (mid?.type === \"FROM\") {\n parser.consume();\n parser.parseExpression(BindingPower.Lowest, builder); // lo\n parser.consume(\"TO\");\n parser.parseExpression(BindingPower.Lowest, builder); // hi\n } else {\n throw ErrorFactory.parsing(\n \"CLAMP_EXPECTED_BETWEEN_OR_FROM\",\n `Expected \"between\" or \"from\" after \"clamp <value>\" but got ${mid ? `\"${mid.value}\"` : \"end of input\"}`,\n );\n }\n\n builder.emitOpcode(OpCode.CALL_BUILTIN);\n builder.emitIndex(CLAMP_BUILTIN_INDEX);\n builder.emitIndex(3);\n }\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\nconst PROPORTION_BUILTIN_INDEX = 46;\n\n/**\n * `A is to B as C is to what`, a unit-aware proportion, e.g.\n * `5 km is to 500m as 5 cm is to what` -> `0.5cm` (see VMBuiltins.ts index\n * 46 for the unifyUom-based ratio math).\n *\n * Triggered on the `IS_TO` token, fused from the literal two-word phrase\n * \"is to\" by `MathPhrasesPackage.ts`'s `phrases` field (\"is\" alone isn't a\n * keyword anywhere else, so this fusion can't collide with anything).\n * \"what\" is matched by raw token text rather than a dedicated keyword\n * it's meaningful only in this one terminal position, so claiming a new\n * global keyword for it isn't worth the collision risk with a variable\n * literally named \"what\".\n *\n * Both `B` and `C` are parsed at `this.bindingPower` (not `Lowest`) so\n * that parsing `C` stops right before the SECOND \"is to\" instead of\n * recursing into this same parselet again, the standard Pratt rule that\n * a same-precedence infix operator doesn't get consumed by an inner\n * `parseExpression()` call at that same minimum binding power (verified\n * empirically for `AS` in ConvertersParselets.spec.ts's `(800 to 1000) as\n * decimal` composition test).\n */\nexport class ProportionParselet implements InfixParselet {\n readonly category = \"MathPhrases\";\n readonly bindingPower = BindingPower.Conditional;\n\n parse(parser: Parser, left: Token, token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(this.bindingPower, builder); // B\n parser.consume(\"AS\");\n parser.parseExpression(this.bindingPower, builder); // C\n parser.consume(\"IS_TO\");\n\n const whatToken = parser.consume();\n if (!whatToken || whatToken.value.toLowerCase() !== \"what\") {\n throw ErrorFactory.parsing(\n \"PROPORTION_EXPECTED_WHAT\",\n `Expected \"what\" but got ${whatToken ? `\"${whatToken.value}\"` : \"end of input\"}`,\n );\n }\n\n builder.emitOpcode(OpCode.CALL_BUILTIN);\n builder.emitIndex(PROPORTION_BUILTIN_INDEX);\n builder.emitIndex(3);\n }\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { VariadicAggregateParselet } from \"./parselets/VariadicAggregateParselet\";\nimport { unaryPhraseParselet } from \"./parselets/UnaryPhraseParselet\";\nimport { remainderOfParselet, nthRootParselet, logBaseParselet } from \"./parselets/RemainderRootParselets\";\nimport { functionPhraseNormalizerRule } from \"./normalizer/FunctionPhraseNormalizerRule\";\nimport { largerSmallerParselet } from \"./parselets/LargerSmallerParselet\";\nimport { halfParselet } from \"./parselets/HalfParselet\";\nimport { midpointParselet } from \"./parselets/MidpointParselet\";\nimport { randomNumberParselet } from \"./parselets/RandomNumberParselet\";\nimport { ClampParselet } from \"./parselets/ClampParselet\";\nimport { ProportionParselet } from \"./parselets/ProportionParselet\";\n\n// CALL_BUILTIN indices. See VMBuiltins.ts for the handler implementations.\nconst AVERAGE = 42, MEDIAN = 43, TOTAL = 44, COUNT = 45;\nconst MIN = 9, MAX = 10;\n// Reused builtins: gcd/lcm and the two roots already exist as functions.\nconst GCD = 38, LCM = 39, SQRT = 0, CBRT = 21;\n\n/**\n * Phrase-grammar math functions: `average/median/total/count of X, Y, Z`,\n * `larger/smaller of X and Y`, `half of X`, `midpoint between X and Y`,\n * `random number between X and Y`, `clamp X between Y and Z`, and the\n * unit-aware proportion form `A is to B as C is to what`.\n *\n * TRIGGER-WORD COLLISION DESIGN NOTE (real regression found and fixed\n * during development): \"average\", \"total\", \"count\", \"larger\", \"smaller\",\n * \"half\", and \"midpoint\" are all common, natural variable names (a\n * shipped playground example uses `:total = :afterDiscount + :tax`).\n * This codebase has a tested, intentional policy that a colon-prefixed\n * variable name can't be a keyword-shaped word (see VariableParselet.ts's\n * doc comment and its \"reserved-keyword regression\" test), so claiming\n * any of those as a BARE global keyword would have permanently broken\n * `:total = ...`-style usage, which is exactly what happened on the first\n * pass of this package.\n *\n * The fix: fuse the full two-word phrase (\"average of\", \"total of\",\n * \"half of\", \"midpoint between\", ...) into its own token via the\n * `phrases` field below, instead of claiming the leading word alone. The\n * bare word then never becomes its own token type, it stays a plain\n * IDENT (usable as a variable) unless immediately followed by its\n * qualifying keyword. `clamp` is the one exception, kept as a bare\n * keyword: \"clamp X between Y and Z\" has the value X sitting between the\n * trigger and \"between\"/\"from\", so the two words aren't adjacent and\n * can't be phrase-fused. Same accepted risk profile as this codebase's\n * existing bare \"between\"/\"from\"/\"next\"/\"last\"/\"best\" keywords.\n *\n * Most of the fused-trigger parselets are hand-written rather than built\n * on `PhrasePattern`, for a related reason: once the leading keyword is\n * fused away into the trigger token itself, the next thing in each\n * grammar is an `expr` (the value), not a keyword, `definePhrasePattern`\n * requires every alternative to start with a keyword slot. See\n * `ClampParselet.ts`'s doc comment for the same structural point.\n * `random number between X and Y` and `A is to B as C is to what` are the\n * two exceptions that still have a genuine keyword after their own fused\n * trigger (\"between\"/\"as\"), so `RandomNumberParselet` stays\n * PhrasePattern-based.\n */\nexport const MATHPHRASES_PACKAGE: IEnginePackage = {\n name: \"solve-mathphrases\",\n phrases: {\n \"average of\": \"AVERAGE_OF\",\n \"median of\": \"MEDIAN_OF\",\n \"total of\": \"TOTAL_OF\",\n \"count of\": \"COUNT_OF\",\n \"larger of\": \"LARGER_OF\",\n \"greater of\": \"LARGER_OF\",\n \"smaller of\": \"SMALLER_OF\",\n \"lesser of\": \"SMALLER_OF\",\n \"half of\": \"HALF_OF\",\n \"midpoint between\": \"MIDPOINT_BETWEEN\",\n \"random number\": \"RANDOM_NUMBER\",\n \"is to\": \"IS_TO\",\n // Two-operand phrases reusing the gcd/lcm builtins.\n \"remainder of\": \"REMAINDER_OF\",\n \"gcd of\": \"GCD_OF\",\n \"lcm of\": \"LCM_OF\",\n // sqrt/cbrt spelled the way they are said.\n \"square root of\": \"SQUARE_ROOT_OF\",\n \"cube root of\": \"CUBE_ROOT_OF\",\n },\n prefixParselets: {\n AVERAGE_OF: new VariadicAggregateParselet(AVERAGE),\n MEDIAN_OF: new VariadicAggregateParselet(MEDIAN),\n TOTAL_OF: new VariadicAggregateParselet(TOTAL),\n COUNT_OF: new VariadicAggregateParselet(COUNT),\n LARGER_OF: largerSmallerParselet(MAX),\n SMALLER_OF: largerSmallerParselet(MIN),\n HALF_OF: halfParselet,\n MIDPOINT_BETWEEN: midpointParselet,\n RANDOM_NUMBER: randomNumberParselet,\n REMAINDER_OF: remainderOfParselet,\n NTH_ROOT: nthRootParselet,\n LOG_PHRASE: logBaseParselet,\n GCD_OF: largerSmallerParselet(GCD),\n LCM_OF: largerSmallerParselet(LCM),\n SQUARE_ROOT_OF: unaryPhraseParselet(SQRT),\n CUBE_ROOT_OF: unaryPhraseParselet(CBRT),\n CLAMP: new ClampParselet(),\n },\n infixParselets: {\n IS_TO: new ProportionParselet(),\n },\n normalizerRules: [\n functionPhraseNormalizerRule(),\n ],\n};\n","import { PrefixParselet, InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/**\n * Soulver's documented investment grammar.\n *\n * `CompoundInterestParselet` already covers the maths, but only through this\n * package's own `compound interest on X over Y years at Z%` phrasing, and its\n * doc comment said so: the documented spellings are `$1,000 after 3 years at\n * 7%` and `$1,000 for 3 years at 7% compounding monthly`, with no leading verb\n * at all. Those are the forms anyone reading Soulver's documentation will type,\n * and none of them parsed. This file adds them, alongside the return-on-\n * investment and present-value phrases from the same page.\n *\n * The older `over` spelling still works. Nothing that parsed before stops.\n */\n\n/** How many times a year each documented interval compounds. */\nconst PERIODS_PER_YEAR: Record<string, number> = {\n\tannually: 1,\n\tyearly: 1,\n\t// Semi-annual is not on Soulver's page, but it is the one interval a\n\t// reader of the other four would expect to exist, and leaving it out makes\n\t// the set look arbitrary rather than deliberate.\n\t\"semi-annually\": 2,\n\tquarterly: 4,\n\tmonthly: 12,\n\tfortnightly: 26,\n\tweekly: 52,\n\tdaily: 365,\n};\n\n/**\n * Consumes an optional `compounding <interval>` tail.\n *\n * @param parser - The parser positioned after the rate.\n * @returns Periods per year, or 1 when no interval was given, which is the\n * annual compounding the bare form already meant.\n */\nexport function readCompoundingInterval(parser: Parser): number {\n\tif (!parser.match(\"COMPOUNDING\")) return 1;\n\tconst token = parser.peek();\n\tconst name = (token?.text ?? token?.value ?? \"\").toLowerCase();\n\tconst periods = PERIODS_PER_YEAR[name];\n\tif (periods === undefined) {\n\t\t// Naming the accepted set beats \"unexpected token\": the whole point of\n\t\t// this phrase is that it is written in words, so a typo is the likely\n\t\t// failure and the fix should be visible in the message.\n\t\tthrow ErrorFactory.parsing(\n\t\t\t\"UNKNOWN_COMPOUNDING_INTERVAL\",\n\t\t\t`compounding ${name || \"?\"}: expected one of ${Object.keys(PERIODS_PER_YEAR).join(\", \")}`,\n\t\t);\n\t}\n\tparser.consume();\n\treturn periods;\n}\n\n/**\n * `<principal> after <years> years at <rate>%` and the `for ... compounding\n * <interval>` variant, as an infix on the pivot word.\n *\n * Infix rather than prefix because the principal comes first and there is no\n * leading keyword to trigger on, which is the same structural reason\n * `PercentageChangeParselet` hangs off `to` rather than off the number.\n *\n * Binding power is `Conditional`, below arithmetic, so the principal is a whole\n * expression: `$500 + $500 after 3 years at 7%` compounds the thousand rather\n * than only the second five hundred.\n */\nexport class InvestmentGrowthParselet implements InfixParselet {\n\treadonly category = \"Finance\";\n\treadonly bindingPower = BindingPower.Conditional;\n\n\t/**\n\t * @param annualIndex - Builtin for the annual case, so the common form\n\t * keeps using the same code path (and the same rounding) it always did.\n\t * @param intervalIndex - Builtin taking an explicit periods-per-year.\n\t */\n\tconstructor(\n\t\tprivate readonly annualIndex: number,\n\t\tprivate readonly intervalIndex: number,\n\t) {}\n\n\tparse(parser: Parser, _left: Token, _token: Token, builder: BytecodeBuilder): void {\n\t\t// The principal is already on the stack: this is an infix.\n\t\tparser.parseExpression(BindingPower.Conditional, builder); // years\n\t\tif (!parser.match(\"RATE_AT\")) parser.consume(\"AT\");\n\t\tparser.parseExpression(BindingPower.Conditional, builder); // rate\n\t\tconst periods = readCompoundingInterval(parser);\n\n\t\t// [principal, years, rate] -> [principal, rate, years], matching the\n\t\t// builtins' (principal, rate, years) order. Same SWAP as\n\t\t// CompoundInterestParselet, for the same reason.\n\t\tbuilder.emitOpcode(OpCode.SWAP);\n\n\t\tif (periods === 1) {\n\t\t\tbuilder.emitOpcode(OpCode.CALL_BUILTIN);\n\t\t\tbuilder.emitIndex(this.annualIndex);\n\t\t\tbuilder.emitIndex(3);\n\t\t\treturn;\n\t\t}\n\t\tbuilder.emitOpcode(OpCode.PUSH_NUMBER);\n\t\tbuilder.emitNumber(periods);\n\t\tbuilder.emitOpcode(OpCode.CALL_BUILTIN);\n\t\tbuilder.emitIndex(this.intervalIndex);\n\t\tbuilder.emitIndex(4);\n\t}\n}\n\n/**\n * `present value of <amount> after <years> years at <rate>%`.\n *\n * What a sum promised in the future is worth today, the inverse of compound\n * growth. Triggered on the fused `PRESENT_VALUE_OF` phrase rather than a bare\n * \"present\"/\"value\" keyword, for the reason this package fuses every other\n * trigger: both are ordinary words someone might use as a variable name.\n */\nexport class PresentValueParselet implements PrefixParselet {\n\treadonly category = \"Finance\";\n\n\tconstructor(private readonly builtinIndex: number) {}\n\n\tparse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n\t\tparser.parseExpression(BindingPower.Conditional, builder); // future value\n\t\t// `after` is the documented pivot; `over` and `in` are accepted because\n\t\t// this reads as English and all three are natural here.\n\t\tif (!parser.match(\"AFTER\") && !parser.match(\"OVER\") && !parser.match(\"IN\")) {\n\t\t\tparser.consume(\"AFTER\");\n\t\t}\n\t\tparser.parseExpression(BindingPower.Conditional, builder); // years\n\t\tif (!parser.match(\"RATE_AT\")) parser.consume(\"AT\");\n\t\tparser.parseExpression(BindingPower.Conditional, builder); // rate\n\n\t\tbuilder.emitOpcode(OpCode.SWAP);\n\t\tbuilder.emitOpcode(OpCode.CALL_BUILTIN);\n\t\tbuilder.emitIndex(this.builtinIndex);\n\t\tbuilder.emitIndex(3);\n\t}\n}\n\n/**\n * `<invested> invested <returned> returned`, the return on the investment.\n *\n * `$500 invested $1,500 returned` is 2x, not 3x: ROI measures the profit\n * against the cost, so doubling your money is a 1x return and tripling it is\n * 2x. Worth stating because \"3x\" is what the money multiple would be, and the\n * two get confused; `$1,500 / $500` gives that instead.\n *\n * Infix on `invested`, so the amount invested is the left operand.\n */\nexport class ReturnOnInvestmentParselet implements InfixParselet {\n\treadonly category = \"Finance\";\n\treadonly bindingPower = BindingPower.Conditional;\n\n\tconstructor(private readonly builtinIndex: number) {}\n\n\tparse(parser: Parser, _left: Token, _token: Token, builder: BytecodeBuilder): void {\n\t\tparser.parseExpression(BindingPower.Conditional, builder); // returned\n\t\tparser.consume(\"RETURNED\");\n\n\t\tbuilder.emitOpcode(OpCode.CALL_BUILTIN);\n\t\tbuilder.emitIndex(this.builtinIndex);\n\t\tbuilder.emitIndex(2);\n\t}\n}\n\n/**\n * `annual return on <invested> invested <returned> returned after <years> years`.\n *\n * The compound annual growth rate: the constant yearly rate that turns the one\n * into the other. Returned as a percentage, so it renders \"13.99%\".\n */\nexport class AnnualReturnParselet implements PrefixParselet {\n\treadonly category = \"Finance\";\n\n\tconstructor(private readonly builtinIndex: number) {}\n\n\tparse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n\t\tparser.parseExpression(BindingPower.Conditional, builder); // invested\n\t\tparser.consume(\"INVESTED\");\n\t\tparser.parseExpression(BindingPower.Conditional, builder); // returned\n\t\tparser.consume(\"RETURNED\");\n\t\tif (!parser.match(\"AFTER\") && !parser.match(\"OVER\") && !parser.match(\"IN\")) {\n\t\t\tparser.consume(\"AFTER\");\n\t\t}\n\t\tparser.parseExpression(BindingPower.Conditional, builder); // years\n\n\t\tbuilder.emitOpcode(OpCode.CALL_BUILTIN);\n\t\tbuilder.emitIndex(this.builtinIndex);\n\t\tbuilder.emitIndex(3);\n\t}\n}\n","import { Parser } from \"@solve-js/parser/Parser\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * Parse the two trailing clauses an interest or repayment phrase carries, the\n * TERM (`over|after|for <years>`) and the RATE (`at|@ <rate>`), in EITHER order.\n *\n * A calculator that reads like a sentence should accept both `... over 3 years\n * at 5%` and `... at 5% over 3 years`, since the two clauses are independent and\n * a person has no way to know which order the grammar wants (#120). Each caller\n * keeps its own term words (`termWords`) so the fix is purely about order, not\n * about admitting new spellings.\n *\n * The value expression for each clause is emitted onto the stack as it is parsed,\n * so term-first leaves `[years, rate]` on top and rate-first leaves `[rate,\n * years]`. The shared finance builtins take the operands `[..., rate, years]`,\n * so this returns whether the caller must `SWAP` the top two to get there: a swap\n * for the term-first order, none for the rate-first order.\n */\nexport function parseTermAndRate(parser: Parser, builder: BytecodeBuilder, termWords: readonly string[]): { swap: boolean } {\n\tif (matchTerm(parser, termWords)) {\n\t\tparser.parseExpression(BindingPower.Lowest, builder); // years\n\t\tconsumeRate(parser);\n\t\tparser.parseExpression(BindingPower.Lowest, builder); // rate\n\t\treturn { swap: true };\n\t}\n\tconsumeRate(parser);\n\tparser.parseExpression(BindingPower.Lowest, builder); // rate\n\tconsumeTerm(parser, termWords);\n\tparser.parseExpression(BindingPower.Lowest, builder); // years\n\treturn { swap: false };\n}\n\n/** True if the next token is one of the accepted term words, consuming it. */\nfunction matchTerm(parser: Parser, termWords: readonly string[]): boolean {\n\tfor (const word of termWords) {\n\t\tif (parser.match(word)) return true;\n\t}\n\treturn false;\n}\n\n/** Require a term word, throwing the same `Expected OVER` a caller used to. */\nfunction consumeTerm(parser: Parser, termWords: readonly string[]): void {\n\tif (matchTerm(parser, termWords)) return;\n\tparser.consume(termWords[0]);\n}\n\n/** Require the rate word: the fused `@` (`RATE_AT`) or the bare `at`. */\nfunction consumeRate(parser: Parser): void {\n\tif (!parser.match(\"RATE_AT\")) parser.consume(\"AT\");\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { readCompoundingInterval } from \"./InvestmentParselets\";\nimport { parseTermAndRate } from \"./TermRateClauses\";\n\n/**\n * `compound interest on <principal> over <years> at <rate>%` -> future\n * value, and `interest on <principal> over <years> at <rate>%` -> interest\n * earned only. Triggered on the fused `COMPOUND_INTEREST_ON`/`INTEREST_ON`\n * tokens (see FinancePackage.ts's `phrases` field) rather than a bare\n * \"interest\"/\"compound\" keyword, \"interest\" is a very plausible variable\n * name (see FinancePackage.ts's doc comment), so it's phrase-fused instead\n * of claimed as a bare keyword, matching MathPhrasesPackage.ts's\n * established pattern.\n *\n * NOT built on {@link definePhrasePattern}: that builder requires every\n * alternative's first slot to be a `keyword`, but here the value\n * (`principal`) comes immediately after the fused trigger token, there's\n * no keyword to peek at until AFTER `principal` is already parsed. Same\n * structural reason `ClampParselet` is hand-written (see its doc comment).\n *\n * `over`/`at` are ordinary bare keywords here (not phrase-fused). See\n * Token.ts's OVER/RATE_AT doc comment for why that's safe. No \"and\"\n * (PLUS-collision) guard is needed anywhere in this grammar since neither\n * connector word is \"and\".\n *\n * The grammar parses in `principal, years, rate` order (forced by the\n * word order \"on P over Y at R%\"), but this package's shared\n * `compoundFutureValue`/`compoundInterestEarned` builtins (VMBuiltins.ts\n * indices 48/49) take `(principal, rate, years)`, matching the\n * function-call form `compoundInterest(principal, rate, years)` the task\n * asked for. The `SWAP` below reorders the top two stack values\n * (`[principal, years, rate]` -> `[principal, rate, years]`) right before\n * the builtin call so both call styles share one implementation.\n *\n * This parselet used to be the only way to reach compound interest, using\n * \"over\" where Soulver documents \"after\"/\"for\", and requiring the leading\n * \"compound interest on\"/\"interest on\" verb where Soulver has none. That was\n * recorded here as a deliberate deviation, and it meant that every expression\n * on Soulver's investments page threw.\n *\n * It is closed. `InvestmentParselets.ts` adds the bare `$1,000 after 3 years\n * at 7%` form, the `for ... compounding monthly` interval variant, present\n * value and return on investment. This parselet keeps its \"compound interest\n * on X over Y at Z%\" spelling so nothing that already parsed stops, and now\n * also accepts \"after\"/\"for\" and the \"@\" rate separator.\n */\nexport class CompoundInterestParselet implements PrefixParselet {\n readonly category = \"Finance\";\n\n /**\n * @param builtinIndex - Annual-compounding builtin.\n * @param intervalIndex - Builtin taking an explicit periods-per-year, used\n * when the expression ends in \"compounding monthly\" or similar.\n */\n constructor(\n private readonly builtinIndex: number,\n private readonly intervalIndex: number,\n ) {}\n\n parse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n // Above `Conditional`, which is where InvestmentGrowthParselet registers\n // \"after\"/\"for\". Parsed at `Lowest` the principal would swallow the whole\n // \"after 3 years at 7%\" tail as its own investment expression, and this\n // parselet would then run out of input looking for the connective.\n parser.parseExpression(BindingPower.Conditional, builder); // principal\n // The term (`over`/`after`/`for <years>`) and the rate (`at <rate>`) in\n // either order, so `interest on 1000 at 5% over 3 years` reads as naturally\n // as `... over 3 years at 5%` (#120). \"over\" was this package's own\n // substitution for Soulver's \"after\"/\"for\" (see the DEVIATION note above);\n // all three are accepted.\n const { swap } = parseTermAndRate(parser, builder, [\"AFTER\", \"FOR_DURATION\", \"OVER\"]);\n const periods = readCompoundingInterval(parser);\n\n // The builtins take `(principal, rate, years)`, which the term-first order\n // reaches only after a SWAP of the top two; the rate-first order is already\n // in that order.\n if (swap) builder.emitOpcode(OpCode.SWAP);\n\n if (periods === 1) {\n builder.emitOpcode(OpCode.CALL_BUILTIN);\n builder.emitIndex(this.builtinIndex);\n builder.emitIndex(3);\n return;\n }\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(periods);\n builder.emitOpcode(OpCode.CALL_BUILTIN);\n builder.emitIndex(this.intervalIndex);\n builder.emitIndex(4);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { parseTermAndRate } from \"./TermRateClauses\";\n\n/**\n * `[daily|monthly|annual|total] repayment on <principal> over <years> at\n * <rate>%` and the `interest` variants, the standard mortgage/loan\n * amortization formula (see VMBuiltins.ts indices 52/53's `amortizeLoan()`\n * for the math, verified against real worked numbers).\n *\n * Triggered on 8 distinct fused tokens (`DAILY_REPAYMENT_ON`,\n * `MONTHLY_REPAYMENT_ON`, `ANNUAL_REPAYMENT_ON`, `TOTAL_REPAYMENT_ON`,\n * `DAILY_LOAN_INTEREST_ON`, `MONTHLY_LOAN_INTEREST_ON`,\n * `ANNUAL_LOAN_INTEREST_ON`, `TOTAL_LOAN_INTEREST_ON`. See\n * FinancePackage.ts's `phrases` field), one instance of this class per\n * token, each parameterized by which builtin to call and which\n * `periodsPerYear` divisor to push (365/12/1/0). Same\n * one-class-many-registrations shape as MathPhrases'\n * `VariadicAggregateParselet`/`ClampParselet`.\n *\n * Not `definePhrasePattern`-based for the same structural reason as\n * `CompoundInterestParselet` (the value comes right after the fused\n * trigger, not a keyword).\n *\n * Emits `[principal, years, rate]` in parse order, `SWAP`s to\n * `[principal, rate, years]` (matching the shared builtins'\n * `(principal, rate, years, periodsPerYear)` signature. See\n * CompoundInterestParselet.ts's doc comment for why), then pushes the\n * literal `periodsPerYear` constant before the CALL_BUILTIN.\n */\nexport class LoanRepaymentParselet implements PrefixParselet {\n readonly category = \"Finance\";\n\n constructor(\n private readonly builtinIndex: number,\n private readonly periodsPerYear: number,\n ) {}\n\n parse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(BindingPower.Lowest, builder); // principal\n // The term (`over <years>`) and the rate (`at <rate>`) in either order, so\n // `monthly repayment on 200000 at 4% over 25 years` reads as well as\n // `... over 25 years at 4%` (#120).\n const { swap } = parseTermAndRate(parser, builder, [\"OVER\"]);\n\n // The builtins take `(principal, rate, years, periodsPerYear)`; the term-first\n // order reaches `[principal, rate, years]` only after a SWAP of the top two,\n // the rate-first order is already there.\n if (swap) builder.emitOpcode(OpCode.SWAP);\n\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(this.periodsPerYear);\n\n builder.emitOpcode(OpCode.CALL_BUILTIN);\n builder.emitIndex(this.builtinIndex);\n builder.emitIndex(4);\n }\n}\n","import { PrefixParselet, InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `split <amount> between <N> [people]`, the per-person bill split written with\n * the verb first. Triggered on the `SPLIT` token that BillSplitNormalizerRule\n * retypes the ordinary word `split` to, only inside this shape, so `split` stays\n * an ordinary word and variable name everywhere else. Both `between` and a\n * trailing `people` are consumed here: `between` is the engine's pre-existing\n * bare keyword, and `people` is the `PEOPLE` token the normalizer retypes so the\n * count does not read as `N * people`.\n *\n * Not `definePhrasePattern`-based, for the same structural reason as\n * `SalesTaxParselet`: the value comes right after the trigger, not a keyword.\n * The amount and the count parse at `Lowest`, so each is a whole expression up\n * to the next keyword, and `split $120 + $5 between 3` splits the $125.\n */\nexport class SplitBetweenParselet implements PrefixParselet {\n readonly category = \"Finance\";\n\n constructor(private readonly builtinIndex: number) {}\n\n parse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(BindingPower.Lowest, builder); // amount\n parser.consume(\"BETWEEN\");\n parser.parseExpression(BindingPower.Lowest, builder); // number of shares\n parser.match(\"PEOPLE\"); // optional trailing \"people\"\n\n builder.emitOpcode(OpCode.CALL_BUILTIN);\n builder.emitIndex(this.builtinIndex);\n builder.emitIndex(2);\n }\n}\n\n/**\n * `<amount> split <N> ways`, the same split written with the amount first.\n *\n * Infix on the `SPLIT` token, so the amount is the left operand, with a binding\n * power of `Conditional` (below arithmetic) exactly like `InvestmentGrowthParselet`\n * and `ReturnOnInvestmentParselet`: in `$120 + 18% split 3 ways` the `+` and the\n * `%` bind first, so the whole `$120 + 18%` (an exact `$141.60`) is what gets\n * split, not the `18%` alone. The count parses at `Conditional` and stops at the\n * `WAYS` token the normalizer retyped `ways` to.\n */\nexport class SplitWaysParselet implements InfixParselet {\n readonly category = \"Finance\";\n readonly bindingPower = BindingPower.Conditional;\n\n constructor(private readonly builtinIndex: number) {}\n\n parse(parser: Parser, _left: Token, _token: Token, builder: BytecodeBuilder): void {\n // The amount is already on the stack: this is an infix.\n parser.parseExpression(BindingPower.Conditional, builder); // number of shares\n parser.consume(\"WAYS\");\n\n builder.emitOpcode(OpCode.CALL_BUILTIN);\n builder.emitIndex(this.builtinIndex);\n builder.emitIndex(2);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/** A savings contribution period: how many fall in a year, and the unit the answer reads in. */\ninterface SavingsPeriod {\n readonly perYear: number;\n readonly unit: string;\n}\n\n/**\n * The contribution periods a savings goal accepts. `annually` reads as `yearly`.\n * The answer's unit is the period's own, so \"how long to save ... weekly\" is\n * counted and reported in weeks.\n */\nconst SAVINGS_PERIODS: Record<string, SavingsPeriod> = {\n daily: { perYear: 365, unit: \"days\" },\n weekly: { perYear: 52, unit: \"weeks\" },\n monthly: { perYear: 12, unit: \"months\" },\n yearly: { perYear: 1, unit: \"years\" },\n annually: { perYear: 1, unit: \"years\" },\n};\n\n/** Reads and consumes the contribution period word, naming the accepted set on a typo. */\nfunction readSavingsPeriod(parser: Parser): SavingsPeriod {\n const token = parser.peek();\n const word = (token?.text ?? token?.value ?? \"\").toLowerCase();\n const period = SAVINGS_PERIODS[word];\n if (period === undefined) {\n throw ErrorFactory.parsing(\n \"UNKNOWN_SAVINGS_PERIOD\",\n `savings goal: expected a contribution period, one of ${Object.keys(SAVINGS_PERIODS).join(\", \")}, not \"${word || \"?\"}\".`,\n );\n }\n parser.consume();\n return period;\n}\n\n/**\n * `how long to save <target> at <amount> <period> [at <rate>]`, answering the\n * number of whole periods to reach the target, in the contribution's own unit.\n *\n * The contribution parses at Product (the implicit-multiply level), so a `*`\n * inserted before a period word that is not a phrase-starter (`weekly`, `daily`,\n * `yearly`) is left unconsumed and then skipped, while `monthly` (which starts\n * `monthly repayment on`, so no `*` is inserted) reads with the skip a no-op.\n * Hand-written for the same reason as LoanRepaymentParselet: the value follows\n * the trigger, not a keyword.\n */\nexport class SavingsDurationParselet implements PrefixParselet {\n readonly category = \"Finance\";\n\n constructor(private readonly builtinIndex: number) {}\n\n parse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(BindingPower.Conditional, builder); // target\n if (!parser.match(\"RATE_AT\")) parser.consume(\"AT\"); // \"at\" before the contribution\n parser.parseExpression(BindingPower.Product, builder); // contribution amount\n parser.match(\"STAR\"); // skip an implicit-multiply STAR before weekly/daily/...\n const period = readSavingsPeriod(parser);\n\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(period.perYear);\n // An optional trailing \"at <rate>\", else a zero rate.\n if (parser.match(\"RATE_AT\") || parser.match(\"AT\")) {\n parser.parseExpression(BindingPower.Conditional, builder); // rate\n } else {\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(0);\n }\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(period.unit);\n\n // savingsGoalPeriods(target, contribution, periodsPerYear, annualRate, periodUnit)\n builder.emitOpcode(OpCode.CALL_BUILTIN);\n builder.emitIndex(this.builtinIndex);\n builder.emitIndex(5);\n }\n}\n\n/**\n * `how much per month to save|reach <target> in <duration> [at <rate>]`,\n * answering the level monthly contribution. The target parses at Conditional and\n * `in` is consumed here, so the currency-conversion `in` infix never fires, the\n * same guard PresentValueParselet uses.\n */\nexport class SavingsContributionParselet implements PrefixParselet {\n readonly category = \"Finance\";\n\n constructor(private readonly builtinIndex: number) {}\n\n parse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n // Product, not Conditional: the bare `in` right after the target would\n // otherwise be swallowed by the currency package's conversion parselet, the\n // same guard InflationFutureValueParselet draws.\n parser.parseExpression(BindingPower.Product, builder); // target\n parser.consume(\"IN\");\n parser.parseExpression(BindingPower.Conditional, builder); // duration (months/years)\n // An optional trailing \"at <rate>\", else a zero rate.\n if (parser.match(\"RATE_AT\") || parser.match(\"AT\")) {\n parser.parseExpression(BindingPower.Conditional, builder); // rate\n } else {\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(0);\n }\n\n // savingsGoalPayment(target, duration, annualRate)\n builder.emitOpcode(OpCode.CALL_BUILTIN);\n builder.emitIndex(this.builtinIndex);\n builder.emitIndex(3);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * `tax on <amount> at <rate>%` / `tax off <amount> at <rate>%`, and the\n * `vat on`/`vat off` alias spellings, sales tax / VAT add-and-remove.\n * Triggered on the fused `TAX_ON`/`TAX_OFF` tokens (see FinancePackage.ts's\n * `phrases` field: both \"tax on\"/\"vat on\" fuse to `TAX_ON`, both\n * \"tax off\"/\"vat off\" fuse to `TAX_OFF`) rather than a bare \"tax\"/\"vat\"\n * keyword, \"tax\" in particular is a real, shipped playground variable\n * name (see MathPhrasesPackage.ts's doc comment: `:total = :subtotal +\n * :tax`), so it's phrase-fused instead of claimed as a bare keyword.\n *\n * No default tax rate is baked in anywhere in this package, `<rate>` is\n * always required and always explicit, since sales tax/VAT rates vary by\n * region and product and change over time; guessing a specific country's\n * rate here would go stale and mislead. `tax on 300` with no rate is a\n * parse error, not a silently-assumed percentage.\n *\n * Not `definePhrasePattern`-based for the same structural reason as\n * `CompoundInterestParselet` (the value comes right after the fused\n * trigger, not a keyword). No `SWAP` is needed here (unlike\n * CompoundInterestParselet/LoanRepaymentParselet), the grammar's natural\n * parse order, `[amount, rate]`, already matches the shared\n * `taxAdd`/`taxRemove` builtins' `(amount, rate)` signature.\n *\n * SCOPE DECISION: no bare-arithmetic `$300 + VAT` form (mentioned as a\n * possible style in the task brief), \"VAT\" alone carries no rate, and\n * this package deliberately never assumes one (see above), so there is no\n * well-defined meaning for a bare \"+ VAT\" addend without either a\n * hardcoded rate (rejected) or a prior variable definition, which is just\n * ordinary arithmetic + variables already supported by the engine, not a\n * new grammar feature.\n */\nexport class SalesTaxParselet implements PrefixParselet {\n readonly category = \"Finance\";\n\n constructor(private readonly builtinIndex: number) {}\n\n parse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(BindingPower.Lowest, builder); // amount\n parser.consume(\"RATE_AT\");\n parser.parseExpression(BindingPower.Lowest, builder); // rate\n\n builder.emitOpcode(OpCode.CALL_BUILTIN);\n builder.emitIndex(this.builtinIndex);\n builder.emitIndex(2);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n// CALL_BUILTIN index. See VMBuiltins.ts for the inflationAdjust(amount\n// fromYear, toYear) handler. Also reachable via the function-call form\n// inflationAdjust(...) (FunctionCallParselet's builtinNameToIndex map).\nconst INFLATION_ADJUST_BUILTIN_IDX = 60;\n\n/** Which leading phrase triggered this parselet. See FinancePackage.ts's `phrases` field. */\ntype InflationQueryVariant = \"what-is\" | \"what-was\";\n\n/**\n * Whether the rest of the line actually spells an inflation query.\n *\n * \"what is\" is an ordinary way to open a question, and this parselet used to\n * claim every line that began with one: `what is 10% of 200` parsed the\n * amount, found \"of\" where it wanted a year, and threw `Expected \"from\n * <year>\"` at a question with nothing to do with inflation.\n *\n * Both grammars handled here are identified by a keyword further along the\n * line, so look for one before committing to them:\n *\n * what is $X from <year> FROM\n * what is $X in <y1> worth in <y2> WORTH_IN\n * what was $X worth in <year> WORTH_IN\n *\n * A bare IN is deliberately not enough by itself, because `what is 10% of 200\n * in euros` has one and is not an inflation query either.\n */\nfunction hasInflationKeyword(parser: Parser): boolean {\n for (let offset = 0; ; offset++) {\n const token = parser.peekAt(offset);\n if (!token) return false;\n if (token.type === \"FROM\" || token.type === \"WORTH_IN\") return true;\n }\n}\n\n/**\n * `what is $X from <year>` -> X (given as that year's dollars) expressed\n * in present-day dollars; `what is $X in <year1> worth in <year2>` -> X\n * adjusted between two arbitrary (non-present) years; `what was $X worth\n * in <year>` -> X (given as present-day dollars) expressed in that year's\n * dollars.\n *\n * Fused on WHAT_IS/WHAT_WAS (see FinancePackage.ts's `phrases` field)\n * \"is\"/\"was\"/\"what\" are ordinary English words, not fused as bare\n * keywords, so a `:what = 5` style variable name is unaffected; only the\n * exact two-word phrases are claimed.\n *\n * NOT `definePhrasePattern`-based: the amount comes right after the fused\n * trigger, before any keyword to peek at. Same structural reason\n * `ClampParselet`/`CompoundInterestParselet` are hand-written.\n *\n * BINDING-POWER GUARD (why the amount parses at `BindingPower.Product`,\n * not `Lowest`): the \"what is ... in <year1> worth in <year2>\" branch has\n * a bare `IN` token directly after the amount. The currency package's\n * `InParselet` is a generic infix parselet registered on `IN`\n * (bindingPower 35) that fires unconditionally as soon as it's the next\n * lookahead token inside ANY sub-expression parse, including one this\n * parselet kicks off for the amount, even when the token after `IN`\n * isn't a valid conversion target, it still consumes `IN` and silently\n * no-ops, stranding the rest of the grammar. Parsing the amount at\n * `BindingPower.Product` (40) makes the Pratt loop's `bp <= minBp` check\n * block `IN` (35 <= 40) from ever being consumed there, leaving it for\n * this parselet to consume explicitly. Trade-off: the amount can't\n * contain a top-level Sum-tier `+`/`-` in this form, parenthesize if\n * needed, e.g. \"what is ($300 + $50) from 2003\". The \"what was ... worth\n * in\" branch has no such collision (the next token is the fused\n * WORTH_IN, which has no infix parselet registered at all), but uses the\n * same guarded parse for consistency between both variants of this class.\n */\nexport class InflationQueryParselet implements PrefixParselet {\n readonly category = \"Finance\";\n\n constructor(private readonly variant: InflationQueryVariant) {}\n\n parse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n if (!hasInflationKeyword(parser)) {\n // Not an inflation query, \"what is\" was just how the question opened.\n // Read what follows as the ordinary expression it is, at `Lowest`, so\n // the amount-guard binding power below does not truncate it either.\n parser.parseExpression(BindingPower.Lowest, builder);\n return;\n }\n\n parser.parseExpression(BindingPower.Product, builder); // amount\n\n if (this.variant === \"what-was\") {\n parser.consume(\"WORTH_IN\");\n parser.parseExpression(BindingPower.Lowest, builder); // toYear\n builder.emitPluginCall(\"inflationToYearFromPresent\", 2);\n return;\n }\n\n const next = parser.peek();\n if (next?.type === \"FROM\") {\n parser.consume();\n parser.parseExpression(BindingPower.Lowest, builder); // fromYear\n builder.emitPluginCall(\"inflationFromYearToPresent\", 2);\n return;\n }\n if (next?.type === \"IN\") {\n parser.consume();\n parser.parseExpression(BindingPower.Lowest, builder); // fromYear (year1)\n parser.consume(\"WORTH_IN\");\n parser.parseExpression(BindingPower.Lowest, builder); // toYear (year2)\n builder.emitOpcode(OpCode.CALL_BUILTIN);\n builder.emitIndex(INFLATION_ADJUST_BUILTIN_IDX);\n builder.emitIndex(3);\n return;\n }\n\n const gotType = next ? next.type : \"end of input\";\n throw ErrorFactory.parsing(\n \"INFLATION_EXPECTED_FROM_OR_IN\",\n `Expected \"from <year>\" or \"in <year> worth in <year>\" after \"what is <amount>\", got ${gotType}`,\n );\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/**\n * `value of $X in <futureYear> assuming N% inflation` -> a simple flat-rate\n * future-value projection (NOT CPI-table-based, future years aren't in\n * the bundled historical table, see `data/CpiTable.ts`). Fused on\n * VALUE_OF (see FinancePackage.ts's `phrases` field), \"value\"/\"of\" are\n * ordinary words elsewhere in the grammar (fusing the whole two-word\n * phrase keeps a bare `:value = 5` variable name unaffected).\n *\n * \"assuming\" IS registered as a bare single-word keyword (ASSUMING, see\n * Token.ts's doc comment on it), unlike \"value\"/\"worth\", it has near-zero\n * plausibility as a variable name (the same accepted-risk category as\n * this codebase's existing bare \"over\"/\"at\" keywords). This is required,\n * not just a style choice: without \"assuming\" being a recognized\n * phrase-starter, the normalizer's implicit-multiply rule silently\n * rewrote \"<year> assuming\" into \"<year> * assuming\" before parsing ever\n * ran, corrupting the whole grammar. \"inflation\" has no such collision\n * (it never directly follows a NUMBER, it follows the rate's \"%\" token\n * which implicit-multiply doesn't trigger on) and stays a plain-text\n * `IDENT` check, the same technique `FpsRateNormalizerRule`/\n * `ClockTimeNormalizerRule` use for \"fps\"/\"am\"/\"pm\".\n *\n * Same `BindingPower.Product` guard on the amount as\n * `InflationQueryParselet` (see its doc comment), the bare `IN` right\n * after the amount would otherwise be swallowed by the currency\n * package's `InParselet`.\n */\nexport class InflationFutureValueParselet implements PrefixParselet {\n readonly category = \"Finance\";\n\n parse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n parser.parseExpression(BindingPower.Product, builder); // amount\n parser.consume(\"IN\");\n parser.parseExpression(BindingPower.Lowest, builder); // futureYear\n parser.consume(\"ASSUMING\");\n parser.parseExpression(BindingPower.Lowest, builder); // rate (e.g. \"3%\" auto-divides to 0.03)\n\n const inflationToken = parser.peek();\n const inflationWord = inflationToken && inflationToken.type === \"IDENT\"\n ? inflationToken.value.toLowerCase()\n : undefined;\n if (inflationWord !== \"inflation\") {\n const got = inflationToken ? inflationToken.value : \"end of input\";\n throw ErrorFactory.parsing(\n \"INFLATION_EXPECTED_INFLATION_WORD\",\n `Expected the word \"inflation\" after \"assuming <rate>%\", got \"${got}\"`,\n );\n }\n parser.consume();\n\n builder.emitPluginCall(\"inflationFutureValue\", 3);\n }\n}\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * Infix parselet for the fused `IN_YEAR_DOLLARS` token (see\n * `normalizer/InYearDollarsNormalizerRule.ts`, fuses \"in <year> dollars\"\n * into one token carrying the year as its value). Backs `$X in <year>\n * dollars`, express a present-day amount in a specific historical year's\n * dollars. Mathematically identical to `what was $X worth in <year>`\n * (`InflationQueryParselet`'s \"what-was\" variant), both reuse the same\n * `inflationToYearFromPresent` plugin function, just reached via a\n * different surface phrasing.\n *\n * Binding power 35, matching the currency/UoM package's `InParselet` tier\n *. This parselet only ever fires on the ALREADY-FUSED `IN_YEAR_DOLLARS`\n * token (the bare `IN` token from the original \"in <year> dollars\" text\n * no longer exists in the stream by the time the parser runs), so there\n * is no runtime collision with `InParselet` to guard against here.\n */\nexport class InYearDollarsParselet implements InfixParselet {\n readonly category = \"Finance\";\n readonly bindingPower = 35;\n\n parse(parser: Parser, _left: Token, token: Token, builder: BytecodeBuilder): void {\n const year = Number(token.value);\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(year);\n builder.emitPluginCall(\"inflationToYearFromPresent\", 2);\n }\n}\n","import { Value, ValueType, numberValue, uomValue, errorValue } from \"@solve-js/vm/Value\";\nimport { adjustForInflation, CPI_MIN_YEAR, CPI_MAX_YEAR } from \"../data/CpiTable\";\n\n/**\n * Inflation plugin functions -- registered via IEnginePackage.pluginFunctions\n * (collision-safe allocator), not VMBuiltins.ts's shared builtinFunctions\n * registry, since none of these three need function-call (`name(args)`)\n * reachability through FunctionCallParselet -- only the general 3-arg\n * `inflationAdjust(amount, fromYear, toYear)` needs that (see VMBuiltins.ts\n * CALL_BUILTIN index 60). Matches TimezonePluginFunctions.ts's pattern.\n *\n * \"Present year\" is computed at VM EXECUTION time (new Date().getFullYear()\n * inside the handler), not baked in at parse time -- same reasoning as this\n * engine's DATE_NOW opcode: a parse-time constant would go stale if the\n * compiled bytecode for a line is ever re-executed on a later date.\n */\n\nfunction yearRangeError(code: string, badYear: number): Value {\n return errorValue(\n code,\n `Year ${badYear} is outside the bundled CPI table's range (${CPI_MIN_YEAR}-${CPI_MAX_YEAR})`,\n );\n}\n\n/** \"what is $X from YEAR\" -> X (given as YEAR's dollars) expressed in present-day dollars. */\nexport function inflationFromYearToPresentHandler(args: Value[]): Value {\n const amountValue = args[0];\n const fromYear = args[1].toNumber();\n const toYear = new Date().getFullYear();\n const result = adjustForInflation(amountValue.toNumber(), fromYear, toYear);\n if (result === undefined) return yearRangeError(\"INFLATION_YEAR_OUT_OF_RANGE\", fromYear);\n return amountValue.type === ValueType.Uom ? uomValue(result, amountValue.unit!) : numberValue(result);\n}\n\n/**\n * \"what was $X worth in YEAR\" / \"$X in YEAR dollars\" -> X (given as\n * present-day dollars) expressed in YEAR's dollars.\n */\nexport function inflationToYearFromPresentHandler(args: Value[]): Value {\n const amountValue = args[0];\n const fromYear = new Date().getFullYear();\n const toYear = args[1].toNumber();\n const result = adjustForInflation(amountValue.toNumber(), fromYear, toYear);\n if (result === undefined) return yearRangeError(\"INFLATION_YEAR_OUT_OF_RANGE\", toYear);\n return amountValue.type === ValueType.Uom ? uomValue(result, amountValue.unit!) : numberValue(result);\n}\n\n/**\n * \"value of $X in FUTURE_YEAR assuming N% inflation\" -> what that money will\n * be WORTH then, i.e. its purchasing power, discounted rather than grown:\n * PV = amount / (1 + rate) ^ years. Not CPI-table-based, since future years\n * are not in the historical table. `rate` is a decimal fraction (0.03 for 3%),\n * matching the convention everywhere else in this codebase.\n *\n * This used to multiply, reporting `value of $500 in 2028 assuming 5%\n * inflation` as a number LARGER than $500. That is the wrong direction and it\n * inverts the meaning of the question: inflation makes money worth less, and\n * the whole point of asking is to see how much less. Soulver answers $411.35\n * for that line, and shows the same figure for its `purchasing power of $500\n * in 2028 at 5% inflation` phrasing, which is what settles the reading.\n *\n * Growing a sum at a rate is still available and is a different question:\n * `$500 after 4 years at 5%` (see InvestmentParselets.ts).\n */\nexport function inflationFutureValueHandler(args: Value[]): Value {\n const amountValue = args[0];\n const futureYear = args[1].toNumber();\n const rate = args[2].toNumber();\n const years = futureYear - new Date().getFullYear();\n if (1 + rate <= 0) {\n return errorValue(\"INVALID_RATE\", `inflationFutureValue: rate ${rate} makes (1 + rate) non-positive`);\n }\n const amount = amountValue.toNumber();\n const worth = amount / Math.pow(1 + rate, years);\n return amountValue.type === ValueType.Uom ? uomValue(worth, amountValue.unit!) : numberValue(worth);\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\n/**\n * Custom normalizer rule instead of IEnginePackage.phrases: the static\n * phrase trie only fuses fixed word sequences, with no slot for an\n * arbitrary NUMBER between two keyword words -- same class of problem as\n * uom/normalizer/IngredientNameNormalizerRule.ts and ClampParselet.ts.\n *\n * Also sidesteps a real collision: the currency package's InParselet is a\n * generic infix parselet on the bare IN token that unconditionally\n * consumes IN as soon as it's seen, even when the following token isn't a\n * valid conversion target (a bare NUMBER year is not) -- it silently\n * no-ops but has already eaten the IN token, stranding \"YEAR dollars\".\n * Fusing the whole span here, before parsing starts, removes the bare IN\n * token from the stream so InParselet never sees it.\n *\n * Only fires when dollars/dollar immediately follows the year, so forms\n * like \"what is $X in YEAR1 worth in YEAR2\" and \"value of $X in YEAR\n * assuming N% inflation\" (which also have a bare IN NUMBER, but not\n * followed by \"dollars\") are left alone for InflationQueryParselet /\n * InflationFutureValueParselet to consume directly -- see their own\n * binding-power guards against the same InParselet collision.\n *\n * \"dollar\"/\"dollars\" now lexes as a UNIT token (uom/CurrencyAliases.ts's\n * word-alias support, added to lexer/units.ts's knownUnits), not IDENT --\n * accept both token types here so this fusion still fires post-expansion.\n */\nexport function inYearDollarsNormalizerRule(priority = 70): NormalizerRule {\n return {\n name: \"finance:in-year-dollars\",\n priority,\n match(tokens, pos): NormalizerMatch | null {\n const inToken = tokens[pos];\n const yearToken = tokens[pos + 1];\n const dollarsToken = tokens[pos + 2];\n if (inToken.type !== \"IN\") return null;\n if (yearToken?.type !== \"NUMBER\") return null;\n if (dollarsToken?.type !== \"IDENT\" && dollarsToken?.type !== \"UNIT\") return null;\n const word = dollarsToken.value.toLowerCase();\n if (word !== \"dollars\" && word !== \"dollar\") return null;\n\n return {\n consumed: 3,\n replacement: [createFusedToken(\"IN_YEAR_DOLLARS\", yearToken.value, tokens.slice(pos, pos + 3))],\n ruleName: \"finance:in-year-dollars\",\n };\n },\n };\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/**\n * Structured error codes this rule can raise. Co-located per the catalog's\n * convention (see `errors/ErrorCode.ts`): a package owns its own codes and\n * unions them in as it migrates, so a free-string throw here still carries a\n * catalogued, typo-checkable name.\n */\nexport const RecurringScheduleErrorCodes = {\n /** `every 0 weeks` and the like: the interval a payment repeats on has to be a positive count, or the number of payments is undefined. */\n RECURRING_INTERVAL_NOT_POSITIVE: \"RECURRING_INTERVAL_NOT_POSITIVE\",\n} as const;\n\n/**\n * How many times a named period falls in a year, for counting payments. This\n * is a scheduling model, not a calendar conversion: a monthly payment happens\n * 12 times a year and a weekly one 52, which is how pay periods are counted,\n * even though 12 thirty-day months and 52 seven-day weeks do not both add up\n * to 365 days. Keeping it independent of the second-based UNIT_TABLE is what\n * makes `every 2 weeks for 6 months` a clean 13 (26 a year, half a year).\n */\nconst PERIODS_PER_YEAR: Record<string, number> = {\n daily: 365,\n weekly: 52,\n monthly: 12,\n yearly: 1,\n // \"annually\" reads better than \"yearly\" before a duration and means the\n // same, so it is accepted as a spelling rather than a separate period.\n annually: 1,\n};\n\n/**\n * The same per-year counts keyed by the time UNIT spellings, used by the\n * `every N <unit>` head and by the `for N <unit>` duration. Plurals share a\n * value: a unit token keeps its written spelling, so both forms appear.\n */\nconst UNIT_PER_YEAR: Record<string, number> = {\n day: 365,\n days: 365,\n week: 52,\n weeks: 52,\n month: 12,\n months: 12,\n year: 1,\n years: 1,\n};\n\n/** Articles standing in for \"one\" on the duration, so `for a year` is `for 1 year`. */\nconst ARTICLES = new Set([\"a\", \"an\"]);\n\n/**\n * Token types that can end a value, and so can be the amount a schedule\n * multiplies. A NUMBER (`450`, and the amount of a currency literal like\n * `£450`, which lexes as POUND then NUMBER), a closing bracket (`(20 + 5)`),\n * a bare variable, or a percentage all qualify. The amount may be several\n * tokens; only its last one sits at the match position, and the rest are\n * before it and left untouched.\n */\nconst VALUE_ENDERS = new Set([\n \"NUMBER\",\n \"FLOAT\",\n \"HEX\",\n \"BIGINT\",\n \"RPAREN\",\n \"RBRACKET\",\n \"IDENT\",\n \"PERCENT\",\n \"STRING\",\n]);\n\n/** The lowercased written form of a token, tolerant of `text` vs `value`. */\nfunction wordOf(token: Token | undefined): string {\n if (token === undefined) return \"\";\n return (token.text ?? token.value ?? \"\").toLowerCase();\n}\n\n/** Whether a token is a time UNIT this rule recognises, and its per-year count. */\nfunction unitPerYear(token: Token | undefined): number | undefined {\n if (token === undefined || token.type !== \"UNIT\") return undefined;\n return UNIT_PER_YEAR[wordOf(token)];\n}\n\n/** A period head parsed off the stream: its per-year rate and how many tokens it spans. */\ninterface PeriodHead {\n perYear: number;\n consumed: number;\n}\n\n/**\n * Reads the period that sits between the amount and `for`: either a single\n * word (`monthly`), or `every N <unit>` (`every 2 weeks`). Returns null when\n * the tokens at `i` are not a period, which is how a plain finance `for`\n * (`$1,000 for 3 years at 7%`, no period word) declines this rule.\n */\nfunction readPeriod(tokens: Token[], i: number): PeriodHead | null {\n const head = tokens[i];\n if (head === undefined) return null;\n\n if (head.type === \"IDENT\") {\n const simple = PERIODS_PER_YEAR[wordOf(head)];\n if (simple !== undefined) return { perYear: simple, consumed: 1 };\n\n if (wordOf(head) === \"every\") {\n const countToken = tokens[i + 1];\n if (countToken?.type !== \"NUMBER\") return null;\n const per = unitPerYear(tokens[i + 2]);\n if (per === undefined) return null;\n const n = Number((countToken.value ?? \"\").replace(/,/g, \"\"));\n // A zero or negative interval has no number of payments to give, so it\n // is reported rather than divided by. Guarded here, at the one place\n // the divisor is known, so the message names the actual mistake.\n if (!(n > 0)) {\n throw ErrorFactory.parsing({\n code: RecurringScheduleErrorCodes.RECURRING_INTERVAL_NOT_POSITIVE,\n message: `A recurring interval must be a positive number, not \"${countToken.value}\".`,\n expected: \"a positive interval, e.g. every 2 weeks\",\n found: `every ${countToken.value} ${wordOf(tokens[i + 2])}`,\n });\n }\n return { perYear: per / n, consumed: 3 };\n }\n }\n return null;\n}\n\n/** A duration parsed off the stream: its length in years and how many tokens it spans. */\ninterface Duration {\n years: number;\n consumed: number;\n}\n\n/**\n * Reads the `N <unit>` (or `a <unit>`) that follows `for`. Returns null when\n * it is not a recognised time quantity, leaving other `for` grammar alone.\n */\nfunction readDuration(tokens: Token[], i: number): Duration | null {\n const first = tokens[i];\n if (first === undefined) return null;\n\n let count: number;\n let unitIndex: number;\n if (first.type === \"NUMBER\") {\n count = Number((first.value ?? \"\").replace(/,/g, \"\"));\n unitIndex = i + 1;\n } else if (first.type === \"IDENT\" && ARTICLES.has(wordOf(first))) {\n count = 1;\n unitIndex = i + 1;\n } else {\n return null;\n }\n\n const per = unitPerYear(tokens[unitIndex]);\n if (per === undefined) return null;\n if (!Number.isFinite(count) || count < 0) return null;\n return { years: count / per, consumed: unitIndex - i + 1 };\n}\n\n/**\n * The number of whole payments a schedule makes over a span. Payments are\n * counted one per completed period, so a final part-period, which has not\n * come due, is not counted: `every 2 weeks for 5 weeks` is 2, not 3. A count\n * that lands on a whole number through floating point (`52/2 * 6/12` is 13)\n * is snapped to it before the floor, so the clean cases stay clean.\n */\nfunction wholePayments(perYear: number, durationYears: number): number {\n const raw = perYear * durationYears;\n const nearest = Math.round(raw);\n return Math.abs(raw - nearest) < 1e-9 ? nearest : Math.floor(raw);\n}\n\n/**\n * `<amount> <period> for <duration>` as a running total: `450 monthly for 18\n * months` is 8,100, `£12.99 monthly for 2 years` is `£311.76`, `2000 every 2\n * weeks for 6 months` is 26,000. The total is the primary result, the payment\n * count is the secondary detail that produced it (total = amount times count).\n *\n * Implemented as a rewrite to a plain multiplication, `amount * count`, rather\n * than a new value kind or builtin. The count is a whole number known from the\n * literal tokens, so the engine's own arithmetic carries everything after:\n * a currency amount stays currency and, when it is exact (`£12.99`), the total\n * stays exact through the same money-multiply path that makes `£12.99 * 24`\n * exactly `£311.76`. A bare decimal stays an ordinary float, as it does\n * everywhere else in the engine.\n *\n * Matched at the amount, the token before the period word, so this rule fires\n * ahead of implicit multiplication at that position (priority 78, above it)\n * and inserts its own `*`. That sidesteps the fact that implicit multiply is\n * suppressed before some of these words and not others (`monthly` can start\n * the fused phrase `monthly repayment on`, `weekly` cannot), which would\n * otherwise give one word a doubled operator and another none.\n *\n * The period word is only ever claimed when the whole `<amount> <period> for\n * <N> <unit>` shape is present, so a bare `monthly`/`weekly`/`:every` stays an\n * ordinary variable everywhere else, the same contextual-claim discipline as\n * `AtRateNormalizerRule` and `ForDurationNormalizerRule`.\n */\nexport function recurringScheduleNormalizerRule(priority = 78): NormalizerRule {\n return {\n name: \"finance:recurring-schedule\",\n priority,\n match(tokens, pos): NormalizerMatch | null {\n const amount = tokens[pos];\n if (amount === undefined || !VALUE_ENDERS.has(amount.type)) return null;\n\n const period = readPeriod(tokens, pos + 1);\n if (period === null) return null;\n\n const forIndex = pos + 1 + period.consumed;\n if (tokens[forIndex]?.type !== \"FOR_DURATION\") return null;\n\n const duration = readDuration(tokens, forIndex + 1);\n if (duration === null) return null;\n\n const count = wholePayments(period.perYear, duration.years);\n const consumed = forIndex + 1 + duration.consumed - pos;\n const source = tokens.slice(pos, pos + consumed);\n\n return {\n // The amount is re-emitted unchanged, then `* <count>`. The amount may\n // be the tail of a longer expression, whose earlier tokens are before\n // `pos` and pass through on their own.\n replacement: [\n amount,\n createFusedToken(\"STAR\", \"*\", source),\n createFusedToken(\"NUMBER\", String(count), source),\n ],\n consumed,\n ruleName: \"finance:recurring-schedule\",\n };\n },\n };\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\n/** The lowercased written form of a token, tolerant of `text` vs `value`. */\nfunction wordOf(token: Token | undefined): string {\n if (token === undefined) return \"\";\n return (token.text ?? token.value ?? \"\").toLowerCase();\n}\n\n/**\n * Token types that can END a value, so one sitting directly before `split`\n * marks the infix shape (`<amount> split N ways`) rather than the prefix one\n * (`split <amount> between N`). The same set the recurring-schedule rule uses.\n */\nconst VALUE_ENDERS = new Set([\n \"NUMBER\",\n \"FLOAT\",\n \"HEX\",\n \"BIGINT\",\n \"RPAREN\",\n \"RBRACKET\",\n \"IDENT\",\n \"PERCENT\",\n \"STRING\",\n]);\n\n/**\n * A numeric-literal count. The shape is only claimed when the share count is a\n * bare number directly in position; a decimal (`between 2.5`) is claimed too so\n * the split builtin can reject it with a clear \"whole number\" message rather\n * than the line silently reading `split` as an ordinary word.\n */\nconst NUMERIC = new Set([\"NUMBER\", \"FLOAT\"]);\n\n/**\n * `split $180 between 4` and `$120 + 18% split 3 ways`, a per-person bill split.\n *\n * `split`, `ways` and `people` are ordinary English words, so, exactly like the\n * recurring-schedule and at-rate rules, they are claimed CONTEXTUALLY here and\n * never as bare keywords: a plain IDENT is retyped to the `SPLIT`/`WAYS`/`PEOPLE`\n * token only inside the complete surrounding shape, so `:split = 5`, a variable\n * named `split`, and prose keep working everywhere else.\n *\n * Two shapes, told apart by the token before `split`:\n * - INFIX `<value> split <N> ways`: a value-ender sits before `split`, and a\n * NUMBER then the word `ways` follow it. Retype `split` to SPLIT and `ways`\n * to WAYS, leaving the count between them for the parselet.\n * - PREFIX `split <amount> between <N> [people]`: no value-ender before\n * `split`, and a BETWEEN with a NUMBER after it sits later on the line.\n * Retype `split` to SPLIT (BETWEEN is already its own token), and a trailing\n * `people` after the count to PEOPLE.\n *\n * Matched ahead of implicit multiplication (priority 77, above its 50) so the\n * count-then-word `3 ways` / `4 people` never has a `*` inserted between them,\n * the same reason the recurring-schedule rule sits at 78. The count is a literal\n * NUMBER: a parenthesised or worded count leaves the shape unclaimed and `split`\n * an ordinary word, a documented boundary.\n */\nexport function billSplitNormalizerRule(priority = 77): NormalizerRule {\n return {\n name: \"finance:bill-split\",\n priority,\n match(tokens, pos): NormalizerMatch | null {\n const head = tokens[pos];\n if (head === undefined) return null;\n\n // INFIX `<amount> split <N> ways`, matched at the amount's last token (a\n // value-ender) rather than at `split`. Anchoring here is what lets this\n // rule win over implicit multiplication at that position (its priority is\n // higher), so no `*` is inserted between the amount and the split. The\n // same anchoring the recurring-schedule rule uses on its amount.\n if (VALUE_ENDERS.has(head.type)) {\n const split = tokens[pos + 1];\n const count = tokens[pos + 2];\n const ways = tokens[pos + 3];\n if (\n split?.type === \"IDENT\" &&\n wordOf(split) === \"split\" &&\n count !== undefined &&\n NUMERIC.has(count.type) &&\n ways?.type === \"IDENT\" &&\n wordOf(ways) === \"ways\"\n ) {\n return {\n consumed: 4,\n replacement: [\n head,\n createFusedToken(\"SPLIT_WAYS\", \"split\", [split]),\n count,\n createFusedToken(\"WAYS\", \"ways\", [ways]),\n ],\n ruleName: \"finance:bill-split\",\n };\n }\n }\n\n // PREFIX `split <amount> between <N> [people]`, matched at `split`. A\n // value-ender directly before `split` would be the infix shape, already\n // handled above, so decline it here.\n if (head.type !== \"IDENT\" || wordOf(head) !== \"split\") return null;\n const before = tokens[pos - 1];\n if (before !== undefined && VALUE_ENDERS.has(before.type)) return null;\n\n // Find the BETWEEN that closes the shape on this line; the amount tokens\n // between are re-emitted as they are, so an arithmetic amount\n // (`split $120 + $5 between 3`) still parses.\n let betweenIndex = -1;\n for (let i = pos + 1; i < tokens.length; i++) {\n if (tokens[i].type === \"BETWEEN\") {\n betweenIndex = i;\n break;\n }\n }\n if (betweenIndex === -1) return null;\n\n const count = tokens[betweenIndex + 1];\n if (count === undefined || !NUMERIC.has(count.type)) return null;\n\n const peopleIndex = betweenIndex + 2;\n const hasPeople = tokens[peopleIndex]?.type === \"IDENT\" && wordOf(tokens[peopleIndex]) === \"people\";\n const lastIndex = hasPeople ? peopleIndex : betweenIndex + 1;\n\n // SPLIT, then the amount ... BETWEEN count re-emitted unchanged, then the\n // optional PEOPLE.\n const replacement: Token[] = [\n createFusedToken(\"SPLIT\", \"split\", [head]),\n ...tokens.slice(pos + 1, betweenIndex + 2),\n ];\n if (hasPeople) replacement.push(createFusedToken(\"PEOPLE\", \"people\", [tokens[peopleIndex]]));\n\n return {\n consumed: lastIndex - pos + 1,\n replacement,\n ruleName: \"finance:bill-split\",\n };\n },\n };\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { InvestmentGrowthParselet, PresentValueParselet, ReturnOnInvestmentParselet, AnnualReturnParselet } from \"./parselets/InvestmentParselets\";\nimport { CompoundInterestParselet } from \"./parselets/CompoundInterestParselet\";\nimport { LoanRepaymentParselet } from \"./parselets/LoanRepaymentParselet\";\nimport { SplitBetweenParselet, SplitWaysParselet } from \"./parselets/BillSplitParselets\";\nimport { SavingsDurationParselet, SavingsContributionParselet } from \"./parselets/SavingsGoalParselets\";\nimport { SalesTaxParselet } from \"./parselets/SalesTaxParselet\";\nimport { InflationQueryParselet } from \"./parselets/InflationQueryParselet\";\nimport { InflationFutureValueParselet } from \"./parselets/InflationFutureValueParselet\";\nimport { InYearDollarsParselet } from \"./parselets/InYearDollarsParselet\";\nimport {\n inflationFromYearToPresentHandler, inflationToYearFromPresentHandler, inflationFutureValueHandler,\n} from \"./parselets/InflationPluginFunctions\";\nimport { inYearDollarsNormalizerRule } from \"./normalizer/InYearDollarsNormalizerRule\";\nimport { recurringScheduleNormalizerRule } from \"./normalizer/RecurringScheduleNormalizerRule\";\nimport { billSplitNormalizerRule } from \"./normalizer/BillSplitNormalizerRule\";\n\n// CALL_BUILTIN indices. See VMBuiltins.ts for the handler implementations.\nconst COMPOUND_FV = 51, COMPOUND_INTEREST = 52;\nconst LOAN_REPAYMENT = 55, LOAN_INTEREST = 56;\n// Investments (documented Soulver spellings). See VMBuiltins.ts 80-84.\nconst COMPOUND_FV_EVERY = 80, COMPOUND_INTEREST_EVERY = 81;\nconst PRESENT_VALUE = 82, ROI = 83, ANNUAL_RETURN = 84;\n// 58 (taxAdd) is not referenced here: it backs the taxAdd() function-call\n// form via the builtin-name registry, not a phrase in this package.\nconst TAX_REMOVE = 59, TAX_IN = 85, TAX_ON = 86;\n// Bill split: `split $180 between 4` / `$120 + 18% split 3 ways`.\nconst SPLIT_EACH = 98;\n// Savings goals: `how much per month to save ...` and `how long to save ...`.\nconst SAVINGS_PAYMENT = 99, SAVINGS_PERIODS = 100;\n// 60 = inflationAdjust(amount, fromYear, toYear). See InflationQueryParselet.ts\n// and VMBuiltins.ts. The other three inflation calculations (present-year\n// forms + the flat-rate future-value projection) are collision-safe\n// pluginFunctions instead. See InflationPluginFunctions.ts.\n\n/**\n * Money & finance phrase grammar: compound interest / investment growth,\n * mortgage/loan repayment (standard amortization formula), sales tax/VAT\n * add-and-remove, and CPI-based inflation-adjusted value. SoulverCore-\n * inspired syntax where it could be confirmed (see each parselet's doc\n * comment for exact worked examples and any deliberate deviations);\n * function-call forms (`compoundInterest(...)`, `monthlyPayment(...)`,\n * `taxAdd(...)`, `inflationAdjust(...)`, ...) are registered separately,\n * in FUNCTION_PACKAGE's shared FUNC dispatch (see\n * `packages/function/parselets/FunctionCallParselet.ts`'s\n * `builtinNameToIndex` map) rather than here.\n *\n * TRIGGER-WORD COLLISION DESIGN NOTE (same regression class documented in\n * MathPhrasesPackage.ts, and explicitly called out for this package\n * up-front): \"interest\", \"tax\", \"principal\", \"payment\", \"rate\", \"balance\",\n * \"what\", \"worth\", \"value\" are all common, plausible variable names, a\n * shipped playground example already uses `:tax` (`:total = :subtotal +\n * :tax`, see MathPhrasesPackage.ts's doc comment). None of those words are\n * bare keywords anywhere in this package. Every trigger is either:\n * - a full phrase fused via `phrases` below (\"interest on\", \"tax on\",\n * \"monthly repayment on\", \"what is\", \"what was\", \"value of\", \"worth\n * in\", ...), so the leading word alone never becomes its own token\n * type and stays usable as `:interest`/`:tax`/`:what`/`:value`/etc., or\n * - a genuine preposition (\"over\", \"at\") with near-zero plausibility as a\n * variable name, the same accepted-risk category as this codebase's\n * existing bare \"between\"/\"from\"/\"next\"/\"last\"/\"best\" keywords (see\n * Token.ts's OVER/RATE_AT doc comment).\n *\n * \"split\", \"ways\" and \"people\" (the bill-split grammar) are likewise ordinary\n * words and never bare keywords: BillSplitNormalizerRule claims them\n * contextually, retyping to SPLIT/WAYS/PEOPLE only inside the full split shape,\n * so `:split`, a variable named `split`, and prose are untouched. \"between\"\n * reuses the pre-existing bare BETWEEN token (the accepted-risk category above).\n *\n * \"how\", \"much\", \"save\" and \"reach\" (the savings-goal phrases) are fused whole,\n * \"how long to save\" and \"how much per month to save\"/\"to reach\", so none of\n * those words becomes a bare keyword and `:save`/`:reach`/`:how` stay usable.\n *\n * Inflation-adjusted value (extends this package, see\n * `parselets/InflationQueryParselet.ts`/`InflationFutureValueParselet.ts`/\n * `InYearDollarsParselet.ts` and `data/CpiTable.ts` for the bundled,\n * clearly-labeled-approximate CPI-U table and its doc comment on\n * vintage/accuracy) was the one topic explicitly deferred from this\n * package's original scope, now implemented.\n */\nexport const FINANCE_PACKAGE: IEnginePackage = {\n name: \"solve-finance\",\n phrases: {\n \"compound interest on\": \"COMPOUND_INTEREST_ON\",\n \"interest on\": \"INTEREST_ON\",\n \"daily repayment on\": \"DAILY_REPAYMENT_ON\",\n \"monthly repayment on\": \"MONTHLY_REPAYMENT_ON\",\n \"annual repayment on\": \"ANNUAL_REPAYMENT_ON\",\n \"total repayment on\": \"TOTAL_REPAYMENT_ON\",\n \"daily interest on\": \"DAILY_LOAN_INTEREST_ON\",\n \"monthly interest on\": \"MONTHLY_LOAN_INTEREST_ON\",\n \"annual interest on\": \"ANNUAL_LOAN_INTEREST_ON\",\n \"total interest on\": \"TOTAL_LOAN_INTEREST_ON\",\n \"tax on\": \"TAX_ON\",\n \"tax off\": \"TAX_OFF\",\n // The tax already inside a gross amount. Soulver documents \"VAT in\",\n // \"VAT of\" and \"VAT from\" as three spellings of one thing.\n \"tax in\": \"TAX_IN_PHRASE\",\n \"tax of\": \"TAX_IN_PHRASE\",\n \"tax from\": \"TAX_IN_PHRASE\",\n \"vat in\": \"TAX_IN_PHRASE\",\n \"vat of\": \"TAX_IN_PHRASE\",\n \"vat from\": \"TAX_IN_PHRASE\",\n \"vat on\": \"TAX_ON\",\n \"vat off\": \"TAX_OFF\",\n \"what is\": \"WHAT_IS\",\n \"what was\": \"WHAT_WAS\",\n \"present value of\": \"PRESENT_VALUE_OF\",\n \"annual return on\": \"ANNUAL_RETURN_ON\",\n \"value of\": \"VALUE_OF\",\n \"worth in\": \"WORTH_IN\",\n \"how long to save\": \"SAVINGS_HOW_LONG\",\n \"how much per month to save\": \"SAVINGS_HOW_MUCH\",\n \"how much per month to reach\": \"SAVINGS_HOW_MUCH\",\n \"assuming\": \"ASSUMING\",\n },\n prefixParselets: {\n PRESENT_VALUE_OF: new PresentValueParselet(PRESENT_VALUE),\n ANNUAL_RETURN_ON: new AnnualReturnParselet(ANNUAL_RETURN),\n COMPOUND_INTEREST_ON: new CompoundInterestParselet(COMPOUND_FV, COMPOUND_FV_EVERY),\n INTEREST_ON: new CompoundInterestParselet(COMPOUND_INTEREST, COMPOUND_INTEREST_EVERY),\n\n DAILY_REPAYMENT_ON: new LoanRepaymentParselet(LOAN_REPAYMENT, 365),\n MONTHLY_REPAYMENT_ON: new LoanRepaymentParselet(LOAN_REPAYMENT, 12),\n ANNUAL_REPAYMENT_ON: new LoanRepaymentParselet(LOAN_REPAYMENT, 1),\n TOTAL_REPAYMENT_ON: new LoanRepaymentParselet(LOAN_REPAYMENT, 0),\n\n DAILY_LOAN_INTEREST_ON: new LoanRepaymentParselet(LOAN_INTEREST, 365),\n MONTHLY_LOAN_INTEREST_ON: new LoanRepaymentParselet(LOAN_INTEREST, 12),\n ANNUAL_LOAN_INTEREST_ON: new LoanRepaymentParselet(LOAN_INTEREST, 1),\n TOTAL_LOAN_INTEREST_ON: new LoanRepaymentParselet(LOAN_INTEREST, 0),\n\n TAX_ON: new SalesTaxParselet(TAX_ON),\n TAX_OFF: new SalesTaxParselet(TAX_REMOVE),\n TAX_IN_PHRASE: new SalesTaxParselet(TAX_IN),\n\n SPLIT: new SplitBetweenParselet(SPLIT_EACH),\n\n WHAT_IS: new InflationQueryParselet(\"what-is\"),\n WHAT_WAS: new InflationQueryParselet(\"what-was\"),\n VALUE_OF: new InflationFutureValueParselet(),\n SAVINGS_HOW_LONG: new SavingsDurationParselet(SAVINGS_PERIODS),\n SAVINGS_HOW_MUCH: new SavingsContributionParselet(SAVINGS_PAYMENT),\n },\n infixParselets: {\n // The documented bare forms: \"$1,000 after 3 years at 7%\" and the \"for\n // ... compounding monthly\" variant. Both pivot words behave identically;\n // Soulver uses \"after\" for the annual form and \"for\" with an interval.\n AFTER: new InvestmentGrowthParselet(COMPOUND_FV, COMPOUND_FV_EVERY),\n FOR_DURATION: new InvestmentGrowthParselet(COMPOUND_FV, COMPOUND_FV_EVERY),\n INVESTED: new ReturnOnInvestmentParselet(ROI),\n IN_YEAR_DOLLARS: new InYearDollarsParselet(),\n // The amount-first split spelling, `<amount> split N ways`. Infix so the\n // amount (which may be `$120 + 18%`) is the left operand. See\n // BillSplitParselets.ts.\n SPLIT_WAYS: new SplitWaysParselet(SPLIT_EACH),\n },\n normalizerRules: [\n inYearDollarsNormalizerRule(),\n // `450 monthly for 18 months` -> `450 * 18`. A recurring schedule totalled\n // as a plain multiplication, so a currency amount stays currency and an\n // exact one stays exact. See RecurringScheduleNormalizerRule.ts.\n recurringScheduleNormalizerRule(),\n // `split $180 between 4` / `$120 + 18% split 3 ways`. Retypes split/ways/\n // people contextually so those words stay ordinary names everywhere else.\n // See BillSplitNormalizerRule.ts.\n billSplitNormalizerRule(),\n ],\n pluginFunctions: {\n inflationFromYearToPresent: inflationFromYearToPresentHandler,\n /** Plugin index for `<amount> in <year>`, today's money valued in a past year. */\n inflationToYearFromPresent: inflationToYearFromPresentHandler,\n /** Plugin index for projecting an amount forward at an assumed rate. */\n inflationFutureValue: inflationFutureValueHandler,\n },\n};\n","import type { QueryClient } from \"@tanstack/query-core\";\nimport type { Token } from \"@solve-js/lexer\";\nimport type { BytecodeProgram } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { errorValue, type Value } from \"@solve-js/vm/Value\";\nimport type { IAsyncResolver, AsyncCheckResult } from \"@solve-js/resolvers/ResolverRegistry\";\nimport { getActiveQueryClient } from \"@solve-js/services/DataQueryService\";\nimport { createTimeoutSignal } from \"@solve-js/utilities/TimeoutSignal\";\nimport { nextInstruction } from \"@solve-js/parser/OperandWidth\";\n\n/**\n * Generic async resolver for the common \"single query string in, single\n * `Value` out\" shape, one `CALL_PLUGIN` opcode with exactly one preceding\n * `PUSH_STRING` argument, resolved via a live fetch and cached through\n * TanStack Query. {@link IAsyncResolver}'s own JSDoc names this exact\n * target (\"currency rates, weather, stock prices, etc.\"); this factory is\n * the generalization of the pattern two existing implementations already\n * prove out by hand, `uom/CurrencyResolver.ts` (a currency-specific\n * dual-operand variant) and `examples/osrs/OsrsAsyncResolver.ts`\n * (`examples/osrs/OsrsVmHandler.ts` for the synchronous read-back half)\n * so a new query-based package (weather, stocks, a knowledge lookup) only\n * needs to supply the fetch call and the response-to-`Value` mapping,\n * not reimplement bytecode scanning, cache-key management, or\n * Suspense/timeout/cooldown plumbing again.\n *\n * Usage: call {@link createQueryResolver} once per package, register the\n * returned `resolver` via `IEnginePackage.asyncResolvers`, and register the\n * returned `pluginFunction` at `pluginFunctionIndex` via\n * `IEnginePackage.pluginFunctions` (the same `CALL_PLUGIN` dispatch every\n * other package function uses. See `allocatePluginFunctionIndex()` in\n * `vm/VMBuiltins.ts`).\n */\nexport interface QueryResolverOptions {\n\t/** Unique namespace for cache-key scoping and diagnostics (e.g. \"weather\"). */\n\tnamespace: string;\n\t/**\n\t * The `CALL_PLUGIN` index this resolver watches for, must be the same\n\t * index the package's parselet emits and registers `pluginFunction`\n\t * under (via `allocatePluginFunctionIndex()`).\n\t */\n\tpluginFunctionIndex: number;\n\t/**\n\t * Perform the live fetch for `query` and return the resolved `Value`.\n\t * Receives an `AbortSignal` that fires on caller cancellation OR the\n\t * `timeoutMs` deadline, whichever comes first, pass it to `fetch()`.\n\t */\n\tfetchQuery: (query: string, signal: AbortSignal) => Promise<Value>;\n\t/** TanStack Query staleTime in ms, how long a resolved value stays cached before a re-evaluation refetches it. Default 5 minutes. */\n\tstaleTimeMs?: number;\n\t/**\n\t * Cadence, in ms, for proactive background refresh: how often a value that\n\t * is on screen refetches on its own, without the reader re-evaluating.\n\t * Omit (the default) for a value that should never refresh in the background\n\t * (an immutable historical close), or that should refresh only on the next\n\t * pull. It has effect only when the host has enabled background refresh\n\t * (`backgroundRefresh.enabled`); otherwise every value stays pull-only. This\n\t * is independent of `staleTimeMs`, which continues to govern the pull path.\n\t */\n\trefetchIntervalMs?: number;\n\t/** Hard timeout for `fetchQuery`, an unresponsive API must not block re-evaluation indefinitely. Default 10s. */\n\ttimeoutMs?: number;\n\t/**\n\t * How long a FAILED fetch's error result stays cached before the next\n\t * evaluation retries it. Without this, a transient outage would either\n\t * retry on every keystroke (no cooldown) or stay failed for the full\n\t * `staleTimeMs` (treating the error like real data). Default 30s.\n\t */\n\tfailureCooldownMs?: number;\n\t/**\n\t * Build the `Value` a failed fetch resolves to. Defaults to an honest\n\t * `errorValue()` (matching `UOM_CONVERT_TO`'s `CURRENCY_RATE_UNAVAILABLE`\n\t * pattern in `vm/VM.ts`, never silently substitute a stale/wrong value\n\t * for a real failure). Override for a package that prefers a graceful\n\t * fallback value instead (e.g. OSRS's `0 gp` with a `timedOut` flag).\n\t */\n\tonError?: (query: string, error: unknown) => Value;\n}\n\n/** A resolver paired with the plugin function that reads its results. */\nexport interface QueryResolverPackage {\n\t/** Register via `IEnginePackage.asyncResolvers`. */\n\tresolver: IAsyncResolver;\n\t/** Register via `IEnginePackage.pluginFunctions` at `pluginFunctionIndex`. */\n\tpluginFunction: (args: Value[]) => Value;\n}\n\nfunction defaultOnError(namespace: string): (query: string, error: unknown) => Value {\n\treturn (query, error) =>\n\t\terrorValue(\n\t\t\t`${namespace.toUpperCase()}_QUERY_FAILED`,\n\t\t\t`Failed to resolve \"${query}\": ${error instanceof Error ? error.message : String(error)}`\n\t\t);\n}\n\n/**\n * Build an async resolver and its plugin function together.\n *\n * The two halves have to agree on a cache key, and writing them separately is\n * how they drift. This returns a matched pair.\n *\n * @param config - Namespace, fetch function, and cache lifetime.\n * @returns The resolver and the plugin function to register alongside it.\n */\nexport function createQueryResolver(opts: QueryResolverOptions): QueryResolverPackage {\n\tconst staleTimeMs = opts.staleTimeMs ?? 5 * 60 * 1000;\n\tconst timeoutMs = opts.timeoutMs ?? 10_000;\n\tconst failureCooldownMs = opts.failureCooldownMs ?? 30_000;\n\tconst onError = opts.onError ?? defaultOnError(opts.namespace);\n\n\tconst queryKeyFor = (query: string) => [opts.namespace, query] as const;\n\n\tasync function fetchAndCache(query: string, signal: AbortSignal, queryClient: QueryClient): Promise<Value> {\n\t\tconst { signal: fetchSignal, cleanup } = createTimeoutSignal(signal, timeoutMs, `${opts.namespace} query`);\n\t\ttry {\n\t\t\treturn await opts.fetchQuery(query, fetchSignal);\n\t\t} catch (error) {\n\t\t\tconst failedValue = onError(query, error);\n\t\t\t// Bound the failure's lifetime in the cache separately from\n\t\t\t// staleTimeMs (which is tuned for successful results), without\n\t\t\t// this, a transient failure would either be retried on every\n\t\t\t// keystroke or persist as \"the answer\" for the full staleTime.\n\t\t\tconst cooldownTimer = setTimeout(() => {\n\t\t\t\tconst current = queryClient.getQueryData(queryKeyFor(query));\n\t\t\t\tif (current === failedValue) {\n\t\t\t\t\tqueryClient.removeQueries({ queryKey: queryKeyFor(query), exact: true });\n\t\t\t\t}\n\t\t\t}, failureCooldownMs);\n\n\t\t\t// Node keeps its event loop alive for any pending timer, so without\n\t\t\t// this each failed query holds the process open for the length of\n\t\t\t// the cooldown. That is wrong on its own terms: evicting a cache\n\t\t\t// entry is not a reason to keep a program running, and if nothing\n\t\t\t// else is alive there is no cache left to evict from. It showed up\n\t\t\t// as a test run that passed every assertion and then hung.\n\t\t\t//\n\t\t\t// `unref` is Node-only; browsers return a number from setTimeout\n\t\t\t// and need no equivalent, since a pending timer there does not keep\n\t\t\t// anything alive.\n\t\t\t(cooldownTimer as unknown as { unref?: () => void }).unref?.();\n\n\t\t\treturn failedValue;\n\t\t} finally {\n\t\t\tcleanup();\n\t\t}\n\t}\n\n\tconst resolver: IAsyncResolver = {\n\t\tnamespace: opts.namespace,\n\n\t\tpreflight(\n\t\t\t_tokens: Token[],\n\t\t\tbytecode: BytecodeProgram,\n\t\t\tpackageId: string,\n\t\t\tsignal: AbortSignal,\n\t\t\tqueryClient: QueryClient\n\t\t): AsyncCheckResult | null {\n\t\t\tconst { opcodes, strings } = bytecode;\n\t\t\tconst len = opcodes.length;\n\t\t\tlet i = 0;\n\n\t\t\twhile (i < len) {\n\t\t\t\tconst op = opcodes[i] as OpCode;\n\n\t\t\t\tif (op === OpCode.CALL_PLUGIN && i + 2 < len) {\n\t\t\t\t\tconst fnIdx = opcodes[i + 1];\n\t\t\t\t\tconst argCount = opcodes[i + 2];\n\n\t\t\t\t\tif (\n\t\t\t\t\t\tfnIdx === opts.pluginFunctionIndex &&\n\t\t\t\t\t\targCount >= 1 &&\n\t\t\t\t\t\ti >= 2 &&\n\t\t\t\t\t\topcodes[i - 2] === OpCode.PUSH_STRING\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst query = strings[opcodes[i - 1]];\n\t\t\t\t\t\tconst key = queryKeyFor(query);\n\t\t\t\t\t\tif (queryClient.getQueryData(key) === undefined) {\n\t\t\t\t\t\t\tconst resolverPromise = queryClient.fetchQuery({\n\t\t\t\t\t\t\t\tqueryKey: key,\n\t\t\t\t\t\t\t\tqueryFn: ({ signal: qSignal }) => fetchAndCache(query, qSignal, queryClient),\n\t\t\t\t\t\t\t\tstaleTime: staleTimeMs,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tconst result: AsyncCheckResult = {\n\t\t\t\t\t\t\t\tqueryKey: key.join(\":\"),\n\t\t\t\t\t\t\t\tresolver: resolverPromise,\n\t\t\t\t\t\t\t\tpackageId,\n\t\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t\t\tmetadata: { query },\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t// A resolver that declares a cadence also hands the engine a way\n\t\t\t\t\t\t\t// to refetch this exact query on its own (staleTime 0, so each\n\t\t\t\t\t\t\t// background tick genuinely refetches rather than reading the\n\t\t\t\t\t\t\t// cache). query-core dedupes an already-in-flight fetch, which is\n\t\t\t\t\t\t\t// the back-pressure a slow source needs.\n\t\t\t\t\t\t\tif (opts.refetchIntervalMs !== undefined) {\n\t\t\t\t\t\t\t\tresult.refetchIntervalMs = opts.refetchIntervalMs;\n\t\t\t\t\t\t\t\tresult.refetch = () =>\n\t\t\t\t\t\t\t\t\tqueryClient.fetchQuery({\n\t\t\t\t\t\t\t\t\t\tqueryKey: key,\n\t\t\t\t\t\t\t\t\t\tqueryFn: ({ signal: qSignal }) => fetchAndCache(query, qSignal, queryClient),\n\t\t\t\t\t\t\t\t\t\tstaleTime: 0,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\ti += 3;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Step over this instruction and its operands. Shared table, because\n\t\t\t\t// three hand-copied versions of this had already drifted.\n\t\t\t\ti = nextInstruction(opcodes, i);\n\t\t\t}\n\n\t\t\treturn null;\n\t\t},\n\n\t\tdestroy(): void {\n\t\t\t// Cache cleared by ResolverRegistry.unregister() via removeQueries({ queryKey: [namespace] }).\n\t\t},\n\t};\n\n\tconst pluginFunction = (args: Value[]): Value => {\n\t\tconst query = args[0]?.value as string;\n\t\tconst cached = getActiveQueryClient()?.getQueryData(queryKeyFor(query));\n\t\tif (cached !== undefined) return cached as Value;\n\t\t// preflight() guarantees this is cached before the VM ever runs the\n\t\t// CALL_PLUGIN that reaches here. This is an honest \"shouldn't\n\t\t// happen\" fallback, not a real code path.\n\t\treturn errorValue(`${opts.namespace.toUpperCase()}_NOT_PREFLIGHTED`, `No cached result for \"${query}\"`);\n\t};\n\n\treturn { resolver, pluginFunction };\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/** The 5 SoulverCore-documented weather query kinds, encoded as the first\n * segment of the query string this package's single async resolver caches\n * on (see WeatherPackage.ts's `WEATHER_FN_IDX` / `createQueryResolver` call\n *, one resolver, one plugin function, kind+city both folded into the\n * query string it scans for). */\nexport type WeatherQueryKind = \"current\" | \"temperature\" | \"feelslike\" | \"high\" | \"low\";\n\n/**\n * Consume a free-form city name: one or more consecutive word tokens\n * (IDENT), joined with single spaces, \"New York\", \"Los Angeles\", \"Rio de\n * Janeiro\". Unlike `time/timezones/CityZones.ts`'s zone lookup, weather has\n * no bundled city table (Open-Meteo's geocoding API resolves arbitrary\n * names), so this consumes GREEDILY until it hits a non-word token\n * (operator, EOF, ...) rather than validating against a known list.\n *\n * Throws if no word token follows at all, \"weather in\" alone isn't a\n * usable query, and silently emitting an empty-string query would just\n * turn into a confusing \"no location found\" error one layer down instead\n * of a clear one here.\n */\nfunction consumeCityName(parser: Parser, triggerText: string): string {\n\tconst words: string[] = [];\n\twhile (parser.peek()?.type === \"IDENT\") {\n\t\twords.push(parser.consume().value);\n\t}\n\tif (words.length === 0) {\n\t\tthrow ErrorFactory.parsing(\n\t\t\t\"WEATHER_EXPECTED_CITY\",\n\t\t\t`Expected a city name after \"${triggerText}\" (e.g. \"${triggerText} London\")`,\n\t\t);\n\t}\n\treturn words.join(\" \");\n}\n\n/**\n * Shared parselet factory for all 5 weather query forms (`weather in`,\n * `temperature in`, `feels like in`, `high in`, `low in`), triggered on\n * the phrase-fused `WEATHER_IN`/`TEMPERATURE_IN`/`FEELS_LIKE_IN`/`HIGH_IN`/\n * `LOW_IN` tokens (see WeatherPackage.ts's `phrases` field). \"weather\",\n * \"temperature\", \"high\", \"low\" are all plausible bare variable names (a\n * real regression happened this session from claiming a common word as a\n * bare keyword. See MathPhrasesPackage.ts's \"total\" note), so every\n * trigger here is the FULL fused phrase, never a bare word, mirroring\n * `time/parselets/TimeInZoneParselet.ts`'s identical reasoning for\n * \"time in\"/\"date in\".\n */\nexport function weatherQueryParselet(kind: WeatherQueryKind, pluginFnName: string): PrefixParselet {\n\treturn {\n\t\tcategory: \"Weather\",\n\t\tparse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n\t\t\tconst city = consumeCityName(parser, token.value);\n\t\t\tbuilder.emitOpcode(OpCode.PUSH_STRING);\n\t\t\tbuilder.emitString(`${kind}:${city}`);\n\t\t\tbuilder.emitPluginCall(pluginFnName, 1);\n\t\t},\n\t};\n}\n","/**\n * WMO (\"World Meteorological Organization\") weather interpretation codes,\n * as returned by Open-Meteo's `weather_code` field\n * (https://open-meteo.com/en/docs, \"WMO Weather interpretation codes\").\n *\n * This is a standard, documented, provider-agnostic code scheme (Open-Meteo\n * didn't invent it, it just surfaces it), the same codes appear in METAR/\n * SYNOP reporting more broadly. Not every possible WMO code has a distinct\n * Open-Meteo meaning; this table only covers the subset Open-Meteo actually\n * emits, confirmed against its docs rather than the full WMO 4677 table.\n */\nexport const WMO_WEATHER_DESCRIPTIONS: Record<number, string> = {\n\t0: \"clear sky\",\n\t1: \"mainly clear\",\n\t2: \"partly cloudy\",\n\t3: \"overcast\",\n\t45: \"fog\",\n\t48: \"depositing rime fog\",\n\t51: \"light drizzle\",\n\t53: \"moderate drizzle\",\n\t55: \"dense drizzle\",\n\t56: \"light freezing drizzle\",\n\t57: \"dense freezing drizzle\",\n\t61: \"slight rain\",\n\t63: \"moderate rain\",\n\t65: \"heavy rain\",\n\t66: \"light freezing rain\",\n\t67: \"heavy freezing rain\",\n\t71: \"slight snow fall\",\n\t73: \"moderate snow fall\",\n\t75: \"heavy snow fall\",\n\t77: \"snow grains\",\n\t80: \"slight rain showers\",\n\t81: \"moderate rain showers\",\n\t82: \"violent rain showers\",\n\t85: \"slight snow showers\",\n\t86: \"heavy snow showers\",\n\t95: \"thunderstorm\",\n\t96: \"thunderstorm with slight hail\",\n\t99: \"thunderstorm with heavy hail\",\n};\n\n/**\n * Map a WMO weather code to a short human-readable description.\n * Falls back to a generic label (rather than throwing) for any code\n * Open-Meteo might add in the future that isn't in the table yet\n * an unrecognized code shouldn't break the whole weather query.\n */\nexport function describeWeatherCode(code: number): string {\n\treturn WMO_WEATHER_DESCRIPTIONS[code] ?? `weather code ${code}`;\n}\n","import { createTimeoutSignal } from \"@solve-js/utilities/TimeoutSignal\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\nimport { describeWeatherCode } from \"./WmoWeatherCodes\";\n\n/**\n * Error codes for this package. Co-located with the package rather than\n * unioned into `errors/ErrorCode.ts`'s core catalog (that catalog is scoped\n * to the parser/VM/engine/errors/config/lexer layers, not yet the ~17\n * domain packages. See that file's module doc for the intended per-package\n * pattern this follows).\n */\nexport const WeatherErrorCodes = {\n\t/** Open-Meteo's geocoding endpoint returned a non-OK HTTP status. */\n\tGEOCODING_API_ERROR: \"WEATHER_GEOCODING_API_ERROR\",\n\t/** Geocoding succeeded (200 OK) but matched no place for the given name, most likely a typo in the city, not an API failure. */\n\tCITY_NOT_FOUND: \"WEATHER_CITY_NOT_FOUND\",\n\t/** Open-Meteo's forecast endpoint returned a non-OK HTTP status. */\n\tFORECAST_API_ERROR: \"WEATHER_FORECAST_API_ERROR\",\n\t/** Forecast endpoint returned 200 OK but the response body is missing the expected current/daily blocks, an API contract violation, not a local bug. */\n\tFORECAST_RESPONSE_MALFORMED: \"WEATHER_FORECAST_RESPONSE_MALFORMED\",\n\t/** WeatherPackage.ts's fetchQuery switch fell through to its default case, unreachable via this package's own parselets, an internal invariant violation if it ever happens. */\n\tUNKNOWN_QUERY_KIND: \"WEATHER_UNKNOWN_QUERY_KIND\",\n} as const;\n\n/**\n * Open-Meteo (https://open-meteo.com), chosen because it's genuinely free\n * and keyless (no signup, no API key, no rate-limit tier to configure) for\n * both geocoding and forecast data, unlike every stock-quote / knowledge-\n * answer API surveyed for the sibling `stocks`/`knowledge` packages. This\n * is why Weather can be a default `BUILTIN_PACKAGES` member while those two\n * are opt-in-only. See `WeatherPackage.ts`'s module doc.\n */\nconst GEOCODING_API_URL = \"https://geocoding-api.open-meteo.com/v1/search\";\nconst FORECAST_API_URL = \"https://api.open-meteo.com/v1/forecast\";\n\n/** Hard timeout for each Open-Meteo HTTP call (geocoding or forecast). */\nconst FETCH_TIMEOUT_MS = 10_000;\n\n/**\n * Current conditions for one place, as returned by Open-Meteo.\n *\n * Temperatures are Celsius at this layer; conversion to the display unit\n * happens later, so the shape stays independent of formatting settings.\n */\nexport interface CityWeather {\n\t/** The resolved place name Open-Meteo matched, e.g. \"London\" for input \"london\". */\n\tresolvedName: string;\n\t/** WMO weather code. See WmoWeatherCodes.ts. */\n\tweatherCode: number;\n\t/** Human-readable description derived from weatherCode, e.g. \"overcast\". */\n\tdescription: string;\n\t/** Current temperature, Celsius. */\n\ttemperature: number;\n\t/** \"Feels like\" temperature, Celsius. */\n\tapparentTemperature: number;\n\t/** Today's forecast high, Celsius. */\n\thigh: number;\n\t/** Today's forecast low, Celsius. */\n\tlow: number;\n}\n\ninterface GeocodingResult {\n\tlatitude: number;\n\tlongitude: number;\n\tname: string;\n}\n\nasync function geocodeCity(city: string, signal: AbortSignal): Promise<GeocodingResult> {\n\tconst url = `${GEOCODING_API_URL}?name=${encodeURIComponent(city)}&count=1&language=en&format=json`;\n\tconst response = await fetch(url, { signal });\n\tif (!response.ok) {\n\t\tthrow ErrorFactory.external(\n\t\t\tWeatherErrorCodes.GEOCODING_API_ERROR,\n\t\t\t`Open-Meteo geocoding API returned ${response.status} for \"${city}\"`,\n\t\t\t{ city, status: response.status },\n\t\t);\n\t}\n\tconst json = await response.json();\n\tconst first = json?.results?.[0];\n\tif (!first) {\n\t\tthrow ErrorFactory.validation(\n\t\t\tWeatherErrorCodes.CITY_NOT_FOUND,\n\t\t\t`No location found for \"${city}\"`,\n\t\t\t{ city },\n\t\t);\n\t}\n\treturn { latitude: first.latitude, longitude: first.longitude, name: first.name };\n}\n\nasync function fetchForecast(lat: number, lon: number, signal: AbortSignal): Promise<{\n\tweatherCode: number;\n\ttemperature: number;\n\tapparentTemperature: number;\n\thigh: number;\n\tlow: number;\n}> {\n\tconst params = new URLSearchParams({\n\t\tlatitude: String(lat),\n\t\tlongitude: String(lon),\n\t\tcurrent: \"temperature_2m,apparent_temperature,weather_code\",\n\t\tdaily: \"temperature_2m_max,temperature_2m_min\",\n\t\ttimezone: \"auto\",\n\t\tforecast_days: \"1\",\n\t});\n\tconst response = await fetch(`${FORECAST_API_URL}?${params.toString()}`, { signal });\n\tif (!response.ok) {\n\t\tthrow ErrorFactory.external(\n\t\t\tWeatherErrorCodes.FORECAST_API_ERROR,\n\t\t\t`Open-Meteo forecast API returned ${response.status}`,\n\t\t\t{ status: response.status },\n\t\t);\n\t}\n\tconst json = await response.json();\n\tconst current = json?.current;\n\tconst daily = json?.daily;\n\tif (!current || !daily) {\n\t\tthrow ErrorFactory.external(\n\t\t\tWeatherErrorCodes.FORECAST_RESPONSE_MALFORMED,\n\t\t\t\"Open-Meteo forecast response missing current/daily blocks\",\n\t\t);\n\t}\n\treturn {\n\t\tweatherCode: current.weather_code,\n\t\ttemperature: current.temperature_2m,\n\t\tapparentTemperature: current.apparent_temperature,\n\t\thigh: daily.temperature_2m_max?.[0],\n\t\tlow: daily.temperature_2m_min?.[0],\n\t};\n}\n\n/**\n * In-flight + short-TTL result cache, keyed by lowercased city name.\n *\n * `createQueryResolver` (see `WeatherPackage.ts`) caches per EXACT query\n * string, and this package's query strings are `\"<kind>:<city>\"` (kind is\n * one of current/temperature/feelslike/high/low), so \"weather in london\"\n * and \"temperature in london\" are DIFFERENT cache entries under TanStack\n * Query, even though both need the exact same underlying Open-Meteo\n * response. Without this module-level cache, a note with several weather\n * queries for the same city would trigger one real HTTP round-trip\n * (geocode + forecast) PER kind, PER city, five real fetches for what is\n * conceptually one answer.\n *\n * This cache is intentionally short-lived (60s), it exists purely to\n * coalesce near-simultaneous multi-kind lookups for the same city, not to\n * replace `createQueryResolver`'s own `staleTimeMs` (which governs how\n * long a RESOLVED value is considered fresh, tuned per WeatherPackage.ts's\n * \"refreshes during the day\" reasoning). After 60s this entry expires and\n * the next lookup (of any kind) re-fetches from Open-Meteo, but by then\n * TanStack Query's own per-kind cache is usually still serving the\n * previously resolved Value anyway.\n */\nconst cityWeatherCache = new Map<string, { promise: Promise<CityWeather>; expiresAt: number }>();\nconst CACHE_TTL_MS = 60_000;\n\n/**\n * Resolve a city name to current conditions + today's forecast high/low via\n * Open-Meteo's geocoding + forecast APIs (two sequential HTTP calls). See\n * the module doc for why results are also coalesced in `cityWeatherCache`.\n */\nexport function fetchCityWeather(city: string, signal: AbortSignal): Promise<CityWeather> {\n\tconst key = city.trim().toLowerCase();\n\tconst now = Date.now();\n\tconst cached = cityWeatherCache.get(key);\n\tif (cached && cached.expiresAt > now) {\n\t\treturn cached.promise;\n\t}\n\n\tconst { signal: fetchSignal, cleanup } = createTimeoutSignal(signal, FETCH_TIMEOUT_MS, \"Open-Meteo weather query\");\n\n\tconst promise = (async () => {\n\t\ttry {\n\t\t\tconst geo = await geocodeCity(city, fetchSignal);\n\t\t\tconst forecast = await fetchForecast(geo.latitude, geo.longitude, fetchSignal);\n\t\t\treturn {\n\t\t\t\tresolvedName: geo.name,\n\t\t\t\tweatherCode: forecast.weatherCode,\n\t\t\t\tdescription: describeWeatherCode(forecast.weatherCode),\n\t\t\t\ttemperature: forecast.temperature,\n\t\t\t\tapparentTemperature: forecast.apparentTemperature,\n\t\t\t\thigh: forecast.high,\n\t\t\t\tlow: forecast.low,\n\t\t\t};\n\t\t} finally {\n\t\t\tcleanup();\n\t\t}\n\t})();\n\n\tcityWeatherCache.set(key, { promise, expiresAt: now + CACHE_TTL_MS });\n\t// A failed lookup shouldn't poison the cache for the full TTL, evict\n\t// immediately on rejection so the next query (of any kind) gets a\n\t// fresh attempt instead of replaying the same failure for 60s.\n\tpromise.catch(() => {\n\t\tconst entry = cityWeatherCache.get(key);\n\t\tif (entry?.promise === promise) {\n\t\t\tcityWeatherCache.delete(key);\n\t\t}\n\t});\n\n\treturn promise;\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { pluginFunctionIndexFor } from \"@solve-js/vm/VMBuiltins\";\nimport { createQueryResolver } from \"@solve-js/resolvers/QueryResolver\";\nimport { stringValue, uomValue, type Value } from \"@solve-js/vm/Value\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\nimport { weatherQueryParselet } from \"./parselets/WeatherQueryParselet\";\nimport { fetchCityWeather, WeatherErrorCodes } from \"./OpenMeteoClient\";\n\n/**\n * This package's descriptor name. The engine derives each plugin function's\n * `CALL_PLUGIN` registry index from `${package name}:${function name}` at\n * registration (see `pluginFunctionIndexFor` / `ExpressionEngine.registerPackage`),\n * so the same string has to serve both the `name` field and the index\n * derivation below, or the async resolver would watch a different index than\n * the parselets emit.\n */\nconst WEATHER_PACKAGE_NAME = \"solve-weather\";\n\n/**\n * The single package-local plugin-function name every weather form emits. All\n * 5 forms fold their kind into the pushed query string (see the module doc\n * below), so one name, one handler, and one registry index serve them all.\n */\nconst WEATHER_FN_NAME = \"weather\";\n\n/**\n * Live weather data, `weather in <city>`, `temperature in <city>`\n * `feels like in <city>`, `high in <city>`, `low in <city>`.\n *\n * Provider: **Open-Meteo** (https://open-meteo.com), free, keyless, no\n * signup, a real production-grade forecast API (used by this exact\n * geocoding + forecast shape, confirmed against the live API rather than\n * guessed field names. See `OpenMeteoClient.ts`). This is the reason\n * Weather ships as a default `BUILTIN_PACKAGES` member while the sibling\n * `stocks`/`knowledge` packages do not: those domains have no equivalent\n * free/keyless provider, so they're opt-in-only and require a host to\n * plug in a fetch function (see `packages/stocks/StocksPackage.ts` /\n * `packages/knowledge/KnowledgePackage.ts`). A host who doesn't want\n * Weather's default network calls can simply filter `WEATHER_PACKAGE` out\n * of the `packages` array passed to `ExpressionEngine`.\n *\n * All 5 forms share ONE `CALL_PLUGIN` index and ONE `createQueryResolver`\n * instance/namespace, the \"kind\" (current/temperature/feelslike/high/low)\n * is folded into the cached query STRING (`\"<kind>:<city>\"`, see\n * `parselets/WeatherQueryParselet.ts`), not encoded as a separate plugin\n * function. This keeps the async-resolver plumbing to exactly the shape\n * `createQueryResolver` was built for (one query string in, one `Value`\n * out) while still letting \"weather in london\" and \"temperature in\n * london\" cache independently (each is its own query string, so a\n * TanStack Query cache hit for one doesn't answer the other), the actual\n * underlying HTTP round-trip is separately coalesced/short-cached across\n * kinds in `OpenMeteoClient.ts`'s `fetchCityWeather()` (see its module\n * doc), so typing several weather queries for the same city doesn't\n * multiply real network calls by 5.\n *\n * `staleTimeMs` is set to 10 minutes: SoulverCore's own docs describe\n * weather values \"automatically updating during the day\" rather than\n * being pinned to when the note was written. Open-Meteo's underlying\n * forecast model itself refreshes roughly hourly, so anything from a few\n * minutes to an hour is defensible; 10 minutes is picked as a middle\n * ground that feels \"live\" in an interactive notepad without re-querying\n * Open-Meteo on every keystroke re-evaluation.\n */\nconst WEATHER_FN_IDX = pluginFunctionIndexFor(`${WEATHER_PACKAGE_NAME}:${WEATHER_FN_NAME}`);\n\nconst { resolver: weatherResolver, pluginFunction: weatherPluginFunction } = createQueryResolver({\n\tnamespace: \"weather\",\n\tpluginFunctionIndex: WEATHER_FN_IDX,\n\tstaleTimeMs: 10 * 60 * 1000, // 10 minutes — see module doc above\n\tfetchQuery: async (query: string, signal: AbortSignal): Promise<Value> => {\n\t\tconst sep = query.indexOf(\":\");\n\t\tconst kind = sep === -1 ? query : query.slice(0, sep);\n\t\tconst city = sep === -1 ? \"\" : query.slice(sep + 1);\n\n\t\tconst data = await fetchCityWeather(city, signal);\n\n\t\tswitch (kind) {\n\t\t\tcase \"current\":\n\t\t\t\treturn stringValue(`${Math.round(data.temperature)}°C, ${data.description}`);\n\t\t\tcase \"temperature\":\n\t\t\t\treturn uomValue(data.temperature, \"C\");\n\t\t\tcase \"feelslike\":\n\t\t\t\treturn uomValue(data.apparentTemperature, \"C\");\n\t\t\tcase \"high\":\n\t\t\t\treturn uomValue(data.high, \"C\");\n\t\t\tcase \"low\":\n\t\t\t\treturn uomValue(data.low, \"C\");\n\t\t\tdefault:\n\t\t\t\t// Unreachable via this package's own parselets (kind is always\n\t\t\t\t// one of the 5 above), an honest error rather than a guessed\n\t\t\t\t// value if some other bytecode ever pushes a malformed query.\n\t\t\t\tthrow ErrorFactory.internal(\n\t\t\t\t\tWeatherErrorCodes.UNKNOWN_QUERY_KIND,\n\t\t\t\t\t`Unknown weather query kind \"${kind}\"`,\n\t\t\t\t\t{ kind },\n\t\t\t\t);\n\t\t}\n\t},\n});\n\n/**\n * Weather lookups through Open-Meteo, a keyless public endpoint.\n *\n * The one built-in package that reaches the network. It needs no configuration,\n * which is why it can be a default where stocks and knowledge cannot, but a\n * host wanting no outbound traffic should assemble its own package list rather\n * than using {@link BUILTIN_PACKAGES}.\n */\nexport const WEATHER_PACKAGE: IEnginePackage = {\n\tname: WEATHER_PACKAGE_NAME,\n\n\tphrases: {\n\t\t\"weather in\": \"WEATHER_IN\",\n\t\t\"temperature in\": \"TEMPERATURE_IN\",\n\t\t\"feels like in\": \"FEELS_LIKE_IN\",\n\t\t\"high in\": \"HIGH_IN\",\n\t\t\"low in\": \"LOW_IN\",\n\t},\n\n\tprefixParselets: {\n\t\tWEATHER_IN: weatherQueryParselet(\"current\", WEATHER_FN_NAME),\n\t\tTEMPERATURE_IN: weatherQueryParselet(\"temperature\", WEATHER_FN_NAME),\n\t\tFEELS_LIKE_IN: weatherQueryParselet(\"feelslike\", WEATHER_FN_NAME),\n\t\tHIGH_IN: weatherQueryParselet(\"high\", WEATHER_FN_NAME),\n\t\tLOW_IN: weatherQueryParselet(\"low\", WEATHER_FN_NAME),\n\t},\n\n\tpluginFunctions: {\n\t\t[WEATHER_FN_NAME]: weatherPluginFunction,\n\t},\n\n\tasyncResolvers: [weatherResolver],\n};\n","import { Parser } from \"@solve-js/parser/Parser\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/**\n * Self-contained date-phrase parser for the Stocks package's `on <date>`\n * suffix (`stock(AAPL) on April 12, 2005`, `AAPL close on 2005-04-12`, ...).\n *\n * This does NOT reuse the engine's general-purpose `DATETIME_LITERAL`\n * work (date-only numeric literals in DD/MM/YYYY, MM-DD-YYYY, YYYY-MM-DD,\n * DD.MM.YYYY, `packages/datetime/normalizer/DateLiteralNormalizerRule.ts`\n * ported from the former `feat/safety-limits-datetime-literals` branch)\n * this is a smaller, independent grammar scoped to exactly what a\n * stock-history query needs: month-name dates (the task's own worked\n * example, \"April 12, 2005\") plus SLASH/MINUS-separated numeric dates. Two\n * DELIBERATE differences from that general-purpose rule's design, both\n * narrowing scope rather than risking a subtle bug:\n *\n * - **4-digit years only.** No 2-digit-year pivot logic (the general rule's\n * `strptime(\"%y\")`-style 00-68/69-99 split), a stock historical lookup\n * realistically always names a 4-digit year, and skipping the pivot\n * removes a whole class of ambiguity to test.\n * - **SLASH is MM/DD/YYYY (US), not DD/MM/YYYY (European).** The\n * general-purpose rule picked European for the engine's date\n * literal; this package is scoped to US-listed tickers (NASDAQ/NYSE), so\n * US-style slash dates match user expectation better here. Documented\n * explicitly because it's the OPPOSITE convention from the other rule\n * do not assume they agree if this is ever unified with that work.\n *\n * MINUS follows the same \"4-digit first group -> ISO, else US\" rule the\n * general-purpose rule uses, and every candidate is validated by\n * constructing a real `Date` and checking the components didn't roll over\n * (e.g. a claimed \"Feb 30\"). Same lesson as that rule's own doc comment:\n * this is what makes the parse safe against malformed input, not a\n * decoration.\n */\n\nconst MONTH_NAMES: Record<string, number> = {\n\tjan: 0, january: 0,\n\tfeb: 1, february: 1,\n\tmar: 2, march: 2,\n\tapr: 3, april: 3,\n\tmay: 4,\n\tjun: 5, june: 5,\n\tjul: 6, july: 6,\n\taug: 7, august: 7,\n\tsep: 8, sept: 8, september: 8,\n\toct: 9, october: 9,\n\tnov: 10, november: 10,\n\tdec: 11, december: 11,\n};\n\n/**\n * A date phrase recognised inside a stock query, normalised to ISO.\n *\n * Carries the original text alongside the parsed date so an error can quote\n * what the user actually wrote rather than the interpretation.\n */\nexport interface ParsedDatePhrase {\n\t/** ISO calendar date, e.g. \"2005-04-12\". */\n\tisoDate: string;\n\t/** The original token text, reassembled, for error messages/diagnostics. */\n\traw: string;\n}\n\nfunction pad2(n: number): string {\n\treturn n < 10 ? `0${n}` : String(n);\n}\n\n/** Construct a real Date and confirm year/month/day didn't roll over (e.g. Feb 30 -> Mar 2). */\nfunction validateAndFormat(year: number, monthIndex: number, day: number, raw: string): ParsedDatePhrase {\n\tconst d = new Date(year, monthIndex, day);\n\tif (d.getFullYear() !== year || d.getMonth() !== monthIndex || d.getDate() !== day) {\n\t\tthrow ErrorFactory.parsing(\n\t\t\t\"STOCKS_INVALID_DATE\",\n\t\t\t`\"${raw}\" is not a valid calendar date`,\n\t\t);\n\t}\n\treturn { isoDate: `${year}-${pad2(monthIndex + 1)}-${pad2(day)}`, raw };\n}\n\nfunction requireYear(text: string, raw: string): number {\n\tif (!/^\\d{4}$/.test(text)) {\n\t\tthrow ErrorFactory.parsing(\n\t\t\t\"STOCKS_INVALID_DATE\",\n\t\t\t`Expected a 4-digit year in \"${raw}\", got \"${text}\"`,\n\t\t);\n\t}\n\treturn parseInt(text, 10);\n}\n\n/**\n * Attempt to consume a date phrase from the parser at the current\n * position. Returns `null` (consuming NOTHING) if the next token doesn't\n * look like the start of any supported date shape, so callers can decide\n * how to react (this helper is speculative only up to that first check\n * once it commits past the first token, a malformed continuation is a\n * hard parse error, not a silent `null`, matching\n * `time/parselets/shared/ZoneReference.ts`'s same \"commit once confident\"\n * discipline).\n */\nexport function tryParseDatePhrase(parser: Parser): ParsedDatePhrase | null {\n\tconst first = parser.peek();\n\tif (!first) return null;\n\n\t// Already fused into a date literal by the datetime package's normalizer,\n\t// which is what \"April 12, 2005\" now becomes before the parser sees it.\n\t// Taking the fused token is strictly better than re-deriving the date from\n\t// its parts: it has already been validated against the calendar.\n\tif (first.type === \"DATETIME_LITERAL\") {\n\t\tparser.consume();\n\t\tconst date = new Date(Number(first.value));\n\t\tif (Number.isNaN(date.getTime())) return null;\n\t\treturn {\n\t\t\tisoDate: `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`,\n\t\t\traw: first.text ?? first.value,\n\t\t};\n\t}\n\n\t// ── Numeric SLASH/MINUS form: NUMBER SEP NUMBER SEP NUMBER ──\n\t// or \"12 April 2005\" (NUMBER IDENT [COMMA] NUMBER), both start with a\n\t// NUMBER, disambiguated below by what follows it. Consume-then-check is\n\t// safe here (rather than needing 2-token lookahead, which `Parser`\n\t// doesn't expose): every branch below either completes a valid date or\n\t// throws, there's no alternative parse to back out to once a NUMBER is\n\t// found in this position.\n\tif (first.type === \"NUMBER\") {\n\t\tconst g1 = parser.consume();\n\t\tconst sepTok = parser.peek();\n\t\tif (sepTok && (sepTok.type === \"SLASH\" || sepTok.type === \"MINUS\")) {\n\t\t\tconst sepType = sepTok.type;\n\t\t\tparser.consume();\n\t\t\tconst g2 = parser.consume(\"NUMBER\");\n\t\t\tparser.consume(sepType);\n\t\t\tconst g3 = parser.consume(\"NUMBER\");\n\t\t\tconst raw = `${g1.value}${sepType === \"SLASH\" ? \"/\" : \"-\"}${g2.value}${sepType === \"SLASH\" ? \"/\" : \"-\"}${g3.value}`;\n\n\t\t\tif (sepType === \"SLASH\") {\n\t\t\t\t// US convention: MM/DD/YYYY. See module doc.\n\t\t\t\tconst month = parseInt(g1.value, 10);\n\t\t\t\tconst day = parseInt(g2.value, 10);\n\t\t\t\tconst year = requireYear(g3.value, raw);\n\t\t\t\treturn validateAndFormat(year, month - 1, day, raw);\n\t\t\t} else {\n\t\t\t\t// MINUS: ISO YYYY-MM-DD if the first group is exactly 4 digits, else US MM-DD-YYYY.\n\t\t\t\tif (/^\\d{4}$/.test(g1.value)) {\n\t\t\t\t\tconst year = parseInt(g1.value, 10);\n\t\t\t\t\tconst month = parseInt(g2.value, 10);\n\t\t\t\t\tconst day = parseInt(g3.value, 10);\n\t\t\t\t\treturn validateAndFormat(year, month - 1, day, raw);\n\t\t\t\t}\n\t\t\t\tconst month = parseInt(g1.value, 10);\n\t\t\t\tconst day = parseInt(g2.value, 10);\n\t\t\t\tconst year = requireYear(g3.value, raw);\n\t\t\t\treturn validateAndFormat(year, month - 1, day, raw);\n\t\t\t}\n\t\t}\n\n\t\t// ── \"12 April 2005\", NUMBER IDENT [COMMA] NUMBER ──\n\t\tconst monthTok = parser.peek();\n\t\tif (monthTok && monthTok.type === \"IDENT\" && MONTH_NAMES[monthTok.value.toLowerCase()] !== undefined) {\n\t\t\tconst monthIndex = MONTH_NAMES[monthTok.value.toLowerCase()];\n\t\t\tparser.consume(); // month name\n\t\t\tif (parser.peek()?.type === \"COMMA\") parser.consume();\n\t\t\tconst yearTok = parser.consume(\"NUMBER\");\n\t\t\tconst raw = `${g1.value} ${monthTok.value} ${yearTok.value}`;\n\t\t\tconst year = requireYear(yearTok.value, raw);\n\t\t\treturn validateAndFormat(year, monthIndex, parseInt(g1.value, 10), raw);\n\t\t}\n\n\t\tthrow ErrorFactory.parsing(\n\t\t\t\"STOCKS_INVALID_DATE\",\n\t\t\t`Expected a date after \"on\" (e.g. \"April 12, 2005\" or \"4/12/2005\"), got \"${g1.value}\"`,\n\t\t);\n\t}\n\n\t// ── \"April 12, 2005\" / \"April 12 2005\", IDENT NUMBER [COMMA] NUMBER ──\n\tif (first.type === \"IDENT\" && MONTH_NAMES[first.value.toLowerCase()] !== undefined) {\n\t\tconst monthIndex = MONTH_NAMES[first.value.toLowerCase()];\n\t\tparser.consume(); // month name\n\t\tconst dayTok = parser.consume(\"NUMBER\");\n\t\tif (parser.peek()?.type === \"COMMA\") parser.consume();\n\t\tconst yearTok = parser.consume(\"NUMBER\");\n\t\tconst raw = `${first.value} ${dayTok.value}, ${yearTok.value}`;\n\t\tconst year = requireYear(yearTok.value, raw);\n\t\treturn validateAndFormat(year, monthIndex, parseInt(dayTok.value, 10), raw);\n\t}\n\n\treturn null;\n}\n","/**\n * Small, bundled allow-list of well-known US-listed ticker symbols.\n *\n * This exists ONLY to safely gate the OPT-IN bare-ticker grammar (\"AAPL\"\n * alone, no `stock(...)` wrapper). See `StocksPackage.ts`'s\n * `enableBareTickerRecognition` option and\n * `normalizer/StockTickerNormalizerRule.ts`. A bare all-caps 1-5 letter\n * word is genuinely ambiguous with a variable name (`:AAPL = 5` is a\n * perfectly reasonable thing for a user to write), so this package never\n * treats EVERY uppercase word as a ticker, only exact (case-sensitive)\n * matches against this bundled list, the same mitigation\n * `time/timezones/CityZones.ts` uses for city names. Deliberately not\n * exhaustive (real ticker universes run into the tens of thousands);\n * additive, extend as gaps are found, no other code needs to change.\n */\nexport const MAJOR_TICKERS: ReadonlySet<string> = new Set([\n\t// ── Big tech ──\n\t\"AAPL\", \"MSFT\", \"GOOGL\", \"GOOG\", \"AMZN\", \"META\", \"NVDA\", \"TSLA\", \"NFLX\",\n\t\"ADBE\", \"CRM\", \"ORCL\", \"IBM\", \"INTC\", \"AMD\", \"CSCO\", \"PYPL\", \"UBER\",\n\t\"ABNB\", \"SPOT\", \"SHOP\", \"SQ\", \"COIN\", \"PLTR\", \"SNOW\", \"ZM\",\n\t// ── Finance ──\n\t\"JPM\", \"V\", \"MA\", \"BAC\", \"WFC\", \"GS\", \"MS\", \"AXP\",\n\t// ── Consumer / retail ──\n\t\"WMT\", \"COST\", \"HD\", \"NKE\", \"SBUX\", \"MCD\", \"KO\", \"PEP\", \"DIS\", \"TGT\",\n\t// ── Industrial / energy / auto ──\n\t\"BA\", \"GE\", \"F\", \"GM\", \"XOM\", \"CVX\",\n\t// ── Healthcare ──\n\t\"PFE\", \"JNJ\", \"UNH\", \"MRNA\",\n\t// ── Semis / hardware (international ADRs) ──\n\t\"TSM\", \"ASML\", \"BABA\",\n]);\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport { tokenTypeId } from \"@solve-js/lexer/Token\";\nimport { LexerToken } from \"@solve-js/lexer/ExpressionLexer\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { MAJOR_TICKERS } from \"../MajorTickers\";\n\n/**\n * Token type a recognised ticker symbol is rewritten to.\n *\n * Exported because the parselet that consumes it registers against this exact\n * string, and a typo would silently produce a rule nothing matches.\n */\nexport const STOCK_TICKER_TYPE = \"STOCK_TICKER\";\n/** Interned numeric id for {@link STOCK_TICKER_TYPE}, for hot-path comparison. */\nexport const STOCK_TICKER_TYPE_ID = tokenTypeId(STOCK_TICKER_TYPE);\n\n/**\n * Bare-ticker recognition (stretch goal), fuses an IDENT token into a\n * `STOCK_TICKER` token ONLY when its raw text is an EXACT (case-sensitive)\n * match against the small bundled `MAJOR_TICKERS` allow-list, mirroring\n * `time/timezones/CityZones.ts`'s \"known table, not a blanket character-\n * class rule\" mitigation for the exact same class of problem (a bare word\n * that's also a plausible variable name).\n *\n * Only registered when `StocksPackageConfig.enableBareTickerRecognition`\n * is true. See StocksPackage.ts. Two safeguards beyond the allow-list\n * itself:\n *\n * - **Case-sensitive.** \"AAPL\" fuses; \"aapl\"/\"Aapl\" do not, lowercase or\n * mixed-case spellings stay ordinary identifiers/variable reads.\n * - **Colon-prefix guard.** `:AAPL = 5` is left alone (not fused) so a\n * host that also uses a ticker-shaped word as a variable name can still\n * declare it. Same reasoning as `VariableParselet.ts`'s keyword-shaped-\n * name policy, applied here even though tickers aren't lexer keywords.\n *\n * This does NOT eliminate the ambiguity (a bare, no-colon `AAPL` read as a\n * previously-declared variable is genuinely indistinguishable from a\n * ticker query without deeper context), it narrows the blast radius from\n * \"every uppercase word\" to \"~50 specific, well-known symbols,\" which is\n * the documented trade-off `enableBareTickerRecognition` asks a host to\n * accept explicitly, opt-in on top of an already-opt-in package.\n */\nexport function stockTickerNormalizerRule(): NormalizerRule {\n\treturn {\n\t\tname: \"stocks:bare-ticker\",\n\t\tpriority: 20,\n\t\tmatch(tokens: Token[], pos: number): NormalizerMatch | null {\n\t\t\tconst token = tokens[pos];\n\t\t\tif (!token || token.type !== \"IDENT\") return null;\n\t\t\tif (!MAJOR_TICKERS.has(token.value)) return null;\n\t\t\tif (pos > 0 && tokens[pos - 1].type === \"COLON\") return null;\n\n\t\t\tconst fusedToken = new LexerToken(\n\t\t\t\tSTOCK_TICKER_TYPE,\n\t\t\t\tSTOCK_TICKER_TYPE_ID,\n\t\t\t\ttoken.value,\n\t\t\t\ttoken.value,\n\t\t\t\ttoken.offset,\n\t\t\t\t0,\n\t\t\t\ttoken.line,\n\t\t\t\ttoken.col,\n\t\t\t);\n\n\t\t\treturn { consumed: 1, replacement: [fusedToken], ruleName: \"stocks:bare-ticker\" };\n\t\t},\n\t};\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\nimport { stripQuotes } from \"@solve-js/utilities/Strings\";\nimport { tryParseDatePhrase, type ParsedDatePhrase } from \"../DatePhrase\";\nimport { STOCK_TICKER_TYPE } from \"../normalizer/StockTickerNormalizerRule\";\n\ninterface DateSuffix {\n\tfield: \"close\" | \"volume\";\n\tdate: ParsedDatePhrase;\n}\n\n/**\n * Optional `on <date>` / `close on <date>` / `volume on <date>` suffix,\n * shared by both the `stock(TICKER)` function-call form and the bare-\n * ticker stretch-goal form. See StocksPackage.ts's module doc for why\n * only the FIRST is always reachable.\n *\n * Returns `null` (consuming nothing) if no suffix is present at all\n * `stock(AAPL)` alone is a complete, valid expression. Once \"close\",\n * \"volume\", or \"on\" IS seen, the rest of the suffix is required; a\n * malformed continuation is a parse error, not a silent fallback to the\n * current-price form (which would misreport a failed historical lookup\n * as a live quote).\n */\nfunction tryParseDateSuffix(parser: Parser): DateSuffix | null {\n\tconst next = parser.peek();\n\tif (!next || next.type !== \"IDENT\") return null;\n\n\tconst lower = next.value.toLowerCase();\n\tlet field: \"close\" | \"volume\" = \"close\";\n\n\tif (lower === \"close\" || lower === \"volume\") {\n\t\tfield = lower;\n\t\tparser.consume();\n\t\tconst onTok = parser.peek();\n\t\tif (!onTok || onTok.type !== \"IDENT\" || onTok.value.toLowerCase() !== \"on\") {\n\t\t\tthrow ErrorFactory.parsing(\n\t\t\t\t\"STOCKS_EXPECTED_ON\",\n\t\t\t\t`Expected \"on <date>\" after \"${lower}\" (e.g. \"AAPL ${lower} on April 12, 2005\")`,\n\t\t\t);\n\t\t}\n\t\tparser.consume();\n\t} else if (lower === \"on\") {\n\t\tparser.consume();\n\t} else {\n\t\treturn null;\n\t}\n\n\tconst date = tryParseDatePhrase(parser);\n\tif (!date) {\n\t\tthrow ErrorFactory.parsing(\n\t\t\t\"STOCKS_EXPECTED_DATE\",\n\t\t\t`Expected a date after \"on\" (e.g. \"April 12, 2005\" or \"4/12/2005\")`,\n\t\t);\n\t}\n\treturn { field, date };\n}\n\n/**\n * Emit either the current-price query (`CALL_PLUGIN currentFnIdx`, query =\n * just the ticker) or the historical query (`CALL_PLUGIN historicalFnIdx`,\n * query = `\"<field>:<ticker>:<isoDate>\"`). See StocksPackage.ts for why\n * these are two separate `createQueryResolver` instances (different\n * staleTimes: live quotes vs. immutable historical closes).\n */\nfunction emitStockQuery(\n\tbuilder: BytecodeBuilder,\n\tticker: string,\n\tsuffix: DateSuffix | null,\n\tcurrentFn: string,\n\thistoricalFn: string,\n): void {\n\tbuilder.emitOpcode(OpCode.PUSH_STRING);\n\tif (!suffix) {\n\t\tbuilder.emitString(ticker);\n\t\tbuilder.emitPluginCall(currentFn, 1);\n\t} else {\n\t\tbuilder.emitString(`${suffix.field}:${ticker}:${suffix.date.isoDate}`);\n\t\tbuilder.emitPluginCall(historicalFn, 1);\n\t}\n}\n\n/**\n * Finish compiling a stock expression once the ticker itself has been\n * resolved (from either `stock(TICKER)` or a fused bare `STOCK_TICKER`\n * token), checks for the `on <date>` / `close on <date>` / `volume on\n * <date>` suffix and emits the right query, sharing this tail between\n * both parselets below.\n *\n * **The phantom-STAR problem**: `normalizer/BuiltinNormalizerRules.ts`'s\n * generic `implicitMultiplyRule` inserts a `STAR` between any `RPAREN`\n * (or, for the bare-ticker form, any word-shaped token) and a following\n * bare word, it has no way to know \"on\"/\"close\"/\"volume\" are reserved by\n * THIS package's grammar rather than an implicit multiplicand (the same\n * ambiguity its own `canStart`-phrase guard exists for, just not\n * reachable from here without registering \"on\"/\"close\"/\"volume\" as\n * globally-visible phrase-start words, which would carry the same\n * variable-name collision risk this package works hard to avoid\n * elsewhere). So: peek for a STAR first. If one is NOT immediately\n * followed by our suffix, it's a real multiplication (either the\n * implicit-multiply artifact against some OTHER right-hand expression, or\n * the user genuinely typed `stock(AAPL) * 2`), since the STAR has\n * already been consumed here, the outer Pratt loop can no longer dispatch\n * it as an infix operator, so this finishes compiling the multiplication\n * itself rather than silently dropping the operator.\n */\nfunction finishStockExpression(\n\tparser: Parser,\n\tbuilder: BytecodeBuilder,\n\tticker: string,\n\tcurrentFn: string,\n\thistoricalFn: string,\n): void {\n\tlet sawStar = false;\n\tif (parser.peek()?.type === \"STAR\") {\n\t\tparser.consume();\n\t\tsawStar = true;\n\t}\n\n\tconst suffix = tryParseDateSuffix(parser);\n\n\tif (sawStar && !suffix) {\n\t\t// Real multiplication, not our suffix, finish it here since the\n\t\t// STAR is already consumed and can't go back to the outer loop.\n\t\temitStockQuery(builder, ticker, null, currentFn, historicalFn);\n\t\tparser.parseExpression(BindingPower.Product, builder);\n\t\tbuilder.emitOpcode(OpCode.MUL);\n\t\treturn;\n\t}\n\n\temitStockQuery(builder, ticker, suffix, currentFn, historicalFn);\n}\n\n/**\n * `stock(TICKER)` / `stock(\"TICKER\")`, the PRIMARY, always-reachable\n * stocks syntax (see StocksPackage.ts's module doc: bare tickers are\n * ambiguous with variable names, so this function-call form is the one\n * form that works regardless of `enableBareTickerRecognition`). Triggered\n * on the `STOCK_FN` keyword token (see StocksPackage.ts's `lexerVocabulary`).\n *\n * Accepts either a quoted ticker (`stock(\"AAPL\")`) or a bare identifier\n * inside the parens (`stock(AAPL)`), unambiguous either way, since the\n * parens make this unmistakably a function call, not a variable read\n * (mirrors `examples/osrs/OsrsParselet.ts`'s `ge(\"Iron Axe\")` /\n * `osrs.ge(Iron Axe)` dual acceptance for the same reason).\n */\nexport function stockFnParselet(currentFn: string, historicalFn: string): PrefixParselet {\n\treturn {\n\t\tcategory: \"Stocks\",\n\t\tparse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n\t\t\tparser.consume(\"LPAREN\");\n\t\t\tconst tickerToken = parser.consume();\n\t\t\tlet ticker: string;\n\t\t\tif (tickerToken.type === \"STRING\") {\n\t\t\t\tticker = stripQuotes(tickerToken.value).toUpperCase();\n\t\t\t} else if (tickerToken.type === \"IDENT\" || tickerToken.type === STOCK_TICKER_TYPE) {\n\t\t\t\tticker = tickerToken.value.toUpperCase();\n\t\t\t} else {\n\t\t\t\tthrow ErrorFactory.parsing(\n\t\t\t\t\t\"STOCKS_INVALID_TICKER\",\n\t\t\t\t\t`Expected a ticker symbol inside stock(...), got \"${tickerToken.value}\" (${tickerToken.type})`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tparser.consume(\"RPAREN\");\n\n\t\t\tfinishStockExpression(parser, builder, ticker, currentFn, historicalFn);\n\t\t},\n\t};\n}\n\n/**\n * Bare-ticker stretch-goal form (`AAPL` alone, no `stock(...)` wrapper)\n * only reachable when `StocksPackageConfig.enableBareTickerRecognition`\n * is true, which registers `StockTickerNormalizerRule` to fuse a KNOWN\n * ticker (from the bundled `MAJOR_TICKERS` allow-list, never a blanket\n * \"any uppercase word\" rule) into a `STOCK_TICKER` token; this parselet\n * handles that fused token.\n */\nexport function bareStockTickerParselet(currentFn: string, historicalFn: string): PrefixParselet {\n\treturn {\n\t\tcategory: \"Stocks\",\n\t\tparse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n\t\t\tconst ticker = token.value.toUpperCase();\n\t\t\tfinishStockExpression(parser, builder, ticker, currentFn, historicalFn);\n\t\t},\n\t};\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { pluginFunctionIndexFor } from \"@solve-js/vm/VMBuiltins\";\nimport { createQueryResolver } from \"@solve-js/resolvers/QueryResolver\";\nimport { errorValue, numberValue, uomValue, type Value } from \"@solve-js/vm/Value\";\nimport { stockFnParselet, bareStockTickerParselet } from \"./parselets/StockParselet\";\nimport { stockTickerNormalizerRule, STOCK_TICKER_TYPE } from \"./normalizer/StockTickerNormalizerRule\";\nimport type { StocksPackageConfig } from \"./types\";\n\n/** Package name, the qualifier for every `${PACKAGE_NAME}:${fn}` registry lookup below. */\nconst PACKAGE_NAME = \"solve-stocks\";\n\n/**\n * Package-local plugin-function names, the identity each parselet emits\n * (`builder.emitPluginCall`) and this descriptor keys `pluginFunctions` by.\n * `stock` is the live current-price lookup; `stockhistorical` the dated\n * close/volume lookup, two functions with two resolvers (see the module doc\n * below for why they are kept separate).\n */\nconst CURRENT_FN = \"stock\";\nconst HISTORICAL_FN = \"stockhistorical\";\n\n/**\n * Live stock prices, `stock(TICKER)`, `stock(TICKER) on <date>`\n * `stock(TICKER) close on <date>`, `stock(TICKER) volume on <date>`, plus\n * an opt-in bare-ticker form (`AAPL`, `AAPL on April 12, 2005`, ...).\n *\n * **Why a factory, not a constant `STOCKS_PACKAGE` export**: unlike\n * Weather's Open-Meteo, there is no free/keyless stock-quote API, every\n * option surveyed (Alpha Vantage, Finnhub, Twelve Data, IEX Cloud, ...)\n * requires the HOST application to sign up for its own API key. Baking in\n * a specific paid provider (or worse, a hardcoded key) would either not\n * work out of the box for most hosts or silently commit them to a vendor\n * choice they didn't make. Instead this package is an extension point\n * \"packages are our approach, we're providing an SDK\", a host supplies\n * `fetchQuote`/`fetchHistoricalQuote` (backed by whichever provider and\n * key THEY have) via {@link createStocksPackage}'s `config` argument. No\n * config -> every stock expression resolves to a clearly-worded\n * `STOCKS_NOT_CONFIGURED` error `Value`, never a faked or zero price (see\n * `packages/finance/parselets/SalesTaxParselet.ts`'s doc comment for the\n * same \"never guess a number the caller didn't provide\" principle applied\n * to a different package).\n *\n * **Not a member of `BUILTIN_PACKAGES`** (see `packages/builtins.ts`)\n * unconfigured, this package does nothing useful, exactly like\n * `examples/osrs` is deliberately excluded from the built-in set. A host\n * that wants it calls `createStocksPackage({ fetchQuote, ... })` and adds\n * the result to their `ExpressionEngine`'s `packages` array themselves.\n *\n * **Ticker recognition**: the function-call form `stock(TICKER)` is the\n * PRIMARY, always-reachable syntax, a bare all-caps word (\"AAPL\") is\n * genuinely ambiguous with a variable name (`:AAPL = 5` is a reasonable\n * thing to write), so it is never claimed unconditionally. The bare form\n * is available only via `config.enableBareTickerRecognition`, gated to a\n * small bundled allow-list of major tickers (`MajorTickers.ts`). See\n * `normalizer/StockTickerNormalizerRule.ts`'s doc comment for the full\n * reasoning, mirroring `time/timezones/CityZones.ts`'s known-table\n * mitigation for the same class of ambiguity.\n *\n * **Two separate async resolvers** (`stocks-current`/`stocks-historical`,\n * two distinct `CALL_PLUGIN` indices) rather than one shared one, unlike\n * Weather's single shared resolver, current-price and historical-close\n * lookups warrant genuinely different `staleTimeMs` (a live quote goes\n * stale in seconds; a historical close for a fixed past date never goes\n * stale at all), and `createQueryResolver` bakes `staleTimeMs` into the\n * resolver instance, not the per-call query.\n */\nexport function createStocksPackage(config: StocksPackageConfig = {}): IEnginePackage {\n\tfunction notConfigured(what: string): Value {\n\t\treturn errorValue(\n\t\t\t\"STOCKS_NOT_CONFIGURED\",\n\t\t\t`Stock data provider not configured. Supply ${what} via createStocksPackage({ ... }); see the StocksPackage doc comment.`,\n\t\t);\n\t}\n\n\tconst { resolver: currentResolver, pluginFunction: currentPluginFunction } = createQueryResolver({\n\t\tnamespace: \"stocks-current\",\n\t\t// The engine assigns `stock` its registry index at registration; the\n\t\t// resolver's preflight still matches CALL_PLUGIN by that numeric index,\n\t\t// so watch the same engine-assigned slot the emit-by-name path produces.\n\t\tpluginFunctionIndex: pluginFunctionIndexFor(`${PACKAGE_NAME}:${CURRENT_FN}`),\n\t\tstaleTimeMs: config.staleTimeMs ?? 60_000, // 1 min — intraday quotes move continuously\n\t\t// Only the live current quote refreshes in the background, and only when\n\t\t// the host asked for it; a historical close (below) never does.\n\t\trefetchIntervalMs: config.refetchIntervalMs,\n\t\tfetchQuery: async (ticker: string, signal: AbortSignal): Promise<Value> => {\n\t\t\tif (!config.fetchQuote) return notConfigured(\"fetchQuote\");\n\t\t\tconst quote = await config.fetchQuote(ticker, signal);\n\t\t\treturn uomValue(quote.price, quote.currency ?? \"USD\");\n\t\t},\n\t});\n\n\tconst { resolver: historicalResolver, pluginFunction: historicalPluginFunction } = createQueryResolver({\n\t\tnamespace: \"stocks-historical\",\n\t\t// Same bridge as the current-price resolver above: watch the engine-\n\t\t// assigned index for `stockhistorical` so preflight matches the bytecode.\n\t\tpluginFunctionIndex: pluginFunctionIndexFor(`${PACKAGE_NAME}:${HISTORICAL_FN}`),\n\t\tstaleTimeMs: config.historicalStaleTimeMs ?? 30 * 24 * 60 * 60 * 1000, // 30 days — a past close never changes\n\t\tfetchQuery: async (query: string, signal: AbortSignal): Promise<Value> => {\n\t\t\tconst [field, ticker, isoDate] = query.split(\":\");\n\t\t\tif (!config.fetchHistoricalQuote) return notConfigured(\"fetchHistoricalQuote\");\n\t\t\tconst quote = await config.fetchHistoricalQuote(ticker, isoDate, signal);\n\t\t\tif (field === \"volume\") return numberValue(quote.volume ?? 0);\n\t\t\treturn uomValue(quote.close, quote.currency ?? \"USD\");\n\t\t},\n\t});\n\n\tconst pkg: IEnginePackage = {\n\t\tname: PACKAGE_NAME,\n\n\t\tlexerVocabulary: {\n\t\t\t// \"stock\" is claimed as a bare keyword (unlike Weather's phrase-\n\t\t\t// fused triggers). Same trade-off `examples/osrs/OsrsLexerVocabulary.ts`\n\t\t\t// accepts for \"ge\"/\"osrs\"/\"price\": acceptable ONLY because this\n\t\t\t// whole package is opt-in (never in BUILTIN_PACKAGES), so the risk\n\t\t\t// (\":stock = 5\" would collide if a host both uses that variable\n\t\t\t// name AND opts into this package) is scoped to hosts who\n\t\t\t// explicitly chose both. A host hitting this collision should pick\n\t\t\t// a different variable name; `stock(...)` itself never collides\n\t\t\t// since the LPAREN makes it unambiguous.\n\t\t\tkeywords: { stock: \"STOCK_FN\" },\n\t\t},\n\n\t\tprefixParselets: {\n\t\t\tSTOCK_FN: stockFnParselet(CURRENT_FN, HISTORICAL_FN),\n\t\t\t...(config.enableBareTickerRecognition\n\t\t\t\t? { [STOCK_TICKER_TYPE]: bareStockTickerParselet(CURRENT_FN, HISTORICAL_FN) }\n\t\t\t\t: {}),\n\t\t},\n\n\t\tnormalizerRules: config.enableBareTickerRecognition ? [stockTickerNormalizerRule()] : [],\n\n\t\tpluginFunctions: {\n\t\t\t[CURRENT_FN]: currentPluginFunction,\n\t\t\t[HISTORICAL_FN]: historicalPluginFunction,\n\t\t},\n\n\t\tasyncResolvers: [currentResolver, historicalResolver],\n\t};\n\n\treturn pkg;\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * Prefix parselet for the `KNOWLEDGE_QUERY` token, the token IS the\n * entire query, already extracted from the raw line text (everything\n * before a trailing `= ?`) by the lexer's `rawLinePatterns` hook (see\n * `LexerVocabulary.rawLinePatterns` in `ExpressionLexer.ts`, and\n * `KnowledgePackage.ts`'s `lexerVocabulary` for the pattern that produces\n * it). Structurally identical to `examples/osrs/OsrsParselet.ts`'s\n * `GameItemParselet` (one token in, `PUSH_STRING` + `CALL_PLUGIN` out)\n * the only thing architecturally unusual about this package is how the\n * token got made, not how it's parsed.\n */\nexport function knowledgeQueryParselet(pluginFnName: string): PrefixParselet {\n\treturn {\n\t\tcategory: \"Knowledge\",\n\t\tparse(_parser: Parser, token: Token, builder: BytecodeBuilder): void {\n\t\t\tbuilder.emitOpcode(OpCode.PUSH_STRING);\n\t\t\tbuilder.emitString(token.value);\n\t\t\tbuilder.emitPluginCall(pluginFnName, 1);\n\t\t},\n\t};\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { pluginFunctionIndexFor } from \"@solve-js/vm/VMBuiltins\";\nimport { createQueryResolver } from \"@solve-js/resolvers/QueryResolver\";\nimport { errorValue, stringValue, type Value } from \"@solve-js/vm/Value\";\nimport { knowledgeQueryParselet } from \"./parselets/KnowledgeQueryParselet\";\nimport type { KnowledgePackageConfig } from \"./types\";\n\n/**\n * Knowledge-assistant queries, open-ended questions answered by a\n * host-supplied provider. Two supported surface forms, both producing the\n * exact same `KNOWLEDGE_QUERY` token/behavior (see the two\n * `rawLinePatterns` entries below):\n *\n * - **`search: <query>` / `ask: <query>` / `google: <query>`** (preferred,\n * added this iteration), a clear, self-documenting leading verb, e.g.\n * `search: distance to the moon`. Reads like an instruction, not a\n * cryptic punctuation puzzle.\n * - **`<query> = ?`** (the original form, kept for Calca-style\n * compatibility. see the internal parity notes)\n * e.g. `distance to the moon = ?`. Less discoverable (a bare trailing\n * `= ?` doesn't read as \"ask a question\" the way a leading verb does),\n * but harmless to keep alongside the clearer form. This package has no\n * opinion about which one a host's users end up preferring.\n *\n * SoulverCore's own version of this feature calls out to Wolfram|Alpha;\n * there is no free equivalent of comparable quality, so. Same\n * pluggable-provider approach as `packages/stocks`, a host supplies\n * `answerQuery` via {@link createKnowledgePackage}'s `config` argument. No\n * config -> every query resolves to a clearly-worded\n * `KNOWLEDGE_NOT_CONFIGURED` error `Value`, never a hallucinated/guessed\n * answer.\n *\n * **Not a member of `BUILTIN_PACKAGES`**, unconfigured, this package does\n * nothing useful, exactly like `packages/stocks` and `examples/osrs`.\n *\n * ## Why this package is architecturally different from every other one\n *\n * Every other package in this codebase (including its sibling\n * `weather`/`stocks`) is \"structured syntax evaluates to a value\", the\n * grammar is known in advance, and the lexer/parser tokenize it like any\n * other expression. This package's grammar is \"arbitrary free text,\n * terminated (or introduced) by a fixed marker, gets shipped to an\n * external function verbatim\", `distance to the moon` is not valid Solve\n * syntax (it would never parse as arithmetic), so it can't be\n * tokenized-then-parsed the normal way at all.\n *\n * The fix lives one layer below the parser: `ExpressionLexer.ts` gained a\n * new, generic extension point, `LexerVocabulary.rawLinePatterns` (see\n * its doc comment there for the full design). A `rawLinePatterns` rule\n * tests the RAW line text, before any per-character tokenization, and\n * if it matches, the whole line becomes ONE synthetic token whose value\n * is the matched capture group, verbatim. This package is that\n * mechanism's reference/motivating use.\n *\n * **Why `search:`/`ask:`/`google:` require a literal trailing colon, not\n * just a following space**: without it, `search 5` (a line reading the\n * plain variable `search`, legitimately assignable via `:search = 5`\n * since these are ordinary lowercase words, not reserved, followed by\n * what would otherwise be implicit-multiply-adjacent text) would be\n * silently hijacked into a knowledge query for `\"5\"` instead of failing\n * or reading the variable. Requiring `:` immediately after the keyword\n * (`search:`, not `search `) is not valid syntax ANYWHERE else in this\n * grammar, so it introduces zero ambiguity with a real `:name = value`\n * variable of the same name. See the regression test guarding this\n * exact scenario.\n *\n * No existing Solve syntax uses a bare `= ?` marker either (the\n * codebase's other \"possibilities\" feature, `cm to ?`, is a different\n * token shape, `TO QUESTION`, not `EQUALS QUESTION`. See\n * `packages/uom/normalizer/PossibilitiesNormalizerRule.ts`), so neither\n * form claims any ambiguity with existing grammar; both only activate for\n * lines a host has opted into via this package in the first place.\n */\nexport function createKnowledgePackage(config: KnowledgePackageConfig = {}): IEnginePackage {\n\tconst packageName = \"solve-knowledge\";\n\t// This package's single plugin function. The parselet emits its CALL_PLUGIN by\n\t// this name, and the engine assigns the registry index at registration (keyed\n\t// by `${packageName}:${name}`). The async resolver below scans compiled\n\t// bytecode, which carries that numeric index rather than the name, so it must\n\t// watch the same slot: pluginFunctionIndexFor is stable and cached per\n\t// qualified name, so resolving it here yields exactly the index the engine\n\t// will later assign, in any registration order.\n\tconst fnName = \"knowledge\";\n\tconst fnIdx = pluginFunctionIndexFor(`${packageName}:${fnName}`);\n\n\tconst { resolver, pluginFunction } = createQueryResolver({\n\t\tnamespace: \"knowledge\",\n\t\tpluginFunctionIndex: fnIdx,\n\t\tstaleTimeMs: config.staleTimeMs ?? 5 * 60 * 1000,\n\t\tfetchQuery: async (query: string, signal: AbortSignal): Promise<Value> => {\n\t\t\tif (!config.answerQuery) {\n\t\t\t\treturn errorValue(\n\t\t\t\t\t\"KNOWLEDGE_NOT_CONFIGURED\",\n\t\t\t\t\t`Knowledge answer provider not configured. Supply answerQuery via createKnowledgePackage({ ... }); see the KnowledgePackage doc comment.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst answer = await config.answerQuery(query, signal);\n\t\t\treturn stringValue(answer);\n\t\t},\n\t});\n\n\treturn {\n\t\tname: packageName,\n\n\t\tlexerVocabulary: {\n\t\t\trawLinePatterns: [\n\t\t\t\t// Preferred: a clear leading verb + mandatory colon (see this\n\t\t\t\t// file's own doc comment for why the colon isn't optional).\n\t\t\t\t{ pattern: /^(?:search|ask|google)\\s*:\\s*(.+)$/i, tokenType: \"KNOWLEDGE_QUERY\" },\n\t\t\t\t// Original Calca-style trailing marker, kept for compatibility.\n\t\t\t\t{ pattern: /^(.+?)=\\s*\\?\\s*$/, tokenType: \"KNOWLEDGE_QUERY\" },\n\t\t\t],\n\t\t},\n\n\t\tprefixParselets: {\n\t\t\tKNOWLEDGE_QUERY: knowledgeQueryParselet(fnName),\n\t\t},\n\n\t\tpluginFunctions: {\n\t\t\t[fnName]: pluginFunction,\n\t\t},\n\n\t\tasyncResolvers: [resolver],\n\t};\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\n\n/**\n * `prev` -- the immediately-preceding line's cached result (Numi). Bare\n * keyword (see LinesPackage.ts's doc comment for the collision-risk\n * reasoning) resolving entirely at RUNTIME via `context.lineIndex` --\n * there's nothing to push onto the stack at parse time, the handler reads\n * \"which line am I on\" from the `LineExecutionContext` `CALL_PLUGIN`\n * threads through (see `vm/VM.ts`'s `LineExecutionContext`).\n */\nexport class PrevParselet implements PrefixParselet {\n readonly category = \"Lines\";\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n builder.emitPluginCall(\"prev\", 0);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * `line1` / `line 1` -- an arbitrary line's cached result by 1-based line\n * number (Notes Calculator, NumPad). Fused into a single `LINE_REF` token\n * by `LineRefNormalizerRule.ts`, carrying the parsed line number as\n * `token.value`.\n *\n * The line number is pushed via `PUSH_NUMBER`/`emitNumber()` (the\n * `Float64Array` constant pool) -- deliberately NOT `emitIndex()`, which\n * writes a single raw byte (0-255) directly into the opcode stream and\n * would silently break for any document past 255 lines.\n */\nexport class LineRefParselet implements PrefixParselet {\n readonly category = \"Lines\";\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n const lineNumber = parseInt(token.value, 10);\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(lineNumber);\n builder.emitPluginCall(\"lineRef\", 1);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * `sum(line 1 : line 4)` / `total(line 1 : line 4)` / `average(line 1 :\n * line 4)` (NumPad's documented range-aggregation syntax) -- trigger\n * token is `SUM_RANGE_CALL` (sum and total share one token/parselet\n * instance, per NumPad's own docs treating them as synonyms) or\n * `AVERAGE_RANGE_CALL`.\n *\n * Critical correctness point: `line 1`/`line 4` here denote RAW BOUNDS\n * (the integers 1 and 4), not \"evaluate line 1's value, evaluate line\n * 4's value\". This parselet deliberately does NOT call\n * `parser.parseExpression()` for the bounds -- that would route through\n * `LineRefParselet`, which fetches the referenced line's VALUE, not its\n * raw number. Instead it hand-consumes the constrained\n * `LPAREN LINE_REF COLON LINE_REF RPAREN` grammar directly, reading each\n * `LINE_REF` token's already-fused `.value` (its raw digit text) via\n * `parseInt`.\n *\n * The mid-expression `COLON` here is safe: `VariableParselet` is only\n * registered as a PREFIX parselet for `COLON` (`packages/variables/`),\n * so a `COLON` encountered in this infix/mid-expression position is never\n * intercepted by it -- the Pratt loop simply has no infix parselet for\n * `COLON`, letting this hand-written parselet consume it explicitly, the\n * same pattern `ClampParselet`/`IfThenElseParselet` already use for\n * grammars `PhrasePattern` can't express.\n */\nexport class RangeAggregateParselet implements PrefixParselet {\n readonly category = \"Lines\";\n constructor(private readonly isAverage: boolean) {}\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n parser.consume(\"LPAREN\");\n const fromToken = parser.consume(\"LINE_REF\");\n parser.consume(\"COLON\");\n const toToken = parser.consume(\"LINE_REF\");\n parser.consume(\"RPAREN\");\n\n const from = parseInt(fromToken.value, 10);\n const to = parseInt(toToken.value, 10);\n\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(from);\n builder.emitOpcode(OpCode.PUSH_NUMBER);\n builder.emitNumber(to);\n builder.emitPluginCall(this.isAverage ? \"averageRange\" : \"sumRange\", 2);\n }\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\n\n/**\n * `total above` / `sum above` / `average above` -- aggregate every line's\n * result from the immediately-preceding line backward, stopping at (not\n * including) the nearest blank line or `#` heading (Numbr's \"sum to\n * nearest header\", NumPad-adjacent, and SoulverCore's own\n * totals-and-subtotals feature, all the SAME underlying capability).\n *\n * Phrase-fused via `LinesPackage.ts`'s `phrases` field (\"total above\" /\n * \"sum above\" / \"average above\") -- a deliberate departure from Numi/\n * Numbr's bare \"total\"/\"sum\" wording, to avoid the exact bare-keyword\n * collision this codebase already regressed on once (see\n * `MathPhrasesPackage.ts`'s \"total\" note). \"total above\"/\"sum above\"\n * share one token type/parselet instance (synonyms, matching NumPad's/\n * Numbr's own treatment of sum and total).\n */\nexport class AboveAggregateParselet implements PrefixParselet {\n readonly category = \"Lines\";\n constructor(private readonly isAverage: boolean) {}\n\n parse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n builder.emitPluginCall(this.isAverage ? \"averageAbove\" : \"totalAbove\", 0);\n }\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport { tokenTypeId } from \"@solve-js/lexer/Token\";\nimport { LexerToken } from \"@solve-js/lexer/ExpressionLexer\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\n\nconst LINE_REF_TYPE_ID = tokenTypeId(\"LINE_REF\");\nconst SUM_RANGE_CALL_TYPE_ID = tokenTypeId(\"SUM_RANGE_CALL\");\nconst AVERAGE_RANGE_CALL_TYPE_ID = tokenTypeId(\"AVERAGE_RANGE_CALL\");\n\n/**\n * Fuses `line1` (glued, `ExpressionLexer.tokenizeIdentifier()` already\n * consumes trailing digits into one IDENT token, so \"line1\" lexes as a\n * SINGLE token, not two) and `line 1` (spaced, two tokens, IDENT \"line\"\n * + NUMBER) into one `LINE_REF` token carrying the parsed line number as\n * its `.value`.\n *\n * Deliberately does NOT implement the `l1`/`l 1` short alias documented\n * by some competitor apps (Notes Calculator, NumPad), `l` is one of the\n * most common short variable names in this category of app (length,\n * etc.); shipping `line<N>` alone first and adding a narrower `l<N>`\n * form later, once real usage confirms it's wanted, is the documented\n * v1 scope decision (see the internal parity notes).\n *\n * Colon-prefix guard: never fires immediately after a variable-definition\n * `COLON`, so `:line1 = 5` / `:line = 5` (a variable named \"line1\"/\"line\")\n * stay completely untouched, matches every other trigger-word guard\n * already established this session (e.g. `StockTickerNormalizerRule.ts`).\n *\n * That guard can't be the blanket \"any preceding COLON\" check the stocks\n * precedent uses, though: `rangeCallNormalizerRule()`'s OWN grammar,\n * `sum(line 1 : line 4)`, ALSO puts a bare COLON directly before the\n * second `line 4`, as a range separator, not a variable sigil. A blanket\n * guard blocks that second fusion, leaving `line 4` as raw\n * `IDENT NUMBER` tokens and breaking `RangeAggregateParselet`, which\n * expects `LINE_REF COLON LINE_REF`. The fix: only suppress fusion when\n * the COLON is genuinely in a variable-definition position, which\n * since nothing else in this codebase's grammar ever puts a COLON\n * directly after a LINE_REF, is precisely and only NOT the case when the\n * COLON itself is immediately preceded by an already-fused `LINE_REF`\n * (our own range separator). The normalizer's multi-pass loop guarantees\n * the first `line 1` → `LINE_REF` fusion has already landed in an\n * earlier pass by the time this check runs on the second occurrence.\n */\nexport function lineRefNormalizerRule(): NormalizerRule {\n\treturn {\n\t\tname: \"lines:line-ref\",\n\t\tpriority: 80,\n\t\tmatch(tokens: Token[], pos: number): NormalizerMatch | null {\n\t\t\tif (pos > 0 && tokens[pos - 1].type === \"COLON\") {\n\t\t\t\tconst isRangeSeparator = pos > 1 && tokens[pos - 2].type === \"LINE_REF\";\n\t\t\t\tif (!isRangeSeparator) return null;\n\t\t\t}\n\t\t\tconst token = tokens[pos];\n\t\t\tif (!token) return null;\n\n\t\t\t// Glued form: \"line1\" is already a single IDENT token by the time\n\t\t\t// it reaches the normalizer.\n\t\t\tif (token.type === \"IDENT\") {\n\t\t\t\tconst m = /^line(\\d+)$/i.exec(token.value);\n\t\t\t\tif (m) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tconsumed: 1,\n\t\t\t\t\t\treplacement: [makeLineRefToken(m[1], token)],\n\t\t\t\t\t\truleName: \"lines:line-ref\",\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Spaced form: IDENT \"line\" + NUMBER, two separate tokens.\n\t\t\tif (token.type === \"IDENT\" && /^line$/i.test(token.value)) {\n\t\t\t\tconst next = tokens[pos + 1];\n\t\t\t\tif (next && next.type === \"NUMBER\" && /^\\d+$/.test(next.value)) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tconsumed: 2,\n\t\t\t\t\t\treplacement: [makeLineRefToken(next.value, token)],\n\t\t\t\t\t\truleName: \"lines:line-ref\",\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn null;\n\t\t},\n\t};\n}\n\nfunction makeLineRefToken(lineNumberText: string, sourceToken: Token): Token {\n\treturn new LexerToken(\n\t\t\"LINE_REF\",\n\t\tLINE_REF_TYPE_ID,\n\t\tlineNumberText,\n\t\tlineNumberText,\n\t\tsourceToken.offset,\n\t\t0,\n\t\tsourceToken.line,\n\t\tsourceToken.col,\n\t);\n}\n\n/**\n * Fuses `sum(`/`total(`/`average(`, but ONLY when the bare word is\n * IMMEDIATELY followed by `LPAREN`, into a single `SUM_RANGE_CALL`/\n * `AVERAGE_RANGE_CALL` token (sum and total share one token type/parselet,\n * since `sum(...)`/`total(...)` are documented as synonyms. See Numbr's\n * and NumPad's own docs).\n *\n * The lookahead-on-LPAREN guard is what keeps this safe: `:sum = 100`\n * (no following paren) is untouched, and MathPhrasesPackage's existing\n * `\"total of X, Y, Z\"` phrase (no paren after \"of\") is a structurally\n * different, non-conflicting shape, confirmed by reading\n * `MathPhrasesPackage.ts` before adding this rule.\n */\nexport function rangeCallNormalizerRule(): NormalizerRule {\n\treturn {\n\t\tname: \"lines:range-call\",\n\t\tpriority: 80,\n\t\tmatch(tokens: Token[], pos: number): NormalizerMatch | null {\n\t\t\tconst token = tokens[pos];\n\t\t\tif (!token || token.type !== \"IDENT\") return null;\n\t\t\tconst word = token.value.toLowerCase();\n\t\t\tif (word !== \"sum\" && word !== \"total\" && word !== \"average\") return null;\n\t\t\tif (tokens[pos + 1]?.type !== \"LPAREN\") return null;\n\n\t\t\tconst tokenType = word === \"average\" ? \"AVERAGE_RANGE_CALL\" : \"SUM_RANGE_CALL\";\n\t\t\tconst typeId = word === \"average\" ? AVERAGE_RANGE_CALL_TYPE_ID : SUM_RANGE_CALL_TYPE_ID;\n\t\t\treturn {\n\t\t\t\tconsumed: 1,\n\t\t\t\treplacement: [\n\t\t\t\t\tnew LexerToken(tokenType, typeId, token.value, token.value, token.offset, 0, token.line, token.col),\n\t\t\t\t],\n\t\t\t\truleName: \"lines:range-call\",\n\t\t\t};\n\t\t},\n\t};\n}\n","import { Value, ValueType, numberValue, uomValue, errorValue } from \"@solve-js/vm/Value\";\nimport type { LineExecutionContext } from \"@solve-js/vm/VM\";\n\n/**\n * Cross-line data access, `prev`, `line<N>`, `sum(line X : line Y)`\n * `total(line X : line Y)`, `average(line X : line Y)`, and `total above`/\n * `sum above`/`average above`.\n *\n * Every handler here follows the SAME short-circuit discipline, which is\n * the actual load-bearing correctness requirement of this whole package\n * (not the line-number arithmetic, which is trivial): `Value.toNumber()`\n * returns `0` for BOTH `Pending` and `Error` types (confirmed in\n * `vm/Value.ts`), so any handler that skipped this check would silently\n * compute a wrong number instead of surfacing a clear error the moment it\n * touched an unresolved-async or already-errored line, exactly the\n * failure class `ARCHITECTURE.md` §12 P0 item 1 (still open) describes,\n * and exactly the class of bug this codebase treats as its worst. See\n * `checkLineValue()` below, every handler routes through it.\n */\n\nfunction checkLineValue(v: Value | undefined, lineNumber: number): Value | null {\n if (v === undefined) {\n return errorValue(\"LINE_NOT_YET_EVALUATED\", `Line ${lineNumber} has not been evaluated yet (forward reference, or out of range)`);\n }\n if (v.type === ValueType.Pending) {\n return errorValue(\"LINE_RESULT_PENDING\", `Line ${lineNumber}'s result is still resolving`);\n }\n if (v.type === ValueType.Error) {\n return errorValue(\"LINE_RESULT_ERROR\", `Line ${lineNumber} has an error`);\n }\n return null; // no problem — safe to use v\n}\n\nfunction requireContext(context: LineExecutionContext | undefined): Value | null {\n if (!context?.getLineResult) {\n return errorValue(\"LINE_REF_NO_DOCUMENT\", \"Cross-line references require a real document — not available outside one (e.g. evaluateExpression()'s single-expression path)\");\n }\n return null;\n}\n\n/** `prev`, the immediately-preceding line's cached result. */\n/**\n * The immediately preceding line's cached result.\n *\n * @param _args - Unused. `prev` takes no arguments.\n * @param context - Per-line execution context. Supplies the document's\n * cached results; without it the handler returns a LINES_NO_CONTEXT error\n * rather than guessing, since line references are meaningless outside a\n * document.\n * @returns The computed Value, or an error Value when the context is\n * missing or a referenced line has no numeric result.\n */\nexport function prevHandler(_args: Value[], context?: LineExecutionContext): Value {\n const ctxError = requireContext(context);\n if (ctxError) return ctxError;\n const targetLine = context!.lineIndex - 1;\n const v = context!.getLineResult!(targetLine);\n const err = checkLineValue(v, targetLine);\n return err ?? v!;\n}\n\n/** `line<N>` / `line N`, an arbitrary line's cached result by 1-based number. */\n/**\n * An arbitrary line's cached result, by one-based line number.\n *\n * @param args - A single Number holding the target line number.\n * @param context - Per-line execution context. Supplies the document's\n * cached results; without it the handler returns a LINES_NO_CONTEXT error\n * rather than guessing, since line references are meaningless outside a\n * document.\n * @returns The computed Value, or an error Value when the context is\n * missing or a referenced line has no numeric result.\n */\nexport function lineRefHandler(args: Value[], context?: LineExecutionContext): Value {\n const ctxError = requireContext(context);\n if (ctxError) return ctxError;\n const targetLine = args[0].toNumber();\n const v = context!.getLineResult!(targetLine);\n const err = checkLineValue(v, targetLine);\n return err ?? v!;\n}\n\n/**\n * Shared range-walk for `sum(line X : line Y)` / `total(...)` /\n * `average(...)`. Restricted to plain `Number`/`Uom` values of the SAME\n * measure, errors on `String`/`Boolean`/`Datetime` rather than silently\n * coercing via `.toNumber()` (which would, e.g., turn a Datetime into an\n * epoch-ms number and silently \"sum\" timestamps together).\n */\nfunction aggregateRange(from: number, to: number, context: LineExecutionContext, isAverage: boolean): Value {\n let total = 0;\n let count = 0;\n let commonUnit: string | undefined;\n let sawUom = false;\n const step = from <= to ? 1 : -1;\n for (let n = from; step > 0 ? n <= to : n >= to; n += step) {\n const v = context.getLineResult!(n);\n const err = checkLineValue(v, n);\n if (err) return err;\n if (v!.type !== ValueType.Number && v!.type !== ValueType.Uom) {\n return errorValue(\"LINE_RANGE_NON_NUMERIC\", `Line ${n} is not a plain number or unit value — cannot include it in a sum/total/average range`);\n }\n if (v!.type === ValueType.Uom) {\n sawUom = true;\n if (commonUnit === undefined) commonUnit = v!.unit;\n else if (v!.unit !== commonUnit) {\n return errorValue(\"LINE_RANGE_MIXED_UNITS\", `Line ${n}'s unit (${v!.unit}) doesn't match the range's first unit (${commonUnit}) — aggregating mixed units would silently misrepresent the result`);\n }\n }\n total += v!.toNumber();\n count++;\n }\n const result = isAverage ? total / count : total;\n return sawUom ? uomValue(result, commonUnit!) : numberValue(result);\n}\n\n/** `sum(line X : line Y)` / `total(line X : line Y)`. */\n/**\n * Sum of the numeric results across an inclusive line range.\n *\n * @param args - Two Numbers: the first and last line of the range.\n * @param context - Per-line execution context. Supplies the document's\n * cached results; without it the handler returns a LINES_NO_CONTEXT error\n * rather than guessing, since line references are meaningless outside a\n * document.\n * @returns The computed Value, or an error Value when the context is\n * missing or a referenced line has no numeric result.\n */\nexport function sumRangeHandler(args: Value[], context?: LineExecutionContext): Value {\n const ctxError = requireContext(context);\n if (ctxError) return ctxError;\n return aggregateRange(args[0].toNumber(), args[1].toNumber(), context!, false);\n}\n\n/** `average(line X : line Y)`. */\n/**\n * Mean of the numeric results across an inclusive line range.\n *\n * @param args - Two Numbers: the first and last line of the range.\n * @param context - Per-line execution context. Supplies the document's\n * cached results; without it the handler returns a LINES_NO_CONTEXT error\n * rather than guessing, since line references are meaningless outside a\n * document.\n * @returns The computed Value, or an error Value when the context is\n * missing or a referenced line has no numeric result.\n */\nexport function averageRangeHandler(args: Value[], context?: LineExecutionContext): Value {\n const ctxError = requireContext(context);\n if (ctxError) return ctxError;\n return aggregateRange(args[0].toNumber(), args[1].toNumber(), context!, true);\n}\n\n/**\n * `total above` / `sum above` / `average above`, aggregate every line's\n * result from the current line's immediate predecessor backward, stopping\n * at (not including) the nearest blank line or `#` heading.\n */\nfunction aggregateAbove(context: LineExecutionContext, isAverage: boolean): Value {\n const boundaryCheck = context.isLineBoundary;\n if (!boundaryCheck) {\n return errorValue(\"LINE_REF_NO_DOCUMENT\", \"\\\"above\\\" aggregation requires a real document\");\n }\n let total = 0;\n let count = 0;\n let commonUnit: string | undefined;\n let sawUom = false;\n for (let n = context.lineIndex - 1; n >= 1; n--) {\n if (boundaryCheck(n)) break;\n const v = context.getLineResult!(n);\n const err = checkLineValue(v, n);\n if (err) return err;\n if (v!.type !== ValueType.Number && v!.type !== ValueType.Uom) {\n return errorValue(\"LINE_RANGE_NON_NUMERIC\", `Line ${n} is not a plain number or unit value — cannot include it in \"above\" aggregation`);\n }\n if (v!.type === ValueType.Uom) {\n sawUom = true;\n if (commonUnit === undefined) commonUnit = v!.unit;\n else if (v!.unit !== commonUnit) {\n return errorValue(\"LINE_RANGE_MIXED_UNITS\", `Line ${n}'s unit (${v!.unit}) doesn't match the range's unit (${commonUnit})`);\n }\n }\n total += v!.toNumber();\n count++;\n }\n if (count === 0) return errorValue(\"LINE_RANGE_EMPTY\", \"No lines above to aggregate (hit the top of the document, a blank line, or a heading immediately)\");\n const result = isAverage ? total / count : total;\n return sawUom ? uomValue(result, commonUnit!) : numberValue(result);\n}\n\n/** `total above`, every numeric result on lines before this one. */\n/**\n * Sum of every numeric result on lines above this one.\n *\n * @param _args - Unused. `total above` takes no arguments.\n * @param context - Per-line execution context. Supplies the document's\n * cached results; without it the handler returns a LINES_NO_CONTEXT error\n * rather than guessing, since line references are meaningless outside a\n * document.\n * @returns The computed Value, or an error Value when the context is\n * missing or a referenced line has no numeric result.\n */\nexport function totalAboveHandler(_args: Value[], context?: LineExecutionContext): Value {\n const ctxError = requireContext(context);\n if (ctxError) return ctxError;\n return aggregateAbove(context!, false);\n}\n\n/** `average above`, the mean of every numeric result before this line. */\n/**\n * Mean of every numeric result on lines above this one.\n *\n * @param _args - Unused. `average above` takes no arguments.\n * @param context - Per-line execution context. Supplies the document's\n * cached results; without it the handler returns a LINES_NO_CONTEXT error\n * rather than guessing, since line references are meaningless outside a\n * document.\n * @returns The computed Value, or an error Value when the context is\n * missing or a referenced line has no numeric result.\n */\nexport function averageAboveHandler(_args: Value[], context?: LineExecutionContext): Value {\n const ctxError = requireContext(context);\n if (ctxError) return ctxError;\n return aggregateAbove(context!, true);\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { PrevParselet } from \"./parselets/PrevParselet\";\nimport { LineRefParselet } from \"./parselets/LineRefParselet\";\nimport { RangeAggregateParselet } from \"./parselets/RangeAggregateParselet\";\nimport { AboveAggregateParselet } from \"./parselets/AboveAggregateParselet\";\nimport { lineRefNormalizerRule, rangeCallNormalizerRule } from \"./normalizer/LineRefNormalizerRule\";\nimport {\n prevHandler, lineRefHandler, sumRangeHandler, averageRangeHandler,\n totalAboveHandler, averageAboveHandler,\n} from \"./LinesPluginFunctions\";\n\n/**\n * Cross-line data access, reading another line's cached result from\n * inside an expression. Confirmed by FOUR independent competitor apps\n * wanting the exact same underlying capability (Numi's `prev`, Notes\n * Calculator's `line<N>`, Numbr's `sum`/`total`-to-header, NumPad's\n * `line<N>` plus range aggregation). See the internal parity notes'\n * \"Confirmed engine limitations\" item 1, now closed.\n *\n * Built entirely on `vm/VM.ts`'s `LineExecutionContext`, threaded\n * optionally through `CALL_PLUGIN`, every handler here explicitly checks\n * for `Pending`/`Error`/unevaluated lines before doing arithmetic (see\n * `LinesPluginFunctions.ts`'s module doc) rather than silently coercing\n * via `.toNumber()` (which returns `0` for both `Pending` and `Error`).\n *\n * Trigger-word collision decisions (this codebase's established\n * phrase-fusion-vs-bare-keyword policy, see `ARCHITECTURE.md` §5.1):\n * - `prev`, bare keyword (nothing to phrase-fuse against, same shape as\n * `clamp`). Accepted risk.\n * - `line1`/`line 1`, normalizer-fused into `LINE_REF`, never claims\n * bare `line` as a keyword (`:line = 5` stays untouched). The `l1`/`l 1`\n * short alias documented by some competitors is deliberately NOT\n * implemented in this pass, `l` is too common a variable name; ship\n * `line<N>` first, add a narrower `l<N>` form later if real usage wants\n * it.\n * - `sum(`/`total(`/`average(`, normalizer-fused ONLY when immediately\n * followed by `LPAREN`, so `:sum = 100` and MathPhrases' existing\n * `\"total of X, Y\"` phrase (no paren after \"of\") are both unaffected.\n * - \"aggregate everything above until a blank line/heading\"\n * phrase-fused as `\"total above\"`/`\"sum above\"`/`\"average above\"`\n * (deliberately NOT Numi/Numbr's bare `total`/`sum` wording, that's\n * exactly the bare-keyword collision class this codebase already\n * regressed on once, see `MathPhrasesPackage.ts`'s \"total\" note).\n */\nexport const LINES_PACKAGE: IEnginePackage = {\n name: \"solve-lines\",\n lexerVocabulary: {\n keywords: { prev: \"PREV\" },\n },\n phrases: {\n \"total above\": \"TOTAL_ABOVE\",\n \"sum above\": \"SUM_ABOVE\",\n \"average above\": \"AVERAGE_ABOVE\",\n },\n normalizerRules: [lineRefNormalizerRule(), rangeCallNormalizerRule()],\n prefixParselets: {\n PREV: new PrevParselet(),\n LINE_REF: new LineRefParselet(),\n SUM_RANGE_CALL: new RangeAggregateParselet(false),\n AVERAGE_RANGE_CALL: new RangeAggregateParselet(true),\n TOTAL_ABOVE: new AboveAggregateParselet(false),\n SUM_ABOVE: new AboveAggregateParselet(false),\n AVERAGE_ABOVE: new AboveAggregateParselet(true),\n },\n pluginFunctions: {\n prev: prevHandler,\n lineRef: lineRefHandler,\n sumRange: sumRangeHandler,\n averageRange: averageRangeHandler,\n totalAbove: totalAboveHandler,\n averageAbove: averageAboveHandler,\n },\n};\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\n\n/**\n * `sum of column \"cost\" in table above` and its siblings (`total`/`average`/\n * `mean`/`min`/`max`/`count`/`median` of column ...).\n *\n * The leading verb-and-noun is phrase-fused by `TablesPackage.ts` into one\n * trigger token (`... of column`), so by the time this runs only the column\n * name and an optional address remain. The grammar is:\n *\n * <trigger> STRING [ in [the] ] [ table ] [ above ]\n *\n * The address words are consumed and discarded: the only table a column can be\n * read from in this slice is the nearest one above, so `above`, `table above`,\n * `in table above` and a bare `\"name\"` all resolve the same way. Consumption\n * stops at the first token that is not an address word, so\n * `sum of column \"cost\" above + 100` still adds 100 to the total.\n *\n * Hand-written rather than built on `definePhrasePattern` for the same reason\n * as the math-phrase parselets: once the verb is fused into the trigger, the\n * next slot is a value (the string), not a keyword, which that builder cannot\n * express.\n */\nexport class ColumnAggregateParselet implements PrefixParselet {\n readonly category = \"Tables\";\n\n constructor(private readonly pluginFnName: string) {}\n\n parse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n const next = parser.peek();\n if (!next || next.type !== \"STRING\") {\n throw ErrorFactory.parsing(\n \"TABLE_COLUMN_NAME_EXPECTED\",\n `Expected a quoted column name after \"... of column\" but got ${next ? `\"${next.value}\"` : \"end of input\"}`,\n );\n }\n const nameToken = parser.consume(\"STRING\");\n\n // Discard an optional address: \"in\", \"the\", \"table\", \"above\", in any of\n // the orders those words are naturally written. IN is the locale keyword\n // token for \"in\"; the rest lex as plain identifiers.\n while (true) {\n const t = parser.peek();\n if (!t) break;\n if (t.type === \"IN\") {\n parser.consume();\n continue;\n }\n if (t.type === \"IDENT\" && ADDRESS_WORDS.has(t.value.toLowerCase())) {\n parser.consume();\n continue;\n }\n break;\n }\n\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(nameToken.value);\n builder.emitPluginCall(this.pluginFnName, 1);\n }\n}\n\n/** Trailing words that only address the table and carry no other meaning here. */\nconst ADDRESS_WORDS = new Set([\"the\", \"table\", \"above\"]);\n","/**\n * Pure markdown-table reading, no engine or Value types involved.\n *\n * A markdown table is the one block the evaluator sees and skips, so its\n * numbers cannot be totalled from where they already sit. These helpers turn\n * the raw source lines of a leading-pipe table back into a header row and its\n * data rows, so a named column can be read as a list of numbers. Everything\n * here works on plain strings and is exercised directly in\n * `__tests__/packages/tables/TableReader.spec.ts`, separately from the\n * document plumbing that feeds it.\n *\n * Scope, stated where it is enforced rather than only in the docs:\n * - Only tables whose rows begin with `|` are recognised. The borderless\n * GitHub form (`item | cost` with no leading pipe) is deferred, since a\n * bare `a | b` line is ambiguous with a bitwise-or expression and needs\n * cross-line context to tell the two apart.\n * - A cell is numeric only when it is a plain number (optionally signed, with\n * `,` thousands separators). A cell carrying a currency symbol or a unit is\n * read as non-numeric and skipped, reading those is the more useful version\n * and is deliberately left for a later slice.\n */\n\n/** One markdown table found above a query line: its header cells and data rows. */\nexport interface MarkdownTable {\n /** Header cell texts, trimmed, in column order. */\n header: string[];\n /** Each data row, already split into trimmed cell texts. */\n rows: string[][];\n}\n\n/** Whether a line, once trimmed, begins a markdown table row (a leading `|`). */\nexport function isTableRow(line: string): boolean {\n return line.trimStart().startsWith(\"|\");\n}\n\n/**\n * Whether a line is a table delimiter row, e.g. `|---|:--:|`.\n *\n * A delimiter is built only from pipes, dashes, colons and whitespace, and\n * carries at least one dash and one pipe. The dash requirement keeps a data\n * row of all-empty cells (`| | |`) from being read as the delimiter.\n */\nexport function isSeparatorRow(line: string): boolean {\n const s = line.trim();\n if (s.length === 0) return false;\n if (!s.includes(\"-\") || !s.includes(\"|\")) return false;\n return /^[|\\s:-]+$/.test(s);\n}\n\n/**\n * Split one markdown table row into trimmed cell texts.\n *\n * The optional outer pipes are stripped first, so `| rent | 1200 |` and\n * `rent | 1200` both yield two cells. A `\\|` escapes a pipe inside a cell\n * rather than ending it.\n */\nexport function splitTableRow(line: string): string[] {\n let s = line.trim();\n if (s.startsWith(\"|\")) s = s.slice(1);\n if (s.endsWith(\"|\") && !s.endsWith(\"\\\\|\")) s = s.slice(0, -1);\n\n const cells: string[] = [];\n let cell = \"\";\n for (let i = 0; i < s.length; i++) {\n const ch = s[i];\n if (ch === \"\\\\\" && i + 1 < s.length && s[i + 1] === \"|\") {\n // An escaped pipe is a literal pipe in the cell, not a separator.\n cell += \"|\";\n i++;\n continue;\n }\n if (ch === \"|\") {\n cells.push(cell.trim());\n cell = \"\";\n continue;\n }\n cell += ch;\n }\n cells.push(cell.trim());\n return cells;\n}\n\n/**\n * Parse a single cell as a plain number, or return `null` when it is not one.\n *\n * Thousands separators (`,`) are removed first, so `4,812` reads as 4812. A\n * cell that carries a currency symbol, a unit, or any other text is not a\n * plain number and returns `null`, the caller skips it rather than guessing.\n */\nexport function parseNumericCell(cell: string | undefined): number | null {\n if (cell === undefined) return null;\n const trimmed = cell.trim();\n if (trimmed.length === 0) return null;\n const cleaned = trimmed.replace(/,/g, \"\");\n if (!/^[+-]?(\\d+(\\.\\d+)?|\\.\\d+)$/.test(cleaned)) return null;\n const n = Number.parseFloat(cleaned);\n return Number.isFinite(n) ? n : null;\n}\n\n/**\n * Find the nearest markdown table whose last row sits above `fromLine`.\n *\n * Walks upward from `fromLine - 1`, takes the first table row it meets as the\n * table's bottom, then extends up through the contiguous run of table rows to\n * the top. A valid table has a delimiter row with a header row directly above\n * it, the rows below the delimiter are the data. Returns `null` when no table\n * row is found above, or when the block has no delimiter or no header (both of\n * which mean there is nothing to name a column against).\n *\n * @param getLineText - Reads a line's raw text by 1-based number.\n * @param fromLine - The 1-based line the query sits on, exclusive upper bound.\n */\nexport function findTableAbove(\n getLineText: (lineNumber: number) => string | undefined,\n fromLine: number,\n): MarkdownTable | null {\n // Nearest table row above the query, scanning past any intervening prose.\n let bottom = -1;\n for (let n = fromLine - 1; n >= 1; n--) {\n const text = getLineText(n);\n if (text === undefined) break;\n if (isTableRow(text)) {\n bottom = n;\n break;\n }\n }\n if (bottom === -1) return null;\n\n // The contiguous block of table rows ending at `bottom`.\n let top = bottom;\n while (top - 1 >= 1) {\n const text = getLineText(top - 1);\n if (text === undefined || !isTableRow(text)) break;\n top--;\n }\n\n // Locate the delimiter row within the block.\n let separator = -1;\n for (let n = top; n <= bottom; n++) {\n if (isSeparatorRow(getLineText(n)!)) {\n separator = n;\n break;\n }\n }\n // No delimiter, or no header row above it: not a table we can read columns from.\n if (separator === -1 || separator === top) return null;\n\n const header = splitTableRow(getLineText(separator - 1)!);\n const rows: string[][] = [];\n for (let n = separator + 1; n <= bottom; n++) {\n rows.push(splitTableRow(getLineText(n)!));\n }\n return { header, rows };\n}\n\n/**\n * Zero-based index of the column named `name` in a header, or `-1` if absent.\n *\n * Matching is case-insensitive and trims surrounding whitespace, so `\"Cost\"`\n * finds a `cost` header. The first matching column wins.\n */\nexport function columnIndex(header: string[], name: string): number {\n const target = name.trim().toLowerCase();\n for (let i = 0; i < header.length; i++) {\n if (header[i].trim().toLowerCase() === target) return i;\n }\n return -1;\n}\n\n/**\n * The numeric cells of one column, in row order, skipping non-numeric ones.\n *\n * A row too short to reach the column contributes nothing, as does a cell that\n * is not a plain number (see {@link parseNumericCell}).\n */\nexport function numericColumn(table: MarkdownTable, colIndex: number): number[] {\n const out: number[] = [];\n for (const row of table.rows) {\n const value = parseNumericCell(row[colIndex]);\n if (value !== null) out.push(value);\n }\n return out;\n}\n","import { Value, ValueType, numberValue, errorValue } from \"@solve-js/vm/Value\";\nimport type { LineExecutionContext } from \"@solve-js/vm/VM\";\nimport { findTableAbove, columnIndex, numericColumn } from \"./TableReader\";\n\n/**\n * Runtime handlers for `sum of column \"name\" above` and its siblings.\n *\n * Every handler resolves entirely at execution time: the parselet pushes only\n * the column name, and the handler reads \"which line am I on\" and the raw text\n * of the lines above from the {@link LineExecutionContext} that `CALL_PLUGIN`\n * threads through (the same mechanism the lines package uses for `total\n * above`). It walks up to the nearest markdown table, reads the named column,\n * and reduces its numeric cells.\n *\n * The error discipline mirrors the lines package: a missing document, a table\n * that cannot be found, a column name that is not present, and a column with no\n * numeric cells each surface a clear, coded error Value rather than a silent\n * zero, which is the failure class this codebase treats as its worst.\n */\n\n/** Scoped error codes this package owns, co-located per `errors/ErrorCode.ts`. */\nexport const TablesErrorCodes = {\n /** Cross-line table reads need a real document, absent on the single-expression path. */\n TABLE_NO_DOCUMENT: \"TABLE_NO_DOCUMENT\",\n /** No markdown table was found above the query line. */\n TABLE_NOT_FOUND: \"TABLE_NOT_FOUND\",\n /** The named column is not one of the table's headers. */\n TABLE_COLUMN_NOT_FOUND: \"TABLE_COLUMN_NOT_FOUND\",\n /** The column held no plain-number cells to aggregate. */\n TABLE_COLUMN_NO_NUMERIC_CELLS: \"TABLE_COLUMN_NO_NUMERIC_CELLS\",\n} as const;\n\n/** The reductions a column aggregate can apply to its numeric cells. */\nexport type ColumnAggregateOp = \"sum\" | \"average\" | \"min\" | \"max\" | \"count\" | \"median\";\n\nfunction reduce(op: ColumnAggregateOp, cells: number[]): number {\n switch (op) {\n case \"sum\":\n return cells.reduce((acc, n) => acc + n, 0);\n case \"average\":\n return cells.reduce((acc, n) => acc + n, 0) / cells.length;\n case \"min\":\n return Math.min(...cells);\n case \"max\":\n return Math.max(...cells);\n case \"count\":\n return cells.length;\n case \"median\": {\n const sorted = [...cells].sort((a, b) => a - b);\n const mid = Math.floor(sorted.length / 2);\n return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];\n }\n }\n}\n\n/**\n * Read the named column of the nearest table above and aggregate it.\n *\n * `count` is defined over the numeric cells found, so a column with no numbers\n * counts as zero rather than erroring. Every other reduction has no meaningful\n * value over an empty set, so it returns a coded error instead of a misleading\n * number.\n */\nfunction aggregateColumn(\n op: ColumnAggregateOp,\n columnName: string,\n context: LineExecutionContext | undefined,\n): Value {\n const getLineText = context?.getLineText;\n if (!getLineText || context!.lineIndex < 1) {\n return errorValue(\n TablesErrorCodes.TABLE_NO_DOCUMENT,\n 'Reading a table column needs a real document, which the single-expression path (evaluateExpression) does not have',\n );\n }\n\n const table = findTableAbove(getLineText, context!.lineIndex);\n if (!table) {\n return errorValue(\n TablesErrorCodes.TABLE_NOT_FOUND,\n 'No markdown table found above this line (a table row starts with \"|\", and needs a \"|---|\" separator under its header)',\n );\n }\n\n const index = columnIndex(table.header, columnName);\n if (index === -1) {\n const names = table.header.map((h) => `\"${h}\"`).join(\", \");\n return errorValue(\n TablesErrorCodes.TABLE_COLUMN_NOT_FOUND,\n `The table above has no column named \"${columnName}\". Its columns are: ${names}`,\n );\n }\n\n const cells = numericColumn(table, index);\n if (op === \"count\") return numberValue(cells.length);\n if (cells.length === 0) {\n return errorValue(\n TablesErrorCodes.TABLE_COLUMN_NO_NUMERIC_CELLS,\n `Column \"${columnName}\" has no plain-number cells to aggregate (currency and unit cells are not read yet)`,\n );\n }\n\n return numberValue(reduce(op, cells));\n}\n\nfunction columnName(args: Value[]): string {\n const value = args[0];\n if (value && value.type === ValueType.String && typeof value.value === \"string\") {\n return value.value;\n }\n // The parselet always pushes a string, so this is defensive only.\n return String(value?.value ?? \"\");\n}\n\n/** `sum of column \"name\" [in table] above` / `total of column ...`. */\n/**\n * Total of a named column's numeric cells in the nearest table above.\n *\n * @param args - One String: the column name.\n * @param context - Per-line execution context, source of the current line\n * number and the raw text of the lines above. Missing outside a document, in\n * which case a coded TABLE_NO_DOCUMENT error is returned.\n * @returns The column total, or a coded error Value.\n */\nexport function tableColumnSumHandler(args: Value[], context?: LineExecutionContext): Value {\n return aggregateColumn(\"sum\", columnName(args), context);\n}\n\n/** `average of column \"name\" [in table] above` / `mean of column ...`. */\n/**\n * Mean of a named column's numeric cells in the nearest table above.\n *\n * @param args - One String: the column name.\n * @param context - Per-line execution context (see {@link tableColumnSumHandler}).\n * @returns The column mean, or a coded error Value.\n */\nexport function tableColumnAverageHandler(args: Value[], context?: LineExecutionContext): Value {\n return aggregateColumn(\"average\", columnName(args), context);\n}\n\n/** `min of column \"name\" [in table] above`. */\n/**\n * Smallest numeric cell of a named column in the nearest table above.\n *\n * @param args - One String: the column name.\n * @param context - Per-line execution context (see {@link tableColumnSumHandler}).\n * @returns The column minimum, or a coded error Value.\n */\nexport function tableColumnMinHandler(args: Value[], context?: LineExecutionContext): Value {\n return aggregateColumn(\"min\", columnName(args), context);\n}\n\n/** `max of column \"name\" [in table] above`. */\n/**\n * Largest numeric cell of a named column in the nearest table above.\n *\n * @param args - One String: the column name.\n * @param context - Per-line execution context (see {@link tableColumnSumHandler}).\n * @returns The column maximum, or a coded error Value.\n */\nexport function tableColumnMaxHandler(args: Value[], context?: LineExecutionContext): Value {\n return aggregateColumn(\"max\", columnName(args), context);\n}\n\n/** `count of column \"name\" [in table] above`. */\n/**\n * How many numeric cells a named column holds in the nearest table above.\n *\n * @param args - One String: the column name.\n * @param context - Per-line execution context (see {@link tableColumnSumHandler}).\n * @returns The count (zero when the column has no numbers), or a coded error Value.\n */\nexport function tableColumnCountHandler(args: Value[], context?: LineExecutionContext): Value {\n return aggregateColumn(\"count\", columnName(args), context);\n}\n\n/** `median of column \"name\" [in table] above`. */\n/**\n * Middle numeric cell of a named column in the nearest table above.\n *\n * @param args - One String: the column name.\n * @param context - Per-line execution context (see {@link tableColumnSumHandler}).\n * @returns The column median, or a coded error Value.\n */\nexport function tableColumnMedianHandler(args: Value[], context?: LineExecutionContext): Value {\n return aggregateColumn(\"median\", columnName(args), context);\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { ColumnAggregateParselet } from \"./parselets/ColumnAggregateParselet\";\nimport {\n tableColumnSumHandler,\n tableColumnAverageHandler,\n tableColumnMinHandler,\n tableColumnMaxHandler,\n tableColumnCountHandler,\n tableColumnMedianHandler,\n} from \"./TablesPluginFunctions\";\n\n/**\n * Read a markdown table's column as data: `sum of column \"cost\" in table\n * above`, `average of column \"cost\" above`.\n *\n * A markdown table is the one block the evaluator classifies and then skips,\n * so a note can hold a table of numbers and none of them can be totalled from\n * where they sit. This package addresses the nearest table above the query\n * line, names one of its columns, and reduces that column's numeric cells,\n * reusing the same cross-line plumbing the lines package built for `total\n * above` (a per-line `LineExecutionContext`, extended with `getLineText` so\n * the skipped table rows can be read back from source).\n *\n * Trigger-word collision policy (this codebase's phrase-fusion rule, see\n * `MathPhrasesPackage.ts`): the whole verb-and-noun is phrase-fused, never the\n * bare verb. `average of` is already fused to `AVERAGE_OF` by the math-phrases\n * package; registering the longer `average of column` in the same phrase trie\n * wins by longest-match, so plain `average of 1, 2, 3` is untouched while\n * `average of column \"x\"` routes here. `sum of` is claimed by no one, so\n * `sum of column` fuses cleanly. Synonyms (`total`->sum, `mean`->average)\n * share a token type and parselet, matching how the lines package treats\n * `sum`/`total` as one.\n *\n * Decisions worth stating (also in the docs and changeset):\n * - Addressing: nearest table above only. An explicit table label is deferred.\n * - Non-numeric cells: skipped, not an error, so a stray label or a blank cell\n * does not break an otherwise-numeric column. A column with no numbers at all\n * is a clear error (except `count`, which is then zero).\n * - Currency and units in cells: not read yet. A cell carrying a `$` or a unit\n * is treated as non-numeric and skipped. Plain numbers first, on purpose.\n */\nexport const TABLES_PACKAGE: IEnginePackage = {\n name: \"solve-tables\",\n phrases: {\n \"sum of column\": \"TABLE_COLUMN_SUM\",\n \"total of column\": \"TABLE_COLUMN_SUM\",\n \"average of column\": \"TABLE_COLUMN_AVERAGE\",\n \"mean of column\": \"TABLE_COLUMN_AVERAGE\",\n \"min of column\": \"TABLE_COLUMN_MIN\",\n \"minimum of column\": \"TABLE_COLUMN_MIN\",\n \"max of column\": \"TABLE_COLUMN_MAX\",\n \"maximum of column\": \"TABLE_COLUMN_MAX\",\n \"count of column\": \"TABLE_COLUMN_COUNT\",\n \"median of column\": \"TABLE_COLUMN_MEDIAN\",\n },\n prefixParselets: {\n TABLE_COLUMN_SUM: new ColumnAggregateParselet(\"TABLE_COLUMN_SUM\"),\n TABLE_COLUMN_AVERAGE: new ColumnAggregateParselet(\"TABLE_COLUMN_AVERAGE\"),\n TABLE_COLUMN_MIN: new ColumnAggregateParselet(\"TABLE_COLUMN_MIN\"),\n TABLE_COLUMN_MAX: new ColumnAggregateParselet(\"TABLE_COLUMN_MAX\"),\n TABLE_COLUMN_COUNT: new ColumnAggregateParselet(\"TABLE_COLUMN_COUNT\"),\n TABLE_COLUMN_MEDIAN: new ColumnAggregateParselet(\"TABLE_COLUMN_MEDIAN\"),\n },\n pluginFunctions: {\n TABLE_COLUMN_SUM: tableColumnSumHandler,\n TABLE_COLUMN_AVERAGE: tableColumnAverageHandler,\n TABLE_COLUMN_MIN: tableColumnMinHandler,\n TABLE_COLUMN_MAX: tableColumnMaxHandler,\n TABLE_COLUMN_COUNT: tableColumnCountHandler,\n TABLE_COLUMN_MEDIAN: tableColumnMedianHandler,\n },\n};\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * `total of #tag` / `sum of #tag` / `average of #tag` / `count of #tag`. The tag\n * name rides on the fused trigger token (TagAggregateNormalizerRule folds the\n * trigger and the TAG together into one token whose value is the name), so the\n * parselet pushes it as the single string argument and calls the plugin\n * handler, which resolves the whole document from the line context. The same\n * shape as ColumnAggregateParselet.\n */\nexport class TagAggregateParselet implements PrefixParselet {\n readonly category = \"Tags\";\n\n constructor(private readonly pluginFnName: string) {}\n\n parse(_parser: Parser, token: Token, builder: BytecodeBuilder): void {\n builder.emitOpcode(OpCode.PUSH_STRING);\n builder.emitString(token.value);\n builder.emitPluginCall(this.pluginFnName, 1);\n }\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\n/** The lowercased written form of a token, tolerant of `text` vs `value`. */\nfunction wordOf(token: Token | undefined): string {\n if (token === undefined) return \"\";\n return (token.text ?? token.value ?? \"\").toLowerCase();\n}\n\n/** The fused-trigger token each math-phrase aggregate maps to for a tag. */\nconst AGGREGATE_TOKEN: Record<string, string> = {\n TOTAL_OF: \"TAG_SUM\",\n COUNT_OF: \"TAG_COUNT\",\n AVERAGE_OF: \"TAG_AVERAGE\",\n};\n\n/** The words that open an `... of #tag` aggregate, before the phrase fuses. */\nconst AGGREGATE_WORDS: ReadonlySet<string> = new Set([\"total\", \"sum\", \"count\", \"average\"]);\n\n/**\n * `total of #tag` / `count of #tag` / `average of #tag` / `sum of #tag`, the\n * category tag aggregates. Fuses the trigger and the following TAG into one\n * TAG_SUM / TAG_COUNT / TAG_AVERAGE token whose value is the tag name.\n *\n * Two shapes. The math-phrase package fuses \"total of\"/\"count of\"/\"average of\"\n * into TOTAL_OF/COUNT_OF/AVERAGE_OF, so those reach this rule as a single\n * trigger token; \"sum of\" has no such phrase, so it arrives as the raw words\n * `sum` `of`. Either shape must be immediately followed by a TAG, so\n * `total of 1, 2, 3` (a number follows, not a tag) still falls through to the\n * ordinary variadic aggregate, and `:total = 5` is untouched.\n */\nexport function tagAggregateNormalizerRule(priority = 80): NormalizerRule {\n return {\n name: \"tags:aggregate\",\n priority,\n match(tokens, pos): NormalizerMatch | null {\n const head = tokens[pos];\n if (head === undefined) return null;\n\n // Shape A: a fused math-phrase trigger directly followed by a TAG.\n const fused = AGGREGATE_TOKEN[head.type];\n if (fused !== undefined) {\n const tag = tokens[pos + 1];\n if (tag?.type !== \"TAG\") return null;\n return {\n consumed: 2,\n replacement: [createFusedToken(fused, tag.value ?? \"\", [head, tag])],\n ruleName: \"tags:aggregate\",\n };\n }\n\n // Shape B: bare `sum of #tag` (there is no \"sum of\" math phrase, so it\n // arrives as the raw word `sum` then the `of` keyword). Match on the\n // written word, not the token type: standalone \"of\" lexes to an `OF`\n // token, not an IDENT.\n if (head.type === \"IDENT\" && wordOf(head) === \"sum\") {\n const of = tokens[pos + 1];\n const tag = tokens[pos + 2];\n if (wordOf(of) !== \"of\") return null;\n if (tag?.type !== \"TAG\") return null;\n return {\n consumed: 3,\n replacement: [createFusedToken(\"TAG_SUM\", tag.value ?? \"\", [head, of as Token, tag])],\n ruleName: \"tags:aggregate\",\n };\n }\n\n return null;\n },\n };\n}\n\n/**\n * A lone TAG token, a data-line annotation like `1200 #housing`, is dropped so\n * the line evaluates to its number. Lower priority than the aggregate rule, and\n * declines a TAG that directly follows `of` or a fused `...of` trigger, so a TAG\n * destined for `total of #tag` is never eaten before the aggregate rule (which\n * only sees the fused trigger on a later pass) can claim it.\n */\nexport function tagStripNormalizerRule(priority = 40): NormalizerRule {\n return {\n name: \"tags:strip\",\n priority,\n match(tokens, pos): NormalizerMatch | null {\n if (tokens[pos]?.type !== \"TAG\") return null;\n const before = tokens[pos - 1];\n if (before !== undefined) {\n // A TAG right after a fused aggregate trigger (`total of`/`count of`/\n // `average of`) is claimed by the aggregate rule on the next pass.\n if (AGGREGATE_TOKEN[before.type] !== undefined) return null;\n // A TAG after the bare word \"of\" is only part of an aggregate when the\n // \"of\" completes one of the aggregate phrases (`total`/`sum`/`count`/\n // `average` of). `cost of #living` is ordinary prose, so its tag is a\n // data-line annotation to strip, not a stray TAG left to error.\n if (wordOf(before) === \"of\" && AGGREGATE_WORDS.has(wordOf(tokens[pos - 2]))) return null;\n }\n return { consumed: 1, replacement: [], ruleName: \"tags:strip\" };\n },\n };\n}\n","/**\n * Pure `#tag` detection on a raw line, kept engine-free so it is unit-testable\n * in isolation, the same design {@link TableReader} draws.\n */\n\n/** Escapes any regex-special character a tag name could carry. */\nexport function escapeTag(name: string): string {\n return name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Whether `rawText` carries `#tag` as a mid-line annotation, case-insensitively.\n *\n * The tag must not be part of a longer word before (`a#tag`) or after\n * (`#housingcost` is not `#housing`), and must not be the line's first\n * non-whitespace token, since a line starting with `#` is a heading, not a\n * tagged data line.\n */\nexport function lineCarriesTag(rawText: string, tag: string): boolean {\n if (tag === \"\") return false;\n const re = new RegExp(`(?:^|[^0-9A-Za-z_])#${escapeTag(tag)}(?![0-9A-Za-z_-])`, \"i\");\n const m = re.exec(rawText);\n if (m === null) return false;\n // The `#` sits inside the match; reject one that is the line's first\n // non-whitespace token (a heading).\n const hashIndex = m.index + m[0].indexOf(\"#\");\n return rawText.slice(0, hashIndex).trim().length > 0;\n}\n","import { Value, ValueType, numberValue, uomValue, errorValue } from \"@solve-js/vm/Value\";\nimport type { LineExecutionContext } from \"@solve-js/vm/VM\";\nimport { lineCarriesTag } from \"./TagScanner\";\n\n/**\n * Category tag sums, `total of #tag` / `sum of #tag` / `average of #tag` /\n * `count of #tag`, which collect every line in the document carrying that tag.\n *\n * Like the lines package, every handler routes a fetched line through\n * {@link checkLineValue} before arithmetic: `Value.toNumber()` returns `0` for\n * both `Pending` and `Error`, so touching an unresolved or errored line without\n * this check would silently compute a wrong number rather than surface the\n * error, the failure class this codebase treats as its worst.\n */\n\ntype TagMode = \"sum\" | \"average\" | \"count\";\n\nfunction checkLineValue(v: Value | undefined, lineNumber: number): Value | null {\n if (v === undefined) {\n return errorValue(\"LINE_NOT_YET_EVALUATED\", `Line ${lineNumber} has not been evaluated yet`);\n }\n if (v.type === ValueType.Pending) {\n return errorValue(\"LINE_RESULT_PENDING\", `Line ${lineNumber}'s result is still resolving`);\n }\n if (v.type === ValueType.Error) {\n return errorValue(\"LINE_RESULT_ERROR\", `Line ${lineNumber} has an error`);\n }\n return null;\n}\n\nfunction requireContext(context: LineExecutionContext | undefined): Value | null {\n if (!context?.getLineResult || !context.getLineText) {\n return errorValue(\"TAG_NO_DOCUMENT\", \"Category tag sums require a real document, not available outside one (e.g. evaluateExpression()'s single-expression path)\");\n }\n return null;\n}\n\n/**\n * Walks the whole document, keeping every line whose raw text carries `#tag`,\n * and reduces their results. Skips the querying line itself (its own text\n * carries the tag) and any boundary line (a blank line or heading), and holds\n * the same Pending/Error and single-unit guards the lines package uses.\n */\nfunction aggregateTagged(context: LineExecutionContext, tag: string, mode: TagMode): Value {\n const getText = context.getLineText!;\n const getResult = context.getLineResult!;\n const isBoundary = context.isLineBoundary;\n const needle = tag.toLowerCase();\n\n let total = 0;\n let count = 0;\n let commonUnit: string | undefined;\n let sawUom = false;\n\n for (let n = 1; ; n++) {\n const text = getText(n);\n if (text === undefined) break; // past the end of the document\n if (n === context.lineIndex) continue; // the query line reads its own tag\n if (isBoundary && isBoundary(n)) continue; // a blank line or heading\n if (!lineCarriesTag(text, needle)) continue;\n\n const v = getResult(n);\n const err = checkLineValue(v, n);\n if (err) return err;\n if (mode !== \"count\") {\n // `count of #tag` is \"how many lines carry the tag\", so a non-numeric\n // tagged line still counts; only sum and average need a number to add.\n if (v!.type !== ValueType.Number && v!.type !== ValueType.Uom) {\n return errorValue(\"TAG_NON_NUMERIC\", `Line ${n}, tagged #${tag}, is not a plain number or unit value.`);\n }\n if (v!.type === ValueType.Uom) {\n sawUom = true;\n if (commonUnit === undefined) commonUnit = v!.unit;\n else if (v!.unit !== commonUnit) {\n return errorValue(\"TAG_MIXED_UNITS\", `Line ${n}'s unit (${v!.unit}) doesn't match #${tag}'s first unit (${commonUnit}), aggregating mixed units would misrepresent the result.`);\n }\n }\n total += v!.toNumber();\n }\n count++;\n }\n\n if (mode === \"count\") return numberValue(count);\n if (count === 0) return errorValue(\"TAG_EMPTY\", `No lines are tagged #${tag}.`);\n const result = mode === \"average\" ? total / count : total;\n return sawUom ? uomValue(result, commonUnit!) : numberValue(result);\n}\n\n/** `total of #tag` / `sum of #tag`, the sum of every line carrying the tag. */\n/** Sum of every tagged line's result. @param args A String holding the tag name. */\nexport function tagSumHandler(args: Value[], context?: LineExecutionContext): Value {\n const ctxError = requireContext(context);\n if (ctxError) return ctxError;\n return aggregateTagged(context!, String(args[0].value), \"sum\");\n}\n\n/** `average of #tag`, the mean of every line carrying the tag. */\n/** Mean of every tagged line's result. @param args A String holding the tag name. */\nexport function tagAverageHandler(args: Value[], context?: LineExecutionContext): Value {\n const ctxError = requireContext(context);\n if (ctxError) return ctxError;\n return aggregateTagged(context!, String(args[0].value), \"average\");\n}\n\n/** `count of #tag`, how many lines carry the tag. */\n/** Count of the lines carrying the tag. @param args A String holding the tag name. */\nexport function tagCountHandler(args: Value[], context?: LineExecutionContext): Value {\n const ctxError = requireContext(context);\n if (ctxError) return ctxError;\n return aggregateTagged(context!, String(args[0].value), \"count\");\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { TagAggregateParselet } from \"./parselets/TagAggregateParselet\";\nimport { tagAggregateNormalizerRule, tagStripNormalizerRule } from \"./normalizer/TagAggregateNormalizerRule\";\nimport {\n tagSumHandler,\n tagAverageHandler,\n tagCountHandler,\n} from \"./TagsPluginFunctions\";\n\n/**\n * Category tag sums: a mid-line `#tag` annotates a data line, and `total of\n * #tag` / `sum of #tag` / `average of #tag` / `count of #tag` collect every line\n * in the document carrying that tag, across the non-adjacent lines that\n * `total above` and the table-column aggregates cannot reach.\n *\n * Collision-safety (this codebase's phrase-fusion-not-bare-keyword policy): the\n * `#tag` token is claimed in the lexer only for a `#` directly followed by a\n * letter mid-line, so `# ` (space) and `//` stay free-text comments and a\n * line-start `#` stays a heading. The aggregate triggers reuse the math-phrase\n * fusions (`total of`, `count of`, `average of`) and the raw `sum of`, so bare\n * `total`/`sum`/`count`/`average` never become keywords and `:total = 5` is\n * untouched.\n */\nexport const TAGS_PACKAGE: IEnginePackage = {\n name: \"solve-tags\",\n normalizerRules: [tagAggregateNormalizerRule(), tagStripNormalizerRule()],\n prefixParselets: {\n TAG_SUM: new TagAggregateParselet(\"sum\"),\n TAG_AVERAGE: new TagAggregateParselet(\"average\"),\n TAG_COUNT: new TagAggregateParselet(\"count\"),\n },\n pluginFunctions: {\n sum: tagSumHandler,\n average: tagAverageHandler,\n count: tagCountHandler,\n },\n};\n","import { InfixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\n\n/**\n * The uncertainty operator, `a ± b` (or the ASCII `a +/- b`), which builds a\n * measurement carrying a one-sigma tolerance.\n *\n * A Tier-2 infix parselet: the center is already on the stack as `left`, so this\n * parses the spread and emits MAKE_UNCERTAIN to pair them. It binds at\n * {@link BindingPower.Uncertainty}, tighter than `+ - * /` and looser than `^`,\n * so the tolerance attaches to the values either side of it (see that level's\n * own comment for the worked cases). Left-associative: the right operand is\n * parsed at this same power, so a rare chain like `1 ± 0.1 ± 0.2` groups left.\n */\nexport class UncertaintyParselet implements InfixParselet {\n readonly category = \"operator\";\n readonly bindingPower = BindingPower.Uncertainty;\n\n parse(parser: Parser, _left: Token, _token: Token, builder: BytecodeBuilder): void {\n // Parse the spread at this operator's own power, so the chain groups left\n // rather than swallowing a following `±`.\n parser.parseExpression(this.bindingPower, builder);\n builder.emitOpcode(OpCode.MAKE_UNCERTAIN);\n }\n}\n","import type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { createFusedToken } from \"@solve-js/normalizer/TokenNormalizer\";\n\n/**\n * `a +/- b` as the uncertainty operator, the ASCII spelling of `a ± b`.\n *\n * The `±` symbol lexes directly (ExpressionLexer.ts, alongside `×`/`÷`), but the\n * ASCII form arrives as three separate tokens, PLUS then SLASH then MINUS, since\n * each is an ordinary operator character. This fuses that exact run into a single\n * PLUS_MINUS token so both spellings reach the one infix parselet.\n *\n * The run is unambiguous: `+/-` has no other reading, `/` has no prefix parselet,\n * so `x + /- y` is a parse error today rather than any valid expression this\n * could be stealing. The rule requires the three consecutively and in order, so\n * an ordinary `10 / -5` (SLASH then MINUS, no leading PLUS) is untouched.\n */\nexport function asciiPlusMinusNormalizerRule(priority = 90): NormalizerRule {\n return {\n name: \"uncertainty:ascii-plus-minus\",\n priority,\n match(tokens, pos): NormalizerMatch | null {\n const plus = tokens[pos];\n if (plus?.type !== \"PLUS\") return null;\n const slash = tokens[pos + 1];\n if (slash?.type !== \"SLASH\") return null;\n const minus = tokens[pos + 2];\n if (minus?.type !== \"MINUS\") return null;\n\n return {\n consumed: 3,\n replacement: [createFusedToken(\"PLUS_MINUS\", \"±\", [plus, slash, minus])],\n ruleName: \"uncertainty:ascii-plus-minus\",\n };\n },\n };\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { UncertaintyParselet } from \"./parselets/UncertaintyParselet\";\nimport { asciiPlusMinusNormalizerRule } from \"./normalizer/AsciiPlusMinusNormalizerRule\";\n\n/**\n * Uncertainty propagation: a measurement that carries a one-sigma tolerance,\n * written `a ± b` or the ASCII `a +/- b`, propagated through `+`, `-`, `*`, `/`\n * for independent errors combined in quadrature.\n *\n * The `±` symbol is lexed directly; the ASCII spelling is fused by the\n * normalizer rule. Both reach the one infix parselet, which emits\n * MAKE_UNCERTAIN. Correlated errors are out of scope (a much larger problem);\n * a comparison or transcendental function reads the center and drops the\n * tolerance. See `vm/VMConversion.ts`'s uncertainOp for the propagation rules.\n */\nexport const UNCERTAINTY_PACKAGE: IEnginePackage = {\n name: \"solve-uncertainty\",\n infixParselets: {\n PLUS_MINUS: new UncertaintyParselet(),\n },\n normalizerRules: [asciiPlusMinusNormalizerRule()],\n // PLUS_MINUS is a core lexer token, so its highlight category lives in the\n // built-in language/TokenCategoryMap.ts rather than being contributed here.\n};\n","import { Value, ValueType, numberValue, errorValue, symbolicValue } from \"@solve-js/vm/Value\";\nimport type { LineExecutionContext } from \"@solve-js/vm/VM\";\nimport {\n\ttype SymbolicNode,\n\tvarNode,\n\tconstNode,\n\trationalFromNumber,\n\trationalToNumber,\n\tsimplifySymbolic,\n} from \"@solve-js/symbolic\";\nimport { solveForVariable, type SolveOutcome } from \"@solve-js/symbolic/Solve\";\n\n/**\n * Goal seek, `solve line N for <var> = <target>`.\n *\n * Inverts a line against a target: it finds the value of a variable that makes\n * a referenced line's result equal a number, the thing a forwards-only engine\n * otherwise leaves to editing an input and re-reading the answer by hand.\n *\n * Two mechanisms, chosen automatically per the issue's own framing:\n * - Closed form. Reading the target line with the variable bound to itself\n * (symbolically) yields an expression when the line is closed form in it,\n * which the algebra solver inverts exactly. `2*x+10 = 30` returns `10`, no\n * search.\n * - Bounded numeric search. When the relationship is not closed form (a\n * finance formula, say, whose builtin has no symbolic reading), a bisection\n * over a fixed, non-negative domain narrows in on the input. It assumes the\n * relationship is monotonic over that domain (one crossing of the target)\n * and is capped hard at `config.vm.maxGoalSeekIterations` steps, so an\n * untrusted document can never make it spin. No bracket, a non-finite\n * sample, or exhausting the cap is a structured error, never a hang and\n * never a guess.\n *\n * The search domain and tolerances are the deliberate v1 decisions: a positive\n * input up to a billion (deposits, rates, amounts, years are all positive, and\n * a strictly-positive floor sidesteps the finance builtins that reject a zero\n * principal), and convergence judged on the target value rather than the input,\n * so the answer is \"close enough on screen\" regardless of how steep the\n * relationship is. A solution outside that domain, or a non-monotonic\n * relationship with several crossings, is out of scope for this slice and\n * reported rather than half-answered.\n */\n\n/**\n * Lowest input the numeric search will consider. Goal-seek quantities\n * (deposits, rates, amounts, durations) are positive; a strictly-positive floor\n * rather than zero also keeps the bottom endpoint valid for the finance\n * builtins that reject a zero principal, while staying small enough to read as\n * \"effectively nothing\" (a rate this near zero is the 0% floor).\n */\nconst SEARCH_LOWER_BOUND = 1e-9;\n/** Highest input the numeric search will consider, far above any realistic deposit/rate/amount yet finite. */\nconst SEARCH_UPPER_BOUND = 1e9;\n/** Fallback iteration cap when the context carries none (single-expression paths never reach the search, so this only guards a malformed context). */\nconst FALLBACK_MAX_ITERATIONS = 100;\n/** Absolute tolerance on the target value, so a target of zero still has a floor to converge against. */\nconst ABSOLUTE_VALUE_TOLERANCE = 1e-9;\n/** Relative tolerance on the target value, so a large target is matched to the same number of significant figures as a small one. */\nconst RELATIVE_VALUE_TOLERANCE = 1e-9;\n/** Interval width at which the search declares the relationship discontinuous rather than continuing to halve a point. */\nconst INTERVAL_COLLAPSE_WIDTH = 1e-12;\n/** Largest imaginary part a numerically-found root may carry and still count as real. */\nconst REAL_ROOT_IMAGINARY_TOLERANCE = 1e-9;\n\n/** The package-local name the goal-seek plugin function is registered and emitted under (`solve line N for <var> = <target>`). */\nexport const GOAL_SEEK_FN_NAME = \"goalseek\";\n\n/**\n * Solve for the variable that makes a line equal a target.\n *\n * @param args - Three values, in the order the parselet pushes them: the target\n * line number, the variable name as a String, and the target as a Number.\n * @param context - Per-line execution context. Supplies the re-evaluation\n * primitive and the iteration cap; without a document the handler returns a\n * structured error rather than guessing, since there is no line to solve.\n * @returns The solved input as a Number, or a structured error Value when there\n * is no solution in range, the search does not converge, or the target line\n * cannot be probed.\n */\nexport function goalSeekHandler(args: Value[], context?: LineExecutionContext): Value {\n\tconst probe = context?.evaluateLineWithBinding;\n\tconst getLineReads = context?.getLineReads;\n\tif (!probe || !getLineReads) {\n\t\treturn errorValue(\n\t\t\t\"GOAL_SEEK_NO_DOCUMENT\",\n\t\t\t\"Goal seek only works inside a document, since it re-runs another line. The single-expression entry point has no document to solve against.\",\n\t\t);\n\t}\n\n\tconst targetLine = args[0].toNumber();\n\tconst varName = typeof args[1].value === \"string\" ? (args[1].value as string) : String(args[1].value);\n\tconst targetArg = args[2];\n\tif (targetArg.type !== ValueType.Number && targetArg.type !== ValueType.Uom) {\n\t\treturn errorValue(\"GOAL_SEEK_TARGET_NOT_NUMERIC\", `Goal seek's target must be a number, as in \"solve line ${targetLine} for ${varName} = 900\".`);\n\t}\n\tconst target = targetArg.toNumber();\n\tif (!Number.isFinite(target)) {\n\t\treturn errorValue(\"GOAL_SEEK_TARGET_NOT_NUMERIC\", \"Goal seek's target is not a finite number.\");\n\t}\n\n\t// Refuse before any searching when the target line does not read the\n\t// variable: nothing about changing it could move that line's result, and a\n\t// silent \"no solution\" would be a confusing way to say so.\n\tconst reads = getLineReads(targetLine);\n\tif (reads === undefined) {\n\t\treturn errorValue(\"GOAL_SEEK_LINE_NOT_READY\", `Line ${targetLine} has no evaluated expression to solve (forward reference, out of range, or not an expression).`);\n\t}\n\tif (!reads.includes(varName)) {\n\t\treturn errorValue(\"GOAL_SEEK_VARIABLE_NOT_USED\", `Line ${targetLine} does not use ${varName}, so changing ${varName} cannot move its result toward ${target}.`);\n\t}\n\n\t// ── Closed-form fast path ──\n\t// Bind the variable to itself and read the line back symbolically. A closed\n\t// form comes back as an expression the algebra solver inverts exactly; a\n\t// non-closed-form line (its builtin has no symbolic reading) comes back as an\n\t// error Value, and the numeric search below takes over.\n\tconst symbolic = probe(targetLine, varName, symbolicValue(varNode(varName)), true);\n\tif (symbolic.type === ValueType.Symbolic) {\n\t\tconst exact = solveClosedForm(symbolic.value as SymbolicNode, target, varName);\n\t\tif (exact !== null) return numberValue(exact);\n\t}\n\n\t// ── Bounded numeric search ──\n\tconst maxIterations = context.goalSeekMaxIterations ?? FALLBACK_MAX_ITERATIONS;\n\treturn bisect(probe, targetLine, varName, target, maxIterations);\n}\n\n/**\n * Inverts a closed-form relationship exactly, or returns null so the caller\n * falls back to the numeric search.\n *\n * Returns null rather than an error for every case it cannot answer cleanly (an\n * unsolvable or partial outcome, only complex or irrational roots, no finite\n * root), because those are not failures of goal seek, they are simply where the\n * numeric search is the better tool.\n *\n * @param lhs - The line read back as an expression in the variable.\n * @param target - The value the line should equal.\n * @param variable - The unknown being solved for.\n * @returns A finite real root, preferring one inside the numeric search domain, or null.\n */\nfunction solveClosedForm(lhs: SymbolicNode, target: number, variable: string): number | null {\n\tconst rhs = constNode(rationalFromNumber(target));\n\tlet outcome: SolveOutcome;\n\ttry {\n\t\toutcome = solveForVariable(lhs, rhs, variable);\n\t} catch {\n\t\treturn null;\n\t}\n\tif (outcome.kind !== \"roots\") return null;\n\n\tconst candidates: number[] = [];\n\tfor (const node of outcome.exact) {\n\t\t// Only a root that simplifies to a bare rational has a numeric value to\n\t\t// return; a surd or complex root is left to the numeric search.\n\t\tconst simplified = simplifySymbolic(node);\n\t\tif (simplified.kind === \"const\") {\n\t\t\tconst value = rationalToNumber(simplified.value);\n\t\t\tif (Number.isFinite(value)) candidates.push(value);\n\t\t}\n\t}\n\tfor (const root of outcome.approximate) {\n\t\tif (Math.abs(root.im) <= REAL_ROOT_IMAGINARY_TOLERANCE && Number.isFinite(root.re)) candidates.push(root.re);\n\t}\n\tif (candidates.length === 0) return null;\n\n\t// Prefer a root inside the numeric search domain, then the one nearest zero,\n\t// so a quadratic with two roots returns the \"sensible\" small non-negative one\n\t// rather than an arbitrary pick.\n\tconst inDomain = candidates.filter((c) => c >= SEARCH_LOWER_BOUND && c <= SEARCH_UPPER_BOUND);\n\tconst pool = inDomain.length > 0 ? inDomain : candidates;\n\tpool.sort((a, b) => Math.abs(a) - Math.abs(b));\n\treturn pool[0];\n}\n\n/** One numeric sample of the target line, or an error Value if it could not be read as a finite number. */\ntype Sample = { value: number } | { error: Value };\n\n/**\n * Bisects for the input that makes the target line equal the target value.\n *\n * Assumes the relationship is monotonic over the search domain (a single\n * crossing), the assumption stated in the module doc. It requires the endpoints\n * to bracket the target (their results straddle it); without that there is no\n * crossing to find and it reports no solution. It stops on the first of three\n * things: the value converging, the interval collapsing (a discontinuity, not a\n * smooth crossing), or the hard iteration cap.\n *\n * @param probe - Re-evaluates the target line with the variable set to a candidate.\n * @param targetLine - The line whose result is being driven to the target.\n * @param variable - The unknown being varied.\n * @param target - The value the line should reach.\n * @param maxIterations - The hard ceiling on bisection steps.\n * @returns The solved input as a Number, or a structured error Value.\n */\nfunction bisect(\n\tprobe: NonNullable<LineExecutionContext[\"evaluateLineWithBinding\"]>,\n\ttargetLine: number,\n\tvariable: string,\n\ttarget: number,\n\tmaxIterations: number,\n): Value {\n\tconst sample = (candidate: number): Sample => {\n\t\tconst result = probe(targetLine, variable, numberValue(candidate), false);\n\t\tif (result.type === ValueType.Error) return { error: result };\n\t\tif (result.type !== ValueType.Number && result.type !== ValueType.Uom) {\n\t\t\treturn { error: errorValue(\"GOAL_SEEK_TARGET_NOT_NUMERIC\", `Line ${targetLine} did not produce a number when ${variable} was set to ${candidate}, so goal seek cannot compare it to the target.`) };\n\t\t}\n\t\tconst numeric = result.toNumber();\n\t\tif (!Number.isFinite(numeric)) return { error: errorValue(\"GOAL_SEEK_NON_FINITE\", `Line ${targetLine}'s result is not finite when ${variable} is ${candidate}.`) };\n\t\treturn { value: numeric };\n\t};\n\n\tlet lo = SEARCH_LOWER_BOUND;\n\tlet hi = SEARCH_UPPER_BOUND;\n\tconst loSample = sample(lo);\n\tif (\"error\" in loSample) return loSample.error;\n\tconst hiSample = sample(hi);\n\tif (\"error\" in hiSample) return hiSample.error;\n\n\tconst tolerance = Math.max(ABSOLUTE_VALUE_TOLERANCE, Math.abs(target) * RELATIVE_VALUE_TOLERANCE);\n\tlet flo = loSample.value - target;\n\tconst fhi = hiSample.value - target;\n\tif (Math.abs(flo) <= tolerance) return numberValue(lo);\n\tif (Math.abs(fhi) <= tolerance) return numberValue(hi);\n\tif (Math.sign(flo) === Math.sign(fhi)) {\n\t\treturn errorValue(\n\t\t\t\"GOAL_SEEK_NO_SOLUTION\",\n\t\t\t`No value of ${variable} between ${lo} and ${hi} makes line ${targetLine} equal ${target}: across that whole range its result stays on one side of the target.`,\n\t\t);\n\t}\n\n\tfor (let step = 0; step < maxIterations; step++) {\n\t\tconst mid = (lo + hi) / 2;\n\t\tconst midSample = sample(mid);\n\t\tif (\"error\" in midSample) return midSample.error;\n\t\tconst fmid = midSample.value - target;\n\t\tif (Math.abs(fmid) <= tolerance) return numberValue(mid);\n\t\tif (hi - lo <= INTERVAL_COLLAPSE_WIDTH) {\n\t\t\treturn errorValue(\n\t\t\t\t\"GOAL_SEEK_DID_NOT_CONVERGE\",\n\t\t\t\t`Goal seek narrowed ${variable} to a single point near ${mid} without line ${targetLine} reaching ${target}: the relationship jumps across the target rather than passing through it.`,\n\t\t\t);\n\t\t}\n\t\t// Keep the endpoint whose side of the target the midpoint shares, so the\n\t\t// bracket always still straddles the target.\n\t\tif (Math.sign(fmid) === Math.sign(flo)) {\n\t\t\tlo = mid;\n\t\t\tflo = fmid;\n\t\t} else {\n\t\t\thi = mid;\n\t\t}\n\t}\n\n\treturn errorValue(\n\t\t\"GOAL_SEEK_DID_NOT_CONVERGE\",\n\t\t`Goal seek did not bring line ${targetLine} within tolerance of ${target} by varying ${variable} in ${maxIterations} steps.`,\n\t);\n}\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\nimport { GOAL_SEEK_FN_NAME } from \"../GoalSeekPluginFunctions\";\n\n/**\n * `solve line M for <var> = <target>`, goal seek over a line reference.\n *\n * Reads as \"find the value of `<var>` that makes line M's result equal\n * `<target>`\". The word `solve` has already been fused into a `GOAL_SEEK`\n * token by `GoalSeekNormalizerRule` (it fires only when `solve` sits directly\n * before a `LINE_REF`, so the ordinary `solve(...)` call form is untouched),\n * and this parselet reads the rest of the line itself rather than through the\n * ordinary infix grammar, because none of `for`/the bare variable name/`=`\n * would combine into one expression on their own.\n *\n * Emits three arguments for the goal-seek plugin function, in the order it\n * reads them: the target line number, the variable name as a String (a name,\n * not a value, the same reason the algebra verbs push their unknown as a\n * String), and the target as an ordinary expression so `900`, `1,200` or a\n * small calculation all work. The plugin does the search.\n */\nexport class GoalSeekParselet implements PrefixParselet {\n\treadonly category = \"GoalSeek\";\n\n\tparse(parser: Parser, _token: Token, builder: BytecodeBuilder): void {\n\t\t// Target line, the LINE_REF the normalizer left in place after fusing\n\t\t// \"solve\" into the GOAL_SEEK token this parselet fired on.\n\t\tconst lineRef = parser.peek();\n\t\tif (!lineRef || lineRef.type !== \"LINE_REF\") {\n\t\t\tthrow ErrorFactory.parsing(\n\t\t\t\t\"GOAL_SEEK_SYNTAX\",\n\t\t\t\t`Goal seek reads \"solve line N for <var> = <target>\", for example \"solve line 4 for rate = 900\".`,\n\t\t\t\t{ found: lineRef?.type ?? \"end of input\" },\n\t\t\t);\n\t\t}\n\t\tparser.consume();\n\t\tconst targetLine = parseInt(lineRef.value, 10);\n\n\t\t// The \"for\" connective. It lexes as FOR_DURATION in the English locale\n\t\t// (a bare keyword there) and as a plain IDENT elsewhere, so both are\n\t\t// accepted rather than depending on the finance package's token being\n\t\t// present.\n\t\tconst connective = parser.peek();\n\t\tconst isFor =\n\t\t\t!!connective &&\n\t\t\t(connective.type === \"FOR_DURATION\" || (connective.type === \"IDENT\" && connective.value.toLowerCase() === \"for\"));\n\t\tif (!isFor) {\n\t\t\tthrow ErrorFactory.parsing(\n\t\t\t\t\"GOAL_SEEK_SYNTAX\",\n\t\t\t\t`Goal seek reads \"solve line N for <var> = <target>\". Expected \"for\" after the line reference.`,\n\t\t\t\t{ found: connective?.type ?? \"end of input\" },\n\t\t\t);\n\t\t}\n\t\tparser.consume();\n\n\t\t// The unknown to vary. Requiring IDENT or UNIT gives the same\n\t\t// reserved-word protection the algebra verbs rely on: a keyword lexes as\n\t\t// its own token type and can never be taken for a variable name here.\n\t\tconst variable = parser.peek();\n\t\tif (!variable || (variable.type !== \"IDENT\" && variable.type !== \"UNIT\")) {\n\t\t\tthrow ErrorFactory.parsing(\n\t\t\t\t\"GOAL_SEEK_REQUIRES_VARIABLE_NAME\",\n\t\t\t\t`Goal seek needs the name of the variable to vary here, as in \"solve line 4 for rate = 900\".`,\n\t\t\t\t{ found: variable?.type ?? \"end of input\" },\n\t\t\t);\n\t\t}\n\t\tparser.consume();\n\n\t\tparser.consume(\"EQUALS\");\n\n\t\t// Push the two known arguments first, then let the target parse inline so\n\t\t// the stack ends as [line, variable, target] for the CALL_PLUGIN below.\n\t\tbuilder.emitOpcode(OpCode.PUSH_NUMBER);\n\t\tbuilder.emitNumber(targetLine);\n\t\tbuilder.emitOpcode(OpCode.PUSH_STRING);\n\t\tbuilder.emitString(variable.value);\n\n\t\t// The target value, an ordinary expression (usually a literal).\n\t\tparser.parseExpression(BindingPower.Lowest, builder);\n\n\t\tbuilder.emitPluginCall(GOAL_SEEK_FN_NAME, 3);\n\t}\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport { tokenTypeId } from \"@solve-js/lexer/Token\";\nimport { LexerToken } from \"@solve-js/lexer/ExpressionLexer\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\n\nconst GOAL_SEEK_TYPE_ID = tokenTypeId(\"GOAL_SEEK\");\n\n/**\n * Fuses the bare word `solve` into a `GOAL_SEEK` token, but ONLY when it is\n * immediately followed by a `LINE_REF` (`solve line 4 for rate = 900`).\n *\n * The lookahead is what keeps this from touching anything that already worked.\n * `solve(x^2-4=0, x)` puts an `LPAREN` after `solve`, which\n * `SymbolicCallNormalizerRule` fuses into `SOLVE_FN` instead; a `LINE_REF`\n * there is a different shape entirely, so the two never compete. And `:solve =\n * 2` keeps `solve` usable as an ordinary variable name, since `solve` is never\n * a bare lexer keyword and the colon guard below refuses the one case where a\n * definition's name could sit directly before a line reference.\n *\n * Depends on `LineRefNormalizerRule` having already fused `line 4` into a\n * `LINE_REF` in an earlier pass. The normalizer's multi-pass loop guarantees\n * that: the line-ref rule runs at priority 80, this one lower, and a fusion\n * only becomes visible to other rules on the pass after it lands, so by the\n * time this rule sees `solve` the `LINE_REF` beside it already exists.\n *\n * @param priority - Rule ordering within the normalizer, below the line-ref\n * rule's own band so this reads a `LINE_REF` the earlier rule minted.\n * @returns The normalizer rule.\n */\nexport function goalSeekNormalizerRule(priority = 75): NormalizerRule {\n\treturn {\n\t\tname: \"goalseek:solve-line\",\n\t\tpriority,\n\t\tmatch(tokens: Token[], pos: number): NormalizerMatch | null {\n\t\t\t// A definition's own name (`:solve = ...`) is never this trigger, so a\n\t\t\t// COLON immediately before the word rules it out, the same guard every\n\t\t\t// other trigger-word rule in this codebase uses.\n\t\t\tif (pos > 0 && tokens[pos - 1].type === \"COLON\") return null;\n\n\t\t\tconst token = tokens[pos];\n\t\t\tif (!token || token.type !== \"IDENT\" || token.value.toLowerCase() !== \"solve\") return null;\n\t\t\tif (tokens[pos + 1]?.type !== \"LINE_REF\") return null;\n\n\t\t\t// consumed = 1: only the word \"solve\" becomes GOAL_SEEK; the LINE_REF\n\t\t\t// stays for GoalSeekParselet to read as the target line.\n\t\t\treturn {\n\t\t\t\tconsumed: 1,\n\t\t\t\treplacement: [\n\t\t\t\t\tnew LexerToken(\"GOAL_SEEK\", GOAL_SEEK_TYPE_ID, token.value, token.value, token.offset, 0, token.line, token.col),\n\t\t\t\t],\n\t\t\t\truleName: \"goalseek:solve-line\",\n\t\t\t};\n\t\t},\n\t};\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport { GoalSeekParselet } from \"./parselets/GoalSeekParselet\";\nimport { goalSeekNormalizerRule } from \"./normalizer/GoalSeekNormalizerRule\";\nimport { GOAL_SEEK_FN_NAME, goalSeekHandler } from \"./GoalSeekPluginFunctions\";\n\n/**\n * Goal seek, inverting a line against a target.\n *\n * `solve line 4 for rate = 900` reads as \"find the value of rate that makes\n * line 4 equal 900\". It is the answer to the forwards-only engine's one\n * standing gap: every \"what input gives me this answer\" otherwise means editing\n * a number and re-reading the result until it looks right (see GitHub issue\n * #98). The relationship comes from a line reference, so there is a well-defined\n * target to solve against without inventing any syntax for the relationship\n * itself, the line already expresses it.\n *\n * Grammar and search live apart, the same split every other package here uses:\n * - `GoalSeekNormalizerRule` fuses the bare word `solve` into a GOAL_SEEK\n * token, but only directly before a LINE_REF, so `solve(x^2-4=0, x)` (the\n * algebra verb) and `:solve = 2` (a variable) are both untouched.\n * - `GoalSeekParselet` reads `line N for <var> = <target>` and emits the\n * plugin call.\n * - `GoalSeekPluginFunctions` does the work: a closed-form inversion when the\n * line is closed form in the variable, a bounded, hard-capped numeric search\n * otherwise. See its module doc for the search domain, the monotonicity\n * assumption, and the iteration cap.\n *\n * Trigger-word decision: `solve` is never a bare lexer keyword (it is the\n * product's own name and a plausible variable), so, exactly like the algebra\n * verbs' own `symbolic:call` rule, it is fused only in the one shape that means\n * goal seek, `solve` immediately before a line reference.\n */\nexport const GOALSEEK_PACKAGE: IEnginePackage = {\n\tname: \"solve-goalseek\",\n\tnormalizerRules: [goalSeekNormalizerRule()],\n\tprefixParselets: { GOAL_SEEK: new GoalSeekParselet() },\n\tpluginFunctions: { [GOAL_SEEK_FN_NAME]: goalSeekHandler },\n\t// GOAL_SEEK's highlight category lives in the static TokenCategoryMap\n\t// alongside every other token declared in Token.ts's TokenTypes (the same\n\t// place the lines package's LINE_REF etc. are categorized), so it is not\n\t// repeated here.\n};\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { OpCode } from \"@solve-js/parser/OpCode\";\n\n/**\n * A `#hex` colour literal (`#f00`, `#ff0000`, `#ff0000aa`). The core lexer has\n * already validated the shape and emitted a single `HEX_COLOUR` token carrying\n * the literal text, so this just hands that text to the same parse handler\n * `color(\"#...\")` uses: push the string, call the colour-parse plugin. Parsing\n * cannot fail here (the lexer only emits `HEX_COLOUR` for a valid 3/4/6/8-digit\n * form), but routing through the one handler keeps a single source of truth.\n */\nexport class HexColourParselet implements PrefixParselet {\n\treadonly category = \"Colour\";\n\tparse(_parser: Parser, token: Token, builder: BytecodeBuilder): void {\n\t\tbuilder.emitOpcode(OpCode.PUSH_STRING);\n\t\tbuilder.emitString(token.value);\n\t\tbuilder.emitPluginCall(\"colour\", 1);\n\t}\n}\n","/**\n * Runtime handlers for the colour functions, dispatched through `CALL_PLUGIN`.\n *\n * Each handler reads its already-evaluated arguments off the stack (as `Value`s),\n * validates arity and type, and returns a colour `Value` (or a `Number` for the\n * measuring functions `contrast`/`luminance`). A bad argument surfaces a clear,\n * coded error Value rather than a silent wrong colour, matching the error\n * discipline the rest of the engine uses.\n *\n * The channel maths lives in the pure {@link ./ColourMath} module; these handlers\n * only marshal `Value`s in and out of it. Only the name-to-handler map is\n * exported (the rest is internal wiring): the parselets, normalizer and\n * completions reach for that one map alone.\n */\nimport { Value, ValueType, colourValue, numberValue, boolValue, stringValue, errorValue, type ColourData } from \"@solve-js/vm/Value\";\nimport type { PluginFunctionHandler } from \"@solve-js/engine/EngineContext\";\nimport * as Colour from \"./ColourMath\";\n\n/** Scoped error codes this package owns (see `errors/ErrorCode.ts` convention). */\nexport const ColourErrorCodes = {\n\t/** A string was not a hex colour or a known CSS colour name. */\n\tCOLOUR_INVALID: \"COLOUR_INVALID\",\n\t/** A function expected a colour argument and got something else. */\n\tCOLOUR_EXPECTED_COLOUR: \"COLOUR_EXPECTED_COLOUR\",\n\t/** A function was called with the wrong number or type of arguments. */\n\tCOLOUR_BAD_ARGUMENTS: \"COLOUR_BAD_ARGUMENTS\",\n} as const;\n\n/** The colour payload of a Value, or null if it is not a colour. */\nfunction colourOf(v: Value | undefined): ColourData | null {\n\treturn v && v.type === ValueType.Colour ? (v.value as ColourData) : null;\n}\n\n/**\n * Read an amount argument as a fraction. A `Percentage` already stores its\n * fraction (0.2 for `20%`), a bare number greater than 1 is read as a percent\n * (`20` means 0.2), and a number in 0..1 is taken as the fraction itself. So\n * `20%`, `0.2` and `20` all mean the same adjustment.\n */\nfunction readAmount(v: Value | undefined): number {\n\tif (!v) return 0;\n\tconst n = v.toNumber();\n\tif (v.type === ValueType.Percentage) return n;\n\treturn n > 1 ? n / 100 : n;\n}\n\nfunction badArgs(fn: string, expected: string): Value {\n\treturn errorValue(ColourErrorCodes.COLOUR_BAD_ARGUMENTS, `${fn}(...) expects ${expected}`);\n}\n\nfunction notAColour(fn: string): Value {\n\treturn errorValue(ColourErrorCodes.COLOUR_EXPECTED_COLOUR, `${fn}(...) expects a colour as its first argument`);\n}\n\n// ── Constructors ────────────────────────────────────────────────────────\n\n/** `color(\"#ff0000\")` / `colour(\"red\")`, and the backing handler for a `#hex` literal. */\nfunction colourParseHandler(args: Value[]): Value {\n\tconst arg = args[0];\n\tif (!arg || arg.type !== ValueType.String || typeof arg.value !== \"string\") {\n\t\treturn badArgs(\"color\", \"a string, e.g. color(\\\"#ff0000\\\") or color(\\\"red\\\")\");\n\t}\n\tconst parsed = Colour.parseColour(arg.value);\n\tif (!parsed) {\n\t\treturn errorValue(\n\t\t\tColourErrorCodes.COLOUR_INVALID,\n\t\t\t`\"${arg.value}\" is not a hex colour (#rgb, #rgba, #rrggbb, #rrggbbaa) or a CSS colour name`,\n\t\t);\n\t}\n\treturn colourValue(parsed);\n}\n\nfunction rgbHandler(args: Value[]): Value {\n\tif (args.length !== 3) return badArgs(\"rgb\", \"three numbers: rgb(red, green, blue)\");\n\treturn colourValue({\n\t\tr: Colour.clamp255(args[0].toNumber()),\n\t\tg: Colour.clamp255(args[1].toNumber()),\n\t\tb: Colour.clamp255(args[2].toNumber()),\n\t\ta: 1,\n\t\tformat: \"rgb\",\n\t});\n}\n\nfunction rgbaHandler(args: Value[]): Value {\n\tif (args.length !== 4) return badArgs(\"rgba\", \"four numbers: rgba(red, green, blue, alpha)\");\n\treturn colourValue({\n\t\tr: Colour.clamp255(args[0].toNumber()),\n\t\tg: Colour.clamp255(args[1].toNumber()),\n\t\tb: Colour.clamp255(args[2].toNumber()),\n\t\ta: Colour.clamp01(readAmount(args[3])),\n\t\tformat: \"rgba\",\n\t});\n}\n\nfunction hslHandler(args: Value[]): Value {\n\tif (args.length !== 3) return badArgs(\"hsl\", \"three values: hsl(hue, saturation%, lightness%)\");\n\tconst { r, g, b } = Colour.hslToRgb(args[0].toNumber(), readAmount(args[1]), readAmount(args[2]));\n\treturn colourValue({ r, g, b, a: 1, format: \"hsl\" });\n}\n\nfunction hslaHandler(args: Value[]): Value {\n\tif (args.length !== 4) return badArgs(\"hsla\", \"four values: hsla(hue, saturation%, lightness%, alpha)\");\n\tconst { r, g, b } = Colour.hslToRgb(args[0].toNumber(), readAmount(args[1]), readAmount(args[2]));\n\treturn colourValue({ r, g, b, a: Colour.clamp01(readAmount(args[3])), format: \"hsla\" });\n}\n\n// ── Adjusters ───────────────────────────────────────────────────────────\n\n/** Build a one-colour, one-amount adjuster handler (lighten/darken/saturate/desaturate). */\nfunction adjuster(name: string, op: (c: ColourData, amount: number) => ColourData): (args: Value[]) => Value {\n\treturn (args: Value[]): Value => {\n\t\tconst c = colourOf(args[0]);\n\t\tif (!c) return notAColour(name);\n\t\tif (args.length < 2) return badArgs(name, `a colour and an amount, e.g. ${name}(#3366cc, 20%)`);\n\t\treturn colourValue(op(c, readAmount(args[1])));\n\t};\n}\n\nconst lightenHandler = adjuster(\"lighten\", Colour.lighten);\nconst darkenHandler = adjuster(\"darken\", Colour.darken);\nconst saturateHandler = adjuster(\"saturate\", Colour.saturate);\nconst desaturateHandler = adjuster(\"desaturate\", Colour.desaturate);\n\nfunction rotateHandler(args: Value[]): Value {\n\tconst c = colourOf(args[0]);\n\tif (!c) return notAColour(\"rotate\");\n\tif (args.length < 2) return badArgs(\"rotate\", \"a colour and a hue angle in degrees, e.g. rotate(#ff0000, 90)\");\n\treturn colourValue(Colour.rotateHue(c, args[1].toNumber()));\n}\n\nfunction complementHandler(args: Value[]): Value {\n\tconst c = colourOf(args[0]);\n\tif (!c) return notAColour(\"complement\");\n\treturn colourValue(Colour.complement(c));\n}\n\nfunction grayscaleHandler(args: Value[]): Value {\n\tconst c = colourOf(args[0]);\n\tif (!c) return notAColour(\"grayscale\");\n\treturn colourValue(Colour.grayscale(c));\n}\n\nfunction invertHandler(args: Value[]): Value {\n\tconst c = colourOf(args[0]);\n\tif (!c) return notAColour(\"invert\");\n\treturn colourValue(Colour.invert(c, args.length > 1 ? readAmount(args[1]) : 1));\n}\n\nfunction mixHandler(args: Value[]): Value {\n\tconst a = colourOf(args[0]);\n\tconst b = colourOf(args[1]);\n\tif (!a || !b) return errorValue(ColourErrorCodes.COLOUR_EXPECTED_COLOUR, \"mix(...) expects two colours to blend\");\n\treturn colourValue(Colour.mix(a, b, args.length > 2 ? readAmount(args[2]) : 0.5));\n}\n\nfunction alphaHandler(args: Value[]): Value {\n\tconst c = colourOf(args[0]);\n\tif (!c) return notAColour(\"alpha\");\n\t// One argument reads the alpha, two set it: `alpha(c)` is 0..1, `alpha(c, 0.5)`\n\t// returns the colour at that alpha.\n\tif (args.length < 2) return numberValue(c.a);\n\treturn colourValue(Colour.withAlpha(c, readAmount(args[1])));\n}\n\n// ── Measurements (return a Number) ──────────────────────────────────────\n\nfunction contrastHandler(args: Value[]): Value {\n\tconst a = colourOf(args[0]);\n\tconst b = colourOf(args[1]);\n\tif (!a || !b) return errorValue(ColourErrorCodes.COLOUR_EXPECTED_COLOUR, \"contrast(...) expects two colours\");\n\treturn numberValue(Colour.contrastRatio(a, b));\n}\n\nfunction luminanceHandler(args: Value[]): Value {\n\tconst c = colourOf(args[0]);\n\tif (!c) return notAColour(\"luminance\");\n\treturn numberValue(Colour.relativeLuminance(c));\n}\n\n// ── Channel extractors (return a Number) ────────────────────────────────\n\n/** Build a one-colour extractor that returns a channel as a Number. */\nfunction extractor(name: string, get: (c: ColourData) => number): (args: Value[]) => Value {\n\treturn (args: Value[]): Value => {\n\t\tconst c = colourOf(args[0]);\n\t\tif (!c) return notAColour(name);\n\t\treturn numberValue(get(c));\n\t};\n}\n\nconst redHandler = extractor(\"red\", (c) => c.r);\nconst greenHandler = extractor(\"green\", (c) => c.g);\nconst blueHandler = extractor(\"blue\", (c) => c.b);\nconst hueHandler = extractor(\"hue\", (c) => Colour.rgbToHsl(c.r, c.g, c.b).h);\nconst saturationHandler = extractor(\"saturation\", (c) => Colour.rgbToHsl(c.r, c.g, c.b).s * 100);\nconst lightnessHandler = extractor(\"lightness\", (c) => Colour.rgbToHsl(c.r, c.g, c.b).l * 100);\n\n// ── Extra colour-space constructors ─────────────────────────────────────\n\nfunction hsvHandler(args: Value[]): Value {\n\tif (args.length !== 3) return badArgs(\"hsv\", \"three values: hsv(hue, saturation%, value%)\");\n\tconst { r, g, b } = Colour.hsvToRgb(args[0].toNumber(), readAmount(args[1]), readAmount(args[2]));\n\treturn colourValue({ r, g, b, a: 1, format: \"hex\" });\n}\n\nfunction hsvaHandler(args: Value[]): Value {\n\tif (args.length !== 4) return badArgs(\"hsva\", \"four values: hsva(hue, saturation%, value%, alpha)\");\n\tconst { r, g, b } = Colour.hsvToRgb(args[0].toNumber(), readAmount(args[1]), readAmount(args[2]));\n\treturn colourValue({ r, g, b, a: Colour.clamp01(readAmount(args[3])), format: \"hex\" });\n}\n\nfunction hwbHandler(args: Value[]): Value {\n\tif (args.length !== 3) return badArgs(\"hwb\", \"three values: hwb(hue, whiteness%, blackness%)\");\n\tconst { r, g, b } = Colour.hwbToRgb(args[0].toNumber(), readAmount(args[1]), readAmount(args[2]));\n\treturn colourValue({ r, g, b, a: 1, format: \"hex\" });\n}\n\n// ── Tint / shade / tone (mix toward white / black / grey) ────────────────\n\nconst tintHandler = adjuster(\"tint\", Colour.tint);\nconst shadeHandler = adjuster(\"shade\", Colour.shade);\nconst toneHandler = adjuster(\"tone\", Colour.tone);\n\n// ── Accessibility helpers ───────────────────────────────────────────────\n\nfunction isdarkHandler(args: Value[]): Value {\n\tconst c = colourOf(args[0]);\n\tif (!c) return notAColour(\"isdark\");\n\treturn boolValue(Colour.isDark(c));\n}\n\nfunction islightHandler(args: Value[]): Value {\n\tconst c = colourOf(args[0]);\n\tif (!c) return notAColour(\"islight\");\n\treturn boolValue(!Colour.isDark(c));\n}\n\nfunction readableHandler(args: Value[]): Value {\n\tconst c = colourOf(args[0]);\n\tif (!c) return notAColour(\"readable\");\n\treturn colourValue(Colour.readableColour(c));\n}\n\n/**\n * The minimum contrast ratio to test against. A number is used as the ratio\n * directly; a level name maps to the WCAG threshold (`AA` = 4.5, `AAA` = 7,\n * `AA large` = 3, `AAA large` = 4.5); anything else, or absent, defaults to AA.\n */\nfunction readWcagThreshold(v: Value | undefined): number {\n\tif (!v) return Colour.WCAG_THRESHOLDS.aa;\n\tif (v.type === ValueType.Number) return v.toNumber();\n\tif (v.type === ValueType.String) {\n\t\tconst key = (v.value as string).trim().toLowerCase();\n\t\tif (key === \"aaa\") return Colour.WCAG_THRESHOLDS.aaa;\n\t\tif (key === \"aa large\" || key === \"aa-large\" || key === \"large\") return Colour.WCAG_THRESHOLDS.aaLarge;\n\t\tif (key === \"aaa large\" || key === \"aaa-large\") return Colour.WCAG_THRESHOLDS.aaaLarge;\n\t}\n\treturn Colour.WCAG_THRESHOLDS.aa;\n}\n\nfunction contrastCompliantHandler(args: Value[]): Value {\n\tconst a = colourOf(args[0]);\n\tconst b = colourOf(args[1]);\n\tif (!a || !b) {\n\t\treturn errorValue(ColourErrorCodes.COLOUR_EXPECTED_COLOUR, \"iscontrastcompliant(...) expects two colours\");\n\t}\n\treturn boolValue(Colour.contrastRatio(a, b) >= readWcagThreshold(args[2]));\n}\n\nfunction wcagLevelHandler(args: Value[]): Value {\n\tconst a = colourOf(args[0]);\n\tconst b = colourOf(args[1]);\n\tif (!a || !b) return errorValue(ColourErrorCodes.COLOUR_EXPECTED_COLOUR, \"wcaglevel(...) expects two colours\");\n\treturn stringValue(Colour.wcagLevel(a, b));\n}\n\n/**\n * The call-name grammar: every function name (and alias) a `COLOUR_CALL` token\n * can carry, mapped to the handler that runs it. Also the source of truth\n * for the normalizer (which names to fuse) and completions.\n */\nexport const COLOUR_FUNCTION_HANDLERS: Readonly<Record<string, PluginFunctionHandler>> = {\n\tcolor: colourParseHandler,\n\tcolour: colourParseHandler,\n\trgb: rgbHandler,\n\trgba: rgbaHandler,\n\thsl: hslHandler,\n\thsla: hslaHandler,\n\tlighten: lightenHandler,\n\tdarken: darkenHandler,\n\tsaturate: saturateHandler,\n\tdesaturate: desaturateHandler,\n\tdesat: desaturateHandler,\n\trotate: rotateHandler,\n\tspin: rotateHandler,\n\tadjusthue: rotateHandler,\n\tcomplement: complementHandler,\n\tgrayscale: grayscaleHandler,\n\tgreyscale: grayscaleHandler,\n\tinvert: invertHandler,\n\tmix: mixHandler,\n\talpha: alphaHandler,\n\topacity: alphaHandler,\n\tfade: alphaHandler,\n\tcontrast: contrastHandler,\n\tluminance: luminanceHandler,\n\tred: redHandler,\n\tgreen: greenHandler,\n\tblue: blueHandler,\n\thue: hueHandler,\n\tsaturation: saturationHandler,\n\tlightness: lightnessHandler,\n\thsv: hsvHandler,\n\thsb: hsvHandler,\n\thsva: hsvaHandler,\n\thwb: hwbHandler,\n\ttint: tintHandler,\n\tshade: shadeHandler,\n\ttone: toneHandler,\n\tnegate: invertHandler,\n\tisdark: isdarkHandler,\n\tislight: islightHandler,\n\treadable: readableHandler,\n\tcontrastcolor: readableHandler,\n\tcontrastcolour: readableHandler,\n\tiscontrastcompliant: contrastCompliantHandler,\n\twcaglevel: wcagLevelHandler,\n\twcag: wcagLevelHandler,\n};\n","import { PrefixParselet } from \"@solve-js/parser/Parselet\";\nimport { Parser } from \"@solve-js/parser/Parser\";\nimport { Token } from \"@solve-js/lexer/Token\";\nimport { BytecodeBuilder } from \"@solve-js/parser/BytecodeBuilder\";\nimport { BindingPower } from \"@solve-js/parser/BindingPower\";\nimport { ErrorFactory } from \"@solve-js/errors/UnifiedErrorFramework\";\nimport { COLOUR_FUNCTION_HANDLERS } from \"../ColourPluginFunctions\";\n\n/**\n * A colour function call with parenthesised arguments: `rgb(255, 0, 0)`,\n * `lighten(#3366cc, 20%)`, `mix(#f00, #00f, 0.25)`, `contrast(a, b)`.\n *\n * The `ColourCallNormalizerRule` has already fused a recognised `name(` into a\n * single `COLOUR_CALL` token carrying the function name, so this resolves that\n * name to its plugin index at parse time and emits the same comma-separated\n * argument loop `FunctionCallParselet` uses, dispatched through `CALL_PLUGIN`.\n * Argument arity and type are checked by the handler at run time, so a wrong\n * call surfaces a coded error Value rather than a parse failure.\n */\nexport class ColourCallParselet implements PrefixParselet {\n\treadonly category = \"Colour\";\n\tparse(parser: Parser, token: Token, builder: BytecodeBuilder): void {\n\t\tconst name = token.value.toLowerCase();\n\t\tif (COLOUR_FUNCTION_HANDLERS[name] === undefined) {\n\t\t\t// Unreachable in practice: the normalizer only mints COLOUR_CALL for a\n\t\t\t// name in COLOUR_FUNCTION_HANDLERS. Defensive, and a clear message if the\n\t\t\t// two ever drift apart.\n\t\t\tthrow ErrorFactory.execution(\"UNKNOWN_COLOUR_FUNCTION\", `Unknown colour function: ${name}`, {\n\t\t\t\tfunctionName: name,\n\t\t\t});\n\t\t}\n\n\t\tparser.consume(\"LPAREN\");\n\t\tlet argCount = 0;\n\t\tif (parser.peek()?.type !== \"RPAREN\") {\n\t\t\tparser.parseExpression(BindingPower.Lowest, builder);\n\t\t\targCount++;\n\t\t\twhile (parser.match(\"COMMA\")) {\n\t\t\t\tparser.parseExpression(BindingPower.Lowest, builder);\n\t\t\t\targCount++;\n\t\t\t}\n\t\t}\n\t\tparser.consume(\"RPAREN\");\n\n\t\tbuilder.emitPluginCall(name, argCount);\n\t}\n}\n","import type { Token } from \"@solve-js/lexer/Token\";\nimport { tokenTypeId } from \"@solve-js/lexer/Token\";\nimport { LexerToken } from \"@solve-js/lexer/ExpressionLexer\";\nimport type { NormalizerRule, NormalizerMatch } from \"@solve-js/normalizer/NormalizerRule\";\nimport { COLOUR_FUNCTION_HANDLERS } from \"../ColourPluginFunctions\";\n\n/**\n * The recognised call-names, derived once from the handler map's keys. Kept as a\n * `Set` so the per-token {@link colourCallNormalizerRule} membership test stays\n * O(1) rather than scanning the map on every identifier.\n */\nconst COLOUR_FUNCTION_NAMES = new Set(Object.keys(COLOUR_FUNCTION_HANDLERS));\n\n/**\n * Fuse a colour function name into a single `COLOUR_CALL` token, but ONLY when\n * the bare word is immediately followed by `LPAREN` (mirrors\n * `mapreduce`/`lines`' conditional-on-`LPAREN` fusion). This keeps `mix`,\n * `rotate`, `alpha`, `fade` and the rest usable as ordinary variable names when\n * they are not being called: `:mix = 5` stays a variable, `mix(a, b)` becomes a\n * call. The recognised names come from {@link COLOUR_FUNCTION_HANDLERS}, so the\n * grammar and the runtime dispatch can never drift apart.\n */\nexport function colourCallNormalizerRule(priority = 80): NormalizerRule {\n\treturn {\n\t\tname: \"colour:call\",\n\t\tpriority,\n\t\tmatch(tokens: Token[], pos: number): NormalizerMatch | null {\n\t\t\tconst token = tokens[pos];\n\t\t\tif (!token || token.type !== \"IDENT\") return null;\n\t\t\tconst word = token.value.toLowerCase();\n\t\t\tif (!COLOUR_FUNCTION_NAMES.has(word)) return null;\n\t\t\tif (tokens[pos + 1]?.type !== \"LPAREN\") return null;\n\n\t\t\treturn {\n\t\t\t\tconsumed: 1,\n\t\t\t\treplacement: [\n\t\t\t\t\tnew LexerToken(\n\t\t\t\t\t\t\"COLOUR_CALL\",\n\t\t\t\t\t\ttokenTypeId(\"COLOUR_CALL\"),\n\t\t\t\t\t\ttoken.value,\n\t\t\t\t\t\ttoken.value,\n\t\t\t\t\t\ttoken.offset,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\ttoken.line,\n\t\t\t\t\t\ttoken.col,\n\t\t\t\t\t),\n\t\t\t\t],\n\t\t\t\truleName: \"colour:call\",\n\t\t\t};\n\t\t},\n\t};\n}\n","import type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\nimport type { CompletionItem } from \"@solve-js/language\";\nimport { Value, ValueType, colourValue, type ColourData, type ColourFormat } from \"@solve-js/vm/Value\";\nimport { HexColourParselet } from \"./parselets/HexColourParselet\";\nimport { ColourCallParselet } from \"./parselets/ColourCallParselet\";\nimport { colourCallNormalizerRule } from \"./normalizer/ColourCallNormalizerRule\";\nimport { COLOUR_FUNCTION_HANDLERS } from \"./ColourPluginFunctions\";\n\n/**\n * Re-tag a colour's display format for `<colour> as rgb|rgba|hsl|hsla`. The\n * channels are unchanged, only how the value renders; a non-colour passes\n * through untouched (the same lenient shape the other `asConverters` use).\n * `as hex` is not here: `hex` is a built-in converter that lowers to\n * `OpCode.TO_HEX`, which the VM handles for colours directly.\n */\nconst retag = (format: ColourFormat) => (v: Value): Value => {\n\tif (v.type !== ValueType.Colour) return v;\n\tconst c = v.value as ColourData;\n\treturn colourValue({ r: c.r, g: c.g, b: c.b, a: c.a, format });\n};\n\n/** One completion candidate per distinct colour function name. */\nconst completionItems: CompletionItem[] = Object.keys(COLOUR_FUNCTION_HANDLERS).map((label) => ({\n\tlabel,\n\tcategory: \"function\",\n\tdetail: \"colour\",\n}));\n\n/**\n * Colours as first-class values: `#ff0000` hex literals, `rgb()`/`hsl()` and CSS\n * named colours via `color(\"...\")`, and the DevTools-style adjusters (`lighten`,\n * `darken`, `rotate`, `mix`, `contrast`, ...). A colour result carries its\n * channels and a render-ready CSS string across the worker boundary so a host\n * can draw a swatch. See `ColourMath.ts` for the maths.\n *\n * The `#hex` literal is recognised by the core lexer (it has to disambiguate `#`\n * from a markdown heading/comment); this package supplies the parselet that turns\n * the resulting `HEX_COLOUR` token into a colour. Function names are fused only\n * when immediately called, so `mix`/`rotate`/`alpha` stay usable as variables.\n */\nexport const COLOUR_PACKAGE: IEnginePackage = {\n\tname: \"solve-colour\",\n\tprefixParselets: {\n\t\tHEX_COLOUR: new HexColourParselet(),\n\t\tCOLOUR_CALL: new ColourCallParselet(),\n\t},\n\tnormalizerRules: [colourCallNormalizerRule()],\n\tpluginFunctions: COLOUR_FUNCTION_HANDLERS,\n\tasConverters: {\n\t\trgb: retag(\"rgb\"),\n\t\trgba: retag(\"rgba\"),\n\t\thsl: retag(\"hsl\"),\n\t\thsla: retag(\"hsla\"),\n\t},\n\t// HEX_COLOUR/COLOUR_CALL categories live in the core TokenCategoryMap (both\n\t// are declared TokenTypes), so no per-package tokenCategories are needed.\n\tcompletionItems,\n};\n","/**\n * Built-in packages.\n *\n * Each domain is a self-contained package (its own directory, its own\n * `{Domain}Package.ts` defining an IEnginePackage, its own `index.ts`\n * barrel), the same shape as third-party packages such as the OSRS\n * example in `src/solve-js/examples/osrs/`. This file's only job is to\n * assemble them into BUILTIN_PACKAGES; it re-exports each named package\n * too, since existing call sites import them directly from here.\n *\n * This enables:\n * - Selective disable of built-in packages\n * - External packages to replace/extend built-in ones\n */\nimport type { IEnginePackage } from \"@solve-js/api/PackageRegistry\";\n\nimport { ARITHMETIC_PACKAGE } from \"./arithmetic\";\nimport { PERCENTAGE_PACKAGE } from \"./percentage\";\nimport { FUNCTION_PACKAGE } from \"./function\";\nimport { DATETIME_PACKAGE } from \"./datetime\";\nimport { TIME_PACKAGE } from \"./time\";\nimport { DICE_PACKAGE } from \"./dice\";\nimport { VARIABLES_PACKAGE } from \"./variables\";\nimport { UOM_PACKAGE } from \"./uom\";\nimport { CURRENCY_PACKAGE, createCurrencyPackage } from \"./currency\";\nimport { VECTOR_PACKAGE } from \"./vector\";\nimport { MATRIX_PACKAGE } from \"./matrix\";\nimport { MAPREDUCE_PACKAGE } from \"./mapreduce\";\nimport { SYMBOLIC_PACKAGE } from \"./symbolic\";\nimport { BIGINT_PACKAGE } from \"./biginteger\";\nimport { CONDITIONALS_PACKAGE } from \"./conditionals\";\nimport { CONVERTERS_PACKAGE } from \"./converters\";\nimport { MATHPHRASES_PACKAGE } from \"./mathphrases\";\nimport { FINANCE_PACKAGE } from \"./finance\";\nimport { UNCERTAINTY_PACKAGE } from \"./uncertainty\";\nimport { WEATHER_PACKAGE } from \"./weather\";\nimport { createStocksPackage } from \"./stocks\";\nimport { createKnowledgePackage } from \"./knowledge\";\nimport { LINES_PACKAGE } from \"./lines\";\nimport { GOALSEEK_PACKAGE } from \"./goalseek\";\nimport { TABLES_PACKAGE } from \"./tables\";\nimport { COLOUR_PACKAGE } from \"./colour\";\nimport { TAGS_PACKAGE } from \"./tags\";\n\nexport {\n ARITHMETIC_PACKAGE,\n PERCENTAGE_PACKAGE,\n FUNCTION_PACKAGE,\n DATETIME_PACKAGE,\n TIME_PACKAGE,\n DICE_PACKAGE,\n VARIABLES_PACKAGE,\n UOM_PACKAGE,\n CURRENCY_PACKAGE,\n VECTOR_PACKAGE,\n MATRIX_PACKAGE,\n MAPREDUCE_PACKAGE,\n SYMBOLIC_PACKAGE,\n BIGINT_PACKAGE,\n CONDITIONALS_PACKAGE,\n CONVERTERS_PACKAGE,\n MATHPHRASES_PACKAGE,\n FINANCE_PACKAGE,\n UNCERTAINTY_PACKAGE,\n WEATHER_PACKAGE,\n createStocksPackage,\n createCurrencyPackage,\n createKnowledgePackage,\n LINES_PACKAGE,\n GOALSEEK_PACKAGE,\n TABLES_PACKAGE,\n COLOUR_PACKAGE,\n TAGS_PACKAGE,\n};\n\n// ── All built-in packages (registration order matters: arithmetic first) ──\n// Note: OSRS is NOT a built-in package, it's a full worked example of\n// writing a package with the framework (lexer plugin, async resolver, VM\n// handler), kept in src/solve-js/examples/osrs/ rather than shipped as\n// part of the engine. See ExpressionEngine's `packages` constructor\n// parameter to register it (or any other package) alongside these.\n//\n// Of the three \"Live Data\" packages (weather/stocks/knowledge), only\n// WEATHER_PACKAGE is included below. Open-Meteo (weather's provider) is\n// genuinely free and keyless, no configuration burden on a host who\n// doesn't want its network calls, who can filter it out of their own\n// `packages` array. Stocks and Knowledge have no equivalent free provider\n// (see their own module docs), unconfigured, `createStocksPackage()`/\n// `createKnowledgePackage()` do nothing useful beyond returning an honest\n// \"not configured\" error, so, matching OSRS's own precedent, they are\n// exported but deliberately left OUT of BUILTIN_PACKAGES. A host that\n// wants them calls the factory with their own fetch function/API key and\n// adds the result to their ExpressionEngine's `packages` array directly.\n/**\n * The packages an engine registers when the caller names none.\n *\n * Twenty-five of the twenty-seven. Stocks and knowledge are excluded because\n * both need a host-supplied data source and do nothing useful without one, so\n * registering them by default would only produce NOT_CONFIGURED results.\n *\n * Pass a filtered copy to the {@link ExpressionEngine} constructor to opt out\n * of a feature, or add to it to register your own alongside the built-ins.\n */\nexport const BUILTIN_PACKAGES: IEnginePackage[] = [\n ARITHMETIC_PACKAGE,\n PERCENTAGE_PACKAGE,\n FUNCTION_PACKAGE,\n DATETIME_PACKAGE,\n TIME_PACKAGE,\n DICE_PACKAGE,\n VARIABLES_PACKAGE,\n UOM_PACKAGE,\n CURRENCY_PACKAGE,\n VECTOR_PACKAGE,\n MATRIX_PACKAGE,\n MAPREDUCE_PACKAGE,\n SYMBOLIC_PACKAGE,\n BIGINT_PACKAGE,\n CONDITIONALS_PACKAGE,\n CONVERTERS_PACKAGE,\n MATHPHRASES_PACKAGE,\n FINANCE_PACKAGE,\n UNCERTAINTY_PACKAGE,\n WEATHER_PACKAGE,\n LINES_PACKAGE,\n // After LINES_PACKAGE: goal seek's normalizer reads a LINE_REF the lines\n // rule mints, and its parselet targets a line reference.\n GOALSEEK_PACKAGE,\n TABLES_PACKAGE,\n COLOUR_PACKAGE,\n // After MATHPHRASES_PACKAGE and LINES_PACKAGE: tag aggregates reuse the\n // `total of`/`count of`/`average of` fusions and walk the document the same\n // way the lines cross-line reads do.\n TAGS_PACKAGE,\n];\n"]}
|