Implement Bytecode Executor for Kym #19
| @ -1,3 +1,5 @@ | ||||
| const debug = @import("./debug.zig"); | ||||
| 
 | ||||
| const io = @import("./io.zig"); | ||||
| 
 | ||||
| const math = @import("./math.zig"); | ||||
| @ -6,6 +8,11 @@ pub const DecimalFormat = struct { | ||||
| 	delimiter: []const io.Byte, | ||||
| 	positive_prefix: enum {none, plus, space}, | ||||
| 
 | ||||
| 	const default = DecimalFormat{ | ||||
| 		.delimiter = "", | ||||
| 		.positive_prefix = .none, | ||||
| 	}; | ||||
| 
 | ||||
| 	pub fn parse(self: DecimalFormat, utf8: []const io.Byte, comptime Decimal: type) ?Decimal { | ||||
| 		if (utf8.len == 0) { | ||||
| 			return null; | ||||
| @ -104,5 +111,170 @@ pub const DecimalFormat = struct { | ||||
| 			else => @compileError("`" ++ @typeName(Decimal) ++ "` cannot be formatted as a decimal string"), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn print(self: DecimalFormat, writer: io.Writer, value: anytype) PrintError!void { | ||||
| 		if (value == 0) { | ||||
| 			return print_string(writer, switch (self.positive_prefix) { | ||||
| 				.none => "0", | ||||
| 				.plus => "+0", | ||||
| 				.space => " 0", | ||||
| 			}); | ||||
| 		} | ||||
| 
 | ||||
| 		const ValueType = @TypeOf(value); | ||||
| 
 | ||||
| 		switch (@typeInfo(ValueType)) { | ||||
| 			.Int => |int| { | ||||
| 				const radix = 10; | ||||
| 				var buffer = [_]u8{0} ** (1 + math.max(int.bits, 1)); | ||||
| 				var buffer_start = buffer.len - 1; | ||||
| 
 | ||||
| 				{ | ||||
| 					var decomposable_value = value; | ||||
| 
 | ||||
| 					while (decomposable_value != 0) : (buffer_start -= 1) { | ||||
| 						buffer[buffer_start] = @intCast((decomposable_value % radix) + '0'); | ||||
| 						decomposable_value = (decomposable_value / radix); | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				if (int.signedness == .unsigned and value < 0) { | ||||
| 					buffer[buffer_start] = '-'; | ||||
| 				} else { | ||||
| 					switch (self.positive_prefix) { | ||||
| 						.none => buffer_start += 1, | ||||
| 						.plus => buffer[buffer_start] = '+', | ||||
| 						.space => buffer[buffer_start] = ' ', | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				try print_string(writer, buffer[buffer_start ..]); | ||||
| 			}, | ||||
| 
 | ||||
| 			else => unformattableMessage(ValueType), | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| pub const HexadecimalFormat = struct { | ||||
| 	delimiter: []const u8 = "", | ||||
| 	positive_prefix: enum {none, plus, space} = .none, | ||||
| 	casing: enum {lower, upper} = .lower, | ||||
| 
 | ||||
| 	const default = HexadecimalFormat{ | ||||
| 		.delimiter = "", | ||||
| 		.positive_prefix = .none, | ||||
| 		.casing = .lower, | ||||
| 	}; | ||||
| 
 | ||||
| 	pub fn print(self: HexadecimalFormat, writer: io.Writer, value: anytype) PrintError!void { | ||||
| 		// TODO: Implement. | ||||
| 		_ = self; | ||||
| 		_ = writer; | ||||
| 		_ = value; | ||||
| 
 | ||||
| 		unreachable; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| pub const PrintError = error { | ||||
| 	PrintFailed, | ||||
| 	PrintIncomplete, | ||||
| }; | ||||
| 
 | ||||
| pub fn print_string(writer: io.Writer, utf8: []const io.Byte) PrintError!void { | ||||
| 	if ((writer.invoke(utf8) orelse return error.PrintFailed) != utf8.len) { | ||||
| 		return error.PrintIncomplete; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn print_formatted(writer: io.Writer, comptime format: []const u8, arguments: anytype) PrintError!void { | ||||
| 	switch (@typeInfo(@TypeOf(arguments))) { | ||||
| 		.Struct => |arguments_struct| { | ||||
| 			comptime var arg_index = 0; | ||||
| 			comptime var head = 0; | ||||
| 			comptime var tail = 0; | ||||
| 
 | ||||
| 			inline while (tail < format.len) : (tail += 1) { | ||||
| 				if (format[tail] == '{') { | ||||
| 					if (tail > format.len) { | ||||
| 						@compileError("expected an idenifier after opening `{`"); | ||||
| 					} | ||||
| 
 | ||||
| 					tail += 1; | ||||
| 
 | ||||
| 					switch (format[tail]) { | ||||
| 						'{' => { | ||||
| 							try print_string(writer, format[head .. (tail - 1)]); | ||||
| 
 | ||||
| 							tail += 1; | ||||
| 							head = tail; | ||||
| 						}, | ||||
| 
 | ||||
| 						'}' => { | ||||
| 							if (!arguments_struct.is_tuple) { | ||||
| 								@compileError("all format specifiers must be named when using a named struct"); | ||||
| 							} | ||||
| 
 | ||||
| 							try print_string(writer, arguments[arg_index]); | ||||
| 
 | ||||
| 							arg_index += 1; | ||||
| 							tail += 1; | ||||
| 							head = tail; | ||||
| 						}, | ||||
| 
 | ||||
| 						else => { | ||||
| 							if (arguments_struct.is_tuple) { | ||||
| 								@compileError("format specifiers cannot be named when using a tuple struct"); | ||||
| 							} | ||||
| 
 | ||||
| 							try print_string(writer, format[head .. (tail - 1)]); | ||||
| 
 | ||||
| 							head = tail; | ||||
| 							tail += 1; | ||||
| 
 | ||||
| 							if (tail >= format.len) { | ||||
| 								@compileError("expected closing `}` or another `{` after opening `{`"); | ||||
| 							} | ||||
| 
 | ||||
| 							debug.assert(tail < format.len); | ||||
| 
 | ||||
| 							inline while (format[tail] != '}') { | ||||
| 								tail += 1; | ||||
| 
 | ||||
| 								debug.assert(tail < format.len); | ||||
| 							} | ||||
| 
 | ||||
| 							try print_value(writer, @field(arguments, format[head .. tail])); | ||||
| 
 | ||||
| 							tail += 1; | ||||
| 							head = tail; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		}, | ||||
| 
 | ||||
| 		else => @compileError("`arguments` must be a struct type"), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| noinline fn print_value(writer: io.Writer, value: anytype) PrintError!void { | ||||
| 	const Value = @TypeOf(value); | ||||
| 
 | ||||
| 	return switch (@typeInfo(Value)) { | ||||
| 		.Int => DecimalFormat.default.print(writer, value), | ||||
| 		.Float => DecimalFormat.default.print(writer, value), | ||||
| 
 | ||||
| 		.Pointer => |pointer| switch (pointer.size) { | ||||
| 			.One, .Many, .C => HexadecimalFormat.default.print(writer, @intFromPtr(value)), | ||||
| 			.Slice => if (pointer.child == u8) print_string(writer, value) else @compileError(unformattableMessage(Value)), | ||||
| 		}, | ||||
| 
 | ||||
| 		else => @compileError(unformattableMessage(Value)), | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| fn unformattableMessage(comptime Value: type) []const u8 { | ||||
| 	return "type `" ++ @typeName(Value) ++ "` is not formattable with this formatter"; | ||||
| } | ||||
|  | ||||
| @ -456,9 +456,12 @@ pub const RuntimeEnv = struct { | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn raise(self: *RuntimeEnv, runtime_error: RuntimeError, error_message: []const u8) RuntimeError { | ||||
| 		if (self.err_writer.invoke(error_message) == null) { | ||||
| 			return error.SystemFailure; | ||||
| 		} | ||||
| 		// TODO: Print stack trace from state. | ||||
| 		coral.utf8.print_formatted(self.err_writer, "{name}@{line}: {message}", .{ | ||||
| 			.name = "???", | ||||
| 			.line = @as(u64, 0), | ||||
| 			.message = error_message, | ||||
| 		}) catch return error.SystemFailure; | ||||
| 
 | ||||
| 		return runtime_error; | ||||
| 	} | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user