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> {
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<Value, RuntimeError> {
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<Value, RuntimeError> {
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<Value, RuntimeError> {
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<Value, RuntimeError> {
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<Val
TokenType::Percent => &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<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::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<Valu
.ok_or_else(mk_err)
.map(|o| Value::from(o as i8)),
_ => 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
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 {
TokenType::PipePoint => r.call(vec![l]),
TokenType::PipeColon => Ok(Value::Func(mk_pipecolon_inner(r.clone(), l.iter()?))),

View file

@ -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<Expr>, arg2: Box<Expr>, arg3: Box<Expr>, op: Token },
Unary { arg: Box<Expr>, op: Token },
Range { start: Box<Expr>, end: Option<Box<Expr>>, step: Option<Box<Expr>>, incl: bool },
BoxedInfix { func: Func },
Ident { value: Token },
Literal { value: Token },
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::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),

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 {
tokens: Vec<Token>,
@ -400,6 +402,7 @@ impl Parser {
}
}
// unary: !x, -x
fn unary(&mut self) -> Result<Expr, ParserError> {
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<Expr, ParserError> {
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<Expr, ParserError> {
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<Expr, ParserError> {
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<Expr, ParserError> {
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"))

View file

@ -61,11 +61,11 @@ fn fn_copy(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> {
Ok(Value::String(args[0].repr()))
Ok(Value::from(args[0].repr()))
}
fn fn_ord(args: Vec<Value>) -> Result<Value, RuntimeError> {

View file

@ -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 {

View file

@ -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<Type> {
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)]
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<str> {
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!("<type {}>", t.name)),
Self::Func(Func::Builtin { name, func, .. }) => Rc::from(format!("<builtin fn {} at {:?}>", name, *func as *const ())),
Self::Func(Func::BuiltinClosure { .. }) => Rc::from(format!("<builtin anonymous fn>")),
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!("<type {}>", t.name),
Self::Func(Func::Builtin { name, func, .. }) => format!("<builtin fn {} at {:?}>", name, *func as *const ()),
Self::Func(Func::BuiltinClosure { .. }) => format!("<builtin anonymous fn>"),
Self::Func(f @ Func::Partial { .. }) => match f.name() {
Some(name) => Rc::from(format!("<partial of fn {}>", name)),
None => Rc::from("<partial of anonymous fn>"),
Some(name) => format!("<partial of fn {}>", name),
None => "<partial of anonymous fn>".into(),
}
Self::Func(Func::Func { name, .. }) => match name {
Some(name) => Rc::from(format!("<fn {}>", name)),
None => Rc::from("<anonymous fn>"),
Some(name) => format!("<fn {}>", name),
None => "<anonymous fn>".into(),
},
Self::Data(_) => todo!(),
}
}
pub fn repr(&self) -> Rc<str> {
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()))
}
}
}