1
|
'use strict';
|
2
|
|
3
|
var use = require('use');
|
4
|
var define = require('define-property');
|
5
|
var debug = require('debug')('snapdragon:compiler');
|
6
|
var utils = require('./utils');
|
7
|
|
8
|
/**
|
9
|
* Create a new `Compiler` with the given `options`.
|
10
|
* @param {Object} `options`
|
11
|
*/
|
12
|
|
13
|
function Compiler(options, state) {
|
14
|
debug('initializing', __filename);
|
15
|
this.options = utils.extend({source: 'string'}, options);
|
16
|
this.state = state || {};
|
17
|
this.compilers = {};
|
18
|
this.output = '';
|
19
|
this.set('eos', function(node) {
|
20
|
return this.emit(node.val, node);
|
21
|
});
|
22
|
this.set('noop', function(node) {
|
23
|
return this.emit(node.val, node);
|
24
|
});
|
25
|
this.set('bos', function(node) {
|
26
|
return this.emit(node.val, node);
|
27
|
});
|
28
|
use(this);
|
29
|
}
|
30
|
|
31
|
/**
|
32
|
* Prototype methods
|
33
|
*/
|
34
|
|
35
|
Compiler.prototype = {
|
36
|
|
37
|
/**
|
38
|
* Throw an error message with details including the cursor position.
|
39
|
* @param {String} `msg` Message to use in the Error.
|
40
|
*/
|
41
|
|
42
|
error: function(msg, node) {
|
43
|
var pos = node.position || {start: {column: 0}};
|
44
|
var message = this.options.source + ' column:' + pos.start.column + ': ' + msg;
|
45
|
|
46
|
var err = new Error(message);
|
47
|
err.reason = msg;
|
48
|
err.column = pos.start.column;
|
49
|
err.source = this.pattern;
|
50
|
|
51
|
if (this.options.silent) {
|
52
|
this.errors.push(err);
|
53
|
} else {
|
54
|
throw err;
|
55
|
}
|
56
|
},
|
57
|
|
58
|
/**
|
59
|
* Define a non-enumberable property on the `Compiler` instance.
|
60
|
*
|
61
|
* ```js
|
62
|
* compiler.define('foo', 'bar');
|
63
|
* ```
|
64
|
* @name .define
|
65
|
* @param {String} `key` propery name
|
66
|
* @param {any} `val` property value
|
67
|
* @return {Object} Returns the Compiler instance for chaining.
|
68
|
* @api public
|
69
|
*/
|
70
|
|
71
|
define: function(key, val) {
|
72
|
define(this, key, val);
|
73
|
return this;
|
74
|
},
|
75
|
|
76
|
/**
|
77
|
* Emit `node.val`
|
78
|
*/
|
79
|
|
80
|
emit: function(str, node) {
|
81
|
this.output += str;
|
82
|
return str;
|
83
|
},
|
84
|
|
85
|
/**
|
86
|
* Add a compiler `fn` with the given `name`
|
87
|
*/
|
88
|
|
89
|
set: function(name, fn) {
|
90
|
this.compilers[name] = fn;
|
91
|
return this;
|
92
|
},
|
93
|
|
94
|
/**
|
95
|
* Get compiler `name`.
|
96
|
*/
|
97
|
|
98
|
get: function(name) {
|
99
|
return this.compilers[name];
|
100
|
},
|
101
|
|
102
|
/**
|
103
|
* Get the previous AST node.
|
104
|
*/
|
105
|
|
106
|
prev: function(n) {
|
107
|
return this.ast.nodes[this.idx - (n || 1)] || { type: 'bos', val: '' };
|
108
|
},
|
109
|
|
110
|
/**
|
111
|
* Get the next AST node.
|
112
|
*/
|
113
|
|
114
|
next: function(n) {
|
115
|
return this.ast.nodes[this.idx + (n || 1)] || { type: 'eos', val: '' };
|
116
|
},
|
117
|
|
118
|
/**
|
119
|
* Visit `node`.
|
120
|
*/
|
121
|
|
122
|
visit: function(node, nodes, i) {
|
123
|
var fn = this.compilers[node.type];
|
124
|
this.idx = i;
|
125
|
|
126
|
if (typeof fn !== 'function') {
|
127
|
throw this.error('compiler "' + node.type + '" is not registered', node);
|
128
|
}
|
129
|
return fn.call(this, node, nodes, i);
|
130
|
},
|
131
|
|
132
|
/**
|
133
|
* Map visit over array of `nodes`.
|
134
|
*/
|
135
|
|
136
|
mapVisit: function(nodes) {
|
137
|
if (!Array.isArray(nodes)) {
|
138
|
throw new TypeError('expected an array');
|
139
|
}
|
140
|
var len = nodes.length;
|
141
|
var idx = -1;
|
142
|
while (++idx < len) {
|
143
|
this.visit(nodes[idx], nodes, idx);
|
144
|
}
|
145
|
return this;
|
146
|
},
|
147
|
|
148
|
/**
|
149
|
* Compile `ast`.
|
150
|
*/
|
151
|
|
152
|
compile: function(ast, options) {
|
153
|
var opts = utils.extend({}, this.options, options);
|
154
|
this.ast = ast;
|
155
|
this.parsingErrors = this.ast.errors;
|
156
|
this.output = '';
|
157
|
|
158
|
// source map support
|
159
|
if (opts.sourcemap) {
|
160
|
var sourcemaps = require('./source-maps');
|
161
|
sourcemaps(this);
|
162
|
this.mapVisit(this.ast.nodes);
|
163
|
this.applySourceMaps();
|
164
|
this.map = opts.sourcemap === 'generator' ? this.map : this.map.toJSON();
|
165
|
return this;
|
166
|
}
|
167
|
|
168
|
this.mapVisit(this.ast.nodes);
|
169
|
return this;
|
170
|
}
|
171
|
};
|
172
|
|
173
|
/**
|
174
|
* Expose `Compiler`
|
175
|
*/
|
176
|
|
177
|
module.exports = Compiler;
|