126 lines
4.9 KiB
JavaScript
126 lines
4.9 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright (c) 2024 Handsoncode. All rights reserved.
|
|
*/
|
|
import { AbsoluteCellRange } from './AbsoluteCellRange';
|
|
import { absolutizeDependencies } from './absolutizeDependencies';
|
|
import { CellError, ErrorType } from './Cell';
|
|
import { ContentChanges } from './ContentChanges';
|
|
import { ArrayVertex, RangeVertex } from './DependencyGraph';
|
|
import { FormulaVertex } from './DependencyGraph/FormulaCellVertex';
|
|
import { InterpreterState } from './interpreter/InterpreterState';
|
|
import { EmptyValue, getRawValue } from './interpreter/InterpreterValue';
|
|
import { SimpleRangeValue } from './SimpleRangeValue';
|
|
import { StatType } from './statistics';
|
|
export class Evaluator {
|
|
constructor(config, stats, interpreter, lazilyTransformingAstService, dependencyGraph, columnSearch) {
|
|
this.config = config;
|
|
this.stats = stats;
|
|
this.interpreter = interpreter;
|
|
this.lazilyTransformingAstService = lazilyTransformingAstService;
|
|
this.dependencyGraph = dependencyGraph;
|
|
this.columnSearch = columnSearch;
|
|
}
|
|
run() {
|
|
this.stats.start(StatType.TOP_SORT);
|
|
const {
|
|
sorted,
|
|
cycled
|
|
} = this.dependencyGraph.topSortWithScc();
|
|
this.stats.end(StatType.TOP_SORT);
|
|
this.stats.measure(StatType.EVALUATION, () => {
|
|
this.recomputeFormulas(cycled, sorted);
|
|
});
|
|
}
|
|
partialRun(vertices) {
|
|
const changes = ContentChanges.empty();
|
|
this.stats.measure(StatType.EVALUATION, () => {
|
|
this.dependencyGraph.graph.getTopSortedWithSccSubgraphFrom(vertices, vertex => {
|
|
if (vertex instanceof FormulaVertex) {
|
|
const currentValue = vertex.isComputed() ? vertex.getCellValue() : undefined;
|
|
const newCellValue = this.recomputeFormulaVertexValue(vertex);
|
|
if (newCellValue !== currentValue) {
|
|
const address = vertex.getAddress(this.lazilyTransformingAstService);
|
|
changes.addChange(newCellValue, address);
|
|
this.columnSearch.change(getRawValue(currentValue), getRawValue(newCellValue), address);
|
|
return true;
|
|
}
|
|
return false;
|
|
} else if (vertex instanceof RangeVertex) {
|
|
vertex.clearCache();
|
|
return true;
|
|
} else {
|
|
return true;
|
|
}
|
|
}, vertex => {
|
|
if (vertex instanceof RangeVertex) {
|
|
vertex.clearCache();
|
|
} else if (vertex instanceof FormulaVertex) {
|
|
const address = vertex.getAddress(this.lazilyTransformingAstService);
|
|
this.columnSearch.remove(getRawValue(vertex.valueOrUndef()), address);
|
|
const error = new CellError(ErrorType.CYCLE, undefined, vertex);
|
|
vertex.setCellValue(error);
|
|
changes.addChange(error, address);
|
|
}
|
|
});
|
|
});
|
|
return changes;
|
|
}
|
|
runAndForget(ast, address, dependencies) {
|
|
const tmpRanges = [];
|
|
for (const dep of absolutizeDependencies(dependencies, address)) {
|
|
if (dep instanceof AbsoluteCellRange) {
|
|
const range = dep;
|
|
if (this.dependencyGraph.getRange(range.start, range.end) === undefined) {
|
|
const rangeVertex = new RangeVertex(range);
|
|
this.dependencyGraph.rangeMapping.setRange(rangeVertex);
|
|
tmpRanges.push(rangeVertex);
|
|
}
|
|
}
|
|
}
|
|
const ret = this.evaluateAstToCellValue(ast, new InterpreterState(address, this.config.useArrayArithmetic));
|
|
tmpRanges.forEach(rangeVertex => {
|
|
this.dependencyGraph.rangeMapping.removeRange(rangeVertex);
|
|
});
|
|
return ret;
|
|
}
|
|
/**
|
|
* Recalculates formulas in the topological sort order
|
|
*/
|
|
recomputeFormulas(cycled, sorted) {
|
|
cycled.forEach(vertex => {
|
|
if (vertex instanceof FormulaVertex) {
|
|
vertex.setCellValue(new CellError(ErrorType.CYCLE, undefined, vertex));
|
|
}
|
|
});
|
|
sorted.forEach(vertex => {
|
|
if (vertex instanceof FormulaVertex) {
|
|
const newCellValue = this.recomputeFormulaVertexValue(vertex);
|
|
const address = vertex.getAddress(this.lazilyTransformingAstService);
|
|
this.columnSearch.add(getRawValue(newCellValue), address);
|
|
} else if (vertex instanceof RangeVertex) {
|
|
vertex.clearCache();
|
|
}
|
|
});
|
|
}
|
|
recomputeFormulaVertexValue(vertex) {
|
|
const address = vertex.getAddress(this.lazilyTransformingAstService);
|
|
if (vertex instanceof ArrayVertex && (vertex.array.size.isRef || !this.dependencyGraph.isThereSpaceForArray(vertex))) {
|
|
return vertex.setNoSpace();
|
|
} else {
|
|
const formula = vertex.getFormula(this.lazilyTransformingAstService);
|
|
const newCellValue = this.evaluateAstToCellValue(formula, new InterpreterState(address, this.config.useArrayArithmetic, vertex));
|
|
return vertex.setCellValue(newCellValue);
|
|
}
|
|
}
|
|
evaluateAstToCellValue(ast, state) {
|
|
const interpreterValue = this.interpreter.evaluateAst(ast, state);
|
|
if (interpreterValue instanceof SimpleRangeValue) {
|
|
return interpreterValue;
|
|
} else if (interpreterValue === EmptyValue && this.config.evaluateNullToZero) {
|
|
return 0;
|
|
} else {
|
|
return interpreterValue;
|
|
}
|
|
}
|
|
} |