I wrote a post exploring egg, a new tool that generates recursive descent parsers from Go-style EBNF. The most interesting feature is that it doesn't generate struct nodes; it encodes the AST into a flat []int32 slice for cache locality and reduced GC pressure. I included a walkthrough of generating a JSON parser with it.
Types with inner pointers add difficulty to be sure, but it’s still possible to use them with this pattern. You have to make sure of three things to do so: 1) no pointers outside of the backing memory; 2) an explicit “clear()” function that manually nulls out inner pointers in the stored object (even inner pointers to other things in the backing slice); 3) clear() is called for all such objects that were ever stored before the backing slice is dropped and before those objects are garbage collected.
> The page you linked compares golang bindings against each other, not C against golang like my test did
No one disputed that. But it follows that your benchmarks are comparing C to [some very] outdated versions of Go packages. Which is what I tried to point out.
That's an interesting result, thanks. I just calculated the geomean of all relations, which is 4.32. The original C code doesn't seem to make use of GCC-specific tricks such as computed gotos, so your experiment essentially demonstrates the optimization efficiency of the Go compiler compared to the C compiler. It would be interesting to use a more established benchmark suite like Are-we-fast-yet, which has a JS implementation.