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
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),
|
|
)
|
|
}
|
|
|
|
}
|
|
}
|