diff options
author | Dominick Allen <dominick.allen1989@gmail.com> | 2020-06-26 21:12:16 -0500 |
---|---|---|
committer | Dominick Allen <dominick.allen1989@gmail.com> | 2020-06-26 21:12:16 -0500 |
commit | ad9f7e6a689c7fe42444cbe22ecdf1de8d092d5b (patch) | |
tree | f387be46e818cdfafeae36c69a94d09ed1da26f9 | |
parent | 36e1bf722a3d366cea20ab7315d63d588d23dc48 (diff) |
Evaluate basic arithmetic.
-rw-r--r-- | src/lib/eval/arith.rs | 83 | ||||
-rw-r--r-- | src/lib/eval/mod.rs | 62 | ||||
-rw-r--r-- | src/lib/mod.rs | 6 | ||||
-rw-r--r-- | src/lib/parse.rs | 12 | ||||
-rw-r--r-- | src/lib/sexpr.rs | 42 | ||||
-rw-r--r-- | src/lib/types.rs | 43 | ||||
-rw-r--r-- | src/main.rs | 7 |
7 files changed, 158 insertions, 97 deletions
diff --git a/src/lib/eval/arith.rs b/src/lib/eval/arith.rs index fae69de..fe3a06b 100644 --- a/src/lib/eval/arith.rs +++ b/src/lib/eval/arith.rs @@ -1,34 +1,67 @@ -use std::ops::Add; +use std::ops::{Add, Sub, Mul, Div}; use super::super::types::Type; -use super::super::types::Type::*; use super::super::types::Number; +fn apply_arithmetic( + func_i: fn(isize, isize) -> isize, + func_f: fn(f32, f32) -> f32, + operand_a: &Type, + operand_b: &Type) -> Result<Type, String> { + + match (operand_a, operand_b) { + (Type::Bool(_), _) | (_, Type::Bool(_)) | + (Type::Str(_), _) | (_, Type::Str(_)) | + (Type::Operator(_), _) | (_, Type::Operator(_)) => { + Err("Incompatible types".to_string()) + }, + + (Type::Number(Number::Int(i)), Type::Number(Number::Float(f))) | + (Type::Number(Number::Float(f)), Type::Number(Number::Int(i))) => { + Ok(Type::Number(Number::Float(func_f(*f, *i as f32)))) + }, + + (Type::Number(Number::Int(a)), Type::Number(Number::Int(b))) => { + Ok(Type::Number(Number::Int(func_i(*a, *b)))) + }, + + (Type::Number(Number::Float(a)), Type::Number(Number::Float(b))) => { + Ok(Type::Number(Number::Float(func_f(*a, *b)))) + }, + + (Type::Symbol(_), Type::Number(_)) | + (Type::Number(_), Type::Symbol(_)) | + (Type::Symbol(_), Type::Symbol(_)) => Err("Not yet implemented".to_string()) + } +} + impl Add for Type { type Output = Result<Type, String>; fn add(self, other: Type) -> Result<Type, String> { - match (self, other) { - (Bool(_), _) | (_, Bool(_)) | - (Str(_), _) | (_, Str(_)) | - (Operator(_), _) | (_, Operator(_)) => { - Err("Cannot add these types".to_string()) - }, - (Number(Number::Int(i)), Number(Number::Float(f))) | - (Number(Number::Float(f)), Number(Number::Int(i))) => { - Ok(Number(Number::Float(f + i as f32))) - }, - - (Number(Number::Int(a)), Number(Number::Int(b))) => { - Ok(Number(Number::Int(a + b))) - }, - - (Number(Number::Float(a)), Number(Number::Float(b))) => { - Ok(Number(Number::Float(a + b))) - }, - - (Symbol(_), Number(_)) | - (Number(_), Symbol(_)) | - (Symbol(_), Symbol(_)) => Err("Not yet implemented".to_string()) - } + apply_arithmetic(|a, b| a + b, |a, b| a + b, &self, &other) + } +} + +impl Sub for Type { + type Output = Result<Type, String>; + + fn sub(self, other: Type) -> Result<Type, String> { + apply_arithmetic(|a, b| a - b, |a, b| a - b, &self, &other) + } +} + +impl Mul for Type { + type Output = Result<Type, String>; + + fn mul(self, other: Type) -> Result<Type, String> { + apply_arithmetic(|a, b| a * b, |a, b| a * b, &self, &other) + } +} + +impl Div for Type { + type Output = Result<Type, String>; + + fn div(self, other: Type) -> Result<Type, String> { + apply_arithmetic(|a, b| a / b, |a, b| a / b, &self, &other) } } diff --git a/src/lib/eval/mod.rs b/src/lib/eval/mod.rs index b582397..adee0e5 100644 --- a/src/lib/eval/mod.rs +++ b/src/lib/eval/mod.rs @@ -1,54 +1,84 @@ -use std::collections::HashMap; use super::types::Op; use super::types::Op::*; -use super::types::SEXP; -use super::types::SEXP::*; -use super::types::Type::*; +use super::sexpr::SExpr; +use super::sexpr::SExpr::*; +use super::types::Type; use super::types::Number; +use super::environment::Environment; pub mod arith; -pub type Env = HashMap<String, SEXP>; - -pub fn eval(expr: &SEXP, env: &mut Env) -> Result<SEXP, String> { +pub fn eval(expr: &SExpr, env: &mut Environment) -> Result<SExpr, String> { match expr { Atom(ref x) => Ok(Atom((*x).clone())), Sexpr(ref s) => seval(s, env) } } -fn seval(sexpr: &[SEXP], env: &mut Env) -> Result<SEXP, String> { +fn seval(sexpr: &[SExpr], env: &mut Environment) -> Result<SExpr, String> { if sexpr.is_empty() { return Err("Empty S Expression".to_string()) } let op = match &sexpr[0] { - Atom(Operator(x)) => *x, - Atom(Symbol(_s)) => return Err("Not yet implemented".to_string()), + Atom(Type::Operator(x)) => *x, + Atom(Type::Symbol(_s)) => return Err("Not yet implemented".to_string()), x => return Err(format!("{:?} is not a procedure", x)) }; op_eval(op, &sexpr[1..], env) } -fn op_eval(op: Op, expr: &[SEXP], env: &mut Env) -> Result<SEXP, String> { +fn op_eval(op: Op, expr: &[SExpr], env: &mut Environment) -> Result<SExpr, String> { match op { Add => op_add(expr, env), + Sub => op_sub(expr, env), + Mul => op_mul(expr, env), + Div => op_div(expr, env), //Define => define(expr, env), _ => Err("Not yet implemented".to_string()) } } -fn op_add(expr: &[SEXP], env: &mut Env) -> Result<SEXP, String> { - let mut acc = Number(Number::Int(0)); +fn arith_op( + expr: &[SExpr], + env: &mut Environment, + identity: Number, + f: fn(Type, Type) -> Result<Type, String>) -> Result<SExpr, String>{ + + let mut acc = Type::Number(identity); let mut i = 0; + if expr.len() > 1 { + let b = match eval(&expr[i], env)? { + Atom(x) => x, + Sexpr(_) => panic!("This should have evaluated to an atom") + }; + acc = b; + i = 1; + } while i < expr.len() { let b = match eval(&expr[i], env)? { Atom(x) => x, Sexpr(_) => panic!("This should have evaluated to an atom") }; - acc = (acc + b)?; - i += 1; + acc = (f(acc, b))?; + i += 1 } - Ok(SEXP::Atom(acc)) + Ok(SExpr::Atom(acc)) +} + +fn op_add(expr: &[SExpr], env: &mut Environment) -> Result<SExpr, String> { + arith_op(expr, env, Number::Int(0), |a, b| a + b) +} + +fn op_sub(expr: &[SExpr], env: &mut Environment) -> Result<SExpr, String> { + arith_op(expr, env, Number::Int(0), |a, b| a - b) +} + +fn op_mul(expr: &[SExpr], env: &mut Environment) -> Result<SExpr, String> { + arith_op(expr, env, Number::Int(1), |a, b| a * b) +} + +fn op_div(expr: &[SExpr], env: &mut Environment) -> Result<SExpr, String> { + arith_op(expr, env, Number::Float(1.0), |a, b| a / b) } diff --git a/src/lib/mod.rs b/src/lib/mod.rs index a547a2d..bf4584c 100644 --- a/src/lib/mod.rs +++ b/src/lib/mod.rs @@ -1,3 +1,5 @@ -pub mod types; -pub mod parse; +pub mod environment; pub mod eval; +pub mod parse; +pub mod sexpr; +pub mod types; diff --git a/src/lib/parse.rs b/src/lib/parse.rs index 8e9c785..047b374 100644 --- a/src/lib/parse.rs +++ b/src/lib/parse.rs @@ -1,7 +1,7 @@ use super::types::Type; use super::types::Number; use super::types::Op; -use super::types::SEXP; +use super::sexpr::SExpr; pub type MaybeToken = (Option<Result<Token, String>>, usize); @@ -250,7 +250,7 @@ pub fn is_float(word: &str) -> MaybeToken { } } -pub fn parse(expr: &str) -> Result<SEXP, String> { +pub fn parse(expr: &str) -> Result<SExpr, String> { let mut tokenstream = TokenStream::default(expr); match tokenstream.peek() { Some(Ok(Token::LParen)) => { @@ -258,13 +258,13 @@ pub fn parse(expr: &str) -> Result<SEXP, String> { descend(&mut tokenstream) }, Some(Ok(Token::RParen)) => Err("Malformed expression".to_string()), - Some(Ok(Token::Value(x))) => Ok(SEXP::Atom(x)), + Some(Ok(Token::Value(x))) => Ok(SExpr::Atom(x)), Some(Err(f)) => Err(f), None => Err("Empty expression".to_string()) } } -pub fn descend(tokenstream: &mut TokenStream) -> Result<SEXP, String> { +pub fn descend(tokenstream: &mut TokenStream) -> Result<SExpr, String> { let mut sexp = Vec::new(); loop { let token = match tokenstream.next() { @@ -286,11 +286,11 @@ pub fn descend(tokenstream: &mut TokenStream) -> Result<SEXP, String> { break; }, Token::Value(atom) => { - sexp.push(SEXP::Atom(atom)); + sexp.push(SExpr::Atom(atom)); continue; } } } - Ok(SEXP::Sexpr(sexp)) + Ok(SExpr::Sexpr(sexp)) } diff --git a/src/lib/sexpr.rs b/src/lib/sexpr.rs index 0d37332..a6fbe49 100644 --- a/src/lib/sexpr.rs +++ b/src/lib/sexpr.rs @@ -1,7 +1,45 @@ use super::types::Type; -pub enum SEXP { +#[derive(PartialEq, Debug)] +pub enum SExpr { Atom(Type), - List(Vec<SEXP>) + Sexpr(Vec<SExpr>) } +#[test] +fn construct() { + let atom1 = SExpr::Atom(Type::Number(Number::Int(1))); + let atom2 = SExpr::Atom(Type::Number(Number::Int(2))); + let atom3 = SExpr::Atom(Type::Number(Number::Int(3))); + let atom4 = SExpr::Atom(Type::Number(Number::Int(4))); + let sexp = SExpr::Sexpr(vec!(atom1, atom2, atom3, atom4)); + match sexp { + SExpr::Sexpr(ref x) => { + assert_eq!(x[0], SExpr::Atom(Type::Number(Number::Int(1)))); + }, + _ => panic!("What") + } +} + +#[test] +fn mutability() { + let atom1 = SExpr::Atom(Type::Number(Number::Int(1))); + let atom2 = SExpr::Atom(Type::Number(Number::Int(2))); + let atom3 = SExpr::Atom(Type::Number(Number::Int(3))); + let atom4 = SExpr::Atom(Type::Number(Number::Int(4))); + let mut sexp = SExpr::Sexpr(vec!(atom1, atom2, atom3, atom4)); + match sexp { + SExpr::Sexpr(ref mut x) => match x[0] { + SExpr::Atom(Type::Number(Number::Int(ref mut x))) => *x += 7, + _ => panic!("What") + }, + _ => panic!("What") + } + + match sexp { + SExpr::Sexpr(ref x) => { + assert_eq!(x[0], SExpr::Atom(Type::Number(Number::Int(8)))); + }, + _ => panic!("What") + } +} diff --git a/src/lib/types.rs b/src/lib/types.rs index 4c2900a..7755c04 100644 --- a/src/lib/types.rs +++ b/src/lib/types.rs @@ -51,47 +51,4 @@ pub enum Type { Operator(Op), } -#[derive(PartialEq, Debug)] -pub enum SEXP { - Atom(Type), - Sexpr(Vec<SEXP>) -} - -#[test] -fn construct() { - let atom1 = SEXP::Atom(Type::Number(Number::Int(1))); - let atom2 = SEXP::Atom(Type::Number(Number::Int(2))); - let atom3 = SEXP::Atom(Type::Number(Number::Int(3))); - let atom4 = SEXP::Atom(Type::Number(Number::Int(4))); - let sexp = SEXP::Sexpr(vec!(atom1, atom2, atom3, atom4)); - match sexp { - SEXP::Sexpr(ref x) => { - assert_eq!(x[0], SEXP::Atom(Type::Number(Number::Int(1)))); - }, - _ => panic!("What") - } -} - -#[test] -fn mutability() { - let atom1 = SEXP::Atom(Type::Number(Number::Int(1))); - let atom2 = SEXP::Atom(Type::Number(Number::Int(2))); - let atom3 = SEXP::Atom(Type::Number(Number::Int(3))); - let atom4 = SEXP::Atom(Type::Number(Number::Int(4))); - let mut sexp = SEXP::Sexpr(vec!(atom1, atom2, atom3, atom4)); - match sexp { - SEXP::Sexpr(ref mut x) => match x[0] { - SEXP::Atom(Type::Number(Number::Int(ref mut x))) => *x += 7, - _ => panic!("What") - }, - _ => panic!("What") - } - - match sexp { - SEXP::Sexpr(ref x) => { - assert_eq!(x[0], SEXP::Atom(Type::Number(Number::Int(8)))); - }, - _ => panic!("What") - } -} diff --git a/src/main.rs b/src/main.rs index 15cf60b..49c916a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,8 @@ pub mod lib; use rustyline::error::ReadlineError; use rustyline::Editor; +use lib::environment::Environment; + fn read<T: rustyline::Helper>(rl: &mut Editor<T>) -> Option<String> { let readline = rl.readline(">> "); match readline { @@ -35,13 +37,12 @@ fn means_exit(input: &str) -> bool { } } -fn eval(env: &mut lib::eval::Env, input: &str) -> String { +fn eval(env: &mut Environment, input: &str) -> String { let sexp = match lib::parse::parse(input) { Ok(x) => x, Err(f) => return f }; - println!("{:?}", sexp); let res = lib::eval::eval(&sexp, env); match res { Ok(x) => format!("{:?}", x), @@ -50,7 +51,7 @@ fn eval(env: &mut lib::eval::Env, input: &str) -> String { } fn main() { - let mut env = lib::eval::Env::new(); + let mut env = Environment::new(); let hist_file = "history.txt"; // `()` can be used when no completer is required |