Remove last, add unscan and rescan, add handle for if not exist, just gets skipped for now
This commit is contained in:
@@ -74,5 +74,6 @@ CREATE TABLE IF NOT EXISTS auth_states (
|
||||
}
|
||||
|
||||
stmt.Print()
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
3
go.mod
3
go.mod
@@ -1,6 +1,3 @@
|
||||
module git.pablu.de/pablu/sqv-engine
|
||||
|
||||
go 1.25.3
|
||||
|
||||
require github.com/rqlite/sql v0.0.0-20250623131620-453fa49cad04
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ const (
|
||||
RPAREN
|
||||
COMMA
|
||||
|
||||
// Keywords
|
||||
CREATE
|
||||
TABLE
|
||||
|
||||
@@ -47,6 +48,8 @@ var keywords map[string]Token = map[string]Token{
|
||||
"TEXT": TEXT,
|
||||
"INTEGER": INTEGER,
|
||||
"NULL": NULL,
|
||||
"IF": IF,
|
||||
"EXISTS": EXISTS,
|
||||
}
|
||||
|
||||
type Position struct {
|
||||
|
||||
120
sql/parser.go
120
sql/parser.go
@@ -7,12 +7,15 @@ import (
|
||||
)
|
||||
|
||||
type Parser struct {
|
||||
s *Lexer
|
||||
last struct {
|
||||
pos Position
|
||||
tok Token
|
||||
lit string
|
||||
}
|
||||
s *Lexer
|
||||
buf LexRes
|
||||
}
|
||||
|
||||
type LexRes struct {
|
||||
pos Position
|
||||
tok Token
|
||||
lit string
|
||||
avail bool
|
||||
}
|
||||
|
||||
func NewParser(r io.Reader) *Parser {
|
||||
@@ -27,25 +30,37 @@ func (p *Parser) Parse() (*CreateTableStatement, error) {
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
if !p.expectSequence(TABLE, IDENT) {
|
||||
if !p.expectNext(TABLE) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
|
||||
tok, ok = p.expectOne(IDENT, IF)
|
||||
if !ok {
|
||||
return nil, p.unexpectedToken(IDENT, IF)
|
||||
} else if tok == IF && !p.expectSequence(NOT, EXISTS, IDENT) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
_, _, lit := p.rescan()
|
||||
|
||||
stmt := CreateTableStatement{
|
||||
TableName: p.last.lit,
|
||||
TableName: lit,
|
||||
Columns: make([]Column, 0),
|
||||
}
|
||||
|
||||
|
||||
if !p.expectNext(LPAREN) {
|
||||
return nil, p.unexpectedToken(LPAREN)
|
||||
}
|
||||
|
||||
for {
|
||||
lastTok := p.last.tok
|
||||
_, tok, _ := p.scan()
|
||||
|
||||
switch tok {
|
||||
case RPAREN:
|
||||
if !p.expectNext(SEMI) {
|
||||
return nil, p.unexpectedToken(SEMI)
|
||||
}
|
||||
return &stmt, nil
|
||||
|
||||
case IDENT:
|
||||
column, err := p.parseColumn()
|
||||
if err != nil {
|
||||
@@ -53,24 +68,11 @@ func (p *Parser) Parse() (*CreateTableStatement, error) {
|
||||
}
|
||||
stmt.Columns = append(stmt.Columns, column)
|
||||
|
||||
case RPAREN:
|
||||
if !p.expectNext(SEMI) {
|
||||
return nil, p.unexpectedToken(SEMI)
|
||||
}
|
||||
return &stmt, nil
|
||||
|
||||
case SEMI:
|
||||
if lastTok != RPAREN {
|
||||
return nil, p.unexpectedToken(RPAREN)
|
||||
}
|
||||
|
||||
return &stmt, nil
|
||||
|
||||
case FOREIGN:
|
||||
if !p.expectSequence(KEY, LPAREN, IDENT) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
columnName := p.last.lit
|
||||
_, _, columnName := p.rescan()
|
||||
|
||||
if !p.expectSequence(RPAREN, REFERENCES) {
|
||||
return nil, p.unexpectedToken()
|
||||
@@ -87,31 +89,31 @@ func (p *Parser) Parse() (*CreateTableStatement, error) {
|
||||
|
||||
stmt.Columns[column].Extra = append(stmt.Columns[column].Extra, ref)
|
||||
|
||||
case COMMA:
|
||||
continue
|
||||
|
||||
default:
|
||||
return nil, p.unexpectedToken(IDENT, RPAREN, SEMI, FOREIGN)
|
||||
return nil, p.unexpectedToken(IDENT, RPAREN, FOREIGN, COMMA)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseColumn() (Column, error) {
|
||||
column := Column{Name: p.last.lit, Extra: make([]string, 0)}
|
||||
_, _, lit := p.rescan()
|
||||
column := Column{Name: lit, Extra: make([]string, 0)}
|
||||
|
||||
_, tok, lit := p.scan()
|
||||
switch tok {
|
||||
case TEXT:
|
||||
fallthrough
|
||||
case INTEGER:
|
||||
column.Type = lit
|
||||
default:
|
||||
if _, ok := p.expectOne(TEXT, INTEGER); !ok {
|
||||
return Column{}, p.unexpectedToken(TEXT, INTEGER)
|
||||
}
|
||||
_, _, column.Type = p.rescan()
|
||||
|
||||
for {
|
||||
_, tok, lit := p.scan()
|
||||
switch tok {
|
||||
case COMMA:
|
||||
fallthrough
|
||||
return column, nil
|
||||
case RPAREN:
|
||||
p.unscan()
|
||||
return column, nil
|
||||
|
||||
case PRIMARY:
|
||||
@@ -120,7 +122,8 @@ func (p *Parser) parseColumn() (Column, error) {
|
||||
if _, ok := p.expectOne(NULL, KEY); !ok {
|
||||
return Column{}, p.unexpectedToken(NULL, KEY)
|
||||
}
|
||||
column.Extra = append(column.Extra, fmt.Sprintf("%v_%v", lit, p.last.lit))
|
||||
_, _, rlit := p.rescan()
|
||||
column.Extra = append(column.Extra, fmt.Sprintf("%v_%v", lit, rlit))
|
||||
|
||||
case REFERENCES:
|
||||
ref, err := p.references()
|
||||
@@ -128,6 +131,7 @@ func (p *Parser) parseColumn() (Column, error) {
|
||||
return Column{}, err
|
||||
}
|
||||
column.Extra = append(column.Extra, ref)
|
||||
fmt.Println(ref)
|
||||
|
||||
default:
|
||||
return Column{}, p.unexpectedToken(COMMA, RPAREN, PRIMARY, NOT, REFERENCES)
|
||||
@@ -137,22 +141,23 @@ func (p *Parser) parseColumn() (Column, error) {
|
||||
|
||||
func (p *Parser) unexpectedToken(expected ...Token) error {
|
||||
l := len(expected)
|
||||
pos, tok, lit := p.rescan()
|
||||
if l <= 0 {
|
||||
return fmt.Errorf("Encountered unexpected token: %v lit: '%v' on pos: %v", p.last.tok, p.last.lit, p.last.pos)
|
||||
return fmt.Errorf("Encountered unexpected token: %v lit: '%v' on pos: %v", tok, lit, pos)
|
||||
} else if l == 1 {
|
||||
return fmt.Errorf(
|
||||
"Encountered unexpected token: %v lit: '%v' on pos: %v, expected %v",
|
||||
p.last.tok,
|
||||
p.last.lit,
|
||||
p.last.pos,
|
||||
tok,
|
||||
lit,
|
||||
pos,
|
||||
expected[0],
|
||||
)
|
||||
} else {
|
||||
return fmt.Errorf(
|
||||
"Encountered unexpected token: %v lit: '%v' on pos: %v, expected one of '%v'",
|
||||
p.last.tok,
|
||||
p.last.lit,
|
||||
p.last.pos,
|
||||
tok,
|
||||
lit,
|
||||
pos,
|
||||
arrayToString(expected, ", "),
|
||||
)
|
||||
}
|
||||
@@ -162,13 +167,13 @@ func (p *Parser) references() (string, error) {
|
||||
if !p.expectNext(IDENT) {
|
||||
return "", p.unexpectedToken(IDENT)
|
||||
}
|
||||
referenceTableName := p.last.lit
|
||||
_, _, referenceTableName := p.rescan()
|
||||
|
||||
if !p.expectSequence(LPAREN, IDENT) {
|
||||
return "", p.unexpectedToken()
|
||||
}
|
||||
|
||||
referenceColumnName := p.last.lit
|
||||
_, _, referenceColumnName := p.rescan()
|
||||
|
||||
if !p.expectNext(RPAREN) {
|
||||
return "", p.unexpectedToken(RPAREN)
|
||||
@@ -201,17 +206,22 @@ func (p *Parser) expectOne(token ...Token) (Token, bool) {
|
||||
}
|
||||
|
||||
func (p *Parser) scan() (Position, Token, string) {
|
||||
pos, tok, lit := p.s.Lex()
|
||||
// fmt.Printf("Scanning next Token: %v | pos: %v | lit: %v\n", tok, pos, lit)
|
||||
|
||||
p.last = struct {
|
||||
pos Position
|
||||
tok Token
|
||||
lit string
|
||||
}{
|
||||
pos,
|
||||
tok,
|
||||
lit,
|
||||
if p.buf.avail {
|
||||
p.buf.avail = false
|
||||
return p.buf.pos, p.buf.tok, p.buf.lit
|
||||
}
|
||||
|
||||
pos, tok, lit := p.s.Lex()
|
||||
|
||||
p.buf.pos, p.buf.tok, p.buf.lit = pos, tok, lit
|
||||
return pos, tok, lit
|
||||
}
|
||||
|
||||
func (p *Parser) unscan() {
|
||||
p.buf.avail = true
|
||||
}
|
||||
|
||||
func (p *Parser) rescan() (Position, Token, string) {
|
||||
p.unscan()
|
||||
return p.scan()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user