Add simple tests

This commit is contained in:
Apache 2024-06-21 16:18:47 -05:00
parent 2f0744d5ee
commit a39914bc35
Signed by: apache
GPG key ID: 6B10F3EAF14F4C77
5 changed files with 336 additions and 96 deletions

View file

@ -1,16 +1,88 @@
use std::{collections::HashMap, fmt};
#[cfg(test)]
mod tests {
use crate::{parse, LispState, LispValue, Tokenizer, TokenizerError};
use super::add_function;
fn get_exec_result(source: &str) -> Result<Vec<LispValue>, TokenizerError> {
let mut state = LispState::new();
state.table.insert(String::from("output"), LispValue::List(vec![], false));
add_function(&mut state.table, "print", |st, args| {
if let LispValue::List(v, _) = st.table.get_mut("output").unwrap() {
v.push(LispValue::List(args, false))
}
vec![LispValue::Nil]
});
let mut tokenizer = Tokenizer::new(String::from(source));
let instructions = parse(tokenizer.tokenize()?);
state.execute(instructions);
let mut out = Vec::new();
if let LispValue::List(outerlist, _) = &state.table["output"] {
for list in outerlist {
if let LispValue::List(innerlist, _) = list {
for innervalue in innerlist {
out.push(innervalue.to_owned());
}
}
}
}
Ok(out)
}
#[test]
fn test_hello_world() -> Result<(), TokenizerError> {
assert_eq!(
vec![
LispValue::String(String::from("Hello, World!"))
],
get_exec_result("
(print \"Hello, World!\")
")?
);
Ok(())
}
#[test]
fn test_math() -> Result<(), TokenizerError> {
assert_eq!(
vec![
LispValue::Float(16.05)
],
get_exec_result("
(print
(add
(div
4.5
(sub 0.5 0.2)
)
(mul 21 0.05)
)
)
")?
);
Ok(())
}
}
use crate::PreLispValue;
pub struct LispState {
table: HashMap<String, LispValue>,
}
fn add_function<T: Into<String> + Clone>(table: &mut HashMap<String, LispValue>, name: T, func: fn(Vec<LispValue>) -> Vec<LispValue>) {
fn add_function<T: Into<String> + Clone>(table: &mut HashMap<String, LispValue>, name: T, func: fn(&mut LispState, Vec<LispValue>) -> Vec<LispValue>) {
table.insert(name.clone().into(), LispValue::RustFunction(name.into(), func));
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub enum LispValue {
Nil,
String(String),
@ -18,11 +90,12 @@ pub enum LispValue {
Float(f32),
List(Vec<LispValue>, bool),
LispFunction(String, Vec<LispValue>),
RustFunction(String, fn(Vec<LispValue>) -> Vec<LispValue>),
RustFunction(String, fn(&mut LispState, Vec<LispValue>) -> Vec<LispValue>),
}
impl fmt::Display for LispValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[allow(unreachable_patterns)]
match self {
LispValue::Nil => write!(f, "nil"),
LispValue::String(str) => write!(f, "\"{}\"", str),
@ -38,9 +111,9 @@ impl fmt::Display for LispValue {
}
if *quoted {
write!(f, "'({})", strs.join(", "))
write!(f, "'({})", strs.join(" "))
} else {
write!(f, "({})", strs.join(", "))
write!(f, "({})", strs.join(" "))
}
},
val => {
@ -75,9 +148,9 @@ impl LispState {
pub fn new() -> LispState {
let mut table = HashMap::new();
add_function(&mut table, "print", |x| {
add_function(&mut table, "print", |_, args| {
let mut strings = Vec::new();
for val in x {
for val in args {
strings.push(val.to_string());
}
@ -87,9 +160,9 @@ impl LispState {
vec![LispValue::Nil]
});
add_function(&mut table, "concat", |x| {
add_function(&mut table, "concat", |_, args| {
let mut strings = Vec::new();
for val in x {
for val in args {
strings.push(val.to_string());
}
@ -97,15 +170,15 @@ impl LispState {
vec![LispValue::String(str)]
});
add_function(&mut table, "add", |x| {
if x.len() < 1 {
add_function(&mut table, "add", |_, args| {
if args.len() < 1 {
return vec![LispValue::Nil]
} else if x.len() == 1 {
return vec![x[0].clone()];
} else if args.len() == 1 {
return vec![args[0].clone()];
}
let mut t = "int";
for v in &x {
for v in &args {
match v {
LispValue::Float(_) => t = "float",
LispValue::Integer(_) => (),
@ -116,7 +189,7 @@ impl LispState {
match t {
"int" => {
let mut sum = 0;
for v in &x {
for v in &args {
if let LispValue::Integer(n) = v {
sum += n;
} else {
@ -127,7 +200,7 @@ impl LispState {
},
"float" => {
let mut sum = 0.0 as f32;
for v in &x {
for v in &args {
if let LispValue::Float(n) = v {
sum += n;
} else if let LispValue::Integer(n) = v{
@ -142,15 +215,15 @@ impl LispState {
}
});
add_function(&mut table, "sub", |x| {
if x.len() < 1 {
add_function(&mut table, "sub", |_, args| {
if args.len() < 1 {
return vec![LispValue::Nil]
} else if x.len() == 1 {
return vec![x[0].clone()];
} else if args.len() == 1 {
return vec![args[0].clone()];
}
let mut t = "int";
for v in &x {
for v in &args {
match v {
LispValue::Float(_) => t = "float",
LispValue::Integer(_) => (),
@ -160,10 +233,10 @@ impl LispState {
match t {
"int" => {
if let LispValue::Integer(n) = x[0] {
if let LispValue::Integer(n) = args[0] {
let mut sum = n;
for i in 1..x.len() {
if let LispValue::Integer(n) = &x[i] {
for i in 1..args.len() {
if let LispValue::Integer(n) = &args[i] {
sum -= n;
} else {
unreachable!()
@ -175,24 +248,24 @@ impl LispState {
}
},
"float" => {
if let LispValue::Float(n) = x[0] {
if let LispValue::Float(n) = args[0] {
let mut sum = n;
for i in 1..x.len() {
if let LispValue::Float(n) = &x[i] {
for i in 1..args.len() {
if let LispValue::Float(n) = &args[i] {
sum -= *n;
} else if let LispValue::Integer(n) = &x[i] {
} else if let LispValue::Integer(n) = &args[i] {
sum -= *n as f32;
} else {
unreachable!()
}
}
return vec![LispValue::Float(sum)];
} else if let LispValue::Integer(n) = x[0] {
} else if let LispValue::Integer(n) = args[0] {
let mut sum = n as f32;
for i in 1..x.len() {
if let LispValue::Float(n) = &x[i] {
for i in 1..args.len() {
if let LispValue::Float(n) = &args[i] {
sum -= *n;
} else if let LispValue::Integer(n) = &x[i] {
} else if let LispValue::Integer(n) = &args[i] {
sum -= *n as f32;
} else {
unreachable!()
@ -207,15 +280,15 @@ impl LispState {
}
});
add_function(&mut table, "mul", |x| {
if x.len() < 1 {
add_function(&mut table, "mul", |_, args| {
if args.len() < 1 {
return vec![LispValue::Nil]
} else if x.len() == 1 {
return vec![x[0].clone()];
} else if args.len() == 1 {
return vec![args[0].clone()];
}
let mut t = "int";
for v in &x {
for v in &args {
match v {
LispValue::Float(_) => t = "float",
LispValue::Integer(_) => (),
@ -225,10 +298,10 @@ impl LispState {
match t {
"int" => {
if let LispValue::Integer(n) = x[0] {
if let LispValue::Integer(n) = args[0] {
let mut sum = n;
for i in 1..x.len() {
if let LispValue::Integer(n) = &x[i] {
for i in 1..args.len() {
if let LispValue::Integer(n) = &args[i] {
sum *= n;
} else {
unreachable!()
@ -240,24 +313,24 @@ impl LispState {
}
},
"float" => {
if let LispValue::Float(n) = x[0] {
if let LispValue::Float(n) = args[0] {
let mut product = n;
for i in 1..x.len() {
if let LispValue::Float(n) = &x[i] {
for i in 1..args.len() {
if let LispValue::Float(n) = &args[i] {
product *= *n;
} else if let LispValue::Integer(n) = &x[i] {
} else if let LispValue::Integer(n) = &args[i] {
product *= *n as f32;
} else {
unreachable!()
}
}
return vec![LispValue::Float(product)];
} else if let LispValue::Integer(n) = x[0] {
} else if let LispValue::Integer(n) = args[0] {
let mut product = n as f32;
for i in 1..x.len() {
if let LispValue::Float(n) = &x[i] {
for i in 1..args.len() {
if let LispValue::Float(n) = &args[i] {
product *= *n;
} else if let LispValue::Integer(n) = &x[i] {
} else if let LispValue::Integer(n) = &args[i] {
product *= *n as f32;
} else {
unreachable!()
@ -272,15 +345,15 @@ impl LispState {
}
});
add_function(&mut table, "div", |x| {
if x.len() < 1 {
add_function(&mut table, "div", |_, args| {
if args.len() < 1 {
return vec![LispValue::Nil]
} else if x.len() == 1 {
return vec![x[0].clone()];
} else if args.len() == 1 {
return vec![args[0].clone()];
}
let mut t = "int";
for v in &x {
for v in &args {
match v {
LispValue::Float(_) => t = "float",
LispValue::Integer(_) => (),
@ -290,10 +363,10 @@ impl LispState {
match t {
"int" => {
if let LispValue::Integer(n) = x[0] {
if let LispValue::Integer(n) = args[0] {
let mut sum = n;
for i in 1..x.len() {
if let LispValue::Integer(n) = &x[i] {
for i in 1..args.len() {
if let LispValue::Integer(n) = &args[i] {
sum /= n;
} else {
unreachable!()
@ -305,24 +378,24 @@ impl LispState {
}
},
"float" => {
if let LispValue::Float(n) = x[0] {
if let LispValue::Float(n) = args[0] {
let mut product = n;
for i in 1..x.len() {
if let LispValue::Float(n) = &x[i] {
for i in 1..args.len() {
if let LispValue::Float(n) = &args[i] {
product /= *n;
} else if let LispValue::Integer(n) = &x[i] {
} else if let LispValue::Integer(n) = &args[i] {
product /= *n as f32;
} else {
unreachable!()
}
}
return vec![LispValue::Float(product)];
} else if let LispValue::Integer(n) = x[0] {
} else if let LispValue::Integer(n) = args[0] {
let mut product = n as f32;
for i in 1..x.len() {
if let LispValue::Float(n) = &x[i] {
for i in 1..args.len() {
if let LispValue::Float(n) = &args[i] {
product /= *n;
} else if let LispValue::Integer(n) = &x[i] {
} else if let LispValue::Integer(n) = &args[i] {
product /= *n as f32;
} else {
unreachable!()
@ -366,7 +439,7 @@ impl LispState {
out
}
fn eval_list(list: &Vec<LispValue>, quoted: bool) -> (Vec<LispValue>, bool) {
fn eval_list(&mut self, list: &Vec<LispValue>, quoted: bool) -> (Vec<LispValue>, bool) {
if list.len() < 1 {
return (vec![LispValue::List(Vec::new(), quoted)], false);
}
@ -375,7 +448,7 @@ impl LispState {
for i in 0..list.len() {
if let LispValue::List(elements, quoted) = &list[i] {
let (elems, astuple) = Self::eval_list(elements, *quoted);
let (elems, astuple) = self.eval_list(elements, *quoted);
if astuple {
list[i] = elems.get(0).unwrap_or(&LispValue::Nil).to_owned();
for j in 1..elems.len() {
@ -392,23 +465,23 @@ impl LispState {
}
if let LispValue::RustFunction(_name, f) = &list[0] {
(f(list[1..].to_vec()), true)
} else if let LispValue::LispFunction(_name, ins) = &list[0] {
(Self::call_lisp_func(&list[0], list[1..].to_vec()), true)
(f(self, list[1..].to_vec()), true)
} else if let LispValue::LispFunction(_name, _) = &list[0] {
(self.call_lisp_func(&list[0], list[1..].to_vec()), true)
} else {
(list.to_vec(), false)
}
}
fn call_lisp_func(f: &LispValue, args: Vec<LispValue>) -> Vec<LispValue> {
todo!()
fn call_lisp_func(&mut self, f: &LispValue, args: Vec<LispValue>) -> Vec<LispValue> {
todo!("Calling lisp function {} with args {:?}", f, args)
}
pub fn execute(&self, instructions: Vec<PreLispValue>) {
pub fn execute(&mut self, instructions: Vec<PreLispValue>) {
let instructions = Self::handle_prevalues(self, instructions);
for val in instructions {
if let LispValue::List(elements, quoted) = val {
Self::eval_list(&elements, quoted);
self.eval_list(&elements, quoted);
}
}
}

View file

@ -7,20 +7,16 @@ use parser::*;
mod executor;
use executor::*;
fn main() {
fn main() -> Result<(), TokenizerError> {
let source = std::fs::read_to_string("src/test.lisp").unwrap();
let mut tokenizer = Tokenizer::new(source);
let tokens = match tokenizer.tokenize() {
Ok(tokens) => tokens,
Err(e) => {
println!("{}", e);
Vec::new()
}
};
let tokens = tokenizer.tokenize()?;
let instructions = parse(tokens);
let state = LispState::new();
let mut state = LispState::new();
state.execute(instructions);
Ok(())
}

View file

@ -1,8 +1,91 @@
use std::fmt;
#[cfg(test)]
mod tests {
use crate::{parse, 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))
}
fn var(name: &str) -> PreLispValue {
PreLispValue::Var(String::from(name))
}
fn string(s: &str) -> PreLispValue {
PreLispValue::String(String::from(s))
}
fn list(v: Vec<PreLispValue>, quoted: bool) -> PreLispValue {
PreLispValue::List(v, quoted)
}
fn int(n: i32) -> PreLispValue {
PreLispValue::Integer(n)
}
fn float(n: f32) -> PreLispValue {
PreLispValue::Float(n)
}
#[test]
fn test_hello_world() -> Result<(), TokenizerError> {
assert_eq!(
vec![
list(vec![
var("print"),
string("Hello, World!")
], false)
],
quick_parse("
(print \"Hello, World!\")
")?
);
Ok(())
}
#[test]
fn test_math() -> Result<(), TokenizerError> {
assert_eq!(
vec![
list(vec![
var("print"),
list(vec![
var("add"),
list(vec![
var("div"),
float(4.5),
list(vec![
var("sub"),
float(0.5),
float(0.2)
], false)
], false),
list(vec![
var("mul"),
int(21),
float(0.05)
], false)
], false)
], false)
],
quick_parse("
(print
(add
(div
4.5
(sub 0.5 0.2)
)
(mul 21 0.05)
)
)
")?
);
Ok(())
}
}
use crate::Token;
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub enum PreLispValue {
Nil,
String(String),
@ -110,7 +193,11 @@ fn parse_exp(tokens: Vec<Token>, quoted: bool) -> PreLispValue {
unquote_next_list = false;
},
Token::Identifier(name) => {
if name == &String::from("nil") {
values.push(PreLispValue::Nil)
} else {
values.push(PreLispValue::Var(name.to_owned()));
}
},
Token::Integer(num) => {
values.push(PreLispValue::Integer(*num));

View file

@ -1,13 +1,11 @@
; This is a comment
(print "Hello, world!")
(print (1 2 3))
(print
(add 15 105.3)
)
(print
'(add 15 105.3)
(add
(div
4.5
(sub 0.5 0.2)
)
(mul 21 0.05)
)
)

View file

@ -1,6 +1,92 @@
use std::fmt;
#[derive(Debug, Clone)]
#[cfg(test)]
mod tests {
use crate::{Token, Tokenizer, TokenizerError};
fn quick_tokenize(source: &str) -> Result<Vec<Token>, TokenizerError> {
let mut tokenizer = Tokenizer::new(String::from(source));
return tokenizer.tokenize();
}
fn open() -> Token {
Token::OpenParen
}
fn close() -> Token {
Token::CloseParen
}
fn iden(s: &str) -> Token {
Token::Identifier(String::from(s))
}
fn string(s: &str) -> Token {
Token::String(String::from(s))
}
fn int(n: i32) -> Token {
Token::Integer(n)
}
fn float(n: f32) -> Token {
Token::Float(n)
}
#[test]
fn test_hello_world() -> Result<(), TokenizerError> {
assert_eq!(
vec![
open(),
iden("print"),
string("Hello, World!"),
close()
],
quick_tokenize("
(print \"Hello, World!\")
")?
);
Ok(())
}
#[test]
fn test_math() -> Result<(), TokenizerError> {
assert_eq!(
vec![
open(),
iden("print"),
open(),
iden("add"),
open(),
iden("div"),
float(4.5),
open(),
iden("sub"),
float(0.5),
float(0.2),
close(),
close(),
open(),
iden("mul"),
int(21),
float(0.05),
close(),
close(),
close()
],
quick_tokenize("
(print
(add
(div
4.5
(sub 0.5 0.2)
)
(mul 21 0.05)
)
)
")?
);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Token {
OpenParen,
CloseParen,
@ -12,7 +98,7 @@ pub enum Token {
Float(f32)
}
#[derive(Debug)]
pub struct TokenizerError {
line: u64,
column: u64,