| ... | @@ -99,40 +99,45 @@ pub const ErrorMsg = struct { | ... | @@ -99,40 +99,45 @@ pub const ErrorMsg = struct { |
| 99 | }; | 99 | }; |
| 100 | | 100 | |
| 101 | pub const Tree = struct { | 101 | pub const Tree = struct { |
| 102 | decls: std.ArrayList(*Inst), | 102 | decls: []*Inst, |
| 103 | errors: std.ArrayList(ErrorMsg), | 103 | errors: []ErrorMsg, |
| 104 | }; | 104 | }; |
| 105 | | 105 | |
| 106 | const ParseContext = struct { | 106 | const ParseContext = struct { |
| 107 | allocator: *Allocator, | 107 | allocator: *Allocator, |
| 108 | i: usize, | 108 | i: usize, |
| 109 | source: []const u8, | 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 | pub fn parse(allocator: *Allocator, source: []const u8) Allocator.Error!Tree { | 115 | pub fn parse(allocator: *Allocator, source: []const u8) Allocator.Error!Tree { |
| 114 | var tree: Tree = .{ | 116 | var global_name_map = std.StringHashMap(usize).init(allocator); |
| 115 | .decls = std.ArrayList(*Inst).init(allocator), | 117 | defer global_name_map.deinit(); |
| 116 | .errors = std.ArrayList(ErrorMsg).init(allocator), | 118 | |
| 117 | }; | | |
| 118 | var ctx: ParseContext = .{ | 119 | var ctx: ParseContext = .{ |
| 119 | .allocator = allocator, | 120 | .allocator = allocator, |
| 120 | .i = 0, | 121 | .i = 0, |
| 121 | .source = source, | 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 | error.ParseFailure => { | 128 | error.ParseFailure => { |
| 126 | assert(tree.errors.items.len != 0); | 129 | assert(ctx.errors.items.len != 0); |
| 127 | }, | 130 | }, |
| 128 | else => |e| return e, | 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 | // The IR format is designed so that it can be tokenized and parsed at the same time. | 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 | while (ctx.i < ctx.source.len) : (ctx.i += 1) switch (ctx.source[ctx.i]) { | 141 | while (ctx.i < ctx.source.len) : (ctx.i += 1) switch (ctx.source[ctx.i]) { |
| 137 | ';' => _ = try skipToAndOver(ctx, '\n'), | 142 | ';' => _ = try skipToAndOver(ctx, '\n'), |
| 138 | '@' => { | 143 | '@' => { |
| ... | @@ -145,11 +150,11 @@ pub fn parseRoot(ctx: *ParseContext, tree: *Tree) !void { | ... | @@ -145,11 +150,11 @@ pub fn parseRoot(ctx: *ParseContext, tree: *Tree) !void { |
| 145 | } | 150 | } |
| 146 | try requireEatBytes(ctx, "= "); | 151 | try requireEatBytes(ctx, "= "); |
| 147 | const inst = try parseInstruction(ctx); | 152 | const inst = try parseInstruction(ctx); |
| 148 | const ident_index = tree.decls.items.len; | 153 | const ident_index = ctx.decls.items.len; |
| 149 | if (try global_name_map.put(ident, ident_index)) |_| { | 154 | if (try ctx.global_name_map.put(ident, ident_index)) |_| { |
| 150 | return parseError(ctx, "redefinition of identifier '{}'", .{ident}); | 155 | return parseError(ctx, "redefinition of identifier '{}'", .{ident}); |
| 151 | } | 156 | } |
| 152 | try tree.decls.append(inst); | 157 | try ctx.decls.append(inst); |
| 153 | continue; | 158 | continue; |
| 154 | }, | 159 | }, |
| 155 | ' ', '\n' => continue, | 160 | ' ', '\n' => continue, |
| ... | @@ -201,7 +206,7 @@ fn parseType(ctx: *ParseContext) !*Value { | ... | @@ -201,7 +206,7 @@ fn parseType(ctx: *ParseContext) !*Value { |
| 201 | return parseError(ctx, "TODO parse type", .{}); | 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 | switch (ctx.source[ctx.i]) { | 210 | switch (ctx.source[ctx.i]) { |
| 206 | '"' => return parseStringLiteralConst(ctx), | 211 | '"' => return parseStringLiteralConst(ctx), |
| 207 | '0'...'9' => return parseIntegerLiteralConst(ctx), | 212 | '0'...'9' => return parseIntegerLiteralConst(ctx), |
| ... | @@ -260,19 +265,54 @@ fn parseParameterGeneric(ctx: *ParseContext, comptime T: type) !T { | ... | @@ -260,19 +265,54 @@ fn parseParameterGeneric(ctx: *ParseContext, comptime T: type) !T { |
| 260 | return parseError(ctx, "unexpected EOF in enum parameter", .{}); | 265 | return parseError(ctx, "unexpected EOF in enum parameter", .{}); |
| 261 | } | 266 | } |
| 262 | switch (T) { | 267 | switch (T) { |
| 263 | Inst.Fn.Body => { | 268 | Inst.Fn.Body => return parseBody(ctx), |
| 264 | var instructions = std.ArrayList(*Inst).init(ctx.allocator); | | |
| 265 | try requireEatBytes(ctx, "{"); | | |
| 266 | return T{ | | |
| 267 | .instructions = instructions.toOwnedSlice(), | | |
| 268 | }; | | |
| 269 | }, | | |
| 270 | Value => return parseError(ctx, "TODO implement parseParameterGeneric for type Value", .{}), | 269 | Value => return parseError(ctx, "TODO implement parseParameterGeneric for type Value", .{}), |
| 271 | else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), | 270 | else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), |
| 272 | } | 271 | } |
| 273 | return parseError(ctx, "TODO parse parameter {}", .{@typeName(T)}); | 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 | fn parseStringLiteralConst(ctx: *ParseContext) !*Inst { | 316 | fn parseStringLiteralConst(ctx: *ParseContext) !*Inst { |
| 277 | const start = ctx.i; | 317 | const start = ctx.i; |
| 278 | ctx.i += 1; // skip over '"' | 318 | ctx.i += 1; // skip over '"' |
| ... | @@ -333,8 +373,8 @@ pub fn main() anyerror!void { | ... | @@ -333,8 +373,8 @@ pub fn main() anyerror!void { |
| 333 | const source = try std.fs.cwd().readFileAlloc(allocator, src_path, std.math.maxInt(u32)); | 373 | const source = try std.fs.cwd().readFileAlloc(allocator, src_path, std.math.maxInt(u32)); |
| 334 | | 374 | |
| 335 | const tree = try parse(allocator, source); | 375 | const tree = try parse(allocator, source); |
| 336 | if (tree.errors.items.len != 0) { | 376 | if (tree.errors.len != 0) { |
| 337 | for (tree.errors.items) |err_msg| { | 377 | for (tree.errors) |err_msg| { |
| 338 | const loc = findLineColumn(source, err_msg.byte_offset); | 378 | const loc = findLineColumn(source, err_msg.byte_offset); |
| 339 | std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); | 379 | std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); |
| 340 | } | 380 | } |