Files
sqv-engine/sql/lexer.go

174 lines
2.5 KiB
Go

package sql
import (
"bufio"
"errors"
"io"
"strings"
"unicode"
)
type Token int
const (
EOF Token = iota
ILLEGAL
IDENT
SEMI
LPAREN
RPAREN
COMMA
ASTERIKS
ASSIGN
// Keywords
CREATE
TABLE
SELECT
FROM
WHERE
AND
OR
ORDER
TOP
PRIMARY
FOREIGN
REFERENCES
KEY
NOT
IF
EXISTS
TEXT
INTEGER
NULL
REAL
BLOB
)
var keywords map[string]Token = map[string]Token{
"CREATE": CREATE,
"TABLE": TABLE,
"PRIMARY": PRIMARY,
"FOREIGN": FOREIGN,
"REFERENCES": REFERENCES,
"KEY": KEY,
"NOT": NOT,
"TEXT": TEXT,
"INTEGER": INTEGER,
"NULL": NULL,
"IF": IF,
"EXISTS": EXISTS,
"SELECT": SELECT,
"FROM": FROM,
"WHERE": WHERE,
"AND": AND,
"OR": OR,
"ORDER": ORDER,
"TOP": TOP,
"REAL": REAL,
"BLOB": BLOB,
}
type Position struct {
line int
column int
}
type Lexer struct {
pos Position
reader *bufio.Reader
}
func NewLexer(reader io.Reader) *Lexer {
return &Lexer{
pos: Position{line: 1, column: 0},
reader: bufio.NewReader(reader),
}
}
func (l *Lexer) Lex() (Position, Token, string) {
for {
r, _, err := l.reader.ReadRune()
if err != nil && errors.Is(err, io.EOF) {
return l.pos, EOF, ""
} else if err != nil {
// Change this
panic(err)
}
l.pos.column++
switch r {
case '\n':
l.resetPosition()
case ';':
return l.pos, SEMI, ";"
case ',':
return l.pos, COMMA, ","
case '(':
return l.pos, LPAREN, "("
case ')':
return l.pos, RPAREN, ")"
case '*':
return l.pos, ASTERIKS, "*"
case '=':
return l.pos, ASSIGN, "="
default:
if unicode.IsSpace(r) {
continue
} else if unicode.IsLetter(r) {
startPos := l.pos
l.backup()
lit := l.lexIdent()
litUpper := strings.ToUpper(lit)
if token, ok := keywords[litUpper]; ok {
return startPos, token, litUpper
}
return startPos, IDENT, lit
} else {
return l.pos, ILLEGAL, string(r)
}
}
}
}
func (l *Lexer) lexIdent() string {
var lit string
for {
r, _, err := l.reader.ReadRune()
if err != nil && errors.Is(err, io.EOF) {
return lit
} else if err != nil {
// Change this
panic(err)
}
l.pos.column++
if unicode.IsLetter(r) || unicode.IsNumber(r) || r == '_' {
// Change this to stringstream or something similar
lit = lit + string(r)
} else {
l.backup()
return lit
}
}
}
func (l *Lexer) backup() {
if err := l.reader.UnreadRune(); err != nil {
panic(err)
}
l.pos.column--
}
func (l *Lexer) resetPosition() {
l.pos.line++
l.pos.column = 0
}