diff --git a/src/cell.rs b/src/cell.rs deleted file mode 100644 index cb7d4b0..0000000 --- a/src/cell.rs +++ /dev/null @@ -1,245 +0,0 @@ -/* relish: versatile lisp shell - * Copyright (C) 2021 Aidan Hahn - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -use std::fmt; -use std::boxed::Box; - - -// Container -#[derive(Clone)] -pub enum Ctr { - Symbol(String), - String(String), - Integer(i128), - Float(f64), - Bool(bool), - Cell(Box), - None -} - -// Type of Container -#[derive(PartialEq)] -#[derive(Clone)] -pub enum Type { - Symbol, - String, - Integer, - Float, - Bool, - Cell, - None -} - -/* Cell - * Holds two Containers. - * Basic building block for more complex data structures. - */ -#[derive(Clone)] -pub struct Cell { - /* "Cell Address Register" - * Historical way of referring to the first value in a cell. - */ - pub car: Ctr, - - /* "Cell Decrement Register" - * Historical way of referring to the second value in a cell. - */ - pub cdr: Ctr -} - -impl Ctr { - pub fn to_type(&self) -> Type { - match self { - Ctr::Symbol(_s) => Type::Symbol, - Ctr::String(_s) => Type::String, - Ctr::Integer(_s) => Type::Integer, - Ctr::Float(_s) => Type::Float, - Ctr::Bool(_s) => Type::Bool, - Ctr::Cell(_s) => Type::Cell, - Ctr::None => Type::None - } - } -} - -impl Type { - pub fn to_str(&self) -> String { - let ret: &str; - match self { - Type::Symbol => ret = "symbol", - Type::String => ret = "string", - Type::Integer => ret = "integer", - Type::Float => ret = "float", - Type::Bool => ret = "bool", - Type::Cell => ret = "cell", - Type::None => ret = "none" - } - - ret.to_owned() - } -} - -// creates a cell containing two boxes -pub fn cons (l_ctr: Ctr, r_ctr: Ctr) -> Cell { - Cell { - car: l_ctr, - cdr: r_ctr - } -} - -/* Prints any cell as a string - * recurs on CELL type Containers - */ -pub fn cell_as_string(c: &Cell, with_parens: bool) -> String { - let mut string = String::new(); - let mut prn_space = true; - match &c.car { - Ctr::Symbol(s) => string.push_str(&s), - Ctr::String(s) => { - string.push('\''); - string.push_str(&s); - string.push('\''); - }, - Ctr::Integer(i) => string = string + &i.to_string(), - Ctr::Float(f) => string = string + &f.to_string(), - Ctr::Bool(b) => string = string + &b.to_string(), - Ctr::Cell(c) => string.push_str(cell_as_string(&c, true).as_str()), - Ctr::None => prn_space = false - } - - if prn_space { - string.push(' '); - } - - match &c.cdr { - Ctr::Symbol(s) => string.push_str(&s), - Ctr::String(s) => { - string.push('\''); - string.push_str(&s); - string.push('\''); - }, - Ctr::Integer(i) => string = string + &i.to_string(), - Ctr::Float(f) => string = string + &f.to_string(), - Ctr::Bool(b) => string = string + &b.to_string(), - Ctr::Cell(c) => string.push_str(cell_as_string(&c, false).as_str()), - Ctr::None => { - if prn_space { - string.pop(); - } - } - } - - // TODO: maybe a better way to do this - if with_parens { - let mut extra = String::from("("); - extra.push_str(&string); - extra.push(')'); - string = extra - } - return string -} - -impl fmt::Display for Cell { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", cell_as_string(self, true).as_str()) - } -} - -/* NOTE: "Standard form" is used here to refer to a list of cells - * that resembles a typical linked list. This means that Car may hold whatever, - * but Cdr must either be Cell or None. - */ -impl Cell { - /* applies a function across a Cell list in standard form - * function must take a Ctr and return a bool - * short circuits on the first false returned. - * also returns false on a non standard form list - */ - pub fn circuit bool>(&self, func: &mut F) -> bool{ - if func(&self.car) { - match &self.cdr { - Ctr::None => true, - Ctr::Cell(c) => c.circuit(func), - _ => false - } - } else { - false - } - } - - /* recurs over a chain of cells - * adds obj to chain - * not public, only meant for internal use... yet - * steals ownership of obj - */ - pub fn append(&mut self, obj: Ctr) { - match &mut self.car { - Ctr::None => { - self.car = obj; - }, - _ => { - match &mut self.cdr { - Ctr::None => { - self.cdr = Ctr::Cell(Box::new(Cell{ - car: obj, - cdr: Ctr::None - })); - }, - Ctr::Cell(cell) => { - cell.append(obj); - }, - _ => () - } - } - } - } - - /* recurs over a chain of cell - * returns length of chain - */ - pub fn len(&self) -> i128 { - match &self.cdr { - Ctr::Cell(c) => c.len() + 1, - _ => 1 - } - } - - /* iterates through a list. - * returns a CLONE of the cell at the cooresponding index - */ - pub fn index(&self, idx: usize) -> Ctr { - if idx > 0 { - match &self.cdr { - Ctr::None => Ctr::None, - Ctr::Cell(c) => c.index(idx-1), - _ => { - if idx == 1 { - self.cdr.clone() - } else { - Ctr::None - } - } - } - } else { - match self.car { - Ctr::None => Ctr::None, - _ => { - self.cdr.clone() - } - } - } - } -} diff --git a/src/eval.rs b/src/eval.rs index 17a05b3..8769087 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -15,8 +15,9 @@ * along with this program. If not, see . */ -use std::boxed::Box; -use crate::cell::{Cell}; +use std::rc::Rc; +use std::cell::RefCell; +use crate::segment::{Ast}; use crate::func::FTable; use crate::vars::VTable; @@ -25,10 +26,10 @@ use crate::vars::VTable; * representing the simplest possible form of the input */ pub fn eval( - _ast: &Box, - _vars: &Box, - _funcs: &Box, + _ast: Ast, + _vars: Rc>, + _funcs: Rc>, _sym_loose: bool -) -> Result, String> { +) -> Result { Err("Unimplemented".to_string()) } diff --git a/src/func.rs b/src/func.rs index 4d54c70..e118856 100644 --- a/src/func.rs +++ b/src/func.rs @@ -15,23 +15,24 @@ * along with this program. If not, see . */ -use std::boxed::Box; +use std::rc::Rc; +use std::cell::RefCell; use std::convert::TryInto; use std::collections::HashMap; -use crate::cell::{Ctr, Cell, Type}; +use crate::segment::{Ctr, Type, circuit, list_len, list_idx, Ast}; use crate::vars::{VTable}; use crate::eval::eval; -pub type FTable = HashMap>; +pub type FTable = HashMap>>; // Standardized function signature for stdlib functions -pub type InternalOperation = fn(&Box, &mut Box, &mut Box) -> Box; +pub type InternalOperation = fn(Ast, Rc>, Rc>) -> Ast; pub struct ExternalOperation { // Un-evaluated abstract syntax tree // TODO: Intermediate evaluation to simplify branches with no argument in them // Simplified branches must not have side effects. // TODO: Apply Memoization? - ast: Box, + ast: Ast, // list of argument string tokens arg_syms: Vec } @@ -67,113 +68,110 @@ pub struct Function { pub eval_lazy: bool } -impl Function { - /* call - * routine is called by eval when a function call is detected - */ - pub fn call( - &self, - args: &Box, - vars: &mut Box, - funcs: &mut Box - ) -> Result, String> { - let n_args: &Box; - let outer_owner: Box; - if !self.eval_lazy { - match eval(args, vars, funcs, self.loose_syms) { - Ok(box_cell) => outer_owner = box_cell, - Err(s) => return Err( - format!( - "error evaluating args to {}: {}", - self.name, - s - ) +/* call + * routine is called by eval when a function call is detected + */ +pub fn func_call( + function: Rc>, + args: Ast, + vars: Rc>, + funcs: Rc> +) -> Result { + let called_func = function.borrow_mut(); + let mut n_args: Ast = args.clone(); + if !called_func.eval_lazy { + match eval(args, vars.clone(), funcs.clone(), called_func.loose_syms) { + Ok(rc_seg) => n_args = rc_seg.clone(), + Err(s) => return Err( + format!( + "error evaluating args to {}: {}", + called_func.name, + s ) - } - n_args = &outer_owner; - } else { - n_args = args; + ) } + } - match &self.args { - Args::Lazy(num) => { - if *num < 0 { + match &called_func.args { + Args::Lazy(num) => { + if *num < 0 { + } + + if !(*num == (list_len(n_args.clone()) as i128 - 1)) { + return Err(format!("expected {} args in call to {}", num, called_func.name)) + } + }, + + Args::Strict(arg_types) => { + let mut idx: usize = 0; + let passes = circuit(n_args.clone(), &mut |c: &Ctr| { + if idx >= arg_types.len() { + return false; } - if !(*num == (n_args.len() - 1)) { - return Err(format!("expected {} args in call to {}", num, self.name)) + if let Ctr::None = c { + return false; } - }, - Args::Strict(arg_types) => { - let mut idx: usize = 0; - let passes = n_args.circuit(&mut |c: &Ctr| { - if idx >= arg_types.len() { - return false; - } + let ret = arg_types[idx] == c.to_type(); + if ret { + idx += 1; + } + return ret; + }); - if let Ctr::None = c { - return false; - } + if passes && idx < (arg_types.len() - 1) { + return Err(format!( + "{} too little arguments in call to {}", + arg_types.len() - (idx + 1), + called_func.name + )); + } - let ret = arg_types[idx] == c.to_type(); - if ret { - idx += 1; - } - return ret; - }); - - if passes && idx < (arg_types.len() - 1) { + if !passes { + if idx < (arg_types.len() - 1) { return Err(format!( - "{} too little arguments in call to {}", - arg_types.len() - (idx + 1), - self.name + "argument {} in call to {} is of wrong type (expected {})", + idx + 1, + called_func.name, + arg_types[idx].to_str() )); } - if !passes { - if idx < (arg_types.len() - 1) { - return Err(format!( - "argument {} in call to {} is of wrong type (expected {})", - idx + 1, - self.name, - arg_types[idx].to_str() - )); - } - - if idx == (arg_types.len() - 1) { - return Err(format!( - "too many arguments in call to {}", - self.name - )); - } + if idx == (arg_types.len() - 1) { + return Err(format!( + "too many arguments in call to {}", + called_func.name + )); } } } + } - match &self.function { - Operation::Internal(f) => Ok((f)(&n_args, vars, funcs)), - Operation::External(f) => { - // copy var table and add args - let mut temp = vars.clone(); - for n in 0..f.arg_syms.len() { - temp.insert( - f.arg_syms[n].clone(), - Box::new(n_args.index(n)) - ); - } - eval(&f.ast, &temp, funcs, self.loose_syms) + match &called_func.function { + Operation::Internal(f) => Ok((f)(n_args, vars, funcs)), + Operation::External(f) => { + let mut temp = vars.borrow().clone(); + for n in 0..f.arg_syms.len() { + temp.insert( + f.arg_syms[n].clone(), + Rc::new(list_idx(n_args.clone(), n as u128)) + ); } + + eval(f.ast.clone(), Rc::new(RefCell::new(temp)), funcs, called_func.loose_syms) } } } -pub fn declare( - ft: &mut Box, - f: Box +pub fn func_declare( + ft: Rc>, + f: Rc> ) -> Option { - if let Operation::External(fun) = &f.function { - if let Args::Lazy(i) = f.args { + let func = f.borrow(); + let name = func.name.clone(); + if let Operation::External(fun) = &func.function { + if let Args::Lazy(i) = func.args { if fun.arg_syms.len() != i.try_into().unwrap() { return Some( "external function must have lazy args equal to declared arg_syms length" @@ -188,6 +186,7 @@ pub fn declare( } } - ft.insert(f.name.clone(), f); + drop(func); + ft.borrow_mut().insert(name, f); None } diff --git a/src/lex.rs b/src/lex.rs index 8232e45..60ebdad 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -15,8 +15,7 @@ * along with this program. If not, see . */ -use std::boxed::Box; -use crate::cell::{Ctr, Cell}; +use crate::segment::{Ctr, Ast, list_append, new_ast}; const UNMATCHED_STR_DELIM: &str = "Unmatched string delimiter in input"; const UNMATCHED_LIST_DELIM: &str = "Unmatched list delimiter in input"; @@ -24,7 +23,7 @@ const UNMATCHED_LIST_DELIM: &str = "Unmatched list delimiter in input"; /* takes a line of user input * returns an unsimplified tree of tokens. */ -pub fn lex(document: String) -> Result, String> { +pub fn lex(document: String) -> Result { if !document.is_ascii() { return Err("document may only contain ascii characters".to_string()); } @@ -40,10 +39,10 @@ pub fn lex(document: String) -> Result, String> { } /* The logic used in lex - * Returns Ok(Box) if lexing passes + * Returns Ok(Rc) if lexing passes * Returns Err(String) if an error occurs */ -fn process(document: String) -> Result, String> { +fn process(document: String) -> Result { let doc_len = document.len(); if doc_len == 0 { @@ -110,16 +109,13 @@ fn process(document: String) -> Result, String> { // match a delimiter if !needs_alloc { match c { - // add a new Cell reference to the stack + // add a new Seg reference to the stack '(' => { if token != "" { return Err("list started in middle of another token".to_string()); } - ref_stack.push(Box::new(Cell{ - car: Ctr::None, - cdr: Ctr::None - })); + ref_stack.push(new_ast(Ctr::None, Ctr::None)); delim_stack.push(')'); }, @@ -151,7 +147,7 @@ fn process(document: String) -> Result, String> { return Err("Empty token".to_string()); } - let mut current_cell_ref = ref_stack.pop().unwrap(); + let mut current_seg_ref = ref_stack.pop().unwrap(); let mut obj; if token.len() > 0 { if is_str { @@ -172,22 +168,22 @@ fn process(document: String) -> Result, String> { } token = String::new(); - current_cell_ref.append(obj); + list_append(current_seg_ref.clone(), obj); } if alloc_list { // return if we have finished the document if ref_stack.len() == 0 { - return Ok(Box::new(*current_cell_ref)); + return Ok(current_seg_ref); } // shortening this will lead to naught but pain - obj = Ctr::Cell(Box::new(*current_cell_ref)); - current_cell_ref = ref_stack.pop().unwrap(); - current_cell_ref.append(obj); + obj = Ctr::Seg(current_seg_ref.clone()); + current_seg_ref = ref_stack.pop().unwrap(); + list_append(current_seg_ref.clone(), obj); } - ref_stack.push(current_cell_ref); + ref_stack.push(current_seg_ref); } } diff --git a/src/lib.rs b/src/lib.rs index e0a4022..9c4bc73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,18 +15,18 @@ * along with this program. If not, see . */ -mod cell; +mod segment; mod lex; mod func; mod eval; mod vars; pub mod ast { - pub use crate::cell::{Cell, Ctr, cons, cell_as_string}; + pub use crate::segment::{Seg, Ctr, ast_to_string, Type, Ast, new_ast}; pub use crate::lex::lex; pub use crate::func::{Function, Operation, FTable, Args, InternalOperation, ExternalOperation, - declare}; - pub use crate::vars::VTable; + func_declare, func_call}; + pub use crate::vars::{VTable, define}; pub use crate::eval::eval; } diff --git a/src/segment.rs b/src/segment.rs new file mode 100644 index 0000000..bf6f4cd --- /dev/null +++ b/src/segment.rs @@ -0,0 +1,245 @@ +/* relish: versatile lisp shell + * Copyright (C) 2021 Aidan Hahn + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +use std::rc::Rc; +use std::cell::RefCell; + +// Recursive data type for a tree of Segments +pub type Ast = Rc>; + +// Container +#[derive(Clone)] +pub enum Ctr { + Symbol(String), + String(String), + Integer(i128), + Float(f64), + Bool(bool), + Seg(Ast), + None +} + +// Type of Container +#[derive(PartialEq)] +#[derive(Clone)] +pub enum Type { + Symbol, + String, + Integer, + Float, + Bool, + Seg, + None +} + +/* Segment + * Holds two Containers. + * Basic building block for more complex data structures. + * I was going to call it Cell and then I learned about + * how important RefCells were in Rust + */ +#[derive(Clone)] +pub struct Seg { + /* "Contents of Address Register" + * Historical way of referring to the first value in a cell. + */ + pub car: Ctr, + + /* "Contents of Decrement Register" + * Historical way of referring to the second value in a cell. + */ + pub cdr: Ctr +} + +impl Ctr { + pub fn to_type(&self) -> Type { + match self { + Ctr::Symbol(_s) => Type::Symbol, + Ctr::String(_s) => Type::String, + Ctr::Integer(_s) => Type::Integer, + Ctr::Float(_s) => Type::Float, + Ctr::Bool(_s) => Type::Bool, + Ctr::Seg(_s) => Type::Seg, + Ctr::None => Type::None + } + } +} + +impl Type { + pub fn to_str(&self) -> String { + let ret: &str; + match self { + Type::Symbol => ret = "symbol", + Type::String => ret = "string", + Type::Integer => ret = "integer", + Type::Float => ret = "float", + Type::Bool => ret = "bool", + Type::Seg => ret = "segment", + Type::None => ret = "none" + } + + ret.to_owned() + } +} + +/* Prints a Syntax Tree as a string + */ +pub fn ast_as_string(c: Ast, with_parens: bool) -> String { + let mut string = String::new(); + let mut prn_space = true; + let seg = c.borrow(); + match &seg.car { + Ctr::Symbol(s) => string.push_str(&s), + Ctr::String(s) => { + string.push('\''); + string.push_str(&s); + string.push('\''); + }, + Ctr::Integer(i) => string = string + &i.to_string(), + Ctr::Float(f) => string = string + &f.to_string(), + Ctr::Bool(b) => string = string + &b.to_string(), + Ctr::Seg(c) => string.push_str(ast_as_string(c.clone(), true).as_str()), + Ctr::None => prn_space = false + } + + if prn_space { + string.push(' '); + } + + match &seg.cdr { + Ctr::Symbol(s) => string.push_str(&s), + Ctr::String(s) => { + string.push('\''); + string.push_str(&s); + string.push('\''); + }, + Ctr::Integer(i) => string = string + &i.to_string(), + Ctr::Float(f) => string = string + &f.to_string(), + Ctr::Bool(b) => string = string + &b.to_string(), + Ctr::Seg(c) => string.push_str(ast_as_string(c.clone(), false).as_str()), + Ctr::None => { + if prn_space { + string.pop(); + } + } + } + + // TODO: maybe a better way to do this + if with_parens { + let mut extra = String::from("("); + extra.push_str(&string); + extra.push(')'); + string = extra + } + return string +} + +pub fn ast_to_string(c: Ast) -> String { + ast_as_string(c.clone(), true) +} + +/* NOTE: "Standard form" is used here to refer to a list of segments + * that resembles a typical linked list. This means that Car may hold whatever, + * but Cdr must either be Seg or None. + */ + +/* Initializes a new ast node with segment car and cdr passed in + */ +pub fn new_ast(car: Ctr, cdr: Ctr) -> Ast { + Rc::new(RefCell::new(Seg{ + car: car, + cdr: cdr + })) +} + +/* applies a function across a list in standard form + * function must take a Ctr and return a bool + * short circuits on the first false returned. + * also returns false on a non standard form list + */ +pub fn circuit bool>(tree: Ast, func: &mut F) -> bool{ + let inner = tree.borrow(); + if func(&inner.car) { + match &inner.cdr { + Ctr::None => true, + Ctr::Seg(c) => circuit(c.clone(), func), + _ => false + } + } else { + false + } +} + +/* recurs over ast assumed to be list in standard form + * returns length + */ +pub fn list_len(tree: Ast) -> u128 { + match &tree.borrow().cdr { + Ctr::Seg(c) => list_len(c.clone()) + 1, + _ => 1 + } +} + +/* recurs over tree assumed to be list in standard form + * returns clone of ctr at index provided + */ +pub fn list_idx(tree: Ast, idx: u128) -> Ctr { + let inner = tree.borrow(); + if idx > 0 { + match &inner.cdr { + Ctr::None => Ctr::None, + Ctr::Seg(c) => list_idx(c.clone(), idx - 1), + _ => { + if idx == 1 { + inner.cdr.clone() + } else { + Ctr::None + } + } + } + } else { + match inner.car { + Ctr::None => Ctr::None, + _ => { + inner.cdr.clone() + } + } + } +} + +/* recurs over tree assumed to be list in standard form + * appends object to end of list + */ +pub fn list_append(tree: Ast, obj: Ctr) { + let mut inner = tree.borrow_mut(); + match &inner.car { + Ctr::None => { + inner.car = obj; + }, + _ => { + match &inner.cdr { + Ctr::None => { + inner.cdr = Ctr::Seg(new_ast(obj, Ctr::None)); + }, + Ctr::Seg(tr) => { + list_append(tr.clone(), obj); + }, + _ => () + } + } + } +} diff --git a/src/vars.rs b/src/vars.rs index 3799123..82ba6d3 100644 --- a/src/vars.rs +++ b/src/vars.rs @@ -15,22 +15,23 @@ * along with this program. If not, see . */ -use std::boxed::Box; +use std::cell::RefCell; +use std::rc::Rc; use std::collections::HashMap; -use crate::cell::{Ctr}; +use crate::segment::{Ctr}; -/* Mapping between a string token and a tree of Cells +/* Mapping between a string token and a tree of Segments * The string token can be found in any Ctr::Symbol value * it is expected that the trees stored are already evaluated */ -pub type VTable = HashMap>; +pub type VTable = HashMap>; pub fn define( - vt: &mut Box, + vt: Rc>, identifier: String, - var_tree: Box + var_tree: Rc ) { - if let Some(boxed_cell) = vt.insert(identifier, var_tree) { - drop(boxed_cell); + if let Some(rc_segment) = vt.borrow_mut().insert(identifier, var_tree) { + drop(rc_segment); } } diff --git a/tests/test_func.rs b/tests/test_func.rs new file mode 100644 index 0000000..05cd14c --- /dev/null +++ b/tests/test_func.rs @@ -0,0 +1,58 @@ +mod func_tests { + use std::rc::Rc; + use std::cell::RefCell; + use relish::ast::{Ast, Type, Ctr, new_ast}; + use relish::ast::VTable; + use relish::ast::{Function, Operation, FTable, Args, func_declare, func_call}; + + #[test] + fn decl_and_call_internal_func() { + let test_internal_func: Function = Function{ + name: String::from("test_func_in"), + loose_syms: false, + eval_lazy: true, + args: Args::Strict(vec![Type::Bool]), + function: Operation::Internal( + |a: Ast, _b: Rc>, _c: Rc>| -> Ast { + let inner = a.borrow(); + let mut is_bool = false; + if let Ctr::Bool(_) = &inner.car { + is_bool = true; + } + + new_ast(Ctr::Bool(is_bool), Ctr::None) + } + ) + }; + let ft = Rc::new(RefCell::new(FTable::new())); + let vt = Rc::new(RefCell::new(VTable::new())); + let args = new_ast(Ctr::Bool(true), Ctr::None); + if let Some(s) = func_declare(ft.clone(), + Rc::new(RefCell::new(test_internal_func))) { + print!("{}", s); + assert!(false); + } + + let func: Rc>; + if let Some(f) = ft.borrow().get(&"test_func_in".to_string()) { + func = f.clone(); + } else { + print!("failed to retrieve function!"); + assert!(false); + return; + } + + if let Ok(ast) = func_call(func, args, vt, ft) { + match &ast.borrow().car { + Ctr::Bool(b) => assert!(b), + _ => { + print!("invalid return from func!"); + assert!(false); + } + } + } else { + print!("call to function failed!"); + assert!(false); + } + } +} diff --git a/tests/test_lex.rs b/tests/test_lex.rs index f7e9f1d..0e9fb5f 100644 --- a/tests/test_lex.rs +++ b/tests/test_lex.rs @@ -1,12 +1,12 @@ mod lex_tests { - use relish::ast::{lex}; + use relish::ast::{lex, ast_to_string}; #[test] fn test_lex_basic_pair() { let document: &str = "(hello 'world')"; match lex(document.to_string()) { - Ok(box_cell) => { - assert_eq!(format!("{}", *box_cell), document); + Ok(tree) => { + assert_eq!(ast_to_string(tree), document); }, Err(s) => { print!("{}\n", s); @@ -19,8 +19,8 @@ mod lex_tests { fn test_lex_basic_list() { let document: &str = "(hello 'world' 1 2 3)"; match lex(document.to_string()) { - Ok(box_cell) => { - assert_eq!(format!("{}", *box_cell), document); + Ok(tree) => { + assert_eq!(ast_to_string(tree), document); }, Err(s) => { print!("{}\n", s); @@ -33,8 +33,8 @@ mod lex_tests { fn test_lex_complex_list() { let document: &str = "(hello 'world' (1 2 (1 2 3)) 1 2 3)"; match lex(document.to_string()) { - Ok(box_cell) => { - assert_eq!(format!("{}", *box_cell), document); + Ok(tree) => { + assert_eq!(ast_to_string(tree), document); }, Err(s) => { print!("{}\n", s); @@ -48,8 +48,8 @@ mod lex_tests { let document: &str = "(as;dd)"; let output: &str = "Problem lexing document: \"Unparsable token:as;dd\""; match lex(document.to_string()) { - Ok(box_cell) => { - print!("Bad token yielded: {}\n", *box_cell); + Ok(tree) => { + print!("Bad token yielded: {}\n", ast_to_string(tree)); assert!(false); }, Err(s) => { @@ -63,8 +63,8 @@ mod lex_tests { let document: &str = "(one two"; let output: &str = "Problem lexing document: \"Unmatched list delimiter in input\""; match lex(document.to_string()) { - Ok(box_cell) => { - print!("Bad token yielded: {}\n", *box_cell); + Ok(tree) => { + print!("Bad token yielded: {}\n", ast_to_string(tree)); assert!(false); }, Err(s) => { @@ -78,8 +78,8 @@ mod lex_tests { let document: &str = "(one two (three)"; let output: &str = "Problem lexing document: \"Unmatched list delimiter in input\""; match lex(document.to_string()) { - Ok(box_cell) => { - print!("Bad token yielded: {}\n", *box_cell); + Ok(tree) => { + print!("Bad token yielded: {}\n", ast_to_string(tree)); assert!(false); }, Err(s) => { @@ -93,8 +93,8 @@ mod lex_tests { let document: &str = "#!/bin/relish\n(one two)"; let output: &str = "(one two)"; match lex(document.to_string()) { - Ok(box_cell) => { - assert_eq!(format!("{}", *box_cell), output.to_string()); + Ok(tree) => { + assert_eq!(ast_to_string(tree), output.to_string()); }, Err(s) => { print!("{}\n", s); @@ -108,8 +108,8 @@ mod lex_tests { let document: &str = "#!/bin/relish\n((one two)# another doc comment\n(three four))"; let output: &str = "((one two) (three four))"; match lex(document.to_string()) { - Ok(box_cell) => { - assert_eq!(format!("{}", *box_cell), output.to_string()); + Ok(tree) => { + assert_eq!(ast_to_string(tree), output.to_string()); }, Err(s) => { print!("{}\n", s); @@ -123,8 +123,8 @@ mod lex_tests { let document: &str = "#!/bin/relish\n((one two)\n# another doc comment\nthree)"; let output: &str = "((one two) three)"; match lex(document.to_string()) { - Ok(box_cell) => { - assert_eq!(format!("{}", *box_cell), output.to_string()); + Ok(tree) => { + assert_eq!(ast_to_string(tree), output.to_string()); }, Err(s) => { print!("{}\n", s); @@ -138,8 +138,8 @@ mod lex_tests { let document: &str = "(one t(wo)"; let output: &str = "Problem lexing document: \"list started in middle of another token\""; match lex(document.to_string()) { - Ok(box_cell) => { - print!("Bad token yielded: {}\n", *box_cell); + Ok(tree) => { + print!("Bad token yielded: {}\n", ast_to_string(tree)); assert!(false); }, Err(s) => {