ayin/src/interpret/types.rs
2025-12-26 08:59:13 +02:00

239 lines
8 KiB
Rust

//! Types used in the interpreter.
use crate::ast;
use std::backtrace::Backtrace;
use std::collections::BTreeMap;
use std::collections::HashMap;
#[derive(Debug)]
pub enum Error {
DuplicateEnvNames(ast::EnvName, Backtrace),
DuplicateNames(ast::Name, Backtrace),
NameNotFound(ast::Name, Env, Backtrace),
IndexOutOfBounds(usize, Backtrace),
FieldNotFound(ast::Label, Backtrace),
EnvNotFound(ast::EnvName, Backtrace),
LastStatementNotAnExpr(Backtrace),
NotAFunction(ast::Expr, Backtrace),
NotARecord(ast::Value, Backtrace),
NotAReference(ast::Expr, ast::Value, Backtrace),
NotABoolean(ast::Value, Backtrace),
NotAnInt(ast::Value, Backtrace),
OpError(ast::Value, ast::Op, ast::Value, Backtrace),
MigrationError(ast::Value, String, Backtrace),
ArgumentsMismatch(Backtrace),
}
impl PartialEq for Error {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Error::DuplicateEnvNames(a1, _), Error::DuplicateEnvNames(a2, _)) => a1 == a2,
(Error::DuplicateNames(a1, _), Error::DuplicateNames(a2, _)) => a1 == a2,
(Error::IndexOutOfBounds(a1, _), Error::IndexOutOfBounds(a2, _)) => a1 == a2,
(Error::NameNotFound(a1, _, _), Error::NameNotFound(a2, _, _)) => a1 == a2,
(Error::FieldNotFound(a1, _), Error::FieldNotFound(a2, _)) => a1 == a2,
(Error::EnvNotFound(a1, _), Error::EnvNotFound(a2, _)) => a1 == a2,
(Error::LastStatementNotAnExpr(_), Error::LastStatementNotAnExpr(_)) => true,
(Error::NotAFunction(a1, _), Error::NotAFunction(a2, _)) => a1 == a2,
(Error::NotARecord(a1, _), Error::NotARecord(a2, _)) => a1 == a2,
(Error::NotAReference(a1, b1, _), Error::NotAReference(a2, b2, _)) => {
a1 == a2 && b1 == b2
}
(Error::NotABoolean(a1, _), Error::NotABoolean(a2, _)) => a1 == a2,
(Error::NotAnInt(a1, _), Error::NotAnInt(a2, _)) => a1 == a2,
(Error::MigrationError(a1, b1, _), Error::MigrationError(a2, b2, _)) => {
a1 == a2 && b1 == b2
}
(Error::ArgumentsMismatch(_), Error::ArgumentsMismatch(_)) => true,
_ => false,
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::DuplicateEnvNames(a, b) => {
write!(
f,
"Closure generation failed. Duplicate env names: {a:?}\n{b}"
)
}
Error::DuplicateNames(a, b) => write!(f, "Duplicate names: {a:?}\n{b}"),
Error::IndexOutOfBounds(a, b) => write!(f, "Index out of bounds: {a:?}\n{b}"),
Error::NameNotFound(a, e, b) => write!(
f,
"Name not found: {a:?} in {:#?}\n{b}",
e.env.keys().collect::<Vec<_>>()
),
Error::FieldNotFound(a, b) => write!(f, "Field not found: {a:?}\n{b}"),
Error::EnvNotFound(a, b) => write!(f, "Env not found: {a:?}\n{b}"),
Error::LastStatementNotAnExpr(a) => {
write!(f, "Last statement is not an expression\n{a}")
}
Error::NotAFunction(a, b) => write!(f, "Not a function {a:?}\n{b}"),
Error::NotARecord(a, b) => write!(f, "Not a record {a:?}\n{b}"),
Error::NotAReference(a, b, c) => write!(
f,
"Not a reference. Are you missing a `mut`? {a:?} {b:?}\n{c}"
),
Error::NotABoolean(a, b) => write!(f, "Not a boolean {a:?}\n{b}"),
Error::NotAnInt(a, b) => write!(f, "Not an integer {a:?}\n{b}"),
Error::ArgumentsMismatch(a) => write!(f, "Arguments mismatch\n{a}"),
Error::OpError(a, b, c, d) => write!(f, "Op Error {a:?} {b:?} {c:?}\n{d}"),
Error::MigrationError(a, b, c) => {
write!(f, "Migration error for:\n{a:#?}\n{b:#?}\n{c}")
}
}
}
}
impl From<ast::Error> for Error {
fn from(error: ast::Error) -> Error {
match error {
ast::Error::LabelNotFound(l) => Error::FieldNotFound(l, Backtrace::capture()),
ast::Error::IndexOutOfBounds(i) => Error::IndexOutOfBounds(i, Backtrace::capture()),
}
}
}
#[derive(PartialEq, Debug, Clone)]
struct Namer {
counter: u64,
}
impl Namer {
fn new() -> Namer {
Namer { counter: 0 }
}
pub fn next(&mut self) -> u64 {
self.counter += 1;
self.counter
}
}
pub struct State {
pub name: String,
env_namer: Namer,
pub envs: Envs,
pub variables: Variables,
pub primitive_funcs: PrimitiveFuncs,
}
pub struct PrimitiveFuncs {
pub map: HashMap<ast::Name, PrimFunc>,
}
pub type PrimFunc = fn(Vec<ast::Expr>) -> ast::Value;
impl PrimitiveFuncs {
pub fn new(prims: Vec<(&str, PrimFunc)>) -> Self {
let mut map: HashMap<ast::Name, PrimFunc> = HashMap::new();
for (key, func) in prims.into_iter() {
map.insert(key.into(), func);
}
PrimitiveFuncs { map }
}
pub fn get(&self, name: &ast::Name) -> Option<&PrimFunc> {
self.map.get(name)
}
}
pub struct Variables {
pub vars: HashMap<ast::Ref, ast::Value>,
namer: Namer,
}
impl Variables {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Variables {
vars: HashMap::new(),
namer: Namer::new(),
}
}
pub fn insert(&mut self, value: ast::Value) -> ast::Ref {
let name = ast::Ref(self.namer.next());
self.vars.insert(name.clone(), value);
name
}
pub fn get<'a>(&'a self, reference: &ast::Ref) -> &'a ast::Value {
self.vars.get(reference).unwrap()
}
pub fn set(&mut self, reference: ast::Ref, new_value: ast::Value) {
self.vars.insert(reference, new_value);
}
}
impl State {
pub fn new(name: String, primitive_funcs: PrimitiveFuncs) -> State {
State {
name,
env_namer: Namer::new(),
envs: Envs(BTreeMap::new()),
variables: Variables::new(),
primitive_funcs,
}
}
pub fn generate_env(&mut self, env: Env) -> Result<ast::EnvName, Error> {
let env_name = ast::EnvName(format!("{}_{}", self.name, self.env_namer.next()));
self.envs.insert(&env_name, env)?;
Ok(env_name)
}
}
#[derive(PartialEq, Debug, Clone)]
pub struct Env {
env: BTreeMap<ast::Name, ast::Value>,
pub name: ast::EnvName,
}
impl Env {
pub fn new(name: ast::EnvName) -> Env {
Env {
env: BTreeMap::new(),
name,
}
}
pub fn get(&self, name: &ast::Name) -> Result<&ast::Value, Error> {
match self.env.get(name) {
None => Err(Error::NameNotFound(
name.clone(),
self.clone(),
Backtrace::capture(),
)),
Some(v) => Ok(v),
}
}
pub fn insert(&mut self, name: ast::Name, value: ast::Value) {
self.env.insert(name.clone(), value);
}
pub fn insert_nodup(&mut self, name: &ast::Name, value: ast::Value) -> Result<(), Error> {
if self.env.insert(name.clone(), value).is_some() {
Err(Error::DuplicateNames(name.clone(), Backtrace::capture()))
} else {
Ok(())
}
}
}
#[derive(PartialEq, Debug, Clone)]
pub struct Envs(pub BTreeMap<ast::EnvName, Env>);
impl Envs {
pub fn get(&self, env_name: &ast::EnvName) -> Result<&Env, Error> {
self.0
.get(env_name)
.ok_or(Error::EnvNotFound(env_name.clone(), Backtrace::capture()))
}
pub fn get_mut(&mut self, env_name: &ast::EnvName) -> Result<&mut Env, Error> {
self.0
.get_mut(env_name)
.ok_or(Error::EnvNotFound(env_name.clone(), Backtrace::capture()))
}
pub fn insert(&mut self, name: &ast::EnvName, env: Env) -> Result<(), Error> {
if self.0.insert(name.clone(), env).is_some() {
Err(Error::DuplicateEnvNames(name.clone(), Backtrace::capture()))
} else {
Ok(())
}
}
}