Parse break, continue and return expression

This commit is contained in:
Laurenz 2022-01-28 21:01:05 +01:00
parent 3a07603b66
commit 9c906f92c5
9 changed files with 164 additions and 2 deletions

View File

@ -390,6 +390,9 @@ impl Eval for Expr {
Self::For(v) => v.eval(ctx),
Self::Import(v) => v.eval(ctx),
Self::Include(v) => v.eval(ctx),
Self::Break(v) => v.eval(ctx),
Self::Continue(v) => v.eval(ctx),
Self::Return(v) => v.eval(ctx),
}
}
}
@ -905,6 +908,30 @@ impl Eval for IncludeExpr {
}
}
impl Eval for BreakExpr {
type Output = Value;
fn eval(&self, _: &mut EvalContext) -> TypResult<Self::Output> {
Err("break is not yet implemented").at(self.span())
}
}
impl Eval for ContinueExpr {
type Output = Value;
fn eval(&self, _: &mut EvalContext) -> TypResult<Self::Output> {
Err("continue is not yet implemented").at(self.span())
}
}
impl Eval for ReturnExpr {
type Output = Value;
fn eval(&self, _: &mut EvalContext) -> TypResult<Self::Output> {
Err("return is not yet implemented").at(self.span())
}
}
/// Try to mutably access the value an expression points to.
///
/// This only works if the expression is a valid lvalue.

View File

@ -548,7 +548,10 @@ impl NodeKind {
| Self::ShowExpr
| Self::WrapExpr
| Self::ImportExpr
| Self::IncludeExpr => SuccessionRule::AtomicPrimary,
| Self::IncludeExpr
| Self::BreakExpr
| Self::ContinueExpr
| Self::ReturnExpr => SuccessionRule::AtomicPrimary,
// This element always has to remain in the same column so better
// reparse the whole parent.

View File

@ -405,6 +405,9 @@ fn primary(p: &mut Parser, atomic: bool) -> ParseResult {
Some(NodeKind::For) => for_expr(p),
Some(NodeKind::Import) => import_expr(p),
Some(NodeKind::Include) => include_expr(p),
Some(NodeKind::Break) => break_expr(p),
Some(NodeKind::Continue) => continue_expr(p),
Some(NodeKind::Return) => return_expr(p),
Some(NodeKind::Error(_, _)) => {
p.eat();
@ -833,6 +836,33 @@ fn include_expr(p: &mut Parser) -> ParseResult {
})
}
/// Parse a break expression.
fn break_expr(p: &mut Parser) -> ParseResult {
p.perform(NodeKind::BreakExpr, |p| {
p.eat_assert(&NodeKind::Break);
Ok(())
})
}
/// Parse a continue expression.
fn continue_expr(p: &mut Parser) -> ParseResult {
p.perform(NodeKind::ContinueExpr, |p| {
p.eat_assert(&NodeKind::Continue);
Ok(())
})
}
/// Parse a return expression.
fn return_expr(p: &mut Parser) -> ParseResult {
p.perform(NodeKind::ReturnExpr, |p| {
p.eat_assert(&NodeKind::Return);
if !p.eof() {
expr(p)?;
}
Ok(())
})
}
/// Parse an identifier.
fn ident(p: &mut Parser) -> ParseResult {
match p.peek() {

View File

@ -231,6 +231,12 @@ pub enum Expr {
Import(ImportExpr),
/// An include expression: `include "chapter1.typ"`.
Include(IncludeExpr),
/// A break expression: `break`.
Break(BreakExpr),
/// A continue expression: `continue`.
Continue(ContinueExpr),
/// A return expression: `return`.
Return(ReturnExpr),
}
impl TypedNode for Expr {
@ -256,6 +262,9 @@ impl TypedNode for Expr {
NodeKind::ForExpr => node.cast().map(Self::For),
NodeKind::ImportExpr => node.cast().map(Self::Import),
NodeKind::IncludeExpr => node.cast().map(Self::Include),
NodeKind::BreakExpr => node.cast().map(Self::Break),
NodeKind::ContinueExpr => node.cast().map(Self::Continue),
NodeKind::ReturnExpr => node.cast().map(Self::Return),
_ => node.cast().map(Self::Lit),
}
}
@ -283,6 +292,9 @@ impl TypedNode for Expr {
Self::For(v) => v.as_red(),
Self::Import(v) => v.as_red(),
Self::Include(v) => v.as_red(),
Self::Break(v) => v.as_red(),
Self::Continue(v) => v.as_red(),
Self::Return(v) => v.as_red(),
}
}
}
@ -1041,6 +1053,28 @@ impl IncludeExpr {
}
}
node! {
/// A break expression: `break`.
BreakExpr
}
node! {
/// A continue expression: `continue`.
ContinueExpr
}
node! {
/// A return expression: `return x + 1`.
ReturnExpr
}
impl ReturnExpr {
/// The expression to return.
pub fn body(&self) -> Option<Expr> {
self.0.cast_last_child()
}
}
node! {
/// An identifier.
Ident: NodeKind::Ident(_)

View File

@ -223,6 +223,9 @@ impl Category {
NodeKind::ImportExpr => None,
NodeKind::ImportItems => None,
NodeKind::IncludeExpr => None,
NodeKind::BreakExpr => None,
NodeKind::ContinueExpr => None,
NodeKind::ReturnExpr => None,
}
}

