From ceaf256c6c64d3f55dc30f466b1d3b5bed19fdbd Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Wed, 15 May 2024 11:53:59 +0200 Subject: [PATCH] feat: Loop labels closes #199 --- src/Ast.zig | 7 +- src/Codegen.zig | 232 +++++++++++++++++------------ src/Parser.zig | 335 ++++++++++++++++++++++++++++++------------ src/Reporter.zig | 1 + tests/072-labels.buzz | 14 ++ 5 files changed, 394 insertions(+), 195 deletions(-) create mode 100644 tests/072-labels.buzz diff --git a/src/Ast.zig b/src/Ast.zig index 6be6d79a..a8f3e8b3 100644 --- a/src/Ast.zig +++ b/src/Ast.zig @@ -157,9 +157,9 @@ pub const Node = struct { Block: []Node.Index, BlockExpression: []Node.Index, Boolean: bool, - Break: void, + Break: ?Node.Index, Call: Call, - Continue: void, + Continue: ?Node.Index, Dot: Dot, DoUntil: WhileDoUntil, Enum: Enum, @@ -458,6 +458,7 @@ pub const For = struct { condition: Node.Index, post_loop: []Node.Index, body: Node.Index, + label: ?TokenIndex, }; pub const ForEach = struct { @@ -466,6 +467,7 @@ pub const ForEach = struct { value: Node.Index, body: Node.Index, key_omitted: bool, + label: ?TokenIndex, }; pub const Function = struct { @@ -697,6 +699,7 @@ pub const VarDeclaration = struct { pub const WhileDoUntil = struct { condition: Node.Index, body: Node.Index, + label: ?TokenIndex, }; pub const Zdef = struct { diff --git a/src/Codegen.zig b/src/Codegen.zig index 9cfc4166..30943983 100644 --- a/src/Codegen.zig +++ b/src/Codegen.zig @@ -38,9 +38,16 @@ pub const Frame = struct { const NodeGen = *const fn ( self: *Self, node: Ast.Node.Index, - breaks: ?*std.ArrayList(usize), + breaks: ?*Breaks, ) Error!?*obj.ObjFunction; +const Break = struct { + ip: usize, // The op code will tell us if this is a continue or a break statement + label_node: ?Ast.Node.Index = null, +}; + +const Breaks = std.ArrayList(Break); + current: ?*Frame = null, ast: Ast, gc: *GarbageCollector, @@ -193,7 +200,7 @@ pub fn emitOpCode(self: *Self, location: Ast.TokenIndex, code: Chunk.OpCode) !vo pub fn emitLoop(self: *Self, location: Ast.TokenIndex, loop_start: usize) !void { const offset: usize = self.currentCode() - loop_start + 1; - if (offset > 16777215) { + if (offset > std.math.maxInt(u24)) { self.reportError(.loop_body_too_large, "Loop body too large."); } @@ -214,7 +221,7 @@ pub fn patchJumpOrLoop(self: *Self, offset: usize, loop_start: ?usize) !void { if (code == .OP_LOOP) { // Patching a continue statement std.debug.assert(loop_start != null); const loop_offset: usize = offset - loop_start.? + 1; - if (loop_offset > 16777215) { + if (loop_offset > std.math.maxInt(u24)) { self.reportError(.loop_body_too_large, "Loop body too large."); } @@ -225,6 +232,18 @@ pub fn patchJumpOrLoop(self: *Self, offset: usize, loop_start: ?usize) !void { } } +fn patchBreaks(self: *Self, breaks: *Breaks, previous_breaks: ?*Breaks, loop_node: Ast.Node.Index, loop_start: usize) !void { + for (breaks.items) |brk| { + if (brk.label_node == null or brk.label_node.? == loop_node) { + try self.patchJumpOrLoop(brk.ip, loop_start); + } else if (previous_breaks) |brks| { // The break/continue if for an upper scope + try brks.append(brk); + } else { + unreachable; // Should not happen: we search for the scope during parsing + } + } +} + pub fn patchJump(self: *Self, offset: usize) void { std.debug.assert(offset < self.currentCode()); @@ -368,7 +387,7 @@ fn endScope(self: *Self, node: Ast.Node.Index) Error!void { } } -inline fn generateNode(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +inline fn generateNode(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { if (self.synchronize(node)) { return null; } @@ -392,7 +411,7 @@ fn nodeValue(self: *Self, node: Ast.Node.Index) Error!?Value { return value.*; } -fn generateAs(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateAs(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const locations = self.ast.nodes.items(.location); const node_location = locations[node]; const components = self.ast.nodes.items(.components)[node].As; @@ -429,7 +448,7 @@ fn generateAs(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) return null; } -fn generateAsyncCall(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateAsyncCall(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const locations = self.ast.nodes.items(.location); const type_defs = self.ast.nodes.items(.type_def); const node_location = locations[node]; @@ -451,7 +470,7 @@ fn generateAsyncCall(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList( return null; } -fn generateBinary(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateBinary(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const components = self.ast.nodes.items(.components)[node].Binary; const locations = self.ast.nodes.items(.location); @@ -798,7 +817,7 @@ fn generateBinary(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usi return null; } -fn generateBlock(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateBlock(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const tags = self.ast.nodes.items(.tag); var seen_return = false; @@ -828,7 +847,7 @@ fn generateBlock(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usiz return null; } -fn generateBlockExpression(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateBlockExpression(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { for (self.ast.nodes.items(.components)[node].BlockExpression) |statement| { _ = try self.generateNode(statement, breaks); } @@ -839,7 +858,7 @@ fn generateBlockExpression(self: *Self, node: Ast.Node.Index, breaks: ?*std.Arra return null; } -fn generateOut(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateOut(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { _ = try self.generateNode(self.ast.nodes.items(.components)[node].Out, breaks); try self.patchOptJumps(node); @@ -848,7 +867,7 @@ fn generateOut(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize) return null; } -fn generateBoolean(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateBoolean(self: *Self, node: Ast.Node.Index, _: Breaks) Error!?*obj.ObjFunction { try self.emitOpCode( self.ast.nodes.items(.location)[node], if (self.ast.nodes.items(.components)[node].Boolean) .OP_TRUE else .OP_FALSE, @@ -860,14 +879,35 @@ fn generateBoolean(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) return null; } -fn generateBreak(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateBreak(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { // Close scope(s), then jump try self.endScope(node); try breaks.?.append( - try self.emitJump( - self.ast.nodes.items(.location)[node], - .OP_JUMP, - ), + .{ + .ip = try self.emitJump( + self.ast.nodes.items(.location)[node], + .OP_JUMP, + ), + .label_node = self.ast.nodes.items(.components)[node].Break, + }, + ); + + try self.patchOptJumps(node); + + return null; +} + +fn generateContinue(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { + // Close scope(s), then jump + try self.endScope(node); + try breaks.?.append( + .{ + .ip = try self.emitJump( + self.ast.nodes.items(.location)[node], + .OP_LOOP, + ), + .label_node = self.ast.nodes.items(.components)[node].Continue, + }, ); try self.patchOptJumps(node); @@ -875,7 +915,7 @@ fn generateBreak(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usiz return null; } -fn generateCall(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateCall(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const type_defs = self.ast.nodes.items(.type_def); const locations = self.ast.nodes.items(.location); const node_components = self.ast.nodes.items(.components); @@ -1352,22 +1392,7 @@ fn generateCall(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize return null; } -fn generateContinue(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { - // Close scope(s), then jump - try self.endScope(node); - try breaks.?.append( - try self.emitJump( - self.ast.nodes.items(.location)[node], - .OP_LOOP, - ), - ); - - try self.patchOptJumps(node); - - return null; -} - -fn generateDot(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateDot(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const node_components = self.ast.nodes.items(.components); const type_defs = self.ast.nodes.items(.type_def); const locations = self.ast.nodes.items(.location); @@ -1550,7 +1575,7 @@ fn generateDot(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize) return null; } -fn generateDoUntil(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateDoUntil(self: *Self, node: Ast.Node.Index, breaks: Breaks) Error!?*obj.ObjFunction { const locations = self.ast.nodes.items(.location); const type_defs = self.ast.nodes.items(.type_def); const node_components = self.ast.nodes.items(.components); @@ -1558,10 +1583,10 @@ fn generateDoUntil(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) const loop_start = self.currentCode(); - var breaks = std.ArrayList(usize).init(self.gc.allocator); - defer breaks.deinit(); + var lbreaks = std.ArrayList(usize).init(self.gc.allocator); + defer lbreaks.deinit(); - _ = try self.generateNode(components.body, &breaks); + _ = try self.generateNode(components.body, &lbreaks); const condition_type_def = type_defs[components.condition].?; @@ -1577,7 +1602,7 @@ fn generateDoUntil(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) ); } - _ = try self.generateNode(components.condition, &breaks); + _ = try self.generateNode(components.condition, &lbreaks); try self.emitOpCode(locations[node], .OP_NOT); const exit_jump = try self.emitJump(locations[node], .OP_JUMP_IF_FALSE); @@ -1589,9 +1614,12 @@ fn generateDoUntil(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) try self.emitOpCode(locations[node], .OP_POP); // Pop condition // Patch breaks - for (breaks.items) |jump| { - try self.patchJumpOrLoop(jump, loop_start); - } + try self.patchBreaks( + lbreaks, + breaks, + node, + loop_start, + ); try self.patchOptJumps(node); try self.endScope(node); @@ -1599,7 +1627,7 @@ fn generateDoUntil(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) return null; } -fn generateEnum(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateEnum(self: *Self, node: Ast.Node.Index, _: Breaks) Error!?*obj.ObjFunction { const locations = self.ast.nodes.items(.location); const type_defs = self.ast.nodes.items(.type_def); const node_components = self.ast.nodes.items(.components); @@ -1661,7 +1689,7 @@ fn generateEnum(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) Er return null; } -fn generateExport(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateExport(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const components = self.ast.nodes.items(.components)[node].Export; if (components.declaration) |decl| { @@ -1674,7 +1702,7 @@ fn generateExport(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usi return null; } -fn generateExpression(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateExpression(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const locations = self.ast.nodes.items(.location); const components = self.ast.nodes.items(.components); const expr = components[node].Expression; @@ -1713,7 +1741,7 @@ fn generateExpression(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList return null; } -fn generateFloat(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateFloat(self: *Self, node: Ast.Node.Index, _: Breaks) Error!?*obj.ObjFunction { try self.emitConstant( self.ast.nodes.items(.location)[node], try self.ast.toValue(node, self.gc), @@ -1725,7 +1753,7 @@ fn generateFloat(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) E return null; } -fn generateFor(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateFor(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const locations = self.ast.nodes.items(.location); const type_defs = self.ast.nodes.items(.type_def); const node_components = self.ast.nodes.items(.components); @@ -1781,7 +1809,7 @@ fn generateFor(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize) self.patchJump(body_jump); - var lbreaks: std.ArrayList(usize) = std.ArrayList(usize).init(self.gc.allocator); + var lbreaks = Breaks.init(self.gc.allocator); defer lbreaks.deinit(); _ = try self.generateNode(components.body, &lbreaks); @@ -1793,9 +1821,12 @@ fn generateFor(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize) try self.emitOpCode(locations[node], .OP_POP); // Pop condition // Patch breaks - for (lbreaks.items) |jump| { - try self.patchJumpOrLoop(jump, loop_start); - } + try self.patchBreaks( + lbreaks, + breaks, + node, + loop_start, + ); try self.patchOptJumps(node); try self.endScope(node); @@ -1805,7 +1836,7 @@ fn generateFor(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize) return null; } -fn generateForceUnwrap(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateForceUnwrap(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const locations = self.ast.nodes.items(.location); const components = self.ast.nodes.items(.components)[node].ForceUnwrap; @@ -1833,7 +1864,7 @@ fn generateForceUnwrap(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayLis return null; } -fn generateForEach(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateForEach(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const node_components = self.ast.nodes.items(.components); const locations = self.ast.nodes.items(.location); const type_defs = self.ast.nodes.items(.type_def); @@ -2047,7 +2078,7 @@ fn generateForEach(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(us const exit_jump: usize = try self.emitJump(locations[node], .OP_JUMP_IF_FALSE); try self.emitOpCode(locations[node], .OP_POP); // Pop condition result - var lbreaks: std.ArrayList(usize) = std.ArrayList(usize).init(self.gc.allocator); + var lbreaks = Breaks.init(self.gc.allocator); defer lbreaks.deinit(); _ = try self.generateNode(components.body, &lbreaks); @@ -2060,9 +2091,12 @@ fn generateForEach(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(us try self.emitOpCode(locations[node], .OP_POP); // Pop condition result // Patch breaks - for (lbreaks.items) |jump| { - try self.patchJumpOrLoop(jump, loop_start); - } + try self.patchBreaks( + lbreaks, + breaks, + node, + loop_start, + ); try self.patchOptJumps(node); // Should have key, [value,] iterable to pop @@ -2076,7 +2110,7 @@ fn generateForEach(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(us return null; } -fn generateFunction(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateFunction(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const node_components = self.ast.nodes.items(.components); const type_defs = self.ast.nodes.items(.type_def); const locations = self.ast.nodes.items(.location); @@ -2201,11 +2235,12 @@ fn generateFunction(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(u // If we're being imported, put all globals on the stack if (components.import_root) { - if (components.entry.?.exported_count > 16777215) { - self.reporter.reportErrorAt( + if (components.entry.?.exported_count > std.math.maxInt(u24)) { + self.reporter.reportErrorFmt( .export_count, self.ast.tokens.get(locations[node]), - "Can't export more than 16777215 values.", + "Can't export more than {} values.", + .{std.math.maxInt(u24)}, ); } @@ -2286,7 +2321,7 @@ fn generateFunction(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(u return current_function; } -fn generateFunDeclaration(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateFunDeclaration(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const node_components = self.ast.nodes.items(.components); const components = node_components[node].FunDeclaration; @@ -2306,7 +2341,7 @@ fn generateFunDeclaration(self: *Self, node: Ast.Node.Index, breaks: ?*std.Array return null; } -fn generateGenericResolve(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateGenericResolve(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const type_def = self.ast.nodes.items(.type_def)[node].?; const expr = self.ast.nodes.items(.components)[node].GenericResolve; const node_location = self.ast.nodes.items(.location)[node]; @@ -2397,7 +2432,7 @@ fn generateGenericResolve(self: *Self, node: Ast.Node.Index, breaks: ?*std.Array return null; } -fn generateGrouping(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateGrouping(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const components = self.ast.nodes.items(.components); const expr = components[node].Grouping; @@ -2409,7 +2444,7 @@ fn generateGrouping(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(u return null; } -fn generateIf(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateIf(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const type_defs = self.ast.nodes.items(.type_def); const locations = self.ast.nodes.items(.location); const node_components = self.ast.nodes.items(.components); @@ -2527,7 +2562,7 @@ fn generateIf(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) return null; } -fn generateImport(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateImport(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const components = self.ast.nodes.items(.components)[node].Import; const location = self.ast.nodes.items(.location)[node]; @@ -2546,7 +2581,7 @@ fn generateImport(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usi return null; } -fn generateInteger(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateInteger(self: *Self, node: Ast.Node.Index, _: Breaks) Error!?*obj.ObjFunction { try self.emitConstant( self.ast.nodes.items(.location)[node], try self.ast.toValue(node, self.gc), @@ -2558,7 +2593,7 @@ fn generateInteger(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) return null; } -fn generateIs(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateIs(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const components = self.ast.nodes.items(.components)[node].Is; const location = self.ast.nodes.items(.location)[node]; const constant = try self.ast.toValue(components.constant, self.gc); @@ -2586,7 +2621,7 @@ fn generateIs(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) return null; } -fn generateList(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateList(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const locations = self.ast.nodes.items(.location); const components = self.ast.nodes.items(.components)[node].List; const type_defs = self.ast.nodes.items(.type_def); @@ -2630,7 +2665,7 @@ fn generateList(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize return null; } -fn generateMap(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateMap(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const locations = self.ast.nodes.items(.location); const components = self.ast.nodes.items(.components)[node].Map; const type_defs = self.ast.nodes.items(.type_def); @@ -2700,7 +2735,7 @@ fn generateMap(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize) return null; } -fn generateNamedVariable(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateNamedVariable(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const components = self.ast.nodes.items(.components)[node].NamedVariable; const locations = self.ast.nodes.items(.location); const type_defs = self.ast.nodes.items(.type_def); @@ -2761,7 +2796,7 @@ fn generateNamedVariable(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayL return null; } -fn generateNull(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateNull(self: *Self, node: Ast.Node.Index, _: Breaks) Error!?*obj.ObjFunction { try self.emitOpCode(self.ast.nodes.items(.location)[node], .OP_NULL); try self.patchOptJumps(node); @@ -2770,7 +2805,7 @@ fn generateNull(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) Er return null; } -fn generateObjectDeclaration(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateObjectDeclaration(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const locations = self.ast.nodes.items(.location); const type_defs = self.ast.nodes.items(.type_def); const lexemes = self.ast.tokens.items(.lexeme); @@ -2955,7 +2990,7 @@ fn generateObjectDeclaration(self: *Self, node: Ast.Node.Index, breaks: ?*std.Ar return null; } -fn generateObjectInit(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateObjectInit(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const locations = self.ast.nodes.items(.location); const type_defs = self.ast.nodes.items(.type_def); const lexemes = self.ast.tokens.items(.lexeme); @@ -3100,7 +3135,7 @@ fn generateObjectInit(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList return null; } -fn generatePattern(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generatePattern(self: *Self, node: Ast.Node.Index, _: Breaks) Error!?*obj.ObjFunction { try self.emitConstant( self.ast.nodes.items(.location)[node], try self.ast.toValue(node, self.gc), @@ -3112,7 +3147,7 @@ fn generatePattern(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) return null; } -fn generateProtocolDeclaration(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateProtocolDeclaration(self: *Self, node: Ast.Node.Index, _: Breaks) Error!?*obj.ObjFunction { const location = self.ast.nodes.items(.location)[node]; const components = self.ast.nodes.items(.components)[node].ProtocolDeclaration; const type_def = self.ast.nodes.items(.type_def)[node].?; @@ -3130,7 +3165,7 @@ fn generateProtocolDeclaration(self: *Self, node: Ast.Node.Index, _: ?*std.Array return null; } -fn generateRange(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateRange(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const type_defs = self.ast.nodes.items(.type_def); const components = self.ast.nodes.items(.components)[node].Range; const locations = self.ast.nodes.items(.location); @@ -3177,7 +3212,7 @@ fn generateRange(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usiz return null; } -fn generateResolve(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateResolve(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const fiber = self.ast.nodes.items(.components)[node].Resolve; const fiber_type_def = self.ast.nodes.items(.type_def)[fiber].?; const locations = self.ast.nodes.items(.location); @@ -3206,7 +3241,7 @@ fn generateResolve(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(us return null; } -fn generateResume(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateResume(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const fiber = self.ast.nodes.items(.components)[node].Resume; const fiber_type_def = self.ast.nodes.items(.type_def)[fiber].?; const locations = self.ast.nodes.items(.location); @@ -3235,7 +3270,7 @@ fn generateResume(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usi return null; } -fn generateReturn(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateReturn(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const components = self.ast.nodes.items(.components)[node].Return; const type_defs = self.ast.nodes.items(.type_def); const locations = self.ast.nodes.items(.location); @@ -3278,7 +3313,7 @@ fn generateReturn(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usi return null; } -fn generateString(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateString(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const location = self.ast.nodes.items(.location)[node]; const type_defs = self.ast.nodes.items(.type_def); const elements = self.ast.nodes.items(.components)[node].String; @@ -3317,7 +3352,7 @@ fn generateString(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usi return null; } -fn generateStringLiteral(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateStringLiteral(self: *Self, node: Ast.Node.Index, _: Breaks) Error!?*obj.ObjFunction { try self.emitConstant( self.ast.nodes.items(.location)[node], self.ast.nodes.items(.components)[node].StringLiteral.toValue(), @@ -3329,7 +3364,7 @@ fn generateStringLiteral(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(u return null; } -fn generateSubscript(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateSubscript(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const locations = self.ast.nodes.items(.location); const location = locations[node]; const type_defs = self.ast.nodes.items(.type_def); @@ -3442,7 +3477,7 @@ fn generateSubscript(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList( return null; } -fn generateTry(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateTry(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const components = self.ast.nodes.items(.components)[node].Try; const type_defs = self.ast.nodes.items(.type_def); const locations = self.ast.nodes.items(.location); @@ -3562,7 +3597,7 @@ fn generateTry(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize) return null; } -fn generateThrow(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateThrow(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const components = self.ast.nodes.items(.components)[node].Throw; const type_defs = self.ast.nodes.items(.type_def); const location = self.ast.nodes.items(.location)[node]; @@ -3616,7 +3651,7 @@ fn generateThrow(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usiz return null; } -fn generateTypeExpression(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateTypeExpression(self: *Self, node: Ast.Node.Index, _: Breaks) Error!?*obj.ObjFunction { const node_components = self.ast.nodes.items(.components); const type_defs = self.ast.nodes.items(.type_def); @@ -3631,7 +3666,7 @@ fn generateTypeExpression(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList( return null; } -fn generateTypeOfExpression(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateTypeOfExpression(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { _ = try self.generateNode(self.ast.nodes.items(.components)[node].TypeOfExpression, breaks); try self.emitOpCode( @@ -3645,7 +3680,7 @@ fn generateTypeOfExpression(self: *Self, node: Ast.Node.Index, breaks: ?*std.Arr return null; } -fn generateUnary(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateUnary(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const components = self.ast.nodes.items(.components)[node].Unary; const location = self.ast.nodes.items(.location)[node]; const expression_location = self.ast.nodes.items(.location)[components.expression]; @@ -3705,7 +3740,7 @@ fn generateUnary(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usiz return null; } -fn generateUnwrap(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateUnwrap(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const locations = self.ast.nodes.items(.location); const location = locations[node]; const components = self.ast.nodes.items(.components)[node].Unwrap; @@ -3746,7 +3781,7 @@ fn generateUnwrap(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usi return null; } -fn generateVarDeclaration(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateVarDeclaration(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const components = self.ast.nodes.items(.components)[node].VarDeclaration; const type_defs = self.ast.nodes.items(.type_def); const type_def = type_defs[node].?; @@ -3788,7 +3823,7 @@ fn generateVarDeclaration(self: *Self, node: Ast.Node.Index, breaks: ?*std.Array return null; } -fn generateVoid(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateVoid(self: *Self, node: Ast.Node.Index, _: Breaks) Error!?*obj.ObjFunction { try self.emitOpCode( self.ast.nodes.items(.location)[node], .OP_VOID, @@ -3800,7 +3835,7 @@ fn generateVoid(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) Er return null; } -fn generateWhile(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateWhile(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const components = self.ast.nodes.items(.components)[node].While; const type_defs = self.ast.nodes.items(.type_def); const locations = self.ast.nodes.items(.location); @@ -3837,7 +3872,7 @@ fn generateWhile(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usiz const exit_jump = try self.emitJump(location, .OP_JUMP_IF_FALSE); try self.emitOpCode(location, .OP_POP); - var while_breaks = std.ArrayList(usize).init(self.gc.allocator); + var while_breaks = Breaks.init(self.gc.allocator); defer while_breaks.deinit(); _ = try self.generateNode(components.body, &while_breaks); @@ -3848,9 +3883,12 @@ fn generateWhile(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usiz try self.emitOpCode(location, .OP_POP); // Pop condition (is not necessary if broke out of the loop) // Patch breaks - for (while_breaks.items) |jump| { - try self.patchJumpOrLoop(jump, loop_start); - } + try self.patchBreaks( + while_breaks, + Breaks, + node, + loop_start, + ); try self.patchOptJumps(node); try self.endScope(node); @@ -3860,7 +3898,7 @@ fn generateWhile(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usiz return null; } -fn generateYield(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateYield(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj.ObjFunction { const expression = self.ast.nodes.items(.components)[node].Yield; const type_defs = self.ast.nodes.items(.type_def); const type_def = type_defs[node]; @@ -3913,7 +3951,7 @@ fn generateYield(self: *Self, node: Ast.Node.Index, breaks: ?*std.ArrayList(usiz return null; } -fn generateZdef(self: *Self, node: Ast.Node.Index, _: ?*std.ArrayList(usize)) Error!?*obj.ObjFunction { +fn generateZdef(self: *Self, node: Ast.Node.Index, _: Breaks) Error!?*obj.ObjFunction { if (is_wasm) { return null; } diff --git a/src/Parser.zig b/src/Parser.zig index 827c28cb..86790ba2 100644 --- a/src/Parser.zig +++ b/src/Parser.zig @@ -227,13 +227,13 @@ pub const UpValue = struct { pub const Frame = struct { enclosing: ?*Frame = null, - // TODO: make this a multiarray? locals: [255]Local, local_count: u8 = 0, - // TODO: make this a multiarray? upvalues: [255]UpValue, upvalue_count: u8 = 0, scope_depth: u32 = 0, + // Keep track of the node that introduced the scope (useful for labeled break/continue statements) + scopes: std.ArrayList(?Ast.Node.Index), // If false, `return` was omitted or within a conditionned block (if, loop, etc.) // We only count `return` emitted within the scope_depth 0 of the current function or unconditionned else statement function_node: Ast.Node.Index, @@ -244,6 +244,14 @@ pub const Frame = struct { in_try: bool = false, in_block_expression: ?u32 = null, + pub fn deinit(self: *Frame) void { + self.scopes.deinit(); + if (self.generics != null) { + self.generics.?.deinit(); + } + self.constants.deinit(); + } + pub fn resolveGeneric(self: Frame, name: *obj.ObjString) ?*obj.ObjTypeDef { if (self.generics) |generics| { if (generics.get(name)) |type_def| { @@ -954,6 +962,7 @@ fn beginFrame(self: *Self, function_type: obj.ObjFunction.FunctionType, function .enclosing = enclosing, .function_node = function_node, .constants = std.ArrayList(Value).init(self.gc.allocator), + .scopes = std.ArrayList(Ast.Node.Index).init(self.gc.allocator), }; if (function_type == .Extern) { @@ -1044,18 +1053,22 @@ fn endFrame(self: *Self) Ast.Node.Index { } } + self.current.?.deinit(); + const current_node = self.current.?.function_node; self.current = self.current.?.enclosing; return current_node; } -fn beginScope(self: *Self) void { +fn beginScope(self: *Self, at: ?Ast.Node.Index) !void { + try self.current.?.scopes.append(at); self.current.?.scope_depth += 1; } fn endScope(self: *Self) ![]Chunk.OpCode { const current = self.current.?; + _ = current.scopes.pop(); var closing = std.ArrayList(Chunk.OpCode).init(self.gc.allocator); current.scope_depth -= 1; @@ -4653,7 +4666,7 @@ fn @"if"(self: *Self, is_statement: bool, loop_scope: ?LoopScope) Error!Ast.Node try self.consume(.LeftParen, "Expected `(` after `if`."); - self.beginScope(); + self.beginScope(null); const condition = try self.expression(false); const condition_type_def = self.ast.nodes.items(.type_def)[condition]; @@ -4699,7 +4712,7 @@ fn @"if"(self: *Self, is_statement: bool, loop_scope: ?LoopScope) Error!Ast.Node } else if (is_statement) { try self.consume(.LeftBrace, "Expected `{` after `else`."); - self.beginScope(); + self.beginScope(null); else_branch = try self.block(loop_scope); self.ast.nodes.items(.ends_scope)[else_branch.?] = try self.endScope(); } else { @@ -4953,7 +4966,7 @@ fn function( ); try self.beginFrame(function_type, function_node, this); - self.beginScope(); + self.beginScope(null); // The functiont tyepdef is created in several steps, some need already parsed information like return type // We create the incomplete type now and enrich it. @@ -5669,7 +5682,7 @@ fn blockExpression(self: *Self, _: bool) Error!Ast.Node.Index { try self.consume(.LeftBrace, "Expected `{` at start of block expression"); - self.beginScope(); + self.beginScope(null); self.current.?.in_block_expression = self.current.?.scope_depth; var statements = std.ArrayList(Ast.Node.Index).init(self.gc.allocator); @@ -6139,7 +6152,7 @@ fn objectDeclaration(self: *Self) Error!Ast.Node.Index { try self.consume(.Greater, "Expected `>` after generic types list"); } - self.beginScope(); + self.beginScope(null); // Body try self.consume(.LeftBrace, "Expected `{` before object body."); @@ -6458,7 +6471,7 @@ fn protocolDeclaration(self: *Self) Error!Ast.Node.Index { .resolved_type = resolved_type, }; - self.beginScope(); + self.beginScope(null); // Body try self.consume(.LeftBrace, "Expected `{` before protocol body."); @@ -7916,7 +7929,7 @@ fn forStatement(self: *Self) Error!Ast.Node.Index { try self.consume(.LeftParen, "Expected `(` after `for`."); - self.beginScope(); + self.beginScope(null); // Should be either VarDeclaration or expression var init_declarations = std.ArrayList(Ast.Node.Index).init(self.gc.allocator); @@ -7957,33 +7970,46 @@ fn forStatement(self: *Self) Error!Ast.Node.Index { try self.consume(.RightParen, "Expected `)` after `for` expressions."); - try self.consume(.LeftBrace, "Expected `{` after `for` definition."); + const label = if (try self.match(.Colon)) lbl: { + try self.consume(.Identifier, "Expected label after `:`."); - self.beginScope(); - const body = try self.block( - .{ - .loop_type = .For, - .loop_body_scope = self.current.?.scope_depth, - }, - ); - self.ast.nodes.items(.ends_scope)[body] = try self.endScope(); + break :lbl self.current_token.? - 1; + } else null; - return try self.ast.appendNode( + try self.consume(.LeftBrace, "Expected `{`."); + + // We add it before parsing the body so that we can find it on a labeled break/continue statement + const for_node = try self.ast.appendNode( .{ .tag = .For, .location = start_location, - .end_location = self.current_token.? - 1, + .end_location = undefined, .components = .{ .For = .{ .init_declarations = init_declarations.items, .condition = condition, .post_loop = post_loop.items, - .body = body, + .body = undefined, + .label = label, }, }, - .ends_scope = try self.endScope(), }, ); + + self.beginScope(for_node); + const body = try self.block( + .{ + .loop_type = .For, + .loop_body_scope = self.current.?.scope_depth, + }, + ); + self.ast.nodes.items(.ends_scope)[body] = try self.endScope(); + + self.ast.nodes.items(.end_location)[for_node] = self.current_token - 1; + self.ast.nodes.items(.components)[for_node].For.body = body; + self.ast.nodes.items(.ends_scope)[for_node] = try self.endScope(); + + return for_node; } fn forEachStatement(self: *Self) Error!Ast.Node.Index { @@ -7991,7 +8017,7 @@ fn forEachStatement(self: *Self) Error!Ast.Node.Index { try self.consume(.LeftParen, "Expected `(` after `foreach`."); - self.beginScope(); + self.beginScope(null); var key = try self.varDeclaration( false, @@ -8054,34 +8080,47 @@ fn forEachStatement(self: *Self) Error!Ast.Node.Index { try self.consume(.RightParen, "Expected `)` after `foreach`."); - try self.consume(.LeftBrace, "Expected `{` after `foreach` definition."); + const label = if (try self.match(.Colon)) lbl: { + try self.consume(.Identifier, "Expected label after `:`."); - self.beginScope(); - const body = try self.block( - .{ - .loop_type = .ForEach, - .loop_body_scope = self.current.?.scope_depth, - }, - ); - self.ast.nodes.items(.ends_scope)[body] = try self.endScope(); + break :lbl self.current_token.? - 1; + } else null; - return try self.ast.appendNode( + try self.consume(.LeftBrace, "Expected `{`."); + + // We add it before parsing the body so that we can find it on a labeled break/continue statement + const foreach_node = try self.ast.appendNode( .{ .tag = .ForEach, .location = start_location, - .end_location = self.current_token.? - 1, + .end_location = undefined, .components = .{ .ForEach = .{ .key = key, .value = value.?, .iterable = iterable, - .body = body, + .body = undefined, .key_omitted = key_omitted, + .label = label, }, }, - .ends_scope = try self.endScope(), }, ); + + self.beginScope(foreach_node); + const body = try self.block( + .{ + .loop_type = .ForEach, + .loop_body_scope = self.current.?.scope_depth, + }, + ); + self.ast.nodes.items(.ends_scope)[body] = try self.endScope(); + + self.ast.nodes.items(.end_location)[foreach_node] = self.current_token - 1; + self.ast.nodes.items(.components)[foreach_node].ForEach.body = body; + self.ast.nodes.items(.ends_scope)[foreach_node] = try self.endScope(); + + return foreach_node; } fn whileStatement(self: *Self) Error!Ast.Node.Index { @@ -8093,38 +8132,73 @@ fn whileStatement(self: *Self) Error!Ast.Node.Index { try self.consume(.RightParen, "Expected `)` after `while` condition."); - try self.consume(.LeftBrace, "Expected `{` after `if` condition."); + const label = if (try self.match(.Colon)) lbl: { + try self.consume(.Identifier, "Expected label after `:`."); - self.beginScope(); - const body = try self.block( - .{ - .loop_type = .While, - .loop_body_scope = self.current.?.scope_depth, - }, - ); - self.ast.nodes.items(.ends_scope)[body] = try self.endScope(); + break :lbl self.current_token.? - 1; + } else null; - return try self.ast.appendNode( + try self.consume(.LeftBrace, "Expected `{`."); + + // We add it before parsing the body so that we can find it on a labeled break/continue statement + const while_node = try self.ast.appendNode( .{ .tag = .While, .location = start_location, - .end_location = self.current_token.? - 1, + .end_location = undefined, .components = .{ .While = .{ .condition = condition, - .body = body, + .body = undefined, + .label = label, }, }, }, ); + + self.beginScope(while_node); + const body = try self.block( + .{ + .loop_type = .While, + .loop_body_scope = self.current.?.scope_depth, + }, + ); + self.ast.nodes.items(.ends_scope)[body] = try self.endScope(); + + self.ast.nodes.items(.end_location)[while_node] = self.current_token - 1; + self.ast.nodes.items(.components)[while_node].While.body = body; + + return while_node; } fn doUntilStatement(self: *Self) Error!Ast.Node.Index { const start_location = self.current_token.? - 1; - try self.consume(.LeftBrace, "Expected `{` after `do`."); + const label = if (try self.match(.Colon)) lbl: { + try self.consume(.Identifier, "Expected label after `:`."); + + break :lbl self.current_token.? - 1; + } else null; + + try self.consume(.LeftBrace, "Expected `{`."); + + // We add it before parsing the body so that we can find it on a labeled break/continue statement + const dountil_node = try self.ast.appendNode( + .{ + .tag = .DoUntil, + .location = start_location, + .end_location = undefined, + .components = .{ + .DoUntil = .{ + .condition = undefined, + .body = undefined, + .label = label, + }, + }, + }, + ); - self.beginScope(); + self.beginScope(null); const body = try self.block( .{ .loop_type = .Do, @@ -8141,19 +8215,11 @@ fn doUntilStatement(self: *Self) Error!Ast.Node.Index { try self.consume(.RightParen, "Expected `)` after `until` condition."); - return try self.ast.appendNode( - .{ - .tag = .DoUntil, - .location = start_location, - .end_location = self.current_token.? - 1, - .components = .{ - .DoUntil = .{ - .condition = condition, - .body = body, - }, - }, - }, - ); + self.ast.nodes.items(.end_location)[dountil_node] = self.current_token - 1; + self.ast.nodes.items(.components)[dountil_node].DoUntil.condition = condition; + self.ast.nodes.items(.components)[dountil_node].DoUntil.body = body; + + return dountil_node; } fn returnStatement(self: *Self) Error!Ast.Node.Index { @@ -8266,7 +8332,7 @@ fn tryStatement(self: *Self) Error!Ast.Node.Index { try self.consume(.LeftBrace, "Expected `{` after `try`"); - self.beginScope(); + self.beginScope(null); const body = try self.block(null); self.ast.nodes.items(.ends_scope)[body] = try self.endScope(); @@ -8280,7 +8346,7 @@ fn tryStatement(self: *Self) Error!Ast.Node.Index { self.reportError(.syntax, "Catch clause not allowed after unconditional catch"); } - self.beginScope(); + self.beginScope(null); const type_def = try self.parseTypeDef(null, true); @@ -8310,7 +8376,7 @@ fn tryStatement(self: *Self) Error!Ast.Node.Index { } else if (unconditional_clause == null) { try self.consume(.LeftBrace, "Expected `{` after `catch`"); - self.beginScope(); + self.beginScope(null); unconditional_clause = try self.block(null); self.ast.nodes.items(.ends_scope)[unconditional_clause.?] = try self.endScope(); } else { @@ -8336,52 +8402,129 @@ fn tryStatement(self: *Self) Error!Ast.Node.Index { ); } -fn breakStatement(self: *Self, loop_scope: ?LoopScope) Error!Ast.Node.Index { - const start_location = self.current_token.? - 1; +// Go up scopes until it finds a loop node with a matching label +fn findLabel(self: *Self, label: Ast.TokenIndex) ?struct { node: Ast.Node.Index, depth: u32 } { + const tags = self.ast.nodes.items(.tag); + const components = self.ast.nodes.items(.components); + const lexemes = self.ast.tokens.items(.lexeme); - if (loop_scope == null) { - self.reportError(.syntax, "break is not allowed here."); + var depth = self.current.?.scope_depth; + while (depth >= 0) : (depth -= 1) { + if (self.current.?.scopes[depth]) |scope_node| { + switch (tags[scope_node]) { + .For => { + if (components[scope_node].For.label) |scope_label| { + if (std.mem.eql(u8, lexemes[scope_label], lexemes[label])) { + return .{ + .node = scope_node, + .depth = depth, + }; + } + } + }, + .ForEach => { + if (components[scope_node].ForEach.label) |scope_label| { + if (std.mem.eql(u8, lexemes[scope_label], lexemes[label])) { + return .{ + .node = scope_node, + .depth = depth, + }; + } + } + }, + .While => { + if (components[scope_node].While.label) |scope_label| { + if (std.mem.eql(u8, lexemes[scope_label], lexemes[label])) { + return .{ + .node = scope_node, + .depth = depth, + }; + } + } + }, + .DoUntil => { + if (components[scope_node].DoUntil.label) |scope_label| { + if (std.mem.eql(u8, lexemes[scope_label], lexemes[label])) { + return .{ + .node = scope_node, + .depth = depth, + }; + } + } + }, + else => {}, + } + } } - try self.consume(.Semicolon, "Expected `;` after statement."); - - return try self.ast.appendNode( - .{ - .tag = .Break, - .location = start_location, - .end_location = self.current_token.? - 1, - .ends_scope = if (loop_scope != null) - try self.closeScope(loop_scope.?.loop_body_scope) - else - null, - .components = .{ - .Break = {}, - }, - }, - ); + return null; } -fn continueStatement(self: *Self, loop_scope: ?LoopScope) Error!Ast.Node.Index { +fn breakContinueStatement(self: *Self, @"break": bool, loop_scope: ?LoopScope) Error!Ast.Node.Index { const start_location = self.current_token.? - 1; - if (loop_scope == null) { - self.reportError(.syntax, "continue is not allowed here."); + const label = if (try self.match(.Identifier)) + self.current_token - 1 + else + null; + + const label_scope = if (label) |lbl| + self.findLabel(lbl) + else + null; + + if (label != null and label_scope == null) { + self.reportErrorFmt( + .label_does_not_exists, + "Label `{s}` does not exists.", + .{ + self.ast.tokens.items(.lexeme)[label.?], + }, + ); + } + + if (label == null and loop_scope == null) { + self.reportError(.syntax, "break is not allowed here."); } try self.consume(.Semicolon, "Expected `;` after statement."); return try self.ast.appendNode( .{ - .tag = .Continue, + .tag = if (@"break") .Break else .Continue, .location = start_location, .end_location = self.current_token.? - 1, - .ends_scope = if (loop_scope != null) - try self.closeScope(loop_scope.?.loop_body_scope) + .ends_scope = if (loop_scope != null or label_scope != null) + try self.closeScope( + if (label_scope) |scope| + scope.depth + else + loop_scope.?.loop_body_scope, + ) else null, - .components = .{ - .Continue = {}, - }, + .components = if (@"break") + .{ + .Break = if (label_scope) |scope| + scope.node + else + null, + } + else + .{ + .Continue = if (label_scope) |scope| + scope.node + else + null, + }, }, ); } + +fn continueStatement(self: *Self, loop_scope: ?LoopScope) Error!Ast.Node.Index { + return self.breakContinueStatement(false, loop_scope); +} + +fn breakStatement(self: *Self, loop_scope: ?LoopScope) Error!Ast.Node.Index { + return self.breakContinueStatement(true, loop_scope); +} diff --git a/src/Reporter.zig b/src/Reporter.zig index a4a42aaf..512d75a5 100644 --- a/src/Reporter.zig +++ b/src/Reporter.zig @@ -108,6 +108,7 @@ pub const Error = enum(u8) { import_already_exists = 92, code_after_return = 93, unused_import = 94, + label_does_not_exists = 95, }; // Inspired by https://github.com/zesterer/ariadne diff --git a/tests/072-labels.buzz b/tests/072-labels.buzz new file mode 100644 index 00000000..c924cf66 --- /dev/null +++ b/tests/072-labels.buzz @@ -0,0 +1,14 @@ +import "std"; + +test "labeled break" { + var i = 0; + while (i < 100) :out { + i = i + 1; + + if (i == 10) { + break out; + } + } + + std.assert(i == 10); +}