From fe48937cfaef36603a7ff779acfbb2314d1c93e6 Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Sun, 3 Aug 2014 10:46:34 -0700 Subject: [PATCH] Add Save() and SavedLiteral() matchers. Add badpenny grammar. --- static/grammars/badpenny.js | 47 ++++++++++++++ static/recentrunes.js | 125 +++++++++++++++++++++++++++++++++++- static/test.html | 1 + static/test.js | 20 ++++++ 4 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 static/grammars/badpenny.js diff --git a/static/grammars/badpenny.js b/static/grammars/badpenny.js new file mode 100644 index 0000000..86865b9 --- /dev/null +++ b/static/grammars/badpenny.js @@ -0,0 +1,47 @@ +badpenny = rr.Parser({ + 'container': rr.Node('container', + rr.Save('tagname', + rr.Sequence( + rr.Literal('{{('), + rr.SingleLineText(), + rr.Literal('}}')), + rr.Sequence( + rr.Literal('{{('), + rr.SavedLiteral('tagname'), + rr.Literal('}}'), + rr.MultiLineText(), + rr.Literal('{{)'), + rr.SavedLiteral('tagname'), + rr.Literal('}}')))), + + 'repeated': rr.Node('repeated', + rr.Save('tagname', + rr.Sequence( + rr.Literal('{{['), + rr.SingleLineText(), + rr.Literal('}}')), + rr.Sequence( + rr.Literal('{{['), + rr.SavedLiteral('tagname'), + rr.Literal('}}'), + rr.MultiLineText(), + rr.Literal('{{]'), + rr.SavedLiteral('tagname'), + rr.Literal('}}')))), + + 'value': rr.Node('value', rr.Sequence( + rr.Literal('{{'), + rr.SingleLineText(), + rr.Literal('}}'))), + + 'chunk': rr.Or( + rr.Ref('container'), + rr.Ref('repeated'), + rr.Ref('value'), + rr.MultiLineText()), + + 'main': rr.Node('badpenny', rr.Sequence( + rr.ZeroOrMore(rr.Ref('chunk')), + rr.EndOfText())) +}, [ +]); diff --git a/static/recentrunes.js b/static/recentrunes.js index 8931370..992b856 100644 --- a/static/recentrunes.js +++ b/static/recentrunes.js @@ -468,6 +468,100 @@ rr.Ref.cache_ = {}; +/** + * @constructor + * + * @param {string} key + * @param {rr.typeMatcher} child + * @private + */ +rr.Save_ = function(key, child) { + this.key_ = key; + this.child_ = child; +}; + + +/** + * @param {rr.Context} context + * @return {rr.typeIterator} + */ +rr.Save_.prototype.match = function(context) { + var iterator = this.child_.match(context); + return { + 'next': function() { + var next = iterator.next(); + if (next['done']) { + return { 'done': true }; + } + var value = ''; + var nodes = next['value']['nodes']; + for (var i = 0; i < nodes.length; i++) { + value += nodes[i].textContent; + } + return { + 'done': false, + 'value': { + 'context': context.saveValue(this.key_, value), + 'nodes': [] + } + }; + }.bind(this) + }; +}; + + +/** + * @param {string} key + * @param {rr.typeMatcher} saveChild + * @param {rr.typeMatcher} matchChild + * @return {rr.SequentialPair_} + */ +rr.Save = function(key, saveChild, matchChild) { + return new rr.SequentialPair_(new rr.Save_(key, saveChild), matchChild); +}; + + + +/** + * @constructor + * + * @param {string} key + * @private + */ +rr.SavedLiteral_ = function(key) { + this.key_ = key; +}; + + +/** + * @param {rr.Context} context + * @return {rr.typeIterator} + */ +rr.SavedLiteral_.prototype.match = function(context) { + var literal = rr.Literal(context.getValue(this.key_)); + return literal.match(context); +}; + + +/** + * @param {string} key + * @return {rr.SavedLiteral_} + */ +rr.SavedLiteral = function(key) { + return (rr.SavedLiteral.cache_[key] || + (rr.SavedLiteral.cache_[key] = new rr.SavedLiteral_(key))); +}; + + +/** + * @type {Object.} + * @const + * @private + */ +rr.SavedLiteral.cache_ = {}; + + + /** * @constructor * @@ -771,7 +865,7 @@ rr.ExtractElement = function(nodeName) { /** * @param {string} parentName - * @param {string} childNames + * @param {Array.} childNames * @return {rr.typeFilter} */ rr.GroupSiblings = function(parentName, childNames) { @@ -885,11 +979,14 @@ rr.ApplyFilters = function(node, filters) { * @param {Object.} rules * @param {string} input * @param {number=} opt_inputIndex + * @param {Object.=} opt_savedValues */ -rr.Context = function(rules, input, opt_inputIndex) { +rr.Context = function(rules, input, opt_inputIndex, opt_savedValues) { this.rules = rules; this.input = input; this.inputIndex = opt_inputIndex || 0; + this.savedValues = /** @type {Object.} */ ( + JSON.parse(JSON.stringify(opt_savedValues || {}))); }; @@ -897,7 +994,8 @@ rr.Context = function(rules, input, opt_inputIndex) { * @return {rr.Context} */ rr.Context.prototype.copy = function() { - return new rr.Context(this.rules, this.input, this.inputIndex); + return new rr.Context( + this.rules, this.input, this.inputIndex, this.savedValues); }; @@ -965,6 +1063,27 @@ rr.Context.prototype.advance = function(numChars) { }; +/** + * @param {string} key + * @return {string} + */ +rr.Context.prototype.getValue = function(key) { + return this.savedValues[key]; +}; + + +/** + * @param {string} key + * @param {string} value + * @return {rr.Context} + */ +rr.Context.prototype.saveValue = function(key, value) { + var context = this.copy(); + context.savedValues[key] = value; + return context; +}; + + /** * @constructor diff --git a/static/test.html b/static/test.html index f3e18ce..a3a58a4 100644 --- a/static/test.html +++ b/static/test.html @@ -9,6 +9,7 @@
+ diff --git a/static/test.js b/static/test.js index 536c7dc..d83f135 100644 --- a/static/test.js +++ b/static/test.js @@ -106,3 +106,23 @@ QUnit.test('Figure', function(assert) { assert.equal(mediawiki.parseFromString(content).innerHTML, expected); }); + + +QUnit.module('badpenny'); + +QUnit.test('Base', function(assert) { + assert.expect(1); + var content = [ + 'foo{{value1}}bar', + 'foo{{(container}}contents{{)nottheend}}more contents{{)container}}bar', + 'foo{{[repeated}}testing{{]notthis}}{{)repeated}}zig{{]repeated}}bar' + ].join('\n'); + + var expected = [ + 'foovalue1bar\n', + 'foocontents{{)nottheend}}more contentsbar\n', + 'footesting{{]notthis}}{{)repeated}}zigbar' + ].join(''); + + assert.equal(badpenny.parseFromString(content).innerHTML, expected); +});