diff --git a/complexpr/examples/iterator.cxpr b/complexpr/examples/iterator.cxpr index 5e5d6ae..b47a8d6 100644 --- a/complexpr/examples/iterator.cxpr +++ b/complexpr/examples/iterator.cxpr @@ -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 diff --git a/complexpr/examples/quine.cxpr b/complexpr/examples/quine.cxpr new file mode 100644 index 0000000..82c6bdb --- /dev/null +++ b/complexpr/examples/quine.cxpr @@ -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)); diff --git a/complexpr/src/eval.rs b/complexpr/src/eval.rs index f11960d..39f4a04 100644 --- a/complexpr/src/eval.rs +++ b/complexpr/src/eval.rs @@ -130,7 +130,7 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result { => 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 { => 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 { + 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::>>(); + 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!() } diff --git a/complexpr/src/expr.rs b/complexpr/src/expr.rs index 9067f3b..5ef40d5 100644 --- a/complexpr/src/expr.rs +++ b/complexpr/src/expr.rs @@ -36,6 +36,7 @@ impl fmt::Debug for Stmt { #[derive(Clone)] pub enum Expr { Binary { lhs: Box, rhs: Box, op: Token }, + Ternary { arg1: Box, arg2: Box, arg3: Box, op: Token }, Unary { arg: Box, 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), diff --git a/complexpr/src/lexer.rs b/complexpr/src/lexer.rs index bf79d30..e02e0b9 100644 --- a/complexpr/src/lexer.rs +++ b/complexpr/src/lexer.rs @@ -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' => (), diff --git a/complexpr/src/parser.rs b/complexpr/src/parser.rs index 2a2278e..c2e33e7 100644 --- a/complexpr/src/parser.rs +++ b/complexpr/src/parser.rs @@ -272,11 +272,30 @@ impl Parser { } fn pipeline(&mut self) -> Result { - 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 { - self.expr(OpType::Boolean, Self::comparison) + fn logical_or(&mut self) -> Result { + self.expr(OpType::LogicalOr, Self::logical_and) + } + + fn logical_and(&mut self) -> Result { + self.expr(OpType::LogicalAnd, Self::comparison) } fn comparison(&mut self) -> Result { @@ -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()?; diff --git a/complexpr/src/stdlib/mod.rs b/complexpr/src/stdlib/mod.rs index 0f58214..b491ded 100644 --- a/complexpr/src/stdlib/mod.rs +++ b/complexpr/src/stdlib/mod.rs @@ -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) -> Result { @@ -160,3 +162,27 @@ fn fn_list(args: Vec) -> Result { for v in a { res.push(v?); } Ok(Value::from(res)) } + +fn take_inner(_: Vec, data: Rc>>, iter_data: Rc>>) -> Result { + // 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) -> Result { + 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 + })) +} diff --git a/complexpr/src/token.rs b/complexpr/src/token.rs index 85c26d0..7e1e3f0 100644 --- a/complexpr/src/token.rs +++ b/complexpr/src/token.rs @@ -22,12 +22,14 @@ pub enum TokenType { Ident(Rc), 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 { diff --git a/complexpr/src/value.rs b/complexpr/src/value.rs index 7a22427..ae472c1 100644 --- a/complexpr/src/value.rs +++ b/complexpr/src/value.rs @@ -25,6 +25,10 @@ pub enum Func { data: Rc>>, iter_data: Rc>>, arg_count: usize, + }, + Partial { + inner: Box, + filled_args: Vec, } } @@ -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) -> Result { + 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) -> Result { 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!("", name, *func as *const ())), Self::Func(Func::BuiltinClosure { func, .. }) => Rc::from(format!("", *func as *const ())), + Self::Func(f @ Func::Partial { .. }) => match f.name() { + Some(name) => Rc::from(format!("", name)), + None => Rc::from(""), + } Self::Func(Func::Func { name, .. }) => match name { Some(name) => Rc::from(format!("", name)), None => Rc::from(""),