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: counter is an iterator
# - take no arguments iterators are functions that:
# - return nil once done - take no arguments
# - once returned nil once, must do so for all subsequent calls - return nil once done
# the interpreter only checks the first requirement - once returned nil once, must do so for all subsequent calls
the interpreter only checks the first requirement
}#
let counter = count_by(2, 5); let counter = count_by(2, 5);
println(counter()); # 0 println(counter()); # 0
println(counter()); # 2 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), => eval_assignment(lhs, rhs, op, env),
Some(OpType::Additive) | Some(OpType::Multiplicative) | Some(OpType::Exponential) Some(OpType::Additive) | Some(OpType::Multiplicative) | Some(OpType::Exponential)
=> eval_arith(lhs, rhs, op, env), => eval_arith(lhs, rhs, op, env),
Some(OpType::Boolean) Some(OpType::LogicalAnd) | Some(OpType::LogicalOr)
=> eval_boolean(lhs, rhs, op, env), => eval_boolean(lhs, rhs, op, env),
Some(OpType::Comparison) Some(OpType::Comparison)
=> eval_comp(lhs, rhs, op, env), => 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), => eval_pipeline(lhs, rhs, op, env),
o => todo!("{:?}", o) // TODO other operations o => todo!("{:?}", o) // TODO other operations
}, },
Expr::Ternary { .. } => todo!(),
Expr::Unary { arg, op } => eval_unary(arg, op, env), Expr::Unary { arg, op } => eval_unary(arg, op, env),
Expr::List { items } => { Expr::List { items } => {
let mut list = Vec::with_capacity(items.len()); 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 // plain assignment
let r = eval_expr(rhs, env.clone())?; let r = eval_expr(rhs, env.clone())?;
env.borrow_mut() env.borrow_mut()
.set(name.clone(), r) .set(name.clone(), r.clone())
.map_err(|_| RuntimeError::new("Variable not declared before assignment", op.pos.clone()))?; .map_err(|_| RuntimeError::new("Variable not declared before assignment", op.pos.clone()))?;
Ok(Value::Nil) Ok(r)
} else { } else {
// compound assignment // compound assignment
let prev_value = env.borrow_mut() 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, 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!() _ => todo!()
} }

View file

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

View file

@ -191,12 +191,20 @@ impl Lexer {
Some('&') => self.add_token(TokenType::DoubleAmper, "&&"), Some('&') => self.add_token(TokenType::DoubleAmper, "&&"),
_ => self.add_token(TokenType::Amper, "&"), _ => self.add_token(TokenType::Amper, "&"),
}, },
'|' => match self.expect(&['|', ':', '?', '>', '&']) { '|' => match self.expect(&['|', ':', '?', '>', '&', '/', '\\']) {
Some('|') => self.add_token(TokenType::DoublePipe, "||"), Some('|') => self.add_token(TokenType::DoublePipe, "||"),
Some(':') => self.add_token(TokenType::PipeColon, "|:"), Some(':') => self.add_token(TokenType::PipeColon, "|:"),
Some('?') => self.add_token(TokenType::PipeQuestion, "|?"), Some('?') => self.add_token(TokenType::PipeQuestion, "|?"),
Some('>') => self.add_token(TokenType::PipePoint, "|>"), Some('>') => self.add_token(TokenType::PipePoint, "|>"),
Some('&') => self.add_token(TokenType::PipeAmper, "|&"), 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::Pipe, "|"),
}, },
',' => self.add_token(TokenType::Comma, ","), ',' => self.add_token(TokenType::Comma, ","),
@ -208,12 +216,30 @@ impl Lexer {
']' => self.add_token(TokenType::RBrack, "]"), ']' => self.add_token(TokenType::RBrack, "]"),
'{' => self.add_token(TokenType::LBrace, "{"), '{' => self.add_token(TokenType::LBrace, "{"),
'}' => self.add_token(TokenType::RBrace, "}"), '}' => self.add_token(TokenType::RBrace, "}"),
'#' => { '#' => 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' { while !self.at_end() && self.peek() != '\n' {
self.advance(false); self.advance(false);
} }
self.advance(true); self.advance(true);
}, },
}
'"' => self.string()?, '"' => self.string()?,
'\'' => self.char()?, '\'' => self.char()?,
' ' | '\t' | '\r' | '\n' => (), ' ' | '\t' | '\r' | '\n' => (),

View file

