From 6607a88478812c9e37537da9eb6b0e4a5412bfe4 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 23 Oct 2014 22:34:07 -0700 Subject: inlines.js: Implemented stack-based emph parsing. --- js/lib/inlines.js | 135 +++++++++++++++++++++++++----------------------------- 1 file changed, 62 insertions(+), 73 deletions(-) (limited to 'js') diff --git a/js/lib/inlines.js b/js/lib/inlines.js index 34f1560..4d73d3e 100644 --- a/js/lib/inlines.js +++ b/js/lib/inlines.js @@ -262,93 +262,82 @@ var Str = function(s) { // Attempt to parse emphasis or strong emphasis. var parseEmphasis = function(cc,inlines) { var startpos = this.pos; - var c ; - var first_close = 0; - c = fromCodePoint(cc); - var numdelims; - var numclosedelims; - var delimpos; - - // Get opening delimiters. - res = this.scanDelims(cc); - numdelims = res.numdelims; + var res = this.scanDelims(cc); + var numdelims = res.numdelims; if (numdelims === 0) { this.pos = startpos; return false; } - if (numdelims >= 4 || !res.can_open) { - this.pos += numdelims; - inlines.push(Str(this.subject.slice(startpos, startpos + numdelims))); - return true; - } + if (res.can_close) { - this.pos += numdelims; + // Walk the stack and find a matching opener, if possible + var i = this.emph_stack.length - 1; + while (i >= 0) { - var delims_to_match = numdelims; - - var current = []; - var firstend; - var firstpos; - var state = 0; - var can_close = false; - var can_open = false; - var last_emphasis_closer = null; - while (this.last_emphasis_closer[c] >= this.pos) { - res = this.scanDelims(cc); - numclosedelims = res.numdelims; - - if (res.can_close) { - if (last_emphasis_closer === null || - last_emphasis_closer < this.pos) { - last_emphasis_closer = this.pos; - } - if (numclosedelims === 3 && delims_to_match === 3) { - delims_to_match -= 3; - this.pos += 3; - current = [{t: 'Strong', c: [{t: 'Emph', c: current}]}]; - } else if (numclosedelims >= 2 && delims_to_match >= 2) { - delims_to_match -= 2; - this.pos += 2; - firstend = current.length; - firstpos = this.pos; - current = [{t: 'Strong', c: current}]; - } else if (numclosedelims >= 1 && delims_to_match >= 1) { - delims_to_match -= 1; - this.pos += 1; - firstend = current.length; - firstpos = this.pos; - current = [{t: 'Emph', c: current}]; - } else { - if (!(this.parseInline(current,true))) { - break; - } - } - if (delims_to_match === 0) { - Array.prototype.push.apply(inlines, current); - return true; + var opener = this.emph_stack[i]; + if (opener.cc === cc) { // we have a match! + + if (opener.numdelims <= numdelims) { // all openers used + + this.pos += opener.numdelims; + var X; + switch (opener.numdelims) { + case 3: + X = function(x) { return Strong([Emph(x)]); }; + break; + case 2: + X = Strong; + break; + case 1: + default: + X = Emph; + break; } - } else if (!(this.parseInline(current,true))) { - break; + inlines[opener.pos] = X(inlines.slice(opener.pos + 1)); + inlines.splice(opener.pos + 1, inlines.length - (opener.pos + 1)); + // Remove entries after this, to prevent overlapping nesting: + this.emph_stack.splice(i, this.emph_stack.length - i); + return true; + + } else if (opener.numdelims > numdelims) { // only some openers used + + this.pos += numdelims; + opener.numdelims -= numdelims; + inlines[opener.pos].c = + inlines[opener.pos].c.slice(0, opener.numdelims); + var X = numdelims === 2 ? Strong : Emph; + inlines[opener.pos + 1] = X(inlines.slice(opener.pos + 1)); + inlines.splice(opener.pos + 2, inlines.length - (opener.pos + 2)); + // Remove entries after this, to prevent overlapping nesting: + this.emph_stack.splice(i + 1, this.emph_stack.length - (i + 1)); + return true; + + } + } + i--; + } } - // we didn't match emphasis: fallback - inlines.push(Str(this.subject.slice(startpos, - startpos + delims_to_match))); - if (delims_to_match < numdelims) { - Array.prototype.push.apply(inlines, current.slice(0,firstend)); - this.pos = firstpos; - } else { // delims_to_match === numdelims - this.pos = startpos + delims_to_match; - } + // If we're here, we didn't match a closer. + + this.pos += numdelims; + inlines.push(Str(this.subject.slice(startpos, startpos + numdelims))); + + if (res.can_open) { + + // Add entry to stack for this opener + this.emph_stack.push({ cc: cc, + numdelims: numdelims, + pos: inlines.length - 1 }); - if (last_emphasis_closer) { - this.last_emphasis_closer[c] = last_emphasis_closer; } + return true; + }; // Attempt to parse link title (sans quotes), returning the string @@ -696,7 +685,7 @@ var parseInlines = function(s, refmap) { this.pos = 0; this.refmap = refmap || {}; this.memo = {}; - this.last_emphasis_closer = { '*': s.length, '_': s.length }; + this.emph_stack = []; var inlines = []; while (this.parseInline(inlines, false)) { } @@ -708,7 +697,7 @@ function InlineParser(){ return { subject: '', label_nest_level: 0, // used by parseLinkLabel method - last_emphasis_closer: null, // used by parseEmphasis method + emph_stack: [], // used by parseEmphasis method pos: 0, refmap: {}, memo: {}, -- cgit v1.2.3