UNPKG

hyperformula

Version:

HyperFormula is a JavaScript engine for efficient processing of spreadsheet-like data and formulas

135 lines 4.45 kB
/** * @license * Copyright (c) 2025 Handsoncode. All rights reserved. */ import { CellError, ErrorType } from "../Cell.mjs"; import { AstNodeType, buildCellErrorAst, CellAddress } from "../parser/index.mjs"; export class Transformer { performEagerTransformations(graph, parser) { for (const node of graph.arrayFormulaNodes()) { const [newAst, newAddress] = this.transformSingleAst(node.getFormula(graph.lazilyTransformingAstService), node.getAddress(graph.lazilyTransformingAstService)); const cachedAst = parser.rememberNewAst(newAst); node.setFormula(cachedAst); node.setAddress(newAddress); } } transformSingleAst(ast, address) { const newAst = this.transformAst(ast, address); const newAddress = this.fixNodeAddress(address); return [newAst, newAddress]; } transformAst(ast, address) { switch (ast.type) { case AstNodeType.CELL_REFERENCE: { return this.transformCellReferenceAst(ast, address); } case AstNodeType.CELL_RANGE: { return this.transformCellRangeAst(ast, address); } case AstNodeType.COLUMN_RANGE: { return this.transformColumnRangeAst(ast, address); } case AstNodeType.ROW_RANGE: { return this.transformRowRangeAst(ast, address); } case AstNodeType.EMPTY: case AstNodeType.ERROR: case AstNodeType.NUMBER: case AstNodeType.NAMED_EXPRESSION: case AstNodeType.ERROR_WITH_RAW_INPUT: case AstNodeType.STRING: { return ast; } case AstNodeType.PERCENT_OP: case AstNodeType.MINUS_UNARY_OP: case AstNodeType.PLUS_UNARY_OP: { return Object.assign(Object.assign({}, ast), { value: this.transformAst(ast.value, address) }); } case AstNodeType.FUNCTION_CALL: { return Object.assign(Object.assign({}, ast), { procedureName: ast.procedureName, args: ast.args.map(arg => this.transformAst(arg, address)) }); } case AstNodeType.PARENTHESIS: { return Object.assign(Object.assign({}, ast), { expression: this.transformAst(ast.expression, address) }); } case AstNodeType.ARRAY: { return Object.assign(Object.assign({}, ast), { args: ast.args.map(row => row.map(val => this.transformAst(val, address))) }); } default: { return Object.assign(Object.assign({}, ast), { left: this.transformAst(ast.left, address), right: this.transformAst(ast.right, address) }); } } } transformCellReferenceAst(ast, formulaAddress) { const newCellAddress = this.transformCellAddress(ast.reference, formulaAddress); if (newCellAddress instanceof CellAddress) { return Object.assign(Object.assign({}, ast), { reference: newCellAddress }); } else if (newCellAddress === ErrorType.REF) { return buildCellErrorAst(new CellError(ErrorType.REF)); } else { return ast; } } transformCellRangeAst(ast, formulaAddress) { const newRange = this.transformCellRange(ast.start, ast.end, formulaAddress); if (Array.isArray(newRange)) { return Object.assign(Object.assign({}, ast), { start: newRange[0], end: newRange[1] }); } else if (newRange === ErrorType.REF) { return buildCellErrorAst(new CellError(ErrorType.REF)); } else { return ast; } } transformColumnRangeAst(ast, formulaAddress) { const newRange = this.transformColumnRange(ast.start, ast.end, formulaAddress); if (Array.isArray(newRange)) { return Object.assign(Object.assign({}, ast), { start: newRange[0], end: newRange[1] }); } else if (newRange === ErrorType.REF) { return buildCellErrorAst(new CellError(ErrorType.REF)); } else { return ast; } } transformRowRangeAst(ast, formulaAddress) { const newRange = this.transformRowRange(ast.start, ast.end, formulaAddress); if (Array.isArray(newRange)) { return Object.assign(Object.assign({}, ast), { start: newRange[0], end: newRange[1] }); } else if (newRange === ErrorType.REF) { return buildCellErrorAst(new CellError(ErrorType.REF)); } else { return ast; } } }