From 8a6eb35a900081967db16d313ab7ed470de6570f Mon Sep 17 00:00:00 2001 From: Mel Date: Sat, 23 Oct 2021 22:01:52 +0200 Subject: Loop expressions and concrete walker errors. --- Cargo.lock | 56 ++++++++++++ Cargo.toml | 3 +- grammar.ebnf | 7 +- src/interpret/value.rs | 103 +++++++++++++-------- src/interpret/walker.rs | 97 +++++++++++++++++--- src/lex/lexer.rs | 3 + src/lex/mod.rs | 2 +- src/lex/token.rs | 3 + src/parse/ast/expression.rs | 15 ++- src/parse/ast/mod.rs | 2 +- src/parse/ast/nodes.rs | 6 ++ src/parse/ast/statement.rs | 13 +++ src/parse/parser.rs | 218 ++++++++++++++++++++++++++------------------ 13 files changed, 384 insertions(+), 144 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 84c792a..5ba3feb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,9 +8,65 @@ version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" +[[package]] +name = "proc-macro2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rabbithole" version = "0.1.0" dependencies = [ "anyhow", + "thiserror", +] + +[[package]] +name = "syn" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" diff --git a/Cargo.toml b/Cargo.toml index fe24d01..627219b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,5 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -anyhow = "1.0" \ No newline at end of file +anyhow = "1.0" +thiserror = "1.0" \ No newline at end of file diff --git a/grammar.ebnf b/grammar.ebnf index c4c6914..7428788 100644 --- a/grammar.ebnf +++ b/grammar.ebnf @@ -4,10 +4,12 @@ Program = {Statement} EOF; (* Statement *) -Statement = ExpressionStatement | ReturnStatement | PrintStatement; +Statement = ExpressionStatement | ReturnStatement | BreakStatement | ContinueExpression | PrintStatement; ExpressionStatement = Expression ";"; ReturnStatement = "return" Expression ";"; +BreakStatement = "break" [Expression] ";"; +ContinueStatement = "continue" ";"; (* NOTE: Hard-coded PrintStatement until Rabbithole has an standard library *) PrintStatement = "print" Expression ";"; @@ -27,7 +29,7 @@ UnaryExpression = ( "-" | "!" ) UnaryExpression | UnitExpression ; (* Unaffected Expressions *) -UnitExpression = FLOAT | INT | STR | TRUE | FALSE |GroupExpression | BlockExpression | FnExpression | TypeExpression | FormExpression | IfExpression; +UnitExpression = FLOAT | INT | STR | TRUE | FALSE | GroupExpression | BlockExpression | FnExpression | TypeExpression | FormExpression | IfExpression; GroupExpression = "(" Expression ")"; BlockExpression = Block; @@ -36,6 +38,7 @@ TypeExpression = "type" TypeBlock; (* NOTE: Will associated functions clash with fields? *) FormExpression = "form" TypeBlock; IfExpression = "if" Expression Block { "elif" Expression Block } [ "else" Block ]; +LoopExpression = "loop" ["if" Expression] Block; (* Parts *) diff --git a/src/interpret/value.rs b/src/interpret/value.rs index 27f2668..86fe094 100644 --- a/src/interpret/value.rs +++ b/src/interpret/value.rs @@ -1,6 +1,6 @@ use crate::parse::ast::nodes::FnNode; -use anyhow::{anyhow, Result}; use std::{cell::RefCell, fmt::Display, rc::Rc}; +use thiserror::Error; type Ref = RefCell>; @@ -15,79 +15,79 @@ pub enum Value { } impl Value { - pub fn add(self, rhs: Value) -> Result { + pub fn add(self, rhs: Value) -> Result { match self { - Value::Str(l) => match rhs { - Value::Str(r) => Ok(Value::Str(l + &r)), - Value::Float(r) => Ok(Value::Str(l + &r.to_string())), - Value::Int(r) => Ok(Value::Str(l + &r.to_string())), - _ => Err(anyhow!("Right operand can't be added.")), + Value::Str(ref l) => match rhs { + Value::Str(r) => Ok(Value::Str(format!("{}{}", l, r))), + Value::Float(r) => Ok(Value::Str(format!("{}{}", l, r))), + Value::Int(r) => Ok(Value::Str(format!("{}{}", l, r))), + r => Err(OperationError::AddTypes(self, r)), }, Value::Float(l) => match rhs { Value::Str(r) => Ok(Value::Str(l.to_string() + &r)), Value::Float(r) => Ok(Value::Float(l + r)), Value::Int(r) => Ok(Value::Float(l + r as f64)), - _ => Err(anyhow!("Right operand can't be added.")), + r => Err(OperationError::AddTypes(self, r)), }, Value::Int(l) => match rhs { Value::Str(r) => Ok(Value::Str(l.to_string() + &r)), Value::Float(r) => Ok(Value::Float(l as f64 + r)), Value::Int(r) => Ok(Value::Int(l + r)), - _ => Err(anyhow!("Right operand can't be added.")), + r => Err(OperationError::AddTypes(self, r)), }, - _ => Err(anyhow!("Left operand can't be added.")), + _ => Err(OperationError::AddTypes(self, rhs)), } } - pub fn sub(self, rhs: Value) -> Result { + pub fn sub(self, rhs: Value) -> Result { match self { Value::Float(l) => match rhs { Value::Float(r) => Ok(Value::Float(l - r)), Value::Int(r) => Ok(Value::Float(l - r as f64)), - _ => Err(anyhow!("Right operand can't be substracted.")), + r => Err(OperationError::SubTypes(self, r)), }, Value::Int(l) => match rhs { Value::Float(r) => Ok(Value::Float(l as f64 - r)), Value::Int(r) => Ok(Value::Int(l - r)), - _ => Err(anyhow!("Right operand can't be substracted.")), + r => Err(OperationError::SubTypes(self, r)), }, - _ => Err(anyhow!("Left operand can't be substracted from.")), + _ => Err(OperationError::SubTypes(self, rhs)), } } - pub fn mul(self, rhs: Value) -> Result { + pub fn mul(self, rhs: Value) -> Result { match self { Value::Float(l) => match rhs { Value::Float(r) => Ok(Value::Float(l * r)), Value::Int(r) => Ok(Value::Float(l * r as f64)), - _ => Err(anyhow!("Right operand can't be multiplied.")), + r => Err(OperationError::MulTypes(self, r)), }, Value::Int(l) => match rhs { Value::Float(r) => Ok(Value::Float(l as f64 * r)), Value::Int(r) => Ok(Value::Int(l * r)), - _ => Err(anyhow!("Right operand can't be multiplied.")), + r => Err(OperationError::MulTypes(self, r)), }, - _ => Err(anyhow!("Left operand can't be multiplied.")), + _ => Err(OperationError::MulTypes(self, rhs)), } } - pub fn div(self, rhs: Value) -> Result { + pub fn div(self, rhs: Value) -> Result { match self { Value::Float(l) => match rhs { Value::Float(r) => Ok(Value::Float(l / r)), Value::Int(r) => Ok(Value::Float(l / r as f64)), - _ => Err(anyhow!("Right operand can't be multiplied.")), + r => Err(OperationError::DivTypes(self, r)), }, Value::Int(l) => match rhs { Value::Float(r) => Ok(Value::Float(l as f64 / r)), Value::Int(r) => Ok(Value::Float(l as f64 / r as f64)), - _ => Err(anyhow!("Right operand can't be multiplied.")), + r => Err(OperationError::DivTypes(self, r)), }, - _ => Err(anyhow!("Left operand can't be multiplied.")), + _ => Err(OperationError::DivTypes(self, rhs)), } } - pub fn eq(self, rhs: Value) -> Result { + pub fn eq(self, rhs: Value) -> Result { match self { Value::Str(l) => match rhs { Value::Str(r) => Ok(Value::Bool(l == r)), @@ -109,7 +109,7 @@ impl Value { } } - pub fn neq(self, rhs: Value) -> Result { + pub fn neq(self, rhs: Value) -> Result { if let Ok(Value::Bool(value)) = self.eq(rhs) { Ok(Value::Bool(!value)) } else { @@ -117,50 +117,63 @@ impl Value { } } - pub fn gt(self, rhs: Value) -> Result { + pub fn gt(self, rhs: Value) -> Result { match self { Value::Float(r) => match rhs { Value::Float(l) => Ok(Value::Bool(r > l)), Value::Int(l) => Ok(Value::Bool(r > l as f64)), - _ => Err(anyhow!("Right operand can't be compared.")), + r => Err(OperationError::CompareTypes(self, r)), }, Value::Int(r) => match rhs { Value::Float(l) => Ok(Value::Bool(r as f64 > l)), Value::Int(l) => Ok(Value::Bool(r > l)), - _ => Err(anyhow!("Right operand can't be compared.")), + r => Err(OperationError::CompareTypes(self, r)), }, - _ => Err(anyhow!("Left operand can't be compared.")), + _ => Err(OperationError::CompareTypes(self, rhs)), } } - pub fn gte(self, rhs: Value) -> Result { + pub fn gte(self, rhs: Value) -> Result { match self { Value::Float(r) => match rhs { Value::Float(l) => Ok(Value::Bool(r >= l)), Value::Int(l) => Ok(Value::Bool(r >= l as f64)), - _ => Err(anyhow!("Right operand can't be compared.")), + r => Err(OperationError::CompareTypes(self, r)), }, Value::Int(r) => match rhs { Value::Float(l) => Ok(Value::Bool(r as f64 >= l)), Value::Int(l) => Ok(Value::Bool(r >= l)), - _ => Err(anyhow!("Right operand can't be compared.")), + r => Err(OperationError::CompareTypes(self, r)), }, - _ => Err(anyhow!("Left operand can't be compared.")), + _ => Err(OperationError::CompareTypes(self, rhs)), } } - pub fn neg(self) -> Result { + pub fn neg(self) -> Result { match self { Value::Float(float) => Ok(Value::Float(-float)), Value::Int(int) => Ok(Value::Int(-int)), - _ => Err(anyhow!("Can't negate value.")), + _ => Err(OperationError::NegType(self)), } } - pub fn not(self) -> Result { + pub fn not(self) -> Result { match self { Value::Bool(bool) => Ok(Value::Bool(bool)), - _ => Err(anyhow!("Can't flip non-bool value.")), + _ => Err(OperationError::NotType(self)), + } + } +} + +impl Value { + pub fn type_name(&self) -> &'static str { + match self { + Value::Str(_) => "Str", + Value::Float(_) => "Float", + Value::Int(_) => "Int", + Value::Bool(_) => "Bool", + Value::Fn(_) => "Fn", + Value::Void => "Void", } } } @@ -177,3 +190,21 @@ impl Display for Value { } } } + +#[derive(Error, Debug)] +pub enum OperationError { + #[error("Can't add value '{0}' of type '{}' to value '{1}' of type '{}'.", .0.type_name(), .1.type_name())] + AddTypes(Value, Value), + #[error("Can't subtract value '{1}' of type '{}' from value '{0}' of type '{}'.", .1.type_name(), .0.type_name())] + SubTypes(Value, Value), + #[error("Can't multiply value '{0}' of type '{}' with value '{1}' of type '{}'.", .0.type_name(), .1.type_name())] + MulTypes(Value, Value), + #[error("Can't divide value '{0}' of type '{}' by value '{1}' of type '{}'.", .0.type_name(), .1.type_name())] + DivTypes(Value, Value), + #[error("Can't compare value '{0}' of type '{}' with value '{1}' of type '{}'.", .0.type_name(), .1.type_name())] + CompareTypes(Value, Value), + #[error("Can't negate value '{0}' of type '{}'.", .0.type_name())] + NegType(Value), + #[error("Can't flip value '{0}' of type '{}'.", .0.type_name())] + NotType(Value), +} diff --git a/src/interpret/walker.rs b/src/interpret/walker.rs index 7684747..db37a26 100644 --- a/src/interpret/walker.rs +++ b/src/interpret/walker.rs @@ -2,13 +2,16 @@ use std::{cell::RefCell, rc::Rc}; use crate::parse::ast::{ expression::Expression, - nodes::{BinaryOperator as BinOp, BlockNode, Literal, UnaryOperator as UnOp}, + nodes::{BinaryOperator as BinOp, BlockNode, Identifier, Literal, UnaryOperator as UnOp}, statement::Statement, Program, }; -use anyhow::{anyhow, Result}; +use thiserror::Error; -use super::{scope::Scope, value::Value}; +use super::{ + scope::Scope, + value::{OperationError, Value}, +}; pub struct Walker { scope: Scope, @@ -28,7 +31,7 @@ impl Walker { } } - fn walk_statement(&mut self, statement: &Statement) -> Result> { + fn walk_statement(&mut self, statement: &Statement) -> Result, WalkerError> { let result = match statement { Statement::Expression(node) => { self.walk_expression(node)?; @@ -40,12 +43,23 @@ impl Walker { None } Statement::Return(node) => Some(self.walk_expression(node)?), + Statement::Break(node) => { + let returned = if let Some(expression) = node { + Some(self.walk_expression(expression)?) + } else { + None + }; + // If there's a loop above us it will catch this error. + return Err(WalkerError::LoopBreak(returned)); + } + // Same here. + Statement::Continue => return Err(WalkerError::LoopContinue), }; Ok(result) } - pub fn walk_expression(&mut self, node: &Expression) -> Result { + pub fn walk_expression(&mut self, node: &Expression) -> Result { match node { Expression::Binary { left, op, right } => { // Assignment @@ -79,7 +93,8 @@ impl Walker { // No structure access yet. BinOp::Dot => todo!(), _ => unreachable!(), - }?; + } + .map_err(WalkerError::OperationError)?; Ok(new_value) } @@ -87,9 +102,10 @@ impl Walker { let value = self.walk_expression(right)?; let new_value = match op { - UnOp::Minus => value.neg()?, - UnOp::Not => value.not()?, - }; + UnOp::Minus => value.neg(), + UnOp::Not => value.not(), + } + .map_err(WalkerError::OperationError)?; Ok(new_value) } @@ -116,9 +132,7 @@ impl Walker { return self.walk_block(&conditional.block); } } else { - return Err(anyhow!( - "If and Elif expressions can only take boolean values as conditions." - )); + return Err(WalkerError::WrongIfConditionType); } } @@ -128,17 +142,53 @@ impl Walker { Ok(Value::Void) } } + Expression::Loop(loop_node) => { + if let Some(condition) = &loop_node.condition { + loop { + if let Value::Bool(should_repeat) = self.walk_expression(condition)? { + if should_repeat { + match self.walk_block(&loop_node.body) { + Err(WalkerError::LoopBreak(loop_value)) => { + return Ok(loop_value.unwrap_or(Value::Void)); + } + // Do nothing for continue and continue looping of course, you dummy. + Err(WalkerError::LoopContinue) => {} + // This is probably an actual error. + Err(x) => return Err(x), + _ => {} + } + } else { + return Ok(Value::Void); + } + } else { + return Err(WalkerError::WrongLoopConditionType); + } + } + } else { + // Same as above. + loop { + match self.walk_block(&loop_node.body) { + Err(WalkerError::LoopBreak(loop_value)) => { + break Ok(loop_value.unwrap_or(Value::Void)); + } + Err(WalkerError::LoopContinue) => {} + Err(x) => return Err(x), + _ => {} + } + } + } + } Expression::Identifier(ident) => { if let Some(value) = self.scope.get_var(ident) { Ok(value) } else { - Err(anyhow!("Unknown identifier: {}.", ident)) + Err(WalkerError::UnknownIdentifier(ident.clone())) } } } } - fn walk_block(&mut self, block: &BlockNode) -> Result { + fn walk_block(&mut self, block: &BlockNode) -> Result { self.scope.nest(); for statement in block.statements.iter() { @@ -156,3 +206,22 @@ impl Walker { result } } + +// TODO: Add source maps to the errors. +#[derive(Error, Debug)] +pub enum WalkerError { + #[error("Unknown identifier '{0}'")] + UnknownIdentifier(Identifier), + #[error("Loop expressions can only take boolean values as conditions.")] + WrongLoopConditionType, + #[error("If and Elif expressions can only take boolean values as conditions.")] + WrongIfConditionType, + #[error(transparent)] + OperationError(OperationError), + // These are used for loop control flow and are only errors + // if continue and break statements are used outside of loops. + #[error("Continue statements are only valid inside loops.")] + LoopContinue, + #[error("Break statements are only valid inside loops.")] + LoopBreak(Option), +} diff --git a/src/lex/lexer.rs b/src/lex/lexer.rs index 4d980d6..60301c3 100644 --- a/src/lex/lexer.rs +++ b/src/lex/lexer.rs @@ -190,12 +190,15 @@ impl<'s> Lexer<'s> { "if" => TokenVariant::KeywordIf, "elif" => TokenVariant::KeywordElif, "else" => TokenVariant::KeywordElse, + "loop" => TokenVariant::KeywordLoop, "type" => TokenVariant::KeywordType, "form" => TokenVariant::KeywordForm, "self" => TokenVariant::KeywordSelf, "true" => TokenVariant::KeywordTrue, "false" => TokenVariant::KeywordFalse, "return" => TokenVariant::KeywordReturn, + "break" => TokenVariant::KeywordBreak, + "continue" => TokenVariant::KeywordContinue, "print" => TokenVariant::KeywordPrint, _ => TokenVariant::Ident(buffer), }; diff --git a/src/lex/mod.rs b/src/lex/mod.rs index f785280..e12719b 100644 --- a/src/lex/mod.rs +++ b/src/lex/mod.rs @@ -1,2 +1,2 @@ pub mod lexer; -pub mod token; \ No newline at end of file +pub mod token; diff --git a/src/lex/token.rs b/src/lex/token.rs index 5debbfd..2fb5d5b 100644 --- a/src/lex/token.rs +++ b/src/lex/token.rs @@ -53,11 +53,14 @@ pub enum TokenVariant { KeywordIf, KeywordElif, KeywordElse, + KeywordLoop, KeywordForm, KeywordType, KeywordTrue, KeywordFalse, KeywordSelf, + KeywordBreak, + KeywordContinue, KeywordReturn, KeywordPrint, diff --git a/src/parse/ast/expression.rs b/src/parse/ast/expression.rs index 1749c40..c7f6d9b 100644 --- a/src/parse/ast/expression.rs +++ b/src/parse/ast/expression.rs @@ -1,6 +1,8 @@ use std::fmt::{self, Display, Formatter}; -use super::nodes::{BinaryOperator, BlockNode, FnNode, Identifier, IfNode, Literal, UnaryOperator}; +use super::nodes::{ + BinaryOperator, BlockNode, FnNode, Identifier, IfNode, Literal, LoopNode, UnaryOperator, +}; #[derive(Debug, Clone)] pub enum Expression { @@ -17,6 +19,7 @@ pub enum Expression { Block(Box), Fn(Box), If(Box), + Loop(Box), Literal(Literal), Identifier(Identifier), } @@ -97,6 +100,16 @@ impl Expression { Self::block_fmt(f, e, depth + 1)?; } } + Expression::Loop(node) => { + writeln!(f, "{}Loop:", pad)?; + if let Some(loop_condition) = &node.condition { + writeln!(f, "{}- Condition:", pad)?; + loop_condition.nested_fmt(f, depth + 1)?; + } + + writeln!(f, "{}- Body:", pad)?; + Self::block_fmt(f, &node.body, depth + 1)?; + } } Ok(()) diff --git a/src/parse/ast/mod.rs b/src/parse/ast/mod.rs index 93944b2..7d0b58b 100644 --- a/src/parse/ast/mod.rs +++ b/src/parse/ast/mod.rs @@ -3,8 +3,8 @@ use std::fmt::Display; use self::statement::Statement; pub mod expression; -pub mod statement; pub mod nodes; +pub mod statement; #[derive(Debug)] pub struct Program { diff --git a/src/parse/ast/nodes.rs b/src/parse/ast/nodes.rs index 822ffb6..2a4f7c0 100644 --- a/src/parse/ast/nodes.rs +++ b/src/parse/ast/nodes.rs @@ -106,6 +106,12 @@ pub struct IfNode { pub else_block: Option, } +#[derive(Debug, Clone)] +pub struct LoopNode { + pub condition: Option, + pub body: BlockNode, +} + #[derive(Debug, Clone)] pub struct BlockNode { pub statements: Vec, diff --git a/src/parse/ast/statement.rs b/src/parse/ast/statement.rs index b0e626d..99ee0e0 100644 --- a/src/parse/ast/statement.rs +++ b/src/parse/ast/statement.rs @@ -6,6 +6,8 @@ use super::expression::Expression; pub enum Statement { Expression(Expression), Print(Expression), + Break(Option), + Continue, Return(Expression), } @@ -32,6 +34,17 @@ impl Statement { writeln!(f, "{}Return:", pad)?; expression.nested_fmt(f, depth + 1)?; } + Statement::Break(expression) => { + if let Some(returned_on_break) = expression { + writeln!(f, "{}Break:", pad)?; + returned_on_break.nested_fmt(f, depth + 1)?; + } else { + writeln!(f, "{}Break", pad)?; + } + } + Statement::Continue => { + writeln!(f, "{}Continue", pad)?; + } } Ok(()) diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 9b72597..4a84101 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -1,4 +1,5 @@ use super::ast::expression::Expression; +use super::ast::nodes::LoopNode; use super::ast::nodes::UnaryOperator; use super::ast::statement::Statement; use super::ast::Program; @@ -46,6 +47,8 @@ impl> Parser { match token.variant { KeywordPrint => self.print_statement(), KeywordReturn => self.return_statement(), + KeywordBreak => self.break_statement(), + KeywordContinue => self.continue_statement(), _ => self.expression_statement(), } } @@ -57,6 +60,24 @@ impl> Parser { Ok(Statement::Return(expression)) } + fn break_statement(&mut self) -> Result { + consume!(self, KeywordBreak)?; + let returned_on_break = if consume_if!(self, SemiColon).is_none() { + let expression = self.expression()?; + consume!(self, SemiColon)?; + Some(expression) + } else { + None + }; + Ok(Statement::Break(returned_on_break)) + } + + fn continue_statement(&mut self) -> Result { + consume!(self, KeywordContinue)?; + consume!(self, SemiColon)?; + Ok(Statement::Continue) + } + fn print_statement(&mut self) -> Result { consume!(self, KeywordPrint)?; let expression = self.expression()?; @@ -179,105 +200,126 @@ impl> Parser { self.tokens.next().unwrap(), Ident ))), - GroupOpen => { - consume!(self, GroupOpen)?; - let expression = self.expression()?; - consume!(self, GroupClose)?; - Ok(Expression::Group(Box::new(expression))) - } - BlockOpen => Ok(Expression::Block(Box::new(self.block()?))), - KeywordFn => { - consume!(self, KeywordFn)?; - let token = self.tokens.next().expect("Expected function header."); - - let header = { - let has_self_receiver = if let KeywordSelf = token.variant { - consume_if!(self, Comma); - true - } else { - false - }; - - let mut parameters = Vec::new(); - while let Some(token) = consume_if!(self, Ident(_)) { - let parameter_name = inner!(token, Ident); - - let type_constraint = if consume_if!(self, Colon).is_some() { - Some(inner!(consume!(self, Ident(_))?, Ident)) - } else { - None - }; - - parameters.push(TypedIdentifier { - identifier: parameter_name, - type_constraint, - }); - - if consume_if!(self, Comma).is_none() { - break; - } - } - - let return_type = if consume_if!(self, Arrow).is_some() { - Some(inner!(consume!(self, Ident(_))?, Ident)) - } else { - None - }; - - FnHeader { - has_self_receiver, - parameters, - return_type, - } - }; - - let body = self.block()?; - - Ok(Expression::Fn(Box::new(FnNode { header, body }))) - } - KeywordIf => { - consume!(self, KeywordIf)?; + GroupOpen => Ok(Expression::Group(Box::new(self.group()?))), + BlockOpen => Ok(Expression::Block(Box::new(self.generic_block()?))), + KeywordFn => Ok(Expression::Fn(Box::new(self.function()?))), + KeywordIf => Ok(Expression::If(Box::new(self.conditional()?))), + KeywordLoop => Ok(Expression::Loop(Box::new(self.repeating()?))), + _ => Err(anyhow!("Unexpected token: {:?}", token.variant)), + } + } else { + Err(anyhow!("Expected expression.")) + } + } - let mut conditionals = Vec::new(); + fn group(&mut self) -> Result { + consume!(self, GroupOpen)?; + let expression = self.expression()?; + consume!(self, GroupClose)?; + Ok(expression) + } - let if_condition = self.expression()?; - let if_block = self.block()?; + fn function(&mut self) -> Result { + consume!(self, KeywordFn)?; + let token = self.tokens.next().expect("Expected function header."); - conditionals.push(ConditionalBlock { - condition: if_condition, - block: if_block, - }); + let header = { + let has_self_receiver = if let KeywordSelf = token.variant { + consume_if!(self, Comma); + true + } else { + false + }; - // Elifs - while consume_if!(self, KeywordElif).is_some() { - let elif_condition = self.expression()?; - let elif_block = self.block()?; + let mut parameters = Vec::new(); + while let Some(token) = consume_if!(self, Ident(_)) { + let parameter_name = inner!(token, Ident); - conditionals.push(ConditionalBlock { - condition: elif_condition, - block: elif_block, - }); - } + let type_constraint = if consume_if!(self, Colon).is_some() { + Some(inner!(consume!(self, Ident(_))?, Ident)) + } else { + None + }; - let else_block = if consume_if!(self, KeywordElse).is_some() { - Some(self.block()?) - } else { - None - }; + parameters.push(TypedIdentifier { + identifier: parameter_name, + type_constraint, + }); - Ok(Expression::If(Box::new(IfNode { - conditionals, - else_block, - }))) + if consume_if!(self, Comma).is_none() { + break; } - _ => Err(anyhow!("Unexpected token: {:?}", token.variant)), } - } else { - Err(anyhow!("Expected expression.")) + + let return_type = if consume_if!(self, Arrow).is_some() { + Some(inner!(consume!(self, Ident(_))?, Ident)) + } else { + None + }; + + FnHeader { + has_self_receiver, + parameters, + return_type, + } + }; + + let body = self.generic_block()?; + + Ok(FnNode { header, body }) + } + + fn conditional(&mut self) -> Result { + consume!(self, KeywordIf)?; + + let mut conditionals = Vec::new(); + + let if_condition = self.expression()?; + let if_block = self.generic_block()?; + + conditionals.push(ConditionalBlock { + condition: if_condition, + block: if_block, + }); + + // Elifs + while consume_if!(self, KeywordElif).is_some() { + let elif_condition = self.expression()?; + let elif_block = self.generic_block()?; + + conditionals.push(ConditionalBlock { + condition: elif_condition, + block: elif_block, + }); } + + let else_block = if consume_if!(self, KeywordElse).is_some() { + Some(self.generic_block()?) + } else { + None + }; + + Ok(IfNode { + conditionals, + else_block, + }) + } + + fn repeating(&mut self) -> Result { + consume!(self, KeywordLoop)?; + + let condition = if consume_if!(self, KeywordIf).is_some() { + let expression = self.expression()?; + Some(expression) + } else { + None + }; + + let body = self.generic_block()?; + Ok(LoopNode { body, condition }) } - fn block(&mut self) -> Result { + fn generic_block(&mut self) -> Result { consume!(self, BlockOpen)?; let mut statements = Vec::new(); @@ -286,7 +328,7 @@ impl> Parser { loop { let token = self.tokens.peek().expect("Unclosed block."); match token.variant { - KeywordReturn | KeywordPrint => { + KeywordReturn | KeywordPrint | KeywordContinue | KeywordBreak => { statements.push(self.statement()?); } BlockClose => { -- cgit 1.4.1