Skip to content

This site describes solve-engine 2.44.0, the version npm installs.

LineExecutionContext

Defined in: packages/engine/src/vm/VM.ts:329

Per-line execution context, threaded optionally through executeBytecode down to CALL_PLUGIN’s plugin-function handlers (see vm/VMBuiltins.ts’s pluginFunctionRegistry).

Exists so a package can implement cross-line features (prev, line<N>, range/above aggregation. See packages/lines/) without every other plugin function having to care: it’s optional, and every existing handler ignores it unchanged. Before this, a plugin function’s only input was its own call-site arguments, no line number, no access to any other line’s cached result. getLineResult/isLineBoundary are both undefined when there’s no real document (e.g. ExpressionEngine.evaluateExpression()’s single-expression path, which uses lineIndex = -1 as its existing “no document” sentinel), a plugin function needing document access must check for that itself and return a clear error, never silently treat it as line 0.

calendar?: CalendarBackend;

Defined in: packages/engine/src/vm/VM.ts:369

The calendar backend this engine computes dates with (EngineContext.calendar). A plugin function that reads or steps a date computes through it, so the date it answers agrees with the one the VM’s own opcodes would produce. Absent means the Date backend; calendarOf() in calendar/DateCalendar.ts resolves either case.


evaluateLineWithBinding?: (lineNumber, variable, bound, symbolicTolerant) => Value;

Defined in: packages/engine/src/vm/VM.ts:519

Re-evaluate another line’s already-compiled expression with variable bound to bound for that one evaluation, without disturbing the document’s own value for it. This is the primitive goal seek (packages/goalseek/) drives: binding a numeric candidate probes the relationship, binding a symbolic placeholder (with symbolicTolerant) reads it back in closed form. Returns an error Value when there is no document, the line is not a plain expression ready to run, or its re-evaluation itself faults. The binding is a call frame, so it shadows the document’s value exactly the way a function parameter does and is gone the moment the probe returns.

ParameterType
lineNumbernumber
variablestring
boundValue
symbolicTolerantboolean

Value


getLineCount?: () => number;

Defined in: packages/engine/src/vm/VM.ts:346

How many lines the document has now; absent when there is no document.

A form that declares a span before reading it (see noteLineRead) needs to know where the document ends: a range written as line 1 : line 3000000 has no line to read past the last one, and declaring three million positions that exist nowhere cost the heap for nothing. The walk that reads the span already stops at the first line it cannot use.

Asked for rather than copied, because one context serves a document for as long as it is open: a count taken when the context was built was the count before the last insert, and a range declared under it stopped a line short.

number


getLineReads?: (lineNumber) => string[] | undefined;

Defined in: packages/engine/src/vm/VM.ts:497

The variables another line’s expression reads, by 1-based line number, or undefined when the line has no evaluated expression (forward reference, out of range, or markdown). Goal seek (packages/goalseek/) uses it to refuse up front when the variable it was asked to vary is one the target line never reads, rather than searching a relationship that cannot move.

ParameterType
lineNumbernumber

string[] | undefined


getLineResult?: (lineNumber, declared?) => Value | undefined;

Defined in: packages/engine/src/vm/VM.ts:419

Look up another line’s cached result by 1-based line number. undefined means not evaluated yet, out of range, or below this line: a note is read from the top, so a line further down is never read, on any pass. No Value is undefined-like, so the two cannot be confused.

On the incremental path a read records that this line depends on that position. Pass declared as true for a read an edge the form already took covers (a member of a tag asked for through getTaggedLines, a line inside a span given to noteFigureSpanRead), so the graph records nothing more; a form that reads a thousand members then costs one edge, not a thousand.

ParameterType
lineNumbernumber
declared?boolean

Value | undefined


getLineText?: (lineNumber) => string | undefined;

Defined in: packages/engine/src/vm/VM.ts:585

The RAW markdown text of line lineNumber (1-based), or undefined when there is no real document or the line is out of range. Distinct from getLineResult, which returns a line’s evaluated Value: a markdown table’s rows are skipped by the evaluator and hold no result, so reading a column as data has to go back to the source text. Backs the tables package (packages/tables/), which walks upward from the current line to find the nearest table and read one of its columns.

ParameterType
lineNumbernumber

string | undefined


getTaggedLines?: (tag) => readonly number[] | undefined;

Defined in: packages/engine/src/vm/VM.ts:462

The 1-based positions of the lines carrying #tag, ascending, or undefined when this path keeps no index and the caller should walk the document itself.

total of #tag used to look at every line of the document, so a notepad of tagged amounts and totals cost aggregates x lines per pass. Both document paths maintain an index instead, and answer from it here.

