Functions!!!
No scoping yet though
This commit is contained in:
parent
59bae011a3
commit
43a6e4c1fd
5 changed files with 219 additions and 31 deletions
|
@ -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!("<function {}>", name),
|
||||
LispValue::RustFunction(name, _) => format!("<function {}>", 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();
|
||||
|
|
|
@ -7,16 +7,17 @@ use parser::*;
|
|||
mod executor;
|
||||
use executor::*;
|
||||
|
||||
fn main() -> Result<(), TokenizerError> {
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
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(())
|
||||
}
|
||||
|
|
159
src/parser.rs
159
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<Vec<PreLispValue>, 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<Token>, quoted: bool) -> PreLispValue {
|
|||
PreLispValue::List(values, quoted)
|
||||
}
|
||||
|
||||
pub fn parse(tokens: Vec<Token>) -> Vec<PreLispValue> {
|
||||
let mut values = Vec::new();
|
||||
pub struct Parser {
|
||||
tokens: Vec<Token>,
|
||||
macros: HashMap<String, fn(Vec<PreLispValue>) -> Vec<PreLispValue>>,
|
||||
}
|
||||
|
||||
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;
|
||||
fn add_macro(
|
||||
macros: &mut HashMap<String, fn(Vec<PreLispValue>) -> Vec<PreLispValue>>,
|
||||
name: impl Into<String>,
|
||||
mac: fn(Vec<PreLispValue>) -> Vec<PreLispValue>) {
|
||||
|
||||
macros.insert(name.into(), mac);
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
pub fn new(tokens: Vec<Token>) -> 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<PreLispValue> = &Vec::new();
|
||||
let mut list: &Vec<PreLispValue> = &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<PreLispValue>, quoted: bool) -> Vec<PreLispValue> {
|
||||
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<PreLispValue> {
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -1 +1,11 @@
|
|||
(print '(add 1 2))
|
||||
(fn hi (name) (
|
||||
(print (concat "Hello, " name "!"))
|
||||
))
|
||||
|
||||
(hi "Joe")
|
||||
|
||||
(hi (fn(x) (
|
||||
(print x)
|
||||
)))
|
||||
|
||||
(name "Balls")
|
|
@ -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();
|
||||
|
|
Reference in a new issue