parser_inline.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /** internal
  2. * class ParserInline
  3. *
  4. * Tokenizes paragraph content.
  5. **/
  6. 'use strict';
  7. var Ruler = require('./ruler');
  8. ////////////////////////////////////////////////////////////////////////////////
  9. // Parser rules
  10. var _rules = [
  11. [ 'text', require('./rules_inline/text') ],
  12. [ 'newline', require('./rules_inline/newline') ],
  13. [ 'escape', require('./rules_inline/escape') ],
  14. [ 'backticks', require('./rules_inline/backticks') ],
  15. [ 'strikethrough', require('./rules_inline/strikethrough').tokenize ],
  16. [ 'emphasis', require('./rules_inline/emphasis').tokenize ],
  17. [ 'link', require('./rules_inline/link') ],
  18. [ 'image', require('./rules_inline/image') ],
  19. [ 'autolink', require('./rules_inline/autolink') ],
  20. [ 'html_inline', require('./rules_inline/html_inline') ],
  21. [ 'entity', require('./rules_inline/entity') ]
  22. ];
  23. var _rules2 = [
  24. [ 'balance_pairs', require('./rules_inline/balance_pairs') ],
  25. [ 'strikethrough', require('./rules_inline/strikethrough').postProcess ],
  26. [ 'emphasis', require('./rules_inline/emphasis').postProcess ],
  27. [ 'text_collapse', require('./rules_inline/text_collapse') ]
  28. ];
  29. /**
  30. * new ParserInline()
  31. **/
  32. function ParserInline() {
  33. var i;
  34. /**
  35. * ParserInline#ruler -> Ruler
  36. *
  37. * [[Ruler]] instance. Keep configuration of inline rules.
  38. **/
  39. this.ruler = new Ruler();
  40. for (i = 0; i < _rules.length; i++) {
  41. this.ruler.push(_rules[i][0], _rules[i][1]);
  42. }
  43. /**
  44. * ParserInline#ruler2 -> Ruler
  45. *
  46. * [[Ruler]] instance. Second ruler used for post-processing
  47. * (e.g. in emphasis-like rules).
  48. **/
  49. this.ruler2 = new Ruler();
  50. for (i = 0; i < _rules2.length; i++) {
  51. this.ruler2.push(_rules2[i][0], _rules2[i][1]);
  52. }
  53. }
  54. // Skip single token by running all rules in validation mode;
  55. // returns `true` if any rule reported success
  56. //
  57. ParserInline.prototype.skipToken = function (state) {
  58. var ok, i, pos = state.pos,
  59. rules = this.ruler.getRules(''),
  60. len = rules.length,
  61. maxNesting = state.md.options.maxNesting,
  62. cache = state.cache;
  63. if (typeof cache[pos] !== 'undefined') {
  64. state.pos = cache[pos];
  65. return;
  66. }
  67. if (state.level < maxNesting) {
  68. for (i = 0; i < len; i++) {
  69. // Increment state.level and decrement it later to limit recursion.
  70. // It's harmless to do here, because no tokens are created. But ideally,
  71. // we'd need a separate private state variable for this purpose.
  72. //
  73. state.level++;
  74. ok = rules[i](state, true);
  75. state.level--;
  76. if (ok) { break; }
  77. }
  78. } else {
  79. // Too much nesting, just skip until the end of the paragraph.
  80. //
  81. // NOTE: this will cause links to behave incorrectly in the following case,
  82. // when an amount of `[` is exactly equal to `maxNesting + 1`:
  83. //
  84. // [[[[[[[[[[[[[[[[[[[[[foo]()
  85. //
  86. // TODO: remove this workaround when CM standard will allow nested links
  87. // (we can replace it by preventing links from being parsed in
  88. // validation mode)
  89. //
  90. state.pos = state.posMax;
  91. }
  92. if (!ok) { state.pos++; }
  93. cache[pos] = state.pos;
  94. };
  95. // Generate tokens for input range
  96. //
  97. ParserInline.prototype.tokenize = function (state) {
  98. var ok, i,
  99. rules = this.ruler.getRules(''),
  100. len = rules.length,
  101. end = state.posMax,
  102. maxNesting = state.md.options.maxNesting;
  103. while (state.pos < end) {
  104. // Try all possible rules.
  105. // On success, rule should:
  106. //
  107. // - update `state.pos`
  108. // - update `state.tokens`
  109. // - return true
  110. if (state.level < maxNesting) {
  111. for (i = 0; i < len; i++) {
  112. ok = rules[i](state, false);
  113. if (ok) { break; }
  114. }
  115. }
  116. if (ok) {
  117. if (state.pos >= end) { break; }
  118. continue;
  119. }
  120. state.pending += state.src[state.pos++];
  121. }
  122. if (state.pending) {
  123. state.pushPending();
  124. }
  125. };
  126. /**
  127. * ParserInline.parse(str, md, env, outTokens)
  128. *
  129. * Process input string and push inline tokens into `outTokens`
  130. **/
  131. ParserInline.prototype.parse = function (str, md, env, outTokens) {
  132. var i, rules, len;
  133. var state = new this.State(str, md, env, outTokens);
  134. this.tokenize(state);
  135. rules = this.ruler2.getRules('');
  136. len = rules.length;
  137. for (i = 0; i < len; i++) {
  138. rules[i](state);
  139. }
  140. };
  141. ParserInline.prototype.State = require('./rules_inline/state_inline');
  142. module.exports = ParserInline;