From ad9f7e6a689c7fe42444cbe22ecdf1de8d092d5b Mon Sep 17 00:00:00 2001 From: Dominick Allen Date: Fri, 26 Jun 2020 21:12:16 -0500 Subject: Evaluate basic arithmetic. --- src/lib/eval/arith.rs | 83 +++++++++++++++++++++++++++++++++++---------------- src/lib/eval/mod.rs | 62 ++++++++++++++++++++++++++++---------- src/lib/mod.rs | 6 ++-- src/lib/parse.rs | 12 ++++---- src/lib/sexpr.rs | 42 ++++++++++++++++++++++++-- src/lib/types.rs | 43 -------------------------- 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 { + + 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; fn add(self, other: Type) -> Result { - 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; + + fn sub(self, other: Type) -> Result { + apply_arithmetic(|a, b| a - b, |a, b| a - b, &self, &other) + } +} + +impl Mul for Type { + type Output = Result; + + fn mul(self, other: Type) -> Result { + apply_arithmetic(|a, b| a * b, |a, b| a * b, &self, &other) + } +} + +impl Div for Type { + type Output = Result; + + fn div(self, other: Type) -> Result { + 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; - -pub fn eval(expr: &SEXP, env: &mut Env) -> Result { +pub fn eval(expr: &SExpr, env: &mut Environment) -> Result { match expr { Atom(ref x) => Ok(Atom((*x).clone())), Sexpr(ref s) => seval(s, env) } } -fn seval(sexpr: &[SEXP], env: &mut Env) -> Result { +fn seval(sexpr: &[SExpr], env: &mut Environment) -> Result { 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 { +fn op_eval(op: Op, expr: &[SExpr], env: &mut Environment) -> Result { 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 { - let mut acc = Number(Number::Int(0)); +fn arith_op( + expr: &[SExpr], + env: &mut Environment, + identity: Number, + f: fn(Type, Type) -> Result) -> Result{ + + 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 { + arith_op(expr, env, Number::Int(0), |a, b| a + b) +} + +fn op_sub(expr: &[SExpr], env: &mut Environment) -> Result { + arith_op(expr, env, Number::Int(0), |a, b| a - b) +} + +fn op_mul(expr: &[SExpr], env: &mut Environment) -> Result { + arith_op(expr, env, Number::Int(1), |a, b| a * b) +} + +fn op_div(expr: &[SExpr], env: &mut Environment) -> Result { + 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>, usize); @@ -250,7 +250,7 @@ pub fn is_float(word: &str) -> MaybeToken { } } -pub fn parse(expr: &str) -> Result { +pub fn parse(expr: &str) -> Result { let mut tokenstream = TokenStream::default(expr); match tokenstream.peek() { Some(Ok(Token::LParen)) => { @@ -258,13 +258,13 @@ pub fn parse(expr: &str) -> Result { 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 { +pub fn descend(tokenstream: &mut TokenStream) -> Result { let mut sexp = Vec::new(); loop { let token = match tokenstream.next() { @@ -286,11 +286,11 @@ pub fn descend(tokenstream: &mut TokenStream) -> Result { 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) + Sexpr(Vec) } +#[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) -} - -#[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(rl: &mut Editor) -> Option { 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 -- cgit v1.2.3