refactor: change object.zig module into file struct
This commit is contained in:
parent
33e3c1c271
commit
e8ad1cd5b5
3 changed files with 313 additions and 315 deletions
|
|
@ -5,7 +5,7 @@ const tokenizer = @import("tokenizer.zig");
|
||||||
const Ast = @import("Ast.zig");
|
const Ast = @import("Ast.zig");
|
||||||
const AstGen = @import("AstGen.zig");
|
const AstGen = @import("AstGen.zig");
|
||||||
const Sema = @import("Sema.zig");
|
const Sema = @import("Sema.zig");
|
||||||
pub const Object = @import("Story/object.zig").Object;
|
pub const Object = @import("Story/Object.zig");
|
||||||
const Dumper = @import("Story/Dumper.zig");
|
const Dumper = @import("Story/Dumper.zig");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const Story = @This();
|
const Story = @This();
|
||||||
|
|
|
||||||
312
src/Story/Object.zig
Normal file
312
src/Story/Object.zig
Normal file
|
|
@ -0,0 +1,312 @@
|
||||||
|
//! Runtime Object, whose memory is managed through the virtual machine's
|
||||||
|
//! garbage collector.
|
||||||
|
//!
|
||||||
|
//! This structure is the base object type that is embedded into subtypes.
|
||||||
|
//! Each subtype has a `.create` method to allocate a new object.
|
||||||
|
const std = @import("std");
|
||||||
|
const Story = @import("../Story.zig");
|
||||||
|
const Object = @This();
|
||||||
|
|
||||||
|
tag: Tag,
|
||||||
|
is_marked: bool,
|
||||||
|
node: std.SinglyLinkedList.Node,
|
||||||
|
|
||||||
|
pub const Tag = enum {
|
||||||
|
number,
|
||||||
|
string,
|
||||||
|
content_path,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn destroy(obj: *Object, story: *Story) void {
|
||||||
|
switch (obj.tag) {
|
||||||
|
.number => {
|
||||||
|
const typed_obj: *Object.Number = @alignCast(@fieldParentPtr("base", obj));
|
||||||
|
typed_obj.destroy(story);
|
||||||
|
},
|
||||||
|
.string => {
|
||||||
|
const typed_obj: *Object.String = @alignCast(@fieldParentPtr("base", obj));
|
||||||
|
typed_obj.destroy(story);
|
||||||
|
},
|
||||||
|
.content_path => {
|
||||||
|
const typed_obj: *Object.ContentPath = @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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const String = struct {
|
||||||
|
base: Object,
|
||||||
|
hash: u32, // TODO: Not implemented.
|
||||||
|
length: usize, // TODO: This could probably be u32.
|
||||||
|
bytes: [*]const u8,
|
||||||
|
|
||||||
|
const Type = Object.String;
|
||||||
|
|
||||||
|
pub fn create(
|
||||||
|
story: *Story,
|
||||||
|
bytes: []const u8,
|
||||||
|
) error{OutOfMemory}!*Object.String {
|
||||||
|
const gpa = story.allocator;
|
||||||
|
const alloc_len = @sizeOf(Type) + bytes.len + 1;
|
||||||
|
const raw = try gpa.alignedAlloc(u8, .of(Type), alloc_len);
|
||||||
|
const object: *Type = @ptrCast(raw);
|
||||||
|
|
||||||
|
object.* = .{
|
||||||
|
.base = .{
|
||||||
|
.tag = .string,
|
||||||
|
.is_marked = false,
|
||||||
|
.node = .{},
|
||||||
|
},
|
||||||
|
.hash = 0,
|
||||||
|
.length = bytes.len,
|
||||||
|
.bytes = undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Point bytes slice to the memory *after* the struct
|
||||||
|
const buf = raw[@sizeOf(Type)..][0 .. bytes.len + 1];
|
||||||
|
object.bytes = buf.ptr;
|
||||||
|
@memcpy(buf[0..bytes.len], bytes);
|
||||||
|
buf[bytes.len] = 0;
|
||||||
|
|
||||||
|
story.gc_objects.prepend(&object.base.node);
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(obj: *String, story: *Story) void {
|
||||||
|
const gpa = story.allocator;
|
||||||
|
const alloc_len = @sizeOf(Type) + obj.length + 1;
|
||||||
|
const base: [*]align(@alignOf(Type)) u8 = @ptrCast(obj);
|
||||||
|
gpa.free(base[0..alloc_len]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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, number_bytes);
|
||||||
|
},
|
||||||
|
.string => return @ptrCast(obj),
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn concat(story: *Story, lhs: *String, rhs: *String) !*Object.String {
|
||||||
|
const gpa = story.allocator;
|
||||||
|
const length = lhs.length + rhs.length;
|
||||||
|
const bytes = try gpa.alloc(u8, length + 1);
|
||||||
|
defer gpa.free(bytes);
|
||||||
|
|
||||||
|
@memcpy(bytes[0..lhs.length], lhs.bytes[0..lhs.length]);
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ContentPath = struct {
|
||||||
|
base: Object,
|
||||||
|
name: *Object.String,
|
||||||
|
arity: usize,
|
||||||
|
// TODO: Rename this to stack size.
|
||||||
|
locals_count: usize,
|
||||||
|
// TODO: Rename this to constant_pool.
|
||||||
|
const_pool: []u32,
|
||||||
|
bytes: []const u8,
|
||||||
|
|
||||||
|
const Type = Object.ContentPath;
|
||||||
|
|
||||||
|
pub const CreateOptions = struct {
|
||||||
|
name: *Object.String,
|
||||||
|
arity: usize,
|
||||||
|
locals_count: usize,
|
||||||
|
const_pool: []u32,
|
||||||
|
bytes: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn create(story: *Story, options: CreateOptions) error{OutOfMemory}!*Object.ContentPath {
|
||||||
|
const gpa = story.allocator;
|
||||||
|
const alloc_len = @sizeOf(Type);
|
||||||
|
const raw = try gpa.alignedAlloc(u8, .of(Type), alloc_len);
|
||||||
|
const obj: *Type = @ptrCast(raw);
|
||||||
|
|
||||||
|
obj.* = .{
|
||||||
|
.base = .{
|
||||||
|
.tag = .content_path,
|
||||||
|
.is_marked = false,
|
||||||
|
.node = .{},
|
||||||
|
},
|
||||||
|
.name = options.name,
|
||||||
|
.arity = options.arity,
|
||||||
|
.locals_count = options.locals_count,
|
||||||
|
.const_pool = options.const_pool,
|
||||||
|
.bytes = options.bytes,
|
||||||
|
};
|
||||||
|
|
||||||
|
story.gc_objects.prepend(&obj.base.node);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(obj: *ContentPath, story: *Story) void {
|
||||||
|
const gpa = story.allocator;
|
||||||
|
gpa.free(obj.const_pool);
|
||||||
|
gpa.free(obj.bytes);
|
||||||
|
|
||||||
|
const base: [*]align(@alignOf(Type)) u8 = @ptrCast(obj);
|
||||||
|
gpa.free(base[0..@sizeOf(Type)]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,314 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const Story = @import("../Story.zig");
|
|
||||||
|
|
||||||
/// Runtime Object, whose memory is managed through the virtual machine's
|
|
||||||
/// garbage collector.
|
|
||||||
///
|
|
||||||
/// This structure is the base object type that is embedded into subtypes.
|
|
||||||
/// Each subtype has a `.create` method to allocate a new object.
|
|
||||||
pub const Object = struct {
|
|
||||||
tag: Tag,
|
|
||||||
is_marked: bool,
|
|
||||||
node: std.SinglyLinkedList.Node,
|
|
||||||
|
|
||||||
pub const Tag = enum {
|
|
||||||
number,
|
|
||||||
string,
|
|
||||||
content_path,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn destroy(object: *Object, story: *Story) void {
|
|
||||||
switch (object.tag) {
|
|
||||||
.number => {
|
|
||||||
const obj: *Object.Number = @alignCast(@fieldParentPtr("base", object));
|
|
||||||
obj.destroy(story);
|
|
||||||
},
|
|
||||||
.string => {
|
|
||||||
const obj: *Object.String = @alignCast(@fieldParentPtr("base", object));
|
|
||||||
obj.destroy(story);
|
|
||||||
},
|
|
||||||
.content_path => {
|
|
||||||
const obj: *Object.ContentPath = @alignCast(@fieldParentPtr("base", object));
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn create(story: *Story, data: Data) error{OutOfMemory}!*Object.Number {
|
|
||||||
const gpa = story.allocator;
|
|
||||||
const alloc_len = @sizeOf(Object.Number);
|
|
||||||
const raw = try gpa.alignedAlloc(u8, .of(Object.Number), alloc_len);
|
|
||||||
const object: *Object.Number = @ptrCast(raw);
|
|
||||||
|
|
||||||
object.* = .{
|
|
||||||
.base = .{
|
|
||||||
.tag = .number,
|
|
||||||
.is_marked = false,
|
|
||||||
.node = .{},
|
|
||||||
},
|
|
||||||
.data = data,
|
|
||||||
};
|
|
||||||
|
|
||||||
story.gc_objects.prepend(&object.base.node);
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn destroy(obj: *Object.Number, story: *Story) void {
|
|
||||||
const gpa = story.allocator;
|
|
||||||
const alloc_len = @sizeOf(Object.Number);
|
|
||||||
const base: [*]align(@alignOf(Object.Number)) u8 = @ptrCast(obj);
|
|
||||||
gpa.free(base[0..alloc_len]);
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const String = struct {
|
|
||||||
base: Object,
|
|
||||||
hash: u32, // TODO: Not implemented.
|
|
||||||
length: usize, // TODO: This could probably be u32.
|
|
||||||
bytes: [*]const u8,
|
|
||||||
|
|
||||||
pub fn create(
|
|
||||||
story: *Story,
|
|
||||||
bytes: []const u8,
|
|
||||||
) error{OutOfMemory}!*Object.String {
|
|
||||||
const gpa = story.allocator;
|
|
||||||
const alloc_len = @sizeOf(String) + bytes.len + 1;
|
|
||||||
const raw = try gpa.alignedAlloc(u8, .of(String), alloc_len);
|
|
||||||
const object: *String = @ptrCast(raw);
|
|
||||||
|
|
||||||
object.* = .{
|
|
||||||
.base = .{
|
|
||||||
.tag = .string,
|
|
||||||
.is_marked = false,
|
|
||||||
.node = .{},
|
|
||||||
},
|
|
||||||
.hash = 0,
|
|
||||||
.length = bytes.len,
|
|
||||||
.bytes = undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Point bytes slice to the memory *after* the struct
|
|
||||||
const buf = raw[@sizeOf(String)..][0 .. bytes.len + 1];
|
|
||||||
object.bytes = buf.ptr;
|
|
||||||
@memcpy(buf[0..bytes.len], bytes);
|
|
||||||
buf[bytes.len] = 0;
|
|
||||||
|
|
||||||
story.gc_objects.prepend(&object.base.node);
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn destroy(object: *Object.String, story: *Story) void {
|
|
||||||
const gpa = story.allocator;
|
|
||||||
const alloc_len = @sizeOf(Object.String) + object.length + 1;
|
|
||||||
const base: [*]align(@alignOf(Object.String)) u8 = @ptrCast(object);
|
|
||||||
gpa.free(base[0..alloc_len]);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fromObject(story: *Story, object: *Object) !*Object.String {
|
|
||||||
switch (object.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(object);
|
|
||||||
const number_bytes = try std.fmt.bufPrint(&print_buffer, "{}", .{
|
|
||||||
number_object.data.integer,
|
|
||||||
});
|
|
||||||
return Object.String.create(story, number_bytes);
|
|
||||||
},
|
|
||||||
.string => return @ptrCast(object),
|
|
||||||
else => unreachable,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn concat(story: *Story, lhs: *String, rhs: *String) !*Object.String {
|
|
||||||
const gpa = story.allocator;
|
|
||||||
const length = lhs.length + rhs.length;
|
|
||||||
const bytes = try gpa.alloc(u8, length + 1);
|
|
||||||
defer gpa.free(bytes);
|
|
||||||
|
|
||||||
@memcpy(bytes[0..lhs.length], lhs.bytes[0..lhs.length]);
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const ContentPath = struct {
|
|
||||||
base: Object,
|
|
||||||
name: *Object.String,
|
|
||||||
arity: usize,
|
|
||||||
// TODO: Rename this to stack size.
|
|
||||||
locals_count: usize,
|
|
||||||
// TODO: Rename this to constant_pool.
|
|
||||||
const_pool: []u32,
|
|
||||||
bytes: []const u8,
|
|
||||||
|
|
||||||
pub const CreateOptions = struct {
|
|
||||||
name: *Object.String,
|
|
||||||
arity: usize,
|
|
||||||
locals_count: usize,
|
|
||||||
const_pool: []u32,
|
|
||||||
bytes: []const u8,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn create(
|
|
||||||
story: *Story,
|
|
||||||
options: CreateOptions,
|
|
||||||
) error{OutOfMemory}!*ContentPath {
|
|
||||||
const gpa = story.allocator;
|
|
||||||
const alloc_len = @sizeOf(ContentPath);
|
|
||||||
const raw = try gpa.alignedAlloc(u8, .of(ContentPath), alloc_len);
|
|
||||||
const object: *ContentPath = @ptrCast(raw);
|
|
||||||
|
|
||||||
object.* = .{
|
|
||||||
.base = .{
|
|
||||||
.tag = .content_path,
|
|
||||||
.is_marked = false,
|
|
||||||
.node = .{},
|
|
||||||
},
|
|
||||||
.name = options.name,
|
|
||||||
.arity = options.arity,
|
|
||||||
.locals_count = options.locals_count,
|
|
||||||
.const_pool = options.const_pool,
|
|
||||||
.bytes = options.bytes,
|
|
||||||
};
|
|
||||||
|
|
||||||
story.gc_objects.prepend(&object.base.node);
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn destroy(obj: *ContentPath, story: *Story) void {
|
|
||||||
const gpa = story.allocator;
|
|
||||||
gpa.free(obj.const_pool);
|
|
||||||
gpa.free(obj.bytes);
|
|
||||||
|
|
||||||
const alloc_len = @sizeOf(ContentPath);
|
|
||||||
const base: [*]align(@alignOf(ContentPath)) u8 = @ptrCast(obj);
|
|
||||||
gpa.free(base[0..alloc_len]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue