diff --git a/README.md b/README.md index 743db5b..e4e53d4 100644 --- a/README.md +++ b/README.md @@ -229,6 +229,10 @@ let add7 = add(7); println(add7(3)); # prints 10 ``` +### Boxed infix operators + +Some infix operators can be converted to functions by prefixing them with a backslash `\`. For example, `\+` is a function that computes the sum of its two arguments (the same as `fn(x, y) (x + y)`). This can be done for arithmetic, comparison, and bitwise operators only. + ## Iterators An iterator is simply a function with the following properties: diff --git a/complexpr/src/eval.rs b/complexpr/src/eval.rs index 6ca7251..6fa2c8e 100644 --- a/complexpr/src/eval.rs +++ b/complexpr/src/eval.rs @@ -144,7 +144,9 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result { Some(OpType::Additive) | Some(OpType::Multiplicative) | Some(OpType::Exponential) - | Some(OpType::Comparison) => { + | Some(OpType::Comparison) + | Some(OpType::BitwiseAnd) + | Some(OpType::BitwiseOr) => { let l = eval_expr(lhs, env.clone())?; let r = eval_expr(rhs, env)?; eval_standard_binary(l, r, &op.ty, &op.pos) @@ -361,6 +363,8 @@ pub fn eval_standard_binary(l: Value, r: Value, opty: &TokenType, pos: &Position TokenType::Star => &l * &r, TokenType::Slash => &l / &r, TokenType::Percent => &l % &r, + TokenType::Amper => &l & &r, + TokenType::Pipe => &l | &r, TokenType::Caret => l.pow(&r), TokenType::DoubleSlash => l.fracdiv(&r), TokenType::DoubleEqual => Ok(Value::Bool(l == r)), @@ -511,8 +515,9 @@ pub fn eval_ternary(arg1: &Expr, arg2: &Expr, arg3: &Expr, op: &Token, env: EnvR pub fn eval_unary(arg: &Expr, op: &Token, env: EnvRef) -> Result { let a = eval_expr(arg, env)?; match op.ty { - TokenType::Minus => -a, + TokenType::Minus => -&a, TokenType::Bang => Ok(Value::Bool(!a.truthy())), + TokenType::Tilde => !&a, _ => todo!(), }.map_err(|e| RuntimeError::new(e, op.pos.clone())) } diff --git a/complexpr/src/interpreter.rs b/complexpr/src/interpreter.rs index 52530d7..9db92bd 100644 --- a/complexpr/src/interpreter.rs +++ b/complexpr/src/interpreter.rs @@ -6,7 +6,8 @@ pub fn interpret(src: &str, fname: Option, env: Option, repl: bo let ctx_name = if repl { "" } else { fname.as_ref().map(|s| s.as_ref()).unwrap_or("") }; let mut lexer = Lexer::new(src, fname.clone()); lexer.lex()?; - let mut parser = Parser::new(lexer.into_tokens(), repl); + let tokens = lexer.into_tokens(); + let mut parser = Parser::new(tokens); let ast = parser.parse()?; let environ; if let Some(env) = env { diff --git a/complexpr/src/lexer.rs b/complexpr/src/lexer.rs index 1152f6f..49afcf5 100644 --- a/complexpr/src/lexer.rs +++ b/complexpr/src/lexer.rs @@ -2,6 +2,19 @@ use std::rc::Rc; use crate::{ParserError, Position, token::{Token, TokenType}}; +fn is_in_base(c: char, base: u32) -> bool { + match (c, base) { + ('0' | '1', 2) => true, + ('0'..='5', 6) => true, + ('0'..='7', 8) => true, + ('0'..='9', 10) => true, + ('0'..='9' | 'a' | 'b' | 'A' | 'B', 12) => true, + ('0'..='9' | 'a'..='f' | 'A'..='F', 16) => true, + _ => false + } +} + + pub struct Lexer { // name of file being lexed filename: Option>, @@ -220,6 +233,7 @@ impl Lexer { _ => self.add_token(TokenType::Pipe, "|"), }, '~' => self.add_token(TokenType::Tilde, "~"), + '\\' => self.add_token(TokenType::Backslash, "\\"), ',' => self.add_token(TokenType::Comma, ","), ';' => self.add_token(TokenType::Semicolon, ";"), ':' => self.add_token(TokenType::Colon, ":"), @@ -256,7 +270,15 @@ impl Lexer { '"' => self.string()?, '\'' => self.char()?, ' ' | '\t' | '\r' | '\n' => (), - '0'..='9' => self.number()?, + '0' => match self.expect(&['b', 's', 'o', 'd', 'x']) { + Some('b') => self.number(2)?, + Some('s') => self.number(6)?, + Some('o') => self.number(8)?, + Some('d') => self.number(12)?, + Some('x') => self.number(16)?, + _ => self.number(10)?, + }, + '0'..='9' => self.number(10)?, 'a'..='z' | 'A'..='Z' | '_' => self.ident()?, c => return Err(self.mk_error(format!("Unexpected character: {}", c))) } @@ -306,9 +328,9 @@ impl Lexer { Ok(()) } - fn number(&mut self) -> Result<(), ParserError> { + fn number(&mut self, base: u32) -> Result<(), ParserError> { let mut has_dot = false; - while !self.at_end() && (self.peek().is_numeric() || self.peek() == '.') { + while !self.at_end() && (is_in_base(self.peek(), base) || self.peek() == '.') { if self.peek() == '.' { if has_dot { break; @@ -316,6 +338,9 @@ impl Lexer { if self.peek_ahead(1) == Some('.') { break; } + if base != 10 { + return Err(self.mk_error("Numeric literals using bases other than 10 must be integers.")) + } has_dot = true; } } @@ -334,10 +359,16 @@ impl Lexer { Ok(num) => self.add_token(TokenType::Float(num), literal), Err(e) => return Err(self.mk_error(format!("Error parsing float: {}", e))) } + } else if base != 10 { + match i64::from_str_radix(&literal[2..literal.len()], base) { + Ok(num) => self.add_token(TokenType::Int(num), literal), + Err(e) => return Err(self.mk_error(format!("Error parsing integer: {}", e))) + } + } else { match literal.parse::() { Ok(num) => self.add_token(TokenType::Int(num), literal), - Err(e) => return Err(self.mk_error(format!("Error parsing float: {}", e))) + Err(e) => return Err(self.mk_error(format!("Error parsing integer: {}", e))) } } Ok(()) diff --git a/complexpr/src/parser.rs b/complexpr/src/parser.rs index 8d76019..666dc8d 100644 --- a/complexpr/src/parser.rs +++ b/complexpr/src/parser.rs @@ -4,19 +4,18 @@ use crate::{token::{Token, TokenType, OpType}, ParserError, expr::{Stmt, Expr}, pub struct Parser { tokens: Vec, - repl: bool, idx: usize } impl Parser { - pub fn new(tokens: Vec, repl: bool) -> Self { - Self { tokens, repl, idx: 0 } + pub fn new(tokens: Vec) -> Self { + Self { tokens, idx: 0 } } pub fn parse(&mut self) -> Result, ParserError> { let mut stmts = vec![]; while !self.at_end() { - stmts.push(self.statement(!self.repl)?); + stmts.push(self.statement()?); } Ok(stmts) } @@ -96,14 +95,19 @@ impl Parser { // // ////////////////// - fn statement(&mut self, req_semicolon: bool) -> Result { + fn statement(&mut self) -> Result { let next_ty = &self.peek().ty; match next_ty { + TokenType::Semicolon => { + // skip lonely semicolon + self.next(); + self.statement() + } TokenType::Let => { // let statement self.next(); - self.letstmt(req_semicolon) + self.letstmt() }, TokenType::LBrace => { // block @@ -127,16 +131,16 @@ impl Parser { }, TokenType::Break => { let tok = self.next(); - self.terminate_stmt(Stmt::Break{ pos: tok.pos }, req_semicolon) + self.terminate_stmt(Stmt::Break{ pos: tok.pos }) }, TokenType::Continue => { let tok = self.next(); - self.terminate_stmt(Stmt::Continue{ pos: tok.pos }, req_semicolon) + self.terminate_stmt(Stmt::Continue{ pos: tok.pos }) }, TokenType::Return => { let tok = self.next(); let expr = self.assignment()?; - self.terminate_stmt(Stmt::Return{ pos: tok.pos, expr }, req_semicolon) + self.terminate_stmt(Stmt::Return{ pos: tok.pos, expr }) }, TokenType::Fn => { self.next(); @@ -149,39 +153,34 @@ impl Parser { _ => { // fallback to an expression terminated with a semicolon let expr = self.assignment()?; - self.terminate_stmt(Stmt::Expr{ expr }, req_semicolon) + self.terminate_stmt(Stmt::Expr{ expr }) } } } - fn terminate_stmt(&mut self, stmt: Stmt, req_semicolon: bool) -> Result { + fn terminate_stmt(&mut self, stmt: Stmt) -> Result { if self.at_end() { - if req_semicolon { - self.err_on_eof()?; - } else{ - return Ok(stmt) - } + self.err_on_eof()?; } match self.expect(TokenType::Semicolon).0 { true => Ok(stmt), - false if !req_semicolon => Ok(stmt), false => Err(self.mk_error("Missing semicolon after statement")) } } - fn letstmt(&mut self, req_semicolon: bool) -> Result { + fn letstmt(&mut self) -> Result { let expr = self.assignment()?; // must be followed by an assignment expression if let Expr::Binary{lhs, rhs, op: Token{ty: TokenType::Equal,..}} = expr { if let Expr::Ident{value: tok} = *lhs { - self.terminate_stmt(Stmt::Let{lhs: tok, rhs: Some(*rhs)}, req_semicolon) + self.terminate_stmt(Stmt::Let{lhs: tok, rhs: Some(*rhs)}) } else { Err(self.mk_error("Invalid expression after 'let'".to_owned())) } } else if let Expr::Ident{value: tok} = expr { - self.terminate_stmt(Stmt::Let{lhs: tok, rhs: None}, req_semicolon) + self.terminate_stmt(Stmt::Let{lhs: tok, rhs: None}) } else { Err(self.mk_error("Invalid expression after 'let'".to_owned())) } @@ -192,7 +191,7 @@ impl Parser { let mut ec = false; loop { let condition = self.assignment()?; - let body = self.statement(true)?; + let body = self.statement()?; if_clauses.push((condition, body)); match self.peek().ty { TokenType::Elif => { self.next(); continue }, @@ -201,7 +200,7 @@ impl Parser { } } let else_clause = if ec { - Some(Box::new(self.statement(true)?)) + Some(Box::new(self.statement()?)) } else { None }; @@ -222,7 +221,7 @@ impl Parser { self.err_on_eof()?; let expr = self.assignment()?; self.err_on_eof()?; - let stmt = self.statement(true)?; + let stmt = self.statement()?; Ok(Stmt::For{ var, expr, stmt: Box::new(stmt), iter_pos: colon.pos }) } else { Err(self.mk_error("Expected identifier after for")) @@ -233,7 +232,7 @@ impl Parser { self.err_on_eof()?; let expr = self.assignment()?; self.err_on_eof()?; - let stmt = self.statement(true)?; + let stmt = self.statement()?; Ok(Stmt::While{ expr, stmt: Box::new(stmt) }) } @@ -273,7 +272,7 @@ impl Parser { fn block(&mut self) -> Result { let mut stmts = vec![]; while !self.at_end() && self.peek().ty != TokenType::RBrace { - stmts.push(self.statement(true)?) + stmts.push(self.statement()?) } self.err_on_eof()?; self.next(); @@ -357,7 +356,15 @@ impl Parser { } fn comparison(&mut self) -> Result { - self.expr(OpType::Comparison, Self::additive) + self.expr(OpType::Comparison, Self::bitwise_or) + } + + fn bitwise_or(&mut self) -> Result { + self.expr(OpType::BitwiseOr, Self::bitwise_and) + } + + fn bitwise_and(&mut self) -> Result { + self.expr(OpType::BitwiseAnd, Self::additive) } fn additive(&mut self) -> Result { @@ -422,9 +429,9 @@ impl Parser { // unary: !x, -x fn unary(&mut self) -> Result { self.err_on_eof()?; - if matches!(self.peek().ty, TokenType::Bang | TokenType::Minus) { + if matches!(self.peek().ty, TokenType::Minus | TokenType::Bang | TokenType::Tilde) { let op = self.next(); - Ok(Expr::Unary { arg: Box::new(self.fieldaccess()?), op }) + Ok(Expr::Unary { arg: Box::new(self.unary()?), op }) } else { self.fieldaccess() } @@ -511,29 +518,26 @@ impl Parser { // 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 + // Grouping with parentheses let expr = self.assignment()?; if self.at_end() || TokenType::RParen != self.next().ty { Err(self.mk_error("Left parenthesis never closed")) } else { Ok(expr) } + } else if next.ty == TokenType::Backslash { + self.err_on_eof()?; + let op = self.next(); + if !op.ty.is_infix_op() { + return Err(self.mk_error("Expected infix operator after backslash")) + } + 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) + }) + }; + Ok(Expr::BoxedInfix { func }) } else if next.ty == TokenType::LBrack { // list literal let items = self.commalist(TokenType::RBrack, Self::assignment)?; diff --git a/complexpr/src/token.rs b/complexpr/src/token.rs index 2566c1c..3fa45e8 100644 --- a/complexpr/src/token.rs +++ b/complexpr/src/token.rs @@ -32,6 +32,7 @@ pub enum TokenType { PipeColon, PipePoint, PipeQuestion, PipeAmper, PipeSlash, PipeBackslash, PipeDoubleSlash, PipeDoubleBackslash, + Backslash, Comma, Semicolon, Colon, @@ -63,6 +64,9 @@ impl TokenType { | Self::StarEqual | Self::SlashEqual | Self::DoubleSlashEqual | Self::CaretEqual | Self::PercentEqual => Some(OpType::Assignment), + Self::Amper => Some(OpType::BitwiseAnd), + Self::Pipe => Some(OpType::BitwiseOr), + Self::DoubleAmper => Some(OpType::LogicalAnd), Self::DoublePipe => Some(OpType::LogicalOr), @@ -83,6 +87,8 @@ impl TokenType { | OpType::Multiplicative | OpType::Exponential | OpType::Comparison + | OpType::BitwiseAnd + | OpType::BitwiseOr // TODO | OpType::Pipeline )) } @@ -90,7 +96,10 @@ impl TokenType { #[derive(Clone,Copy,Debug,PartialEq,Eq)] pub enum OpType { - Assignment, Comparison, Pipeline, Additive, Multiplicative, Exponential, LogicalAnd, LogicalOr + Assignment, Comparison, Pipeline, + Additive, Multiplicative, Exponential, + LogicalAnd, LogicalOr, + BitwiseAnd, BitwiseOr, } impl OpType { diff --git a/complexpr/src/value/mod.rs b/complexpr/src/value/mod.rs index 789633f..0403c9d 100644 --- a/complexpr/src/value/mod.rs +++ b/complexpr/src/value/mod.rs @@ -480,7 +480,7 @@ value_from!(Map, RefCell>); macro_rules! impl_numeric_op { - ($optrait:ty, $fnname:ident, { $($bonus:tt)* }) => { + ($optrait:ty, $fnname:ident, $op:literal, { $($bonus:tt)* }) => { impl $optrait for &Value { type Output = Result; fn $fnname(self, other: Self) -> Self::Output { @@ -504,14 +504,14 @@ 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.repr(), rhs.repr())) + (lhs, rhs) => Err(format!("Unsupported operation '{}' between {} and {}", $op, lhs.repr(), rhs.repr())) } } } } } -impl Neg for Value { +impl Neg for &Value { type Output = Result; fn neg(self) -> Self::Output { match self { @@ -524,7 +524,7 @@ impl Neg for Value { } } -impl_numeric_op!(Add, add, { +impl_numeric_op!(Add, add, "+", { (String(a), String(b)) => Ok(((**a).to_owned() + b).into()), (String(a), Char(c)) => { let mut s = (**a).to_owned(); @@ -543,20 +543,20 @@ impl_numeric_op!(Add, add, { Ok(a.into()) }, }); -impl_numeric_op!(Sub, sub, {}); -impl_numeric_op!(Mul, mul, { +impl_numeric_op!(Sub, sub, "-", {}); +impl_numeric_op!(Mul, mul, "*", { (String(a), Int(b)) | (Int(b), String(a)) => Ok(Value::from(a.chars().cycle().take(a.chars().count()*(*b as usize)).collect::())), (List(a), Int(b)) | (Int(b), List(a)) => Ok(Value::from(a.borrow().iter().cycle().take(a.borrow().len()*(*b as usize)).cloned().collect::>())), }); -impl_numeric_op!(Div, div, { +impl_numeric_op!(Div, div, "/", { (Int(_), Int(b)) if *b == 0 => Err("Integer division by zero".into()), (Rational(_), Int(b)) if *b == 0 => Err("Rational division by zero".into()), (Int(_), Rational(b)) if b.is_zero() => Err("Rational division by zero".into()), (Rational(_), Rational(b)) if b.is_zero() => Err("Rational division by zero".into()), }); -impl_numeric_op!(Rem, rem, { +impl_numeric_op!(Rem, rem, "%", { (Int(_), Int(b)) if *b == 0 => Err("Integer modulo by zero".into()), (Rational(_), Int(b)) if *b == 0 => Err("Rational modulo by zero".into()), (Int(_), Rational(b)) if b.is_zero() => Err("Rational modulo by zero".into()), @@ -594,7 +594,46 @@ 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.repr(), rhs.repr())) + (lhs, rhs) => Err(format!("Unsupported operation '^' between {} and {}", lhs.repr(), rhs.repr())) + } + } +} + +impl BitAnd for &Value { + type Output = Result; + + fn bitand(self, other: Self) -> Self::Output { + use Value::*; + match (self, other) { + (Int(a), Int(b)) => Ok(Int(a & b)), + (Bool(a), Bool(b)) => Ok(Bool(a & b)), + _ => Err(format!("Unsupported operation '&' between {} and {}", self.repr(), other.repr())) + } + } +} + +impl BitOr for &Value { + type Output = Result; + + fn bitor(self, other: Self) -> Self::Output { + use Value::*; + match (self, other) { + (Int(a), Int(b)) => Ok(Int(a | b)), + (Bool(a), Bool(b)) => Ok(Bool(a | b)), + _ => Err(format!("Unsupported operation '|' between {} and {}", self.repr(), other.repr())) + } + } +} + +impl Not for &Value { + type Output = Result; + + fn not(self) -> Self::Output { + use Value::*; + match self { + Int(a) => Ok(Int(!a)), + Bool(a) => Ok(Bool(!a)), + _ => Err(format!("Unsupported operation '~' for {}", self.repr())), } } } diff --git a/examples/cat.cxpr b/examples/cat.cxpr index ffaf3c9..2ef8812 100644 --- a/examples/cat.cxpr +++ b/examples/cat.cxpr @@ -1,3 +1,3 @@ while true { - print(input()); + println(input()); } diff --git a/examples/factorial.cxpr b/examples/factorial.cxpr index d1889ef..645b699 100644 --- a/examples/factorial.cxpr +++ b/examples/factorial.cxpr @@ -1,11 +1,29 @@ -fn factorial(n) { +fn fact_recursive(n) { if n <= 1 { return 1; - } else { - return n * factorial(n-1); } + return n * fact_recursive(n-1); } -for n: 0..10 { - println(factorial(n)); +fn fact_imperative(n) { + if n <= 1 { + return 1; + } + let res = 1; + for i: 1..=n { + res *= i; + } + return res; } + +fn fact_pipeline(n) { + if n <= 1 { + return 1; + } + return 1..=n |// \*; +} + +# 10*9*8*7*6*5*4*3*2*1 = 10 +println(fact_recursive(10)); +println(fact_imperative(10)); +println(fact_pipeline(10)); diff --git a/examples/prime.cxpr b/examples/prime.cxpr index 4942bb0..e7d0445 100644 --- a/examples/prime.cxpr +++ b/examples/prime.cxpr @@ -1,6 +1,6 @@ # compute the primorial of n - the product of all primes <= n fn primorial(n) { - return 2..(n+1) |? is_prime |// fn(x,y) { return x*y; }; + return 2..(n+1) |? is_prime |// \*; } println(primorial(10)); # 2*3*5*7 = 210