2017-07-15 21:49:08 +02:00
|
|
|
package treerack
|
2017-06-25 17:51:08 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
2017-12-29 18:36:58 +01:00
|
|
|
"strings"
|
2017-06-25 17:51:08 +02:00
|
|
|
"unicode"
|
|
|
|
)
|
|
|
|
|
|
|
|
type context struct {
|
2017-11-25 17:37:05 +01:00
|
|
|
reader io.RuneReader
|
|
|
|
offset int
|
|
|
|
readOffset int
|
|
|
|
consumed int
|
|
|
|
failOffset int
|
|
|
|
failingParser parser
|
|
|
|
readErr error
|
|
|
|
eof bool
|
|
|
|
results *results
|
|
|
|
tokens []rune
|
|
|
|
matchLast bool
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func newContext(r io.RuneReader) *context {
|
|
|
|
return &context{
|
2017-11-28 20:52:02 +01:00
|
|
|
reader: r,
|
|
|
|
results: &results{},
|
|
|
|
failOffset: -1,
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *context) read() bool {
|
|
|
|
if c.eof || c.readErr != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2017-11-02 22:55:19 +01:00
|
|
|
token, n, err := c.reader.ReadRune()
|
2017-06-25 17:51:08 +02:00
|
|
|
if err != nil {
|
|
|
|
if err == io.EOF {
|
|
|
|
if n == 0 {
|
|
|
|
c.eof = true
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
c.readErr = err
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
c.readOffset++
|
|
|
|
|
2017-11-02 22:55:19 +01:00
|
|
|
if token == unicode.ReplacementChar {
|
2017-06-26 02:20:23 +02:00
|
|
|
c.readErr = ErrInvalidUnicodeCharacter
|
2017-06-25 17:51:08 +02:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2017-11-02 22:55:19 +01:00
|
|
|
c.tokens = append(c.tokens, token)
|
2017-06-25 17:51:08 +02:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *context) token() (rune, bool) {
|
|
|
|
if c.offset == c.readOffset {
|
|
|
|
if !c.read() {
|
|
|
|
return 0, false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.tokens[c.offset], true
|
|
|
|
}
|
|
|
|
|
2017-11-25 17:37:05 +01:00
|
|
|
func (c *context) fromResults(p parser) bool {
|
|
|
|
to, m, ok := c.results.longestResult(c.offset, p.nodeID())
|
2017-06-25 17:51:08 +02:00
|
|
|
if !ok {
|
2017-07-30 05:10:46 +02:00
|
|
|
return false
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if m {
|
2017-07-17 04:23:29 +02:00
|
|
|
c.success(to)
|
2017-06-25 17:51:08 +02:00
|
|
|
} else {
|
2017-11-25 22:01:02 +01:00
|
|
|
c.fail(c.offset)
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
2017-07-30 05:10:46 +02:00
|
|
|
return true
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
2017-07-17 04:23:29 +02:00
|
|
|
func (c *context) success(to int) {
|
|
|
|
c.offset = to
|
2017-11-02 20:49:49 +01:00
|
|
|
c.matchLast = true
|
2017-11-25 17:37:05 +01:00
|
|
|
if to > c.consumed {
|
|
|
|
c.consumed = to
|
|
|
|
}
|
2017-07-17 01:41:38 +02:00
|
|
|
}
|
|
|
|
|
2017-11-25 22:01:02 +01:00
|
|
|
func (c *context) fail(offset int) {
|
2017-06-25 17:51:08 +02:00
|
|
|
c.offset = offset
|
2017-11-02 20:49:49 +01:00
|
|
|
c.matchLast = false
|
2017-11-25 22:01:02 +01:00
|
|
|
}
|
|
|
|
|
2017-11-26 18:48:56 +01:00
|
|
|
// TODO:
|
|
|
|
// - need to know which choice branch the failure happened on because if there is a non-user branch that has the
|
|
|
|
// longest failure, it can be reported to an unrelevant user defined choice on another branch
|
|
|
|
|
|
|
|
func (c *context) recordFailure(offset int, p parser) {
|
|
|
|
if offset < c.failOffset {
|
2017-11-25 22:01:02 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-11-26 18:48:56 +01:00
|
|
|
if c.failingParser != nil && offset == c.failOffset {
|
2017-11-25 22:01:02 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-11-26 18:48:56 +01:00
|
|
|
c.failOffset = offset
|
2017-11-26 02:34:34 +01:00
|
|
|
if p.commitType()&userDefined != 0 && p.commitType()&Whitespace == 0 {
|
2017-11-25 22:01:02 +01:00
|
|
|
c.failingParser = p
|
2017-11-25 17:37:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func findLine(tokens []rune, offset int) (line, column int) {
|
|
|
|
tokens = tokens[:offset]
|
|
|
|
for i := range tokens {
|
|
|
|
column++
|
|
|
|
if tokens[i] == '\n' {
|
|
|
|
column = 0
|
|
|
|
line++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
2017-11-25 22:01:02 +01:00
|
|
|
func (c *context) parseError(p parser) error {
|
|
|
|
definition := p.nodeName()
|
2017-12-29 18:36:58 +01:00
|
|
|
flagIndex := strings.Index(definition, ":")
|
|
|
|
if flagIndex > 0 {
|
|
|
|
definition = definition[:flagIndex]
|
|
|
|
}
|
|
|
|
|
2017-11-25 17:37:05 +01:00
|
|
|
if c.failingParser == nil {
|
|
|
|
c.failOffset = c.consumed
|
|
|
|
}
|
|
|
|
|
|
|
|
line, col := findLine(c.tokens, c.failOffset)
|
|
|
|
|
|
|
|
return &ParseError{
|
|
|
|
Offset: c.failOffset,
|
|
|
|
Line: line,
|
|
|
|
Column: col,
|
|
|
|
Definition: definition,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *context) finalizeParse(root parser) error {
|
2017-11-25 22:01:02 +01:00
|
|
|
p := c.failingParser
|
|
|
|
if p == nil {
|
|
|
|
p = root
|
2017-11-02 22:55:19 +01:00
|
|
|
}
|
|
|
|
|
2017-11-25 17:37:05 +01:00
|
|
|
to, match, found := c.results.longestResult(0, root.nodeID())
|
|
|
|
|
2017-11-25 22:01:02 +01:00
|
|
|
if found && match && to < c.readOffset {
|
2017-11-28 20:52:02 +01:00
|
|
|
return c.parseError(p)
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
2017-11-25 22:01:02 +01:00
|
|
|
if !found || !match {
|
|
|
|
return c.parseError(p)
|
|
|
|
}
|
|
|
|
|
2017-06-25 17:51:08 +02:00
|
|
|
if !c.eof {
|
|
|
|
c.read()
|
|
|
|
if !c.eof {
|
|
|
|
if c.readErr != nil {
|
|
|
|
return c.readErr
|
|
|
|
}
|
|
|
|
|
2017-11-25 17:37:05 +01:00
|
|
|
return c.parseError(root)
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|