-
Notifications
You must be signed in to change notification settings - Fork 124
/
Copy pathCell.ts
233 lines (193 loc) · 6.31 KB
/
Cell.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/**
* @license
* Copyright (c) 2025 Handsoncode. All rights reserved.
*/
import {ArrayVertex, CellVertex, FormulaCellVertex, ParsingErrorVertex, ValueCellVertex} from './DependencyGraph'
import {FormulaVertex} from './DependencyGraph/FormulaCellVertex'
import {ErrorMessage} from './error-message'
import {
EmptyValue,
getFormatOfExtendedNumber,
getTypeOfExtendedNumber,
InterpreterValue,
isExtendedNumber,
NumberType,
} from './interpreter/InterpreterValue'
import {SimpleRangeValue} from './SimpleRangeValue'
import {Maybe} from './Maybe'
import {CellAddress} from './parser'
import {AddressWithSheet} from './parser/Address'
/**
* Possible errors returned by our interpreter.
*/
export enum ErrorType {
/** Division by zero. */
DIV_BY_ZERO = 'DIV_BY_ZERO',
/** Unknown function name. */
NAME = 'NAME',
VALUE = 'VALUE',
NUM = 'NUM',
NA = 'NA',
/** Cyclic dependency. */
CYCLE = 'CYCLE',
/** Wrong address reference. */
REF = 'REF',
/** Array spill error. */
SPILL = 'SPILL',
/** Invalid/missing licence error. */
LIC = 'LIC',
/** Generic error */
ERROR = 'ERROR'
}
export type TranslatableErrorType = Exclude<ErrorType, ErrorType.LIC>
export enum CellType {
FORMULA = 'FORMULA',
VALUE = 'VALUE',
ARRAY = 'ARRAY',
EMPTY = 'EMPTY',
ARRAYFORMULA = 'ARRAYFORMULA',
}
export const getCellType = (vertex: Maybe<CellVertex>, address: SimpleCellAddress): CellType => {
if (vertex instanceof ArrayVertex) {
if (vertex.isLeftCorner(address)) {
return CellType.ARRAYFORMULA
} else {
return CellType.ARRAY
}
}
if (vertex instanceof FormulaCellVertex || vertex instanceof ParsingErrorVertex) {
return CellType.FORMULA
}
if (vertex instanceof ValueCellVertex) {
return CellType.VALUE
}
return CellType.EMPTY
}
export enum CellValueNoNumber {
EMPTY = 'EMPTY',
NUMBER = 'NUMBER',
STRING = 'STRING',
BOOLEAN = 'BOOLEAN',
ERROR = 'ERROR',
}
export enum CellValueJustNumber {
NUMBER = 'NUMBER'
}
export type CellValueType = CellValueNoNumber | CellValueJustNumber
export const CellValueType = {...CellValueNoNumber, ...CellValueJustNumber}
export type CellValueDetailedType = CellValueNoNumber | NumberType
export const CellValueDetailedType = {...CellValueNoNumber, ...NumberType}
export const CellValueTypeOrd = (arg: CellValueType): number => {
switch (arg) {
case CellValueType.EMPTY:
return 0
case CellValueType.NUMBER:
return 1
case CellValueType.STRING:
return 2
case CellValueType.BOOLEAN:
return 3
case CellValueType.ERROR:
return 4
}
throw new Error('Cell value not computed')
}
export const getCellValueType = (cellValue: InterpreterValue): CellValueType => {
if (cellValue === EmptyValue) {
return CellValueType.EMPTY
}
if (cellValue instanceof CellError || cellValue instanceof SimpleRangeValue) {
return CellValueType.ERROR
}
if (typeof cellValue === 'string') {
return CellValueType.STRING
} else if (isExtendedNumber(cellValue)) {
return CellValueType.NUMBER
} else if (typeof cellValue === 'boolean') {
return CellValueType.BOOLEAN
}
throw new Error('Cell value not computed')
}
export const getCellValueDetailedType = (cellValue: InterpreterValue): CellValueDetailedType => {
if (isExtendedNumber(cellValue)) {
return getTypeOfExtendedNumber(cellValue)
} else {
return getCellValueType(cellValue) as CellValueDetailedType
}
}
export const getCellValueFormat = (cellValue: InterpreterValue): string | undefined => {
if (isExtendedNumber(cellValue)) {
return getFormatOfExtendedNumber(cellValue)
} else {
return undefined
}
}
export class CellError {
constructor(
public readonly type: ErrorType,
public readonly message?: string,
public readonly root?: FormulaVertex
) {
}
/**
* Returns a CellError with a given message.
* @param {string} detailedMessage - message to be displayed
*/
public static parsingError(detailedMessage?: string): CellError {
return new CellError(ErrorType.ERROR, `${ErrorMessage.ParseError}${detailedMessage ? ' ' + detailedMessage : ''}`)
}
public attachRootVertex(vertex: FormulaVertex): CellError {
if (this.root === undefined) {
return new CellError(this.type, this.message, vertex)
} else {
return this
}
}
}
export interface SimpleRowAddress {
row: number,
sheet: number,
}
export const simpleRowAddress = (sheet: number, row: number): SimpleRowAddress => ({sheet, row})
export const invalidSimpleRowAddress = (address: SimpleRowAddress): boolean => (address.row < 0)
export interface SimpleColumnAddress {
col: number,
sheet: number,
}
export const simpleColumnAddress = (sheet: number, col: number): SimpleColumnAddress => ({sheet, col})
export const invalidSimpleColumnAddress = (address: SimpleColumnAddress): boolean => (address.col < 0)
export interface SimpleCellAddress {
col: number,
row: number,
sheet: number,
}
export const simpleCellAddress = (sheet: number, col: number, row: number): SimpleCellAddress => ({sheet, col, row})
export const invalidSimpleCellAddress = (address: SimpleCellAddress): boolean => (address.col < 0 || address.row < 0)
export const movedSimpleCellAddress = (address: SimpleCellAddress, toSheet: number, toRight: number, toBottom: number): SimpleCellAddress => {
return simpleCellAddress(toSheet, address.col + toRight, address.row + toBottom)
}
export const addressKey = (address: SimpleCellAddress) => `${address.sheet},${address.row},${address.col}`
/**
* Checks if the object is a simple cell address.
*/
export function isSimpleCellAddress(obj: unknown): obj is SimpleCellAddress {
return obj
&& (typeof obj === 'object' || typeof obj === 'function')
&& typeof (obj as SimpleCellAddress)?.sheet === 'number'
&& typeof (obj as SimpleCellAddress)?.col === 'number'
&& typeof (obj as SimpleCellAddress)?.row === 'number'
}
export const absoluteSheetReference = (address: AddressWithSheet, baseAddress: SimpleCellAddress): number => {
return address.sheet ?? baseAddress.sheet
}
export const equalSimpleCellAddress = (left: SimpleCellAddress, right: SimpleCellAddress) => {
return left.sheet === right.sheet && left.col === right.col && left.row === right.row
}
export interface SheetCellAddress {
col: number,
row: number,
}
export interface CellRange {
start: CellAddress,
end: CellAddress,
}