@ -272,11 +272,30 @@ impl Parser {
} }
fn pipeline(&mut self) -> Result<Expr, ParserError> { 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> { fn logical_or(&mut self) -> Result<Expr, ParserError> {
self.expr(OpType::Boolean, Self::comparison) 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> { fn comparison(&mut self) -> Result<Expr, ParserError> {
@ -339,7 +358,7 @@ impl Parser {
let index = self.assignment()?; let index = self.assignment()?;
self.err_on_eof()?; self.err_on_eof()?;
if self.next().ty != TokenType::RBrack { 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 }) Ok(Expr::Index { lhs: Box::new(expr), index: Box::new(index), pos: lbrack.pos })
} }
@ -349,7 +368,7 @@ impl Parser {
self.err_on_eof()?; self.err_on_eof()?;
let next = self.next(); let next = self.next();
if next.ty != TokenType::Colon { 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()?; self.err_on_eof()?;
let value = self.assignment()?; let value = self.assignment()?;
@ -384,7 +403,7 @@ impl Parser {
self.err_on_eof()?; self.err_on_eof()?;
let next = self.next(); let next = self.next();
if let TokenType::LParen = next.ty {} else { 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)?; let args = self.commalist(TokenType::RParen, Self::ident)?;
self.err_on_eof()?; 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 })); env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_time, arg_count: 0, name }));
name = Rc::from("list"); name = Rc::from("list");
env.declare(name.clone(), Value::Func(Func::Builtin { func: fn_list, arg_count: 1, name })); 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> { 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?); } for v in a { res.push(v?); }
Ok(Value::from(res)) 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>), Ident(Rc<str>),
Plus, Minus, Star, Slash, Percent, DoubleSlash, Caret, 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, Equal, PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual, DoubleSlashEqual, CaretEqual,
DoubleEqual, BangEqual, Greater, GreaterEqual, Less, LessEqual, Spaceship, DoubleEqual, BangEqual, Greater, GreaterEqual, Less, LessEqual, Spaceship,
PipeColon, PipePoint, PipeQuestion, PipeAmper, PipeColon, PipePoint, PipeQuestion, PipeAmper,
PipeSlash, PipeBackslash, PipeDoubleSlash, PipeDoubleBackslash,
Comma, Semicolon, Colon, Comma, Semicolon, Colon,
@ -48,8 +50,8 @@ impl TokenType {
Self::Caret => Some(OpType::Exponential), Self::Caret => Some(OpType::Exponential),
Self::PipeColon | Self::PipeAmper | Self::PipePoint Self::PipeColon | Self::PipeAmper | Self::PipePoint | Self::PipeQuestion
| Self::PipeQuestion => Some(OpType::Pipeline), | Self::PipeSlash | Self::PipeDoubleSlash | Self::PipeBackslash | Self::PipeDoubleBackslash => Some(OpType::Pipeline),
Self::Greater | Self::GreaterEqual | Self::Less | Self::LessEqual Self::Greater | Self::GreaterEqual | Self::Less | Self::LessEqual
| Self::DoubleEqual | Self::BangEqual | Self::Spaceship => Some(OpType::Comparison), | Self::DoubleEqual | Self::BangEqual | Self::Spaceship => Some(OpType::Comparison),
@ -58,7 +60,8 @@ impl TokenType {
| Self::StarEqual | Self::SlashEqual | Self::DoubleSlashEqual | Self::StarEqual | Self::SlashEqual | Self::DoubleSlashEqual
| Self::CaretEqual | Self::PercentEqual => Some(OpType::Assignment), | 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 _ => None
} }
@ -74,7 +77,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, Boolean Assignment, Comparison, Pipeline, Additive, Multiplicative, Exponential, LogicalAnd, LogicalOr
} }
impl OpType { impl OpType {

View file

@ -25,6 +25,10 @@ pub enum Func {
data: Rc<RefCell<Vec<Value>>>, data: Rc<RefCell<Vec<Value>>>,
iter_data: Rc<RefCell<Vec<CIterator>>>, iter_data: Rc<RefCell<Vec<CIterator>>>,
arg_count: usize, 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("arg_count", arg_count)
.field("data", data) .field("data", data)
.finish_non_exhaustive(), .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::Builtin { arg_count, .. } => *arg_count,
Self::BuiltinClosure { arg_count, .. } => *arg_count, Self::BuiltinClosure { arg_count, .. } => *arg_count,
Self::Func { args, .. } => args.len(), 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()) { match arg_values.len().cmp(&self.arg_count()) {
Ordering::Equal => match self { Ordering::Equal => match self {
Self::Builtin { func, .. } 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( Ordering::Less => {
format!("Not enough arguments for function: expected {}, got {}", self.arg_count(), arg_values.len()) Ok(Value::Func(Func::Partial { inner: Box::new(self.clone()), filled_args: arg_values }))
)), },
Ordering::Greater => Err(RuntimeError::new_incomplete( Ordering::Greater => Err(RuntimeError::new_incomplete(
format!("Too many arguments for function: expected {}, got {}", self.arg_count(), arg_values.len()) 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, .. } => { Self::BuiltinClosure { arg_count, data, .. } => {
arg_count.hash(state); arg_count.hash(state);
data.borrow().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::Type(_) => todo!(),
Self::Func(Func::Builtin { name, func, .. }) => Rc::from(format!("<builtin fn {} at {:?}>", name, *func as *const ())), 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(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 { Self::Func(Func::Func { name, .. }) => match name {
Some(name) => Rc::from(format!("<fn {}>", name)), Some(name) => Rc::from(format!("<fn {}>", name)),
None => Rc::from("<anonymous fn>"), None => Rc::from("<anonymous fn>"),