glox/lox/scanner.go

141 lines
2.6 KiB
Go

package lox
type Lexer struct {
start int
current int
line int
column int
source []rune
tokens []Token
}
func (lexer Lexer) lex() {
for !lexer.eof() {
lexer.start = lexer.current
c := lexer.advance()
switch c {
case '(':
lexer.addToken(LeftParen)
case ')':
lexer.addToken(RightParen)
case '{':
lexer.addToken(LeftBrace)
case '}':
lexer.addToken(RightBrace)
case ',':
lexer.addToken(Comma)
case '.':
lexer.addToken(Dot)
case '-':
lexer.addToken(Minus)
case '+':
lexer.addToken(Plus)
case ';':
lexer.addToken(Semicolon)
case '*':
lexer.addToken(Star)
case '!':
if lexer.match('=') {
lexer.addToken(BangEqual)
} else {
lexer.addToken(Bang)
}
case '=':
if lexer.match('=') {
lexer.addToken(EqualEqual)
} else {
lexer.addToken(Equal)
}
case '<':
if lexer.match('=') {
lexer.addToken(LessEqual)
} else {
lexer.addToken(Less)
}
case '>':
if lexer.match('=') {
lexer.addToken(GreaterEqual)
} else {
lexer.addToken(Greater)
}
case '/':
if lexer.match('/') {
for lexer.peek() != '\n' && !lexer.eof() {
lexer.advance()
}
} else {
lexer.addToken(Slash)
}
case ' ', '\r', '\t': // Ignore whitespace
continue
case '\n':
lexer.line++
lexer.column = 0
case '"':
for lexer.peek() != '"' && !lexer.eof() {
if lexer.peek() == '\n' {
lexer.line++
lexer.column--
}
lexer.advance()
}
// if lexer.eof() {
// // Add error (unterminated string)
// }
// Consume the terminating "
lexer.advance()
//value := lexer.source[lexer.start+1 : lexer.current-1]
//lexer.addToken(String, value)
}
}
lexer.addToken(Eof)
}
func (s *Lexer) addToken(tt TokenType) {
s.tokens = append(s.tokens, *makeToken(tt, "", nil, s.line, s.column))
}
// eof returns true if the read pointer has reached the end of the source file
func (s Lexer) eof() bool {
return s.current >= len(s.source)
}
// peek looks one rune ahead without advancing the cursor
func (s Lexer) peek() rune {
if s.eof() {
return '\u0004' // emit end of file character
}
return s.source[s.current]
}
// advance increments the read pointer and returns the rune at that position
func (s *Lexer) advance() rune {
r := s.source[s.current]
s.current++
s.column++
return r
}
// match tried to match a single rune and advances the read pointer if it does.
func (s *Lexer) match(expected rune) bool {
if s.eof() {
return false
}
if s.source[s.current] != expected {
return false
}
s.current++
s.column++
return true
}
func newLexer(source string) *Lexer {
return &Lexer{line: 1, column: 0, source: []rune(source)}
}