box-o-sand/argh/scanner.go

160 lines
2.4 KiB
Go
Raw Normal View History

package argh
import (
"bufio"
"bytes"
"errors"
"io"
"log"
"unicode"
)
type Scanner struct {
r *bufio.Reader
2022-05-14 00:58:55 +00:00
i int
cfg *ScannerConfig
}
func NewScanner(r io.Reader, cfg *ScannerConfig) *Scanner {
if cfg == nil {
cfg = POSIXyScannerConfig
}
return &Scanner{
r: bufio.NewReader(r),
cfg: cfg,
}
}
func (s *Scanner) Scan() (Token, string, Pos) {
2022-05-14 00:58:55 +00:00
ch, pos := s.read()
if s.cfg.IsBlankspace(ch) {
2022-05-14 00:58:55 +00:00
_ = s.unread()
return s.scanBlankspace()
}
if s.cfg.IsAssignmentOperator(ch) {
2022-05-14 00:58:55 +00:00
return ASSIGN, string(ch), pos
}
if s.cfg.IsMultiValueDelim(ch) {
2022-05-14 00:58:55 +00:00
return MULTI_VALUE_DELIMITER, string(ch), pos
}
if ch == eol {
2022-05-14 00:58:55 +00:00
return EOL, "", pos
}
if ch == nul {
2022-05-14 00:58:55 +00:00
return ARG_DELIMITER, string(ch), pos
}
if unicode.IsGraphic(ch) {
2022-05-14 00:58:55 +00:00
_ = s.unread()
return s.scanArg()
}
2022-05-14 00:58:55 +00:00
return ILLEGAL, string(ch), pos
}
func (s *Scanner) read() (rune, Pos) {
ch, _, err := s.r.ReadRune()
2022-05-14 00:58:55 +00:00
s.i++
if errors.Is(err, io.EOF) {
return eol, Pos(s.i)
} else if err != nil {
log.Printf("unknown scanner error=%+v", err)
return eol, Pos(s.i)
}
return ch, Pos(s.i)
}
func (s *Scanner) unread() Pos {
_ = s.r.UnreadRune()
2022-05-14 00:58:55 +00:00
s.i--
return Pos(s.i)
}
func (s *Scanner) scanBlankspace() (Token, string, Pos) {
buf := &bytes.Buffer{}
2022-05-14 00:58:55 +00:00
ch, pos := s.read()
buf.WriteRune(ch)
for {
2022-05-14 00:58:55 +00:00
ch, pos = s.read()
if ch == eol {
break
} else if !s.cfg.IsBlankspace(ch) {
2022-05-14 00:58:55 +00:00
pos = s.unread()
break
} else {
_, _ = buf.WriteRune(ch)
}
}
2022-05-14 00:58:55 +00:00
return BS, buf.String(), pos
}
func (s *Scanner) scanArg() (Token, string, Pos) {
buf := &bytes.Buffer{}
2022-05-14 00:58:55 +00:00
ch, pos := s.read()
buf.WriteRune(ch)
for {
2022-05-14 00:58:55 +00:00
ch, pos = s.read()
if ch == eol || ch == nul || s.cfg.IsAssignmentOperator(ch) || s.cfg.IsMultiValueDelim(ch) {
2022-05-14 00:58:55 +00:00
pos = s.unread()
break
}
_, _ = buf.WriteRune(ch)
}
str := buf.String()
if len(str) == 0 {
2022-05-14 00:58:55 +00:00
return EMPTY, str, pos
}
ch0 := rune(str[0])
if len(str) == 1 {
if s.cfg.IsFlagPrefix(ch0) {
2022-05-14 00:58:55 +00:00
return STDIN_FLAG, str, pos
}
if s.cfg.IsAssignmentOperator(ch0) {
return ASSIGN, str, pos
}
2022-05-14 00:58:55 +00:00
return IDENT, str, pos
}
ch1 := rune(str[1])
if len(str) == 2 {
if s.cfg.IsFlagPrefix(ch0) && s.cfg.IsFlagPrefix(ch1) {
2022-05-14 00:58:55 +00:00
return STOP_FLAG, str, pos
}
if s.cfg.IsFlagPrefix(ch0) {
2022-05-14 00:58:55 +00:00
return SHORT_FLAG, str, pos
}
}
if s.cfg.IsFlagPrefix(ch0) {
if s.cfg.IsFlagPrefix(ch1) {
2022-05-14 00:58:55 +00:00
return LONG_FLAG, str, pos
}
2022-05-14 00:58:55 +00:00
return COMPOUND_SHORT_FLAG, str, pos
}
2022-05-14 00:58:55 +00:00
return IDENT, str, pos
}