Mal step 3

This commit is contained in:
Roman Godmaire 2023-09-19 10:15:40 -04:00
parent dfeb5d1b45
commit 75591cac9a
5 changed files with 232 additions and 156 deletions

View file

@ -1,123 +0,0 @@
use std::{borrow::Borrow, collections::HashMap, rc::Rc};
use crate::eval::{Error, Expression};
pub type Environment = HashMap<String, Rc<Expression>>;
pub fn core_environment() -> Environment {
[
// Arithmetic operations
(
"+".to_string(),
Expression::NativeFunc {
func: |args| {
let res = args
.into_iter()
.reduce(|lhs, rhs| match (lhs.borrow(), rhs.borrow()) {
(Expression::Int(lhs), Expression::Int(rhs)) => {
Expression::Int(lhs + rhs).into()
}
_ => todo!(),
})
.unwrap_or(Rc::new(Expression::Int(0)));
Ok(res)
},
}
.into(),
),
(
"-".to_string(),
Expression::NativeFunc {
func: |args| {
let res = args
.into_iter()
.reduce(|lhs, rhs| match (lhs.borrow(), rhs.borrow()) {
(Expression::Int(lhs), Expression::Int(rhs)) => {
Expression::Int(lhs - rhs).into()
}
_ => todo!(),
})
.unwrap_or(Rc::new(Expression::Int(0)));
Ok(res)
},
}
.into(),
),
(
"*".to_string(),
Expression::NativeFunc {
func: |args| {
let res = args
.into_iter()
.reduce(|lhs, rhs| match (lhs.borrow(), rhs.borrow()) {
(Expression::Int(lhs), Expression::Int(rhs)) => {
Expression::Int(lhs * rhs).into()
}
_ => todo!(),
})
.unwrap_or(Rc::new(Expression::Int(0)));
Ok(res)
},
}
.into(),
),
(
"/".to_string(),
Expression::NativeFunc {
func: |args| {
let res = args
.into_iter()
.reduce(|lhs, rhs| match (lhs.borrow(), rhs.borrow()) {
(Expression::Int(lhs), Expression::Int(rhs)) => {
Expression::Int(lhs / rhs).into()
}
_ => todo!(),
})
.unwrap_or(Rc::new(Expression::Int(0)));
Ok(res)
},
}
.into(),
),
// Collections
(
"vector".to_string(),
Expression::NativeFunc {
func: |args| Ok(Rc::new(Expression::Vector(args))),
}
.into(),
),
(
"hashmap".to_string(),
Expression::NativeFunc {
func: |args| {
if args.len() % 2 != 0 {
Err(Error::MismatchedArgCount)?
}
let mut index = -1;
let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().partition(|_| {
index += 1;
index % 2 == 0
});
let res = keys
.into_iter()
// We turn the keys into strings because they're hashable
// This feels so hacky, but ¯\_(ツ)_/¯
.map(|key| key.to_string())
.zip(values.into_iter())
.collect();
Ok(Rc::new(Expression::HashMap(res)))
},
}
.into(),
),
]
.into()
}

186
src/evaluator/env.rs Normal file
View file

