Skip to content

Instantly share code, notes, and snippets.

@stopachka
Last active March 7, 2023 04:24

Revisions

  1. stopachka revised this gist May 4, 2019. 1 changed file with 2 additions and 263 deletions.
    265 changes: 2 additions & 263 deletions risp-1.1.rs
    Original file line number Diff line number Diff line change
    @@ -7,16 +7,6 @@ use std::num::ParseFloatError;
    Types
    */

    #[derive(Clone)]
    enum RispExp {use std::collections::HashMap;
    use std::fmt;
    use std::io;
    use std::num::ParseFloatError;

    /*
    Types
    */

    #[derive(Clone)]
    enum RispExp {
    Bool(bool),
    @@ -29,7 +19,7 @@ enum RispExp {
    impl fmt::Display for RispExp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    let str = match self {
    RispExp::Bool(s) => s.to_string(),
    RispExp::Bool(a) => a.to_string(),
    RispExp::Symbol(s) => s.clone(),
    RispExp::Number(n) => n.to_string(),
    RispExp::List(list) => {
    @@ -72,7 +62,7 @@ fn tokenize(expr: String) -> Vec<String> {
    fn parse<'a>(tokens: &'a [String]) -> Result<(RispExp, &'a [String]), RispErr> {
    let (token, rest) = tokens.split_first()
    .ok_or(
    RispErr::Reason(format!("could not get token for pos=''"))
    RispErr::Reason("could not get token".to_string())
    )?;
    match &token[..] {
    "(" => read_seq(rest),
    @@ -269,255 +259,4 @@ fn main() {
    },
    }
    }
    }
    Bool(bool),
    Symbol(String),
    Number(f64),
    List(Vec<RispExp>),
    Func(fn(&[RispExp]) -> Result<RispExp, RispErr>),
    }

    impl fmt::Display for RispExp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    let str = match self {
    RispExp::Bool(s) => s.to_string(),
    RispExp::Symbol(s) => s.clone(),
    RispExp::Number(n) => n.to_string(),
    RispExp::List(list) => {
    let xs: Vec<String> = list
    .iter()
    .map(|x| x.to_string())
    .collect();
    format!("({})", xs.join(","))
    },
    RispExp::Func(_) => "Function {}".to_string(),
    };

    write!(f, "{}", str)
    }
    }

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

    #[derive(Clone)]
    struct RispEnv {
    data: HashMap<String, RispExp>,
    }

    /*
    Parse
    */

    fn tokenize(expr: String) -> Vec<String> {
    expr
    .replace("(", " ( ")
    .replace(")", " ) ")
    .split_whitespace()
    .map(|x| x.to_string())
    .collect()
    }

    fn parse<'a>(tokens: &'a [String]) -> Result<(RispExp, &'a [String]), RispErr> {
    let (token, rest) = tokens.split_first()
    .ok_or(
    RispErr::Reason(format!("could not get token for pos=''"))
    )?;
    match &token[..] {
    "(" => read_seq(rest),
    ")" => Err(RispErr::Reason("unexpected `)`".to_string())),
    _ => Ok((parse_atom(token), rest)),
    }
    }

    fn read_seq<'a>(tokens: &'a [String]) -> Result<(RispExp, &'a [String]), RispErr> {
    let mut res: Vec<RispExp> = vec![];
    let mut xs = tokens;
    loop {
    let (next_token, rest) = xs
    .split_first()
    .ok_or(RispErr::Reason("could not find closing `)`".to_string()))
    ?;
    if next_token == ")" {
    return Ok((RispExp::List(res), rest)) // skip `)`, head to the token after
    }
    let (exp, new_xs) = parse(&xs)?;
    res.push(exp);
    xs = new_xs;
    }
    }

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

    /*
    Env
    */

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

    fn default_env() -> RispEnv {
    let mut data: HashMap<String, RispExp> = HashMap::new();
    data.insert(
    "+".to_string(),
    RispExp::Func(
    |args: &[RispExp]| -> Result<RispExp, RispErr> {
    let sum = parse_list_of_floats(args)?.iter().fold(0.0, |sum, a| sum + a);

    Ok(RispExp::Number(sum))
    }
    )
    );
    data.insert(
    "-".to_string(),
    RispExp::Func(
    |args: &[RispExp]| -> Result<RispExp, RispErr> {
    let floats = parse_list_of_floats(args)?;
    let first = *floats.first().ok_or(RispErr::Reason("expected at least one number".to_string()))?;
    let sum_of_rest = floats[1..].iter().fold(0.0, |sum, a| sum + a);

    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))
    );

    RispEnv {data}
    }

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

    fn parse_single_float(exp: &RispExp) -> Result<f64, RispErr> {
    match exp {
    RispExp::Number(num) => Ok(*num),
    _ => Err(RispErr::Reason("expected a number".to_string())),
    }
    }

    /*
    Eval
    */

    fn eval(exp: &RispExp, env: &mut RispEnv) -> Result<RispExp, RispErr> {
    match exp {
    RispExp::Symbol(k) =>
    env.data.get(k)
    .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("expected a non-empty list".to_string()))?;
    let arg_forms = &list[1..];
    let first_eval = eval(first_form, env)?;
    match first_eval {
    RispExp::Func(f) => {
    let args_eval = arg_forms
    .iter()
    .map(|x| eval(x, env))
    .collect::<Result<Vec<RispExp>, RispErr>>();
    f(&args_eval?)
    },
    _ => Err(
    RispErr::Reason("first form must be a function".to_string())
    ),
    }
    },
    RispExp::Func(_) => Err(
    RispErr::Reason("unexpected form".to_string())
    ),
    }
    }

    /*
    Repl
    */

    fn parse_eval_print(expr: String, env: &mut RispEnv) -> Result<RispExp, RispErr> {
    let (parsed_exp, _) = parse(&tokenize(expr))?;
    let evaled_exp = eval(&parsed_exp, env)?;

    Ok(evaled_exp)
    }

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

    io::stdin().read_line(&mut expr)
    .expect("Failed to read line");

    expr
    }

    fn main() {
    let env = &mut default_env();
    loop {
    println!("risp >");
    let expr = slurp_expr();
    match parse_eval_print(expr, env) {
    Ok(res) => println!("// 🔥 => {}", res),
    Err(e) => match e {
    RispErr::Reason(msg) => println!("// 🙀 => {}", msg),
    },
    }
    }
    }
  2. stopachka revised this gist May 4, 2019. 1 changed file with 261 additions and 0 deletions.
    261 changes: 261 additions & 0 deletions risp-1.1.rs
    Original file line number Diff line number Diff line change
    @@ -7,6 +7,16 @@ use std::num::ParseFloatError;
    Types
    */

    #[derive(Clone)]
    enum RispExp {use std::collections::HashMap;
    use std::fmt;
    use std::io;
    use std::num::ParseFloatError;

    /*
    Types
    */

    #[derive(Clone)]
    enum RispExp {
    Bool(bool),
    @@ -231,6 +241,257 @@ fn eval(exp: &RispExp, env: &mut RispEnv) -> Result<RispExp, RispErr> {
    Repl
    */

    fn parse_eval(expr: String, env: &mut RispEnv) -> Result<RispExp, RispErr> {
    let (parsed_exp, _) = parse(&tokenize(expr))?;
    let evaled_exp = eval(&parsed_exp, env)?;

    Ok(evaled_exp)
    }

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

    io::stdin().read_line(&mut expr)
    .expect("Failed to read line");

    expr
    }

    fn main() {
    let env = &mut default_env();
    loop {
    println!("risp >");
    let expr = slurp_expr();
    match parse_eval(expr, env) {
    Ok(res) => println!("// 🔥 => {}", res),
    Err(e) => match e {
    RispErr::Reason(msg) => println!("// 🙀 => {}", msg),
    },
    }
    }
    }
    Bool(bool),
    Symbol(String),
    Number(f64),
    List(Vec<RispExp>),
    Func(fn(&[RispExp]) -> Result<RispExp, RispErr>),
    }

    impl fmt::Display for RispExp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    let str = match self {
    RispExp::Bool(s) => s.to_string(),
    RispExp::Symbol(s) => s.clone(),
    RispExp::Number(n) => n.to_string(),
    RispExp::List(list) => {
    let xs: Vec<String> = list
    .iter()
    .map(|x| x.to_string())
    .collect();
    format!("({})", xs.join(","))
    },
    RispExp::Func(_) => "Function {}".to_string(),
    };

    write!(f, "{}", str)
    }
    }

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

    #[derive(Clone)]
    struct RispEnv {
    data: HashMap<String, RispExp>,
    }

    /*
    Parse
    */

    fn tokenize(expr: String) -> Vec<String> {
    expr
    .replace("(", " ( ")
    .replace(")", " ) ")
    .split_whitespace()
    .map(|x| x.to_string())
    .collect()
    }

    fn parse<'a>(tokens: &'a [String]) -> Result<(RispExp, &'a [String]), RispErr> {
    let (token, rest) = tokens.split_first()
    .ok_or(
    RispErr::Reason(format!("could not get token for pos=''"))
    )?;
    match &token[..] {
    "(" => read_seq(rest),
    ")" => Err(RispErr::Reason("unexpected `)`".to_string())),
    _ => Ok((parse_atom(token), rest)),
    }
    }

    fn read_seq<'a>(tokens: &'a [String]) -> Result<(RispExp, &'a [String]), RispErr> {
    let mut res: Vec<RispExp> = vec![];
    let mut xs = tokens;
    loop {
    let (next_token, rest) = xs
    .split_first()
    .ok_or(RispErr::Reason("could not find closing `)`".to_string()))
    ?;
    if next_token == ")" {
    return Ok((RispExp::List(res), rest)) // skip `)`, head to the token after
    }
    let (exp, new_xs) = parse(&xs)?;
    res.push(exp);
    xs = new_xs;
    }
    }

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

    /*
    Env
    */

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

    fn default_env() -> RispEnv {
    let mut data: HashMap<String, RispExp> = HashMap::new();
    data.insert(
    "+".to_string(),
    RispExp::Func(
    |args: &[RispExp]| -> Result<RispExp, RispErr> {
    let sum = parse_list_of_floats(args)?.iter().fold(0.0, |sum, a| sum + a);

    Ok(RispExp::Number(sum))
    }
    )
    );
    data.insert(
    "-".to_string(),
    RispExp::Func(
    |args: &[RispExp]| -> Result<RispExp, RispErr> {
    let floats = parse_list_of_floats(args)?;
    let first = *floats.first().ok_or(RispErr::Reason("expected at least one number".to_string()))?;
    let sum_of_rest = floats[1..].iter().fold(0.0, |sum, a| sum + a);

    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))
    );

    RispEnv {data}
    }

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

    fn parse_single_float(exp: &RispExp) -> Result<f64, RispErr> {
    match exp {
    RispExp::Number(num) => Ok(*num),
    _ => Err(RispErr::Reason("expected a number".to_string())),
    }
    }

    /*
    Eval
    */

    fn eval(exp: &RispExp, env: &mut RispEnv) -> Result<RispExp, RispErr> {
    match exp {
    RispExp::Symbol(k) =>
    env.data.get(k)
    .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("expected a non-empty list".to_string()))?;
    let arg_forms = &list[1..];
    let first_eval = eval(first_form, env)?;
    match first_eval {
    RispExp::Func(f) => {
    let args_eval = arg_forms
    .iter()
    .map(|x| eval(x, env))
    .collect::<Result<Vec<RispExp>, RispErr>>();
    f(&args_eval?)
    },
    _ => Err(
    RispErr::Reason("first form must be a function".to_string())
    ),
    }
    },
    RispExp::Func(_) => Err(
    RispErr::Reason("unexpected form".to_string())
    ),
    }
    }

    /*
    Repl
    */

    fn parse_eval_print(expr: String, env: &mut RispEnv) -> Result<RispExp, RispErr> {
    let (parsed_exp, _) = parse(&tokenize(expr))?;
    let evaled_exp = eval(&parsed_exp, env)?;
  3. stopachka revised this gist May 4, 2019. 1 changed file with 10 additions and 7 deletions.
    17 changes: 10 additions & 7 deletions risp-1.1.rs
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,5 @@
    use std::collections::HashMap;
    use std::fmt;
    use std::io;
    use std::num::ParseFloatError;

    @@ -15,10 +16,10 @@ enum RispExp {
    Func(fn(&[RispExp]) -> Result<RispExp, RispErr>),
    }

    impl ToString for RispExp {
    fn to_string(&self) -> String {
    match self {
    RispExp::Bool(a) => a.to_string(),
    impl fmt::Display for RispExp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    let str = match self {
    RispExp::Bool(s) => s.to_string(),
    RispExp::Symbol(s) => s.clone(),
    RispExp::Number(n) => n.to_string(),
    RispExp::List(list) => {
    @@ -29,7 +30,9 @@ impl ToString for RispExp {
    format!("({})", xs.join(","))
    },
    RispExp::Func(_) => "Function {}".to_string(),
    }
    };

    write!(f, "{}", str)
    }
    }

    @@ -228,11 +231,11 @@ fn eval(exp: &RispExp, env: &mut RispEnv) -> Result<RispExp, RispErr> {
    Repl
    */

    fn parse_eval_print(expr: String, env: &mut RispEnv) -> Result<String, RispErr> {
    fn parse_eval_print(expr: String, env: &mut RispEnv) -> Result<RispExp, RispErr> {
    let (parsed_exp, _) = parse(&tokenize(expr))?;
    let evaled_exp = eval(&parsed_exp, env)?;

    Ok(evaled_exp.to_string())
    Ok(evaled_exp)
    }

    fn slurp_expr() -> String {
  4. stopachka revised this gist May 4, 2019. 1 changed file with 15 additions and 15 deletions.
    30 changes: 15 additions & 15 deletions risp-1.1.rs
    Original file line number Diff line number Diff line change
    @@ -56,33 +56,32 @@ fn tokenize(expr: String) -> Vec<String> {
    .collect()
    }

    fn parse(tokens: &[String], pos: usize) -> Result<(RispExp, usize), RispErr> {
    let token = tokens
    .get(pos)
    fn parse<'a>(tokens: &'a [String]) -> Result<(RispExp, &'a [String]), RispErr> {
    let (token, rest) = tokens.split_first()
    .ok_or(
    RispErr::Reason(format!("could not get token for pos='{}'", pos))
    RispErr::Reason(format!("could not get token for pos=''"))
    )?;
    match &token[..] {
    "(" => read_seq(tokens, pos + 1),
    "(" => read_seq(rest),
    ")" => Err(RispErr::Reason("unexpected `)`".to_string())),
    _ => Ok((parse_atom(token), pos + 1)),
    _ => Ok((parse_atom(token), rest)),
    }
    }

    fn read_seq(tokens: &[String], start: usize) -> Result<(RispExp, usize), RispErr> {
    fn read_seq<'a>(tokens: &'a [String]) -> Result<(RispExp, &'a [String]), RispErr> {
    let mut res: Vec<RispExp> = vec![];
    let mut next = start;
    let mut xs = tokens;
    loop {
    let next_token = tokens
    .get(next)
    let (next_token, rest) = xs
    .split_first()
    .ok_or(RispErr::Reason("could not find closing `)`".to_string()))
    ?;
    if next_token == ")" {
    return Ok((RispExp::List(res), next + 1)) // skip `)`, head to the token after
    return Ok((RispExp::List(res), rest)) // skip `)`, head to the token after
    }
    let (exp, new_next) = parse(&tokens, next)?;
    let (exp, new_xs) = parse(&xs)?;
    res.push(exp);
    next = new_next;
    xs = new_xs;
    }
    }

    @@ -230,8 +229,9 @@ fn eval(exp: &RispExp, env: &mut RispEnv) -> Result<RispExp, RispErr> {
    */

    fn parse_eval_print(expr: String, env: &mut RispEnv) -> Result<String, RispErr> {
    let (parsed_exp, _) = parse(&tokenize(expr), 0)?;
    let (parsed_exp, _) = parse(&tokenize(expr))?;
    let evaled_exp = eval(&parsed_exp, env)?;

    Ok(evaled_exp.to_string())
    }

    @@ -248,7 +248,7 @@ fn main() {
    let env = &mut default_env();
    loop {
    println!("risp >");
    let expr = slurp_expr();;
    let expr = slurp_expr();
    match parse_eval_print(expr, env) {
    Ok(res) => println!("// 🔥 => {}", res),
    Err(e) => match e {
  5. stopachka revised this gist May 2, 2019. 1 changed file with 16 additions and 13 deletions.
    29 changes: 16 additions & 13 deletions risp-1.1.rs
    Original file line number Diff line number Diff line change
    @@ -26,7 +26,7 @@ impl ToString for RispExp {
    .iter()
    .map(|x| x.to_string())
    .collect();
    return format!("({})", xs.join(","));
    format!("({})", xs.join(","))
    },
    RispExp::Func(_) => "Function {}".to_string(),
    }
    @@ -48,12 +48,12 @@ struct RispEnv {
    */

    fn tokenize(expr: String) -> Vec<String> {
    return expr
    expr
    .replace("(", " ( ")
    .replace(")", " ) ")
    .split_whitespace()
    .map(|x| x.to_string())
    .collect();
    .collect()
    }

    fn parse(tokens: &[String], pos: usize) -> Result<(RispExp, usize), RispErr> {
    @@ -128,7 +128,8 @@ fn default_env() -> RispEnv {
    RispExp::Func(
    |args: &[RispExp]| -> Result<RispExp, RispErr> {
    let sum = parse_list_of_floats(args)?.iter().fold(0.0, |sum, a| sum + a);
    return Ok(RispExp::Number(sum));

    Ok(RispExp::Number(sum))
    }
    )
    );
    @@ -139,7 +140,8 @@ fn default_env() -> RispEnv {
    let floats = parse_list_of_floats(args)?;
    let first = *floats.first().ok_or(RispErr::Reason("expected at least one number".to_string()))?;
    let sum_of_rest = floats[1..].iter().fold(0.0, |sum, a| sum + a);
    return Ok(RispExp::Number(first - sum_of_rest));

    Ok(RispExp::Number(first - sum_of_rest))
    }
    )
    );
    @@ -164,14 +166,14 @@ fn default_env() -> RispEnv {
    RispExp::Func(ensure_tonicity!(|a, b| a <= b))
    );

    return RispEnv {data}
    RispEnv {data}
    }

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

    fn parse_single_float(exp: &RispExp) -> Result<f64, RispErr> {
    @@ -196,21 +198,21 @@ fn eval(exp: &RispExp, env: &mut RispEnv) -> Result<RispExp, RispErr> {
    )
    .map(|x| x.clone())
    ,
    RispExp::Number(_a) => Ok(exp.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("expected a non-empty list".to_string()))?;
    let arg_forms = &list[1..];
    let first_eval = eval(first_form, env)?;
    return match first_eval {
    match first_eval {
    RispExp::Func(f) => {
    let args_eval = arg_forms
    .iter()
    .map(|x| eval(x, env))
    .collect::<Result<Vec<RispExp>, RispErr>>();
    return f(&args_eval?);
    f(&args_eval?)
    },
    _ => Err(
    RispErr::Reason("first form must be a function".to_string())
    @@ -230,15 +232,16 @@ fn eval(exp: &RispExp, env: &mut RispEnv) -> Result<RispExp, RispErr> {
    fn parse_eval_print(expr: String, env: &mut RispEnv) -> Result<String, RispErr> {
    let (parsed_exp, _) = parse(&tokenize(expr), 0)?;
    let evaled_exp = eval(&parsed_exp, env)?;
    return Ok(evaled_exp.to_string());
    Ok(evaled_exp.to_string())
    }

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

    io::stdin().read_line(&mut expr)
    .expect("Failed to read line");
    return expr;

    expr
    }

    fn main() {
  6. stopachka revised this gist May 2, 2019. 1 changed file with 38 additions and 0 deletions.
    38 changes: 38 additions & 0 deletions risp-1.1.rs
    Original file line number Diff line number Diff line change
    @@ -104,6 +104,23 @@ fn parse_atom(token: &str) -> RispExp {
    Env
    */

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

    fn default_env() -> RispEnv {
    let mut data: HashMap<String, RispExp> = HashMap::new();
    data.insert(
    @@ -126,6 +143,27 @@ fn default_env() -> RispEnv {
    }
    )
    );
    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}
    }

  7. stopachka revised this gist May 2, 2019. 1 changed file with 25 additions and 65 deletions.
    90 changes: 25 additions & 65 deletions risp-1.1.rs
    Original file line number Diff line number Diff line change
    @@ -15,6 +15,24 @@ enum RispExp {
    Func(fn(&[RispExp]) -> Result<RispExp, RispErr>),
    }

    impl ToString for RispExp {
    fn to_string(&self) -> String {
    match self {
    RispExp::Bool(a) => a.to_string(),
    RispExp::Symbol(s) => s.clone(),
    RispExp::Number(n) => n.to_string(),
    RispExp::List(list) => {
    let xs: Vec<String> = list
    .iter()
    .map(|x| x.to_string())
    .collect();
    return format!("({})", xs.join(","));
    },
    RispExp::Func(_) => "Function {}".to_string(),
    }
    }
    }

    #[derive(Debug)]
    enum RispErr {
    Reason(String),
    @@ -33,9 +51,8 @@ fn tokenize(expr: String) -> Vec<String> {
    return expr
    .replace("(", " ( ")
    .replace(")", " ) ")
    .split(" ")
    .map(|x| x.trim().to_string())
    .filter(|x| !x.is_empty())
    .split_whitespace()
    .map(|x| x.to_string())
    .collect();
    }

    @@ -45,13 +62,10 @@ fn parse(tokens: &[String], pos: usize) -> Result<(RispExp, usize), RispErr> {
    .ok_or(
    RispErr::Reason(format!("could not get token for pos='{}'", pos))
    )?;
    let to_match = &token[..];
    match to_match {
    match &token[..] {
    "(" => read_seq(tokens, pos + 1),
    ")" => Err(RispErr::Reason("unexpected `)`".to_string())),
    _ => Ok(
    (parse_atom(token), pos + 1)
    ),
    _ => Ok((parse_atom(token), pos + 1)),
    }
    }

    @@ -78,7 +92,7 @@ fn parse_atom(token: &str) -> RispExp {
    "false" => RispExp::Bool(false),
    _ => {
    let potential_float: Result<f64, ParseFloatError> = token.parse();
    return match potential_float {
    match potential_float {
    Ok(v) => RispExp::Number(v),
    Err(_) => RispExp::Symbol(token.to_string().clone())
    }
    @@ -89,22 +103,6 @@ fn parse_atom(token: &str) -> RispExp {
    /*
    Env
    */
    macro_rules! ensure_tonicity {
    ($check_fn:expr) => {{
    |args: &[RispExp]| -> Result<RispExp, RispErr> {
    let floats = parse_list_of_floats(args)?;
    let first = floats.first().ok_or(RispErr::Reason("expected at least one number".to_string()))?;
    let rest = &floats[1..];
    fn f (prev: &f64, xs: &[f64]) -> bool {
    match 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 {
    let mut data: HashMap<String, RispExp> = HashMap::new();
    @@ -124,33 +122,11 @@ fn default_env() -> RispEnv {
    let floats = parse_list_of_floats(args)?;
    let first = *floats.first().ok_or(RispErr::Reason("expected at least one number".to_string()))?;
    let sum_of_rest = floats[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}
    return RispEnv {data}
    }

    fn parse_list_of_floats(args: &[RispExp]) -> Result<Vec<f64>, RispErr> {
    @@ -213,26 +189,10 @@ fn eval(exp: &RispExp, env: &mut RispEnv) -> Result<RispExp, RispErr> {
    Repl
    */

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

    fn parse_eval_print(expr: String, env: &mut RispEnv) -> Result<String, RispErr> {
    let (parsed_exp, _) = parse(&tokenize(expr), 0)?;
    let evaled_exp = eval(&parsed_exp, env)?;
    return Ok(to_str(&evaled_exp));
    return Ok(evaled_exp.to_string());
    }

    fn slurp_expr() -> String {
  8. stopachka revised this gist May 1, 2019. No changes.
  9. stopachka revised this gist May 1, 2019. 1 changed file with 11 additions and 5 deletions.
    16 changes: 11 additions & 5 deletions risp-1.1.rs
    Original file line number Diff line number Diff line change
    @@ -72,11 +72,17 @@ fn read_seq(tokens: &[String], start: usize) -> Result<(RispExp, usize), RispErr
    }
    }

    fn parse_atom(token: &str) -> RispExp {
    let potential_float: Result<f64, ParseFloatError> = token.parse();
    return match potential_float {
    Ok(v) => RispExp::Number(v),
    Err(_) => RispExp::Symbol(token.to_string().clone())
    fn parse_atom(token: &str) -> RispExp {
    match token.as_ref() {
    "true" => RispExp::Bool(true),
    "false" => RispExp::Bool(false),
    _ => {
    let potential_float: Result<f64, ParseFloatError> = token.parse();
    return match potential_float {
    Ok(v) => RispExp::Number(v),
    Err(_) => RispExp::Symbol(token.to_string().clone())
    }
    }
    }
    }

  10. stopachka created this gist May 1, 2019.
    252 changes: 252 additions & 0 deletions risp-1.1.rs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,252 @@
    use std::collections::HashMap;
    use std::io;
    use std::num::ParseFloatError;

    /*
    Types
    */

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

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

    #[derive(Clone)]
    struct RispEnv {
    data: HashMap<String, RispExp>,
    }

    /*
    Parse
    */

    fn tokenize(expr: String) -> Vec<String> {
    return expr
    .replace("(", " ( ")
    .replace(")", " ) ")
    .split(" ")
    .map(|x| x.trim().to_string())
    .filter(|x| !x.is_empty())
    .collect();
    }

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

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

    fn parse_atom(token: &str) -> RispExp {
    let potential_float: Result<f64, ParseFloatError> = token.parse();
    return match potential_float {
    Ok(v) => RispExp::Number(v),
    Err(_) => RispExp::Symbol(token.to_string().clone())
    }
    }

    /*
    Env
    */
    macro_rules! ensure_tonicity {
    ($check_fn:expr) => {{
    |args: &[RispExp]| -> Result<RispExp, RispErr> {
    let floats = parse_list_of_floats(args)?;
    let first = floats.first().ok_or(RispErr::Reason("expected at least one number".to_string()))?;
    let rest = &floats[1..];
    fn f (prev: &f64, xs: &[f64]) -> bool {
    match 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 {
    let mut data: HashMap<String, RispExp> = HashMap::new();
    data.insert(
    "+".to_string(),
    RispExp::Func(
    |args: &[RispExp]| -> Result<RispExp, RispErr> {
    let sum = parse_list_of_floats(args)?.iter().fold(0.0, |sum, a| sum + a);
    return Ok(RispExp::Number(sum));
    }
    )
    );
    data.insert(
    "-".to_string(),
    RispExp::Func(
    |args: &[RispExp]| -> Result<RispExp, RispErr> {
    let floats = parse_list_of_floats(args)?;
    let first = *floats.first().ok_or(RispErr::Reason("expected at least one number".to_string()))?;
    let sum_of_rest = floats[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}
    }

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

    fn parse_single_float(exp: &RispExp) -> Result<f64, RispErr> {
    match exp {
    RispExp::Number(num) => Ok(*num),
    _ => Err(RispErr::Reason("expected a number".to_string())),
    }
    }

    /*
    Eval
    */

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

    /*
    Repl
    */

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

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

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

    io::stdin().read_line(&mut expr)
    .expect("Failed to read line");
    return expr;
    }

    fn main() {
    let env = &mut default_env();
    loop {
    println!("risp >");
    let expr = slurp_expr();;
    match parse_eval_print(expr, env) {
    Ok(res) => println!("// 🔥 => {}", res),
    Err(e) => match e {
    RispErr::Reason(msg) => println!("// 🙀 => {}", msg),
    },
    }
    }
    }