diff --git a/Makefile b/Makefile index 0bf8477..4a46cca 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ test: .PHONY: run run: - cargo run -- game.ayin + RUST_BACKTRACE=1 cargo run -- game.ayin .PHONY: review review: diff --git a/game.ayin b/game.ayin index 5790989..b771c25 100644 --- a/game.ayin +++ b/game.ayin @@ -10,8 +10,8 @@ let update = fn(state, input) { return state; }; -let draw = fn(frame, state) { - frame.clear(0,0,0); +let draw = fn(state) { + frame_clear(0,0,0); }; let migrate = fn(state) { diff --git a/src/ast/types.rs b/src/ast/types.rs index 05690ab..c577e47 100644 --- a/src/ast/types.rs +++ b/src/ast/types.rs @@ -71,6 +71,7 @@ pub enum Value { Label(Label), Ref(Ref), Closure { env: EnvName, expr: Box }, + PrimitiveFunc(Name), } /// A mutable variable. @@ -91,7 +92,7 @@ pub struct Arg { } /// A symbol. -#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)] +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone)] pub struct Name(pub String); /// A field. diff --git a/src/interpret/interpret.rs b/src/interpret/interpret.rs index b2ca6c5..19a2606 100644 --- a/src/interpret/interpret.rs +++ b/src/interpret/interpret.rs @@ -1,6 +1,7 @@ //! Interpreter for Ayin. use super::types::*; +use std::backtrace::Backtrace; use std::collections::BTreeMap; use crate::ast; @@ -9,8 +10,8 @@ pub fn global_env_name() -> ast::EnvName { ast::EnvName("global".to_string()) } -pub fn setup(program: ast::Program) -> Result { - let mut state = State::new(global_env_name().0.clone()); +pub fn setup(program: ast::Program, prim_funcs: PrimitiveFuncs) -> Result { + let mut state = State::new(global_env_name().0.clone(), prim_funcs); defs_to_env(program.0, &global_env_name(), &mut state)?; Ok(state) @@ -39,7 +40,7 @@ pub fn run( func: ast::Name, args: Vec, ) -> Result { - let mut state = setup(program)?; + let mut state = setup(program, PrimitiveFuncs::new(vec![]))?; interpret(&mut state, func, args) } @@ -89,7 +90,7 @@ fn eval_expr(expr_env: &Env, state: &mut State, expr: &ast::Expr) -> Result Err(Error::NotABoolean(v)), + v => Err(Error::NotABoolean(v, Backtrace::capture())), }, ast::Expr::Func(func) => { let env_name = state.generate_env(expr_env.clone())?; @@ -113,12 +114,12 @@ fn eval_expr(expr_env: &Env, state: &mut State, expr: &ast::Expr) -> Result match eval_expr(expr_env, state, expr)? { ast::Value::Record(record) => Ok(state.variables.get(record.get(&field)?).clone()), - v => Err(Error::NotARecord(v)), + v => Err(Error::NotARecord(v, Backtrace::capture())), }, ast::Expr::Var(var) => { let value = expr_env.get(var)?.clone(); @@ -131,7 +132,7 @@ fn eval_expr(expr_env: &Env, state: &mut State, expr: &ast::Expr) -> Result Result Err(Error::NotAFunction(e)), + e => Err(Error::NotAFunction(e, Backtrace::capture())), }, - e => Err(Error::NotAFunction(ast::Expr::Value(e))), + ast::Value::PrimitiveFunc(func) => { + let mut argsvec = vec![]; + for arg in args.iter() { + let evalled = eval_expr(&expr_env, state, arg)?; + argsvec.push(evalled); + } + if let Some(func) = state.primitive_funcs.get(&func) { + Ok(func(argsvec)) + } else { + Err(Error::NotAFunction( + ast::Expr::Value(ast::Value::PrimitiveFunc(func)), + Backtrace::capture(), + )) + } + } + e => Err(Error::NotAFunction( + ast::Expr::Value(e), + Backtrace::capture(), + )), }, ast::Expr::Value(v) => match v { ast::Value::Closure { expr, env } => { @@ -159,7 +178,7 @@ fn eval_expr(expr_env: &Env, state: &mut State, expr: &ast::Expr) -> Result Err(Error::NotAReference(v.clone())), + v => Err(Error::NotAReference(v.clone(), Backtrace::capture())), }, /* _ => { @@ -200,12 +219,12 @@ fn eval_expr_shallow( let result = expr_env.get(&var)?; match result { ast::Value::Ref(_) => Ok(result.clone()), - v => Err(Error::NotAReference(v.clone())), + v => Err(Error::NotAReference(v.clone(), Backtrace::capture())), } } ast::Expr::Access { expr, field } => match eval_expr(expr_env, state, expr)? { ast::Value::Record(record) => Ok(ast::Value::Ref(record.get(&field)?.clone())), - v => Err(Error::NotARecord(v)), + v => Err(Error::NotARecord(v, Backtrace::capture())), }, _ => eval_expr(expr_env, state, expr), } @@ -385,7 +404,10 @@ mod tests { ] .into(); let result = run(program, "main".into(), vec![]); - assert_eq!(result, Err(Error::DuplicateNames("main".into()))); + assert_eq!( + result, + Err(Error::DuplicateNames("main".into(), Backtrace::capture())) + ); } #[test] @@ -396,7 +418,10 @@ mod tests { ] .into(); let result = run(program, "main".into(), vec![]); - assert_eq!(result, Err(Error::NotARecord(0.into()))); + assert_eq!( + result, + Err(Error::NotARecord(0.into(), Backtrace::capture())) + ); } #[test] fn fun_call_not_a_function() { @@ -406,6 +431,9 @@ mod tests { ] .into(); let result = run(program, "main".into(), vec![]); - assert_eq!(result, Err(Error::NotAFunction(0.into()))); + assert_eq!( + result, + Err(Error::NotAFunction(0.into(), Backtrace::capture())) + ); } } diff --git a/src/interpret/types.rs b/src/interpret/types.rs index 1d912ba..57e52c2 100644 --- a/src/interpret/types.rs +++ b/src/interpret/types.rs @@ -1,39 +1,73 @@ //! Types used in the interpreter. use crate::ast; +use std::backtrace::Backtrace; use std::collections::BTreeMap; use std::collections::HashMap; -#[derive(PartialEq, Debug, thiserror::Error)] +#[derive(Debug)] pub enum Error { - #[error("Closure generation failed. Duplicate env names: {0:?}")] - DuplicateEnvNames(ast::EnvName), - #[error("Duplicate names: {0:?}")] - DuplicateNames(ast::Name), - #[error("Name not found: {0:?}")] - NameNotFound(ast::Name), - #[error("Field not found: {0:?}")] - FieldNotFound(ast::Label), - #[error("Env not found: {0:?}")] - EnvNotFound(ast::EnvName), - #[error("Last statement is not an expression")] - LastStatementNotAnExpr, - #[error("Not a function {0:?}")] - NotAFunction(ast::Expr), - #[error("Not a record {0:?}")] - NotARecord(ast::Value), - #[error("Not a reference {0:?}")] - NotAReference(ast::Value), - #[error("Not a boolean {0:?}")] - NotABoolean(ast::Value), - #[error("Arguments mismatch")] - ArgumentsMismatch, + DuplicateEnvNames(ast::EnvName, Backtrace), + DuplicateNames(ast::Name, Backtrace), + NameNotFound(ast::Name, Backtrace), + FieldNotFound(ast::Label, Backtrace), + EnvNotFound(ast::EnvName, Backtrace), + LastStatementNotAnExpr(Backtrace), + NotAFunction(ast::Expr, Backtrace), + NotARecord(ast::Value, Backtrace), + NotAReference(ast::Value, Backtrace), + NotABoolean(ast::Value, 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::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, _), Error::NotAReference(a2, _)) => a1 == a2, + (Error::NotABoolean(a1, _), Error::NotABoolean(a2, _)) => a1 == a2, + (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::NameNotFound(a, b) => write!(f, "Name not found: {a:?}\n{b}"), + 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) => write!(f, "Not a reference {a:?}\n{b}"), + Error::NotABoolean(a, b) => write!(f, "Not a boolean {a:?}\n{b}"), + Error::ArgumentsMismatch(a) => write!(f, "Arguments mismatch\n{a}"), + } + } } impl From for Error { fn from(error: ast::Error) -> Error { match error { - ast::Error::LabelNotFound(l) => Error::FieldNotFound(l), + ast::Error::LabelNotFound(l) => Error::FieldNotFound(l, Backtrace::capture()), } } } @@ -57,6 +91,26 @@ pub struct State { env_namer: Namer, pub envs: Envs, pub variables: Variables, + pub primitive_funcs: PrimitiveFuncs, +} + +pub struct PrimitiveFuncs { + pub map: HashMap, +} + +pub type PrimFunc = fn(Vec) -> ast::Value; + +impl PrimitiveFuncs { + pub fn new(prims: Vec<(&str, PrimFunc)>) -> Self { + let mut map: HashMap = 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 { @@ -84,12 +138,13 @@ impl Variables { } } impl State { - pub fn new(name: String) -> 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 { @@ -113,14 +168,16 @@ impl Env { } } pub fn get(&self, name: &ast::Name) -> Result<&ast::Value, Error> { - self.env.get(name).ok_or(Error::NameNotFound(name.clone())) + self.env + .get(name) + .ok_or(Error::NameNotFound(name.clone(), Backtrace::capture())) } 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())) + Err(Error::DuplicateNames(name.clone(), Backtrace::capture())) } else { Ok(()) } @@ -134,16 +191,16 @@ 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())) + .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())) + .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())) + Err(Error::DuplicateEnvNames(name.clone(), Backtrace::capture())) } else { Ok(()) } diff --git a/src/main.rs b/src/main.rs index 6965ef0..71ed8dc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,7 @@ async fn main() { loop { let events = fetch_events(&mut state); update(&mut state, events); - draw(&state); + draw(&mut state); next_frame().await; } } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 16df112..b784f7d 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1,3 +1,5 @@ +pub mod primitive_functions; + pub mod runtime; pub use runtime::*; diff --git a/src/runtime/primitive_functions.rs b/src/runtime/primitive_functions.rs new file mode 100644 index 0000000..d346537 --- /dev/null +++ b/src/runtime/primitive_functions.rs @@ -0,0 +1,22 @@ +use crate::ast; +use crate::interpret::PrimitiveFuncs; +use macroquad::prelude::*; + +pub fn primitive_funcs() -> PrimitiveFuncs { + PrimitiveFuncs::new(vec![("frame_clear", clear)]) +} + +fn clear(args: Vec) -> ast::Value { + let r = to_u32(args.get(0)); + let g = to_u32(args.get(1)); + let b = to_u32(args.get(2)); + clear_background(Color::from_rgba(r, g, b, 255)); + ast::UNIT_VALUE +} + +fn to_u32(value: Option<&ast::Value>) -> u8 { + match value { + Some(ast::Value::Int(i)) => *i as u8, + _ => 0, + } +} diff --git a/src/runtime/runtime.rs b/src/runtime/runtime.rs index 5ce600f..8369249 100644 --- a/src/runtime/runtime.rs +++ b/src/runtime/runtime.rs @@ -1,3 +1,4 @@ +use super::primitive_functions::primitive_funcs; use super::types::*; use crate::ast; use crate::ast::helpers; @@ -8,11 +9,11 @@ use macroquad::prelude::*; pub async fn setup(code: ast::Program) -> State { let font = load_ttf_font("./assets/fonts/monogram.ttf").await.unwrap(); - let mut state = match interpret::setup(code) { + let mut state = match interpret::setup(code, primitive_funcs()) { Ok(state) => state, Err(err) => { println!("Error: {}", err); - interpret::State::new("game".into()) + interpret::State::new("game".into(), primitive_funcs()) } }; let game_state = match interpret::interpret(&mut state, "setup".into(), vec![]) { @@ -32,7 +33,7 @@ pub async fn setup(code: ast::Program) -> State { } pub async fn migrate(mut state: State, code: ast::Program) -> State { - match interpret::setup(code) { + match interpret::setup(code, primitive_funcs()) { Err(err) => { println!("Error: {}", err); state @@ -136,7 +137,7 @@ pub fn fetch_events(state: &mut State) -> Input { input } -pub fn draw(state: &State) { +pub fn draw(state: &mut State) { clear_background(Color::from_hex(0x081829)); set_default_camera(); @@ -151,6 +152,15 @@ pub fn draw(state: &State) { set_default_camera(); + match interpret::interpret( + &mut state.state, + "draw".into(), + vec![ast::Expr::Value(state.game_state.clone())], + ) { + Ok(_) => {} + Err(err) => println!("Error: {}", err), + } + println!( "{:#?}", interpret::value_to_stadnalone_expr(&state.state, state.game_state.clone())