@ -0,0 +1,186 @@
use std::{borrow::Borrow, cell::RefCell, collections::HashMap, rc::Rc};
use super::{eval_ast_node, Error, Expression};
use crate::parser::Node;
pub type RawEnvironment = HashMap<String, Rc<Expression>>;
pub struct Environment {
current: RefCell<RawEnvironment>,
outer: Option<Rc<Environment>>,
}
impl Environment {
pub fn get(&self, ident: &str) -> Option<Rc<Expression>> {
if let Some(val) = self.current.borrow().get(ident) {
return Some(val.clone());
}
self.outer.clone()?.get(ident)
}
fn set(&self, ident: String, val: Rc<Expression>) {
self.current.borrow_mut().insert(ident, val);
}
}
pub fn core_environment() -> Rc<Environment> {
let env = [
// Arithmetic operations
(
"+".to_string(),
Expression::NativeFunc(|args| {
let res = args
.into_iter()
.reduce(|lhs, rhs| match (lhs.borrow(), rhs.borrow()) {
(Expression::Int(lhs), Expression::Int(rhs)) => {
Expression::Int(lhs + rhs).into()
}
_ => todo!(),
})
.unwrap_or(Rc::new(Expression::Int(0)));
Ok(res)
})
.into(),
),
(
"-".to_string(),
Expression::NativeFunc(|args| {
let res = args
.into_iter()
.reduce(|lhs, rhs| match (lhs.borrow(), rhs.borrow()) {
(Expression::Int(lhs), Expression::Int(rhs)) => {
Expression::Int(lhs - rhs).into()
}
_ => todo!(),
})
.unwrap_or(Rc::new(Expression::Int(0)));
Ok(res)
})
.into(),
),
(
"*".to_string(),
Expression::NativeFunc(|args| {
let res = args
.into_iter()
.reduce(|lhs, rhs| match (lhs.borrow(), rhs.borrow()) {
(Expression::Int(lhs), Expression::Int(rhs)) => {
Expression::Int(lhs * rhs).into()
}
_ => todo!(),
})
.unwrap_or(Rc::new(Expression::Int(0)));
Ok(res)
})
.into(),
),
(
"/".to_string(),
Expression::NativeFunc(|args| {
let res = args
.into_iter()
.reduce(|lhs, rhs| match (lhs.borrow(), rhs.borrow()) {
(Expression::Int(lhs), Expression::Int(rhs)) => {
Expression::Int(lhs / rhs).into()
}
_ => todo!(),
})
.unwrap_or(Rc::new(Expression::Int(0)));
Ok(res)
})
.into(),
),
// Collections
(
"vector".to_string(),
Expression::NativeFunc(|args| Ok(Rc::new(Expression::Vector(args)))).into(),
),
(
"hashmap".to_string(),
Expression::NativeFunc(|args| {
if args.len() % 2 != 0 {
Err(Error::MismatchedArgCount)?
}
let mut index = -1;
let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().partition(|_| {
index += 1;
index % 2 == 0
});
let res = keys
.into_iter()
// We turn the keys into strings because they're hashable
// This feels so hacky, but ¯\_(ツ)_/¯
.map(|key| key.to_string())
.zip(values.into_iter())
.collect();
Ok(Rc::new(Expression::HashMap(res)))
})
.into(),
),
// Environment Manipulation
(
"define!".to_string(),
Expression::Special(|env, args| {
let mut args = args.into_iter();
if args.len() != 2 {
Err(Error::MismatchedArgCount)?
}
let key = match args.next().unwrap() {
Node::Symbol(name) => name,
_ => Err(Error::ExpectedSymbol)?,
};
let val = eval_ast_node(env.clone(), args.next().unwrap())?;
env.set(key, val.clone());
Ok(val)
})
.into(),
),
(
"let*".to_string(),
Expression::Special(|env, args| {
if args.len() != 2 {
Err(Error::MismatchedArgCount)?
}
let mut args = args.into_iter();
let new_env = match args.next().unwrap() {
Node::List(list) if list.len() == 2 => {
let mut list = list;
let sym = match list.remove(0) {
Node::Symbol(name) => name,
_ => Err(Error::ExpectedSymbol)?,
};
let val = eval_ast_node(env.clone(), list.remove(0))?;
Environment {
current: RefCell::new([(sym.clone(), val)].into()),
outer: Some(env.clone()),
}
}
Node::List(_) => Err(Error::MismatchedArgCount)?,
_ => Err(Error::ExpectedList)?,
};
eval_ast_node(Rc::new(new_env), args.next().unwrap())
})
.into(),
),
];
Environment {
current: RefCell::new(env.into()),
outer: None,
}
.into()
}

View file

