Big referencing refactor

- RC+RefCell pattern used... everywhere
- Ast type implemented
- unit tests for func_call
- more changes, but this commit scope has grown significantly and I
cannot list them all
This commit is contained in:
Aidan 2021-03-14 16:14:57 -07:00
parent 76b12a8214
commit 3434a49cc1
No known key found for this signature in database
GPG key ID: 327711E983899316
9 changed files with 446 additions and 391 deletions

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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<Cell>),
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<F: FnMut(&Ctr)-> 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()
}
}
}
}
}

View file

@ -15,8 +15,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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<Cell>,
_vars: &Box<VTable>,
_funcs: &Box<FTable>,
_ast: Ast,
_vars: Rc<RefCell<VTable>>,
_funcs: Rc<RefCell<FTable>>,
_sym_loose: bool
) -> Result<Box<Cell>, String> {
) -> Result<Ast, String> {
Err("Unimplemented".to_string())
}

View file

@ -15,23 +15,24 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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<String, Box<Function>>;
pub type FTable = HashMap<String, Rc<RefCell<Function>>>;
// Standardized function signature for stdlib functions
pub type InternalOperation = fn(&Box<Cell>, &mut Box<VTable>, &mut Box<FTable>) -> Box<Cell>;
pub type InternalOperation = fn(Ast, Rc<RefCell<VTable>>, Rc<RefCell<FTable>>) -> 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<Cell>,
ast: Ast,
// list of argument string tokens
arg_syms: Vec<String>
}
@ -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<Cell>,
vars: &mut Box<VTable>,
funcs: &mut Box<FTable>
) -> Result<Box<Cell>, String> {
let n_args: &Box<Cell>;
let outer_owner: Box<Cell>;
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<RefCell<Function>>,
args: Ast,
vars: Rc<RefCell<VTable>>,
funcs: Rc<RefCell<FTable>>
) -> Result<Ast, String> {
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<FTable>,
f: Box<Function>
pub fn func_declare(
ft: Rc<RefCell<FTable>>,
f: Rc<RefCell<Function>>
) -> Option<String> {
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
}

View file

@ -15,8 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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<Box<Cell>, String> {
pub fn lex(document: String) -> Result<Ast, String> {
if !document.is_ascii() {
return Err("document may only contain ascii characters".to_string());
}
@ -40,10 +39,10 @@ pub fn lex(document: String) -> Result<Box<Cell>, String> {
}
/* The logic used in lex
* Returns Ok(Box<Cell>) if lexing passes
* Returns Ok(Rc<Seg>) if lexing passes
* Returns Err(String) if an error occurs
*/
fn process(document: String) -> Result<Box<Cell>, String> {
fn process(document: String) -> Result<Ast, String> {
let doc_len = document.len();
if doc_len == 0 {
@ -110,16 +109,13 @@ fn process(document: String) -> Result<Box<Cell>, 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<Box<Cell>, 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<Box<Cell>, 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);
}
}

View file

@ -15,18 +15,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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;
}

245
src/segment.rs Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
use std::rc::Rc;
use std::cell::RefCell;
// Recursive data type for a tree of Segments
pub type Ast = Rc<RefCell<Seg>>;
// 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<F: FnMut(&Ctr)-> 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);
},
_ => ()
}
}
}
}

View file

@ -15,22 +15,23 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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<String, Box<Ctr>>;
pub type VTable = HashMap<String, Rc<Ctr>>;
pub fn define(
vt: &mut Box<VTable>,
vt: Rc<RefCell<VTable>>,
identifier: String,
var_tree: Box<Ctr>
var_tree: Rc<Ctr>
) {
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);
}
}

58
tests/test_func.rs Normal file
View file

@ -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<RefCell<VTable>>, _c: Rc<RefCell<FTable>>| -> 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<RefCell<Function>>;
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);
}
}
}

View file

@ -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) => {