You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
177 lines
3.5 KiB
177 lines
3.5 KiB
'use strict'; |
|
|
|
var use = require('use'); |
|
var define = require('define-property'); |
|
var debug = require('debug')('snapdragon:compiler'); |
|
var utils = require('./utils'); |
|
|
|
/** |
|
* Create a new `Compiler` with the given `options`. |
|
* @param {Object} `options` |
|
*/ |
|
|
|
function Compiler(options, state) { |
|
debug('initializing', __filename); |
|
this.options = utils.extend({source: 'string'}, options); |
|
this.state = state || {}; |
|
this.compilers = {}; |
|
this.output = ''; |
|
this.set('eos', function(node) { |
|
return this.emit(node.val, node); |
|
}); |
|
this.set('noop', function(node) { |
|
return this.emit(node.val, node); |
|
}); |
|
this.set('bos', function(node) { |
|
return this.emit(node.val, node); |
|
}); |
|
use(this); |
|
} |
|
|
|
/** |
|
* Prototype methods |
|
*/ |
|
|
|
Compiler.prototype = { |
|
|
|
/** |
|
* Throw an error message with details including the cursor position. |
|
* @param {String} `msg` Message to use in the Error. |
|
*/ |
|
|
|
error: function(msg, node) { |
|
var pos = node.position || {start: {column: 0}}; |
|
var message = this.options.source + ' column:' + pos.start.column + ': ' + msg; |
|
|
|
var err = new Error(message); |
|
err.reason = msg; |
|
err.column = pos.start.column; |
|
err.source = this.pattern; |
|
|
|
if (this.options.silent) { |
|
this.errors.push(err); |
|
} else { |
|
throw err; |
|
} |
|
}, |
|
|
|
/** |
|
* Define a non-enumberable property on the `Compiler` instance. |
|
* |
|
* ```js |
|
* compiler.define('foo', 'bar'); |
|
* ``` |
|
* @name .define |
|
* @param {String} `key` propery name |
|
* @param {any} `val` property value |
|
* @return {Object} Returns the Compiler instance for chaining. |
|
* @api public |
|
*/ |
|
|
|
define: function(key, val) { |
|
define(this, key, val); |
|
return this; |
|
}, |
|
|
|
/** |
|
* Emit `node.val` |
|
*/ |
|
|
|
emit: function(str, node) { |
|
this.output += str; |
|
return str; |
|
}, |
|
|
|
/** |
|
* Add a compiler `fn` with the given `name` |
|
*/ |
|
|
|
set: function(name, fn) { |
|
this.compilers[name] = fn; |
|
return this; |
|
}, |
|
|
|
/** |
|
* Get compiler `name`. |
|
*/ |
|
|
|
get: function(name) { |
|
return this.compilers[name]; |
|
}, |
|
|
|
/** |
|
* Get the previous AST node. |
|
*/ |
|
|
|
prev: function(n) { |
|
return this.ast.nodes[this.idx - (n || 1)] || { type: 'bos', val: '' }; |
|
}, |
|
|
|
/** |
|
* Get the next AST node. |
|
*/ |
|
|
|
next: function(n) { |
|
return this.ast.nodes[this.idx + (n || 1)] || { type: 'eos', val: '' }; |
|
}, |
|
|
|
/** |
|
* Visit `node`. |
|
*/ |
|
|
|
visit: function(node, nodes, i) { |
|
var fn = this.compilers[node.type]; |
|
this.idx = i; |
|
|
|
if (typeof fn !== 'function') { |
|
throw this.error('compiler "' + node.type + '" is not registered', node); |
|
} |
|
return fn.call(this, node, nodes, i); |
|
}, |
|
|
|
/** |
|
* Map visit over array of `nodes`. |
|
*/ |
|
|
|
mapVisit: function(nodes) { |
|
if (!Array.isArray(nodes)) { |
|
throw new TypeError('expected an array'); |
|
} |
|
var len = nodes.length; |
|
var idx = -1; |
|
while (++idx < len) { |
|
this.visit(nodes[idx], nodes, idx); |
|
} |
|
return this; |
|
}, |
|
|
|
/** |
|
* Compile `ast`. |
|
*/ |
|
|
|
compile: function(ast, options) { |
|
var opts = utils.extend({}, this.options, options); |
|
this.ast = ast; |
|
this.parsingErrors = this.ast.errors; |
|
this.output = ''; |
|
|
|
// source map support |
|
if (opts.sourcemap) { |
|
var sourcemaps = require('./source-maps'); |
|
sourcemaps(this); |
|
this.mapVisit(this.ast.nodes); |
|
this.applySourceMaps(); |
|
this.map = opts.sourcemap === 'generator' ? this.map : this.map.toJSON(); |
|
return this; |
|
} |
|
|
|
this.mapVisit(this.ast.nodes); |
|
return this; |
|
} |
|
}; |
|
|
|
/** |
|
* Expose `Compiler` |
|
*/ |
|
|
|
module.exports = Compiler;
|
|
|