11 files changed, 212 insertions(+), 35 deletions(-)
src/core/Air.Dump.zig+2-1| ... | @@ -104,6 +104,7 @@ pub const State = struct { | ... | @@ -104,6 +104,7 @@ pub const State = struct { |
| 104 | }, | 104 | }, |
| 105 | inline .negate, | 105 | inline .negate, |
| 106 | .bool_not, | 106 | .bool_not, |
| 107 | .array_len, | ||
| 107 | => |comptime_tag| { | 108 | => |comptime_tag| { |
| 108 | const bin: Air.Un = air.instData(comptime_tag, inst); | 109 | const bin: Air.Un = air.instData(comptime_tag, inst); |
| 109 | try w.writeAll("("); | 110 | try w.writeAll("("); |
| ... | @@ -193,7 +194,7 @@ pub const State = struct { | ... | @@ -193,7 +194,7 @@ pub const State = struct { |
| 193 | 194 | ||
| 194 | try w.writeAll("("); | 195 | try w.writeAll("("); |
| 195 | try s.dumpValue(w, array_type.item_type.value()); | 196 | try s.dumpValue(w, array_type.item_type.value()); |
| 196 | try s.dumpRefList(w, array_init.list.slice(air), '[', ']'); | 197 | try s.dumpRefList(w, array_init.list.get(air).slice(air), '[', ']'); |
| 197 | try w.writeAll(")"); | 198 | try w.writeAll(")"); |
| 198 | }, | 199 | }, |
| 199 | } | 200 | } |
src/core/Air.zig+10-4| ... | @@ -30,6 +30,8 @@ pub const Inst = struct { | ... | @@ -30,6 +30,8 @@ pub const Inst = struct { |
| 30 | field_opaque, | 30 | field_opaque, |
| 31 | field_struct, | 31 | field_struct, |
| 32 | 32 | ||
| 33 | array_len, | ||
| 34 | |||
| 33 | index, | 35 | index, |
| 34 | 36 | ||
| 35 | // bin | 37 | // bin |
| ... | @@ -70,7 +72,10 @@ pub const Inst = struct { | ... | @@ -70,7 +72,10 @@ pub const Inst = struct { |
| 70 | .gt, | 72 | .gt, |
| 71 | .eq, | 73 | .eq, |
| 72 | => Bin, | 74 | => Bin, |
| 73 | .negate, .bool_not => Un, | 75 | .negate, |
| 76 | .bool_not, | ||
| 77 | .array_len, | ||
| 78 | => Un, | ||
| 74 | }; | 79 | }; |
| 75 | } | 80 | } |
| 76 | }; | 81 | }; |
| ... | @@ -98,8 +103,8 @@ pub const Inst = struct { | ... | @@ -98,8 +103,8 @@ pub const Inst = struct { |
| 98 | return if (ref.kind == .value) return @enumFromInt(ref.data) else null; | 103 | return if (ref.kind == .value) return @enumFromInt(ref.data) else null; |
| 99 | } | 104 | } |
| 100 | 105 | ||
| 101 | pub fn index(ref: Ref) ?Value { | 106 | pub fn index(ref: Ref) ?Index { |
| 102 | return if (ref.kind == .value) return @enumFromInt(ref.data) else null; | 107 | return if (ref.kind == .index) return @enumFromInt(ref.data) else null; |
| 103 | } | 108 | } |
| 104 | 109 | ||
| 105 | pub fn toOptional(ref: Ref) Optional { | 110 | pub fn toOptional(ref: Ref) Optional { |
| ... | @@ -279,7 +284,7 @@ pub const StructInit = struct { | ... | @@ -279,7 +284,7 @@ pub const StructInit = struct { |
| 279 | 284 | ||
| 280 | pub const ArrayInit = struct { | 285 | pub const ArrayInit = struct { |
| 281 | ty: Type, | 286 | ty: Type, |
| 282 | list: RefList, | 287 | list: ExtraIndex(RefList), |
| 283 | }; | 288 | }; |
| 284 | 289 | ||
| 285 | pub const FieldStruct = struct { | 290 | pub const FieldStruct = struct { |
| ... | @@ -329,6 +334,7 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In | ... | @@ -329,6 +334,7 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In |
| 329 | .lt, .gt, .eq, .bool_not => .bool, | 334 | .lt, .gt, .eq, .bool_not => .bool, |
| 330 | .@"break" => .noreturn, | 335 | .@"break" => .noreturn, |
| 331 | .field_opaque => .@"opaque", | 336 | .field_opaque => .@"opaque", |
| 337 | .array_len => .int32, | ||
| 332 | 338 | ||
| 333 | // For many instructions, the type is stored on it's data directly. This | 339 | // For many instructions, the type is stored on it's data directly. This |
| 334 | // is avoided when possible to reduce the size of Air. | 340 | // is avoided when possible to reduce the size of Air. |
src/core/Comp.zig+1-1| ... | @@ -185,7 +185,7 @@ pub const File = struct { | ... | @@ -185,7 +185,7 @@ pub const File = struct { |
| 185 | error.ParseFail => { | 185 | error.ParseFail => { |
| 186 | // TODO: let this merge with other report | 186 | // TODO: let this merge with other report |
| 187 | var report = try err_data.toReport(comp, file); | 187 | var report = try err_data.toReport(comp, file); |
| 188 | report.dumpToStdErr(comp) catch {}; | 188 | report.dumpToWriter(comp, comp.out.writer, comp.out.tty) catch {}; |
| 189 | report.deinit(comp.gpa); | 189 | report.deinit(comp.gpa); |
| 190 | return err; | 190 | return err; |
| 191 | }, | 191 | }, |
src/core/Parse.zig+8-1| ... | @@ -105,6 +105,7 @@ pub const ExpectedContext = enum { | ... | @@ -105,6 +105,7 @@ pub const ExpectedContext = enum { |
| 105 | else_clause, | 105 | else_clause, |
| 106 | struct_field, | 106 | struct_field, |
| 107 | param_list, | 107 | param_list, |
| 108 | static_field, | ||
| 108 | 109 | ||
| 109 | pub fn description(tag: ExpectedContext) []const u8 { | 110 | pub fn description(tag: ExpectedContext) []const u8 { |
| 110 | return switch (tag) { | 111 | return switch (tag) { |
| ... | @@ -115,6 +116,7 @@ pub const ExpectedContext = enum { | ... | @@ -115,6 +116,7 @@ pub const ExpectedContext = enum { |
| 115 | .expression => "an expression", | 116 | .expression => "an expression", |
| 116 | .else_clause => "an else clause", | 117 | .else_clause => "an else clause", |
| 117 | .struct_field => "a field definition", | 118 | .struct_field => "a field definition", |
| 119 | .static_field => "a field name or number", | ||
| 118 | }; | 120 | }; |
| 119 | } | 121 | } |
| 120 | }; | 122 | }; |
| ... | @@ -897,7 +899,10 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { | ... | @@ -897,7 +899,10 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 897 | switch (p.currentTag()) { | 899 | switch (p.currentTag()) { |
| 898 | .dot => { | 900 | .dot => { |
| 899 | const main_token = p.nextToken(); | 901 | const main_token = p.nextToken(); |
| 900 | const field = try p.expect(.id); | 902 | const field = switch (p.currentTag()) { |
| 903 | .id, .number_int => p.nextToken(), | ||
| 904 | else => return p.failUnexpectedToken(.static_field), | ||
| 905 | }; | ||
| 901 | 906 | ||
| 902 | expr = try p.addNode(.{ | 907 | expr = try p.addNode(.{ |
| 903 | .tag = .field, | 908 | .tag = .field, |
| ... | @@ -934,6 +939,8 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { | ... | @@ -934,6 +939,8 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 934 | 939 | ||
| 935 | const index = try p.tryParseExpression(.expression); | 940 | const index = try p.tryParseExpression(.expression); |
| 936 | 941 | ||
| 942 | _ = try p.expect(.end_bracket); | ||
| 943 | |||
| 937 | expr = try p.addNode(.{ | 944 | expr = try p.addNode(.{ |
| 938 | .tag = .index, | 945 | .tag = .index, |
| 939 | .main_token = main_token, | 946 | .main_token = main_token, |
src/core/Report.zig+21-4| ... | @@ -770,18 +770,36 @@ pub const Builder = struct { | ... | @@ -770,18 +770,36 @@ pub const Builder = struct { |
| 770 | current_file_str: ?String = null, | 770 | current_file_str: ?String = null, |
| 771 | 771 | ||
| 772 | pub fn err(b: *Builder, comptime kind: Err.FieldEnum, data: std.meta.FieldType(Err, kind)) !void { | 772 | pub fn err(b: *Builder, comptime kind: Err.FieldEnum, data: std.meta.FieldType(Err, kind)) !void { |
| 773 | try errAt(b, kind, data, @returnAddress()); | ||
| 774 | } | ||
| 775 | |||
| 776 | pub fn warn(b: *Builder, comptime kind: Warn.FieldEnum, data: std.meta.FieldType(Warn, kind)) !void { | ||
| 777 | try warnAt(b, kind, data, @returnAddress()); | ||
| 778 | } | ||
| 779 | |||
| 780 | pub fn errAt( | ||
| 781 | b: *Builder, | ||
| 782 | comptime kind: Err.FieldEnum, | ||
| 783 | data: std.meta.FieldType(Err, kind), | ||
| 784 | return_address: usize, | ||
| 785 | ) !void { | ||
| 773 | try b.errors.append(b.gpa, .{ | 786 | try b.errors.append(b.gpa, .{ |
| 774 | .data = .{ .@"error" = @unionInit(Err, @tagName(kind), data) }, | 787 | .data = .{ .@"error" = @unionInit(Err, @tagName(kind), data) }, |
| 775 | .url = try b.currentFileString(), | 788 | .url = try b.currentFileString(), |
| 776 | .debug_stack_trace = DebugStoredTrace.capture(@returnAddress()), | 789 | .debug_stack_trace = DebugStoredTrace.capture(return_address), |
| 777 | }); | 790 | }); |
| 778 | } | 791 | } |
| 779 | 792 | ||
| 780 | pub fn warn(b: *Builder, comptime kind: Warn.FieldEnum, data: std.meta.FieldType(Warn, kind)) !void { | 793 | pub fn warnAt( |
| 794 | b: *Builder, | ||
| 795 | comptime kind: Warn.FieldEnum, | ||
| 796 | data: std.meta.FieldType(Warn, kind), | ||
| 797 | return_address: usize, | ||
| 798 | ) !void { | ||
| 781 | try b.warnings.append(b.gpa, .{ | 799 | try b.warnings.append(b.gpa, .{ |
| 782 | .data = .{ .warn = @unionInit(Warn, @tagName(kind), data) }, | 800 | .data = .{ .warn = @unionInit(Warn, @tagName(kind), data) }, |
| 783 | .url = try b.currentFileString(), | 801 | .url = try b.currentFileString(), |
| 784 | .debug_stack_trace = DebugStoredTrace.capture(@returnAddress()), | 802 | .debug_stack_trace = DebugStoredTrace.capture(return_address), |
| 785 | }); | 803 | }); |
| 786 | } | 804 | } |
| 787 | 805 | ||
| ... | @@ -1078,7 +1096,6 @@ pub const LazyNode = struct { | ... | @@ -1078,7 +1096,6 @@ pub const LazyNode = struct { |
| 1078 | return items[lazy.operand.getArgId()]; | 1096 | return items[lazy.operand.getArgId()]; |
| 1079 | }, | 1097 | }, |
| 1080 | .field => { | 1098 | .field => { |
| 1081 | assert(node_tag == .field); | ||
| 1082 | return base_node; | 1099 | return base_node; |
| 1083 | }, | 1100 | }, |
| 1084 | else => {}, | 1101 | else => {}, |
src/core/Sema.zig+38-8| ... | @@ -389,12 +389,42 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { | ... | @@ -389,12 +389,42 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 389 | .field => { | 389 | .field => { |
| 390 | const field: Teg.Bin = s.code.instPayload(.field, in); | 390 | const field: Teg.Bin = s.code.instPayload(.field, in); |
| 391 | const base = try s.resolveRef(field.lhs); | 391 | const base = try s.resolveRef(field.lhs); |
| 392 | const ty = s.typeOfRef(base); | ||
| 393 | |||
| 394 | const rhs_raw = try s.resolveRef(field.rhs); | ||
| 395 | |||
| 396 | if (ty.toArrayType(s.ip)) |_| { | ||
| 397 | // Special case `.len` | ||
| 398 | get_array_len: { | ||
| 399 | const value = rhs_raw.value() orelse break :get_array_len; | ||
| 400 | if (value.valueTag(s.ip) != .string) break :get_array_len; | ||
| 401 | const string = value.asString(s.ip).toSlice(s.ip); | ||
| 402 | if (!std.mem.eql(u8, string, "len")) break :get_array_len; | ||
| 403 | |||
| 404 | switch (base.decode()) { | ||
| 405 | .value => |v| { | ||
| 406 | const items_len = v.valueData(s.ip, .array_value).get(s.ip).items_len; | ||
| 407 | return s.makeValueAir(.{ .int32 = @intCast(items_len) }); | ||
| 408 | }, | ||
| 409 | .index => { | ||
| 410 | return s.addInstRef(.array_len, .{ | ||
| 411 | .operand = base, | ||
| 412 | }); | ||
| 413 | }, | ||
| 414 | } | ||
| 415 | } | ||
| 416 | |||
| 417 | // Arrays use runtime known int32s | ||
| 418 | const rhs = try s.coerceRef(in.op(.rhs), rhs_raw, .int32); | ||
| 419 | return s.addInstRef(.index, .{ | ||
| 420 | .lhs = base, | ||
| 421 | .rhs = rhs, | ||
| 422 | }); | ||
| 423 | } | ||
| 392 | 424 | ||
| 393 | const rhs = try s.resolveCoerceValue(in.op(.rhs), field.rhs, .string); | 425 | const rhs = try s.resolveCoerceValue(in.op(.rhs), field.rhs, .string); |
| 394 | const string = rhs.asString(s.ip); | 426 | const string = rhs.asString(s.ip); |
| 395 | 427 | ||
| 396 | const ty = s.typeOfRef(base); | ||
| 397 | |||
| 398 | if (ty == .@"opaque") { | 428 | if (ty == .@"opaque") { |
| 399 | return s.addInstRef(.field_opaque, .{ | 429 | return s.addInstRef(.field_opaque, .{ |
| 400 | .base = base, | 430 | .base = base, |
| ... | @@ -930,10 +960,10 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { | ... | @@ -930,10 +960,10 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 930 | // Create a runtime array instantiation | 960 | // Create a runtime array instantiation |
| 931 | return try s.addInstRef(.array_init, .{ | 961 | return try s.addInstRef(.array_init, .{ |
| 932 | .ty = ty, | 962 | .ty = ty, |
| 933 | .list = .{ | 963 | .list = try s.air_extra.append(s.gpa, Air.RefList, .{ |
| 934 | .start = try s.air_extra.appendSlice(s.gpa, scratch.slice()), | 964 | .start = try s.air_extra.appendSlice(s.gpa, scratch.slice()), |
| 935 | .len = node.list.len, | 965 | .len = node.list.len, |
| 936 | }, | 966 | }), |
| 937 | }); | 967 | }); |
| 938 | } | 968 | } |
| 939 | }, | 969 | }, |
| ... | @@ -1277,7 +1307,7 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) Error!Air.Inst.R | ... | @@ -1277,7 +1307,7 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) Error!Air.Inst.R |
| 1277 | .numeric_literal => { | 1307 | .numeric_literal => { |
| 1278 | const float = v.toFloat(s.ip) orelse unreachable; | 1308 | const float = v.toFloat(s.ip) orelse unreachable; |
| 1279 | 1309 | ||
| 1280 | if (!core.inInt32Range(float)) { | 1310 | if (core.inInt32Range(float)) { |
| 1281 | try s.report.warn(.numeric_int_var_ambiguity, .{ | 1311 | try s.report.warn(.numeric_int_var_ambiguity, .{ |
| 1282 | .src = s.lazyNode(src), | 1312 | .src = s.lazyNode(src), |
| 1283 | }); | 1313 | }); |
| ... | @@ -1687,15 +1717,15 @@ fn nodeLoc(s: *const Sema, in: Teg.Inst.Index, op: Report.Operand) Report.LazyNo | ... | @@ -1687,15 +1717,15 @@ fn nodeLoc(s: *const Sema, in: Teg.Inst.Index, op: Report.Operand) Report.LazyNo |
| 1687 | } | 1717 | } |
| 1688 | 1718 | ||
| 1689 | fn addWarn(s: *Sema, comptime kind: Report.Err.FieldEnum, data: std.meta.FieldType(Report.Err, kind)) !void { | 1719 | fn addWarn(s: *Sema, comptime kind: Report.Err.FieldEnum, data: std.meta.FieldType(Report.Err, kind)) !void { |
| 1690 | try s.report.warn(kind, data); | 1720 | try s.report.warnAt(kind, data, @returnAddress()); |
| 1691 | } | 1721 | } |
| 1692 | 1722 | ||
| 1693 | fn addError(s: *Sema, comptime kind: Report.Err.FieldEnum, data: std.meta.FieldType(Report.Err, kind)) !void { | 1723 | fn addError(s: *Sema, comptime kind: Report.Err.FieldEnum, data: std.meta.FieldType(Report.Err, kind)) !void { |
| 1694 | try s.report.err(kind, data); | 1724 | try s.report.errAt(kind, data, @returnAddress()); |
| 1695 | } | 1725 | } |
| 1696 | 1726 | ||
| 1697 | fn fail(s: *Sema, comptime kind: Report.Err.FieldEnum, data: std.meta.FieldType(Report.Err, kind)) Error { | 1727 | fn fail(s: *Sema, comptime kind: Report.Err.FieldEnum, data: std.meta.FieldType(Report.Err, kind)) Error { |
| 1698 | try s.report.err(kind, data); | 1728 | try s.report.errAt(kind, data, @returnAddress()); |
| 1699 | return Error.AnalysisFail; | 1729 | return Error.AnalysisFail; |
| 1700 | } | 1730 | } |
| 1701 | 1731 |
src/core/Tokenizer.zig+38-5| ... | @@ -344,11 +344,6 @@ pub fn next(t: *Tokenizer) !?Token { | ... | @@ -344,11 +344,6 @@ pub fn next(t: *Tokenizer) !?Token { |
| 344 | 344 | ||
| 345 | '0'...'9' => return try t.tokenizeNumber(), | 345 | '0'...'9' => return try t.tokenizeNumber(), |
| 346 | 346 | ||
| 347 | '.' => switch (t.nextByte()) { | ||
| 348 | '0'...'9' => return try t.tokenizeNumber(), | ||
| 349 | else => return t.simpleToken(.dot), | ||
| 350 | }, | ||
| 351 | |||
| 352 | '/' => switch (t.nextByte()) { | 347 | '/' => switch (t.nextByte()) { |
| 353 | '=' => return t.simpleToken(.slash_equal), | 348 | '=' => return t.simpleToken(.slash_equal), |
| 354 | '/' => { | 349 | '/' => { |
| ... | @@ -490,6 +485,7 @@ pub fn next(t: *Tokenizer) !?Token { | ... | @@ -490,6 +485,7 @@ pub fn next(t: *Tokenizer) !?Token { |
| 490 | '{' => return t.simpleToken(.start_brace), | 485 | '{' => return t.simpleToken(.start_brace), |
| 491 | '}' => return t.simpleToken(.end_brace), | 486 | '}' => return t.simpleToken(.end_brace), |
| 492 | ',' => return t.simpleToken(.comma), | 487 | ',' => return t.simpleToken(.comma), |
| 488 | '.' => return t.simpleToken(.dot), | ||
| 493 | 489 | ||
| 494 | 0x80...0xFF => { | 490 | 0x80...0xFF => { |
| 495 | // need to decode utf-8 | 491 | // need to decode utf-8 |
| ... | @@ -1250,6 +1246,43 @@ test "basic tokenizer example" { | ... | @@ -1250,6 +1246,43 @@ test "basic tokenizer example" { |
| 1250 | try testing.expectEqual(null, (try tokenizer.next())); | 1246 | try testing.expectEqual(null, (try tokenizer.next())); |
| 1251 | } | 1247 | } |
| 1252 | 1248 | ||
| 1249 | test "field access" { | ||
| 1250 | const testing = std.testing; | ||
| 1251 | try unicode_props.init(); | ||
| 1252 | |||
| 1253 | const source = | ||
| 1254 | \\.0 hello.1.2 | ||
| 1255 | ; | ||
| 1256 | const expected_tokens = [_]Token{ | ||
| 1257 | .{ .tag = .kw_var, .offset = 0 }, | ||
| 1258 | .{ .tag = .id, .offset = 4 }, | ||
| 1259 | .{ .tag = .equal, .offset = 10 }, | ||
| 1260 | .{ .tag = .number_float, .offset = 12 }, | ||
| 1261 | .{ .tag = .number_int, .offset = 20 }, | ||
| 1262 | .{ .tag = .semi, .offset = 21 }, | ||
| 1263 | .{ .tag = .kw_pub, .offset = 42 }, | ||
| 1264 | .{ .tag = .kw_fn, .offset = 46 }, | ||
| 1265 | .{ .tag = .id, .offset = 49 }, | ||
| 1266 | .{ .tag = .start_paren, .offset = 54 }, | ||
| 1267 | .{ .tag = .colon, .offset = 55 }, | ||
| 1268 | .{ .tag = .plus, .offset = 56 }, | ||
| 1269 | .{ .tag = .id, .offset = 58 }, | ||
| 1270 | .{ .tag = .end_paren, .offset = 62 }, | ||
| 1271 | .{ .tag = .id, .offset = 64 }, | ||
| 1272 | .{ .tag = .kw_return, .offset = 67 }, | ||
| 1273 | .{ .tag = .id, .offset = 74 }, | ||
| 1274 | .{ .tag = .id, .offset = 77 }, | ||
| 1275 | .{ .tag = .string, .offset = 86 }, | ||
| 1276 | }; | ||
| 1277 | var tokenizer = Tokenizer{ .code = source }; | ||
| 1278 | for (expected_tokens) |tok| { | ||
| 1279 | tokenizer.skipWhitespace(); | ||
| 1280 | try testing.expectEqual(tok, (try tokenizer.next()).?); | ||
| 1281 | } | ||
| 1282 | tokenizer.skipWhitespace(); | ||
| 1283 | try testing.expectEqual(null, (try tokenizer.next())); | ||
| 1284 | } | ||
| 1285 | |||
| 1253 | test "string escapes" { | 1286 | test "string escapes" { |
| 1254 | try testStringDecode("hello world", "hello world"); | 1287 | try testStringDecode("hello world", "hello world"); |
| 1255 | try testStringDecode("hello\n\rw\x00or\x5ald", "hello\\n\\rw\\0or\\x5ald"); | 1288 | try testStringDecode("hello\n\rw\x00or\x5ald", "hello\\n\\rw\\0or\\x5ald"); |
src/core/Transform.zig+44-7| ... | @@ -1174,20 +1174,57 @@ fn transformExpression( | ... | @@ -1174,20 +1174,57 @@ fn transformExpression( |
| 1174 | var base_result: ResultType = .none; | 1174 | var base_result: ResultType = .none; |
| 1175 | const base = try t.transformExpression(ctx, &base_result, field.base) orelse return null; | 1175 | const base = try t.transformExpression(ctx, &base_result, field.base) orelse return null; |
| 1176 | 1176 | ||
| 1177 | // TODO: handle identifier escapes | 1177 | const rhs = switch (t.ast.tokens.items(.tag)[field.field_token]) { |
| 1178 | const id = t.ast.tokenSlice(field.field_token); | 1178 | .id => rhs: { |
| 1179 | const prop_name_str = try t.addSlicedString(id); | 1179 | const id = t.ast.tokenSlice(field.field_token); |
| 1180 | const id_decoded = try t.decodeIdentifier(id); | ||
| 1181 | const prop_name_str = try t.addSlicedString(id_decoded); | ||
| 1180 | 1182 | ||
| 1181 | const string_ref = try t.addRef(.string, .{ | 1183 | break :rhs try t.addRef(.string, .{ |
| 1184 | .src_node = ctx.nodeOffset(node), | ||
| 1185 | .extra_index = try t.extra.append(t.gpa, SlicedString, prop_name_str), | ||
| 1186 | }); | ||
| 1187 | }, | ||
| 1188 | .number_int => rhs: { | ||
| 1189 | const text = t.ast.tokenSlice(field.field_token); | ||
| 1190 | |||
| 1191 | break :rhs try t.addRef(.number, .{ | ||
| 1192 | .src_node = ctx.nodeOffset(node), | ||
| 1193 | .extra_index = try t.extra.append( | ||
| 1194 | t.gpa, | ||
| 1195 | Float, | ||
| 1196 | Float.encode(std.fmt.parseFloat(f64, text) catch @panic("TODO")), | ||
| 1197 | ), | ||
| 1198 | }); | ||
| 1199 | }, | ||
| 1200 | else => unreachable, // `Parse` filters out all other token tags | ||
| 1201 | }; | ||
| 1202 | |||
| 1203 | return try t.addRef(.field, .{ | ||
| 1182 | .src_node = ctx.nodeOffset(node), | 1204 | .src_node = ctx.nodeOffset(node), |
| 1183 | .extra_index = try t.extra.append(t.gpa, SlicedString, prop_name_str), | 1205 | .extra_index = try t.extra.append(t.gpa, Teg.Bin, .{ |
| 1206 | .lhs = base, | ||
| 1207 | .rhs = rhs, | ||
| 1208 | }), | ||
| 1184 | }); | 1209 | }); |
| 1210 | }, | ||
| 1211 | |||
| 1212 | .index => { | ||
| 1213 | const bin = t.ast.nodeData(node).bin; | ||
| 1214 | |||
| 1215 | var lhs_result: ResultType = .none; | ||
| 1216 | const lhs = try t.transformExpression(ctx, &lhs_result, bin.lhs) orelse | ||
| 1217 | return null; | ||
| 1218 | |||
| 1219 | var rhs_result: ResultType = .none; | ||
| 1220 | const rhs = try t.transformExpression(ctx, &rhs_result, bin.rhs) orelse | ||
| 1221 | return null; | ||
| 1185 | 1222 | ||
| 1186 | return try t.addRef(.field, .{ | 1223 | return try t.addRef(.field, .{ |
| 1187 | .src_node = ctx.nodeOffset(node), | 1224 | .src_node = ctx.nodeOffset(node), |
| 1188 | .extra_index = try t.extra.append(t.gpa, Teg.Bin, .{ | 1225 | .extra_index = try t.extra.append(t.gpa, Teg.Bin, .{ |
| 1189 | .lhs = base, | 1226 | .lhs = lhs, |
| 1190 | .rhs = string_ref, | 1227 | .rhs = rhs, |
| 1191 | }), | 1228 | }), |
| 1192 | }); | 1229 | }); |
| 1193 | }, | 1230 | }, |
src/core/array.zig+2-1| ... | @@ -168,7 +168,8 @@ pub fn EncodedData(comptime Tag: type, backing_type: type) type { | ... | @@ -168,7 +168,8 @@ pub fn EncodedData(comptime Tag: type, backing_type: type) type { |
| 168 | return @enumFromInt(@intFromEnum(data)); | 168 | return @enumFromInt(@intFromEnum(data)); |
| 169 | } | 169 | } |
| 170 | if (info == .Struct) { | 170 | if (info == .Struct) { |
| 171 | assert(backing_type == u64); | 171 | comptime assert(backing_type == u64); |
| 172 | comptime assert(@sizeOf(Data) == 8); | ||
| 172 | var fields: [2]u32 = undefined; | 173 | var fields: [2]u32 = undefined; |
| 173 | setAsU32(&fields, Data, 0, data); | 174 | setAsU32(&fields, Data, 0, data); |
| 174 | return @enumFromInt(@as(backing_type, @bitCast(fields))); | 175 | return @enumFromInt(@as(backing_type, @bitCast(fields))); |
src/core/js/NodeGen.zig+36-3| ... | @@ -2,7 +2,7 @@ | ... | @@ -2,7 +2,7 @@ |
| 2 | gpa: Allocator, | 2 | gpa: Allocator, |
| 3 | arena: Allocator, | 3 | arena: Allocator, |
| 4 | air: *const Air, | 4 | air: *const Air, |
| 5 | ip: *const InternPool, | 5 | ip: *InternPool, |
| 6 | 6 | ||
| 7 | nodes: Node.List, | 7 | nodes: Node.List, |
| 8 | extra: dod.ExtraStore(.js_tree, .@"var"), | 8 | extra: dod.ExtraStore(.js_tree, .@"var"), |
| ... | @@ -28,7 +28,7 @@ const MapEntry = union { | ... | @@ -28,7 +28,7 @@ const MapEntry = union { |
| 28 | }, | 28 | }, |
| 29 | }; | 29 | }; |
| 30 | 30 | ||
| 31 | pub fn init(gpa: Allocator, arena: Allocator, ip: *const InternPool, air: *const Air) !NodeGen { | 31 | pub fn init(gpa: Allocator, arena: Allocator, ip: *InternPool, air: *const Air) !NodeGen { |
| 32 | const t = tracer.trace(@src()); | 32 | const t = tracer.trace(@src()); |
| 33 | defer t.end(); | 33 | defer t.end(); |
| 34 | 34 | ||
| ... | @@ -171,6 +171,7 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void { | ... | @@ -171,6 +171,7 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void { |
| 171 | .sub, | 171 | .sub, |
| 172 | .mul, | 172 | .mul, |
| 173 | .div, | 173 | .div, |
| 174 | .index, | ||
| 174 | => |tag| { | 175 | => |tag| { |
| 175 | const data: Air.Bin = c.air.instData(tag, inst); | 176 | const data: Air.Bin = c.air.instData(tag, inst); |
| 176 | const node = try c.add(@field(Tree.Tag, @tagName(tag)), .{ | 177 | const node = try c.add(@field(Tree.Tag, @tagName(tag)), .{ |
| ... | @@ -273,6 +274,18 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void { | ... | @@ -273,6 +274,18 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void { |
| 273 | try c.markExpression(inst, node); | 274 | try c.markExpression(inst, node); |
| 274 | }, | 275 | }, |
| 275 | 276 | ||
| 277 | .array_len => { | ||
| 278 | const data: Air.Un = c.air.instData(.array_len, inst); | ||
| 279 | const node = try c.add(.field, .{ | ||
| 280 | .base = try c.fetchInstExpression( | ||
| 281 | data.operand.index() orelse | ||
| 282 | unreachable, // TODO: make a second version of Un | ||
| 283 | ), | ||
| 284 | .field = try c.ip.getOrPutString(c.gpa, "length", .no_embedded_nulls), | ||
| 285 | }); | ||
| 286 | try c.markExpression(inst, node); | ||
| 287 | }, | ||
| 288 | |||
| 276 | inline .call, .js_new => |tag| { | 289 | inline .call, .js_new => |tag| { |
| 277 | const data: Air.Call = c.air.instData(tag, inst); | 290 | const data: Air.Call = c.air.instData(tag, inst); |
| 278 | const call_data = data.call_data.get(c.air); | 291 | const call_data = data.call_data.get(c.air); |
| ... | @@ -422,7 +435,26 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void { | ... | @@ -422,7 +435,26 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void { |
| 422 | try c.markExpression(inst, node); | 435 | try c.markExpression(inst, node); |
| 423 | }, | 436 | }, |
| 424 | 437 | ||
| 425 | else => |tag| std.debug.panic("TODO JS codeGenInst for .{s}", .{@tagName(tag)}), | 438 | .array_init => { |
| 439 | const struct_init: Air.ArrayInit = c.air.instData(.array_init, inst); | ||
| 440 | |||
| 441 | const items = struct_init.list.get(c.air).slice(c.air); | ||
| 442 | const store_offset = try c.extra.alloc(c.gpa, items.len); | ||
| 443 | |||
| 444 | for ( | ||
| 445 | items, | ||
| 446 | store_offset.., | ||
| 447 | ) |item, item_i| { | ||
| 448 | const in = try c.fetchExpression(item); | ||
| 449 | c.extra.list.items[item_i] = @intFromEnum(in); | ||
| 450 | } | ||
| 451 | |||
| 452 | const node = try c.add(.array, .{ | ||
| 453 | .start = store_offset, | ||
| 454 | .len = @intCast(items.len), | ||
| 455 | }); | ||
| 456 | try c.markExpression(inst, node); | ||
| 457 | }, | ||
| 426 | } | 458 | } |
| 427 | } | 459 | } |
| 428 | 460 | ||
| ... | @@ -676,6 +708,7 @@ fn canLowerToExpression(c: *const NodeGen, inst: Air.Inst.Ref) bool { | ... | @@ -676,6 +708,7 @@ fn canLowerToExpression(c: *const NodeGen, inst: Air.Inst.Ref) bool { |
| 676 | .struct_init, | 708 | .struct_init, |
| 677 | .array_init, | 709 | .array_init, |
| 678 | .index, | 710 | .index, |
| 711 | .array_len, | ||
| 679 | => true, | 712 | => true, |
| 680 | 713 | ||
| 681 | // JavaScript variable declarations require statements. | 714 | // JavaScript variable declarations require statements. |
test/behavior/array created+12| ... | @@ -0,0 +1,12 @@ | ||
| 1 | fn i(a: int) -> int { a } | ||
| 2 | |||
| 3 | let array = [i(9), i(3), i(8), i(3), i(-1)]; | ||
| 4 | |||
| 5 | let assertEqual = @js("assertEqual"); | ||
| 6 | |||
| 7 | assertEqual(array.len, i(5)); | ||
| 8 | assertEqual(array[0], i(9)); | ||
| 9 | assertEqual(array[1], i(3)); | ||
| 10 | assertEqual(array.2, i(8)); | ||
| 11 | assertEqual(array.3, i(3)); | ||
| 12 | assertEqual(array[i(1) + 2] + array[i(2) + 2], i(2)); | ||