| ... | ... | @@ -99,40 +99,45 @@ pub const ErrorMsg = struct { |
| 99 | 99 | }; |
| 100 | 100 | |
| 101 | 101 | pub const Tree = struct { |
| 102 | | decls: std.ArrayList(*Inst), |
| 103 | | errors: std.ArrayList(ErrorMsg), |
| 102 | decls: []*Inst, |
| 103 | errors: []ErrorMsg, |
| 104 | 104 | }; |
| 105 | 105 | |
| 106 | 106 | const ParseContext = struct { |
| 107 | 107 | allocator: *Allocator, |
| 108 | 108 | i: usize, |
| 109 | 109 | source: []const u8, |
| 110 | | errors: *std.ArrayList(ErrorMsg), |
| 110 | errors: std.ArrayList(ErrorMsg), |
| 111 | decls: std.ArrayList(*Inst), |
| 112 | global_name_map: *std.StringHashMap(usize), |
| 111 | 113 | }; |
| 112 | 114 | |
| 113 | 115 | pub fn parse(allocator: *Allocator, source: []const u8) Allocator.Error!Tree { |
| 114 | | var tree: Tree = .{ |
| 115 | | .decls = std.ArrayList(*Inst).init(allocator), |
| 116 | | .errors = std.ArrayList(ErrorMsg).init(allocator), |
| 117 | | }; |
| 116 | var global_name_map = std.StringHashMap(usize).init(allocator); |
| 117 | defer global_name_map.deinit(); |
| 118 | |
| 118 | 119 | var ctx: ParseContext = .{ |
| 119 | 120 | .allocator = allocator, |
| 120 | 121 | .i = 0, |
| 121 | 122 | .source = source, |
| 122 | | .errors = &tree.errors, |
| 123 | .decls = std.ArrayList(*Inst).init(allocator), |
| 124 | .errors = std.ArrayList(ErrorMsg).init(allocator), |
| 125 | .global_name_map = &global_name_map, |
| 123 | 126 | }; |
| 124 | | parseRoot(&ctx, &tree) catch |err| switch (err) { |
| 127 | parseRoot(&ctx) catch |err| switch (err) { |
| 125 | 128 | error.ParseFailure => { |
| 126 | | assert(tree.errors.items.len != 0); |
| 129 | assert(ctx.errors.items.len != 0); |
| 127 | 130 | }, |
| 128 | 131 | else => |e| return e, |
| 129 | 132 | }; |
| 130 | | return tree; |
| 133 | return Tree{ |
| 134 | .decls = ctx.decls.toOwnedSlice(), |
| 135 | .errors = ctx.errors.toOwnedSlice(), |
| 136 | }; |
| 131 | 137 | } |
| 132 | 138 | |
| 133 | | pub fn parseRoot(ctx: *ParseContext, tree: *Tree) !void { |
| 139 | pub fn parseRoot(ctx: *ParseContext) !void { |
| 134 | 140 | // The IR format is designed so that it can be tokenized and parsed at the same time. |
| 135 | | var global_name_map = std.StringHashMap(usize).init(ctx.allocator); |
| 136 | 141 | while (ctx.i < ctx.source.len) : (ctx.i += 1) switch (ctx.source[ctx.i]) { |
| 137 | 142 | ';' => _ = try skipToAndOver(ctx, '\n'), |
| 138 | 143 | '@' => { |
| ... | ... | @@ -145,11 +150,11 @@ pub fn parseRoot(ctx: *ParseContext, tree: *Tree) !void { |
| 145 | 150 | } |
| 146 | 151 | try requireEatBytes(ctx, "= "); |
| 147 | 152 | const inst = try parseInstruction(ctx); |
| 148 | | const ident_index = tree.decls.items.len; |
| 149 | | if (try global_name_map.put(ident, ident_index)) |_| { |
| 153 | const ident_index = ctx.decls.items.len; |
| 154 | if (try ctx.global_name_map.put(ident, ident_index)) |_| { |
| 150 | 155 | return parseError(ctx, "redefinition of identifier '{}'", .{ident}); |
| 151 | 156 | } |
| 152 | | try tree.decls.append(inst); |
| 157 | try ctx.decls.append(inst); |
| 153 | 158 | continue; |
| 154 | 159 | }, |
| 155 | 160 | ' ', '\n' => continue, |
| ... | ... | @@ -201,7 +206,7 @@ fn parseType(ctx: *ParseContext) !*Value { |
| 201 | 206 | return parseError(ctx, "TODO parse type", .{}); |
| 202 | 207 | } |
| 203 | 208 | |
| 204 | | fn parseInstruction(ctx: *ParseContext) !*Inst { |
| 209 | fn parseInstruction(ctx: *ParseContext) error{ OutOfMemory, ParseFailure }!*Inst { |
| 205 | 210 | switch (ctx.source[ctx.i]) { |
| 206 | 211 | '"' => return parseStringLiteralConst(ctx), |
| 207 | 212 | '0'...'9' => return parseIntegerLiteralConst(ctx), |
| ... | ... | @@ -260,19 +265,54 @@ fn parseParameterGeneric(ctx: *ParseContext, comptime T: type) !T { |
| 260 | 265 | return parseError(ctx, "unexpected EOF in enum parameter", .{}); |
| 261 | 266 | } |
| 262 | 267 | switch (T) { |
| 263 | | Inst.Fn.Body => { |
| 264 | | var instructions = std.ArrayList(*Inst).init(ctx.allocator); |
| 265 | | try requireEatBytes(ctx, "{"); |
| 266 | | return T{ |
| 267 | | .instructions = instructions.toOwnedSlice(), |
| 268 | | }; |
| 269 | | }, |
| 268 | Inst.Fn.Body => return parseBody(ctx), |
| 270 | 269 | Value => return parseError(ctx, "TODO implement parseParameterGeneric for type Value", .{}), |
| 271 | 270 | else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), |
| 272 | 271 | } |
| 273 | 272 | return parseError(ctx, "TODO parse parameter {}", .{@typeName(T)}); |
| 274 | 273 | } |
| 275 | 274 | |
| 275 | fn parseBody(ctx: *ParseContext) !Inst.Fn.Body { |
| 276 | var instructions = std.ArrayList(*Inst).init(ctx.allocator); |
| 277 | defer instructions.deinit(); |
| 278 | |
| 279 | var name_map = std.StringHashMap(usize).init(ctx.allocator); |
| 280 | defer name_map.deinit(); |
| 281 | |
| 282 | try requireEatBytes(ctx, "{"); |
| 283 | skipSpace(ctx); |
| 284 | |
| 285 | while (ctx.i < ctx.source.len) : (ctx.i += 1) switch (ctx.source[ctx.i]) { |
| 286 | ';' => _ = try skipToAndOver(ctx, '\n'), |
| 287 | '%' => { |
| 288 | const at_start = ctx.i; |
| 289 | const ident = try skipToAndOver(ctx, ' '); |
| 290 | var ty: ?*Value = null; |
| 291 | if (eatByte(ctx, ':')) { |
| 292 | skipSpace(ctx); |
| 293 | ty = try parseType(ctx); |
| 294 | skipSpace(ctx); |
| 295 | } |
| 296 | skipSpace(ctx); |
| 297 | try requireEatBytes(ctx, "="); |
| 298 | skipSpace(ctx); |
| 299 | const inst = try parseInstruction(ctx); |
| 300 | const ident_index = instructions.items.len; |
| 301 | if (try name_map.put(ident, ident_index)) |_| { |
| 302 | return parseError(ctx, "redefinition of identifier '{}'", .{ident}); |
| 303 | } |
| 304 | try instructions.append(inst); |
| 305 | continue; |
| 306 | }, |
| 307 | ' ', '\n' => continue, |
| 308 | else => |byte| return parseError(ctx, "unexpected byte: '{c}'", .{byte}), |
| 309 | }; |
| 310 | |
| 311 | return Inst.Fn.Body{ |
| 312 | .instructions = instructions.toOwnedSlice(), |
| 313 | }; |
| 314 | } |
| 315 | |
| 276 | 316 | fn parseStringLiteralConst(ctx: *ParseContext) !*Inst { |
| 277 | 317 | const start = ctx.i; |
| 278 | 318 | ctx.i += 1; // skip over '"' |
| ... | ... | @@ -333,8 +373,8 @@ pub fn main() anyerror!void { |
| 333 | 373 | const source = try std.fs.cwd().readFileAlloc(allocator, src_path, std.math.maxInt(u32)); |
| 334 | 374 | |
| 335 | 375 | const tree = try parse(allocator, source); |
| 336 | | if (tree.errors.items.len != 0) { |
| 337 | | for (tree.errors.items) |err_msg| { |
| 376 | if (tree.errors.len != 0) { |
| 377 | for (tree.errors) |err_msg| { |
| 338 | 378 | const loc = findLineColumn(source, err_msg.byte_offset); |
| 339 | 379 | std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); |
| 340 | 380 | } |