| ... | @@ -15,7 +15,7 @@ pub const Inst = struct { | ... | @@ -15,7 +15,7 @@ pub const Inst = struct { |
| 15 | fieldptr, | 15 | fieldptr, |
| 16 | deref, | 16 | deref, |
| 17 | @"asm", | 17 | @"asm", |
| 18 | unreach, | 18 | @"unreachable", |
| 19 | @"fn", | 19 | @"fn", |
| 20 | }; | 20 | }; |
| 21 | | 21 | |
| ... | @@ -26,7 +26,7 @@ pub const Inst = struct { | ... | @@ -26,7 +26,7 @@ pub const Inst = struct { |
| 26 | .fieldptr => FieldPtr, | 26 | .fieldptr => FieldPtr, |
| 27 | .deref => Deref, | 27 | .deref => Deref, |
| 28 | .@"asm" => Assembly, | 28 | .@"asm" => Assembly, |
| 29 | .unreach => Unreach, | 29 | .@"unreachable" => Unreach, |
| 30 | .@"fn" => Fn, | 30 | .@"fn" => Fn, |
| 31 | }; | 31 | }; |
| 32 | } | 32 | } |
| ... | @@ -75,8 +75,16 @@ pub const Inst = struct { | ... | @@ -75,8 +75,16 @@ pub const Inst = struct { |
| 75 | pub const Assembly = struct { | 75 | pub const Assembly = struct { |
| 76 | base: Inst = Inst{ .tag = .@"asm" }, | 76 | base: Inst = Inst{ .tag = .@"asm" }, |
| 77 | | 77 | |
| 78 | positionals: struct {}, | 78 | positionals: struct { |
| 79 | kw_args: struct {}, | 79 | asm_source: *Inst, |
| | 80 | }, |
| | 81 | kw_args: struct { |
| | 82 | @"volatile": bool = false, |
| | 83 | output: ?*Inst = null, |
| | 84 | inputs: []*Inst = &[0]*Inst{}, |
| | 85 | clobbers: []*Inst = &[0]*Inst{}, |
| | 86 | args: []*Inst = &[0]*Inst{}, |
| | 87 | }, |
| 80 | }; | 88 | }; |
| 81 | | 89 | |
| 82 | pub const Unreach = struct { | 90 | pub const Unreach = struct { |
| ... | @@ -174,7 +182,9 @@ fn eatByte(ctx: *ParseContext, byte: u8) bool { | ... | @@ -174,7 +182,9 @@ fn eatByte(ctx: *ParseContext, byte: u8) bool { |
| 174 | } | 182 | } |
| 175 | | 183 | |
| 176 | fn skipSpace(ctx: *ParseContext) void { | 184 | fn skipSpace(ctx: *ParseContext) void { |
| 177 | while (ctx.i < ctx.source.len and ctx.source[ctx.i] == ' ') : (ctx.i += 1) {} | 185 | while (ctx.i < ctx.source.len and (ctx.source[ctx.i] == ' ' or ctx.source[ctx.i] == '\n')) { |
| | 186 | ctx.i += 1; |
| | 187 | } |
| 178 | } | 188 | } |
| 179 | | 189 | |
| 180 | fn requireEatBytes(ctx: *ParseContext, bytes: []const u8) !void { | 190 | fn requireEatBytes(ctx: *ParseContext, bytes: []const u8) !void { |
| ... | @@ -284,14 +294,17 @@ fn parseInstructionGeneric( | ... | @@ -284,14 +294,17 @@ fn parseInstructionGeneric( |
| 284 | skipSpace(ctx); | 294 | skipSpace(ctx); |
| 285 | const name = try skipToAndOver(ctx, '='); | 295 | const name = try skipToAndOver(ctx, '='); |
| 286 | inline for (@typeInfo(KW_Args).Struct.fields) |arg_field| { | 296 | inline for (@typeInfo(KW_Args).Struct.fields) |arg_field| { |
| 287 | if (mem.eql(u8, name, arg_field.name)) { | 297 | const field_name = arg_field.name; |
| 288 | @field(inst_specific.kw_args, arg_field.name) = try parseParameterGeneric( | 298 | if (mem.eql(u8, name, field_name)) { |
| 289 | ctx, | 299 | const NonOptional = switch (@typeInfo(arg_field.field_type)) { |
| 290 | arg_field.field_type, | 300 | .Optional => |info| info.child, |
| 291 | body_ctx, | 301 | else => arg_field.field_type, |
| 292 | ); | 302 | }; |
| | 303 | @field(inst_specific.kw_args, field_name) = try parseParameterGeneric(ctx, NonOptional, body_ctx); |
| 293 | break; | 304 | break; |
| 294 | } | 305 | } |
| | 306 | } else { |
| | 307 | return parseError(ctx, "unrecognized keyword parameter: '{}'", .{name}); |
| 295 | } | 308 | } |
| 296 | skipSpace(ctx); | 309 | skipSpace(ctx); |
| 297 | } | 310 | } |
| ... | @@ -317,45 +330,72 @@ fn parseParameterGeneric(ctx: *ParseContext, comptime T: type, body_ctx: ?*BodyC | ... | @@ -317,45 +330,72 @@ fn parseParameterGeneric(ctx: *ParseContext, comptime T: type, body_ctx: ?*BodyC |
| 317 | } | 330 | } |
| 318 | switch (T) { | 331 | switch (T) { |
| 319 | Inst.Fn.Body => return parseBody(ctx), | 332 | Inst.Fn.Body => return parseBody(ctx), |
| 320 | *Inst => { | 333 | bool => { |
| 321 | const local_ref = switch (ctx.source[ctx.i]) { | 334 | const bool_value = switch (ctx.source[ctx.i]) { |
| 322 | '@' => false, | 335 | '0' => false, |
| 323 | '%' => true, | 336 | '1' => true, |
| 324 | '"' => return parseStringLiteralConst(ctx, null), | 337 | else => |byte| return parseError(ctx, "expected '0' or '1' for boolean value, found {c}", .{byte}), |
| 325 | else => |byte| return parseError(ctx, "unexpected byte: '{c}'", .{byte}), | | |
| 326 | }; | 338 | }; |
| 327 | const map = if (local_ref) | | |
| 328 | if (body_ctx) |bc| | | |
| 329 | &bc.name_map | | |
| 330 | else | | |
| 331 | return parseError(ctx, "referencing a % instruction in global scope", .{}) | | |
| 332 | else | | |
| 333 | ctx.global_name_map; | | |
| 334 | | | |
| 335 | ctx.i += 1; | 339 | ctx.i += 1; |
| 336 | const name_start = ctx.i; | 340 | return bool_value; |
| 337 | while (ctx.i < ctx.source.len) : (ctx.i += 1) switch (ctx.source[ctx.i]) { | 341 | }, |
| 338 | ' ', '\n', ',', ')' => break, | 342 | []*Inst => { |
| 339 | else => continue, | 343 | try requireEatBytes(ctx, "["); |
| 340 | }; | 344 | skipSpace(ctx); |
| 341 | const ident = ctx.source[name_start..ctx.i]; | 345 | if (eatByte(ctx, ']')) return &[0]*Inst{}; |
| 342 | const kv = map.get(ident) orelse { | 346 | |
| 343 | const bad_name = ctx.source[name_start - 1 .. ctx.i]; | 347 | var instructions = std.ArrayList(*Inst).init(ctx.allocator); |
| 344 | ctx.i = name_start - 1; | 348 | defer instructions.deinit(); |
| 345 | return parseError(ctx, "unrecognized identifier: {}", .{bad_name}); | 349 | while (true) { |
| 346 | }; | 350 | skipSpace(ctx); |
| 347 | if (local_ref) { | 351 | try instructions.append(try parseParameterInst(ctx, body_ctx)); |
| 348 | return body_ctx.?.instructions.items[kv.value]; | 352 | skipSpace(ctx); |
| 349 | } else { | 353 | if (!eatByte(ctx, ',')) break; |
| 350 | return ctx.decls.items[kv.value]; | | |
| 351 | } | 354 | } |
| | 355 | try requireEatBytes(ctx, "]"); |
| | 356 | return instructions.toOwnedSlice(); |
| 352 | }, | 357 | }, |
| | 358 | *Inst => return parseParameterInst(ctx, body_ctx), |
| 353 | Value => return parseError(ctx, "TODO implement parseParameterGeneric for type Value", .{}), | 359 | Value => return parseError(ctx, "TODO implement parseParameterGeneric for type Value", .{}), |
| 354 | else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), | 360 | else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), |
| 355 | } | 361 | } |
| 356 | return parseError(ctx, "TODO parse parameter {}", .{@typeName(T)}); | 362 | return parseError(ctx, "TODO parse parameter {}", .{@typeName(T)}); |
| 357 | } | 363 | } |
| 358 | | 364 | |
| | 365 | fn parseParameterInst(ctx: *ParseContext, body_ctx: ?*BodyContext) !*Inst { |
| | 366 | const local_ref = switch (ctx.source[ctx.i]) { |
| | 367 | '@' => false, |
| | 368 | '%' => true, |
| | 369 | '"' => return parseStringLiteralConst(ctx, null), |
| | 370 | else => |byte| return parseError(ctx, "unexpected byte: '{c}'", .{byte}), |
| | 371 | }; |
| | 372 | const map = if (local_ref) |
| | 373 | if (body_ctx) |bc| |
| | 374 | &bc.name_map |
| | 375 | else |
| | 376 | return parseError(ctx, "referencing a % instruction in global scope", .{}) |
| | 377 | else |
| | 378 | ctx.global_name_map; |
| | 379 | |
| | 380 | ctx.i += 1; |
| | 381 | const name_start = ctx.i; |
| | 382 | while (ctx.i < ctx.source.len) : (ctx.i += 1) switch (ctx.source[ctx.i]) { |
| | 383 | ' ', '\n', ',', ')', ']' => break, |
| | 384 | else => continue, |
| | 385 | }; |
| | 386 | const ident = ctx.source[name_start..ctx.i]; |
| | 387 | const kv = map.get(ident) orelse { |
| | 388 | const bad_name = ctx.source[name_start - 1 .. ctx.i]; |
| | 389 | ctx.i = name_start - 1; |
| | 390 | return parseError(ctx, "unrecognized identifier: {}", .{bad_name}); |
| | 391 | }; |
| | 392 | if (local_ref) { |
| | 393 | return body_ctx.?.instructions.items[kv.value]; |
| | 394 | } else { |
| | 395 | return ctx.decls.items[kv.value]; |
| | 396 | } |
| | 397 | } |
| | 398 | |
| 359 | const BodyContext = struct { | 399 | const BodyContext = struct { |
| 360 | instructions: std.ArrayList(*Inst), | 400 | instructions: std.ArrayList(*Inst), |
| 361 | name_map: std.StringHashMap(usize), | 401 | name_map: std.StringHashMap(usize), |