178 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			178 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								'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;
							 |