diff --git a/char.go b/char.go index dd5dee2..57ac6c7 100644 --- a/char.go +++ b/char.go @@ -95,6 +95,12 @@ func (p *charParser) match(t rune) bool { func (p *charParser) parse(c *context) { if tok, ok := c.token(); !ok || !p.match(tok) { + if c.offset > c.failOffset { + c.failOffset = c.offset + // println("clearing failing parser") + c.failingParser = nil + } + c.fail(c.offset) return } diff --git a/choice.go b/choice.go index 46b4265..694c433 100644 --- a/choice.go +++ b/choice.go @@ -163,6 +163,8 @@ func (p *choiceParser) parse(c *context) { return } + // println("parsing choice", p.name, c.offset) + c.results.markPending(c.offset, p.id) from := c.offset to := c.offset @@ -175,12 +177,6 @@ func (p *choiceParser) parse(c *context) { // - if there is a failure already, it should be left alone // - what if reading more means that the previous failurs don't count initialFailOffset := c.failOffset - initialFailingParser := c.failingParser - c.failingParser = nil - var ( - failOffset int - failingParser parser - ) for { foundMatch = false @@ -192,13 +188,6 @@ func (p *choiceParser) parse(c *context) { if !c.matchLast || match && c.offset <= to { c.offset = from - if c.failOffset > failOffset { - failOffset = c.failOffset - failingParser = c.failingParser - } - - c.failOffset = initialFailOffset - c.failingParser = nil continue } @@ -206,8 +195,6 @@ func (p *choiceParser) parse(c *context) { foundMatch = true to = c.offset c.offset = from - c.failOffset = initialFailOffset - c.failingParser = nil c.results.setMatch(from, p.id, to) } @@ -217,34 +204,23 @@ func (p *choiceParser) parse(c *context) { } if match { - c.failOffset = initialFailOffset - c.failingParser = initialFailingParser + if to >= c.failOffset { + c.failOffset = -1 + c.failingParser = nil + } + c.success(to) c.results.unmarkPending(from, p.id) return } - if failOffset > initialFailOffset { - // println("recording choice failure", p.name, failOffset) - c.failOffset = failOffset - if failingParser == nil && p.commit&userDefined != 0 && p.commit&Whitespace == 0 { - // println("setting failing choice parser", p.nodeName(), failOffset) + // TODO: + // - what if all of it pending? + if c.failOffset > initialFailOffset && c.failingParser == nil { + if p.commitType()&userDefined != 0 && p.commitType()&Whitespace == 0 { + // println("recording choice failure", p.name, from, c.failOffset) c.failingParser = p - } else { - c.failingParser = failingParser } - } else if failOffset == initialFailOffset && initialFailingParser == nil { - // println("recording choice failure", p.name, failOffset) - c.failOffset = failOffset - if failingParser == nil && p.commit&userDefined != 0 && p.commit&Whitespace == 0 { - // println("setting failing choice parser", p.nodeName(), failOffset) - c.failingParser = p - } else { - c.failingParser = failingParser - } - } else { - c.failOffset = initialFailOffset - c.failingParser = initialFailingParser } c.results.setNoMatch(from, p.id) diff --git a/context.go b/context.go index 05d106a..bc7cfc7 100644 --- a/context.go +++ b/context.go @@ -21,8 +21,9 @@ type context struct { func newContext(r io.RuneReader) *context { return &context{ - reader: r, - results: &results{}, + reader: r, + results: &results{}, + failOffset: -1, } } @@ -145,13 +146,17 @@ func (c *context) parseError(p parser) error { func (c *context) finalizeParse(root parser) error { p := c.failingParser if p == nil { + // println("failing parser is nil") p = root } + // println("failing parser is", p.nodeName()) + to, match, found := c.results.longestResult(0, root.nodeID()) if found && match && to < c.readOffset { - return c.parseError(root) + // println("forcing root", found, match, to, c.readOffset) + return c.parseError(p) } // TODO: test both cases diff --git a/errors_test.go b/errors_test.go index 09ac352..1ba8e76 100644 --- a/errors_test.go +++ b/errors_test.go @@ -149,7 +149,8 @@ func TestError(t *testing.T) { } func TestErrorRecursive(t *testing.T) { - const doc = `a[b][1a]` + // const doc = `a[b][1a]` + const doc = `a[1a]` s, err := openSyntaxFile("examples/mml.treerack") if err != nil { @@ -157,7 +158,7 @@ func TestErrorRecursive(t *testing.T) { return } - // println("starting") + // println("\n<<<< starting >>>>\n") _, err = s.Parse(bytes.NewBufferString(doc)) perr, ok := err.(*ParseError) if !ok { diff --git a/sequence.go b/sequence.go index 542b4dd..e203acc 100644 --- a/sequence.go +++ b/sequence.go @@ -307,13 +307,19 @@ func (p *sequenceParser) parse(c *context) { from := c.offset to := c.offset var parsed bool + initialFailOffset := c.failOffset for itemIndex < len(p.items) { p.items[itemIndex].parse(c) if !c.matchLast { if currentCount < p.ranges[itemIndex][0] { - // println("recording sequence failure", p.name, c.offset) - c.recordFailure(c.offset, p) + if c.failOffset > initialFailOffset && c.failingParser == nil { + if p.commitType()&userDefined != 0 && p.commitType()&Whitespace == 0 { + // println("recording sequence failure", p.name, from, c.failOffset) + c.failingParser = p + } + } + c.fail(from) if !p.allChars { c.results.unmarkPending(from, p.id) @@ -346,6 +352,11 @@ func (p *sequenceParser) parse(c *context) { } } + if to >= c.failOffset { + c.failOffset = -1 + c.failingParser = nil + } + c.results.setMatch(from, p.id, to) c.success(to) if !p.allChars {