aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBryan Newbold <bnewbold@robocracy.org>2021-10-24 00:58:49 -0700
committerBryan Newbold <bnewbold@robocracy.org>2021-10-24 00:58:49 -0700
commit5f501b3654d6b36e850044ba492dd7bc7ee56438 (patch)
treea3122d81196c4ef7d01edb015f0fe466e846fd57
parent2ad36af41d237f3fb4be4d8ed0c78cf227612ea3 (diff)
downloadcasual-5f501b3654d6b36e850044ba492dd7bc7ee56438.tar.gz
casual-5f501b3654d6b36e850044ba492dd7bc7ee56438.zip
progress
-rw-r--r--src/cexpr.rs179
-rw-r--r--src/main.rs51
-rw-r--r--src/sexpr.rs21
3 files changed, 154 insertions, 97 deletions
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<CNumber>, Vec<CExpr>),
Product(Option<CNumber>, Vec<CExpr>),
- Power(Box<CExpr>, Box<CExpr>),
+ Exponent(Box<CExpr>, Box<CExpr>),
Factorial(Box<CExpr>),
UnaryFunction(String, Box<CExpr>),
-
- // 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<CExpr, String> {
// 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<SExpr>) -> Result<CExpr, String> {
- 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::<Result<Vec<SExpr>, 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::<Result<Vec<CExpr>, String>>()?),
+ ("*", 2..=5000) => CExpr::new_product(rest.iter().map(|v| CExpr::from_sexpr(v)).collect::<Result<Vec<CExpr>, 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<SExpr>) -> Result<CExpr, String> {
- unimplemented!()
+ pub fn new_sum(mut list: Vec<CExpr>) -> Result<CExpr, String> {
+ 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<SExpr>) -> Result<CExpr, String> {
- unimplemented!()
+ pub fn new_product(mut list: Vec<CExpr>) -> Result<CExpr, String> {
+ 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<SExpr, String> {
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::<Result<Vec<SExpr>, 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::<Result<Vec<SExpr>, 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::<Result<Vec<SExpr>, 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::<Result<Vec<SExpr>, 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<CExpr, String> {
+ 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<SExpr, String> {
+ 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),
+ }
+ }
+}
+