✨ instruction executor.
parent
b72614afaf
commit
586676b519
|
@ -6,6 +6,7 @@ pub trait Command {
|
||||||
fn requires(&self) -> Vec<Expr> { vec![] }
|
fn requires(&self) -> Vec<Expr> { vec![] }
|
||||||
fn provides(&self) -> Vec<Expr> { vec![] }
|
fn provides(&self) -> Vec<Expr> { vec![] }
|
||||||
fn call(&mut self, args: Vec<Value>) -> Value;
|
fn call(&mut self, args: Vec<Value>) -> Value;
|
||||||
|
fn clone_box(&self) -> Box<dyn Command>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -79,19 +79,38 @@ impl Evaluator {
|
||||||
self.commands.insert(name.to_string(), cmd);
|
self.commands.insert(name.to_string(), cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sym<T>(&mut self, callback: T)
|
pub fn clone_sym(&self) -> Sym {
|
||||||
where T: Fn(&mut Sym) {
|
self.sym.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getsym<U>(&mut self, callback: U)
|
||||||
|
where U: Fn(&mut Sym) {
|
||||||
callback(&mut self.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 {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
type TestResult = Result<(), Box<dyn std::error::Error>>;
|
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
|
-> TestResult
|
||||||
where T: Fn(&mut Evaluator) {
|
where U: Fn(&mut Evaluator) {
|
||||||
let sym = Sym::new();
|
let sym = Sym::new();
|
||||||
let mut eval = Evaluator::new(sym);
|
let mut eval = Evaluator::new(sym);
|
||||||
init(&mut eval);
|
init(&mut eval);
|
||||||
|
@ -143,7 +162,7 @@ where T: Fn(&mut Evaluator) {
|
||||||
Expr::Ident(String::from("X")),
|
Expr::Ident(String::from("X")),
|
||||||
Value::Symbol(String::from("hello")),
|
Value::Symbol(String::from("hello")),
|
||||||
|eval| {
|
|eval| {
|
||||||
eval.sym(|s| {
|
eval.getsym(|s| {
|
||||||
s.assign("X", Value::Symbol("hello".to_string()));
|
s.assign("X", Value::Symbol("hello".to_string()));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -154,6 +173,7 @@ where T: Fn(&mut Evaluator) {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn command() -> TestResult {
|
fn command() -> TestResult {
|
||||||
|
#[derive(Clone)]
|
||||||
struct MockedCmd {
|
struct MockedCmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,6 +185,10 @@ where T: Fn(&mut Evaluator) {
|
||||||
Value::Int(0)
|
Value::Int(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clone_box(&self) -> Box<dyn Command> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test_eval_init(
|
test_eval_init(
|
||||||
|
@ -172,7 +196,7 @@ where T: Fn(&mut Evaluator) {
|
||||||
Value::Int(24),
|
Value::Int(24),
|
||||||
|eval| {
|
|eval| {
|
||||||
eval.new_command("CMD", Box::new(MockedCmd {}));
|
eval.new_command("CMD", Box::new(MockedCmd {}));
|
||||||
eval.sym(|s| {
|
eval.getsym(|s| {
|
||||||
s.assign("X", Value::Command(String::from("CMD")));
|
s.assign("X", Value::Command(String::from("CMD")));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,6 @@ pub enum Instr {
|
||||||
Assign(String, Vec<Expr>),
|
Assign(String, Vec<Expr>),
|
||||||
AssignIf(String, Vec<Expr>),
|
AssignIf(String, Vec<Expr>),
|
||||||
AssignArray(String, Vec<Expr>),
|
AssignArray(String, Vec<Expr>),
|
||||||
Command(String, Vec<Expr>, Vec<Expr>, Vec<Instr>),
|
Command(String, Vec<Expr>, Vec<Expr>, Vec<Expr>, Vec<Instr>),
|
||||||
Task(String, Vec<Instr>)
|
Task(String, Vec<Expr>, Vec<Instr>)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,3 +4,4 @@ pub mod value;
|
||||||
pub mod eval;
|
pub mod eval;
|
||||||
pub mod sym;
|
pub mod sym;
|
||||||
pub mod cmd;
|
pub mod cmd;
|
||||||
|
pub mod exec;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use crate::core::value::Value;
|
use crate::core::value::Value;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct Sym {
|
pub struct Sym {
|
||||||
table: HashMap<String, Value>
|
table: HashMap<String, Value>
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ use crate::core::cmd::Command;
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
|
Nil,
|
||||||
Int(i32),
|
Int(i32),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
|
|
Loading…
Reference in New Issue