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:
teo3300
2023-06-18 01:00:00 +02:00
parent b3b1e7f5ae
commit 3456a62879
4 changed files with 99 additions and 41 deletions

View File

@ -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))
)
}

View File

@ -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)?)),

View File

@ -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();

View File

@ -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()))),
);
}