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' }