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));
}
}
);
}