diff --git a/civet.dev/reference.md b/civet.dev/reference.md
index 43d9ce77..bb589b48 100644
--- a/civet.dev/reference.md
+++ b/civet.dev/reference.md
@@ -1744,6 +1744,16 @@ found :=
process item
+
+function process(lists)
+ :outer for list of lists
+ for item of list
+ if item is "abort"
+ break outer with item
+ if item is "length"
+ continue :outer with list.length
+
+
## Other Blocks
### Try Blocks
diff --git a/source/parser.hera b/source/parser.hera
index f3157499..d69a76f8 100644
--- a/source/parser.hera
+++ b/source/parser.hera
@@ -4224,7 +4224,11 @@ LabelledStatement
Label
# NOTE: `:label` instead of `label:` to not clash with implicit object literal
Colon:colon Identifier:id Whitespace:w ->
- return [ id, colon, w ]
+ return {
+ type: "Label",
+ name: id.name,
+ children: [ id, colon, w ]
+ }
# Argument to break/continue, which can include colon or not in input,
# but should not have colon in output
@@ -5058,6 +5062,7 @@ KeywordStatement
})
return {
type: "BreakStatement",
+ label: $2?.[1],
with: $3?.[2],
children,
}
@@ -5082,6 +5087,7 @@ KeywordStatement
})
return {
type: "ContinueStatement",
+ label: $2?.[1],
with: $3?.[2],
children,
}
diff --git a/source/parser/function.civet b/source/parser/function.civet
index ec006e17..51385d9e 100644
--- a/source/parser/function.civet
+++ b/source/parser/function.civet
@@ -10,7 +10,7 @@ import type {
IterationExpression
IterationFamily
IterationStatement
- LabeledStatement
+ LabelledStatement
Parameter
ParametersNode
StatementTuple
@@ -268,7 +268,7 @@ function assignResults(node: StatementTuple[] | ASTNode, collect: (node: ASTNode
outer := exp
{type} .= exp
if type is "LabelledStatement"
- exp = (exp as LabeledStatement).statement
+ exp = exp.statement
{type} = exp
switch type
@@ -369,7 +369,7 @@ function insertReturn(node: ASTNode, outerNode: ASTNode = node): void
outer := exp
{type} .= exp
if type is "LabelledStatement"
- exp = (exp as LabeledStatement).statement
+ exp = exp.statement
{type} = exp
switch type
@@ -464,14 +464,20 @@ function processBreakContinueWith(statement: IterationStatement | ForStatement):
// break with overwrites the results of the loop
// continue with appends to the results of the loop
if control.with
- // Verify there wasn't another loop or switch in between
- {ancestor} := findAncestor control,
- (s: ASTNodeObject): s is IterationStatement | ForStatement | SwitchStatement => (or)
- s is statement
- s.type is "IterationStatement"
- s.type is "ForStatement"
- s.type is "SwitchStatement" and control.type is "BreakStatement"
- continue unless ancestor is statement
+ if control.label
+ continue unless statement.parent is like {
+ type: "LabelledStatement"
+ label: { name: ^control.label.name }
+ }
+ else
+ // Verify there wasn't another loop or switch in between
+ {ancestor} := findAncestor control,
+ (s: ASTNodeObject): s is IterationStatement | ForStatement | SwitchStatement => (or)
+ s is statement
+ s.type is "IterationStatement"
+ s.type is "ForStatement"
+ s.type is "SwitchStatement" and control.type is "BreakStatement"
+ continue unless ancestor is statement
control.children.unshift
if control.type is "BreakStatement"
diff --git a/source/parser/types.civet b/source/parser/types.civet
index 15f4c605..56bfdae4 100644
--- a/source/parser/types.civet
+++ b/source/parser/types.civet
@@ -24,7 +24,7 @@ export type StatementNode =
| ForStatement
| IfStatement
| IterationStatement
- | LabeledStatement
+ | LabelledStatement
| ReturnStatement
| SwitchStatement
| ThrowStatement
@@ -79,6 +79,7 @@ export type OtherNode =
| FinallyClause
| Index
| Initializer
+ | Label
| ObjectBindingPattern
| Parameter
| ParametersNode
@@ -289,6 +290,7 @@ export type BreakStatement
children: Children
parent?: Parent
with: ASTNode?
+ label: Label?
export type ComptimeStatement
type: "ComptimeStatement"
@@ -308,6 +310,7 @@ export type ContinueStatement
parent?: Parent
special?: "switch"
with: ASTNode?
+ label: Label?
export type DoStatement
type: "DoStatement"
@@ -360,13 +363,19 @@ export type DefaultClause
children: Children
block: BlockStatement
-export type LabeledStatement
- type: "LabeledStatement"
- label: ASTNode
+export type LabelledStatement
+ type: "LabelledStatement"
+ label: Label
statement: ASTNodeBase
children: Children
parent?: Parent
+export type Label
+ type: "Label"
+ children: Children
+ parent?: Parent
+ name: string
+
export type AccessStart
type: "AccessStart"
children: Children
diff --git a/source/parser/util.civet b/source/parser/util.civet
index 7d454e85..911110b3 100644
--- a/source/parser/util.civet
+++ b/source/parser/util.civet
@@ -128,7 +128,7 @@ statementTypes := new Set [
"ForStatement"
"IfStatement"
"IterationStatement"
- "LabeledStatement"
+ "LabelledStatement"
"ReturnStatement"
"SwitchStatement"
"ThrowStatement"
diff --git a/test/for.civet b/test/for.civet
index e81c523b..2b86b013 100644
--- a/test/for.civet
+++ b/test/for.civet
@@ -826,3 +826,32 @@ describe "for", ->
};return results;
}
"""
+
+ testCase """
+ labelled break/continue with
+ ---
+ function f(arrays)
+ :outer for array of arrays
+ :inner for item of array
+ break :outer with [] if item is "ABORT"
+ continue outer with [] if item is "SKIP"
+ break inner with [] if item is "abort"
+ continue :outer with [] if item is "skip"
+ break with [] if item is "abort"
+ continue with [] if item is "skip"
+ item * item
+ ---
+ function f(arrays) {
+ let results;results=[];outer: for (const array of arrays) {
+ let results1;results1=[];inner: for (const item of array) {
+ if (item === "ABORT") { results = [];break outer }
+ if (item === "SKIP") { results.push([]);continue outer }
+ if (item === "abort") { results1 = [];break inner }
+ if (item === "skip") { results.push([]);continue outer }
+ if (item === "abort") { results1 = [];break }
+ if (item === "skip") { results1.push([]);continue }
+ results1.push(item * item)
+ }results.push(results1)
+ };return results;
+ }
+ """