From 5f501b3654d6b36e850044ba492dd7bc7ee56438 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Sun, 24 Oct 2021 00:58:49 -0700 Subject: progress --- src/cexpr.rs | 179 ++++++++++++++++++++++++++++++++++++++++++----------------- src/main.rs | 51 ++--------------- src/sexpr.rs | 21 ++++++- 3 files changed, 154 insertions(+), 97 deletions(-) (limited to 'src') diff --git a/src/cexpr.rs b/src/cexpr.rs index 5014f74..39c708e 100644 --- a/src/cexpr.rs +++ b/src/cexpr.rs @@ -5,20 +5,22 @@ * */ +use std::fmt; use crate::sexpr::SExpr; +/* pub enum NumericConstant { Pi, // 3.141592... E, // 2.718281... - Infinity, } +*/ -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, PartialOrd)] pub enum CNumber { + // order here is important for sorting, etc Integer(i64), - Rational(i64, u64), - // Float - // Constant + Rational(i64, i64), + Float(f64), } impl CNumber { @@ -28,29 +30,46 @@ impl CNumber { CNumber::Rational(a, b) => Ok(SExpr::SList(vec![ SExpr::SIdentifier("/".to_string()), SExpr::SInteger(*a), - SExpr::SInteger(*b as i64), + SExpr::SInteger(*b), ])), + CNumber::Float(v) => Ok(SExpr::SFloat(*v)), } } } -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, PartialOrd)] pub enum CExpr { - Symbol(String), + // order here is important for sorting, etc Number(CNumber), + Symbol(String), Sum(Option, Vec), Product(Option, Vec), - Power(Box, Box), + Exponent(Box, Box), Factorial(Box), UnaryFunction(String, Box), - - // TODO: Infinity? - // TODO: Limit? - // TODO: Vector? } impl CExpr { +/* + fn sort_type_val(&self) -> u8 { + use CExpr::*; + match self { + Number(Integer(_)) => 1, + Number(Rational(_)) => 2, + Number(Float(_)) => 3, + Symbol(_) => 3, + Sum(Some(_), _) => 4, + Sum(None, _) => 4, + Product(Some(_), _) => 4, + Product(None, _) => 4, + Exponent(_, _) => 4, + Factorial(_) => 4, + UnaryFunction(_, _) => 4, + } + } +*/ + pub fn from_sexpr(sexpr: &SExpr) -> Result { // not all cases are handled; some atoms are covered trivialy @@ -58,61 +77,106 @@ impl CExpr { SExpr::SNull => Err("null not handled".to_string()), SExpr::SBoolean(_) => Err("booleans not handled".to_string()), SExpr::SInteger(v) => Ok(CExpr::Number(CNumber::Integer(*v))), - SExpr::SFloat(v) => Err("floats not handled".to_string()), + SExpr::SFloat(v) => Ok(CExpr::Number(CNumber::Float(*v))), SExpr::SString(_) => Err("null not handled".to_string()), SExpr::SIdentifier(v) => Ok(CExpr::Symbol(v.to_string())), - SExpr::SList(_) => Err("null not handled".to_string()), - //SExpr::SList(l) => CExpr::from_sexpr_list(l), + SExpr::SList(l) => CExpr::from_sexpr_list(l), } } -/* pub fn from_sexpr_list(list: &Vec) -> Result { - if list.is_empty() { - unimplemented!() - } - match list[0] { - SExpr::SIdentifier("+") => { - Ok(CExpr::Sum( - // XXX - None, - list[1..].iter().map(|v| CExpr::from_sexpr(v)).collect::, String>>()?, - )) - }, - SExpr::SIdentifier("*") => { - }, - SExpr::SIdentifier("^") => { - }, - _ => { - unimplemented!() + // https://adventures.michaelfbryan.com/posts/daily/slice-patterns/ + if let [SExpr::SIdentifier(ident), rest @ ..] = list.as_slice() { + match (ident.as_str(), rest.len()) { + ("factorial", 1) => + Ok(CExpr::Factorial(Box::new(CExpr::from_sexpr(&rest[0])?))), + ("cos" | "sin" | "tan", 1) => + Ok(CExpr::UnaryFunction(ident.to_string(), Box::new(CExpr::from_sexpr(&rest[0])?))), + ("^", 2) => { + let base = CExpr::from_sexpr(&rest[0])?; + let power = CExpr::from_sexpr(&rest[1])?; + use CExpr::*; + use CNumber::*; + match (base, power) { + (Number(Integer(0)), _) => Ok(Number(Integer(0))), + (Number(Integer(1)), _) => Ok(Number(Integer(1))), + (_, Number(Integer(0))) => Ok(Number(Integer(1))), + (base, Number(Integer(1))) => Ok(base), + (base, power) => Ok(Exponent(Box::new(base), Box::new(power))), + } + }, + ("/", 2) => { + if let (SExpr::SInteger(numer), SExpr::SInteger(denom)) = (&rest[0], &rest[1]) { + match (numer, denom) { + (_, 0) => Err("division by zero".to_string()), + (0, _) => Ok(CExpr::Number(CNumber::Integer(0))), + (a, 1) => Ok(CExpr::Number(CNumber::Integer(*a))), + (a, b) if a == b => Ok(CExpr::Number(CNumber::Integer(1))), + (a, b) if a % b == 0 => Ok(CExpr::Number(CNumber::Integer(a/b))), + _ => Ok(CExpr::Number(CNumber::Rational(*numer, *denom))), + } + } else { + Err("only integers in fractions supported".to_string()) + } + }, + // TODO: how to make range unbounded? or less bounded? + ("+", 2..=5000) => CExpr::new_sum(rest.iter().map(|v| CExpr::from_sexpr(v)).collect::, String>>()?), + ("*", 2..=5000) => CExpr::new_product(rest.iter().map(|v| CExpr::from_sexpr(v)).collect::, String>>()?), + // TODO: subtraction, division + ("factorial" | "^" | "cos" | "sin" | "tan", count) => + Err(format!("wrong number of arguments to {}: {}", ident, count)), + _ => Err(format!("procedure not handled: {}", ident)), } + } else { + Err(format!("S-Expr pattern not handled: {:?}", list)) } } -*/ - pub fn new_sum(list: &Vec) -> Result { - unimplemented!() + pub fn new_sum(mut list: Vec) -> Result { + use CExpr::*; + use CNumber::*; + list.sort_by(|a, b| a.partial_cmp(b).unwrap()); + match list.as_slice() { + [Number(Integer(0)), e] => Ok(e.clone()), // XXX: remove clone() + [Number(Integer(a)), Number(Integer(b))] => Ok(Number(Integer(a+b))), + [Number(Integer(a)), Number(Rational(n, d))] => Ok(Number(Rational(n+a*d, *d))), + _ => Ok(Sum(None, list)) + } } - pub fn new_product(list: &Vec) -> Result { - unimplemented!() + pub fn new_product(mut list: Vec) -> Result { + use CExpr::*; + use CNumber::*; + list.sort_by(|a, b| a.partial_cmp(b).unwrap()); + match list.as_slice() { + [Number(Integer(1)), e] => Ok(e.clone()), // XXX: remove clone() + [Number(Integer(a)), Number(Integer(b))] => Ok(Number(Integer(a*b))), + [Number(Integer(a)), Number(Rational(n, d))] => Ok(Number(Rational(a*n, *d))), + _ => Ok(Product(None, list)) + } } pub fn to_sexpr(&self) -> Result { match self { CExpr::Symbol(s) => Ok(SExpr::SIdentifier(s.to_string())), CExpr::Number(n) => n.to_sexpr(), - CExpr::Sum(n, l) => Ok(SExpr::SList(vec![ - SExpr::SIdentifier("+".to_string()), - SExpr::SList(l.iter().map(|v| v.to_sexpr()).collect::, String>>()?), - // XXX: n - ])), - CExpr::Product(n, l) => Ok(SExpr::SList(vec![ - SExpr::SIdentifier("*".to_string()), - SExpr::SList(l.iter().map(|v| v.to_sexpr()).collect::, String>>()?), - // XXX: n - ])), - CExpr::Power(a, b) => Ok(SExpr::SList(vec![ + CExpr::Sum(n, l) => { + let mut list = l.iter().map(|v| v.to_sexpr()).collect::, String>>()?; + list.insert(0, SExpr::SIdentifier("+".to_string())); + if let Some(num) = n { + list.insert(1, num.to_sexpr()?); + } + Ok(SExpr::SList(list)) + }, + CExpr::Product(n, l) => { + let mut list = l.iter().map(|v| v.to_sexpr()).collect::, String>>()?; + list.insert(0, SExpr::SIdentifier("*".to_string())); + if let Some(num) = n { + list.insert(1, num.to_sexpr()?); + } + Ok(SExpr::SList(list)) + }, + CExpr::Exponent(a, b) => Ok(SExpr::SList(vec![ SExpr::SIdentifier("^".to_string()), a.to_sexpr()?, b.to_sexpr()?, @@ -127,4 +191,19 @@ impl CExpr { ])), } } + + pub fn from_str(raw: &str) -> Result { + let ast = SExpr::from_str(raw)?; + CExpr::from_sexpr(&ast) + } } + +impl fmt::Display for CExpr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.to_sexpr() { + Ok(ast) => ast.fmt(f), + Err(_) => Err(std::fmt::Error), + } + } +} + diff --git a/src/main.rs b/src/main.rs index 0f67aa4..9a025b9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,7 @@ mod cexpr; use cexpr::CExpr; -fn repl(verbose: bool) { +fn repl(_verbose: bool) { let stdin = io::stdin(); let mut stdout = io::stdout(); @@ -25,55 +25,14 @@ fn repl(verbose: bool) { stdout.write(b"\nCiao!\n").unwrap(); return; } - // TODO: use debug or something instead of "verbose"? - let tokens = match sexpr::sexpr_tokenize(&raw_input) { - Ok(tokens) => { - if verbose { println!("Tokens: {}", tokens.join(", ")); }; - tokens - }, - Err(e) => { - println!("couldn't tokenize: {}", e); - continue; - } - }; - let ast = match sexpr::sexpr_parse(&tokens, 0) { - Ok((mut ast_list, _)) => { - if verbose { - for ast in &ast_list { - println!("AST: {}", sexpr::sexpr_repr(ast).unwrap()); - }; - }; - // We're a REPL, so only one expression at a time - if ast_list.len() > 1 { - println!("one expression at a time on the REPL, please!"); - continue; - } else if ast_list.len() == 0 { - sexpr::SExpr::SNull - } else { - let ast = ast_list.pop().unwrap(); - ast - } - }, + let expr = match CExpr::from_str(&raw_input) { + Ok(expr) => expr, Err(e) => { - println!("couldn't parse: {}", e); + println!("error: {}", e); continue; } }; - let expr = match CExpr::from_sexpr(&ast) { - Ok(v) => v, - Err(e) => { - println!("couldn't parse as math: {}", e); - continue; - }, - }; - match expr.to_sexpr() { - Ok(out) => println!("{}", sexpr::sexpr_repr(&ast).unwrap()), - Err(e) => { - println!("couldn't return to sexpr: {}", e); - continue; - }, - - } + println!("{}", expr); } } diff --git a/src/sexpr.rs b/src/sexpr.rs index ea07bf9..d761192 100644 --- a/src/sexpr.rs +++ b/src/sexpr.rs @@ -10,13 +10,14 @@ */ use std::str; +use std::fmt; use std::io::Read; use std::fs::File; use std::path::Path; //////////// Types and Constants -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Debug)] pub enum SExpr { SNull, SBoolean(bool), @@ -238,3 +239,21 @@ pub fn sexpr_parse_file(fpath: &Path) -> Result<(), String> { let (_ast_list, _) = sexpr_parse(&tokens, 0)?; Ok(()) } + +impl SExpr { + pub fn from_str(raw: &str) -> Result { + let tokens = sexpr_tokenize(&raw)?; + let (mut ast, _) = sexpr_parse(&tokens, 0)?; + Ok(ast.remove(0)) + } +} + +impl fmt::Display for SExpr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match sexpr_repr(self) { + Ok(repr) => write!(f, "{}", repr), + Err(_) => Err(std::fmt::Error), + } + } +} + -- cgit v1.2.3