package io.gitlab.jfronny.muscript.ast.context; import io.gitlab.jfronny.commons.StringFormatter; import io.gitlab.jfronny.muscript.ast.*; import io.gitlab.jfronny.muscript.ast.bool.BoolAssign; import io.gitlab.jfronny.muscript.ast.bool.BoolConditional; import io.gitlab.jfronny.muscript.ast.bool.BoolLiteral; import io.gitlab.jfronny.muscript.ast.bool.BoolUnpack; import io.gitlab.jfronny.muscript.ast.dynamic.*; import io.gitlab.jfronny.muscript.ast.number.NumberAssign; import io.gitlab.jfronny.muscript.ast.number.NumberConditional; import io.gitlab.jfronny.muscript.ast.number.NumberLiteral; import io.gitlab.jfronny.muscript.ast.number.NumberUnpack; import io.gitlab.jfronny.muscript.ast.string.*; import java.util.Set; public class ExprUtils { public static DynamicExpr asDynamic(Expr expression) { expression = unpack(expression); return expression instanceof DynamicExpr dynamic ? dynamic : new DynamicCoerce(expression); } public static BoolExpr asBool(Expr expression) { return switch (unpack(expression)) { case BoolExpr bool -> bool; case DynamicConditional(var location, var condition, var ifTrue, var ifFalse) -> new BoolConditional(location, condition, asBool(ifTrue), asBool(ifFalse)); case DynamicAssign(var location, var left, var right) -> new BoolAssign(location, left, asBool(right)); case DynamicExpr dynamic -> new BoolUnpack(dynamic); case NullLiteral nl -> throw new TypeMismatchException("Expected boolean expression but got null"); case StringExpr string -> throw new TypeMismatchException("Expected boolean expression but got string"); case NumberExpr number -> throw new TypeMismatchException("Expected boolean expression but got number"); }; } public static NumberExpr asNumber(Expr expression) { return switch (unpack(expression)) { case NumberExpr number -> number; case DynamicConditional(var location, var condition, var ifTrue, var ifFalse) -> new NumberConditional(location, condition, asNumber(ifTrue), asNumber(ifFalse)); case DynamicAssign(var location, var left, var right) -> new NumberAssign(location, left, asNumber(right)); case DynamicExpr dynamic -> new NumberUnpack(dynamic); case NullLiteral nl -> throw new TypeMismatchException("Expected number expression but got null"); case StringExpr string -> throw new TypeMismatchException("Expected number expression but got string"); case BoolExpr bool -> throw new TypeMismatchException("Expected number expression but got boolean"); }; } public static StringExpr asString(Expr expression) { return switch (unpack(expression)) { case StringExpr string -> string; case NumberLiteral number -> new StringLiteral(expression.location(), StringFormatter.toString(number.value())); case BoolLiteral bool -> new StringLiteral(expression.location(), bool.value() ? "true" : "false"); case NullLiteral nl -> new StringLiteral(expression.location(), "null"); case DynamicConditional(var location, var condition, var ifTrue, var ifFalse) -> new StringConditional(location, condition, asString(ifTrue), asString(ifFalse)); case BoolConditional(var location, var condition, var ifTrue, var ifFalse) -> new StringConditional(location, condition, asString(ifTrue), asString(ifFalse)); case NumberConditional(var location, var condition, var ifTrue, var ifFalse) -> new StringConditional(location, condition, asString(ifTrue), asString(ifFalse)); case DynamicAssign(var location, var left, var right) -> new StringAssign(location, left, asString(right)); case DynamicExpr dynamic -> new StringUnpack(dynamic); default -> new StringCoerce(expression); }; } public static Expr unpack(Expr expression) { return switch (expression) { case null -> throw new NullPointerException(); case BoolUnpack(var inner) -> unpack(inner); case NumberUnpack(var inner) -> unpack(inner); case StringUnpack(var inner) -> unpack(inner); case DynamicCoerce(var inner) -> unpack(inner); case StringCoerce(var inner) -> unpack(inner); default -> expression; }; } private static final Set allowedVariables = Set.of("date", "time", "enum", "listOf"); public static boolean isDirect(Expr expr) { return switch (expr) { case NullLiteral ignored -> true; case StringLiteral ignored -> true; case NumberLiteral ignored -> true; case BoolLiteral ignored -> true; case DynamicLiteral(var location, var value) -> value.isDirect(); case DynamicCoerce(var inner) -> isDirect(inner); case ObjectLiteral(var location, var content) -> content.values().stream().allMatch(ExprUtils::isDirect); case Concatenate(var location, var left, var right) -> isDirect(left) && isDirect(right); case BoolUnpack(var inner) -> isDirect(inner); case NumberUnpack(var inner) -> isDirect(inner); case StringUnpack(var inner) -> isDirect(inner); case Bind(var location, var callable, var parameter) -> isDirect(callable) && isDirect(parameter); case Call(var location, var left, var args) -> isDirect(left) && args.stream().allMatch(a -> !a.variadic() && isDirect(a.value())); case Variable(var location, var name) -> allowedVariables.contains(name); default -> false; }; } }