Array and Table Literal Expressions for Kym #11
| @ -25,18 +25,19 @@ pub fn Functor(comptime Output: type, comptime Input: type) type { | |||||||
| 		const Self = @This(); | 		const Self = @This(); | ||||||
| 
 | 
 | ||||||
| 		pub fn bind(comptime State: type, state: *const State, comptime invoker: fn (capture: *const State, input: Input) Output) Self { | 		pub fn bind(comptime State: type, state: *const State, comptime invoker: fn (capture: *const State, input: Input) Output) Self { | ||||||
|  | 			const alignment = @alignOf(State); | ||||||
|  | 			const is_zero_aligned = alignment == 0; | ||||||
|  | 
 | ||||||
| 			return .{ | 			return .{ | ||||||
| 				.context = state, | 				.context = if (is_zero_aligned) state else @ptrCast(*const anyopaque, state), | ||||||
| 
 | 
 | ||||||
| 				.invoker = struct { | 				.invoker = struct { | ||||||
| 					fn invoke_opaque(context: *const anyopaque, input: Input) Output { | 					fn invoke_opaque(context: *const anyopaque, input: Input) Output { | ||||||
| 						const state_alignment = @alignOf(State); | 						if (is_zero_aligned) { | ||||||
| 
 |  | ||||||
| 						if (state_alignment == 0) { |  | ||||||
| 							return invoker(@ptrCast(*const State, context), input); | 							return invoker(@ptrCast(*const State, context), input); | ||||||
| 						} | 						} | ||||||
| 
 | 
 | ||||||
| 						return invoker(@ptrCast(*const State, @alignCast(state_alignment, context)), input); | 						return invoker(@ptrCast(*const State, @alignCast(alignment, context)), input); | ||||||
| 					} | 					} | ||||||
| 				}.invoke_opaque, | 				}.invoke_opaque, | ||||||
| 			}; | 			}; | ||||||
| @ -60,18 +61,19 @@ pub fn Generator(comptime Output: type, comptime Input: type) type { | |||||||
| 		const Self = @This(); | 		const Self = @This(); | ||||||
| 
 | 
 | ||||||
| 		pub fn bind(comptime State: type, state: *State, comptime invoker: fn (capture: *State, input: Input) Output) Self { | 		pub fn bind(comptime State: type, state: *State, comptime invoker: fn (capture: *State, input: Input) Output) Self { | ||||||
|  | 			const alignment = @alignOf(State); | ||||||
|  | 			const is_zero_aligned = alignment == 0; | ||||||
|  | 
 | ||||||
| 			return .{ | 			return .{ | ||||||
| 				.context = state, | 				.context = if (is_zero_aligned) state else @ptrCast(*anyopaque, state), | ||||||
| 
 | 
 | ||||||
| 				.invoker = struct { | 				.invoker = struct { | ||||||
| 					fn invoke_opaque(context: *anyopaque, input: Input) Output { | 					fn invoke_opaque(context: *anyopaque, input: Input) Output { | ||||||
| 						const state_alignment = @alignOf(State); | 						if (is_zero_aligned) { | ||||||
| 
 |  | ||||||
| 						if (state_alignment == 0) { |  | ||||||
| 							return invoker(@ptrCast(*State, context), input); | 							return invoker(@ptrCast(*State, context), input); | ||||||
| 						} | 						} | ||||||
| 
 | 
 | ||||||
| 						return invoker(@ptrCast(*State, @alignCast(state_alignment, context)), input); | 						return invoker(@ptrCast(*State, @alignCast(alignment, context)), input); | ||||||
| 					} | 					} | ||||||
| 				}.invoke_opaque, | 				}.invoke_opaque, | ||||||
| 			}; | 			}; | ||||||
|  | |||||||
| @ -9,26 +9,18 @@ const std = @import("std"); | |||||||
| /// | /// | ||||||
| /// | /// | ||||||
| /// | /// | ||||||
| pub const ArgsContext = struct { | pub const DecimalFormat = struct { | ||||||
| 	writer: io.Writer, | 	delimiter: []const u8 = "", | ||||||
| 	index: usize, | 	positive_prefix: enum {none, plus, space} = .none, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// | /// | ||||||
| /// | /// | ||||||
| /// | /// | ||||||
| pub const ArgsFormatter = io.Functor(PrintError!void, ArgsContext); | pub const HexadecimalFormat = struct { | ||||||
| 
 |  | ||||||
| /// |  | ||||||
| /// Errors that may occur during utf8-encoded int parsing. |  | ||||||
| /// |  | ||||||
| pub const IntParseError = math.CheckedArithmeticError || ParseError; |  | ||||||
| 
 |  | ||||||
| /// |  | ||||||
| /// Optional rules for int parsing logic to consider during parsing. |  | ||||||
| /// |  | ||||||
| pub const IntParseOptions = struct { |  | ||||||
| 	delimiter: []const u8 = "", | 	delimiter: []const u8 = "", | ||||||
|  | 	positive_prefix: enum {none, plus, space} = .none, | ||||||
|  | 	casing: enum {lower, upper} = .lower, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// | /// | ||||||
| @ -46,48 +38,85 @@ pub const PrintError = error { | |||||||
| 	PrintIncomplete, | 	PrintIncomplete, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| pub fn format_args(args: anytype) ArgsFormatter { | /// | ||||||
| 	const Args = @TypeOf(args); | /// | ||||||
| 
 | /// | ||||||
| 	return ArgsFormatter.bind(Args, &args, struct { | pub fn parse_decimal(comptime Decimal: type, utf8: []const u8, format: DecimalFormat) !Decimal { | ||||||
| 		fn get(typed_args: *const Args, context: ArgsContext) PrintError!void { | 	if (utf8.len == 0) { | ||||||
| 			_ = typed_args; | 		return error.BadSyntax; | ||||||
| 			_ = context; |  | ||||||
| 		} |  | ||||||
| 	}.get); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| /// | 	switch (@typeInfo(Decimal)) { | ||||||
| /// Attempts to parse a float value of type described by `float` from `utf8`. | 		.Int => |int| { | ||||||
| /// | 			var has_sign = switch (utf8[0]) { | ||||||
| /// The function returns a [ParseError] if `utf8` does not conform to the syntax of a float. | 				'-', '+', ' ' => true, | ||||||
| /// | 				else => false, | ||||||
| pub fn parse_float(comptime float: std.builtin.Type.Float, utf8: []const u8) ParseError!math.Float(float) { | 			}; | ||||||
|  | 
 | ||||||
|  | 			var result = @as(Decimal, 0); | ||||||
|  | 
 | ||||||
|  | 			for (@boolToInt(has_sign) .. utf8.len) |index| { | ||||||
|  | 				const radix = 10; | ||||||
|  | 				const code = utf8[index]; | ||||||
|  | 
 | ||||||
|  | 				switch (code) { | ||||||
|  | 					'0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => { | ||||||
|  | 						result = try math.checked_add( | ||||||
|  | 							try math.checked_mul(result, radix), | ||||||
|  | 							try math.checked_sub(code, '0')); | ||||||
|  | 					}, | ||||||
|  | 
 | ||||||
|  | 					else => { | ||||||
|  | 						if (format.delimiter.len == 0 or !io.equals(format.delimiter, utf8[index ..])) { | ||||||
|  | 							return error.BadSyntax; | ||||||
|  | 						} | ||||||
|  | 					}, | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			switch (int.signedness) { | ||||||
|  | 				.signed => { | ||||||
|  | 					return result * @as(Decimal, if (has_sign and utf8[0] == '-') -1 else 1); | ||||||
|  | 				}, | ||||||
|  | 
 | ||||||
|  | 				.unsigned => { | ||||||
|  | 					if (has_sign and utf8[0] == '-') { | ||||||
|  | 						return error.OutOfMemory; | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					return result; | ||||||
|  | 				}, | ||||||
|  | 			} | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		.Float => { | ||||||
| 			// "" | 			// "" | ||||||
| 			if (utf8.len == 0) { | 			if (utf8.len == 0) { | ||||||
| 				return error.BadSyntax; | 				return error.BadSyntax; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 	const is_negative = utf8[0] == '-'; | 			var has_sign = switch (utf8[0]) { | ||||||
|  | 				'-', '+', ' ' => true, | ||||||
|  | 				else => false, | ||||||
|  | 			}; | ||||||
| 
 | 
 | ||||||
| 			// "-" | 			// "-" | ||||||
| 	if (is_negative and (utf8.len == 1)) { | 			if (has_sign and utf8.len == 1) { | ||||||
| 				return error.BadSyntax; | 				return error.BadSyntax; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 	const negative_offset = @boolToInt(is_negative); | 			const sign_offset = @boolToInt(has_sign); | ||||||
| 	var has_decimal = utf8[negative_offset] == '.'; | 			var has_decimal = utf8[sign_offset] == '.'; | ||||||
| 
 | 
 | ||||||
| 			// "-." | 			// "-." | ||||||
| 			if (has_decimal and (utf8.len == 2)) { | 			if (has_decimal and (utf8.len == 2)) { | ||||||
| 				return error.BadSyntax; | 				return error.BadSyntax; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 	const Float = math.Float(float); | 			var result = @as(Decimal, 0); | ||||||
| 	var result: Float = 0; | 			var factor = @as(Decimal, if (has_sign and utf8[0] == '-') -1 else 1); | ||||||
| 	var factor: Float = 1; |  | ||||||
| 
 | 
 | ||||||
| 	for (utf8[0 .. negative_offset + @boolToInt(has_decimal)]) |code| switch (code) { | 			for (utf8[0 .. (sign_offset + @boolToInt(has_decimal))]) |code| switch (code) { | ||||||
| 				'.' => { | 				'.' => { | ||||||
| 					if (has_decimal) return error.BadSyntax; | 					if (has_decimal) return error.BadSyntax; | ||||||
| 
 | 
 | ||||||
| @ -97,68 +126,17 @@ pub fn parse_float(comptime float: std.builtin.Type.Float, utf8: []const u8) Par | |||||||
| 				'0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => { | 				'0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => { | ||||||
| 					if (has_decimal) factor /= 10.0; | 					if (has_decimal) factor /= 10.0; | ||||||
| 
 | 
 | ||||||
| 			result = ((result * 10.0) + @intToFloat(Float, code - '0')); | 					result = ((result * 10.0) + @intToFloat(Decimal, code - '0')); | ||||||
| 				}, | 				}, | ||||||
| 
 | 
 | ||||||
| 				else => return error.BadSyntax, | 				else => return error.BadSyntax, | ||||||
| 			}; | 			}; | ||||||
| 
 | 
 | ||||||
| 			return result * factor; | 			return result * factor; | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// |  | ||||||
| /// Attempts to parse an int value of type described by `int` from `utf8`, with `options` as additional rules for the |  | ||||||
| /// parsing logic to consider. |  | ||||||
| /// |  | ||||||
| /// The function returns a [IntParseError] if `utf8` does not conform to the syntax of a float, does not match the rules |  | ||||||
| /// specified in `option`, or exceeds the maximum size of the int described by `int`. |  | ||||||
| /// |  | ||||||
| pub fn parse_int( |  | ||||||
| 	comptime int: std.builtin.Type.Int, |  | ||||||
| 	utf8: []const u8, |  | ||||||
| 	options: IntParseOptions) IntParseError!math.Int(int) { |  | ||||||
| 
 |  | ||||||
| 	if (utf8.len == 0) { |  | ||||||
| 		return error.BadSyntax; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	{ |  | ||||||
| 		const is_negative = utf8[0] == '-'; |  | ||||||
| 
 |  | ||||||
| 		switch (int.signedness) { |  | ||||||
| 			.signed => { |  | ||||||
| 				if (is_negative and utf8.len == 1) { |  | ||||||
| 					return error.BadSyntax; |  | ||||||
| 				} |  | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 			.unsigned => { | 		else => @compileError("`" ++ @typeName(Decimal) ++ "` cannot be formatted as a decimal string"), | ||||||
| 				if (is_negative) { |  | ||||||
| 					return error.BadSyntax; |  | ||||||
| 	} | 	} | ||||||
| 			}, |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	var result = @as(math.Int(int), 0); |  | ||||||
| 
 |  | ||||||
| 	for (0 .. utf8.len) |index| { |  | ||||||
| 		const code = utf8[index]; |  | ||||||
| 
 |  | ||||||
| 		switch (code) { |  | ||||||
| 			'0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => { |  | ||||||
| 				result = try math.checked_add(try math.checked_mul(result, 10), try math.checked_sub(code, '0')); |  | ||||||
| 			}, |  | ||||||
| 
 |  | ||||||
| 			else => { |  | ||||||
| 				if (options.delimiter.len == 0 or !io.equals(options.delimiter, utf8[index ..])) { |  | ||||||
| 					return error.BadSyntax; |  | ||||||
| 				} |  | ||||||
| 			}, |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return result; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// | /// | ||||||
| @ -175,53 +153,35 @@ pub fn print(writer: io.Writer, utf8: []const u8) PrintError!void { | |||||||
| /// | /// | ||||||
| /// | /// | ||||||
| /// | /// | ||||||
| pub fn print_float(comptime float: std.builtin.Type.Float, writer: io.Writer, value: @Type(float)) PrintError!void { | pub fn print_formatted(writer: io.Writer, comptime format: []const u8, arguments: anytype) PrintError!void { | ||||||
| 	_ = writer; | 	switch (@typeInfo(@TypeOf(arguments))) { | ||||||
| 	_ = value; | 		.Struct => |arguments_struct| { | ||||||
| } | 			comptime var arg_index = 0; | ||||||
|  | 			comptime var head = 0; | ||||||
|  | 			comptime var tail = 0; | ||||||
| 
 | 
 | ||||||
| /// | 			inline while (tail < format.len) : (tail += 1) { | ||||||
| /// Prints the format string `format` with all `{...}` placeholders substituted with the a value in `args` corresponding |  | ||||||
| /// to the ordinality of each substitution. |  | ||||||
| /// |  | ||||||
| /// Specifiers may be placed inside `{}` to augment the behavior of the corresponding [FormatArg]. For example, to print |  | ||||||
| /// a float formatted with an integer component padding of `3`, a decimal padding of `2`, and a prefix for positives as |  | ||||||
| /// well as negatives, you may specify it as `{+3.2}`. |  | ||||||
| /// |  | ||||||
| /// To prevent braces from being interpreted as format placeholders, simply double-brace them (`"{{"`) and the format |  | ||||||
| /// string will substitute them as a single brace. |  | ||||||
| /// |  | ||||||
| /// *Note* the function assumes that, for every specifier in `format`, there is a corresponding [FormatArg] in `args`. |  | ||||||
| /// Further, any instance of `{` is assumed to be a placeholder and must be terminated with a corresponding `}` before |  | ||||||
| /// the end of string. Failure to meet any of these requirements will result in safety-checked runtime behavior. |  | ||||||
| /// |  | ||||||
| pub fn print_formatted(writer: io.Writer, format: []const u8, args_formatter: ArgsFormatter) PrintError!void { |  | ||||||
| 	const usize_int = @typeInfo(usize).Int; |  | ||||||
| 	var head = @as(usize, 0); |  | ||||||
| 	var tail = @as(usize, 0); |  | ||||||
| 	var arg_index = @as(usize, 0); |  | ||||||
| 
 |  | ||||||
| 	while (tail < format.len) : (tail += 1) { |  | ||||||
| 				if (format[tail] == '{') { | 				if (format[tail] == '{') { | ||||||
| 			debug.assert(tail < format.len); | 					if (tail > format.len) { | ||||||
|  | 						@compileError("expected an idenifier after opening `{`"); | ||||||
|  | 					} | ||||||
| 
 | 
 | ||||||
| 					tail += 1; | 					tail += 1; | ||||||
| 
 | 
 | ||||||
| 					switch (format[tail]) { | 					switch (format[tail]) { | ||||||
| 						'{' => { | 						'{' => { | ||||||
| 					try print(writer, format[head .. tail]); | 							try print(writer, format[head .. (tail - 1)]); | ||||||
| 
 | 
 | ||||||
| 							tail += 1; | 							tail += 1; | ||||||
| 							head = tail; | 							head = tail; | ||||||
| 						}, | 						}, | ||||||
| 
 | 
 | ||||||
| 						'}' => { | 						'}' => { | ||||||
| 					try print(writer, format[head .. tail]); | 							if (!arguments_struct.is_tuple) { | ||||||
|  | 								@compileError("all format specifiers must be named when using a named struct"); | ||||||
|  | 							} | ||||||
| 
 | 
 | ||||||
| 					try args_formatter.invoke(.{ | 							try print(writer, arguments[arg_index]); | ||||||
| 						.index = arg_index, |  | ||||||
| 						.writer = writer, |  | ||||||
| 					}); |  | ||||||
| 
 | 
 | ||||||
| 							arg_index += 1; | 							arg_index += 1; | ||||||
| 							tail += 1; | 							tail += 1; | ||||||
| @ -229,25 +189,28 @@ pub fn print_formatted(writer: io.Writer, format: []const u8, args_formatter: Ar | |||||||
| 						}, | 						}, | ||||||
| 
 | 
 | ||||||
| 						else => { | 						else => { | ||||||
| 					try print(writer, format[head .. tail]); | 							if (arguments_struct.is_tuple) { | ||||||
|  | 								@compileError("format specifiers cannot be named when using a tuple struct"); | ||||||
|  | 							} | ||||||
|  | 
 | ||||||
|  | 							try print(writer, format[head .. (tail - 1)]); | ||||||
| 
 | 
 | ||||||
| 					tail += 1; |  | ||||||
| 							head = tail; | 							head = tail; | ||||||
|  | 							tail += 1; | ||||||
|  | 
 | ||||||
|  | 							if (tail >= format.len) { | ||||||
|  | 								@compileError("expected closing `}` or another `{` after opening `{`"); | ||||||
|  | 							} | ||||||
| 
 | 
 | ||||||
| 							debug.assert(tail < format.len); | 							debug.assert(tail < format.len); | ||||||
| 
 | 
 | ||||||
| 					while (format[tail] != '}') { | 							inline while (format[tail] != '}') { | ||||||
| 								tail += 1; | 								tail += 1; | ||||||
| 
 | 
 | ||||||
| 								debug.assert(tail < format.len); | 								debug.assert(tail < format.len); | ||||||
| 							} | 							} | ||||||
| 
 | 
 | ||||||
| 					const arg_index_name = format[head .. tail]; | 							try print_value(writer, @field(arguments, format[head .. tail])); | ||||||
| 
 |  | ||||||
| 					try args_formatter.invoke(.{ |  | ||||||
| 						.index = parse_int(usize_int, arg_index_name, .{}) catch @panic("invalid arg index value"), |  | ||||||
| 						.writer = writer, |  | ||||||
| 					}); |  | ||||||
| 
 | 
 | ||||||
| 							tail += 1; | 							tail += 1; | ||||||
| 							head = tail; | 							head = tail; | ||||||
| @ -255,43 +218,81 @@ pub fn print_formatted(writer: io.Writer, format: []const u8, args_formatter: Ar | |||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		else => @compileError("`arguments` must be a struct type"), | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// | /// | ||||||
| /// Attempts to print the int `value` described by `int` to `writer`. |  | ||||||
| /// | /// | ||||||
| /// The function returns [PrintError] if the write failed to complete partially or entirely. |  | ||||||
| /// | /// | ||||||
| pub fn print_int(comptime int: std.builtin.Type.Int, writer: io.Writer, value: math.Int(int)) PrintError!void { | pub fn print_decimal(writer: io.Writer, value: anytype, format: DecimalFormat) PrintError!void { | ||||||
| 	if (value == 0) { | 	if (value == 0) { | ||||||
| 		return try print(writer, "0"); | 		return print(writer, switch (format.positive_prefix) { | ||||||
|  | 			.none => "0", | ||||||
|  | 			.plus => "+0", | ||||||
|  | 			.space => " 0", | ||||||
|  | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// TODO: Don't make this buffer arbitrarily size cause big int types WILL overflow. | 	switch (@typeInfo(@TypeOf(value))) { | ||||||
| 	var buffer = [_]u8{0} ** 40; | 		.Int => |int| { | ||||||
| 	var buffer_count: usize = 0; |  | ||||||
| 	var split_value = value; |  | ||||||
| 
 |  | ||||||
| 	if ((int.signedness == .unsigned) and (value < 0)) { |  | ||||||
| 		buffer[0] = '-'; |  | ||||||
| 		buffer_count += 1; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	while (split_value != 0) : (buffer_count += 1) { |  | ||||||
| 			const radix = 10; | 			const radix = 10; | ||||||
| 
 | 			var buffer = [_]u8{0} ** (1 + math.max(int.bits, 1)); | ||||||
| 		buffer[buffer_count] = @intCast(u8, (split_value % radix) + '0'); | 			var buffer_start = buffer.len - 1; | ||||||
| 		split_value = (split_value / radix); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 			{ | 			{ | ||||||
| 		const half_buffer_count = buffer_count / 2; | 				var decomposable_value = value; | ||||||
| 		var index: usize = 0; |  | ||||||
| 
 | 
 | ||||||
| 		while (index < half_buffer_count) : (index += 1) { | 				while (decomposable_value != 0) : (buffer_start -= 1) { | ||||||
| 			io.swap(u8, &buffer[index], &buffer[buffer_count - index - 1]); | 					buffer[buffer_start] = @intCast(u8, (decomposable_value % radix) + '0'); | ||||||
|  | 					decomposable_value = (decomposable_value / radix); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 	try print(writer, buffer[0 .. buffer_count]); | 			if (int.signedness == .unsigned and value < 0) { | ||||||
|  | 				buffer[buffer_start] = '-'; | ||||||
|  | 			} else { | ||||||
|  | 				switch (format.positive_prefix) { | ||||||
|  | 					.none => buffer_start += 1, | ||||||
|  | 					.plus => buffer[buffer_start] = '+', | ||||||
|  | 					.space => buffer[buffer_start] = ' ', | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			try print(writer, buffer[buffer_start ..]); | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		else => @compileError("`arguments` must be a struct type"), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn print_hexadecimal(writer: io.Writer, value: anytype, format: HexadecimalFormat) PrintError!void { | ||||||
|  | 	// TODO: Implement. | ||||||
|  | 	_ = writer; | ||||||
|  | 	_ = value; | ||||||
|  | 	_ = format; | ||||||
|  | 
 | ||||||
|  | 	unreachable; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | noinline fn print_value(writer: io.Writer, value: anytype) PrintError!void { | ||||||
|  | 	const Value = @TypeOf(value); | ||||||
|  | 
 | ||||||
|  | 	return switch (@typeInfo(Value)) { | ||||||
|  | 		.Int => print_decimal(writer, value, .{}), | ||||||
|  | 		.Float => print_decimal(writer, value, .{}), | ||||||
|  | 
 | ||||||
|  | 		.Pointer => |pointer| switch (pointer.size) { | ||||||
|  | 			.One, .Many, .C => print_hexadecimal(writer, @ptrToInt(value), .{}), | ||||||
|  | 			.Slice => if (pointer.child == u8) print(writer, value) else @compileError(unformattableMessage(Value)), | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		else => @compileError(unformattableMessage(Value)), | ||||||
|  | 	}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn unformattableMessage(comptime Value: type) []const u8 { | ||||||
|  | 	return "`" ++ @typeName(Value) ++ "` are not formattable"; | ||||||
| } | } | ||||||
|  | |||||||
| @ -189,7 +189,7 @@ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) types.ParseError!Expr | |||||||
| 			_ = tokenizer.step(.{.include_newlines = false}); | 			_ = tokenizer.step(.{.include_newlines = false}); | ||||||
| 
 | 
 | ||||||
| 			return Expression{ | 			return Expression{ | ||||||
| 				.integer_literal = coral.utf8.parse_int(@typeInfo(types.Integer).Int, value, .{}) catch |parse_error| { | 				.integer_literal = coral.utf8.parse_decimal(types.Integer, value, .{}) catch |parse_error| { | ||||||
| 					return self.fail_syntax(switch (parse_error) { | 					return self.fail_syntax(switch (parse_error) { | ||||||
| 						error.BadSyntax => "invalid integer literal", | 						error.BadSyntax => "invalid integer literal", | ||||||
| 						error.IntOverflow => "integer literal is too big", | 						error.IntOverflow => "integer literal is too big", | ||||||
| @ -202,7 +202,7 @@ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) types.ParseError!Expr | |||||||
| 			_ = tokenizer.step(.{.include_newlines = false}); | 			_ = tokenizer.step(.{.include_newlines = false}); | ||||||
| 
 | 
 | ||||||
| 			return Expression{ | 			return Expression{ | ||||||
| 				.float_literal = coral.utf8.parse_float(@typeInfo(types.Float).Float, value) catch |parse_error| { | 				.float_literal = coral.utf8.parse_decimal(types.Float, value, .{}) catch |parse_error| { | ||||||
| 					return self.fail_syntax(switch (parse_error) { | 					return self.fail_syntax(switch (parse_error) { | ||||||
| 						error.BadSyntax => "invalid float literal", | 						error.BadSyntax => "invalid float literal", | ||||||
| 					}); | 					}); | ||||||
|  | |||||||
| @ -59,25 +59,15 @@ pub fn compile(self: *Self, data: []const u8) types.RuntimeError!void { | |||||||
| 		var tokenizer = tokens.Tokenizer{.source = data}; | 		var tokenizer = tokens.Tokenizer{.source = data}; | ||||||
| 
 | 
 | ||||||
| 		ast.parse(&tokenizer) catch |init_error| { | 		ast.parse(&tokenizer) catch |init_error| { | ||||||
| 			if (init_error == error.OutOfMemory) { | 			if (init_error == error.BadSyntax) { | ||||||
| 				self.clear_error_details(); | 				self.clear_error_details(); | ||||||
| 
 | 
 | ||||||
| 				try self.message_data.push_all(self.env.allocator, "@("); |  | ||||||
| 
 |  | ||||||
| 				var message_buffer = self.message_data.as_buffer(self.env.allocator); | 				var message_buffer = self.message_data.as_buffer(self.env.allocator); | ||||||
| 				const message_writer = message_buffer.as_writer(); |  | ||||||
| 
 | 
 | ||||||
| 				coral.utf8.print_int(@typeInfo(usize).Int, message_writer, tokenizer.lines_stepped) catch { | 				coral.utf8.print_formatted(message_buffer.as_writer(), "@({line}): {name}", .{ | ||||||
| 					return error.OutOfMemory; | 					.line = tokenizer.lines_stepped, | ||||||
| 				}; | 					.name = ast.error_message, | ||||||
| 
 | 				}) catch return error.OutOfMemory; | ||||||
| 				coral.utf8.print(message_writer, "): ") catch { |  | ||||||
| 					return error.OutOfMemory; |  | ||||||
| 				}; |  | ||||||
| 
 |  | ||||||
| 				coral.utf8.print(message_writer, ast.error_message) catch { |  | ||||||
| 					return error.OutOfMemory; |  | ||||||
| 				}; |  | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			return init_error; | 			return init_error; | ||||||
| @ -216,7 +206,7 @@ pub fn emit_opcode(self: *Self, opcode: Opcode) coral.io.AllocationError!void { | |||||||
| pub fn error_details(self: Self) []const u8 { | pub fn error_details(self: Self) []const u8 { | ||||||
| 	coral.debug.assert(self.message_data.values.len >= self.message_name_len); | 	coral.debug.assert(self.message_data.values.len >= self.message_name_len); | ||||||
| 
 | 
 | ||||||
| 	return self.message_data.values[self.message_name_len .. ]; | 	return self.message_data.values; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn init(env: *Environment, chunk_name: []const u8) coral.io.AllocationError!Self { | pub fn init(env: *Environment, chunk_name: []const u8) coral.io.AllocationError!Self { | ||||||
|  | |||||||
| @ -91,8 +91,6 @@ pub const Tokenizer = struct { | |||||||
| 		include_newlines: bool, | 		include_newlines: bool, | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const default_token = Token{.unknown = 0}; |  | ||||||
| 
 |  | ||||||
| 	pub fn step(self: *Tokenizer, options: StepOptions) bool { | 	pub fn step(self: *Tokenizer, options: StepOptions) bool { | ||||||
| 		var cursor = @as(usize, 0); | 		var cursor = @as(usize, 0); | ||||||
| 
 | 
 | ||||||
| @ -114,11 +112,10 @@ pub const Tokenizer = struct { | |||||||
| 
 | 
 | ||||||
| 				'\n' => { | 				'\n' => { | ||||||
| 					cursor += 1; | 					cursor += 1; | ||||||
|  | 					self.current_token = .newline; | ||||||
|  | 					self.lines_stepped += 1; | ||||||
| 
 | 
 | ||||||
| 					if (options.include_newlines) { | 					if (options.include_newlines) { | ||||||
| 						self.lines_stepped += 1; |  | ||||||
| 						self.current_token = .newline; |  | ||||||
| 
 |  | ||||||
| 						return true; | 						return true; | ||||||
| 					} | 					} | ||||||
| 				}, | 				}, | ||||||
|  | |||||||
| @ -3,10 +3,5 @@ const coral = @import("coral"); | |||||||
| const ona = @import("ona"); | const ona = @import("ona"); | ||||||
| 
 | 
 | ||||||
| pub fn main() !void { | pub fn main() !void { | ||||||
| 	var data = [_]u8{0} ** 32; |  | ||||||
| 	var fixed_buffer = coral.io.FixedBuffer{.slice = &data}; |  | ||||||
| 
 |  | ||||||
| 	try coral.utf8.print_formatted(fixed_buffer.as_writer(), "hello, {}", coral.utf8.format_args(.{"world"})); |  | ||||||
| 
 |  | ||||||
| 	ona.run_app(.{.sandboxed_path = &ona.file.Path.cwd}); | 	ona.run_app(.{.sandboxed_path = &ona.file.Path.cwd}); | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user