diff --git a/debug/app.ona b/debug/app.ona index 8a50cd9..7ac7bb0 100644 --- a/debug/app.ona +++ b/debug/app.ona @@ -1,6 +1,8 @@ # Test comment. +pos = @vec3(10, 20, 0.3) + options = { .title = "Afterglow", .width = 1280, diff --git a/source/coral/io.zig b/source/coral/io.zig index 39025ee..ed90db8 100644 --- a/source/coral/io.zig +++ b/source/coral/io.zig @@ -207,6 +207,16 @@ pub fn Tag(comptime Element: type) type { pub const Writer = Generator(?usize, []const Byte); +pub fn all_equals(target: []const Byte, match: Byte) bool { + for (0 .. target.len) |index| { + if (target[index] != match) { + return false; + } + } + + return true; +} + pub fn allocate_copy(allocator: Allocator, source: []const Byte) AllocationError![]Byte { const allocation = try allocator.actions.reallocate(allocator.context, @returnAddress(), null, source.len); @@ -265,6 +275,20 @@ pub fn allocate_string(allocator: Allocator, source: []const Byte) AllocationErr return @ptrCast(allocation); } +pub fn are_equal(target: []const Byte, match: []const Byte) bool { + if (target.len != match.len) { + return false; + } + + for (0 .. target.len) |index| { + if (target[index] != match[index]) { + return false; + } + } + + return true; +} + pub fn bytes_of(value: anytype) []const Byte { const pointer_info = @typeInfo(@TypeOf(value)).Pointer; @@ -326,20 +350,6 @@ pub fn ends_with(target: []const Byte, match: []const Byte) bool { return true; } -pub fn equals(target: []const Byte, match: []const Byte) bool { - if (target.len != match.len) { - return false; - } - - for (0 .. target.len) |index| { - if (target[index] != match[index]) { - return false; - } - } - - return true; -} - pub fn find_first(haystack: []const Byte, needle: Byte) ?usize { for (0 .. haystack.len) |i| { if (haystack[i] == needle) { @@ -350,15 +360,23 @@ pub fn find_first(haystack: []const Byte, needle: Byte) ?usize { return null; } -var null_context = @as(usize, 0); +pub fn jenkins_hash(comptime int: std.builtin.Type.Int, bytes: []const Byte) math.Int(int) { + var hash = @as(math.Int(int), 0); -pub const null_writer = Writer.bind(usize, &null_context, struct { - fn write(context: *usize, buffer: []const u8) ?usize { - debug.assert(context.* == 0); + for (bytes) |byte| { + hash +%= byte; + hash +%= (hash << 10); + hash ^= (hash >> 6); + } - return buffer.len; - } -}.write); + hash +%= (hash << 3); + hash ^= (hash >> 11); + hash +%= (hash << 15); + + return hash; +} + +pub const null_writer = Writer.from(write_null); pub fn slice_sentineled(comptime sen: anytype, ptr: [*:sen]const @TypeOf(sen)) [:sen]const @TypeOf(sen) { var len = @as(usize, 0); @@ -374,6 +392,10 @@ pub fn tag_of(comptime value: anytype) Tag(@TypeOf(value)) { return @as(Tag(@TypeOf(value)), value); } +pub fn write_null(buffer: []const u8) ?usize { + return buffer.len; +} + pub fn zero(target: []Byte) void { for (target) |*t| t.* = 0; } diff --git a/source/coral/map.zig b/source/coral/map.zig index 30a8bdb..f19cd27 100644 --- a/source/coral/map.zig +++ b/source/coral/map.zig @@ -11,7 +11,7 @@ pub fn StringTable(comptime Value: type) type { const Self = @This(); fn equals(key_a: []const io.Byte, key_b: []const io.Byte) bool { - return io.equals(key_a, key_b); + return io.are_equal(key_a, key_b); } fn hash(key: []const io.Byte) usize { diff --git a/source/coral/utf8.zig b/source/coral/utf8.zig index 97fb597..c1e7843 100644 --- a/source/coral/utf8.zig +++ b/source/coral/utf8.zig @@ -40,7 +40,7 @@ pub const DecimalFormat = struct { }, else => { - if (self.delimiter.len == 0 or !io.equals(self.delimiter, utf8[index ..])) { + if (self.delimiter.len == 0 or !io.are_equal(self.delimiter, utf8[index ..])) { return null; } }, diff --git a/source/ona/kym.zig b/source/ona/kym.zig index 920ca6f..9489d4a 100644 --- a/source/ona/kym.zig +++ b/source/ona/kym.zig @@ -24,6 +24,8 @@ pub const RuntimeEnv = struct { const Builtin = enum { import, print, + vec2, + vec3, }; const Constant = union (enum) { @@ -167,15 +169,33 @@ pub const RuntimeEnv = struct { .builtin => |builtin| { coral.debug.assert(builtin.len != 0); - const decoded_builtin = @as(?Builtin, switch (builtin[0]) { - 'i' => if (coral.io.ends_with(builtin, "mport")) .import else null, - 'p' => if (coral.io.ends_with(builtin, "rint")) .print else null, - else => null, - }); + switch (builtin[0]) { + 'i' => { + if (coral.io.ends_with(builtin, "mport")) { + return chunk.opcodes.push_one(.{.push_builtin = .import}); + } + }, - try chunk.opcodes.push_one(.{.push_builtin = decoded_builtin orelse { - return chunk.env.raise(error.BadSyntax, "unknown builtin"); - }}); + 'p' => { + if (coral.io.ends_with(builtin, "rint")) { + return chunk.opcodes.push_one(.{.push_builtin = .print}); + } + }, + + 'v' => { + if (coral.io.ends_with(builtin, "ec2")) { + return chunk.opcodes.push_one(.{.push_builtin = .vec2}); + } + + if (coral.io.ends_with(builtin, "ec3")) { + return chunk.opcodes.push_one(.{.push_builtin = .vec3}); + } + }, + + else => {}, + } + + return chunk.env.raise(error.BadSyntax, "unknown builtin"); }, .local_get => |local_get| { @@ -257,7 +277,7 @@ pub const RuntimeEnv = struct { var index = @as(u8, self.local_identifiers_count - 1); while (true) : (index -= 1) { - if (coral.io.equals(local_identifier, self.local_identifiers_buffer[index])) { + if (coral.io.are_equal(local_identifier, self.local_identifiers_buffer[index])) { return index; } @@ -361,6 +381,8 @@ pub const RuntimeEnv = struct { const builtin_syscall = try self.env.new_syscall(switch (push_builtin) { .import => syscall_import, .print => syscall_print, + .vec2 => syscall_vec2, + .vec3 => syscall_vec3, }); errdefer self.env.discard(builtin_syscall); @@ -934,7 +956,7 @@ pub const RuntimeEnv = struct { const frame = self.frames.peek() orelse return self.raise(error.IllegalState, "stack underflow"); if (index < frame.arg_count) { - if (self.locals.values[frame.locals_top - (1 + index)]) |local| { + if (self.locals.values[(frame.locals_top - frame.arg_count) + index]) |local| { return self.acquire(local); } } @@ -991,7 +1013,7 @@ pub const RuntimeEnv = struct { if (object.ref_count == 0) { switch (object.payload) { - .false, .true, .float, .fixed, .symbol, .syscall => {}, + .false, .true, .float, .fixed, .symbol, .vector2, .vector3, .syscall => {}, .string => |string| { coral.debug.assert(string.len >= 0); @@ -1190,6 +1212,20 @@ pub const RuntimeEnv = struct { }); } + pub fn new_vector2(self: *RuntimeEnv, x: f32, y: f32) RuntimeError!*RuntimeRef { + return RuntimeRef.allocate(self.allocator, .{ + .ref_count = 1, + .payload = .{.vector2 = .{x, y}}, + }); + } + + pub fn new_vector3(self: *RuntimeEnv, x: f32, y: f32, z: f32) RuntimeError!*RuntimeRef { + return RuntimeRef.allocate(self.allocator, .{ + .ref_count = 1, + .payload = .{.vector3 = .{x, y, z}}, + }); + } + pub fn print(self: *RuntimeEnv, buffer: []const coral.io.Byte) void { if (self.options.print) |bound_print| { bound_print(buffer); @@ -1287,6 +1323,8 @@ pub const RuntimeRef = opaque { float: Float, fixed: Fixed, symbol: [*:0]const coral.io.Byte, + vector2: [2]f32, + vector3: [3]f32, syscall: *const Syscall, string: struct { @@ -1357,13 +1395,23 @@ pub const RuntimeRef = opaque { else => false, }, + .vector2 => |self_vector| switch (other.object().payload) { + .vector2 => |other_vector| coral.io.are_equal(coral.io.bytes_of(&self_vector), coral.io.bytes_of(&other_vector)), + else => false, + }, + + .vector3 => |self_vector| switch (other.object().payload) { + .vector3 => |other_vector| coral.io.are_equal(coral.io.bytes_of(&self_vector), coral.io.bytes_of(&other_vector)), + else => false, + }, + .syscall => |self_syscall| switch (other.object().payload) { .syscall => |other_syscall| self_syscall == other_syscall, else => false, }, .string => |self_string| switch (other.object().payload) { - .string => |other_string| coral.io.equals(self_string.unpack(), other_string.unpack()), + .string => |other_string| coral.io.are_equal(self_string.unpack(), other_string.unpack()), else => false, }, @@ -1384,6 +1432,8 @@ pub const RuntimeRef = opaque { .float => |float| @bitCast(float), .fixed => |fixed| @intCast(@as(u32, @bitCast(fixed))), .symbol => |symbol| @intFromPtr(symbol), + .vector2 => |vector| @bitCast(vector), + .vector3 => |vector| coral.io.jenkins_hash(@typeInfo(usize).Int, coral.io.bytes_of(&vector)), .syscall => |syscall| @intFromPtr(syscall), .string => |string| coral.io.djb2_hash(@typeInfo(usize).Int, string.unpack()), .dynamic => |dynamic| @intFromPtr(dynamic.typeinfo()) ^ @intFromPtr(dynamic.userdata().ptr), @@ -1404,6 +1454,8 @@ pub const RuntimeRef = opaque { .float => |float| float != 0, .fixed => |fixed| fixed != 0, .symbol => true, + .vector2 => |vector| coral.io.all_equals(coral.io.bytes_of(&vector), 0), + .vector3 => |vector| coral.io.all_equals(coral.io.bytes_of(&vector), 0), .syscall => true, .string => |string| string.len != 0, .dynamic => true, @@ -1480,3 +1532,43 @@ fn syscall_print(env: *RuntimeEnv) RuntimeError!?*RuntimeRef { return null; } + +fn syscall_vec2(env: *RuntimeEnv) RuntimeError!?*RuntimeRef { + const x = decode: { + const value = try env.expect(try env.acquire_arg(0)); + + defer env.discard(value); + + break: decode @as(f32, @floatCast(try env.unbox_float(value))); + }; + + if (try env.acquire_arg(1)) |y| { + defer env.discard(y); + + return env.new_vector2(x, @floatCast(try env.unbox_float(y))); + } + + return env.new_vector2(x, x); +} + +fn syscall_vec3(env: *RuntimeEnv) RuntimeError!?*RuntimeRef { + const x = decode: { + const value = try env.expect(try env.acquire_arg(0)); + + defer env.discard(value); + + break: decode @as(f32, @floatCast(try env.unbox_float(value))); + }; + + if (try env.acquire_arg(1)) |y| { + defer env.discard(y); + + const z = try env.expect(try env.acquire_arg(2)); + + defer env.discard(z); + + return env.new_vector3(x, @floatCast(try env.unbox_float(y)), @floatCast(try env.unbox_float(z))); + } + + return env.new_vector3(x, x, x); +}