Add Function Literal Syntax to Ona Script #39
| @ -13,7 +13,7 @@ pub const Manifest = struct { | ||||
| 	tick_rate: f32 = 60.0, | ||||
| 
 | ||||
| 	pub fn load(self: *Manifest, env: *kym.RuntimeEnv) kym.RuntimeError!void { | ||||
| 		const manifest = try env.expect(try env.import(file.Path.from(&.{"app.ona"}))); | ||||
| 		const manifest = try env.import(file.Path.from(&.{"app.ona"})) orelse return; | ||||
| 
 | ||||
| 		defer env.discard(manifest); | ||||
| 
 | ||||
|  | ||||
| @ -4,6 +4,24 @@ const coral = @import("coral"); | ||||
| 
 | ||||
| const file = @import("./file.zig"); | ||||
| 
 | ||||
| pub const Frame = struct { | ||||
| 	name: []const coral.io.Byte = "", | ||||
| 	arg_count: u8, | ||||
| 	locals_top: usize, | ||||
| 
 | ||||
| 	pub fn args(self: *const Frame, env: *RuntimeEnv) []const ?*const RuntimeRef { | ||||
| 		return env.locals.values[self.locals_top .. (self.locals_top + self.arg_count)]; | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn get_arg(self: *const Frame, env: *RuntimeEnv, arg_index: u8) RuntimeError!*const RuntimeRef { | ||||
| 		return self.has_arg(env, arg_index) orelse env.raise(error.BadOperation, "nil reference"); | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn has_arg(self: *const Frame, env: *RuntimeEnv, arg_index: u8) ?*const RuntimeRef { | ||||
| 		return if (arg_index >= self.arg_count) null else env.locals.values[self.locals_top + arg_index]; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| pub const Fixed = i32; | ||||
| 
 | ||||
| pub const Float = f64; | ||||
| @ -44,7 +62,6 @@ pub const RuntimeEnv = struct { | ||||
| 			push_false, | ||||
| 			push_const: u16, | ||||
| 			push_local: u8, | ||||
| 			push_arg: u8, | ||||
| 			push_table: u32, | ||||
| 			push_builtin: Builtin, | ||||
| 			local_set: u8, | ||||
| @ -73,7 +90,6 @@ pub const RuntimeEnv = struct { | ||||
| 		const OpcodeList = coral.list.Stack(Opcode); | ||||
| 
 | ||||
| 		const CompilationUnit = struct { | ||||
| 			args: []const []const coral.io.Byte, | ||||
| 			locals_buffer: [255]Local = [_]Local{.{}} ** 255, | ||||
| 			locals_count: u8 = 0, | ||||
| 
 | ||||
| @ -243,10 +259,6 @@ pub const RuntimeEnv = struct { | ||||
| 							return chunk.opcodes.push_one(.{.push_local = local.index}); | ||||
| 						} | ||||
| 
 | ||||
| 						if (self.resolve_arg(local_get.identifier)) |arg| { | ||||
| 							return chunk.opcodes.push_one(.{.push_arg = arg}); | ||||
| 						} | ||||
| 
 | ||||
| 						return chunk.env.raise(error.OutOfMemory, "undefined local"); | ||||
| 					}, | ||||
| 
 | ||||
| @ -317,16 +329,26 @@ pub const RuntimeEnv = struct { | ||||
| 
 | ||||
| 						try self.compile_expression(chunk, declare.assigned_expression); | ||||
| 
 | ||||
| 						if (self.locals_count == self.locals_buffer.len) { | ||||
| 							return chunk.env.raise(error.BadSyntax, "chunks may have a maximum of 255 locals"); | ||||
| 						switch (declare.storage) { | ||||
| 							.@"var" => { | ||||
| 								if (!self.declare_local(.{ | ||||
| 									.identifier = declare.identifier, | ||||
| 									.is_readonly = false, | ||||
| 								})) { | ||||
| 									return chunk.env.raise(error.BadSyntax, "too many locals"); | ||||
| 								} | ||||
| 							}, | ||||
| 
 | ||||
| 							.let => { | ||||
| 								// TODO: investigate constant folding. | ||||
| 								if (!self.declare_local(.{ | ||||
| 									.identifier = declare.identifier, | ||||
| 									.is_readonly = false, | ||||
| 								})) { | ||||
| 									return chunk.env.raise(error.BadSyntax, "too many locals"); | ||||
| 								} | ||||
| 							}, | ||||
| 						} | ||||
| 
 | ||||
| 						self.locals_buffer[self.locals_count] = .{ | ||||
| 							.identifier = declare.identifier, | ||||
| 							.is_readonly = declare.storage != .variant, | ||||
| 						}; | ||||
| 
 | ||||
| 						self.locals_count += 1; | ||||
| 					}, | ||||
| 
 | ||||
| 					.block => |block| { | ||||
| @ -378,16 +400,15 @@ pub const RuntimeEnv = struct { | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			fn resolve_arg(self: *CompilationUnit, arg_identifier: []const coral.io.Byte) ?u8 { | ||||
| 				var index = @as(u8, 0); | ||||
| 
 | ||||
| 				while (index < self.args.len) { | ||||
| 					if (coral.io.are_equal(self.args[index], arg_identifier)) { | ||||
| 						return index; | ||||
| 					} | ||||
| 			fn declare_local(self: *CompilationUnit, local: Local) bool { | ||||
| 				if (self.locals_count == self.locals_buffer.len) { | ||||
| 					return false; | ||||
| 				} | ||||
| 
 | ||||
| 				return null; | ||||
| 				self.locals_buffer[self.locals_count] = local; | ||||
| 				self.locals_count += 1; | ||||
| 
 | ||||
| 				return true; | ||||
| 			} | ||||
| 
 | ||||
| 			fn resolve_local(self: *CompilationUnit, local_identifier: []const coral.io.Byte) ?ResolvedLocal { | ||||
| @ -415,9 +436,18 @@ pub const RuntimeEnv = struct { | ||||
| 		}; | ||||
| 
 | ||||
| 		fn compile(self: *Chunk, statements: []const ast.Statement, args: []const []const coral.io.Byte) RuntimeError!void { | ||||
| 			var unit = CompilationUnit{.args = args}; | ||||
| 			var unit = CompilationUnit{}; | ||||
| 			var has_returned = false; | ||||
| 
 | ||||
| 			for (args) |arg| { | ||||
| 				if (!unit.declare_local(.{ | ||||
| 					.is_readonly = true, | ||||
| 					.identifier = arg, | ||||
| 				})) { | ||||
| 					return self.env.raise(error.BadSyntax, "too many arguments"); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			for (statements) |statement| { | ||||
| 				try unit.compile_statement(self, statement); | ||||
| 
 | ||||
| @ -456,7 +486,7 @@ pub const RuntimeEnv = struct { | ||||
| 			return @intCast(self.constants.values.len - 1); | ||||
| 		} | ||||
| 
 | ||||
| 		fn execute(self: *Chunk) RuntimeError!?*RuntimeRef { | ||||
| 		fn execute(self: *Chunk, frame: Frame) RuntimeError!?*RuntimeRef { | ||||
| 			var opcode_cursor = @as(u32, 0); | ||||
| 
 | ||||
| 			while (opcode_cursor < self.opcodes.values.len) : (opcode_cursor += 1) { | ||||
| @ -484,25 +514,13 @@ pub const RuntimeEnv = struct { | ||||
| 							return self.env.raise(error.IllegalState, "invalid local"); | ||||
| 						} | ||||
| 
 | ||||
| 						if (self.env.locals.values[push_local]) |local| { | ||||
| 						if (self.env.locals.values[frame.locals_top + push_local]) |local| { | ||||
| 							try self.env.locals.push_one(try self.env.acquire(local)); | ||||
| 						} else { | ||||
| 							try self.env.locals.push_one(null); | ||||
| 						} | ||||
| 					}, | ||||
| 
 | ||||
| 					.push_arg => |push_arg| { | ||||
| 						const arg = try self.env.acquire_arg(push_arg); | ||||
| 
 | ||||
| 						errdefer { | ||||
| 							if (arg) |ref| { | ||||
| 								self.env.discard(ref); | ||||
| 							} | ||||
| 						} | ||||
| 
 | ||||
| 						try self.env.locals.push_one(arg); | ||||
| 					}, | ||||
| 
 | ||||
| 					.push_table => |push_table| { | ||||
| 						const table = try self.env.new_table(); | ||||
| 
 | ||||
| @ -511,25 +529,19 @@ pub const RuntimeEnv = struct { | ||||
| 						{ | ||||
| 							const dynamic = table.object().payload.dynamic; | ||||
| 							const userdata = dynamic.userdata(); | ||||
| 							const table_set = dynamic.typeinfo().set; | ||||
| 							var popped = @as(usize, 0); | ||||
| 
 | ||||
| 							while (popped < push_table) : (popped += 1) { | ||||
| 								const index = try self.env.expect(try self.pop_local()); | ||||
| 								const index = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 								defer self.env.discard(index); | ||||
| 
 | ||||
| 								const maybe_value = try self.pop_local(); | ||||
| 								if (try self.pop_local()) |value| { | ||||
| 									defer self.env.discard(value); | ||||
| 
 | ||||
| 								defer { | ||||
| 									if (maybe_value) |value| { | ||||
| 										self.env.discard(value); | ||||
| 									} | ||||
| 									try table_set(self.env, userdata, index, value); | ||||
| 								} | ||||
| 
 | ||||
| 								try dynamic.typeinfo().set(.{ | ||||
| 									.userdata = userdata, | ||||
| 									.env = self.env, | ||||
| 								}, index, maybe_value); | ||||
| 							} | ||||
| 						} | ||||
| 
 | ||||
| @ -550,7 +562,7 @@ pub const RuntimeEnv = struct { | ||||
| 					}, | ||||
| 
 | ||||
| 					.local_set => |local_set| { | ||||
| 						const local = &self.env.locals.values[local_set]; | ||||
| 						const local = &self.env.locals.values[frame.locals_top + local_set]; | ||||
| 
 | ||||
| 						if (local.*) |previous_local| { | ||||
| 							self.env.discard(previous_local); | ||||
| @ -560,11 +572,11 @@ pub const RuntimeEnv = struct { | ||||
| 					}, | ||||
| 
 | ||||
| 					.object_get => { | ||||
| 						const index = try self.env.expect(try self.pop_local()); | ||||
| 						const index = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(index); | ||||
| 
 | ||||
| 						const indexable = try self.env.expect(try self.pop_local()); | ||||
| 						const indexable = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(indexable); | ||||
| 
 | ||||
| @ -605,28 +617,17 @@ pub const RuntimeEnv = struct { | ||||
| 
 | ||||
| 					.object_call => |object_call| { | ||||
| 						const result = call: { | ||||
| 							const callable = try self.env.expect(try self.pop_local()); | ||||
| 							const callable = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 							defer self.env.discard(callable); | ||||
| 
 | ||||
| 							try self.env.frames.push_one(.{ | ||||
| 								.name = "<chunk>", | ||||
| 								.arg_count = object_call, | ||||
| 								.locals_top = self.env.locals.values.len, | ||||
| 							}); | ||||
| 							const call_frame = try self.env.push_frame(object_call); | ||||
| 
 | ||||
| 							defer coral.debug.assert(self.env.frames.pop() != null); | ||||
| 
 | ||||
| 							const payload = callable.object().payload; | ||||
| 
 | ||||
| 							break: call try switch (payload) { | ||||
| 								.syscall => |syscall| syscall(self.env), | ||||
| 
 | ||||
| 								.dynamic => |dynamic| dynamic.typeinfo().call(.{ | ||||
| 									.userdata = dynamic.userdata(), | ||||
| 									.env = self.env, | ||||
| 								}), | ||||
| 							defer self.env.pop_frame(); | ||||
| 
 | ||||
| 							break: call try switch (callable.object().payload) { | ||||
| 								.syscall => |syscall| syscall(self.env, call_frame), | ||||
| 								.dynamic => |dynamic| dynamic.typeinfo().call(self.env, dynamic.userdata(), call_frame), | ||||
| 								else => self.env.raise(error.TypeMismatch, "object is not callable"), | ||||
| 							}; | ||||
| 						}; | ||||
| @ -637,12 +638,6 @@ pub const RuntimeEnv = struct { | ||||
| 							} | ||||
| 						} | ||||
| 
 | ||||
| 						for (0 .. object_call) |_| { | ||||
| 							if (try self.pop_local()) |popped_arg| { | ||||
| 								self.env.discard(popped_arg); | ||||
| 							} | ||||
| 						} | ||||
| 
 | ||||
| 						try self.env.locals.push_one(result); | ||||
| 					}, | ||||
| 
 | ||||
| @ -657,7 +652,7 @@ pub const RuntimeEnv = struct { | ||||
| 					}, | ||||
| 
 | ||||
| 					.neg => { | ||||
| 						const value = try self.env.expect(try self.pop_local()); | ||||
| 						const value = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(value); | ||||
| 
 | ||||
| @ -669,11 +664,11 @@ pub const RuntimeEnv = struct { | ||||
| 					}, | ||||
| 
 | ||||
| 					.add => { | ||||
| 						const rhs = try self.env.expect(try self.pop_local()); | ||||
| 						const rhs = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(rhs); | ||||
| 
 | ||||
| 						const lhs = try self.env.expect(try self.pop_local()); | ||||
| 						const lhs = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(lhs); | ||||
| 
 | ||||
| @ -709,11 +704,11 @@ pub const RuntimeEnv = struct { | ||||
| 					}, | ||||
| 
 | ||||
| 					.sub => { | ||||
| 						const rhs = try self.env.expect(try self.pop_local()); | ||||
| 						const rhs = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(rhs); | ||||
| 
 | ||||
| 						const lhs = try self.env.expect(try self.pop_local()); | ||||
| 						const lhs = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(lhs); | ||||
| 
 | ||||
| @ -749,11 +744,11 @@ pub const RuntimeEnv = struct { | ||||
| 					}, | ||||
| 
 | ||||
| 					.mul => { | ||||
| 						const rhs = try self.env.expect(try self.pop_local()); | ||||
| 						const rhs = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(rhs); | ||||
| 
 | ||||
| 						const lhs = try self.env.expect(try self.pop_local()); | ||||
| 						const lhs = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(lhs); | ||||
| 
 | ||||
| @ -789,11 +784,11 @@ pub const RuntimeEnv = struct { | ||||
| 					}, | ||||
| 
 | ||||
| 					.div => { | ||||
| 						const rhs = try self.env.expect(try self.pop_local()); | ||||
| 						const rhs = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(rhs); | ||||
| 
 | ||||
| 						const lhs = try self.env.expect(try self.pop_local()); | ||||
| 						const lhs = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(lhs); | ||||
| 
 | ||||
| @ -845,11 +840,11 @@ pub const RuntimeEnv = struct { | ||||
| 					}, | ||||
| 
 | ||||
| 					.cgt => { | ||||
| 						const rhs = try self.env.expect(try self.pop_local()); | ||||
| 						const rhs = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(rhs); | ||||
| 
 | ||||
| 						const lhs = try self.env.expect(try self.pop_local()); | ||||
| 						const lhs = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(lhs); | ||||
| 
 | ||||
| @ -871,11 +866,11 @@ pub const RuntimeEnv = struct { | ||||
| 					}, | ||||
| 
 | ||||
| 					.clt => { | ||||
| 						const rhs = try self.env.expect(try self.pop_local()); | ||||
| 						const rhs = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(rhs); | ||||
| 
 | ||||
| 						const lhs = try self.env.expect(try self.pop_local()); | ||||
| 						const lhs = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(lhs); | ||||
| 
 | ||||
| @ -897,11 +892,11 @@ pub const RuntimeEnv = struct { | ||||
| 					}, | ||||
| 
 | ||||
| 					.cge => { | ||||
| 						const rhs = try self.env.expect(try self.pop_local()); | ||||
| 						const rhs = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(rhs); | ||||
| 
 | ||||
| 						const lhs = try self.env.expect(try self.pop_local()); | ||||
| 						const lhs = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(lhs); | ||||
| 
 | ||||
| @ -923,11 +918,11 @@ pub const RuntimeEnv = struct { | ||||
| 					}, | ||||
| 
 | ||||
| 					.cle => { | ||||
| 						const rhs = try self.env.expect(try self.pop_local()); | ||||
| 						const rhs = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(rhs); | ||||
| 
 | ||||
| 						const lhs = try self.env.expect(try self.pop_local()); | ||||
| 						const lhs = try self.expect(try self.pop_local()); | ||||
| 
 | ||||
| 						defer self.env.discard(lhs); | ||||
| 
 | ||||
| @ -977,6 +972,10 @@ pub const RuntimeEnv = struct { | ||||
| 			return self.pop_local(); | ||||
| 		} | ||||
| 
 | ||||
| 		fn expect(self: *Chunk, value: ?*RuntimeRef) RuntimeError!*RuntimeRef { | ||||
| 			return value orelse self.env.raise(error.TypeMismatch, "nil reference"); | ||||
| 		} | ||||
| 
 | ||||
| 		fn free(self: *Chunk) void { | ||||
| 			while (self.constants.pop()) |constant| { | ||||
| 				self.env.discard(constant); | ||||
| @ -1001,28 +1000,26 @@ pub const RuntimeEnv = struct { | ||||
| 			return self.env.locals.pop() orelse self.env.raise(error.IllegalState, "stack underflow"); | ||||
| 		} | ||||
| 
 | ||||
| 		fn typeinfo_call(method: Typeinfo.Method) RuntimeError!?*RuntimeRef { | ||||
| 			const chunk = @as(*Chunk, @ptrCast(@alignCast(method.userdata))); | ||||
| 		fn typeinfo_call(env: *RuntimeEnv, userdata: []coral.io.Byte, frame: Frame) RuntimeError!?*RuntimeRef { | ||||
| 			const chunk = @as(*Chunk, @ptrCast(@alignCast(userdata))); | ||||
| 
 | ||||
| 			if ((method.env.view_args() catch unreachable).len < chunk.arity) { | ||||
| 				return method.env.raise(error.BadOperation, "expected more arguments"); | ||||
| 			if (frame.arg_count < chunk.arity) { | ||||
| 				return env.raise(error.BadOperation, "expected more arguments"); | ||||
| 			} | ||||
| 
 | ||||
| 			return chunk.execute(); | ||||
| 			return chunk.execute(frame); | ||||
| 		} | ||||
| 
 | ||||
| 		fn typeinfo_destruct(method: Typeinfo.Method) void { | ||||
| 			@as(*Chunk, @ptrCast(@alignCast(method.userdata))).free(); | ||||
| 		fn typeinfo_destruct(env: *RuntimeEnv, userdata: []coral.io.Byte) void { | ||||
| 			_ = env; | ||||
| 
 | ||||
| 			@as(*Chunk, @ptrCast(@alignCast(userdata))).free(); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	const ConstList = coral.list.Stack(*RuntimeRef); | ||||
| 
 | ||||
| 	const FrameStack = coral.list.Stack(struct { | ||||
| 		name: []const coral.io.Byte, | ||||
| 		arg_count: u8, | ||||
| 		locals_top: usize, | ||||
| 	}); | ||||
| 	const FrameStack = coral.list.Stack(Frame); | ||||
| 
 | ||||
| 	pub const Options = struct { | ||||
| 		import_access: file.Access = .null, | ||||
| @ -1046,15 +1043,15 @@ pub const RuntimeEnv = struct { | ||||
| 		associative: RefTable, | ||||
| 		contiguous: RefList, | ||||
| 
 | ||||
| 		fn typeinfo_destruct(method: Typeinfo.Method) void { | ||||
| 			const table = @as(*Table, @ptrCast(@alignCast(method.userdata))); | ||||
| 		fn typeinfo_destruct(env: *RuntimeEnv, userdata: []coral.io.Byte) void { | ||||
| 			const table = @as(*Table, @ptrCast(@alignCast(userdata))); | ||||
| 
 | ||||
| 			{ | ||||
| 				var field_iterable = table.associative.as_iterable(); | ||||
| 
 | ||||
| 				while (field_iterable.next()) |entry| { | ||||
| 					method.env.discard(entry.key); | ||||
| 					method.env.discard(entry.value); | ||||
| 					env.discard(entry.key); | ||||
| 					env.discard(entry.value); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| @ -1062,18 +1059,18 @@ pub const RuntimeEnv = struct { | ||||
| 
 | ||||
| 			while (table.contiguous.pop()) |value| { | ||||
| 				if (value) |ref| { | ||||
| 					method.env.discard(ref); | ||||
| 					env.discard(ref); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			table.contiguous.free(); | ||||
| 		} | ||||
| 
 | ||||
| 		fn typeinfo_get(method: Typeinfo.Method, index: *const RuntimeRef) RuntimeError!?*RuntimeRef { | ||||
| 			const table = @as(*Table, @ptrCast(@alignCast(method.userdata))); | ||||
| 			const acquired_index = try method.env.acquire(index); | ||||
| 		fn typeinfo_get(env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef) RuntimeError!?*RuntimeRef { | ||||
| 			const table = @as(*Table, @ptrCast(@alignCast(userdata))); | ||||
| 			const acquired_index = try env.acquire(index); | ||||
| 
 | ||||
| 			defer method.env.discard(acquired_index); | ||||
| 			defer env.discard(acquired_index); | ||||
| 
 | ||||
| 			if (acquired_index.is_fixed()) |fixed| { | ||||
| 				if (fixed < 0) { | ||||
| @ -1082,22 +1079,22 @@ pub const RuntimeEnv = struct { | ||||
| 				} | ||||
| 
 | ||||
| 				if (fixed < table.contiguous.values.len) { | ||||
| 					return method.env.acquire(table.contiguous.values[@intCast(fixed)] orelse return null); | ||||
| 					return env.acquire(table.contiguous.values[@intCast(fixed)] orelse return null); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			if (table.associative.lookup(acquired_index)) |value_ref| { | ||||
| 				return method.env.acquire(value_ref); | ||||
| 				return env.acquire(value_ref); | ||||
| 			} | ||||
| 
 | ||||
| 			return null; | ||||
| 		} | ||||
| 
 | ||||
| 		fn typeinfo_set(method: Typeinfo.Method, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void { | ||||
| 			const table = @as(*Table, @ptrCast(@alignCast(method.userdata))); | ||||
| 			const acquired_index = try method.env.acquire(index); | ||||
| 		fn typeinfo_set(env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void { | ||||
| 			const table = @as(*Table, @ptrCast(@alignCast(userdata))); | ||||
| 			const acquired_index = try env.acquire(index); | ||||
| 
 | ||||
| 			errdefer method.env.discard(acquired_index); | ||||
| 			errdefer env.discard(acquired_index); | ||||
| 
 | ||||
| 			if (acquired_index.is_fixed()) |fixed| { | ||||
| 				if (fixed < 0) { | ||||
| @ -1109,29 +1106,29 @@ pub const RuntimeEnv = struct { | ||||
| 					const maybe_replacing = &table.contiguous.values[@intCast(fixed)]; | ||||
| 
 | ||||
| 					if (maybe_replacing.*) |replacing| { | ||||
| 						method.env.discard(replacing); | ||||
| 						env.discard(replacing); | ||||
| 					} | ||||
| 
 | ||||
| 					maybe_replacing.* = if (value) |ref| try method.env.acquire(ref) else null; | ||||
| 					maybe_replacing.* = if (value) |ref| try env.acquire(ref) else null; | ||||
| 
 | ||||
| 					return; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			const acquired_value = try method.env.acquire(value orelse { | ||||
| 			const acquired_value = try env.acquire(value orelse { | ||||
| 				if (table.associative.remove(acquired_index)) |removed| { | ||||
| 					method.env.discard(removed.key); | ||||
| 					method.env.discard(removed.value); | ||||
| 					env.discard(removed.key); | ||||
| 					env.discard(removed.value); | ||||
| 				} | ||||
| 
 | ||||
| 				return; | ||||
| 			}); | ||||
| 
 | ||||
| 			errdefer method.env.discard(acquired_value); | ||||
| 			errdefer env.discard(acquired_value); | ||||
| 
 | ||||
| 			if (try table.associative.replace(acquired_index, acquired_value)) |replaced| { | ||||
| 				method.env.discard(replaced.key); | ||||
| 				method.env.discard(replaced.value); | ||||
| 				env.discard(replaced.key); | ||||
| 				env.discard(replaced.value); | ||||
| 			} | ||||
| 		} | ||||
| 	}; | ||||
| @ -1146,30 +1143,15 @@ pub const RuntimeEnv = struct { | ||||
| 		return @ptrCast(object); | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn acquire_arg(self: *RuntimeEnv, index: usize) RuntimeError!?*RuntimeRef { | ||||
| 		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 - frame.arg_count) + index]) |local| { | ||||
| 				return self.acquire(local); | ||||
| 			} | ||||
| 	pub fn call(self: *RuntimeEnv, callable: *RuntimeRef, args: []const *RuntimeRef) RuntimeError!?*RuntimeRef { | ||||
| 		// TODO: Handle errors. | ||||
| 		for (args) |arg| { | ||||
| 			try self.locals.push_one(try self.acquire(arg)); | ||||
| 		} | ||||
| 
 | ||||
| 		return null; | ||||
| 	} | ||||
| 		try self.push_frame(args.len); | ||||
| 
 | ||||
| 	pub fn call(self: *RuntimeEnv, callable: *RuntimeRef, args: []const *RuntimeRef) RuntimeError!?*RuntimeRef { | ||||
| 		try self.locals.push_all(args); | ||||
| 
 | ||||
| 		defer coral.io.assert(self.locals.drop(args.len)); | ||||
| 
 | ||||
| 		try self.frames.push_one(.{ | ||||
| 			.name = "<native>", | ||||
| 			.arg_count = args.len, | ||||
| 			.locals_top = self.locals.values.len, | ||||
| 		}); | ||||
| 
 | ||||
| 		defer coral.io.assert(self.frames.pop() != null); | ||||
| 		defer self.pop_frame(); | ||||
| 
 | ||||
| 		return switch (callable.object().payload) { | ||||
| 			.syscall => |syscall| syscall(self.env), | ||||
| @ -1201,10 +1183,7 @@ pub const RuntimeEnv = struct { | ||||
| 
 | ||||
| 				.dynamic => |dynamic| { | ||||
| 					if (dynamic.typeinfo().destruct) |destruct| { | ||||
| 						destruct(.{ | ||||
| 							.userdata = dynamic.userdata(), | ||||
| 							.env = self, | ||||
| 						}); | ||||
| 						destruct(self, dynamic.userdata()); | ||||
| 					} | ||||
| 
 | ||||
| 					self.allocator.deallocate(dynamic.unpack()); | ||||
| @ -1236,19 +1215,11 @@ pub const RuntimeEnv = struct { | ||||
| 			error.OutOfMemory => error.OutOfMemory, | ||||
| 		}, &.{}); | ||||
| 
 | ||||
| 		try self.frames.push_one(.{ | ||||
| 			.name = file_name, | ||||
| 			.arg_count = 0, | ||||
| 			.locals_top = self.locals.values.len, | ||||
| 		}); | ||||
| 		const frame = try self.push_frame(0); | ||||
| 
 | ||||
| 		defer coral.debug.assert(self.frames.pop() != null); | ||||
| 		defer self.pop_frame(); | ||||
| 
 | ||||
| 		return chunk.execute(); | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn expect(self: *RuntimeEnv, value: ?*RuntimeRef) RuntimeError!*RuntimeRef { | ||||
| 		return value orelse self.raise(error.TypeMismatch, "nil reference"); | ||||
| 		return chunk.execute(frame); | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn free(self: *RuntimeEnv) void { | ||||
| @ -1273,6 +1244,14 @@ pub const RuntimeEnv = struct { | ||||
| 
 | ||||
| 	pub fn get(self: *RuntimeEnv, indexable: *RuntimeRef, index: *const RuntimeRef) RuntimeError!?*RuntimeRef { | ||||
| 		return switch (indexable.object().payload) { | ||||
| 			.false => self.raise(error.TypeMismatch, "false is not get-indexable"), | ||||
| 			.true => self.raise(error.TypeMismatch, "true is not get-indexable"), | ||||
| 			.fixed => self.raise(error.TypeMismatch, "fixed is not get-indexable"), | ||||
| 			.float => self.raise(error.TypeMismatch, "float is not get-indexable"), | ||||
| 			.string => self.raise(error.TypeMismatch, "string is not get-indexable"), | ||||
| 			.symbol => self.raise(error.TypeMismatch, "symbol is not get-indexable"), | ||||
| 			.syscall => self.raise(error.TypeMismatch, "syscall is not get-indexable"), | ||||
| 
 | ||||
| 			.vector2 => |vector2| swizzle: { | ||||
| 				const swizzle_symbol = try self.unbox_symbol(index); | ||||
| 				var swizzle_buffer = [_]f32{0} ** 3; | ||||
| @ -1324,12 +1303,7 @@ pub const RuntimeEnv = struct { | ||||
| 				} | ||||
| 			}, | ||||
| 
 | ||||
| 			.dynamic => |dynamic| dynamic.typeinfo().get(.{ | ||||
| 				.userdata = dynamic.userdata(), | ||||
| 				.env = self, | ||||
| 			}, index), | ||||
| 
 | ||||
| 			else => self.raise(error.TypeMismatch, "object is not get-indexable"), | ||||
| 			.dynamic => |dynamic| dynamic.typeinfo().get(self, dynamic.userdata(), index), | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
| @ -1474,6 +1448,29 @@ pub const RuntimeEnv = struct { | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fn pop_frame(self: *RuntimeEnv) void { | ||||
| 		var to_pop = self.locals.values.len - (self.frames.pop() orelse unreachable).locals_top; | ||||
| 
 | ||||
| 		while (to_pop != 0) { | ||||
| 			if (self.locals.pop() orelse unreachable) |local| { | ||||
| 				self.discard(local); | ||||
| 			} | ||||
| 
 | ||||
| 			to_pop -= 1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fn push_frame(self: *RuntimeEnv, arg_count: u8) RuntimeError!Frame { | ||||
| 		const frame = Frame{ | ||||
| 			.arg_count = arg_count, | ||||
| 			.locals_top = self.locals.values.len - arg_count, | ||||
| 		}; | ||||
| 
 | ||||
| 		try self.frames.push_one(frame); | ||||
| 
 | ||||
| 		return frame; | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn raise(self: *RuntimeEnv, error_value: RuntimeError, message: []const coral.io.Byte) RuntimeError { | ||||
| 		self.print_error(message); | ||||
| 
 | ||||
| @ -1494,11 +1491,7 @@ pub const RuntimeEnv = struct { | ||||
| 
 | ||||
| 	pub fn set(self: *RuntimeEnv, indexable: *RuntimeRef, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void { | ||||
| 		return switch (indexable.object().payload) { | ||||
| 			.dynamic => |dynamic| dynamic.typeinfo().set(.{ | ||||
| 				.userdata = dynamic.userdata(), | ||||
| 				.env = self, | ||||
| 			}, index, value), | ||||
| 
 | ||||
| 			.dynamic => |dynamic| dynamic.typeinfo().set(self, dynamic.userdata(), index, value), | ||||
| 			else => self.raise(error.TypeMismatch, "object is not set-indexable"), | ||||
| 		}; | ||||
| 	} | ||||
| @ -1540,12 +1533,6 @@ pub const RuntimeEnv = struct { | ||||
| 			else => env.raise(error.TypeMismatch, "expected symbol object") | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn view_args(self: *RuntimeEnv) RuntimeError![]const ?*const RuntimeRef { | ||||
| 		const frame = self.frames.peek() orelse return self.raise(error.IllegalState, "stack underflow"); | ||||
| 
 | ||||
| 		return self.locals.values[(frame.locals_top - frame.arg_count) ..]; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| pub const RuntimeError = coral.io.AllocationError || error { | ||||
| @ -1720,31 +1707,26 @@ pub const RuntimeRef = opaque { | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| pub const Syscall = fn (env: *RuntimeEnv) RuntimeError!?*RuntimeRef; | ||||
| pub const Syscall = fn (env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef; | ||||
| 
 | ||||
| pub const Typeinfo = struct { | ||||
| 	name: []const coral.io.Byte, | ||||
| 	size: usize, | ||||
| 	destruct: ?*const fn (method: Method) void = null, | ||||
| 	call: *const fn (method: Method) RuntimeError!?*RuntimeRef = default_call, | ||||
| 	get: *const fn (method: Method, index: *const RuntimeRef) RuntimeError!?*RuntimeRef = default_get, | ||||
| 	set: *const fn (method: Method, value: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set, | ||||
| 	destruct: ?*const fn (env: *RuntimeEnv, userdata: []coral.io.Byte) void = null, | ||||
| 	call: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, frame: Frame) RuntimeError!?*RuntimeRef = default_call, | ||||
| 	get: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef) RuntimeError!?*RuntimeRef = default_get, | ||||
| 	set: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, value: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set, | ||||
| 
 | ||||
| 	pub const Method = struct { | ||||
| 		env: *RuntimeEnv, | ||||
| 		userdata: []coral.io.Byte, | ||||
| 	}; | ||||
| 
 | ||||
| 	fn default_call(method: Method) RuntimeError!?*RuntimeRef { | ||||
| 		return method.env.raise(error.TypeMismatch, "object is not callable"); | ||||
| 	fn default_call(env: *RuntimeEnv, _: []coral.io.Byte, _: Frame) RuntimeError!?*RuntimeRef { | ||||
| 		return env.raise(error.BadOperation, "object is not callable"); | ||||
| 	} | ||||
| 
 | ||||
| 	fn default_get(method: Method, _: *const RuntimeRef) RuntimeError!?*RuntimeRef { | ||||
| 		return method.env.raise(error.TypeMismatch, "object is not index-gettable"); | ||||
| 	fn default_get(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef) RuntimeError!?*RuntimeRef { | ||||
| 		return env.raise(error.BadOperation, "object is not get-indexable"); | ||||
| 	} | ||||
| 
 | ||||
| 	fn default_set(method: Method, _: *const RuntimeRef, _: ?*const RuntimeRef) RuntimeError!void { | ||||
| 		return method.env.raise(error.TypeMismatch, "object is not index-settable"); | ||||
| 	fn default_set(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef, _: ?*const RuntimeRef) RuntimeError!void { | ||||
| 		return env.raise(error.BadOperation, "object is not set-indexable"); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| @ -1772,59 +1754,35 @@ pub fn get_key(env: *RuntimeEnv, indexable: *RuntimeRef, key: []const coral.io.B | ||||
| 	return env.get(indexable, key_string); | ||||
| } | ||||
| 
 | ||||
| fn syscall_import(env: *RuntimeEnv) RuntimeError!?*RuntimeRef { | ||||
| 	const arg = try env.expect(try env.acquire_arg(0)); | ||||
| 
 | ||||
| 	defer env.discard(arg); | ||||
| 
 | ||||
| 	return env.import(file.Path.from(&.{try env.unbox_string(arg)})); | ||||
| fn syscall_import(env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef { | ||||
| 	return env.import(file.Path.from(&.{try env.unbox_string(try frame.get_arg(env, 0))})); | ||||
| } | ||||
| 
 | ||||
| fn syscall_print(env: *RuntimeEnv) RuntimeError!?*RuntimeRef { | ||||
| 	const arg = try env.expect(try env.acquire_arg(0)); | ||||
| 
 | ||||
| 	defer env.discard(arg); | ||||
| 
 | ||||
| 	env.print(try env.unbox_string(arg)); | ||||
| fn syscall_print(env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef { | ||||
| 	env.print(try env.unbox_string(try frame.get_arg(env, 0))); | ||||
| 
 | ||||
| 	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); | ||||
| fn syscall_vec2(env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef { | ||||
| 	const x = @as(f32, @floatCast(try env.unbox_float(try frame.get_arg(env, 0)))); | ||||
| 
 | ||||
| 	if (frame.has_arg(env, 1)) |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)); | ||||
| fn syscall_vec3(env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef { | ||||
| 	const x = @as(f32, @floatCast(try env.unbox_float(try frame.get_arg(env, 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))); | ||||
| 	if (frame.has_arg(env, 1)) |y| { | ||||
| 		return env.new_vector3( | ||||
| 			x, | ||||
| 			@floatCast(try env.unbox_float(y)), | ||||
| 			@floatCast(try env.unbox_float(try frame.get_arg(env, 2))), | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	return env.new_vector3(x, x, x); | ||||
|  | ||||
| @ -134,11 +134,6 @@ pub const Expression = union (enum) { | ||||
| 	}); | ||||
| }; | ||||
| 
 | ||||
| pub const DeclarationStorage = enum { | ||||
| 	variant, | ||||
| 	readonly, | ||||
| }; | ||||
| 
 | ||||
| const ExpressionBuilder = fn (self: *Tree) ParseError!Expression; | ||||
| 
 | ||||
| const IdentifierList = coral.list.Stack([]const coral.io.Byte); | ||||
| @ -171,6 +166,11 @@ pub const Statement = union (enum) { | ||||
| 	block: List, | ||||
| 	expression: Expression, | ||||
| 
 | ||||
| 	pub const DeclarationStorage = enum { | ||||
| 		@"var", | ||||
| 		let, | ||||
| 	}; | ||||
| 
 | ||||
| 	const List = coral.list.Stack(Statement); | ||||
| }; | ||||
| 
 | ||||
| @ -740,7 +740,7 @@ pub const Tree = struct { | ||||
| 				return .{ | ||||
| 					.declare = .{ | ||||
| 						.assigned_expression = try self.parse_expression(), | ||||
| 						.storage = .variant, | ||||
| 						.storage = .@"var", | ||||
| 						.identifier = identifier, | ||||
| 					}, | ||||
| 				}; | ||||
| @ -765,7 +765,7 @@ pub const Tree = struct { | ||||
| 				return .{ | ||||
| 					.declare = .{ | ||||
| 						.assigned_expression = try self.parse_expression(), | ||||
| 						.storage = .readonly, | ||||
| 						.storage = .let, | ||||
| 						.identifier = identifier, | ||||
| 					}, | ||||
| 				}; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user