@borgar/fx 4.12.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index-BMr6cTgc.d.cts +1444 -0
- package/dist/index-BMr6cTgc.d.ts +1444 -0
- package/dist/index.cjs +3054 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2984 -0
- package/dist/index.js.map +1 -0
- package/dist/xlsx/index.cjs +3120 -0
- package/dist/xlsx/index.cjs.map +1 -0
- package/dist/xlsx/index.d.cts +55 -0
- package/dist/xlsx/index.d.ts +55 -0
- package/dist/xlsx/index.js +3049 -0
- package/dist/xlsx/index.js.map +1 -0
- package/docs/API.md +2959 -718
- package/docs/AST_format.md +2 -2
- package/eslint.config.mjs +40 -0
- package/lib/a1.spec.ts +32 -0
- package/lib/a1.ts +26 -0
- package/lib/addA1RangeBounds.ts +50 -0
- package/lib/addTokenMeta.spec.ts +166 -0
- package/lib/{addTokenMeta.js → addTokenMeta.ts} +53 -33
- package/lib/astTypes.ts +211 -0
- package/lib/cloneToken.ts +29 -0
- package/lib/{constants.js → constants.ts} +6 -3
- package/lib/fixRanges.spec.ts +220 -0
- package/lib/fixRanges.ts +260 -0
- package/lib/fromCol.spec.ts +15 -0
- package/lib/{fromCol.js → fromCol.ts} +1 -1
- package/lib/index.spec.ts +119 -0
- package/lib/index.ts +76 -0
- package/lib/isNodeType.ts +151 -0
- package/lib/isType.spec.ts +208 -0
- package/lib/{isType.js → isType.ts} +26 -25
- package/lib/lexers/advRangeOp.ts +18 -0
- package/lib/lexers/canEndRange.ts +25 -0
- package/lib/lexers/lexBoolean.ts +55 -0
- package/lib/lexers/lexContext.ts +104 -0
- package/lib/lexers/lexError.ts +15 -0
- package/lib/lexers/lexFunction.ts +37 -0
- package/lib/lexers/lexNameFuncCntx.ts +112 -0
- package/lib/lexers/lexNamed.ts +60 -0
- package/lib/lexers/lexNewLine.ts +12 -0
- package/lib/lexers/lexNumber.ts +48 -0
- package/lib/lexers/lexOperator.ts +26 -0
- package/lib/lexers/lexRange.ts +15 -0
- package/lib/lexers/lexRangeA1.ts +134 -0
- package/lib/lexers/lexRangeR1C1.ts +146 -0
- package/lib/lexers/lexRangeTrim.ts +26 -0
- package/lib/lexers/lexRefOp.ts +19 -0
- package/lib/lexers/lexString.ts +22 -0
- package/lib/lexers/lexStructured.ts +25 -0
- package/lib/lexers/lexWhitespace.ts +31 -0
- package/lib/lexers/sets.ts +51 -0
- package/lib/mergeRefTokens.spec.ts +141 -0
- package/lib/{mergeRefTokens.js → mergeRefTokens.ts} +47 -32
- package/lib/nodeTypes.ts +54 -0
- package/lib/parse.spec.ts +1410 -0
- package/lib/{parser.js → parse.ts} +81 -63
- package/lib/parseA1Range.spec.ts +233 -0
- package/lib/parseA1Range.ts +206 -0
- package/lib/parseA1Ref.spec.ts +337 -0
- package/lib/parseA1Ref.ts +115 -0
- package/lib/parseR1C1Range.ts +191 -0
- package/lib/parseR1C1Ref.spec.ts +323 -0
- package/lib/parseR1C1Ref.ts +127 -0
- package/lib/parseRef.spec.ts +90 -0
- package/lib/parseRef.ts +240 -0
- package/lib/parseSRange.ts +240 -0
- package/lib/parseStructRef.spec.ts +168 -0
- package/lib/parseStructRef.ts +76 -0
- package/lib/stringifyA1Range.spec.ts +72 -0
- package/lib/stringifyA1Range.ts +72 -0
- package/lib/stringifyA1Ref.spec.ts +64 -0
- package/lib/stringifyA1Ref.ts +59 -0
- package/lib/{stringifyPrefix.js → stringifyPrefix.ts} +17 -2
- package/lib/stringifyR1C1Range.spec.ts +92 -0
- package/lib/stringifyR1C1Range.ts +73 -0
- package/lib/stringifyR1C1Ref.spec.ts +63 -0
- package/lib/stringifyR1C1Ref.ts +67 -0
- package/lib/stringifyStructRef.spec.ts +124 -0
- package/lib/stringifyStructRef.ts +113 -0
- package/lib/stringifyTokens.ts +15 -0
- package/lib/toCol.spec.ts +11 -0
- package/lib/{toCol.js → toCol.ts} +4 -4
- package/lib/tokenTypes.ts +76 -0
- package/lib/tokenize-srefs.spec.ts +429 -0
- package/lib/tokenize.spec.ts +2103 -0
- package/lib/tokenize.ts +346 -0
- package/lib/translate.spec.ts +35 -0
- package/lib/translateToA1.spec.ts +247 -0
- package/lib/translateToA1.ts +231 -0
- package/lib/translateToR1C1.spec.ts +227 -0
- package/lib/translateToR1C1.ts +145 -0
- package/lib/types.ts +179 -0
- package/lib/xlsx/index.spec.ts +27 -0
- package/lib/xlsx/index.ts +32 -0
- package/package.json +46 -30
- package/tsconfig.json +28 -0
- package/typedoc-ignore-links.ts +17 -0
- package/typedoc.json +41 -0
- package/.eslintrc +0 -22
- package/dist/fx.d.ts +0 -823
- package/dist/fx.js +0 -2
- package/dist/package.json +0 -1
- package/lib/a1.js +0 -348
- package/lib/a1.spec.js +0 -458
- package/lib/addTokenMeta.spec.js +0 -153
- package/lib/astTypes.js +0 -96
- package/lib/extraTypes.js +0 -74
- package/lib/fixRanges.js +0 -104
- package/lib/fixRanges.spec.js +0 -170
- package/lib/fromCol.spec.js +0 -11
- package/lib/index.js +0 -134
- package/lib/index.spec.js +0 -67
- package/lib/isType.spec.js +0 -168
- package/lib/lexer-srefs.spec.js +0 -324
- package/lib/lexer.js +0 -283
- package/lib/lexer.spec.js +0 -1953
- package/lib/lexerParts.js +0 -228
- package/lib/mergeRefTokens.spec.js +0 -121
- package/lib/package.json +0 -1
- package/lib/parseRef.js +0 -157
- package/lib/parseRef.spec.js +0 -71
- package/lib/parseSRange.js +0 -167
- package/lib/parseStructRef.js +0 -48
- package/lib/parseStructRef.spec.js +0 -164
- package/lib/parser.spec.js +0 -1208
- package/lib/rc.js +0 -341
- package/lib/rc.spec.js +0 -403
- package/lib/stringifyStructRef.js +0 -80
- package/lib/stringifyStructRef.spec.js +0 -182
- package/lib/toCol.spec.js +0 -11
- package/lib/translate-toA1.spec.js +0 -214
- package/lib/translate-toRC.spec.js +0 -197
- package/lib/translate.js +0 -239
- package/lib/translate.spec.js +0 -21
- package/rollup.config.mjs +0 -22
- package/tsd.json +0 -12
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { describe, test, expect } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
REF_RANGE, REF_BEAM, REF_NAMED, REF_TERNARY, REF_STRUCT,
|
|
4
|
+
FX_PREFIX, WHITESPACE, NEWLINE,
|
|
5
|
+
FUNCTION, OPERATOR,
|
|
6
|
+
ERROR, STRING, NUMBER, BOOLEAN
|
|
7
|
+
} from './constants.ts';
|
|
8
|
+
import { isRange, isReference, isLiteral, isError, isWhitespace, isFunction, isFxPrefix, isOperator } from './isType.ts';
|
|
9
|
+
|
|
10
|
+
describe('isRange', () => {
|
|
11
|
+
test('returns false for non-range types', () => {
|
|
12
|
+
expect(isRange(null)).toBe(false);
|
|
13
|
+
// @ts-expect-error -- testing invalid input
|
|
14
|
+
expect(isRange({})).toBe(false);
|
|
15
|
+
expect(isRange({ type: BOOLEAN })).toBe(false);
|
|
16
|
+
expect(isRange({ type: ERROR })).toBe(false);
|
|
17
|
+
expect(isRange({ type: FUNCTION })).toBe(false);
|
|
18
|
+
expect(isRange({ type: FX_PREFIX })).toBe(false);
|
|
19
|
+
expect(isRange({ type: NEWLINE })).toBe(false);
|
|
20
|
+
expect(isRange({ type: NUMBER })).toBe(false);
|
|
21
|
+
expect(isRange({ type: OPERATOR })).toBe(false);
|
|
22
|
+
expect(isRange({ type: REF_NAMED })).toBe(false);
|
|
23
|
+
expect(isRange({ type: REF_STRUCT })).toBe(false);
|
|
24
|
+
expect(isRange({ type: STRING })).toBe(false);
|
|
25
|
+
expect(isRange({ type: WHITESPACE })).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('returns true for range types', () => {
|
|
29
|
+
expect(isRange({ type: REF_BEAM })).toBe(true);
|
|
30
|
+
expect(isRange({ type: REF_RANGE })).toBe(true);
|
|
31
|
+
expect(isRange({ type: REF_TERNARY })).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('isReference', () => {
|
|
36
|
+
test('returns false for non-reference types', () => {
|
|
37
|
+
expect(isReference(null)).toBe(false);
|
|
38
|
+
// @ts-expect-error -- testing invalid input
|
|
39
|
+
expect(isReference({})).toBe(false);
|
|
40
|
+
expect(isReference({ type: BOOLEAN })).toBe(false);
|
|
41
|
+
expect(isReference({ type: ERROR })).toBe(false);
|
|
42
|
+
expect(isReference({ type: FUNCTION })).toBe(false);
|
|
43
|
+
expect(isReference({ type: FX_PREFIX })).toBe(false);
|
|
44
|
+
expect(isReference({ type: NEWLINE })).toBe(false);
|
|
45
|
+
expect(isReference({ type: NUMBER })).toBe(false);
|
|
46
|
+
expect(isReference({ type: OPERATOR })).toBe(false);
|
|
47
|
+
expect(isReference({ type: STRING })).toBe(false);
|
|
48
|
+
expect(isReference({ type: WHITESPACE })).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('returns true for reference types', () => {
|
|
52
|
+
expect(isReference({ type: REF_BEAM })).toBe(true);
|
|
53
|
+
expect(isReference({ type: REF_NAMED })).toBe(true);
|
|
54
|
+
expect(isReference({ type: REF_RANGE })).toBe(true);
|
|
55
|
+
expect(isReference({ type: REF_STRUCT })).toBe(true);
|
|
56
|
+
expect(isReference({ type: REF_TERNARY })).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('isLiteral', () => {
|
|
61
|
+
test('returns false for non-literal types', () => {
|
|
62
|
+
expect(isLiteral(null)).toBe(false);
|
|
63
|
+
// @ts-expect-error -- testing invalid input
|
|
64
|
+
expect(isLiteral({})).toBe(false);
|
|
65
|
+
expect(isLiteral({ type: FUNCTION })).toBe(false);
|
|
66
|
+
expect(isLiteral({ type: FX_PREFIX })).toBe(false);
|
|
67
|
+
expect(isLiteral({ type: NEWLINE })).toBe(false);
|
|
68
|
+
expect(isLiteral({ type: OPERATOR })).toBe(false);
|
|
69
|
+
expect(isLiteral({ type: REF_BEAM })).toBe(false);
|
|
70
|
+
expect(isLiteral({ type: REF_NAMED })).toBe(false);
|
|
71
|
+
expect(isLiteral({ type: REF_RANGE })).toBe(false);
|
|
72
|
+
expect(isLiteral({ type: REF_STRUCT })).toBe(false);
|
|
73
|
+
expect(isLiteral({ type: REF_TERNARY })).toBe(false);
|
|
74
|
+
expect(isLiteral({ type: WHITESPACE })).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('returns true for literal types', () => {
|
|
78
|
+
expect(isLiteral({ type: BOOLEAN })).toBe(true);
|
|
79
|
+
expect(isLiteral({ type: ERROR })).toBe(true);
|
|
80
|
+
expect(isLiteral({ type: NUMBER })).toBe(true);
|
|
81
|
+
expect(isLiteral({ type: STRING })).toBe(true);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('isError', () => {
|
|
86
|
+
test('returns false for non-error types', () => {
|
|
87
|
+
expect(isError(null)).toBe(false);
|
|
88
|
+
// @ts-expect-error -- testing invalid input
|
|
89
|
+
expect(isError({})).toBe(false);
|
|
90
|
+
expect(isError({ type: BOOLEAN })).toBe(false);
|
|
91
|
+
expect(isError({ type: FUNCTION })).toBe(false);
|
|
92
|
+
expect(isError({ type: FX_PREFIX })).toBe(false);
|
|
93
|
+
expect(isError({ type: NEWLINE })).toBe(false);
|
|
94
|
+
expect(isError({ type: NUMBER })).toBe(false);
|
|
95
|
+
expect(isError({ type: OPERATOR })).toBe(false);
|
|
96
|
+
expect(isError({ type: REF_BEAM })).toBe(false);
|
|
97
|
+
expect(isError({ type: REF_NAMED })).toBe(false);
|
|
98
|
+
expect(isError({ type: REF_RANGE })).toBe(false);
|
|
99
|
+
expect(isError({ type: REF_STRUCT })).toBe(false);
|
|
100
|
+
expect(isError({ type: REF_TERNARY })).toBe(false);
|
|
101
|
+
expect(isError({ type: STRING })).toBe(false);
|
|
102
|
+
expect(isError({ type: WHITESPACE })).toBe(false);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('returns true for error type', () => {
|
|
106
|
+
expect(isError({ type: ERROR })).toBe(true);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('isWhitespace', () => {
|
|
111
|
+
test('returns false for non-whitespace types', () => {
|
|
112
|
+
expect(isWhitespace(null)).toBe(false);
|
|
113
|
+
// @ts-expect-error -- testing invalid input
|
|
114
|
+
expect(isWhitespace({})).toBe(false);
|
|
115
|
+
expect(isWhitespace({ type: BOOLEAN })).toBe(false);
|
|
116
|
+
expect(isWhitespace({ type: ERROR })).toBe(false);
|
|
117
|
+
expect(isWhitespace({ type: FUNCTION })).toBe(false);
|
|
118
|
+
expect(isWhitespace({ type: FX_PREFIX })).toBe(false);
|
|
119
|
+
expect(isWhitespace({ type: NUMBER })).toBe(false);
|
|
120
|
+
expect(isWhitespace({ type: OPERATOR })).toBe(false);
|
|
121
|
+
expect(isWhitespace({ type: REF_BEAM })).toBe(false);
|
|
122
|
+
expect(isWhitespace({ type: REF_NAMED })).toBe(false);
|
|
123
|
+
expect(isWhitespace({ type: REF_RANGE })).toBe(false);
|
|
124
|
+
expect(isWhitespace({ type: REF_STRUCT })).toBe(false);
|
|
125
|
+
expect(isWhitespace({ type: REF_TERNARY })).toBe(false);
|
|
126
|
+
expect(isWhitespace({ type: STRING })).toBe(false);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('returns true for whitespace types', () => {
|
|
130
|
+
expect(isWhitespace({ type: NEWLINE })).toBe(true);
|
|
131
|
+
expect(isWhitespace({ type: WHITESPACE })).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('isFunction', () => {
|
|
136
|
+
test('returns false for non-function types', () => {
|
|
137
|
+
expect(isFunction(null)).toBe(false);
|
|
138
|
+
// @ts-expect-error -- testing invalid input
|
|
139
|
+
expect(isFunction({})).toBe(false);
|
|
140
|
+
expect(isFunction({ type: BOOLEAN })).toBe(false);
|
|
141
|
+
expect(isFunction({ type: ERROR })).toBe(false);
|
|
142
|
+
expect(isFunction({ type: FX_PREFIX })).toBe(false);
|
|
143
|
+
expect(isFunction({ type: NEWLINE })).toBe(false);
|
|
144
|
+
expect(isFunction({ type: NUMBER })).toBe(false);
|
|
145
|
+
expect(isFunction({ type: OPERATOR })).toBe(false);
|
|
146
|
+
expect(isFunction({ type: REF_BEAM })).toBe(false);
|
|
147
|
+
expect(isFunction({ type: REF_NAMED })).toBe(false);
|
|
148
|
+
expect(isFunction({ type: REF_RANGE })).toBe(false);
|
|
149
|
+
expect(isFunction({ type: REF_STRUCT })).toBe(false);
|
|
150
|
+
expect(isFunction({ type: REF_TERNARY })).toBe(false);
|
|
151
|
+
expect(isFunction({ type: STRING })).toBe(false);
|
|
152
|
+
expect(isFunction({ type: WHITESPACE })).toBe(false);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('returns true for function type', () => {
|
|
156
|
+
expect(isFunction({ type: FUNCTION })).toBe(true);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('isFxPrefix', () => {
|
|
161
|
+
test('returns false for non-prefix types', () => {
|
|
162
|
+
expect(isFxPrefix(null)).toBe(false);
|
|
163
|
+
// @ts-expect-error -- testing invalid input
|
|
164
|
+
expect(isFxPrefix({})).toBe(false);
|
|
165
|
+
expect(isFxPrefix({ type: BOOLEAN })).toBe(false);
|
|
166
|
+
expect(isFxPrefix({ type: ERROR })).toBe(false);
|
|
167
|
+
expect(isFxPrefix({ type: FUNCTION })).toBe(false);
|
|
168
|
+
expect(isFxPrefix({ type: NEWLINE })).toBe(false);
|
|
169
|
+
expect(isFxPrefix({ type: NUMBER })).toBe(false);
|
|
170
|
+
expect(isFxPrefix({ type: OPERATOR })).toBe(false);
|
|
171
|
+
expect(isFxPrefix({ type: REF_BEAM })).toBe(false);
|
|
172
|
+
expect(isFxPrefix({ type: REF_NAMED })).toBe(false);
|
|
173
|
+
expect(isFxPrefix({ type: REF_RANGE })).toBe(false);
|
|
174
|
+
expect(isFxPrefix({ type: REF_STRUCT })).toBe(false);
|
|
175
|
+
expect(isFxPrefix({ type: REF_TERNARY })).toBe(false);
|
|
176
|
+
expect(isFxPrefix({ type: STRING })).toBe(false);
|
|
177
|
+
expect(isFxPrefix({ type: WHITESPACE })).toBe(false);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('returns true for FX_PREFIX type', () => {
|
|
181
|
+
expect(isFxPrefix({ type: FX_PREFIX })).toBe(true);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
describe('isOperator', () => {
|
|
186
|
+
test('returns false for non-operator types', () => {
|
|
187
|
+
expect(isOperator(null)).toBe(false);
|
|
188
|
+
// @ts-expect-error -- testing invalid input
|
|
189
|
+
expect(isOperator({})).toBe(false);
|
|
190
|
+
expect(isOperator({ type: BOOLEAN })).toBe(false);
|
|
191
|
+
expect(isOperator({ type: ERROR })).toBe(false);
|
|
192
|
+
expect(isOperator({ type: FUNCTION })).toBe(false);
|
|
193
|
+
expect(isOperator({ type: FX_PREFIX })).toBe(false);
|
|
194
|
+
expect(isOperator({ type: NEWLINE })).toBe(false);
|
|
195
|
+
expect(isOperator({ type: NUMBER })).toBe(false);
|
|
196
|
+
expect(isOperator({ type: REF_BEAM })).toBe(false);
|
|
197
|
+
expect(isOperator({ type: REF_NAMED })).toBe(false);
|
|
198
|
+
expect(isOperator({ type: REF_RANGE })).toBe(false);
|
|
199
|
+
expect(isOperator({ type: REF_STRUCT })).toBe(false);
|
|
200
|
+
expect(isOperator({ type: REF_TERNARY })).toBe(false);
|
|
201
|
+
expect(isOperator({ type: STRING })).toBe(false);
|
|
202
|
+
expect(isOperator({ type: WHITESPACE })).toBe(false);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('returns true for operator type', () => {
|
|
206
|
+
expect(isOperator({ type: OPERATOR })).toBe(true);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
@@ -3,7 +3,8 @@ import {
|
|
|
3
3
|
FX_PREFIX, WHITESPACE, NEWLINE,
|
|
4
4
|
FUNCTION, OPERATOR,
|
|
5
5
|
ERROR, STRING, NUMBER, BOOLEAN
|
|
6
|
-
} from './constants.
|
|
6
|
+
} from './constants.ts';
|
|
7
|
+
import type { Token } from './types.ts';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Determines whether the specified token is a range.
|
|
@@ -12,10 +13,10 @@ import {
|
|
|
12
13
|
* (`A1` or `A1:B2`), REF_TERNARY (`A1:A`, `A1:1`, `1:A1`, or `A:A1`), or
|
|
13
14
|
* REF_BEAM (`A:A` or `1:1`). In all other cases `false` is returned.
|
|
14
15
|
*
|
|
15
|
-
* @param
|
|
16
|
-
* @returns
|
|
16
|
+
* @param token A token
|
|
17
|
+
* @returns True if the specified token is range, False otherwise.
|
|
17
18
|
*/
|
|
18
|
-
export function isRange (token) {
|
|
19
|
+
export function isRange (token?: Pick<Token, 'type'> | null): boolean {
|
|
19
20
|
return !!token && (
|
|
20
21
|
token.type === REF_RANGE ||
|
|
21
22
|
token.type === REF_BEAM ||
|
|
@@ -30,10 +31,10 @@ export function isRange (token) {
|
|
|
30
31
|
* REF_TERNARY (`A1:A`, `A1:1`, `1:A1`, or `A:A1`), REF_BEAM (`A:A` or `1:1`),
|
|
31
32
|
* or REF_NAMED (`myrange`). In all other cases `false` is returned.
|
|
32
33
|
*
|
|
33
|
-
* @param
|
|
34
|
-
* @returns
|
|
34
|
+
* @param token The token
|
|
35
|
+
* @returns True if the specified token is reference, False otherwise.
|
|
35
36
|
*/
|
|
36
|
-
export function isReference (token) {
|
|
37
|
+
export function isReference (token?: Pick<Token, 'type'> | null): boolean {
|
|
37
38
|
return !!token && (
|
|
38
39
|
token.type === REF_RANGE ||
|
|
39
40
|
token.type === REF_BEAM ||
|
|
@@ -50,10 +51,10 @@ export function isReference (token) {
|
|
|
50
51
|
* ERROR (`#VALUE!`), NUMBER (123.4), or STRING (`"lorem ipsum"`). In all other
|
|
51
52
|
* cases `false` is returned.
|
|
52
53
|
*
|
|
53
|
-
* @param
|
|
54
|
-
* @returns
|
|
54
|
+
* @param token The token
|
|
55
|
+
* @returns True if the specified token is literal, False otherwise.
|
|
55
56
|
*/
|
|
56
|
-
export function isLiteral (token) {
|
|
57
|
+
export function isLiteral (token?: Pick<Token, 'type'> | null): boolean {
|
|
57
58
|
return !!token && (
|
|
58
59
|
token.type === BOOLEAN ||
|
|
59
60
|
token.type === ERROR ||
|
|
@@ -68,10 +69,10 @@ export function isLiteral (token) {
|
|
|
68
69
|
* Returns `true` if the input is a token of type ERROR (`#VALUE!`). In all
|
|
69
70
|
* other cases `false` is returned.
|
|
70
71
|
*
|
|
71
|
-
* @param
|
|
72
|
-
* @returns
|
|
72
|
+
* @param token The token
|
|
73
|
+
* @returns True if the specified token is error, False otherwise.
|
|
73
74
|
*/
|
|
74
|
-
export function isError (token) {
|
|
75
|
+
export function isError (token?: Pick<Token, 'type'> | null): boolean {
|
|
75
76
|
return !!token && token.type === ERROR;
|
|
76
77
|
}
|
|
77
78
|
|
|
@@ -81,10 +82,10 @@ export function isError (token) {
|
|
|
81
82
|
* Returns `true` if the input is a token of type WHITESPACE (` `) or
|
|
82
83
|
* NEWLINE (`\n`). In all other cases `false` is returned.
|
|
83
84
|
*
|
|
84
|
-
* @param
|
|
85
|
-
* @returns
|
|
85
|
+
* @param token The token
|
|
86
|
+
* @returns True if the specified token is whitespace, False otherwise.
|
|
86
87
|
*/
|
|
87
|
-
export function isWhitespace (token) {
|
|
88
|
+
export function isWhitespace (token?: Pick<Token, 'type'> | null): boolean {
|
|
88
89
|
return !!token && (
|
|
89
90
|
token.type === WHITESPACE ||
|
|
90
91
|
token.type === NEWLINE
|
|
@@ -97,10 +98,10 @@ export function isWhitespace (token) {
|
|
|
97
98
|
* Returns `true` if the input is a token of type FUNCTION.
|
|
98
99
|
* In all other cases `false` is returned.
|
|
99
100
|
*
|
|
100
|
-
* @param
|
|
101
|
-
* @returns
|
|
101
|
+
* @param token The token
|
|
102
|
+
* @returns True if the specified token is function, False otherwise.
|
|
102
103
|
*/
|
|
103
|
-
export function isFunction (token) {
|
|
104
|
+
export function isFunction (token?: Pick<Token, 'type'> | null): boolean {
|
|
104
105
|
return !!token && token.type === FUNCTION;
|
|
105
106
|
}
|
|
106
107
|
|
|
@@ -108,10 +109,10 @@ export function isFunction (token) {
|
|
|
108
109
|
* Returns `true` if the input is a token of type FX_PREFIX (leading `=` in
|
|
109
110
|
* formula). In all other cases `false` is returned.
|
|
110
111
|
*
|
|
111
|
-
* @param
|
|
112
|
-
* @returns
|
|
112
|
+
* @param token The token
|
|
113
|
+
* @returns True if the specified token is effects prefix, False otherwise.
|
|
113
114
|
*/
|
|
114
|
-
export function isFxPrefix (token) {
|
|
115
|
+
export function isFxPrefix (token?: Pick<Token, 'type'> | null): boolean {
|
|
115
116
|
return !!token && token.type === FX_PREFIX;
|
|
116
117
|
}
|
|
117
118
|
|
|
@@ -121,9 +122,9 @@ export function isFxPrefix (token) {
|
|
|
121
122
|
* Returns `true` if the input is a token of type OPERATOR (`+` or `:`). In all
|
|
122
123
|
* other cases `false` is returned.
|
|
123
124
|
*
|
|
124
|
-
* @param
|
|
125
|
-
* @returns
|
|
125
|
+
* @param token The token
|
|
126
|
+
* @returns True if the specified token is operator, False otherwise.
|
|
126
127
|
*/
|
|
127
|
-
export function isOperator (token) {
|
|
128
|
+
export function isOperator (token?: Pick<Token, 'type'> | null): boolean {
|
|
128
129
|
return !!token && token.type === OPERATOR;
|
|
129
130
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const PERIOD = 46;
|
|
2
|
+
const COLON = 58;
|
|
3
|
+
|
|
4
|
+
export function advRangeOp (str: string, pos: number): number {
|
|
5
|
+
const c0 = str.charCodeAt(pos);
|
|
6
|
+
if (c0 === PERIOD) {
|
|
7
|
+
const c1 = str.charCodeAt(pos + 1);
|
|
8
|
+
if (c1 === COLON) {
|
|
9
|
+
return str.charCodeAt(pos + 2) === PERIOD ? 3 : 2;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
else if (c0 === COLON) {
|
|
13
|
+
const c1 = str.charCodeAt(pos + 1);
|
|
14
|
+
return c1 === PERIOD ? 2 : 1;
|
|
15
|
+
}
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// regular: [A-Za-z0-9_\u00a1-\uffff]
|
|
2
|
+
export function canEndRange (str: string, pos: number): boolean {
|
|
3
|
+
const c = str.charCodeAt(pos);
|
|
4
|
+
return !(
|
|
5
|
+
(c >= 65 && c <= 90) || // A-Z
|
|
6
|
+
(c >= 97 && c <= 122) || // a-z
|
|
7
|
+
(c >= 48 && c <= 57) || // 0-9
|
|
8
|
+
(c === 95) || // _
|
|
9
|
+
(c > 0xA0) // \u00a1-\uffff
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// partial: [A-Za-z0-9_($.]
|
|
14
|
+
export function canEndPartialRange (str: string, pos: number): boolean {
|
|
15
|
+
const c = str.charCodeAt(pos);
|
|
16
|
+
return !(
|
|
17
|
+
(c >= 65 && c <= 90) || // A-Z
|
|
18
|
+
(c >= 97 && c <= 122) || // a-z
|
|
19
|
+
(c >= 48 && c <= 57) || // 0-9
|
|
20
|
+
(c === 95) || // _
|
|
21
|
+
(c === 40) || // (
|
|
22
|
+
(c === 36) || // $
|
|
23
|
+
(c === 46) // .
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { BOOLEAN } from '../constants.ts';
|
|
2
|
+
import type { Token } from '../types.ts';
|
|
3
|
+
|
|
4
|
+
function preventMatch (c: number) {
|
|
5
|
+
return (
|
|
6
|
+
(c >= 65 && c <= 90) || // A-Z
|
|
7
|
+
(c >= 97 && c <= 122) || // a-z
|
|
8
|
+
(c >= 48 && c <= 57) || // 0-9
|
|
9
|
+
(c === 95) || // _
|
|
10
|
+
(c === 92) || // \
|
|
11
|
+
(c === 40) || // (
|
|
12
|
+
(c === 46) || // .
|
|
13
|
+
(c === 63) || // ?
|
|
14
|
+
(c > 0xA0) // \u00a1-\uffff
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function lexBoolean (str: string, pos: number): Token | undefined {
|
|
19
|
+
// "true" (case insensitive)
|
|
20
|
+
const c0 = str.charCodeAt(pos);
|
|
21
|
+
if (c0 === 84 || c0 === 116) {
|
|
22
|
+
const c1 = str.charCodeAt(pos + 1);
|
|
23
|
+
if (c1 === 82 || c1 === 114) {
|
|
24
|
+
const c2 = str.charCodeAt(pos + 2);
|
|
25
|
+
if (c2 === 85 || c2 === 117) {
|
|
26
|
+
const c3 = str.charCodeAt(pos + 3);
|
|
27
|
+
if (c3 === 69 || c3 === 101) {
|
|
28
|
+
const c4 = str.charCodeAt(pos + 4);
|
|
29
|
+
if (!preventMatch(c4)) {
|
|
30
|
+
return { type: BOOLEAN, value: str.slice(pos, pos + 4) };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// "false" (case insensitive)
|
|
37
|
+
if (c0 === 70 || c0 === 102) {
|
|
38
|
+
const c1 = str.charCodeAt(pos + 1);
|
|
39
|
+
if (c1 === 65 || c1 === 97) {
|
|
40
|
+
const c2 = str.charCodeAt(pos + 2);
|
|
41
|
+
if (c2 === 76 || c2 === 108) {
|
|
42
|
+
const c3 = str.charCodeAt(pos + 3);
|
|
43
|
+
if (c3 === 83 || c3 === 115) {
|
|
44
|
+
const c4 = str.charCodeAt(pos + 4);
|
|
45
|
+
if (c4 === 69 || c4 === 101) {
|
|
46
|
+
const c5 = str.charCodeAt(pos + 5);
|
|
47
|
+
if (!preventMatch(c5)) {
|
|
48
|
+
return { type: BOOLEAN, value: str.slice(pos, pos + 5) };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { CONTEXT, CONTEXT_QUOTE } from '../constants.ts';
|
|
2
|
+
import type { Token } from '../types.ts';
|
|
3
|
+
|
|
4
|
+
const QUOT_SINGLE = 39; // '
|
|
5
|
+
const BR_OPEN = 91; // [
|
|
6
|
+
const BR_CLOSE = 93; // ]
|
|
7
|
+
const EXCL = 33; // !
|
|
8
|
+
|
|
9
|
+
// xlsx xml uses a variant of the syntax that has external references in
|
|
10
|
+
// bracets. Any of: [1]Sheet1!A1, '[1]Sheet one'!A1, [1]!named
|
|
11
|
+
export function lexContextQuoted (str: string, pos: number, options: { xlsx: boolean }): Token | undefined {
|
|
12
|
+
const c0 = str.charCodeAt(pos);
|
|
13
|
+
let br1: number;
|
|
14
|
+
let br2: number;
|
|
15
|
+
// quoted context: '(?:''|[^'])*('|$)(?=!)
|
|
16
|
+
if (c0 === QUOT_SINGLE) {
|
|
17
|
+
const start = pos;
|
|
18
|
+
pos++;
|
|
19
|
+
while (pos < str.length) {
|
|
20
|
+
const c = str.charCodeAt(pos);
|
|
21
|
+
if (c === BR_OPEN) {
|
|
22
|
+
if (br1) { return; } // only 1 allowed
|
|
23
|
+
br1 = pos;
|
|
24
|
+
}
|
|
25
|
+
else if (c === BR_CLOSE) {
|
|
26
|
+
if (br2) { return; } // only 1 allowed
|
|
27
|
+
br2 = pos;
|
|
28
|
+
}
|
|
29
|
+
else if (c === QUOT_SINGLE) {
|
|
30
|
+
pos++;
|
|
31
|
+
if (str.charCodeAt(pos) !== QUOT_SINGLE) {
|
|
32
|
+
let valid = br1 == null && br2 == null;
|
|
33
|
+
if (options.xlsx && (br1 === start + 1) && (br2 === pos - 2)) {
|
|
34
|
+
valid = true;
|
|
35
|
+
}
|
|
36
|
+
if ((br1 >= start + 1) && (br2 < pos - 2) && (br2 > br1 + 1)) {
|
|
37
|
+
valid = true;
|
|
38
|
+
}
|
|
39
|
+
if (valid && str.charCodeAt(pos) === EXCL) {
|
|
40
|
+
return { type: CONTEXT_QUOTE, value: str.slice(start, pos) };
|
|
41
|
+
}
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
pos++;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// xlsx xml uses a variant of the syntax that has external references in
|
|
51
|
+
// bracets. Any of: [1]Sheet1!A1, '[1]Sheet one'!A1, [1]!named
|
|
52
|
+
export function lexContextUnquoted (str: string, pos: number, options: { xlsx: boolean }): Token | undefined {
|
|
53
|
+
const c0 = str.charCodeAt(pos);
|
|
54
|
+
let br1: number;
|
|
55
|
+
let br2: number;
|
|
56
|
+
if (c0 !== QUOT_SINGLE && c0 !== EXCL) {
|
|
57
|
+
const start = pos;
|
|
58
|
+
while (pos < str.length) {
|
|
59
|
+
const c = str.charCodeAt(pos);
|
|
60
|
+
if (c === BR_OPEN) {
|
|
61
|
+
if (br1) { return; } // only 1 allowed
|
|
62
|
+
br1 = pos;
|
|
63
|
+
}
|
|
64
|
+
else if (c === BR_CLOSE) {
|
|
65
|
+
if (br2) { return; } // only 1 allowed
|
|
66
|
+
br2 = pos;
|
|
67
|
+
}
|
|
68
|
+
else if (c === EXCL) {
|
|
69
|
+
let valid = br1 == null && br2 == null;
|
|
70
|
+
if (options.xlsx && (br1 === start) && (br2 === pos - 1)) {
|
|
71
|
+
valid = true;
|
|
72
|
+
}
|
|
73
|
+
if ((br1 >= start) && (br2 < pos - 1) && (br2 > br1 + 1)) {
|
|
74
|
+
valid = true;
|
|
75
|
+
}
|
|
76
|
+
if (valid) {
|
|
77
|
+
return { type: CONTEXT, value: str.slice(start, pos) };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else if (
|
|
81
|
+
(br1 == null || br2 != null) &&
|
|
82
|
+
// [0-9A-Za-z._¡¤§¨ª\u00ad¯-\uffff]
|
|
83
|
+
!(
|
|
84
|
+
(c >= 65 && c <= 90) || // A-Z
|
|
85
|
+
(c >= 97 && c <= 122) || // a-z
|
|
86
|
+
(c >= 48 && c <= 57) || // 0-9
|
|
87
|
+
(c === 46) || // .
|
|
88
|
+
(c === 95) || // _
|
|
89
|
+
(c === 161) || // ¡
|
|
90
|
+
(c === 164) || // ¤
|
|
91
|
+
(c === 167) || // §
|
|
92
|
+
(c === 168) || // ¨
|
|
93
|
+
(c === 170) || // ª
|
|
94
|
+
(c === 173) || // \u00ad
|
|
95
|
+
(c >= 175) // ¯-\uffff
|
|
96
|
+
)
|
|
97
|
+
) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// 0-9A-Za-z._¡¤§¨ª\u00ad¯-\uffff
|
|
101
|
+
pos++;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ERROR } from '../constants.ts';
|
|
2
|
+
import type { Token } from '../types.ts';
|
|
3
|
+
|
|
4
|
+
const re_ERROR = /#(?:NAME\?|FIELD!|CALC!|VALUE!|REF!|DIV\/0!|NULL!|NUM!|N\/A|GETTING_DATA\b|SPILL!|UNKNOWN!|SYNTAX\?|ERROR!|CONNECT!|BLOCKED!|EXTERNAL!)/iy;
|
|
5
|
+
const HASH = 35;
|
|
6
|
+
|
|
7
|
+
export function lexError (str: string, pos: number): Token | undefined {
|
|
8
|
+
if (str.charCodeAt(pos) === HASH) {
|
|
9
|
+
re_ERROR.lastIndex = pos;
|
|
10
|
+
const m = re_ERROR.exec(str);
|
|
11
|
+
if (m) {
|
|
12
|
+
return { type: ERROR, value: m[0] };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { FUNCTION } from '../constants.ts';
|
|
2
|
+
import type { Token } from '../types.ts';
|
|
3
|
+
|
|
4
|
+
const PAREN_OPEN = 40;
|
|
5
|
+
|
|
6
|
+
// [A-Za-z_]+[A-Za-z\d_.]*(?=\()
|
|
7
|
+
export function lexFunction (str: string, pos: number): Token | undefined {
|
|
8
|
+
const start = pos;
|
|
9
|
+
// starts with: a-zA-Z_
|
|
10
|
+
let c = str.charCodeAt(pos);
|
|
11
|
+
if (
|
|
12
|
+
(c < 65 || c > 90) && // A-Z
|
|
13
|
+
(c < 97 || c > 122) && // a-z
|
|
14
|
+
(c !== 95) // _
|
|
15
|
+
) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
pos++;
|
|
19
|
+
// has any number of: a-zA-Z0-9_.
|
|
20
|
+
do {
|
|
21
|
+
c = str.charCodeAt(pos);
|
|
22
|
+
if (
|
|
23
|
+
(c < 65 || c > 90) && // A-Z
|
|
24
|
+
(c < 97 || c > 122) && // a-z
|
|
25
|
+
(c < 48 || c > 57) && // 0-9
|
|
26
|
+
(c !== 95) && // _
|
|
27
|
+
(c !== 46) // .
|
|
28
|
+
) {
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
pos++;
|
|
32
|
+
} while (pos < str.length);
|
|
33
|
+
// followed by a (
|
|
34
|
+
if (str.charCodeAt(pos) === PAREN_OPEN) {
|
|
35
|
+
return { type: FUNCTION, value: str.slice(start, pos) };
|
|
36
|
+
}
|
|
37
|
+
}
|