feat: cheaper runtime value type, float and int arithmetic rules
This commit is contained in:
parent
9b5cd4038f
commit
92e8bcd866
13 changed files with 630 additions and 448 deletions
|
|
@ -8,193 +8,42 @@ const Story = @import("../Story.zig");
|
|||
const Object = @This();
|
||||
|
||||
tag: Tag,
|
||||
is_marked: bool,
|
||||
node: std.SinglyLinkedList.Node,
|
||||
is_marked: bool = false,
|
||||
node: std.SinglyLinkedList.Node = .{},
|
||||
|
||||
pub const Tag = enum {
|
||||
number,
|
||||
string,
|
||||
code,
|
||||
knot,
|
||||
|
||||
pub fn toStr(tag: Tag) []const u8 {
|
||||
pub fn tagBytes(tag: Tag) []const u8 {
|
||||
return switch (tag) {
|
||||
.number => "Number",
|
||||
.string => "String",
|
||||
.code => "Code",
|
||||
.knot => "Knot",
|
||||
};
|
||||
}
|
||||
|
||||
pub fn ObjectType(comptime tag: Tag) type {
|
||||
return switch (tag) {
|
||||
.string => Object.String,
|
||||
.code => Object.Code,
|
||||
.knot => Object.Knot,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn destroy(obj: *Object, story: *Story) void {
|
||||
switch (obj.tag) {
|
||||
.number => {
|
||||
const typed_obj: *Object.Number = @alignCast(@fieldParentPtr("base", obj));
|
||||
inline for (comptime std.meta.fields(Tag)) |field| {
|
||||
const tag = @field(Tag, field.name);
|
||||
if (obj.tag == tag) {
|
||||
const typed_obj: *Tag.ObjectType(tag) = @alignCast(@fieldParentPtr("base", obj));
|
||||
typed_obj.destroy(story);
|
||||
},
|
||||
.string => {
|
||||
const typed_obj: *Object.String = @alignCast(@fieldParentPtr("base", obj));
|
||||
typed_obj.destroy(story);
|
||||
},
|
||||
.code => {
|
||||
const typed_obj: *Object.Code = @alignCast(@fieldParentPtr("base", obj));
|
||||
typed_obj.destroy(story);
|
||||
},
|
||||
.knot => {
|
||||
const typed_obj: *Object.Knot = @alignCast(@fieldParentPtr("base", obj));
|
||||
typed_obj.destroy(story);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(story: *Story, lhs: *Object, rhs: *Object) !*Object {
|
||||
if (lhs.tag == .string or rhs.tag == .string) {
|
||||
const _lhs = try Object.String.fromObject(story, lhs);
|
||||
const _rhs = try Object.String.fromObject(story, rhs);
|
||||
const result = try Object.String.concat(story, _lhs, _rhs);
|
||||
return @ptrCast(result);
|
||||
} else if (lhs.tag == .number or rhs.tag == .number) {
|
||||
const _lhs = try Object.Number.fromObject(story, lhs);
|
||||
const _rhs = try Object.Number.fromObject(story, rhs);
|
||||
const result = try Object.Number.performArithmetic(story, .add, _lhs, _rhs);
|
||||
return @ptrCast(result);
|
||||
} else {
|
||||
return error.InvalidArgument;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cmpEql(story: *Story, lhs: *Object, rhs: *Object) !*Object {
|
||||
// TODO: This is temporary
|
||||
if (lhs.tag != .number or lhs.tag != rhs.tag) return error.InvalidComparison;
|
||||
const result = try Object.Number.performLogic(story, .cmp_eq, @ptrCast(lhs), @ptrCast(rhs));
|
||||
return @ptrCast(result);
|
||||
}
|
||||
|
||||
pub fn isFalsey(obj: *Object) bool {
|
||||
switch (obj.tag) {
|
||||
.number => {
|
||||
const number: *Object.Number = @ptrCast(obj);
|
||||
switch (number.data) {
|
||||
.boolean => |value| return !value,
|
||||
else => return false,
|
||||
}
|
||||
},
|
||||
else => return false,
|
||||
}
|
||||
}
|
||||
|
||||
pub const Number = struct {
|
||||
base: Object,
|
||||
data: Data,
|
||||
|
||||
pub const Data = union(enum) {
|
||||
boolean: bool,
|
||||
integer: i64,
|
||||
floating: f64,
|
||||
};
|
||||
|
||||
const Type = Object.Number;
|
||||
|
||||
pub fn create(story: *Story, data: Data) error{OutOfMemory}!*Object.Number {
|
||||
const gpa = story.allocator;
|
||||
const raw = try gpa.alignedAlloc(u8, .of(Type), @sizeOf(Type));
|
||||
const obj: *Type = @ptrCast(raw);
|
||||
obj.* = .{
|
||||
.base = .{
|
||||
.tag = .number,
|
||||
.is_marked = false,
|
||||
.node = .{},
|
||||
},
|
||||
.data = data,
|
||||
};
|
||||
|
||||
story.gc_objects.prepend(&obj.base.node);
|
||||
return obj;
|
||||
}
|
||||
|
||||
pub fn destroy(obj: *Object.Number, story: *Story) void {
|
||||
const gpa = story.allocator;
|
||||
const base: [*]align(@alignOf(Type)) u8 = @ptrCast(obj);
|
||||
gpa.free(base[0..@sizeOf(Type)]);
|
||||
}
|
||||
|
||||
pub fn fromObject(story: *Story, object: *Object) !*Object.Number {
|
||||
const data: Object.Number.Data = v: switch (object.tag) {
|
||||
.number => {
|
||||
const number: *Object.Number = @ptrCast(object);
|
||||
switch (number.data) {
|
||||
.boolean => |value| break :v .{ .integer = @intFromBool(value) },
|
||||
.integer => |value| break :v .{ .integer = value },
|
||||
.floating => |value| break :v .{ .floating = value },
|
||||
}
|
||||
},
|
||||
else => break :v .{ .boolean = true },
|
||||
};
|
||||
|
||||
const obj = Object.Number.create(story, data);
|
||||
// ink_gc_own(story, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
fn logicalOp(comptime T: type, op: Story.Opcode, lhs: T, rhs: T) bool {
|
||||
switch (op) {
|
||||
.cmp_eq => return lhs == rhs,
|
||||
.cmp_lt => return lhs < rhs,
|
||||
.cmp_gt => return lhs > rhs,
|
||||
.cmp_lte => return lhs <= rhs,
|
||||
.cmp_gte => return lhs >= rhs,
|
||||
else => unreachable,
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fn arithmeticOp(comptime T: type, op: Story.Opcode, lhs: T, rhs: T) T {
|
||||
switch (op) {
|
||||
.add => return lhs + rhs,
|
||||
.sub => return lhs - rhs,
|
||||
.mul => return lhs * rhs,
|
||||
.div => return @divTrunc(lhs, rhs),
|
||||
.mod => return @mod(lhs, rhs),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn negate(object: *Object.Number) *Object.Number {
|
||||
switch (object.data) {
|
||||
.integer => |*value| value.* = -value.*,
|
||||
.floating => |*value| value.* = -value.*,
|
||||
else => unreachable,
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
pub fn performLogic(
|
||||
story: *Story,
|
||||
op: Story.Opcode,
|
||||
lhs: *Object.Number,
|
||||
rhs: *Object.Number,
|
||||
) !*Object.Number {
|
||||
return .create(story, .{
|
||||
.boolean = logicalOp(i64, op, lhs.data.integer, rhs.data.integer),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn performArithmetic(
|
||||
story: *Story,
|
||||
op: Story.Opcode,
|
||||
lhs: *Object.Number,
|
||||
rhs: *Object.Number,
|
||||
) !*Object.Number {
|
||||
if (lhs.data == .floating or rhs.data == .floating) {
|
||||
return .create(story, .{
|
||||
.floating = arithmeticOp(f64, op, lhs.data.floating, rhs.data.floating),
|
||||
});
|
||||
}
|
||||
return .create(story, .{
|
||||
.integer = arithmeticOp(i64, op, lhs.data.integer, rhs.data.integer),
|
||||
});
|
||||
}
|
||||
};
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub const String = struct {
|
||||
base: Object,
|
||||
|
|
@ -215,11 +64,7 @@ pub const String = struct {
|
|||
const object: *Type = @ptrCast(raw);
|
||||
|
||||
object.* = .{
|
||||
.base = .{
|
||||
.tag = .string,
|
||||
.is_marked = false,
|
||||
.node = .{},
|
||||
},
|
||||
.base = .{ .tag = .string },
|
||||
.hash = 0,
|
||||
.length = options.bytes.len,
|
||||
.bytes = undefined,
|
||||
|
|
@ -246,22 +91,29 @@ pub const String = struct {
|
|||
return obj.bytes[0..obj.length];
|
||||
}
|
||||
|
||||
pub fn fromObject(story: *Story, obj: *Object) !*Object.String {
|
||||
switch (obj.tag) {
|
||||
.number => {
|
||||
// NOTE: 20 bytes should be enough.
|
||||
const print_buffer_len = 20;
|
||||
var print_buffer: [print_buffer_len]u8 = undefined;
|
||||
const number_object: *Object.Number = @ptrCast(obj);
|
||||
const number_bytes = try std.fmt.bufPrint(&print_buffer, "{}", .{
|
||||
number_object.data.integer,
|
||||
});
|
||||
return .create(story, .{
|
||||
.bytes = number_bytes,
|
||||
pub fn fromValue(story: *Story, value: Story.Value) !*Object.String {
|
||||
// NOTE: 20 bytes should be enough.
|
||||
const print_buffer_len = 20;
|
||||
var print_buffer: [print_buffer_len]u8 = undefined;
|
||||
switch (value) {
|
||||
.bool => |boolean| {
|
||||
const bytes = try std.fmt.bufPrint(&print_buffer, "{s}", .{
|
||||
if (boolean) "true" else "false",
|
||||
});
|
||||
return .create(story, .{ .bytes = bytes });
|
||||
},
|
||||
.int => |int| {
|
||||
const bytes = try std.fmt.bufPrint(&print_buffer, "{d}", .{int});
|
||||
return .create(story, .{ .bytes = bytes });
|
||||
},
|
||||
.float => |float| {
|
||||
const bytes = try std.fmt.bufPrint(&print_buffer, "{d}", .{float});
|
||||
return .create(story, .{ .bytes = bytes });
|
||||
},
|
||||
.object => |object| switch (object.tag) {
|
||||
.string => return @ptrCast(object),
|
||||
else => return error.TypeError,
|
||||
},
|
||||
.string => return @ptrCast(obj),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -275,9 +127,7 @@ pub const String = struct {
|
|||
@memcpy(bytes[lhs.length..], rhs.bytes[0..rhs.length]);
|
||||
//ink_gc_disown(story, INK_OBJ(lhs));
|
||||
//ink_gc_disown(story, INK_OBJ(rhs));
|
||||
return .create(story, .{
|
||||
.bytes = bytes,
|
||||
});
|
||||
return .create(story, .{ .bytes = bytes });
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -311,11 +161,7 @@ pub const Code = struct {
|
|||
const obj: *Type = @ptrCast(raw);
|
||||
|
||||
obj.* = .{
|
||||
.base = .{
|
||||
.tag = .code,
|
||||
.is_marked = false,
|
||||
.node = .{},
|
||||
},
|
||||
.base = .{ .tag = .code },
|
||||
.args_count = options.args_count,
|
||||
.locals_count = options.locals_count,
|
||||
.stack_size = options.stack_size,
|
||||
|
|
@ -358,11 +204,7 @@ pub const Knot = struct {
|
|||
const obj: *Type = @ptrCast(raw);
|
||||
|
||||
obj.* = .{
|
||||
.base = .{
|
||||
.tag = .knot,
|
||||
.is_marked = false,
|
||||
.node = .{},
|
||||
},
|
||||
.base = .{ .tag = .knot },
|
||||
.name = try .create(story, .{
|
||||
.bytes = options.name,
|
||||
}),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue