bitwise operators, hex/bin/seximal literals, changed infix boxing notation, fixed semicolons in repl

This commit is contained in:
TriMill 2022-10-18 08:35:44 -04:00
parent 443262f711
commit 7be878b97a
10 changed files with 180 additions and 69 deletions

View File

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

View File

@ -144,7 +144,9 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result<Value, RuntimeError> {
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<Value, RuntimeError> {
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()))
}

View File

@ -6,7 +6,8 @@ pub fn interpret(src: &str, fname: Option<String>, env: Option<EnvRef>, repl: bo
let ctx_name = if repl { "<interactive input>" } else { fname.as_ref().map(|s| s.as_ref()).unwrap_or("<unknown>") };
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 {

View File

@ -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<Rc<str>>,
@ -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::<i64>() {
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(())

View File

@ -4,19 +4,18 @@ use crate::{token::{Token, TokenType, OpType}, ParserError, expr::{Stmt, Expr},
pub struct Parser {
tokens: Vec<Token>,
repl: bool,
idx: usize
}
impl Parser {
pub fn new(tokens: Vec<Token>, repl: bool) -> Self {
Self { tokens, repl, idx: 0 }
pub fn new(tokens: Vec<Token>) -> Self {
Self { tokens, idx: 0 }
}
pub fn parse(&mut self) -> Result<Vec<Stmt>, 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<Stmt, ParserError> {
fn statement(&mut self) -> Result<Stmt, ParserError> {
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<Stmt, ParserError> {
fn terminate_stmt(&mut self, stmt: Stmt) -> Result<Stmt, ParserError> {
if self.at_end() {
if req_semicolon {
self.err_on_eof()?;
} else{
return Ok(stmt)
}
}
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<Stmt, ParserError> {
fn letstmt(&mut self) -> Result<Stmt, ParserError> {
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<Stmt, ParserError> {
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<Expr, ParserError> {
self.expr(OpType::Comparison, Self::additive)
self.expr(OpType::Comparison, Self::bitwise_or)
}
fn bitwise_or(&mut self) -> Result<Expr, ParserError> {
self.expr(OpType::BitwiseOr, Self::bitwise_and)
}
fn bitwise_and(&mut self) -> Result<Expr, ParserError> {
self.expr(OpType::BitwiseAnd, Self::additive)
}
fn additive(&mut self) -> Result<Expr, ParserError> {
@ -422,9 +429,9 @@ 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) {
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)?;

View File

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

View File

@ -480,7 +480,7 @@ value_from!(Map, RefCell<HashMap<Value,Value>>);
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<Value, String>;
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<Value, String>;
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::<std::string::String>())),
(List(a), Int(b)) | (Int(b), List(a))
=> Ok(Value::from(a.borrow().iter().cycle().take(a.borrow().len()*(*b as usize)).cloned().collect::<Vec<Value>>())),
});
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<Value, String>;
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<Value, String>;
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<Value, String>;
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())),
}
}
}

View File

@ -1,3 +1,3 @@
while true {
print(input());
println(input());
}

View File

@ -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));

View File

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