diff --git a/compiler/cl-ast/src/ast.rs b/compiler/cl-ast/src/ast.rs index c1988e0..1b1b3a0 100644 --- a/compiler/cl-ast/src/ast.rs +++ b/compiler/cl-ast/src/ast.rs @@ -398,6 +398,10 @@ pub enum ExprKind { If(If), /// A [For] expression: `for` Pattern `in` [`Expr`] [`Block`] [`Else`]? For(For), + /// A [Break] expression: `break` [`Expr`]? + Break(Break), + /// A [Return] expression `return` [`Expr`]? + Return(Return), /// A continue expression: `continue` Continue, } @@ -478,8 +482,6 @@ pub enum UnaryKind { Not, /// A Loop expression: `loop` [`Block`] Loop, - Break, - Return, /// Unused At, /// Unused @@ -598,3 +600,15 @@ pub struct For { pub struct Else { pub body: Option>, } + +/// A [Break] expression: `break` [`Expr`]? +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Break { + pub body: Option>, +} + +/// A [Return] expression `return` [`Expr`]? +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Return { + pub body: Option>, +} diff --git a/compiler/cl-ast/src/ast_impl.rs b/compiler/cl-ast/src/ast_impl.rs index 8158519..e5aa446 100644 --- a/compiler/cl-ast/src/ast_impl.rs +++ b/compiler/cl-ast/src/ast_impl.rs @@ -451,6 +451,8 @@ mod display { ExprKind::While(v) => v.fmt(f), ExprKind::If(v) => v.fmt(f), ExprKind::For(v) => v.fmt(f), + ExprKind::Break(v) => v.fmt(f), + ExprKind::Return(v) => v.fmt(f), ExprKind::Continue => "continue".fmt(f), } } @@ -540,8 +542,6 @@ mod display { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { UnaryKind::Loop => "loop ", - UnaryKind::Break => "break ", - UnaryKind::Return => "return ", UnaryKind::Deref => "*", UnaryKind::Neg => "-", UnaryKind::Not => "!", @@ -684,6 +684,26 @@ mod display { } } } + + impl Display for Break { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "break")?; + match &self.body { + Some(body) => write!(f, " {body}"), + _ => Ok(()), + } + } + } + + impl Display for Return { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "return")?; + match &self.body { + Some(body) => write!(f, " {body}"), + _ => Ok(()), + } + } + } } mod convert { @@ -767,6 +787,8 @@ mod convert { While => ExprKind::While, If => ExprKind::If, For => ExprKind::For, + Break => ExprKind::Break, + Return => ExprKind::Return, } impl From for Literal { bool => Literal::Bool, diff --git a/compiler/cl-ast/src/ast_visitor/fold.rs b/compiler/cl-ast/src/ast_visitor/fold.rs index 92d3f34..c784348 100644 --- a/compiler/cl-ast/src/ast_visitor/fold.rs +++ b/compiler/cl-ast/src/ast_visitor/fold.rs @@ -368,6 +368,14 @@ pub trait Fold { let Else { body } = e; Else { body: body.map(|e| Box::new(self.fold_expr(*e))) } } + fn fold_break(&mut self, b: Break) -> Break { + let Break { body } = b; + Break { body: body.map(|e| Box::new(self.fold_expr(*e))) } + } + fn fold_return(&mut self, r: Return) -> Return { + let Return { body } = r; + Return { body: body.map(|e| Box::new(self.fold_expr(*e))) } + } } #[inline] @@ -539,6 +547,8 @@ pub fn or_fold_expr_kind(folder: &mut F, kind: ExprKind) -> Ex ExprKind::While(w) => ExprKind::While(folder.fold_while(w)), ExprKind::If(i) => ExprKind::If(folder.fold_if(i)), ExprKind::For(f) => ExprKind::For(folder.fold_for(f)), + ExprKind::Break(b) => ExprKind::Break(folder.fold_break(b)), + ExprKind::Return(r) => ExprKind::Return(folder.fold_return(r)), ExprKind::Continue => ExprKind::Continue, } } diff --git a/compiler/cl-ast/src/ast_visitor/visit.rs b/compiler/cl-ast/src/ast_visitor/visit.rs index 7519f12..5926cd2 100644 --- a/compiler/cl-ast/src/ast_visitor/visit.rs +++ b/compiler/cl-ast/src/ast_visitor/visit.rs @@ -322,6 +322,18 @@ pub trait Visit<'a>: Sized { self.visit_expr(body) } } + fn visit_break(&mut self, b: &'a Break) { + let Break { body } = b; + if let Some(body) = body { + self.visit_expr(body) + } + } + fn visit_return(&mut self, r: &'a Return) { + let Return { body } = r; + if let Some(body) = body { + self.visit_expr(body) + } + } fn visit_continue(&mut self) {} } @@ -461,6 +473,8 @@ pub fn or_visit_expr_kind<'a, V: Visit<'a>>(visitor: &mut V, e: &'a ExprKind) { ExprKind::While(w) => visitor.visit_while(w), ExprKind::If(i) => visitor.visit_if(i), ExprKind::For(f) => visitor.visit_for(f), + ExprKind::Break(b) => visitor.visit_break(b), + ExprKind::Return(r) => visitor.visit_return(r), ExprKind::Continue => visitor.visit_continue(), } } diff --git a/compiler/cl-ast/src/desugar/while_else.rs b/compiler/cl-ast/src/desugar/while_else.rs index d9d1a12..3a75f02 100644 --- a/compiler/cl-ast/src/desugar/while_else.rs +++ b/compiler/cl-ast/src/desugar/while_else.rs @@ -21,12 +21,9 @@ fn desugar_while(extents: Span, kind: ExprKind) -> ExprKind { match kind { // work backwards: fail -> break -> if -> loop ExprKind::While(While { cond, pass, fail: Else { body } }) => { - let Expr { extents, kind } = body - .map(|b| *b) - .unwrap_or(Expr { extents, kind: ExprKind::Empty }); // Preserve the else-expression's extents, if present, or use the parent's extents - let kind = ExprKind::Unary(Unary { kind: UnaryKind::Break, tail: kind.into() }); - let break_expr = Expr { extents, kind }; + let fail_span = body.as_ref().map(|body| body.extents).unwrap_or(extents); + let break_expr = Expr { extents: fail_span, kind: ExprKind::Break(Break { body }) }; let loop_body = If { cond, pass, fail: Else { body: Some(Box::new(break_expr)) } }; let loop_body = ExprKind::If(loop_body); diff --git a/compiler/cl-interpret/src/interpret.rs b/compiler/cl-interpret/src/interpret.rs index 779371a..a007ecb 100644 --- a/compiler/cl-interpret/src/interpret.rs +++ b/compiler/cl-interpret/src/interpret.rs @@ -154,6 +154,8 @@ impl Interpret for ExprKind { ExprKind::While(v) => v.interpret(env), ExprKind::If(v) => v.interpret(env), ExprKind::For(v) => v.interpret(env), + ExprKind::Break(v) => v.interpret(env), + ExprKind::Return(v) => v.interpret(env), ExprKind::Continue => Err(Error::Continue), } } @@ -328,8 +330,6 @@ impl Interpret for Unary { e => e?, }; }, - UnaryKind::Break => Err(Error::Break(tail.interpret(env)?)), - UnaryKind::Return => Err(Error::Return(tail.interpret(env)?)), UnaryKind::Deref => { let operand = tail.interpret(env)?; env.call("deref".into(), &[operand]) @@ -579,3 +579,23 @@ impl Interpret for Else { } } } +impl Interpret for Return { + fn interpret(&self, env: &mut Environment) -> IResult { + let Self { body } = self; + Err(Error::Return( + body.as_ref() + .map(|body| body.interpret(env)) + .unwrap_or(Ok(ConValue::Empty))?, + )) + } +} +impl Interpret for Break { + fn interpret(&self, env: &mut Environment) -> IResult { + let Self { body } = self; + Err(Error::Break( + body.as_ref() + .map(|body| body.interpret(env)) + .unwrap_or(Ok(ConValue::Empty))?, + )) + } +} diff --git a/compiler/cl-parser/src/parser.rs b/compiler/cl-parser/src/parser.rs index 75c88e7..9f66705 100644 --- a/compiler/cl-parser/src/parser.rs +++ b/compiler/cl-parser/src/parser.rs @@ -836,7 +836,6 @@ impl<'t> Parser<'t> { let parsing = Parsing::ExprKind; // Prefix expressions let mut head = match self.peek_kind(Parsing::Unary)? { - TokenKind::Semi => return Ok(ExprKind::Empty), // TODO: is this okay? literal_like!() => self.literal()?.into(), path_like!() => self.exprkind_pathlike()?, TokenKind::Amp | TokenKind::AmpAmp => self.addrof()?.into(), @@ -847,6 +846,8 @@ impl<'t> Parser<'t> { TokenKind::While => ExprKind::While(self.parse_while()?), TokenKind::If => ExprKind::If(self.parse_if()?), TokenKind::For => ExprKind::For(self.parse_for()?), + TokenKind::Break => ExprKind::Break(self.parse_break()?), + TokenKind::Return => ExprKind::Return(self.parse_return()?), TokenKind::Continue => { self.consume_peeked(); ExprKind::Continue @@ -1158,6 +1159,24 @@ impl<'t> Parser<'t> { } /// ## Control flow subexpressions impl<'t> Parser<'t> { + /// [Break] = `break` (*unconsumed* `;` | [Expr]) + pub fn parse_break(&mut self) -> PResult { + self.consume_peeked(); + Ok(Break { body: self.ret_body(Parsing::Break)? }) + } + /// [Return] = `return` (*unconsumed* `;` | [Expr]) + pub fn parse_return(&mut self) -> PResult { + self.consume_peeked(); + Ok(Return { body: self.ret_body(Parsing::Return)? }) + } + /// ret_body = (*unconsumed* `;` | [Expr]) + fn ret_body(&mut self, while_parsing: Parsing) -> PResult>> { + Ok(match self.peek_kind(while_parsing)? { + TokenKind::Semi => None, + _ => Some(self.expr()?.into()), + }) + } + /// [While] = `while` [Expr] [Block] [Else]? pub fn parse_while(&mut self) -> PResult { self.consume_peeked(); @@ -1272,7 +1291,7 @@ impl From for Precedence { fn from(value: UnaryKind) -> Self { use UnaryKind as Op; match value { - Op::Loop | Op::Break | Op::Return => Precedence::Assign, + Op::Loop => Precedence::Assign, Op::Deref | Op::Neg | Op::Not | Op::At | Op::Tilde => Precedence::Unary, } } @@ -1291,8 +1310,6 @@ macro operator($($name:ident ($takes:ident => $returns:ident) {$($t:ident => $p: operator! { from_prefix (TokenKind => UnaryKind) { Loop => Loop, - Break => Break, - Return => Return, Star => Deref, Minus => Neg, Bang => Not, diff --git a/compiler/cl-repl/examples/yaml.rs b/compiler/cl-repl/examples/yaml.rs index d14daed..9425f2a 100644 --- a/compiler/cl-repl/examples/yaml.rs +++ b/compiler/cl-repl/examples/yaml.rs @@ -409,6 +409,8 @@ pub mod yamlify { ExprKind::While(k) => k.yaml(y), ExprKind::If(k) => k.yaml(y), ExprKind::For(k) => k.yaml(y), + ExprKind::Break(k) => k.yaml(y), + ExprKind::Return(k) => k.yaml(y), ExprKind::Continue => { y.key("Continue"); } @@ -567,6 +569,18 @@ pub mod yamlify { .yaml(fail); } } + impl Yamlify for Break { + fn yaml(&self, y: &mut Yamler) { + let Self { body } = self; + y.key("Break").yaml(body); + } + } + impl Yamlify for Return { + fn yaml(&self, y: &mut Yamler) { + let Self { body } = self; + y.key("Return").yaml(body); + } + } impl Yamlify for Literal { fn yaml(&self, y: &mut Yamler) { y.value(format_args!("\"{self}\""));