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