mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Parse break
, continue
and return
expression
This commit is contained in:
parent
3a07603b66
commit
9c906f92c5
@ -390,6 +390,9 @@ impl Eval for Expr {
|
|||||||
Self::For(v) => v.eval(ctx),
|
Self::For(v) => v.eval(ctx),
|
||||||
Self::Import(v) => v.eval(ctx),
|
Self::Import(v) => v.eval(ctx),
|
||||||
Self::Include(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.
|
/// Try to mutably access the value an expression points to.
|
||||||
///
|
///
|
||||||
/// This only works if the expression is a valid lvalue.
|
/// This only works if the expression is a valid lvalue.
|
||||||
|
@ -548,7 +548,10 @@ impl NodeKind {
|
|||||||
| Self::ShowExpr
|
| Self::ShowExpr
|
||||||
| Self::WrapExpr
|
| Self::WrapExpr
|
||||||
| Self::ImportExpr
|
| 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
|
// This element always has to remain in the same column so better
|
||||||
// reparse the whole parent.
|
// reparse the whole parent.
|
||||||
|
@ -405,6 +405,9 @@ fn primary(p: &mut Parser, atomic: bool) -> ParseResult {
|
|||||||
Some(NodeKind::For) => for_expr(p),
|
Some(NodeKind::For) => for_expr(p),
|
||||||
Some(NodeKind::Import) => import_expr(p),
|
Some(NodeKind::Import) => import_expr(p),
|
||||||
Some(NodeKind::Include) => include_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(_, _)) => {
|
Some(NodeKind::Error(_, _)) => {
|
||||||
p.eat();
|
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.
|
/// Parse an identifier.
|
||||||
fn ident(p: &mut Parser) -> ParseResult {
|
fn ident(p: &mut Parser) -> ParseResult {
|
||||||
match p.peek() {
|
match p.peek() {
|
||||||
|
@ -231,6 +231,12 @@ pub enum Expr {
|
|||||||
Import(ImportExpr),
|
Import(ImportExpr),
|
||||||
/// An include expression: `include "chapter1.typ"`.
|
/// An include expression: `include "chapter1.typ"`.
|
||||||
Include(IncludeExpr),
|
Include(IncludeExpr),
|
||||||
|
/// A break expression: `break`.
|
||||||
|
Break(BreakExpr),
|
||||||
|
/// A continue expression: `continue`.
|
||||||
|
Continue(ContinueExpr),
|
||||||
|
/// A return expression: `return`.
|
||||||
|
Return(ReturnExpr),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypedNode for Expr {
|
impl TypedNode for Expr {
|
||||||
@ -256,6 +262,9 @@ impl TypedNode for Expr {
|
|||||||
NodeKind::ForExpr => node.cast().map(Self::For),
|
NodeKind::ForExpr => node.cast().map(Self::For),
|
||||||
NodeKind::ImportExpr => node.cast().map(Self::Import),
|
NodeKind::ImportExpr => node.cast().map(Self::Import),
|
||||||
NodeKind::IncludeExpr => node.cast().map(Self::Include),
|
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),
|
_ => node.cast().map(Self::Lit),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -283,6 +292,9 @@ impl TypedNode for Expr {
|
|||||||
Self::For(v) => v.as_red(),
|
Self::For(v) => v.as_red(),
|
||||||
Self::Import(v) => v.as_red(),
|
Self::Import(v) => v.as_red(),
|
||||||
Self::Include(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! {
|
node! {
|
||||||
/// An identifier.
|
/// An identifier.
|
||||||
Ident: NodeKind::Ident(_)
|
Ident: NodeKind::Ident(_)
|
||||||
|
@ -223,6 +223,9 @@ impl Category {
|
|||||||
NodeKind::ImportExpr => None,
|
NodeKind::ImportExpr => None,
|
||||||
NodeKind::ImportItems => None,
|
NodeKind::ImportItems => None,
|
||||||
NodeKind::IncludeExpr => None,
|
NodeKind::IncludeExpr => None,
|
||||||
|
NodeKind::BreakExpr => None,
|
||||||
|
NodeKind::ContinueExpr => None,
|
||||||
|
NodeKind::ReturnExpr => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -689,6 +689,12 @@ pub enum NodeKind {
|
|||||||
ImportItems,
|
ImportItems,
|
||||||
/// An include expression: `include "chapter1.typ"`.
|
/// An include expression: `include "chapter1.typ"`.
|
||||||
IncludeExpr,
|
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 line comment, two slashes followed by inner contents, terminated with
|
||||||
/// a newline: `//<str>\n`.
|
/// a newline: `//<str>\n`.
|
||||||
LineComment,
|
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> {
|
pub fn mode(&self) -> Option<TokenMode> {
|
||||||
match self {
|
match self {
|
||||||
Self::Markup(_)
|
Self::Markup(_)
|
||||||
@ -780,6 +786,9 @@ impl NodeKind {
|
|||||||
| Self::Block
|
| Self::Block
|
||||||
| Self::Ident(_)
|
| Self::Ident(_)
|
||||||
| Self::LetExpr
|
| Self::LetExpr
|
||||||
|
| Self::SetExpr
|
||||||
|
| Self::ShowExpr
|
||||||
|
| Self::WrapExpr
|
||||||
| Self::IfExpr
|
| Self::IfExpr
|
||||||
| Self::WhileExpr
|
| Self::WhileExpr
|
||||||
| Self::ForExpr
|
| Self::ForExpr
|
||||||
@ -897,6 +906,9 @@ impl NodeKind {
|
|||||||
Self::ImportExpr => "`import` expression",
|
Self::ImportExpr => "`import` expression",
|
||||||
Self::ImportItems => "import items",
|
Self::ImportItems => "import items",
|
||||||
Self::IncludeExpr => "`include` expression",
|
Self::IncludeExpr => "`include` expression",
|
||||||
|
Self::BreakExpr => "`break` expression",
|
||||||
|
Self::ContinueExpr => "`continue` expression",
|
||||||
|
Self::ReturnExpr => "`return` expression",
|
||||||
Self::LineComment => "line comment",
|
Self::LineComment => "line comment",
|
||||||
Self::BlockComment => "block comment",
|
Self::BlockComment => "block comment",
|
||||||
Self::Error(_, _) => "parse error",
|
Self::Error(_, _) => "parse error",
|
||||||
|
@ -234,6 +234,9 @@ impl Pretty for Expr {
|
|||||||
Self::For(v) => v.pretty(p),
|
Self::For(v) => v.pretty(p),
|
||||||
Self::Import(v) => v.pretty(p),
|
Self::Import(v) => v.pretty(p),
|
||||||
Self::Include(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 {
|
impl Pretty for Ident {
|
||||||
fn pretty(&self, p: &mut Printer) {
|
fn pretty(&self, p: &mut Printer) {
|
||||||
p.push_str(self);
|
p.push_str(self);
|
||||||
@ -681,5 +706,9 @@ mod tests {
|
|||||||
roundtrip("#for k, x in y {z}");
|
roundtrip("#for k, x in y {z}");
|
||||||
roundtrip("#import * from \"file.typ\"");
|
roundtrip("#import * from \"file.typ\"");
|
||||||
roundtrip("#include \"chapter1.typ\"");
|
roundtrip("#include \"chapter1.typ\"");
|
||||||
|
roundtrip("{break}");
|
||||||
|
roundtrip("{continue}");
|
||||||
|
roundtrip("{return}");
|
||||||
|
roundtrip("{return x + 1}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
tests/typ/code/break-continue.typ
Normal file
14
tests/typ/code/break-continue.typ
Normal 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
10
tests/typ/code/return.typ
Normal 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)
|
Loading…
x
Reference in New Issue
Block a user