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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{parse, LispState, LispValue, Tokenizer};
|
use crate::{Parser, LispState, LispValue, Tokenizer};
|
||||||
|
|
||||||
use super::add_function;
|
use super::add_function;
|
||||||
|
|
||||||
|
@ -21,7 +21,8 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut tokenizer = Tokenizer::new(String::from(source));
|
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)?;
|
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 {
|
fn get_type(v: &LispValue) -> String {
|
||||||
match v {
|
match v {
|
||||||
LispValue::Nil => String::from("nil"),
|
LispValue::Nil => String::from("nil"),
|
||||||
|
@ -183,10 +212,32 @@ impl LispState {
|
||||||
pub fn new() -> LispState {
|
pub fn new() -> LispState {
|
||||||
let mut table = HashMap::new();
|
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| {
|
add_function(&mut table, "print", |_, args| {
|
||||||
let mut strings = Vec::new();
|
let mut strings = Vec::new();
|
||||||
for val in args {
|
for val in args {
|
||||||
strings.push(val.to_string());
|
strings.push(lisp_display(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
let str = strings.join(" ");
|
let str = strings.join(" ");
|
||||||
|
@ -198,7 +249,7 @@ impl LispState {
|
||||||
add_function(&mut table, "concat", |_, args| {
|
add_function(&mut table, "concat", |_, args| {
|
||||||
let mut strings = Vec::new();
|
let mut strings = Vec::new();
|
||||||
for val in args {
|
for val in args {
|
||||||
strings.push(val.to_string());
|
strings.push(lisp_display(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
let str = strings.concat();
|
let str = strings.concat();
|
||||||
|
|
|
@ -7,16 +7,17 @@ use parser::*;
|
||||||
mod executor;
|
mod executor;
|
||||||
use 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 source = std::fs::read_to_string("src/test.lisp").unwrap();
|
||||||
|
|
||||||
let mut tokenizer = Tokenizer::new(source);
|
let mut tokenizer = Tokenizer::new(source);
|
||||||
let tokens = tokenizer.tokenize()?;
|
let tokens = tokenizer.tokenize()?;
|
||||||
let instructions = parse(tokens);
|
let mut parser = Parser::new(tokens);
|
||||||
|
let instructions = parser.parse();
|
||||||
|
|
||||||
let mut state = LispState::new();
|
let mut state = LispState::new();
|
||||||
|
|
||||||
state.execute(instructions);
|
state.execute(instructions)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
161
src/parser.rs
161
src/parser.rs
|
@ -1,13 +1,14 @@
|
||||||
use std::fmt;
|
use std::{collections::HashMap, fmt};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{parse, PreLispValue, Tokenizer, TokenizerError};
|
use crate::{Parser, PreLispValue, Tokenizer, TokenizerError};
|
||||||
|
|
||||||
fn quick_parse(source: &str) -> Result<Vec<PreLispValue>, TokenizerError> {
|
fn quick_parse(source: &str) -> Result<Vec<PreLispValue>, TokenizerError> {
|
||||||
let mut tokenizer = Tokenizer::new(String::from(source));
|
let mut tokenizer = Tokenizer::new(String::from(source));
|
||||||
let tokens = tokenizer.tokenize()?;
|
let tokens = tokenizer.tokenize()?;
|
||||||
Ok(parse(tokens))
|
let mut parser = Parser::new(tokens);
|
||||||
|
Ok(parser.parse())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn var(name: &str) -> PreLispValue {
|
fn var(name: &str) -> PreLispValue {
|
||||||
|
@ -218,24 +219,148 @@ fn parse_exp(tokens: Vec<Token>, quoted: bool) -> PreLispValue {
|
||||||
PreLispValue::List(values, quoted)
|
PreLispValue::List(values, quoted)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(tokens: Vec<Token>) -> Vec<PreLispValue> {
|
pub struct Parser {
|
||||||
let mut values = Vec::new();
|
tokens: Vec<Token>,
|
||||||
|
macros: HashMap<String, fn(Vec<PreLispValue>) -> Vec<PreLispValue>>,
|
||||||
let mut i = 0;
|
}
|
||||||
while i < tokens.len() {
|
|
||||||
match &tokens[i] {
|
fn add_macro(
|
||||||
Token::OpenParen => {
|
macros: &mut HashMap<String, fn(Vec<PreLispValue>) -> Vec<PreLispValue>>,
|
||||||
let (tkns, close_paren_idx) = read_exp(i, &tokens).unwrap();
|
name: impl Into<String>,
|
||||||
values.push(parse_exp(tkns, false));
|
mac: fn(Vec<PreLispValue>) -> Vec<PreLispValue>) {
|
||||||
i = close_paren_idx;
|
|
||||||
|
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
|
let mut list = x.clone();
|
||||||
panic!("Unexpected token {:?}", tkn);
|
|
||||||
|
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 self.reading_identifier {
|
||||||
if !char.is_alphabetic() {
|
if char.is_alphabetic() {
|
||||||
|
self.storage.push(char);
|
||||||
|
} else {
|
||||||
self.reading_identifier = false;
|
self.reading_identifier = false;
|
||||||
|
|
||||||
tokens.push(Token::Identifier(self.storage.iter().collect()));
|
tokens.push(Token::Identifier(self.storage.iter().collect()));
|
||||||
self.storage.clear();
|
self.storage.clear();
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.storage.push(char);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.reading_num {
|
if self.reading_num {
|
||||||
|
@ -259,6 +257,9 @@ impl Tokenizer {
|
||||||
'\'' => tokens.push(Token::Quote),
|
'\'' => tokens.push(Token::Quote),
|
||||||
',' => tokens.push(Token::Unquote),
|
',' => tokens.push(Token::Unquote),
|
||||||
c => {
|
c => {
|
||||||
|
if self.reading_identifier || self.reading_num || self.reading_string {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if c.is_alphabetic() {
|
if c.is_alphabetic() {
|
||||||
self.reading_identifier = true;
|
self.reading_identifier = true;
|
||||||
self.storage.clear();
|
self.storage.clear();
|
||||||
|
|
Reference in a new issue