(Lisp in (Rust)) – Code Revision Stack Exchange

I liked the Norvig Lispy and made it the first version in Rust. I'd like your thoughts 🙂

Two main questions:

  1. Is there a way to write assurer_tonicité without macro? I tried it, but I had a hard time typing it
  2. If you were to continue and allow to definewhich would define a RispExp in RispEnvWhat is Rust's idiomatic way of doing this? I could pass & mut RispExpbut it is wrong to mutate and return to eval. Would you do some kind of system based on the "effects"? (i.e, eval returns (RispExp, RispEffects)

And of course, any other thought greatly appreciated 🙂

use std :: collections :: HashMap;
use std :: io;
use std :: num :: ParseFloatError;

/ *
Types
* /

#[derive(Clone)]
enum RispExp {
Bool (bool),
Symbol (string),
Number (f64),
List (Vec)
Func (fn (&[RispExp]) -> Result)
}

#[derive(Debug)]
enum RispErr {
Reason (chain),
}

struct RispEnv {
data: HashMap,
}

impl PartialEq for RispExp {
fn eq (& self, other: & RispExp) -> bool {
match (auto, other) {
(RispExp :: Bool (ref A), RispExp :: Bool (ref B)) => a == b,
(RispExp :: Symbol (ref), RispExp :: Symbol (b)) => a == b,
(RispExp :: Number (ref A), RispExp :: Number (ref) => a == b,
(RispExp :: List (ref), RispExp :: List (ref b)) => a == b,
_ => false,
}
}
}

/ *
Impression
* /

fn to_str (exp: & RispExp) -> String {
match exp {
RispExp :: Symbol (s) => s.clone (),
RispExp :: Number (n) => n.to_string (),
RispExp :: Bool (b) => b.to_string (),
RispExp :: List (list) => {
let xs: Vec = list
.iter ()
.map (| x | to_str (x))
.collection();
return the format! ("({})", xs.join (","));
}
RispExp :: Func (_) => "Function {}". To_string (),
}
}

/ *
ca.
* /

fn parse_single_float (exp: & RispExp) -> Result {
match exp {
RispExp :: Number (num) => Ok (* num),
_ => Err (
RispErr :: Reason (
format! ("we expect a number, got form ="} ", to_str (exp))
)
)
}
}

fn parse_list_of_floats (arguments: &[RispExp]) -> Result <Vec, RispErr> {
return args
.iter ()
.map (| x | parse_single_float (x))
.collect :: <Result <Vec, RispErr >> ();
}

macro_rules! Ensure_tonicity {
($ check_fn: expr) => {{
| args: &[RispExp]| -> Result {
let floats = parse_list_of_floats (args)?;
let first = floats.first (). ok_or (RispErr :: Reason ("wait for at least one number" .to_string ()))?;
let rest = & float[1..];
fn f (prev: & f64, xs: &[f64]) -> bool {
corresponds to xs.first () {
Some (x) => $ check_fn (prev, x) && f (x, & xs)[1..])
None => true,
}
};
return Ok (RispExp :: Bool (f (first, rest)));
}
}};
}

fn default_env () -> RispEnv {
leave mut data: HashMap = HashMap :: new ();
data.insert (
"+". to_string (),
RispExp :: Func (
| args: &[RispExp]| -> Result {
let sum = list_of_floats (args)? iter (). fold (0.0, | sum, a | sum + a);
returns Ok (RispExp :: Number (sum));
}
)
)
data.insert (
"-". to_string (),
RispExp :: Func (
| args: &[RispExp]| -> Result {
let floats = parse_list_of_floats (args)?;
let first = * floats.first (). ok_or (RispErr :: Reason ("wait for at least one number" .to_string ()))?;
leave sum_of_rest = float[1..].iter () fold (0.0, | sum, a | sum + a);

return Ok (RispExp :: Number (first - sum_of_rest));
}
)
)
data.insert (
"=". to_string (),
RispExp :: Func (Ensure_tonicity! (| A, b | a == b))
)
data.insert (
">". to_string (),
RispExp :: Func (Ensure_tonicity! (| A, b | a> b))
)
data.insert (
"> =". to_string (),
RispExp :: Func (Ensure_tonicity! (| A, b | a> = b))
)
data.insert (
"<". to_string (),
RispExp :: Func (Ensure_tonicity! (| A, b | a <b))
)
data.insert (
"<=". to_string (),
RispExp :: Func (Ensure_tonicity! (| A, b | a <= b))
  );

  return RispEnv {data: data}
}

/* 
  Eval
*/

fn eval_if_args(arg_forms: &[RispExp], env: &RispEnv) -> Result {
let test_form = arg_forms.first (). ok_or (
RispErr :: Reason (
"expected test form" .to_string (),
)
)?
let test_eval = eval (test_form, env)?;
match test_eval {
RispExp :: Bool (b) => {
let form_idx = if b {1} else {2}
let res_form = arg_forms.get (form_id)
.ok_or (RispErr :: Reason (
format! ("expected form idx = {}", record_idx)
))?
let res_eval = eval (res_form, env);

return res_eval;
}
_ => Err (
RispErr :: Reason (format! ("Unexpected test form ="} ", to_str (test_form)))
)
}
}

fn eval_built_in_symbol (sym: String, arg_forms: &[RispExp], env: & RispEnv) -> Result {
correspond to sym.as_ref () {
"if" => eval_if_args (arg_forms, env),
_ => Err (RispErr :: Reason (format! ("Unknown embedded symbol ="} ", sym))),
}
}

fn eval (exp: & RispExp, env: & RispEnv) -> Result {
match exp {
RispExp :: Symbol (k) =>
env.data.get (k)
.or (Some (exp))
.ok_or (
RispErr :: Reason (
format! ("unexpected symbol k = {}", k)
)
)
.map (| x | x.clone ())
,
RispExp :: Bool (_a) => Ok (exp.clone ()),
RispExp :: Number (_a) => Ok (exp.clone ()),
RispExp :: List (list) => {
let first_form = list
.first()
.ok_or (RispErr :: Reason ("expects a non-empty list" .to_string ()))?;
let arg_forms = & list[1..];
let first_eval = eval (first_form, env)?;
back match first_eval {
RispExp :: Symbol (sym) => eval_built_in_symbol (sym, arg_forms, env),
RispExp :: Func (f) => {
let arg_evals = arg_forms
.iter ()
.map (| x | eval (x, env))
.collect :: <Result <Vec, RispErr >> ()?;
return f (& arg_evals);
}
_ => Err (
RispErr :: Reason (
format! ("the first form must be a function, but got form ="} ", to_str (& first_eval))
)
)
}
}
RispExp :: Func (_) => Err (
RispErr :: Reason (
format! ("unexpected form ="} ", to_str (exp))
)
)
}
}

/ *
Analyze
* /

fn read_seq (tokens: &[String], start: usize) -> Result<(RispExp, usize), RispErr> {
leave mut res: Vec = vec![];
let mut next = start;
loop {
leave next_token = tokens
.get (next)
.ok_or (RispErr :: Reason ("did not find closing") `" .to_string ()))
;
if next_token == ")" {
returns Ok ((RispExp :: List (res), next + 1)) // skip `)`, token header after
}
let (exp, new_next) = parse (& tokens, next)?;
res.push (exp);
next = new_next;
}
}

fn parse_atom (token: & str) -> RispExp {
corresponds to token.as_ref () {
"true" => RispExp :: Bool (true),
"false" => RispExp :: Bool (false),
_ => {
let potential_float: Result = token.parse ();
back match potential_float {
Ok (v) => RispExp :: Number (v),
Err (_) => RispExp :: Symbol (token.to_string (). Clone ())
}
}
}
}

fn parse (tokens: &[String], pos: usize) -> Result<(RispExp, usize), RispErr> {
leave token = tokens
.get (pos)
.ok_or (
RispErr :: Reason (format! ("Unable to get the token for pos ="} ", pos))
)?
let to_match = & token[..];
corresponds to to_match {
"(" => read_seq (tokens, pos + 1),
")" => Err (RispErr :: Reason ("unexpected") `" .to_string ())),
_ => Ok (
(parse_atom (token), pos + 1)
)
}
}

fn tokenize (expr: String) -> Vec {
back express
.replace ("(", "(")
.replace (")", ")")
.Split(" ")
.map (| x | x.trim (). to_string ())
.filter (| x |! x.is_empty ())
.collection();
}

/ *
REPL
* /

fn parse_eval_print (expr: String) -> Result {
let (parsed_exp, _) = parse (& tokenize (expr), 0)?;
let evaled_exp = eval (& parsed_exp, & default_env ())?;
return Ok (to_str (& evaled_exp));
}

fn slurp_expr () -> String {
let mut expr = String :: new ();

io :: stdin (). read_line (& mut expr)
.expect ("Can not read the line");

return expr;
}

fn main () {
loop {
println! ( "Risp>");
let expr = slurp_expr ();
corresponds to parse_eval_print (expr) {
Ok (res) => println! ("// => {}", res),
Err (e) => match e {
RispErr :: Reason (msg) => println! ("// => {}", msg),
}
}
}
}
`` `