This commit is contained in:
Andrew Pimenov 2026-10-07 07:00:31 +03:00 • committed by GitHub
commit be55a9ab50
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,7 +1,7 @@
import { logger } from "../logger"; import { logger } from "../logger";
// Public facade: evaluates whole documents line by line and exposes the // Public facade: evaluates whole documents line by line and exposes the
// extension API (numi.addUnit / addFunction / setVariable). // extension API (numi.addUnit / addFunction / setVariable).
import { DateOrder, Decimal, EngineSettings, EvalError, Quantity, Unit, Value, XRefError, defaultSettings, qty } from "./types"; import { DateOrder, Decimal, DEC_ZERO, EngineSettings, EvalError, Quantity, Unit, Value, XRefError, defaultSettings, qty } from "./types";
import { Registry, buildRegistry, Completion } from "./registry"; import { Registry, buildRegistry, Completion } from "./registry";
import { tokenize, Token } from "./tokenizer"; import { tokenize, Token } from "./tokenizer";
import { parseLine, ParsedLine } from "./parser"; import { parseLine, ParsedLine } from "./parser";
@ -93,30 +93,46 @@ export class SumEngine {
} }
evaluateDocument(text: string, resolveXRef?: XRefResolver): LineResult[] { evaluateDocument(text: string, resolveXRef?: XRefResolver): LineResult[] {
const lines = text.split("\n");
const results: LineResult[] = []; const results: LineResult[] = [];
const vars = new Map<string, Value>(this.globals); const vars = new Map<string, Value>(this.globals);
const lineValues: (Value | null)[] = []; const lineValues: (Value | null)[] = [];
const lineKinds: ("empty" | "header" | "normal")[] = []; const lineKinds: ("empty" | "header" | "normal")[] = [];
for (let i = 0; i < lines.length; i++) { let lastIndex = 0;
const rawLine = lines[i]; let i = 0;
while (lastIndex <= text.length) {
let nextIndex = text.indexOf("\n", lastIndex);
if (nextIndex === -1) nextIndex = text.length;
const rawLine = text.slice(lastIndex, nextIndex);
results.push(this.evaluateLine(rawLine, i, vars, lineValues, lineKinds, resolveXRef));
lastIndex = nextIndex + 1;
i++;
}
return results;
}
private evaluateLine(
rawLine: string,
index: number,
vars: Map<string, Value>,
lineValues: (Value | null)[],
lineKinds: ("empty" | "header" | "normal")[],
resolveXRef?: XRefResolver
): LineResult {
const commentStart = findCommentStart(rawLine); const commentStart = findCommentStart(rawLine);
const line = commentStart >= 0 ? rawLine.slice(0, commentStart) : rawLine; const line = commentStart >= 0 ? rawLine.slice(0, commentStart) : rawLine;
const trimmed = line.trim(); const trimmed = line.trim();
if (trimmed.length === 0) { if (trimmed.length === 0) {
const kind = commentStart >= 0 ? "comment" : "empty"; const kind = commentStart >= 0 ? "comment" : "empty";
results.push({ text: null, value: null, kind, tokens: [], commentStart: commentStart >= 0 ? commentStart : null });
lineValues.push(null); lineValues.push(null);
lineKinds.push("empty"); lineKinds.push("empty");
continue; return { text: null, value: null, kind, tokens: [], commentStart: commentStart >= 0 ? commentStart : null };
} }
if (trimmed.startsWith("#")) { if (trimmed.startsWith("#")) {
results.push({ text: null, value: null, kind: "header", tokens: [], commentStart: commentStart >= 0 ? commentStart : null });
lineValues.push(null); lineValues.push(null);
lineKinds.push("header"); lineKinds.push("header");
continue; return { text: null, value: null, kind: "header", tokens: [], commentStart: commentStart >= 0 ? commentStart : null };
} }
// tokenize/parseLine/evaluate all run under one try/catch: pathological // tokenize/parseLine/evaluate all run under one try/catch: pathological
@ -134,7 +150,7 @@ export class SumEngine {
const ctx: EvalCtx = { const ctx: EvalCtx = {
reg: this.reg, reg: this.reg,
vars, vars,
line: { lineValues, lineKinds, index: i, lineText: rawLine }, line: { lineValues, lineKinds, index, lineText: rawLine },
historicalRates: this.historicalRates, historicalRates: this.historicalRates,
resolveXRef, resolveXRef,
}; };
@ -158,7 +174,7 @@ export class SumEngine {
lineValues.push(value); lineValues.push(value);
lineKinds.push("normal"); lineKinds.push("normal");
results.push({ return {
text: (value && value.kind !== "chart") ? formatValue(value, this.settings) : null, text: (value && value.kind !== "chart") ? formatValue(value, this.settings) : null,
value, value,
kind: "normal", kind: "normal",
@ -166,9 +182,7 @@ export class SumEngine {
commentStart: commentStart >= 0 ? commentStart : null, commentStart: commentStart >= 0 ? commentStart : null,
assign: parsed.assign, assign: parsed.assign,
error: lineError, error: lineError,
}); };
}
return results;
} }
/** Evaluate a single expression (used by the extension runtime and tests). */ /** Evaluate a single expression (used by the extension runtime and tests). */
@ -188,7 +202,7 @@ export class SumEngine {
.filter((v): v is Quantity => v !== null && v.kind === "quantity"); .filter((v): v is Quantity => v !== null && v.kind === "quantity");
if (qs.length < 2) return null; if (qs.length < 2) return null;
const anchor = qs.find((q) => q.unit) ?? qs[0]; const anchor = qs.find((q) => q.unit) ?? qs[0];
let acc = new Decimal(0); let acc = DEC_ZERO;
let count = 0; let count = 0;
for (const q of qs) { for (const q of qs) {
if (anchor.unit && q.unit && q.unit.dimension === anchor.unit.dimension) { if (anchor.unit && q.unit && q.unit.dimension === anchor.unit.dimension) {