#[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec};
use hashbrown::HashMap;
use crate::ast::{BinOp, BuiltinFn, Expr};
use crate::error::CompileError;
#[derive(Debug, Clone)]
pub enum Instruction {
LoadConst(f64),
LoadCurrent,
LoadVar(usize),
Negate,
BinOp(BinOp),
Call(BuiltinFn),
}
#[derive(Debug, Clone)]
pub struct CompiledExpr {
pub(crate) instructions: Vec<Instruction>,
pub(crate) variable_indices: HashMap<String, usize>,
pub(crate) uses_current: bool,
}
impl CompiledExpr {
pub fn compile(expr: &Expr, var_names: &[&str]) -> Result<Self, CompileError> {
let variable_indices: HashMap<String, usize> = var_names
.iter()
.enumerate()
.map(|(i, &name)| (String::from(name), i))
.collect();
let mut instructions = Vec::new();
let mut uses_current = false;
Self::compile_expr(
expr,
&variable_indices,
&mut instructions,
&mut uses_current,
)?;
Ok(CompiledExpr {
instructions,
variable_indices,
uses_current,
})
}
fn compile_expr(
expr: &Expr,
var_indices: &HashMap<String, usize>,
instructions: &mut Vec<Instruction>,
uses_current: &mut bool,
) -> Result<(), CompileError> {
match expr {
Expr::Number(n) => {
instructions.push(Instruction::LoadConst(*n));
}
Expr::CurrentValue => {
*uses_current = true;
instructions.push(Instruction::LoadCurrent);
}
Expr::Variable(name) => {
if let Some(&idx) = var_indices.get(name) {
instructions.push(Instruction::LoadVar(idx));
} else {
return Err(CompileError::UndefinedVariable(name.clone()));
}
}
Expr::UnaryMinus(e) => {
Self::compile_expr(e, var_indices, instructions, uses_current)?;
instructions.push(Instruction::Negate);
}
Expr::BinaryOp { op, left, right } => {
Self::compile_expr(left, var_indices, instructions, uses_current)?;
Self::compile_expr(right, var_indices, instructions, uses_current)?;
instructions.push(Instruction::BinOp(*op));
}
Expr::FunctionCall { name, args } => {
if let Some((func, _arity)) = BuiltinFn::from_name(name) {
func.check_arity(args.len(), name)?;
for arg in args {
Self::compile_expr(arg, var_indices, instructions, uses_current)?;
}
instructions.push(Instruction::Call(func));
} else {
return Err(CompileError::UnknownFunction(name.clone()));
}
}
}
Ok(())
}
pub fn num_variables(&self) -> usize {
self.variable_indices.len()
}
pub fn uses_current_value(&self) -> bool {
self.uses_current
}
#[cfg(feature = "std")]
pub fn variable_names(&self) -> Vec<&str> {
let mut names: Vec<_> = self.variable_indices.iter().collect();
names.sort_by_key(|(_, &idx)| idx);
names.into_iter().map(|(name, _)| name.as_str()).collect()
}
}