You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

144 lines
2.5 KiB
Go

package shell
import (
"bufio"
"errors"
"fmt"
"reflect"
)
type Parser interface {
Parse(*CharIterator, *CharCollection) (Token, error)
Parameters() ParserParameters
}
type BaseParser struct {
parameters ParserParameters
}
func (p *BaseParser) Parameters() ParserParameters {
return p.parameters
}
type CharIterator struct {
reader *bufio.Reader
currentChar rune
lastChar rune
}
func NewCharIterator(reader *bufio.Reader) (*CharIterator, error) {
char, _, err := reader.ReadRune()
if err != nil {
return nil, err
}
return &CharIterator{
reader: reader,
currentChar: char,
}, nil
}
func (i *CharIterator) Next() error {
var err error
i.lastChar = i.currentChar
i.currentChar, _, err = i.reader.ReadRune()
return err
}
func (i *CharIterator) Previous() error {
if i.lastChar == -1 {
return errors.New("Chariterator can only go back once")
}
err := i.reader.UnreadRune()
if err != nil {
return err
}
i.currentChar = i.lastChar
i.lastChar = -1
return err
}
type CharCollection struct {
chars []rune
}
func (c *CharCollection) Append(r rune) {
c.chars = append(c.chars, r)
}
func (c *CharCollection) Chars() []rune {
return c.chars
}
func (i CharIterator) Current() rune {
return i.currentChar
}
func (p *BaseParser) Parse(i *CharIterator, charsBefore *CharCollection) (Token, error) {
token := p.Parameters().MakeToken()
if enter, ok := p.parameters.(Enterable); ok {
err := enter.Enter(i)
if err != nil {
return nil, err
}
}
parsers := p.Parameters().SubParsers()
ParseLoop:
for {
char := i.Current()
if p.Parameters().ShouldLeave(charsBefore.Chars(), char) {
if leave, ok := p.Parameters().(Leavable); ok {
err := leave.Leave(i)
if err != nil {
return token, err
}
}
return token, nil
}
var matchedParser bool
for _, parser := range parsers {
if parser.Parameters().Supports(charsBefore.Chars(), char) {
matchedParser = true
nestedToken, err := parser.Parse(i, charsBefore)
if token != nil {
token.AddToken(nestedToken)
}
if err != nil {
return token, err
}
charsBefore.Append(char)
err = i.Next()
if err != nil {
return token, err
}
continue ParseLoop
}
}
if !matchedParser {
return token, fmt.Errorf(
"Parser encountered unsupported token or char. Parser: %s full text: %s -\n Char: %s",
reflect.TypeOf(*&p.parameters).String(),
string(charsBefore.Chars()),
string(char),
)
}
}
}