Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- Added a new function: N. [#1585](https://github.com/handsontable/hyperformula/issues/1585)
- Added a new function: VALUE. [#1592](https://github.com/handsontable/hyperformula/issues/1592)

### Fixed

- Fixed `Error Map maximum size exceeded` error when loading big spreadsheets. [#1602](https://github.com/handsontable/hyperformula/issues/1602)

## [3.1.1] - 2025-12-18

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion src/DependencyGraph/AddressMapping/AddressMapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {Maybe} from '../../Maybe'
import {SheetBoundaries} from '../../Sheet'
import {ColumnsSpan, RowsSpan} from '../../Span'
import {ArrayFormulaVertex, DenseStrategy, ValueCellVertex} from '../index'
import {CellVertex} from '../Vertex'
import {CellVertex} from '../CellVertex'
import {ChooseAddressMapping} from './ChooseAddressMappingPolicy'
import {AddressMappingStrategy} from './AddressMappingStrategy'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import {SheetCellAddress, SimpleCellAddress} from '../../Cell'
import {Maybe} from '../../Maybe'
import {ColumnsSpan, RowsSpan} from '../../Span'
import {CellVertex} from '../Vertex'
import {CellVertex} from '../CellVertex'

export type AddressMappingStrategyConstructor = new (width: number, height: number) => AddressMappingStrategy

Expand Down
2 changes: 1 addition & 1 deletion src/DependencyGraph/AddressMapping/DenseStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import {SheetCellAddress, simpleCellAddress, SimpleCellAddress} from '../../Cell'
import {Maybe} from '../../Maybe'
import {ColumnsSpan, RowsSpan} from '../../Span'
import {CellVertex} from '../Vertex'
import {CellVertex} from '../CellVertex'
import {AddressMappingStrategy} from './AddressMappingStrategy'

/**
Expand Down
2 changes: 1 addition & 1 deletion src/DependencyGraph/AddressMapping/SparseStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import {SheetCellAddress, simpleCellAddress, SimpleCellAddress} from '../../Cell'
import {Maybe} from '../../Maybe'
import {ColumnsSpan, RowsSpan} from '../../Span'
import {CellVertex} from '../Vertex'
import {CellVertex} from '../CellVertex'
import {AddressMappingStrategy} from './AddressMappingStrategy'

/**
Expand Down
18 changes: 18 additions & 0 deletions src/DependencyGraph/CellVertex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* @license
* Copyright (c) 2025 Handsoncode. All rights reserved.
*/

import {InterpreterValue} from '../interpreter/InterpreterValue'
import {Vertex} from './Vertex'

/**
* Represents vertex which keeps values of one or more cells
*/
export abstract class CellVertex extends Vertex {
public abstract getCellValue(): InterpreterValue

constructor() {
super()
}
}
32 changes: 16 additions & 16 deletions src/DependencyGraph/DependencyGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ export class DependencyGraph {
}

public processCellDependencies(cellDependencies: CellDependency[], endVertex: Vertex) {
const endVertexId = this.graph.getNodeId(endVertex)
const endVertexId = endVertex.idInGraph

if (endVertexId === undefined) {
throw new Error('End vertex not found')
Expand All @@ -188,8 +188,8 @@ export class DependencyGraph {
this.rangeMapping.addOrUpdateVertex(rangeVertex)
}

this.graph.addNodeAndReturnId(rangeVertex)
const rangeVertexId = this.graph.getNodeId(rangeVertex)
this.graph.addNodeIfNotExists(rangeVertex)
const rangeVertexId = rangeVertex.idInGraph

if (rangeVertexId === undefined) {
throw new Error('Range vertex not found')
Expand Down Expand Up @@ -257,7 +257,7 @@ export class DependencyGraph {
}

const newVertex = new EmptyCellVertex()
const newVertexId = this.graph.addNodeAndReturnId(newVertex)
const newVertexId = this.graph.addNodeIfNotExists(newVertex)
this.addressMapping.setCell(address, newVertex)

return { vertex: newVertex, id: newVertexId }
Expand Down Expand Up @@ -567,12 +567,12 @@ export class DependencyGraph {
}

public addVertex(address: SimpleCellAddress, vertex: CellVertex): void {
this.graph.addNodeAndReturnId(vertex)
this.graph.addNodeIfNotExists(vertex)
this.addressMapping.setCell(address, vertex)
}

public addArrayVertex(address: SimpleCellAddress, vertex: ArrayFormulaVertex): void {
this.graph.addNodeAndReturnId(vertex)
this.graph.addNodeIfNotExists(vertex)
this.setAddressMappingForArrayVertex(vertex, address)
}

Expand Down Expand Up @@ -861,7 +861,7 @@ export class DependencyGraph {
}

private exchangeGraphNode(oldNode: Vertex, newNode: Vertex) {
this.graph.addNodeAndReturnId(newNode)
this.graph.addNodeIfNotExists(newNode)
const adjNodesStored = this.graph.adjacentNodes(oldNode)
this.removeVertex(oldNode)
adjNodesStored.forEach((adjacentNode) => {
Expand All @@ -876,30 +876,30 @@ export class DependencyGraph {
}

private correctInfiniteRangesDependency(address: SimpleCellAddress) {
const relevantInfiniteRanges = (this.graph.getInfiniteRanges())
.filter(({ node }) => (node as RangeVertex).range.addressInRange(address))
const relevantInfiniteRanges = this.graph.getInfiniteRanges()
.filter(node => (node as RangeVertex).range.addressInRange(address))

if (relevantInfiniteRanges.length <= 0) {
return
}

const { vertex, id: maybeVertexId } = this.fetchCellOrCreateEmpty(address)
const vertexId = maybeVertexId ?? this.graph.getNodeId(vertex)
const vertexId = maybeVertexId ?? vertex.idInGraph

if (vertexId === undefined) {
throw new Error('Vertex not found')
}

relevantInfiniteRanges.forEach(({ id }) => {
this.graph.addEdge(vertexId, id)
relevantInfiniteRanges.forEach((node) => {
this.graph.addEdge(vertexId, node)
})
}

private exchangeOrAddGraphNode(oldNode: Maybe<Vertex>, newNode: Vertex) {
if (oldNode) {
this.exchangeGraphNode(oldNode, newNode)
} else {
this.graph.addNodeAndReturnId(newNode)
this.graph.addNodeIfNotExists(newNode)
}
}

Expand Down Expand Up @@ -946,7 +946,7 @@ export class DependencyGraph {

private correctInfiniteRangesDependenciesByRangeVertex(vertex: RangeVertex) {
this.graph.getInfiniteRanges()
.forEach(({ id: infiniteRangeVertexId, node: infiniteRangeVertex }) => {
.forEach((infiniteRangeVertex) => {
const intersection = vertex.range.intersectionWith((infiniteRangeVertex as RangeVertex).range)

if (intersection === undefined) {
Expand All @@ -955,7 +955,7 @@ export class DependencyGraph {

intersection.addresses(this).forEach((address: SimpleCellAddress) => {
const { vertex, id } = this.fetchCellOrCreateEmpty(address)
this.graph.addEdge(id ?? vertex, infiniteRangeVertexId)
this.graph.addEdge(id ?? vertex, infiniteRangeVertex)
})
})
}
Expand Down Expand Up @@ -1069,7 +1069,7 @@ export class DependencyGraph {
while (find.smallerRangeVertex === undefined) {
const newRangeVertex = new RangeVertex(AbsoluteCellRange.spanFrom(currentRangeVertex.range.start, currentRangeVertex.range.width(), currentRangeVertex.range.height() - 1))
this.rangeMapping.addOrUpdateVertex(newRangeVertex)
this.graph.addNodeAndReturnId(newRangeVertex)
this.graph.addNodeIfNotExists(newRangeVertex)
const restRange = new AbsoluteCellRange(simpleCellAddress(currentRangeVertex.range.start.sheet, currentRangeVertex.range.start.col, currentRangeVertex.range.end.row), currentRangeVertex.range.end)
this.addAllFromRange(restRange, currentRangeVertex)
this.graph.addEdge(newRangeVertex, currentRangeVertex)
Expand Down
7 changes: 5 additions & 2 deletions src/DependencyGraph/EmptyCellVertex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
* Copyright (c) 2025 Handsoncode. All rights reserved.
*/

import {CellVertex} from './CellVertex'
import {EmptyValue, EmptyValueType} from '../interpreter/InterpreterValue'

/**
* Represents singleton vertex bound to all empty cells
*/
export class EmptyCellVertex {
constructor() {}
export class EmptyCellVertex extends CellVertex {
constructor() {
super()
}

/**
* Retrieves cell value bound to that singleton
Expand Down
23 changes: 21 additions & 2 deletions src/DependencyGraph/FormulaVertex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,23 @@ import {LazilyTransformingAstService} from '../LazilyTransformingAstService'
import {Maybe} from '../Maybe'
import {Ast} from '../parser'
import {ColumnsSpan, RowsSpan} from '../Span'
import {CellVertex} from './CellVertex'

export abstract class FormulaVertex {

/**
* Abstract base class for vertices that contain formulas in the dependency graph.
*
* Stores formula AST, cell address, and version for lazy transformation support.
* Has two concrete implementations: {@link ScalarFormulaVertex} for single-cell formulas
* and {@link ArrayFormulaVertex} for array formulas that span multiple cells.
*/
export abstract class FormulaVertex extends CellVertex {
protected constructor(
protected formula: Ast,
protected cellAddress: SimpleCellAddress,
public version: number
) {
super()
}

public get width(): number {
Expand Down Expand Up @@ -79,6 +89,12 @@ export abstract class FormulaVertex {
public abstract isComputed(): boolean
}

/**
* Represents a formula vertex that produces an array result spanning multiple cells.
*
* Array formulas are transformed eagerly (unlike scalar formulas) and store their
* computed values in a {@link CellArray} structure.
*/
export class ArrayFormulaVertex extends FormulaVertex {
array: CellArray

Expand Down Expand Up @@ -221,7 +237,10 @@ export class ArrayFormulaVertex extends FormulaVertex {
}

/**
* Represents vertex which keeps formula
* Represents a formula vertex that produces a single scalar value.
*
* Unlike {@link ArrayFormulaVertex}, scalar formulas are transformed lazily
* and cache their computed value for retrieval.
*/
export class ScalarFormulaVertex extends FormulaVertex {
/** Most recently computed value of this formula. */
Expand Down
Loading
Loading