use crate::engine::Engine;
use crate::id::IdRef;
use crate::name_resolution::{self, NameResolveQuery};
use crate::static_args::NodeInstance;
use crate::value::{UValue, Value};
use comemo::TrackedMut;
use decorum::{Real, N32};
use num_traits::{FromPrimitive, ToPrimitive};
use rustre_parser::ast::*;
#[memoize]
pub fn eval_const_node<'a>(
mut engine: TrackedMut<Engine>,
node: ExpressionNode,
instance: Option<NodeInstance>,
) -> Option<Value> {
match node {
ExpressionNode::ConstantNode(node) => {
if node.r#true().is_some() {
return Some(Value::from_bool(true));
}
if node.r#false().is_some() {
return Some(Value::from_bool(false));
}
if node.i_const().is_some() {
return Some(Value::from_int(
node.i_const().unwrap().text().parse().unwrap(),
));
}
if node.r_const().is_some() {
return Some(Value::from_real(
node.r_const().unwrap().text().parse().unwrap(),
));
}
todo!()
}
ExpressionNode::IdentExpressionNode(node) => {
eval_id_ref(engine, node.id_ref_node()?, instance)
}
ExpressionNode::NotExpressionNode(node) => {
let value = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.operand()?,
instance,
)?;
match value.unpack() {
UValue::Bool(value) => Some(Value::from_bool(!value)),
_ => todo!(),
}
}
ExpressionNode::NegExpressionNode(node) => {
let value = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.operand()?,
instance,
)?;
match value.unpack() {
UValue::Int(value) => Some(Value::from_int(-value)),
UValue::Real(value) => Some(Value::from_real(-value)),
_ => todo!(),
}
}
ExpressionNode::PreExpressionNode(_) => todo!(),
ExpressionNode::CurrentExpressionNode(_) => todo!(),
ExpressionNode::IntExpressionNode(node) => {
let value = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.operand()?,
instance,
)?;
match value.unpack() {
UValue::Int(value) => Some(Value::from_int(value)),
UValue::Real(value) => Some(Value::from_int(value.to_i32().unwrap())),
_ => todo!(),
}
}
ExpressionNode::RealExpressionNode(node) => {
let value = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.operand()?,
instance,
)?;
match value.unpack() {
UValue::Int(value) => Some(Value::from_real(N32::from_i32(value).unwrap())),
UValue::Real(value) => Some(Value::from_real(value)),
_ => todo!(),
}
}
ExpressionNode::WhenExpressionNode(_) => todo!(),
ExpressionNode::FbyExpressionNode(_) => todo!(),
ExpressionNode::ArrowExpressionNode(_) => todo!(),
ExpressionNode::AndExpressionNode(node) => {
let left = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.left()?,
instance.clone(),
)?;
let right = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.right()?,
instance,
)?;
match (left.unpack(), right.unpack()) {
(UValue::Bool(left), UValue::Bool(right)) => Some(Value::from_bool(left && right)),
_ => todo!(),
}
}
ExpressionNode::OrExpressionNode(node) => {
let left = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.left()?,
instance.clone(),
)?;
let right = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.right()?,
instance,
)?;
match (left.unpack(), right.unpack()) {
(UValue::Bool(left), UValue::Bool(right)) => Some(Value::from_bool(left || right)),
_ => todo!(),
}
}
ExpressionNode::XorExpressionNode(node) => {
let left = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.left()?,
instance.clone(),
)?;
let right = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.right()?,
instance,
)?;
match (left.unpack(), right.unpack()) {
(UValue::Bool(left), UValue::Bool(right)) => Some(Value::from_bool(left ^ right)),
_ => todo!(),
}
}
ExpressionNode::ImplExpressionNode(node) => {
let left = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.left()?,
instance.clone(),
)?;
let right = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.right()?,
instance,
)?;
match (left.unpack(), right.unpack()) {
(UValue::Bool(left), UValue::Bool(right)) => Some(Value::from_bool(!left || right)),
_ => todo!(),
}
}
ExpressionNode::EqExpressionNode(node) => {
let left = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.left()?,
instance.clone(),
)?;
let right = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.right()?,
instance,
)?;
match (left.unpack(), right.unpack()) {
(UValue::Bool(left), UValue::Bool(right)) => Some(Value::from_bool(left == right)),
(UValue::Int(left), UValue::Int(right)) => Some(Value::from_bool(left == right)),
(UValue::Real(left), UValue::Real(right)) => Some(Value::from_bool(left == right)),
_ => todo!(),
}
}
ExpressionNode::NeqExpressionNode(node) => {
let left = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.left()?,
instance.clone(),
)?;
let right = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.right()?,
instance,
)?;
match (left.unpack(), right.unpack()) {
(UValue::Bool(left), UValue::Bool(right)) => Some(Value::from_bool(left != right)),
(UValue::Int(left), UValue::Int(right)) => Some(Value::from_bool(left != right)),
(UValue::Real(left), UValue::Real(right)) => Some(Value::from_bool(left != right)),
_ => todo!(),
}
}
ExpressionNode::LtExpressionNode(node) => {
let left = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.left()?,
instance.clone(),
)?;
let right = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.right()?,
instance,
)?;
match (left.unpack(), right.unpack()) {
(UValue::Int(left), UValue::Int(right)) => Some(Value::from_bool(left < right)),
(UValue::Real(left), UValue::Real(right)) => Some(Value::from_bool(left < right)),
(UValue::Int(left), UValue::Real(right)) => {
Some(Value::from_bool(N32::from_i32(left).unwrap() < right))
}
(UValue::Real(left), UValue::Int(right)) => {
Some(Value::from_bool(left < (right as f32)))
}
_ => todo!(),
}
}
ExpressionNode::LteExpressionNode(node) => {
let left = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.left()?,
instance.clone(),
)?;
let right = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.right()?,
instance,
)?;
match (left.unpack(), right.unpack()) {
(UValue::Int(left), UValue::Int(right)) => Some(Value::from_bool(left <= right)),
(UValue::Real(left), UValue::Real(right)) => Some(Value::from_bool(left <= right)),
(UValue::Int(left), UValue::Real(right)) => {
Some(Value::from_bool(N32::from_i32(left).unwrap() <= right))
}
(UValue::Real(left), UValue::Int(right)) => {
Some(Value::from_bool(left <= (right as f32)))
}
_ => todo!(),
}
}
ExpressionNode::GtExpressionNode(node) => {
let left = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.left()?,
instance.clone(),
)?;
let right = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.right()?,
instance,
)?;
match (left.unpack(), right.unpack()) {
(UValue::Int(left), UValue::Int(right)) => Some(Value::from_bool(left > right)),
(UValue::Real(left), UValue::Real(right)) => Some(Value::from_bool(left > right)),
(UValue::Int(left), UValue::Real(right)) => {
Some(Value::from_bool(N32::from_i32(left).unwrap() > right))
}
(UValue::Real(left), UValue::Int(right)) => {
Some(Value::from_bool(left > (right as f32)))
}
_ => todo!(),
}
}
ExpressionNode::GteExpressionNode(node) => {
let left = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.left()?,
instance.clone(),
)?;
let right = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.right()?,
instance,
)?;
match (left.unpack(), right.unpack()) {
(UValue::Int(left), UValue::Int(right)) => Some(Value::from_bool(left >= right)),
(UValue::Real(left), UValue::Real(right)) => Some(Value::from_bool(left >= right)),
(UValue::Int(left), UValue::Real(right)) => {
Some(Value::from_bool(N32::from_i32(left).unwrap() >= right))
}
(UValue::Real(left), UValue::Int(right)) => {
Some(Value::from_bool(left >= (right as f32)))
}
_ => todo!(),
}
}
ExpressionNode::DivExpressionNode(node) => {
let left = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.left()?,
instance.clone(),
)?;
let right = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.right()?,
instance,
)?;
match (left.unpack(), right.unpack()) {
(UValue::Int(left), UValue::Int(right)) => Some(Value::from_int(left / right)),
(UValue::Real(left), UValue::Real(right)) => Some(Value::from_real(left / right)),
(UValue::Int(left), UValue::Real(right)) => {
Some(Value::from_real(N32::from_i32(left).unwrap() / right))
}
(UValue::Real(left), UValue::Int(right)) => {
Some(Value::from_real(left / right as f32))
}
_ => todo!(),
}
}
ExpressionNode::ModExpressionNode(node) => {
let left = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.left()?,
instance.clone(),
)?;
let right = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.right()?,
instance,
)?;
match (left.unpack(), right.unpack()) {
(UValue::Int(left), UValue::Int(right)) => Some(Value::from_int(left % right)),
(UValue::Real(left), UValue::Real(right)) => Some(Value::from_real(left % right)),
(UValue::Int(left), UValue::Real(right)) => {
Some(Value::from_real(N32::from_i32(left).unwrap() % right))
}
(UValue::Real(left), UValue::Int(right)) => {
Some(Value::from_real(left % right as f32))
}
_ => todo!(),
}
}
ExpressionNode::SubExpressionNode(node) => {
let left = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.left()?,
instance.clone(),
)?;
let right = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.right()?,
instance,
)?;
match (left.unpack(), right.unpack()) {
(UValue::Int(left), UValue::Int(right)) => Some(Value::from_int(left - right)),
(UValue::Real(left), UValue::Real(right)) => Some(Value::from_real(left - right)),
(UValue::Int(left), UValue::Real(right)) => {
Some(Value::from_real(N32::from_i32(left).unwrap() - right))
}
(UValue::Real(left), UValue::Int(right)) => {
Some(Value::from_real(left - right as f32))
}
_ => todo!(),
}
}
ExpressionNode::AddExpressionNode(node) => {
let left = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.left()?,
instance.clone(),
)?;
let right = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.right()?,
instance,
)?;
match (left.unpack(), right.unpack()) {
(UValue::Int(left), UValue::Int(right)) => Some(Value::from_int(left + right)),
(UValue::Real(left), UValue::Real(right)) => Some(Value::from_real(left + right)),
(UValue::Int(left), UValue::Real(right)) => {
Some(Value::from_real(N32::from_i32(left).unwrap() + right))
}
(UValue::Real(left), UValue::Int(right)) => {
Some(Value::from_real(left + right as f32))
}
_ => todo!(),
}
}
ExpressionNode::MulExpressionNode(node) => {
let left = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.left()?,
instance.clone(),
)?;
let right = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.right()?,
instance,
)?;
match (left.unpack(), right.unpack()) {
(UValue::Int(left), UValue::Int(right)) => Some(Value::from_int(left * right)),
(UValue::Real(left), UValue::Real(right)) => Some(Value::from_real(left * right)),
(UValue::Int(left), UValue::Real(right)) => {
Some(Value::from_real(N32::from_i32(left).unwrap() * right))
}
(UValue::Real(left), UValue::Int(right)) => {
Some(Value::from_real(left * right as f32))
}
_ => todo!(),
}
}
ExpressionNode::PowerExpressionNode(node) => {
let left = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.left()?,
instance.clone(),
)?;
let right = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.right()?,
instance,
)?;
match (left.unpack(), right.unpack()) {
(UValue::Int(left), UValue::Int(right)) => {
Some(Value::from_int(left.pow(
u32::try_from(right).expect("exponent must be positive"),
)))
}
(UValue::Real(left), UValue::Real(right)) => {
Some(Value::from_real(left.powf(right)))
}
(UValue::Int(left), UValue::Real(right)) => {
Some(Value::from_real(N32::from_i32(left).unwrap().powf(right)))
}
(UValue::Real(left), UValue::Int(right)) => {
Some(Value::from_real(left.powf(N32::from_i32(right).unwrap())))
}
_ => todo!(),
}
}
ExpressionNode::IfExpressionNode(node) => {
let cond = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.cond()?,
instance.clone(),
)?;
match cond.unpack() {
UValue::Bool(true) => eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.if_body()?,
instance,
),
UValue::Bool(false) => eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.else_body()?,
instance,
),
_ => todo!(),
}
}
ExpressionNode::WithExpressionNode(node) => {
let cond = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.cond()?,
instance.clone(),
)?;
match cond.unpack() {
UValue::Bool(true) => eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.with_body()?,
instance,
),
UValue::Bool(false) => eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.else_body()?,
instance,
),
_ => todo!(),
}
}
ExpressionNode::HatExpressionNode(node) => {
let left = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.left()?,
instance.clone(),
)?;
let right = eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.right()?,
instance,
)?;
match right.unpack() {
UValue::Int(i) => {
Some(left.repeat(usize::try_from(i).expect("negative array size")))
}
_ => None,
}
}
ExpressionNode::ArrayAccessExpressionNode(_) => todo!(),
ExpressionNode::DieseExpressionNode(_) => todo!(),
ExpressionNode::NorExpressionNode(_) => todo!(),
ExpressionNode::ParExpressionNode(node) => node.expression_node().and_then(|inner| {
eval_const_node(TrackedMut::reborrow_mut(&mut engine), inner, instance)
}),
ExpressionNode::CallByPosExpressionNode(_) => todo!(),
}
}
#[memoize]
pub fn eval_id_ref(
mut engine: TrackedMut<Engine>,
id_ref_node: IdRefNode,
instance: Option<NodeInstance>,
) -> Option<Value> {
let id_ref = IdRef::from(&id_ref_node);
if id_ref.as_package().is_none() {
if let Some(static_args) = instance.as_ref().map(|i| &i.static_args) {
let value = static_args.resolve_const(id_ref.as_member());
if let Some(value) = value {
return Some(value.clone());
}
}
}
let node = name_resolution::resolve_const_expr_node(
TrackedMut::reborrow_mut(&mut engine),
NameResolveQuery {
ident: id_ref_node.member(),
in_node: instance.clone(),
},
);
if let Some(node) = &node {
eval_const_node(
TrackedMut::reborrow_mut(&mut engine),
node.clone(),
instance,
)
} else if let Some(static_args) = instance.as_ref().map(|i| &i.static_args) {
assert!(id_ref.as_package().is_none());
static_args.resolve_const(id_ref.as_member()).cloned()
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::engine::test::SingleFile;
use crate::engine::Engine;
use comemo::{Track, TrackedMut};
#[test]
fn one_plus_one() {
let world = SingleFile::from_source("const x = 1 + 1;");
let mut engine = Engine::new(&world);
let mut engine = engine.track_mut();
let root = crate::all_roots(TrackedMut::reborrow_mut(&mut engine))
.last()
.unwrap()
.clone();
let expr = root
.all_constant_decl_node()
.next()
.unwrap()
.all_one_constant_decl_node()
.next()
.unwrap()
.expression_node()
.unwrap();
let value = eval_const_node(TrackedMut::reborrow_mut(&mut engine), expr, None);
let value = value.as_ref().unwrap();
assert_eq!(*value, Value::from_int(2));
}
#[test]
fn not_true() {
let world = SingleFile::from_source("const x = not true;");
let mut engine = Engine::new(&world);
let mut engine = engine.track_mut();
let root = crate::all_roots(TrackedMut::reborrow_mut(&mut engine))
.last()
.unwrap()
.clone();
let expr = root
.all_constant_decl_node()
.next()
.unwrap()
.all_one_constant_decl_node()
.next()
.unwrap()
.expression_node()
.unwrap();
let value = eval_const_node(TrackedMut::reborrow_mut(&mut engine), expr, None);
let value = value.as_ref().unwrap();
assert_eq!(*value, Value::from_bool(false));
}
#[test]
fn one_plus_x() {
let world = SingleFile::from_source("const x = 1; const y = 2 + x;");
let mut engine = Engine::new(&world);
let mut engine = engine.track_mut();
let root = crate::all_roots(TrackedMut::reborrow_mut(&mut engine))
.last()
.unwrap()
.clone();
let expr = root
.all_constant_decl_node()
.last()
.unwrap()
.all_one_constant_decl_node()
.next()
.unwrap()
.expression_node()
.unwrap();
let value = eval_const_node(TrackedMut::reborrow_mut(&mut engine), expr, None);
let value = value.as_ref().unwrap();
assert_eq!(*value, Value::from_int(3));
}
#[test]
fn if_true() {
let world = SingleFile::from_source("const x = true; const y = if x then 1 else 2;");
let mut engine = Engine::new(&world);
let mut engine = engine.track_mut();
let root = crate::all_roots(TrackedMut::reborrow_mut(&mut engine))
.last()
.unwrap()
.clone();
let expr = root
.all_constant_decl_node()
.last()
.unwrap()
.all_one_constant_decl_node()
.next()
.unwrap()
.expression_node()
.unwrap();
let value = eval_const_node(TrackedMut::reborrow_mut(&mut engine), expr, None);
let value = value.as_ref().unwrap();
assert_eq!(*value, Value::from_int(1));
}
#[test]
fn if_false() {
let world = SingleFile::from_source("const x = false; const y = if x then 1 else 2;");
let mut engine = Engine::new(&world);
let mut engine = engine.track_mut();
let root = crate::all_roots(TrackedMut::reborrow_mut(&mut engine))
.last()
.unwrap()
.clone();
let expr = root
.all_constant_decl_node()
.last()
.unwrap()
.all_one_constant_decl_node()
.next()
.unwrap()
.expression_node()
.unwrap();
let value = eval_const_node(TrackedMut::reborrow_mut(&mut engine), expr, None);
let value = value.as_ref().unwrap();
assert_eq!(*value, Value::from_int(2));
}
}