122 lines
1.9 KiB
Go
122 lines
1.9 KiB
Go
package lexer
|
|
|
|
type TokenType byte
|
|
|
|
const (
|
|
TokenEOF TokenType = iota
|
|
TokenIdentifier
|
|
TokenString
|
|
TokenNumber
|
|
TokenEqual
|
|
TokenEcho
|
|
TokenSemicolon
|
|
)
|
|
|
|
type Token struct {
|
|
Type TokenType
|
|
Value string
|
|
}
|
|
|
|
type Lexer struct {
|
|
input string
|
|
pos int
|
|
readPos int
|
|
ch byte
|
|
}
|
|
|
|
func New(input string) *Lexer {
|
|
l := &Lexer{input: input}
|
|
l.readChar()
|
|
return l
|
|
}
|
|
|
|
func (l *Lexer) readChar() {
|
|
if l.readPos >= len(l.input) {
|
|
l.ch = 0
|
|
} else {
|
|
l.ch = l.input[l.readPos]
|
|
}
|
|
l.pos = l.readPos
|
|
l.readPos++
|
|
}
|
|
|
|
func (l *Lexer) NextToken() Token {
|
|
var tok Token
|
|
|
|
l.skipWhitespace()
|
|
|
|
switch l.ch {
|
|
case '=':
|
|
tok = Token{Type: TokenEqual, Value: "="}
|
|
case ';':
|
|
tok = Token{Type: TokenSemicolon, Value: ";"}
|
|
case '"':
|
|
tok = Token{Type: TokenString, Value: l.readString()}
|
|
return tok
|
|
case 0:
|
|
tok = Token{Type: TokenEOF, Value: ""}
|
|
default:
|
|
if isLetter(l.ch) {
|
|
tok.Value = l.readIdentifier()
|
|
if tok.Value == "echo" {
|
|
tok.Type = TokenEcho
|
|
} else {
|
|
tok.Type = TokenIdentifier
|
|
}
|
|
return tok
|
|
} else if isDigit(l.ch) {
|
|
tok.Type = TokenNumber
|
|
tok.Value = l.readNumber()
|
|
return tok
|
|
} else {
|
|
tok = Token{Type: TokenEOF, Value: ""}
|
|
}
|
|
}
|
|
|
|
l.readChar()
|
|
return tok
|
|
}
|
|
|
|
func (l *Lexer) skipWhitespace() {
|
|
for l.ch == ' ' || l.ch == '\t' || l.ch == '\n' || l.ch == '\r' {
|
|
l.readChar()
|
|
}
|
|
}
|
|
|
|
func (l *Lexer) readIdentifier() string {
|
|
pos := l.pos
|
|
for isLetter(l.ch) || isDigit(l.ch) {
|
|
l.readChar()
|
|
}
|
|
return l.input[pos:l.pos]
|
|
}
|
|
|
|
func (l *Lexer) readNumber() string {
|
|
pos := l.pos
|
|
for isDigit(l.ch) {
|
|
l.readChar()
|
|
}
|
|
return l.input[pos:l.pos]
|
|
}
|
|
|
|
func (l *Lexer) readString() string {
|
|
pos := l.pos + 1
|
|
for {
|
|
l.readChar()
|
|
if l.ch == '"' || l.ch == 0 {
|
|
break
|
|
}
|
|
}
|
|
str := l.input[pos:l.pos]
|
|
l.readChar() // Skip closing quote
|
|
return str
|
|
}
|
|
|
|
func isLetter(ch byte) bool {
|
|
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_'
|
|
}
|
|
|
|
func isDigit(ch byte) bool {
|
|
return '0' <= ch && ch <= '9'
|
|
}
|