fix finding deepest user defined failing node

This commit is contained in:
Arpad Ryszka 2017-11-28 20:52:02 +01:00
parent 11ba9708d9
commit 692811a1a3
5 changed files with 42 additions and 43 deletions

View File

@ -95,6 +95,12 @@ func (p *charParser) match(t rune) bool {
func (p *charParser) parse(c *context) { func (p *charParser) parse(c *context) {
if tok, ok := c.token(); !ok || !p.match(tok) { 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) c.fail(c.offset)
return return
} }

View File

@ -163,6 +163,8 @@ func (p *choiceParser) parse(c *context) {
return return
} }
// println("parsing choice", p.name, c.offset)
c.results.markPending(c.offset, p.id) c.results.markPending(c.offset, p.id)
from := c.offset from := c.offset
to := 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 // - if there is a failure already, it should be left alone
// - what if reading more means that the previous failurs don't count // - what if reading more means that the previous failurs don't count
initialFailOffset := c.failOffset initialFailOffset := c.failOffset
initialFailingParser := c.failingParser
c.failingParser = nil
var (
failOffset int
failingParser parser
)
for { for {
foundMatch = false foundMatch = false
@ -192,13 +188,6 @@ func (p *choiceParser) parse(c *context) {
if !c.matchLast || match && c.offset <= to { if !c.matchLast || match && c.offset <= to {
c.offset = from c.offset = from
if c.failOffset > failOffset {
failOffset = c.failOffset
failingParser = c.failingParser
}
c.failOffset = initialFailOffset
c.failingParser = nil
continue continue
} }
@ -206,8 +195,6 @@ func (p *choiceParser) parse(c *context) {
foundMatch = true foundMatch = true
to = c.offset to = c.offset
c.offset = from c.offset = from
c.failOffset = initialFailOffset
c.failingParser = nil
c.results.setMatch(from, p.id, to) c.results.setMatch(from, p.id, to)
} }
@ -217,34 +204,23 @@ func (p *choiceParser) parse(c *context) {
} }
if match { if match {
c.failOffset = initialFailOffset if to >= c.failOffset {
c.failingParser = initialFailingParser c.failOffset = -1
c.failingParser = nil
}
c.success(to) c.success(to)
c.results.unmarkPending(from, p.id) c.results.unmarkPending(from, p.id)
return return
} }
if failOffset > initialFailOffset { // TODO:
// println("recording choice failure", p.name, failOffset) // - what if all of it pending?
c.failOffset = failOffset if c.failOffset > initialFailOffset && c.failingParser == nil {
if failingParser == nil && p.commit&userDefined != 0 && p.commit&Whitespace == 0 { if p.commitType()&userDefined != 0 && p.commitType()&Whitespace == 0 {
// println("setting failing choice parser", p.nodeName(), failOffset) // println("recording choice failure", p.name, from, c.failOffset)
c.failingParser = p 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) c.results.setNoMatch(from, p.id)

View File

@ -23,6 +23,7 @@ func newContext(r io.RuneReader) *context {
return &context{ return &context{
reader: r, reader: r,
results: &results{}, results: &results{},
failOffset: -1,
} }
} }
@ -145,13 +146,17 @@ func (c *context) parseError(p parser) error {
func (c *context) finalizeParse(root parser) error { func (c *context) finalizeParse(root parser) error {
p := c.failingParser p := c.failingParser
if p == nil { if p == nil {
// println("failing parser is nil")
p = root p = root
} }
// println("failing parser is", p.nodeName())
to, match, found := c.results.longestResult(0, root.nodeID()) to, match, found := c.results.longestResult(0, root.nodeID())
if found && match && to < c.readOffset { 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 // TODO: test both cases

View File

@ -149,7 +149,8 @@ func TestError(t *testing.T) {
} }
func TestErrorRecursive(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") s, err := openSyntaxFile("examples/mml.treerack")
if err != nil { if err != nil {
@ -157,7 +158,7 @@ func TestErrorRecursive(t *testing.T) {
return return
} }
// println("starting") // println("\n<<<< starting >>>>\n")
_, err = s.Parse(bytes.NewBufferString(doc)) _, err = s.Parse(bytes.NewBufferString(doc))
perr, ok := err.(*ParseError) perr, ok := err.(*ParseError)
if !ok { if !ok {

View File

@ -307,13 +307,19 @@ func (p *sequenceParser) parse(c *context) {
from := c.offset from := c.offset
to := c.offset to := c.offset
var parsed bool var parsed bool
initialFailOffset := c.failOffset
for itemIndex < len(p.items) { for itemIndex < len(p.items) {
p.items[itemIndex].parse(c) p.items[itemIndex].parse(c)
if !c.matchLast { if !c.matchLast {
if currentCount < p.ranges[itemIndex][0] { if currentCount < p.ranges[itemIndex][0] {
// println("recording sequence failure", p.name, c.offset) if c.failOffset > initialFailOffset && c.failingParser == nil {
c.recordFailure(c.offset, p) if p.commitType()&userDefined != 0 && p.commitType()&Whitespace == 0 {
// println("recording sequence failure", p.name, from, c.failOffset)
c.failingParser = p
}
}
c.fail(from) c.fail(from)
if !p.allChars { if !p.allChars {
c.results.unmarkPending(from, p.id) 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.results.setMatch(from, p.id, to)
c.success(to) c.success(to)
if !p.allChars { if !p.allChars {