| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package tokenizer |
| |
| import "fmt" |
| |
| // Iterator iterates over a stream of tokens. This provides the core functionality for a |
| // TokenStream. |
| type iterator interface { |
| Next() Token |
| Peek() Token |
| Raw() *rawIterator |
| } |
| |
| // NewTokenStream creates a stream of Token values read from input. |
| func NewTokenStream(input []byte) *TokenStream { |
| return &TokenStream{ |
| iter: &noSpacesIterator{ |
| raw: &rawIterator{ |
| stream: Tokenize(input), |
| }, |
| }, |
| } |
| } |
| |
| // TokenStream is a read-only queue of Token values. The next Token in the stream can be |
| // consumed by calling Next(). The next token can be inspected without being consumed by |
| // calling Peek(). By default, TokenStream discards whitespace characters as though they |
| // are not part of the stream. Use Raw get a TokenStream that respects whitespace. |
| type TokenStream struct { |
| iter iterator |
| } |
| |
| // Next consumes the next token in the stream. |
| func (s *TokenStream) Next() Token { |
| return s.iter.Next() |
| } |
| |
| // Peek returns a read-only copy of the next token in the stream, without consuming it. |
| func (s *TokenStream) Peek() Token { |
| return s.iter.Peek() |
| } |
| |
| // Raw returns a TokenStream that includes whitespace characters. |
| func (s *TokenStream) Raw() *TokenStream { |
| return &TokenStream{iter: s.iter.Raw()} |
| } |
| |
| // Eat consumes the next token from the stream iff it's type matches typ. If the types |
| // are different, an error is returned. |
| func (s *TokenStream) Eat(typ TokenType) error { |
| token := s.iter.Peek() |
| if token.Type != typ { |
| return fmt.Errorf("unexpected token: %q", token.Type) |
| } |
| s.iter.Next() |
| return nil |
| } |
| |
| // ConcatUntil concatenates the values of the next tokens in this stream as long as their |
| // types are not anyOf. TypeEOF is implied and need not be specified. Returns the |
| // contatenated output with outer spaces trimmed. |
| func (s *TokenStream) ConcatUntil(anyOf ...TokenType) string { |
| var values string |
| stopAtType := map[TokenType]bool{TypeEOF: true} |
| for i := range anyOf { |
| stopAtType[anyOf[i]] = true |
| } |
| for !stopAtType[s.iter.Peek().Type] { |
| values += s.iter.Next().Value |
| } |
| return values |
| } |