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; + } + """