@kaskad/formula-parser 0.0.1

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/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # formula-parser
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Running unit tests
6
+
7
+ Run `nx test formula-parser` to execute the unit tests.
@@ -0,0 +1,815 @@
1
+ import * as he from 'he';
2
+
3
+ function isNumber(str) {
4
+ const trimmed = str.trim();
5
+ if (!trimmed) {
6
+ return false;
7
+ }
8
+ const num = Number(trimmed);
9
+ return !Number.isNaN(num) && Number.isFinite(num);
10
+ }
11
+
12
+ const tokenConfig = {
13
+ leftParen: { literal: '(', category: 'delimiter' },
14
+ rightParen: { literal: ')', category: 'delimiter' },
15
+ leftBracket: { literal: '[', category: 'delimiter' },
16
+ rightBracket: { literal: ']', category: 'delimiter' },
17
+ leftBrace: { literal: '{', category: 'delimiter' },
18
+ rightBrace: { literal: '}', category: 'delimiter' },
19
+ plus: { literal: '+', category: 'operator' },
20
+ minus: { literal: '-', category: 'operator' },
21
+ multiply: { literal: '*', category: 'operator' },
22
+ divide: { literal: '/', category: 'operator' },
23
+ invert: { literal: '!', category: 'operator' },
24
+ questionMark: { literal: '?', category: 'operator' },
25
+ eq: { literal: '==', category: 'operator' },
26
+ neq: { literal: '!=', category: 'operator' },
27
+ gt: { literal: '>', category: 'operator' },
28
+ lt: { literal: '<', category: 'operator' },
29
+ gte: { literal: '>=', category: 'operator' },
30
+ lte: { literal: '<=', category: 'operator' },
31
+ comma: { literal: ',', category: 'punctuation' },
32
+ colon: { literal: ':', category: 'punctuation' },
33
+ identifier: { category: 'entity' },
34
+ value: { category: 'entity' },
35
+ endOfFile: { literal: 'EOF', category: 'misc' },
36
+ };
37
+ const getTokenCategory = (type) => {
38
+ return tokenConfig[type].category;
39
+ };
40
+
41
+ class Token {
42
+ type;
43
+ value;
44
+ column;
45
+ line;
46
+ constructor(type, value, column = -1, line = '') {
47
+ this.type = type;
48
+ this.column = column;
49
+ this.line = line;
50
+ this.value = this.parseValue(type, value);
51
+ }
52
+ parseValue(type, value) {
53
+ if (type === 'endOfFile') {
54
+ return 'EOF';
55
+ }
56
+ if (this.isDelimiterOrOperator(type)) {
57
+ return value;
58
+ }
59
+ if (type === 'value') {
60
+ return this.parseValueToken(value);
61
+ }
62
+ if (type === 'identifier') {
63
+ return value;
64
+ }
65
+ return value;
66
+ }
67
+ isDelimiterOrOperator(type) {
68
+ const category = getTokenCategory(type);
69
+ return category === 'delimiter' || category === 'operator' || category === 'punctuation';
70
+ }
71
+ parseValueToken(value) {
72
+ const normalizedValue = value.toLowerCase();
73
+ if (normalizedValue === 'null') {
74
+ return null;
75
+ }
76
+ if (normalizedValue === 'true') {
77
+ return true;
78
+ }
79
+ if (normalizedValue === 'false') {
80
+ return false;
81
+ }
82
+ if (isNumber(value)) {
83
+ return Number(value);
84
+ }
85
+ return value.replace(/((^'|'$)|(?:^"|"$))/g, '');
86
+ }
87
+ }
88
+
89
+ const LITERAL_REGEX = /^(null|NULL|true|TRUE|false|FALSE)(?=[^\w]|$)/;
90
+ // Matches identifiers including those with -> for component property access
91
+ // Allows $ at start and after -> (e.g., $value, &ref->$value)
92
+ const IDENTIFIER_REGEX = /^[a-zA-Z$&%][\w\-_.?>$]+/;
93
+ const NUMBER_REGEX = /^\d+\.?\d*/;
94
+ const STRING_PATTERNS = [/^'[^']+'/, /^"[^"]+"/, /^''/, /^""/];
95
+ const MULTI_CHAR_LITERALS = new Map();
96
+ const SINGLE_CHAR_LITERALS = new Map();
97
+ for (const [tokenType, config] of Object.entries(tokenConfig)) {
98
+ if ('literal' in config && config.literal !== 'EOF') {
99
+ if (config.literal.length > 1) {
100
+ MULTI_CHAR_LITERALS.set(config.literal, tokenType);
101
+ }
102
+ else {
103
+ SINGLE_CHAR_LITERALS.set(config.literal, tokenType);
104
+ }
105
+ }
106
+ }
107
+ class Lexer {
108
+ text = '';
109
+ index = 0;
110
+ allowIncomplete = false;
111
+ incompletePatterns = null;
112
+ tokenize(text, allowIncomplete = false) {
113
+ this.text = text;
114
+ this.index = 0;
115
+ this.allowIncomplete = allowIncomplete;
116
+ const result = [];
117
+ while (this.index < this.text.length + 1) {
118
+ result.push(this.getNextToken());
119
+ }
120
+ return result;
121
+ }
122
+ advance(step = 1) {
123
+ this.index += step;
124
+ }
125
+ getCurrentLine() {
126
+ return this.index < this.text.length ? this.text.substring(this.index) : '';
127
+ }
128
+ getNextToken() {
129
+ const remaining = this.getCurrentLine();
130
+ if (remaining === '') {
131
+ this.advance();
132
+ return new Token('endOfFile', 'EOF', this.index, this.text);
133
+ }
134
+ let skipSpaces = 0;
135
+ while (skipSpaces < remaining.length && remaining[skipSpaces] === ' ') {
136
+ skipSpaces++;
137
+ }
138
+ if (skipSpaces > 0) {
139
+ this.advance(skipSpaces);
140
+ return this.getNextToken();
141
+ }
142
+ const literal = remaining.match(LITERAL_REGEX);
143
+ if (literal) {
144
+ this.advance(literal[0].length);
145
+ return new Token('value', literal[0], this.index, this.text);
146
+ }
147
+ // Check for identifier BEFORE single-character tokens
148
+ // This allows ?-> to be part of identifiers (e.g., &ref?->value)
149
+ const identifier = remaining.match(IDENTIFIER_REGEX);
150
+ if (identifier) {
151
+ this.advance(identifier[0].length);
152
+ return new Token('identifier', identifier[0], this.index, this.text);
153
+ }
154
+ for (const [literal, type] of MULTI_CHAR_LITERALS) {
155
+ if (remaining.startsWith(literal)) {
156
+ this.advance(literal.length);
157
+ return new Token(type, literal, this.index, this.text);
158
+ }
159
+ }
160
+ const firstChar = remaining[0];
161
+ const tokenType = SINGLE_CHAR_LITERALS.get(firstChar);
162
+ if (tokenType) {
163
+ this.advance(1);
164
+ return new Token(tokenType, firstChar, this.index, this.text);
165
+ }
166
+ const patterns = this.getValuePatterns();
167
+ for (const pattern of patterns) {
168
+ const value = remaining.match(pattern);
169
+ if (value) {
170
+ this.advance(value[0].length);
171
+ return new Token('value', value[0], this.index, this.text);
172
+ }
173
+ }
174
+ throw new SyntaxError(`Invalid syntax at position ${this.index}\n` + `Formula: ${this.text}\n` + ` ${' '.repeat(this.index)}^`);
175
+ }
176
+ getValuePatterns() {
177
+ if (this.allowIncomplete) {
178
+ if (!this.incompletePatterns) {
179
+ this.incompletePatterns = STRING_PATTERNS.map((pattern) => new RegExp(pattern.source + '?')).concat(NUMBER_REGEX);
180
+ }
181
+ return this.incompletePatterns;
182
+ }
183
+ return [...STRING_PATTERNS, NUMBER_REGEX];
184
+ }
185
+ }
186
+
187
+ class Parser {
188
+ tokens = [];
189
+ tokenIndex = 0;
190
+ allowIncomplete = false;
191
+ atomParsers = {
192
+ identifier: () => (this.peekToken()?.type == 'leftParen' ? this.parseFunction() : this.parseReference()),
193
+ leftParen: () => this.parseGroup(),
194
+ value: () => this.parseLiteral(),
195
+ leftBracket: () => this.parseArray(),
196
+ leftBrace: () => this.parseObject(),
197
+ invert: () => this.parseUnaryNot(),
198
+ };
199
+ parse(tokens, allowIncomplete = false) {
200
+ this.tokens = tokens;
201
+ this.tokenIndex = 0;
202
+ this.allowIncomplete = allowIncomplete;
203
+ return this.parseFormula();
204
+ }
205
+ getCurrentToken() {
206
+ if (this.tokenIndex >= this.tokens.length) {
207
+ throw new SyntaxError('Unexpected end of input');
208
+ }
209
+ return this.tokens[this.tokenIndex];
210
+ }
211
+ peekToken(offset = 1) {
212
+ const nextIndex = this.tokenIndex + offset;
213
+ return nextIndex < this.tokens.length ? this.tokens[nextIndex] : undefined;
214
+ }
215
+ throwExpectedButFound(expected) {
216
+ throw new SyntaxError(`Expected a "${expected}" token but found: "${this.getCurrentToken().value}"\n` +
217
+ `formula ${this.getCurrentToken().line}\n` +
218
+ ` ${' '.repeat(this.getCurrentToken().column)}^`);
219
+ }
220
+ throwUnexpectedToken() {
221
+ throw new SyntaxError(`Unexpected "${this.getCurrentToken().value}"\n` +
222
+ `formula ${this.getCurrentToken().line}\n` +
223
+ ` ${' '.repeat(this.getCurrentToken().column)}^`);
224
+ }
225
+ consume(type) {
226
+ if (this.getCurrentToken().type === type) {
227
+ this.tokenIndex += 1;
228
+ }
229
+ else {
230
+ this.throwExpectedButFound(type);
231
+ }
232
+ }
233
+ consumeIfPresent(type) {
234
+ if (this.allowIncomplete) {
235
+ try {
236
+ this.consume(type);
237
+ return true;
238
+ }
239
+ catch {
240
+ return false;
241
+ }
242
+ }
243
+ else {
244
+ this.consume(type);
245
+ return true;
246
+ }
247
+ }
248
+ parseFormula() {
249
+ const entity = this.parseTernary();
250
+ this.consume('endOfFile');
251
+ return entity;
252
+ }
253
+ parseFunction() {
254
+ const functionName = this.getCurrentToken().value;
255
+ this.consume('identifier');
256
+ this.consume('leftParen');
257
+ const args = [];
258
+ do {
259
+ if (this.getCurrentToken().type === 'comma') {
260
+ this.consume('comma');
261
+ }
262
+ if (this.allowIncomplete && this.getCurrentToken().type === 'endOfFile') {
263
+ break;
264
+ }
265
+ if (this.getCurrentToken().type === 'rightParen') {
266
+ break;
267
+ }
268
+ args.push(this.parseTernary());
269
+ } while (this.getCurrentToken().type === 'comma');
270
+ const closed = this.consumeIfPresent('rightParen');
271
+ return { type: 'function', name: functionName, args, closed };
272
+ }
273
+ parseExpression() {
274
+ let result = this.parseTerm();
275
+ while (['plus', 'minus'].includes(this.getCurrentToken().type)) {
276
+ const operator = this.getCurrentToken().type;
277
+ result = this.parseAdditionOperator(result, operator);
278
+ }
279
+ if (result == null) {
280
+ this.throwUnexpectedToken();
281
+ }
282
+ return result;
283
+ }
284
+ parseTerm() {
285
+ let result = this.parseAtom();
286
+ while (['multiply', 'divide'].includes(this.getCurrentToken().type)) {
287
+ const operator = this.getCurrentToken().type;
288
+ result = this.parseMultiplicationOperator(result, operator);
289
+ }
290
+ if (result == null) {
291
+ this.throwUnexpectedToken();
292
+ }
293
+ return result;
294
+ }
295
+ parseAtom() {
296
+ const tokenType = this.getCurrentToken().type;
297
+ const parser = this.atomParsers[tokenType];
298
+ if (!parser) {
299
+ this.throwUnexpectedToken();
300
+ }
301
+ return parser();
302
+ }
303
+ parseTernary() {
304
+ const condition = this.parseComparison();
305
+ if (this.getCurrentToken().type !== 'questionMark') {
306
+ return condition;
307
+ }
308
+ this.consume('questionMark');
309
+ const result = {
310
+ type: 'ternary',
311
+ condition,
312
+ thenBranch: null,
313
+ elseBranch: null,
314
+ closed: false,
315
+ };
316
+ if (this.getCurrentToken().type === 'endOfFile') {
317
+ return result;
318
+ }
319
+ result.thenBranch = this.parseTernary();
320
+ if (this.getCurrentToken().type === 'endOfFile') {
321
+ return result;
322
+ }
323
+ this.consume('colon');
324
+ if (this.getCurrentToken().type === 'endOfFile') {
325
+ return result;
326
+ }
327
+ result.elseBranch = this.parseTernary();
328
+ result.closed = true;
329
+ return result;
330
+ }
331
+ parseGroup() {
332
+ this.consume('leftParen');
333
+ const entity = this.parseTernary();
334
+ const closed = this.consumeIfPresent('rightParen');
335
+ return { type: 'group', item: entity, closed };
336
+ }
337
+ parseUnaryNot() {
338
+ this.consume('invert');
339
+ const result = { type: 'not', operand: null, closed: false };
340
+ if (this.getCurrentToken().type === 'endOfFile') {
341
+ return result;
342
+ }
343
+ result.operand = this.parseAtom();
344
+ result.closed = true;
345
+ return result;
346
+ }
347
+ parseAdditionOperator(left, operator) {
348
+ this.consume(operator);
349
+ const isEOF = this.getCurrentToken().type === 'endOfFile';
350
+ return {
351
+ type: operator,
352
+ left,
353
+ right: isEOF ? null : this.parseTerm(),
354
+ closed: !isEOF,
355
+ };
356
+ }
357
+ parseMultiplicationOperator(left, operator) {
358
+ this.consume(operator);
359
+ const isEOF = this.getCurrentToken().type === 'endOfFile';
360
+ return {
361
+ type: operator,
362
+ left,
363
+ right: isEOF ? null : this.parseAtom(),
364
+ closed: !isEOF,
365
+ };
366
+ }
367
+ parseComparison() {
368
+ const left = this.parseExpression();
369
+ const comparisonTypes = ['eq', 'neq', 'gt', 'lt', 'gte', 'lte'];
370
+ if (comparisonTypes.includes(this.getCurrentToken().type)) {
371
+ return this.parseComparisonOperator(left);
372
+ }
373
+ return left;
374
+ }
375
+ parseComparisonOperator(left) {
376
+ const operator = this.getCurrentToken().type;
377
+ this.consume(operator);
378
+ const isEOF = this.getCurrentToken().type === 'endOfFile';
379
+ return {
380
+ type: operator,
381
+ left,
382
+ right: isEOF ? null : this.parseExpression(),
383
+ closed: !isEOF,
384
+ };
385
+ }
386
+ parseReference() {
387
+ const variable = this.getCurrentToken().value;
388
+ this.consume('identifier');
389
+ return { type: 'reference', name: variable };
390
+ }
391
+ parseLiteral() {
392
+ const value = this.getCurrentToken().value;
393
+ this.consume('value');
394
+ return { type: 'value', value };
395
+ }
396
+ parseArray() {
397
+ this.consume('leftBracket');
398
+ const items = [];
399
+ do {
400
+ if (this.getCurrentToken().type === 'comma') {
401
+ this.consume('comma');
402
+ }
403
+ if (this.allowIncomplete && this.getCurrentToken().type === 'endOfFile') {
404
+ break;
405
+ }
406
+ if (this.getCurrentToken().type === 'rightBracket') {
407
+ break;
408
+ }
409
+ items.push(this.parseTernary());
410
+ } while (this.getCurrentToken().type === 'comma');
411
+ const closed = this.consumeIfPresent('rightBracket');
412
+ return { type: 'array', items, closed };
413
+ }
414
+ parseObjectKey() {
415
+ const currentToken = this.getCurrentToken();
416
+ // Unquoted identifier → IdentifierAstNode (literal key)
417
+ if (currentToken.type === 'identifier') {
418
+ const name = currentToken.value;
419
+ this.consume('identifier');
420
+ return { type: 'identifier', name };
421
+ }
422
+ // Anything else (quoted strings, numbers, expressions) → parse normally
423
+ return this.parseAtom();
424
+ }
425
+ parseObject() {
426
+ this.consume('leftBrace');
427
+ const properties = [];
428
+ do {
429
+ if (this.getCurrentToken().type === 'rightBrace') {
430
+ break;
431
+ }
432
+ if (this.getCurrentToken().type === 'comma') {
433
+ this.consume('comma');
434
+ }
435
+ if (this.allowIncomplete && this.getCurrentToken().type === 'endOfFile') {
436
+ break;
437
+ }
438
+ if (this.getCurrentToken().type === 'rightBracket') {
439
+ break;
440
+ }
441
+ const key = this.parseObjectKey();
442
+ this.consume('colon');
443
+ const value = this.parseTernary();
444
+ properties.push({ key, value });
445
+ } while (this.getCurrentToken().type === 'comma');
446
+ const closed = this.consumeIfPresent('rightBrace');
447
+ return { type: 'object', properties, closed };
448
+ }
449
+ }
450
+
451
+ class Stringifier {
452
+ nodeVisitors = {
453
+ function: (node) => this.visitFunctionNode(node),
454
+ reference: (node) => this.visitVariableNode(node),
455
+ identifier: (node) => this.visitIdentifierNode(node),
456
+ value: (node) => this.visitValueNode(node),
457
+ array: (node) => this.visitArrayNode(node),
458
+ object: (node) => this.visitObjectNode(node),
459
+ not: (node) => this.visitInvertNode(node),
460
+ plus: (node) => this.visitOperatorNode(node),
461
+ minus: (node) => this.visitOperatorNode(node),
462
+ multiply: (node) => this.visitOperatorNode(node),
463
+ divide: (node) => this.visitOperatorNode(node),
464
+ eq: (node) => this.visitOperatorNode(node),
465
+ neq: (node) => this.visitOperatorNode(node),
466
+ gt: (node) => this.visitOperatorNode(node),
467
+ lt: (node) => this.visitOperatorNode(node),
468
+ gte: (node) => this.visitOperatorNode(node),
469
+ lte: (node) => this.visitOperatorNode(node),
470
+ group: (node) => this.visitGroup(node),
471
+ ternary: (node) => this.visitTernaryNode(node),
472
+ };
473
+ visitNode(node) {
474
+ const visitor = this.nodeVisitors[node.type];
475
+ if (!visitor) {
476
+ throw new Error(`Unrecognised AST node type: ${node.type}`);
477
+ }
478
+ return visitor(node);
479
+ }
480
+ visitValueNode(node) {
481
+ if (node.value && typeof node.value === 'object') {
482
+ return this.processObjectValue(node.value);
483
+ }
484
+ return this.processStringValue(node.value);
485
+ }
486
+ processObjectValue(item) {
487
+ const object = Object.entries(item);
488
+ const key_values = object.map(([key, value]) => {
489
+ let result_value;
490
+ if (typeof value === 'object') {
491
+ result_value = this.processObjectValue(value);
492
+ }
493
+ else {
494
+ result_value = this.processStringValue(value);
495
+ }
496
+ return `${key}: ${result_value}`;
497
+ });
498
+ return `{ ${key_values.join(', ')} }`;
499
+ }
500
+ processStringValue(item) {
501
+ if (typeof item === 'string') {
502
+ const escaped = he.escape(item.toString());
503
+ return `'${String(escaped)}'`;
504
+ }
505
+ return String(item);
506
+ }
507
+ visitOperatorNode(node) {
508
+ const operatorToken = tokenConfig[node.type];
509
+ if (!operatorToken || !('literal' in operatorToken)) {
510
+ throw new Error(`Unrecognised operator type: ${node.type}`);
511
+ }
512
+ const operator = ` ${operatorToken.literal} `;
513
+ const left = this.visitNode(node.left);
514
+ const right = node.closed && node.right ? this.visitNode(node.right) : '';
515
+ return `${left}${operator}${right}`;
516
+ }
517
+ visitTernaryNode(node) {
518
+ let result = this.visitNode(node.condition) + ' ? ';
519
+ if (node.thenBranch == null) {
520
+ return result;
521
+ }
522
+ result += this.visitNode(node.thenBranch) + ' : ';
523
+ if (node.elseBranch == null) {
524
+ return result;
525
+ }
526
+ result += this.visitNode(node.elseBranch);
527
+ return result;
528
+ }
529
+ }
530
+
531
+ class DefaultStringifier extends Stringifier {
532
+ stringify(tree) {
533
+ return this.visitNode(tree);
534
+ }
535
+ visitFunctionNode(node) {
536
+ return `${node.name}(${node.args.map((node) => this.visitNode(node)).join(', ')})`;
537
+ }
538
+ visitVariableNode(node) {
539
+ return node.name;
540
+ }
541
+ visitIdentifierNode(node) {
542
+ return node.name;
543
+ }
544
+ visitArrayNode(node) {
545
+ return '[' + node.items.map((node) => this.visitNode(node)).join(', ') + ']';
546
+ }
547
+ visitObjectNode(node) {
548
+ let result = '';
549
+ result += '{';
550
+ result += node.properties
551
+ .map((prop) => {
552
+ const key = this.visitNode(prop.key);
553
+ const value = this.visitNode(prop.value);
554
+ return `${key}: ${value}`;
555
+ })
556
+ .join(', ');
557
+ result += '}';
558
+ return result;
559
+ }
560
+ visitInvertNode(node) {
561
+ let result = '!';
562
+ if (node.operand == null) {
563
+ return result;
564
+ }
565
+ result += this.visitNode(node.operand);
566
+ return result;
567
+ }
568
+ visitGroup(node) {
569
+ let result = '';
570
+ result += `(`;
571
+ result += this.visitNode(node.item);
572
+ result += node.closed ? ')' : '';
573
+ return result;
574
+ }
575
+ }
576
+
577
+ class HtmlStringifier extends Stringifier {
578
+ parenStyleCycle;
579
+ currentDeep = 1;
580
+ constructor(parenStyleCycle = 3) {
581
+ super();
582
+ this.parenStyleCycle = parenStyleCycle;
583
+ }
584
+ stringify(tree) {
585
+ this.currentDeep = 1;
586
+ return `<div>${this.visitNode(tree)}</div>`;
587
+ }
588
+ setParenStyleCycle(newParenStyleCycle) {
589
+ this.parenStyleCycle = newParenStyleCycle;
590
+ }
591
+ visitFunctionNode(node) {
592
+ let result = '';
593
+ const paren = `paren-deep-${this.currentDeep}`;
594
+ this.incrementParenDeep();
595
+ result += this.createHtmlSpan('function', node.name);
596
+ result += this.createHtmlSpan(paren, '(');
597
+ result += node.args.map((arg) => this.visitNode(arg)).join(', ');
598
+ result += node.closed ? this.createHtmlSpan(paren, ')') : ``;
599
+ return result;
600
+ }
601
+ incrementParenDeep() {
602
+ if (this.currentDeep < this.parenStyleCycle) {
603
+ this.currentDeep += 1;
604
+ }
605
+ else {
606
+ this.currentDeep = 1;
607
+ }
608
+ }
609
+ visitVariableNode(node) {
610
+ return this.createHtmlSpan('variable', node.name);
611
+ }
612
+ visitIdentifierNode(node) {
613
+ return this.createHtmlSpan('identifier', node.name);
614
+ }
615
+ processStringValue(value) {
616
+ return this.createHtmlSpan('value', super.processStringValue(value));
617
+ }
618
+ visitArrayNode(node) {
619
+ let result = '';
620
+ const paren = `paren-deep-${this.currentDeep}`;
621
+ this.incrementParenDeep();
622
+ result += this.createHtmlSpan(paren, '[');
623
+ result += node.items
624
+ .map((node) => {
625
+ const deepStored = this.currentDeep;
626
+ const result = this.visitNode(node);
627
+ this.currentDeep = deepStored;
628
+ return result;
629
+ })
630
+ .join(', ');
631
+ result += node.closed ? this.createHtmlSpan(paren, ']') : ``;
632
+ return result;
633
+ }
634
+ visitObjectNode(node) {
635
+ let result = '';
636
+ const paren = `paren-deep-${this.currentDeep}`;
637
+ this.incrementParenDeep();
638
+ result += this.createHtmlSpan(paren, '{');
639
+ result += node.properties
640
+ .map((prop) => {
641
+ const deepStored = this.currentDeep;
642
+ const key = this.visitNode(prop.key);
643
+ const value = this.visitNode(prop.value);
644
+ this.currentDeep = deepStored;
645
+ return `${key}: ${value}`;
646
+ })
647
+ .join(', ');
648
+ result += node.closed ? this.createHtmlSpan(paren, '}') : ``;
649
+ return result;
650
+ }
651
+ visitInvertNode(node) {
652
+ const paren = `paren-deep-${this.currentDeep}`;
653
+ let result = this.createHtmlSpan(paren, '!');
654
+ if (node.operand == null) {
655
+ return result;
656
+ }
657
+ result += this.visitNode(node.operand);
658
+ return result;
659
+ }
660
+ visitGroup(node) {
661
+ let result = '';
662
+ const paren = `paren-deep-${this.currentDeep}`;
663
+ this.incrementParenDeep();
664
+ result += this.createHtmlSpan(paren, '(');
665
+ result += this.visitNode(node.item);
666
+ result += node.closed ? this.createHtmlSpan(paren, ')') : ``;
667
+ return result;
668
+ }
669
+ createHtmlSpan(class_attr, value) {
670
+ return `<span class="${class_attr}">${value}</span>`;
671
+ }
672
+ }
673
+
674
+ /**
675
+ * Parses a formula string into an Abstract Syntax Tree (AST)
676
+ *
677
+ * @param formula - The formula string to parse
678
+ * @returns The parsed AST representation that can be traversed or evaluated
679
+ *
680
+ * @example Basic expressions
681
+ * ```typescript
682
+ * parseFormula('42'); // Literal number
683
+ * parseFormula('"hello"'); // Literal string
684
+ * parseFormula('myVariable'); // Variable reference
685
+ * ```
686
+ *
687
+ * @example Function calls
688
+ * ```typescript
689
+ * parseFormula('sum(1, 2, 3)'); // Function with arguments
690
+ * parseFormula('getValue()'); // Function without arguments
691
+ * parseFormula('calc(sum(1, 2), multiply(3, 4))'); // Nested functions
692
+ * ```
693
+ *
694
+ * @example Operators and arithmetic
695
+ * ```typescript
696
+ * parseFormula('price * quantity'); // Multiplication
697
+ * parseFormula('total - discount'); // Subtraction
698
+ * parseFormula('(base + bonus) * multiplier'); // Grouped expressions
699
+ * ```
700
+ *
701
+ * @example Complex structures
702
+ * ```typescript
703
+ * parseFormula('[1, 2, 3]'); // Arrays
704
+ * parseFormula('{name: "John", age: 30}'); // Objects
705
+ * parseFormula('isValid ? "yes" : "no"'); // Ternary conditionals
706
+ * parseFormula('!isDisabled'); // Logical NOT
707
+ * ```
708
+ *
709
+ * @throws {SyntaxError} When the formula contains invalid syntax
710
+ * - Invalid characters (e.g., '@', '#', etc.)
711
+ * - Unclosed parentheses, brackets, or braces
712
+ * - Invalid token sequences
713
+ *
714
+ * @remarks
715
+ * Supported syntax elements:
716
+ * - **Literals**: numbers, strings (single/double quoted), booleans
717
+ * - **Variables**: identifiers starting with letters, $, &, or %
718
+ * - **Functions**: identifier followed by parentheses with optional arguments
719
+ * - **Arrays**: square brackets with comma-separated values
720
+ * - **Objects**: curly braces with key-value pairs
721
+ * - **Operators**: +, -, *, / (following standard precedence)
722
+ * - **Comparison**: ==, !=, >, <, >=, <= (lower precedence than arithmetic)
723
+ * - **Logical**: ! (NOT operator)
724
+ * - **Ternary**: condition ? trueBranch : falseBranch
725
+ * - **Grouping**: parentheses for overriding precedence
726
+ */
727
+ function parseFormula(formula) {
728
+ const lexer = new Lexer();
729
+ const parser = new Parser();
730
+ const tokenized = lexer.tokenize(formula);
731
+ return parser.parse(tokenized);
732
+ }
733
+ /**
734
+ * Converts a formula AST back into a formula string
735
+ *
736
+ * @param ast - The AST to stringify (as returned by parseFormula)
737
+ * @returns The formula string representation
738
+ *
739
+ * @example Basic usage
740
+ * ```typescript
741
+ * const ast = parseFormula('sum(1, 2, 3)');
742
+ * const formula = stringifyFormulaAst(ast); // "sum(1, 2, 3)"
743
+ * ```
744
+ *
745
+ * @example Round-trip parsing
746
+ * ```typescript
747
+ * const original = 'price * quantity + tax';
748
+ * const ast = parseFormula(original);
749
+ * const reconstructed = stringifyFormulaAst(ast); // "price * quantity + tax"
750
+ * ```
751
+ *
752
+ * @remarks
753
+ * - The output may differ slightly from the original in spacing
754
+ * - Parentheses may be added or removed based on operator precedence
755
+ * - String quotes will be normalized to the stringifier's preference
756
+ */
757
+ function stringifyFormulaAst(ast) {
758
+ const stringifier = new DefaultStringifier();
759
+ return stringifier.stringify(ast);
760
+ }
761
+ /**
762
+ * Formats a formula string as HTML with syntax highlighting
763
+ *
764
+ * @param formula - The formula string to format
765
+ * @param allowIncomplete - Whether to allow incomplete formulas (useful for live editing). Default: false
766
+ * @param parenStyleCycle - Number of parenthesis depth styles to cycle through. Default: 3
767
+ * @returns HTML string with syntax highlighting using span elements with CSS classes
768
+ *
769
+ * @example Basic usage
770
+ * ```typescript
771
+ * const html = formatFormulaAsHtml('sum(1, 2, 3)');
772
+ * // Returns: '<span class="function">sum</span><span class="paren-0">(</span>...'
773
+ * ```
774
+ *
775
+ * @example Live editor with incomplete formulas
776
+ * ```typescript
777
+ * // Allow incomplete formulas for real-time syntax highlighting as user types
778
+ * const html = formatFormulaAsHtml('sum(1, 2', true);
779
+ * // Won't throw error even though parenthesis is unclosed
780
+ * ```
781
+ *
782
+ * @example Custom parenthesis depth cycling
783
+ * ```typescript
784
+ * // Use 5 different colors for nested parentheses
785
+ * const html = formatFormulaAsHtml('f(g(h(i(j(k())))))', false, 5);
786
+ * ```
787
+ *
788
+ * @remarks
789
+ * CSS classes used in the output:
790
+ * - `function` - Function names
791
+ * - `variable` - Variable identifiers
792
+ * - `value` - Literal values (numbers, strings, booleans)
793
+ * - `operator` - Arithmetic operators (+, -, *, /)
794
+ * - `paren-N` - Parentheses at depth N (cycles based on parenStyleCycle)
795
+ * - `bracket` - Square brackets for arrays
796
+ * - `brace` - Curly braces for objects
797
+ * - `punctuation` - Commas, colons, etc.
798
+ *
799
+ * @throws {SyntaxError} When formula has invalid syntax (unless allowIncomplete is true)
800
+ */
801
+ function formatFormulaAsHtml(formula, allowIncomplete = false, parenStyleCycle = 3) {
802
+ const lexer = new Lexer();
803
+ const parser = new Parser();
804
+ const stringifier = new HtmlStringifier(parenStyleCycle);
805
+ const tokenized = lexer.tokenize(formula, allowIncomplete);
806
+ const node = parser.parse(tokenized, allowIncomplete);
807
+ return stringifier.stringify(node);
808
+ }
809
+
810
+ /**
811
+ * Generated bundle index. Do not edit.
812
+ */
813
+
814
+ export { DefaultStringifier, HtmlStringifier, Lexer, Parser, Stringifier, Token, formatFormulaAsHtml, getTokenCategory, parseFormula, stringifyFormulaAst, tokenConfig };
815
+ //# sourceMappingURL=kaskad-formula-parser.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kaskad-formula-parser.mjs","sources":["../../../../libs/formula-parser/src/lib/utils.ts","../../../../libs/formula-parser/src/lib/lexer/token-config.ts","../../../../libs/formula-parser/src/lib/lexer/token.ts","../../../../libs/formula-parser/src/lib/lexer/lexer.ts","../../../../libs/formula-parser/src/lib/parser/parser.ts","../../../../libs/formula-parser/src/lib/stringifier/stringifier.ts","../../../../libs/formula-parser/src/lib/stringifier/default-stringifier.ts","../../../../libs/formula-parser/src/lib/stringifier/html-stringifier.ts","../../../../libs/formula-parser/src/lib/formula-parser.ts","../../../../libs/formula-parser/src/kaskad-formula-parser.ts"],"sourcesContent":["export function isNumber(str: string): boolean {\n const trimmed = str.trim();\n\n if (!trimmed) {\n return false;\n }\n\n const num = Number(trimmed);\n return !Number.isNaN(num) && Number.isFinite(num);\n}\n","export const tokenConfig = {\n leftParen: { literal: '(', category: 'delimiter' },\n rightParen: { literal: ')', category: 'delimiter' },\n leftBracket: { literal: '[', category: 'delimiter' },\n rightBracket: { literal: ']', category: 'delimiter' },\n leftBrace: { literal: '{', category: 'delimiter' },\n rightBrace: { literal: '}', category: 'delimiter' },\n plus: { literal: '+', category: 'operator' },\n minus: { literal: '-', category: 'operator' },\n multiply: { literal: '*', category: 'operator' },\n divide: { literal: '/', category: 'operator' },\n invert: { literal: '!', category: 'operator' },\n questionMark: { literal: '?', category: 'operator' },\n eq: { literal: '==', category: 'operator' },\n neq: { literal: '!=', category: 'operator' },\n gt: { literal: '>', category: 'operator' },\n lt: { literal: '<', category: 'operator' },\n gte: { literal: '>=', category: 'operator' },\n lte: { literal: '<=', category: 'operator' },\n comma: { literal: ',', category: 'punctuation' },\n colon: { literal: ':', category: 'punctuation' },\n identifier: { category: 'entity' },\n value: { category: 'entity' },\n endOfFile: { literal: 'EOF', category: 'misc' },\n} as const;\n\nexport type TokenType = keyof typeof tokenConfig;\n\nexport type TokenTypeMap = {\n [K in keyof typeof tokenConfig]: K extends 'identifier'\n ? string\n : K extends 'value'\n ? string | boolean | number\n : (typeof tokenConfig)[K] extends { literal: infer L }\n ? L\n : never;\n};\n\nexport const getTokenCategory = (type: TokenType): string => {\n return tokenConfig[type].category;\n};\n","import { isNumber } from '../utils';\nimport { getTokenCategory, TokenType, TokenTypeMap } from './token-config';\n\nexport type TokenValue = string | boolean | number | null;\n\nexport class Token<T extends TokenType = TokenType> {\n public readonly type: T;\n public readonly value: TokenTypeMap[T];\n public readonly column: number;\n public readonly line: string;\n\n constructor(type: T, value: string, column = -1, line = '') {\n this.type = type;\n this.column = column;\n this.line = line;\n\n this.value = this.parseValue(type, value) as TokenTypeMap[T];\n }\n\n private parseValue(type: T, value: string): TokenValue {\n if (type === 'endOfFile') {\n return 'EOF' as TokenValue;\n }\n\n if (this.isDelimiterOrOperator(type)) {\n return value as TokenValue;\n }\n\n if (type === 'value') {\n return this.parseValueToken(value);\n }\n\n if (type === 'identifier') {\n return value;\n }\n\n return value;\n }\n\n private isDelimiterOrOperator(type: T): boolean {\n const category = getTokenCategory(type);\n return category === 'delimiter' || category === 'operator' || category === 'punctuation';\n }\n\n private parseValueToken(value: string): TokenValue {\n const normalizedValue = value.toLowerCase();\n\n if (normalizedValue === 'null') {\n return null;\n }\n\n if (normalizedValue === 'true') {\n return true;\n }\n\n if (normalizedValue === 'false') {\n return false;\n }\n\n if (isNumber(value)) {\n return Number(value);\n }\n\n return value.replace(/((^'|'$)|(?:^\"|\"$))/g, '');\n }\n}\n","import { Token } from './token';\nimport { tokenConfig, TokenType } from './token-config';\n\nconst LITERAL_REGEX = /^(null|NULL|true|TRUE|false|FALSE)(?=[^\\w]|$)/;\n// Matches identifiers including those with -> for component property access\n// Allows $ at start and after -> (e.g., $value, &ref->$value)\nconst IDENTIFIER_REGEX = /^[a-zA-Z$&%][\\w\\-_.?>$]+/;\nconst NUMBER_REGEX = /^\\d+\\.?\\d*/;\nconst STRING_PATTERNS = [/^'[^']+'/, /^\"[^\"]+\"/, /^''/, /^\"\"/];\n\nconst MULTI_CHAR_LITERALS = new Map<string, TokenType>();\nconst SINGLE_CHAR_LITERALS = new Map<string, TokenType>();\nfor (const [tokenType, config] of Object.entries(tokenConfig)) {\n if ('literal' in config && config.literal !== 'EOF') {\n if (config.literal.length > 1) {\n MULTI_CHAR_LITERALS.set(config.literal, tokenType as TokenType);\n } else {\n SINGLE_CHAR_LITERALS.set(config.literal, tokenType as TokenType);\n }\n }\n}\n\nexport class Lexer {\n private text = '';\n private index = 0;\n private allowIncomplete = false;\n private incompletePatterns: RegExp[] | null = null;\n\n tokenize(text: string, allowIncomplete = false): Array<Token> {\n this.text = text;\n this.index = 0;\n this.allowIncomplete = allowIncomplete;\n\n const result: Array<Token> = [];\n while (this.index < this.text.length + 1) {\n result.push(this.getNextToken());\n }\n\n return result;\n }\n\n private advance(step = 1): void {\n this.index += step;\n }\n\n private getCurrentLine(): string {\n return this.index < this.text.length ? this.text.substring(this.index) : '';\n }\n\n private getNextToken(): Token {\n const remaining = this.getCurrentLine();\n if (remaining === '') {\n this.advance();\n return new Token('endOfFile', 'EOF', this.index, this.text);\n }\n\n let skipSpaces = 0;\n while (skipSpaces < remaining.length && remaining[skipSpaces] === ' ') {\n skipSpaces++;\n }\n if (skipSpaces > 0) {\n this.advance(skipSpaces);\n return this.getNextToken();\n }\n\n const literal = remaining.match(LITERAL_REGEX);\n if (literal) {\n this.advance(literal[0].length);\n return new Token('value', literal[0], this.index, this.text);\n }\n\n // Check for identifier BEFORE single-character tokens\n // This allows ?-> to be part of identifiers (e.g., &ref?->value)\n const identifier = remaining.match(IDENTIFIER_REGEX);\n if (identifier) {\n this.advance(identifier[0].length);\n return new Token('identifier', identifier[0], this.index, this.text);\n }\n\n for (const [literal, type] of MULTI_CHAR_LITERALS) {\n if (remaining.startsWith(literal)) {\n this.advance(literal.length);\n return new Token(type, literal, this.index, this.text);\n }\n }\n\n const firstChar = remaining[0];\n const tokenType = SINGLE_CHAR_LITERALS.get(firstChar);\n if (tokenType) {\n this.advance(1);\n return new Token(tokenType, firstChar, this.index, this.text);\n }\n\n const patterns = this.getValuePatterns();\n for (const pattern of patterns) {\n const value = remaining.match(pattern);\n if (value) {\n this.advance(value[0].length);\n return new Token('value', value[0], this.index, this.text);\n }\n }\n\n throw new SyntaxError(\n `Invalid syntax at position ${this.index}\\n` + `Formula: ${this.text}\\n` + ` ${' '.repeat(this.index)}^`,\n );\n }\n\n private getValuePatterns(): RegExp[] {\n if (this.allowIncomplete) {\n if (!this.incompletePatterns) {\n this.incompletePatterns = STRING_PATTERNS.map((pattern) => new RegExp(pattern.source + '?')).concat(\n NUMBER_REGEX,\n );\n }\n return this.incompletePatterns;\n }\n return [...STRING_PATTERNS, NUMBER_REGEX];\n }\n}\n","import { Token, TokenType } from '../lexer';\nimport {\n ArrayAstNode,\n FormulaAstNode,\n FunctionAstNode,\n GroupAstNode,\n NotAstNode,\n ObjectAstNode,\n OperatorAstNode,\n ReferenceAstNode,\n TernaryAstNode,\n ValueAstNode,\n} from '../node';\n\nexport class Parser {\n private tokens: Token[] = [];\n private tokenIndex = 0;\n private allowIncomplete = false;\n private readonly atomParsers: Record<string, () => FormulaAstNode> = {\n identifier: () => (this.peekToken()?.type == 'leftParen' ? this.parseFunction() : this.parseReference()),\n leftParen: () => this.parseGroup(),\n value: () => this.parseLiteral(),\n leftBracket: () => this.parseArray(),\n leftBrace: () => this.parseObject(),\n invert: () => this.parseUnaryNot(),\n };\n\n parse(tokens: Token[], allowIncomplete = false): FormulaAstNode {\n this.tokens = tokens;\n this.tokenIndex = 0;\n this.allowIncomplete = allowIncomplete;\n\n return this.parseFormula();\n }\n\n private getCurrentToken(): Token {\n if (this.tokenIndex >= this.tokens.length) {\n throw new SyntaxError('Unexpected end of input');\n }\n return this.tokens[this.tokenIndex];\n }\n\n private peekToken(offset = 1): Token | undefined {\n const nextIndex = this.tokenIndex + offset;\n return nextIndex < this.tokens.length ? this.tokens[nextIndex] : undefined;\n }\n\n private throwExpectedButFound(expected: TokenType): never {\n throw new SyntaxError(\n `Expected a \"${expected}\" token but found: \"${this.getCurrentToken().value}\"\\n` +\n `formula ${this.getCurrentToken().line}\\n` +\n ` ${' '.repeat(this.getCurrentToken().column)}^`,\n );\n }\n\n private throwUnexpectedToken(): never {\n throw new SyntaxError(\n `Unexpected \"${this.getCurrentToken().value}\"\\n` +\n `formula ${this.getCurrentToken().line}\\n` +\n ` ${' '.repeat(this.getCurrentToken().column)}^`,\n );\n }\n\n private consume(type: TokenType): void {\n if (this.getCurrentToken().type === type) {\n this.tokenIndex += 1;\n } else {\n this.throwExpectedButFound(type);\n }\n }\n\n private consumeIfPresent(type: TokenType): boolean {\n if (this.allowIncomplete) {\n try {\n this.consume(type);\n return true;\n } catch {\n return false;\n }\n } else {\n this.consume(type);\n return true;\n }\n }\n\n private parseFormula(): FormulaAstNode {\n const entity = this.parseTernary();\n this.consume('endOfFile');\n return entity;\n }\n\n private parseFunction(): FunctionAstNode {\n const functionName = this.getCurrentToken().value as string;\n\n this.consume('identifier');\n this.consume('leftParen');\n\n const args: FormulaAstNode[] = [];\n do {\n if (this.getCurrentToken().type === 'comma') {\n this.consume('comma');\n }\n if (this.allowIncomplete && this.getCurrentToken().type === 'endOfFile') {\n break;\n }\n if (this.getCurrentToken().type === 'rightParen') {\n break;\n }\n args.push(this.parseTernary());\n } while (this.getCurrentToken().type === 'comma');\n\n const closed: boolean = this.consumeIfPresent('rightParen');\n\n return { type: 'function', name: functionName, args, closed };\n }\n\n private parseExpression(): FormulaAstNode {\n let result: FormulaAstNode = this.parseTerm();\n while (['plus', 'minus'].includes(this.getCurrentToken().type)) {\n const operator = this.getCurrentToken().type as 'plus' | 'minus';\n result = this.parseAdditionOperator(result, operator);\n }\n if (result == null) {\n this.throwUnexpectedToken();\n }\n return result;\n }\n\n private parseTerm(): FormulaAstNode {\n let result: FormulaAstNode = this.parseAtom();\n while (['multiply', 'divide'].includes(this.getCurrentToken().type)) {\n const operator = this.getCurrentToken().type as 'multiply' | 'divide';\n result = this.parseMultiplicationOperator(result, operator);\n }\n if (result == null) {\n this.throwUnexpectedToken();\n }\n return result;\n }\n\n private parseAtom(): FormulaAstNode {\n const tokenType = this.getCurrentToken().type;\n const parser = this.atomParsers[tokenType];\n if (!parser) {\n this.throwUnexpectedToken();\n }\n return parser();\n }\n\n private parseTernary(): TernaryAstNode | FormulaAstNode {\n const condition = this.parseComparison();\n if (this.getCurrentToken().type !== 'questionMark') {\n return condition;\n }\n\n this.consume('questionMark');\n const result: TernaryAstNode = {\n type: 'ternary',\n condition,\n thenBranch: null,\n elseBranch: null,\n closed: false,\n };\n\n if (this.getCurrentToken().type === 'endOfFile') {\n return result;\n }\n\n result.thenBranch = this.parseTernary();\n if (this.getCurrentToken().type === 'endOfFile') {\n return result;\n }\n\n this.consume('colon');\n if (this.getCurrentToken().type === 'endOfFile') {\n return result;\n }\n\n result.elseBranch = this.parseTernary();\n result.closed = true;\n return result;\n }\n\n private parseGroup(): GroupAstNode {\n this.consume('leftParen');\n const entity = this.parseTernary();\n const closed = this.consumeIfPresent('rightParen');\n return { type: 'group', item: entity, closed };\n }\n\n private parseUnaryNot(): NotAstNode {\n this.consume('invert');\n const result = { type: 'not', operand: null, closed: false } as NotAstNode;\n if (this.getCurrentToken().type === 'endOfFile') {\n return result;\n }\n result.operand = this.parseAtom();\n result.closed = true;\n return result;\n }\n\n private parseAdditionOperator(left: FormulaAstNode, operator: 'plus' | 'minus'): OperatorAstNode {\n this.consume(operator);\n const isEOF = this.getCurrentToken().type === 'endOfFile';\n return {\n type: operator,\n left,\n right: isEOF ? null : this.parseTerm(),\n closed: !isEOF,\n };\n }\n\n private parseMultiplicationOperator(left: FormulaAstNode, operator: 'divide' | 'multiply'): OperatorAstNode {\n this.consume(operator);\n const isEOF = this.getCurrentToken().type === 'endOfFile';\n return {\n type: operator,\n left,\n right: isEOF ? null : this.parseAtom(),\n closed: !isEOF,\n };\n }\n\n private parseComparison(): FormulaAstNode {\n const left = this.parseExpression();\n const comparisonTypes = ['eq', 'neq', 'gt', 'lt', 'gte', 'lte'];\n if (comparisonTypes.includes(this.getCurrentToken().type)) {\n return this.parseComparisonOperator(left);\n }\n return left;\n }\n\n private parseComparisonOperator(left: FormulaAstNode): OperatorAstNode {\n const operator = this.getCurrentToken().type as 'eq' | 'neq' | 'gt' | 'lt' | 'gte' | 'lte';\n this.consume(operator);\n const isEOF = this.getCurrentToken().type === 'endOfFile';\n return {\n type: operator,\n left,\n right: isEOF ? null : this.parseExpression(),\n closed: !isEOF,\n };\n }\n\n private parseReference(): ReferenceAstNode {\n const variable = this.getCurrentToken().value as string;\n this.consume('identifier');\n return { type: 'reference', name: variable };\n }\n\n private parseLiteral(): ValueAstNode {\n const value = this.getCurrentToken().value;\n this.consume('value');\n return { type: 'value', value };\n }\n\n private parseArray(): ArrayAstNode {\n this.consume('leftBracket');\n\n const items: FormulaAstNode[] = [];\n do {\n if (this.getCurrentToken().type === 'comma') {\n this.consume('comma');\n }\n if (this.allowIncomplete && this.getCurrentToken().type === 'endOfFile') {\n break;\n }\n if (this.getCurrentToken().type === 'rightBracket') {\n break;\n }\n items.push(this.parseTernary());\n } while (this.getCurrentToken().type === 'comma');\n\n const closed = this.consumeIfPresent('rightBracket');\n\n return { type: 'array', items, closed };\n }\n\n private parseObjectKey(): FormulaAstNode {\n const currentToken = this.getCurrentToken();\n\n // Unquoted identifier → IdentifierAstNode (literal key)\n if (currentToken.type === 'identifier') {\n const name = currentToken.value as string;\n this.consume('identifier');\n return { type: 'identifier', name };\n }\n\n // Anything else (quoted strings, numbers, expressions) → parse normally\n return this.parseAtom();\n }\n\n private parseObject(): ObjectAstNode {\n this.consume('leftBrace');\n\n const properties: { key: FormulaAstNode; value: FormulaAstNode }[] = [];\n do {\n if (this.getCurrentToken().type === 'rightBrace') {\n break;\n }\n\n if (this.getCurrentToken().type === 'comma') {\n this.consume('comma');\n }\n if (this.allowIncomplete && this.getCurrentToken().type === 'endOfFile') {\n break;\n }\n if (this.getCurrentToken().type === 'rightBracket') {\n break;\n }\n const key = this.parseObjectKey();\n this.consume('colon');\n const value = this.parseTernary();\n properties.push({ key, value });\n } while (this.getCurrentToken().type === 'comma');\n\n const closed = this.consumeIfPresent('rightBrace');\n\n return { type: 'object', properties, closed };\n }\n}\n","import * as he from 'he';\n\nimport { tokenConfig, TokenType } from '../lexer';\nimport {\n ArrayAstNode,\n FormulaAstNode,\n FunctionAstNode,\n GroupAstNode,\n IdentifierAstNode,\n NotAstNode,\n ObjectAstNode,\n OperatorAstNode,\n ReferenceAstNode,\n TernaryAstNode,\n ValueAstNode,\n} from '../node';\n\nexport abstract class Stringifier {\n private readonly nodeVisitors: Record<string, (node: FormulaAstNode) => string> = {\n function: (node) => this.visitFunctionNode(node as FunctionAstNode),\n reference: (node) => this.visitVariableNode(node as ReferenceAstNode),\n identifier: (node) => this.visitIdentifierNode(node as IdentifierAstNode),\n value: (node) => this.visitValueNode(node as ValueAstNode),\n array: (node) => this.visitArrayNode(node as ArrayAstNode),\n object: (node) => this.visitObjectNode(node as ObjectAstNode),\n not: (node) => this.visitInvertNode(node as NotAstNode),\n plus: (node) => this.visitOperatorNode(node as OperatorAstNode),\n minus: (node) => this.visitOperatorNode(node as OperatorAstNode),\n multiply: (node) => this.visitOperatorNode(node as OperatorAstNode),\n divide: (node) => this.visitOperatorNode(node as OperatorAstNode),\n eq: (node) => this.visitOperatorNode(node as OperatorAstNode),\n neq: (node) => this.visitOperatorNode(node as OperatorAstNode),\n gt: (node) => this.visitOperatorNode(node as OperatorAstNode),\n lt: (node) => this.visitOperatorNode(node as OperatorAstNode),\n gte: (node) => this.visitOperatorNode(node as OperatorAstNode),\n lte: (node) => this.visitOperatorNode(node as OperatorAstNode),\n group: (node) => this.visitGroup(node as GroupAstNode),\n ternary: (node) => this.visitTernaryNode(node as TernaryAstNode),\n };\n\n protected visitNode(node: FormulaAstNode): string {\n const visitor = this.nodeVisitors[node.type];\n if (!visitor) {\n throw new Error(`Unrecognised AST node type: ${node.type}`);\n }\n return visitor(node);\n }\n\n protected visitValueNode(node: ValueAstNode): string {\n if (node.value && typeof node.value === 'object') {\n return this.processObjectValue(node.value);\n }\n\n return this.processStringValue(node.value);\n }\n\n protected processObjectValue(item: object) {\n const object = Object.entries(item);\n const key_values = object.map(([key, value]) => {\n let result_value: string;\n if (typeof value === 'object') {\n result_value = this.processObjectValue(value);\n } else {\n result_value = this.processStringValue(value);\n }\n return `${key}: ${result_value}`;\n });\n return `{ ${key_values.join(', ')} }`;\n }\n\n protected processStringValue(item: unknown): string {\n if (typeof item === 'string') {\n const escaped: string = he.escape(item.toString());\n return `'${String(escaped)}'`;\n }\n\n return String(item);\n }\n\n protected visitOperatorNode(node: OperatorAstNode): string {\n const operatorToken = tokenConfig[node.type as TokenType];\n if (!operatorToken || !('literal' in operatorToken)) {\n throw new Error(`Unrecognised operator type: ${node.type}`);\n }\n\n const operator = ` ${operatorToken.literal} `;\n const left = this.visitNode(node.left);\n const right = node.closed && node.right ? this.visitNode(node.right) : '';\n return `${left}${operator}${right}`;\n }\n\n protected visitTernaryNode(node: TernaryAstNode): string {\n let result: string = this.visitNode(node.condition) + ' ? ';\n if (node.thenBranch == null) {\n return result;\n }\n result += this.visitNode(node.thenBranch) + ' : ';\n if (node.elseBranch == null) {\n return result;\n }\n result += this.visitNode(node.elseBranch);\n return result;\n }\n\n protected abstract visitFunctionNode(node: FunctionAstNode): string;\n\n protected abstract visitVariableNode(node: ReferenceAstNode): string;\n\n protected abstract visitIdentifierNode(node: IdentifierAstNode): string;\n\n protected abstract visitArrayNode(node: ArrayAstNode): string;\n\n protected abstract visitObjectNode(node: ObjectAstNode): string;\n\n protected abstract visitInvertNode(node: NotAstNode): string;\n\n protected abstract visitGroup(node: GroupAstNode): string;\n}\n","import {\n ArrayAstNode,\n FormulaAstNode,\n FunctionAstNode,\n GroupAstNode,\n IdentifierAstNode,\n NotAstNode,\n ObjectAstNode,\n ReferenceAstNode,\n} from '../node';\nimport { Stringifier } from './stringifier';\n\nexport class DefaultStringifier extends Stringifier {\n stringify(tree: FormulaAstNode): string {\n return this.visitNode(tree);\n }\n\n protected visitFunctionNode(node: FunctionAstNode): string {\n return `${node.name}(${node.args.map((node) => this.visitNode(node)).join(', ')})`;\n }\n\n protected visitVariableNode(node: ReferenceAstNode): string {\n return node.name;\n }\n\n protected visitIdentifierNode(node: IdentifierAstNode): string {\n return node.name;\n }\n\n protected visitArrayNode(node: ArrayAstNode): string {\n return '[' + node.items.map((node) => this.visitNode(node)).join(', ') + ']';\n }\n\n protected visitObjectNode(node: ObjectAstNode): string {\n let result = '';\n\n result += '{';\n result += node.properties\n .map((prop) => {\n const key = this.visitNode(prop.key);\n const value = this.visitNode(prop.value);\n return `${key}: ${value}`;\n })\n .join(', ');\n result += '}';\n\n return result;\n }\n\n protected visitInvertNode(node: NotAstNode): string {\n let result = '!';\n\n if (node.operand == null) {\n return result;\n }\n result += this.visitNode(node.operand);\n\n return result;\n }\n\n protected visitGroup(node: GroupAstNode): string {\n let result = '';\n result += `(`;\n result += this.visitNode(node.item);\n result += node.closed ? ')' : '';\n return result;\n }\n}\n","import {\n ArrayAstNode,\n FormulaAstNode,\n FunctionAstNode,\n GroupAstNode,\n IdentifierAstNode,\n NotAstNode,\n ObjectAstNode,\n ReferenceAstNode,\n} from '../node';\nimport { Stringifier } from './stringifier';\n\nexport class HtmlStringifier extends Stringifier {\n private parenStyleCycle: number;\n private currentDeep = 1;\n\n constructor(parenStyleCycle = 3) {\n super();\n this.parenStyleCycle = parenStyleCycle;\n }\n\n stringify(tree: FormulaAstNode): string {\n this.currentDeep = 1;\n return `<div>${this.visitNode(tree)}</div>`;\n }\n\n setParenStyleCycle(newParenStyleCycle: number): void {\n this.parenStyleCycle = newParenStyleCycle;\n }\n\n protected visitFunctionNode(node: FunctionAstNode): string {\n let result = '';\n\n const paren = `paren-deep-${this.currentDeep}`;\n this.incrementParenDeep();\n\n result += this.createHtmlSpan('function', node.name);\n result += this.createHtmlSpan(paren, '(');\n result += node.args.map((arg) => this.visitNode(arg)).join(', ');\n result += node.closed ? this.createHtmlSpan(paren, ')') : ``;\n\n return result;\n }\n\n protected incrementParenDeep(): void {\n if (this.currentDeep < this.parenStyleCycle) {\n this.currentDeep += 1;\n } else {\n this.currentDeep = 1;\n }\n }\n\n protected visitVariableNode(node: ReferenceAstNode): string {\n return this.createHtmlSpan('variable', node.name);\n }\n\n protected visitIdentifierNode(node: IdentifierAstNode): string {\n return this.createHtmlSpan('identifier', node.name);\n }\n\n protected override processStringValue(value: unknown): string {\n return this.createHtmlSpan('value', super.processStringValue(value));\n }\n\n protected visitArrayNode(node: ArrayAstNode): string {\n let result = '';\n\n const paren = `paren-deep-${this.currentDeep}`;\n this.incrementParenDeep();\n\n result += this.createHtmlSpan(paren, '[');\n result += node.items\n .map((node) => {\n const deepStored = this.currentDeep;\n const result = this.visitNode(node);\n this.currentDeep = deepStored;\n return result;\n })\n .join(', ');\n result += node.closed ? this.createHtmlSpan(paren, ']') : ``;\n\n return result;\n }\n\n protected visitObjectNode(node: ObjectAstNode): string {\n let result = '';\n\n const paren = `paren-deep-${this.currentDeep}`;\n this.incrementParenDeep();\n\n result += this.createHtmlSpan(paren, '{');\n result += node.properties\n .map((prop) => {\n const deepStored = this.currentDeep;\n const key = this.visitNode(prop.key);\n const value = this.visitNode(prop.value);\n this.currentDeep = deepStored;\n return `${key}: ${value}`;\n })\n .join(', ');\n result += node.closed ? this.createHtmlSpan(paren, '}') : ``;\n\n return result;\n }\n\n protected visitInvertNode(node: NotAstNode): string {\n const paren = `paren-deep-${this.currentDeep}`;\n\n let result: string = this.createHtmlSpan(paren, '!');\n if (node.operand == null) {\n return result;\n }\n result += this.visitNode(node.operand);\n\n return result;\n }\n\n protected visitGroup(node: GroupAstNode): string {\n let result = '';\n\n const paren = `paren-deep-${this.currentDeep}`;\n this.incrementParenDeep();\n\n result += this.createHtmlSpan(paren, '(');\n result += this.visitNode(node.item);\n result += node.closed ? this.createHtmlSpan(paren, ')') : ``;\n\n return result;\n }\n\n private createHtmlSpan(class_attr: string, value: string): string {\n return `<span class=\"${class_attr}\">${value}</span>`;\n }\n}\n","import { Lexer } from './lexer';\nimport { FormulaAstNode } from './node';\nimport { Parser } from './parser';\nimport { DefaultStringifier, HtmlStringifier } from './stringifier';\n\n/**\n * Parses a formula string into an Abstract Syntax Tree (AST)\n *\n * @param formula - The formula string to parse\n * @returns The parsed AST representation that can be traversed or evaluated\n *\n * @example Basic expressions\n * ```typescript\n * parseFormula('42'); // Literal number\n * parseFormula('\"hello\"'); // Literal string\n * parseFormula('myVariable'); // Variable reference\n * ```\n *\n * @example Function calls\n * ```typescript\n * parseFormula('sum(1, 2, 3)'); // Function with arguments\n * parseFormula('getValue()'); // Function without arguments\n * parseFormula('calc(sum(1, 2), multiply(3, 4))'); // Nested functions\n * ```\n *\n * @example Operators and arithmetic\n * ```typescript\n * parseFormula('price * quantity'); // Multiplication\n * parseFormula('total - discount'); // Subtraction\n * parseFormula('(base + bonus) * multiplier'); // Grouped expressions\n * ```\n *\n * @example Complex structures\n * ```typescript\n * parseFormula('[1, 2, 3]'); // Arrays\n * parseFormula('{name: \"John\", age: 30}'); // Objects\n * parseFormula('isValid ? \"yes\" : \"no\"'); // Ternary conditionals\n * parseFormula('!isDisabled'); // Logical NOT\n * ```\n *\n * @throws {SyntaxError} When the formula contains invalid syntax\n * - Invalid characters (e.g., '@', '#', etc.)\n * - Unclosed parentheses, brackets, or braces\n * - Invalid token sequences\n *\n * @remarks\n * Supported syntax elements:\n * - **Literals**: numbers, strings (single/double quoted), booleans\n * - **Variables**: identifiers starting with letters, $, &, or %\n * - **Functions**: identifier followed by parentheses with optional arguments\n * - **Arrays**: square brackets with comma-separated values\n * - **Objects**: curly braces with key-value pairs\n * - **Operators**: +, -, *, / (following standard precedence)\n * - **Comparison**: ==, !=, >, <, >=, <= (lower precedence than arithmetic)\n * - **Logical**: ! (NOT operator)\n * - **Ternary**: condition ? trueBranch : falseBranch\n * - **Grouping**: parentheses for overriding precedence\n */\nexport function parseFormula(formula: string): FormulaAstNode {\n const lexer = new Lexer();\n const parser = new Parser();\n const tokenized = lexer.tokenize(formula);\n return parser.parse(tokenized);\n}\n\n/**\n * Converts a formula AST back into a formula string\n *\n * @param ast - The AST to stringify (as returned by parseFormula)\n * @returns The formula string representation\n *\n * @example Basic usage\n * ```typescript\n * const ast = parseFormula('sum(1, 2, 3)');\n * const formula = stringifyFormulaAst(ast); // \"sum(1, 2, 3)\"\n * ```\n *\n * @example Round-trip parsing\n * ```typescript\n * const original = 'price * quantity + tax';\n * const ast = parseFormula(original);\n * const reconstructed = stringifyFormulaAst(ast); // \"price * quantity + tax\"\n * ```\n *\n * @remarks\n * - The output may differ slightly from the original in spacing\n * - Parentheses may be added or removed based on operator precedence\n * - String quotes will be normalized to the stringifier's preference\n */\nexport function stringifyFormulaAst(ast: FormulaAstNode): string {\n const stringifier = new DefaultStringifier();\n return stringifier.stringify(ast);\n}\n\n/**\n * Formats a formula string as HTML with syntax highlighting\n *\n * @param formula - The formula string to format\n * @param allowIncomplete - Whether to allow incomplete formulas (useful for live editing). Default: false\n * @param parenStyleCycle - Number of parenthesis depth styles to cycle through. Default: 3\n * @returns HTML string with syntax highlighting using span elements with CSS classes\n *\n * @example Basic usage\n * ```typescript\n * const html = formatFormulaAsHtml('sum(1, 2, 3)');\n * // Returns: '<span class=\"function\">sum</span><span class=\"paren-0\">(</span>...'\n * ```\n *\n * @example Live editor with incomplete formulas\n * ```typescript\n * // Allow incomplete formulas for real-time syntax highlighting as user types\n * const html = formatFormulaAsHtml('sum(1, 2', true);\n * // Won't throw error even though parenthesis is unclosed\n * ```\n *\n * @example Custom parenthesis depth cycling\n * ```typescript\n * // Use 5 different colors for nested parentheses\n * const html = formatFormulaAsHtml('f(g(h(i(j(k())))))', false, 5);\n * ```\n *\n * @remarks\n * CSS classes used in the output:\n * - `function` - Function names\n * - `variable` - Variable identifiers\n * - `value` - Literal values (numbers, strings, booleans)\n * - `operator` - Arithmetic operators (+, -, *, /)\n * - `paren-N` - Parentheses at depth N (cycles based on parenStyleCycle)\n * - `bracket` - Square brackets for arrays\n * - `brace` - Curly braces for objects\n * - `punctuation` - Commas, colons, etc.\n *\n * @throws {SyntaxError} When formula has invalid syntax (unless allowIncomplete is true)\n */\nexport function formatFormulaAsHtml(formula: string, allowIncomplete = false, parenStyleCycle = 3): string {\n const lexer = new Lexer();\n const parser = new Parser();\n const stringifier = new HtmlStringifier(parenStyleCycle);\n const tokenized = lexer.tokenize(formula, allowIncomplete);\n const node = parser.parse(tokenized, allowIncomplete);\n return stringifier.stringify(node);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@kaskad/formula-parser",
3
+ "version": "0.0.1",
4
+ "dependencies": {
5
+ "he": "^1.2.0",
6
+ "tslib": "^2.3.0"
7
+ },
8
+ "sideEffects": false,
9
+ "module": "fesm2022/kaskad-formula-parser.mjs",
10
+ "typings": "types/kaskad-formula-parser.d.ts",
11
+ "exports": {
12
+ "./package.json": {
13
+ "default": "./package.json"
14
+ },
15
+ ".": {
16
+ "types": "./types/kaskad-formula-parser.d.ts",
17
+ "default": "./fesm2022/kaskad-formula-parser.mjs"
18
+ }
19
+ }
20
+ }
@@ -0,0 +1,381 @@
1
+ interface AbstractAstNode {
2
+ type: string;
3
+ }
4
+ interface ClosableAstNode extends AbstractAstNode {
5
+ closed: boolean;
6
+ }
7
+ interface FunctionAstNode extends ClosableAstNode {
8
+ type: 'function';
9
+ name: string;
10
+ args: FormulaAstNode[];
11
+ }
12
+ interface ArrayAstNode extends ClosableAstNode {
13
+ type: 'array';
14
+ items: FormulaAstNode[];
15
+ }
16
+ interface OperatorAstNode extends ClosableAstNode {
17
+ type: 'plus' | 'minus' | 'multiply' | 'divide' | 'eq' | 'neq' | 'gt' | 'lt' | 'gte' | 'lte';
18
+ left: FormulaAstNode;
19
+ right: FormulaAstNode | null;
20
+ }
21
+ interface ObjectAstNode extends ClosableAstNode {
22
+ type: 'object';
23
+ properties: {
24
+ key: FormulaAstNode;
25
+ value: FormulaAstNode;
26
+ }[];
27
+ }
28
+ interface ReferenceAstNode extends AbstractAstNode {
29
+ type: 'reference';
30
+ name: string;
31
+ }
32
+ interface IdentifierAstNode extends AbstractAstNode {
33
+ type: 'identifier';
34
+ name: string;
35
+ }
36
+ type PrimitiveValue = string | number | boolean;
37
+ type CompositeValue = Record<string, unknown> | PrimitiveValue[];
38
+ interface ValueAstNode extends AbstractAstNode {
39
+ type: 'value';
40
+ value: PrimitiveValue | CompositeValue;
41
+ }
42
+ interface NotAstNode extends ClosableAstNode {
43
+ type: 'not';
44
+ operand: FormulaAstNode | null;
45
+ }
46
+ interface GroupAstNode extends ClosableAstNode {
47
+ type: 'group';
48
+ item: FormulaAstNode;
49
+ }
50
+ interface TernaryAstNode extends ClosableAstNode {
51
+ type: 'ternary';
52
+ condition: FormulaAstNode;
53
+ thenBranch: FormulaAstNode | null;
54
+ elseBranch: FormulaAstNode | null;
55
+ }
56
+ type FormulaAstNode = FunctionAstNode | ReferenceAstNode | IdentifierAstNode | ValueAstNode | ArrayAstNode | ObjectAstNode | NotAstNode | OperatorAstNode | GroupAstNode | TernaryAstNode;
57
+
58
+ /**
59
+ * Parses a formula string into an Abstract Syntax Tree (AST)
60
+ *
61
+ * @param formula - The formula string to parse
62
+ * @returns The parsed AST representation that can be traversed or evaluated
63
+ *
64
+ * @example Basic expressions
65
+ * ```typescript
66
+ * parseFormula('42'); // Literal number
67
+ * parseFormula('"hello"'); // Literal string
68
+ * parseFormula('myVariable'); // Variable reference
69
+ * ```
70
+ *
71
+ * @example Function calls
72
+ * ```typescript
73
+ * parseFormula('sum(1, 2, 3)'); // Function with arguments
74
+ * parseFormula('getValue()'); // Function without arguments
75
+ * parseFormula('calc(sum(1, 2), multiply(3, 4))'); // Nested functions
76
+ * ```
77
+ *
78
+ * @example Operators and arithmetic
79
+ * ```typescript
80
+ * parseFormula('price * quantity'); // Multiplication
81
+ * parseFormula('total - discount'); // Subtraction
82
+ * parseFormula('(base + bonus) * multiplier'); // Grouped expressions
83
+ * ```
84
+ *
85
+ * @example Complex structures
86
+ * ```typescript
87
+ * parseFormula('[1, 2, 3]'); // Arrays
88
+ * parseFormula('{name: "John", age: 30}'); // Objects
89
+ * parseFormula('isValid ? "yes" : "no"'); // Ternary conditionals
90
+ * parseFormula('!isDisabled'); // Logical NOT
91
+ * ```
92
+ *
93
+ * @throws {SyntaxError} When the formula contains invalid syntax
94
+ * - Invalid characters (e.g., '@', '#', etc.)
95
+ * - Unclosed parentheses, brackets, or braces
96
+ * - Invalid token sequences
97
+ *
98
+ * @remarks
99
+ * Supported syntax elements:
100
+ * - **Literals**: numbers, strings (single/double quoted), booleans
101
+ * - **Variables**: identifiers starting with letters, $, &, or %
102
+ * - **Functions**: identifier followed by parentheses with optional arguments
103
+ * - **Arrays**: square brackets with comma-separated values
104
+ * - **Objects**: curly braces with key-value pairs
105
+ * - **Operators**: +, -, *, / (following standard precedence)
106
+ * - **Comparison**: ==, !=, >, <, >=, <= (lower precedence than arithmetic)
107
+ * - **Logical**: ! (NOT operator)
108
+ * - **Ternary**: condition ? trueBranch : falseBranch
109
+ * - **Grouping**: parentheses for overriding precedence
110
+ */
111
+ declare function parseFormula(formula: string): FormulaAstNode;
112
+ /**
113
+ * Converts a formula AST back into a formula string
114
+ *
115
+ * @param ast - The AST to stringify (as returned by parseFormula)
116
+ * @returns The formula string representation
117
+ *
118
+ * @example Basic usage
119
+ * ```typescript
120
+ * const ast = parseFormula('sum(1, 2, 3)');
121
+ * const formula = stringifyFormulaAst(ast); // "sum(1, 2, 3)"
122
+ * ```
123
+ *
124
+ * @example Round-trip parsing
125
+ * ```typescript
126
+ * const original = 'price * quantity + tax';
127
+ * const ast = parseFormula(original);
128
+ * const reconstructed = stringifyFormulaAst(ast); // "price * quantity + tax"
129
+ * ```
130
+ *
131
+ * @remarks
132
+ * - The output may differ slightly from the original in spacing
133
+ * - Parentheses may be added or removed based on operator precedence
134
+ * - String quotes will be normalized to the stringifier's preference
135
+ */
136
+ declare function stringifyFormulaAst(ast: FormulaAstNode): string;
137
+ /**
138
+ * Formats a formula string as HTML with syntax highlighting
139
+ *
140
+ * @param formula - The formula string to format
141
+ * @param allowIncomplete - Whether to allow incomplete formulas (useful for live editing). Default: false
142
+ * @param parenStyleCycle - Number of parenthesis depth styles to cycle through. Default: 3
143
+ * @returns HTML string with syntax highlighting using span elements with CSS classes
144
+ *
145
+ * @example Basic usage
146
+ * ```typescript
147
+ * const html = formatFormulaAsHtml('sum(1, 2, 3)');
148
+ * // Returns: '<span class="function">sum</span><span class="paren-0">(</span>...'
149
+ * ```
150
+ *
151
+ * @example Live editor with incomplete formulas
152
+ * ```typescript
153
+ * // Allow incomplete formulas for real-time syntax highlighting as user types
154
+ * const html = formatFormulaAsHtml('sum(1, 2', true);
155
+ * // Won't throw error even though parenthesis is unclosed
156
+ * ```
157
+ *
158
+ * @example Custom parenthesis depth cycling
159
+ * ```typescript
160
+ * // Use 5 different colors for nested parentheses
161
+ * const html = formatFormulaAsHtml('f(g(h(i(j(k())))))', false, 5);
162
+ * ```
163
+ *
164
+ * @remarks
165
+ * CSS classes used in the output:
166
+ * - `function` - Function names
167
+ * - `variable` - Variable identifiers
168
+ * - `value` - Literal values (numbers, strings, booleans)
169
+ * - `operator` - Arithmetic operators (+, -, *, /)
170
+ * - `paren-N` - Parentheses at depth N (cycles based on parenStyleCycle)
171
+ * - `bracket` - Square brackets for arrays
172
+ * - `brace` - Curly braces for objects
173
+ * - `punctuation` - Commas, colons, etc.
174
+ *
175
+ * @throws {SyntaxError} When formula has invalid syntax (unless allowIncomplete is true)
176
+ */
177
+ declare function formatFormulaAsHtml(formula: string, allowIncomplete?: boolean, parenStyleCycle?: number): string;
178
+
179
+ declare const tokenConfig: {
180
+ readonly leftParen: {
181
+ readonly literal: "(";
182
+ readonly category: "delimiter";
183
+ };
184
+ readonly rightParen: {
185
+ readonly literal: ")";
186
+ readonly category: "delimiter";
187
+ };
188
+ readonly leftBracket: {
189
+ readonly literal: "[";
190
+ readonly category: "delimiter";
191
+ };
192
+ readonly rightBracket: {
193
+ readonly literal: "]";
194
+ readonly category: "delimiter";
195
+ };
196
+ readonly leftBrace: {
197
+ readonly literal: "{";
198
+ readonly category: "delimiter";
199
+ };
200
+ readonly rightBrace: {
201
+ readonly literal: "}";
202
+ readonly category: "delimiter";
203
+ };
204
+ readonly plus: {
205
+ readonly literal: "+";
206
+ readonly category: "operator";
207
+ };
208
+ readonly minus: {
209
+ readonly literal: "-";
210
+ readonly category: "operator";
211
+ };
212
+ readonly multiply: {
213
+ readonly literal: "*";
214
+ readonly category: "operator";
215
+ };
216
+ readonly divide: {
217
+ readonly literal: "/";
218
+ readonly category: "operator";
219
+ };
220
+ readonly invert: {
221
+ readonly literal: "!";
222
+ readonly category: "operator";
223
+ };
224
+ readonly questionMark: {
225
+ readonly literal: "?";
226
+ readonly category: "operator";
227
+ };
228
+ readonly eq: {
229
+ readonly literal: "==";
230
+ readonly category: "operator";
231
+ };
232
+ readonly neq: {
233
+ readonly literal: "!=";
234
+ readonly category: "operator";
235
+ };
236
+ readonly gt: {
237
+ readonly literal: ">";
238
+ readonly category: "operator";
239
+ };
240
+ readonly lt: {
241
+ readonly literal: "<";
242
+ readonly category: "operator";
243
+ };
244
+ readonly gte: {
245
+ readonly literal: ">=";
246
+ readonly category: "operator";
247
+ };
248
+ readonly lte: {
249
+ readonly literal: "<=";
250
+ readonly category: "operator";
251
+ };
252
+ readonly comma: {
253
+ readonly literal: ",";
254
+ readonly category: "punctuation";
255
+ };
256
+ readonly colon: {
257
+ readonly literal: ":";
258
+ readonly category: "punctuation";
259
+ };
260
+ readonly identifier: {
261
+ readonly category: "entity";
262
+ };
263
+ readonly value: {
264
+ readonly category: "entity";
265
+ };
266
+ readonly endOfFile: {
267
+ readonly literal: "EOF";
268
+ readonly category: "misc";
269
+ };
270
+ };
271
+ type TokenType = keyof typeof tokenConfig;
272
+ type TokenTypeMap = {
273
+ [K in keyof typeof tokenConfig]: K extends 'identifier' ? string : K extends 'value' ? string | boolean | number : (typeof tokenConfig)[K] extends {
274
+ literal: infer L;
275
+ } ? L : never;
276
+ };
277
+ declare const getTokenCategory: (type: TokenType) => string;
278
+
279
+ type TokenValue = string | boolean | number | null;
280
+ declare class Token<T extends TokenType = TokenType> {
281
+ readonly type: T;
282
+ readonly value: TokenTypeMap[T];
283
+ readonly column: number;
284
+ readonly line: string;
285
+ constructor(type: T, value: string, column?: number, line?: string);
286
+ private parseValue;
287
+ private isDelimiterOrOperator;
288
+ private parseValueToken;
289
+ }
290
+
291
+ declare class Lexer {
292
+ private text;
293
+ private index;
294
+ private allowIncomplete;
295
+ private incompletePatterns;
296
+ tokenize(text: string, allowIncomplete?: boolean): Array<Token>;
297
+ private advance;
298
+ private getCurrentLine;
299
+ private getNextToken;
300
+ private getValuePatterns;
301
+ }
302
+
303
+ declare class Parser {
304
+ private tokens;
305
+ private tokenIndex;
306
+ private allowIncomplete;
307
+ private readonly atomParsers;
308
+ parse(tokens: Token[], allowIncomplete?: boolean): FormulaAstNode;
309
+ private getCurrentToken;
310
+ private peekToken;
311
+ private throwExpectedButFound;
312
+ private throwUnexpectedToken;
313
+ private consume;
314
+ private consumeIfPresent;
315
+ private parseFormula;
316
+ private parseFunction;
317
+ private parseExpression;
318
+ private parseTerm;
319
+ private parseAtom;
320
+ private parseTernary;
321
+ private parseGroup;
322
+ private parseUnaryNot;
323
+ private parseAdditionOperator;
324
+ private parseMultiplicationOperator;
325
+ private parseComparison;
326
+ private parseComparisonOperator;
327
+ private parseReference;
328
+ private parseLiteral;
329
+ private parseArray;
330
+ private parseObjectKey;
331
+ private parseObject;
332
+ }
333
+
334
+ declare abstract class Stringifier {
335
+ private readonly nodeVisitors;
336
+ protected visitNode(node: FormulaAstNode): string;
337
+ protected visitValueNode(node: ValueAstNode): string;
338
+ protected processObjectValue(item: object): string;
339
+ protected processStringValue(item: unknown): string;
340
+ protected visitOperatorNode(node: OperatorAstNode): string;
341
+ protected visitTernaryNode(node: TernaryAstNode): string;
342
+ protected abstract visitFunctionNode(node: FunctionAstNode): string;
343
+ protected abstract visitVariableNode(node: ReferenceAstNode): string;
344
+ protected abstract visitIdentifierNode(node: IdentifierAstNode): string;
345
+ protected abstract visitArrayNode(node: ArrayAstNode): string;
346
+ protected abstract visitObjectNode(node: ObjectAstNode): string;
347
+ protected abstract visitInvertNode(node: NotAstNode): string;
348
+ protected abstract visitGroup(node: GroupAstNode): string;
349
+ }
350
+
351
+ declare class DefaultStringifier extends Stringifier {
352
+ stringify(tree: FormulaAstNode): string;
353
+ protected visitFunctionNode(node: FunctionAstNode): string;
354
+ protected visitVariableNode(node: ReferenceAstNode): string;
355
+ protected visitIdentifierNode(node: IdentifierAstNode): string;
356
+ protected visitArrayNode(node: ArrayAstNode): string;
357
+ protected visitObjectNode(node: ObjectAstNode): string;
358
+ protected visitInvertNode(node: NotAstNode): string;
359
+ protected visitGroup(node: GroupAstNode): string;
360
+ }
361
+
362
+ declare class HtmlStringifier extends Stringifier {
363
+ private parenStyleCycle;
364
+ private currentDeep;
365
+ constructor(parenStyleCycle?: number);
366
+ stringify(tree: FormulaAstNode): string;
367
+ setParenStyleCycle(newParenStyleCycle: number): void;
368
+ protected visitFunctionNode(node: FunctionAstNode): string;
369
+ protected incrementParenDeep(): void;
370
+ protected visitVariableNode(node: ReferenceAstNode): string;
371
+ protected visitIdentifierNode(node: IdentifierAstNode): string;
372
+ protected processStringValue(value: unknown): string;
373
+ protected visitArrayNode(node: ArrayAstNode): string;
374
+ protected visitObjectNode(node: ObjectAstNode): string;
375
+ protected visitInvertNode(node: NotAstNode): string;
376
+ protected visitGroup(node: GroupAstNode): string;
377
+ private createHtmlSpan;
378
+ }
379
+
380
+ export { DefaultStringifier, HtmlStringifier, Lexer, Parser, Stringifier, Token, formatFormulaAsHtml, getTokenCategory, parseFormula, stringifyFormulaAst, tokenConfig };
381
+ export type { AbstractAstNode, ArrayAstNode, ClosableAstNode, CompositeValue, FormulaAstNode, FunctionAstNode, GroupAstNode, IdentifierAstNode, NotAstNode, ObjectAstNode, OperatorAstNode, PrimitiveValue, ReferenceAstNode, TernaryAstNode, TokenType, TokenTypeMap, TokenValue, ValueAstNode };