more pipeline, partial fns, block comments
This commit is contained in:
parent
19b7215234
commit
c5380e383e
9 changed files with 172 additions and 31 deletions
|
@ -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
|
||||
|
|
1
complexpr/examples/quine.cxpr
Normal file
1
complexpr/examples/quine.cxpr
Normal 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));
|
|
@ -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!()
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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, "}"),
|
||||
'#' => {
|
||||
'#' => 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' => (),
|
||||
|
|
|
@ -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()?;
|
||||
|
|
|
@ -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
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>"),
|
||||
|
|
Loading…
Reference in a new issue