new range syntax
This commit is contained in:
parent
14b749d2ed
commit
4b063086e4
10 changed files with 126 additions and 65 deletions
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -6,6 +6,6 @@ fn factorial(n) {
|
|||
}
|
||||
}
|
||||
|
||||
for n : range(0, 10) {
|
||||
for n: 0..10 {
|
||||
println(factorial(n));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in a new issue