mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 17:25:33 +01:00
Added environment with basic math functions
Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
30
src/envs.rs
30
src/envs.rs
@ -1,6 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::types::{KeyType, MalRet, MalType};
|
||||
use crate::types::MalType::{Fun, Str};
|
||||
use crate::types::{int_op, MalArgs, MalRet, MalType};
|
||||
|
||||
pub struct Env {
|
||||
map: HashMap<String, MalType>,
|
||||
@ -8,16 +9,29 @@ pub struct Env {
|
||||
|
||||
impl Env {
|
||||
pub fn new() -> Self {
|
||||
Env {
|
||||
let mut env = Env {
|
||||
map: HashMap::new(),
|
||||
};
|
||||
env.init();
|
||||
env
|
||||
}
|
||||
|
||||
pub fn solve(&self, sym: String) -> MalRet {
|
||||
match self.map.get(&sym) {
|
||||
Some(val) => Ok(val.clone()),
|
||||
None => Err(format!("symbol {:?} not defined", sym)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn solve(&self, sym: KeyType) -> MalRet {
|
||||
let v = sym.val;
|
||||
match self.map.get(&v) {
|
||||
Some(val) => Ok(val.clone()),
|
||||
None => Err(format!("symbol {:?} not defined", v)),
|
||||
}
|
||||
fn define(&mut self, sym: &str, f: fn(MalArgs) -> MalRet) {
|
||||
self.map.insert(sym.to_string(), Fun(f));
|
||||
}
|
||||
|
||||
fn init(&mut self) {
|
||||
self.define("test", |_| Ok(Str("This is a test function".to_string())));
|
||||
self.define("+", |args| int_op(0, |a, b| a + b, args));
|
||||
self.define("-", |args| int_op(0, |a, b| a - b, args));
|
||||
self.define("*", |args| int_op(1, |a, b| a * b, args));
|
||||
self.define("/", |args| int_op(1, |a, b| a / b, args));
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,8 +40,8 @@ fn main() {
|
||||
println!("; [{}]> Error {}", num, err);
|
||||
}
|
||||
}
|
||||
num += 1;
|
||||
}
|
||||
num += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,17 +1,24 @@
|
||||
use crate::envs::Env;
|
||||
use crate::types::MalType::*;
|
||||
use crate::types::{MalRet, MalType};
|
||||
use crate::types::{MalArgs, MalRet, MalType};
|
||||
|
||||
fn function_call(list: MalType) -> MalRet {
|
||||
fn call_func(func: &MalType, args: MalArgs) -> MalRet {
|
||||
match func {
|
||||
Fun(func) => func(args),
|
||||
_ => panic!("Calling not a function"),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_func(list: MalType) -> MalRet {
|
||||
match list {
|
||||
List(list) => {
|
||||
let _func = &list[0];
|
||||
if list.len() > 1 {
|
||||
let _ast = &list[1..list.len() - 1];
|
||||
todo!("call: func(args)");
|
||||
let func = &list[0];
|
||||
let args = if list.len() > 1 {
|
||||
&list[1..]
|
||||
} else {
|
||||
todo!("call: func()");
|
||||
}
|
||||
&list[0..0]
|
||||
};
|
||||
call_func(func, args.to_vec())
|
||||
}
|
||||
_ => Err("YOU SHOULD NOT BE HERE".to_string()),
|
||||
}
|
||||
@ -21,10 +28,10 @@ pub fn eval(ast: MalType, env: &Env) -> MalRet {
|
||||
match &ast {
|
||||
List(list) => {
|
||||
if list.is_empty() {
|
||||
Ok(ast)
|
||||
Ok(Nil)
|
||||
// Previously Ok(ast)
|
||||
} else {
|
||||
let ev = eval_ast(ast, env)?;
|
||||
function_call(ev)
|
||||
eval_func(eval_ast(ast, env)?)
|
||||
}
|
||||
}
|
||||
_ => eval_ast(ast, env),
|
||||
@ -42,10 +49,10 @@ fn eval_list(list: Vec<MalType>, env: &Env) -> MalRet {
|
||||
Ok(List(ret))
|
||||
}
|
||||
|
||||
pub fn eval_ast(ast: MalType, env: &Env) -> MalRet {
|
||||
fn eval_ast(ast: MalType, env: &Env) -> MalRet {
|
||||
match ast {
|
||||
Sym(sym) => env.solve(sym), // resolve
|
||||
Sym(sym) => env.solve(sym),
|
||||
List(list) => eval_list(list, env),
|
||||
_ => Ok(ast), // Default behavior, do not resolve
|
||||
_ => Ok(ast),
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,14 +5,15 @@ use crate::types::MalType::*;
|
||||
pub fn pr_str(ast: &MalType, print_readably: bool) -> String {
|
||||
match ast {
|
||||
Nil => "nil".to_string(),
|
||||
Sym(sym) | Key(sym) => sym.val.to_string(),
|
||||
Sym(sym) => sym.to_string(),
|
||||
Key(sym) => sym[1..sym.len() - 1].to_string(),
|
||||
Int(val) => val.to_string(),
|
||||
Bool(val) => val.to_string(),
|
||||
Str(str) => {
|
||||
if print_readably {
|
||||
escape_str(&str.val)
|
||||
escape_str(str)
|
||||
} else {
|
||||
str.val.to_string()
|
||||
str.to_string()
|
||||
}
|
||||
}
|
||||
List(el) => format!(
|
||||
@ -33,9 +34,10 @@ pub fn pr_str(ast: &MalType, print_readably: bool) -> String {
|
||||
Map(el) => format!(
|
||||
"{{{}}}",
|
||||
el.iter()
|
||||
.map(|sub| vec![sub.0.val.to_string(), pr_str(sub.1, print_readably)].join(" "))
|
||||
.map(|sub| vec![sub.0.to_string(), pr_str(sub.1, print_readably)].join(" "))
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ")
|
||||
),
|
||||
Fun(func) => format!("{:?}", func),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
// Specyfy components in "types"
|
||||
use crate::types::*;
|
||||
// By specifying enum variants it's possible to omit namespace
|
||||
use crate::types::KeyVar;
|
||||
use crate::types::MalType::*;
|
||||
|
||||
use regex::Regex;
|
||||
@ -72,11 +71,11 @@ impl Reader {
|
||||
if Regex::new(r"^-?[0-9]+$").unwrap().is_match(tk) {
|
||||
Ok(Int(tk.parse::<isize>().unwrap()))
|
||||
} else if tk.starts_with('\"') {
|
||||
Ok(Str(map_key(KeyVar::Str, &unescape_str(tk))))
|
||||
Ok(Str(unescape_str(tk)))
|
||||
} else if tk.starts_with(':') {
|
||||
Ok(Key(map_key(KeyVar::Key, tk)))
|
||||
Ok(Key(format!("ʞ{}", tk)))
|
||||
} else {
|
||||
Ok(Sym(map_key(KeyVar::Sym, tk)))
|
||||
Ok(Sym(tk.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@ fn PRINT(output: MalType) -> String {
|
||||
|
||||
pub fn rep(input: &str, env: &Env) -> Result<String, String> {
|
||||
let ast = READ(input)?;
|
||||
// println!("{:#?}", ast);
|
||||
let out = EVAL(ast, env)?;
|
||||
Ok(PRINT(out))
|
||||
}
|
||||
|
||||
46
src/types.rs
46
src/types.rs
@ -2,35 +2,16 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||
pub enum KeyVar {
|
||||
Key,
|
||||
Sym,
|
||||
Str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||
pub struct KeyType {
|
||||
pub val: String,
|
||||
var: KeyVar,
|
||||
}
|
||||
|
||||
pub fn map_key(var: KeyVar, val: &str) -> KeyType {
|
||||
KeyType {
|
||||
val: val.to_string(),
|
||||
var,
|
||||
}
|
||||
}
|
||||
|
||||
// All Mal types should inherit from this
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MalType {
|
||||
List(Vec<MalType>),
|
||||
Vector(Vec<MalType>),
|
||||
Map(HashMap<KeyType, MalType>),
|
||||
Sym(KeyType),
|
||||
Key(KeyType),
|
||||
Str(KeyType),
|
||||
Map(HashMap<String, MalType>),
|
||||
Fun(fn(MalArgs) -> MalRet),
|
||||
Sym(String),
|
||||
Key(String),
|
||||
Str(String),
|
||||
Int(isize),
|
||||
Bool(bool),
|
||||
Nil,
|
||||
@ -87,3 +68,20 @@ pub fn unescape_str(s: &str) -> String {
|
||||
.replace("\\n", "\n")
|
||||
.replace("\\\"", "\"")
|
||||
}
|
||||
|
||||
use MalType::Int;
|
||||
|
||||
pub fn int_op(set: isize, f: fn(isize, isize) -> isize, args: MalArgs) -> MalRet {
|
||||
let mut tmp: isize = set;
|
||||
for el in args {
|
||||
match el {
|
||||
Int(val) => {
|
||||
tmp = f(tmp, val);
|
||||
}
|
||||
_ => {
|
||||
return Err(format!("{:?} is not a number", el));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Int(tmp))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user