Created
August 4, 2022 12:53
-
-
Save MichaelVoelkel/57d07547372a6fc44371f7e8721b3adb to your computer and use it in GitHub Desktop.
Zig Format put split into functions pseudo code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pub fn format( | |
writer: anytype, | |
comptime fmt: []const u8, | |
args: anytype, | |
) !void { | |
const ArgsType = @TypeOf(args); | |
const args_type_info = @typeInfo(ArgsType); | |
_checkPreconditions(...); | |
@setEvalBranchQuota(2000000); | |
comptime var arg_state: ArgState = .{ .args_len = fields_info.len }; | |
comptime var i = 0; | |
inline while (i < fmt.len) { | |
const start_index = i; | |
_proceedToAnyBrace(...); | |
comptime var end_index = i; | |
comptime var unescape_brace = false; | |
encountered_double_braces = _handleDoubleBracesIfEncountered(...); | |
// Write out the literal | |
_writeOutLiteral(...); | |
// We've already skipped the other brace, restart the loop | |
next_step = _proceedAfterLiteralEnd(...); | |
comptime switch next_step {.skip_loop: continue, .break: break, .proceed: {}}; | |
const fmt_begin = i; | |
const fmt_end = _findAndSkipClosingBrace(...); | |
const placeholder = comptime parsePlaceholder(fmt[fmt_begin..fmt_end].*); | |
const arg_pos = _getPlaceholderPos(...); | |
const width = _getPlaceholderWidth(...); | |
const precision = _getPlaceholderPrecision(...); | |
const arg_to_print = comptime arg_state.nextArg(arg_pos) orelse | |
@compileError("too few arguments"); | |
_formatTheType(...); | |
} | |
_assertUnusedArgs(...); | |
} | |
pub fn _checkPreconditions(...) { | |
if (args_type_info != .Struct) { | |
@compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType)); | |
} | |
const fields_info = args_type_info.Struct.fields; | |
if (fields_info.len > max_format_args) { | |
@compileError("32 arguments max are supported per format call"); | |
} | |
} | |
fn _proceedToAnyBrace(...) { | |
inline while (i < fmt.len) : (i += 1) { | |
switch (fmt[i]) { | |
'{', '}' => break, | |
else => {}, | |
} | |
} | |
} | |
fn _handleDoubleBracesIfEncountered(...) { | |
if (i + 1 < fmt.len and fmt[i + 1] == fmt[i]) { | |
unescape_brace = true; | |
// Make the first brace part of the literal... | |
end_index += 1; | |
// ...and skip both | |
i += 2; | |
} | |
} | |
fn _writeOutLiteral(...) { | |
// evtl. Kommentar zu der Bedingung hinzufügen im Funktionsnamen, aber wsl unnötig | |
if (start_index != end_index) { | |
try writer.writeAll(fmt[start_index..end_index]); | |
} | |
} | |
fn _proceedAfterLiteralEnd(...) { | |
if (unescape_brace) return .skip_loop; | |
if (i >= fmt.len) return .break; | |
if (fmt[i] == '}') { | |
@compileError("missing opening {"); | |
} | |
// Get past the { | |
comptime assert(fmt[i] == '{'); | |
i += 1; | |
return .proceed; | |
} | |
fn _findAndSkipClosingBrace(...) { | |
inline while (i < fmt.len and fmt[i] != '}') : (i += 1) {} | |
if (i >= fmt.len) { | |
@compileError("missing closing }"); | |
} | |
comptime assert(fmt[i] == '}'); // überhaupt noch notwendig? | |
i += 1; | |
return i; | |
} | |
fn _getPlaceholderPos(...) { | |
return comptime switch (placeholder.arg) { | |
.none => null, | |
.number => |pos| pos, | |
.named => |arg_name| meta.fieldIndex(ArgsType, arg_name) orelse | |
@compileError("no argument with name '" ++ arg_name ++ "'"), | |
}; | |
} | |
fn _getArgWidth(...) { | |
// wieso ist das eigentlich kein comptime switch, das für placeholder arg schon? | |
return switch (placeholder.width) { | |
.none => null, | |
.number => |v| v, | |
.named => |arg_name| blk: { | |
const arg_i = comptime meta.fieldIndex(ArgsType, arg_name) orelse | |
@compileError("no argument with name '" ++ arg_name ++ "'"); | |
_ = comptime arg_state.nextArg(arg_i) orelse @compileError("too few arguments"); | |
break :blk @field(args, arg_name); | |
}, | |
}; | |
} | |
fn _getPlaceholderPrecision(...) { | |
return switch (placeholder.precision) { | |
.none => null, | |
.number => |v| v, | |
.named => |arg_name| blk: { | |
const arg_i = comptime meta.fieldIndex(ArgsType, arg_name) orelse | |
@compileError("no argument with name '" ++ arg_name ++ "'"); | |
_ = comptime arg_state.nextArg(arg_i) orelse @compileError("too few arguments"); | |
break :blk @field(args, arg_name); | |
}, | |
}; | |
} | |
fn _formatTheType(...) { | |
try formatType( | |
@field(args, fields_info[arg_to_print].name), | |
placeholder.specifier_arg, | |
FormatOptions{ | |
.fill = placeholder.fill, | |
.alignment = placeholder.alignment, | |
.width = width, | |
.precision = precision, | |
}, | |
writer, | |
default_max_depth, | |
); | |
} | |
fn _assertUnusedArgs(...) { | |
if (comptime arg_state.hasUnusedArgs()) { | |
const missing_count = arg_state.args_len - @popCount(ArgSetType, arg_state.used_args); | |
switch (missing_count) { | |
0 => unreachable, | |
1 => @compileError("unused argument in '" ++ fmt ++ "'"), | |
else => @compileError((comptime comptimePrint("{d}", .{missing_count})) ++ " unused arguments in '" ++ fmt ++ "'"), | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment