diff --git a/pkg/govis/unvis.go b/pkg/govis/unvis.go
index 5dbf935..c6f8cc1 100644
--- a/pkg/govis/unvis.go
+++ b/pkg/govis/unvis.go
@@ -36,7 +36,13 @@ var (
type unvisParser struct {
tokens []rune
idx int
- flag VisFlag
+ flags VisFlag
+}
+
+// Input resets the parser with a new input string.
+func (p *unvisParser) Input(input string) {
+ p.tokens = []rune(input)
+ p.idx = 0
}
// Next moves the index to the next character.
@@ -57,11 +63,11 @@ func (p *unvisParser) End() bool {
return p.idx >= len(p.tokens)
}
-func newParser(input string, flag VisFlag) *unvisParser {
+func newParser(flags VisFlag) *unvisParser {
return &unvisParser{
- tokens: []rune(input),
+ tokens: nil,
idx: 0,
- flag: flag,
+ flags: flags,
}
}
@@ -69,8 +75,8 @@ func newParser(input string, flag VisFlag) *unvisParser {
// codes, this is IMO much easier to read than the ugly 80s coroutine code used
// by the original unvis(3) parser. Here's the EBNF for an unvis sequence:
//
-// ::= ()*
-// ::= ("\" ) | ("%" ) |
+// ::= ()*
+// ::= ("\" ) | ("%" ) |
// ::= any rune
// ::= ("x" ) | ("M" ) | ("^" |
// ::= ("-" ) | ("^" )
@@ -80,7 +86,7 @@ func newParser(input string, flag VisFlag) *unvisParser {
// ::= [0-9a-f] [0-9a-f]
// ::= [0-7] ([0-7] ([0-7])?)?
-func unvisPlainRune(p *unvisParser) ([]byte, error) {
+func (p *unvisParser) plainRune() ([]byte, error) {
ch, err := p.Peek()
if err != nil {
return nil, fmt.Errorf("plain rune: %w", err)
@@ -89,7 +95,7 @@ func unvisPlainRune(p *unvisParser) ([]byte, error) {
return []byte(string(ch)), nil
}
-func unvisEscapeCStyle(p *unvisParser) ([]byte, error) {
+func (p *unvisParser) escapeCStyle() ([]byte, error) {
ch, err := p.Peek()
if err != nil {
return nil, fmt.Errorf("escape cstyle: %w", err)
@@ -128,7 +134,7 @@ func unvisEscapeCStyle(p *unvisParser) ([]byte, error) {
return []byte(output), nil
}
-func unvisEscapeDigits(p *unvisParser, base int, force bool) ([]byte, error) {
+func (p *unvisParser) escapeDigits(base int, force bool) ([]byte, error) {
var code int
for i := int(0xFF); i > 0; i /= base {
@@ -160,7 +166,7 @@ func unvisEscapeDigits(p *unvisParser, base int, force bool) ([]byte, error) {
return []byte{char}, nil
}
-func unvisEscapeCtrl(p *unvisParser, mask byte) ([]byte, error) {
+func (p *unvisParser) escapeCtrl(mask byte) ([]byte, error) {
ch, err := p.Peek()
if err != nil {
return nil, fmt.Errorf("escape ctrl: %w", err)
@@ -178,7 +184,7 @@ func unvisEscapeCtrl(p *unvisParser, mask byte) ([]byte, error) {
return []byte{mask | char}, nil
}
-func unvisEscapeMeta(p *unvisParser) ([]byte, error) {
+func (p *unvisParser) escapeMeta() ([]byte, error) {
ch, err := p.Peek()
if err != nil {
return nil, fmt.Errorf("escape meta: %w", err)
@@ -190,7 +196,7 @@ func unvisEscapeMeta(p *unvisParser) ([]byte, error) {
case '^':
// The same as "\^..." except we apply a mask.
p.Next()
- return unvisEscapeCtrl(p, mask)
+ return p.escapeCtrl(mask)
case '-':
p.Next()
@@ -211,7 +217,7 @@ func unvisEscapeMeta(p *unvisParser) ([]byte, error) {
return nil, fmt.Errorf("escape meta: %w %q", errUnknownEscapeChar, ch)
}
-func unvisEscapeSequence(p *unvisParser) ([]byte, error) {
+func (p *unvisParser) escapeSequence() ([]byte, error) {
ch, err := p.Peek()
if err != nil {
return nil, fmt.Errorf("escape sequence: %w", err)
@@ -223,26 +229,26 @@ func unvisEscapeSequence(p *unvisParser) ([]byte, error) {
return []byte("\\"), nil
case '0', '1', '2', '3', '4', '5', '6', '7':
- return unvisEscapeDigits(p, 8, false)
+ return p.escapeDigits(8, false)
case 'x':
p.Next()
- return unvisEscapeDigits(p, 16, true)
+ return p.escapeDigits(16, true)
case '^':
p.Next()
- return unvisEscapeCtrl(p, 0x00)
+ return p.escapeCtrl(0x00)
case 'M':
p.Next()
- return unvisEscapeMeta(p)
+ return p.escapeMeta()
default:
- return unvisEscapeCStyle(p)
+ return p.escapeCStyle()
}
}
-func unvisRune(p *unvisParser) ([]byte, error) {
+func (p *unvisParser) element() ([]byte, error) {
ch, err := p.Peek()
if err != nil {
return nil, err
@@ -251,22 +257,23 @@ func unvisRune(p *unvisParser) ([]byte, error) {
switch ch {
case '\\':
p.Next()
- return unvisEscapeSequence(p)
+ return p.escapeSequence()
case '%':
// % HEX HEX only applies to HTTPStyle encodings.
- if p.flag&VisHTTPStyle == VisHTTPStyle {
+ if p.flags&VisHTTPStyle == VisHTTPStyle {
p.Next()
- return unvisEscapeDigits(p, 16, true)
+ return p.escapeDigits(16, true)
}
}
- return unvisPlainRune(p)
+ return p.plainRune()
}
-func unvis(p *unvisParser) (string, error) {
+func (p *unvisParser) unvis(input string) (string, error) {
+ p.Input(input)
var output []byte
for !p.End() {
- ch, err := unvisRune(p)
+ ch, err := p.element()
if err != nil {
return "", err
}
@@ -283,8 +290,8 @@ func Unvis(input string, flags VisFlag) (string, error) {
if unknown := flags &^ visMask; unknown != 0 {
return "", unknownVisFlagsError{flags: flags}
}
- p := newParser(input, flags)
- output, err := unvis(p)
+ p := newParser(flags)
+ output, err := p.unvis(input)
if err != nil {
return "", fmt.Errorf("unvis '%s': %w", input, err)
}