GoLox/scanner.go

237 lines
3.8 KiB
Go
Raw Normal View History

2024-09-22 16:50:20 +00:00
package main
2024-09-23 10:56:32 +00:00
import (
"fmt"
"strconv"
)
2024-09-22 16:50:20 +00:00
type Scanner struct {
2024-09-23 08:32:55 +00:00
source string
tokens []Token
start int
current int
line int
2024-09-22 16:50:20 +00:00
}
2024-09-23 08:32:55 +00:00
func (s *Scanner) ScanTokens() []Token {
for !s.isAtEnd() {
s.start = s.current
s.scanToken()
}
s.tokens = append(s.tokens, Token{
Type: EOF,
Lexeme: "",
Literal: "",
Line: s.line,
})
2024-09-22 16:50:20 +00:00
return s.tokens
}
2024-09-23 08:32:55 +00:00
func (s *Scanner) scanToken() {
c := s.advance()
switch c {
case '(':
2024-09-23 11:34:53 +00:00
s.addToken(LeftParen)
2024-09-23 08:32:55 +00:00
break
case ')':
2024-09-23 11:34:53 +00:00
s.addToken(RightParen)
2024-09-23 08:32:55 +00:00
break
case '{':
2024-09-23 08:32:55 +00:00
s.addToken(LeftBrace)
break
case '}':
2024-09-23 08:32:55 +00:00
s.addToken(RightBrace)
break
case ',':
2024-09-23 08:32:55 +00:00
s.addToken(Comma)
break
case '.':
2024-09-23 08:32:55 +00:00
s.addToken(Dot)
break
case '-':
2024-09-23 08:32:55 +00:00
s.addToken(Minus)
break
case '+':
2024-09-23 08:32:55 +00:00
s.addToken(Plus)
break
case ';':
2024-09-23 08:32:55 +00:00
s.addToken(Semicolon)
break
case '*':
2024-09-23 08:32:55 +00:00
s.addToken(Star)
break
case '!':
if s.match('=') {
2024-09-23 08:32:55 +00:00
s.addToken(BangEqual)
} else {
s.addToken(Bang)
}
break
case '=':
if s.match('=') {
2024-09-23 08:32:55 +00:00
s.addToken(EqualEqual)
} else {
s.addToken(Equal)
}
break
case '<':
if s.match('=') {
2024-09-23 08:32:55 +00:00
s.addToken(LessEqual)
} else {
s.addToken(Less)
}
break
case '>':
if s.match('=') {
2024-09-23 08:32:55 +00:00
s.addToken(GreaterEqual)
} else {
s.addToken(Greater)
}
break
case '/':
if s.match('/') {
for s.peek() != '\n' && !s.isAtEnd() {
2024-09-23 08:32:55 +00:00
s.advance()
}
} else {
s.addToken(Slash)
}
2024-09-23 10:56:32 +00:00
break
case '"':
for s.peek() != '"' && !s.isAtEnd() {
if s.peek() == '\n' {
2024-09-23 10:56:32 +00:00
s.line += 1
}
s.advance()
}
if s.isAtEnd() {
fuck(s.line, "Undetermined string")
return
}
s.advance() // close "
s.addTokenWithLiteral(String, s.source[s.start+1:s.current-1]) // +- to remove ""
2024-09-23 08:32:55 +00:00
break
case ' ':
case '\t':
case '\r':
2024-09-23 08:32:55 +00:00
break
case '\n':
2024-09-23 08:32:55 +00:00
s.line += 1
break
default:
2024-09-23 10:56:32 +00:00
if s.isDigit(c) {
for s.isDigit(s.peek()) {
s.advance()
}
if s.peek() == '.' && s.isDigit(s.peekAfter()) {
2024-09-23 10:56:32 +00:00
s.advance() // eated .
for s.isDigit(s.peek()) {
s.advance()
}
}
number, err := strconv.ParseFloat(s.source[s.start:s.current], 64)
if err != nil {
fuck(s.line, "Undetermined int")
}
s.addTokenWithLiteral(Number, fmt.Sprintf("%f", number)) // convert back to string, lol
} else if s.isAlpha(c) {
for s.isAlpha(s.peek()) {
s.advance()
}
keyword := s.source[s.start:s.current]
val, ok := Tokens[keyword]
if !ok {
val = Identifier
}
s.addToken(val)
2024-09-23 10:56:32 +00:00
} else {
fuck(s.line, "Unexpected Character")
}
2024-09-23 08:32:55 +00:00
break
}
}
func (s *Scanner) advance() rune {
c := rune(s.source[s.current])
2024-09-23 08:32:55 +00:00
s.current += 1
return c
}
func (s *Scanner) peek() rune {
2024-09-23 08:32:55 +00:00
if s.isAtEnd() {
return '0'
2024-09-23 08:32:55 +00:00
}
return rune(s.source[s.current])
2024-09-23 08:32:55 +00:00
}
func (s *Scanner) peekAfter() rune {
2024-09-23 10:56:32 +00:00
if s.current+1 >= len(s.source) {
return '0'
2024-09-23 10:56:32 +00:00
}
return rune(s.source[s.current+1])
2024-09-23 10:56:32 +00:00
}
func (s *Scanner) match(c rune) bool {
2024-09-23 08:32:55 +00:00
if s.isAtEnd() {
return false
}
if rune(s.source[s.current]) != c {
2024-09-23 08:32:55 +00:00
return false
}
s.current += 1
return true
}
func (s *Scanner) isDigit(c rune) bool {
return c >= '0' && c <= '9'
}
func (s *Scanner) isAlpha(c rune) bool {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}
func (s *Scanner) isAlphaNumeric(c rune) bool {
return s.isAlpha(c) || s.isDigit(c)
}
func (s *Scanner) isAtEnd() bool {
return s.current >= len(s.source)
2024-09-23 10:56:32 +00:00
}
2024-09-23 08:32:55 +00:00
func (s *Scanner) addToken(token int) {
s.tokens = append(s.tokens, Token{
Type: token,
Lexeme: s.source[s.start:s.current],
Literal: "",
Line: s.line,
})
}
2024-09-23 10:56:32 +00:00
func (s *Scanner) addTokenWithLiteral(token int, value string) {
s.tokens = append(s.tokens, Token{
Type: token,
Lexeme: s.source[s.start:s.current],
Literal: value,
Line: s.line,
})
}
2024-09-23 08:32:55 +00:00
func NewScanner(source string) *Scanner {
return &Scanner{
source: source,
tokens: []Token{},
start: 0,
current: 0,
line: 0,
}
2024-09-22 16:50:20 +00:00
}