authorchloe caruso <git@paperclover.net> 2024-07-29 01:15:50-07:00
committerchloe caruso <git@paperclover.net> 2024-07-29 01:15:50-07:00
log1a86087b164d2b3d8fbdfa9b09dae0bcef1b333c
tree2b80758a08373445412b0de6614a9eec7e5d6176
parenta48b4aaf01270f1fd4f562c8265e4edbe910db19

add array index syntax


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 {
193194
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,
3232
33 array_len,
34
33 index,35 index,
3436
35 // bin37 // 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 }
100105
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 }
104109
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 {
279284
280pub const ArrayInit = struct {285pub const ArrayInit = struct {
281 ty: Type,286 ty: Type,
282 list: RefList,287 list: ExtraIndex(RefList),
283};288};
284289
285pub const FieldStruct = struct {290pub 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,
332338
333 // For many instructions, the type is stored on it's data directly. This339 // 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 report186 // 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,
108109
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 };
901906
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 {
934939
935 const index = try p.tryParseExpression(.expression);940 const index = try p.tryParseExpression(.expression);
936941
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,
771771
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 }
779792
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 }
787805
...@@ -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 }
392424
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);
395427
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 instantiation960 // 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;
12791309
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}
16881718
1689fn addWarn(s: *Sema, comptime kind: Report.Err.FieldEnum, data: std.meta.FieldType(Report.Err, kind)) !void {1719fn 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}
16921722
1693fn addError(s: *Sema, comptime kind: Report.Err.FieldEnum, data: std.meta.FieldType(Report.Err, kind)) !void {1723fn 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}
16961726
1697fn fail(s: *Sema, comptime kind: Report.Err.FieldEnum, data: std.meta.FieldType(Report.Err, kind)) Error {1727fn 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}
17011731
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 {
344344
345 '0'...'9' => return try t.tokenizeNumber(),345 '0'...'9' => return try t.tokenizeNumber(),
346346
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),
493489
494 0x80...0xFF => {490 0x80...0xFF => {
495 // need to decode utf-8491 // 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}
12521248
1249test "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
1253test "string escapes" {1286test "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;
11761176
1177 // TODO: handle identifier escapes1177 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);
11801182
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;
11851222
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 @@
2gpa: Allocator,2gpa: Allocator,
3arena: Allocator,3arena: Allocator,
4air: *const Air,4air: *const Air,
5ip: *const InternPool,5ip: *InternPool,
66
7nodes: Node.List,7nodes: Node.List,
8extra: dod.ExtraStore(.js_tree, .@"var"),8extra: dod.ExtraStore(.js_tree, .@"var"),
...@@ -28,7 +28,7 @@ const MapEntry = union {...@@ -28,7 +28,7 @@ const MapEntry = union {
28 },28 },
29};29};
3030
31pub fn init(gpa: Allocator, arena: Allocator, ip: *const InternPool, air: *const Air) !NodeGen {31pub 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();
3434
...@@ -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 },
275276
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 },
424437
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}
428460
...@@ -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,
680713
681 // JavaScript variable declarations require statements.714 // JavaScript variable declarations require statements.
test/behavior/array created+12
...@@ -0,0 +1,12 @@
1fn i(a: int) -> int { a }
2
3let array = [i(9), i(3), i(8), i(3), i(-1)];
4
5let assertEqual = @js("assertEqual");
6
7assertEqual(array.len, i(5));
8assertEqual(array[0], i(9));
9assertEqual(array[1], i(3));
10assertEqual(array.2, i(8));
11assertEqual(array.3, i(3));
12assertEqual(array[i(1) + 2] + array[i(2) + 2], i(2));