Skip to content

This site describes solve-engine as it is on main: 2.43.0, which npm does not have yet. npm installs 2.40.0, so a page may show an answer that version does not give yet.

extractReadsAndWrites

function extractReadsAndWrites(tokens, onName?): {
reads: string[];
writes: string[];
};

Defined in: packages/engine/src/engine/ExpressionEngineSafety.ts:269

Extract variable reads and writes from a token stream.

Handles both IDENT and UNIT tokens as potential variable references. UNIT tokens occur when the variable name collides with a known unit (e.g., “b” for bits, “s” for seconds). The colon prefix unambiguously signals a variable definition context (handled by VariableParselet). A standalone UNIT token is only a real variable reference when it isn’t in unit-literal position (see isUnitLiteralContext) otherwise it’s a quantity/conversion unit name, never LOAD_VAR’d.

Also detects user-defined-function DEFINITIONS (name(params) = body) as a read+write of the function’s own name, mirroring :name = value’s existing convention of registering the defined name as both, and excludes the definition’s own PARAMETER names from reads/writes entirely (see collectFunctionParamNames). A function CALL (name(args), no trailing =) needs no special detection: the call’s own name falls through to the ordinary bare-identifier read-tracking below, the same as any other LOAD_VAR-producing identifier. This is already correct once calls compile successfully, no change needed.

A goal seek’s unknown (solve line 4 for rate = 900) is neither a read nor a write, although rate = has the shape of a definition: the seek binds it in its own call frame and the document’s variable is untouched. See goalSeekUnknownIndex.

The same is true of a what-if’s inputs (line 4 with deposit = 150000) and a sweep’s input (line 4 for rate from 3% to 6% step 1%): each is a name the form holds fixed in a scratch re-run, never a write to the document’s own variable, and never a read of it either. The value an input is given is an ordinary expression, and its reads count as usual.

onName, when given, is told the position of every name as it is decided, so a caller that needs spans (the language service’s references and rename) reads them from these exact rules rather than a second copy that could drift. The graph’s callers omit it and pay nothing.

ParameterTypeDescription
tokensToken[]One expression’s normalised tokens.
onName?(index, key, use) => voidCalled with each name’s token index, its graph key (a global’s key carries the global: prefix) and how it is used.
{
reads: string[];
writes: string[];
}

The names read and written, as graph keys.

reads: string[];
writes: string[];