diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cd3da761bfae74a4cebdc0793fca5677c80fabb..800e61a74a80102ec33d0a9c4728729125d42051 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## 0.1.8 +- feat: implement basic enum types + ## 0.1.7 - news: rename the language to "ix" diff --git a/src/core/Ast.Dump.zig b/src/core/Ast.Dump.zig index 820004af937a755181a10d82406db9e852f8d755..b1d6baaf62378853fa7cbaf3831faaf8557f07ed 100644 --- a/src/core/Ast.Dump.zig +++ b/src/core/Ast.Dump.zig @@ -177,6 +177,21 @@ fn dumpNode(writer: anytype, state: *State, node: NodeIndex) @TypeOf(writer).Err try dumpNode(writer, state, ty); } }, + .enum_field => { + try writer.writeAll(" "); + const enum_field = state.ast.fullEnumField(node); + try tty.setColor(writer, .magenta); + try writer.writeAll(enum_field.id); + try tty.setColor(writer, .reset); + if (enum_field.payload) |ty| { + try writer.writeAll(": "); + try dumpNode(writer, state, ty); + } + if (enum_field.value) |ty| { + try writer.writeAll(" = "); + try dumpNode(writer, state, ty); + } + }, .bin => { try writer.writeAll(" ("); { diff --git a/src/core/Ast.zig b/src/core/Ast.zig index 5404ccb3ac2e3601867cf50b925fcc3a12cd4b1d..cb3bea4028a42d124641bc0db37f9fe65a4c9c77 100644 --- a/src/core/Ast.zig +++ b/src/core/Ast.zig @@ -215,6 +215,12 @@ pub const Node = struct { struct_field, /// Struct Init struct_init, + /// Enum Declaration + @"enum", + /// Enum Field + enum_field, + /// Enum Literal + enum_literal, /// Array type array_type, /// Array initialization @@ -336,6 +342,9 @@ pub const Node = struct { .shl_assign = .bin, .shr = .bin, .shr_assign = .bin, + .@"enum" = .list, + .enum_literal = .none, + .enum_field = .none, .string = .none, .struct_decl = .list, .array_type = .un, @@ -371,6 +380,10 @@ pub const Node = struct { type: OptionalNodeIndex, unused: Unused = .zero, }, + enum_field: struct { + payload: OptionalNodeIndex, + value: OptionalNodeIndex, + }, bin: struct { lhs: NodeIndex, rhs: NodeIndex, @@ -566,6 +579,13 @@ pub const full = struct { id: []const u8, type: ?NodeIndex, }; + + pub const EnumField = struct { + start_token: TokenIndex, + id: []const u8, + payload: ?NodeIndex, + value: ?NodeIndex, + }; }; pub fn deinit(ast: *Ast, gpa: std.mem.Allocator) void { @@ -691,6 +711,21 @@ pub fn fullParamDecl(ast: Ast, node: NodeIndex) full.ParamDecl { }; } +pub fn fullEnumField(ast: Ast, node: NodeIndex) full.EnumField { + const tok_tags = ast.tokens.items(.tag); + + const main_token = ast.nodes.items(.main_token)[node]; + assert(tok_tags[main_token] == .id); + const id = ast.tokenSlice(main_token); + const data = ast.nodes.items(.data)[node].enum_field; + return .{ + .start_token = main_token, + .id = id, + .payload = data.payload.unwrap(), + .value = data.value.unwrap(), + }; +} + pub fn fullStructField(ast: Ast, node: NodeIndex) full.StructField { const tok_tags = ast.tokens.items(.tag); diff --git a/src/core/Parse.zig b/src/core/Parse.zig index 487c8195bd1986dfe7dcae8f93c750cbe55567b5..d6ecfff05e6dc4764da98bb3480024c2515933ae 100644 --- a/src/core/Parse.zig +++ b/src/core/Parse.zig @@ -86,9 +86,10 @@ pub const ExpectedContext = enum { statement, expression, else_clause, + enum_field, struct_field, - param_list, static_field, + param_list, pub fn description(tag: ExpectedContext) []const u8 { return switch (tag) { @@ -99,6 +100,7 @@ pub const ExpectedContext = enum { .expression => "an expression", .else_clause => "an else clause", .struct_field => "a field definition", + .enum_field => "an enum field", .static_field => "a field name or number", }; } @@ -415,7 +417,8 @@ fn parseModuleMembers(p: *Parse) Error!SubList { => try p.scratch.append(p.gpa, try p.parseModuleDecl()), .kw_struct => try p.scratch.append(p.gpa, try p.parseStructDecl(.declaration)), - .kw_tuple => try p.scratch.append(p.gpa, try p.parseStructDecl(.declaration)), + .kw_tuple => try p.scratch.append(p.gpa, try p.parseTupleDecl(.declaration)), + .kw_enum => try p.scratch.append(p.gpa, try p.parseEnumDecl(.declaration)), .id => if (p.isContextualKeyword("test")) { try p.scratch.append(p.gpa, try p.parseTestDecl()); @@ -423,7 +426,6 @@ fn parseModuleMembers(p: *Parse) Error!SubList { try p.parseExpressionStatement(); }, - .kw_enum, .kw_trait, .kw_impl, => |tag| std.debug.panic("TODO: parse {s}", .{@tagName(tag)}), @@ -608,6 +610,28 @@ fn parseTupleDecl(p: *Parse, ctx: ExprParseContext) Error!NodeIndex { }); } +fn parseEnumDecl(p: *Parse, ctx: ExprParseContext) Error!NodeIndex { + const trace = t.trace(@src()); + defer trace.end(); + + const main_token = p.assertToken(.kw_enum); + + switch (ctx) { + .declaration => _ = try p.expect(.id), + else => {}, + } + + _ = try p.expect(.start_brace); + + const fields = try p.parseEnumFieldList(); + + return try p.addNode(.{ + .tag = .@"enum", + .main_token = main_token, + .data = .{ .list = fields }, + }); +} + fn parseTestDecl(p: *Parse) Error!NodeIndex { const trace = t.trace(@src()); defer trace.end(); @@ -657,6 +681,33 @@ fn parseStructFieldList(p: *Parse) Error!SubList { return p.scratchToSubList(scratch_top); } +fn parseEnumFieldList(p: *Parse) Error!SubList { + const trace = t.trace(@src()); + defer trace.end(); + + const scratch_top = p.scratch.items.len; + defer p.scratch.shrinkRetainingCapacity(scratch_top); + + while (true) { + const trace_param = t.traceFmt(@src(), "Param {d}", .{p.scratch.items.len - scratch_top}); + defer trace_param.end(); + + if (p.currentTag() == .end_brace) break; + + try p.scratch.append(p.gpa, try p.parseEnumField()); + + switch (p.currentTag()) { + .comma => _ = p.nextToken(), + .end_brace => break, + else => return p.failUnexpectedToken(.enum_field), + } + } + + _ = p.assertToken(.end_brace); + + return p.scratchToSubList(scratch_top); +} + fn parseStructField(p: *Parse) Error!NodeIndex { const trace = t.trace(@src()); defer trace.end(); @@ -674,6 +725,32 @@ fn parseStructField(p: *Parse) Error!NodeIndex { }); } +fn parseEnumField(p: *Parse) Error!NodeIndex { + const trace = t.trace(@src()); + defer trace.end(); + + const main_token = try p.expect(.id); + + const payload = if (p.eatToken(.colon)) |_| + try p.parseExpressionOrFail(.expression) + else + null; + + const value = if (p.eatToken(.equal)) |_| + try p.parseExpressionOrFail(.expression) + else + null; + + return try p.addNode(.{ + .tag = .struct_field, + .main_token = main_token, + .data = .{ .enum_field = .{ + .payload = .init(payload), + .value = .init(value), + } }, + }); +} + fn parseBlock(p: *Parse) Error!NodeIndex { const trace = t.trace(@src()); defer trace.end(); diff --git a/test/behavior/enum.ix b/test/behavior/enum.ix new file mode 100644 index 0000000000000000000000000000000000000000..79643f3c089e007e13c7fd39f4d0435f794cebfc --- /dev/null +++ b/test/behavior/enum.ix @@ -0,0 +1,5 @@ +enum Mode { + first, + second, + @"enum", +}