import type { Cell, ICellAddress, ICellAddress2, UnionValue, EvaluateOptions, ArrayUnion, IArea, CellDataWithAddress, CellValue, Cells } from '../../treb-base-types/src/index'; import { Area } from '../../treb-base-types/src/index'; import type { ExpressionUnit, DependencyList, UnitRange, UnitAddress, UnitIdentifier } from '../../treb-parser/src/index'; import { Parser, DecimalMarkType } from '../../treb-parser/src/index'; import { Graph } from './dag/graph'; import type { SpreadsheetVertex } from './dag/spreadsheet_vertex'; import type { CalculationResult } from './dag/spreadsheet_vertex_base'; import { ExpressionCalculator } from './expression-calculator'; import { FunctionLibrary } from './function-library'; import type { CompositeFunctionDescriptor, FunctionMap } from './descriptors'; import type { FunctionDescriptor } from '../../treb-grid/src/index'; import type { LeafVertex } from './dag/graph'; import { StateLeafVertex } from './dag/state_leaf_vertex'; import { Sheet } from '../../treb-data-model/src/index'; import type { Annotation, DataModel, ConnectedElementType, ConditionalFormat } from '../../treb-data-model/src/index'; /** * we're providing a runtime option for how to handle complex numbers. * we will need to pass that into the calculator when it's created to * control which functions are loaded. */ export interface CalculatorOptions { /** * enable handling complex numbers in function calculation. * @see EmbeddedSpreadsheetOptions */ complex_numbers: 'on' | 'off'; /** enable spill arrays */ spill?: boolean; /** helpful flag for resource initialization */ headless?: boolean; } /** * Calculator now extends graph. there's a 1-1 relationship between the * two, and we wind up passing a lot of operations from one to the other. * this also simplifies the callback structure, as we can use local methods. * * NOTE: graph vertices hold references to cells. while that makes lookups * more efficient, it causes problems if you mutate the sheet (adding or * removing rows or columns). * * in that event, you need to flush the graph to force rebuilding references * (TODO: just rebuild references). after mutating the sheet, call * ``` * Calculator.Reset(); * ``` * */ export declare class Calculator extends Graph { protected readonly model: DataModel; protected static extended_functions: Map; protected static extended_aliases: [string, string][]; static AddExtendedFunction(name: string, descriptor: CompositeFunctionDescriptor): void; static AddAlias(aliases: [string, string][]): void; /** * localized parser instance. we're sharing. * FIXME: remove local references so we can remove this accessor */ protected get parser(): Parser; protected readonly library: FunctionLibrary; protected registered_libraries: Record; protected expression_calculator: ExpressionCalculator; /** the next calculation must do a full rebuild -- set on reset */ protected full_rebuild_required: boolean; protected options: CalculatorOptions; protected async_resource_init: boolean; /** * this is a flag we're using to communicate back to the embedded * sheet, when the grid has expanded as a result of a calculation * (because of a spill). */ grid_expanded: boolean; constructor(model: DataModel, calculator_options?: Partial); /** * new async init method. we need this for subclasses, but * we should consider moving base methods in here as well. * * this function must be idempotent, so we can call it from * multiple paths */ InitResources(): Promise; /** * support for co-editing. we need to export calculated values from * the leader instance, because things like RAND() and NOW() are * nondeterministic (within reason). * * so the leader does the calculation and then we broadcast calculated * values to followers. */ ExportCalculatedValues(): Record; /** * support for co-editing. if we get calculated values from the leader, * we need to apply them to cells. * * to _see_ the data, you still have to make a couple of calls to repaint * and update annotations. see EmbeddedSpreadsheetBase.Recalculate for hints. * * note that we're checking for list mismatch in one direction but not the * other direction. should probably check both. */ ApplyCalculatedValues(data: Record): void; AttachSpillData(area: Area, cells?: Cells): { vertex: StateLeafVertex; area: Area; error: boolean; }; SpillCallback(vertex: SpreadsheetVertex, result: ArrayUnion): SpreadsheetVertex[]; /** * this is a mess [not as bad as it used to be] */ SpreadCallback(vertex: SpreadsheetVertex, value: UnionValue): void; /** * FIXME: for this version, this should be synchronous; the whole thing * should run in a worker. should be much faster than context switching * every time. */ CalculationCallback(vertex: SpreadsheetVertex): CalculationResult; /** * generic function, broken out from the Indirect function. checks dynamic * dependency for missing edges, and adds those edges. * * returns error on bad reference or circular dependency. this method * does not set the "short circuit" flag, callers should set as appropriate. */ DynamicDependencies(expression: ExpressionUnit, context?: ICellAddress, offset?: boolean, offset_rows?: number, offset_columns?: number, resize_rows?: number, resize_columns?: number): { dirty: boolean; area: Area; } | undefined; /** * if locale has changed in Localization, update local resources. * this is necessary because (in chrome) worker doesn't get the system * locale properly (also, we might change it via parameter). we used to * just drop and reconstruct calculator, but we want to stop doing that * as part of supporting dynamic extension. */ UpdateLocale(): void; /** * returns a list of available functions, for AC/tooltips * FIXME: categories? * FIXME: need to separate annotation functions and sheet functions */ SupportedFunctions(): FunctionDescriptor[]; /** * * @param name * @param map */ RegisterLibrary(name: string, map: FunctionMap): boolean; /** * dynamic extension */ RegisterFunction(map: FunctionMap): void; /** * wrapper method for calculation */ Calculate(subset?: Area): void; /** * resets graph and graph status. this is called when structure changes -- * such as adding or removing sheets -- so we need to preserve notifiers * across resets. we need to either add a flag or add a separate method * to handle clearing notifiers. */ Reset(): void; /** * get a list of functions that require decorating with "_xlfn" on * export. the embed caller will pass this to the export worker. * since we manage functions, we can manage the list. * * UPDATE: to support our MC functions (which may need _xll decoration), * map to type and then overload as necessary * */ DecoratedFunctionList(): Record; /** overload */ Evaluate(expression: string, active_sheet?: Sheet, options?: EvaluateOptions, raw_result?: false): CellValue | CellValue[][]; /** overload */ Evaluate(expression: string, active_sheet?: Sheet, options?: EvaluateOptions, raw_result?: true): UnionValue; /** * calculate an expression, optionally setting a fake cell address. * this may have weird side-effects. */ CalculateExpression(expression: ExpressionUnit, address?: ICellAddress, preserve_flags?: boolean): UnionValue; /** * rebuild the graph, and set cells as clean. the vertices need internal * references to the calculated value, so that's set via the vertex method. * * we also need to manage the list of volatile cells, which is normally * built as a side-effect of calculation. * * UPDATE: optionally recalculate if there are volatile cells. that's used * for loading documents. */ RebuildClean(recalculate_if_volatile?: boolean): void; /** * remove duplicates from list, dropping absolute */ FlattenCellList(list: ICellAddress[]): ICellAddress[]; Unresolve(ref: IArea | ICellAddress, context: Sheet, qualified?: boolean, named?: boolean): string; /** * FIXME: just add a quote option to the model method and we can drop this function */ ResolveSheetName(id: number, quote?: boolean): string | undefined; RemoveConditional(conditional: ConditionalFormat): void; RemoveConnectedELement(element: ConnectedElementType): boolean; UpdateConnectedElements(context?: Sheet, element?: ConnectedElementType): void; UpdateConditionals(list?: ConditionalFormat | ConditionalFormat[], context?: Sheet): void; RemoveAnnotation(annotation: Annotation): void; UpdateAnnotations(list?: Annotation | Annotation[], context?: Sheet): void; /** * assuming the expression is an address, range, or named range, resolve * to an address/area. returns undefined if the expression can't be resolved. */ protected ResolveExpressionAddress(expr: ExpressionUnit, context?: ICellAddress): Area | undefined; protected NamedRangeToAddressUnit(unit: UnitIdentifier, context: ICellAddress): UnitAddress | UnitRange | undefined; /** named range support */ protected ConstructAddressUnit(address: ICellAddress, label: string, id: number, position: number): UnitAddress; /** * rebuild dependencies for a single expression (might be a cell, or an * annotation/leaf node). can recurse on elements, so the return value * is passed through. the first (outer) call can just leave it blank and * use the return value. * * we're adding the sheet name so that (in mc expression calculator) we * can turn address parameters into qualified labels. the normal routine * will just use the ID as the name, that's fine, as long as it's unique * (which it is). * * this might cause issues if we ever try to actually resolve from the * sheet name, though, so (...) * * * Q: why does this not use the parser Walk/Walk2 routine? * */ protected RebuildDependencies(unit: ExpressionUnit, relative_sheet_id: number, relative_sheet_name: string, dependencies: DependencyList | undefined, context_address: ICellAddress): DependencyList; protected UpdateLeafVertex(vertex: LeafVertex, formula: string, context: Sheet, decimal_mark?: DecimalMarkType): void; /** * */ protected RebuildGraphCell(cell: Cell, address: ICellAddress2): void; /** * rebuild the graph; parse expressions, build a dependency map, * initialize edges between nodes. * * FIXME: if we want to compose functions, we could do that here, * which might result in some savings [?] */ protected RebuildGraph(subset?: Area): void; /** * check if a cell is volatile. normally this falls out of the calculation, * but if we build the graph and set values explicitly, we need to check. */ protected CheckVolatile(vertex: SpreadsheetVertex): boolean; }