Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New format string for std.fmt.format #8291

Open
JoaoSilveira opened this issue Mar 19, 2021 · 3 comments
Open

New format string for std.fmt.format #8291

JoaoSilveira opened this issue Mar 19, 2021 · 3 comments
Labels
breaking Implementing this issue could cause existing code to no longer compile or have different behavior. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. standard library This issue involves writing Zig code for the standard library.
Milestone

Comments

@JoaoSilveira
Copy link

There's a TL;DR go down there if you don't feel like reading all of this.

The problem

Some time ago I was writing a DateTime struct and stumbled upon a problem when writing the format method for it. My first idea was to use the [specifier] parameter to format the fields, and everything looked great until I needed to print a : (ISO8601 format) because you can't scape it in the [specifier]. There isn't (that I'm aware of) a solution for this other than to create a function to format the DateTime and print it's result.

The other thing I felt a bit bothered was the FormatOptions parameter. IMHO I think that the std.fmt.format function should be the responsible for the fill, alignment and width parameters, and precision is a floating point number only parameter (yes, you can find new uses for these in your format function, but you got the point).

Adding a condition to escape a : isn't that hard but I don't believe this is the way to go.

Proposal

I'd to propose the following format string {[argument][placement]:[specifier]}, where placement => '['[fill][alignment][width]']' (single quote meaning literal).

As I didn't find the issue that explains why any became obligatory in first place, any would maintain the status quo, although I personally don't find that it is necessary.

Pros

  • Remove unrelated parameters in the types' format. Ex: an error wouldn't get a precision parameter
  • Remove the necessity of putting argument inside brackets [] when referring to it by name, or when specifier starts with a number
  • The placement is inside brackets [], symbolizing the box where the value will fit (A really nice way of saying boilerplate)
  • The implementator of format don't need to worry about it's positioning
  • Remove the necessity of escaping :

Cons

  • This will break things, and broken things must be fixed
  • Every implementator of format will have to parse its own specifier (status quo, I guess)
  • This could be (and probably is) a really hard way to add an escape condition (I'm probably giving an edge case too much credit)

Examples

The first line is status quo, the second is the proposal.

// argument by index 
print("{0any}", .{ value });
print("{0:any}", .{ value });

// argument by name
print("{[arg]any}", .{ .arg = value });
print("{arg:any}", .{ .arg = value });

// with fill/align/width
print("{any:0<15}", .{ value });
print("{[0<15]:any}", .{ value });

// with index and fill/align/width
print("{0any:0<15}", .{ value });
print("{0[0<15]:any}", .{ value });

// floating point with precision
print("{e:.2}", .{ value });
print("{:e.2}", .{ value });

// when `specifier` starts with a number
print("{[0]555}", .{ value });
print("{:555}", .{ value });

// if we could escape `:` with \\, ISO8601 would look like this
print("{yyyy-MM-dd'T'HH\\:mm\\:ss.fffZ}", .{ value });
// the `specifier` is between the first `:` and `}`
// the only escape needed is for `}`
print("{:yyyy-MM-dd'T'HH:mm:ss.fffZ}", .{ value });

TL;DR

Change the current string format from (values inside '' are literals)

{[(index|'['name']')][specifier]:[fill][alignment][width].[precision]}

to

{[(index|name)]['['[fill][alignment][width]']']:[specifier]}

so we don't need to escape :, or to put argument inside brackets [] when referring to it by name or when its specifier starts with numbers

@jayschwa jayschwa added breaking Implementing this issue could cause existing code to no longer compile or have different behavior. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. standard library This issue involves writing Zig code for the standard library. labels Mar 19, 2021
@jayschwa jayschwa added this to the 0.9.0 milestone Mar 19, 2021
@andrewrk andrewrk modified the milestones: 0.9.0, 0.10.0 May 19, 2021
@iacore
Copy link
Contributor

iacore commented Sep 18, 2023

In DateTime, the format specifier ({YYYY-MM-DD kk:mm}) is currently a compile error because of the :.

Work around:

const now = DateTime.now();
const desc = try std.fmt.allocPrint(a,
    \\Hello
    \\
    \\last alive at {YYYY-MM-DD kk}:{0mm}
, .{now});

@iacore
Copy link
Contributor

iacore commented Sep 18, 2023

Question: is it possible to make the current implementation "split right"? For example, specifier:also specifier:precision etc instead of the current compile error.

Using : as fill would be complicated though.

@iacore
Copy link
Contributor

iacore commented Sep 18, 2023

Also, are we going to support nested specifier?

Example from python:

width = 10

precision = 4

value = decimal.Decimal("12.34567")

f"result: {value:{width}.{precision}}"  # nested fields

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking Implementing this issue could cause existing code to no longer compile or have different behavior. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. standard library This issue involves writing Zig code for the standard library.
Projects
None yet
Development

No branches or pull requests

4 participants