lib/chant/src/parse/statement.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const ast = @import("../ast/root.zig");
  3 const assertion = @import("assert.zig");
  4 const ctype = @import("type/root.zig");
  5 const declaration = @import("declaration.zig");
  6 const expression = @import("expression.zig");
  7 const state = @import("state/root.zig");
  8 const cursor = state.cursor;
  9 const diagnostic = state.diagnostic;
 10 const memory = state.memory;
 11 const object = state.object;
 12 const start = state.start;
 13 const Error = @import("error.zig").Error;
 14 
 15 const Parser = state.Parser;
 16 
 17 pub fn parseStatement(parser: *Parser) Error!*ast.Stmt {
 18     if (isLabel(parser)) return parseLabel(parser);
 19     switch (cursor.peek(parser).kind) {
 20         .lbrace => return parseCompound(parser),
 21         .semicolon => {
 22             _ = cursor.advance(parser);
 23             return memory.create(parser, ast.Stmt, .empty);
 24         },
 25         .kw_if => return parseIf(parser),
 26         .kw_for => return parseFor(parser),
 27         .kw_while => return parseWhile(parser),
 28         .kw_do => return parseDo(parser),
 29         .kw_return => {
 30             _ = cursor.advance(parser);
 31             var value: ?*ast.Expr = null;
 32             if (cursor.peek(parser).kind != .semicolon) {
 33                 value = try expression.parseExpression(parser);
 34             }
 35             _ = try cursor.expect(parser, .semicolon);
 36             return memory.create(parser, ast.Stmt, .{ .return_stmt = value });
 37         },
 38         .kw_break => {
 39             _ = cursor.advance(parser);
 40             _ = try cursor.expect(parser, .semicolon);
 41             return memory.create(parser, ast.Stmt, .break_stmt);
 42         },
 43         .kw_continue => {
 44             _ = cursor.advance(parser);
 45             _ = try cursor.expect(parser, .semicolon);
 46             return memory.create(parser, ast.Stmt, .continue_stmt);
 47         },
 48         else => {
 49             const value = try expression.parseExpression(parser);
 50             _ = try cursor.expect(parser, .semicolon);
 51             return memory.create(parser, ast.Stmt, .{ .expression = value });
 52         },
 53     }
 54 }
 55 
 56 pub fn parseCompound(parser: *Parser) Error!*ast.Stmt {
 57     _ = try cursor.expect(parser, .lbrace);
 58     try object.push(parser);
 59     defer object.pop(parser);
 60     var items = std.ArrayListUnmanaged(*ast.Stmt).empty;
 61     while (cursor.peek(parser).kind != .rbrace) {
 62         if (cursor.peek(parser).kind == .eof) {
 63             return diagnostic.fail(parser, error.UnexpectedToken, "unterminated block");
 64         }
 65         try items.append(parser.arena, try parseBlockItem(parser));
 66     }
 67     _ = try cursor.expect(parser, .rbrace);
 68     const stmt = try memory.create(parser, ast.Stmt, .{ .compound = try items.toOwnedSlice(parser.arena) });
 69     return stmt;
 70 }
 71 
 72 fn parseBlockItem(parser: *Parser) Error!*ast.Stmt {
 73     if (isLabel(parser)) return parseLabel(parser);
 74     if (try assertion.consume(parser)) {
 75         return memory.create(parser, ast.Stmt, .empty);
 76     }
 77     if (try ctype.consumeAttributeDeclaration(parser)) {
 78         return memory.create(parser, ast.Stmt, .empty);
 79     }
 80     if (start.declaration(parser)) {
 81         const variables = try declaration.parseLocal(parser);
 82         return memory.create(parser, ast.Stmt, .{ .declaration = variables });
 83     }
 84     return parseStatement(parser);
 85 }
 86 
 87 fn isLabel(parser: *const Parser) bool {
 88     return cursor.peek(parser).kind == .identifier and cursor.ahead(parser, 1).kind == .colon;
 89 }
 90 
 91 fn parseLabel(parser: *Parser) Error!*ast.Stmt {
 92     const name = (try cursor.expect(parser, .identifier)).text;
 93     _ = try cursor.expect(parser, .colon);
 94     const body = if (cursor.peek(parser).kind == .rbrace)
 95         try memory.create(parser, ast.Stmt, .empty)
 96     else
 97         try parseBlockItem(parser);
 98     return memory.create(parser, ast.Stmt, .{ .label = .{ .name = name, .body = body } });
 99 }
