Changes
I was reading the parser code and saw a few things that could improve the performance, they are summarized below.
Use integers constants for states
I saw that the state constants where strings. I thought that it could be a quick gain to replace them with named integer constants. The benchmark results I get are that there is a ~20% improvement to parse() by doing so.
Avoid allocation
While I was looking for other improvements, I also noticed that the token variable was assigned a new object everytime, I therefore created an object tokenRef, which is updated every time token needs to be assigned. I couldn't get a statistically significant improvement here, results were either 1% better or equal. But I've included it anyway because removing allocations alleviates the job of the garbage collector, which is something that's hard to benchmark but always nice to have.
Use codepoints instead of chars
Similarly to the first point, I thought it could be faster to use (integer) codepoints instead of chars. I ran the code through some code to get new code:
newCode = fs.readFileSync('./lib/parse.js').toString() .replaceAll(/'(\\\w|.|\\u....)'/g, (m) => `${eval(m).codePointAt(0)} /* ${m} */`)
And made sure functions were adapted to receive codepoints instead of chars. This last change had a bigger impact, I would say about 70-80% improvement over the previous one.
Summary
The changes above end up giving an improvement of around 100% compared to the original version. All three of them are independant of each other, so you could choose to pick some of them only. If this PR seems too dauting to read in one go, each commit is complete and represents one change.
The benchmark code: https://gist.github.com/romgrk/eb4a2a16422e50bc37e811c934a66f8f
Small file:
$ node benchmark.js ./package.json5 parseOriginal x 4,210 ops/sec ±0.93% (95 runs sampled) parseImproved x 8,285 ops/sec ±0.92% (94 runs sampled) Fastest is parseImproved
Large file:
$ node benchmark.js ./data.json5 parseOriginal x 255 ops/sec ±0.31% (92 runs sampled) parseImproved x 544 ops/sec ±0.78% (96 runs sampled) Fastest is parseImproved # `data.json5` is `package.json5` pasted a bunch of times in an array, with a few # added values to hit the numbers & booleans codepaths