bufPrint bug workaround

Hi guys!

I’m building a language parser and trying to make the resulting AST printable for inspection and testing. Each expression has a string() method, that uses std.fmt.bufPrint() method to represent the expression as a string. Everything is fine until I try to use return value of one bufPrint in another bufPrint. If there are 2 placeholders, it outputs some gibberish, and if I leave only one placeholder I get a panic.

Here is the code. The issue is in the .pref branch.

pub const Expression = union(enum) {
    ident: Identifier,
    int: Integer,
    pref: Prefix,
    inf: Infix,

    pub fn string(self: @This(), buf: []u8) ![]u8 {
        return switch (self) {
            .ident => |ident| bufPrint(buf, "{s}", .{ident.token.get_value().?}),
            .int => |int| bufPrint(buf, "{d}", .{int.value}),
            .pref => |pref| bufPrint(buf, "({s}{s})", .{
                @tagName(pref.token),
                try pref.right.string(buf),
            }),
            .inf => unreachable,
        };
    }
};

Given expressions
“!5” and “-15”
expected output:
“(bang5)” and “(minus15)”
got:
“(bang()” and “(minus(m)”

Panic on removing the first placeholder and value:
thread 322558 panic: @memcpy arguments alias
Unwind error at address exe:0x8051d33 (error.MissingFDE), trace may be incomplete

Looking at the following issue it seems to be a bug with bufPrint, so I don’t think I’m doing something wrong. The question is if there is some workaround to be able to continue using bufPrint, or is there a good alternative that can be used for this task.

Hi @Av1d Welcome to ziggit :slight_smile:

Here buf is both output and input.
There is no guarantee that bufPrint is not going to override the input stored in the output buffer.
To solve this you need different buffers.

2 Likes

I think I would define and use a format function instead and then just print the object in whatever way is appropriate. With the writer you don’t have to pre-decide whether you want to use a buffer or not and your code stays agnostic.
This post is a good description:

2 Likes

Thank you all! That was quite helpful. I’ve actually tried @dimdin 's solution before, but decided that it’s too hacky. Now though, it does not look too bad to me.

The std.fmt.format stuff is really interesting. Didn’t know Zig has such things in std. I’ll try to make use of it.

Appreciate the help. Cheers!

2 Likes