View File

@ -689,6 +689,12 @@ pub enum NodeKind {
ImportItems,
/// An include expression: `include "chapter1.typ"`.
IncludeExpr,
/// A break expression: `break`.
BreakExpr,
/// A continue expression: `continue`.
ContinueExpr,
/// A return expression: `return x + 1`.
ReturnExpr,
/// A line comment, two slashes followed by inner contents, terminated with
/// a newline: `//<str>\n`.
LineComment,
@ -755,7 +761,7 @@ impl NodeKind {
}
}
/// Whether this token appears in Markup.
/// Which mode this token can appear in, in both if `None`.
pub fn mode(&self) -> Option<TokenMode> {
match self {
Self::Markup(_)
@ -780,6 +786,9 @@ impl NodeKind {
| Self::Block
| Self::Ident(_)
| Self::LetExpr
| Self::SetExpr
| Self::ShowExpr
| Self::WrapExpr
| Self::IfExpr
| Self::WhileExpr
| Self::ForExpr
@ -897,6 +906,9 @@ impl NodeKind {
Self::ImportExpr => "`import` expression",
Self::ImportItems => "import items",
Self::IncludeExpr => "`include` expression",
Self::BreakExpr => "`break` expression",
Self::ContinueExpr => "`continue` expression",
Self::ReturnExpr => "`return` expression",
Self::LineComment => "line comment",
Self::BlockComment => "block comment",
Self::Error(_, _) => "parse error",

View File

@ -234,6 +234,9 @@ impl Pretty for Expr {
Self::For(v) => v.pretty(p),
Self::Import(v) => v.pretty(p),
Self::Include(v) => v.pretty(p),
Self::Break(v) => v.pretty(p),
Self::Continue(v) => v.pretty(p),
Self::Return(v) => v.pretty(p),
}
}
}
@ -547,6 +550,28 @@ impl Pretty for IncludeExpr {
}
}
impl Pretty for BreakExpr {
fn pretty(&self, p: &mut Printer) {
p.push_str("break");
}
}
impl Pretty for ContinueExpr {
fn pretty(&self, p: &mut Printer) {
p.push_str("continue");
}
}
impl Pretty for ReturnExpr {
fn pretty(&self, p: &mut Printer) {
p.push_str("return");
if let Some(body) = self.body() {
p.push(' ');
body.pretty(p);
}
}
}
impl Pretty for Ident {
fn pretty(&self, p: &mut Printer) {
p.push_str(self);
@ -681,5 +706,9 @@ mod tests {
roundtrip("#for k, x in y {z}");
roundtrip("#import * from \"file.typ\"");
roundtrip("#include \"chapter1.typ\"");
roundtrip("{break}");
roundtrip("{continue}");
roundtrip("{return}");
roundtrip("{return x + 1}");
}
}

View File

@ -0,0 +1,14 @@
// Test break and continue in loops.
// Ref: false
---
#for i in range(10) {
if i > 5 {
// Error: 5-10 break is not yet implemented
break
}
}
---
// Error: 1-10 unexpected keyword `continue`
#continue

10
tests/typ/code/return.typ Normal file
View File

@ -0,0 +1,10 @@
// Test return out of functions.
// Ref: false
---
#let f(x) = {
// Error: 3-15 return is not yet implemented
return x + 1
}
#f(1)