@ -3,7 +3,10 @@ use std::{borrow::Borrow, collections::HashMap, rc::Rc};
use anyhow::Result; use anyhow::Result;
use thiserror::Error; use thiserror::Error;
use crate::{env::Environment, parser::Node}; use crate::parser::Node;
mod env;
pub use env::{core_environment, Environment};
thread_local! { thread_local! {
static TRUE: Rc<Expression> = Rc::new(Expression::Boolean(true)); static TRUE: Rc<Expression> = Rc::new(Expression::Boolean(true));
@ -24,9 +27,8 @@ pub enum Expression {
Vector(Vec<Rc<Expression>>), Vector(Vec<Rc<Expression>>),
HashMap(HashMap<String, Rc<Expression>>), HashMap(HashMap<String, Rc<Expression>>),
NativeFunc { NativeFunc(fn(args: Vec<Rc<Expression>>) -> Result<Rc<Expression>>),
func: fn(args: Vec<Rc<Expression>>) -> Result<Rc<Expression>>, Special(fn(env: Rc<Environment>, args: Vec<Node>) -> Result<Rc<Expression>>),
},
} }
impl std::fmt::Display for Expression { impl std::fmt::Display for Expression {
@ -58,7 +60,8 @@ impl std::fmt::Display for Expression {
write!(f, "{{{res}}}") write!(f, "{{{res}}}")
} }
Expression::NativeFunc { .. } => write!(f, "function"), Expression::NativeFunc(func) => write!(f, "{func:?}"),
Expression::Special(func) => write!(f, "{func:?}"),
} }
} }
} }
@ -69,38 +72,45 @@ pub enum Error {
NotInEnv, NotInEnv,
#[error("expression does not have a valid operator")] #[error("expression does not have a valid operator")]
InvalidOperator, InvalidOperator,
#[error("expected symbol")]
ExpectedSymbol,
#[error("expected list")]
ExpectedList,
#[error("incorrect number of arguments passed to function")] #[error("incorrect number of arguments passed to function")]
MismatchedArgCount, MismatchedArgCount,
} }
pub fn eval(env: &Environment, ast: Vec<Node>) -> Result<Vec<Rc<Expression>>> { pub fn eval(env: Rc<Environment>, ast: Vec<Node>) -> Result<Vec<Rc<Expression>>> {
let mut res = Vec::new(); let mut res = Vec::new();
for node in ast { for node in ast {
res.push(eval_ast_node(env, node)?); res.push(eval_ast_node(env.clone(), node)?);
} }
Ok(res) Ok(res)
} }
fn eval_ast_node(env: &Environment, ast_node: Node) -> Result<Rc<Expression>> { fn eval_ast_node(env: Rc<Environment>, ast_node: Node) -> Result<Rc<Expression>> {
let expr = match ast_node { let expr = match ast_node {
Node::List(list) => { Node::List(list) => {
let mut res = Vec::new(); let mut list = list.into_iter();
for node in list { let operator = eval_ast_node(env.clone(), list.next().ok_or(Error::InvalidOperator)?)?;
res.push(eval_ast_node(env, node)?);
match operator.borrow() {
Expression::NativeFunc(func) => {
let mut args = Vec::new();
for node in list {
args.push(eval_ast_node(env.clone(), node)?);
}
func(args)?
}
Expression::Special(func) => {
let args = list.collect();
func(env, args)?
}
_ => Err(Error::InvalidOperator)?,
} }
let mut list = res.into_iter();
let operator = match list.next() {
Some(rc) => match rc.borrow() {
Expression::NativeFunc { func } => *func,
_ => Err(Error::InvalidOperator)?,
},
None => Err(Error::InvalidOperator)?,
};
operator(list.collect())?
} }
Node::Symbol(sym) => env.get(&sym).ok_or(Error::NotInEnv)?.to_owned(), Node::Symbol(sym) => env.get(&sym).ok_or(Error::NotInEnv)?.to_owned(),
@ -118,7 +128,6 @@ fn eval_ast_node(env: &Environment, ast_node: Node) -> Result<Rc<Expression>> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::core_environment;
use crate::lexer; use crate::lexer;
use crate::parser; use crate::parser;
@ -149,8 +158,11 @@ mod test {
#[case("[1 (+ 1 2)]", "[1 3]")] #[case("[1 (+ 1 2)]", "[1 3]")]
#[case("{}", "{}")] #[case("{}", "{}")]
#[case("{:a \"uwu\"}", "{:a: uwu}")] #[case("{:a \"uwu\"}", "{:a: uwu}")]
// Environment manipulation
#[case("(define! asdf (+ 2 2)) (+ asdf 2)", "4\n6")]
#[case("(let* (a 2) (+ a 2))", "4")]
fn test_evaluator(#[case] input: &str, #[case] expected: &str) { fn test_evaluator(#[case] input: &str, #[case] expected: &str) {
let env = &core_environment(); let env = core_environment();
let tokens = lexer::read(input).unwrap(); let tokens = lexer::read(input).unwrap();
let ast = parser::parse(tokens).unwrap(); let ast = parser::parse(tokens).unwrap();
let res = eval(env, ast) let res = eval(env, ast)
@ -167,7 +179,7 @@ mod test {
#[case("{:a}")] #[case("{:a}")]
#[case("(not-a-func :uwu)")] #[case("(not-a-func :uwu)")]
fn test_evaluator_fail(#[case] input: &str) { fn test_evaluator_fail(#[case] input: &str) {
let env = &core_environment(); let env = core_environment();
let tokens = lexer::read(input).unwrap(); let tokens = lexer::read(input).unwrap();
let ast = parser::parse(tokens).unwrap(); let ast = parser::parse(tokens).unwrap();
let res = eval(env, ast); let res = eval(env, ast);

View file

@ -108,7 +108,7 @@ fn next_token(input: &mut Peekable<Chars>) -> Result<Option<Token>> {
Some(tok) => tok, Some(tok) => tok,
None => return Ok(None), None => return Ok(None),
}, },
_ => bail!("ilegal token"), _ => bail!("illegal token"),
}; };
Ok(Some(tok)) Ok(Some(tok))
@ -164,10 +164,11 @@ fn read_int(input: &mut Peekable<Chars>, first: char) -> Token {
} }
fn read_ident(input: &mut Peekable<Chars>, first: char) -> Token { fn read_ident(input: &mut Peekable<Chars>, first: char) -> Token {
let special_characters = ['!', '?', '*', '-', '_'];
let mut raw_ident = vec![first]; let mut raw_ident = vec![first];
while let Some(c) = input.peek() { while let Some(c) = input.peek() {
if !c.is_ascii_alphanumeric() { if !c.is_ascii_alphanumeric() && !special_characters.contains(c) {
break; break;
} }
@ -198,6 +199,9 @@ mod test {
#[case("(/ 1 2)", vec![Token::LeftParen, Token::Slash, Token::Int(1), Token::Int(2), Token::RightParen])] #[case("(/ 1 2)", vec![Token::LeftParen, Token::Slash, Token::Int(1), Token::Int(2), Token::RightParen])]
#[case("(- -2 1)", vec![Token::LeftParen, Token::Minus, Token::Int(-2), Token::Int(1), Token::RightParen])] #[case("(- -2 1)", vec![Token::LeftParen, Token::Minus, Token::Int(-2), Token::Int(1), Token::RightParen])]
#[case("(\"string and stuff\")", vec![Token::LeftParen, Token::String("string and stuff".into()), Token::RightParen])] #[case("(\"string and stuff\")", vec![Token::LeftParen, Token::String("string and stuff".into()), Token::RightParen])]
#[case("define!", vec![Token::Ident("define!".into())])]
#[case("let*", vec![Token::Ident("let*".into())])]
#[case("is-int?", vec![Token::Ident("is-int?".into())])]
#[case( #[case(
"(func a b)", "(func a b)",
vec![ vec![

View file

@ -1,14 +1,11 @@
use std::io::{self, Write}; use std::io::{self, Write};
use crate::env::core_environment; mod evaluator;
mod env;
mod eval;
mod lexer; mod lexer;
mod parser; mod parser;
fn main() { fn main() {
let env = core_environment(); let env = evaluator::core_environment();
let mut input = String::new(); let mut input = String::new();
println!("MAL -- REPL"); println!("MAL -- REPL");
@ -27,7 +24,7 @@ fn main() {
let tokens = lexer::read(&input).unwrap(); let tokens = lexer::read(&input).unwrap();
let ast = parser::parse(tokens).unwrap(); let ast = parser::parse(tokens).unwrap();
let res = eval::eval(&env, ast).unwrap(); let res = evaluator::eval(env.clone(), ast).unwrap();
for expr in res { for expr in res {
println!("{expr}") println!("{expr}")