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.

I'm using PEG.js to create a parser to transform a simple schema specification into a formal JSON schema.

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 post 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 });