Crafting Interpreters was one of the first projects I did with zig, and it was super fun. I’ve started writing a tutorial to do the same project in zig. The aim is for people who want to learn zig to read this alongside the original book, so they can learn the interpreter theory and also how to implement it in zig instead of c.
I’ve posted the first draft for the intro + first chapter as one big unlisted blog post, and I’m linking it here so zig people can give feedback.
This is a first draft, so feedback about grammar/spelling is not what I’m looking for. “Higher level” advice or suggestions about how to make this better for zig beginners would be useful though.
If people think this would be a good resource I hope to continue writing and will probably update the code to 0.15 (or later) when it becomes stable.
I am confused how 24886 breaks your code, you’re not using an array list with a zero sized type. I did see in some later snippets u8 changed to OpCode which would be zero sized when it only has one variant, I don’t think you meant for that to happen.
I don’t think you meant to have Overall Curriculum Status: Pending in the code for constant instructions.
IMO this is already one of the better learning resources for zig, assuming you continue to cover more chapters.
You do a good job of explaining relevant language features succinctly and touching on others while relevant not yet important without going into too much detail.
Btw 0.15 should be released soon, all required issues have been resolved. Might as well start updating for writergate especially since the changes to your code so far would be minimal.
You’re right. I started with Opcode and switched to u8 later, so that bug is not relevant anymore.
Yup, accidental copy-paste.
Thank you! That’s encouraging.
Thanks. I didn’t want to give a full explanation of all the features in fear of it becoming too long and dense. Just a basic explanation which covers how a feature is being used and then a link to further documentation seems like a good approach.
Awesome! Looks like I have to get up to speed with that.
Thanks for crafting this guide. I started the official book earlier this year and love it. I look forward to using this for part two of the book.
When it comes to installing zig, have you heard of zig version manager? Its a thrid party tool that is written in zig and runs on every offical supported platform.
It makes installing the right zig version as a curl command and $ zvm i 0.14.1 for the version. This tool also makes it easy to swap between zig versions.
I’ve heard of several zig version managers but I’ve never used one myself. I might include some links to them in the ‘downloading zig’ section as an alternative.
Did you finish the book?
I ask because when it comes to GC, we’ll see that the compiler and the VM use the same allocator (function markCompilerRoots in the C version).
So the whole implementation uses exactly one allocator.
In my implementation (my first non-trivial Zig project) I had a hard time how to track memory allocation for triggering GC, which was caused by
Zig’s much more safer allocator API. I decided to store strings zero-terminated for safety when dealing with C native functions, so I ended up with three different pairs for allocation and freeing: one for single struts, one for normal arrays, and one for sentinel-terminated arrays. This was one (probably the only one) place where working with Zig felt harder than working with C.
And I’m looking forward how you solved it.
Another minor difficulty I had was dealing with the different-sied objects. But with the help of @fieldParentPtr I could solve it in a safe and quite straightforward way. I didn’t want to use a tagged union for the objects to avoid wasting memory.
In summary, I was mildly surprised how easy it was , given that I was using a new language.
And when something didn’t work, how helpful the stack traces in case of panics actually were.
The one thing I still hate in Zig is the absolutely not helpful error message when you mess up calls to std.debug.printf, eg forget putting .{ } around the arg if you only need one. I know this caused by the way Zig optimizes this at comp time, but it is still very confusing for beginners.
But in Germany we say “Das ist Jammern auf hohem Niveau!”.
(complaining at high level?)
Zig is my new favourite low-level language while Python will probably be my fav high-level language for the next decades.
I have not. I’ve actually only implemented about half of the bytecode interpreter myself, so I’ll need to spend some time figuring out the right approach before writing the later chapters. Thankfully there are already some zig implementations out there which could be useful.
I encourage you to go on with the idea of developing chapter-wise and then write it down in your blog.
It’s something I had planned, too, but then I just had not enough time for it.
Just one advice: I found, in later chapters, that it gets more and more difficult to see where to change what when your coding (naming conventions, patterns, file structure) differs from the book, because Bob only shows one or two lines of context around the changes.
I sometimes found it necessary to look at Bob’s complete (final) C code in the munificent repo at GH.
Which is in itself not easily readable because he used C comment lines for describing the development process. Just using different Git tags, chapters, one per chapter, would have been easier to understand and compare with one’s own code.