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::Ternary { arg1, arg2, arg3, op } => eval_ternary(arg1, arg2, arg3, op, env),
|
||||||
Expr::Unary { arg, op } => eval_unary(arg, 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 } => {
|
Expr::List { items } => {
|
||||||
let mut list = Vec::with_capacity(items.len());
|
let mut list = Vec::with_capacity(items.len());
|
||||||
for item in items {
|
for item in items {
|
||||||
|
@ -454,3 +459,61 @@ pub fn eval_unary(arg: &Expr, op: &Token, env: EnvRef) -> Result<Value, RuntimeE
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}.map_err(|e| RuntimeError::new(e, op.pos.clone()))
|
}.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 },
|
Binary { lhs: Box<Expr>, rhs: Box<Expr>, op: Token },
|
||||||
Ternary { arg1: Box<Expr>, arg2: Box<Expr>, arg3: Box<Expr>, op: Token },
|
Ternary { arg1: Box<Expr>, arg2: Box<Expr>, arg3: Box<Expr>, op: Token },
|
||||||
Unary { arg: 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 },
|
Ident { value: Token },
|
||||||
Literal { value: Token },
|
Literal { value: Token },
|
||||||
List { items: Vec<Expr> },
|
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::Binary { lhs: left, rhs: right, op} => write!(f, "({:?} {:?} {:?})", op, left, right),
|
||||||
Self::Ternary { arg1, arg2, arg3, op} => write!(f, "({:?} {:?} {:?} {:?})", op, arg1, arg2, arg3),
|
Self::Ternary { arg1, arg2, arg3, op} => write!(f, "({:?} {:?} {:?} {:?})", op, arg1, arg2, arg3),
|
||||||
Self::Unary { arg, op } => write!(f, "({:?} {:?})", op, arg),
|
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::Ident { value } => write!(f, "(ident {:?})", value),
|
||||||
Self::Literal { value } => write!(f, "(lit {:?})", value),
|
Self::Literal { value } => write!(f, "(lit {:?})", value),
|
||||||
Self::List { items } => write!(f, "(list {:?})", items),
|
Self::List { items } => write!(f, "(list {:?})", items),
|
||||||
|
|
|
@ -55,6 +55,10 @@ impl Lexer {
|
||||||
self.code[self.current]
|
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> {
|
fn add_token<S>(&mut self, ty: TokenType, text: S) where S: Into<String> {
|
||||||
self.tokens.push(Token {
|
self.tokens.push(Token {
|
||||||
ty,
|
ty,
|
||||||
|
@ -144,6 +148,10 @@ impl Lexer {
|
||||||
while !self.at_end() {
|
while !self.at_end() {
|
||||||
self.start = self.current;
|
self.start = self.current;
|
||||||
match self.next() {
|
match self.next() {
|
||||||
|
'.' => match self.expect(&['.']) {
|
||||||
|
Some('.') => self.add_token(TokenType::DoubleDot, ".."),
|
||||||
|
_ => return Err(self.mk_error("Expected '.' after previous '.'"))
|
||||||
|
},
|
||||||
'+' => match self.expect(&['=']) {
|
'+' => match self.expect(&['=']) {
|
||||||
Some('=') => self.add_token(TokenType::PlusEqual, "+="),
|
Some('=') => self.add_token(TokenType::PlusEqual, "+="),
|
||||||
_ => self.add_token(TokenType::Plus, "+"),
|
_ => self.add_token(TokenType::Plus, "+"),
|
||||||
|
@ -304,6 +312,9 @@ impl Lexer {
|
||||||
if has_dot {
|
if has_dot {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
if self.peek_ahead(1) == Some('.') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
has_dot = true;
|
has_dot = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -352,11 +352,11 @@ impl Parser {
|
||||||
// Right associative, so cannot use self.expr(..)
|
// Right associative, so cannot use self.expr(..)
|
||||||
fn exponential(&mut self) -> Result<Expr, ParserError> {
|
fn exponential(&mut self) -> Result<Expr, ParserError> {
|
||||||
let mut stack= vec![];
|
let mut stack= vec![];
|
||||||
let mut expr = self.unary()?;
|
let mut expr = self.range()?;
|
||||||
while !self.at_end() && self.peek().ty == TokenType::Caret {
|
while !self.at_end() && self.peek().ty == TokenType::Caret {
|
||||||
let op = self.next();
|
let op = self.next();
|
||||||
stack.push((expr, op));
|
stack.push((expr, op));
|
||||||
expr = self.unary()?;
|
expr = self.range()?;
|
||||||
}
|
}
|
||||||
while let Some(item) = stack.pop() {
|
while let Some(item) = stack.pop() {
|
||||||
expr = Expr::Binary{ lhs: Box::new(item.0), rhs: Box::new(expr), op: item.1 };
|
expr = Expr::Binary{ lhs: Box::new(item.0), rhs: Box::new(expr), op: item.1 };
|
||||||
|
@ -364,6 +364,42 @@ impl Parser {
|
||||||
Ok(expr)
|
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> {
|
fn unary(&mut self) -> Result<Expr, ParserError> {
|
||||||
self.err_on_eof()?;
|
self.err_on_eof()?;
|
||||||
if matches!(self.peek().ty, TokenType::Bang | TokenType::Minus) {
|
if matches!(self.peek().ty, TokenType::Bang | TokenType::Minus) {
|
||||||
|
|
|
@ -2,9 +2,9 @@ pub mod io;
|
||||||
pub mod iter;
|
pub mod iter;
|
||||||
pub mod math;
|
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_export]
|
||||||
macro_rules! declare_fn {
|
macro_rules! declare_fn {
|
||||||
|
@ -26,8 +26,6 @@ pub fn load(env: &mut Environment) {
|
||||||
declare_fn!(env, repr, 1);
|
declare_fn!(env, repr, 1);
|
||||||
declare_fn!(env, ord, 1);
|
declare_fn!(env, ord, 1);
|
||||||
declare_fn!(env, chr, 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, has, 2);
|
||||||
declare_fn!(env, len, 1);
|
declare_fn!(env, len, 1);
|
||||||
declare_fn!(env, time, 0);
|
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())
|
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> {
|
fn fn_len(args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||||
Ok(Value::Int(args[0].len().map_err(RuntimeError::new_no_pos)? as i64))
|
Ok(Value::Int(args[0].len().map_err(RuntimeError::new_no_pos)? as i64))
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@ pub enum TokenType {
|
||||||
Bang, DoubleAmper, DoublePipe,
|
Bang, DoubleAmper, DoublePipe,
|
||||||
Tilde, Amper, Pipe,
|
Tilde, Amper, Pipe,
|
||||||
|
|
||||||
|
DoubleDot,
|
||||||
|
|
||||||
Equal, PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual, DoubleSlashEqual, CaretEqual,
|
Equal, PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual, DoubleSlashEqual, CaretEqual,
|
||||||
DoubleEqual, BangEqual, Greater, GreaterEqual, Less, LessEqual, Spaceship,
|
DoubleEqual, BangEqual, Greater, GreaterEqual, Less, LessEqual, Spaceship,
|
||||||
|
|
||||||
|
@ -64,6 +66,8 @@ impl TokenType {
|
||||||
Self::DoubleAmper => Some(OpType::LogicalAnd),
|
Self::DoubleAmper => Some(OpType::LogicalAnd),
|
||||||
Self::DoublePipe => Some(OpType::LogicalOr),
|
Self::DoublePipe => Some(OpType::LogicalOr),
|
||||||
|
|
||||||
|
Self::DoubleDot => Some(OpType::Range),
|
||||||
|
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,7 +82,7 @@ impl TokenType {
|
||||||
|
|
||||||
#[derive(Clone,Copy,Debug,PartialEq,Eq)]
|
#[derive(Clone,Copy,Debug,PartialEq,Eq)]
|
||||||
pub enum OpType {
|
pub enum OpType {
|
||||||
Assignment, Comparison, Pipeline, Additive, Multiplicative, Exponential, LogicalAnd, LogicalOr
|
Assignment, Comparison, Pipeline, Additive, Multiplicative, Exponential, LogicalAnd, LogicalOr, Range
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OpType {
|
impl OpType {
|
||||||
|
|
|
@ -6,6 +6,6 @@ fn factorial(n) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for n : range(0, 10) {
|
for n: 0..10 {
|
||||||
println(factorial(n));
|
println(factorial(n));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
let xscale = 100;
|
let xscale = 100;
|
||||||
let yscale = xscale/2;
|
let yscale = xscale/2;
|
||||||
|
|
||||||
for y: range(0,yscale) {
|
for y: 0..yscale {
|
||||||
for x: range(0,xscale) {
|
for x: 0..xscale {
|
||||||
|
|
||||||
let a = (1.0*x/xscale)*3 - 2;
|
let a = (1.0*x/xscale)*3 - 2;
|
||||||
let b = (1.0*y/yscale)*3 - 1.5;
|
let b = (1.0*y/yscale)*3 - 1.5;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# compute the primorial of n - the product of all primes <= n
|
# compute the primorial of n - the product of all primes <= n
|
||||||
fn primorial(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
|
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