///|
/// Compile a MoonRule expression into an immutable program.
pub fn compile(source : String) -> Result[Program, Diagnostic] {
compile_with_limits(source, CompileLimits::default())
}
///|
/// Compile an expression while enforcing limits suitable for untrusted input.
pub fn compile_with_limits(
source : String,
limits : CompileLimits,
) -> Result[Program, Diagnostic] {
match validate_compile_limits(limits) {
Err(diagnostic) => return Err(diagnostic)
Ok(_) => ()
}
if source.length() > limits.max_source_length {
return Err(
Diagnostic::new(
Parse,
"P020",
"rule source exceeds the configured length limit",
Span::new(0, source.length()),
hint="Shorten the expression or raise max_source_length explicitly.",
),
)
}
let parsed : Result[Program, Diagnostic] = Ok(parse_program(source)) catch {
RuleFailure(diagnostic) => Err(diagnostic)
}
match parsed {
Err(diagnostic) => Err(diagnostic)
Ok(program) =>
if program.node_count() > limits.max_ast_nodes {
Err(
Diagnostic::new(
Parse,
"P021",
"rule AST exceeds the configured node limit",
program.span(),
hint="Simplify the expression or raise max_ast_nodes explicitly.",
),
)
} else if program.ast_depth() > limits.max_ast_depth {
Err(
Diagnostic::new(
Parse,
"P022",
"rule AST exceeds the configured depth limit",
program.span(),
hint="Reduce expression nesting or raise max_ast_depth explicitly.",
),
)
} else {
Ok(program)
}
}
}
///|
/// Evaluate a compiled program against a JSON context.
pub fn evaluate(program : Program, context : Json) -> Result[Json, Diagnostic] {
evaluate_with_limits(program, context, EvaluationLimits::default())
}
///|
/// Evaluate a compiled program with an explicit execution-step budget.
pub fn evaluate_with_limits(
program : Program,
context : Json,
limits : EvaluationLimits,
) -> Result[Json, Diagnostic] {
match validate_evaluation_limits(limits) {
Err(diagnostic) => return Err(diagnostic)
Ok(_) => ()
}
let budget = EvaluationBudget::new(limits)
Ok(evaluate_expr(program.root, context, budget)) catch {
RuleFailure(diagnostic) => Err(diagnostic)
}
}
///|
/// Compile and evaluate a rule that must produce a boolean decision.
pub fn check(source : String, context : Json) -> Result[Bool, Diagnostic] {
match compile(source) {
Err(diagnostic) => Err(diagnostic)
Ok(program) =>
match evaluate(program, context) {
Err(diagnostic) => Err(diagnostic)
Ok(True) => Ok(true)
Ok(False) => Ok(false)
Ok(other) =>
Err(
Diagnostic::new(
Evaluate,
"E022",
"rule returned \{value_kind(other)} instead of boolean",
program.span(),
hint="Finish the expression with a comparison or boolean operator.",
),
)
}
}
}