///|
priv struct Evaluator {
mut activation : Activation
limits : EvalLimits
mut cost : Int
mut depth : Int
mut iterations : Int
}
///|
fn Evaluator::new(activation : Activation, limits : EvalLimits) -> Evaluator {
{ activation, limits, cost: 0, depth: 0, iterations: 0 }
}
///|
fn literal_value(literal : Literal) -> Value {
match literal {
Literal::Null => Value::Null
Literal::Bool(value) => Value::Bool(value)
Literal::Int(value) => Value::Int(value)
Literal::UInt(value) => Value::UInt(value)
Literal::Double(value) => Value::Double(value)
Literal::String(value) => Value::String(value)
Literal::Bytes(value) => Value::Bytes(value)
}
}
///|
fn type_mismatch(
expected : String,
actual : Value,
span : SourceSpan,
) -> Value raise EvalError {
raise TypeMismatch(expected~, actual=actual.kind_name(), span~)
}
///|
fn require_bool(value : Value, span : SourceSpan) -> Bool raise EvalError {
match value {
Value::Bool(result) => result
other => {
ignore(type_mismatch("bool", other, span))
false
}
}
}
///|
fn Evaluator::eval_identifier(
self : Evaluator,
name : String,
span : SourceSpan,
) -> Value raise EvalError {
match self.activation.lookup(name) {
Some(value) => value
None => raise UnboundVariable(name~, span~)
}
}
///|
fn Evaluator::eval_unary(
self : Evaluator,
operator : UnaryOp,
operand : Expr,
span : SourceSpan,
) -> Value raise EvalError {
let value = self.eval_expr(operand)
match (operator, value) {
(UnaryOp::Not, Value::Bool(value)) => Value::Bool(!value)
(UnaryOp::Negate, Value::Int(value)) => safe_negate_int(value, span)
(UnaryOp::Negate, Value::Double(value)) => Value::Double(-value)
(UnaryOp::Not, other) => type_mismatch("bool", other, span)
(UnaryOp::Negate, other) => type_mismatch("int or double", other, span)
}
}
///|
fn Evaluator::eval_logical(
self : Evaluator,
operator : BinaryOp,
left : Expr,
right : Expr,
) -> Value raise EvalError {
let left_value = require_bool(self.eval_expr(left), left.span)
match operator {
BinaryOp::And =>
if !left_value {
Value::Bool(false)
} else {
Value::Bool(require_bool(self.eval_expr(right), right.span))
}
BinaryOp::Or =>
if left_value {
Value::Bool(true)
} else {
Value::Bool(require_bool(self.eval_expr(right), right.span))
}
_ => abort("eval_logical requires && or ||")
}
}
///|
fn compare_int(operator : BinaryOp, left : Int64, right : Int64) -> Bool {
match operator {
BinaryOp::Less => left < right
BinaryOp::LessEqual => left <= right
BinaryOp::Greater => left > right
BinaryOp::GreaterEqual => left >= right
_ => false
}
}
///|
fn compare_uint(operator : BinaryOp, left : UInt64, right : UInt64) -> Bool {
match operator {
BinaryOp::Less => left < right
BinaryOp::LessEqual => left <= right
BinaryOp::Greater => left > right
BinaryOp::GreaterEqual => left >= right
_ => false
}
}
///|
fn compare_double(operator : BinaryOp, left : Double, right : Double) -> Bool {
match operator {
BinaryOp::Less => left < right
BinaryOp::LessEqual => left <= right
BinaryOp::Greater => left > right
BinaryOp::GreaterEqual => left >= right
_ => false
}
}
///|
fn compare_string(operator : BinaryOp, left : String, right : String) -> Bool {
match operator {
BinaryOp::Less => left < right
BinaryOp::LessEqual => left <= right
BinaryOp::Greater => left > right
BinaryOp::GreaterEqual => left >= right
_ => false
}
}
///|
fn eval_relation(
operator : BinaryOp,
left : Value,
right : Value,
span : SourceSpan,
) -> Value raise EvalError {
let result = match (left, right) {
(Value::Int(a), Value::Int(b)) => compare_int(operator, a, b)
(Value::UInt(a), Value::UInt(b)) => compare_uint(operator, a, b)
(Value::Double(a), Value::Double(b)) => compare_double(operator, a, b)
(Value::String(a), Value::String(b)) => compare_string(operator, a, b)
(a, _) => {
ignore(type_mismatch("matching comparable values", a, span))
false
}
}
Value::Bool(result)
}
///|
fn eval_add(
left : Value,
right : Value,
span : SourceSpan,
) -> Value raise EvalError {
match (left, right) {
(Value::Int(a), Value::Int(b)) => safe_add_int(a, b, span)
(Value::UInt(a), Value::UInt(b)) => safe_add_uint(a, b, span)
(Value::Double(a), Value::Double(b)) => Value::Double(a + b)
(Value::String(a), Value::String(b)) => Value::String(a + b)
(Value::Bytes(a), Value::Bytes(b)) => Value::Bytes(concat_bytes(a, b))
(Value::List(a), Value::List(b)) => Value::List(concat_lists(a, b))
(a, _) => type_mismatch("matching addable values", a, span)
}
}
///|
fn eval_subtract(
left : Value,
right : Value,
span : SourceSpan,
) -> Value raise EvalError {
match (left, right) {
(Value::Int(a), Value::Int(b)) => safe_subtract_int(a, b, span)
(Value::UInt(a), Value::UInt(b)) => safe_subtract_uint(a, b, span)
(Value::Double(a), Value::Double(b)) => Value::Double(a - b)
(a, _) => type_mismatch("matching numeric values", a, span)
}
}
///|
fn eval_multiply(
left : Value,
right : Value,
span : SourceSpan,
) -> Value raise EvalError {
match (left, right) {
(Value::Int(a), Value::Int(b)) => safe_multiply_int(a, b, span)
(Value::UInt(a), Value::UInt(b)) => safe_multiply_uint(a, b, span)
(Value::Double(a), Value::Double(b)) => Value::Double(a * b)
(a, _) => type_mismatch("matching numeric values", a, span)
}
}
///|
fn eval_divide(
left : Value,
right : Value,
span : SourceSpan,
) -> Value raise EvalError {
match (left, right) {
(_, Value::UInt(0UL)) | (_, Value::Double(0.0)) =>
raise DivisionByZero(span~)
(Value::Int(a), Value::Int(b)) => safe_divide_int(a, b, span)
(Value::UInt(a), Value::UInt(b)) => Value::UInt(a / b)
(Value::Double(a), Value::Double(b)) => Value::Double(a / b)
(a, _) => type_mismatch("matching numeric values", a, span)
}
}
///|
fn eval_modulo(
left : Value,
right : Value,
span : SourceSpan,
) -> Value raise EvalError {
match (left, right) {
(_, Value::UInt(0UL)) => raise DivisionByZero(span~)
(Value::Int(a), Value::Int(b)) => safe_modulo_int(a, b, span)
(Value::UInt(a), Value::UInt(b)) => Value::UInt(a % b)
(a, _) => type_mismatch("matching integral values", a, span)
}
}
///|
fn Evaluator::eval_binary(
self : Evaluator,
operator : BinaryOp,
left_expr : Expr,
right_expr : Expr,
span : SourceSpan,
) -> Value raise EvalError {
if operator == BinaryOp::And || operator == BinaryOp::Or {
return self.eval_logical(operator, left_expr, right_expr)
}
let left = self.eval_expr(left_expr)
let right = self.eval_expr(right_expr)
match operator {
BinaryOp::Equal => Value::Bool(left == right)
BinaryOp::NotEqual => Value::Bool(left != right)
BinaryOp::Less
| BinaryOp::LessEqual
| BinaryOp::Greater
| BinaryOp::GreaterEqual => eval_relation(operator, left, right, span)
BinaryOp::In => Value::Bool(right.contains(left))
BinaryOp::Add => eval_add(left, right, span)
BinaryOp::Subtract => eval_subtract(left, right, span)
BinaryOp::Multiply => eval_multiply(left, right, span)
BinaryOp::Divide => eval_divide(left, right, span)
BinaryOp::Modulo => eval_modulo(left, right, span)
BinaryOp::And | BinaryOp::Or => abort("logical operators returned early")
}
}
///|
fn Evaluator::eval_conditional(
self : Evaluator,
condition : Expr,
when_true : Expr,
when_false : Expr,
) -> Value raise EvalError {
if require_bool(self.eval_expr(condition), condition.span) {
self.eval_expr(when_true)
} else {
self.eval_expr(when_false)
}
}
///|
fn Evaluator::eval_expr_unmetered(
self : Evaluator,
expr : Expr,
) -> Value raise EvalError {
match expr.kind {
ExprKind::Literal(literal) => literal_value(literal)
ExprKind::Identifier(name) => self.eval_identifier(name, expr.span)
ExprKind::Unary(operator, operand) =>
self.eval_unary(operator, operand, expr.span)
ExprKind::Binary(operator, left, right) =>
self.eval_binary(operator, left, right, expr.span)
ExprKind::Conditional(condition, when_true, when_false) =>
self.eval_conditional(condition, when_true, when_false)
ExprKind::List(items) => self.eval_list(items)
ExprKind::Map(entries) => self.eval_map(entries)
ExprKind::Select(target, field) =>
self.eval_select(target, field, expr.span)
ExprKind::Index(target, index) => self.eval_index(target, index, expr.span)
ExprKind::Call("has", arguments) => self.eval_has(arguments, expr.span)
ExprKind::Call(name, arguments) =>
self.eval_call(name, arguments, expr.span)
ExprKind::ReceiverCall(target, name, arguments) =>
if is_comprehension_macro(name) {
self.eval_comprehension(target, name, arguments, expr.span)
} else {
self.eval_receiver(target, name, arguments, expr.span)
}
}
}
///|
fn Evaluator::eval_expr(self : Evaluator, expr : Expr) -> Value raise EvalError {
self.cost = self.cost + 1
if self.cost > self.limits.max_cost {
raise CostLimitExceeded(limit=self.limits.max_cost, span=expr.span)
}
self.depth = self.depth + 1
if self.depth > self.limits.max_depth {
raise DepthLimitExceeded(limit=self.limits.max_depth, span=expr.span)
}
let result = self.eval_expr_unmetered(expr)
self.depth = self.depth - 1
result
}
///|
fn Evaluator::charge_iteration(
self : Evaluator,
span : SourceSpan,
) -> Unit raise EvalError {
self.iterations = self.iterations + 1
if self.iterations > self.limits.max_iterations {
raise IterationLimitExceeded(limit=self.limits.max_iterations, span~)
}
}
///|
/// Evaluate a compiled expression against an immutable activation.
pub fn Program::eval(
self : Program,
activation : Activation,
limits? : EvalLimits = EvalLimits::default(),
) -> Value raise EvalError {
if !limits.is_valid() {
raise InvalidArgument(
message="evaluation limits must all be positive",
span=self.checked.parsed.root.span,
)
}
Evaluator::new(activation, limits).eval_expr(self.checked.parsed.root)
}