Refactor Kym #47
| @ -57,7 +57,7 @@ pub const Stacking = struct { | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn free(self: *Stacking) void { | ||||
| 	pub fn deinit(self: *Stacking) void { | ||||
| 		while (self.head_region) |region| { | ||||
| 			const next_region = region.next; | ||||
| 
 | ||||
| @ -75,6 +75,15 @@ pub const Stacking = struct { | ||||
| 		unreachable; | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn init(allocator: io.Allocator, min_region_size: usize) Stacking { | ||||
| 		return Stacking{ | ||||
| 			.allocator = allocator, | ||||
| 			.min_region_size = min_region_size, | ||||
| 			.head_region = null, | ||||
| 			.tail_region = null, | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
| 	fn reallocate(self: *Stacking, _: usize, allocation: ?[]io.Byte, byte_size: usize) io.AllocationError![]io.Byte { | ||||
| 		if (allocation) |buffer| { | ||||
| 			if (byte_size < buffer.len) { | ||||
| @ -104,13 +113,4 @@ pub const Stacking = struct { | ||||
| 
 | ||||
| 		return @as([*]io.Byte, @ptrCast(tail_region.allocate(region_size)))[0 .. byte_size]; | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn make(allocator: io.Allocator, min_region_size: usize) Stacking { | ||||
| 		return Stacking{ | ||||
| 			.allocator = allocator, | ||||
| 			.min_region_size = min_region_size, | ||||
| 			.head_region = null, | ||||
| 			.tail_region = null, | ||||
| 		}; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| @ -16,6 +16,16 @@ pub fn Stack(comptime Value: type) type { | ||||
| 			self.values = self.values[0 .. 0]; | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn deinit(self: *Self) void { | ||||
| 			if (self.capacity == 0) { | ||||
| 				return; | ||||
| 			} | ||||
| 
 | ||||
| 			self.allocator.deallocate(self.values.ptr[0 .. self.capacity]); | ||||
| 
 | ||||
| 			self.values = &.{}; | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn drop(self: *Self, count: usize) bool { | ||||
| 			if (math.checked_sub(self.values.len, count)) |updated_count| { | ||||
| 				self.values = self.values[0 .. updated_count]; | ||||
| @ -26,16 +36,6 @@ pub fn Stack(comptime Value: type) type { | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn free(self: *Self) void { | ||||
| 			if (self.capacity == 0) { | ||||
| 				return; | ||||
| 			} | ||||
| 
 | ||||
| 			self.allocator.deallocate(self.values.ptr[0 .. self.capacity]); | ||||
| 
 | ||||
| 			self.values = &.{}; | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn grow(self: *Self, growth_amount: usize) io.AllocationError!void { | ||||
| 			const grown_capacity = self.capacity + growth_amount; | ||||
| 			const buffer = try self.allocator.reallocate(null, @sizeOf(Value) * grown_capacity); | ||||
| @ -51,11 +51,7 @@ pub fn Stack(comptime Value: type) type { | ||||
| 			self.capacity = grown_capacity; | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn is_empty(self: Self) bool { | ||||
| 			return self.values.len == 0; | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn make(allocator: io.Allocator) Self { | ||||
| 		pub fn init(allocator: io.Allocator) Self { | ||||
| 			return .{ | ||||
| 				.allocator = allocator, | ||||
| 				.capacity = 0, | ||||
| @ -63,7 +59,11 @@ pub fn Stack(comptime Value: type) type { | ||||
| 			}; | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn pack(self: *Self) io.AllocationError!void { | ||||
| 		pub fn is_empty(self: Self) bool { | ||||
| 			return self.values.len == 0; | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn pack(self: *Self) io.AllocationError![]Value { | ||||
| 			const packed_size = self.values.len; | ||||
| 			const buffer = try self.allocator.reallocate(null, @sizeOf(Value) * self.values.len); | ||||
| 
 | ||||
| @ -75,6 +75,8 @@ pub fn Stack(comptime Value: type) type { | ||||
| 
 | ||||
| 			self.values = @as([*]Value, @ptrCast(@alignCast(buffer)))[0 .. packed_size]; | ||||
| 			self.capacity = packed_size; | ||||
| 
 | ||||
| 			return self.values; | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn peek(self: Self) ?Value { | ||||
|  | ||||
| @ -174,7 +174,7 @@ pub fn Table(comptime Key: type, comptime Value: type, comptime Traits: type) ty | ||||
| 			self.count = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn free(self: *Self) void { | ||||
| 		pub fn deinit(self: *Self) void { | ||||
| 			if (self.entries.len == 0) { | ||||
| 				return; | ||||
| 			} | ||||
| @ -185,6 +185,15 @@ pub fn Table(comptime Key: type, comptime Value: type, comptime Traits: type) ty | ||||
| 			self.count = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn init(allocator: io.Allocator, traits: Traits) Self { | ||||
| 			return .{ | ||||
| 				.allocator = allocator, | ||||
| 				.count = 0, | ||||
| 				.entries = &.{}, | ||||
| 				.traits = traits, | ||||
| 			}; | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn insert(self: *Self, key: Key, value: Value) io.AllocationError!bool { | ||||
| 			try self.rehash(load_max); | ||||
| 
 | ||||
| @ -235,23 +244,14 @@ pub fn Table(comptime Key: type, comptime Value: type, comptime Traits: type) ty | ||||
| 			return null; | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn make(allocator: io.Allocator, traits: Traits) Self { | ||||
| 			return .{ | ||||
| 				.allocator = allocator, | ||||
| 				.count = 0, | ||||
| 				.entries = &.{}, | ||||
| 				.traits = traits, | ||||
| 			}; | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn rehash(self: *Self, max_load: f32) io.AllocationError!void { | ||||
| 			if (self.calculate_load_factor() <= max_load) { | ||||
| 				return; | ||||
| 			} | ||||
| 
 | ||||
| 			var table = make(self.allocator, self.traits); | ||||
| 			var table = init(self.allocator, self.traits); | ||||
| 
 | ||||
| 			errdefer table.free(); | ||||
| 			errdefer table.deinit(); | ||||
| 
 | ||||
| 			table.entries = allocate: { | ||||
| 				const min_count = @max(1, self.count); | ||||
| @ -266,7 +266,7 @@ pub fn Table(comptime Key: type, comptime Value: type, comptime Traits: type) ty | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			self.free(); | ||||
| 			self.deinit(); | ||||
| 
 | ||||
| 			self.* = table; | ||||
| 		} | ||||
|  | ||||
| @ -13,15 +13,15 @@ pub const Manifest = struct { | ||||
| 	tick_rate: f32 = 60.0, | ||||
| 
 | ||||
| 	pub fn load(self: *Manifest, env: *kym.RuntimeEnv) kym.RuntimeError!void { | ||||
| 		const manifest = try env.import(file.Path.from(&.{"app.ona"})) orelse return; | ||||
| 		const manifest = (try env.import(file.Path.from(&.{"app.ona"}))).pop() orelse return; | ||||
| 
 | ||||
| 		defer env.discard(manifest); | ||||
| 		defer env.release(manifest); | ||||
| 
 | ||||
| 		const width = @as(u16, get: { | ||||
| 			if (try kym.get_field(env, manifest, "width")) |ref| { | ||||
| 				defer env.discard(ref); | ||||
| 				defer env.release(ref); | ||||
| 
 | ||||
| 				const fixed = try env.unwrap_fixed(ref); | ||||
| 				const fixed = try env.expect_fixed(ref); | ||||
| 
 | ||||
| 				if (fixed > 0 and fixed < coral.math.max_int(@typeInfo(@TypeOf(self.width)).Int)) { | ||||
| 					break: get @intCast(fixed); | ||||
| @ -33,9 +33,9 @@ pub const Manifest = struct { | ||||
| 
 | ||||
| 		const height = @as(u16, get: { | ||||
| 			if (try kym.get_field(env, manifest, "height")) |ref| { | ||||
| 				defer env.discard(ref); | ||||
| 				defer env.release(ref); | ||||
| 
 | ||||
| 				const fixed = try env.unwrap_fixed(ref); | ||||
| 				const fixed = try env.expect_fixed(ref); | ||||
| 
 | ||||
| 				if (fixed > 0 and fixed < coral.math.max_int(@typeInfo(@TypeOf(self.height)).Int)) { | ||||
| 					break: get @intCast(fixed); | ||||
| @ -47,18 +47,18 @@ pub const Manifest = struct { | ||||
| 
 | ||||
| 		const tick_rate = @as(f32, get: { | ||||
| 			if (try kym.get_field(env, manifest, "tick_rate")) |ref| { | ||||
| 				defer env.discard(ref); | ||||
| 				defer env.release(ref); | ||||
| 
 | ||||
| 				break: get @floatCast(try env.unwrap_float(ref)); | ||||
| 				break: get @floatCast(try env.expect_float(ref)); | ||||
| 			} | ||||
| 
 | ||||
| 			break: get self.tick_rate; | ||||
| 		}); | ||||
| 
 | ||||
| 		if (try kym.get_field(env, manifest, "title")) |ref| { | ||||
| 			defer env.discard(ref); | ||||
| 			defer env.release(ref); | ||||
| 
 | ||||
| 			const title_string = try env.unwrap_string(ref); | ||||
| 			const title_string = try env.expect_string(ref); | ||||
| 			const limited_title_len = @min(title_string.len, self.title.len); | ||||
| 
 | ||||
| 			coral.io.copy(&self.title, title_string[0 .. limited_title_len]); | ||||
|  | ||||
							
								
								
									
										1727
									
								
								source/ona/kym.zig
									
									
									
									
									
								
							
							
						
						
									
										1727
									
								
								source/ona/kym.zig
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -10,13 +10,13 @@ const tokens = @import("./tokens.zig"); | ||||
| 
 | ||||
| const tree = @import("./tree.zig"); | ||||
| 
 | ||||
| name: *kym.RuntimeRef, | ||||
| name: *kym.RuntimeObj, | ||||
| arity: u8, | ||||
| opcodes: OpcodeList, | ||||
| lines: LineList, | ||||
| cursor: usize, | ||||
| constants: ConstList, | ||||
| bindings: []?*kym.RuntimeRef, | ||||
| bindings: []?*kym.RuntimeObj, | ||||
| 
 | ||||
| const Builtin = enum { | ||||
| 	import, | ||||
| @ -118,9 +118,9 @@ const Compiler = struct { | ||||
| 			}, | ||||
| 
 | ||||
| 			.lambda_construct => |lambda_construct| { | ||||
| 				var chunk = try Self.make(self.env, name orelse "<lambda>", lambda_construct.environment); | ||||
| 				var chunk = try Self.init(self.env, name orelse "<lambda>", lambda_construct.environment); | ||||
| 
 | ||||
| 				errdefer chunk.free(self.env); | ||||
| 				errdefer chunk.deinit(self.env); | ||||
| 
 | ||||
| 				if (lambda_construct.environment.capture_count == 0) { | ||||
| 					try self.chunk.write(expression.line, .{.push_const = try self.declare_chunk(chunk)}); | ||||
| @ -193,7 +193,7 @@ const Compiler = struct { | ||||
| 					return; | ||||
| 				} | ||||
| 
 | ||||
| 				if (try self.get_binding_index(environment, declaration_get.declaration)) |index| { | ||||
| 				if (try get_binding_index(environment, declaration_get.declaration)) |index| { | ||||
| 					try self.chunk.write(expression.line, .{.push_binding = index}); | ||||
| 
 | ||||
| 					if (is_declaration_boxed(declaration_get.declaration)) { | ||||
| @ -208,19 +208,19 @@ const Compiler = struct { | ||||
| 
 | ||||
| 			.declaration_set => |declaration_set| { | ||||
| 				if (get_local_index(environment, declaration_set.declaration)) |index| { | ||||
| 					try self.compile_expression(environment, declaration_set.assign, null); | ||||
| 
 | ||||
| 					if (is_declaration_boxed(declaration_set.declaration)) { | ||||
| 						try self.chunk.write(expression.line, .{.push_local = index}); | ||||
| 						try self.compile_expression(environment, declaration_set.assign, null); | ||||
| 						try self.chunk.write(expression.line, .set_box); | ||||
| 					} else { | ||||
| 						try self.compile_expression(environment, declaration_set.assign, null); | ||||
| 						try self.chunk.write(expression.line, .{.set_local = index}); | ||||
| 					} | ||||
| 
 | ||||
| 					return; | ||||
| 				} | ||||
| 
 | ||||
| 				if (try self.get_binding_index(environment, declaration_set.declaration)) |index| { | ||||
| 				if (try get_binding_index(environment, declaration_set.declaration)) |index| { | ||||
| 					try self.compile_expression(environment, declaration_set.assign, null); | ||||
| 					try self.chunk.write(expression.line, .{.push_binding = index}); | ||||
| 
 | ||||
| @ -345,9 +345,9 @@ const Compiler = struct { | ||||
| 			}); | ||||
| 		} | ||||
| 
 | ||||
| 		const constant = try self.env.new_dynamic(coral.io.bytes_of(&chunk), typeinfo); | ||||
| 		const constant = (try self.env.new_dynamic(coral.io.bytes_of(&chunk), typeinfo)).pop().?; | ||||
| 
 | ||||
| 		errdefer self.env.discard(constant); | ||||
| 		errdefer self.env.release(constant); | ||||
| 
 | ||||
| 		try self.chunk.constants.push_one(constant); | ||||
| 
 | ||||
| @ -361,9 +361,9 @@ const Compiler = struct { | ||||
| 			}); | ||||
| 		} | ||||
| 
 | ||||
| 		const constant = try self.env.new_fixed(fixed); | ||||
| 		const constant = (try self.env.new_fixed(fixed)).pop().?; | ||||
| 
 | ||||
| 		errdefer self.env.discard(constant); | ||||
| 		errdefer self.env.release(constant); | ||||
| 
 | ||||
| 		try self.chunk.constants.push_one(constant); | ||||
| 
 | ||||
| @ -377,9 +377,9 @@ const Compiler = struct { | ||||
| 			}); | ||||
| 		} | ||||
| 
 | ||||
| 		const constant = try self.env.new_float(float); | ||||
| 		const constant = (try self.env.new_float(float)).pop().?; | ||||
| 
 | ||||
| 		errdefer self.env.discard(constant); | ||||
| 		errdefer self.env.release(constant); | ||||
| 
 | ||||
| 		try self.chunk.constants.push_one(constant); | ||||
| 
 | ||||
| @ -393,9 +393,9 @@ const Compiler = struct { | ||||
| 			}); | ||||
| 		} | ||||
| 
 | ||||
| 		const constant = try self.env.new_string(string); | ||||
| 		const constant = (try self.env.new_string(string)).pop().?; | ||||
| 
 | ||||
| 		errdefer self.env.discard(constant); | ||||
| 		errdefer self.env.release(constant); | ||||
| 
 | ||||
| 		try self.chunk.constants.push_one(constant); | ||||
| 
 | ||||
| @ -409,16 +409,16 @@ const Compiler = struct { | ||||
| 			}); | ||||
| 		} | ||||
| 
 | ||||
| 		const constant = try self.env.new_symbol(symbol); | ||||
| 		const constant = (try self.env.new_symbol(symbol)).pop().?; | ||||
| 
 | ||||
| 		errdefer self.env.discard(constant); | ||||
| 		errdefer self.env.release(constant); | ||||
| 
 | ||||
| 		try self.chunk.constants.push_one(constant); | ||||
| 
 | ||||
| 		return @intCast(self.chunk.constants.values.len - 1); | ||||
| 	} | ||||
| 
 | ||||
| 	fn get_binding_index(self: *const Compiler, environment: *const tree.Environment, declaration: *const tree.Declaration) kym.RuntimeError!?u8 { | ||||
| 	fn get_binding_index(environment: *const tree.Environment, declaration: *const tree.Declaration) kym.RuntimeError!?u8 { | ||||
| 		var binding_index = @as(u8, 0); | ||||
| 
 | ||||
| 		while (binding_index < environment.capture_count) : (binding_index += 1) { | ||||
| @ -430,7 +430,7 @@ const Compiler = struct { | ||||
| 				target_environment = target_environment.enclosing orelse return null; | ||||
| 			} | ||||
| 
 | ||||
| 			try kym.assert(self.env, capture.* == .declaration_index); | ||||
| 			coral.debug.assert(capture.* == .declaration_index); | ||||
| 
 | ||||
| 			if (&target_environment.declarations[capture.declaration_index] == declaration) { | ||||
| 				return binding_index; | ||||
| @ -459,7 +459,7 @@ const Compiler = struct { | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| const ConstList = coral.list.Stack(*kym.RuntimeRef); | ||||
| const ConstList = coral.list.Stack(*kym.RuntimeObj); | ||||
| 
 | ||||
| const LineList = coral.list.Stack(tokens.Line); | ||||
| 
 | ||||
| @ -507,11 +507,33 @@ const OpcodeList = coral.list.Stack(Opcode); | ||||
| 
 | ||||
| const Self = @This(); | ||||
| 
 | ||||
| pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef { | ||||
| 	var opcode_cursor = @as(u32, 0); | ||||
| 	var buffer = coral.list.ByteStack.make(env.allocator); | ||||
| pub fn deinit(self: *Self, env: *kym.RuntimeEnv) void { | ||||
| 	while (self.constants.pop()) |constant| { | ||||
| 		env.release(constant); | ||||
| 	} | ||||
| 
 | ||||
| 	defer buffer.free(); | ||||
| 	self.constants.deinit(); | ||||
| 	self.opcodes.deinit(); | ||||
| 	env.release(self.name); | ||||
| 
 | ||||
| 	if (self.bindings.len != 0) { | ||||
| 		for (self.bindings) |binding| { | ||||
| 			if (binding) |value| { | ||||
| 				env.release(value); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		env.allocator.deallocate(self.bindings); | ||||
| 	} | ||||
| 
 | ||||
| 	self.bindings = &.{}; | ||||
| } | ||||
| 
 | ||||
| pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeObj { | ||||
| 	var opcode_cursor = @as(u32, 0); | ||||
| 	var buffer = coral.list.ByteStack.init(env.allocator); | ||||
| 
 | ||||
| 	defer buffer.deinit(); | ||||
| 
 | ||||
| 	const writer = coral.list.stack_as_writer(&buffer); | ||||
| 
 | ||||
| @ -528,15 +550,11 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef | ||||
| 			.push_false => coral.utf8.print_string(writer, "push false\n"), | ||||
| 
 | ||||
| 			.push_const => |push_const| print: { | ||||
| 				try kym.assert(env, push_const < chunk.constants.values.len); | ||||
| 				const string_ref = (try (try env.push(try chunk.get_constant(env, push_const))).to_string()).pop().?; | ||||
| 
 | ||||
| 				const string_ref = try env.to_string(chunk.constants.values[push_const]); | ||||
| 				defer env.release(string_ref); | ||||
| 
 | ||||
| 				defer env.discard(string_ref); | ||||
| 
 | ||||
| 				const string = string_ref.as_string(); | ||||
| 
 | ||||
| 				coral.debug.assert(string != null); | ||||
| 				const string = string_ref.is_string(); | ||||
| 
 | ||||
| 				break: print coral.utf8.print_formatted(writer, "push const ({value})\n", .{.value = string.?}); | ||||
| 			}, | ||||
| @ -599,407 +617,247 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
| 	return env.new_string(buffer.values); | ||||
| 	return (try env.new_string(buffer.values)).pop().?; | ||||
| } | ||||
| 
 | ||||
| pub fn execute(self: *Self, env: *kym.RuntimeEnv, frame: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { | ||||
| pub fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeObj { | ||||
| 	self.cursor = 0; | ||||
| 
 | ||||
| 	while (self.cursor < self.opcodes.values.len) : (self.cursor += 1) { | ||||
| 		switch (self.opcodes.values[self.cursor]) { | ||||
| 			.ret => break, | ||||
| 
 | ||||
| 			.pop => { | ||||
| 				if (try env.pop_local()) |ref| { | ||||
| 					env.discard(ref); | ||||
| 				} | ||||
| 			}, | ||||
| 
 | ||||
| 			.push_nil => try env.locals.push_one(null), | ||||
| 			.push_true => try env.locals.push_one(try env.new_boolean(true)), | ||||
| 			.push_false => try env.locals.push_one(try env.new_boolean(false)), | ||||
| 
 | ||||
| 			.push_const => |push_const| { | ||||
| 				try kym.assert(env, push_const < self.constants.values.len); | ||||
| 				try env.locals.push_one(self.constants.values[push_const].acquire()); | ||||
| 			}, | ||||
| 
 | ||||
| 			.push_local => |push_local| { | ||||
| 				try kym.assert(env, push_local < (env.locals.values.len - frame.locals_top)); | ||||
| 
 | ||||
| 				if (env.locals.values[frame.locals_top + push_local]) |local| { | ||||
| 					try env.locals.push_one(local.acquire()); | ||||
| 				} else { | ||||
| 					try env.locals.push_one(null); | ||||
| 				} | ||||
| 			}, | ||||
| 
 | ||||
| 			.push_top => { | ||||
| 				const frame_locals = env.locals.values[frame.locals_top ..]; | ||||
| 
 | ||||
| 				try kym.assert(env, frame_locals.len != 0); | ||||
| 
 | ||||
| 				if (frame_locals[frame_locals.len - 1]) |local| { | ||||
| 					try env.locals.push_one(local.acquire()); | ||||
| 				} else { | ||||
| 					try env.locals.push_one(null); | ||||
| 				} | ||||
| 			}, | ||||
| 			.pop => env.discard(), | ||||
| 			.push_nil => _ = try env.push(null), | ||||
| 			.push_true => _ = try env.new_boolean(true), | ||||
| 			.push_false => _ = try env.new_boolean(false), | ||||
| 			.push_const => |push_const| _ = try env.push(try self.get_constant(env, push_const)), | ||||
| 			.push_local => |push_local| _ = try env.local_get(push_local), | ||||
| 			.push_top => _ = try env.local_get(env.local_count() - 1), | ||||
| 
 | ||||
| 			.push_table => |push_table| { | ||||
| 				const table = try env.new_table(); | ||||
| 				const table = (try env.new_table()).pop().?; | ||||
| 
 | ||||
| 				errdefer env.discard(table); | ||||
| 				errdefer env.release(table); | ||||
| 
 | ||||
| 				{ | ||||
| 				if (push_table != 0) { | ||||
| 					var popped = @as(usize, 0); | ||||
| 
 | ||||
| 					while (popped < push_table) : (popped += 1) { | ||||
| 						const index = try env.expect(try env.pop_local()); | ||||
| 						const index = try env.expect_object(env.pop()); | ||||
| 
 | ||||
| 						defer env.discard(index); | ||||
| 						defer env.release(index); | ||||
| 
 | ||||
| 						if (try env.pop_local()) |value| { | ||||
| 							defer env.discard(value); | ||||
| 						if (env.pop()) |object| { | ||||
| 							defer env.release(object); | ||||
| 
 | ||||
| 							try env.set(table, index, value); | ||||
| 							try (try env.push(table)).index_set(index, object); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				try env.locals.push_one(table); | ||||
| 				_ = try env.push(table); | ||||
| 			}, | ||||
| 
 | ||||
| 			.push_boxed => { | ||||
| 				const value = try env.pop_local(); | ||||
| 				const value = env.pop(); | ||||
| 
 | ||||
| 				defer { | ||||
| 					if (value) |ref| { | ||||
| 						env.discard(ref); | ||||
| 					if (value) |object| { | ||||
| 						env.release(object); | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				const boxable = try env.new_boxed(value); | ||||
| 
 | ||||
| 				errdefer env.discard(boxable); | ||||
| 
 | ||||
| 				try env.locals.push_one(boxable); | ||||
| 				_ = try env.new_boxed(value); | ||||
| 			}, | ||||
| 
 | ||||
| 			.push_binding => |push_binding| { | ||||
| 				try kym.assert(env, push_binding <= self.bindings.len); | ||||
| 				try env.locals.push_one(if (self.bindings[push_binding]) |value| value.acquire() else null); | ||||
| 			}, | ||||
| 
 | ||||
| 			.push_concat => |push_concat| { | ||||
| 				const frame_locals = env.locals.values[frame.locals_top ..]; | ||||
| 
 | ||||
| 				try kym.assert(env, push_concat <= frame_locals.len); | ||||
| 
 | ||||
| 				const concat_locals = frame_locals[(frame_locals.len - push_concat) .. frame_locals.len]; | ||||
| 
 | ||||
| 				for (concat_locals) |value| { | ||||
| 					_ = try env.expect(value); | ||||
| 				} | ||||
| 
 | ||||
| 				const concatenated_value = try env.concat(@ptrCast(concat_locals)); | ||||
| 
 | ||||
| 				errdefer env.discard(concatenated_value); | ||||
| 
 | ||||
| 				var to_pop = concat_locals.len; | ||||
| 
 | ||||
| 				while (to_pop != 0) : (to_pop -= 1) { | ||||
| 					const popped = env.pop_local() catch unreachable; | ||||
| 
 | ||||
| 					coral.debug.assert(popped != null); | ||||
| 					env.discard(popped.?); | ||||
| 				} | ||||
| 
 | ||||
| 				try env.locals.push_one(concatenated_value); | ||||
| 			}, | ||||
| 			.push_binding => |push_binding| _ = try env.push(try self.get_binding(env, push_binding)), | ||||
| 			.push_concat => |push_concat| _ = try env.concat(push_concat), | ||||
| 
 | ||||
| 			.bind => |bind| { | ||||
| 				const callable = try env.expect(try env.pop_local()); | ||||
| 				const callable = try env.expect_object(env.pop()); | ||||
| 
 | ||||
| 				errdefer env.discard(callable); | ||||
| 				defer env.release(callable); | ||||
| 
 | ||||
| 				const chunk = @as(*Self, @ptrCast(@alignCast(try env.unwrap_dynamic(callable, typeinfo)))); | ||||
| 				const chunk = @as(*Self, @ptrCast(@alignCast(try env.expect_dynamic(callable, typeinfo)))); | ||||
| 
 | ||||
| 				chunk.bindings = try coral.io.allocate_many(env.allocator, bind, @as(?*kym.RuntimeRef, null)); | ||||
| 				if (chunk.bindings.len != 0) { | ||||
| 					return env.raise(error.IllegalState, "cannot bind values to an already-bound chunk", .{}); | ||||
| 				} | ||||
| 
 | ||||
| 				chunk.bindings = try coral.io.allocate_many(env.allocator, bind, @as(?*kym.RuntimeObj, null)); | ||||
| 
 | ||||
| 				for (0 .. bind) |index| { | ||||
| 					const value = try env.pop_local(); | ||||
| 					const value = env.pop(); | ||||
| 
 | ||||
| 					errdefer { | ||||
| 						if (value) |ref| { | ||||
| 							env.discard(ref); | ||||
| 						if (value) |object| { | ||||
| 							env.release(object); | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 					const binding = &chunk.bindings[index]; | ||||
| 
 | ||||
| 					if (binding.*) |*existing_binding| { | ||||
| 						env.discard(existing_binding.*); | ||||
| 					} | ||||
| 
 | ||||
| 					binding.* = value; | ||||
| 					chunk.bindings[index] = value; | ||||
| 				} | ||||
| 
 | ||||
| 				try env.locals.push_one(callable); | ||||
| 				_ = try env.push(callable); | ||||
| 			}, | ||||
| 
 | ||||
| 			.push_builtin => |push_builtin| { | ||||
| 				const builtin_syscall = try env.new_syscall(switch (push_builtin) { | ||||
| 					.import => syscall_import, | ||||
| 					.print => syscall_print, | ||||
| 					.vec2 => syscall_vec2, | ||||
| 					.vec3 => syscall_vec3, | ||||
| 				}); | ||||
| 			.push_builtin => |push_builtin| _ = try env.new_syscall(switch (push_builtin) { | ||||
| 				.import => syscall_import, | ||||
| 				.print => syscall_print, | ||||
| 				.vec2 => syscall_vec2, | ||||
| 				.vec3 => syscall_vec3, | ||||
| 			}), | ||||
| 
 | ||||
| 				errdefer env.discard(builtin_syscall); | ||||
| 
 | ||||
| 				try env.locals.push_one(builtin_syscall); | ||||
| 			}, | ||||
| 
 | ||||
| 			.set_local => |local_set| { | ||||
| 				const local = &env.locals.values[frame.locals_top + local_set]; | ||||
| 
 | ||||
| 				if (local.*) |previous_local| { | ||||
| 					env.discard(previous_local); | ||||
| 				} | ||||
| 
 | ||||
| 				local.* = try env.pop_local(); | ||||
| 			}, | ||||
| 
 | ||||
| 			.get_box => { | ||||
| 				const box = try env.expect(try env.pop_local()); | ||||
| 
 | ||||
| 				defer env.discard(box); | ||||
| 
 | ||||
| 				if (try env.get_boxed(box)) |unboxed| { | ||||
| 					errdefer env.discard(unboxed); | ||||
| 
 | ||||
| 					try env.locals.push_one(unboxed); | ||||
| 				} else { | ||||
| 					try env.locals.push_one(null); | ||||
| 				} | ||||
| 			}, | ||||
| 			.set_local => |local_set| _ = env.local_set(local_set, env.pop()), | ||||
| 			.get_box => _ = try env.boxed_get(), | ||||
| 
 | ||||
| 			.set_box => { | ||||
| 				const box = try env.expect(try env.pop_local()); | ||||
| 				const value = env.pop(); | ||||
| 
 | ||||
| 				defer env.discard(box); | ||||
| 				defer { | ||||
| 					if (value) |object| { | ||||
| 						env.release(object); | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				const value = try env.expect(try env.pop_local()); | ||||
| 
 | ||||
| 				errdefer env.discard(value); | ||||
| 
 | ||||
| 				try env.set_boxed(box, value); | ||||
| 				try env.boxed_set(value); | ||||
| 			}, | ||||
| 
 | ||||
| 			.get_dynamic => { | ||||
| 				const index = try env.expect(try env.pop_local()); | ||||
| 				const index = try env.expect_object(env.pop()); | ||||
| 
 | ||||
| 				defer env.discard(index); | ||||
| 				defer env.release(index); | ||||
| 
 | ||||
| 				const indexable = try env.expect(try env.pop_local()); | ||||
| 
 | ||||
| 				defer env.discard(indexable); | ||||
| 
 | ||||
| 				const value = try env.get(indexable, index); | ||||
| 
 | ||||
| 				errdefer { | ||||
| 					if (value) |ref| { | ||||
| 						env.discard(ref); | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				try env.locals.push_one(value); | ||||
| 				_ = try env.index_get(index); | ||||
| 			}, | ||||
| 
 | ||||
| 			.set_dynamic => { | ||||
| 				const value = try env.pop_local(); | ||||
| 				const value = env.pop(); | ||||
| 
 | ||||
| 				defer { | ||||
| 					if (value) |ref| { | ||||
| 						env.discard(ref); | ||||
| 					if (value) |object| { | ||||
| 						env.release(object); | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				const index = try env.expect(try env.pop_local()); | ||||
| 				const index = try env.expect_object(env.pop()); | ||||
| 
 | ||||
| 				defer env.discard(index); | ||||
| 				defer env.release(index); | ||||
| 
 | ||||
| 				const indexable = try env.expect(try env.pop_local()); | ||||
| 
 | ||||
| 				defer env.discard(indexable); | ||||
| 
 | ||||
| 				try env.set(indexable, index, value); | ||||
| 				try env.index_set(index, value); | ||||
| 			}, | ||||
| 
 | ||||
| 			.call => |call| { | ||||
| 				const result = call: { | ||||
| 					const callable = try env.expect(try env.pop_local()); | ||||
| 
 | ||||
| 					defer env.discard(callable); | ||||
| 
 | ||||
| 					const call_frame = try env.push_frame(callable, call); | ||||
| 
 | ||||
| 					defer env.pop_frame(); | ||||
| 
 | ||||
| 					break: call try env.call_frame(&call_frame); | ||||
| 				}; | ||||
| 
 | ||||
| 				errdefer { | ||||
| 					if (result) |ref| { | ||||
| 						env.discard(ref); | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				try env.locals.push_one(result); | ||||
| 			}, | ||||
| 			.call => |call| _ = try env.call(call), | ||||
| 
 | ||||
| 			.not => { | ||||
| 				if (try env.pop_local()) |value| { | ||||
| 					defer env.discard(value); | ||||
| 				const object = try env.expect_object(env.pop()); | ||||
| 
 | ||||
| 					try env.locals.push_one(try env.new_boolean(!value.is_truthy())); | ||||
| 				} else { | ||||
| 					try env.locals.push_one(try env.new_boolean(true)); | ||||
| 				} | ||||
| 				defer env.release(object); | ||||
| 
 | ||||
| 				_ = try env.new_boolean(object.is_false()); | ||||
| 			}, | ||||
| 
 | ||||
| 			.neg => { | ||||
| 				const value = try env.expect(try env.pop_local()); | ||||
| 
 | ||||
| 				defer env.discard(value); | ||||
| 
 | ||||
| 				try env.locals.push_one(try env.neg(value)); | ||||
| 			}, | ||||
| 			.neg => _ = try env.neg(), | ||||
| 
 | ||||
| 			.add => { | ||||
| 				const rhs = try env.expect(try env.pop_local()); | ||||
| 				const addable = try env.expect_object(env.pop()); | ||||
| 
 | ||||
| 				defer env.discard(rhs); | ||||
| 				defer env.release(addable); | ||||
| 
 | ||||
| 				const lhs = try env.expect(try env.pop_local()); | ||||
| 
 | ||||
| 				defer env.discard(lhs); | ||||
| 
 | ||||
| 				try env.locals.push_one(try env.add(lhs, rhs)); | ||||
| 				_ = switch (try env.expect_numeric(addable)) { | ||||
| 					.fixed => |fixed| try env.add_fixed(fixed), | ||||
| 					.float => |float| try env.add_float(float), | ||||
| 					.vector2 => |vector2| try env.add_vector2(vector2), | ||||
| 					.vector3 => |vector3| try env.add_vector3(vector3), | ||||
| 				}; | ||||
| 			}, | ||||
| 
 | ||||
| 			.sub => { | ||||
| 				const rhs = try env.expect(try env.pop_local()); | ||||
| 				const subtractable = try env.expect_object(env.pop()); | ||||
| 
 | ||||
| 				defer env.discard(rhs); | ||||
| 				defer env.release(subtractable); | ||||
| 
 | ||||
| 				const lhs = try env.expect(try env.pop_local()); | ||||
| 
 | ||||
| 				defer env.discard(lhs); | ||||
| 
 | ||||
| 				try env.locals.push_one(try env.sub(lhs, rhs)); | ||||
| 				_ = switch (try env.expect_numeric(subtractable)) { | ||||
| 					.fixed => |fixed| try env.sub_fixed(fixed), | ||||
| 					.float => |float| try env.sub_float(float), | ||||
| 					.vector2 => |vector2| try env.sub_vector2(vector2), | ||||
| 					.vector3 => |vector3| try env.sub_vector3(vector3), | ||||
| 				}; | ||||
| 			}, | ||||
| 
 | ||||
| 			.mul => { | ||||
| 				const rhs = try env.expect(try env.pop_local()); | ||||
| 				const multiplicable = try env.expect_object(env.pop()); | ||||
| 
 | ||||
| 				defer env.discard(rhs); | ||||
| 				defer env.release(multiplicable); | ||||
| 
 | ||||
| 				const lhs = try env.expect(try env.pop_local()); | ||||
| 
 | ||||
| 				defer env.discard(lhs); | ||||
| 
 | ||||
| 				try env.locals.push_one(try env.mul(lhs, rhs)); | ||||
| 				_ = switch (try env.expect_numeric(multiplicable)) { | ||||
| 					.fixed => |fixed| try env.mul_fixed(fixed), | ||||
| 					.float => |float| try env.mul_float(float), | ||||
| 					.vector2 => |vector2| try env.mul_vector2(vector2), | ||||
| 					.vector3 => |vector3| try env.mul_vector3(vector3), | ||||
| 				}; | ||||
| 			}, | ||||
| 
 | ||||
| 			.div => { | ||||
| 				const rhs = try env.expect(try env.pop_local()); | ||||
| 				const divisible = try env.expect_object(env.pop()); | ||||
| 
 | ||||
| 				defer env.discard(rhs); | ||||
| 				defer env.release(divisible); | ||||
| 
 | ||||
| 				const lhs = try env.expect(try env.pop_local()); | ||||
| 
 | ||||
| 				defer env.discard(lhs); | ||||
| 
 | ||||
| 				try env.locals.push_one(try env.div(lhs, rhs)); | ||||
| 				_ = switch (try env.expect_numeric(divisible)) { | ||||
| 					.fixed => |fixed| try env.div_fixed(fixed), | ||||
| 					.float => |float| try env.div_float(float), | ||||
| 					.vector2 => |vector2| try env.div_vector2(vector2), | ||||
| 					.vector3 => |vector3| try env.div_vector3(vector3), | ||||
| 				}; | ||||
| 			}, | ||||
| 
 | ||||
| 			.eql => { | ||||
| 				if (try env.pop_local()) |rhs| { | ||||
| 					env.discard(rhs); | ||||
| 				if (env.pop()) |equatable| { | ||||
| 					defer env.release(equatable); | ||||
| 
 | ||||
| 					if (try env.pop_local()) |lhs| { | ||||
| 						env.discard(lhs); | ||||
| 
 | ||||
| 						try env.locals.push_one(try env.new_boolean(lhs.equals(rhs))); | ||||
| 					} else { | ||||
| 						try env.locals.push_one(try env.new_boolean(false)); | ||||
| 					} | ||||
| 					_ = try env.equals_object(equatable); | ||||
| 				} else { | ||||
| 					if (try env.pop_local()) |lhs| { | ||||
| 						env.discard(lhs); | ||||
| 
 | ||||
| 						try env.locals.push_one(try env.new_boolean(false)); | ||||
| 					} else { | ||||
| 						try env.locals.push_one(try env.new_boolean(true)); | ||||
| 					} | ||||
| 					_ = try env.equals_nil(); | ||||
| 				} | ||||
| 			}, | ||||
| 
 | ||||
| 			.cgt => { | ||||
| 				const rhs = try env.expect(try env.pop_local()); | ||||
| 				const comparable = try env.expect_object(env.pop()); | ||||
| 
 | ||||
| 				defer env.discard(rhs); | ||||
| 				defer env.release(comparable); | ||||
| 
 | ||||
| 				const lhs = try env.expect(try env.pop_local()); | ||||
| 
 | ||||
| 				defer env.discard(lhs); | ||||
| 
 | ||||
| 				try env.locals.push_one(try env.new_boolean(try env.compare(lhs, rhs) > 0)); | ||||
| 				_ = try env.compare_greater(comparable); | ||||
| 			}, | ||||
| 
 | ||||
| 			.clt => { | ||||
| 				const rhs = try env.expect(try env.pop_local()); | ||||
| 				const comparable = try env.expect_object(env.pop()); | ||||
| 
 | ||||
| 				defer env.discard(rhs); | ||||
| 				defer env.release(comparable); | ||||
| 
 | ||||
| 				const lhs = try env.expect(try env.pop_local()); | ||||
| 
 | ||||
| 				defer env.discard(lhs); | ||||
| 
 | ||||
| 				try env.locals.push_one(try env.new_boolean(try env.compare(lhs, rhs) < 0)); | ||||
| 				_ = try env.compare_less(comparable); | ||||
| 			}, | ||||
| 
 | ||||
| 			.cge => { | ||||
| 				const rhs = try env.expect(try env.pop_local()); | ||||
| 				const comparable = try env.expect_object(env.pop()); | ||||
| 
 | ||||
| 				defer env.discard(rhs); | ||||
| 				defer env.release(comparable); | ||||
| 
 | ||||
| 				const lhs = try env.expect(try env.pop_local()); | ||||
| 
 | ||||
| 				defer env.discard(lhs); | ||||
| 
 | ||||
| 				try env.locals.push_one(try env.new_boolean(try env.compare(lhs, rhs) >= 0)); | ||||
| 				_ = try env.compare_greater_equals(comparable); | ||||
| 			}, | ||||
| 
 | ||||
| 			.cle => { | ||||
| 				const rhs = try env.expect(try env.pop_local()); | ||||
| 				const comparable = try env.expect_object(env.pop()); | ||||
| 
 | ||||
| 				defer env.discard(rhs); | ||||
| 				defer env.release(comparable); | ||||
| 
 | ||||
| 				const lhs = try env.expect(try env.pop_local()); | ||||
| 
 | ||||
| 				defer env.discard(lhs); | ||||
| 
 | ||||
| 				try env.locals.push_one(try env.new_boolean(try env.compare(lhs, rhs) <= 0)); | ||||
| 				_ = try env.compare_less_equals(comparable); | ||||
| 			}, | ||||
| 
 | ||||
| 			.jf => |jf| { | ||||
| 				if (try env.pop_local()) |condition| { | ||||
| 					defer env.discard(condition); | ||||
| 				if (env.pop()) |condition| { | ||||
| 					defer env.release(condition); | ||||
| 
 | ||||
| 					if (!condition.is_truthy()) { | ||||
| 					if (condition.is_false()) { | ||||
| 						self.cursor = jf; | ||||
| 					} | ||||
| 				} else { | ||||
| @ -1008,10 +866,10 @@ pub fn execute(self: *Self, env: *kym.RuntimeEnv, frame: *const kym.Frame) kym.R | ||||
| 			}, | ||||
| 
 | ||||
| 			.jt => |jt| { | ||||
| 				if (try env.pop_local()) |condition| { | ||||
| 					defer env.discard(condition); | ||||
| 				if (env.pop()) |condition| { | ||||
| 					defer env.release(condition); | ||||
| 
 | ||||
| 					if (condition.is_truthy()) { | ||||
| 					if (condition.is_true()) { | ||||
| 						self.cursor = jt; | ||||
| 					} | ||||
| 				} | ||||
| @ -1019,37 +877,31 @@ pub fn execute(self: *Self, env: *kym.RuntimeEnv, frame: *const kym.Frame) kym.R | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return env.pop_local(); | ||||
| 	return env.pop(); | ||||
| } | ||||
| 
 | ||||
| pub fn free(self: *Self, env: *kym.RuntimeEnv) void { | ||||
| 	while (self.constants.pop()) |constant| { | ||||
| 		env.discard(constant); | ||||
| fn get_binding(self: *Self, env: *kym.RuntimeEnv, index: usize) kym.RuntimeError!?*kym.RuntimeObj { | ||||
| 	if (index >= self.bindings.len) { | ||||
| 		return env.raise(error.IllegalState, "invalid binding", .{}); | ||||
| 	} | ||||
| 
 | ||||
| 	self.constants.free(); | ||||
| 	self.opcodes.free(); | ||||
| 	env.discard(self.name); | ||||
| 
 | ||||
| 	if (self.bindings.len != 0) { | ||||
| 		for (self.bindings) |binding| { | ||||
| 			if (binding) |value| { | ||||
| 				env.discard(value); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		env.allocator.deallocate(self.bindings); | ||||
| 	} | ||||
| 
 | ||||
| 	self.bindings = &.{}; | ||||
| 	return self.bindings[index]; | ||||
| } | ||||
| 
 | ||||
| pub fn make(env: *kym.RuntimeEnv, name: []const coral.io.Byte, environment: *const tree.Environment) kym.RuntimeError!Self { | ||||
| fn get_constant(self: *const Self, env: *kym.RuntimeEnv, index: usize) kym.RuntimeError!*kym.RuntimeObj { | ||||
| 	if (index >= self.constants.values.len) { | ||||
| 		return env.raise(error.IllegalState, "invalid constant", .{}); | ||||
| 	} | ||||
| 
 | ||||
| 	return self.constants.values[index]; | ||||
| } | ||||
| 
 | ||||
| pub fn init(env: *kym.RuntimeEnv, name: []const coral.io.Byte, environment: *const tree.Environment) kym.RuntimeError!Self { | ||||
| 	var chunk = Self{ | ||||
| 		.name = try env.new_symbol(name), | ||||
| 		.opcodes = OpcodeList.make(env.allocator), | ||||
| 		.constants = ConstList.make(env.allocator), | ||||
| 		.lines = LineList.make(env.allocator), | ||||
| 		.name = (try env.new_symbol(name)).pop().?, | ||||
| 		.opcodes = OpcodeList.init(env.allocator), | ||||
| 		.constants = ConstList.init(env.allocator), | ||||
| 		.lines = LineList.init(env.allocator), | ||||
| 		.bindings = &.{}, | ||||
| 		.arity = environment.argument_count, | ||||
| 		.cursor = 0, | ||||
| @ -1062,41 +914,81 @@ pub fn make(env: *kym.RuntimeEnv, name: []const coral.io.Byte, environment: *con | ||||
| 
 | ||||
| 	try compiler.compile_environment(environment); | ||||
| 
 | ||||
| 	@import("../app.zig").log_info(""); | ||||
| 	@import("../app.zig").log_info((try (try env.push(try chunk.dump(env))).to_string()).pop().?.is_string().?); | ||||
| 
 | ||||
| 	return chunk; | ||||
| } | ||||
| 
 | ||||
| fn syscall_import(env: *kym.RuntimeEnv, frame: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { | ||||
| 	return env.import(file.Path.from(&.{try env.unwrap_string(try frame.expect_arg(env, 0))})); | ||||
| fn syscall_import(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeObj { | ||||
| 	const arg = (try env.arg_get(0)).pop() orelse { | ||||
| 		return env.raise(error.BadOperation, "`@import` requires one argument to be a valid import path", .{}); | ||||
| 	}; | ||||
| 
 | ||||
| 	defer env.release(arg); | ||||
| 
 | ||||
| 	return (try env.import(file.Path.from(&.{try env.expect_string(arg)}))).pop(); | ||||
| } | ||||
| 
 | ||||
| fn syscall_print(env: *kym.RuntimeEnv, frame: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { | ||||
| 	env.print(try env.unwrap_string(try frame.expect_arg(env, 0))); | ||||
| fn syscall_print(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeObj { | ||||
| 	const string = (try (try env.arg_get(0)).to_string()).pop().?; | ||||
| 
 | ||||
| 	defer env.release(string); | ||||
| 
 | ||||
| 	env.print(string.is_string().?); | ||||
| 
 | ||||
| 	return null; | ||||
| } | ||||
| 
 | ||||
| fn syscall_vec2(env: *kym.RuntimeEnv, frame: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { | ||||
| 	const x = @as(f32, @floatCast(try env.unwrap_float(try frame.expect_arg(env, 0)))); | ||||
| fn syscall_vec2(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeObj { | ||||
| 	const x = @as(f32, get_x: { | ||||
| 		const x = (try env.arg_get(0)).pop() orelse { | ||||
| 			return env.raise(error.BadOperation, "a first argument is required to create a vector", .{}); | ||||
| 		}; | ||||
| 
 | ||||
| 	if (frame.has_arg(env, 1)) |y| { | ||||
| 		return env.new_vector2(x, @floatCast(try env.unwrap_float(y))); | ||||
| 		defer env.release(x); | ||||
| 
 | ||||
| 		break: get_x @floatCast(try env.expect_float(x)); | ||||
| 	}); | ||||
| 
 | ||||
| 	if ((try env.arg_get(1)).pop()) |y| { | ||||
| 		defer env.release(y); | ||||
| 
 | ||||
| 		return (try env.new_vector2(x, @floatCast(try env.expect_float(y)))).pop(); | ||||
| 	} | ||||
| 
 | ||||
| 	return env.new_vector2(x, x); | ||||
| 	return (try env.new_vector2(x, x)).pop(); | ||||
| } | ||||
| 
 | ||||
| fn syscall_vec3(env: *kym.RuntimeEnv, frame: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { | ||||
| 	const x = @as(f32, @floatCast(try env.unwrap_float(try frame.expect_arg(env, 0)))); | ||||
| fn syscall_vec3(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeObj { | ||||
| 	const x = @as(f32, get_x: { | ||||
| 		const x = (try env.arg_get(0)).pop() orelse { | ||||
| 			return env.raise(error.BadOperation, "a first argument is required to create a vector", .{}); | ||||
| 		}; | ||||
| 
 | ||||
| 	if (frame.has_arg(env, 1)) |y| { | ||||
| 		return env.new_vector3( | ||||
| 			x, | ||||
| 			@floatCast(try env.unwrap_float(y)), | ||||
| 			@floatCast(try env.unwrap_float(try frame.expect_arg(env, 2))), | ||||
| 		); | ||||
| 		defer env.release(x); | ||||
| 
 | ||||
| 		break: get_x @floatCast(try env.expect_float(x)); | ||||
| 	}); | ||||
| 
 | ||||
| 	if ((try env.arg_get(1)).pop()) |y| { | ||||
| 		defer env.release(y); | ||||
| 
 | ||||
| 		const z = @as(f32, get_z: { | ||||
| 			const z = (try env.arg_get(0)).pop() orelse { | ||||
| 				return env.raise(error.BadOperation, | ||||
| 					"a third argument is required to create a vector if a first and second exist", .{}); | ||||
| 			}; | ||||
| 
 | ||||
| 			defer env.release(z); | ||||
| 
 | ||||
| 			break: get_z @floatCast(try env.expect_float(z)); | ||||
| 		}); | ||||
| 
 | ||||
| 		return (try env.new_vector3(x, @floatCast(try env.expect_float(y)), z)).pop(); | ||||
| 	} | ||||
| 
 | ||||
| 	return env.new_vector3(x, x, x); | ||||
| 	return (try env.new_vector3(x, x, x)).pop(); | ||||
| } | ||||
| 
 | ||||
| pub const typeinfo = &kym.Typeinfo{ | ||||
| @ -1107,16 +999,16 @@ pub const typeinfo = &kym.Typeinfo{ | ||||
| 	.to_string = typeinfo_to_string, | ||||
| }; | ||||
| 
 | ||||
| fn typeinfo_call(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, frame: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { | ||||
| 	return @as(*Self, @ptrCast(@alignCast(userdata))).execute(env, frame); | ||||
| fn typeinfo_call(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) kym.RuntimeError!?*kym.RuntimeObj { | ||||
| 	return @as(*Self, @ptrCast(@alignCast(userdata))).execute(env); | ||||
| } | ||||
| 
 | ||||
| fn typeinfo_destruct(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) void { | ||||
| 	@as(*Self, @ptrCast(@alignCast(userdata))).free(env); | ||||
| 	@as(*Self, @ptrCast(@alignCast(userdata))).deinit(env); | ||||
| } | ||||
| 
 | ||||
| fn typeinfo_to_string(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) coral.io.AllocationError!*kym.RuntimeRef { | ||||
| 	return env.to_string(@as(*Self, @ptrCast(@alignCast(userdata))).name); | ||||
| fn typeinfo_to_string(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) kym.RuntimeError!*kym.RuntimeObj { | ||||
| 	return (try (try env.push(@as(*Self, @ptrCast(@alignCast(userdata))).name)).to_string()).pop().?; | ||||
| } | ||||
| 
 | ||||
| pub fn write(self: *Self, line: tokens.Line, opcode: Opcode) coral.io.AllocationError!void { | ||||
|  | ||||
| @ -5,41 +5,41 @@ const kym = @import("../kym.zig"); | ||||
| associative: RefTable, | ||||
| contiguous: RefList, | ||||
| 
 | ||||
| const RefList = coral.list.Stack(?*kym.RuntimeRef); | ||||
| const RefList = coral.list.Stack(?*kym.RuntimeObj); | ||||
| 
 | ||||
| const RefTable = coral.map.Table(*kym.RuntimeRef, *kym.RuntimeRef, struct { | ||||
| 	pub const hash = kym.RuntimeRef.hash; | ||||
| const RefTable = coral.map.Table(*kym.RuntimeObj, *kym.RuntimeObj, struct { | ||||
| 	pub const hash = kym.RuntimeObj.get_hash; | ||||
| 
 | ||||
| 	pub const equals = kym.RuntimeRef.equals; | ||||
| 	pub const equals = kym.RuntimeObj.equals; | ||||
| }); | ||||
| 
 | ||||
| const Self = @This(); | ||||
| 
 | ||||
| pub fn free(self: *Self, env: *kym.RuntimeEnv) void { | ||||
| pub fn deinit(self: *Self, env: *kym.RuntimeEnv) void { | ||||
| 	{ | ||||
| 		var field_iterable = self.associative.as_iterable(); | ||||
| 
 | ||||
| 		while (field_iterable.next()) |entry| { | ||||
| 			env.discard(entry.key); | ||||
| 			env.discard(entry.value); | ||||
| 			env.release(entry.key); | ||||
| 			env.release(entry.value); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	self.associative.free(); | ||||
| 	self.associative.deinit(); | ||||
| 
 | ||||
| 	while (self.contiguous.pop()) |value| { | ||||
| 		if (value) |ref| { | ||||
| 			env.discard(ref); | ||||
| 			env.release(ref); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	self.contiguous.free(); | ||||
| 	self.contiguous.deinit(); | ||||
| } | ||||
| 
 | ||||
| pub fn make(env: *kym.RuntimeEnv) Self { | ||||
| pub fn init(env: *kym.RuntimeEnv) Self { | ||||
| 	return .{ | ||||
| 		.associative = RefTable.make(env.allocator, .{}), | ||||
| 		.contiguous = RefList.make(env.allocator), | ||||
| 		.associative = RefTable.init(env.allocator, .{}), | ||||
| 		.contiguous = RefList.init(env.allocator), | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| @ -52,16 +52,16 @@ pub const typeinfo = &kym.Typeinfo{ | ||||
| }; | ||||
| 
 | ||||
| fn typeinfo_destruct(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) void { | ||||
| 	@as(*Self, @ptrCast(@alignCast(userdata))).free(env); | ||||
| 	@as(*Self, @ptrCast(@alignCast(userdata))).deinit(env); | ||||
| } | ||||
| 
 | ||||
| fn typeinfo_get(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const kym.RuntimeRef) kym.RuntimeError!?*kym.RuntimeRef { | ||||
| fn typeinfo_get(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const kym.RuntimeObj) kym.RuntimeError!?*kym.RuntimeObj { | ||||
| 	const table = @as(*Self, @ptrCast(@alignCast(userdata))); | ||||
| 	const acquired_index = index.acquire(); | ||||
| 
 | ||||
| 	defer env.discard(acquired_index); | ||||
| 	defer env.release(acquired_index); | ||||
| 
 | ||||
| 	if (acquired_index.as_fixed()) |fixed| { | ||||
| 	if (acquired_index.is_fixed()) |fixed| { | ||||
| 		if (fixed < 0) { | ||||
| 			// TODO: Negative indexing. | ||||
| 			unreachable; | ||||
| @ -79,13 +79,13 @@ fn typeinfo_get(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const k | ||||
| 	return null; | ||||
| } | ||||
| 
 | ||||
| fn typeinfo_set(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const kym.RuntimeRef, value: ?*const kym.RuntimeRef) kym.RuntimeError!void { | ||||
| fn typeinfo_set(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const kym.RuntimeObj, value: ?*const kym.RuntimeObj) kym.RuntimeError!void { | ||||
| 	const table = @as(*Self, @ptrCast(@alignCast(userdata))); | ||||
| 	const acquired_index = index.acquire(); | ||||
| 
 | ||||
| 	errdefer env.discard(acquired_index); | ||||
| 	errdefer env.release(acquired_index); | ||||
| 
 | ||||
| 	if (acquired_index.as_fixed()) |fixed| { | ||||
| 	if (acquired_index.is_fixed()) |fixed| { | ||||
| 		if (fixed < 0) { | ||||
| 			// TODO: Negative indexing. | ||||
| 			unreachable; | ||||
| @ -95,7 +95,7 @@ fn typeinfo_set(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const k | ||||
| 			const maybe_replacing = &table.contiguous.values[@intCast(fixed)]; | ||||
| 
 | ||||
| 			if (maybe_replacing.*) |replacing| { | ||||
| 				env.discard(replacing); | ||||
| 				env.release(replacing); | ||||
| 			} | ||||
| 
 | ||||
| 			maybe_replacing.* = if (value) |ref| ref.acquire() else null; | ||||
| @ -106,17 +106,17 @@ fn typeinfo_set(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const k | ||||
| 
 | ||||
| 	const acquired_value = (value orelse { | ||||
| 		if (table.associative.remove(acquired_index)) |removed| { | ||||
| 			env.discard(removed.key); | ||||
| 			env.discard(removed.value); | ||||
| 			env.release(removed.key); | ||||
| 			env.release(removed.value); | ||||
| 		} | ||||
| 
 | ||||
| 		return; | ||||
| 	}).acquire(); | ||||
| 
 | ||||
| 	errdefer env.discard(acquired_value); | ||||
| 	errdefer env.release(acquired_value); | ||||
| 
 | ||||
| 	if (try table.associative.replace(acquired_index, acquired_value)) |replaced| { | ||||
| 		env.discard(replaced.key); | ||||
| 		env.discard(replaced.value); | ||||
| 		env.release(replaced.key); | ||||
| 		env.release(replaced.value); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -192,17 +192,17 @@ pub const Root = struct { | ||||
| 		return coral.io.allocate_one(self.arena.as_allocator(), stmt); | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn free(self: *Root) void { | ||||
| 		self.error_messages.free(); | ||||
| 		self.arena.free(); | ||||
| 	pub fn deinit(self: *Root) void { | ||||
| 		self.error_messages.deinit(); | ||||
| 		self.arena.deinit(); | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn make(allocator: coral.io.Allocator) coral.io.AllocationError!Root { | ||||
| 	pub fn init(allocator: coral.io.Allocator) coral.io.AllocationError!Root { | ||||
| 		const arena_page_size = 4096; | ||||
| 
 | ||||
| 		return .{ | ||||
| 			.arena = coral.arena.Stacking.make(allocator, arena_page_size), | ||||
| 			.error_messages = MessageList.make(allocator), | ||||
| 			.arena = coral.arena.Stacking.init(allocator, arena_page_size), | ||||
| 			.error_messages = MessageList.init(allocator), | ||||
| 			.environment = .{}, | ||||
| 		}; | ||||
| 	} | ||||
|  | ||||
| @ -23,7 +23,7 @@ pub fn run_app(file_access: file.Access) void { | ||||
| 
 | ||||
| 	defer ext.SDL_Quit(); | ||||
| 
 | ||||
| 	var script_env = kym.RuntimeEnv.make(heap.allocator, .{ | ||||
| 	var script_env = kym.RuntimeEnv.init(heap.allocator, 255, .{ | ||||
| 		.print = app.log_info, | ||||
| 		.print_error = app.log_fail, | ||||
| 		.import_access = file_access, | ||||
| @ -31,7 +31,7 @@ pub fn run_app(file_access: file.Access) void { | ||||
| 		return app.log_fail("failed to initialize script runtime"); | ||||
| 	}; | ||||
| 
 | ||||
| 	defer script_env.free(); | ||||
| 	defer script_env.deinit(); | ||||
| 
 | ||||
| 	var manifest = app.Manifest{}; | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user