First post here, not sure if this belongs in here or in the Explain topic. I’ve also tried googling this issue but I haven’t found anything helpful.
I’m trying to use this blog to create a parser but I’m running into issues with comptime and const. The blog is old and made for an older version of Zig so it not working out of the box is expected but I get confused by the errors.
The code consists of a parser interface
const std = @import("std");
const Allocator = std.mem.Allocator;
pub const Error = error{
EndOfStream,
Utf8InvalidStartByte,
} || std.fs.File.ReadError || std.fs.File.SeekError || std.mem.Allocator.Error;
pub fn Parser(comptime Value: type, comptime Reader: type) type {
return struct {
const Self = @This();
_parse: fn (self: *Self, allocator: *Allocator, src: *Reader) callconv(.Inline) Error!?Value,
pub inline fn parse(self: *Self, allocator: *Allocator, src: *Reader) Error!?Value {
return self._parse(self, allocator, src);
}
};
}
pub fn OneOf(comptime Value: type, comptime Reader: type) type {
return struct {
parser: Parser(Value, Reader) = .{
._parse = parse,
},
parsers: []*Parser(Value, Reader),
const Self = @This();
// `parsers` slice must stay alive for as long as the parser will be
// used.
pub fn init(parsers: []*Parser(Value, Reader)) Self {
return Self{
.parsers = parsers,
};
}
// Caller is responsible for freeing the value, if any.
inline fn parse(parser: *Parser(Value, Reader), allocator: *Allocator, src: *Reader) Error!?Value {
const self: Self = @fieldParentPtr("parser", parser);
for (self.parsers) |one_of_parser| {
const result = try one_of_parser.parse(allocator, src);
if (result != null) {
return result;
}
}
return null;
}
};
}
And an implementation of a literal parser
const std = @import("std");
const Allocator = std.mem.Allocator;
const Parser = @import("parser.zig").Parser;
const OneOf = @import("parser.zig").OneOf;
const Error = @import("parser.zig").Error;
pub fn Literal(comptime Reader: type) type {
return struct {
parser: Parser([]u8, Reader) = .{
._parse = parse,
},
want: []const u8,
const Self = @This();
pub fn init(want: []const u8) Self {
return Self{ .want = want };
}
inline fn parse(parser: *Parser([]u8, Reader), allocator: *Allocator, src: *Reader) Error!?[]u8 {
const self: Self = @fieldParentPtr("parser", parser);
const buf = try allocator.alloc(u8, self.want.len);
const read = try src.reader().readAll(buf);
if (read < self.want.len or !std.mem.eql(u8, buf, self.want)) {
try src.seekableStream().seekBy(-@as(i64, @intCast(read)));
allocator.free(buf);
return null;
}
return buf;
}
};
}
test "literal" {
const allocator = std.testing.allocator;
var reader = std.io.fixedBufferStream("abcdef");
const want: []const u8 = "abc";
var literal = Literal(@TypeOf(reader)).init(want);
const p = &literal.parser;
const result = try p.parse(allocator, &reader);
std.testing.expectEqualStrings(want, result.?);
if (result) |r| {
allocator.free(r);
}
}
test "oneof_literal" {
const allocator = std.testing.allocator;
var reader = std.io.fixedBufferStream("catdogsheep");
// Define our parser.
var one_of = OneOf([]u8, @TypeOf(reader)).init(&.{
&Literal(@TypeOf(reader)).init("dog").parser,
&Literal(@TypeOf(reader)).init("sheep").parser,
&Literal(@TypeOf(reader)).init("cat").parser,
});
var p = &one_of.parser;
// Parse!
const result = try p.parse(allocator, &reader);
std.testing.expectEqualStrings("cat", result.?);
if (result) |r| {
allocator.free(r);
}
}
Note that I’ve updated the code so that @fieldParentPtr
doesn’t error.
Here I get the following compilation error:
test
└─ run test
└─ zig test Debug native 2 errors
src/syntax.zig:38:9: error: variable of type 'syntax.Literal(io.fixed_buffer_stream.FixedBufferStream([]const u8))' must be const or comptime
var literal = Literal(@TypeOf(reader)).init(want);
^~~~~~~
src/syntax.zig:9:23: note: struct requires comptime because of this field
parser: Parser([]u8, Reader) = .{
~~~~~~^~~~~~~~~~~~~~
src/parser.zig:12:17: note: struct requires comptime because of this field
_parse: fn (self: *Self, allocator: *Allocator, src: *Reader) callconv(.Inline) Error!?Value,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/parser.zig:12:17: note: use '*const fn (comptime *parser.Parser([]u8,io.fixed_buffer_stream.FixedBufferStream([]const u8)), *mem.Allocator, *io.fixed_buffer_stream.FixedBufferStream([]const u8)) callconv(.Inline) error{EndOfSt
ream,Utf8InvalidStartByte,InputOutput,SystemResources,IsDir,OperationAborted,BrokenPipe,ConnectionResetByPeer,ConnectionTimedOut,NotOpenForReading,SocketNotConnected,WouldBlock,AccessDenied,Unexpected,Unseekable,OutOfMemory}!?[]u8
' for a function pointer type
_parse: fn (self: *Self, allocator: *Allocator, src: *Reader) callconv(.Inline) Error!?Value,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/syntax.zig:53:9: error: expected type '*parser.Parser([]u8,io.fixed_buffer_stream.FixedBufferStream([]const u8))', found '*const parser.Parser([]u8,io.fixed_buffer_stream.FixedBufferStream([]const u8))'
&Literal(@TypeOf(reader)).init("dog").parser,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/syntax.zig:53:9: note: cast discards const qualifier
If I follow the note and change the type annotation of _parse
to be *const fn...
I get an error saying that the assignment of ._parse = parse
in the Literal
parser is wrong because the first argument is not a comptime pointer to a function
Log here
test
└─ run test
└─ zig test Debug native 1 errors
src/syntax.zig:10:14: error: expected type '*const fn (*parser.Parser([]u8,io.fixed_buffer_stream.FixedBufferStream([]const u8)), *mem.Allocator, *io.fixed_buffer_stream.FixedBufferStream([]const u8)) callconv(.Inline) error{EndOf
Stream,Utf8InvalidStartByte,InputOutput,SystemResources,IsDir,OperationAborted,BrokenPipe,ConnectionResetByPeer,ConnectionTimedOut,NotOpenForReading,SocketNotConnected,WouldBlock,AccessDenied,Unexpected,Unseekable,OutOfMemory}!?[]
u8', found '*const fn (comptime *parser.Parser([]u8,io.fixed_buffer_stream.FixedBufferStream([]const u8)), *mem.Allocator, *io.fixed_buffer_stream.FixedBufferStream([]const u8)) callconv(.Inline) error{EndOfStream,Utf8InvalidStart
Byte,InputOutput,SystemResources,IsDir,OperationAborted,BrokenPipe,ConnectionResetByPeer,ConnectionTimedOut,NotOpenForReading,SocketNotConnected,WouldBlock,AccessDenied,Unexpected,Unseekable,OutOfMemory}!?[]u8'
._parse = parse,
~^~~~~~~~~~~~~~
src/syntax.zig:10:14: note: pointer type child 'fn (comptime *parser.Parser([]u8,io.fixed_buffer_stream.FixedBufferStream([]const u8)), *mem.Allocator, *io.fixed_buffer_stream.FixedBufferStream([]const u8)) callconv(.Inline) error
{EndOfStream,Utf8InvalidStartByte,InputOutput,SystemResources,IsDir,OperationAborted,BrokenPipe,ConnectionResetByPeer,ConnectionTimedOut,NotOpenForReading,SocketNotConnected,WouldBlock,AccessDenied,Unexpected,Unseekable,OutOfMemor
y}!?[]u8' cannot cast into pointer type child 'fn (*parser.Parser([]u8,io.fixed_buffer_stream.FixedBufferStream([]const u8)), *mem.Allocator, *io.fixed_buffer_stream.FixedBufferStream([]const u8)) callconv(.Inline) error{EndOfStre
am,Utf8InvalidStartByte,InputOutput,SystemResources,IsDir,OperationAborted,BrokenPipe,ConnectionResetByPeer,ConnectionTimedOut,NotOpenForReading,SocketNotConnected,WouldBlock,AccessDenied,Unexpected,Unseekable,OutOfMemory}!?[]u8'
src/syntax.zig:10:14: note: generic function cannot cast into a non-generic function
referenced by:
Self: src/syntax.zig:14:22
init: src/syntax.zig:17:20
And if I instead change the var literal = ...
declaration to be const literal = ...
I get an error saying that p.parse
got a constant pointer where it expected a non-const pointer
Log here
test
└─ run test
└─ zig test Debug native 1 errors
src/syntax.zig:40:25: error: expected type '*parser.Parser([]u8,io.fixed_buffer_stream.FixedBufferStream([]const u8))', found '*const parser.Parser([]u8,io.fixed_buffer_stream.FixedBufferStream([]const u8))'
const result = try p.parse(allocator, &reader);
~^~~~~~
src/syntax.zig:40:25: note: cast discards const qualifier
src/parser.zig:14:35: note: parameter type declared here
pub inline fn parse(self: *Self, allocator: *Allocator, src: *Reader) Error!?Value {
^~~~~
The suggestion to make the first argument a const pointer will lead to an error like the first one.
I don’t really understand at all what’s going on here and whenever I try to implement the suggestions from the compiler I just a more cryptic error. Why is it complaining about the constness when it seems to me like most things are defined at comptime? I’ve just started trying Zig so I’m sure there’s something fundamental that I’m not understanding yet.