All files / src/cds/expressions cds_arithmetics.ts

100% Statements 60/60
100% Branches 1/1
100% Functions 1/1
100% Lines 60/60

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 611x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x  
import {CDSAggregate, CDSArithParen, CDSCase, CDSCast, CDSFunction, CDSPrefixedName, CDSString} from ".";
import {altPrio, Expression, plusPrio, seq, starPrio} from "../../abap/2_statements/combi";
import {IStatementRunnable} from "../../abap/2_statements/statement_runnable";
import {CDSInteger} from "./cds_integer";
 
/**
 * Arithmetic expression: one or more values joined by +, -, *, /.
 *
 * Parenthesized sub-expressions of any nesting depth are handled via the
 * separate CDSArithParen singleton (which wraps back to CDSArithmetics).
 * The mutual reference between two distinct singletons enables true n-level
 * nesting with no fixed limit and no infinite recursion.
 *
 * Grammar (simplified):
 *   CDSArithmetics  →  operand (op operand)+          -- with-operator form
 *                    | unary val                       -- unary form
 *                    | unary val (op operand)+         -- unary + continuation
 *                    | unary CDSArithParen             -- unary applied to paren, e.g. -(field)
 *                    | unary CDSArithParen (op op)+    -- unary paren + continuation
 *   operand         →  CDSArithParen | val
 *   CDSArithParen   →  "(" CDSArithmetics ")" | "(" CDSArithParen ")" | "(" val ")"
 */
export class CDSArithmetics extends Expression {
  public getRunnable(): IStatementRunnable {
    const val = altPrio(CDSInteger, CDSFunction, CDSCase, CDSCast, CDSString, CDSAggregate, CDSPrefixedName);
    const operator = altPrio("+", "-", "*", "/");
 
    // Unary operator prefix, e.g. -field, +field
    const unary = altPrio("-", "+");
    const unaryExpression = seq(unary, val);
 
    // An operand is either a parenthesized sub-expression (any depth) or a bare value.
    // CDSArithParen = "(" altPrio(CDSArithmetics, CDSArithParen, val) ")" — separate singleton that
    // can recursively contain itself, enabling deeply nested parentheses without infinite recursion.
    const operand = altPrio(CDSArithParen, val);
    const operatorValue = seq(operator, operand);
 
    // Main form: operand op operand op ... (leading term may itself be a paren)
    const withOperators = seq(operand, plusPrio(operatorValue));
 
    // Unary followed by optional continuation operators: -1 * field, -field + 1
    const unaryWithOps = seq(unaryExpression, plusPrio(operatorValue));
 
    // Top-level parenthesized expression as a standalone field value: (A + B) * C
    const parenExpr = altPrio(withOperators, unaryExpression);
    const paren = seq("(", parenExpr, ")");
 
    // Unary applied to a parenthesized group, e.g. -(TaxAmount)
    const unaryParen = seq(unary, CDSArithParen);
 
    return altPrio(
      seq(paren, starPrio(operatorValue)),        // (expr) op ...
      seq(unaryParen, starPrio(operatorValue)),   // -(paren) op ...
      unaryWithOps,                               // -val op ...
      unaryExpression,                            // -val
      unaryParen,                                 // -(paren)
      withOperators,                              // operand op operand ...
    );
  }
}