diff --git a/src/executor.rs b/src/executor.rs index 19254b1..adc7ccb 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -4,7 +4,7 @@ use crate::PreLispValue; #[cfg(test)] mod tests { - use crate::{parse, LispState, LispValue, Tokenizer}; + use crate::{Parser, LispState, LispValue, Tokenizer}; use super::add_function; @@ -21,7 +21,8 @@ mod tests { }); let mut tokenizer = Tokenizer::new(String::from(source)); - let instructions = parse(tokenizer.tokenize()?); + let mut parser = Parser::new(tokenizer.tokenize()?); + let instructions = parser.parse(); state.execute(instructions)?; @@ -136,6 +137,34 @@ impl fmt::Display for LispValue { } } +fn lisp_display(v: LispValue) -> String { + #[allow(unreachable_patterns)] + match v { + LispValue::Nil => String::from("nil"), + LispValue::String(str) => str, + LispValue::LispFunction(name, _, _) => format!("", name), + LispValue::RustFunction(name, _) => format!("", name), + LispValue::Integer(num) => num.to_string(), + LispValue::Float(num) => num.to_string(), + LispValue::List(elems, quoted) => { + let mut strs = Vec::new(); + + for e in elems { + strs.push(e.to_string()); + } + + if quoted { + format!("'({})", strs.join(" ")) + } else { + format!("({})", strs.join(" ")) + } + }, + val => { + format!("{}", val) + } + } +} + fn get_type(v: &LispValue) -> String { match v { LispValue::Nil => String::from("nil"), @@ -183,10 +212,32 @@ impl LispState { pub fn new() -> LispState { let mut table = HashMap::new(); + add_function(&mut table, "echo", |_, args| { + return Ok(args); + }); + + add_function(&mut table, "set", |state, args| { + if args.len() != 2 { + return Err(RuntimeError { + message: format!("Bad arguments to set (expected 2, got {})", args.len()) + }) + } + + if let LispValue::String(name) = &args[0] { + state.table.insert(name.to_owned(), args[1].clone()); + } else { + return Err(RuntimeError { + message: format!("Bad argument 1 to set (expected string, got {})", get_type(&args[0])) + }) + } + + Ok(vec![args[1].clone()]) + }); + add_function(&mut table, "print", |_, args| { let mut strings = Vec::new(); for val in args { - strings.push(val.to_string()); + strings.push(lisp_display(val)); } let str = strings.join(" "); @@ -198,7 +249,7 @@ impl LispState { add_function(&mut table, "concat", |_, args| { let mut strings = Vec::new(); for val in args { - strings.push(val.to_string()); + strings.push(lisp_display(val)); } let str = strings.concat(); diff --git a/src/main.rs b/src/main.rs index 1430ecc..17dafaa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,16 +7,17 @@ use parser::*; mod executor; use executor::*; -fn main() -> Result<(), TokenizerError> { +fn main() -> Result<(), Box> { let source = std::fs::read_to_string("src/test.lisp").unwrap(); let mut tokenizer = Tokenizer::new(source); let tokens = tokenizer.tokenize()?; - let instructions = parse(tokens); + let mut parser = Parser::new(tokens); + let instructions = parser.parse(); let mut state = LispState::new(); - state.execute(instructions); + state.execute(instructions)?; Ok(()) } diff --git a/src/parser.rs b/src/parser.rs index c59f27b..e397464 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,13 +1,14 @@ -use std::fmt; +use std::{collections::HashMap, fmt}; #[cfg(test)] mod tests { - use crate::{parse, PreLispValue, Tokenizer, TokenizerError}; + use crate::{Parser, PreLispValue, Tokenizer, TokenizerError}; fn quick_parse(source: &str) -> Result, TokenizerError> { let mut tokenizer = Tokenizer::new(String::from(source)); let tokens = tokenizer.tokenize()?; - Ok(parse(tokens)) + let mut parser = Parser::new(tokens); + Ok(parser.parse()) } fn var(name: &str) -> PreLispValue { @@ -218,24 +219,148 @@ fn parse_exp(tokens: Vec, quoted: bool) -> PreLispValue { PreLispValue::List(values, quoted) } -pub fn parse(tokens: Vec) -> Vec { - let mut values = Vec::new(); - - let mut i = 0; - while i < tokens.len() { - match &tokens[i] { - Token::OpenParen => { - let (tkns, close_paren_idx) = read_exp(i, &tokens).unwrap(); - values.push(parse_exp(tkns, false)); - i = close_paren_idx; +pub struct Parser { + tokens: Vec, + macros: HashMap) -> Vec>, +} + +fn add_macro( + macros: &mut HashMap) -> Vec>, + name: impl Into, + mac: fn(Vec) -> Vec) { + + macros.insert(name.into(), mac); +} + +impl Parser { + pub fn new(tokens: Vec) -> Parser { + let mut macros = HashMap::new(); + + add_macro(&mut macros, "set", |x| { + if x.len() != 3 || + ( + !matches!(x[1], PreLispValue::Var(_)) && + !matches!(x[1], PreLispValue::String(_)) + ) { + return x; } - tkn => { - // TODO: Include more error data - panic!("Unexpected token {:?}", tkn); + + let mut list = x.clone(); + + if let PreLispValue::Var(name) = &list[1] { + list[1] = PreLispValue::String(name.to_owned()) } + + list + }); + + add_macro(&mut macros, "fn", |x| { + if x.len() < 3 || x.len() > 4 { + return x; + } + + let mut name: String = String::new(); + let mut args: &Vec = &Vec::new(); + let mut list: &Vec = &Vec::new(); + + if x.len() == 3 { + if + !matches!(x[1], PreLispValue::List(_, _)) || + !matches!(x[2], PreLispValue::List(_, _)) + { + return x; + } + name = String::from("Anonymous"); + if let PreLispValue::List(l, _) = &x[1] { + args = l; + } + if let PreLispValue::List(l, _) = &x[2] { + list = l; + } + } else if x.len() == 4 { + if + !matches!(x[1], PreLispValue::Var(_)) || + !matches!(x[2], PreLispValue::List(_, _)) || + !matches!(x[3], PreLispValue::List(_, _)) + { + return x; + } + if let PreLispValue::Var(n) = &x[1] { + name = n.to_owned(); + } + if let PreLispValue::List(l, _) = &x[2] { + args = l; + } + if let PreLispValue::List(l, _) = &x[3] { + list = l; + } + } else { + return x; + } + + let mut arg_names = Vec::new(); + for arg in args { + if let PreLispValue::Var(n) = arg { + arg_names.push(n.to_owned()); + } + } + + let func = PreLispValue::LispFunction(name.clone(), arg_names, list.to_owned()); + + if x.len() == 4 { + return vec![PreLispValue::Var(String::from("set")), name.into(), func] + } + + return vec![PreLispValue::Var(String::from("echo")), func]; + }); + + Parser { + tokens, + macros } - i += 1; } - values + fn macro_check(&mut self, input: Vec, quoted: bool) -> Vec { + if quoted || input.len() < 1 { + return input; + } + + let mut output = input.clone(); + + for i in 0..output.len() { + if let PreLispValue::List(elems, quoted) = &output[i] { + output[i] = PreLispValue::List(self.macro_check(elems.to_vec(), *quoted), *quoted); + } + } + + if let PreLispValue::Var(name) = &output[0] { + if self.macros.contains_key(name) { + return self.macros.get(name).unwrap()(output); + } + } + + output + } + + pub fn parse(&mut self) -> Vec { + let mut values = Vec::new(); + + let mut i = 0; + while i < self.tokens.len() { + match &self.tokens[i] { + Token::OpenParen => { + let (tkns, close_paren_idx) = read_exp(i, &self.tokens).unwrap(); + values.push(parse_exp(tkns, false)); + i = close_paren_idx; + } + tkn => { + // TODO: Include more error data + panic!("Unexpected token {:?}", tkn); + } + } + i += 1; + } + + self.macro_check(values, false) + } } \ No newline at end of file diff --git a/src/test.lisp b/src/test.lisp index f162e02..0347f2d 100644 --- a/src/test.lisp +++ b/src/test.lisp @@ -1 +1,11 @@ -(print '(add 1 2)) \ No newline at end of file +(fn hi (name) ( + (print (concat "Hello, " name "!")) +)) + +(hi "Joe") + +(hi (fn(x) ( + (print x) +))) + +(name "Balls") \ No newline at end of file diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 7a5ac25..692221f 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -180,16 +180,14 @@ impl Tokenizer { } if self.reading_identifier { - if !char.is_alphabetic() { + if char.is_alphabetic() { + self.storage.push(char); + } else { self.reading_identifier = false; tokens.push(Token::Identifier(self.storage.iter().collect())); self.storage.clear(); - continue; } - - self.storage.push(char); - continue; } if self.reading_num { @@ -259,6 +257,9 @@ impl Tokenizer { '\'' => tokens.push(Token::Quote), ',' => tokens.push(Token::Unquote), c => { + if self.reading_identifier || self.reading_num || self.reading_string { + continue; + } if c.is_alphabetic() { self.reading_identifier = true; self.storage.clear();