Add simple tests
This commit is contained in:
parent
2f0744d5ee
commit
a39914bc35
5 changed files with 336 additions and 96 deletions
219
src/executor.rs
219
src/executor.rs
|
@ -1,16 +1,88 @@
|
||||||
use std::{collections::HashMap, fmt};
|
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;
|
use crate::PreLispValue;
|
||||||
|
|
||||||
pub struct LispState {
|
pub struct LispState {
|
||||||
table: HashMap<String, LispValue>,
|
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));
|
table.insert(name.clone().into(), LispValue::RustFunction(name.into(), func));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum LispValue {
|
pub enum LispValue {
|
||||||
Nil,
|
Nil,
|
||||||
String(String),
|
String(String),
|
||||||
|
@ -18,11 +90,12 @@ pub enum LispValue {
|
||||||
Float(f32),
|
Float(f32),
|
||||||
List(Vec<LispValue>, bool),
|
List(Vec<LispValue>, bool),
|
||||||
LispFunction(String, Vec<LispValue>),
|
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 {
|
impl fmt::Display for LispValue {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
match self {
|
match self {
|
||||||
LispValue::Nil => write!(f, "nil"),
|
LispValue::Nil => write!(f, "nil"),
|
||||||
LispValue::String(str) => write!(f, "\"{}\"", str),
|
LispValue::String(str) => write!(f, "\"{}\"", str),
|
||||||
|
@ -38,9 +111,9 @@ impl fmt::Display for LispValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
if *quoted {
|
if *quoted {
|
||||||
write!(f, "'({})", strs.join(", "))
|
write!(f, "'({})", strs.join(" "))
|
||||||
} else {
|
} else {
|
||||||
write!(f, "({})", strs.join(", "))
|
write!(f, "({})", strs.join(" "))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
val => {
|
val => {
|
||||||
|
@ -75,9 +148,9 @@ impl LispState {
|
||||||
pub fn new() -> LispState {
|
pub fn new() -> LispState {
|
||||||
let mut table = HashMap::new();
|
let mut table = HashMap::new();
|
||||||
|
|
||||||
add_function(&mut table, "print", |x| {
|
add_function(&mut table, "print", |_, args| {
|
||||||
let mut strings = Vec::new();
|
let mut strings = Vec::new();
|
||||||
for val in x {
|
for val in args {
|
||||||
strings.push(val.to_string());
|
strings.push(val.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,9 +160,9 @@ impl LispState {
|
||||||
vec![LispValue::Nil]
|
vec![LispValue::Nil]
|
||||||
});
|
});
|
||||||
|
|
||||||
add_function(&mut table, "concat", |x| {
|
add_function(&mut table, "concat", |_, args| {
|
||||||
let mut strings = Vec::new();
|
let mut strings = Vec::new();
|
||||||
for val in x {
|
for val in args {
|
||||||
strings.push(val.to_string());
|
strings.push(val.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,15 +170,15 @@ impl LispState {
|
||||||
vec![LispValue::String(str)]
|
vec![LispValue::String(str)]
|
||||||
});
|
});
|
||||||
|
|
||||||
add_function(&mut table, "add", |x| {
|
add_function(&mut table, "add", |_, args| {
|
||||||
if x.len() < 1 {
|
if args.len() < 1 {
|
||||||
return vec![LispValue::Nil]
|
return vec![LispValue::Nil]
|
||||||
} else if x.len() == 1 {
|
} else if args.len() == 1 {
|
||||||
return vec![x[0].clone()];
|
return vec![args[0].clone()];
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut t = "int";
|
let mut t = "int";
|
||||||
for v in &x {
|
for v in &args {
|
||||||
match v {
|
match v {
|
||||||
LispValue::Float(_) => t = "float",
|
LispValue::Float(_) => t = "float",
|
||||||
LispValue::Integer(_) => (),
|
LispValue::Integer(_) => (),
|
||||||
|
@ -116,7 +189,7 @@ impl LispState {
|
||||||
match t {
|
match t {
|
||||||
"int" => {
|
"int" => {
|
||||||
let mut sum = 0;
|
let mut sum = 0;
|
||||||
for v in &x {
|
for v in &args {
|
||||||
if let LispValue::Integer(n) = v {
|
if let LispValue::Integer(n) = v {
|
||||||
sum += n;
|
sum += n;
|
||||||
} else {
|
} else {
|
||||||
|
@ -127,7 +200,7 @@ impl LispState {
|
||||||
},
|
},
|
||||||
"float" => {
|
"float" => {
|
||||||
let mut sum = 0.0 as f32;
|
let mut sum = 0.0 as f32;
|
||||||
for v in &x {
|
for v in &args {
|
||||||
if let LispValue::Float(n) = v {
|
if let LispValue::Float(n) = v {
|
||||||
sum += n;
|
sum += n;
|
||||||
} else if let LispValue::Integer(n) = v{
|
} else if let LispValue::Integer(n) = v{
|
||||||
|
@ -142,15 +215,15 @@ impl LispState {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
add_function(&mut table, "sub", |x| {
|
add_function(&mut table, "sub", |_, args| {
|
||||||
if x.len() < 1 {
|
if args.len() < 1 {
|
||||||
return vec![LispValue::Nil]
|
return vec![LispValue::Nil]
|
||||||
} else if x.len() == 1 {
|
} else if args.len() == 1 {
|
||||||
return vec![x[0].clone()];
|
return vec![args[0].clone()];
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut t = "int";
|
let mut t = "int";
|
||||||
for v in &x {
|
for v in &args {
|
||||||
match v {
|
match v {
|
||||||
LispValue::Float(_) => t = "float",
|
LispValue::Float(_) => t = "float",
|
||||||
LispValue::Integer(_) => (),
|
LispValue::Integer(_) => (),
|
||||||
|
@ -160,10 +233,10 @@ impl LispState {
|
||||||
|
|
||||||
match t {
|
match t {
|
||||||
"int" => {
|
"int" => {
|
||||||
if let LispValue::Integer(n) = x[0] {
|
if let LispValue::Integer(n) = args[0] {
|
||||||
let mut sum = n;
|
let mut sum = n;
|
||||||
for i in 1..x.len() {
|
for i in 1..args.len() {
|
||||||
if let LispValue::Integer(n) = &x[i] {
|
if let LispValue::Integer(n) = &args[i] {
|
||||||
sum -= n;
|
sum -= n;
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
@ -175,24 +248,24 @@ impl LispState {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"float" => {
|
"float" => {
|
||||||
if let LispValue::Float(n) = x[0] {
|
if let LispValue::Float(n) = args[0] {
|
||||||
let mut sum = n;
|
let mut sum = n;
|
||||||
for i in 1..x.len() {
|
for i in 1..args.len() {
|
||||||
if let LispValue::Float(n) = &x[i] {
|
if let LispValue::Float(n) = &args[i] {
|
||||||
sum -= *n;
|
sum -= *n;
|
||||||
} else if let LispValue::Integer(n) = &x[i] {
|
} else if let LispValue::Integer(n) = &args[i] {
|
||||||
sum -= *n as f32;
|
sum -= *n as f32;
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return vec![LispValue::Float(sum)];
|
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;
|
let mut sum = n as f32;
|
||||||
for i in 1..x.len() {
|
for i in 1..args.len() {
|
||||||
if let LispValue::Float(n) = &x[i] {
|
if let LispValue::Float(n) = &args[i] {
|
||||||
sum -= *n;
|
sum -= *n;
|
||||||
} else if let LispValue::Integer(n) = &x[i] {
|
} else if let LispValue::Integer(n) = &args[i] {
|
||||||
sum -= *n as f32;
|
sum -= *n as f32;
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
@ -207,15 +280,15 @@ impl LispState {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
add_function(&mut table, "mul", |x| {
|
add_function(&mut table, "mul", |_, args| {
|
||||||
if x.len() < 1 {
|
if args.len() < 1 {
|
||||||
return vec![LispValue::Nil]
|
return vec![LispValue::Nil]
|
||||||
} else if x.len() == 1 {
|
} else if args.len() == 1 {
|
||||||
return vec![x[0].clone()];
|
return vec![args[0].clone()];
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut t = "int";
|
let mut t = "int";
|
||||||
for v in &x {
|
for v in &args {
|
||||||
match v {
|
match v {
|
||||||
LispValue::Float(_) => t = "float",
|
LispValue::Float(_) => t = "float",
|
||||||
LispValue::Integer(_) => (),
|
LispValue::Integer(_) => (),
|
||||||
|
@ -225,10 +298,10 @@ impl LispState {
|
||||||
|
|
||||||
match t {
|
match t {
|
||||||
"int" => {
|
"int" => {
|
||||||
if let LispValue::Integer(n) = x[0] {
|
if let LispValue::Integer(n) = args[0] {
|
||||||
let mut sum = n;
|
let mut sum = n;
|
||||||
for i in 1..x.len() {
|
for i in 1..args.len() {
|
||||||
if let LispValue::Integer(n) = &x[i] {
|
if let LispValue::Integer(n) = &args[i] {
|
||||||
sum *= n;
|
sum *= n;
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
@ -240,24 +313,24 @@ impl LispState {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"float" => {
|
"float" => {
|
||||||
if let LispValue::Float(n) = x[0] {
|
if let LispValue::Float(n) = args[0] {
|
||||||
let mut product = n;
|
let mut product = n;
|
||||||
for i in 1..x.len() {
|
for i in 1..args.len() {
|
||||||
if let LispValue::Float(n) = &x[i] {
|
if let LispValue::Float(n) = &args[i] {
|
||||||
product *= *n;
|
product *= *n;
|
||||||
} else if let LispValue::Integer(n) = &x[i] {
|
} else if let LispValue::Integer(n) = &args[i] {
|
||||||
product *= *n as f32;
|
product *= *n as f32;
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return vec![LispValue::Float(product)];
|
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;
|
let mut product = n as f32;
|
||||||
for i in 1..x.len() {
|
for i in 1..args.len() {
|
||||||
if let LispValue::Float(n) = &x[i] {
|
if let LispValue::Float(n) = &args[i] {
|
||||||
product *= *n;
|
product *= *n;
|
||||||
} else if let LispValue::Integer(n) = &x[i] {
|
} else if let LispValue::Integer(n) = &args[i] {
|
||||||
product *= *n as f32;
|
product *= *n as f32;
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
@ -272,15 +345,15 @@ impl LispState {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
add_function(&mut table, "div", |x| {
|
add_function(&mut table, "div", |_, args| {
|
||||||
if x.len() < 1 {
|
if args.len() < 1 {
|
||||||
return vec![LispValue::Nil]
|
return vec![LispValue::Nil]
|
||||||
} else if x.len() == 1 {
|
} else if args.len() == 1 {
|
||||||
return vec![x[0].clone()];
|
return vec![args[0].clone()];
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut t = "int";
|
let mut t = "int";
|
||||||
for v in &x {
|
for v in &args {
|
||||||
match v {
|
match v {
|
||||||
LispValue::Float(_) => t = "float",
|
LispValue::Float(_) => t = "float",
|
||||||
LispValue::Integer(_) => (),
|
LispValue::Integer(_) => (),
|
||||||
|
@ -290,10 +363,10 @@ impl LispState {
|
||||||
|
|
||||||
match t {
|
match t {
|
||||||
"int" => {
|
"int" => {
|
||||||
if let LispValue::Integer(n) = x[0] {
|
if let LispValue::Integer(n) = args[0] {
|
||||||
let mut sum = n;
|
let mut sum = n;
|
||||||
for i in 1..x.len() {
|
for i in 1..args.len() {
|
||||||
if let LispValue::Integer(n) = &x[i] {
|
if let LispValue::Integer(n) = &args[i] {
|
||||||
sum /= n;
|
sum /= n;
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
@ -305,24 +378,24 @@ impl LispState {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"float" => {
|
"float" => {
|
||||||
if let LispValue::Float(n) = x[0] {
|
if let LispValue::Float(n) = args[0] {
|
||||||
let mut product = n;
|
let mut product = n;
|
||||||
for i in 1..x.len() {
|
for i in 1..args.len() {
|
||||||
if let LispValue::Float(n) = &x[i] {
|
if let LispValue::Float(n) = &args[i] {
|
||||||
product /= *n;
|
product /= *n;
|
||||||
} else if let LispValue::Integer(n) = &x[i] {
|
} else if let LispValue::Integer(n) = &args[i] {
|
||||||
product /= *n as f32;
|
product /= *n as f32;
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return vec![LispValue::Float(product)];
|
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;
|
let mut product = n as f32;
|
||||||
for i in 1..x.len() {
|
for i in 1..args.len() {
|
||||||
if let LispValue::Float(n) = &x[i] {
|
if let LispValue::Float(n) = &args[i] {
|
||||||
product /= *n;
|
product /= *n;
|
||||||
} else if let LispValue::Integer(n) = &x[i] {
|
} else if let LispValue::Integer(n) = &args[i] {
|
||||||
product /= *n as f32;
|
product /= *n as f32;
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
@ -366,7 +439,7 @@ impl LispState {
|
||||||
out
|
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 {
|
if list.len() < 1 {
|
||||||
return (vec![LispValue::List(Vec::new(), quoted)], false);
|
return (vec![LispValue::List(Vec::new(), quoted)], false);
|
||||||
}
|
}
|
||||||
|
@ -375,7 +448,7 @@ impl LispState {
|
||||||
|
|
||||||
for i in 0..list.len() {
|
for i in 0..list.len() {
|
||||||
if let LispValue::List(elements, quoted) = &list[i] {
|
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 {
|
if astuple {
|
||||||
list[i] = elems.get(0).unwrap_or(&LispValue::Nil).to_owned();
|
list[i] = elems.get(0).unwrap_or(&LispValue::Nil).to_owned();
|
||||||
for j in 1..elems.len() {
|
for j in 1..elems.len() {
|
||||||
|
@ -392,23 +465,23 @@ impl LispState {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let LispValue::RustFunction(_name, f) = &list[0] {
|
if let LispValue::RustFunction(_name, f) = &list[0] {
|
||||||
(f(list[1..].to_vec()), true)
|
(f(self, list[1..].to_vec()), true)
|
||||||
} else if let LispValue::LispFunction(_name, ins) = &list[0] {
|
} else if let LispValue::LispFunction(_name, _) = &list[0] {
|
||||||
(Self::call_lisp_func(&list[0], list[1..].to_vec()), true)
|
(self.call_lisp_func(&list[0], list[1..].to_vec()), true)
|
||||||
} else {
|
} else {
|
||||||
(list.to_vec(), false)
|
(list.to_vec(), false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_lisp_func(f: &LispValue, args: Vec<LispValue>) -> Vec<LispValue> {
|
fn call_lisp_func(&mut self, f: &LispValue, args: Vec<LispValue>) -> Vec<LispValue> {
|
||||||
todo!()
|
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);
|
let instructions = Self::handle_prevalues(self, instructions);
|
||||||
for val in instructions {
|
for val in instructions {
|
||||||
if let LispValue::List(elements, quoted) = val {
|
if let LispValue::List(elements, quoted) = val {
|
||||||
Self::eval_list(&elements, quoted);
|
self.eval_list(&elements, quoted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
src/main.rs
14
src/main.rs
|
@ -7,20 +7,16 @@ use parser::*;
|
||||||
mod executor;
|
mod executor;
|
||||||
use executor::*;
|
use executor::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), TokenizerError> {
|
||||||
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 = match tokenizer.tokenize() {
|
let tokens = tokenizer.tokenize()?;
|
||||||
Ok(tokens) => tokens,
|
|
||||||
Err(e) => {
|
|
||||||
println!("{}", e);
|
|
||||||
Vec::new()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let instructions = parse(tokens);
|
let instructions = parse(tokens);
|
||||||
|
|
||||||
let state = LispState::new();
|
let mut state = LispState::new();
|
||||||
|
|
||||||
state.execute(instructions);
|
state.execute(instructions);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,91 @@
|
||||||
use std::fmt;
|
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;
|
use crate::Token;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum PreLispValue {
|
pub enum PreLispValue {
|
||||||
Nil,
|
Nil,
|
||||||
String(String),
|
String(String),
|
||||||
|
@ -110,7 +193,11 @@ fn parse_exp(tokens: Vec<Token>, quoted: bool) -> PreLispValue {
|
||||||
unquote_next_list = false;
|
unquote_next_list = false;
|
||||||
},
|
},
|
||||||
Token::Identifier(name) => {
|
Token::Identifier(name) => {
|
||||||
values.push(PreLispValue::Var(name.to_owned()));
|
if name == &String::from("nil") {
|
||||||
|
values.push(PreLispValue::Nil)
|
||||||
|
} else {
|
||||||
|
values.push(PreLispValue::Var(name.to_owned()));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Token::Integer(num) => {
|
Token::Integer(num) => {
|
||||||
values.push(PreLispValue::Integer(*num));
|
values.push(PreLispValue::Integer(*num));
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
; This is a comment
|
; This is a comment
|
||||||
|
|
||||||
(print "Hello, world!")
|
|
||||||
|
|
||||||
(print (1 2 3))
|
|
||||||
|
|
||||||
(print
|
(print
|
||||||
(add 15 105.3)
|
(add
|
||||||
)
|
(div
|
||||||
|
4.5
|
||||||
(print
|
(sub 0.5 0.2)
|
||||||
'(add 15 105.3)
|
)
|
||||||
)
|
(mul 21 0.05)
|
||||||
|
)
|
||||||
|
)
|
|
@ -1,6 +1,92 @@
|
||||||
use std::fmt;
|
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 {
|
pub enum Token {
|
||||||
OpenParen,
|
OpenParen,
|
||||||
CloseParen,
|
CloseParen,
|
||||||
|
@ -12,7 +98,7 @@ pub enum Token {
|
||||||
Float(f32)
|
Float(f32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct TokenizerError {
|
pub struct TokenizerError {
|
||||||
line: u64,
|
line: u64,
|
||||||
column: u64,
|
column: u64,
|
||||||
|
|
Reference in a new issue