new range syntax

This commit is contained in:
TriMill 2022-09-20 23:38:58 -04:00
parent 14b749d2ed
commit 4b063086e4
10 changed files with 126 additions and 65 deletions

View File

@ -147,6 +147,11 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result<Value, RuntimeError> {
},
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<Value, RuntimeE
_ => todo!(),
}.map_err(|e| RuntimeError::new(e, op.pos.clone()))
}
fn mk_numeric_range(start: Value, stop: Option<Value>, 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<char>, 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<Value, RuntimeError> {
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())
}
}

View File

@ -40,6 +40,7 @@ pub enum Expr {
Binary { lhs: Box<Expr>, rhs: Box<Expr>, op: Token },
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 },
Ident { value: Token },
Literal { value: Token },
List { items: Vec<Expr> },
@ -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),

View File

@ -55,6 +55,10 @@ impl Lexer {
self.code[self.current]
}
fn peek_ahead(&self, n: usize) -> Option<char> {
self.code.get(self.current + n).cloned()
}
fn add_token<S>(&mut self, ty: TokenType, text: S) where S: Into<String> {
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;
}
}

View File

@ -352,11 +352,11 @@ impl Parser {
// Right associative, so cannot use self.expr(..)
fn exponential(&mut self) -> Result<Expr, ParserError> {
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<Expr, ParserError> {
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<Expr, ParserError> {
self.err_on_eof()?;
if matches!(self.peek().ty, TokenType::Bang | TokenType::Minus) {

View File

@ -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<Value>) -> Result<Value, RuntimeError> {
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<Value>) -> Result<Value, RuntimeError> {
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<Value>) -> Result<Value, RuntimeError> {
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<Value>) -> Result<Value, RuntimeError> {
Ok(Value::Int(args[0].len().map_err(RuntimeError::new_no_pos)? as i64))
}

View File

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

View File

@ -6,6 +6,6 @@ fn factorial(n) {
}
}
for n : range(0, 10) {
for n: 0..10 {
println(factorial(n));
}

View File

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

View File

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

View File

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