diff --git a/cmd/sqv/main.go b/cmd/sqv/main.go index bb88be7..050e496 100644 --- a/cmd/sqv/main.go +++ b/cmd/sqv/main.go @@ -74,5 +74,6 @@ CREATE TABLE IF NOT EXISTS auth_states ( } stmt.Print() + fmt.Println() } } diff --git a/go.mod b/go.mod index bde28c3..20a24f2 100644 --- a/go.mod +++ b/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 - diff --git a/sql/lexer.go b/sql/lexer.go index e694343..8ce58d5 100644 --- a/sql/lexer.go +++ b/sql/lexer.go @@ -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 { diff --git a/sql/parser.go b/sql/parser.go index 1d80de0..5c581c3 100644 --- a/sql/parser.go +++ b/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() +}