Skip to content

Commit

Permalink
feat: Type as values
Browse files Browse the repository at this point in the history
closes #21
  • Loading branch information
giann committed Aug 24, 2023
1 parent 24043f7 commit 5823569
Show file tree
Hide file tree
Showing 13 changed files with 400 additions and 16 deletions.
4 changes: 4 additions & 0 deletions src/buzz_api.zig
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,10 @@ export fn bz_valueEqual(self: Value, other: Value) Value {
return Value.fromBoolean(_value.valueEql(self, other));
}

export fn bz_valueTypeOf(self: Value, vm: *VM) Value {
return (self.typeOf(vm.gc) catch @panic("Out of memory")).toValue();
}

export fn bz_newMap(vm: *VM, map_type: Value) Value {
var map: *ObjMap = vm.gc.allocateObject(ObjMap, ObjMap.init(
vm.gc.allocator,
Expand Down
1 change: 1 addition & 0 deletions src/chunk.zig
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ pub const OpCode = enum(u8) {
OP_IMPORT,

OP_TO_STRING,
OP_TYPEOF,
};

/// A chunk of code to execute
Expand Down
1 change: 1 addition & 0 deletions src/disassembler.zig
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ pub fn disassembleInstruction(chunk: *Chunk, offset: usize) !usize {
.OP_RESOLVE,
.OP_TRY_END,
.OP_GET_ENUM_CASE_FROM_VALUE,
.OP_TYPEOF,
=> simpleInstruction(instruction, offset),

.OP_SWAP => bytesInstruction(instruction, chunk, offset),
Expand Down
1 change: 1 addition & 0 deletions src/lib/buzz_api.zig
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ pub const Value = packed struct {
pub extern fn bz_valueToClosure(value: Value) *ObjClosure;
pub extern fn bz_valueEqual(self: Value, other: Value) Value;
pub extern fn bz_valueIs(self: Value, type_def: Value) Value;
pub extern fn bz_valueTypeOf(self: Value, vm: *VM) Value;
};

pub const ObjClosure = opaque {};
Expand Down
51 changes: 51 additions & 0 deletions src/mirjit.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1742,6 +1742,8 @@ fn generateNode(self: *Self, node: *n.ParseNode) Error!?m.MIR_op_t {
.Pattern => try self.generatePattern(n.PatternNode.cast(node).?),
.ForEach => try self.generateForEach(n.ForEachNode.cast(node).?),
.InlineIf => try self.generateInlineIf(n.InlineIfNode.cast(node).?),
.TypeExpression => try self.generateTypeExpression(n.TypeExpressionNode.cast(node).?),
.TypeOfExpression => try self.generateTypeOfExpression(n.TypeOfExpressionNode.cast(node).?),
.AsyncCall,
.Resume,
.Resolve,
Expand Down Expand Up @@ -2536,6 +2538,32 @@ fn generateIf(self: *Self, if_node: *n.IfNode) Error!?m.MIR_op_t {
return null;
}

fn generateTypeExpression(self: *Self, type_expression_node: *n.TypeExpressionNode) Error!?m.MIR_op_t {
return m.MIR_new_uint_op(
self.ctx,
type_expression_node.value.val,
);
}

fn generateTypeOfExpression(self: *Self, typeof_expression_node: *n.TypeOfExpressionNode) Error!?m.MIR_op_t {
const value = (try self.generateNode(typeof_expression_node.expression)).?;
const result = m.MIR_new_reg_op(
self.ctx,
try self.REG("typeof", m.MIR_T_I64),
);

try self.buildExternApiCall(
.bz_valueTypeOf,
result,
&[_]m.MIR_op_t{
value,
m.MIR_new_reg_op(self.ctx, self.state.?.vm_reg.?),
},
);

return result;
}

fn generateInlineIf(self: *Self, inline_if_node: *n.InlineIfNode) Error!?m.MIR_op_t {
const constant_condition = if (inline_if_node.condition.isConstant(inline_if_node.condition))
inline_if_node.condition.toValue(inline_if_node.condition, self.vm.gc) catch unreachable
Expand Down Expand Up @@ -5421,6 +5449,7 @@ pub const ExternApi = enum {
bz_fstructGet,
bz_fstructSet,
bz_fstructInstance,
bz_valueTypeOf,

bz_dumpStack,

Expand Down Expand Up @@ -6192,6 +6221,25 @@ pub const ExternApi = enum {
},
},
),
.bz_valueTypeOf => m.MIR_new_proto_arr(
ctx,
self.pname(),
1,
&[_]m.MIR_type_t{m.MIR_T_I64},
2,
&[_]m.MIR_var_t{
.{
.type = m.MIR_T_U64,
.name = "value",
.size = undefined,
},
.{
.type = m.MIR_T_P,
.name = "vm",
.size = undefined,
},
},
),
};
}

Expand Down Expand Up @@ -6248,6 +6296,7 @@ pub const ExternApi = enum {
.bz_fstructGet => @as(*anyopaque, @ptrFromInt(@intFromPtr(&api.ObjForeignStruct.bz_fstructGet))),
.bz_fstructSet => @as(*anyopaque, @ptrFromInt(@intFromPtr(&api.ObjForeignStruct.bz_fstructSet))),
.bz_fstructInstance => @as(*anyopaque, @ptrFromInt(@intFromPtr(&api.ObjForeignStruct.bz_fstructInstance))),
.bz_valueTypeOf => @as(*anyopaque, @ptrFromInt(@intFromPtr(&api.Value.bz_valueTypeOf))),
.setjmp => @as(
*anyopaque,
@ptrFromInt(
Expand Down Expand Up @@ -6319,6 +6368,7 @@ pub const ExternApi = enum {
.bz_fstructGet => "bz_fstructGet",
.bz_fstructSet => "bz_fstructSet",
.bz_fstructInstance => "bz_fstructInstance",
.bz_valueTypeOf => "bz_valueTypeOf",

.setjmp => if (builtin.os.tag == .macos or builtin.os.tag == .linux or builtin.os.tag == .windows) "_setjmp" else "setjmp",
.exit => "bz_exit",
Expand Down Expand Up @@ -6384,6 +6434,7 @@ pub const ExternApi = enum {
.bz_fstructGet => "p_bz_fstructGet",
.bz_fstructSet => "p_bz_fstructSet",
.bz_fstructInstance => "p_bz_fstructInstance",
.bz_valueTypeOf => "p_bz_valueTypeOf",

.setjmp => if (builtin.os.tag == .macos or builtin.os.tag == .linux or builtin.os.windows) "p__setjmp" else "p_setjmp",
.exit => "p_exit",
Expand Down
183 changes: 183 additions & 0 deletions src/node.zig
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ pub const ParseNodeType = enum(u8) {
Try,
Range,
Zdef,
TypeExpression,
TypeOfExpression,
};

pub const RenderError = Allocator.Error || std.fmt.BufPrintError;
Expand Down Expand Up @@ -7823,6 +7825,187 @@ pub const ExportNode = struct {
}
};

pub const TypeExpressionNode = struct {
const Self = @This();

node: ParseNode = .{
.node_type = .TypeExpression,
.toJson = stringify,
.toByteCode = generate,
.toValue = val,
.isConstant = constant,
.render = render,
},

value: Value,

fn constant(_: *anyopaque) bool {
return true;
}

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

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

if (node.synchronize(codegen)) {
return null;
}

const self = Self.cast(node).?;

try codegen.emitConstant(self.node.location, self.value);

try node.patchOptJumps(codegen);
try node.endScope(codegen);

return null;
}

fn stringify(nodePtr: *anyopaque, out: *const std.ArrayList(u8).Writer) RenderError!void {
var node: *ParseNode = @ptrCast(@alignCast(nodePtr));
var self = Self.cast(node).?;

try out.print("{{\"node\": \"TypeExpression\", \"value\": \"", .{});

try ObjTypeDef.cast(self.value.obj()).?.toString(out);

try out.print("\", ", .{});

try ParseNode.stringify(node, out);

try out.writeAll("}");
}

fn render(nodePtr: *anyopaque, out: *const std.ArrayList(u8).Writer, depth: usize) RenderError!void {
const node: *ParseNode = @ptrCast(@alignCast(nodePtr));
const self = Self.cast(node).?;

try out.writeByteNTimes(' ', depth * 4);

try out.print("<", .{});

try ObjTypeDef.cast(self.value.obj()).?.toString(out);

try out.writeAll(">");
}

pub fn toNode(self: *Self) *ParseNode {
return &self.node;
}

pub fn cast(nodePtr: *anyopaque) ?*Self {
var node: *ParseNode = @ptrCast(@alignCast(nodePtr));

if (node.node_type != .TypeExpression) {
return null;
}

return @fieldParentPtr(Self, "node", node);
}
};

pub const TypeOfExpressionNode = struct {
const Self = @This();

node: ParseNode = .{
.node_type = .TypeOfExpression,
.toJson = stringify,
.toByteCode = generate,
.toValue = val,
.isConstant = constant,
.render = render,
},

expression: *ParseNode,

fn constant(nodePtr: *anyopaque) bool {
const node: *ParseNode = @ptrCast(@alignCast(nodePtr));
const self = Self.cast(node).?;

return self.expression.isConstant(self.expression);
}

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

const expr = try self.expression.toValue(self.expression, gc);

return (try Value.typeOf(expr, gc)).toValue();
}

return GenError.NotConstant;
}

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

if (node.synchronize(codegen)) {
return null;
}

const self = Self.cast(node).?;

_ = try self.expression.toByteCode(self.expression, codegen, breaks);

try codegen.emitOpCode(node.location, .OP_TYPEOF);

try node.patchOptJumps(codegen);
try node.endScope(codegen);

return null;
}

fn stringify(nodePtr: *anyopaque, out: *const std.ArrayList(u8).Writer) RenderError!void {
var node: *ParseNode = @ptrCast(@alignCast(nodePtr));
var self = Self.cast(node).?;

try out.print("{{\"node\": \"TypeOfExpression\", \"expression\": \"", .{});

try self.expression.toJson(self.expression, out);

try out.print(", ", .{});

try ParseNode.stringify(node, out);

try out.writeAll("}");
}

fn render(nodePtr: *anyopaque, out: *const std.ArrayList(u8).Writer, depth: usize) RenderError!void {
const node: *ParseNode = @ptrCast(@alignCast(nodePtr));
const self = Self.cast(node).?;

try out.writeByteNTimes(' ', depth * 4);

try out.print("<", .{});

try self.expression.render(self.expression, out, depth);

try out.writeAll(">");
}

pub fn toNode(self: *Self) *ParseNode {
return &self.node;
}

pub fn cast(nodePtr: *anyopaque) ?*Self {
var node: *ParseNode = @ptrCast(@alignCast(nodePtr));

if (node.node_type != .TypeOfExpression) {
return null;
}

return @fieldParentPtr(Self, "node", node);
}
};

pub const ZdefNode = struct {
const Self = @This();

Expand Down
40 changes: 38 additions & 2 deletions src/obj.zig
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,42 @@ pub const Obj = struct {
return obj.cast(T, obj_type);
}

pub fn typeOf(self: *Self, gc: *GarbageCollector) anyerror!*ObjTypeDef {
return switch (self.obj_type) {
.String => try gc.type_registry.getTypeDef(.{ .def_type = .String }),
.Pattern => try gc.type_registry.getTypeDef(.{ .def_type = .Pattern }),
.Fiber => try gc.type_registry.getTypeDef(.{ .def_type = .Fiber }),
.Type, .Object, .Enum => try gc.type_registry.getTypeDef(.{ .def_type = .Type }),
.ObjectInstance => instance: {
const obj_instance = ObjObjectInstance.cast(self).?;

if (obj_instance.object) |object| {
break :instance object.type_def;
} else {
break :instance obj_instance.type_def.?;
}
},
.EnumInstance => ObjEnumInstance.cast(self).?.enum_ref.type_def,
.Function => ObjFunction.cast(self).?.type_def,
.UpValue => upvalue: {
const upvalue: *ObjUpValue = ObjUpValue.cast(self).?;

break :upvalue (upvalue.closed orelse upvalue.location.*).typeOf(gc);
},
.Closure => ObjClosure.cast(self).?.function.type_def,
.List => ObjList.cast(self).?.type_def,
.Map => ObjMap.cast(self).?.type_def,
.Bound => bound: {
const bound: *ObjBoundMethod = ObjBoundMethod.cast(self).?;
break :bound try (if (bound.closure) |cls| cls.function.toValue() else bound.native.?.toValue()).typeOf(gc);
},
.ForeignStruct => ObjForeignStruct.cast(self).?.type_def,
.UserData => try gc.type_registry.getTypeDef(.{ .def_type = .UserData }),
// FIXME: apart from list/map types we actually can embark typedef of objnatives at runtime
.Native => unreachable,
};
}

pub fn is(self: *Self, type_def: *ObjTypeDef) bool {
return switch (self.obj_type) {
.String => type_def.def_type == .String,
Expand Down Expand Up @@ -110,8 +146,8 @@ pub const Obj = struct {
);
},
.ForeignStruct => type_def.def_type == .ForeignStruct and ObjForeignStruct.cast(self).?.is(type_def),

.UserData, .Native => unreachable, // TODO: we don't know how to embark NativeFn type at runtime yet
.UserData => type_def.def_type == .UserData,
.Native => unreachable, // TODO: we don't know how to embark NativeFn type at runtime yet
};
}

Expand Down
Loading

0 comments on commit 5823569

Please sign in to comment.