Created by [jonschlinkert]({%= author.url %}) and [doowb](https://github.com/doowb). **Features** - Bootstrap your own parser, get sourcemap support for free - All parsing and compiling is handled by simple, reusable middleware functions - Inspired by the parsers in [pug][] and [css][]. ## Quickstart example All of the examples in this document assume the following two lines of setup code exist first: ```js var Snapdragon = require('{%= name %}'); var snapdragon = new Snapdragon(); ``` **Parse a string** ```js var ast = snapdragon.parser // parser handlers (essentially middleware) // used for parsing substrings to create tokens .set('foo', function () {}) .set('bar', function () {}) .parse('some string', options); ``` **Compile an AST returned from `.parse()`** ```js var result = snapdragon.compiler // compiler handlers (essentially middleware), // called on a node when the `node.type` matches // the name of the handler .set('foo', function () {}) .set('bar', function () {}) // pass the `ast` from the parse method .compile(ast) // the compiled string console.log(result.output); ``` See the [examples](./examples/). ## Parsing **Parser handlers** Parser handlers are middleware functions responsible for matching substrings to create tokens: **Example handler** ```js var ast = snapdragon.parser .set('dot', function() { var pos = this.position(); var m = this.match(/^\./); if (!m) return; return pos({ // the "type" will be used by the compiler later on, // we'll go over this in the compiler docs type: 'dot', // "val" is the string captured by ".match", // in this case that would be '.' val: m[0] }); }) .parse('.'[, options]) ``` _As a side node, it's not scrictly required to set the `type` on the token, since the parser will add it to the token if it's undefined, based on the name of the handler. But it's good practice since tokens aren't always returned._ **Example token** And the resulting tokens look something like this: ```js { type: 'dot', val: '.' } ``` **Position** Next, `pos()` is called on the token as it's returned, which patches the token with the `position` of the string that was captured: ```js { type: 'dot', val: '.', position: { start: { lineno: 1, column: 1 }, end: { lineno: 1, column: 2 } }} ``` **Life as an AST node** When the token is returned, the parser pushes it onto the `nodes` array of the "previous" node (since we're in a tree, the "previous" node might be literally the last node that was created, or it might be the "parent" node inside a nested context, like when parsing brackets or something with an open or close), at which point the token begins its life as an AST node. **Wrapping up** In the parser calls all handlers and cannot find a match for a substring, an error is thrown. Assuming the parser finished parsing the entire string, an AST is returned. ## Compiling The compiler's job is to take the AST created by the [parser](#parsing) and convert it to a new string. It does this by iterating over each node on the AST and calling a function on the node based on its `type`. This function is called a "handler". **Compiler handlers** Handlers are _named_ middleware functions that are called on a node when `node.type` matches the name of a registered handler. ```js var result = snapdragon.compiler .set('dot', function (node) { console.log(node.val) //=> '.' return this.emit(node.val); }) ``` If `node.type` does not match a registered handler, an error is thrown. **Source maps** If you want source map support, make sure to emit the entire node as the second argument as well (this allows the compiler to get the `node.position`). ```js var res = snapdragon.compiler .set('dot', function (node) { return this.emit(node.val, node); }) ``` ## All together This is a very basic example, but it shows how to parse a dot, then compile it as an escaped dot. ```js var Snapdragon = require('..'); var snapdragon = new Snapdragon(); var ast = snapdragon.parser .set('dot', function () { var pos = this.position(); var m = this.match(/^\./); if (!m) return; return pos({ type: 'dot', val: m[0] }) }) .parse('.') var result = snapdragon.compiler .set('dot', function (node) { return this.emit('\\' + node.val); }) .compile(ast) console.log(result.output); //=> '\.' ``` ## API ### Parse {%= apidocs("lib/parser.js") %} ### Compile {%= apidocs("lib/compiler.js") %} ## Snapdragon in the wild {%= verb.related.description %} {%= related(verb.related.implementations) %} ## History ### v0.9.0 **Breaking changes!** In an attempt to make snapdragon lighter, more versatile, and more pluggable, some major changes were made in this release. - `parser.capture` was externalized to [snapdragon-capture][] - `parser.capturePair` was externalized to [snapdragon-capture-set][] - Nodes are now an instance of [snapdragon-node][] ### v0.5.0 **Breaking changes!** Substantial breaking changes were made in v0.5.0! Most of these changes are part of a larger refactor that will be finished in 0.6.0, including the introduction of a `Lexer` class. - Renderer was renamed to `Compiler` - the `.render` method was renamed to `.compile`