From 4b063086e40c31a5a459844adf1b2f68151442a2 Mon Sep 17 00:00:00 2001 From: TriMill Date: Tue, 20 Sep 2022 23:38:58 -0400 Subject: [PATCH] new range syntax --- complexpr/src/eval.rs | 63 +++++++++++++++++++++++++++++++++++++ complexpr/src/expr.rs | 2 ++ complexpr/src/lexer.rs | 11 +++++++ complexpr/src/parser.rs | 40 +++++++++++++++++++++-- complexpr/src/stdlib/mod.rs | 59 ++-------------------------------- complexpr/src/token.rs | 6 +++- examples/factorial.cxpr | 2 +- examples/mbrot.cxpr | 4 +-- examples/prime.cxpr | 2 +- examples/quine.cxpr | 2 +- 10 files changed, 126 insertions(+), 65 deletions(-) diff --git a/complexpr/src/eval.rs b/complexpr/src/eval.rs index c8900d2..d4ecaaf 100644 --- a/complexpr/src/eval.rs +++ b/complexpr/src/eval.rs @@ -147,6 +147,11 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result { }, Expr::Ternary { arg1, arg2, arg3, op } => eval_ternary(arg1, arg2, arg3, op, env), Expr::Unary { arg, op } => eval_unary(arg, op, env), + Expr::Range { start, end, step, incl } + => eval_range(start, + end.as_ref().map(|x| x.as_ref()), + step.as_ref().map(|x| x.as_ref()), + *incl, env), Expr::List { items } => { let mut list = Vec::with_capacity(items.len()); for item in items { @@ -454,3 +459,61 @@ pub fn eval_unary(arg: &Expr, op: &Token, env: EnvRef) -> Result todo!(), }.map_err(|e| RuntimeError::new(e, op.pos.clone())) } + +fn mk_numeric_range(start: Value, stop: Option, step: Value, incl: bool) -> Func { + let counter = RefCell::new(start); + Func::BuiltinClosure { + arg_count: 0, + func: Rc::new(move |_| { + let v = counter.borrow().clone(); + if let Some(st) = &stop { + if v > *st || (!incl && v == *st) { + return Ok(Value::Nil) + } + } + *counter.borrow_mut() = (&v + &step)?; + Ok(v) + }) + } +} + +fn mk_char_range(start: char, stop: Option, step: i64) -> Func { + const UNICODE_ERR_MSG: &str = "Char range exceeded range of valid Unicode codepoints"; + let counter = RefCell::new(start); + Func::BuiltinClosure { + arg_count: 0, + func: Rc::new(move |_| { + let v = *counter.borrow(); + if let Some(st) = stop { + if v > st { + return Ok(Value::Nil) + } + } + let next_i64 = (v as u32) as i64 + step; + let next_u32 = u32::try_from(next_i64) + .map_err(|_| UNICODE_ERR_MSG)?; + *counter.borrow_mut() = char::from_u32(next_u32) + .ok_or(UNICODE_ERR_MSG)?; + Ok(Value::Char(v)) + }) + } +} + +pub fn eval_range(start: &Expr, end: Option<&Expr>, step: Option<&Expr>, incl: bool, env: EnvRef) -> Result { + let start = eval_expr(start, env.clone())?; + let end = end.map(|e| eval_expr(e, env.clone())).transpose()?; + let step = step.map(|e| eval_expr(e, env)).transpose()?.unwrap_or(Value::Int(1)); + match (start, end, step, incl) { + ( + n1 @ (Value::Int(_) | Value::Rational(_)), + n2 @ (None | Some(Value::Int(_)) | Some(Value::Rational(_))), + n3 @ (Value::Int(_) | Value::Rational(_)), + incl + ) => Ok(Value::Func(mk_numeric_range(n1, n2, n3, incl))), + (Value::Char(c1), Some(Value::Char(c2)), Value::Int(n), true) + => Ok(Value::Func(mk_char_range(c1, Some(c2), n))), + (Value::Char(c1), None, Value::Int(n), false) + => Ok(Value::Func(mk_char_range(c1, None, n))), + _ => Err("Invalid operands for range".into()) + } +} diff --git a/complexpr/src/expr.rs b/complexpr/src/expr.rs index 9fbed3e..ad30676 100644 --- a/complexpr/src/expr.rs +++ b/complexpr/src/expr.rs @@ -40,6 +40,7 @@ pub enum Expr { Binary { lhs: Box, rhs: Box, op: Token }, Ternary { arg1: Box, arg2: Box, arg3: Box, op: Token }, Unary { arg: Box, op: Token }, + Range { start: Box, end: Option>, step: Option>, incl: bool }, Ident { value: Token }, Literal { value: Token }, List { items: Vec }, @@ -55,6 +56,7 @@ impl fmt::Debug for Expr { Self::Binary { lhs: left, rhs: right, op} => write!(f, "({:?} {:?} {:?})", op, left, right), 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::Ident { value } => write!(f, "(ident {:?})", value), Self::Literal { value } => write!(f, "(lit {:?})", value), Self::List { items } => write!(f, "(list {:?})", items), diff --git a/complexpr/src/lexer.rs b/complexpr/src/lexer.rs index 82be629..d615647 100644 --- a/complexpr/src/lexer.rs +++ b/complexpr/src/lexer.rs @@ -55,6 +55,10 @@ impl Lexer { self.code[self.current] } + fn peek_ahead(&self, n: usize) -> Option { + self.code.get(self.current + n).cloned() + } + fn add_token(&mut self, ty: TokenType, text: S) where S: Into { self.tokens.push(Token { ty, @@ -144,6 +148,10 @@ impl Lexer { while !self.at_end() { self.start = self.current; match self.next() { + '.' => match self.expect(&['.']) { + Some('.') => self.add_token(TokenType::DoubleDot, ".."), + _ => return Err(self.mk_error("Expected '.' after previous '.'")) + }, '+' => match self.expect(&['=']) { Some('=') => self.add_token(TokenType::PlusEqual, "+="), _ => self.add_token(TokenType::Plus, "+"), @@ -304,6 +312,9 @@ impl Lexer { if has_dot { break; } else { + if self.peek_ahead(1) == Some('.') { + break; + } has_dot = true; } } diff --git a/complexpr/src/parser.rs b/complexpr/src/parser.rs index aacbc69..27dff76 100644 --- a/complexpr/src/parser.rs +++ b/complexpr/src/parser.rs @@ -352,11 +352,11 @@ impl Parser { // Right associative, so cannot use self.expr(..) fn exponential(&mut self) -> Result { let mut stack= vec![]; - let mut expr = self.unary()?; + let mut expr = self.range()?; while !self.at_end() && self.peek().ty == TokenType::Caret { let op = self.next(); stack.push((expr, op)); - expr = self.unary()?; + expr = self.range()?; } while let Some(item) = stack.pop() { expr = Expr::Binary{ lhs: Box::new(item.0), rhs: Box::new(expr), op: item.1 }; @@ -364,6 +364,42 @@ impl Parser { Ok(expr) } + fn range(&mut self) -> Result { + let start = self.unary()?; + if !self.at_end() && self.peek().ty == TokenType::DoubleDot { + self.next(); + // consume = if inclusive + let incl = if !self.at_end() && self.peek().ty == TokenType::Equal { + self.next(); + true + } else { + false + }; + // consume end number or * for endless + let end = if !incl && !self.at_end() && self.peek().ty == TokenType::Star { + self.next(); + None + } else { + Some(self.unary()?) + }; + // consume :step if it exists + let step = if !self.at_end() && self.peek().ty == TokenType::Colon { + self.next(); + Some(self.unary()?) + } else { + None + }; + Ok(Expr::Range { + start: Box::new(start), + end: end.map(|x| Box::new(x)), + step: step.map(|x| Box::new(x)), + incl + }) + } else { + Ok(start) + } + } + fn unary(&mut self) -> Result { self.err_on_eof()?; if matches!(self.peek().ty, TokenType::Bang | TokenType::Minus) { diff --git a/complexpr/src/stdlib/mod.rs b/complexpr/src/stdlib/mod.rs index c04e750..2948140 100644 --- a/complexpr/src/stdlib/mod.rs +++ b/complexpr/src/stdlib/mod.rs @@ -2,9 +2,9 @@ pub mod io; pub mod iter; pub mod math; -use std::{rc::Rc, cmp::Ordering, time::{SystemTime, UNIX_EPOCH}, cell::RefCell}; +use std::time::{SystemTime, UNIX_EPOCH}; -use crate::{value::{Value, func::Func}, RuntimeError, env::Environment}; +use crate::{value::Value, RuntimeError, env::Environment}; #[macro_export] macro_rules! declare_fn { @@ -26,8 +26,6 @@ pub fn load(env: &mut Environment) { declare_fn!(env, repr, 1); declare_fn!(env, ord, 1); declare_fn!(env, chr, 1); - declare_fn!(env, range, 2); - declare_fn!(env, count_by, 2); declare_fn!(env, has, 2); declare_fn!(env, len, 1); declare_fn!(env, time, 0); @@ -90,59 +88,6 @@ fn fn_chr(args: Vec) -> Result { Err("Argument to chr must be an integer".into()) } } - -fn mk_range_inner(start: i64, end: i64, delta: i64) -> Func { - let counter = RefCell::new(start); - Func::BuiltinClosure { - arg_count: 0, - func: Rc::new(move |_| { - let c_value = *counter.borrow(); - if delta >= 0 && c_value >= end - || delta <= 0 && c_value <= end { - Ok(Value::Nil) - } else { - let res = *counter.borrow(); - *counter.borrow_mut() += delta; - Ok(Value::Int(res)) - } - }) - } -} - -fn fn_range(args: Vec) -> Result { - let (start, end, delta) = match (&args[0], &args[1]) { - (Value::Int(a), Value::Int(b)) => (a, b, - match a.cmp(&b) { - Ordering::Equal => 0, - Ordering::Less => 1, - Ordering::Greater => -1, - } - ), - _ => return Err("Both arguments to range must be integers".into()) - }; - Ok(Value::Func(mk_range_inner(*start, *end, delta))) -} - -fn mk_countby_inner(start: i64, delta: i64) -> Func { - let counter = RefCell::new(start); - Func::BuiltinClosure { - arg_count: 0, - func: Rc::new(move |_| { - let res = *counter.borrow(); - *counter.borrow_mut() += delta; - Ok(Value::Int(res)) - }) - } -} - -fn fn_count_by(args: Vec) -> Result { - let (start, delta) = match (&args[0], &args[1]) { - (Value::Int(a), Value::Int(b)) => (a, b), - _ => return Err("Both arguments to count_by must be integers".into()) - }; - Ok(Value::Func(mk_countby_inner(*start, *delta))) -} - fn fn_len(args: Vec) -> Result { Ok(Value::Int(args[0].len().map_err(RuntimeError::new_no_pos)? as i64)) } diff --git a/complexpr/src/token.rs b/complexpr/src/token.rs index 95655e8..cbf305d 100644 --- a/complexpr/src/token.rs +++ b/complexpr/src/token.rs @@ -25,6 +25,8 @@ pub enum TokenType { Bang, DoubleAmper, DoublePipe, Tilde, Amper, Pipe, + DoubleDot, + Equal, PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual, DoubleSlashEqual, CaretEqual, DoubleEqual, BangEqual, Greater, GreaterEqual, Less, LessEqual, Spaceship, @@ -64,6 +66,8 @@ impl TokenType { Self::DoubleAmper => Some(OpType::LogicalAnd), Self::DoublePipe => Some(OpType::LogicalOr), + Self::DoubleDot => Some(OpType::Range), + _ => None } } @@ -78,7 +82,7 @@ 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, Range } impl OpType { diff --git a/examples/factorial.cxpr b/examples/factorial.cxpr index cfc5a5e..d1889ef 100644 --- a/examples/factorial.cxpr +++ b/examples/factorial.cxpr @@ -6,6 +6,6 @@ fn factorial(n) { } } -for n : range(0, 10) { +for n: 0..10 { println(factorial(n)); } diff --git a/examples/mbrot.cxpr b/examples/mbrot.cxpr index f958182..a4868d6 100644 --- a/examples/mbrot.cxpr +++ b/examples/mbrot.cxpr @@ -1,8 +1,8 @@ let xscale = 100; let yscale = xscale/2; -for y: range(0,yscale) { - for x: range(0,xscale) { +for y: 0..yscale { + for x: 0..xscale { let a = (1.0*x/xscale)*3 - 2; let b = (1.0*y/yscale)*3 - 1.5; diff --git a/examples/prime.cxpr b/examples/prime.cxpr index cd5b236..4942bb0 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 range(2,n+1) |? is_prime |// fn(x,y) { return x*y; }; + return 2..(n+1) |? is_prime |// fn(x,y) { return x*y; }; } println(primorial(10)); # 2*3*5*7 = 210 diff --git a/examples/quine.cxpr b/examples/quine.cxpr index 82c6bdb..540fde1 100644 --- a/examples/quine.cxpr +++ b/examples/quine.cxpr @@ -1 +1 @@ -let s="let s=;let p=print;for n:range(0,6){p(s[n]);}p(chr(34)+s+chr(34));for n:range(6,105){p(s[n]);}p(chr(10));";let p=print;for n:range(0,6){p(s[n]);}p(chr(34)+s+chr(34));for n:range(6,105){p(s[n]);}p(chr(10)); +let s="let s=;let p=print;for n:0..6{p(s[n]);}p(chr(34)+s+chr(34));for n:6..92{p(s[n]);}p(chr(10));";let p=print;for n:0..6{p(s[n]);}p(chr(34)+s+chr(34));for n:6..92{p(s[n]);}p(chr(10));