Cleanup parser statements, add delete and insert statement, add TASKS.md for tracking tasks
This commit is contained in:
12
sql/ast.go
12
sql/ast.go
@@ -33,5 +33,17 @@ type SelectStatement struct {
|
||||
Fields []string
|
||||
}
|
||||
|
||||
type InsertStatement struct {
|
||||
Table string
|
||||
Values map[string]any
|
||||
}
|
||||
|
||||
type DeleteStatement struct {
|
||||
Table string
|
||||
Extra []string
|
||||
}
|
||||
|
||||
func (_ *CreateTableStatement) isEnumValue() {}
|
||||
func (_ *SelectStatement) isEnumValue() {}
|
||||
func (_ *InsertStatement) isEnumValue() {}
|
||||
func (_ *DeleteStatement) isEnumValue() {}
|
||||
|
||||
12
sql/lexer.go
12
sql/lexer.go
@@ -29,6 +29,11 @@ const (
|
||||
CREATE
|
||||
TABLE
|
||||
|
||||
INSERT
|
||||
INTO
|
||||
VALUES
|
||||
RETURNING
|
||||
|
||||
SELECT
|
||||
FROM
|
||||
WHERE
|
||||
@@ -37,6 +42,8 @@ const (
|
||||
ORDER
|
||||
TOP
|
||||
|
||||
DELETE
|
||||
|
||||
PRIMARY
|
||||
FOREIGN
|
||||
REFERENCES
|
||||
@@ -80,6 +87,11 @@ var keywords map[string]Token = map[string]Token{
|
||||
"AUTOINCREMENT": AUTOINCREMENT,
|
||||
"CONSTRAINT": CONSTRAINT,
|
||||
"NUMERIC": NUMERIC,
|
||||
"INSERT": INSERT,
|
||||
"INTO": INTO,
|
||||
"VALUES": VALUES,
|
||||
"RETURNING": RETURNING,
|
||||
"DELETE": DELETE,
|
||||
}
|
||||
|
||||
type Position struct {
|
||||
|
||||
219
sql/parseCreateStatement.go
Normal file
219
sql/parseCreateStatement.go
Normal file
@@ -0,0 +1,219 @@
|
||||
package sql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
)
|
||||
|
||||
func (p *Parser) parseCreateTable() (*CreateTableStatement, error) {
|
||||
if !p.expectNext(TABLE) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
|
||||
tok, ok := p.expectOne(QUOTE, SINGLE_QUOTE, BACKQUOTE, IDENT, IF)
|
||||
if !ok {
|
||||
return nil, p.unexpectedToken(IDENT, IF)
|
||||
}
|
||||
|
||||
switch tok {
|
||||
case IF:
|
||||
if !p.expectSequence(NOT, EXISTS) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
fallthrough
|
||||
case QUOTE, SINGLE_QUOTE, BACKQUOTE:
|
||||
if !p.expectNext(IDENT) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
}
|
||||
_, _, lit := p.rescan()
|
||||
|
||||
stmt := CreateTableStatement{
|
||||
TableName: lit,
|
||||
Columns: make([]Column, 0),
|
||||
}
|
||||
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
if !p.expectNext(LPAREN) {
|
||||
return nil, p.unexpectedToken(LPAREN)
|
||||
}
|
||||
|
||||
for {
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
_, 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 {
|
||||
return nil, err
|
||||
}
|
||||
stmt.Columns = append(stmt.Columns, column)
|
||||
|
||||
// TODO: HANDLE AND SAVE CONSTRAINTS
|
||||
case CONSTRAINT:
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
if !p.expectNext(IDENT) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
// _, _, constraintName := p.rescan()
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
|
||||
case FOREIGN:
|
||||
if !p.expectSequence(KEY, LPAREN) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
|
||||
if !p.expectNext(IDENT) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
_, _, columnName := p.rescan()
|
||||
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
|
||||
if !p.expectSequence(RPAREN, REFERENCES) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
|
||||
ref, err := p.references()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
column := slices.IndexFunc(stmt.Columns, func(c Column) bool {
|
||||
return c.Name == columnName
|
||||
})
|
||||
|
||||
stmt.Columns[column].Extra = append(stmt.Columns[column].Extra, ref)
|
||||
|
||||
case PRIMARY:
|
||||
if !p.expectSequence(KEY, LPAREN) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
|
||||
if !p.expectNext(IDENT) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
|
||||
primaryKeyNames := make([]string, 0)
|
||||
_, _, columnName := p.rescan()
|
||||
primaryKeyNames = append(primaryKeyNames, columnName)
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
|
||||
for {
|
||||
tok, ok := p.expectOne(RPAREN, COMMA)
|
||||
if !ok {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
if tok == RPAREN {
|
||||
break
|
||||
}
|
||||
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
if !p.expectNext(IDENT) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
|
||||
_, _, columnName := p.rescan()
|
||||
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
primaryKeyNames = append(primaryKeyNames, columnName)
|
||||
}
|
||||
|
||||
for _, pkName := range primaryKeyNames {
|
||||
column := slices.IndexFunc(stmt.Columns, func(c Column) bool {
|
||||
return c.Name == pkName
|
||||
})
|
||||
stmt.Columns[column].Extra = append(stmt.Columns[column].Extra, "PRIMARY_KEY")
|
||||
}
|
||||
|
||||
case COMMA:
|
||||
continue
|
||||
|
||||
default:
|
||||
return nil, p.unexpectedToken(IDENT, RPAREN, FOREIGN, COMMA)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseColumn() (Column, error) {
|
||||
_, _, lit := p.rescan()
|
||||
column := Column{Name: lit, Extra: make([]string, 0)}
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
|
||||
if _, ok := p.expectOne(TEXT, INTEGER, REAL, BLOB, NUMERIC); !ok {
|
||||
return Column{}, p.unexpectedToken(TEXT, INTEGER, REAL, BLOB, NUMERIC)
|
||||
}
|
||||
_, _, column.Type = p.rescan()
|
||||
|
||||
for {
|
||||
_, tok, lit := p.scan()
|
||||
switch tok {
|
||||
case COMMA:
|
||||
return column, nil
|
||||
case RPAREN:
|
||||
p.unscan()
|
||||
return column, nil
|
||||
|
||||
case PRIMARY:
|
||||
fallthrough
|
||||
case NOT:
|
||||
if _, ok := p.expectOne(NULL, KEY); !ok {
|
||||
return Column{}, p.unexpectedToken(NULL, KEY)
|
||||
}
|
||||
_, _, rlit := p.rescan()
|
||||
column.Extra = append(column.Extra, fmt.Sprintf("%v_%v", lit, rlit))
|
||||
|
||||
case REFERENCES:
|
||||
ref, err := p.references()
|
||||
if err != nil {
|
||||
return Column{}, err
|
||||
}
|
||||
column.Extra = append(column.Extra, ref)
|
||||
|
||||
case AUTOINCREMENT:
|
||||
column.Extra = append(column.Extra, "AUTOINCREMENT")
|
||||
|
||||
default:
|
||||
return Column{}, p.unexpectedToken(COMMA, RPAREN, PRIMARY, NOT, REFERENCES)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) references() (string, error) {
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
if !p.expectNext(IDENT) {
|
||||
return "", p.unexpectedToken(IDENT)
|
||||
}
|
||||
_, _, referenceTableName := p.rescan()
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
|
||||
if !p.expectNext(LPAREN) {
|
||||
return "", p.unexpectedToken()
|
||||
}
|
||||
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
|
||||
if !p.expectNext(IDENT) {
|
||||
return "", p.unexpectedToken()
|
||||
}
|
||||
_, _, referenceColumnName := p.rescan()
|
||||
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
|
||||
if !p.expectNext(RPAREN) {
|
||||
return "", p.unexpectedToken(RPAREN)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("ref %v.%v", referenceTableName, referenceColumnName), nil
|
||||
}
|
||||
15
sql/parseDeleteStatement.go
Normal file
15
sql/parseDeleteStatement.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package sql
|
||||
|
||||
func (p *Parser) parseDelete() (*DeleteStatement, error) {
|
||||
if !p.expectSequence(FROM, IDENT) {
|
||||
return nil, p.unexpectedToken(INTO)
|
||||
}
|
||||
|
||||
res := DeleteStatement{}
|
||||
|
||||
_, _, res.Table = p.rescan()
|
||||
|
||||
p.consumeUntilOne(50, EOF, SEMI)
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
69
sql/parseInsertStatement.go
Normal file
69
sql/parseInsertStatement.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package sql
|
||||
|
||||
import "fmt"
|
||||
|
||||
func (p *Parser) parseInsert() (*InsertStatement, error) {
|
||||
if !p.expectSequence(INTO, IDENT) {
|
||||
return nil, p.unexpectedToken(INTO)
|
||||
}
|
||||
|
||||
res := InsertStatement{}
|
||||
|
||||
_, _, res.Table = p.rescan()
|
||||
|
||||
if !p.expectNext(LPAREN) {
|
||||
return nil, p.unexpectedToken(LPAREN)
|
||||
}
|
||||
|
||||
fieldNames := make([]string, 0)
|
||||
|
||||
for loop := true; loop; {
|
||||
_, tok, val := p.scan()
|
||||
switch tok {
|
||||
case IDENT:
|
||||
fieldNames = append(fieldNames, val)
|
||||
case RPAREN:
|
||||
loop = false
|
||||
case COMMA:
|
||||
continue
|
||||
default:
|
||||
return nil, p.unexpectedToken(IDENT, RPAREN, COMMA)
|
||||
}
|
||||
}
|
||||
|
||||
if !p.expectSequence(VALUES, LPAREN) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
|
||||
values := make([]any, 0, len(fieldNames))
|
||||
for loop := true; loop; {
|
||||
_, tok, val := p.scan()
|
||||
switch tok {
|
||||
case IDENT:
|
||||
// TODO, convert to actual datatype?
|
||||
values = append(values, val)
|
||||
case COMMA, QUOTE, SINGLE_QUOTE, BACKQUOTE:
|
||||
continue
|
||||
case RPAREN:
|
||||
loop = false
|
||||
default:
|
||||
return nil, p.unexpectedToken(IDENT, RPAREN, COMMA)
|
||||
}
|
||||
}
|
||||
|
||||
if len(values) != len(fieldNames) {
|
||||
return nil, fmt.Errorf("Expected same amount of Values as Fields, but got %v fields, and %v values", fieldNames, values)
|
||||
}
|
||||
|
||||
// Handle things like RETURNING *, also handle multiple Values
|
||||
if !p.consumeUntilOne(50, SEMI, EOF) {
|
||||
return nil, fmt.Errorf("Expected semicolon but never found after 50 tries")
|
||||
}
|
||||
|
||||
res.Values = make(map[string]any)
|
||||
for i, name := range fieldNames {
|
||||
res.Values[name] = values[i]
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
47
sql/parseSelectStatement.go
Normal file
47
sql/parseSelectStatement.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package sql
|
||||
|
||||
import "fmt"
|
||||
|
||||
func (p *Parser) parseSelect() (*SelectStatement, error) {
|
||||
tok, ok := p.expectOne(ASTERIKS, IDENT)
|
||||
if !ok {
|
||||
return nil, p.unexpectedToken(ASTERIKS, IDENT)
|
||||
}
|
||||
|
||||
fields := make([]string, 1)
|
||||
fields[0] = "*"
|
||||
if tok == IDENT {
|
||||
_, _, n := p.rescan()
|
||||
fields[0] = n
|
||||
for {
|
||||
tok, ok := p.expectOne(COMMA, FROM)
|
||||
if !ok {
|
||||
return nil, p.unexpectedToken(COMMA, FROM)
|
||||
}
|
||||
if tok == FROM {
|
||||
p.unscan()
|
||||
break
|
||||
}
|
||||
|
||||
if !p.expectNext(IDENT) {
|
||||
return nil, p.unexpectedToken(IDENT)
|
||||
}
|
||||
_, _, n := p.rescan()
|
||||
fields = append(fields, n)
|
||||
}
|
||||
}
|
||||
|
||||
if !p.expectSequence(FROM, IDENT) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
|
||||
_, _, tableName := p.rescan()
|
||||
if !p.consumeUntilOne(50, SEMI, EOF) {
|
||||
return nil, fmt.Errorf("Expected semicolon but never found after 50 tries")
|
||||
}
|
||||
|
||||
return &SelectStatement{
|
||||
From: tableName,
|
||||
Fields: fields,
|
||||
}, nil
|
||||
}
|
||||
265
sql/parser.go
265
sql/parser.go
@@ -23,9 +23,9 @@ func NewParser(r io.Reader) *Parser {
|
||||
}
|
||||
|
||||
func (p *Parser) Parse() (Statement, error) {
|
||||
tok, ok := p.expectOne(CREATE, EOF, SELECT)
|
||||
tok, ok := p.expectOne(CREATE, EOF, SELECT, INSERT, DELETE)
|
||||
if !ok {
|
||||
return nil, p.unexpectedToken(CREATE, EOF)
|
||||
return nil, p.unexpectedToken(CREATE, EOF, SELECT, INSERT, DELETE)
|
||||
} else if tok == EOF {
|
||||
return nil, io.EOF
|
||||
}
|
||||
@@ -37,240 +37,15 @@ func (p *Parser) Parse() (Statement, error) {
|
||||
return p.parseCreateTable()
|
||||
case SELECT:
|
||||
return p.parseSelect()
|
||||
case INSERT:
|
||||
return p.parseInsert()
|
||||
case DELETE:
|
||||
return p.parseDelete()
|
||||
default:
|
||||
panic("SHOULD NEVER BE REACHED")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseSelect() (*SelectStatement, error) {
|
||||
tok, ok := p.expectOne(ASTERIKS, IDENT)
|
||||
if !ok {
|
||||
return nil, p.unexpectedToken(ASTERIKS, IDENT)
|
||||
}
|
||||
|
||||
fields := make([]string, 1)
|
||||
fields[0] = "*"
|
||||
if tok == IDENT {
|
||||
_, _, n := p.rescan()
|
||||
fields[0] = n
|
||||
for {
|
||||
tok, ok := p.expectOne(COMMA, FROM)
|
||||
if !ok {
|
||||
return nil, p.unexpectedToken(COMMA, FROM)
|
||||
}
|
||||
if tok == FROM {
|
||||
p.unscan()
|
||||
break
|
||||
}
|
||||
|
||||
if !p.expectNext(IDENT) {
|
||||
return nil, p.unexpectedToken(IDENT)
|
||||
}
|
||||
_, _, n := p.rescan()
|
||||
fields = append(fields, n)
|
||||
}
|
||||
}
|
||||
|
||||
if !p.expectSequence(FROM, IDENT) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
|
||||
_, _, tableName := p.rescan()
|
||||
if !p.consumeUntilOne(50, SEMI, EOF) {
|
||||
return nil, fmt.Errorf("Expected semicolon but never found after 50 tries")
|
||||
}
|
||||
|
||||
return &SelectStatement{
|
||||
From: tableName,
|
||||
Fields: fields,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseCreateTable() (*CreateTableStatement, error) {
|
||||
if !p.expectNext(TABLE) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
|
||||
tok, ok := p.expectOne(QUOTE, SINGLE_QUOTE, BACKQUOTE, IDENT, IF)
|
||||
if !ok {
|
||||
return nil, p.unexpectedToken(IDENT, IF)
|
||||
}
|
||||
|
||||
switch tok {
|
||||
case IF:
|
||||
if !p.expectSequence(NOT, EXISTS) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
fallthrough
|
||||
case QUOTE, SINGLE_QUOTE, BACKQUOTE:
|
||||
if !p.expectNext(IDENT) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
}
|
||||
_, _, lit := p.rescan()
|
||||
|
||||
stmt := CreateTableStatement{
|
||||
TableName: lit,
|
||||
Columns: make([]Column, 0),
|
||||
}
|
||||
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
if !p.expectNext(LPAREN) {
|
||||
return nil, p.unexpectedToken(LPAREN)
|
||||
}
|
||||
|
||||
for {
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
_, 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 {
|
||||
return nil, err
|
||||
}
|
||||
stmt.Columns = append(stmt.Columns, column)
|
||||
|
||||
// TODO: HANDLE AND SAVE CONSTRAINTS
|
||||
case CONSTRAINT:
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
if !p.expectNext(IDENT) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
// _, _, constraintName := p.rescan()
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
|
||||
case FOREIGN:
|
||||
if !p.expectSequence(KEY, LPAREN) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
|
||||
if !p.expectNext(IDENT) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
_, _, columnName := p.rescan()
|
||||
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
|
||||
if !p.expectSequence(RPAREN, REFERENCES) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
|
||||
ref, err := p.references()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
column := slices.IndexFunc(stmt.Columns, func(c Column) bool {
|
||||
return c.Name == columnName
|
||||
})
|
||||
|
||||
stmt.Columns[column].Extra = append(stmt.Columns[column].Extra, ref)
|
||||
|
||||
case PRIMARY:
|
||||
if !p.expectSequence(KEY, LPAREN) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
|
||||
if !p.expectNext(IDENT) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
|
||||
primaryKeyNames := make([]string, 0)
|
||||
_, _, columnName := p.rescan()
|
||||
primaryKeyNames = append(primaryKeyNames, columnName)
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
|
||||
for {
|
||||
tok, ok := p.expectOne(RPAREN, COMMA)
|
||||
if !ok {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
if tok == RPAREN {
|
||||
break
|
||||
}
|
||||
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
if !p.expectNext(IDENT) {
|
||||
return nil, p.unexpectedToken()
|
||||
}
|
||||
|
||||
_, _, columnName := p.rescan()
|
||||
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
primaryKeyNames = append(primaryKeyNames, columnName)
|
||||
}
|
||||
|
||||
for _, pkName := range primaryKeyNames {
|
||||
column := slices.IndexFunc(stmt.Columns, func(c Column) bool {
|
||||
return c.Name == pkName
|
||||
})
|
||||
stmt.Columns[column].Extra = append(stmt.Columns[column].Extra, "PRIMARY_KEY")
|
||||
}
|
||||
|
||||
case COMMA:
|
||||
continue
|
||||
|
||||
default:
|
||||
return nil, p.unexpectedToken(IDENT, RPAREN, FOREIGN, COMMA)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseColumn() (Column, error) {
|
||||
_, _, lit := p.rescan()
|
||||
column := Column{Name: lit, Extra: make([]string, 0)}
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
|
||||
if _, ok := p.expectOne(TEXT, INTEGER, REAL, BLOB, NUMERIC); !ok {
|
||||
return Column{}, p.unexpectedToken(TEXT, INTEGER, REAL, BLOB, NUMERIC)
|
||||
}
|
||||
_, _, column.Type = p.rescan()
|
||||
|
||||
for {
|
||||
_, tok, lit := p.scan()
|
||||
switch tok {
|
||||
case COMMA:
|
||||
return column, nil
|
||||
case RPAREN:
|
||||
p.unscan()
|
||||
return column, nil
|
||||
|
||||
case PRIMARY:
|
||||
fallthrough
|
||||
case NOT:
|
||||
if _, ok := p.expectOne(NULL, KEY); !ok {
|
||||
return Column{}, p.unexpectedToken(NULL, KEY)
|
||||
}
|
||||
_, _, rlit := p.rescan()
|
||||
column.Extra = append(column.Extra, fmt.Sprintf("%v_%v", lit, rlit))
|
||||
|
||||
case REFERENCES:
|
||||
ref, err := p.references()
|
||||
if err != nil {
|
||||
return Column{}, err
|
||||
}
|
||||
column.Extra = append(column.Extra, ref)
|
||||
|
||||
case AUTOINCREMENT:
|
||||
column.Extra = append(column.Extra, "AUTOINCREMENT")
|
||||
|
||||
default:
|
||||
return Column{}, p.unexpectedToken(COMMA, RPAREN, PRIMARY, NOT, REFERENCES)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) unexpectedToken(expected ...Token) error {
|
||||
l := len(expected)
|
||||
pos, tok, lit := p.rescan()
|
||||
@@ -295,34 +70,6 @@ func (p *Parser) unexpectedToken(expected ...Token) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) references() (string, error) {
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
if !p.expectNext(IDENT) {
|
||||
return "", p.unexpectedToken(IDENT)
|
||||
}
|
||||
_, _, referenceTableName := p.rescan()
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
|
||||
if !p.expectNext(LPAREN) {
|
||||
return "", p.unexpectedToken()
|
||||
}
|
||||
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
|
||||
if !p.expectNext(IDENT) {
|
||||
return "", p.unexpectedToken()
|
||||
}
|
||||
_, _, referenceColumnName := p.rescan()
|
||||
|
||||
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
||||
|
||||
if !p.expectNext(RPAREN) {
|
||||
return "", p.unexpectedToken(RPAREN)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("ref %v.%v", referenceTableName, referenceColumnName), nil
|
||||
}
|
||||
|
||||
func (p *Parser) expectSequence(token ...Token) bool {
|
||||
for _, tok := range token {
|
||||
if !p.expectNext(tok) {
|
||||
|
||||
Reference in New Issue
Block a user