more pipeline, partial fns, block comments

This commit is contained in:
TriMill 2022-09-17 10:54:56 -04:00
parent 19b7215234
commit c5380e383e
9 changed files with 172 additions and 31 deletions

View File

@ -11,12 +11,14 @@ fn count_by(delta, limit) {
};
}
# counter is an iterator
# iterators are functions that:
# - take no arguments
# - return nil once done
# - once returned nil once, must do so for all subsequent calls
# the interpreter only checks the first requirement
#{
counter is an iterator
iterators are functions that:
- take no arguments
- return nil once done
- once returned nil once, must do so for all subsequent calls
the interpreter only checks the first requirement
}#
let counter = count_by(2, 5);
println(counter()); # 0
println(counter()); # 2

View File

@ -0,0 +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));

View File

@ -130,7 +130,7 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result<Value, RuntimeError> {
=> eval_assignment(lhs, rhs, op, env),
Some(OpType::Additive) | Some(OpType::Multiplicative) | Some(OpType::Exponential)
=> eval_arith(lhs, rhs, op, env),
Some(OpType::Boolean)
Some(OpType::LogicalAnd) | Some(OpType::LogicalOr)
=> eval_boolean(lhs, rhs, op, env),
Some(OpType::Comparison)
=> eval_comp(lhs, rhs, op, env),
@ -138,6 +138,7 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result<Value, RuntimeError> {
=> eval_pipeline(lhs, rhs, op, env),
o => todo!("{:?}", o) // TODO other operations
},
Expr::Ternary { .. } => todo!(),
Expr::Unary { arg, op } => eval_unary(arg, op, env),
Expr::List { items } => {
let mut list = Vec::with_capacity(items.len());
@ -225,9 +226,9 @@ pub fn eval_assignment(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Resul
// plain assignment
let r = eval_expr(rhs, env.clone())?;
env.borrow_mut()
.set(name.clone(), r)
.set(name.clone(), r.clone())
.map_err(|_| RuntimeError::new("Variable not declared before assignment", op.pos.clone()))?;
Ok(Value::Nil)
Ok(r)
} else {
// compound assignment
let prev_value = env.borrow_mut()
@ -371,6 +372,35 @@ fn eval_pipeline_inner(l: Value, r: Value, op: &Token) -> Result<Value, RuntimeE
func: pipequestion_inner,
}))
},
TokenType::PipeDoubleSlash => {
let mut result = Value::Nil;
let mut first_iter = true;
for v in l.iter().map_err(|e| RuntimeError::new(e, op.pos.clone()))? {
let v = v.map_err(|e| e.complete(op.pos.clone()))?;
if first_iter {
result = v;
first_iter = false;
} else {
result = r.call(vec![result, v]).map_err(|e| e.complete(op.pos.clone()))?;
}
}
Ok(result)
},
TokenType::PipeDoubleBackslash => {
let mut result = Value::Nil;
let mut first_iter = true;
let lst = l.iter().map_err(|e| RuntimeError::new(e, op.pos.clone()))?.collect::<Vec<Result<Value, RuntimeError>>>();
for v in lst.into_iter().rev() {
let v = v.map_err(|e| e.complete(op.pos.clone()))?;
if first_iter {
result = v;
first_iter = false;
} else {
result = r.call(vec![v, result]).map_err(|e| e.complete(op.pos.clone()))?;
}
}
Ok(result)
},
_ => todo!()
}

View File

@ -36,6 +36,7 @@ impl fmt::Debug for Stmt {
#[derive(Clone)]
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 },
Ident { value: Token },
Literal { value: Token },
@ -50,6 +51,7 @@ impl fmt::Debug for Expr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
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::Ident { value } => write!(f, "(ident {:?})", value),
Self::Literal { value } => write!(f, "(lit {:?})", value),

View File

