Skip to content

Instantly share code, notes, and snippets.

@subfuzion
Last active March 6, 2021 12:56
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save subfuzion/808f91bfdfb88c7d5ed7 to your computer and use it in GitHub Desktop.
Save subfuzion/808f91bfdfb88c7d5ed7 to your computer and use it in GitHub Desktop.
Tracing with peg.js

PEG.js is a simple parser generator for JavaScript that produces fast parsers with excellent error reporting. You can use it to process complex data or computer languages and build transformers, interpreters, compilers and other tools easily.

PEG.js offers tracing support to help analyze parser issues with a grammar. The feature is very helpful, but it's not available yet on the version that's published to npm, it's not well-advertised, and not well-documented. This gist explains how to take advantage of PEG.js tracing support.

When you generate your parser, make sure you supply the trace option set to true. If using gulp, do something like this:

var peg = require('gulp-peg');

var paths = {
  peg: ['src/**/*.pegjs'],
  dist: 'dist'
};

var pegOptions = {
  trace: true
};

gulp.task('peg', ['clean'], function() {
  return gulp.src(paths.peg)
    .pipe(peg(pegOptions))
    .pipe(gulp.dest(paths.dist));
});

When calling the parse function of your parser, provide an argument for your tracer object:

parser.parse(text, { tracer: { trace: function(evt) {
   ...
}}});

Trace events objects have the following properties:

{
  "type": " (rule.enter | rule.fail | rule.match) ",
  "rule": " (the name of the rule being evaluated) ",
  "location": {
    "start": {
      "offset": 0,
      "line": 1,
      "column": 1
    },
    "end": {
      "offset": 0,
      "line": 1,
      "column": 1
    }
  }
}

Want your tracer object to behave like an EventEmitter? Provide a tracer that emits events like this:

var EventEmitter = require('events').EventEmitter;
var inherits = require('util').inherits;
var parser = require('./path-to-parser');

function Tracer() {
  EventEmitter.prototype.call(this);
}

inherits(Tracer, EventEmitter);

// tracer instances need to implement a `trace` method for peg
Tracer.prototype.trace = function(evt) {
  this.emit('parse', evt);
};

var tracer = new Tracer();
tracer.on('parse', function(evt) {
  // do something cool with the parse event
});

parser.parse(text, { tracer: tracer });
@BillyWM
Copy link

BillyWM commented Mar 13, 2019

Nice example, thanks. It is in fact present in 0.10.0 currently published to NPM, though as you said it's poorly documented, as are all of the error-handling features of PEG.js generally. This is a real shame, because one of the biggest arguments against parser generators in favor of hand-rolling your own RD parser (or combinator based parser, etc) is the difficulty of handling errors, which tends to be far inferior in available tools vs just inserting the handling into your own parsing code....

I was able to get a trace in 0.10 with a parser I built via the command-line tool; just remember to pass the --trace flag to enable it

(I also stumbled on a next.pegjs.org a few weeks ago but it was gone when I looked a few days later. The author seems to be actively working on improvements behind the scenes. Maybe clear docs are coming)

@localhostdotdev
Copy link

in js you need peg.generate(grammar, { trace: true }) to get the tracer, also https://github.com/okaxaki/pegjs-backtrace is pretty nice to see the backtrace

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment