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