@fincity/kirun-js 1.3.1 → 1.4.2

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.
Files changed (37) hide show
  1. package/__tests__/engine/json/schema/validator/SchemaValidatorTest.ts +51 -1
  2. package/__tests__/engine/runtime/KIRuntimeWithDefinitionTest.ts +76 -0
  3. package/__tests__/engine/runtime/expression/ExpressionEvaluatorTernaryOperatorTest.ts +42 -0
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/module.js +1 -1
  7. package/dist/module.js.map +1 -1
  8. package/dist/types.d.ts +35 -20
  9. package/dist/types.d.ts.map +1 -1
  10. package/package.json +2 -2
  11. package/src/engine/function/AbstractFunction.ts +72 -38
  12. package/src/engine/function/system/GenerateEvent.ts +1 -2
  13. package/src/engine/function/system/string/Reverse.ts +1 -1
  14. package/src/engine/json/schema/Schema.ts +119 -2
  15. package/src/engine/json/schema/SchemaUtil.ts +1 -1
  16. package/src/engine/json/schema/array/ArraySchemaType.ts +6 -0
  17. package/src/engine/json/schema/type/MultipleType.ts +5 -2
  18. package/src/engine/json/schema/type/SingleType.ts +4 -2
  19. package/src/engine/json/schema/validator/ObjectValidator.ts +2 -3
  20. package/src/engine/model/AbstractStatement.ts +10 -0
  21. package/src/engine/model/Event.ts +12 -5
  22. package/src/engine/model/FunctionDefinition.ts +1 -2
  23. package/src/engine/model/FunctionSignature.ts +14 -4
  24. package/src/engine/model/Parameter.ts +13 -3
  25. package/src/engine/model/ParameterReference.ts +12 -3
  26. package/src/engine/model/Position.ts +1 -1
  27. package/src/engine/model/Statement.ts +33 -7
  28. package/src/engine/model/StatementGroup.ts +22 -2
  29. package/src/engine/runtime/KIRuntime.ts +3 -2
  30. package/src/engine/runtime/expression/Expression.ts +120 -0
  31. package/src/engine/runtime/expression/ExpressionEvaluator.ts +26 -0
  32. package/src/engine/runtime/expression/Operation.ts +10 -0
  33. package/src/engine/runtime/expression/operators/ternary/ConditionalTernaryOperator.ts +7 -0
  34. package/src/engine/runtime/expression/operators/ternary/TernaryOperator.ts +15 -0
  35. package/src/engine/runtime/expression/operators/ternary/index.ts +1 -0
  36. package/src/index.ts +1 -1
  37. package/src/engine/json/schema/object/AdditionalPropertiesType.ts +0 -32
@@ -1,13 +1,12 @@
1
1
  import { Namespaces } from '../../namespaces/Namespaces';
2
2
  import { ArraySchemaType } from './array/ArraySchemaType';
3
- import { AdditionalPropertiesType } from './object/AdditionalPropertiesType';
4
3
  import { StringFormat } from './string/StringFormat';
5
4
  import { SchemaType } from './type/SchemaType';
6
5
  import { TypeUtil } from './type/TypeUtil';
7
6
  import { Type } from './type/Type';
8
7
  import { isNullValue } from '../../util/NullCheck';
9
8
  import { SingleType } from './type/SingleType';
10
- import { SchemaReferenceException } from './validator/exception/SchemaReferenceException';
9
+ import { MultipleType } from './type/MultipleType';
11
10
 
12
11
  const ADDITIONAL_PROPERTY: string = 'additionalProperty';
13
12
  const ENUMS: string = 'enums';
@@ -18,6 +17,44 @@ const VERSION_STRING: string = 'version';
18
17
  const NAMESPACE_STRING: string = 'namespace';
19
18
  const TEMPORARY: string = '_';
20
19
 
