use std::collections::HashMap;
use anyhow::{bail, Context as AnyhowContext, Result};
use crate::{
ast::{BinOp, Column, Expr, UnOp},
value::Value,
};
#[derive(Clone, Debug)]
pub struct Context {
bindings: Vec<Value>,
values: Vec<Value>,
col_to_idx: HashMap<Column, usize>,
idx_to_col: Vec<Column>,
}
impl Context {
pub fn new(bindings: Vec<Value>) -> Context {
Context {
bindings,
values: Vec::new(),
col_to_idx: HashMap::new(),
idx_to_col: Vec::new(),
}
}
pub fn set(&mut self, col: Column, value: Value) {
let this_id = self.values.len();
self.values.push(value);
self.idx_to_col.push(col.clone());
self.col_to_idx.insert(col, this_id);
}
pub fn get(&self, col: &Column) -> Option<Value> {
Some(self.values[*self.col_to_idx.get(col)?].clone())
}
pub fn eval(&self, expr: &Expr) -> Result<Value> {
Ok(match expr {
Expr::Literal(v) => v.clone(),
Expr::Binding(b) => self
.bindings
.get(*b as usize)
.cloned()
.with_context(|| format!("binding out of range: {b}"))?,
Expr::Column(col) => self
.get(&col)
.with_context(|| format!("column {col} not found"))?,
Expr::Bin(lhs, op, rhs) => match (self.eval(lhs)?, op, self.eval(rhs)?) {
(Value::Null, BinOp::Eq, Value::Null) => Value::Bool(true),
(lhs, BinOp::Eq, rhs) => Value::Bool(lhs == rhs),
(lhs, BinOp::NEq, rhs) => Value::Bool(lhs != rhs),
(Value::Null, BinOp::Gt, Value::Null) => Value::Bool(false),
(lhs, BinOp::Gt, rhs) => Value::Bool(lhs > rhs),
(lhs, BinOp::GtEq, rhs) => Value::Bool(lhs >= rhs),
(lhs, BinOp::Lt, rhs) => Value::Bool(lhs < rhs),
(lhs, BinOp::LtEq, rhs) => Value::Bool(lhs <= rhs),
(Value::Bool(lhs), BinOp::And, Value::Bool(rhs)) => Value::Bool(lhs && rhs),
(Value::Bool(lhs), BinOp::Or, Value::Bool(rhs)) => Value::Bool(lhs || rhs),
(Value::Int(lhs), BinOp::Add, Value::Int(rhs)) => Value::Int(lhs + rhs),
(Value::Int(lhs), BinOp::Sub, Value::Int(rhs)) => Value::Int(lhs - rhs),
(Value::Int(lhs), BinOp::Mul, Value::Int(rhs)) => Value::Int(lhs * rhs),
(Value::Int(lhs), BinOp::Div, Value::Int(rhs)) => Value::Int(lhs / rhs),
(Value::Int(lhs), BinOp::Mod, Value::Int(rhs)) => Value::Int(lhs % rhs),
(lhs, BinOp::And, rhs)
| (lhs, BinOp::Or, rhs)
| (lhs, BinOp::Add, rhs)
| (lhs, BinOp::Sub, rhs)
| (lhs, BinOp::Mul, rhs)
| (lhs, BinOp::Div, rhs)
| (lhs, BinOp::Mod, rhs) => {
bail!("the {op} operator is not defined for values {lhs} and {rhs}")
}
},
Expr::Unary(op, rhs) => match (op, self.eval(rhs)?) {
(UnOp::Not, Value::Bool(rhs)) => Value::Bool(!rhs),
(UnOp::Not, _) => {
bail!("the {op} operator is not defined for value {rhs}")
}
},
Expr::Edge(_, _, _, e) => self.eval(e)?,
})
}
pub fn as_slice(&self) -> &[Value] {
&self.values
}
}