Class CompileTimeConstraintEvaluator
ConstraintExpr against a compile-time-constant subject value, returning a
tri-state verdict. Used in phase 5 to turn a guaranteed runtime constraint Panic into a compile error
when the constructor argument is a literal.
This is a tiny reflective interpreter over the real EK9 standard library. It constructs the
subject and each constraint literal as actual org.ek9.lang.* objects via their _of(String)
factory — the same factory the bytecode generator emits (see
LiteralBytecodeGenerator) — then invokes the real EK9 operator (resolved through
OperatorMap, e.g. >= → _gteq). Because object construction and operator
evaluation reuse the exact stdlib code the runtime uses, a compile-time verdict is byte-for-byte
faithful to runtime behaviour for any constrainable base type (Integer, Float, Date, Time,
DateTime, Duration, Money, Dimension, Colour, Character, String/RegEx, …) — there is no per-type
special-casing.
AND/OR combine the leaf verdicts with three-valued (Kleene) logic; parentheses are transparent in the model. Only the leaf comparison is reflective; the boolean algebra stays in plain Java.
Deliberately CONSERVATIVE: it returns CompileTimeConstraintEvaluator.Verdict.FAIL only when it can prove the
constraint is violated. Anything it cannot fully evaluate — a type that will not load, a literal that
will not construct (or constructs to an unset value), an operator with no resolvable method, an
operator result that is unset, or any reflective failure — yields CompileTimeConstraintEvaluator.Verdict.UNKNOWN, and the
caller must NOT emit an error (the runtime check still applies). Crucially, because subject and literal
are built with the same _of the runtime uses, the evaluator can never compute a verdict that
diverges from runtime, so it can never raise a false-positive compile error on valid code.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic enumPASS = constraint satisfied; FAIL = provably violated; UNKNOWN = cannot determine statically. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionevaluate(String subjectText, String subjectTypeFqn, String promoteToTypeFqn, ConstraintExpr constraint) As above, but first applies the type's#^PROMOTION when the argument's own type is not the constrained base type.evaluate(String subjectText, String subjectTypeFqn, ConstraintExpr constraint) Evaluate the constraint against a constant subject.steppedLiteral(String text, String typeFqn, boolean increment) The literal text oftext's immediate neighbour, obtained by invoking the type's OWN++/--operator, or null when the type declares none or the result will not round-trip.
-
Constructor Details
-
CompileTimeConstraintEvaluator
public CompileTimeConstraintEvaluator()
-
-
Method Details
-
evaluate
public CompileTimeConstraintEvaluator.Verdict evaluate(String subjectText, String subjectTypeFqn, ConstraintExpr constraint) Evaluate the constraint against a constant subject.- Parameters:
subjectText- the literal source text of the constructor argument (e.g. "15")subjectTypeFqn- the argument's fully qualified type (e.g. "org.ek9.lang::Integer")constraint- the constraint model (may be null)- Returns:
- the verdict; only FAIL should trigger a compile error
-
evaluate
public CompileTimeConstraintEvaluator.Verdict evaluate(String subjectText, String subjectTypeFqn, String promoteToTypeFqn, ConstraintExpr constraint) As above, but first applies the type's#^PROMOTION when the argument's own type is not the constrained base type.🔑 Without this the subject is built as its OWN type and compared against a constraint written in the BASE type, so the reflective comparison never resolves and every such site fell through to a runtime Panic.
Ratio(2)forRatio as Float constrain as >= 0.0 and <= 1.0compiled cleanly and Panicked, while the identicalRatio(2.0)was correctly rejected — the same violation caught or missed purely by how the literal was spelled.- Parameters:
promoteToTypeFqn- the type to promote the built subject to before evaluating, or null for none. A type declares at most ONE promotion, so this is a single, unambiguous step; the caller establishes it viaTypeCoercions.promotionTarget, which is THE accessor for it.
-
steppedLiteral
The literal text oftext's immediate neighbour, obtained by invoking the type's OWN++/--operator, or null when the type declares none or the result will not round-trip.🔑 Why this lives here. Stepping a value needs exactly the machinery this class already owns — build via
_of(String), resolve an operator throughOperatorMap, invoke it reflectively — and a second copy elsewhere would be free to drift from the evaluator about what a literal means. It is also what keeps the caller type-AGNOSTIC: aDateneighbour is the next day and aCharacterneighbour is the next character because those types say so, not because anything here knows about calendars or code points.The result is rendered back to text with
$(_string) so it can be re-probed and emitted as source. A type whose$does not round-trip through its_ofsimply yields a candidate that fails the next evaluation and is dropped — callers must verify anyway, so an imperfect round-trip costs a wasted probe and never a wrong value.- Parameters:
text- the literal source text to step fromtypeFqn- the value's fully qualified typeincrement- true for++, false for--- Returns:
- the neighbour's literal text, or null when it cannot be produced
-