mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 01:05:32 +01:00
Implemented def!
Implemented `def!` to define new symbols into the current environment, still need to implement `let*` Added some helper functon for the eval phase Defined a macro for more readable environment initialization Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
44
src/env.rs
44
src/env.rs
@ -1,5 +1,34 @@
|
||||
use crate::types::{MalMap, MalRet, MalType};
|
||||
|
||||
// This is the first time I implement a macro, and I'm copying it
|
||||
// so I will comment this a LOT
|
||||
macro_rules! env_init {
|
||||
($outer:expr) => {
|
||||
// match any istance with no args
|
||||
{
|
||||
// the macro prevent the macro from disrupting the external code
|
||||
// this is the block of code that will substitute the macro
|
||||
Env::new($outer)
|
||||
// returns an empty map
|
||||
}
|
||||
};
|
||||
($outer:expr, $($key:expr => $val:expr),*) => {
|
||||
// Only if previous statements did not match,
|
||||
// note that the expression with fat arrow is arbitrary,
|
||||
// could have been slim arrow, comma or any other
|
||||
// recognizable structure
|
||||
{
|
||||
// create an empty map
|
||||
let mut map = env_init!($outer);
|
||||
$( // Do this for all elements of the arguments list
|
||||
map.set($key, &$val);
|
||||
)*
|
||||
// return the new map
|
||||
map
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct Env {
|
||||
data: MalMap,
|
||||
outer: Option<Box<Env>>,
|
||||
@ -27,3 +56,18 @@ impl Env {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use crate::types::int_op;
|
||||
use crate::types::MalType::{Fun, Str};
|
||||
use std::process::exit;
|
||||
|
||||
pub fn env_init() -> Env {
|
||||
env_init!(None,
|
||||
"test" => Fun(|_| Ok(Str("This is a test function".to_string()))),
|
||||
"quit" => Fun(|_| {println!("Bye!"); exit(0)}),
|
||||
"+" => Fun(|a| int_op(0, |a, b| a + b, a)),
|
||||
"-" => Fun(|a| int_op(0, |a, b| a - b, a)),
|
||||
"*" => Fun(|a| int_op(1, |a, b| a * b, a)),
|
||||
"/" => Fun(|a| int_op(1, |a, b| a / b, a))
|
||||
)
|
||||
}
|
||||
|
||||
71
src/eval.rs
71
src/eval.rs
@ -12,33 +12,66 @@ fn call_func(func: &MalType, args: &[MalType]) -> MalRet {
|
||||
fn eval_func(list: &MalType) -> MalRet {
|
||||
match list {
|
||||
List(list) => {
|
||||
let func = &list[0];
|
||||
let args = if list.len() > 1 {
|
||||
&list[1..]
|
||||
} else {
|
||||
&list[0..0]
|
||||
};
|
||||
let (func, args) = car_cdr(list);
|
||||
call_func(func, args)
|
||||
}
|
||||
_ => Err("YOU SHOULD NOT BE HERE".to_string()),
|
||||
_ => todo!("Yep! I hate it"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval(ast: &MalType, env: &Env) -> MalRet {
|
||||
match &ast {
|
||||
List(list) => {
|
||||
if list.is_empty() {
|
||||
// Ok(Nil) // Should be the normal behavior
|
||||
Ok(ast.clone())
|
||||
} else {
|
||||
eval_func(&eval_ast(ast, env)?)
|
||||
fn car_cdr(list: &[MalType]) -> (&MalType, &[MalType]) {
|
||||
(
|
||||
&list[0],
|
||||
if list.len() > 1 {
|
||||
&list[1..]
|
||||
} else {
|
||||
&list[0..0]
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn def_bang(list: &[MalType], env: &mut Env) -> MalRet {
|
||||
match list.len() {
|
||||
2 => match &list[0] {
|
||||
Sym(sym) => {
|
||||
let cdr = eval(&list[1], env)?;
|
||||
env.set(sym.as_str(), &cdr);
|
||||
Ok(cdr)
|
||||
}
|
||||
}
|
||||
_ => Err(format!(
|
||||
"Assigning {:?} to {:?}, which is not a symbol",
|
||||
&list[1], &list[0]
|
||||
)),
|
||||
},
|
||||
_ => Err("def! macro has too many arguments, may be less strict in future".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply(list: &MalArgs, env: &mut Env) -> MalRet {
|
||||
let (car, cdr) = car_cdr(list);
|
||||
match car {
|
||||
Sym(sym) => match sym.as_str() {
|
||||
"def!" => def_bang(cdr, env), // already remove the def
|
||||
"let*" => todo!("set new environment and add definitions to it"),
|
||||
// default if no match
|
||||
_ => eval_func(&eval_ast(&List(list.to_vec()), env)?),
|
||||
// Hate this line, should not need to create a whole new vector
|
||||
},
|
||||
_ => Err("First element not a symbol".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval(ast: &MalType, env: &mut Env) -> MalRet {
|
||||
match &ast {
|
||||
List(list) => match list.len() {
|
||||
0 => Ok(ast.clone()),
|
||||
_ => apply(list, env),
|
||||
},
|
||||
_ => eval_ast(ast, env),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_collection(list: &MalArgs, env: &Env) -> Result<MalArgs, String> {
|
||||
fn eval_collection(list: &MalArgs, env: &mut Env) -> Result<MalArgs, String> {
|
||||
let mut ret = MalArgs::new();
|
||||
for el in list {
|
||||
match eval(el, env) {
|
||||
@ -49,7 +82,7 @@ fn eval_collection(list: &MalArgs, env: &Env) -> Result<MalArgs, String> {
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn eval_map(map: &MalMap, env: &Env) -> MalRet {
|
||||
fn eval_map(map: &MalMap, env: &mut Env) -> MalRet {
|
||||
let mut ret = MalMap::new();
|
||||
|
||||
for (k, v) in map {
|
||||
@ -62,7 +95,7 @@ fn eval_map(map: &MalMap, env: &Env) -> MalRet {
|
||||
Ok(Map(ret))
|
||||
}
|
||||
|
||||
fn eval_ast(ast: &MalType, env: &Env) -> MalRet {
|
||||
fn eval_ast(ast: &MalType, env: &mut Env) -> MalRet {
|
||||
match ast {
|
||||
Sym(sym) => env.get(sym),
|
||||
List(list) => Ok(List(eval_collection(list, env)?)),
|
||||
|
||||
@ -6,17 +6,14 @@ mod eval;
|
||||
mod printer;
|
||||
mod reader;
|
||||
mod types;
|
||||
use types::env_init;
|
||||
|
||||
use env::Env;
|
||||
use env::env_init;
|
||||
|
||||
mod step3_env;
|
||||
use step3_env::rep;
|
||||
|
||||
fn main() {
|
||||
let mut num = 0;
|
||||
let mut reply_env = Env::new(None);
|
||||
env_init(&mut reply_env);
|
||||
let mut reply_env = env_init();
|
||||
|
||||
loop {
|
||||
let mut input = String::new();
|
||||
@ -25,7 +22,7 @@ fn main() {
|
||||
// Flush the prompt to appear before command
|
||||
let _ = io::stdout().flush();
|
||||
|
||||
// Read line to compose program inpug
|
||||
// Read line to compose program input
|
||||
let mut line = String::new();
|
||||
io::stdin().read_line(&mut line).unwrap();
|
||||
|
||||
|
||||
16
src/types.rs
16
src/types.rs
@ -1,5 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::process::exit;
|
||||
|
||||
// All Mal types should inherit from this
|
||||
#[derive(Debug, Clone)]
|
||||
@ -93,18 +92,3 @@ pub fn int_op(set: isize, f: fn(isize, isize) -> isize, args: &[MalType]) -> Mal
|
||||
|
||||
Ok(Int(left))
|
||||
}
|
||||
|
||||
use crate::env::Env;
|
||||
use MalType::Fun;
|
||||
|
||||
pub fn env_init(env: &mut Env) {
|
||||
env.set("quit", &Fun(|_| exit(0)));
|
||||
env.set("+", &Fun(|a: &[MalType]| int_op(0, |a, b| a + b, a)));
|
||||
env.set("-", &Fun(|a: &[MalType]| int_op(0, |a, b| a - b, a)));
|
||||
env.set("*", &Fun(|a: &[MalType]| int_op(1, |a, b| a * b, a)));
|
||||
env.set("/", &Fun(|a: &[MalType]| int_op(1, |a, b| a / b, a)));
|
||||
env.set(
|
||||
"test",
|
||||
&Fun(|_| Ok(Str("This is a test function".to_string()))),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user