@fincity/kirun-js 3.1.4 → 3.2.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/__tests__/engine/dsl/GraphDebugTest.ts +316 -0
- package/__tests__/engine/runtime/expression/ExpressionParsingTest.ts +402 -14
- package/dist/index.js +15 -1
- package/dist/index.js.map +1 -1
- package/dist/module.js +15 -1
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +416 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/engine/dsl/DSLCompiler.ts +104 -0
- package/src/engine/dsl/index.ts +30 -0
- package/src/engine/dsl/lexer/DSLLexer.ts +518 -0
- package/src/engine/dsl/lexer/DSLToken.ts +74 -0
- package/src/engine/dsl/lexer/Keywords.ts +80 -0
- package/src/engine/dsl/lexer/LexerError.ts +37 -0
- package/src/engine/dsl/monaco/DSLFunctionProvider.ts +187 -0
- package/src/engine/dsl/parser/DSLParser.ts +1075 -0
- package/src/engine/dsl/parser/DSLParserError.ts +29 -0
- package/src/engine/dsl/parser/ast/ASTNode.ts +23 -0
- package/src/engine/dsl/parser/ast/ArgumentNode.ts +43 -0
- package/src/engine/dsl/parser/ast/ComplexValueNode.ts +22 -0
- package/src/engine/dsl/parser/ast/EventDeclNode.ts +27 -0
- package/src/engine/dsl/parser/ast/ExpressionNode.ts +33 -0
- package/src/engine/dsl/parser/ast/FunctionCallNode.ts +29 -0
- package/src/engine/dsl/parser/ast/FunctionDefNode.ts +37 -0
- package/src/engine/dsl/parser/ast/ParameterDeclNode.ts +25 -0
- package/src/engine/dsl/parser/ast/SchemaLiteralNode.ts +26 -0
- package/src/engine/dsl/parser/ast/SchemaNode.ts +23 -0
- package/src/engine/dsl/parser/ast/StatementNode.ts +41 -0
- package/src/engine/dsl/parser/ast/index.ts +14 -0
- package/src/engine/dsl/transformer/ASTToJSON.ts +378 -0
- package/src/engine/dsl/transformer/ExpressionHandler.ts +48 -0
- package/src/engine/dsl/transformer/JSONToText.ts +694 -0
- package/src/engine/dsl/transformer/SchemaTransformer.ts +110 -0
- package/src/engine/model/FunctionDefinition.ts +23 -0
- package/src/engine/runtime/expression/Expression.ts +5 -1
- package/src/engine/runtime/expression/ExpressionEvaluator.ts +152 -139
- package/src/engine/runtime/expression/ExpressionParser.ts +80 -27
- package/src/engine/util/duplicate.ts +3 -1
- package/src/index.ts +1 -0
|
@@ -9,28 +9,67 @@ export class ExpressionParser {
|
|
|
9
9
|
private lexer: ExpressionLexer;
|
|
10
10
|
private currentToken: Token | null = null;
|
|
11
11
|
private previousTokenValue: Token | null = null;
|
|
12
|
+
private readonly originalExpression: string;
|
|
13
|
+
private parseDepth: number = 0;
|
|
12
14
|
|
|
13
15
|
constructor(expression: string) {
|
|
16
|
+
this.originalExpression = expression;
|
|
14
17
|
this.lexer = new ExpressionLexer(expression);
|
|
15
18
|
this.currentToken = this.lexer.nextToken();
|
|
16
19
|
}
|
|
17
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Create a detailed error message with context about where parsing failed
|
|
23
|
+
*/
|
|
24
|
+
private createParserError(message: string): ExpressionEvaluationException {
|
|
25
|
+
const position = this.lexer.getPosition();
|
|
26
|
+
const contextStart = Math.max(0, position - 20);
|
|
27
|
+
const contextEnd = Math.min(this.originalExpression.length, position + 20);
|
|
28
|
+
const context = this.originalExpression.substring(contextStart, contextEnd);
|
|
29
|
+
const pointer = ' '.repeat(position - contextStart) + '^';
|
|
30
|
+
|
|
31
|
+
const errorDetails = [
|
|
32
|
+
`Parser Error: ${message}`,
|
|
33
|
+
`Expression: ${this.originalExpression}`,
|
|
34
|
+
`Position: ${position}`,
|
|
35
|
+
`Context: ...${context}...`,
|
|
36
|
+
` ${pointer}`,
|
|
37
|
+
`Current token: ${this.currentToken ? `${this.currentToken.type}("${this.currentToken.value}")` : 'null'}`,
|
|
38
|
+
`Previous token: ${this.previousTokenValue ? `${this.previousTokenValue.type}("${this.previousTokenValue.value}")` : 'null'}`,
|
|
39
|
+
`Parse depth: ${this.parseDepth}`,
|
|
40
|
+
].join('\n');
|
|
41
|
+
|
|
42
|
+
console.error('\n' + errorDetails + '\n');
|
|
43
|
+
|
|
44
|
+
return new ExpressionEvaluationException(
|
|
45
|
+
this.originalExpression,
|
|
46
|
+
`${message} at position ${position}`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
18
50
|
public parse(): Expression {
|
|
19
51
|
if (!this.currentToken) {
|
|
20
|
-
throw
|
|
52
|
+
throw this.createParserError('Empty expression');
|
|
21
53
|
}
|
|
22
54
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// Ensure we consumed all tokens
|
|
26
|
-
if (this.currentToken && this.currentToken.type !== TokenType.EOF) {
|
|
27
|
-
throw new ExpressionEvaluationException(
|
|
28
|
-
this.lexer.getPosition().toString(),
|
|
29
|
-
`Unexpected token: ${this.currentToken.value} at position ${this.currentToken.startPos}`,
|
|
30
|
-
);
|
|
31
|
-
}
|
|
55
|
+
try {
|
|
56
|
+
const expr = this.parseExpression();
|
|
32
57
|
|
|
33
|
-
|
|
58
|
+
// Ensure we consumed all tokens
|
|
59
|
+
if (this.currentToken && this.currentToken.type !== TokenType.EOF) {
|
|
60
|
+
throw this.createParserError(
|
|
61
|
+
`Unexpected token "${this.currentToken.value}" - expected end of expression`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return expr;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
// Re-throw with additional context if it's not already a detailed parser error
|
|
68
|
+
if (error instanceof ExpressionEvaluationException && !error.message.includes('Parser Error:')) {
|
|
69
|
+
throw this.createParserError(error.message);
|
|
70
|
+
}
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
34
73
|
}
|
|
35
74
|
|
|
36
75
|
private parseExpression(): Expression {
|
|
@@ -304,18 +343,34 @@ export class ExpressionParser {
|
|
|
304
343
|
// - "Context.a.b.c" -> Context . (a . (b . c))
|
|
305
344
|
// - "Context.obj[\"key\"].value" -> Context . (obj["key"] . value)
|
|
306
345
|
private parsePostfixRightSide(): Expression {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
346
|
+
this.parseDepth++;
|
|
347
|
+
|
|
348
|
+
// Expect an identifier or number (for numeric property names like .123 or ObjectIds like .507f1f77bcf86cd799439011)
|
|
349
|
+
// IMPORTANT: This allows property paths like Page.kycs.123.individual to work
|
|
350
|
+
if (!this.currentToken ||
|
|
351
|
+
(this.currentToken.type !== TokenType.IDENTIFIER && this.currentToken.type !== TokenType.NUMBER)) {
|
|
352
|
+
this.parseDepth--;
|
|
353
|
+
throw this.createParserError(
|
|
354
|
+
`Expected identifier or number after dot, but found ${this.currentToken?.type || 'EOF'}`
|
|
312
355
|
);
|
|
313
356
|
}
|
|
314
357
|
|
|
315
358
|
// The identifier token value might contain static bracket notation like "obj[\"key\"]" or "a[9]"
|
|
316
359
|
// Or it might be a plain identifier if bracket content is dynamic
|
|
317
|
-
|
|
360
|
+
// For NUMBER tokens, we treat them as property names (e.g., .123 in Page.kycs.123.individual)
|
|
361
|
+
let identifierValue = this.currentToken.value;
|
|
318
362
|
this.advance();
|
|
363
|
+
|
|
364
|
+
// IMPORTANT: Handle ObjectId-like values (e.g., 507f1f77bcf86cd799439011) and numeric IDs with underscores (e.g., 123_abc)
|
|
365
|
+
// The lexer splits these into NUMBER (507) + IDENTIFIER (f1f77bcf86cd799439011) or NUMBER (123) + IDENTIFIER (_abc)
|
|
366
|
+
// So if we just consumed a NUMBER token and the next token is an IDENTIFIER (without a DOT/operator between them),
|
|
367
|
+
// concatenate them to form the complete property name
|
|
368
|
+
if (this.currentToken &&
|
|
369
|
+
this.currentToken.type === TokenType.IDENTIFIER &&
|
|
370
|
+
/^[a-zA-Z_]/.test(this.currentToken.value)) { // Next token starts with a letter or underscore (not a dot or operator)
|
|
371
|
+
identifierValue += this.currentToken.value;
|
|
372
|
+
this.advance();
|
|
373
|
+
}
|
|
319
374
|
|
|
320
375
|
// Check if the identifier contains static bracket notation
|
|
321
376
|
const bracketIndex = identifierValue.indexOf('[');
|
|
@@ -344,7 +399,8 @@ export class ExpressionParser {
|
|
|
344
399
|
const right = this.parsePostfixRightSide(); // Recursive call
|
|
345
400
|
expr = new Expression('', expr, right, Operation.OBJECT_OPERATOR);
|
|
346
401
|
}
|
|
347
|
-
|
|
402
|
+
|
|
403
|
+
this.parseDepth--;
|
|
348
404
|
return expr;
|
|
349
405
|
}
|
|
350
406
|
|
|
@@ -434,9 +490,8 @@ export class ExpressionParser {
|
|
|
434
490
|
// - "Context.a[Page.id]" (dynamic) -> IDENTIFIER "Context.a" + LEFT_BRACKET + expression
|
|
435
491
|
private parseIdentifierPath(): Expression {
|
|
436
492
|
if (!this.currentToken || this.currentToken.type !== TokenType.IDENTIFIER) {
|
|
437
|
-
throw
|
|
438
|
-
this.
|
|
439
|
-
'Expected identifier',
|
|
493
|
+
throw this.createParserError(
|
|
494
|
+
`Expected identifier but found ${this.currentToken?.type || 'EOF'}`
|
|
440
495
|
);
|
|
441
496
|
}
|
|
442
497
|
|
|
@@ -496,9 +551,8 @@ export class ExpressionParser {
|
|
|
496
551
|
return expr;
|
|
497
552
|
}
|
|
498
553
|
|
|
499
|
-
throw
|
|
500
|
-
this.
|
|
501
|
-
`Unexpected token: ${this.currentToken?.value || 'EOF'} at position ${this.currentToken?.startPos || this.lexer.getPosition()}`,
|
|
554
|
+
throw this.createParserError(
|
|
555
|
+
`Unexpected token in expression - expected number, string, identifier, or '(' but found ${this.currentToken?.type || 'EOF'}`
|
|
502
556
|
);
|
|
503
557
|
}
|
|
504
558
|
|
|
@@ -525,9 +579,8 @@ export class ExpressionParser {
|
|
|
525
579
|
|
|
526
580
|
private expectToken(type: TokenType): Token {
|
|
527
581
|
if (!this.currentToken || this.currentToken.type !== type) {
|
|
528
|
-
throw
|
|
529
|
-
this.
|
|
530
|
-
`Expected ${type}, got ${this.currentToken?.type || 'EOF'}`,
|
|
582
|
+
throw this.createParserError(
|
|
583
|
+
`Expected ${type}, but found ${this.currentToken?.type || 'EOF'}${this.currentToken ? ` ("${this.currentToken.value}")` : ''}`
|
|
531
584
|
);
|
|
532
585
|
}
|
|
533
586
|
const token = this.currentToken;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export function duplicate(obj: any): any {
|
|
2
2
|
if (!obj) return obj;
|
|
3
|
-
if (typeof globalThis.structuredClone === 'function')
|
|
3
|
+
if (typeof (globalThis as any).structuredClone === 'function') {
|
|
4
|
+
return (globalThis as any).structuredClone(obj);
|
|
5
|
+
}
|
|
4
6
|
return JSON.parse(JSON.stringify(obj));
|
|
5
7
|
}
|
package/src/index.ts
CHANGED
|
@@ -80,3 +80,4 @@ export * from './engine/exception/KIRuntimeException';
|
|
|
80
80
|
export * from './engine/runtime/expression/operators/unary';
|
|
81
81
|
export * from './engine/runtime/expression/operators/binary';
|
|
82
82
|
export * from './engine/runtime/expression/operators/ternary';
|
|
83
|
+
export * from './engine/dsl';
|