Initial working stage, but not with all tests, because IF NOT EXISTS is not implemented
This commit is contained in:
143
sql/lexer.go
Normal file
143
sql/lexer.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package sql
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type Token int
|
||||
|
||||
const (
|
||||
EOF Token = iota
|
||||
ILLEGAL
|
||||
IDENT
|
||||
|
||||
SEMI
|
||||
LPAREN
|
||||
RPAREN
|
||||
COMMA
|
||||
|
||||
CREATE
|
||||
TABLE
|
||||
|
||||
PRIMARY
|
||||
FOREIGN
|
||||
REFERENCES
|
||||
KEY
|
||||
NOT
|
||||
|
||||
TEXT
|
||||
INTEGER
|
||||
NULL
|
||||
)
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
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, ")"
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user