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 {
|
2024-09-23 11:34:17 +00:00
|
|
|
case '(':
|
2024-09-23 11:34:53 +00:00
|
|
|
s.addToken(LeftParen)
|
2024-09-23 08:32:55 +00:00
|
|
|
break
|
2024-09-23 11:34:17 +00:00
|
|
|
case ')':
|
2024-09-23 11:34:53 +00:00
|
|
|
s.addToken(RightParen)
|
2024-09-23 08:32:55 +00:00
|
|
|
break
|
2024-09-23 11:34:17 +00:00
|
|
|
case '{':
|
2024-09-23 08:32:55 +00:00
|
|
|
s.addToken(LeftBrace)
|
|
|
|
break
|
2024-09-23 11:34:17 +00:00
|
|
|
case '}':
|
2024-09-23 08:32:55 +00:00
|
|
|
s.addToken(RightBrace)
|
|
|
|
break
|
2024-09-23 11:34:17 +00:00
|
|
|
case ',':
|
2024-09-23 08:32:55 +00:00
|
|
|
s.addToken(Comma)
|
|
|
|
break
|
2024-09-23 11:34:17 +00:00
|
|
|
case '.':
|
2024-09-23 08:32:55 +00:00
|
|
|
s.addToken(Dot)
|
|
|
|
break
|
2024-09-23 11:34:17 +00:00
|
|
|
case '-':
|
2024-09-23 08:32:55 +00:00
|
|
|
s.addToken(Minus)
|
|
|
|
break
|
2024-09-23 11:34:17 +00:00
|
|
|
case '+':
|
2024-09-23 08:32:55 +00:00
|
|
|
s.addToken(Plus)
|
|
|
|
break
|
2024-09-23 11:34:17 +00:00
|
|
|
case ';':
|
2024-09-23 08:32:55 +00:00
|
|
|
s.addToken(Semicolon)
|
|
|
|
break
|
2024-09-23 11:34:17 +00:00
|
|
|
case '*':
|
2024-09-23 08:32:55 +00:00
|
|
|
s.addToken(Star)
|
|
|
|
break
|
2024-09-23 11:34:17 +00:00
|
|
|
case '!':
|
|
|
|
if s.match('=') {
|
2024-09-23 08:32:55 +00:00
|
|
|
s.addToken(BangEqual)
|
|
|
|
} else {
|
|
|
|
s.addToken(Bang)
|
|
|
|
}
|
|
|
|
break
|
2024-09-23 11:34:17 +00:00
|
|
|
case '=':
|
|
|
|
if s.match('=') {
|
2024-09-23 08:32:55 +00:00
|
|
|
s.addToken(EqualEqual)
|
|
|
|
} else {
|
|
|
|
s.addToken(Equal)
|
|
|
|
}
|
|
|
|
break
|
2024-09-23 11:34:17 +00:00
|
|
|
case '<':
|
|
|
|
if s.match('=') {
|
2024-09-23 08:32:55 +00:00
|
|
|
s.addToken(LessEqual)
|
|
|
|
} else {
|
|
|
|
s.addToken(Less)
|
|
|
|
}
|
|
|
|
break
|
2024-09-23 11:34:17 +00:00
|
|
|
case '>':
|
|
|
|
if s.match('=') {
|
2024-09-23 08:32:55 +00:00
|
|
|
s.addToken(GreaterEqual)
|
|
|
|
} else {
|
|
|
|
s.addToken(Greater)
|
|
|
|
}
|
|
|
|
break
|
2024-09-23 11:34:17 +00:00
|
|
|
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
|
2024-09-23 11:34:17 +00:00
|
|
|
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
|
2024-09-23 11:34:17 +00:00
|
|
|
case ' ':
|
|
|
|
case '\t':
|
|
|
|
case '\r':
|
2024-09-23 08:32:55 +00:00
|
|
|
break
|
2024-09-23 11:34:17 +00:00
|
|
|
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()
|
|
|
|
}
|
2024-09-23 11:34:17 +00:00
|
|
|
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
|
2024-09-23 11:34:17 +00:00
|
|
|
} 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-23 11:34:17 +00:00
|
|
|
func (s *Scanner) advance() rune {
|
|
|
|
c := rune(s.source[s.current])
|
2024-09-23 08:32:55 +00:00
|
|
|
s.current += 1
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
2024-09-23 11:34:17 +00:00
|
|
|
func (s *Scanner) peek() rune {
|
2024-09-23 08:32:55 +00:00
|
|
|
if s.isAtEnd() {
|
2024-09-23 11:34:17 +00:00
|
|
|
return '0'
|
2024-09-23 08:32:55 +00:00
|
|
|
}
|
2024-09-23 11:34:17 +00:00
|
|
|
return rune(s.source[s.current])
|
2024-09-23 08:32:55 +00:00
|
|
|
}
|
|
|
|
|
2024-09-23 11:34:17 +00:00
|
|
|
func (s *Scanner) peekAfter() rune {
|
2024-09-23 10:56:32 +00:00
|
|
|
if s.current+1 >= len(s.source) {
|
2024-09-23 11:34:17 +00:00
|
|
|
return '0'
|
2024-09-23 10:56:32 +00:00
|
|
|
}
|
2024-09-23 11:34:17 +00:00
|
|
|
return rune(s.source[s.current+1])
|
2024-09-23 10:56:32 +00:00
|
|
|
}
|
|
|
|
|
2024-09-23 11:34:17 +00:00
|
|
|
func (s *Scanner) match(c rune) bool {
|
2024-09-23 08:32:55 +00:00
|
|
|
if s.isAtEnd() {
|
|
|
|
return false
|
|
|
|
}
|
2024-09-23 11:34:17 +00:00
|
|
|
if rune(s.source[s.current]) != c {
|
2024-09-23 08:32:55 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
s.current += 1
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2024-09-23 11:34:17 +00:00
|
|
|
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
|
|
|
}
|