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