@ -191,12 +191,20 @@ impl Lexer {
Some('&') => self.add_token(TokenType::DoubleAmper, "&&"),
_ => self.add_token(TokenType::Amper, "&"),
},
'|' => match self.expect(&['|', ':', '?', '>', '&']) {
'|' => match self.expect(&['|', ':', '?', '>', '&', '/', '\\']) {
Some('|') => self.add_token(TokenType::DoublePipe, "||"),
Some(':') => self.add_token(TokenType::PipeColon, "|:"),
Some('?') => self.add_token(TokenType::PipeQuestion, "|?"),
Some('>') => self.add_token(TokenType::PipePoint, "|>"),
Some('&') => self.add_token(TokenType::PipeAmper, "|&"),
Some('/') => match self.expect(&['/']) {
Some(_) => self.add_token(TokenType::PipeDoubleSlash, "|//"),
None => self.add_token(TokenType::PipeSlash, "|/")
},
Some('\\') => match self.expect(&['\\']) {
Some(_) => self.add_token(TokenType::PipeDoubleBackslash, "|\\\\"),
None => self.add_token(TokenType::PipeBackslash, "|\\")
},
_ => self.add_token(TokenType::Pipe, "|"),
},
',' => self.add_token(TokenType::Comma, ","),
@ -208,12 +216,30 @@ impl Lexer {
']' => self.add_token(TokenType::RBrack, "]"),
'{' => self.add_token(TokenType::LBrace, "{"),
'}' => self.add_token(TokenType::RBrace, "}"),
'#' => {
while !self.at_end() && self.peek() != '\n' {
self.advance(false);
}
self.advance(true);
},
'#' => match self.expect(&['{']) {
Some(_) => {
while !self.at_end() {
if self.peek() == '}' {
self.advance(false);
if self.at_end() { break }
if self.peek() == '#' {
break
}
}
self.advance(false);
}
if self.at_end() {
return Err(self.mk_error("Unexpected EOF in block comment"))
}
self.advance(true);
},
None => {
while !self.at_end() && self.peek() != '\n' {
self.advance(false);
}
self.advance(true);
},
}
'"' => self.string()?,
'\'' => self.char()?,
' ' | '\t' | '\r' | '\n' => (),

View File

@ -272,11 +272,30 @@ impl Parser {
}
fn pipeline(&mut self) -> Result<Expr, ParserError> {
self.expr(OpType::Pipeline, Self::boolean)
let mut expr = self.logical_or()?;
while !self.at_end() && self.peek().ty.get_op_type() == Some(OpType::Pipeline) {
let op = self.next();
let right = self.logical_or()?;
if op.ty == TokenType::PipeSlash || op.ty == TokenType::PipeBackslash {
let next = self.next();
if next.ty != TokenType::Comma {
return Err(self.mk_error("Expected comma after first argument"))
}
let right2 = self.logical_or()?;
expr = Expr::Ternary { arg1: Box::new(expr), arg2: Box::new(right), arg3: Box::new(right2), op }
} else {
expr = Expr::Binary { lhs: Box::new(expr), rhs: Box::new(right), op };
}
}
Ok(expr)
}
fn boolean(&mut self) -> Result<Expr, ParserError> {
self.expr(OpType::Boolean, Self::comparison)
fn logical_or(&mut self) -> Result<Expr, ParserError> {
self.expr(OpType::LogicalOr, Self::logical_and)
}
fn logical_and(&mut self) -> Result<Expr, ParserError> {
self.expr(OpType::LogicalAnd, Self::comparison)
}
fn comparison(&mut self) -> Result<Expr, ParserError> {
@ -339,7 +358,7 @@ impl Parser {
let index = self.assignment()?;
self.err_on_eof()?;
if self.next().ty != TokenType::RBrack {
return Err(ParserError { message: "Expected RBrack after collection index".into(), pos: lbrack.pos });
return Err(self.mk_error("Expected RBrack after collection index"))
}
Ok(Expr::Index { lhs: Box::new(expr), index: Box::new(index), pos: lbrack.pos })
}
@ -349,7 +368,7 @@ impl Parser {
self.err_on_eof()?;
let next = self.next();
if next.ty != TokenType::Colon {
return Err(ParserError { message: "Expected colon in key-value pair".into(), pos: next.pos })
return Err(self.mk_error("Expected colon in key-value pair"))
}
self.err_on_eof()?;
let value = self.assignment()?;
@ -384,7 +403,7 @@ impl Parser {
self.err_on_eof()?;
let next = self.next();
if let TokenType::LParen = next.ty {} else {
return Err(ParserError { message: "Expected left parenthesis to start arguments list".into(), pos: next.pos })
return Err(self.mk_error("Expected left parenthesis to start arguments list"))
}
let args = self.commalist(TokenType::RParen, Self::ident)?;
self.err_on_eof()?;

View File

@ -34,6 +34,8 @@ pub fn load(env: &mut Environment) {
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_time, arg_count: 0, name }));
name = Rc::from("list");
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_list, arg_count: 1, name }));
name = Rc::from("take");
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_take, arg_count: 2, name }));
}
fn fn_str(args: Vec<Value>) -> Result<Value, RuntimeError> {
@ -160,3 +162,27 @@ fn fn_list(args: Vec<Value>) -> Result<Value, RuntimeError> {
for v in a { res.push(v?); }
Ok(Value::from(res))
}
fn take_inner(_: Vec<Value>, data: Rc<RefCell<Vec<Value>>>, iter_data: Rc<RefCell<Vec<CIterator>>>) -> Result<Value, RuntimeError> {
// 0: current index
// 1: target index
let mut d = data.borrow_mut();
if d[0] >= d[1] {
Ok(Value::Nil)
} else {
d[0] = (&d[0] + &Value::Int(1))?;
match iter_data.borrow_mut()[0].next() {
None => Ok(Value::Nil),
Some(x) => x
}
}
}
fn fn_take(args: Vec<Value>) -> Result<Value, RuntimeError> {
return Ok(Value::Func(Func::BuiltinClosure {
arg_count: 0,
data: Rc::new(RefCell::new(vec![Value::Int(0), args[0].clone()])),
iter_data: Rc::new(RefCell::new(vec![args[1].iter()?])),
func: take_inner
}))
}

View File

@ -22,12 +22,14 @@ pub enum TokenType {
Ident(Rc<str>),
Plus, Minus, Star, Slash, Percent, DoubleSlash, Caret,
Bang, Amper, Pipe, DoubleAmper, DoublePipe,
Bang, DoubleAmper, DoublePipe,
Tilde, Amper, Pipe,
Equal, PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual, DoubleSlashEqual, CaretEqual,
DoubleEqual, BangEqual, Greater, GreaterEqual, Less, LessEqual, Spaceship,
PipeColon, PipePoint, PipeQuestion, PipeAmper,
PipeSlash, PipeBackslash, PipeDoubleSlash, PipeDoubleBackslash,
Comma, Semicolon, Colon,
@ -48,8 +50,8 @@ impl TokenType {
Self::Caret => Some(OpType::Exponential),
Self::PipeColon | Self::PipeAmper | Self::PipePoint
| Self::PipeQuestion => Some(OpType::Pipeline),
Self::PipeColon | Self::PipeAmper | Self::PipePoint | Self::PipeQuestion
| Self::PipeSlash | Self::PipeDoubleSlash | Self::PipeBackslash | Self::PipeDoubleBackslash => Some(OpType::Pipeline),
Self::Greater | Self::GreaterEqual | Self::Less | Self::LessEqual
| Self::DoubleEqual | Self::BangEqual | Self::Spaceship => Some(OpType::Comparison),
@ -58,7 +60,8 @@ impl TokenType {
| Self::StarEqual | Self::SlashEqual | Self::DoubleSlashEqual
| Self::CaretEqual | Self::PercentEqual => Some(OpType::Assignment),
Self::DoubleAmper | Self::DoublePipe => Some(OpType::Boolean),
Self::DoubleAmper => Some(OpType::LogicalAnd),
Self::DoublePipe => Some(OpType::LogicalOr),
_ => None
}
@ -74,7 +77,7 @@ impl TokenType {
#[derive(Clone,Copy,Debug,PartialEq,Eq)]
pub enum OpType {
Assignment, Comparison, Pipeline, Additive, Multiplicative, Exponential, Boolean
Assignment, Comparison, Pipeline, Additive, Multiplicative, Exponential, LogicalAnd, LogicalOr
}
impl OpType {

View File

@ -25,6 +25,10 @@ pub enum Func {
data: Rc<RefCell<Vec<Value>>>,
iter_data: Rc<RefCell<Vec<CIterator>>>,
arg_count: usize,
},
Partial {
inner: Box<Func>,
filled_args: Vec<Value>,
}
}
@ -46,6 +50,11 @@ impl fmt::Debug for Func {
.field("arg_count", arg_count)
.field("data", data)
.finish_non_exhaustive(),
Self::Partial { inner, filled_args }
=> f.debug_struct("Func::Partial")
.field("inner", inner)
.field("filled_args", filled_args)
.finish(),
}
}
@ -57,10 +66,20 @@ impl Func {
Self::Builtin { arg_count, .. } => *arg_count,
Self::BuiltinClosure { arg_count, .. } => *arg_count,
Self::Func { args, .. } => args.len(),
Self::Partial { inner, filled_args } => inner.arg_count() - filled_args.len(),
}
}
pub fn call(&self, arg_values: Vec<Value>) -> Result<Value, RuntimeError> {
pub fn name(&self) -> Option<&str> {
match self {
Self::Builtin { name, .. } => Some(name.as_ref()),
Self::BuiltinClosure { .. } => None,
Self::Func { name, .. } => name.as_ref().map(|s| s.as_ref()),
Self::Partial { inner, .. } => inner.name()
}
}
pub fn call(&self, mut arg_values: Vec<Value>) -> Result<Value, RuntimeError> {
match arg_values.len().cmp(&self.arg_count()) {
Ordering::Equal => match self {
Self::Builtin { func, .. }
@ -81,11 +100,16 @@ impl Func {
}
}
},
Self::Partial { inner, filled_args } => {
let mut filled_args = filled_args.clone();
filled_args.append(&mut arg_values);
inner.call(filled_args)
}
}
Ordering::Less => Err(RuntimeError::new_incomplete(
format!("Not enough arguments for function: expected {}, got {}", self.arg_count(), arg_values.len())
)),
Ordering::Less => {
Ok(Value::Func(Func::Partial { inner: Box::new(self.clone()), filled_args: arg_values }))
},
Ordering::Greater => Err(RuntimeError::new_incomplete(
format!("Too many arguments for function: expected {}, got {}", self.arg_count(), arg_values.len())
))
@ -108,6 +132,10 @@ impl Hash for Func {
Self::BuiltinClosure { arg_count, data, .. } => {
arg_count.hash(state);
data.borrow().hash(state);
},
Self::Partial { inner, filled_args } => {
filled_args.hash(state);
inner.hash(state);
}
}
}
@ -221,6 +249,10 @@ impl Value {
Self::Type(_) => todo!(),
Self::Func(Func::Builtin { name, func, .. }) => Rc::from(format!("<builtin fn {} at {:?}>", name, *func as *const ())),
Self::Func(Func::BuiltinClosure { func, .. }) => Rc::from(format!("<builtin anonymous fn at {:?}>", *func as *const ())),
Self::Func(f @ Func::Partial { .. }) => match f.name() {
Some(name) => Rc::from(format!("<partial of fn {}>", name)),
None => Rc::from("<partial of anonymous fn>"),
}
Self::Func(Func::Func { name, .. }) => match name {
Some(name) => Rc::from(format!("<fn {}>", name)),
None => Rc::from("<anonymous fn>"),