instruction executor.

main
bog 2024-03-07 17:55:40 +01:00
parent b72614afaf
commit 586676b519
7 changed files with 487 additions and 8 deletions

View File

@ -6,6 +6,7 @@ pub trait Command {
fn requires(&self) -> Vec<Expr> { vec![] }
fn provides(&self) -> Vec<Expr> { vec![] }
fn call(&mut self, args: Vec<Value>) -> Value;
fn clone_box(&self) -> Box<dyn Command>;
}
#[cfg(test)]

View File

@ -79,19 +79,38 @@ impl Evaluator {
self.commands.insert(name.to_string(), cmd);
}
pub fn sym<T>(&mut self, callback: T)
where T: Fn(&mut Sym) {
pub fn clone_sym(&self) -> Sym {
self.sym.clone()
}
pub fn getsym<U>(&mut self, callback: U)
where U: Fn(&mut Sym) {
callback(&mut self.sym);
}
}
// pub struct Evaluator {
// sym: Sym,
// commands: HashMap<String, Box<dyn Command>>
// }
//
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<dyn std::error::Error>>;
fn test_eval_init<T>(expr: Expr, value: Value, init: T)
fn test_eval_init<U>(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<dyn Command> {
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")));
})
}

451
src/core/exec.rs Normal file
View File

@ -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<Expr>,
pub reqs: Vec<Expr>,
pub provs: Vec<Expr>,
pub body: Vec<Instr>,
pub eval: Evaluator
}
impl Command for UserCmd {
fn params(&self) -> Vec<Expr> {
self.pams.clone()
}
fn requires(&self) -> Vec<Expr> {
self.reqs.clone()
}
fn provides(&self) -> Vec<Expr> {
self.provs.clone()
}
fn call(&mut self, args: Vec<Value>) -> 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<dyn Command> {
Box::new(self.clone())
}
}
pub struct Executor {
eval: Evaluator
}
impl Executor {
pub fn new(eval: Evaluator) -> Self {
Self {
eval
}
}
pub fn getsym<U>(&mut self, callback: U)
where U: Fn(&mut Sym) {
self.eval.getsym(|s| {
callback(s);
})
}
pub fn run(&mut self, instr: &Instr)
-> Result<Value, Box<dyn Error>> {
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<Value> = 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<Value> = 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<Value> = 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<dyn Error>>;
fn test_exec<T>(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>) -> 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<dyn Command> {
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(())
}
}

View File

@ -9,6 +9,6 @@ pub enum Instr {
Assign(String, Vec<Expr>),
AssignIf(String, Vec<Expr>),
AssignArray(String, Vec<Expr>),
Command(String, Vec<Expr>, Vec<Expr>, Vec<Instr>),
Task(String, Vec<Instr>)
Command(String, Vec<Expr>, Vec<Expr>, Vec<Expr>, Vec<Instr>),
Task(String, Vec<Expr>, Vec<Instr>)
}

View File

@ -4,3 +4,4 @@ pub mod value;
pub mod eval;
pub mod sym;
pub mod cmd;
pub mod exec;

View File

@ -1,6 +1,7 @@
use std::collections::HashMap;
use crate::core::value::Value;
#[derive(Clone, Debug)]
pub struct Sym {
table: HashMap<String, Value>
}

View File

@ -4,6 +4,7 @@ use crate::core::cmd::Command;
#[derive(PartialEq)]
#[derive(Clone)]
pub enum Value {
Nil,
Int(i32),
Float(f64),
Bool(bool),