Initial commit of lexer
This commit is contained in:
140
lox/scanner.go
Normal file
140
lox/scanner.go
Normal file
@@ -0,0 +1,140 @@
|
||||
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)}
|
||||
}
|
Reference in New Issue
Block a user