diff --git a/complexpr/src/eval.rs b/complexpr/src/eval.rs index d4ecaaf..75d9ba4 100644 --- a/complexpr/src/eval.rs +++ b/complexpr/src/eval.rs @@ -36,6 +36,10 @@ impl From for Unwind { } } +// +// Statements +// + fn unwrap_ident_token(tok: &Token) -> &Rc { if let Token { ty: TokenType::Ident(s),.. } = tok { s @@ -128,21 +132,33 @@ pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), Unwind> { Ok(()) } +// +// Expressions +// + pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result { match expr { Expr::Literal { value } => Ok(eval_literal(value)), Expr::Ident { value } => eval_ident(value, env), Expr::Binary { lhs, rhs, op } => match op.ty.get_op_type() { + Some(OpType::Additive) + | Some(OpType::Multiplicative) + | Some(OpType::Exponential) + | Some(OpType::Comparison) => { + let l = eval_expr(lhs, env.clone())?; + let r = eval_expr(rhs, env)?; + eval_standard_binary(l, r, &op.ty, &op.pos) + }, Some(OpType::Assignment) => eval_assignment(lhs, rhs, op, env), - Some(OpType::Additive) | Some(OpType::Multiplicative) | Some(OpType::Exponential) - => eval_arith(lhs, rhs, op, env), Some(OpType::LogicalAnd) | Some(OpType::LogicalOr) => eval_boolean(lhs, rhs, op, env), - Some(OpType::Comparison) - => eval_comp(lhs, rhs, op, env), - Some(OpType::Pipeline) - => eval_pipeline(lhs, rhs, op, env), + Some(OpType::Pipeline) => { + let l = eval_expr(lhs, env.clone())?; + let r = eval_expr(rhs, env)?; + let f = r.as_func().map_err(|e| RuntimeError::new(e, op.pos.clone()))?; + eval_pipeline(l, f, op).map_err(exit_pipe(&op.pos)) + } o => todo!("{:?}", o) // TODO other operations }, Expr::Ternary { arg1, arg2, arg3, op } => eval_ternary(arg1, arg2, arg3, op, env), @@ -152,6 +168,7 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result { end.as_ref().map(|x| x.as_ref()), step.as_ref().map(|x| x.as_ref()), *incl, env), + Expr::BoxedInfix { func } => Ok(Value::Func(func.clone())), Expr::List { items } => { let mut list = Vec::with_capacity(items.len()); for item in items { @@ -275,10 +292,9 @@ pub fn eval_assignment(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Resul } } -pub fn eval_arith(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result { - let l = eval_expr(lhs, env.clone())?; - let r = eval_expr(rhs, env)?; - match op.ty { +pub fn eval_standard_binary(l: Value, r: Value, opty: &TokenType, pos: &Position) -> Result { + let mk_err = || format!("Cannot compare {:?} with {:?}", l, r); + match opty { TokenType::Plus => &l + &r, TokenType::Minus => &l - &r, TokenType::Star => &l * &r, @@ -286,35 +302,6 @@ pub fn eval_arith(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result &l % &r, TokenType::Caret => l.pow(&r), TokenType::DoubleSlash => l.fracdiv(&r), - _ => todo!() - }.map_err(|e| RuntimeError::new(e, op.pos.clone())) -} - -pub fn eval_boolean(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result { - let l = eval_expr(lhs, env.clone())?; - match op.ty { - TokenType::DoubleAmper => if l.truthy() { - eval_expr(rhs, env) - } else { - Ok(l) - }, - TokenType::DoublePipe => if !l.truthy() { - eval_expr(rhs, env) - } else { - Ok(l) - }, - _ => unreachable!() - } -} - -pub fn eval_comp(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result { - let l = eval_expr(lhs, env.clone())?; - let r = eval_expr(rhs, env)?; - let mk_err = || RuntimeError::new( - format!("Cannot compare {:?} with {:?}", l, r), - op.pos.clone() - ); - match op.ty { TokenType::DoubleEqual => Ok(Value::Bool(l == r)), TokenType::BangEqual => Ok(Value::Bool(l != r)), TokenType::Greater => l.partial_cmp(&r) @@ -333,6 +320,23 @@ pub fn eval_comp(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result unreachable!() + }.map_err(|e| RuntimeError::new(e, pos.clone())) +} + +pub fn eval_boolean(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result { + let l = eval_expr(lhs, env.clone())?; + match op.ty { + TokenType::DoubleAmper => if l.truthy() { + eval_expr(rhs, env) + } else { + Ok(l) + }, + TokenType::DoublePipe => if !l.truthy() { + eval_expr(rhs, env) + } else { + Ok(l) + }, + _ => unreachable!() } } @@ -371,16 +375,8 @@ fn mk_pipequestion_inner(f: Func, it: CIterator) -> Func { } } - -pub fn eval_pipeline(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result { - let l = eval_expr(lhs, env.clone())?; - let r = eval_expr(rhs, env)?; - let f = r.as_func().map_err(|e| RuntimeError::new(e, op.pos.clone()))?; - eval_pipeline_inner(l, f, op).map_err(exit_pipe(&op.pos)) -} - #[allow(clippy::needless_collect)] // collect is necesary to allow for rev() call -fn eval_pipeline_inner(l: Value, r: &Func, op: &Token) -> Result { +fn eval_pipeline(l: Value, r: &Func, op: &Token) -> Result { match op.ty { TokenType::PipePoint => r.call(vec![l]), TokenType::PipeColon => Ok(Value::Func(mk_pipecolon_inner(r.clone(), l.iter()?))), diff --git a/complexpr/src/expr.rs b/complexpr/src/expr.rs index ad30676..3146a85 100644 --- a/complexpr/src/expr.rs +++ b/complexpr/src/expr.rs @@ -1,6 +1,6 @@ use std::fmt; -use crate::{token::{Token, OpType}, Position, value::Type}; +use crate::{token::{Token, OpType}, Position, value::{Type, func::Func}}; #[derive(Clone)] pub enum Stmt { @@ -41,6 +41,7 @@ pub enum Expr { Ternary { arg1: Box, arg2: Box, arg3: Box, op: Token }, Unary { arg: Box, op: Token }, Range { start: Box, end: Option>, step: Option>, incl: bool }, + BoxedInfix { func: Func }, Ident { value: Token }, Literal { value: Token }, List { items: Vec }, @@ -57,6 +58,7 @@ impl fmt::Debug for Expr { Self::Ternary { arg1, arg2, arg3, op} => write!(f, "({:?} {:?} {:?} {:?})", op, arg1, arg2, arg3), Self::Unary { arg, op } => write!(f, "({:?} {:?})", op, arg), Self::Range { start, end, step, incl } => write!(f, "(range {:?}..{:?} step {:?} incl {:?})", start, end, step, incl), + Self::BoxedInfix { func } => write!(f, "(boxed-infix {:?})", func), Self::Ident { value } => write!(f, "(ident {:?})", value), Self::Literal { value } => write!(f, "(lit {:?})", value), Self::List { items } => write!(f, "(list {:?})", items), diff --git a/complexpr/src/parser.rs b/complexpr/src/parser.rs index 27dff76..3ac226a 100644 --- a/complexpr/src/parser.rs +++ b/complexpr/src/parser.rs @@ -1,4 +1,6 @@ -use crate::{token::{Token, TokenType, OpType}, ParserError, expr::{Stmt, Expr}, value}; +use std::rc::Rc; + +use crate::{token::{Token, TokenType, OpType}, ParserError, expr::{Stmt, Expr}, value::{self, func::Func}, eval::eval_standard_binary}; pub struct Parser { tokens: Vec, @@ -400,6 +402,7 @@ impl Parser { } } + // unary: !x, -x fn unary(&mut self) -> Result { self.err_on_eof()?; if matches!(self.peek().ty, TokenType::Bang | TokenType::Minus) { @@ -410,6 +413,7 @@ impl Parser { } } + // function calls and array access fn fncall(&mut self) -> Result { let mut expr = self.expr_base()?; while !self.at_end() { @@ -422,12 +426,14 @@ impl Parser { Ok(expr) } + // function call: a(b) fn fncall_inner(&mut self, expr: Expr) -> Result { let lparen = self.next(); let args = self.commalist(TokenType::RParen, Self::assignment)?; Ok(Expr::FuncCall { func: Box::new(expr), args, pos: lparen.pos }) } + // array index: a[b] fn arrindex_inner(&mut self, expr: Expr) -> Result { let lbrack = self.next(); let index = self.assignment()?; @@ -438,6 +444,7 @@ impl Parser { Ok(Expr::Index { lhs: Box::new(expr), index: Box::new(index), pos: lbrack.pos }) } + // key-value pairs for maps fn kv_pair(&mut self) -> Result<(Expr, Expr), ParserError> { let key = self.assignment()?; self.err_on_eof()?; @@ -449,6 +456,7 @@ impl Parser { Ok((key, value)) } + // After infix operators, unary operators, function calls, and array indexes have been parsed fn expr_base(&mut self) -> Result { self.err_on_eof()?; let next = self.next(); @@ -457,10 +465,29 @@ impl Parser { | TokenType::Int(_) | TokenType::Float(_) | TokenType::ImFloat(_) | TokenType::String(_) | TokenType::Char(_) ) { + // A literal value Ok(Expr::Literal { value: next }) } else if let TokenType::Ident(..) = next.ty { + // An identifier Ok(Expr::Ident { value: next }) } else if next.ty == TokenType::LParen { + // Special case for "boxed infix operators" + if !self.at_end() && self.peek().ty.is_infix_op() { + let op = self.next(); + self.err_on_eof()?; + if self.peek().ty != TokenType::RParen { + return Err(self.mk_error("Expected right parenthesis after enclosed operator")) + } + self.next(); + let func = Func::BuiltinClosure { + arg_count: 2, + func: Rc::new(move |args| { + eval_standard_binary(args[0].clone(), args[1].clone(), &op.ty, &op.pos) + }) + }; + return Ok(Expr::BoxedInfix { func }) + } + // general case: parentheses as grouping symbols let expr = self.assignment()?; if self.at_end() || TokenType::RParen != self.next().ty { Err(self.mk_error("Left parenthesis never closed")) @@ -468,12 +495,15 @@ impl Parser { Ok(expr) } } else if next.ty == TokenType::LBrack { + // list literal let items = self.commalist(TokenType::RBrack, Self::assignment)?; Ok(Expr::List { items }) } else if next.ty == TokenType::LBrace { + // map literal let items = self.commalist(TokenType::RBrace, Self::kv_pair)?; Ok(Expr::Map { items }) } else if next.ty == TokenType::Fn { + // anonymous (lambda) function definition self.err_on_eof()?; if !self.expect(TokenType::LParen).0 { return Err(self.mk_error("Expected left parenthesis to start arguments list")) diff --git a/complexpr/src/stdlib/mod.rs b/complexpr/src/stdlib/mod.rs index 2948140..a779b9f 100644 --- a/complexpr/src/stdlib/mod.rs +++ b/complexpr/src/stdlib/mod.rs @@ -61,11 +61,11 @@ fn fn_copy(args: Vec) -> Result { } fn fn_str(args: Vec) -> Result { - Ok(Value::String(args[0].to_string())) + Ok(Value::from(args[0].to_string())) } fn fn_repr(args: Vec) -> Result { - Ok(Value::String(args[0].repr())) + Ok(Value::from(args[0].repr())) } fn fn_ord(args: Vec) -> Result { diff --git a/complexpr/src/token.rs b/complexpr/src/token.rs index cbf305d..6907ed6 100644 --- a/complexpr/src/token.rs +++ b/complexpr/src/token.rs @@ -66,8 +66,6 @@ impl TokenType { Self::DoubleAmper => Some(OpType::LogicalAnd), Self::DoublePipe => Some(OpType::LogicalOr), - Self::DoubleDot => Some(OpType::Range), - _ => None } } @@ -78,11 +76,21 @@ impl TokenType { _ => None } } + + pub fn is_infix_op(&self) -> bool { + matches!(self.get_op_type(), Some( + OpType::Additive + | OpType::Multiplicative + | OpType::Exponential + | OpType::Comparison + // TODO | OpType::Pipeline + )) + } } #[derive(Clone,Copy,Debug,PartialEq,Eq)] pub enum OpType { - Assignment, Comparison, Pipeline, Additive, Multiplicative, Exponential, LogicalAnd, LogicalOr, Range + Assignment, Comparison, Pipeline, Additive, Multiplicative, Exponential, LogicalAnd, LogicalOr } impl OpType { diff --git a/complexpr/src/value/mod.rs b/complexpr/src/value/mod.rs index 784d417..7a27dee 100644 --- a/complexpr/src/value/mod.rs +++ b/complexpr/src/value/mod.rs @@ -1,5 +1,6 @@ use std::{rc::Rc, collections::HashMap, ops::*, cmp::Ordering, cell::RefCell, hash::Hash, sync::atomic::{AtomicUsize, self}}; +use either::Either; use num_traits::{Zero, ToPrimitive, Pow}; use strum::{EnumCount, EnumDiscriminants, EnumIter, IntoEnumIterator, AsRefStr}; @@ -42,6 +43,73 @@ pub fn generate_builtin_types() -> Vec { types } +fn fmt_list(list: &Vec) -> String { + let mut result: String = "[".into(); + for v in list { + result += &(v.repr() + ", "); + } + result.pop(); + result.pop(); + result + "]" +} + +fn fmt_map(map: &HashMap) -> String { + let mut result: String = "{".into(); + for (k, v) in map { + result += &(k.repr() + ": " + &v.repr() + ", "); + } + result.pop(); + result.pop(); + result + "}" +} + +enum EscapeResult { Char(char), Str(&'static str), String(String) } + +fn escape_char(c: char, is_char: bool) -> EscapeResult { + match c { + '\0' => EscapeResult::Str("\\0"), + '\n' => EscapeResult::Str("\\n"), + '\r' => EscapeResult::Str("\\r"), + '\t' => EscapeResult::Str("\\t"), + '\x1b' => EscapeResult::Str("\\e"), + '\\' if is_char => EscapeResult::Str("\\\\"), + '\'' if is_char => EscapeResult::Str("\\'"), + '"' if !is_char => EscapeResult::Str("\\\""), + _ if c.is_control() => { + if c <= '\u{ff}' { + EscapeResult::String(format!("\\x{:02x}", c as u32)) + } else { + EscapeResult::String(format!("\\u{{{:06x}}}", c as u32)) + } + } + _ => EscapeResult::Char(c) + } +} + +fn repr_string(s: &str) -> String { + let mut result: String = "\"".into(); + for c in s.chars() { + match escape_char(c, false) { + EscapeResult::Char(c) => result.push(c), + EscapeResult::Str(s) => result += s, + EscapeResult::String(s) => result += &s, + } + } + result.push('"'); + result +} + +fn repr_char(c: char) -> String { + let mut result: String = "'".into(); + match escape_char(c, true) { + EscapeResult::Char(c) => result.push(c), + EscapeResult::Str(s) => result += s, + EscapeResult::String(s) => result += &s + } + result.push('\''); + result +} + #[derive(Clone, Debug)] pub struct CxprStruct { pub ty: Type, @@ -91,52 +159,50 @@ impl Value { Err("Only zero-argument functions can be used as iterators".into()) } }, - v => Err(format!("{:?} is not iterable", v)) + v => Err(format!("{} is not iterable", v.repr())) } } pub fn as_func(&self) -> Result<&Func, String> { match self { Value::Func(f) => Ok(f), - v => Err(format!("{:?} is not a function", v)) + v => Err(format!("{} is not a function", v.repr())) } } - pub fn to_string(&self) -> Rc { + pub fn to_string(&self) -> String { match self { - Self::Nil => Rc::from("nil"), - Self::Bool(b) => Rc::from(b.to_string()), - Self::Int(n) => Rc::from(n.to_string()), - Self::Float(f) => Rc::from(f.to_string()), - Self::Rational(r) => Rc::from(r.to_string()), - Self::Complex(z) => Rc::from(z.to_string()), - Self::Char(c) => Rc::from(c.to_string()), - Self::String(s) => s.clone(), - Self::List(l) => Rc::from(format!("{:?}", l)), // TODO fix - Self::Map(m) => Rc::from(format!("{:?}", m)), // TODO fix - Self::Type(t) => Rc::from(format!("", t.name)), - Self::Func(Func::Builtin { name, func, .. }) => Rc::from(format!("", name, *func as *const ())), - Self::Func(Func::BuiltinClosure { .. }) => Rc::from(format!("")), + Self::Nil => "nil".into(), + Self::Bool(b) => b.to_string(), + Self::Int(n) => n.to_string(), + Self::Float(f) => f.to_string(), + Self::Rational(r) => r.to_string(), + Self::Complex(z) => z.to_string(), + Self::Char(c) => c.to_string(), + Self::String(s) => s.as_ref().to_owned(), + Self::List(l) => fmt_list(&l.borrow()), + Self::Map(m) => fmt_map(&m.borrow()), + Self::Type(t) => format!("", t.name), + Self::Func(Func::Builtin { name, func, .. }) => format!("", name, *func as *const ()), + Self::Func(Func::BuiltinClosure { .. }) => format!(""), Self::Func(f @ Func::Partial { .. }) => match f.name() { - Some(name) => Rc::from(format!("", name)), - None => Rc::from(""), + Some(name) => format!("", name), + None => "".into(), } Self::Func(Func::Func { name, .. }) => match name { - Some(name) => Rc::from(format!("", name)), - None => Rc::from(""), + Some(name) => format!("", name), + None => "".into(), }, Self::Data(_) => todo!(), } } - pub fn repr(&self) -> Rc { + pub fn repr(&self) -> String { match self { - Self::Float(f) => Rc::from(format!("{:?}",f)), - Self::Rational(r) => Rc::from(r.numer().to_string() + "//" + &r.denom().to_string()), - Self::Char(c) => Rc::from(format!("'{}'", c)), // TODO escaping - Self::String(s) => Rc::from(format!("\"{}\"", s)), // TODO escaping - Self::List(l) => Rc::from(format!("{:?}", l.borrow())), // TODO fix - Self::Map(m) => Rc::from(format!("{:?}", m)), // TODO fix + Self::Float(f) => format!("{:?}",f), + Self::Rational(r) => r.numer().to_string() + "//" + &r.denom().to_string(), + Self::Char(c) => repr_char(*c), + Self::String(s) => repr_string(s), _ => self.to_string(), } } @@ -148,17 +214,17 @@ impl Value { .ok_or_else(|| format!("String index {} out of bounds for length {}", i, s.chars().count())) .map(Value::Char), Value::Int(i) => Err(format!("String index {} cannot be negative", i)), - _ => Err(format!("Cannot index {:?} with {:?}", self, idx)) + _ => Err(format!("Cannot index {} with {}", self.repr(), idx.repr())) }, Self::List(l) => match idx { Value::Int(i) if *i >= 0 => l.borrow().get(*i as usize) .ok_or_else(|| format!("List index {} out of bounds for length {}", i, l.borrow().len())) .map(|v| v.clone()), Value::Int(i) => Err(format!("List index {} cannot be negative", i)), - _ => Err(format!("Cannot index {:?} with {:?}", self, idx)) + _ => Err(format!("Cannot index {} with {}", self.repr(), idx.repr())) } - Self::Map(m) => m.borrow().get(idx).cloned().ok_or_else(|| format!("Map does not contain key {:?}", idx)), - v => Err(format!("Cannot index into {:?}", v)) + Self::Map(m) => m.borrow().get(idx).cloned().ok_or_else(|| format!("Map does not contain key {}", idx.repr())), + v => Err(format!("Cannot index into {}", v.repr())) } } @@ -171,13 +237,13 @@ impl Value { } Value::Int(i) if *i >= 0 => Err(format!("List index {} out of bounds for length {}", i, l.borrow().len())), Value::Int(i) => Err(format!("List index {} cannot be negative", i)), - _ => Err(format!("Cannot index {:?} with {:?}", self, idx)) + _ => Err(format!("Cannot index {} with {}", self.repr(), idx.repr())) } Self::Map(m) => { m.borrow_mut().insert(idx.clone(), value); Ok(()) } - v => Err(format!("Cannot assign to index in {:?}", v)) + v => Err(format!("Cannot assign to index in {}", v.repr())) } } @@ -186,7 +252,7 @@ impl Value { Value::String(s) => Ok(s.len()), Value::List(l) => Ok(l.borrow().len()), Value::Map(m) => Ok(m.borrow().len()), - v => Err(format!("{:?} has no length", v)) + v => Err(format!("{} has no length", v.repr())) } } @@ -205,7 +271,7 @@ impl Value { (Rational(a), Int(b)) => Ok(Value::from(a/b)), (Int(a), Rational(b)) => Ok(Value::from(b.recip()*a)), (Rational(a), Rational(b)) => Ok(Value::from(a/b)), - (x,y) => Err(format!("Unsupported operation 'fracdiv' between {:?} and {:?}", x, y)) + (x,y) => Err(format!("Unsupported operation 'fracdiv' between {} and {}", x.repr(), y.repr())) } } @@ -400,7 +466,7 @@ macro_rules! impl_numeric_op { (Rational(a), Complex(b)) => Ok(self::Complex::from(a.to_f64().ok_or(RATIO_CAST_FAIL)?).$fnname(b).into()), (Complex(a), Rational(b)) => Ok(a.$fnname(self::Complex::from(b.to_f64().ok_or(RATIO_CAST_FAIL)?)).into()), (Complex(a), Complex(b)) => Ok(a.$fnname(b).into()), - (lhs, rhs) => Err(format!("Unsupported operation '{}' between {:?} and {:?}", stringify!($fnname), lhs, rhs)) + (lhs, rhs) => Err(format!("Unsupported operation '{}' between {} and {}", stringify!($fnname), lhs.repr(), rhs.repr())) } } } @@ -415,7 +481,7 @@ impl Neg for Value { Value::Float(a) => Ok(Value::Float(-a)), Value::Rational(a) => Ok(Value::Rational(-a)), Value::Complex(a) => Ok(Value::Complex(-a)), - _ => Err(format!("Unsupported operation 'neg' on {:?}", self)) + _ => Err(format!("Unsupported operation 'neg' on {}", self.repr())) } } } @@ -490,7 +556,7 @@ impl Pow<&Value> for &Value { (Rational(a), Complex(b)) => Ok(self::Complex::from(a.to_f64().ok_or(RATIO_CAST_FAIL)?).pow(b).into()), (Complex(a), Rational(b)) => Ok(a.pow(self::Complex::from(b.to_f64().ok_or(RATIO_CAST_FAIL)?)).into()), (Complex(a), Complex(b)) => Ok(a.pow(b).into()), - (lhs, rhs) => Err(format!("Unsupported operation 'pow' between {:?} and {:?}", lhs, rhs)) + (lhs, rhs) => Err(format!("Unsupported operation 'pow' between {} and {}", lhs.repr(), rhs.repr())) } } }