| ... | ... | @@ -15,7 +15,7 @@ pub const Inst = struct { |
| 15 | 15 | fieldptr, |
| 16 | 16 | deref, |
| 17 | 17 | @"asm", |
| 18 | | unreach, |
| 18 | @"unreachable", |
| 19 | 19 | @"fn", |
| 20 | 20 | }; |
| 21 | 21 | |
| ... | ... | @@ -26,7 +26,7 @@ pub const Inst = struct { |
| 26 | 26 | .fieldptr => FieldPtr, |
| 27 | 27 | .deref => Deref, |
| 28 | 28 | .@"asm" => Assembly, |
| 29 | | .unreach => Unreach, |
| 29 | .@"unreachable" => Unreach, |
| 30 | 30 | .@"fn" => Fn, |
| 31 | 31 | }; |
| 32 | 32 | } |
| ... | ... | @@ -75,8 +75,16 @@ pub const Inst = struct { |
| 75 | 75 | pub const Assembly = struct { |
| 76 | 76 | base: Inst = Inst{ .tag = .@"asm" }, |
| 77 | 77 | |
| 78 | | positionals: struct {}, |
| 79 | | kw_args: struct {}, |
| 78 | positionals: 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 | 90 | pub const Unreach = struct { |
| ... | ... | @@ -174,7 +182,9 @@ fn eatByte(ctx: *ParseContext, byte: u8) bool { |
| 174 | 182 | } |
| 175 | 183 | |
| 176 | 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 | 190 | fn requireEatBytes(ctx: *ParseContext, bytes: []const u8) !void { |
| ... | ... | @@ -284,14 +294,17 @@ fn parseInstructionGeneric( |
| 284 | 294 | skipSpace(ctx); |
| 285 | 295 | const name = try skipToAndOver(ctx, '='); |
| 286 | 296 | inline for (@typeInfo(KW_Args).Struct.fields) |arg_field| { |
| 287 | | if (mem.eql(u8, name, arg_field.name)) { |
| 288 | | @field(inst_specific.kw_args, arg_field.name) = try parseParameterGeneric( |
| 289 | | ctx, |
| 290 | | arg_field.field_type, |
| 291 | | body_ctx, |
| 292 | | ); |
| 297 | const field_name = arg_field.name; |
| 298 | if (mem.eql(u8, name, field_name)) { |
| 299 | const NonOptional = switch (@typeInfo(arg_field.field_type)) { |
| 300 | .Optional => |info| info.child, |
| 301 | else => arg_field.field_type, |
| 302 | }; |
| 303 | @field(inst_specific.kw_args, field_name) = try parseParameterGeneric(ctx, NonOptional, body_ctx); |
| 293 | 304 | break; |
| 294 | 305 | } |
| 306 | } else { |
| 307 | return parseError(ctx, "unrecognized keyword parameter: '{}'", .{name}); |
| 295 | 308 | } |
| 296 | 309 | skipSpace(ctx); |
| 297 | 310 | } |
| ... | ... | @@ -317,45 +330,72 @@ fn parseParameterGeneric(ctx: *ParseContext, comptime T: type, body_ctx: ?*BodyC |
| 317 | 330 | } |
| 318 | 331 | switch (T) { |
| 319 | 332 | Inst.Fn.Body => return parseBody(ctx), |
| 320 | | *Inst => { |
| 321 | | const local_ref = switch (ctx.source[ctx.i]) { |
| 322 | | '@' => false, |
| 323 | | '%' => true, |
| 324 | | '"' => return parseStringLiteralConst(ctx, null), |
| 325 | | else => |byte| return parseError(ctx, "unexpected byte: '{c}'", .{byte}), |
| 333 | bool => { |
| 334 | const bool_value = switch (ctx.source[ctx.i]) { |
| 335 | '0' => false, |
| 336 | '1' => true, |
| 337 | else => |byte| return parseError(ctx, "expected '0' or '1' for boolean value, found {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 | 339 | ctx.i += 1; |
| 336 | | const name_start = ctx.i; |
| 337 | | while (ctx.i < ctx.source.len) : (ctx.i += 1) switch (ctx.source[ctx.i]) { |
| 338 | | ' ', '\n', ',', ')' => break, |
| 339 | | else => continue, |
| 340 | | }; |
| 341 | | const ident = ctx.source[name_start..ctx.i]; |
| 342 | | const kv = map.get(ident) orelse { |
| 343 | | const bad_name = ctx.source[name_start - 1 .. ctx.i]; |
| 344 | | ctx.i = name_start - 1; |
| 345 | | return parseError(ctx, "unrecognized identifier: {}", .{bad_name}); |
| 346 | | }; |
| 347 | | if (local_ref) { |
| 348 | | return body_ctx.?.instructions.items[kv.value]; |
| 349 | | } else { |
| 350 | | return ctx.decls.items[kv.value]; |
| 340 | return bool_value; |
| 341 | }, |
| 342 | []*Inst => { |
| 343 | try requireEatBytes(ctx, "["); |
| 344 | skipSpace(ctx); |
| 345 | if (eatByte(ctx, ']')) return &[0]*Inst{}; |
| 346 | |
| 347 | var instructions = std.ArrayList(*Inst).init(ctx.allocator); |
| 348 | defer instructions.deinit(); |
| 349 | while (true) { |
| 350 | skipSpace(ctx); |
| 351 | try instructions.append(try parseParameterInst(ctx, body_ctx)); |
| 352 | skipSpace(ctx); |
| 353 | if (!eatByte(ctx, ',')) break; |
| 351 | 354 | } |
| 355 | try requireEatBytes(ctx, "]"); |
| 356 | return instructions.toOwnedSlice(); |
| 352 | 357 | }, |
| 358 | *Inst => return parseParameterInst(ctx, body_ctx), |
| 353 | 359 | Value => return parseError(ctx, "TODO implement parseParameterGeneric for type Value", .{}), |
| 354 | 360 | else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), |
| 355 | 361 | } |
| 356 | 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 | 399 | const BodyContext = struct { |
| 360 | 400 | instructions: std.ArrayList(*Inst), |
| 361 | 401 | name_map: std.StringHashMap(usize), |