diff --git a/cl-ast/src/ast_impl.rs b/cl-ast/src/ast_impl.rs index dd1effe..91ffafa 100644 --- a/cl-ast/src/ast_impl.rs +++ b/cl-ast/src/ast_impl.rs @@ -367,6 +367,7 @@ mod display { ExprKind::Empty => "()".fmt(f), ExprKind::Group(v) => v.fmt(f), ExprKind::Tuple(v) => v.fmt(f), + ExprKind::Loop(v) => v.fmt(f), ExprKind::While(v) => v.fmt(f), ExprKind::If(v) => v.fmt(f), ExprKind::For(v) => v.fmt(f), @@ -534,6 +535,12 @@ mod display { write!(f, "({})", self.expr) } } + impl Display for Loop { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { body } = self; + write!(f, "loop {body}") + } + } impl Display for While { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self { cond, pass, fail } = self; diff --git a/cl-ast/src/lib.rs b/cl-ast/src/lib.rs index 6887005..91ac61c 100644 --- a/cl-ast/src/lib.rs +++ b/cl-ast/src/lib.rs @@ -346,6 +346,8 @@ pub enum ExprKind { Group(Group), /// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)` Tuple(Tuple), + /// A [Loop] expression: `loop` [`Block`] + Loop(Loop), /// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]? While(While), /// An [If] expression: `if` [`Expr`] [`Block`] [`Else`]? @@ -492,6 +494,12 @@ pub struct Tuple { pub exprs: Vec, } +/// A [Loop] expression: `loop` [`Block`] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Loop { + pub body: Block, +} + /// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]? #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct While { diff --git a/cl-interpret/src/interpret.rs b/cl-interpret/src/interpret.rs index 4bac82b..9bcc2c0 100644 --- a/cl-interpret/src/interpret.rs +++ b/cl-interpret/src/interpret.rs @@ -131,6 +131,7 @@ impl Interpret for ExprKind { ExprKind::Empty => Ok(ConValue::Empty), ExprKind::Group(v) => v.interpret(env), ExprKind::Tuple(v) => v.interpret(env), + ExprKind::Loop(v) => v.interpret(env), ExprKind::While(v) => v.interpret(env), ExprKind::If(v) => v.interpret(env), ExprKind::For(v) => v.interpret(env), @@ -365,17 +366,32 @@ impl Interpret for Tuple { )?)) } } -impl Interpret for While { +impl Interpret for Loop { fn interpret(&self, env: &mut Environment) -> IResult { - let Self { cond, pass, fail } = self; - while cond.interpret(env)?.truthy()? { - match pass.interpret(env) { + let Self { body } = self; + loop { + match body.interpret(env) { Err(Error::Break(value)) => return Ok(value), Err(Error::Continue) => continue, e => e?, }; } - fail.interpret(env) + } +} +impl Interpret for While { + fn interpret(&self, env: &mut Environment) -> IResult { + let Self { cond, pass, fail } = self; + loop { + if cond.interpret(env)?.truthy()? { + match pass.interpret(env) { + Err(Error::Break(value)) => return Ok(value), + Err(Error::Continue) => continue, + e => e?, + }; + } else { + break fail.interpret(env); + } + } } } impl Interpret for If { @@ -392,23 +408,24 @@ impl Interpret for For { fn interpret(&self, env: &mut Environment) -> IResult { let Self { bind: Identifier(name), cond, pass, fail } = self; // TODO: A better iterator model - let bounds = match cond.interpret(env)? { + let mut bounds = match cond.interpret(env)? { ConValue::RangeExc(a, b) => a..=b, ConValue::RangeInc(a, b) => a..=b, _ => Err(Error::TypeError)?, }; - { + loop { let mut env = env.frame("loop variable"); - for loop_var in bounds { + if let Some(loop_var) = bounds.next() { env.insert(name, Some(loop_var.into())); match pass.interpret(&mut env) { Err(Error::Break(value)) => return Ok(value), Err(Error::Continue) => continue, result => result?, }; + } else { + break fail.interpret(&mut env); } } - fail.interpret(env) } } impl Interpret for Else { diff --git a/cl-parser/src/error.rs b/cl-parser/src/error.rs index 6988bae..a7c3b6e 100644 --- a/cl-parser/src/error.rs +++ b/cl-parser/src/error.rs @@ -103,6 +103,7 @@ pub enum Parsing { Block, Group, Tuple, + Loop, While, If, For, @@ -204,6 +205,7 @@ impl Display for Parsing { Parsing::Block => "a block", Parsing::Group => "a grouped expression", Parsing::Tuple => "a tuple", + Parsing::Loop => "an unconditional loop expression", Parsing::While => "a while expression", Parsing::If => "an if expression", Parsing::For => "a for expression", diff --git a/cl-parser/src/parser.rs b/cl-parser/src/parser.rs index 0871124..8fde066 100644 --- a/cl-parser/src/parser.rs +++ b/cl-parser/src/parser.rs @@ -809,6 +809,7 @@ impl<'t> Parser<'t> { self.consume_peeked(); Unary { kind, tail: self.exprkind(after)?.into() }.into() } + TokenKind::Loop => ExprKind::Loop(self.parse_loop()?), TokenKind::While => ExprKind::While(self.parse_while()?), TokenKind::If => ExprKind::If(self.parse_if()?), TokenKind::For => ExprKind::For(self.parse_for()?), @@ -1010,6 +1011,12 @@ impl<'t> Parser<'t> { } /// ## Control flow subexpressions impl<'t> Parser<'t> { + /// [Loop] = `loop` [Block] + pub fn parse_loop(&mut self) -> PResult { + self.match_type(TokenKind::Loop, Parsing::Loop)?; + Ok(Loop { body: self.block()? }) + } + /// [While] = `while` [Expr] [Block] [Else]? pub fn parse_while(&mut self) -> PResult { self.match_type(TokenKind::While, Parsing::While)?; diff --git a/cl-repl/examples/yaml.rs b/cl-repl/examples/yaml.rs index 4594892..610682d 100644 --- a/cl-repl/examples/yaml.rs +++ b/cl-repl/examples/yaml.rs @@ -383,6 +383,7 @@ pub mod yamlify { ExprKind::Empty => {} ExprKind::Group(k) => k.yaml(y), ExprKind::Tuple(k) => k.yaml(y), + ExprKind::Loop(k) => k.yaml(y), ExprKind::While(k) => k.yaml(y), ExprKind::If(k) => k.yaml(y), ExprKind::For(k) => k.yaml(y), @@ -472,6 +473,12 @@ pub mod yamlify { y.key("Group").yaml(expr); } } + impl Yamlify for Loop { + fn yaml(&self, y: &mut Yamler) { + let Self { body } = self; + y.key("Loop").yaml(body); + } + } impl Yamlify for While { fn yaml(&self, y: &mut Yamler) { let Self { cond, pass, fail } = self; diff --git a/cl-token/src/token_type.rs b/cl-token/src/token_type.rs index 5563c4b..12e5f2f 100644 --- a/cl-token/src/token_type.rs +++ b/cl-token/src/token_type.rs @@ -26,6 +26,7 @@ pub enum TokenKind { Impl, In, Let, + Loop, Mod, Mut, Pub, @@ -122,6 +123,7 @@ impl Display for TokenKind { TokenKind::Impl => "impl".fmt(f), TokenKind::In => "in".fmt(f), TokenKind::Let => "let".fmt(f), + TokenKind::Loop => "loop".fmt(f), TokenKind::Mod => "mod".fmt(f), TokenKind::Mut => "mut".fmt(f), TokenKind::Pub => "pub".fmt(f), @@ -158,6 +160,7 @@ impl FromStr for TokenKind { "impl" => Self::Impl, "in" => Self::In, "let" => Self::Let, + "loop" => Self::Loop, "mod" => Self::Mod, "mut" => Self::Mut, "pub" => Self::Pub, diff --git a/grammar.ebnf b/grammar.ebnf index 9bafe59..110c694 100644 --- a/grammar.ebnf +++ b/grammar.ebnf @@ -99,7 +99,7 @@ Index = Primary ('[' Indices ']')* ; Indices = (Expr ',')* Expr? ; Primary = Literal | Path | Array | ArrayRep | AddrOf - | Block | Group + | Block | Group | Loop | If | While | For | Break | Return | Continue; Literal = STRING | CHARACTER | FLOAT | INTEGER | Bool ; @@ -114,6 +114,7 @@ Block = '{' Stmt* '}'; Group = Empty | '(' (Expr | Tuple) ')' ; Tuple = (Expr ',')* Expr? ; +Loop = "loop" Block ; While = "while" Expr Block Else ; If = "if" Expr Block Else ; For = "for" Identifier "in" Expr Block Else ;