Functions!!!

No scoping yet though
This commit is contained in:
Apache 2024-06-22 23:41:42 -05:00
parent 59bae011a3
commit 43a6e4c1fd
Signed by: apache
GPG key ID: 6B10F3EAF14F4C77
5 changed files with 219 additions and 31 deletions

View file

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

View file

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

View file

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

View file

@ -1 +1,11 @@
(print '(add 1 2))
(fn hi (name) (
(print (concat "Hello, " name "!"))
))
(hi "Joe")
(hi (fn(x) (
(print x)
)))
(name "Balls")

View file

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