hcl/parser/lexer.go

111 lines
2.3 KiB
Go
Raw Normal View History

2015-10-03 14:08:09 +00:00
package parser
import (
"bufio"
"bytes"
2015-10-03 14:08:09 +00:00
"io"
"unicode"
)
// eof represents a marker rune for the end of the reader.
const eof = rune(0)
// Lexer defines a lexical scanner
2015-10-03 18:25:21 +00:00
type Scanner struct {
src *bufio.Reader // input
ch rune // current character
2015-10-03 14:08:09 +00:00
}
// NewLexer returns a new instance of Lexer.
2015-10-03 18:25:21 +00:00
func NewLexer(src io.Reader) *Scanner {
return &Scanner{
src: bufio.NewReader(src),
2015-10-03 14:08:09 +00:00
}
}
2015-10-03 17:32:27 +00:00
// next reads the next rune from the bufferred reader. Returns the rune(0) if
2015-10-03 14:08:09 +00:00
// an error occurs (or io.EOF is returned).
2015-10-03 18:25:21 +00:00
func (s *Scanner) next() rune {
var err error
2015-10-03 18:25:21 +00:00
s.ch, _, err = s.src.ReadRune()
2015-10-03 14:08:09 +00:00
if err != nil {
return eof
}
2015-10-03 17:32:27 +00:00
2015-10-03 18:25:21 +00:00
return s.ch
2015-10-03 14:08:09 +00:00
}
// unread places the previously read rune back on the reader.
2015-10-03 18:25:21 +00:00
func (s *Scanner) unread() { _ = s.src.UnreadRune() }
2015-10-03 14:08:09 +00:00
2015-10-03 18:25:21 +00:00
func (s *Scanner) peek() rune {
prev := s.ch
peekCh := s.next()
s.unread()
s.ch = prev
2015-10-03 17:33:51 +00:00
return peekCh
}
2015-10-03 14:08:09 +00:00
// Scan scans the next token and returns the token and it's literal string.
2015-10-03 18:25:21 +00:00
func (s *Scanner) Scan() (tok Token, lit string) {
ch := s.next()
2015-10-03 14:08:09 +00:00
// skip white space
for isWhitespace(ch) {
2015-10-03 18:25:21 +00:00
ch = s.next()
2015-10-03 14:08:09 +00:00
}
// identifier
if isLetter(ch) {
2015-10-03 18:25:21 +00:00
return s.scanIdentifier()
}
switch ch {
case eof:
return EOF, ""
}
2015-10-03 14:08:09 +00:00
return 0, ""
}
2015-10-03 18:25:21 +00:00
func (s *Scanner) scanIdentifier() (Token, string) {
// Create a buffer and read the current character into it.
var buf bytes.Buffer
2015-10-03 18:25:21 +00:00
for isLetter(s.ch) || isDigit(s.ch) {
buf.WriteRune(s.ch)
s.next()
2015-10-03 18:06:30 +00:00
}
2015-10-03 18:06:30 +00:00
return IDENT, buf.String()
2015-10-03 14:08:09 +00:00
}
// Pos returns the position of the character immediately after the character or
// token returned by the last call to Next or Scan.
2015-10-03 18:25:21 +00:00
func (s *Scanner) Pos() Position {
2015-10-03 14:08:09 +00:00
return Position{}
}
// isSpace reports whether r is a space character.
func isSpace(r rune) bool {
return r == ' ' || r == '\t'
}
// isEndOfLine reports whether r is an end-of-line character.
func isEndOfLine(r rune) bool {
return r == '\r' || r == '\n'
}
func isLetter(ch rune) bool {
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch)
}
func isDigit(ch rune) bool {
return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch)
}
// isWhitespace returns true if the rune is a space, tab, newline or carriage return
func isWhitespace(ch rune) bool {
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'
}