boxed infix operators, better formatting
This commit is contained in:
parent
4b063086e4
commit
c9905dc313
6 changed files with 195 additions and 93 deletions
|
@ -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()?))),
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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"))
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue