All files / src/ast spel-node.ts

97.91% Statements 94/96
96.29% Branches 26/27
94.73% Functions 18/19
97.91% Lines 94/96

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132    1x 1x 1x   1x 24457x   24457x 24457x 24457x 24457x 24457x 24457x   24457x 24457x 24457x     24457x 1x 1x   24457x 23048x 23048x 23048x 147x 146x 5x 5x 146x 146x 1x 1x 1x 1x 1x 1x 1x 23048x       24457x 2x 2x   24457x 6x 6x 6x 6x 6x 6x   24457x 2x 2x   24457x 78x 78x   24457x 2x 2x   24457x 213x 213x     24457x 2x 2x     24457x 1x 1x     24457x   1x 14117x 14117x 14117x 14117x 14117x 14117x 14117x 14117x     14117x           14117x   1x 9725x   9725x 9725x 9725x 9725x 9725x 9725x 9725x 9725x 9725x 9725x     9725x 1x 1x   9725x 27x 27x     9725x  
import type { ExpressionState } from '../expression-state.js';
import type { TypedValue } from '../typed-value.js';
import { SpelEvaluationException } from '../error/spel-evaluation-exception.js';
import { SpelMessage } from '../error/spel-message.js';
import { NodeType } from '../language/node-type.js';
 
export abstract class SpelNodeImpl {
  protected children: SpelNodeImpl[];
 
  constructor(nodeType: NodeType, startPos: number, endPos: number, ...children: SpelNodeImpl[]) {
    this.nodeType = nodeType;
    this.startPos = startPos;
    this.endPos = endPos;
    this.children = children;
  }
 
  public readonly nodeType: NodeType;
  public readonly startPos: number;
  public readonly endPos: number;
 
  /** Get the canonical node type for this AST node */
  public getNodeType(): NodeType {
    return this.nodeType;
  }
 
  public getValue(state: ExpressionState): TypedValue {
    try {
      return this.getValueInternal(state);
    } catch (e) {
      if (e instanceof SpelEvaluationException) {
        if (e.position < 0) {
          e.position = this.startPos;
        }
        throw e;
      }
      throw new SpelEvaluationException(
        this.startPos,
        SpelMessage.EXCEPTION_DURING_PROPERTY_READ,
        this.toStringAST(),
        (e as Error).message,
      );
    }
  }
 
  public abstract getValueInternal(state: ExpressionState): TypedValue;
 
  public isWritable(_state: ExpressionState): boolean {
    return false;
  }
 
  public setValue(_state: ExpressionState, _newValue: unknown): void {
    throw new SpelEvaluationException(
      this.startPos,
      SpelMessage.NOT_ASSIGNABLE,
      this.toStringAST(),
    );
  }
 
  public getValueType(state: ExpressionState): unknown {
    return this.getValue(state).getTypeDescriptor();
  }
 
  public getChild(index: number): SpelNodeImpl {
    return this.children[index]!;
  }
 
  public setChild(index: number, child: SpelNodeImpl): void {
    this.children[index] = child;
  }
 
  public getChildCount(): number {
    return this.children.length;
  }
 
  /** Check if any direct child is of the given node type */
  public hasChildOfType(type: NodeType): boolean {
    return this.children.some((c) => c.nodeType === type);
  }
 
  /** Get all direct children of the given node type */
  public getChildrenOfType(type: NodeType): SpelNodeImpl[] {
    return this.children.filter((c) => c.nodeType === type);
  }
 
  public abstract toStringAST(): string;
}
 
export abstract class Literal extends SpelNodeImpl {
  constructor(
    nodeType: NodeType,
    startPos: number,
    endPos: number,
    protected readonly literalValue: string,
  ) {
    super(nodeType, startPos, endPos);
  }
 
  /** Get the raw literal value string representation */
  public getLiteralValue(): string {
    return this.literalValue;
  }
 
  public abstract override getValueInternal(state: ExpressionState): TypedValue;
  public abstract override toStringAST(): string;
}
 
export abstract class Operator extends SpelNodeImpl {
  protected readonly operatorName: string;
 
  constructor(
    nodeType: NodeType,
    operatorName: string,
    startPos: number,
    endPos: number,
    ...operands: SpelNodeImpl[]
  ) {
    super(nodeType, startPos, endPos, ...operands);
    this.operatorName = operatorName;
  }
 
  /** Get the operator symbol/name (e.g., '+', '==', 'and') */
  public getOperatorName(): string {
    return this.operatorName;
  }
 
  public toStringAST(): string {
    return `(${this.children[0]?.toStringAST() ?? ''} ${this.operatorName} ${this.children[1]?.toStringAST() ?? ''})`;
  }
 
  public abstract override getValueInternal(state: ExpressionState): TypedValue;
}