20
+ export class AdditionalPropertiesType {
21
+ private booleanValue?: boolean;
22
+ private schemaValue?: Schema;
23
+
24
+ constructor(apt: AdditionalPropertiesType | undefined = undefined) {
25
+ if (!apt) return;
26
+ this.booleanValue = apt.booleanValue;
27
+ if (!apt.schemaValue) return;
28
+ this.schemaValue = new Schema(apt.schemaValue);
29
+ }
30
+
31
+ public getBooleanValue(): boolean | undefined {
32
+ return this.booleanValue;
33
+ }
34
+
35
+ public getSchemaValue(): Schema | undefined {
36
+ return this.schemaValue;
37
+ }
38
+
39
+ public setBooleanValue(booleanValue: boolean): AdditionalPropertiesType {
40
+ this.booleanValue = booleanValue;
41
+ return this;
42
+ }
43
+
44
+ public setSchemaValue(schemaValue: Schema): AdditionalPropertiesType {
45
+ this.schemaValue = schemaValue;
46
+ return this;
47
+ }
48
+
49
+ public static from(obj: any): AdditionalPropertiesType | undefined {
50
+ if (!obj) return undefined;
51
+ const ad = new AdditionalPropertiesType();
52
+ ad.booleanValue = obj.booleanValue;
53
+ ad.schemaValue = obj.schemaValue;
54
+ return ad;
55
+ }
56
+ }
57
+
21
58
  export class Schema {
22
59
  public static readonly NULL: Schema = new Schema()
23
60
  .setNamespace(Namespaces.SYSTEM)
@@ -389,6 +426,86 @@ export class Schema {
389
426
  private $defs?: Map<string, Schema>;
390
427
  private permission?: string;
391
428
 
429
+ public constructor(schema?: Schema) {
430
+ if (!schema) return;
431
+
432
+ this.namespace = schema.namespace;
433
+ this.name = schema.name;
434
+
435
+ this.version = schema.version;
436
+ this.ref = schema.ref;
437
+
438
+ this.type =
439
+ schema.type instanceof SingleType
440
+ ? new SingleType(schema.type as SingleType)
441
+ : new MultipleType(schema.type as MultipleType);
442
+
443
+ this.anyOf = schema.anyOf?.map((x) => new Schema(x));
444
+
445
+ this.allOf = schema.allOf?.map((x) => new Schema(x));
446
+ this.oneOf = schema.oneOf?.map((x) => new Schema(x));
447
+
448
+ this.not = this.not ? new Schema(this.not) : undefined;
449
+
450
+ this.description = schema.description;
451
+ this.examples = schema.examples ? JSON.parse(JSON.stringify(schema.examples)) : undefined;
452
+
453
+ this.defaultValue = schema.defaultValue
454
+ ? JSON.parse(JSON.stringify(schema.defaultValue))
455
+ : undefined;
456
+ this.comment = schema.comment;
457
+ this.enums = schema.enums ? [...schema.enums] : undefined;
458
+ this.constant = schema.constant ? JSON.parse(JSON.stringify(schema.constant)) : undefined;
459
+
460
+ this.pattern = schema.pattern;
461
+ this.format = schema.format;
462
+
463
+ this.minLength = schema.minLength;
464
+ this.maxLength = schema.maxLength;
465
+
466
+ this.multipleOf = schema.multipleOf;
467
+ this.minimum = schema.minimum;
468
+ this.maximum = schema.maximum;
469
+ this.exclusiveMinimum = schema.exclusiveMinimum;
470
+ this.exclusiveMaximum = schema.exclusiveMaximum;
471
+
472
+ this.properties = schema.properties
473
+ ? new Map(Array.from(schema.properties.entries()).map((e) => [e[0], new Schema(e[1])]))
474
+ : undefined;
475
+
476
+ this.additionalProperties = schema.additionalProperties
477
+ ? new AdditionalPropertiesType(schema.additionalProperties)
478
+ : undefined;
479
+
480
+ this.required = schema.required ? [...schema.required] : undefined;
481
+
482
+ this.propertyNames = schema.propertyNames ? new Schema(schema.propertyNames) : undefined;
483
+ this.minProperties = schema.minProperties;
484
+ this.maxProperties = schema.maxProperties;
485
+
486
+ this.patternProperties = schema.patternProperties
487
+ ? new Map(
488
+ Array.from(schema.patternProperties.entries()).map((e) => [
489
+ e[0],
490
+ new Schema(e[1]),
491
+ ]),
492
+ )
493
+ : undefined;
494
+
495
+ this.items = schema.items ? new ArraySchemaType(schema.items) : undefined;
496
+ this.contains = schema.contains ? new Schema(this.contains) : undefined;
497
+
498
+ this.minItems = schema.minItems;
499
+ this.maxItems = schema.maxItems;
500
+ this.uniqueItems = schema.uniqueItems;
501
+
502
+ this.$defs = schema.$defs
503
+ ? new Map(Array.from(schema.$defs.entries()).map((e) => [e[0], new Schema(e[1])]))
504
+ : undefined;
505
+
506
+ this.permission = schema.permission;
507
+ }
508
+
392
509
  public getTitle(): string | undefined {
393
510
  return this.getFullName();
394
511
  }
@@ -120,7 +120,7 @@ export class SchemaUtil {
120
120
  ): Tuple2<Schema, string> | undefined {
121
121
  if (!sRepository) return undefined;
122
122
 
123
- let nms = StringUtil.splitAtFirstOccurance(inSchem?.getRef() ?? '', '/');
123
+ let nms = StringUtil.splitAtFirstOccurance(ref ?? '', '/');
124
124
  if (!nms[0]) return undefined;
125
125
 
126
126
  let nmspnm = StringUtil.splitAtFirstOccurance(nms[0], '.');
@@ -5,6 +5,12 @@ export class ArraySchemaType {
5
5
  private singleSchema: Schema | undefined;
6
6
  private tupleSchema: Schema[] | undefined;
7
7
 
8
+ public constructor(ast?: ArraySchemaType) {
9
+ if (!ast) return;
10
+ this.singleSchema = ast.singleSchema ? new Schema(ast.singleSchema) : undefined;
11
+ this.tupleSchema = ast.tupleSchema ? ast.tupleSchema.map((e) => new Schema(e)) : undefined;
12
+ }
13
+
8
14
  public setSingleSchema(schema: Schema): ArraySchemaType {
9
15
  this.singleSchema = schema;
10
16
  return this;
@@ -4,9 +4,12 @@ import { Type } from './Type';
4
4
  export class MultipleType extends Type {
5
5
  private type: Set<SchemaType>;
6
6
 
7
- constructor(type: Set<SchemaType>) {
7
+ public constructor(type: Set<SchemaType> | MultipleType) {
8
8
  super();
9
- this.type = type;
9
+
10
+ if (type instanceof MultipleType)
11
+ this.type = new Set(Array.from((type as MultipleType).type));
12
+ else this.type = new Set(Array.from(type));
10
13
  }
11
14
 
12
15
  public getType(): Set<SchemaType> {
@@ -4,9 +4,11 @@ import { Type } from './Type';
4
4
  export class SingleType extends Type {
5
5
  private type: SchemaType;
6
6
 
7
- constructor(type: SchemaType) {
7
+ public constructor(type: SchemaType | SingleType) {
8
8
  super();
9
- this.type = type;
9
+
10
+ if (type instanceof SingleType) this.type = (type as SingleType).type;
11
+ else this.type = type as SchemaType;
10
12
  }
11
13
 
12
14
  public getType(): SchemaType {
@@ -1,7 +1,6 @@
1
1
  import { Repository } from '../../../Repository';
2
2
  import { isNullValue } from '../../../util/NullCheck';
3
- import { AdditionalPropertiesType } from '../object/AdditionalPropertiesType';
4
- import { Schema } from '../Schema';
3
+ import { AdditionalPropertiesType, Schema } from '../Schema';
5
4
  import { SchemaValidationException } from './exception/SchemaValidationException';
6
5
  import { SchemaValidator } from './SchemaValidator';
7
6
 
@@ -100,7 +99,7 @@ export class ObjectValidator {
100
99
  newParents,
101
100
  apt.getSchemaValue(),
102
101
  repository,
103
- jsonObject.get(key),
102
+ jsonObject[key],
104
103
  );
105
104
  jsonObject[key] = element;
106
105
  }
@@ -6,6 +6,16 @@ export class AbstractStatement {
6
6
  private position?: Position;
7
7
  private override: boolean = false;
8
8
 
9
+ public constructor(ast?: AbstractStatement) {
10
+ if (!ast) return;
11
+ this.comment = ast.comment;
12
+ this.description = ast.description;
13
+ this.position = ast.position
14
+ ? new Position(ast.position.getLeft(), ast.position.getTop())
15
+ : undefined;
16
+ this.override = ast.override;
17
+ }
18
+
9
19
  public getComment(): string | undefined {
10
20
  return this.comment;
11
21
  }
@@ -1,5 +1,4 @@
1
- import { AdditionalPropertiesType } from '../json/schema/object/AdditionalPropertiesType';
2
- import { Schema } from '../json/schema/Schema';
1
+ import { AdditionalPropertiesType, Schema } from '../json/schema/Schema';
3
2
  import { SchemaType } from '../json/schema/type/SchemaType';
4
3
  import { TypeUtil } from '../json/schema/type/TypeUtil';
5
4
  import { SchemaReferenceException } from '../json/schema/validator/exception/SchemaReferenceException';
@@ -30,9 +29,17 @@ export class Event {
30
29
  private name: string;
31
30
  private parameters: Map<string, Schema>;
32
31
 
33
- constructor(name: string, parameters: Map<string, Schema>) {
34
- this.name = name;
35
- this.parameters = parameters;
32
+ constructor(evn: string | Event, parameters?: Map<string, Schema>) {
33
+ if (evn instanceof Event) {
34
+ this.name = evn.name;
35
+ this.parameters = new Map(
36
+ Array.from(evn.parameters.entries()).map((e) => [e[0], new Schema(e[1])]),
37
+ );
38
+ } else {
39
+ this.name = evn;
40
+ if (!parameters) throw new Error('Unknown constructor format');
41
+ this.parameters = parameters;
42
+ }
36
43
  }
37
44
 
38
45
  public getName(): string {
@@ -1,5 +1,4 @@
1
- import { AdditionalPropertiesType } from '../json/schema/object/AdditionalPropertiesType';
2
- import { Schema } from '../json/schema/Schema';
1
+ import { AdditionalPropertiesType, Schema } from '../json/schema/Schema';
3
2
  import { Namespaces } from '../namespaces/Namespaces';
4
3
  import { Event } from './Event';
5
4
  import { FunctionSignature } from './FunctionSignature';
@@ -1,5 +1,4 @@
1
- import { AdditionalPropertiesType } from '../json/schema/object/AdditionalPropertiesType';
2
- import { Schema } from '../json/schema/Schema';
1
+ import { AdditionalPropertiesType, Schema } from '../json/schema/Schema';
3
2
  import { Namespaces } from '../namespaces/Namespaces';
4
3
  import { Event } from './Event';
5
4
  import { Parameter } from './Parameter';
@@ -32,8 +31,19 @@ export class FunctionSignature {
32
31
  private parameters: Map<string, Parameter> = new Map();
33
32
  private events: Map<string, Event> = new Map();
34
33
 
35
- constructor(name: string) {
36
- this.name = name;
34
+ constructor(value: string | FunctionSignature) {
35
+ if (value instanceof FunctionSignature) {
36
+ this.name = value.name;
37
+ this.namespace = value.namespace;
38
+ this.parameters = new Map(
39
+ Array.from(value.parameters.entries()).map((e) => [e[0], new Parameter(e[1])]),
40
+ );
41
+ this.events = new Map(
42
+ Array.from(value.events.entries()).map((e) => [e[0], new Event(e[1])]),
43
+ );
44
+ } else {
45
+ this.name = value;
46
+ }
37
47
  }
38
48
 
39
49
  public getNamespace(): string {
@@ -43,9 +43,19 @@ export class Parameter {
43
43
  private variableArgument: boolean = false;
44
44
  private type: ParameterType = ParameterType.EXPRESSION;
45
45
 
46
- constructor(parameterName: string, schema: Schema) {
47
- this.schema = schema;
48
- this.parameterName = parameterName;
46
+ constructor(pn: string | Parameter, schema?: Schema) {
47
+ if (pn instanceof Parameter) {
48
+ this.schema = new Schema(pn.schema);
49
+ this.parameterName = pn.parameterName;
50
+ this.variableArgument = pn.variableArgument;
51
+ this.type = pn.type;
52
+ } else {
53
+ if (!schema) {
54
+ throw new Error('Unknown constructor signature');
55
+ }
56
+ this.schema = schema;
57
+ this.parameterName = pn;
58
+ }
49
59
  }
50
60
 
51
61
  public getSchema(): Schema {
@@ -2,6 +2,7 @@ import { Schema } from '../json/schema/Schema';
2
2
  import { SchemaType } from '../json/schema/type/SchemaType';
3
3
  import { TypeUtil } from '../json/schema/type/TypeUtil';
4
4
  import { Namespaces } from '../namespaces/Namespaces';
5
+ import { isNullValue } from '../util/NullCheck';
5
6
  import UUID from '../util/UUID';
6
7
  import { ParameterReferenceType } from './ParameterReferenceType';
7
8
 
@@ -25,9 +26,17 @@ export class ParameterReference {
25
26
  private value: any;
26
27
  private expression?: string;
27
28
 
28
- constructor(type: ParameterReferenceType) {
29
- this.type = type;
30
- this.key = UUID();
29
+ constructor(type: ParameterReferenceType | ParameterReference) {
30
+ if (type instanceof ParameterReference) {
31
+ let pv = type as ParameterReference;
32
+ this.key = pv.key;
33
+ this.type = pv.type;
34
+ this.value = isNullValue(pv.value) ? undefined : JSON.parse(JSON.stringify(pv.value));
35
+ this.expression = pv.expression;
36
+ } else {
37
+ this.type = type as ParameterReferenceType;
38
+ this.key = UUID();
39
+ }
31
40
  }
32
41
 
33
42
  public getType(): ParameterReferenceType {
@@ -18,7 +18,7 @@ export class Position {
18
18
  private left: number;
19
19
  private top: number;
20
20
 
21
- constructor(left: number, top: number) {
21
+ public constructor(left: number, top: number) {
22
22
  this.left = left;
23
23
  this.top = top;
24
24
  }
@@ -1,5 +1,4 @@
1
- import { AdditionalPropertiesType } from '../json/schema/object/AdditionalPropertiesType';
2
- import { Schema } from '../json/schema/Schema';
1
+ import { AdditionalPropertiesType, Schema } from '../json/schema/Schema';
3
2
  import { SchemaType } from '../json/schema/type/SchemaType';
4
3
  import { TypeUtil } from '../json/schema/type/TypeUtil';
5
4
  import { Namespaces } from '../namespaces/Namespaces';
@@ -49,11 +48,38 @@ export class Statement extends AbstractStatement {
49
48
  private parameterMap?: Map<string, Map<string, ParameterReference>>;
50
49
  private dependentStatements?: Map<string, boolean>;
51
50
 
52
- public constructor(statementName: string, namespace: string, name: string) {
53
- super();
54
- this.statementName = statementName;
55
- this.namespace = namespace;
56
- this.name = name;
51
+ public constructor(sn: string | Statement, namespace?: string, name?: string) {
52
+ super(sn instanceof Statement ? (sn as Statement) : undefined);
53
+
54
+ if (sn instanceof Statement) {
55
+ let x = sn as Statement;
56
+ this.statementName = x.statementName;
57
+ this.name = x.name;
58
+ this.namespace = x.namespace;
59
+ if (x.parameterMap)
60
+ this.parameterMap = new Map(
61
+ Array.from(x.parameterMap.entries()).map((f) => [
62
+ f[0],
63
+ new Map(
64
+ Array.from(f[1].entries()).map((e) => [
65
+ e[0],
66
+ new ParameterReference(e[1]),
67
+ ]),
68
+ ),
69
+ ]),
70
+ );
71
+
72
+ if (x.dependentStatements) {
73
+ this.dependentStatements = new Map(Array.from(x.dependentStatements.entries()));
74
+ }
75
+ } else {
76
+ this.statementName = sn;
77
+ if (!name || !namespace) {
78
+ throw new Error('Unknown constructor');
79
+ }
80
+ this.namespace = namespace;
81
+ this.name = name;
82
+ }
57
83
  }
58
84
 
59
85
  public getStatementName(): string {
@@ -4,6 +4,7 @@ import { TypeUtil } from '../json/schema/type/TypeUtil';
4
4
  import { Namespaces } from '../namespaces/Namespaces';
5
5
  import { AbstractStatement } from './AbstractStatement';
6
6
  import { Position } from './Position';
7
+ import { Statement } from './Statement';
7
8
 
8
9
  export class StatementGroup extends AbstractStatement {
9
10
  private static readonly SCHEMA_NAME: string = 'StatementGroup';
@@ -21,10 +22,12 @@ export class StatementGroup extends AbstractStatement {
21
22
  );
22
23
 
23
24
  private statementGroupName: string;
25
+ private statements: Map<string, boolean>;
24
26
 
25
- constructor(statementGroupName: string) {
27
+ constructor(statementGroupName: string, statements: Map<string, boolean> = new Map()) {
26
28
  super();
27
29
  this.statementGroupName = statementGroupName;
30
+ this.statements = statements;
28
31
  }
29
32
 
30
33
  public getStatementGroupName(): string {
@@ -35,8 +38,25 @@ export class StatementGroup extends AbstractStatement {
35
38
  return this;
36
39
  }
37
40
 
41
+ public getStatements(): Map<string, boolean> {
42
+ return this.statements;
43
+ }
44
+
45
+ public setStatements(statements: Map<string, boolean>): StatementGroup {
46
+ this.statements = statements;
47
+ return this;
48
+ }
49
+
38
50
  public static from(json: any): StatementGroup {
39
- return new StatementGroup(json.statementGroupName)
51
+ return new StatementGroup(
52
+ json.statementGroupName,
53
+ new Map(
54
+ Object.entries(json.statements || {}).map(([k, v]) => [
55
+ k,
56
+ ('' + v)?.toLowerCase() == 'true',
57
+ ]),
58
+ ),
59
+ )
40
60
  .setPosition(Position.from(json.position))
41
61
  .setComment(json.comment)
42
62
  .setDescription(json.description) as StatementGroup;
@@ -485,7 +485,7 @@ export class KIRuntime extends AbstractFunction {
485
485
 
486
486
  let refList: ParameterReference[] = Array.from(param[1]?.values() ?? []);
487
487
 
488
- if (!refList.length) {
488
+ if (!refList.length && !p.isVariableArgument()) {
489
489
  if (isNullValue(SchemaUtil.getDefaultValue(p.getSchema(), sRepo)))
490
490
  se.addMessage(
491
491
  StatementMessageType.ERROR,
@@ -500,7 +500,7 @@ export class KIRuntime extends AbstractFunction {
500
500
 
501
501
  if (p.isVariableArgument()) {
502
502
  for (let ref of refList) this.parameterReferenceValidation(se, p, ref, sRepo);
503
- } else {
503
+ } else if (refList.length) {
504
504
  let ref: ParameterReference = refList[0];
505
505
  this.parameterReferenceValidation(se, p, ref, sRepo);
506
506
  }
@@ -515,6 +515,7 @@ export class KIRuntime extends AbstractFunction {
515
515
 
516
516
  if (paramSet.size) {
517
517
  for (let param of Array.from(paramSet.values())) {
518
+ if (param.isVariableArgument()) continue;
518
519
  if (isNullValue(SchemaUtil.getDefaultValue(param.getSchema(), sRepo)))
519
520
  se.addMessage(
520
521
  StatementMessageType.ERROR,
@@ -77,6 +77,33 @@ export class Expression extends ExpressionToken {
77
77
  isPrevOp = result.getT2();
78
78
  break;
79
79
  }
80
+ case '?': {
81
+ if (
82
+ i + 1 < length &&
83
+ this.expression.charAt(i + 1) != '?' &&
84
+ i != 0 &&
85
+ this.expression.charAt(i - 1) != '?'
86
+ ) {
87
+ i = this.processTernaryOperator(length, sb, buff, i, isPrevOp);
88
+ } else {
89
+ let result: Tuple2<number, boolean> = this.processOthers(
90
+ chr,
91
+ length,
92
+ sb,
93
+ buff,
94
+ i,
95
+ isPrevOp,
96
+ );
97
+ i = result.getT1();
98
+ isPrevOp = result.getT2();
99
+ if (isPrevOp && this.ops.peek() == Operation.ARRAY_OPERATOR) {
100
+ result = this.process(length, sb, i);
101
+ i = result.getT1();
102
+ isPrevOp = result.getT2();
103
+ }
104
+ }
105
+ break;
106
+ }
80
107
  default:
81
108
  let result: Tuple2<number, boolean> = this.processOthers(
82
109
  chr,
@@ -188,6 +215,80 @@ export class Expression extends ExpressionToken {
188
215
  return new Tuple2(i, false);
189
216
  }
190
217
 
218
+ private processTernaryOperator(
219
+ length: number,
220
+ sb: StringBuilder,
221
+ buff: string,
222
+ i: number,
223
+ isPrevOp: boolean,
224
+ ): number {
225
+ if (isPrevOp) {
226
+ throw new ExpressionEvaluationException(
227
+ this.expression,
228
+ 'Ternary operator is followed by an operator',
229
+ );
230
+ }
231
+
232
+ if (buff.trim() != '') {
233
+ this.tokens.push(new Expression(buff));
234
+ sb.setLength(0);
235
+ }
236
+
237
+ ++i;
238
+ let cnt: number = 1;
239
+ let inChr = '';
240
+ const start = i;
241
+ while (i < length && cnt > 0) {
242
+ inChr = this.expression.charAt(i);
243
+ if (inChr == '?') ++cnt;
244
+ else if (inChr == ':') --cnt;
245
+ ++i;
246
+ }
247
+
248
+ if (inChr != ':') {
249
+ throw new ExpressionEvaluationException(this.expression, "':' operater is missing");
250
+ }
251
+
252
+ if (i >= length) {
253
+ throw new ExpressionEvaluationException(
254
+ this.expression,
255
+ 'Third part of the ternary expression is missing',
256
+ );
257
+ }
258
+
259
+ while (
260
+ !this.ops.isEmpty() &&
261
+ this.hasPrecedence(Operation.CONDITIONAL_TERNARY_OPERATOR, this.ops.peek())
262
+ ) {
263
+ let prev: Operation = this.ops.pop();
264
+
265
+ if (Operation.UNARY_OPERATORS.has(prev)) {
266
+ const l: ExpressionToken = this.tokens.pop();
267
+ this.tokens.push(new Expression('', l, undefined, prev));
268
+ } else {
269
+ let r = this.tokens.pop();
270
+ let l = this.tokens.pop();
271
+
272
+ this.tokens.push(new Expression('', l, r, prev));
273
+ }
274
+ }
275
+
276
+ this.ops.push(Operation.CONDITIONAL_TERNARY_OPERATOR);
277
+ this.tokens.push(new Expression(this.expression.substring(start, i - 1)));
278
+
279
+ const secondExp: string = this.expression.substring(i);
280
+ if (secondExp.trim() === '') {
281
+ throw new ExpressionEvaluationException(
282
+ this.expression,
283
+ 'Third part of the ternary expression is missing',
284
+ );
285
+ }
286
+
287
+ this.tokens.push(new Expression(secondExp));
288
+
289
+ return length - 1;
290
+ }
291
+
191
292
  private processSubExpression(
192
293
  length: number,
193
294
  sb: StringBuilder,
@@ -329,6 +430,25 @@ export class Expression extends ExpressionToken {
329
430
  )
330
431
  .append(')');
331
432
  ind++;
433
+ } else if (this.ops.get(i) == Operation.CONDITIONAL_TERNARY_OPERATOR) {
434
+ let temp: ExpressionToken = tokens[ind++];
435
+ sb.insert(
436
+ 0,
437
+ temp instanceof Expression ? (temp as Expression).toString() : temp.toString(),
438
+ );
439
+ sb.insert(0, ':');
440
+ temp = tokens[ind++];
441
+ sb.insert(
442
+ 0,
443
+ temp instanceof Expression ? (temp as Expression).toString() : temp.toString(),
444
+ );
445
+ sb.insert(0, '?');
446
+ temp = tokens[ind++];
447
+ sb.insert(
448
+ 0,
449
+ temp instanceof Expression ? (temp as Expression).toString() : temp.toString(),
450
+ ).append(')');
451
+ sb.insert(0, '(');
332
452
  } else {
333
453
  if (ind == 0) {
334
454
  const temp: ExpressionToken = tokens[ind++];