100 
101 fn parseIf(parser: *Parser) Error!*ast.Stmt {
102     _ = try cursor.expect(parser, .kw_if);
103     _ = try cursor.expect(parser, .lparen);
104     const condition = try expression.parseExpression(parser);
105     _ = try cursor.expect(parser, .rparen);
106     const then_body = try parseStatement(parser);
107     var else_body: ?*ast.Stmt = null;
108     if (cursor.consume(parser, .kw_else)) {
109         else_body = try parseStatement(parser);
110     }
111     return memory.create(parser, ast.Stmt, .{ .if_stmt = .{
112         .condition = condition,
113         .then_body = then_body,
114         .else_body = else_body,
115     } });
116 }
117 
118 fn parseFor(parser: *Parser) Error!*ast.Stmt {
119     _ = try cursor.expect(parser, .kw_for);
120     _ = try cursor.expect(parser, .lparen);
121 
122     var init_stmt: ?*ast.Stmt = null;
123     if (!cursor.consume(parser, .semicolon)) {
124         if (start.declaration(parser)) {
125             const variables = try declaration.parseLocal(parser);
126             init_stmt = try memory.create(parser, ast.Stmt, .{ .declaration = variables });
127         } else {
128             const value = try expression.parseExpression(parser);
129             _ = try cursor.expect(parser, .semicolon);
130             init_stmt = try memory.create(parser, ast.Stmt, .{ .expression = value });
131         }
132     }
133 
134     var condition: ?*ast.Expr = null;
135     if (cursor.peek(parser).kind != .semicolon) {
136         condition = try expression.parseExpression(parser);
137     }
138     _ = try cursor.expect(parser, .semicolon);
139 
140     var step: ?*ast.Expr = null;
141     if (cursor.peek(parser).kind != .rparen) {
142         step = try expression.parseExpression(parser);
143     }
144     _ = try cursor.expect(parser, .rparen);
145 
146     const body = try parseStatement(parser);
147     return memory.create(parser, ast.Stmt, .{ .for_stmt = .{
148         .init = init_stmt,
149         .condition = condition,
150         .step = step,
151         .body = body,
152     } });
153 }
154 
155 fn parseWhile(parser: *Parser) Error!*ast.Stmt {
156     _ = try cursor.expect(parser, .kw_while);
157     _ = try cursor.expect(parser, .lparen);
158     const condition = try expression.parseExpression(parser);
159     _ = try cursor.expect(parser, .rparen);
160     const body = try parseStatement(parser);
161     return memory.create(parser, ast.Stmt, .{ .while_stmt = .{ .condition = condition, .body = body } });
162 }
163 
164 fn parseDo(parser: *Parser) Error!*ast.Stmt {
165     _ = try cursor.expect(parser, .kw_do);
166     const body = try parseStatement(parser);
167     _ = try cursor.expect(parser, .kw_while);
168     _ = try cursor.expect(parser, .lparen);
169     const condition = try expression.parseExpression(parser);
170     _ = try cursor.expect(parser, .rparen);
171     _ = try cursor.expect(parser, .semicolon);
172     return memory.create(parser, ast.Stmt, .{ .do_stmt = .{ .condition = condition, .body = body } });
173 }
174 
175 test "for statements carry declaration initializers" {
176     const lexer = @import("../lexer/root.zig");
177     var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
178     defer arena_state.deinit();
179     const arena = arena_state.allocator();
180 
181     const source = "for (int i = 0; i < n; i++) x += i;";
182     const token_survey = try lexer.survey(source, "loop.c");
183     const capacity = try lexer.Capacity.derive(token_survey.limits);
184     const bytes = try arena.alignedAlloc(
185         u8,
186         .fromByteUnits(lexer.Storage.storage_alignment),
187         capacity.storage_bytes,
188     );
189     var storage = try lexer.Storage.init(bytes, token_survey.limits);
190     storage.activate();
191     defer _ = storage.deinit();
192     const tokens = try storage.fill(token_survey, source, "loop.c");
193     var parser = try @import("state/test.zig").initParser(arena, tokens);
194     const stmt = try parseStatement(&parser);
195     const loop = stmt.for_stmt;
196     try std.testing.expect(loop.init.?.* == .declaration);
197     try std.testing.expectEqualStrings("i", loop.init.?.declaration[0].name);
198     try std.testing.expect(loop.condition != null);
199     try std.testing.expect(loop.step != null);
200     try std.testing.expect(loop.body.* == .expression);
201 }