File: .verb.md

package info (click to toggle)
node-snapdragon 0.12.0%2Brepack-4
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 708 kB
  • sloc: javascript: 3,478; makefile: 3
file content (205 lines) | stat: -rw-r--r-- 5,330 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
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`