74 lines
1.1 KiB
Go
74 lines
1.1 KiB
Go
package main
|
|
|
|
type Scanner struct {
|
|
source string
|
|
cursor int
|
|
}
|
|
|
|
func (s *Scanner) Consume() Token {
|
|
if s.cursor >= len(s.source) {
|
|
return Token{kind: "eof", lexeme: "EOF"}
|
|
}
|
|
|
|
s.readWhile(isWhitespace)
|
|
|
|
next := s.peek()
|
|
|
|
if isDigit(next) {
|
|
lexeme := s.readWhile(isDigit)
|
|
|
|
return Token{kind: "number", lexeme: lexeme}
|
|
}
|
|
|
|
kind, ok := kinds[next]
|
|
if ok {
|
|
lexeme := s.read()
|
|
|
|
return Token{kind: kind, lexeme: string(lexeme)}
|
|
}
|
|
|
|
lexeme := s.readWhile(notWhitespace)
|
|
|
|
return Token{kind: "unknown", lexeme: lexeme}
|
|
}
|
|
|
|
func (s *Scanner) peek() rune {
|
|
if s.cursor >= len(s.source) {
|
|
return 0
|
|
}
|
|
|
|
return rune(s.source[s.cursor])
|
|
}
|
|
|
|
func (s *Scanner) read() rune {
|
|
c := rune(s.source[s.cursor])
|
|
|
|
s.cursor += 1
|
|
|
|
return c
|
|
}
|
|
|
|
func (s *Scanner) readWhile(predicate func(rune) bool) string {
|
|
lexeme := ""
|
|
|
|
c := s.peek()
|
|
for s.cursor < len(s.source) && predicate(c) {
|
|
lexeme += string(s.read())
|
|
|
|
c = s.peek()
|
|
}
|
|
|
|
return lexeme
|
|
}
|
|
|
|
func isWhitespace(c rune) bool {
|
|
return c == ' ' || c == '\t' || c == '\n'
|
|
}
|
|
|
|
func notWhitespace(c rune) bool {
|
|
return !isWhitespace(c)
|
|
}
|
|
|
|
func isDigit(c rune) bool {
|
|
return '0' <= c && c <= '9'
|
|
}
|