diff --git a/char.go b/char.go index 2f74da6..e0158be 100644 --- a/char.go +++ b/char.go @@ -58,7 +58,7 @@ func (p *charParser) match(t rune) bool { func (p *charParser) parse(c *context) { if tok, ok := c.token(); !ok || !p.match(tok) { - c.fail(p, c.offset) + c.fail(c.offset) return } diff --git a/choice.go b/choice.go index bb13d98..40b89a9 100644 --- a/choice.go +++ b/choice.go @@ -141,7 +141,7 @@ func (p *choiceParser) parse(c *context) { } if c.results.pending(c.offset, p.id) { - c.fail(p, c.offset) + c.fail(c.offset) return } @@ -186,7 +186,8 @@ func (p *choiceParser) parse(c *context) { } c.results.setNoMatch(from, p.id) - c.fail(p, from) + c.recordFailure(p) + c.fail(from) c.results.unmarkPending(from, p.id) } diff --git a/context.go b/context.go index fda3289..4621e41 100644 --- a/context.go +++ b/context.go @@ -74,7 +74,7 @@ func (c *context) fromResults(p parser) bool { if m { c.success(to) } else { - c.fail(p, c.offset) + c.fail(c.offset) } return true @@ -88,16 +88,23 @@ func (c *context) success(to int) { } } -func (c *context) fail(p parser, offset int) { +func (c *context) fail(offset int) { c.offset = offset c.matchLast = false - if c.failingParser == nil || c.consumed > c.failOffset { - // TODO: choice can be retried - println("setting fail", p.nodeName(), c.failingParser == nil, c.failOffset, c.consumed) - c.failOffset = c.consumed - if p.commitType()&userDefined != 0 { - c.failingParser = p - } +} + +func (c *context) recordFailure(p parser) { + if c.offset < c.failOffset { + return + } + + if c.failingParser != nil && c.offset == c.failOffset { + return + } + + c.failOffset = c.offset + if p.commitType()&userDefined != 0 { + c.failingParser = p } } @@ -114,13 +121,10 @@ func findLine(tokens []rune, offset int) (line, column int) { return } -func (c *context) parseError(root parser) error { - definition := root.nodeName() +func (c *context) parseError(p parser) error { + definition := p.nodeName() if c.failingParser == nil { - println("setting fail", c.failOffset, c.consumed) c.failOffset = c.consumed - } else { - definition = c.failingParser.nodeName() } line, col := findLine(c.tokens, c.failOffset) @@ -134,17 +138,22 @@ func (c *context) parseError(root parser) error { } func (c *context) finalizeParse(root parser) error { - if !c.matchLast { - return c.parseError(root) + p := c.failingParser + if p == nil { + p = root } to, match, found := c.results.longestResult(0, root.nodeID()) - // TODO: test all three cases - if !found || !match || to < c.readOffset { + if found && match && to < c.readOffset { return c.parseError(root) } + // TODO: test both cases + if !found || !match { + return c.parseError(p) + } + if !c.eof { c.read() if !c.eof { diff --git a/errors_test.go b/errors_test.go index f050921..5055786 100644 --- a/errors_test.go +++ b/errors_test.go @@ -53,26 +53,26 @@ func TestError(t *testing.T) { column: 2, definition: "c", }, { - title: "choice, longer option fails", + title: "choice succeeds, document fails", syntax: `a = "12"; b = "1"; c:root = a | b`, text: "13", offset: 1, column: 1, - definition: "a", + definition: "c", }, { - title: "choice, shorter option fails", - syntax: `a = "2"; b = "12"; c:root = a | b`, - text: "123", - offset: 0, - column: 0, - definition: "1", - }, { - title: "choice, both options fail", + title: "choice fails", syntax: `a = "12"; b = "2"; c:root = a | b`, text: "13", offset: 1, column: 1, definition: "a", + }, { + title: "choice fails, longer option reported", + syntax: `a = "12"; b = "134"; c:root = a | b`, + text: "135", + offset: 2, + column: 2, + definition: "b", }} { t.Run(test.title, func(t *testing.T) { s, err := openSyntaxString(test.syntax) diff --git a/open_test.go b/open_test.go index 52d8cf6..b439c29 100644 --- a/open_test.go +++ b/open_test.go @@ -17,7 +17,6 @@ func openSyntaxReader(r io.Reader) (*Syntax, error) { return nil, err } - println("starting") s := &Syntax{} if err := define(s, doc); err != nil { return nil, err diff --git a/sequence.go b/sequence.go index 2bcfdf8..b34c212 100644 --- a/sequence.go +++ b/sequence.go @@ -175,7 +175,7 @@ func (p *sequenceParser) commitType() CommitType { return p.commit } func (p *sequenceParser) parse(c *context) { if !p.allChars { if c.results.pending(c.offset, p.id) { - c.fail(p, c.offset) + c.fail(c.offset) return } @@ -192,7 +192,8 @@ func (p *sequenceParser) parse(c *context) { p.items[itemIndex].parse(c) if !c.matchLast { if currentCount < p.ranges[itemIndex][0] { - c.fail(p, from) + c.recordFailure(p) + c.fail(from) if !p.allChars { c.results.unmarkPending(from, p.id) }