boxed infix operators, better formatting

This commit is contained in:
TriMill 2022-09-21 19:07:31 -04:00
parent 4b063086e4
commit c9905dc313
6 changed files with 195 additions and 93 deletions

View File

@ -36,6 +36,10 @@ impl From<RuntimeError> for Unwind {
} }
} }
//
// Statements
//
fn unwrap_ident_token(tok: &Token) -> &Rc<str> { fn unwrap_ident_token(tok: &Token) -> &Rc<str> {
if let Token { ty: TokenType::Ident(s),.. } = tok { if let Token { ty: TokenType::Ident(s),.. } = tok {
s s
@ -128,21 +132,33 @@ pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), Unwind> {
Ok(()) Ok(())
} }
//
// Expressions
//
pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result<Value, RuntimeError> { pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result<Value, RuntimeError> {
match expr { match expr {
Expr::Literal { value } => Ok(eval_literal(value)), Expr::Literal { value } => Ok(eval_literal(value)),
Expr::Ident { value } => eval_ident(value, env), Expr::Ident { value } => eval_ident(value, env),
Expr::Binary { lhs, rhs, op } => match op.ty.get_op_type() { 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) Some(OpType::Assignment)
=> eval_assignment(lhs, rhs, op, env), => 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) Some(OpType::LogicalAnd) | Some(OpType::LogicalOr)
=> eval_boolean(lhs, rhs, op, env), => eval_boolean(lhs, rhs, op, env),
Some(OpType::Comparison) Some(OpType::Pipeline) => {
=> eval_comp(lhs, rhs, op, env), let l = eval_expr(lhs, env.clone())?;
Some(OpType::Pipeline) let r = eval_expr(rhs, env)?;
=> eval_pipeline(lhs, rhs, op, 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 o => todo!("{:?}", o) // TODO other operations
}, },
Expr::Ternary { arg1, arg2, arg3, op } => eval_ternary(arg1, arg2, arg3, op, env), 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<Value, RuntimeError> {
end.as_ref().map(|x| x.as_ref()), end.as_ref().map(|x| x.as_ref()),
step.as_ref().map(|x| x.as_ref()), step.as_ref().map(|x| x.as_ref()),
*incl, env), *incl, env),
Expr::BoxedInfix { func } => Ok(Value::Func(func.clone())),
Expr::List { items } => { Expr::List { items } => {
let mut list = Vec::with_capacity(items.len()); let mut list = Vec::with_capacity(items.len());
for item in items { 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<Value, RuntimeError> { pub fn eval_standard_binary(l: Value, r: Value, opty: &TokenType, pos: &Position) -> Result<Value, RuntimeError> {
let l = eval_expr(lhs, env.clone())?; let mk_err = || format!("Cannot compare {:?} with {:?}", l, r);
let r = eval_expr(rhs, env)?; match opty {
match op.ty {
TokenType::Plus => &l + &r, TokenType::Plus => &l + &r,
TokenType::Minus => &l - &r, TokenType::Minus => &l - &r,
TokenType::Star => &l * &r, TokenType::Star => &l * &r,
@ -286,35 +302,6 @@ pub fn eval_arith(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result<Val
TokenType::Percent => &l % &r, TokenType::Percent => &l % &r,
TokenType::Caret => l.pow(&r), TokenType::Caret => l.pow(&r),
TokenType::DoubleSlash => l.fracdiv(&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<Value, RuntimeError> {
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<Value, RuntimeError> {
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::DoubleEqual => Ok(Value::Bool(l == r)),
TokenType::BangEqual => Ok(Value::Bool(l != r)), TokenType::BangEqual => Ok(Value::Bool(l != r)),
TokenType::Greater => l.partial_cmp(&r) TokenType::Greater => l.partial_cmp(&r)
@ -333,6 +320,23 @@ pub fn eval_comp(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result<Valu
.ok_or_else(mk_err) .ok_or_else(mk_err)
.map(|o| Value::from(o as i8)), .map(|o| Value::from(o as i8)),
_ => unreachable!() _ => unreachable!()
}.map_err(|e| RuntimeError::new(e, pos.clone()))
}
pub fn eval_boolean(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result<Value, RuntimeError> {
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<Value, RuntimeError> {
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 #[allow(clippy::needless_collect)] // collect is necesary to allow for rev() call
fn eval_pipeline_inner(l: Value, r: &Func, op: &Token) -> Result<Value, RuntimeError> { fn eval_pipeline(l: Value, r: &Func, op: &Token) -> Result<Value, RuntimeError> {
match op.ty { match op.ty {
TokenType::PipePoint => r.call(vec![l]), TokenType::PipePoint => r.call(vec![l]),
TokenType::PipeColon => Ok(Value::Func(mk_pipecolon_inner(r.clone(), l.iter()?))), TokenType::PipeColon => Ok(Value::Func(mk_pipecolon_inner(r.clone(), l.iter()?))),

View File

@ -1,6 +1,6 @@
use std::fmt; use std::fmt;
use crate::{token::{Token, OpType}, Position, value::Type}; use crate::{token::{Token, OpType}, Position, value::{Type, func::Func}};
#[derive(Clone)] #[derive(Clone)]
pub enum Stmt { pub enum Stmt {
@ -41,6 +41,7 @@ pub enum Expr {
Ternary { arg1: Box<Expr>, arg2: Box<Expr>, arg3: Box<Expr>, op: Token }, Ternary { arg1: Box<Expr>, arg2: Box<Expr>, arg3: Box<Expr>, op: Token },
Unary { arg: Box<Expr>, op: Token }, Unary { arg: Box<Expr>, op: Token },
Range { start: Box<Expr>, end: Option<Box<Expr>>, step: Option<Box<Expr>>, incl: bool }, Range { start: Box<Expr>, end: Option<Box<Expr>>, step: Option<Box<Expr>>, incl: bool },
BoxedInfix { func: Func },
Ident { value: Token }, Ident { value: Token },
Literal { value: Token }, Literal { value: Token },
List { items: Vec<Expr> }, List { items: Vec<Expr> },
@ -57,6 +58,7 @@ impl fmt::Debug for Expr {
Self::Ternary { arg1, arg2, arg3, op} => write!(f, "({:?} {:?} {:?} {:?})", op, arg1, arg2, arg3), Self::Ternary { arg1, arg2, arg3, op} => write!(f, "({:?} {:?} {:?} {:?})", op, arg1, arg2, arg3),
Self::Unary { arg, op } => write!(f, "({:?} {:?})", op, arg), Self::Unary { arg, op } => write!(f, "({:?} {:?})", op, arg),
Self::Range { start, end, step, incl } => write!(f, "(range {:?}..{:?} step {:?} incl {:?})", start, end, step, incl), 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::Ident { value } => write!(f, "(ident {:?})", value),
Self::Literal { value } => write!(f, "(lit {:?})", value), Self::Literal { value } => write!(f, "(lit {:?})", value),
Self::List { items } => write!(f, "(list {:?})", items), Self::List { items } => write!(f, "(list {:?})", items),

View File

@ -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 { pub struct Parser {
tokens: Vec<Token>, tokens: Vec<Token>,
@ -400,6 +402,7 @@ impl Parser {
} }
} }
// unary: !x, -x
fn unary(&mut self) -> Result<Expr, ParserError> { fn unary(&mut self) -> Result<Expr, ParserError> {
self.err_on_eof()?; self.err_on_eof()?;
if matches!(self.peek().ty, TokenType::Bang | TokenType::Minus) { 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<Expr, ParserError> { fn fncall(&mut self) -> Result<Expr, ParserError> {
let mut expr = self.expr_base()?; let mut expr = self.expr_base()?;
while !self.at_end() { while !self.at_end() {
@ -422,12 +426,14 @@ impl Parser {
Ok(expr) Ok(expr)
} }
// function call: a(b)
fn fncall_inner(&mut self, expr: Expr) -> Result<Expr, ParserError> { fn fncall_inner(&mut self, expr: Expr) -> Result<Expr, ParserError> {
let lparen = self.next(); let lparen = self.next();
let args = self.commalist(TokenType::RParen, Self::assignment)?; let args = self.commalist(TokenType::RParen, Self::assignment)?;
Ok(Expr::FuncCall { func: Box::new(expr), args, pos: lparen.pos }) Ok(Expr::FuncCall { func: Box::new(expr), args, pos: lparen.pos })
} }
// array index: a[b]
fn arrindex_inner(&mut self, expr: Expr) -> Result<Expr, ParserError> { fn arrindex_inner(&mut self, expr: Expr) -> Result<Expr, ParserError> {
let lbrack = self.next(); let lbrack = self.next();
let index = self.assignment()?; let index = self.assignment()?;
@ -438,6 +444,7 @@ impl Parser {
Ok(Expr::Index { lhs: Box::new(expr), index: Box::new(index), pos: lbrack.pos }) 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> { fn kv_pair(&mut self) -> Result<(Expr, Expr), ParserError> {
let key = self.assignment()?; let key = self.assignment()?;
self.err_on_eof()?; self.err_on_eof()?;
@ -449,6 +456,7 @@ impl Parser {
Ok((key, value)) Ok((key, value))
} }
// After infix operators, unary operators, function calls, and array indexes have been parsed
fn expr_base(&mut self) -> Result<Expr, ParserError> { fn expr_base(&mut self) -> Result<Expr, ParserError> {
self.err_on_eof()?; self.err_on_eof()?;
let next = self.next(); let next = self.next();
@ -457,10 +465,29 @@ impl Parser {
| TokenType::Int(_) | TokenType::Float(_) | TokenType::ImFloat(_) | TokenType::Int(_) | TokenType::Float(_) | TokenType::ImFloat(_)
| TokenType::String(_) | TokenType::Char(_) | TokenType::String(_) | TokenType::Char(_)
) { ) {
// A literal value
Ok(Expr::Literal { value: next }) Ok(Expr::Literal { value: next })
} else if let TokenType::Ident(..) = next.ty { } else if let TokenType::Ident(..) = next.ty {
// An identifier
Ok(Expr::Ident { value: next }) Ok(Expr::Ident { value: next })
} else if next.ty == TokenType::LParen { } 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()?; let expr = self.assignment()?;
if self.at_end() || TokenType::RParen != self.next().ty { if self.at_end() || TokenType::RParen != self.next().ty {
Err(self.mk_error("Left parenthesis never closed")) Err(self.mk_error("Left parenthesis never closed"))
@ -468,12 +495,15 @@ impl Parser {
Ok(expr) Ok(expr)
} }
} else if next.ty == TokenType::LBrack { } else if next.ty == TokenType::LBrack {
// list literal
let items = self.commalist(TokenType::RBrack, Self::assignment)?; let items = self.commalist(TokenType::RBrack, Self::assignment)?;
Ok(Expr::List { items }) Ok(Expr::List { items })
} else if next.ty == TokenType::LBrace { } else if next.ty == TokenType::LBrace {
// map literal
let items = self.commalist(TokenType::RBrace, Self::kv_pair)?; let items = self.commalist(TokenType::RBrace, Self::kv_pair)?;
Ok(Expr::Map { items }) Ok(Expr::Map { items })
} else if next.ty == TokenType::Fn { } else if next.ty == TokenType::Fn {
// anonymous (lambda) function definition
self.err_on_eof()?; self.err_on_eof()?;
if !self.expect(TokenType::LParen).0 { if !self.expect(TokenType::LParen).0 {
return Err(self.mk_error("Expected left parenthesis to start arguments list")) return Err(self.mk_error("Expected left parenthesis to start arguments list"))

View File

@ -61,11 +61,11 @@ fn fn_copy(args: Vec<Value>) -> Result<Value, RuntimeError> {
} }
fn fn_str(args: Vec<Value>) -> Result<Value, RuntimeError> { fn fn_str(args: Vec<Value>) -> Result<Value, RuntimeError> {
Ok(Value::String(args[0].to_string())) Ok(Value::from(args[0].to_string()))
} }
fn fn_repr(args: Vec<Value>) -> Result<Value, RuntimeError> { fn fn_repr(args: Vec<Value>) -> Result<Value, RuntimeError> {
Ok(Value::String(args[0].repr())) Ok(Value::from(args[0].repr()))
} }
fn fn_ord(args: Vec<Value>) -> Result<Value, RuntimeError> { fn fn_ord(args: Vec<Value>) -> Result<Value, RuntimeError> {

View File

@ -66,8 +66,6 @@ impl TokenType {
Self::DoubleAmper => Some(OpType::LogicalAnd), Self::DoubleAmper => Some(OpType::LogicalAnd),
Self::DoublePipe => Some(OpType::LogicalOr), Self::DoublePipe => Some(OpType::LogicalOr),
Self::DoubleDot => Some(OpType::Range),
_ => None _ => None
} }
} }
@ -78,11 +76,21 @@ impl TokenType {
_ => None _ => 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)] #[derive(Clone,Copy,Debug,PartialEq,Eq)]
pub enum OpType { pub enum OpType {
Assignment, Comparison, Pipeline, Additive, Multiplicative, Exponential, LogicalAnd, LogicalOr, Range Assignment, Comparison, Pipeline, Additive, Multiplicative, Exponential, LogicalAnd, LogicalOr
} }
impl OpType { impl OpType {

View File

@ -1,5 +1,6 @@
use std::{rc::Rc, collections::HashMap, ops::*, cmp::Ordering, cell::RefCell, hash::Hash, sync::atomic::{AtomicUsize, self}}; 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 num_traits::{Zero, ToPrimitive, Pow};
use strum::{EnumCount, EnumDiscriminants, EnumIter, IntoEnumIterator, AsRefStr}; use strum::{EnumCount, EnumDiscriminants, EnumIter, IntoEnumIterator, AsRefStr};
@ -42,6 +43,73 @@ pub fn generate_builtin_types() -> Vec<Type> {
types types
} }
fn fmt_list(list: &Vec<Value>) -> String {
let mut result: String = "[".into();
for v in list {
result += &(v.repr() + ", ");
}
result.pop();
result.pop();
result + "]"
}
fn fmt_map(map: &HashMap<Value, Value>) -> 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)] #[derive(Clone, Debug)]
pub struct CxprStruct { pub struct CxprStruct {
pub ty: Type, pub ty: Type,
@ -91,52 +159,50 @@ impl Value {
Err("Only zero-argument functions can be used as iterators".into()) 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> { pub fn as_func(&self) -> Result<&Func, String> {
match self { match self {
Value::Func(f) => Ok(f), 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<str> { pub fn to_string(&self) -> String {
match self { match self {
Self::Nil => Rc::from("nil"), Self::Nil => "nil".into(),
Self::Bool(b) => Rc::from(b.to_string()), Self::Bool(b) => b.to_string(),
Self::Int(n) => Rc::from(n.to_string()), Self::Int(n) => n.to_string(),
Self::Float(f) => Rc::from(f.to_string()), Self::Float(f) => f.to_string(),
Self::Rational(r) => Rc::from(r.to_string()), Self::Rational(r) => r.to_string(),
Self::Complex(z) => Rc::from(z.to_string()), Self::Complex(z) => z.to_string(),
Self::Char(c) => Rc::from(c.to_string()), Self::Char(c) => c.to_string(),
Self::String(s) => s.clone(), Self::String(s) => s.as_ref().to_owned(),
Self::List(l) => Rc::from(format!("{:?}", l)), // TODO fix Self::List(l) => fmt_list(&l.borrow()),
Self::Map(m) => Rc::from(format!("{:?}", m)), // TODO fix Self::Map(m) => fmt_map(&m.borrow()),
Self::Type(t) => Rc::from(format!("<type {}>", t.name)), Self::Type(t) => format!("<type {}>", t.name),
Self::Func(Func::Builtin { name, func, .. }) => Rc::from(format!("<builtin fn {} at {:?}>", name, *func as *const ())), Self::Func(Func::Builtin { name, func, .. }) => format!("<builtin fn {} at {:?}>", name, *func as *const ()),
Self::Func(Func::BuiltinClosure { .. }) => Rc::from(format!("<builtin anonymous fn>")), Self::Func(Func::BuiltinClosure { .. }) => format!("<builtin anonymous fn>"),
Self::Func(f @ Func::Partial { .. }) => match f.name() { Self::Func(f @ Func::Partial { .. }) => match f.name() {
Some(name) => Rc::from(format!("<partial of fn {}>", name)), Some(name) => format!("<partial of fn {}>", name),
None => Rc::from("<partial of anonymous fn>"), None => "<partial of anonymous fn>".into(),
} }
Self::Func(Func::Func { name, .. }) => match name { Self::Func(Func::Func { name, .. }) => match name {
Some(name) => Rc::from(format!("<fn {}>", name)), Some(name) => format!("<fn {}>", name),
None => Rc::from("<anonymous fn>"), None => "<anonymous fn>".into(),
}, },
Self::Data(_) => todo!(), Self::Data(_) => todo!(),
} }
} }
pub fn repr(&self) -> Rc<str> { pub fn repr(&self) -> String {
match self { match self {
Self::Float(f) => Rc::from(format!("{:?}",f)), Self::Float(f) => format!("{:?}",f),
Self::Rational(r) => Rc::from(r.numer().to_string() + "//" + &r.denom().to_string()), Self::Rational(r) => r.numer().to_string() + "//" + &r.denom().to_string(),
Self::Char(c) => Rc::from(format!("'{}'", c)), // TODO escaping Self::Char(c) => repr_char(*c),
Self::String(s) => Rc::from(format!("\"{}\"", s)), // TODO escaping Self::String(s) => repr_string(s),
Self::List(l) => Rc::from(format!("{:?}", l.borrow())), // TODO fix
Self::Map(m) => Rc::from(format!("{:?}", m)), // TODO fix
_ => self.to_string(), _ => self.to_string(),
} }
} }
@ -148,17 +214,17 @@ impl Value {
.ok_or_else(|| format!("String index {} out of bounds for length {}", i, s.chars().count())) .ok_or_else(|| format!("String index {} out of bounds for length {}", i, s.chars().count()))
.map(Value::Char), .map(Value::Char),
Value::Int(i) => Err(format!("String index {} cannot be negative", i)), 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 { Self::List(l) => match idx {
Value::Int(i) if *i >= 0 => l.borrow().get(*i as usize) 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())) .ok_or_else(|| format!("List index {} out of bounds for length {}", i, l.borrow().len()))
.map(|v| v.clone()), .map(|v| v.clone()),
Value::Int(i) => Err(format!("List index {} cannot be negative", i)), 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)), 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)) 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) 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)), 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) => { Self::Map(m) => {
m.borrow_mut().insert(idx.clone(), value); m.borrow_mut().insert(idx.clone(), value);
Ok(()) 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::String(s) => Ok(s.len()),
Value::List(l) => Ok(l.borrow().len()), Value::List(l) => Ok(l.borrow().len()),
Value::Map(m) => Ok(m.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)), (Rational(a), Int(b)) => Ok(Value::from(a/b)),
(Int(a), Rational(b)) => Ok(Value::from(b.recip()*a)), (Int(a), Rational(b)) => Ok(Value::from(b.recip()*a)),
(Rational(a), Rational(b)) => Ok(Value::from(a/b)), (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()), (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), 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()), (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::Float(a) => Ok(Value::Float(-a)),
Value::Rational(a) => Ok(Value::Rational(-a)), Value::Rational(a) => Ok(Value::Rational(-a)),
Value::Complex(a) => Ok(Value::Complex(-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()), (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), 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()), (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()))
} }
} }
} }