Make ZeroOrMore non-greedy to make it nestable without exponential behavior.

This commit is contained in:
Ian Gulliver
2014-06-29 17:39:24 -07:00
parent 2f03f4d9ff
commit 78cc56dc46
3 changed files with 13 additions and 12 deletions

View File

@@ -23,5 +23,5 @@ var mediawiki = {
rr.Ref('nowiki'), rr.Ref('nowiki'),
rr.Ref('text') rr.Ref('text')
), ),
'wikidoc': rr.Node('wikidoc', rr.ZeroOrMore(rr.Ref('wikichunk'))), 'wikidoc': rr.Node('wikidoc', rr.Sequence(rr.ZeroOrMore(rr.Ref('wikichunk')), rr.EndOfText())),
}; };

View File

@@ -493,22 +493,18 @@ rr.ZeroOrMore_ = function(child) {
*/ */
rr.ZeroOrMore_.prototype.match = function(context) { rr.ZeroOrMore_.prototype.match = function(context) {
// Yield: // Yield:
// 1) The results of SequentialPair(child, this) // 1) An empty result
// 2) An empty result // 2) The results of SequentialPair(child, this)
// 3) Done // 3) Done
// //
// We must check for results from 1 that don't reduce context.remaining(); // We must check for results from 2 that don't reduce context.remaining();
// that means infinite recursion. // that means infinite recursion.
var iterator = this.pair_.match(context); var iterator = null;
return { return {
'next': function() { 'next': function() {
if (!iterator) { if (!iterator) {
return { 'done': true }; iterator = this.pair_.match(context);
}
var next = iterator.next();
if (next['done']) {
iterator = null;
return { return {
'done': false, 'done': false,
'value': { 'value': {
@@ -517,6 +513,10 @@ rr.ZeroOrMore_.prototype.match = function(context) {
} }
}; };
} }
var next = iterator.next();
if (next['done']) {
return { 'done': true };
}
if (next['value']['context'].remaining() == context.remaining()) { if (next['value']['context'].remaining() == context.remaining()) {
throw "Child of ZeroOrMore didn't consume input; grammar bug?"; throw "Child of ZeroOrMore didn't consume input; grammar bug?";
} }

View File

@@ -8,7 +8,7 @@ QUnit.test('Simple', function(assert) {
var iterable = context.rules['wikidoc'].match(context); var iterable = context.rules['wikidoc'].match(context);
assert.equal(iterable.next().value.nodes[0].innerHTML, assert.equal(iterable.next().value.nodes[0].innerHTML,
'<h3>Heading</h3>This is a wiki doc.\n' + '<h3>Heading</h3>This is a wiki doc.\n' +
'How about some <b>bold and &lt;i&gt;bold italic&lt;/i&gt;</b>.\n' + "How about some <b>bold and ''bold italic</b>''.\n" +
'I would also love some nowiki &lt;b&gt;foo&lt;/b&gt;'); 'I would also love some nowiki &lt;b&gt;foo&lt;/b&gt;');
}); });
@@ -16,7 +16,8 @@ QUnit.test('Simple', function(assert) {
QUnit.test('ZeroOrMore', function(assert) { QUnit.test('ZeroOrMore', function(assert) {
assert.expect(1); assert.expect(1);
var rules = { var rules = {
'test': rr.Node('test', rr.ZeroOrMore(rr.MultiLineText())) 'test': rr.Node('test',
rr.Sequence(rr.ZeroOrMore(rr.MultiLineText()), rr.EndOfText()))
}; };
var context = new rr.Context(rules, 'foobar'); var context = new rr.Context(rules, 'foobar');
var iterable = context.rules['test'].match(context); var iterable = context.rules['test'].match(context);