diff --git a/lib/lexer_block.js b/lib/lexer_block.js index 703751c..e39ae5a 100644 --- a/lib/lexer_block.js +++ b/lib/lexer_block.js @@ -4,6 +4,7 @@ 'use strict'; +var State = require('./lexer_block/state_block'); var skipEmptyLines = require('./helpers').skipEmptyLines; var isEmpty = require('./helpers').isEmpty; @@ -160,4 +161,52 @@ LexerBlock.prototype.tokenize = function (state, startLine, endLine, stopOnTwoNe }; +LexerBlock.prototype.parse = function (src, options) { + var state, lineStart = 0, lastTabPos = 0; + + if (!src) { return ''; } + + if (src.indexOf('\r') >= 0) { + src = src.replace(/\r/, ''); + } + + if (src.indexOf('\u00a0') >= 0) { + src = src.replace(/\u00a0/g, ' '); + } + + if (src.indexOf('\u2424') >= 0) { + src = src.replace(/\u2424/g, '\n'); + } + + // TODO: benchmark it + // Replace tabs with proper number of spaces (1..4) + if (src.indexOf('\t') >= 0) { + src = src.replace(/[\n\t]/g, function (match, offset) { + var result; + if (src.charCodeAt(offset) === 0x0A) { + lineStart = offset + 1; + lastTabPos = 0; + return match; + } + result = ' '.slice((offset - lineStart - lastTabPos) % 4); + lastTabPos = offset - lineStart + 1; + return result; + }); + } + + + state = new State( + src, + this, + [], + options + ); + + this.tokenize(state, state.line, state.lineMax); + + return state.tokens; + +}; + + module.exports = LexerBlock; diff --git a/lib/state.js b/lib/lexer_block/state_block.js similarity index 93% rename from lib/state.js rename to lib/lexer_block/state_block.js index 80a3710..0bd878a 100644 --- a/lib/state.js +++ b/lib/lexer_block/state_block.js @@ -3,7 +3,7 @@ 'use strict'; -function State(src, lexerBlock, lexerInline, renderer, tokens, options) { +function State(src, lexerBlock, tokens, options) { var ch, s, start, pos, len, indent, indent_found; // TODO: check if we can move string replaces to parser, to avoid @@ -19,8 +19,6 @@ function State(src, lexerBlock, lexerInline, renderer, tokens, options) { // Shortcuts to simplify nested calls this.lexerBlock = lexerBlock; - this.lexerInline = lexerInline; - this.renderer = renderer; // TODO: (?) set directly for faster access. this.options = options; @@ -103,8 +101,6 @@ State.prototype.clone = function clone(src) { return new State( src, this.lexerBlock, - this.lexerInline, - this.renderer, this.tokens, this.options ); diff --git a/lib/lexer_block/table.js b/lib/lexer_block/table.js index eeb1576..a8198bc 100644 --- a/lib/lexer_block/table.js +++ b/lib/lexer_block/table.js @@ -12,7 +12,7 @@ function lineMatch(state, line, reg) { module.exports = function table(state, startLine, endLine, silent) { - var ch, firstLineMatch, secondLineMatch, i, subState, nextLine, m, rows, + var ch, firstLineMatch, secondLineMatch, i, nextLine, m, rows, aligns, t; // should have at least three lines @@ -48,12 +48,14 @@ module.exports = function table(state, startLine, endLine, silent) { if (silent) { return true; } state.tokens.push({ type: 'table_open' }); - state.tokens.push({ type: 'tr_open' }); + state.tokens.push({ type: 'tr_open' }); for (i = 0; i < rows.length; i++) { state.tokens.push({ type: 'th_open', align: aligns[i] }); - subState = state.clone(rows[i].trim()); - state.lexerInline.tokenize(subState, subState.src); + state.tokens.push({ + type: 'inline', + content: rows[i].trim() + }); state.tokens.push({ type: 'th_close' }); } state.tokens.push({ type: 'tr_close' }); @@ -66,8 +68,10 @@ module.exports = function table(state, startLine, endLine, silent) { state.tokens.push({ type: 'tr_open' }); for (i = 0; i < rows.length; i++) { state.tokens.push({ type: 'td_open', align: aligns[i] }); - subState = state.clone(rows[i].replace(/^\|? *| *\|?$/g, '')); - state.lexerInline.tokenize(subState, subState.src); + state.tokens.push({ + type: 'inline', + content: rows[i].replace(/^\|? *| *\|?$/g, '') + }); state.tokens.push({ type: 'td_close' }); } state.tokens.push({ type: 'tr_close' }); diff --git a/lib/lexer_inline.js b/lib/lexer_inline.js index f48566d..856776d 100644 --- a/lib/lexer_inline.js +++ b/lib/lexer_inline.js @@ -3,7 +3,7 @@ 'use strict'; -var StateInline = require('./state_inline'); +var StateInline = require('./lexer_inline/state_inline'); //////////////////////////////////////////////////////////////////////////////// // Lexer rules @@ -143,7 +143,7 @@ LexerInline.prototype.tokenize = function (state) { } if (state.pending) { - state.pushText(); + state.pushPending(); } return state.tokens; diff --git a/lib/state_inline.js b/lib/lexer_inline/state_inline.js similarity index 87% rename from lib/state_inline.js rename to lib/lexer_inline/state_inline.js index ab840ad..66ddcc9 100644 --- a/lib/state_inline.js +++ b/lib/lexer_inline/state_inline.js @@ -14,7 +14,7 @@ function StateInline(src, lexer, options) { } -StateInline.prototype.pushText = function () { +StateInline.prototype.pushPending = function () { var pending = this.pending; this.tokens.push({ @@ -26,7 +26,7 @@ StateInline.prototype.pushText = function () { StateInline.prototype.push = function (token) { if (this.pending) { - this.pushText(); + this.pushPending(); } this.tokens.push(token); diff --git a/lib/parser.js b/lib/parser.js index c56136b..6433b52 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -6,7 +6,6 @@ var assign = require('object-assign'); -var State = require('./state'); var Renderer = require('./renderer'); var LexerBlock = require('./lexer_block'); var LexerInline = require('./lexer_inline'); @@ -32,62 +31,21 @@ Parser.prototype.set = function (options) { Parser.prototype.render = function (src) { - var state, lineStart = 0, lastTabPos = 0, tokens, i, l, t; - - if (!src) { return ''; } - - if (src.indexOf('\r') >= 0) { - src = src.replace(/\r/, ''); - } - - if (src.indexOf('\u00a0') >= 0) { - src = src.replace(/\u00a0/g, ' '); - } - - if (src.indexOf('\u2424') >= 0) { - src = src.replace(/\u2424/g, '\n'); - } - - // TODO: benchmark it - // Replace tabs with proper number of spaces (1..4) - if (src.indexOf('\t') >= 0) { - src = src.replace(/[\n\t]/g, function (match, offset) { - var result; - if (src.charCodeAt(offset) === 0x0A) { - lineStart = offset + 1; - lastTabPos = 0; - return match; - } - result = ' '.slice((offset - lineStart - lastTabPos) % 4); - lastTabPos = offset - lineStart + 1; - return result; - }); - } - - - state = new State( - src, - this.lexerBlock, - this.lexerInline, - this.renderer, - [], - this.options - ); + var tokens, tok, i, l; // Parse blocks - state.lexerBlock.tokenize(state, state.line, state.lineMax); + tokens = this.lexerBlock.parse(src, this.options); // Parse inlines - tokens = state.tokens; for (i = 0, l = tokens.length; i < l; i++) { - t = tokens[i]; - if (t.type === 'inline') { - t.children = state.lexerInline.parse(t.content, state.options); + tok = tokens[i]; + if (tok.type === 'inline') { + tok.children = this.lexerInline.parse(tok.content, this.options); } } // Render - return this.renderer.render(state.tokens, this.options); + return this.renderer.render(tokens, this.options); };