| '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; |