function _astToExpr()

in packages/sqrl/src/ast/AstExpr.ts [319:458]


function _astToExpr(ast: Ast, state: AstExprState): Expr {
  return sqrlErrorWrap(
    {
      location: ast.location,
    },
    (): Expr => {
      if (ast.type === "iterator") {
        invariant(
          state.currentIterator === ast.name,
          `Expected currentIterator to be %s was %s`,
          ast.name,
          state.currentIterator
        );
        return {
          type: "iterator",
          name: ast.name,
        };
      } else if (ast.type === "feature") {
        sqrlInvariant(
          ast,
          state.hasSlot(ast.value),
          "Feature was not defined: %s",
          ast.value
        );
        return slotExpr(state, ast.value);
      } else if (ast.type === "slot") {
        return slotExpr(state, ast.slotName);
      } else if (ast.type === "state") {
        return { type: "state" };
      } else if (ast.type === "whenCause") {
        if (ast.slotName) {
          return slotExpr(state, ast.slotName);
        } else {
          return constantExpr(null);
        }
      } else if (ast.type === "if") {
        const exprAsts: Ast[] = [
          ast.condition,
          ast.trueBranch,
          ast.falseBranch,
        ];

        return ifExpr(exprAsts.map((ast) => _astToExpr(ast, state)));
      } else if (ast.type === "switch") {
        let result = ast.defaultCase
          ? _astToExpr(ast.defaultCase, state)
          : constantExpr(null);

        for (const { expr, where } of ast.cases) {
          // @TODO: Investigate old code if tests are failing due to where clauses:
          // const trueExpr = state.wrapWhere(truthTableWhere, () => _astToExpr(expr, state));
          const trueExpr = _astToExpr(expr, state);
          if (SqrlAst.isConstantTrue(where)) {
            result = trueExpr;
          } else {
            result = ifExpr([_astToExpr(where, state), trueExpr, result]);
          }
        }

        return result;
      } else if (ast.type === "call" || ast.type === "registeredCall") {
        const func = ast.func;
        const location = ast.location || null;
        return sqrlErrorWrap({ location }, () => {
          return callExpr(state, func, ast.args);
        });
      } else if (ast.type === "listComprehension") {
        // The value might still appear as a feature, so make sure to ignore it
        return state.wrapIterator(ast.iterator.name, () => {
          return _astToExprList([ast.input, ast.output, ast.where], state, {
            type: "listComprehension",
            iterator: ast.iterator.name,
          });
        });
      } else if (ast.type === "not") {
        return callExpr(state, "_not", [ast.expr]);
      } else if (ast.type === "constant") {
        return constantExpr(ast.value);
      } else if (ast.type === "binary_expr" || ast.type === "boolean_expr") {
        let func: string;
        let args = [ast.left, ast.right];

        if (ast.operator === "or" || ast.operator === "and") {
          let nextAst = args[args.length - 1];
          while (
            nextAst.type === "boolean_expr" &&
            nextAst.operator === ast.operator
          ) {
            args = [
              ...args.slice(0, args.length - 1),
              nextAst.left,
              nextAst.right,
            ];
            nextAst = args[args.length - 1];
          }
        }

        if (ast.operator === "in") {
          func = "_contains";
          args.reverse();
        } else if (ast.operator === "/") {
          args.unshift({ type: "state" });
          func = "_divide";
        } else if (ast.operator === "%") {
          args.unshift({ type: "state" });
          func = "_modulo";
        } else if (ast.operator === "or") {
          return orExpr(state, args);
        } else if (ast.operator === "and") {
          return andExpr(state, args);
        } else if (ast.operator === "is" || ast.operator === "is not") {
          invariant(
            args.length === 2 && SqrlAst.isConstantNull(args[1]),
            "Expected `null` value for right-hand side of IS"
          );
          let expr = callExpr(state, "_isNull", [args[0]]);
          if (ast.operator === "is not") {
            expr = makeCall("_not", [expr]);
          }
          return expr;
        } else {
          invariant(
            binaryOperatorToFunction.hasOwnProperty(ast.operator),
            "Could not find function for binary operator:: %s",
            ast.operator
          );
          func = binaryOperatorToFunction[ast.operator];
        }

        return callExpr(state, func, args);
      } else if (ast.type === "list") {
        return _astToExprList(ast.exprs, state, {
          type: "list",
        });
      } else {
        throw new Error("Unhandled ast: " + jsonAst(ast));
      }
    }
  );
}