Skip to content

Commit

Permalink
fix(enum): Enum and enum instance are constant value
Browse files Browse the repository at this point in the history
closes #145
  • Loading branch information
giann committed Sep 25, 2023
1 parent 99bf45c commit 52a15c9
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 20 deletions.
69 changes: 51 additions & 18 deletions src/node.zig
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const ObjFunction = _obj.ObjFunction;
const ObjObject = _obj.ObjObject;
const ObjList = _obj.ObjList;
const ObjEnum = _obj.ObjEnum;
const ObjEnumInstance = _obj.ObjEnumInstance;
const ObjPattern = _obj.ObjPattern;
const ObjMap = _obj.ObjMap;
const ObjBoundMethod = _obj.ObjBoundMethod;
Expand Down Expand Up @@ -5091,11 +5092,29 @@ pub const EnumNode = struct {
return false;
}

fn val(_: *anyopaque, _: *GarbageCollector) anyerror!Value {
return GenError.NotConstant;
pub fn val(nodePtr: *anyopaque, gc: *GarbageCollector) anyerror!Value {
const node: *ParseNode = @ptrCast(@alignCast(nodePtr));
const self = Self.cast(node).?;

if (node.type_def.?.resolved_type.?.Enum.value) |enum_| {
return enum_.toValue();
}

var enum_ = try gc.allocateObject(
ObjEnum,
ObjEnum.init(gc.allocator, node.type_def.?),
);

for (self.cases.items) |case| {
try enum_.cases.append(try case.toValue(case, gc));
}

node.type_def.?.resolved_type.?.Enum.value = enum_;

return enum_.toValue();
}

fn generate(nodePtr: *anyopaque, codegenPtr: *anyopaque, breaks: ?*std.ArrayList(usize)) anyerror!?*ObjFunction {
fn generate(nodePtr: *anyopaque, codegenPtr: *anyopaque, _: ?*std.ArrayList(usize)) anyerror!?*ObjFunction {
var codegen: *CodeGen = @ptrCast(@alignCast(codegenPtr));
var node: *ParseNode = @ptrCast(@alignCast(nodePtr));

Expand All @@ -5121,11 +5140,6 @@ pub const EnumNode = struct {
},
}

try codegen.emitCodeArg(self.node.location, .OP_ENUM, try codegen.makeConstant(node.type_def.?.toValue()));
try codegen.emitCodeArg(self.node.location, .OP_DEFINE_GLOBAL, @intCast(self.slot));

try codegen.emitCodeArg(self.node.location, .OP_GET_GLOBAL, @intCast(self.slot));

for (self.cases.items) |case| {
if (case.type_def == null or case.type_def.?.def_type == .Placeholder) {
codegen.reporter.reportPlaceholder(case.type_def.?.resolved_type.?.Placeholder);
Expand All @@ -5139,13 +5153,17 @@ pub const EnumNode = struct {
"Bad enum case type",
);
}

_ = try case.toByteCode(case, codegen, breaks);

try codegen.emitOpCode(self.node.location, .OP_ENUM_CASE);
}

try codegen.emitOpCode(self.node.location, .OP_POP);
// Since an enum contains only constant values we can make the constant right away
try codegen.emitCodeArg(
self.node.location,
.OP_CONSTANT,
try codegen.makeConstant(
try val(self.toNode(), codegen.gc),
),
);
try codegen.emitCodeArg(self.node.location, .OP_DEFINE_GLOBAL, @intCast(self.slot));

try node.patchOptJumps(codegen);
try node.endScope(codegen);
Expand Down Expand Up @@ -6837,13 +6855,28 @@ pub const DotNode = struct {
call: ?*CallNode = null,
enum_index: ?usize = null,

fn constant(_: *anyopaque) bool {
// TODO: should be true, but we have to evaluate a constant call
return false;
fn constant(nodePtr: *anyopaque) bool {
const node: *ParseNode = @ptrCast(@alignCast(nodePtr));
const self = Self.cast(node).?;

return self.callee.type_def.?.def_type == .Enum and self.callee.type_def.?.resolved_type.?.Enum.value != null;
}

fn val(_: *anyopaque, _: *GarbageCollector) anyerror!Value {
return GenError.NotConstant;
fn val(nodePtr: *anyopaque, gc: *GarbageCollector) anyerror!Value {
const node: *ParseNode = @ptrCast(@alignCast(nodePtr));
const self = Self.cast(node).?;

if (self.callee.type_def.?.def_type != .Enum or self.callee.type_def.?.resolved_type.?.Enum.value == null) {
return GenError.NotConstant;
}

return (try gc.allocateObject(
ObjEnumInstance,
ObjEnumInstance{
.enum_ref = self.callee.type_def.?.resolved_type.?.Enum.value.?,
.case = @intCast(self.enum_index.?),
},
)).toValue();
}

fn generate(nodePtr: *anyopaque, codegenPtr: *anyopaque, breaks: ?*std.ArrayList(usize)) anyerror!?*ObjFunction {
Expand Down
2 changes: 2 additions & 0 deletions src/obj.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3186,6 +3186,8 @@ pub const ObjEnum = struct {
enum_type: *ObjTypeDef,
// TODO: should be a slice
cases: std.ArrayList([]const u8),
// Circular reference but needed so that we can generate enum case at compile time
value: ?*ObjEnum = null,

pub fn init(allocator: Allocator, name: *ObjString, qualified_name: *ObjString, enum_type: *ObjTypeDef) EnumDefSelf {
return EnumDefSelf{
Expand Down
2 changes: 2 additions & 0 deletions src/parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3006,6 +3006,8 @@ pub const Parser = struct {
node.node.location = start_location;
node.node.end_location = self.parser.previous_token.?;

enum_type.resolved_type.?.Enum.value = ObjEnum.cast((try EnumNode.val(&node.node, self.gc)).obj()).?;

return &node.node;
}

Expand Down
18 changes: 16 additions & 2 deletions tests/006-enums.buzz
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ enum NaturalEnum {
two,
}

fun getValue(NaturalEnum case = NaturalEnum.zero) > int {
return case.value;
}

object Natural {
NaturalEnum natural = NaturalEnum.zero,
}

test "Enums" {
assert(StrEnum.one.value == "one", message: "str enum");

Expand All @@ -31,6 +39,12 @@ test "Enums" {
assert(myCase.value == 2, message: "enum instance");

NaturalEnum? fromValue = NaturalEnum(0);
assert(fromValue != null, message: "could get enum instance from value");
assert(fromValue!.value == 0, message: "could get correct enum instance from value");
assert(fromValue != null, message: "Could get enum instance from value");
assert(fromValue!.value == 0, message: "Could get correct enum instance from value");
}

test "Enum case as default value" {
assert(getValue() == 0, message: "Could use enum case as function argument default value");

assert(Natural{}.natural == NaturalEnum.zero, message: "Could use enum case as object field default value");
}

0 comments on commit 52a15c9

Please sign in to comment.