| author | |
| committer | |
| log | d44222bc0e79efa9dd6bfe4c9922c0bc97ad63bc |
| tree | f7dfdeb3bcbfa9c2594d06d7b711a964f10dd8e8 |
| parent | e52a575edb42a6339b121ed77629b3874d1fe8f4 |
| signature | Commit is signed but in an unrecognized format. |
10 files changed, 542 insertions(+), 270 deletions(-)
src/core/Air.Dump.zig+16| ... | @@ -379,6 +379,22 @@ pub const State = struct { | ... | @@ -379,6 +379,22 @@ pub const State = struct { |
| 379 | } | 379 | } |
| 380 | try w.writeAll("'"); | 380 | try w.writeAll("'"); |
| 381 | }, | 381 | }, |
| 382 | .tuple_type => |tuple_type| { | ||
| 383 | try w.writeAll("("); | ||
| 384 | for (tuple_type.items, 0..) |item, i| { | ||
| 385 | if (i != 0) try w.writeAll(", "); | ||
| 386 | try s.dumpValue(w, item.value()); | ||
| 387 | } | ||
| 388 | try w.writeAll(")"); | ||
| 389 | }, | ||
| 390 | .tuple_value => |tuple_value| { | ||
| 391 | try w.writeAll("("); | ||
| 392 | for (tuple_value.items, 0..) |item, i| { | ||
| 393 | if (i != 0) try w.writeAll(", "); | ||
| 394 | try s.dumpValue(w, item); | ||
| 395 | } | ||
| 396 | try w.writeAll(")"); | ||
| 397 | }, | ||
| 382 | } | 398 | } |
| 383 | } | 399 | } |
| 384 | 400 |
src/core/InternPool.zig+118-3| ... | @@ -101,6 +101,8 @@ pub const Tag = enum(u8) { | ... | @@ -101,6 +101,8 @@ pub const Tag = enum(u8) { |
| 101 | struct_type, | 101 | struct_type, |
| 102 | /// An array type | 102 | /// An array type |
| 103 | array_type, | 103 | array_type, |
| 104 | /// A tuple type | ||
| 105 | tuple_type, | ||
| 104 | /// A type that can be represented with only an enum tag. | 106 | /// A type that can be represented with only an enum tag. |
| 105 | simple_type, | 107 | simple_type, |
| 106 | 108 | ||
| ... | @@ -129,6 +131,8 @@ pub const Tag = enum(u8) { | ... | @@ -129,6 +131,8 @@ pub const Tag = enum(u8) { |
| 129 | external_value, | 131 | external_value, |
| 130 | /// A value masked as "opaque" type | 132 | /// A value masked as "opaque" type |
| 131 | opaque_value, | 133 | opaque_value, |
| 134 | /// A tuple value | ||
| 135 | tuple_value, | ||
| 132 | 136 | ||
| 133 | pub fn Data(comptime tag: Tag) type { | 137 | pub fn Data(comptime tag: Tag) type { |
| 134 | return switch (tag) { | 138 | return switch (tag) { |
| ... | @@ -161,6 +165,8 @@ pub const Tag = enum(u8) { | ... | @@ -161,6 +165,8 @@ pub const Tag = enum(u8) { |
| 161 | .array_type => ArrayType, | 165 | .array_type => ArrayType, |
| 162 | .array_value => ArrayValue, | 166 | .array_value => ArrayValue, |
| 163 | .external_value => ExternalValue, | 167 | .external_value => ExternalValue, |
| 168 | .tuple_type => TupleType, | ||
| 169 | .tuple_value => TupleValue, | ||
| 164 | }; | 170 | }; |
| 165 | } | 171 | } |
| 166 | 172 | ||
| ... | @@ -199,6 +205,20 @@ pub const Tag = enum(u8) { | ... | @@ -199,6 +205,20 @@ pub const Tag = enum(u8) { |
| 199 | items_len: u32, | 205 | items_len: u32, |
| 200 | }; | 206 | }; |
| 201 | 207 | ||
| 208 | /// Trailing: | ||
| 209 | /// 0. item: Type. Repeated per .items_len | ||
| 210 | pub const TupleType = struct { | ||
| 211 | items_len: u32, | ||
| 212 | }; | ||
| 213 | |||
| 214 | /// Trailing: | ||
| 215 | /// 0. value: Value. Repeated per .items_len | ||
| 216 | pub const TupleValue = struct { | ||
| 217 | /// This is the tuple type, not the item type | ||
| 218 | type: Type, | ||
| 219 | items_len: u32, | ||
| 220 | }; | ||
| 221 | |||
| 202 | /// Trailing: | 222 | /// Trailing: |
| 203 | /// 0. value: Value. Repeated .ty.structInfo().field_len | 223 | /// 0. value: Value. Repeated .ty.structInfo().field_len |
| 204 | pub const StructValue = struct { | 224 | pub const StructValue = struct { |
| ... | @@ -216,9 +236,9 @@ pub const Tag = enum(u8) { | ... | @@ -216,9 +236,9 @@ pub const Tag = enum(u8) { |
| 216 | /// This is an index into .items | 236 | /// This is an index into .items |
| 217 | pub const Value = enum(u32) { | 237 | pub const Value = enum(u32) { |
| 218 | pub const first_type: Value = .opaque_type; | 238 | pub const first_type: Value = .opaque_type; |
| 219 | pub const last_type: Value = .numeric_literal_array_type; | 239 | pub const last_type: Value = .empty_tuple_type; |
| 220 | pub const first_value: Value = .void; | 240 | pub const first_value: Value = .void; |
| 221 | pub const last_value: Value = .empty_array; | 241 | pub const last_value: Value = .empty_tuple; |
| 222 | 242 | ||
| 223 | opaque_type, | 243 | opaque_type, |
| 224 | bool_type, | 244 | bool_type, |
| ... | @@ -230,6 +250,7 @@ pub const Value = enum(u32) { | ... | @@ -230,6 +250,7 @@ pub const Value = enum(u32) { |
| 230 | numeric_literal_type, | 250 | numeric_literal_type, |
| 231 | numeric_literal_array_type, | 251 | numeric_literal_array_type, |
| 232 | never_array_type, | 252 | never_array_type, |
| 253 | empty_tuple_type, | ||
| 233 | 254 | ||
| 234 | void, | 255 | void, |
| 235 | bool_true, | 256 | bool_true, |
| ... | @@ -239,6 +260,7 @@ pub const Value = enum(u32) { | ... | @@ -239,6 +260,7 @@ pub const Value = enum(u32) { |
| 239 | nan, | 260 | nan, |
| 240 | empty_string, | 261 | empty_string, |
| 241 | empty_array, | 262 | empty_array, |
| 263 | empty_tuple, | ||
| 242 | 264 | ||
| 243 | none = std.math.maxInt(u32), | 265 | none = std.math.maxInt(u32), |
| 244 | _, | 266 | _, |
| ... | @@ -316,6 +338,7 @@ pub const Value = enum(u32) { | ... | @@ -316,6 +338,7 @@ pub const Value = enum(u32) { |
| 316 | .simple_type, | 338 | .simple_type, |
| 317 | .struct_type, | 339 | .struct_type, |
| 318 | .array_type, | 340 | .array_type, |
| 341 | .tuple_type, | ||
| 319 | => .type, | 342 | => .type, |
| 320 | 343 | ||
| 321 | .simple_value => switch (value.valueData(ip, .simple_value)) { | 344 | .simple_value => switch (value.valueData(ip, .simple_value)) { |
| ... | @@ -332,6 +355,7 @@ pub const Value = enum(u32) { | ... | @@ -332,6 +355,7 @@ pub const Value = enum(u32) { |
| 332 | .struct_value => value.toKey(ip).struct_value.ty, | 355 | .struct_value => value.toKey(ip).struct_value.ty, |
| 333 | .array_value => value.toKey(ip).array_value.type, | 356 | .array_value => value.toKey(ip).array_value.type, |
| 334 | .external_value, .opaque_value => .@"opaque", | 357 | .external_value, .opaque_value => .@"opaque", |
| 358 | .tuple_value => value.toKey(ip).tuple_value.type, | ||
| 335 | }; | 359 | }; |
| 336 | } | 360 | } |
| 337 | 361 | ||
| ... | @@ -345,6 +369,7 @@ pub const Value = enum(u32) { | ... | @@ -345,6 +369,7 @@ pub const Value = enum(u32) { |
| 345 | .struct_type, | 369 | .struct_type, |
| 346 | .simple_type, | 370 | .simple_type, |
| 347 | .array_type, | 371 | .array_type, |
| 372 | .tuple_type, | ||
| 348 | => @enumFromInt(@intFromEnum(value)), | 373 | => @enumFromInt(@intFromEnum(value)), |
| 349 | 374 | ||
| 350 | // exception: void is a value AND a type | 375 | // exception: void is a value AND a type |
| ... | @@ -395,6 +420,7 @@ pub const Type = enum(u32) { | ... | @@ -395,6 +420,7 @@ pub const Type = enum(u32) { |
| 395 | numeric_literal = @intFromEnum(Value.numeric_literal_type), | 420 | numeric_literal = @intFromEnum(Value.numeric_literal_type), |
| 396 | numeric_literal_array = @intFromEnum(Value.numeric_literal_array_type), | 421 | numeric_literal_array = @intFromEnum(Value.numeric_literal_array_type), |
| 397 | never_array = @intFromEnum(Value.never_array_type), | 422 | never_array = @intFromEnum(Value.never_array_type), |
| 423 | empty_tuple = @intFromEnum(Value.empty_tuple_type), | ||
| 398 | void = @intFromEnum(Value.void), | 424 | void = @intFromEnum(Value.void), |
| 399 | _, | 425 | _, |
| 400 | 426 | ||
| ... | @@ -404,6 +430,7 @@ pub const Type = enum(u32) { | ... | @@ -404,6 +430,7 @@ pub const Type = enum(u32) { |
| 404 | struct_type = @intFromEnum(InternPool.Tag.struct_type), | 430 | struct_type = @intFromEnum(InternPool.Tag.struct_type), |
| 405 | array_type = @intFromEnum(InternPool.Tag.array_type), | 431 | array_type = @intFromEnum(InternPool.Tag.array_type), |
| 406 | simple_type = @intFromEnum(InternPool.Tag.simple_type), | 432 | simple_type = @intFromEnum(InternPool.Tag.simple_type), |
| 433 | tuple_type = @intFromEnum(InternPool.Tag.tuple_type), | ||
| 407 | }; | 434 | }; |
| 408 | 435 | ||
| 409 | /// An array of `Index` existing within the `extra` array. | 436 | /// An array of `Index` existing within the `extra` array. |
| ... | @@ -446,6 +473,11 @@ pub const Type = enum(u32) { | ... | @@ -446,6 +473,11 @@ pub const Type = enum(u32) { |
| 446 | return ip.valueToKey(ty.value()).array_type; | 473 | return ip.valueToKey(ty.value()).array_type; |
| 447 | } | 474 | } |
| 448 | 475 | ||
| 476 | pub fn toTupleType(ty: Type, ip: *const InternPool) ?Key.TupleType { | ||
| 477 | if (ty.tag(ip) != .tuple_type) return null; | ||
| 478 | return ip.valueToKey(ty.value()).tuple_type; | ||
| 479 | } | ||
| 480 | |||
| 449 | /// If it is possible to represent this type at runtime. Some types do not | 481 | /// If it is possible to represent this type at runtime. Some types do not |
| 450 | /// have a defined representation, such as structs (to allow field renaming, unwrapping). | 482 | /// have a defined representation, such as structs (to allow field renaming, unwrapping). |
| 451 | pub fn hasPossibleRepresentation(ty: Type, ip: *const InternPool) bool { | 483 | pub fn hasPossibleRepresentation(ty: Type, ip: *const InternPool) bool { |
| ... | @@ -466,6 +498,15 @@ pub const Type = enum(u32) { | ... | @@ -466,6 +498,15 @@ pub const Type = enum(u32) { |
| 466 | .struct_type, | 498 | .struct_type, |
| 467 | .function_type, | 499 | .function_type, |
| 468 | => true, | 500 | => true, |
| 501 | .tuple_type => { | ||
| 502 | const tuple_type = ty.toTupleType(ip).?; | ||
| 503 | for (tuple_type.items) |item| { | ||
| 504 | if (!item.hasPossibleRepresentation(ip)) { | ||
| 505 | return false; | ||
| 506 | } | ||
| 507 | } | ||
| 508 | return true; | ||
| 509 | }, | ||
| 469 | .array_type => ty.toArrayType(ip).?.item_type.hasPossibleRepresentation(ip), | 510 | .array_type => ty.toArrayType(ip).?.item_type.hasPossibleRepresentation(ip), |
| 470 | }, | 511 | }, |
| 471 | }; | 512 | }; |
| ... | @@ -490,6 +531,7 @@ pub const Type = enum(u32) { | ... | @@ -490,6 +531,7 @@ pub const Type = enum(u32) { |
| 490 | .simple_type => unreachable, // all simple types are already printed | 531 | .simple_type => unreachable, // all simple types are already printed |
| 491 | .struct_type, | 532 | .struct_type, |
| 492 | .function_type, | 533 | .function_type, |
| 534 | .tuple_type, | ||
| 493 | => false, | 535 | => false, |
| 494 | .array_type => ty.toArrayType(ip).?.item_type.hasDefinedRepresentation(ip), | 536 | .array_type => ty.toArrayType(ip).?.item_type.hasDefinedRepresentation(ip), |
| 495 | }, | 537 | }, |
| ... | @@ -546,6 +588,14 @@ pub const Type = enum(u32) { | ... | @@ -546,6 +588,14 @@ pub const Type = enum(u32) { |
| 546 | const array_type = ty.toArrayType(ip).?; | 588 | const array_type = ty.toArrayType(ip).?; |
| 547 | try writer.print("{}", .{array_type.item_type.displayName(ip)}); | 589 | try writer.print("{}", .{array_type.item_type.displayName(ip)}); |
| 548 | }, | 590 | }, |
| 591 | .tuple_type => { | ||
| 592 | try writer.writeAll("("); | ||
| 593 | for (ty.toTupleType(ip).?.items, 0..) |item, i| { | ||
| 594 | if (i != 0) try writer.writeAll(", "); | ||
| 595 | try writer.print("{}", .{item.displayName(ip)}); | ||
| 596 | } | ||
| 597 | try writer.writeAll(")"); | ||
| 598 | }, | ||
| 549 | } | 599 | } |
| 550 | } | 600 | } |
| 551 | }; | 601 | }; |
| ... | @@ -584,6 +634,7 @@ pub const static_keys = [_]Key{ | ... | @@ -584,6 +634,7 @@ pub const static_keys = [_]Key{ |
| 584 | SimpleType.numeric_literal.key(), | 634 | SimpleType.numeric_literal.key(), |
| 585 | .{ .array_type = .{ .item_type = Type.numeric_literal } }, | 635 | .{ .array_type = .{ .item_type = Type.numeric_literal } }, |
| 586 | .{ .array_type = .{ .item_type = Type.never } }, | 636 | .{ .array_type = .{ .item_type = Type.never } }, |
| 637 | .{ .tuple_type = .{ .items = &.{} } }, | ||
| 587 | 638 | ||
| 588 | SimpleValue.void.key(), | 639 | SimpleValue.void.key(), |
| 589 | SimpleValue.true.key(), | 640 | SimpleValue.true.key(), |
| ... | @@ -596,6 +647,10 @@ pub const static_keys = [_]Key{ | ... | @@ -596,6 +647,10 @@ pub const static_keys = [_]Key{ |
| 596 | .items = &.{}, | 647 | .items = &.{}, |
| 597 | } }, | 648 | } }, |
| 598 | .{ .string = "" }, | 649 | .{ .string = "" }, |
| 650 | .{ .tuple_value = .{ | ||
| 651 | .type = .empty_tuple, | ||
| 652 | .items = &.{}, | ||
| 653 | } }, | ||
| 599 | }; | 654 | }; |
| 600 | 655 | ||
| 601 | /// State that is mutable during semantic analysis. This data is not used for | 656 | /// State that is mutable during semantic analysis. This data is not used for |
| ... | @@ -876,11 +931,13 @@ pub const Key = union(enum) { | ... | @@ -876,11 +931,13 @@ pub const Key = union(enum) { |
| 876 | func: Func, | 931 | func: Func, |
| 877 | array_value: ArrayValue, | 932 | array_value: ArrayValue, |
| 878 | opaque_value: Value, | 933 | opaque_value: Value, |
| 934 | tuple_value: TupleValue, | ||
| 879 | // Types | 935 | // Types |
| 880 | simple_type: SimpleType, | 936 | simple_type: SimpleType, |
| 881 | function_type: FunctionType, | 937 | function_type: FunctionType, |
| 882 | struct_type: StructType, | 938 | struct_type: StructType, |
| 883 | array_type: ArrayType, | 939 | array_type: ArrayType, |
| 940 | tuple_type: TupleType, | ||
| 884 | external_value: ExternalValue, | 941 | external_value: ExternalValue, |
| 885 | 942 | ||
| 886 | const Adapter = struct { | 943 | const Adapter = struct { |
| ... | @@ -946,6 +1003,14 @@ pub const Key = union(enum) { | ... | @@ -946,6 +1003,14 @@ pub const Key = union(enum) { |
| 946 | } | 1003 | } |
| 947 | }; | 1004 | }; |
| 948 | 1005 | ||
| 1006 | pub const TupleType = struct { | ||
| 1007 | items: []const Type, | ||
| 1008 | |||
| 1009 | pub fn hash(self: TupleType, hasher: *Hash) void { | ||
| 1010 | std.hash.autoHashStrat(hasher, self.items, .Deep); | ||
| 1011 | } | ||
| 1012 | }; | ||
| 1013 | |||
| 949 | pub const ExternalValue = struct { | 1014 | pub const ExternalValue = struct { |
| 950 | /// A list of strings to generate the property access | 1015 | /// A list of strings to generate the property access |
| 951 | /// so `console.log` would be .{ "console", "log" } | 1016 | /// so `console.log` would be .{ "console", "log" } |
| ... | @@ -967,6 +1032,17 @@ pub const Key = union(enum) { | ... | @@ -967,6 +1032,17 @@ pub const Key = union(enum) { |
| 967 | } | 1032 | } |
| 968 | }; | 1033 | }; |
| 969 | 1034 | ||
| 1035 | pub const TupleValue = struct { | ||
| 1036 | /// This is the tuple type, not the item type | ||
| 1037 | type: Type, | ||
| 1038 | items: []const Value, | ||
| 1039 | |||
| 1040 | pub fn hash(self: TupleValue, hasher: *Hash) void { | ||
| 1041 | std.hash.autoHash(hasher, self.type); | ||
| 1042 | std.hash.autoHashStrat(hasher, self.items, .Deep); | ||
| 1043 | } | ||
| 1044 | }; | ||
| 1045 | |||
| 970 | pub const Func = struct { | 1046 | pub const Func = struct { |
| 971 | ty: Type, | 1047 | ty: Type, |
| 972 | owner_decl: DeclIndex, | 1048 | owner_decl: DeclIndex, |
| ... | @@ -1008,6 +1084,8 @@ pub const Key = union(enum) { | ... | @@ -1008,6 +1084,8 @@ pub const Key = union(enum) { |
| 1008 | .array_value, | 1084 | .array_value, |
| 1009 | .array_type, | 1085 | .array_type, |
| 1010 | .external_value, | 1086 | .external_value, |
| 1087 | .tuple_type, | ||
| 1088 | .tuple_value, | ||
| 1011 | => |x| { | 1089 | => |x| { |
| 1012 | var hasher = Hash.init(seed); | 1090 | var hasher = Hash.init(seed); |
| 1013 | x.hash(&hasher); | 1091 | x.hash(&hasher); |
| ... | @@ -1047,7 +1125,7 @@ pub const Key = union(enum) { | ... | @@ -1047,7 +1125,7 @@ pub const Key = union(enum) { |
| 1047 | return false; | 1125 | return false; |
| 1048 | }, | 1126 | }, |
| 1049 | .struct_type => { | 1127 | .struct_type => { |
| 1050 | return false; // structs are nomial | 1128 | return false; // structs are nominal |
| 1051 | }, | 1129 | }, |
| 1052 | .struct_value => |a_info| { | 1130 | .struct_value => |a_info| { |
| 1053 | const b_info = b.struct_value; | 1131 | const b_info = b.struct_value; |
| ... | @@ -1066,6 +1144,15 @@ pub const Key = union(enum) { | ... | @@ -1066,6 +1144,15 @@ pub const Key = union(enum) { |
| 1066 | const b_info = b.external_value; | 1144 | const b_info = b.external_value; |
| 1067 | return std.mem.eql(NullTerminatedString, a_info.key, b_info.key); | 1145 | return std.mem.eql(NullTerminatedString, a_info.key, b_info.key); |
| 1068 | }, | 1146 | }, |
| 1147 | .tuple_type => |a_info| { | ||
| 1148 | const b_info = b.tuple_type; | ||
| 1149 | return a_info.items.len == b_info.items.len and std.mem.eql(Type, a_info.items, b_info.items); | ||
| 1150 | }, | ||
| 1151 | .tuple_value => |a_info| { | ||
| 1152 | const b_info = b.tuple_value; | ||
| 1153 | return a_info.type == b_info.type and | ||
| 1154 | std.mem.eql(Value, a_info.items, b_info.items); | ||
| 1155 | }, | ||
| 1069 | }; | 1156 | }; |
| 1070 | } | 1157 | } |
| 1071 | }; | 1158 | }; |
| ... | @@ -1336,6 +1423,19 @@ pub fn valueToKey(ip: *const InternPool, index: Value) Key { | ... | @@ -1336,6 +1423,19 @@ pub fn valueToKey(ip: *const InternPool, index: Value) Key { |
| 1336 | .key = trail.readNullTerminatedStringSlice(payload.key_count).get(ip), | 1423 | .key = trail.readNullTerminatedStringSlice(payload.key_count).get(ip), |
| 1337 | } }; | 1424 | } }; |
| 1338 | }, | 1425 | }, |
| 1426 | .tuple_type => { | ||
| 1427 | const payload: Tag.TupleType, var trail = data.decode(.tuple_type, item.tag).getTrail(ip); | ||
| 1428 | return .{ .tuple_type = .{ | ||
| 1429 | .items = trail.readTypeSlice(payload.items_len).get(ip), | ||
| 1430 | } }; | ||
| 1431 | }, | ||
| 1432 | .tuple_value => { | ||
| 1433 | const payload: Tag.TupleValue, var trail = data.decode(.tuple_value, item.tag).getTrail(ip); | ||
| 1434 | return .{ .tuple_value = .{ | ||
| 1435 | .type = payload.type, | ||
| 1436 | .items = trail.readValueSlice(payload.items_len).get(ip), | ||
| 1437 | } }; | ||
| 1438 | }, | ||
| 1339 | }; | 1439 | }; |
| 1340 | } | 1440 | } |
| 1341 | 1441 | ||
| ... | @@ -1439,6 +1539,21 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value { | ... | @@ -1439,6 +1539,21 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value { |
| 1439 | trail.writeSlice(s.key); | 1539 | trail.writeSlice(s.key); |
| 1440 | break :brk Item.encode(.external_value, extra); | 1540 | break :brk Item.encode(.external_value, extra); |
| 1441 | }, | 1541 | }, |
| 1542 | .tuple_type => |s| brk: { | ||
| 1543 | const extra, var trail = try ip.extra.appendTrailing(gpa, Tag.TupleType, .{ | ||
| 1544 | .items_len = @intCast(s.items.len), | ||
| 1545 | }, s.items.len); | ||
| 1546 | trail.writeSlice(s.items); | ||
| 1547 | break :brk Item.encode(.tuple_type, extra); | ||
| 1548 | }, | ||
| 1549 | .tuple_value => |s| brk: { | ||
| 1550 | const extra, var trail = try ip.extra.appendTrailing(gpa, Tag.TupleValue, .{ | ||
| 1551 | .type = s.type, | ||
| 1552 | .items_len = @intCast(s.items.len), | ||
| 1553 | }, s.items.len); | ||
| 1554 | trail.writeSlice(s.items); | ||
| 1555 | break :brk Item.encode(.tuple_value, extra); | ||
| 1556 | }, | ||
| 1442 | }; | 1557 | }; |
| 1443 | ip.items.appendAssumeCapacity(item); | 1558 | ip.items.appendAssumeCapacity(item); |
| 1444 | const index: Value = @enumFromInt(ip.items.len - 1); | 1559 | const index: Value = @enumFromInt(ip.items.len - 1); |
src/core/Parse.zig+296-258| ... | @@ -104,15 +104,216 @@ pub const ExpectedContext = enum { | ... | @@ -104,15 +104,216 @@ pub const ExpectedContext = enum { |
| 104 | } | 104 | } |
| 105 | }; | 105 | }; |
| 106 | 106 | ||
| 107 | const Operator = enum(u8) { | ||
| 108 | add = @intFromEnum(Token.Tag.plus), | ||
| 109 | sub = @intFromEnum(Token.Tag.minus), | ||
| 110 | mul = @intFromEnum(Token.Tag.star), | ||
| 111 | div = @intFromEnum(Token.Tag.slash), | ||
| 112 | rem = @intFromEnum(Token.Tag.percent), | ||
| 113 | pow = @intFromEnum(Token.Tag.star_star), | ||
| 114 | @"and" = @intFromEnum(Token.Tag.and_and), | ||
| 115 | @"or" = @intFromEnum(Token.Tag.pipe_pipe), | ||
| 116 | bit_and = @intFromEnum(Token.Tag.@"and"), | ||
| 117 | bit_or = @intFromEnum(Token.Tag.pipe), | ||
| 118 | bit_xor = @intFromEnum(Token.Tag.caret), | ||
| 119 | shl = @intFromEnum(Token.Tag.lt_lt), | ||
| 120 | shr = @intFromEnum(Token.Tag.gt_gt), | ||
| 121 | eq = @intFromEnum(Token.Tag.equal_equal), | ||
| 122 | ne = @intFromEnum(Token.Tag.bang_equal), | ||
| 123 | lt = @intFromEnum(Token.Tag.lt), | ||
| 124 | le = @intFromEnum(Token.Tag.lt_equal), | ||
| 125 | gt = @intFromEnum(Token.Tag.gt), | ||
| 126 | ge = @intFromEnum(Token.Tag.gt_equal), | ||
| 127 | // Following operators are assignment operators | ||
| 128 | assign = @intFromEnum(Token.Tag.equal), | ||
| 129 | add_assign = @intFromEnum(Token.Tag.plus_equal), | ||
| 130 | sub_assign = @intFromEnum(Token.Tag.minus_equal), | ||
| 131 | mul_assign = @intFromEnum(Token.Tag.star_equal), | ||
| 132 | div_assign = @intFromEnum(Token.Tag.slash_equal), | ||
| 133 | rem_assign = @intFromEnum(Token.Tag.percent_equal), | ||
| 134 | pow_assign = @intFromEnum(Token.Tag.star_star_equal), | ||
| 135 | and_assign = @intFromEnum(Token.Tag.and_and_equal), | ||
| 136 | or_assign = @intFromEnum(Token.Tag.pipe_pipe_equal), | ||
| 137 | bit_and_assign = @intFromEnum(Token.Tag.and_equal), | ||
| 138 | bit_or_assign = @intFromEnum(Token.Tag.pipe_equal), | ||
| 139 | bit_xor_assign = @intFromEnum(Token.Tag.caret_equal), | ||
| 140 | shl_assign = @intFromEnum(Token.Tag.lt_lt_equal), | ||
| 141 | shr_assign = @intFromEnum(Token.Tag.gt_gt_equal), | ||
| 142 | |||
| 143 | const Direction = enum { ltr, rtl }; | ||
| 144 | |||
| 145 | /// Only the binary operator levels are actually used, | ||
| 146 | /// but all are listed for reference. | ||
| 147 | const Level = enum { | ||
| 148 | /// e. e[] e() | ||
| 149 | accessor, | ||
| 150 | /// new | ||
| 151 | new, | ||
| 152 | /// ++e, --e, !e, ~e, +e, -e, typeof, void, delete | ||
| 153 | prefix, | ||
| 154 | /// ** | ||
| 155 | exponentiation, | ||
| 156 | /// *, /, % | ||
| 157 | multiplicative, | ||
| 158 | /// +, - | ||
| 159 | additive, | ||
| 160 | /// <<, >>, >>> | ||
| 161 | bitwise_shift, | ||
| 162 | /// <, <=, >, >= | ||
| 163 | relational, | ||
| 164 | /// ==, != | ||
| 165 | equality, | ||
| 166 | /// & | ||
| 167 | bit_and, | ||
| 168 | /// ^ | ||
| 169 | bit_xor, | ||
| 170 | /// | | ||
| 171 | bit_or, | ||
| 172 | /// && | ||
| 173 | logical_and, | ||
| 174 | /// ||, ?? | ||
| 175 | logical_or, | ||
| 176 | /// =, +=, -=, *=, /=, %=, / <<=, >>=, >>>=, &=, ^=, |= | ||
| 177 | assign, | ||
| 178 | }; | ||
| 179 | |||
| 180 | pub fn direction(op: Operator) Direction { | ||
| 181 | return switch (op) { | ||
| 182 | .pow => .rtl, | ||
| 183 | |||
| 184 | .assign, | ||
| 185 | .add_assign, | ||
| 186 | .sub_assign, | ||
| 187 | .mul_assign, | ||
| 188 | .div_assign, | ||
| 189 | .rem_assign, | ||
| 190 | .pow_assign, | ||
| 191 | .and_assign, | ||
| 192 | .or_assign, | ||
| 193 | .bit_and_assign, | ||
| 194 | .bit_or_assign, | ||
| 195 | .bit_xor_assign, | ||
| 196 | .shl_assign, | ||
| 197 | .shr_assign, | ||
| 198 | => .rtl, | ||
| 199 | |||
| 200 | else => .ltr, | ||
| 201 | }; | ||
| 202 | } | ||
| 203 | |||
| 204 | pub fn level(op: Operator) Level { | ||
| 205 | return switch (op) { | ||
| 206 | .pow => .exponentiation, | ||
| 207 | .mul, .div, .rem => .multiplicative, | ||
| 208 | .add, .sub => .additive, | ||
| 209 | .shl, .shr => .bitwise_shift, | ||
| 210 | .lt, .le, .gt, .ge => .relational, | ||
| 211 | .eq, .ne => .equality, | ||
| 212 | .@"and" => .logical_and, | ||
| 213 | .@"or" => .logical_or, | ||
| 214 | .bit_and => .bit_and, | ||
| 215 | .bit_xor => .bit_xor, | ||
| 216 | .bit_or => .bit_or, | ||
| 217 | .assign, | ||
| 218 | .add_assign, | ||
| 219 | .sub_assign, | ||
| 220 | .mul_assign, | ||
| 221 | .div_assign, | ||
| 222 | .rem_assign, | ||
| 223 | .pow_assign, | ||
| 224 | .and_assign, | ||
| 225 | .or_assign, | ||
| 226 | .bit_and_assign, | ||
| 227 | .bit_or_assign, | ||
| 228 | .bit_xor_assign, | ||
| 229 | .shl_assign, | ||
| 230 | .shr_assign, | ||
| 231 | => .assign, | ||
| 232 | }; | ||
| 233 | } | ||
| 234 | |||
| 235 | const lut = brk: { | ||
| 236 | const token_tags = std.enums.values(Token.Tag); | ||
| 237 | const op_tags = std.enums.values(Operator); | ||
| 238 | |||
| 239 | var is_operator_lut: [token_tags.len]bool = .{false} ** token_tags.len; | ||
| 240 | for (op_tags) |op| { | ||
| 241 | is_operator_lut[@intFromEnum(op)] = true; | ||
| 242 | } | ||
| 243 | |||
| 244 | var ast_tag_lut: [token_tags.len]Node.Tag = .{.root} ** token_tags.len; | ||
| 245 | for (op_tags) |op| { | ||
| 246 | ast_tag_lut[@intFromEnum(op)] = @field(Node.Tag, @tagName(op)); | ||
| 247 | } | ||
| 248 | |||
| 249 | break :brk struct { | ||
| 250 | const is_operator = is_operator_lut; | ||
| 251 | const ast_tag = ast_tag_lut; | ||
| 252 | }; | ||
| 253 | }; | ||
| 254 | |||
| 255 | pub fn fromToken(tag: Token.Tag) ?Operator { | ||
| 256 | return if (lut.is_operator[@intFromEnum(tag)]) | ||
| 257 | @enumFromInt(@intFromEnum(tag)) | ||
| 258 | else | ||
| 259 | null; | ||
| 260 | } | ||
| 261 | |||
| 262 | pub fn astTag(op: Operator) Node.Tag { | ||
| 263 | const tag = lut.ast_tag[@intFromEnum(op)]; | ||
| 264 | assert(tag != .root); // invalid operator or lut construction | ||
| 265 | return tag; | ||
| 266 | } | ||
| 267 | |||
| 268 | pub fn isAssign(op: Operator) bool { | ||
| 269 | return switch (op) { | ||
| 270 | .assign, | ||
| 271 | .add_assign, | ||
| 272 | .sub_assign, | ||
| 273 | .mul_assign, | ||
| 274 | .div_assign, | ||
| 275 | .rem_assign, | ||
| 276 | .pow_assign, | ||
| 277 | .and_assign, | ||
| 278 | .or_assign, | ||
| 279 | .bit_and_assign, | ||
| 280 | .bit_or_assign, | ||
| 281 | .bit_xor_assign, | ||
| 282 | .shl_assign, | ||
| 283 | .shr_assign, | ||
| 284 | => true, | ||
| 285 | else => false, | ||
| 286 | }; | ||
| 287 | } | ||
| 288 | }; | ||
| 289 | |||
| 290 | const ParseExprMode = enum { | ||
| 291 | /// Standard parsing | ||
| 292 | expression, | ||
| 293 | /// Statements allow assignment binary operators in addition to other expressions. | ||
| 294 | statement, | ||
| 295 | /// This bans struct initializations so that parsing stops at a '{' | ||
| 296 | /// Example: "if hello {" must not be parsed as a struct initialization. | ||
| 297 | preceding_block, | ||
| 298 | }; | ||
| 299 | |||
| 300 | /// Some constructs are valid expressions as well as valid top-level | ||
| 301 | /// declarations. Top level declarations must have associated names, but the | ||
| 302 | /// anonymous versions of these must not have names. | ||
| 303 | const ExprParseContext = enum { | ||
| 304 | declaration, | ||
| 305 | expression, | ||
| 306 | }; | ||
| 307 | |||
| 308 | const TypePresence = enum { | ||
| 309 | types_optional, | ||
| 310 | types_required, | ||
| 311 | }; | ||
| 312 | |||
| 107 | /// When error.SyntaxError is returned, `p.err` is set to this as well as `p.pos` | 313 | /// When error.SyntaxError is returned, `p.err` is set to this as well as `p.pos` |
| 108 | /// When printing these, token list and Ast is not actually available, so full tokens | 314 | /// When printing these, token list and Ast is not actually available, so full tokens |
| 109 | /// are stored in the errors. | 315 | /// are stored in the errors. |
| 110 | pub const AstError = union(enum) { | 316 | pub const AstError = union(enum) { |
| 111 | // /// Failure during tokenization | ||
| 112 | // tokenization: struct { | ||
| 113 | // err: Tokenizer.TokenizeError, | ||
| 114 | // byte: ByteOffset, | ||
| 115 | // }, | ||
| 116 | /// Unexpected data, expected a token. | 317 | /// Unexpected data, expected a token. |
| 117 | unexpected_token: struct { | 318 | unexpected_token: struct { |
| 118 | expected: Token.Tag, | 319 | expected: Token.Tag, |
| ... | @@ -134,23 +335,6 @@ pub const AstError = union(enum) { | ... | @@ -134,23 +335,6 @@ pub const AstError = union(enum) { |
| 134 | 335 | ||
| 135 | pub fn build(err: AstError, ctx: *Report.MsgContext) !void { | 336 | pub fn build(err: AstError, ctx: *Report.MsgContext) !void { |
| 136 | switch (err) { | 337 | switch (err) { |
| 137 | // .tokenization => |data| { | ||
| 138 | // // TODO: consider making tokenizer emit AstError more directly, | ||
| 139 | // // so that it can just annotate start and ends | ||
| 140 | // try ctx.msg(switch (data.err) { | ||
| 141 | // error.UnterminatedString => .{ .codepoint_eol = data.byte }, | ||
| 142 | // else => .{ .codepoint = data.byte }, | ||
| 143 | // }, "{s}", .{switch (data.err) { | ||
| 144 | // error.OutOfMemory => unreachable, | ||
| 145 | // error.InvalidSyntax => "unexpected token", | ||
| 146 | // error.TrailingNumericSeparator => "trailing numeric separator", | ||
| 147 | // error.UnterminatedString => "unterminated string literal", | ||
| 148 | // error.InvalidEscapeSequence => "invalid escape sequence", | ||
| 149 | // error.InvalidEscapeCodepoint => "invalid escape codepoint", | ||
| 150 | // error.InvalidUtf8 => "file is not valid utf8", | ||
| 151 | // error.GitConflict => "git merge conflict encountered", | ||
| 152 | // }}); | ||
| 153 | // }, | ||
| 154 | .unexpected_token => |data| { | 338 | .unexpected_token => |data| { |
| 155 | try ctx.msg(.{ .token_stored = data.got }, "expected {s}, but found {s}", .{ | 339 | try ctx.msg(.{ .token_stored = data.got }, "expected {s}, but found {s}", .{ |
| 156 | data.expected.description(), | 340 | data.expected.description(), |
| ... | @@ -230,7 +414,8 @@ fn parseModuleMembers(p: *Parse) Error!SubList { | ... | @@ -230,7 +414,8 @@ fn parseModuleMembers(p: *Parse) Error!SubList { |
| 230 | .kw_pub, | 414 | .kw_pub, |
| 231 | => try p.scratch.append(p.gpa, try p.parseModuleDecl()), | 415 | => try p.scratch.append(p.gpa, try p.parseModuleDecl()), |
| 232 | 416 | ||
| 233 | .kw_struct => try p.scratch.append(p.gpa, try p.parseStructDecl()), | 417 | .kw_struct => try p.scratch.append(p.gpa, try p.parseStructDecl(.declaration)), |
| 418 | .kw_tuple => try p.scratch.append(p.gpa, try p.parseStructDecl(.declaration)), | ||
| 234 | 419 | ||
| 235 | .id => if (p.isContextualKeyword("test")) { | 420 | .id => if (p.isContextualKeyword("test")) { |
| 236 | try p.scratch.append(p.gpa, try p.parseTestDecl()); | 421 | try p.scratch.append(p.gpa, try p.parseTestDecl()); |
| ... | @@ -292,14 +477,6 @@ fn parseModuleDecl(p: *Parse) Error!NodeIndex { | ... | @@ -292,14 +477,6 @@ fn parseModuleDecl(p: *Parse) Error!NodeIndex { |
| 292 | }; | 477 | }; |
| 293 | } | 478 | } |
| 294 | 479 | ||
| 295 | /// Some constructs are valid expressions as well as valid top-level | ||
| 296 | /// declarations. Top level declarations must have associated names, but the | ||
| 297 | /// anonymous versions of these must not have names. | ||
| 298 | const ExprParseContext = enum { | ||
| 299 | declaration, | ||
| 300 | expression, | ||
| 301 | }; | ||
| 302 | |||
| 303 | fn parseFnDecl(p: *Parse) Error!NodeIndex { | 480 | fn parseFnDecl(p: *Parse) Error!NodeIndex { |
| 304 | const trace = t.trace(@src()); | 481 | const trace = t.trace(@src()); |
| 305 | defer trace.end(); | 482 | defer trace.end(); |
| ... | @@ -339,8 +516,6 @@ fn parseFnDecl(p: *Parse) Error!NodeIndex { | ... | @@ -339,8 +516,6 @@ fn parseFnDecl(p: *Parse) Error!NodeIndex { |
| 339 | }); | 516 | }); |
| 340 | } | 517 | } |
| 341 | 518 | ||
| 342 | const TypePresence = enum { types_optional, types_required }; | ||
| 343 | |||
| 344 | fn parseParamList(p: *Parse, types: TypePresence) Error!SubList { | 519 | fn parseParamList(p: *Parse, types: TypePresence) Error!SubList { |
| 345 | const trace = t.trace(@src()); | 520 | const trace = t.trace(@src()); |
| 346 | defer trace.end(); | 521 | defer trace.end(); |
| ... | @@ -390,13 +565,16 @@ fn parseParamDecl(p: *Parse, types: TypePresence) Error!NodeIndex { | ... | @@ -390,13 +565,16 @@ fn parseParamDecl(p: *Parse, types: TypePresence) Error!NodeIndex { |
| 390 | }); | 565 | }); |
| 391 | } | 566 | } |
| 392 | 567 | ||
| 393 | fn parseStructDecl(p: *Parse) Error!NodeIndex { | 568 | fn parseStructDecl(p: *Parse, ctx: ExprParseContext) Error!NodeIndex { |
| 394 | const trace = t.trace(@src()); | 569 | const trace = t.trace(@src()); |
| 395 | defer trace.end(); | 570 | defer trace.end(); |
| 396 | 571 | ||
| 397 | const main_token = p.assertToken(.kw_struct); | 572 | const main_token = p.assertToken(.kw_struct); |
| 398 | 573 | ||
| 399 | _ = try p.expect(.id); | 574 | switch (ctx) { |
| 575 | .declaration => _ = try p.expect(.id), | ||
| 576 | else => {}, | ||
| 577 | } | ||
| 400 | 578 | ||
| 401 | _ = try p.expect(.start_brace); | 579 | _ = try p.expect(.start_brace); |
| 402 | 580 | ||
| ... | @@ -409,6 +587,28 @@ fn parseStructDecl(p: *Parse) Error!NodeIndex { | ... | @@ -409,6 +587,28 @@ fn parseStructDecl(p: *Parse) Error!NodeIndex { |
| 409 | }); | 587 | }); |
| 410 | } | 588 | } |
| 411 | 589 | ||
| 590 | fn parseTupleDecl(p: *Parse, ctx: ExprParseContext) Error!NodeIndex { | ||
| 591 | const trace = t.trace(@src()); | ||
| 592 | defer trace.end(); | ||
| 593 | |||
| 594 | const main_token = p.assertToken(.kw_tuple); | ||
| 595 | |||
| 596 | switch (ctx) { | ||
| 597 | .declaration => _ = try p.expect(.id), | ||
| 598 | else => {}, | ||
| 599 | } | ||
| 600 | |||
| 601 | _ = try p.expect(.start_paren); | ||
| 602 | |||
| 603 | const fields = try p.parseExpressionList(.end_paren, .expression); | ||
| 604 | |||
| 605 | return try p.addNode(.{ | ||
| 606 | .tag = .tuple_type, | ||
| 607 | .main_token = main_token, | ||
| 608 | .data = .{ .list = fields }, | ||
| 609 | }); | ||
| 610 | } | ||
| 611 | |||
| 412 | fn parseTestDecl(p: *Parse) Error!NodeIndex { | 612 | fn parseTestDecl(p: *Parse) Error!NodeIndex { |
| 413 | const trace = t.trace(@src()); | 613 | const trace = t.trace(@src()); |
| 414 | defer trace.end(); | 614 | defer trace.end(); |
| ... | @@ -447,9 +647,7 @@ fn parseStructFieldList(p: *Parse) Error!SubList { | ... | @@ -447,9 +647,7 @@ fn parseStructFieldList(p: *Parse) Error!SubList { |
| 447 | try p.scratch.append(p.gpa, try p.parseStructField()); | 647 | try p.scratch.append(p.gpa, try p.parseStructField()); |
| 448 | 648 | ||
| 449 | switch (p.currentTag()) { | 649 | switch (p.currentTag()) { |
| 450 | .comma => { | 650 | .comma => _ = p.nextToken(), |
| 451 | _ = p.nextToken(); | ||
| 452 | }, | ||
| 453 | .end_brace => break, | 651 | .end_brace => break, |
| 454 | else => return p.failUnexpectedToken(.struct_field), | 652 | else => return p.failUnexpectedToken(.struct_field), |
| 455 | } | 653 | } |
| ... | @@ -578,13 +776,13 @@ fn parseVarDecl(p: *Parse) Error!NodeIndex { | ... | @@ -578,13 +776,13 @@ fn parseVarDecl(p: *Parse) Error!NodeIndex { |
| 578 | _ = try p.expect(.id); | 776 | _ = try p.expect(.id); |
| 579 | 777 | ||
| 580 | const data_type: OptionalNodeIndex = if (p.eatToken(.colon)) |_| | 778 | const data_type: OptionalNodeIndex = if (p.eatToken(.colon)) |_| |
| 581 | optionalNodeIndex(try p.tryParseExpression(.expression)) | 779 | optionalNodeIndex(try p.parseExpressionOrFail(.expression)) |
| 582 | else | 780 | else |
| 583 | .none; | 781 | .none; |
| 584 | 782 | ||
| 585 | _ = try p.expect(.equal); | 783 | _ = try p.expect(.equal); |
| 586 | 784 | ||
| 587 | const data_init = try p.tryParseExpression(.expression); | 785 | const data_init = try p.parseExpressionOrFail(.expression); |
| 588 | 786 | ||
| 589 | _ = try p.expect(.semi); | 787 | _ = try p.expect(.semi); |
| 590 | 788 | ||
| ... | @@ -619,204 +817,13 @@ fn isExpressionStart(p: *Parse) bool { | ... | @@ -619,204 +817,13 @@ fn isExpressionStart(p: *Parse) bool { |
| 619 | .string, | 817 | .string, |
| 620 | .tilde, | 818 | .tilde, |
| 621 | .kw_fn, | 819 | .kw_fn, |
| 820 | .kw_struct, | ||
| 821 | .kw_tuple, | ||
| 622 | => true, | 822 | => true, |
| 623 | else => false, | 823 | else => false, |
| 624 | }; | 824 | }; |
| 625 | } | 825 | } |
| 626 | 826 | ||
| 627 | const Operator = enum(u8) { | ||
| 628 | add = @intFromEnum(Token.Tag.plus), | ||
| 629 | sub = @intFromEnum(Token.Tag.minus), | ||
| 630 | mul = @intFromEnum(Token.Tag.star), | ||
| 631 | div = @intFromEnum(Token.Tag.slash), | ||
| 632 | rem = @intFromEnum(Token.Tag.percent), | ||
| 633 | pow = @intFromEnum(Token.Tag.star_star), | ||
| 634 | @"and" = @intFromEnum(Token.Tag.and_and), | ||
| 635 | @"or" = @intFromEnum(Token.Tag.pipe_pipe), | ||
| 636 | bit_and = @intFromEnum(Token.Tag.@"and"), | ||
| 637 | bit_or = @intFromEnum(Token.Tag.pipe), | ||
| 638 | bit_xor = @intFromEnum(Token.Tag.caret), | ||
| 639 | shl = @intFromEnum(Token.Tag.lt_lt), | ||
| 640 | shr = @intFromEnum(Token.Tag.gt_gt), | ||
| 641 | eq = @intFromEnum(Token.Tag.equal_equal), | ||
| 642 | ne = @intFromEnum(Token.Tag.bang_equal), | ||
| 643 | lt = @intFromEnum(Token.Tag.lt), | ||
| 644 | le = @intFromEnum(Token.Tag.lt_equal), | ||
| 645 | gt = @intFromEnum(Token.Tag.gt), | ||
| 646 | ge = @intFromEnum(Token.Tag.gt_equal), | ||
| 647 | // Following operators are assignment operators | ||
| 648 | assign = @intFromEnum(Token.Tag.equal), | ||
| 649 | add_assign = @intFromEnum(Token.Tag.plus_equal), | ||
| 650 | sub_assign = @intFromEnum(Token.Tag.minus_equal), | ||
| 651 | mul_assign = @intFromEnum(Token.Tag.star_equal), | ||
| 652 | div_assign = @intFromEnum(Token.Tag.slash_equal), | ||
| 653 | rem_assign = @intFromEnum(Token.Tag.percent_equal), | ||
| 654 | pow_assign = @intFromEnum(Token.Tag.star_star_equal), | ||
| 655 | and_assign = @intFromEnum(Token.Tag.and_and_equal), | ||
| 656 | or_assign = @intFromEnum(Token.Tag.pipe_pipe_equal), | ||
| 657 | bit_and_assign = @intFromEnum(Token.Tag.and_equal), | ||
| 658 | bit_or_assign = @intFromEnum(Token.Tag.pipe_equal), | ||
| 659 | bit_xor_assign = @intFromEnum(Token.Tag.caret_equal), | ||
| 660 | shl_assign = @intFromEnum(Token.Tag.lt_lt_equal), | ||
| 661 | shr_assign = @intFromEnum(Token.Tag.gt_gt_equal), | ||
| 662 | |||
| 663 | const Direction = enum { ltr, rtl }; | ||
| 664 | |||
| 665 | /// Only the binary operator levels are actually used, | ||
| 666 | /// but all are listed for reference. | ||
| 667 | const Level = enum { | ||
| 668 | /// e. e[] e() | ||
| 669 | accessor, | ||
| 670 | /// new | ||
| 671 | new, | ||
| 672 | /// ++e, --e, !e, ~e, +e, -e, typeof, void, delete | ||
| 673 | prefix, | ||
| 674 | /// ** | ||
| 675 | exponentiation, | ||
| 676 | /// *, /, % | ||
| 677 | multiplicative, | ||
| 678 | /// +, - | ||
| 679 | additive, | ||
| 680 | /// <<, >>, >>> | ||
| 681 | bitwise_shift, | ||
| 682 | /// <, <=, >, >= | ||
| 683 | relational, | ||
| 684 | /// ==, != | ||
| 685 | equality, | ||
| 686 | /// & | ||
| 687 | bit_and, | ||
| 688 | /// ^ | ||
| 689 | bit_xor, | ||
| 690 | /// | | ||
| 691 | bit_or, | ||
| 692 | /// && | ||
| 693 | logical_and, | ||
| 694 | /// ||, ?? | ||
| 695 | logical_or, | ||
| 696 | /// =, +=, -=, *=, /=, %=, / <<=, >>=, >>>=, &=, ^=, |= | ||
| 697 | assign, | ||
| 698 | }; | ||
| 699 | |||
| 700 | pub fn direction(op: Operator) Direction { | ||
| 701 | return switch (op) { | ||
| 702 | .pow => .rtl, | ||
| 703 | |||
| 704 | .assign, | ||
| 705 | .add_assign, | ||
| 706 | .sub_assign, | ||
| 707 | .mul_assign, | ||
| 708 | .div_assign, | ||
| 709 | .rem_assign, | ||
| 710 | .pow_assign, | ||
| 711 | .and_assign, | ||
| 712 | .or_assign, | ||
| 713 | .bit_and_assign, | ||
| 714 | .bit_or_assign, | ||
| 715 | .bit_xor_assign, | ||
| 716 | .shl_assign, | ||
| 717 | .shr_assign, | ||
| 718 | => .rtl, | ||
| 719 | |||
| 720 | else => .ltr, | ||
| 721 | }; | ||
| 722 | } | ||
| 723 | |||
| 724 | pub fn level(op: Operator) Level { | ||
| 725 | return switch (op) { | ||
| 726 | .pow => .exponentiation, | ||
| 727 | .mul, .div, .rem => .multiplicative, | ||
| 728 | .add, .sub => .additive, | ||
| 729 | .shl, .shr => .bitwise_shift, | ||
| 730 | .lt, .le, .gt, .ge => .relational, | ||
| 731 | .eq, .ne => .equality, | ||
| 732 | .@"and" => .logical_and, | ||
| 733 | .@"or" => .logical_or, | ||
| 734 | .bit_and => .bit_and, | ||
| 735 | .bit_xor => .bit_xor, | ||
| 736 | .bit_or => .bit_or, | ||
| 737 | .assign, | ||
| 738 | .add_assign, | ||
| 739 | .sub_assign, | ||
| 740 | .mul_assign, | ||
| 741 | .div_assign, | ||
| 742 | .rem_assign, | ||
| 743 | .pow_assign, | ||
| 744 | .and_assign, | ||
| 745 | .or_assign, | ||
| 746 | .bit_and_assign, | ||
| 747 | .bit_or_assign, | ||
| 748 | .bit_xor_assign, | ||
| 749 | .shl_assign, | ||
| 750 | .shr_assign, | ||
| 751 | => .assign, | ||
| 752 | }; | ||
| 753 | } | ||
| 754 | |||
| 755 | const lut = brk: { | ||
| 756 | const token_tags = std.enums.values(Token.Tag); | ||
| 757 | const op_tags = std.enums.values(Operator); | ||
| 758 | |||
| 759 | var is_operator_lut: [token_tags.len]bool = .{false} ** token_tags.len; | ||
| 760 | for (op_tags) |op| { | ||
| 761 | is_operator_lut[@intFromEnum(op)] = true; | ||
| 762 | } | ||
| 763 | |||
| 764 | var ast_tag_lut: [token_tags.len]Node.Tag = .{.root} ** token_tags.len; | ||
| 765 | for (op_tags) |op| { | ||
| 766 | ast_tag_lut[@intFromEnum(op)] = @field(Node.Tag, @tagName(op)); | ||
| 767 | } | ||
| 768 | |||
| 769 | break :brk struct { | ||
| 770 | const is_operator = is_operator_lut; | ||
| 771 | const ast_tag = ast_tag_lut; | ||
| 772 | }; | ||
| 773 | }; | ||
| 774 | |||
| 775 | pub fn fromToken(tag: Token.Tag) ?Operator { | ||
| 776 | return if (lut.is_operator[@intFromEnum(tag)]) | ||
| 777 | @enumFromInt(@intFromEnum(tag)) | ||
| 778 | else | ||
| 779 | null; | ||
| 780 | } | ||
| 781 | |||
| 782 | pub fn astTag(op: Operator) Node.Tag { | ||
| 783 | const tag = lut.ast_tag[@intFromEnum(op)]; | ||
| 784 | assert(tag != .root); // invalid operator or lut construction | ||
| 785 | return tag; | ||
| 786 | } | ||
| 787 | |||
| 788 | pub fn isAssign(op: Operator) bool { | ||
| 789 | return switch (op) { | ||
| 790 | .assign, | ||
| 791 | .add_assign, | ||
| 792 | .sub_assign, | ||
| 793 | .mul_assign, | ||
| 794 | .div_assign, | ||
| 795 | .rem_assign, | ||
| 796 | .pow_assign, | ||
| 797 | .and_assign, | ||
| 798 | .or_assign, | ||
| 799 | .bit_and_assign, | ||
| 800 | .bit_or_assign, | ||
| 801 | .bit_xor_assign, | ||
| 802 | .shl_assign, | ||
| 803 | .shr_assign, | ||
| 804 | => true, | ||
| 805 | else => false, | ||
| 806 | }; | ||
| 807 | } | ||
| 808 | }; | ||
| 809 | |||
| 810 | const ParseExprMode = enum { | ||
| 811 | /// Standard parsing | ||
| 812 | expression, | ||
| 813 | /// Statements allow assignment binary operators in addition to other expressions. | ||
| 814 | statement, | ||
| 815 | /// This bans struct initializations so that parsing stops at a '{' | ||
| 816 | /// Example: "if hello {" must not be parsed as a struct initialization. | ||
| 817 | preceding_block, | ||
| 818 | }; | ||
| 819 | |||
| 820 | fn parseExpression(p: *Parse, mode: ParseExprMode) Error!NodeIndex { | 827 | fn parseExpression(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 821 | const trace = t.trace(@src()); | 828 | const trace = t.trace(@src()); |
| 822 | defer trace.end(); | 829 | defer trace.end(); |
| ... | @@ -831,8 +838,13 @@ fn parseExpression(p: *Parse, mode: ParseExprMode) Error!NodeIndex { | ... | @@ -831,8 +838,13 @@ fn parseExpression(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 831 | 838 | ||
| 832 | // These unary operators are greedy, so handle them first. | 839 | // These unary operators are greedy, so handle them first. |
| 833 | switch (p.currentTag()) { | 840 | switch (p.currentTag()) { |
| 834 | .kw_return => return p.parseReturn(), | 841 | .kw_return => return p.parseReturn(mode), |
| 835 | .kw_break => return p.parseBreak(), | 842 | .kw_break => return p.parseBreak(mode), |
| 843 | |||
| 844 | .kw_assert => return p.parseGreedyUnary(.assert, mode), | ||
| 845 | .kw_comptime => return p.parseGreedyUnary(.@"comptime", mode), | ||
| 846 | .kw_pure => return p.parseGreedyUnary(.pure, mode), | ||
| 847 | |||
| 836 | else => {}, | 848 | else => {}, |
| 837 | } | 849 | } |
| 838 | 850 | ||
| ... | @@ -846,7 +858,7 @@ fn parseExpression(p: *Parse, mode: ParseExprMode) Error!NodeIndex { | ... | @@ -846,7 +858,7 @@ fn parseExpression(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 846 | return expr; | 858 | return expr; |
| 847 | } | 859 | } |
| 848 | 860 | ||
| 849 | fn tryParseExpression(p: *Parse, mode: ParseExprMode) Error!NodeIndex { | 861 | fn parseExpressionOrFail(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 850 | return if (p.isExpressionStart()) | 862 | return if (p.isExpressionStart()) |
| 851 | p.parseExpression(mode) | 863 | p.parseExpression(mode) |
| 852 | else | 864 | else |
| ... | @@ -858,7 +870,6 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { | ... | @@ -858,7 +870,6 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 858 | defer trace.end(); | 870 | defer trace.end(); |
| 859 | 871 | ||
| 860 | var expr = switch (p.currentTag()) { | 872 | var expr = switch (p.currentTag()) { |
| 861 | // Literals | ||
| 862 | .id => if (p.nextTag() == .colon) | 873 | .id => if (p.nextTag() == .colon) |
| 863 | try p.parseLabelExpression() | 874 | try p.parseLabelExpression() |
| 864 | else | 875 | else |
| ... | @@ -867,24 +878,27 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { | ... | @@ -867,24 +878,27 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 867 | .number_float => try p.blankNode(.number_float), | 878 | .number_float => try p.blankNode(.number_float), |
| 868 | .string => try p.blankNode(.string), | 879 | .string => try p.blankNode(.string), |
| 869 | 880 | ||
| 870 | // Complex expressions | ||
| 871 | .builtin_id => try p.parseBuiltinCall(), | 881 | .builtin_id => try p.parseBuiltinCall(), |
| 872 | .dot => try p.parseAnonInstantiation(), | 882 | .dot => try p.parseAnonStructInit(), |
| 873 | .kw_break => try p.parseBreak(), | ||
| 874 | .kw_fn => try p.parseAnonFunction(), | 883 | .kw_fn => try p.parseAnonFunction(), |
| 875 | .kw_if => try p.parseIf(), | 884 | .kw_if => try p.parseIf(), |
| 876 | .kw_return => try p.parseReturn(), | 885 | .kw_tuple => try p.parseTupleType(), |
| 886 | |||
| 877 | .start_brace => try p.parseBlock(), | 887 | .start_brace => try p.parseBlock(), |
| 878 | .start_bracket => try p.parseArrayValueOrType(mode), | 888 | .start_bracket => try p.parseArrayValueOrType(mode), |
| 879 | .start_paren => try p.parseTupleType(), | 889 | .start_paren => try p.parseTupleInit(), |
| 890 | |||
| 891 | .kw_struct => try p.parseStructDecl(.expression), | ||
| 892 | |||
| 893 | .kw_return => return p.parseReturn(mode), | ||
| 894 | .kw_break => return p.parseBreak(mode), | ||
| 895 | .kw_assert => return p.parseGreedyUnary(.assert, mode), | ||
| 896 | .kw_comptime => return p.parseGreedyUnary(.@"comptime", mode), | ||
| 897 | .kw_pure => return p.parseGreedyUnary(.pure, mode), | ||
| 880 | 898 | ||
| 881 | // Unary operators | ||
| 882 | inline .bang, | 899 | inline .bang, |
| 883 | .tilde, | 900 | .tilde, |
| 884 | .minus, | 901 | .minus, |
| 885 | .kw_comptime, | ||
| 886 | .kw_pure, | ||
| 887 | .kw_assert, | ||
| 888 | => |tag| unary: { | 902 | => |tag| unary: { |
| 889 | const main_token = p.nextToken(); | 903 | const main_token = p.nextToken(); |
| 890 | const expr = try p.parseExpressionPart(mode); | 904 | const expr = try p.parseExpressionPart(mode); |
| ... | @@ -955,7 +969,7 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { | ... | @@ -955,7 +969,7 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 955 | return p.fail(.{ .array_type_flipped_hint = p.fullToken(p.pos) }); | 969 | return p.fail(.{ .array_type_flipped_hint = p.fullToken(p.pos) }); |
| 956 | } | 970 | } |
| 957 | 971 | ||
| 958 | const index = try p.tryParseExpression(.expression); | 972 | const index = try p.parseExpressionOrFail(.expression); |
| 959 | 973 | ||
| 960 | _ = try p.expect(.end_bracket); | 974 | _ = try p.expect(.end_bracket); |
| 961 | 975 | ||
| ... | @@ -1020,14 +1034,14 @@ fn parseBinarySuffix(p: *Parse, lhs: NodeIndex, op: Operator, mode: ParseExprMod | ... | @@ -1020,14 +1034,14 @@ fn parseBinarySuffix(p: *Parse, lhs: NodeIndex, op: Operator, mode: ParseExprMod |
| 1020 | }); | 1034 | }); |
| 1021 | } | 1035 | } |
| 1022 | 1036 | ||
| 1023 | fn parseReturn(p: *Parse) Error!NodeIndex { | 1037 | fn parseReturn(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 1024 | const trace = t.trace(@src()); | 1038 | const trace = t.trace(@src()); |
| 1025 | defer trace.end(); | 1039 | defer trace.end(); |
| 1026 | 1040 | ||
| 1027 | const main_token = p.assertToken(.kw_return); | 1041 | const main_token = p.assertToken(.kw_return); |
| 1028 | 1042 | ||
| 1029 | const expr = if (p.isExpressionStart()) | 1043 | const expr = if (p.isExpressionStart()) |
| 1030 | try p.parseExpression(.expression) | 1044 | try p.parseExpression(mode) |
| 1031 | else | 1045 | else |
| 1032 | null; | 1046 | null; |
| 1033 | 1047 | ||
| ... | @@ -1040,7 +1054,7 @@ fn parseReturn(p: *Parse) Error!NodeIndex { | ... | @@ -1040,7 +1054,7 @@ fn parseReturn(p: *Parse) Error!NodeIndex { |
| 1040 | }); | 1054 | }); |
| 1041 | } | 1055 | } |
| 1042 | 1056 | ||
| 1043 | fn parseBreak(p: *Parse) Error!NodeIndex { | 1057 | fn parseBreak(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 1044 | const trace = t.trace(@src()); | 1058 | const trace = t.trace(@src()); |
| 1045 | defer trace.end(); | 1059 | defer trace.end(); |
| 1046 | 1060 | ||
| ... | @@ -1052,7 +1066,7 @@ fn parseBreak(p: *Parse) Error!NodeIndex { | ... | @@ -1052,7 +1066,7 @@ fn parseBreak(p: *Parse) Error!NodeIndex { |
| 1052 | null; | 1066 | null; |
| 1053 | 1067 | ||
| 1054 | const expr = if (p.isExpressionStart()) | 1068 | const expr = if (p.isExpressionStart()) |
| 1055 | try p.parseExpression(.expression) | 1069 | try p.parseExpression(mode) |
| 1056 | else | 1070 | else |
| 1057 | null; | 1071 | null; |
| 1058 | 1072 | ||
| ... | @@ -1066,6 +1080,23 @@ fn parseBreak(p: *Parse) Error!NodeIndex { | ... | @@ -1066,6 +1080,23 @@ fn parseBreak(p: *Parse) Error!NodeIndex { |
| 1066 | }); | 1080 | }); |
| 1067 | } | 1081 | } |
| 1068 | 1082 | ||
| 1083 | fn parseGreedyUnary(p: *Parse, tag: Node.Tag, mode: ParseExprMode) Error!NodeIndex { | ||
| 1084 | const trace = t.trace(@src()); | ||
| 1085 | defer trace.end(); | ||
| 1086 | |||
| 1087 | const main_token = p.nextToken(); | ||
| 1088 | |||
| 1089 | const expr = try p.parseExpressionOrFail(mode); | ||
| 1090 | |||
| 1091 | return try p.addNode(.{ | ||
| 1092 | .tag = tag, | ||
| 1093 | .main_token = main_token, | ||
| 1094 | .data = .{ .un = .{ | ||
| 1095 | .expr = expr, | ||
| 1096 | } }, | ||
| 1097 | }); | ||
| 1098 | } | ||
| 1099 | |||
| 1069 | fn parseBuiltinCall(p: *Parse) Error!NodeIndex { | 1100 | fn parseBuiltinCall(p: *Parse) Error!NodeIndex { |
| 1070 | const trace = t.trace(@src()); | 1101 | const trace = t.trace(@src()); |
| 1071 | defer trace.end(); | 1102 | defer trace.end(); |
| ... | @@ -1083,11 +1114,7 @@ fn parseBuiltinCall(p: *Parse) Error!NodeIndex { | ... | @@ -1083,11 +1114,7 @@ fn parseBuiltinCall(p: *Parse) Error!NodeIndex { |
| 1083 | }); | 1114 | }); |
| 1084 | } | 1115 | } |
| 1085 | 1116 | ||
| 1086 | fn parseExpressionList( | 1117 | fn parseExpressionList(p: *Parse, comptime end_token: Token.Tag, fail_context: ExpectedContext) Error!SubList { |
| 1087 | p: *Parse, | ||
| 1088 | comptime end_token: Token.Tag, | ||
| 1089 | comptime fail_context: ExpectedContext, | ||
| 1090 | ) Error!SubList { | ||
| 1091 | const trace = t.trace(@src()); | 1118 | const trace = t.trace(@src()); |
| 1092 | defer trace.end(); | 1119 | defer trace.end(); |
| 1093 | 1120 | ||
| ... | @@ -1161,7 +1188,7 @@ fn parseElseValue(p: *Parse) Error!NodeIndex { | ... | @@ -1161,7 +1188,7 @@ fn parseElseValue(p: *Parse) Error!NodeIndex { |
| 1161 | }; | 1188 | }; |
| 1162 | } | 1189 | } |
| 1163 | 1190 | ||
| 1164 | fn parseAnonInstantiation(p: *Parse) Error!NodeIndex { | 1191 | fn parseAnonStructInit(p: *Parse) Error!NodeIndex { |
| 1165 | const trace = t.trace(@src()); | 1192 | const trace = t.trace(@src()); |
| 1166 | defer trace.end(); | 1193 | defer trace.end(); |
| 1167 | 1194 | ||
| ... | @@ -1272,9 +1299,20 @@ fn parseArrayValueOrType(p: *Parse, mode: ParseExprMode) Error!NodeIndex { | ... | @@ -1272,9 +1299,20 @@ fn parseArrayValueOrType(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 1272 | }); | 1299 | }); |
| 1273 | } | 1300 | } |
| 1274 | 1301 | ||
| 1275 | fn parseTupleType(p: *Parse) Error!NodeIndex { | 1302 | fn parseTupleInit(p: *Parse) Error!NodeIndex { |
| 1276 | const main_token = p.assertToken(.start_paren); | 1303 | const main_token = p.assertToken(.start_paren); |
| 1277 | const list = try p.parseExpressionList(.end_paren, .expression); | 1304 | const list = try p.parseExpressionList(.end_paren, .expression); |
| 1305 | return p.addNode(.{ | ||
| 1306 | .tag = .tuple_init, | ||
| 1307 | .main_token = main_token, | ||
| 1308 | .data = .{ .list = list }, | ||
| 1309 | }); | ||
| 1310 | } | ||
| 1311 | |||
| 1312 | fn parseTupleType(p: *Parse) Error!NodeIndex { | ||
| 1313 | const main_token = p.assertToken(.kw_tuple); | ||
| 1314 | _ = try p.expect(.start_paren); | ||
| 1315 | const list = try p.parseExpressionList(.end_paren, .expression); | ||
| 1278 | return p.addNode(.{ | 1316 | return p.addNode(.{ |
| 1279 | .tag = .tuple_type, | 1317 | .tag = .tuple_type, |
| 1280 | .main_token = main_token, | 1318 | .main_token = main_token, |
src/core/Sema.zig+8-7| ... | @@ -1615,6 +1615,14 @@ fn ensureValueRuntime(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) Error!Air.Inst | ... | @@ -1615,6 +1615,14 @@ fn ensureValueRuntime(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) Error!Air.Inst |
| 1615 | .value => |v| { | 1615 | .value => |v| { |
| 1616 | const ty = v.typeOf(s.ip); | 1616 | const ty = v.typeOf(s.ip); |
| 1617 | switch (ty) { | 1617 | switch (ty) { |
| 1618 | // Allowed at runtime | ||
| 1619 | .bool, | ||
| 1620 | .float, | ||
| 1621 | .int32, | ||
| 1622 | .string, | ||
| 1623 | .void, | ||
| 1624 | .empty_tuple, | ||
| 1625 | => {}, | ||
| 1618 | .numeric_literal => { | 1626 | .numeric_literal => { |
| 1619 | const float = v.toFloat(s.ip) orelse unreachable; | 1627 | const float = v.toFloat(s.ip) orelse unreachable; |
| 1620 | 1628 | ||
| ... | @@ -1649,13 +1657,6 @@ fn ensureValueRuntime(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) Error!Air.Inst | ... | @@ -1649,13 +1657,6 @@ fn ensureValueRuntime(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) Error!Air.Inst |
| 1649 | .type = ty, | 1657 | .type = ty, |
| 1650 | }), | 1658 | }), |
| 1651 | .@"opaque" => @panic("TODO: represent comptime-known opaques better"), | 1659 | .@"opaque" => @panic("TODO: represent comptime-known opaques better"), |
| 1652 | // Allowed at runtime | ||
| 1653 | .bool, | ||
| 1654 | .float, | ||
| 1655 | .int32, | ||
| 1656 | .string, | ||
| 1657 | .void, | ||
| 1658 | => {}, | ||
| 1659 | _ => if (!ty.hasPossibleRepresentation(s.ip)) { | 1660 | _ => if (!ty.hasPossibleRepresentation(s.ip)) { |
| 1660 | return s.fail(.runtime_incompatible_type, .{ | 1661 | return s.fail(.runtime_incompatible_type, .{ |
| 1661 | .src = src, | 1662 | .src = src, |
src/core/Teg.Dump.zig+18| ... | @@ -319,6 +319,24 @@ const State = struct { | ... | @@ -319,6 +319,24 @@ const State = struct { |
| 319 | if (data.type_hint != .none) | 319 | if (data.type_hint != .none) |
| 320 | try writer.writeAll(")"); | 320 | try writer.writeAll(")"); |
| 321 | }, | 321 | }, |
| 322 | .tuple_type => { | ||
| 323 | const data: Teg.TupleType = teg.instPayload(.tuple_type, inst); | ||
| 324 | |||
| 325 | try writer.writeAll("("); | ||
| 326 | try s.dumpRefMarkerList(writer, data.items.slice(teg), '[', ']'); | ||
| 327 | try writer.writeAll(")"); | ||
| 328 | }, | ||
| 329 | .tuple_init => { | ||
| 330 | const data: Teg.TupleInit = teg.instPayload(.tuple_init, inst); | ||
| 331 | |||
| 332 | try writer.writeAll("("); | ||
| 333 | if (data.type_hint.unwrap()) |type_hint| { | ||
| 334 | try s.dumpRef(writer, type_hint); | ||
| 335 | try writer.writeAll(", "); | ||
| 336 | } | ||
| 337 | try s.dumpRefMarkerList(writer, data.items.slice(teg), '[', ']'); | ||
| 338 | try writer.writeAll(")"); | ||
| 339 | }, | ||
| 322 | .result_type_argument => { | 340 | .result_type_argument => { |
| 323 | const data: Teg.ResultTypeArgument = teg.instData(.result_type_argument, inst); | 341 | const data: Teg.ResultTypeArgument = teg.instData(.result_type_argument, inst); |
| 324 | 342 |
src/core/Teg.zig+37-2| ... | @@ -75,6 +75,9 @@ pub const Tag = enum(u8) { | ... | @@ -75,6 +75,9 @@ pub const Tag = enum(u8) { |
| 75 | struct_init, | 75 | struct_init, |
| 76 | array_init, | 76 | array_init, |
| 77 | 77 | ||
| 78 | tuple_init, | ||
| 79 | tuple_type, | ||
| 80 | |||
| 78 | add, | 81 | add, |
| 79 | sub, | 82 | sub, |
| 80 | mul, | 83 | mul, |
| ... | @@ -136,6 +139,8 @@ pub const Tag = enum(u8) { | ... | @@ -136,6 +139,8 @@ pub const Tag = enum(u8) { |
| 136 | .struct_decl => PlNode(StructDecl), | 139 | .struct_decl => PlNode(StructDecl), |
| 137 | .struct_init => PlNode(StructInit), | 140 | .struct_init => PlNode(StructInit), |
| 138 | .array_init => PlNode(ArrayInit), | 141 | .array_init => PlNode(ArrayInit), |
| 142 | .tuple_type => PlNode(TupleType), | ||
| 143 | .tuple_init => PlNode(TupleInit), | ||
| 139 | .func => PlNode(Func), | 144 | .func => PlNode(Func), |
| 140 | 145 | ||
| 141 | .load_decl => StrNode, | 146 | .load_decl => StrNode, |
| ... | @@ -230,7 +235,7 @@ pub const Inst = struct { | ... | @@ -230,7 +235,7 @@ pub const Inst = struct { |
| 230 | main_module_inst = 0, | 235 | main_module_inst = 0, |
| 231 | _, | 236 | _, |
| 232 | 237 | ||
| 233 | pub const static_len = @intFromEnum(Ref.empty_array) + 1; | 238 | pub const static_len = @intFromEnum(Ref.empty_tuple) + 1; |
| 234 | 239 | ||
| 235 | pub fn toRef(i: Index) Inst.Ref { | 240 | pub fn toRef(i: Index) Inst.Ref { |
| 236 | return @enumFromInt(static_len + @intFromEnum(i)); | 241 | return @enumFromInt(static_len + @intFromEnum(i)); |
| ... | @@ -269,6 +274,7 @@ pub const Inst = struct { | ... | @@ -269,6 +274,7 @@ pub const Inst = struct { |
| 269 | numeric_literal_type, | 274 | numeric_literal_type, |
| 270 | numeric_literal_array_type, | 275 | numeric_literal_array_type, |
| 271 | never_array_type, | 276 | never_array_type, |
| 277 | empty_tuple_type, | ||
| 272 | 278 | ||
| 273 | void, | 279 | void, |
| 274 | bool_true, | 280 | bool_true, |
| ... | @@ -278,7 +284,7 @@ pub const Inst = struct { | ... | @@ -278,7 +284,7 @@ pub const Inst = struct { |
| 278 | nan, | 284 | nan, |
| 279 | empty_string, | 285 | empty_string, |
| 280 | empty_array, | 286 | empty_array, |
| 281 | 287 | empty_tuple, | |
| 282 | _, | 288 | _, |
| 283 | 289 | ||
| 284 | pub const Decoded = union(enum) { | 290 | pub const Decoded = union(enum) { |
| ... | @@ -423,6 +429,20 @@ pub const ArrayInit = struct { | ... | @@ -423,6 +429,20 @@ pub const ArrayInit = struct { |
| 423 | list: RefList, | 429 | list: RefList, |
| 424 | }; | 430 | }; |
| 425 | 431 | ||
| 432 | pub const TupleType = struct { | ||
| 433 | /// The items in the tuple | ||
| 434 | items: RefList, | ||
| 435 | }; | ||
| 436 | |||
| 437 | pub const TupleInit = struct { | ||
| 438 | /// Optionally provide a result type for more reliable type coercion. | ||
| 439 | /// If specified, this must be an array type. Otherwise, the first | ||
| 440 | /// element's type is used. | ||
| 441 | type_hint: Inst.Ref.Optional, | ||
| 442 | /// The items in the tuple | ||
| 443 | items: RefList, | ||
| 444 | }; | ||
| 445 | |||
| 426 | pub const Decl = struct { | 446 | pub const Decl = struct { |
| 427 | name: NullTerminatedString, | 447 | name: NullTerminatedString, |
| 428 | hash: Cache.BinDigest, | 448 | hash: Cache.BinDigest, |
| ... | @@ -697,6 +717,8 @@ pub fn writeToHasher( | ... | @@ -697,6 +717,8 @@ pub fn writeToHasher( |
| 697 | .struct_decl, | 717 | .struct_decl, |
| 698 | .struct_init, | 718 | .struct_init, |
| 699 | .array_init, | 719 | .array_init, |
| 720 | .tuple_init, | ||
| 721 | .tuple_type, | ||
| 700 | .func, | 722 | .func, |
| 701 | .store_decl, | 723 | .store_decl, |
| 702 | .store_field, | 724 | .store_field, |
| ... | @@ -902,6 +924,19 @@ fn writePlNodeInnerToHasher( | ... | @@ -902,6 +924,19 @@ fn writePlNodeInnerToHasher( |
| 902 | hasher.add(isolatedIndexValue(decl_index, data.body)); | 924 | hasher.add(isolatedIndexValue(decl_index, data.body)); |
| 903 | hasher.add(data.instruction_count); | 925 | hasher.add(data.instruction_count); |
| 904 | }, | 926 | }, |
| 927 | TupleType => { | ||
| 928 | hasher.add(data.items.len); | ||
| 929 | for (data.items.slice(teg)) |item| { | ||
| 930 | hasher.add(isolatedRefValue(decl_index, item)); | ||
| 931 | } | ||
| 932 | }, | ||
| 933 | TupleInit => { | ||
| 934 | if (data.type_hint.unwrap()) |type_hint| | ||
| 935 | hasher.add(isolatedRefValue(decl_index, type_hint)) | ||
| 936 | else | ||
| 937 | hasher.add(@as(i32, std.math.maxInt(i32))); | ||
| 938 | writeRefListToHasher(hasher, decl_index, data.items.slice(teg)); | ||
| 939 | }, | ||
| 905 | else => @compileError("hash not implemented for " ++ @typeName(T)), | 940 | else => @compileError("hash not implemented for " ++ @typeName(T)), |
| 906 | } | 941 | } |
| 907 | } | 942 | } |
src/core/Tokenizer.zig+1| ... | @@ -75,6 +75,7 @@ pub const Token = struct { | ... | @@ -75,6 +75,7 @@ pub const Token = struct { |
| 75 | kw_return, | 75 | kw_return, |
| 76 | kw_struct, | 76 | kw_struct, |
| 77 | kw_trait, | 77 | kw_trait, |
| 78 | kw_tuple, | ||
| 78 | kw_var, | 79 | kw_var, |
| 79 | kw_while, | 80 | kw_while, |
| 80 | // sort-lines: end | 81 | // sort-lines: end |
src/core/Transform.zig+32| ... | @@ -903,6 +903,7 @@ fn transformExpression( | ... | @@ -903,6 +903,7 @@ fn transformExpression( |
| 903 | switch (node_tag) { | 903 | switch (node_tag) { |
| 904 | .root, | 904 | .root, |
| 905 | .param_decl, | 905 | .param_decl, |
| 906 | .struct_field, | ||
| 906 | => unreachable, // not an expression | 907 | => unreachable, // not an expression |
| 907 | 908 | ||
| 908 | .id => { | 909 | .id => { |
| ... | @@ -1520,6 +1521,37 @@ fn transformExpression( | ... | @@ -1520,6 +1521,37 @@ fn transformExpression( |
| 1520 | }); | 1521 | }); |
| 1521 | }, | 1522 | }, |
| 1522 | 1523 | ||
| 1524 | .tuple_init => { | ||
| 1525 | const tuple_init = t.ast.nodeData(node).list; | ||
| 1526 | |||
| 1527 | const items = try t.transformExpressionList(ctx, .none, tuple_init.slice(&t.ast)) orelse return null; | ||
| 1528 | |||
| 1529 | if (items.len == 0) { | ||
| 1530 | return .empty_tuple; | ||
| 1531 | } | ||
| 1532 | |||
| 1533 | return try t.addRef(.tuple_init, .{ | ||
| 1534 | .src_node = ctx.nodeOffset(node), | ||
| 1535 | .extra_index = try t.extra.append(t.gpa, Teg.TupleInit, .{ | ||
| 1536 | .type_hint = .none, | ||
| 1537 | .items = items, | ||
| 1538 | }), | ||
| 1539 | }); | ||
| 1540 | }, | ||
| 1541 | |||
| 1542 | .tuple_type => { | ||
| 1543 | const tuple_type = t.ast.nodeData(node).list; | ||
| 1544 | |||
| 1545 | const items = try t.transformExpressionList(ctx, .none, tuple_type.slice(&t.ast)) orelse return null; | ||
| 1546 | |||
| 1547 | return try t.addRef(.tuple_type, .{ | ||
| 1548 | .src_node = ctx.nodeOffset(node), | ||
| 1549 | .extra_index = try t.extra.append(t.gpa, Teg.TupleType, .{ | ||
| 1550 | .items = items, | ||
| 1551 | }), | ||
| 1552 | }); | ||
| 1553 | }, | ||
| 1554 | |||
| 1523 | inline .fn_decl, .fn_expr => |tag| { | 1555 | inline .fn_decl, .fn_expr => |tag| { |
| 1524 | const data = switch (tag) { | 1556 | const data = switch (tag) { |
| 1525 | .fn_decl => t.ast.fullFnDecl(node), | 1557 | .fn_decl => t.ast.fullFnDecl(node), |
src/core/js/NodeGen.zig+2| ... | @@ -557,6 +557,7 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index { | ... | @@ -557,6 +557,7 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index { |
| 557 | .simple_type, | 557 | .simple_type, |
| 558 | .struct_type, | 558 | .struct_type, |
| 559 | .array_type, | 559 | .array_type, |
| 560 | .tuple_type, | ||
| 560 | => unreachable, // Type values are not allowed here | 561 | => unreachable, // Type values are not allowed here |
| 561 | 562 | ||
| 562 | .int32, | 563 | .int32, |
| ... | @@ -566,6 +567,7 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index { | ... | @@ -566,6 +567,7 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index { |
| 566 | .simple_value, | 567 | .simple_value, |
| 567 | .struct_value, | 568 | .struct_value, |
| 568 | .array_value, | 569 | .array_value, |
| 570 | .tuple_value, | ||
| 569 | => return try c.add(.plain_value, .{ .value = v }), | 571 | => return try c.add(.plain_value, .{ .value = v }), |
| 570 | 572 | ||
| 571 | .external_value => { | 573 | .external_value => { |
test/behavior/tuple created+14| ... | @@ -0,0 +1,14 @@ | ||
| 1 | let assertEqual = @extern("assertEqual"); | ||
| 2 | |||
| 3 | fn hello() -> tuple (int, float) { | ||
| 4 | (5, 2.5) | ||
| 5 | } | ||
| 6 | |||
| 7 | fn main { | ||
| 8 | let x = hello(); | ||
| 9 | assertEqual(x.0, 5); | ||
| 10 | assertEqual(x.1, 2.5); | ||
| 11 | } | ||
| 12 | |||
| 13 | fn f(a: float) -> float { a } | ||
| 14 | fn i(a: int) -> int { a } | ||