Asking is also what tells the dependency graph that this line reads every line carrying the tag, as one edge on the tag rather than one per member (#733), so a form that asks here needs no noteLineRead for the members. A line joining or leaving the group later is covered by the same edge.

ParameterType
tagstring

readonly number[] | undefined


getTagGroups?: () => ReadonlyMap<string, readonly number[]> | undefined;

Defined in: packages/engine/src/vm/VM.ts:474

Every category tag the document’s lines carry, lower-cased, each with the 1-based positions of its lines ascending, or undefined when this path keeps no index and the caller should walk the document itself.

What total by tag reads its groups from (#734). The answer is the same object for as long as the document’s text is unchanged, within a pass and across passes, so a form may key work of its own on it. Asking tells the dependency graph that this line reads every tagged line, as one edge, the way getTaggedLines does for one tag.

ReadonlyMap<string, readonly number[]> | undefined


getVariable?: (name) => Value | undefined;

Defined in: packages/engine/src/vm/VM.ts:506

The value the document gives a name as it stands, or undefined when no line has set it. Goal seek reads it to learn the unit its unknown is in, so solve line 3 for price = £1,500 answers in pounds when price is an amount of pounds (#835), and a line that is only sum or total reads it before it totals the block above (#742). Present on every path the engine builds a context for, a single expression included.

ParameterType
namestring

Value | undefined


goalSeekMaxIterations?: number;

Defined in: packages/engine/src/vm/VM.ts:531

The hard ceiling on goal seek’s bisection steps, from config.vm.maxGoalSeekIterations. Carried on the context so the search, which runs as a plugin function with no other view of engine config, is bounded by the host’s configured limit rather than a hardcoded one.


inWhatIf?: boolean;

Defined in: packages/engine/src/vm/VM.ts:354

Set when this pass runs a what-if’s scenario (line 4 with deposit = 150000), which works each line out once in a scratch engine. A form that re-runs another line (goal seek) names that as the reason it cannot answer here, rather than the batch pass or a single expression, which the reader did not write.


isBlockEnd?: (lineNumber) => boolean;

Defined in: packages/engine/src/vm/VM.ts:489

Whether line lineNumber ends the block of figures total above and average above read: a blank line, a heading, a rule, a fence or a table (see lexer/BlockBoundary). A line with no figure that does not end the block, a comment or a blockquote, is passed over (#652). When absent the aggregates stop at isLineBoundary, as they did before.

ParameterType
lineNumbernumber

boolean


isLineBoundary?: (lineNumber) => boolean;

Defined in: packages/engine/src/vm/VM.ts:481

Whether line lineNumber has no figure to read: a blank line, a # heading, or a line the classifier skips (a comment, a blockquote, table markup). A line range passes over such a line; the section and tag forms read it the same way.

ParameterType
lineNumbernumber

boolean


lineIndex: number;

Defined in: packages/engine/src/vm/VM.ts:331

1-based current line number, or -1 when there is no real document (see class doc above).


networkEnabled?: boolean;

Defined in: packages/engine/src/vm/VM.ts:361

Whether this engine may fetch live data (network.enabled). A plugin function that reads a resolver’s cache uses it to say “live data is switched off” when the cache is empty, rather than the “not preflighted” message that describes a different fault. Absent means enabled.


noteFigureSpanRead?: (first, last) => void;

Defined in: packages/engine/src/vm/VM.ts:446

Say that this line reads the figures on lines first to last, before reading any of them: every line in the span except a summary line (a total above, a section or tag total), which a span of figures passes over, as a section total does.

One edge however long the span, where noteLineRead per member is one per line (#733). The graph asks the document which lines in the span are summaries when it follows the edge back, so two totals of one section are not taken to read each other. Absent where there is no document; a form falls back to noteLineRead then, or skips the declaration where that is absent too.

ParameterType
firstnumber
lastnumber

void


noteLineRead?: (lineNumber) => void;

Defined in: packages/engine/src/vm/VM.ts:432

Say that this line is about to read lineNumber, before reading it.

A form that reads several lines stops at the first it cannot use, so the lines after that one are never read and, if reading were the only way the dependency graph learned of a read, never recorded. A cycle that closes through one of those lines was then invisible from scratch and visible from a history that had once read the whole span, and the two paths disagreed about whether the line was on a cycle at all. A form declares its whole span through this first, so what the graph knows does not depend on how far the form got. Absent where there is no document.

ParameterType
lineNumbernumber

void


observeCall?: (call) => void;

Defined in: packages/engine/src/vm/VM.ts:597

Receives every call the VM makes while this line runs: a built-in function, a package’s plugin function, an as converter, or a unit or currency conversion, each with its arguments and the value it pushed.

Set only by ExpressionEngine.explainLine(), which runs the line once more with this attached so a derivation can name each step with the engine’s own numbers. Absent on every ordinary evaluation, where the VM reads it once per program and never calls anything, so evaluation costs the same with or without explaining.

ParameterType
callObservedCall

void


queryClient?: QueryClient;

Defined in: packages/engine/src/vm/VM.ts:386

The query cache of the engine running this line, where its asynchronous resolvers keep what they fetched. A plugin function that reads a resolved value back (createQueryResolver’s, the historical currency conversion’s) reads it from here, so it reads its own engine’s cache however many engines share the process (#710). Absent on a context no engine built; such a handler falls back to the deprecated getActiveQueryClient().


random?: () => number;

Defined in: packages/engine/src/vm/VM.ts:397

The random source this line draws from, a number in [0, 1) per call, the way calendar is the clock. roll, random(), pick, shuffle, coin and uuid all read it. Unseeded it is Math.random; with a seed (createEngine({ random: { seed } }) or a random seed line) it is a stream keyed on the seed and this line’s own compiled program, so a draw is the same on every run and changes only when its line is edited. Absent means Math.random. A function rather than a value because the context object is reused across a pass (see lineIndex).

number


readScenario?: (name, lineNumber) => Value;

Defined in: packages/engine/src/vm/VM.ts:575

What line lineNumber says with the inputs of the scenario named name in force (line 5 under bull, #744), or the error that stops it.

The scenario is the scenario <name> with ... line above the asking line; none, or two with the same name, is refused by name. It is read as the what-if it stands for (line 5 with ..., with the declaration’s inputs), evaluated where the asking line stands, so every what-if refusal is a scenario’s too. Every line above the asking line is read, so an edit to any of them re-runs it. Absent where there is no document.

ParameterType
namestring
lineNumbernumber

Value


rerunLines?: (lineNumber) =>
| Value
| LineRerun;

Defined in: packages/engine/src/vm/VM.ts:563

Open a re-run of the document up to and including lineNumber, for asking what that line would say if some inputs were different. This is the primitive the what-if and sweep forms (packages/whatif/) drive.

Unlike evaluateLineWithBinding, which re-runs one line with one name bound in a call frame, this re-runs every line from the top of the document to the target, from their text, in a scratch engine of its own. So an input the target reads through another line (payment on line 3 reading deposit, line 4 reading payment) reaches it, and nothing the document holds (its variables, its cached results, its dependency graph) is touched: the scratch engine is thrown away when the session closes.

Returns the session, or an error Value when there is nothing to re-run: the line is out of range, the re-run would be nested inside another one, or a line in the span writes a global :name, which other documents read. Absent where there is no document (the single-expression path). The caller must LineRerun.close the session, in a finally.

ParameterType
lineNumbernumber

| Value | LineRerun


scope?: ScopeId;

Defined in: packages/engine/src/vm/VM.ts:405

The scope this line executes under: the owner of a global :name cell it writes. Absent on a path with no engine context of its own (a warm-up run, the worker, a lone evaluateExpression); the cell opcodes fall back to the engine’s own EngineContext.scope there. See vm/CellScope.ts and docs-internal/plans/CROSS_SCOPE_CELLS.md.


spendWork?: (lineRuns, form) => Value | null;

Defined in: packages/engine/src/vm/VM.ts:543

Charge work that reaches across lines to the current pass, in line runs: a what-if or sweep re-running lines, a span aggregate reading them (goal seek’s probes are charged by evaluateLineWithBinding itself). Returns the refusal when the charge would take the pass past config.vm.maxLineRunsPerPass, and null, having counted it, when it fits (#711). A function rather than a count, since this context serves a whole pass. Absent where there is no document: the single-expression path, whose cross-line forms refuse before they would spend anything. See vm/PassWork.ts.

ParameterType
lineRunsnumber
formstring

Value | null


traceLine?: (lineNumber) => LineTrace;

Defined in: packages/engine/src/vm/VM.ts:605

The lines that fed line lineNumber’s answer, followed upwards, for the inputs of line N form. A forward reference (a line reading a line below it) is reported in the trace and not followed, since from inside a pass the line below has not been worked out. Absent when there is no document to trace in. See ExpressionEngine.traceLine().

ParameterType
lineNumbernumber

LineTrace


week?: WeekShape;

Defined in: packages/engine/src/vm/VM.ts:376

The shape of the week this engine keeps (EngineContext.week): its weekend days and the day a week starts on. Absent means Saturday and Sunday off, starting on Monday; weekOf() in calendar/WeekShape.ts resolves either case.