145 lines
4.5 KiB
Rust
145 lines
4.5 KiB
Rust
mod value;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use anyhow::Context;
|
|
pub use value::Value;
|
|
|
|
use crate::ast::BinOp;
|
|
use crate::ast::typed::Type;
|
|
use crate::representation::intermediate::{IntermediateExpr, IntermediateValue};
|
|
use crate::symbols::{Symbol, SymbolsTable};
|
|
|
|
pub struct Interpreter<I> {
|
|
exprs: Vec<IntermediateExpr>,
|
|
symbols: SymbolsTable,
|
|
initializer: I,
|
|
}
|
|
|
|
type Variables = HashMap<Symbol, Value>;
|
|
|
|
impl<I> Interpreter<I>
|
|
where
|
|
I: Fn(&Type, &str) -> Value,
|
|
{
|
|
pub fn new(exprs: Vec<IntermediateExpr>, symbols: SymbolsTable, initializer: I) -> Self {
|
|
Self {
|
|
symbols,
|
|
exprs,
|
|
initializer,
|
|
}
|
|
}
|
|
|
|
pub fn run(&mut self) -> anyhow::Result<Option<Value>> {
|
|
let mut variables = self.initialize_variables()?;
|
|
let mut last_value = None;
|
|
|
|
for expr in &self.exprs {
|
|
let (result, value) = match expr {
|
|
IntermediateExpr::IntToFloat { result, value } => {
|
|
let value = Self::intermediate_value(&variables, value)?;
|
|
(result, self.int2float(value))
|
|
},
|
|
IntermediateExpr::BinOp {
|
|
result,
|
|
lhs,
|
|
op,
|
|
rhs,
|
|
} => {
|
|
let lhs = Self::intermediate_value(&variables, lhs)?;
|
|
let rhs = Self::intermediate_value(&variables, rhs)?;
|
|
(result, self.bin_op(lhs, op, rhs)?)
|
|
},
|
|
IntermediateExpr::Identity { result, value } => {
|
|
let value = Self::intermediate_value(&variables, value)?;
|
|
(result, self.identity(value))
|
|
},
|
|
};
|
|
|
|
Self::store(&mut variables, result, value)?;
|
|
last_value = Some(value);
|
|
}
|
|
|
|
Ok(last_value)
|
|
}
|
|
|
|
fn initialize_variables(&self) -> anyhow::Result<Variables> {
|
|
let mut variables = Variables::new();
|
|
for (_, data) in &self.symbols {
|
|
if data.ty.is_none() {
|
|
anyhow::bail!("type not found for '{}'", data.name);
|
|
}
|
|
|
|
if !data.temporary {
|
|
variables.insert(data.id, (self.initializer)(&data.ty.unwrap(), &data.name));
|
|
} else {
|
|
variables.insert(data.id, match data.ty {
|
|
None => continue,
|
|
Some(Type::Int) => Value::Int(0),
|
|
Some(Type::Float) => Value::Float(0.0),
|
|
});
|
|
};
|
|
}
|
|
|
|
Ok(variables)
|
|
}
|
|
|
|
fn int2float(&self, value: Value) -> Value {
|
|
match value {
|
|
Value::Int(v) => Value::Float(v as f64),
|
|
_ => value,
|
|
}
|
|
}
|
|
|
|
fn identity(&self, value: Value) -> Value {
|
|
value
|
|
}
|
|
|
|
fn bin_op(&self, lhs: Value, op: &BinOp, rhs: Value) -> anyhow::Result<Value> {
|
|
match (lhs, rhs) {
|
|
(Value::Int(lhs), Value::Int(rhs)) => Ok(Value::Int(match op {
|
|
BinOp::Add => lhs + rhs,
|
|
BinOp::Sub => lhs - rhs,
|
|
BinOp::Mul => lhs * rhs,
|
|
BinOp::Div => lhs / rhs,
|
|
})),
|
|
(Value::Float(lhs), Value::Float(rhs)) => Ok(Value::Float(match op {
|
|
BinOp::Add => lhs + rhs,
|
|
BinOp::Sub => lhs - rhs,
|
|
BinOp::Mul => lhs * rhs,
|
|
BinOp::Div => lhs / rhs,
|
|
})),
|
|
_ => Err(anyhow::anyhow!("invalid types for binary operation")),
|
|
}
|
|
}
|
|
|
|
fn intermediate_value(
|
|
variables: &Variables,
|
|
value: &IntermediateValue,
|
|
) -> anyhow::Result<Value> {
|
|
Ok(match value {
|
|
IntermediateValue::Int { value } => Value::Int(*value),
|
|
IntermediateValue::Float { value } => Value::Float(*value),
|
|
IntermediateValue::Var { name } => Self::load(variables, name)?,
|
|
})
|
|
}
|
|
|
|
fn load(variables: &Variables, symbol: &Symbol) -> anyhow::Result<Value> {
|
|
let symbol_value = variables.get(symbol).context("no such variable")?;
|
|
|
|
Ok(*symbol_value)
|
|
}
|
|
|
|
fn store(variables: &mut Variables, symbol: &Symbol, value: Value) -> anyhow::Result<()> {
|
|
let symbol_value = variables.get_mut(symbol).context("no such variable")?;
|
|
|
|
match (symbol_value, value) {
|
|
(Value::Int(v), Value::Int(a)) => *v = a,
|
|
(Value::Float(v), Value::Float(a)) => *v = a,
|
|
_ => return Err(anyhow::anyhow!("invalid types for assignment")),
|
|
};
|
|
|
|
Ok(())
|
|
}
|
|
}
|