aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--rust/spectrum.rs88
1 files changed, 47 insertions, 41 deletions
diff --git a/rust/spectrum.rs b/rust/spectrum.rs
index f736f14..19921a0 100644
--- a/rust/spectrum.rs
+++ b/rust/spectrum.rs
@@ -76,7 +76,7 @@ fn is_valid_identifier(s: &str) -> bool {
* This function takes a raw string and splits it up into a flat sequence of string tokens.
* It should handle basic quotes (double quotes only) and comments (';' character to end-of-line).
*/
-fn scheme_tokenize<'a>(raw_str: &'a str) -> Result<Vec<&'a str>, &'static str> {
+fn scheme_tokenize<'a>(raw_str: &'a str) -> Result<Vec<&'a str>, String> {
let mut ret = Vec::<&str>::new();
let mut food: usize = 0; // "how many chars of current token have we read?"
let mut quoted: bool = false;
@@ -89,7 +89,7 @@ fn scheme_tokenize<'a>(raw_str: &'a str) -> Result<Vec<&'a str>, &'static str> {
quoted = false;
food = 0;
} else if raw_str.len() == i+1 {
- return Err("unmatched quote char");
+ return Err(format!("unmatched quote char"));
} else {
food += 1;
}
@@ -105,7 +105,7 @@ fn scheme_tokenize<'a>(raw_str: &'a str) -> Result<Vec<&'a str>, &'static str> {
food = 0;
} else if c == '"' {
if food > 0 {
- return Err("unexpected quote char");
+ return Err(format!("unexpected quote char"));
}
quoted = true;
} else if is_scheme_whitespace(c) || is_scheme_sep(c) {
@@ -124,7 +124,7 @@ fn scheme_tokenize<'a>(raw_str: &'a str) -> Result<Vec<&'a str>, &'static str> {
}
}
if quoted {
- return Err("unmatched (trailing) quote char");
+ return Err(format!("unmatched (trailing) quote char"));
}
return Ok(ret);
}
@@ -132,7 +132,7 @@ fn scheme_tokenize<'a>(raw_str: &'a str) -> Result<Vec<&'a str>, &'static str> {
/*
* This function takes a token (still a string) and parses it into a single SchemeExpression
*/
-fn scheme_parse_token(token: &str) -> Result<SchemeExpr, &'static str> {
+fn scheme_parse_token(token: &str) -> Result<SchemeExpr, String> {
// Is it a constant?
match token {
@@ -167,14 +167,14 @@ fn scheme_parse_token(token: &str) -> Result<SchemeExpr, &'static str> {
return Ok(SchemeExpr::SchemeIdentifier(token.to_string()));
}
- return Err("unparsable token");
+ return Err(format!("unparsable token: \"{}\"", token));
}
/*
* This function takes a flat sequence of string tokens (as output by scheme_tokenize) and parses
* into a SchemeExpression (eg, a nested list of expressions).
*/
-fn scheme_parse<'a>(tokens: &Vec<&'a str>, depth: u32) -> Result<(Vec<SchemeExpr>, usize), &'static str> {
+fn scheme_parse<'a>(tokens: &Vec<&'a str>, depth: u32) -> Result<(Vec<SchemeExpr>, usize), String> {
let mut i: usize = 0;
if tokens.len() == 0 {
return Ok((vec![SchemeExpr::SchemeNull], 0));
@@ -202,7 +202,7 @@ fn scheme_parse<'a>(tokens: &Vec<&'a str>, depth: u32) -> Result<(Vec<SchemeExpr
},
")" => {
if depth == 0 {
- return Err("missing an open bracket");
+ return Err(format!("missing an open bracket"));
}
return Ok((ret, parsed));
},
@@ -214,7 +214,7 @@ fn scheme_parse<'a>(tokens: &Vec<&'a str>, depth: u32) -> Result<(Vec<SchemeExpr
i += 1;
}
if depth > 0 {
- return Err("missing a close bracket");
+ return Err(format!("missing a close bracket"));
}
return Ok((ret, parsed));
}
@@ -224,7 +224,7 @@ fn scheme_parse<'a>(tokens: &Vec<&'a str>, depth: u32) -> Result<(Vec<SchemeExpr
* It's basically the inverse of scheme_tokenize and scheme_parse; the output representation is
* just plain old LISP/Scheme s-expr syntax.
*/
-fn scheme_repr(ast: &SchemeExpr) -> Result<String, &'static str> {
+fn scheme_repr(ast: &SchemeExpr) -> Result<String, String> {
return match ast {
&SchemeExpr::SchemeTrue => Ok("#t".to_string()),
&SchemeExpr::SchemeFalse => Ok("#f".to_string()),
@@ -265,7 +265,7 @@ fn scheme_repr(ast: &SchemeExpr) -> Result<String, &'static str> {
//////////// Expression Evaluation
-fn quote_action<'a>(list: &Vec<SchemeExpr>) -> Result<SchemeExpr, &'static str> {
+fn quote_action<'a>(list: &Vec<SchemeExpr>) -> Result<SchemeExpr, String> {
// XXX: why can't I '.map()' here? (try .iter().skip(1)...)
let mut body = Vec::<SchemeExpr>::new();
for el in list[1..].to_vec() {
@@ -276,12 +276,13 @@ fn quote_action<'a>(list: &Vec<SchemeExpr>) -> Result<SchemeExpr, &'static str>
fn cond_action<'a, 'b>(list: &Vec<SchemeExpr>,
ctx: HashMap<String, SchemeExpr>,
- env: &mut HashMap<String, SchemeExpr>) -> Result<SchemeExpr, &'static str> {
+ env: &mut HashMap<String, SchemeExpr>) -> Result<SchemeExpr, String> {
for line in list.iter().skip(1) {
match line {
&SchemeExpr::SchemeList(ref inner) => {
if inner.len() != 2 {
- return Err("cond must contain tuples of (predicate, value) (len !=2)");
+ return Err(format!("cond must contain tuples of (predicate, value) (len !=2) at: {}",
+ scheme_repr(line).unwrap()));
}
let pred = &inner[0];
let val = &inner[1];
@@ -290,7 +291,8 @@ fn cond_action<'a, 'b>(list: &Vec<SchemeExpr>,
return scheme_meaning(&val, ctx, env);
} },
_ => {
- return Err("cond must contain tuples of (predicate, value)"); },
+ return Err(format!("cond must contain tuples of (predicate, value); got: {}",
+ scheme_repr(line).unwrap())); },
}
}
// "undefined", return empty tuple
@@ -298,35 +300,38 @@ fn cond_action<'a, 'b>(list: &Vec<SchemeExpr>,
}
fn lambda_action<'a>(list: &Vec<SchemeExpr>,
- ctx: HashMap<String, SchemeExpr>) -> Result<SchemeExpr, &'static str> {
+ ctx: HashMap<String, SchemeExpr>) -> Result<SchemeExpr, String> {
if list.len() < 3 {
- return Err("lambda must have a bind and at least one body expr");
+ return Err(format!("lambda must have a bind and at least one body expr"));
}
let mut binds = Vec::<String>::new();
let bind_list = match &list[1] {
&SchemeExpr::SchemeList(ref bl) => bl,
- _ => { return Err("second arg to lambda must be a list of binds") },
+ _ => { return Err(format!("second arg to lambda must be a list of binds; got: {}",
+ scheme_repr(&list[1]).unwrap())); },
};
for bind in bind_list {
match bind {
&SchemeExpr::SchemeIdentifier(ref name) =>
binds.push(name.clone()),
- _ => return Err("lambda binds must all be non-builtin symbols")
+ _ => return Err(format!("lambda binds must all be non-builtin symbols; got: {}",
+ scheme_repr(bind).unwrap()))
}
}
let body = list.iter().skip(2).map(|x| x.clone()).collect();
Ok(SchemeExpr::SchemeProcedure(binds, body, ctx.clone()))
}
-fn apply_math_op<'a>(action: &'a str, args: Vec<SchemeExpr>) -> Result<SchemeExpr, &'static str> {
+fn apply_math_op<'a>(action: &'a str, args: Vec<SchemeExpr>) -> Result<SchemeExpr, String> {
if args.len() < 2 {
- return Err("math builtins take two or more args");
+ return Err(format!("math builtins take two or more args (at {})", action));
}
let mut vals = Vec::<f64>::new();
for arg in args {
match arg {
SchemeExpr::SchemeNum(x) => { vals.push(x) },
- _ => { return Err("math builtins take only numerical types") },
+ _ => { return Err(format!("math builtins take only numerical types (got {})",
+ scheme_repr(&arg).unwrap())) },
}
}
@@ -335,14 +340,14 @@ fn apply_math_op<'a>(action: &'a str, args: Vec<SchemeExpr>) -> Result<SchemeExp
"*" => vals.iter().fold(1., |a, &b| a * b),
"-" => vals[1..].iter().fold(vals[0], |a, &b| a - b),
"/" => vals[1..].iter().fold(vals[0], |a, &b| a / b),
- _ => { return Err("unimplemented math operation"); },
+ _ => { return Err(format!("unimplemented math operation: {}", action)); },
};
Ok(SchemeExpr::SchemeNum(ret))
}
-fn apply_typecheck<'a>(action: &'a str, args: Vec<SchemeExpr>) -> Result<SchemeExpr, &'static str> {
+fn apply_typecheck<'a>(action: &'a str, args: Vec<SchemeExpr>) -> Result<SchemeExpr, String> {
if args.len() != 1 {
- return Err("typecheck builtins take a single argument");
+ return Err(format!("typecheck builtins take a single argument (for {})", action));
}
let arg: &SchemeExpr = &args[0];
let ret: bool = match action {
@@ -357,7 +362,7 @@ fn apply_typecheck<'a>(action: &'a str, args: Vec<SchemeExpr>) -> Result<SchemeE
SchemeExpr::SchemeFalse |
SchemeExpr::SchemeNum(_) => true,
_ => false},
- _ => { return Err("unimplemented typecheck builtin"); },
+ _ => { return Err(format!("unimplemented typecheck builtin: {}", action)); },
};
if ret {
Ok(SchemeExpr::SchemeTrue)
@@ -372,7 +377,7 @@ fn apply_typecheck<'a>(action: &'a str, args: Vec<SchemeExpr>) -> Result<SchemeE
*/
fn apply_action<'a, 'b>(list: &Vec<SchemeExpr>,
ctx: HashMap<String, SchemeExpr>,
- env: &mut HashMap<String, SchemeExpr>) -> Result<SchemeExpr, &'static str> {
+ env: &mut HashMap<String, SchemeExpr>) -> Result<SchemeExpr, String> {
if list.len() == 0 {
// TODO: is this correct?
return Ok(SchemeExpr::SchemeNull);
@@ -388,7 +393,7 @@ fn apply_action<'a, 'b>(list: &Vec<SchemeExpr>,
"null?" | "number?" | "zero?" | "atom?" => apply_typecheck(builtin, args),
"eq?" => {
if args.len() != 2 {
- return Err("eq? takes only two arguments");
+ return Err(format!("eq? takes only two arguments"));
}
if args[0] == args[1] {
return Ok(SchemeExpr::SchemeTrue)
@@ -398,29 +403,29 @@ fn apply_action<'a, 'b>(list: &Vec<SchemeExpr>,
},
"car" => {
if args.len() != 1 {
- return Err("car takes a single list argument");
+ return Err(format!("car takes a single list argument"));
}
match &args[0] {
&SchemeExpr::SchemeList(ref list) => {
Ok(list[0].clone())
},
- _ => Err("cdr takes only lists")
+ _ => Err(format!("cdr takes only lists"))
}
},
"cdr" => {
if args.len() != 1 {
- return Err("cdr takes a single list argument");
+ return Err(format!("cdr takes a single list argument"));
}
match &args[0] {
&SchemeExpr::SchemeList(ref list) => {
Ok(SchemeExpr::SchemeList(list[1..].to_vec()))
},
- _ => Err("car takes only lists")
+ _ => Err(format!("car takes only lists"))
}
},
"cons" => {
if args.len() != 2 {
- return Err("cons takes two arguments");
+ return Err(format!("cons takes two arguments"));
}
match &args[1] {
&SchemeExpr::SchemeList(ref list) => {
@@ -428,10 +433,10 @@ fn apply_action<'a, 'b>(list: &Vec<SchemeExpr>,
ret.extend_from_slice(list);
Ok(SchemeExpr::SchemeList(ret))
},
- _ => Err("cdr takes only lists")
+ _ => Err(format!("cdr takes only lists"))
}
},
- _ => Err("unimplemented builtin"),
+ _ => Err(format!("unimplemented builtin: {}", builtin)),
}; },
&SchemeExpr::SchemeList(_) => {
let procedure: SchemeExpr = try!(scheme_meaning(&action, ctx.clone(), env));
@@ -439,10 +444,10 @@ fn apply_action<'a, 'b>(list: &Vec<SchemeExpr>,
SchemeExpr::SchemeProcedure(binds, body, proc_ctx) => {
// This block of code implements procedure (lambda) application
if body.len() != 1 {
- return Err("prodedure must have single-expression body");
+ return Err(format!("prodedure must have single-expression body"));
}
if binds.len() != args.len() {
- return Err("wrong number of args to procedure");
+ return Err(format!("wrong number of args to procedure"));
}
let mut closure = proc_ctx.clone();
for (name, arg) in binds.iter().zip(args) {
@@ -450,9 +455,10 @@ fn apply_action<'a, 'b>(list: &Vec<SchemeExpr>,
}
return scheme_meaning(&body[0], closure, env);
},
- _ => { return Err("non-procedure at head of expression"); },
+ _ => { return Err(format!("non-procedure at head of expression: {}",
+ scheme_repr(&procedure).unwrap())); },
} },
- _ => { return Err("apply called with something non-applicable"); },
+ _ => { return Err(format!("apply called with something non-applicable")); },
}
}
@@ -461,7 +467,7 @@ fn apply_action<'a, 'b>(list: &Vec<SchemeExpr>,
*/
fn scheme_meaning<'a, 'b>(ast: &SchemeExpr,
ctx: HashMap<String, SchemeExpr>,
- env: &mut HashMap<String, SchemeExpr>) -> Result<SchemeExpr, &'static str> {
+ env: &mut HashMap<String, SchemeExpr>) -> Result<SchemeExpr, String> {
return match ast {
// "identity actions"
@@ -487,7 +493,7 @@ fn scheme_meaning<'a, 'b>(ast: &SchemeExpr,
println!("{}", scheme_repr(val).unwrap());
Ok(SchemeExpr::SchemeNull)
},
- None => Err("symbol not defined"),
+ None => Err(format!("symbol not defined: {}", sym)),
}
}
}
@@ -516,7 +522,7 @@ fn scheme_meaning<'a, 'b>(ast: &SchemeExpr,
}
fn scheme_eval<'a, 'b>(ast: &'a SchemeExpr,
- env: &mut HashMap<String, SchemeExpr>) -> Result<SchemeExpr, &'static str> {
+ env: &mut HashMap<String, SchemeExpr>) -> Result<SchemeExpr, String> {
let ctx = HashMap::<String, SchemeExpr>::new();
Ok(try!(scheme_meaning(ast, ctx, env)))
}