1
0
treerack/context.go
2026-06-10 00:43:27 +02:00

238 lines
4.2 KiB
Go

package treerack
import (
// "errors"
"io"
"strings"
// "unicode"
)
type context struct {
reader io.RuneReader
keywords []parser
offset int
readOffset int
consumed int
offsetLimit int
failOffset int
failingParser parser
readErr error
eof bool
results *results
tokens []rune
matchLast bool
level int
tr []TraceEntry
maxTraceLength int
}
func newContext(r []rune, keywords []parser, maxTraceLength int) *context {
return &context{
// reader: r,
tokens: r,
readOffset: len(r),
eof: true,
keywords: keywords,
results: newResults(len(r)),
offsetLimit: -1,
failOffset: -1,
maxTraceLength: maxTraceLength,
}
}
func (c *context) read() bool {
return false
/*
if c.eof || c.readErr != nil {
return false
}
token, n, err := c.reader.ReadRune()
if err != nil {
if errors.Is(err, io.EOF) {
if n == 0 {
c.eof = true
return false
}
} else {
c.readErr = err
return false
}
}
c.readOffset++
if token == unicode.ReplacementChar {
c.readErr = ErrInvalidUnicodeCharacter
return false
}
c.tokens = append(c.tokens, token)
return true
*/
}
func (c *context) token() (rune, bool) {
if c.offset == c.offsetLimit {
return 0, false
}
if c.offset == c.readOffset {
if !c.read() {
return 0, false
}
}
return c.tokens[c.offset], true
}
func (c *context) fromResults(p parser) bool {
to, m, ok := c.results.longestResult(c.offset, p.nodeID())
if !ok {
return false
}
if m {
c.success(to)
} else {
c.fail(c.offset)
}
return true
}
func (c *context) isKeyword(from, to int) bool {
ol := c.offsetLimit
c.offsetLimit = to
defer func() { c.offsetLimit = ol }()
for _, kw := range c.keywords {
c.offset = from
kw.parse(c)
if c.matchLast && c.offset == to {
return true
}
}
return false
}
func (c *context) success(to int) {
c.offset = to
c.matchLast = true
if to > c.consumed {
c.consumed = to
}
}
func (c *context) fail(offset int) {
c.offset = offset
c.matchLast = false
}
func findLine(tokens []rune, offset int) (line, column int) {
if offset < 0 {
return 0, 0
}
tokens = tokens[:offset]
for i := range tokens {
column++
if tokens[i] == '\n' {
column = 0
line++
}
}
return
}
func (c *context) parseError(p parser, unexpectedInput bool, root int) error {
definition := p.nodeName()
flagIndex := strings.Index(definition, ":")
if flagIndex > 0 {
definition = definition[:flagIndex]
}
if c.failingParser == nil {
c.failOffset = c.consumed
}
line, col := findLine(c.tokens, c.failOffset)
ueLine, ueCol := -1, -1
if unexpectedInput {
to, _, _ := c.results.longestResult(0, root)
ueLine, ueCol = findLine(c.tokens, to)
}
for i := range c.tr {
c.tr[i].FromLine, c.tr[i].FromCol = findLine(c.tokens, c.tr[i].From)
c.tr[i].ToLine, c.tr[i].ToCol = findLine(c.tokens, c.tr[i].To)
}
return &ParseError{
inputContent: c.tokens,
Offset: c.failOffset,
Line: line,
Column: col,
Definition: definition,
Trace: c.tr,
UnexpectedInputLine: ueLine,
UnexpectedInputCol: ueCol,
}
}
func (c *context) finalizeParse(root parser) error {
fp := c.failingParser
if fp == nil {
fp = root
}
to, match, found := c.results.longestResult(0, root.nodeID())
if !found || !match || found && match && to < c.readOffset {
return c.parseError(fp, found && match && to < c.readOffset, root.nodeID())
}
c.read()
if c.eof {
return nil
}
if c.readErr != nil {
return c.readErr
}
return c.parseError(root, false, root.nodeID())
}
func (c *context) trace(p parser, from, to int, event TraceEvent, reason ...string) {
if c.maxTraceLength <= 0 {
return
}
if p.commitType()&userDefined == 0 || p.commitType()&FailPass != 0 {
return
}
if len(c.tr) == c.maxTraceLength {
c.tr = c.tr[1:]
}
switch event {
case Success, Fail:
c.level--
}
c.tr = append(c.tr, TraceEntry{
Level: c.level,
Parser: p.nodeName(),
From: from,
To: to,
Event: event,
Reason: strings.Join(reason, "; "),
})
switch event {
case Enter:
c.level++
}
}