From 586676b519419b97006f32430a79df4e9525bc3e Mon Sep 17 00:00:00 2001 From: bog Date: Thu, 7 Mar 2024 17:55:40 +0100 Subject: [PATCH] :sparkles: instruction executor. --- src/core/cmd.rs | 1 + src/core/eval.rs | 36 +++- src/core/exec.rs | 451 ++++++++++++++++++++++++++++++++++++++++++++++ src/core/instr.rs | 4 +- src/core/mod.rs | 1 + src/core/sym.rs | 1 + src/core/value.rs | 1 + 7 files changed, 487 insertions(+), 8 deletions(-) create mode 100644 src/core/exec.rs diff --git a/src/core/cmd.rs b/src/core/cmd.rs index 4363062..c8bacef 100644 --- a/src/core/cmd.rs +++ b/src/core/cmd.rs @@ -6,6 +6,7 @@ pub trait Command { fn requires(&self) -> Vec { vec![] } fn provides(&self) -> Vec { vec![] } fn call(&mut self, args: Vec) -> Value; + fn clone_box(&self) -> Box; } #[cfg(test)] diff --git a/src/core/eval.rs b/src/core/eval.rs index 5a59ffb..6bdca32 100644 --- a/src/core/eval.rs +++ b/src/core/eval.rs @@ -79,19 +79,38 @@ impl Evaluator { self.commands.insert(name.to_string(), cmd); } - pub fn sym(&mut self, callback: T) - where T: Fn(&mut Sym) { + pub fn clone_sym(&self) -> Sym { + self.sym.clone() + } + + pub fn getsym(&mut self, callback: U) + where U: Fn(&mut Sym) { callback(&mut self.sym); } } +// pub struct Evaluator { +// sym: Sym, +// commands: HashMap> +// } +// +impl Clone for Evaluator { + fn clone(&self) -> Self { + let mut c = Evaluator::new(self.sym.clone()); + for cmd in self.commands.iter() { + c.commands.insert(cmd.0.to_string(), cmd.1.clone_box()); + } + c + } +} + mod test { use super::*; type TestResult = Result<(), Box>; - fn test_eval_init(expr: Expr, value: Value, init: T) + fn test_eval_init(expr: Expr, value: Value, init: U) -> TestResult -where T: Fn(&mut Evaluator) { +where U: Fn(&mut Evaluator) { let sym = Sym::new(); let mut eval = Evaluator::new(sym); init(&mut eval); @@ -143,7 +162,7 @@ where T: Fn(&mut Evaluator) { Expr::Ident(String::from("X")), Value::Symbol(String::from("hello")), |eval| { - eval.sym(|s| { + eval.getsym(|s| { s.assign("X", Value::Symbol("hello".to_string())); }) } @@ -154,6 +173,7 @@ where T: Fn(&mut Evaluator) { #[test] fn command() -> TestResult { + #[derive(Clone)] struct MockedCmd { } @@ -165,6 +185,10 @@ where T: Fn(&mut Evaluator) { Value::Int(0) } } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } } test_eval_init( @@ -172,7 +196,7 @@ where T: Fn(&mut Evaluator) { Value::Int(24), |eval| { eval.new_command("CMD", Box::new(MockedCmd {})); - eval.sym(|s| { + eval.getsym(|s| { s.assign("X", Value::Command(String::from("CMD"))); }) } diff --git a/src/core/exec.rs b/src/core/exec.rs new file mode 100644 index 0000000..dd65390 --- /dev/null +++ b/src/core/exec.rs @@ -0,0 +1,451 @@ +use std::error::Error; +use crate::core::{ + instr::Instr, + expr::Expr, + value::Value, + eval::Evaluator, + sym::Sym, + cmd::Command +}; + +#[derive(Clone)] +struct UserCmd { + pub pams: Vec, + pub reqs: Vec, + pub provs: Vec, + pub body: Vec, + pub eval: Evaluator +} + +impl Command for UserCmd { + fn params(&self) -> Vec { + self.pams.clone() + } + + fn requires(&self) -> Vec { + self.reqs.clone() + } + + fn provides(&self) -> Vec { + self.provs.clone() + } + + fn call(&mut self, args: Vec) -> Value { + let mut exec = Executor::new(self.eval.clone()); + let pams = self.params(); + + for (i, arg) in args.iter().enumerate() { + let Expr::Ident(ref name) = pams[i] else { + return Value::Nil; + }; + + exec.getsym(move |s| { + s.assign(name, arg.clone()); + }); + } + + let mut result = Value::Nil; + + for instr in self.body.iter() { + match exec.run(&instr) { + Ok(value) => { result = value; } + Err(err) => { println!("=> {:?}", err); } + } + } + + result + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } +} + +pub struct Executor { + eval: Evaluator +} + +impl Executor { + pub fn new(eval: Evaluator) -> Self { + Self { + eval + } + } + pub fn getsym(&mut self, callback: U) + where U: Fn(&mut Sym) { + self.eval.getsym(|s| { + callback(s); + }) + } + + pub fn run(&mut self, instr: &Instr) + -> Result> { + match instr { + Instr::Module(instrs) => { + let mut res: Value = Value::Nil; + for instr in instrs { + res = self.run(instr)?; + } + + Ok(res) + }, + + // ignoring tasks + Instr::Task(_, _, _) => { Ok(Value::Nil) }, + + Instr::Expr(expr) => { + let value = self.eval.run(&expr)?; + Ok(value) + }, + + Instr::Assign(name, exprs) => { + let mut values: Vec = vec![]; + for expr in exprs { + values.push(self.eval.run(expr)?); + } + self.eval.getsym(|s| { + if values.len() == 1 { + s.assign(name, values[0].clone()); + } else { + s.assign(name, Value::Array(values.iter().cloned().collect())); + } + + }); + Ok(Value::Nil) + }, + + Instr::AssignIf(name, exprs) => { + let mut values: Vec = vec![]; + for expr in exprs { + values.push(self.eval.run(expr)?); + } + self.eval.getsym(|s| { + if values.len() == 1 { + s.assign_if(name, values[0].clone()); + } else { + s.assign_if(name, Value::Array(values.iter().cloned().collect())); + } + + }); + Ok(Value::Nil) + }, + + Instr::AssignArray(name, exprs) => { + let mut values: Vec = vec![]; + for expr in exprs { + values.push(self.eval.run(expr)?); + } + self.eval.getsym(|s| { + if values.len() == 1 { + s.assign_array(name, values[0].clone()); + } else { + for val in values.iter() { + s.assign_array(name, val.clone()); + } + } + + }); + Ok(Value::Nil) + }, + + Instr::Command(name, params, + requires, provides, body) => { + let cmd = UserCmd { + pams: params.clone(), + reqs: requires.clone(), + provs: provides.clone(), + body: body.to_vec(), + eval: self.eval.clone() + }; + self.eval.new_command(name, Box::new(cmd)); + + self.getsym(|s| { + s.assign(name, Value::Command( + name.clone() + )); + }); + + Ok(Value::Nil) + }, + _ => { + Err(format!( + "cannot execute unknown instruction: {:?}", + instr + ).into()) + } + } + } +} + +#[cfg(test)] +mod test { + use super::*; + type TestResult = Result<(), Box>; + + fn test_exec(instr: Instr, oracle: Value, init: T) + -> TestResult + where T: Fn(&mut Executor) { + let sym = Sym::new(); + let eval = Evaluator::new(sym); + let mut exec = Executor::new(eval); + + init(&mut exec); + + let value = exec.run( + &instr + )?; + + assert_eq!(value, oracle); + + Ok(()) + } + + #[test] + fn expr() -> TestResult { + test_exec( + Instr::Module(vec![ + Instr::Expr(Expr::BuiltIn(Value::Int(74))) + ]), + Value::Int(74), + |_| {} + )?; + + test_exec( + Instr::Module(vec![ + Instr::Expr(Expr::Ident(String::from("Y"))) + ]), + Value::Float(22.5), + |e| { e.getsym(|s|{ + s.assign("Y", Value::Float(22.5)); + }); } + )?; + Ok(()) + } + + #[test] + fn assign() -> TestResult { + // Assign + // ====== + test_exec( + Instr::Module(vec![ + Instr::Assign( + String::from("hello"), + vec![ + Expr::BuiltIn(Value::String("bim".to_string())), + Expr::BuiltIn(Value::Int(12)), + Expr::BuiltIn(Value::Float(3.4)) + ] + ), + Instr::Expr(Expr::Ident(String::from("hello"))) + ]), + Value::Array(vec![ + Value::String("bim".to_string()), + Value::Int(12), + Value::Float(3.4) + ]), + |_| {} + )?; + test_exec( + Instr::Module(vec![ + Instr::Assign( + String::from("hello"), + vec![ + Expr::BuiltIn(Value::String("bim".to_string())) + ] + ), + Instr::Assign( + String::from("hello"), + vec![ + Expr::BuiltIn(Value::String("bam".to_string())) + ] + ), + Instr::Expr(Expr::Ident(String::from("hello"))) + ]), + Value::String("bam".to_string()), + |_| {} + )?; + + test_exec( + Instr::Module(vec![ + Instr::Assign( + String::from("hello"), + vec![ + Expr::BuiltIn(Value::String("bim".to_string())) + ] + ), + Instr::Expr(Expr::Ident(String::from("hello"))) + ]), + Value::String("bim".to_string()), + |_| {} + )?; + + // Assign If + // ========= + test_exec( + Instr::Module(vec![ + Instr::AssignIf( + String::from("hello"), + vec![ + Expr::BuiltIn(Value::String("bim".to_string())) + ] + ), + Instr::Expr(Expr::Ident(String::from("hello"))) + ]), + Value::String("bim".to_string()), + |_| {} + )?; + + + test_exec( + Instr::Module(vec![ + Instr::AssignIf( + String::from("hello"), + vec![ + Expr::BuiltIn(Value::String("bim".to_string())) + ] + ), + Instr::AssignIf( + String::from("hello"), + vec![ + Expr::BuiltIn(Value::String("bam".to_string())) + ] + ), + Instr::Expr(Expr::Ident(String::from("hello"))) + ]), + Value::String("bim".to_string()), + |_| {} + )?; + + // Assign Array + // ============ + test_exec( + Instr::Module(vec![ + Instr::Assign( + String::from("hello"), + vec![ + Expr::BuiltIn(Value::String("bim".to_string())), + ] + ), + Instr::AssignArray( + String::from("hello"), + vec![ + Expr::BuiltIn(Value::String("Pizza".to_string())) + ] + ), + Instr::Expr(Expr::Ident(String::from("hello"))) + ]), + Value::Array(vec![ + Value::String("bim".to_string()), + Value::String("Pizza".to_string()), + ]), + |_| {} + )?; + + test_exec( + Instr::Module(vec![ + Instr::AssignArray( + String::from("hello"), + vec![ + Expr::BuiltIn(Value::String("bim".to_string())), + Expr::BuiltIn(Value::String("bam".to_string())), + Expr::BuiltIn(Value::String("boom".to_string())) + ] + ), + Instr::AssignArray( + String::from("hello"), + vec![ + Expr::BuiltIn(Value::String("I".to_string())), + Expr::BuiltIn(Value::String("Love".to_string())), + Expr::BuiltIn(Value::String("Pizza".to_string())) + ] + ), + Instr::Expr(Expr::Ident(String::from("hello"))) + ]), + Value::Array(vec![ + Value::String("bim".to_string()), + Value::String("bam".to_string()), + Value::String("boom".to_string()), + Value::String("I".to_string()), + Value::String("Love".to_string()), + Value::String("Pizza".to_string()), + ]), + |_| {} + )?; + + test_exec( + Instr::Module(vec![ + Instr::AssignArray( + String::from("hello"), + vec![ + Expr::BuiltIn(Value::String("bim".to_string())) + ] + ), + Instr::Expr(Expr::Ident(String::from("hello"))) + ]), + Value::Array(vec![Value::String("bim".to_string())]), + |_| {} + )?; + Ok(()) + } + + #[test] + fn command() -> TestResult { + #[derive(Clone)] + struct AddIntCmd {} + impl Command for AddIntCmd { + fn call(&mut self, args: Vec) -> Value { + let mut res: i32 = 0; + + for arg in args { + if let Value::Int(val) = arg { + res += val; + } + } + + Value::Int(res) + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + } + + test_exec( + Instr::Module(vec![ + Instr::Command( + String::from("sum"), // name + vec![ + Expr::Ident(String::from("x")), + Expr::Ident(String::from("y")), + ],// args + vec![],// requires + vec![],// provides + vec![ + Instr::Expr( + Expr::Call(String::from("ADD"), vec![ + Expr::Ident(String::from("x")), + Expr::Ident(String::from("y")) + ]) + ) + ] + ), + Instr::Expr(Expr::Call(String::from("sum"), vec![ + Expr::BuiltIn(Value::Int(32)), + Expr::BuiltIn(Value::Int(16)), + ])) + ]), + Value::Int(48), + |exec| { + exec.eval.new_command( + "ADD", + Box::new(AddIntCmd {}) + ); + exec.getsym(|s| { + s.assign("ADD", Value::Command(String::from("ADD"))) + }) + } + )?; + + Ok(()) + } +} diff --git a/src/core/instr.rs b/src/core/instr.rs index 414bb67..40a9a89 100644 --- a/src/core/instr.rs +++ b/src/core/instr.rs @@ -9,6 +9,6 @@ pub enum Instr { Assign(String, Vec), AssignIf(String, Vec), AssignArray(String, Vec), - Command(String, Vec, Vec, Vec), - Task(String, Vec) + Command(String, Vec, Vec, Vec, Vec), + Task(String, Vec, Vec) } diff --git a/src/core/mod.rs b/src/core/mod.rs index 915cf6b..ffe6e6b 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -4,3 +4,4 @@ pub mod value; pub mod eval; pub mod sym; pub mod cmd; +pub mod exec; diff --git a/src/core/sym.rs b/src/core/sym.rs index f13a04e..29cd731 100644 --- a/src/core/sym.rs +++ b/src/core/sym.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use crate::core::value::Value; +#[derive(Clone, Debug)] pub struct Sym { table: HashMap } diff --git a/src/core/value.rs b/src/core/value.rs index 4261857..a6eece2 100644 --- a/src/core/value.rs +++ b/src/core/value.rs @@ -4,6 +4,7 @@ use crate::core::cmd::Command; #[derive(PartialEq)] #[derive(Clone)] pub enum Value { + Nil, Int(i32), Float(f64), Bool(bool),