compiler.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. 'use strict';
  2. var use = require('use');
  3. var define = require('define-property');
  4. var debug = require('debug')('snapdragon:compiler');
  5. var utils = require('./utils');
  6. /**
  7. * Create a new `Compiler` with the given `options`.
  8. * @param {Object} `options`
  9. */
  10. function Compiler(options, state) {
  11. debug('initializing', __filename);
  12. this.options = utils.extend({source: 'string'}, options);
  13. this.state = state || {};
  14. this.compilers = {};
  15. this.output = '';
  16. this.set('eos', function(node) {
  17. return this.emit(node.val, node);
  18. });
  19. this.set('noop', function(node) {
  20. return this.emit(node.val, node);
  21. });
  22. this.set('bos', function(node) {
  23. return this.emit(node.val, node);
  24. });
  25. use(this);
  26. }
  27. /**
  28. * Prototype methods
  29. */
  30. Compiler.prototype = {
  31. /**
  32. * Throw an error message with details including the cursor position.
  33. * @param {String} `msg` Message to use in the Error.
  34. */
  35. error: function(msg, node) {
  36. var pos = node.position || {start: {column: 0}};
  37. var message = this.options.source + ' column:' + pos.start.column + ': ' + msg;
  38. var err = new Error(message);
  39. err.reason = msg;
  40. err.column = pos.start.column;
  41. err.source = this.pattern;
  42. if (this.options.silent) {
  43. this.errors.push(err);
  44. } else {
  45. throw err;
  46. }
  47. },
  48. /**
  49. * Define a non-enumberable property on the `Compiler` instance.
  50. *
  51. * ```js
  52. * compiler.define('foo', 'bar');
  53. * ```
  54. * @name .define
  55. * @param {String} `key` propery name
  56. * @param {any} `val` property value
  57. * @return {Object} Returns the Compiler instance for chaining.
  58. * @api public
  59. */
  60. define: function(key, val) {
  61. define(this, key, val);
  62. return this;
  63. },
  64. /**
  65. * Emit `node.val`
  66. */
  67. emit: function(str, node) {
  68. this.output += str;
  69. return str;
  70. },
  71. /**
  72. * Add a compiler `fn` with the given `name`
  73. */
  74. set: function(name, fn) {
  75. this.compilers[name] = fn;
  76. return this;
  77. },
  78. /**
  79. * Get compiler `name`.
  80. */
  81. get: function(name) {
  82. return this.compilers[name];
  83. },
  84. /**
  85. * Get the previous AST node.
  86. */
  87. prev: function(n) {
  88. return this.ast.nodes[this.idx - (n || 1)] || { type: 'bos', val: '' };
  89. },
  90. /**
  91. * Get the next AST node.
  92. */
  93. next: function(n) {
  94. return this.ast.nodes[this.idx + (n || 1)] || { type: 'eos', val: '' };
  95. },
  96. /**
  97. * Visit `node`.
  98. */
  99. visit: function(node, nodes, i) {
  100. var fn = this.compilers[node.type];
  101. this.idx = i;
  102. if (typeof fn !== 'function') {
  103. throw this.error('compiler "' + node.type + '" is not registered', node);
  104. }
  105. return fn.call(this, node, nodes, i);
  106. },
  107. /**
  108. * Map visit over array of `nodes`.
  109. */
  110. mapVisit: function(nodes) {
  111. if (!Array.isArray(nodes)) {
  112. throw new TypeError('expected an array');
  113. }
  114. var len = nodes.length;
  115. var idx = -1;
  116. while (++idx < len) {
  117. this.visit(nodes[idx], nodes, idx);
  118. }
  119. return this;
  120. },
  121. /**
  122. * Compile `ast`.
  123. */
  124. compile: function(ast, options) {
  125. var opts = utils.extend({}, this.options, options);
  126. this.ast = ast;
  127. this.parsingErrors = this.ast.errors;
  128. this.output = '';
  129. // source map support
  130. if (opts.sourcemap) {
  131. var sourcemaps = require('./source-maps');
  132. sourcemaps(this);
  133. this.mapVisit(this.ast.nodes);
  134. this.applySourceMaps();
  135. this.map = opts.sourcemap === 'generator' ? this.map : this.map.toJSON();
  136. return this;
  137. }
  138. this.mapVisit(this.ast.nodes);
  139. return this;
  140. }
  141. };
  142. /**
  143. * Expose `Compiler`
  144. */
  145. module.exports = Compiler;