Improvements on sql parser
This commit is contained in:
parent
d76038652b
commit
08943946a9
17 changed files with 1447 additions and 733 deletions
199
sql/ast.go
199
sql/ast.go
|
@ -30,20 +30,45 @@ type Statement interface{}
|
||||||
type Statements []Statement
|
type Statements []Statement
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
// Select
|
// Use
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
// UseStatement represents a SQL USE statement.
|
// UseStatement represents a SQL USE statement.
|
||||||
type UseStatement struct {
|
type UseStatement struct {
|
||||||
NS string // Namespace
|
NS string // Namespace
|
||||||
DB string // Database
|
DB string // Database
|
||||||
CK string // Cipherkey
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
// Select
|
// Trans
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
|
type BeginStatement struct{}
|
||||||
|
|
||||||
|
type CancelStatement struct{}
|
||||||
|
|
||||||
|
type CommitStatement struct{}
|
||||||
|
|
||||||
|
// --------------------------------------------------
|
||||||
|
// Normal
|
||||||
|
// --------------------------------------------------
|
||||||
|
|
||||||
|
// ActionStatement represents a SQL ACTION statement.
|
||||||
|
type ActionStatement struct {
|
||||||
|
EX bool // Explain
|
||||||
|
KV string // Bucket
|
||||||
|
NS string // Namespace
|
||||||
|
DB string // Database
|
||||||
|
Expr []*Field // Which fields
|
||||||
|
What []Expr // What to select
|
||||||
|
Cond []Expr // Select conditions
|
||||||
|
Group []*Group // Group by
|
||||||
|
Order []*Order // Order by
|
||||||
|
Limit Expr // Limit by
|
||||||
|
Start Expr // Start at
|
||||||
|
Version Expr // Version
|
||||||
|
}
|
||||||
|
|
||||||
// SelectStatement represents a SQL SELECT statement.
|
// SelectStatement represents a SQL SELECT statement.
|
||||||
type SelectStatement struct {
|
type SelectStatement struct {
|
||||||
EX bool // Explain
|
EX bool // Explain
|
||||||
|
@ -58,12 +83,9 @@ type SelectStatement struct {
|
||||||
Limit Expr // Limit by
|
Limit Expr // Limit by
|
||||||
Start Expr // Start at
|
Start Expr // Start at
|
||||||
Version Expr // Version
|
Version Expr // Version
|
||||||
|
Echo Token // What to return
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
|
||||||
// Items
|
|
||||||
// --------------------------------------------------
|
|
||||||
|
|
||||||
// CreateStatement represents a SQL CREATE statement.
|
// CreateStatement represents a SQL CREATE statement.
|
||||||
//
|
//
|
||||||
// CREATE person SET column = 'value' RETURN ID
|
// CREATE person SET column = 'value' RETURN ID
|
||||||
|
@ -157,22 +179,26 @@ type RecordStatement struct {
|
||||||
//
|
//
|
||||||
// DEFINE RULES person
|
// DEFINE RULES person
|
||||||
type DefineRulesStatement struct {
|
type DefineRulesStatement struct {
|
||||||
EX bool // Explain
|
EX bool `json:"-" msgpack:"-"` // Explain
|
||||||
KV string // Bucket
|
KV string `json:"-" msgpack:"-"` // Bucket
|
||||||
NS string // Namespace
|
NS string `json:"-" msgpack:"-"` // Namespace
|
||||||
DB string // Database
|
DB string `json:"-" msgpack:"-"` // Database
|
||||||
What []Table // Table names
|
What []string `json:"-" msgpack:"-"` // Table names
|
||||||
|
When []string `json:"-" msgpack:"-"` // Action names
|
||||||
|
Rule string `json:"rule" msgpack:"rule"` // Rule behaviour
|
||||||
|
Code string `json:"code" msgpack:"code"` // Rule custom code
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveRulesStatement represents an SQL REMOVE RULES statement.
|
// RemoveRulesStatement represents an SQL REMOVE RULES statement.
|
||||||
//
|
//
|
||||||
// REMOVE RULES person
|
// REMOVE RULES person
|
||||||
type RemoveRulesStatement struct {
|
type RemoveRulesStatement struct {
|
||||||
EX bool // Explain
|
EX bool `json:"-" msgpack:"-"` // Explain
|
||||||
KV string // Bucket
|
KV string `json:"-" msgpack:"-"` // Bucket
|
||||||
NS string // Namespace
|
NS string `json:"-" msgpack:"-"` // Namespace
|
||||||
DB string // Database
|
DB string `json:"-" msgpack:"-"` // Database
|
||||||
What []Table // Table names
|
What []string `json:"-" msgpack:"-"` // Table names
|
||||||
|
When []string `json:"-" msgpack:"-"` // Action names
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
@ -183,22 +209,22 @@ type RemoveRulesStatement struct {
|
||||||
//
|
//
|
||||||
// DEFINE TABLE person
|
// DEFINE TABLE person
|
||||||
type DefineTableStatement struct {
|
type DefineTableStatement struct {
|
||||||
EX bool // Explain
|
EX bool `json:"-" msgpack:"-"` // Explain
|
||||||
KV string // Bucket
|
KV string `json:"-" msgpack:"-"` // Bucket
|
||||||
NS string // Namespace
|
NS string `json:"-" msgpack:"-"` // Namespace
|
||||||
DB string // Database
|
DB string `json:"-" msgpack:"-"` // Database
|
||||||
What []Table // Table names
|
What []string `json:"-" msgpack:"-"` // Table names
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveTableStatement represents an SQL REMOVE TABLE statement.
|
// RemoveTableStatement represents an SQL REMOVE TABLE statement.
|
||||||
//
|
//
|
||||||
// REMOVE TABLE person
|
// REMOVE TABLE person
|
||||||
type RemoveTableStatement struct {
|
type RemoveTableStatement struct {
|
||||||
EX bool // Explain
|
EX bool `json:"-" msgpack:"-"` // Explain
|
||||||
KV string // Bucket
|
KV string `json:"-" msgpack:"-"` // Bucket
|
||||||
NS string // Namespace
|
NS string `json:"-" msgpack:"-"` // Namespace
|
||||||
DB string // Database
|
DB string `json:"-" msgpack:"-"` // Database
|
||||||
What []Table // Table names
|
What []string `json:"-" msgpack:"-"` // Table names
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
@ -211,35 +237,35 @@ type RemoveTableStatement struct {
|
||||||
// DEFINE FIELD name ON person TYPE number MIN 0 MAX 5 DEFAULT 0
|
// DEFINE FIELD name ON person TYPE number MIN 0 MAX 5 DEFAULT 0
|
||||||
// DEFINE FIELD name ON person TYPE custom ENUM [0,1,2,3,4,5] DEFAULT 0
|
// DEFINE FIELD name ON person TYPE custom ENUM [0,1,2,3,4,5] DEFAULT 0
|
||||||
type DefineFieldStatement struct {
|
type DefineFieldStatement struct {
|
||||||
EX bool // Explain
|
EX bool `json:"-" msgpack:"-"` // Explain
|
||||||
KV string // Bucket
|
KV string `json:"-" msgpack:"-"` // Bucket
|
||||||
NS string // Namespace
|
NS string `json:"-" msgpack:"-"` // Namespace
|
||||||
DB string // Database
|
DB string `json:"-" msgpack:"-"` // Database
|
||||||
Name Ident // Field name
|
Name string `json:"name" msgpack:"name"` // Field name
|
||||||
What []Table // Table names
|
What []string `json:"-" msgpack:"-"` // Table names
|
||||||
Type Ident // Field type
|
Type string `json:"type" msgpack:"type"` // Field type
|
||||||
Enum []interface{} // Custom options
|
Enum []interface{} `json:"enum" msgpack:"enum"` // Custom options
|
||||||
Code string // Field code
|
Code string `json:"code" msgpack:"code"` // Field code
|
||||||
Min float64 // Minimum value / length
|
Min float64 `json:"min" msgpack:"min"` // Minimum value / length
|
||||||
Max float64 // Maximum value / length
|
Max float64 `json:"max" msgpack:"max"` // Maximum value / length
|
||||||
Match string // Regex value
|
Match string `json:"match" msgpack:"match"` // Regex value
|
||||||
Default interface{} // Default value
|
Default interface{} `json:"default" msgpack:"default"` // Default value
|
||||||
Notnull bool // Notnull - can not be NULL?
|
Notnull bool `json:"notnull" msgpack:"notnull"` // Notnull - can not be NULL?
|
||||||
Readonly bool // Readonly - can not be changed?
|
Readonly bool `json:"readonly" msgpack:"readonly"` // Readonly - can not be changed?
|
||||||
Mandatory bool // Mandatory - can not be VOID?
|
Mandatory bool `json:"mandatory" msgpack:"mandatory"` // Mandatory - can not be VOID?
|
||||||
Validate bool // Validate - can not be INCORRECT?
|
Validate bool `json:"validate" msgpack:"validate"` // Validate - can not be INCORRECT?
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveFieldStatement represents an SQL REMOVE INDEX statement.
|
// RemoveFieldStatement represents an SQL REMOVE INDEX statement.
|
||||||
//
|
//
|
||||||
// REMOVE FIELD name ON person
|
// REMOVE FIELD name ON person
|
||||||
type RemoveFieldStatement struct {
|
type RemoveFieldStatement struct {
|
||||||
EX bool // Explain
|
EX bool `json:"-" msgpack:"-"` // Explain
|
||||||
KV string // Bucket
|
KV string `json:"-" msgpack:"-"` // Bucket
|
||||||
NS string // Namespace
|
NS string `json:"-" msgpack:"-"` // Namespace
|
||||||
DB string // Database
|
DB string `json:"-" msgpack:"-"` // Database
|
||||||
Name Ident // Field name
|
Name string `json:"-" msgpack:"-"` // Field name
|
||||||
What []Table // Table names
|
What []string `json:"-" msgpack:"-"` // Table names
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
@ -250,38 +276,39 @@ type RemoveFieldStatement struct {
|
||||||
//
|
//
|
||||||
// DEFINE INDEX name ON person COLUMNS (account, age) UNIQUE
|
// DEFINE INDEX name ON person COLUMNS (account, age) UNIQUE
|
||||||
type DefineIndexStatement struct {
|
type DefineIndexStatement struct {
|
||||||
EX bool // Explain
|
EX bool `json:"-" msgpack:"-"` // Explain
|
||||||
KV string // Bucket
|
KV string `json:"-" msgpack:"-"` // Bucket
|
||||||
NS string // Namespace
|
NS string `json:"-" msgpack:"-"` // Namespace
|
||||||
DB string // Database
|
DB string `json:"-" msgpack:"-"` // Database
|
||||||
Name Ident // Index name
|
Name string `json:"name" msgpack:"name"` // Index name
|
||||||
What []Table // Table names
|
What []string `json:"-" msgpack:"-"` // Table names
|
||||||
Code string // Index code
|
Cols []string `json:"cols" msgpack:"cols"` // Index cols
|
||||||
Cols []*Field // Index cols
|
Uniq bool `json:"unique" msgpack:"unique"` // Unique index
|
||||||
Uniq bool // Unique index
|
CI bool
|
||||||
|
CS bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveIndexStatement represents an SQL REMOVE INDEX statement.
|
// RemoveIndexStatement represents an SQL REMOVE INDEX statement.
|
||||||
//
|
//
|
||||||
// REMOVE INDEX name ON person
|
// REMOVE INDEX name ON person
|
||||||
type RemoveIndexStatement struct {
|
type RemoveIndexStatement struct {
|
||||||
EX bool // Explain
|
EX bool `json:"-" msgpack:"-"` // Explain
|
||||||
KV string // Bucket
|
KV string `json:"-" msgpack:"-"` // Bucket
|
||||||
NS string // Namespace
|
NS string `json:"-" msgpack:"-"` // Namespace
|
||||||
DB string // Database
|
DB string `json:"-" msgpack:"-"` // Database
|
||||||
Name Ident // Index name
|
Name string `json:"-" msgpack:"-"` // Index name
|
||||||
What []Table // Table names
|
What []string `json:"-" msgpack:"-"` // Table names
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResyncIndexStatement represents an SQL RESYNC INDEX statement.
|
// ResyncIndexStatement represents an SQL RESYNC INDEX statement.
|
||||||
//
|
//
|
||||||
// RESYNC INDEX name ON person
|
// RESYNC INDEX name ON person
|
||||||
type ResyncIndexStatement struct {
|
type ResyncIndexStatement struct {
|
||||||
EX bool // Explain
|
EX bool `json:"-" msgpack:"-"` // Explain
|
||||||
KV string // Bucket
|
KV string `json:"-" msgpack:"-"` // Bucket
|
||||||
NS string // Namespace
|
NS string `json:"-" msgpack:"-"` // Namespace
|
||||||
DB string // Database
|
DB string `json:"-" msgpack:"-"` // Database
|
||||||
What []Table // Table names
|
What []string `json:"-" msgpack:"-"` // Table names
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
@ -309,9 +336,6 @@ type Void struct{}
|
||||||
// Empty represents an expression which is null or "".
|
// Empty represents an expression which is null or "".
|
||||||
type Empty struct{}
|
type Empty struct{}
|
||||||
|
|
||||||
// Wildcard represents a wildcard expression.
|
|
||||||
type Wildcard struct{}
|
|
||||||
|
|
||||||
// ClosedExpression represents a parenthesized expression.
|
// ClosedExpression represents a parenthesized expression.
|
||||||
type ClosedExpression struct {
|
type ClosedExpression struct {
|
||||||
Expr Expr
|
Expr Expr
|
||||||
|
@ -320,7 +344,7 @@ type ClosedExpression struct {
|
||||||
// BinaryExpression represents a binary expression tree,
|
// BinaryExpression represents a binary expression tree,
|
||||||
type BinaryExpression struct {
|
type BinaryExpression struct {
|
||||||
LHS Expr
|
LHS Expr
|
||||||
Op string
|
Op Token
|
||||||
RHS Expr
|
RHS Expr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,23 +367,14 @@ type ContentExpression struct {
|
||||||
// Parts
|
// Parts
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
type Table string
|
// Ident comment
|
||||||
|
type Ident struct {
|
||||||
func (this Table) String() string {
|
ID string
|
||||||
return string(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this Table) MarshalText() ([]byte, error) {
|
// Table comment
|
||||||
return []byte(string(this)), nil
|
type Table struct {
|
||||||
}
|
TB string
|
||||||
|
|
||||||
type Ident string
|
|
||||||
|
|
||||||
func (this Ident) String() string {
|
|
||||||
return string(this)
|
|
||||||
}
|
|
||||||
func (this Ident) MarshalText() ([]byte, error) {
|
|
||||||
return []byte(string(this)), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thing comment
|
// Thing comment
|
||||||
|
@ -371,15 +386,15 @@ type Thing struct {
|
||||||
// Field comment
|
// Field comment
|
||||||
type Field struct {
|
type Field struct {
|
||||||
Expr Expr
|
Expr Expr
|
||||||
Alias Expr
|
Alias string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group comment
|
// Group represents an sql GROUP BY clause
|
||||||
type Group struct {
|
type Group struct {
|
||||||
Expr Expr
|
Expr Expr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Order comment
|
// Order represents an sql ORDER BY clause
|
||||||
type Order struct {
|
type Order struct {
|
||||||
Expr Expr
|
Expr Expr
|
||||||
Dir Expr
|
Dir Expr
|
||||||
|
|
35
sql/cond.go
35
sql/cond.go
|
@ -28,7 +28,7 @@ func (p *Parser) parseCond() (mul []Expr, err error) {
|
||||||
|
|
||||||
one := &BinaryExpression{}
|
one := &BinaryExpression{}
|
||||||
|
|
||||||
tok, lit, err = p.shouldBe(IDENT, ID, TIME, TRUE, FALSE, STRING, NUMBER, DOUBLE)
|
tok, lit, err = p.shouldBe(ID, IDENT, NULL, VOID, MISSING, EMPTY, NOW, DATE, TIME, TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE, REGEX, JSON, ARRAY, BOUNDPARAM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &ParseError{Found: lit, Expected: []string{"field name"}}
|
return nil, &ParseError{Found: lit, Expected: []string{"field name"}}
|
||||||
}
|
}
|
||||||
|
@ -38,13 +38,39 @@ func (p *Parser) parseCond() (mul []Expr, err error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tok, lit, err = p.shouldBe(IN, EQ, NEQ, GT, LT, GTE, LTE, EQR, NER, SEQ, SNE)
|
tok, lit, err = p.shouldBe(IS, IN, EQ, NEQ, EEQ, NEE, ANY, LT, LTE, GT, GTE, SIN, SNI, INS, NIS, CONTAINS, CONTAINSALL, CONTAINSNONE, CONTAINSSOME, ALLCONTAINEDIN, NONECONTAINEDIN, SOMECONTAINEDIN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
one.Op = lit
|
one.Op = tok
|
||||||
|
|
||||||
tok, lit, err = p.shouldBe(IDENT, ID, NULL, EMPTY, NOW, DATE, TIME, TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE, REGEX, JSON, ARRAY)
|
if tok == IN {
|
||||||
|
one.Op = INS
|
||||||
|
}
|
||||||
|
|
||||||
|
if tok == IS {
|
||||||
|
one.Op = EQ
|
||||||
|
if _, _, exi := p.mightBe(NOT); exi {
|
||||||
|
one.Op = NEQ
|
||||||
|
}
|
||||||
|
if _, _, exi := p.mightBe(IN); exi {
|
||||||
|
switch one.Op {
|
||||||
|
case EQ:
|
||||||
|
one.Op = INS
|
||||||
|
case NEQ:
|
||||||
|
one.Op = NIS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tok == CONTAINS {
|
||||||
|
one.Op = SIN
|
||||||
|
if _, _, exi := p.mightBe(NOT); exi {
|
||||||
|
one.Op = SNI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tok, lit, err = p.shouldBe(ID, IDENT, NULL, VOID, MISSING, EMPTY, NOW, DATE, TIME, TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE, REGEX, JSON, ARRAY, BOUNDPARAM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &ParseError{Found: lit, Expected: []string{"field value"}}
|
return nil, &ParseError{Found: lit, Expected: []string{"field value"}}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +82,6 @@ func (p *Parser) parseCond() (mul []Expr, err error) {
|
||||||
|
|
||||||
mul = append(mul, one)
|
mul = append(mul, one)
|
||||||
|
|
||||||
// Remove the WHERE keyword
|
|
||||||
if _, _, exi := p.mightBe(AND, OR); !exi {
|
if _, _, exi := p.mightBe(AND, OR); !exi {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,9 +65,9 @@ func (p *Parser) parseSet() (mul []Expr, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
one.Op = lit
|
one.Op = tok
|
||||||
|
|
||||||
tok, lit, err = p.shouldBe(IDENT, NULL, VOID, NOW, DATE, TIME, TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE, JSON, ARRAY)
|
tok, lit, err = p.shouldBe(IDENT, NULL, VOID, NOW, DATE, TIME, TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE, JSON, ARRAY, BOUNDPARAM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &ParseError{Found: lit, Expected: []string{"field value"}}
|
return nil, &ParseError{Found: lit, Expected: []string{"field value"}}
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ func (p *Parser) parseDiff() (exp []Expr, err error) {
|
||||||
|
|
||||||
one := &DiffExpression{}
|
one := &DiffExpression{}
|
||||||
|
|
||||||
tok, lit, err := p.shouldBe(JSON)
|
tok, lit, err := p.shouldBe(JSON, ARRAY)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &ParseError{Found: lit, Expected: []string{"json"}}
|
return nil, &ParseError{Found: lit, Expected: []string{"json"}}
|
||||||
}
|
}
|
||||||
|
|
76
sql/exprs.go
76
sql/exprs.go
|
@ -16,26 +16,59 @@ package sql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (p *Parser) parseName() (string, error) {
|
||||||
|
|
||||||
|
_, lit, err := p.shouldBe(IDENT)
|
||||||
|
if err != nil {
|
||||||
|
return string(""), &ParseError{Found: lit, Expected: []string{"name"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err := declare(STRING, lit)
|
||||||
|
|
||||||
|
return val.(string), err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseNames() (mul []string, err error) {
|
||||||
|
|
||||||
|
for {
|
||||||
|
|
||||||
|
one, err := p.parseName()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mul = append(mul, one)
|
||||||
|
|
||||||
|
// If the next token is not a comma then break the loop.
|
||||||
|
if _, _, exi := p.mightBe(COMMA); !exi {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
//
|
//
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
func (p *Parser) parseTable() (Table, error) {
|
func (p *Parser) parseTable() (*Table, error) {
|
||||||
|
|
||||||
_, lit, err := p.shouldBe(IDENT, NUMBER, DATE)
|
_, lit, err := p.shouldBe(IDENT, NUMBER, DATE)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Table(""), &ParseError{Found: lit, Expected: []string{"table name"}}
|
return nil, &ParseError{Found: lit, Expected: []string{"table name"}}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Table(lit), err
|
return &Table{lit}, err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseTables() (mul []Table, err error) {
|
func (p *Parser) parseTables() (mul []*Table, err error) {
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
|
||||||
|
@ -92,14 +125,8 @@ func (p *Parser) parseThing() (one *Thing, err error) {
|
||||||
switch tok {
|
switch tok {
|
||||||
case IDENT:
|
case IDENT:
|
||||||
val = lit
|
val = lit
|
||||||
case NUMBER:
|
default:
|
||||||
val, err = strconv.ParseInt(lit, 10, 64)
|
val, err = declare(tok, lit)
|
||||||
case DOUBLE:
|
|
||||||
val, err = strconv.ParseFloat(lit, 64)
|
|
||||||
case DATE:
|
|
||||||
val, err = time.Parse("2006-01-02", lit)
|
|
||||||
case TIME:
|
|
||||||
val, err = time.Parse(time.RFC3339, lit)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -138,16 +165,16 @@ func (p *Parser) parseThings() (mul []Expr, err error) {
|
||||||
//
|
//
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
func (p *Parser) parseIdent() (Ident, error) {
|
func (p *Parser) parseIdent() (*Ident, error) {
|
||||||
|
|
||||||
_, lit, err := p.shouldBe(IDENT)
|
_, lit, err := p.shouldBe(IDENT)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Ident(""), &ParseError{Found: lit, Expected: []string{"name"}}
|
return nil, &ParseError{Found: lit, Expected: []string{"name"}}
|
||||||
}
|
}
|
||||||
|
|
||||||
val, err := declare(IDENT, lit)
|
val, err := declare(IDENT, lit)
|
||||||
|
|
||||||
return val.(Ident), err
|
return val.(*Ident), err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +247,7 @@ func (p *Parser) parseScript() (string, error) {
|
||||||
|
|
||||||
tok, lit, err := p.shouldBe(STRING, REGION)
|
tok, lit, err := p.shouldBe(STRING, REGION)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return string(""), &ParseError{Found: lit, Expected: []string{"LUA script"}}
|
return string(""), &ParseError{Found: lit, Expected: []string{"js/lua script"}}
|
||||||
}
|
}
|
||||||
|
|
||||||
val, err := declare(tok, lit)
|
val, err := declare(tok, lit)
|
||||||
|
@ -257,7 +284,7 @@ func (p *Parser) parseBoolean() (bool, error) {
|
||||||
|
|
||||||
func (p *Parser) parseDefault() (interface{}, error) {
|
func (p *Parser) parseDefault() (interface{}, error) {
|
||||||
|
|
||||||
tok, lit, err := p.shouldBe(NULL, NOW, DATE, TIME, TRUE, FALSE, NUMBER, STRING, REGION, ARRAY, JSON)
|
tok, lit, err := p.shouldBe(NULL, NOW, DATE, TIME, TRUE, FALSE, NUMBER, DOUBLE, STRING, REGION, IDENT, ARRAY, JSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -271,12 +298,13 @@ func (p *Parser) parseExpr() (mul []*Field, err error) {
|
||||||
var tok Token
|
var tok Token
|
||||||
var lit string
|
var lit string
|
||||||
var exi bool
|
var exi bool
|
||||||
|
var val interface{}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
|
||||||
one := &Field{}
|
one := &Field{}
|
||||||
|
|
||||||
tok, lit, err = p.shouldBe(IDENT, ID, OEDGE, IEDGE, BEDGE, NULL, ALL, TIME, TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE, JSON)
|
tok, lit, err = p.shouldBe(IDENT, ID, NOW, PATH, NULL, ALL, TIME, TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE, JSON, ARRAY)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &ParseError{Found: lit, Expected: []string{"field name"}}
|
return nil, &ParseError{Found: lit, Expected: []string{"field name"}}
|
||||||
}
|
}
|
||||||
|
@ -286,19 +314,23 @@ func (p *Parser) parseExpr() (mul []*Field, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
one.Alias = lit
|
||||||
|
|
||||||
// Next token might be AS
|
// Next token might be AS
|
||||||
if _, _, exi = p.mightBe(AS); exi {
|
if _, _, exi = p.mightBe(AS); exi {
|
||||||
|
|
||||||
tok, lit, err = p.shouldBe(IDENT)
|
_, lit, err = p.shouldBe(IDENT)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &ParseError{Found: lit, Expected: []string{"field alias"}}
|
return nil, &ParseError{Found: lit, Expected: []string{"field alias"}}
|
||||||
}
|
}
|
||||||
|
|
||||||
one.Alias, err = declare(tok, lit)
|
val, err = declare(STRING, lit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
one.Alias = val.(string)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mul = append(mul, one)
|
mul = append(mul, one)
|
||||||
|
|
|
@ -24,7 +24,7 @@ func (p *Parser) parseDefineFieldStatement(explain bool) (stmt *DefineFieldState
|
||||||
stmt.NS = p.c.Get("NS").(string)
|
stmt.NS = p.c.Get("NS").(string)
|
||||||
stmt.DB = p.c.Get("DB").(string)
|
stmt.DB = p.c.Get("DB").(string)
|
||||||
|
|
||||||
if stmt.Name, err = p.parseIdent(); err != nil {
|
if stmt.Name, err = p.parseName(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ func (p *Parser) parseDefineFieldStatement(explain bool) (stmt *DefineFieldState
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if stmt.What, err = p.parseTables(); err != nil {
|
if stmt.What, err = p.parseNames(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +145,7 @@ func (p *Parser) parseRemoveFieldStatement(explain bool) (stmt *RemoveFieldState
|
||||||
stmt.NS = p.c.Get("NS").(string)
|
stmt.NS = p.c.Get("NS").(string)
|
||||||
stmt.DB = p.c.Get("DB").(string)
|
stmt.DB = p.c.Get("DB").(string)
|
||||||
|
|
||||||
if stmt.Name, err = p.parseIdent(); err != nil {
|
if stmt.Name, err = p.parseName(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@ func (p *Parser) parseRemoveFieldStatement(explain bool) (stmt *RemoveFieldState
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if stmt.What, err = p.parseTables(); err != nil {
|
if stmt.What, err = p.parseNames(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
26
sql/index.go
26
sql/index.go
|
@ -24,7 +24,7 @@ func (p *Parser) parseDefineIndexStatement(explain bool) (stmt *DefineIndexState
|
||||||
stmt.NS = p.c.Get("NS").(string)
|
stmt.NS = p.c.Get("NS").(string)
|
||||||
stmt.DB = p.c.Get("DB").(string)
|
stmt.DB = p.c.Get("DB").(string)
|
||||||
|
|
||||||
if stmt.Name, err = p.parseIdent(); err != nil {
|
if stmt.Name, err = p.parseName(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,29 +32,17 @@ func (p *Parser) parseDefineIndexStatement(explain bool) (stmt *DefineIndexState
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if stmt.What, err = p.parseTables(); err != nil {
|
if stmt.What, err = p.parseNames(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if tok, _, err := p.shouldBe(CODE, COLUMNS); tok != 0 {
|
if _, _, err = p.shouldBe(COLUMNS); err != nil {
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if is(tok, CODE) {
|
if stmt.Cols, err = p.parseNames(); err != nil {
|
||||||
if stmt.Code, err = p.parseScript(); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if is(tok, COLUMNS) {
|
|
||||||
if stmt.Cols, err = p.parseExpr(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, stmt.Uniq = p.mightBe(UNIQUE)
|
_, _, stmt.Uniq = p.mightBe(UNIQUE)
|
||||||
|
|
||||||
|
@ -80,7 +68,7 @@ func (p *Parser) parseResyncIndexStatement(explain bool) (stmt *ResyncIndexState
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if stmt.What, err = p.parseTables(); err != nil {
|
if stmt.What, err = p.parseNames(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +90,7 @@ func (p *Parser) parseRemoveIndexStatement(explain bool) (stmt *RemoveIndexState
|
||||||
stmt.NS = p.c.Get("NS").(string)
|
stmt.NS = p.c.Get("NS").(string)
|
||||||
stmt.DB = p.c.Get("DB").(string)
|
stmt.DB = p.c.Get("DB").(string)
|
||||||
|
|
||||||
if stmt.Name, err = p.parseIdent(); err != nil {
|
if stmt.Name, err = p.parseName(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +98,7 @@ func (p *Parser) parseRemoveIndexStatement(explain bool) (stmt *RemoveIndexState
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if stmt.What, err = p.parseTables(); err != nil {
|
if stmt.What, err = p.parseNames(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,10 @@ func (p *Parser) parseModifyStatement(explain bool) (stmt *ModifyStatement, err
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if stmt.Cond, err = p.parseCond(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if stmt.Echo, err = p.parseEcho(); err != nil {
|
if stmt.Echo, err = p.parseEcho(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,13 +111,20 @@ func (p *Parser) ParseSingle() (Statement, error) {
|
||||||
explain = true
|
explain = true
|
||||||
}
|
}
|
||||||
|
|
||||||
tok, _, err := p.shouldBe(USE, SELECT, CREATE, UPDATE, INSERT, UPSERT, MODIFY, DELETE, RELATE, RECORD, DEFINE, RESYNC, REMOVE)
|
tok, _, err := p.shouldBe(USE, LET, BEGIN, CANCEL, COMMIT, ROLLBACK, SELECT, CREATE, UPDATE, INSERT, UPSERT, MODIFY, DELETE, RELATE, RECORD, DEFINE, RESYNC, REMOVE)
|
||||||
|
|
||||||
switch tok {
|
switch tok {
|
||||||
|
|
||||||
case USE:
|
case USE:
|
||||||
return p.parseUseStatement(explain)
|
return p.parseUseStatement(explain)
|
||||||
|
|
||||||
|
case BEGIN:
|
||||||
|
return p.parseBeginStatement(explain)
|
||||||
|
case CANCEL, ROLLBACK:
|
||||||
|
return p.parseCancelStatement(explain)
|
||||||
|
case COMMIT:
|
||||||
|
return p.parseCommitStatement(explain)
|
||||||
|
|
||||||
case SELECT:
|
case SELECT:
|
||||||
return p.parseSelectStatement(explain)
|
return p.parseSelectStatement(explain)
|
||||||
case CREATE, INSERT:
|
case CREATE, INSERT:
|
||||||
|
|
72
sql/rules.go
72
sql/rules.go
|
@ -24,10 +24,51 @@ func (p *Parser) parseDefineRulesStatement(explain bool) (stmt *DefineRulesState
|
||||||
stmt.NS = p.c.Get("NS").(string)
|
stmt.NS = p.c.Get("NS").(string)
|
||||||
stmt.DB = p.c.Get("DB").(string)
|
stmt.DB = p.c.Get("DB").(string)
|
||||||
|
|
||||||
if stmt.What, err = p.parseTables(); err != nil {
|
if _, _, err = p.shouldBe(ON); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if stmt.What, err = p.parseNames(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, _, err = p.shouldBe(FOR); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
|
||||||
|
tok, _, exi := p.mightBe(SELECT, CREATE, UPDATE, DELETE, RELATE, RECORD)
|
||||||
|
if !exi {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt.When = append(stmt.When, tok.String())
|
||||||
|
|
||||||
|
if _, _, exi := p.mightBe(COMMA); !exi {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(stmt.When) == 0 {
|
||||||
|
return nil, &ParseError{Found: "", Expected: []string{"SELECT", "CREATE", "UPDATE", "DELETE", "RELATE", "RECORD"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tok, _, err := p.shouldBe(ACCEPT, REJECT, CUSTOM); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
|
||||||
|
stmt.Rule = tok.String()
|
||||||
|
|
||||||
|
if is(tok, CUSTOM) {
|
||||||
|
if stmt.Code, err = p.parseScript(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
|
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -46,10 +87,37 @@ func (p *Parser) parseRemoveRulesStatement(explain bool) (stmt *RemoveRulesState
|
||||||
stmt.NS = p.c.Get("NS").(string)
|
stmt.NS = p.c.Get("NS").(string)
|
||||||
stmt.DB = p.c.Get("DB").(string)
|
stmt.DB = p.c.Get("DB").(string)
|
||||||
|
|
||||||
if stmt.What, err = p.parseTables(); err != nil {
|
if _, _, err = p.shouldBe(ON); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if stmt.What, err = p.parseNames(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, _, err = p.shouldBe(FOR); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
|
||||||
|
tok, _, exi := p.mightBe(SELECT, CREATE, UPDATE, DELETE, RELATE, RECORD)
|
||||||
|
if !exi {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt.When = append(stmt.When, tok.String())
|
||||||
|
|
||||||
|
if _, _, exi := p.mightBe(COMMA); !exi {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(stmt.When) == 0 {
|
||||||
|
return nil, &ParseError{Found: "", Expected: []string{"SELECT", "CREATE", "UPDATE", "DELETE", "RELATE", "RECORD"}}
|
||||||
|
}
|
||||||
|
|
||||||
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
|
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
459
sql/scanner.go
459
sql/scanner.go
|
@ -18,12 +18,15 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Scanner represents a lexical scanner.
|
// Scanner represents a lexical scanner.
|
||||||
type Scanner struct {
|
type Scanner struct {
|
||||||
|
p []rune
|
||||||
|
n []rune
|
||||||
r *bufio.Reader
|
r *bufio.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,24 +39,21 @@ func NewScanner(r io.Reader) *Scanner {
|
||||||
func (s *Scanner) Scan() (tok Token, lit string) {
|
func (s *Scanner) Scan() (tok Token, lit string) {
|
||||||
|
|
||||||
// Read the next rune.
|
// Read the next rune.
|
||||||
ch := s.read()
|
ch := s.next()
|
||||||
|
|
||||||
// If we see whitespace then consume all contiguous whitespace.
|
// If we see whitespace then consume all contiguous whitespace.
|
||||||
if isWhitespace(ch) {
|
if isWhitespace(ch) {
|
||||||
s.unread()
|
return s.scanBlank(ch)
|
||||||
return s.scanWhitespace()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we see a letter then consume as an string.
|
// If we see a letter then consume as an string.
|
||||||
if isLetter(ch) {
|
if isLetter(ch) {
|
||||||
s.unread()
|
return s.scanIdent(ch)
|
||||||
return s.scanIdent()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we see a number then consume as a number.
|
// If we see a number then consume as a number.
|
||||||
if isNumber(ch) {
|
if isNumber(ch) {
|
||||||
s.unread()
|
return s.scanNumber(ch)
|
||||||
return s.scanNumber()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise read the individual character.
|
// Otherwise read the individual character.
|
||||||
|
@ -63,6 +63,12 @@ func (s *Scanner) Scan() (tok Token, lit string) {
|
||||||
return EOF, ""
|
return EOF, ""
|
||||||
case '*':
|
case '*':
|
||||||
return ALL, string(ch)
|
return ALL, string(ch)
|
||||||
|
case '×':
|
||||||
|
return MUL, string(ch)
|
||||||
|
case '∙':
|
||||||
|
return MUL, string(ch)
|
||||||
|
case '÷':
|
||||||
|
return DIV, string(ch)
|
||||||
case '@':
|
case '@':
|
||||||
return EAT, string(ch)
|
return EAT, string(ch)
|
||||||
case ',':
|
case ',':
|
||||||
|
@ -70,23 +76,19 @@ func (s *Scanner) Scan() (tok Token, lit string) {
|
||||||
case '.':
|
case '.':
|
||||||
return DOT, string(ch)
|
return DOT, string(ch)
|
||||||
case '"':
|
case '"':
|
||||||
s.unread()
|
return s.scanString(ch)
|
||||||
return s.scanString()
|
|
||||||
case '\'':
|
case '\'':
|
||||||
s.unread()
|
return s.scanString(ch)
|
||||||
return s.scanString()
|
|
||||||
case '`':
|
case '`':
|
||||||
s.unread()
|
return s.scanQuoted(ch)
|
||||||
return s.scanQuoted()
|
|
||||||
case '⟨':
|
case '⟨':
|
||||||
s.unread()
|
return s.scanQuoted(ch)
|
||||||
return s.scanQuoted()
|
|
||||||
case '{':
|
case '{':
|
||||||
s.unread()
|
return s.scanObject(ch)
|
||||||
return s.scanObject()
|
|
||||||
case '[':
|
case '[':
|
||||||
s.unread()
|
return s.scanObject(ch)
|
||||||
return s.scanObject()
|
case '$':
|
||||||
|
return s.scanParams(ch)
|
||||||
case ':':
|
case ':':
|
||||||
return COLON, string(ch)
|
return COLON, string(ch)
|
||||||
case ';':
|
case ';':
|
||||||
|
@ -95,102 +97,262 @@ func (s *Scanner) Scan() (tok Token, lit string) {
|
||||||
return LPAREN, string(ch)
|
return LPAREN, string(ch)
|
||||||
case ')':
|
case ')':
|
||||||
return RPAREN, string(ch)
|
return RPAREN, string(ch)
|
||||||
case '=':
|
case '¬':
|
||||||
return EQ, string(ch)
|
return NEQ, string(ch)
|
||||||
case '+':
|
case '≤':
|
||||||
if chn := s.read(); chn == '=' {
|
return LTE, string(ch)
|
||||||
return INC, "+="
|
case '≥':
|
||||||
}
|
return GTE, string(ch)
|
||||||
s.unread()
|
case '~':
|
||||||
return ADD, string(ch)
|
return SIN, string(ch)
|
||||||
case '-':
|
case '∋':
|
||||||
if chn := s.read(); chn == '>' {
|
return SIN, string(ch)
|
||||||
return OEDGE, "->"
|
case '∌':
|
||||||
|
return SNI, string(ch)
|
||||||
|
case '⊇':
|
||||||
|
return CONTAINSALL, string(ch)
|
||||||
|
case '⊃':
|
||||||
|
return CONTAINSSOME, string(ch)
|
||||||
|
case '⊅':
|
||||||
|
return CONTAINSNONE, string(ch)
|
||||||
|
case '∈':
|
||||||
|
return INS, string(ch)
|
||||||
|
case '∉':
|
||||||
|
return NIS, string(ch)
|
||||||
|
case '⊆':
|
||||||
|
return ALLCONTAINEDIN, string(ch)
|
||||||
|
case '⊂':
|
||||||
|
return SOMECONTAINEDIN, string(ch)
|
||||||
|
case '⊄':
|
||||||
|
return NONECONTAINEDIN, string(ch)
|
||||||
|
case '#':
|
||||||
|
return s.scanCommentSingle(ch)
|
||||||
case '/':
|
case '/':
|
||||||
chn := s.next()
|
chn := s.next()
|
||||||
switch {
|
switch {
|
||||||
case chn == '*':
|
case chn == '*':
|
||||||
return s.scanCommentMultiple(ch)
|
return s.scanCommentMultiple(ch)
|
||||||
|
case chn == ' ':
|
||||||
|
s.undo()
|
||||||
|
return DIV, string(ch)
|
||||||
default:
|
default:
|
||||||
s.unread()
|
s.undo()
|
||||||
return s.scanRegexp(ch)
|
return s.scanRegexp(ch)
|
||||||
}
|
}
|
||||||
s.unread()
|
case '=':
|
||||||
if chn := s.read(); chn == '=' {
|
chn := s.next()
|
||||||
return DEC, "-="
|
switch {
|
||||||
|
case chn == '~':
|
||||||
|
return SIN, "=~"
|
||||||
|
case chn == '=':
|
||||||
|
return EEQ, "=="
|
||||||
|
default:
|
||||||
|
s.undo()
|
||||||
|
return EQ, string(ch)
|
||||||
|
}
|
||||||
|
case '?':
|
||||||
|
chn := s.next()
|
||||||
|
switch {
|
||||||
|
case chn == '=':
|
||||||
|
return ANY, "?="
|
||||||
|
default:
|
||||||
|
s.undo()
|
||||||
|
return QMARK, string(ch)
|
||||||
}
|
}
|
||||||
s.unread()
|
|
||||||
return SUB, string(ch)
|
|
||||||
case '!':
|
case '!':
|
||||||
if chn := s.read(); chn == '=' {
|
chn := s.next()
|
||||||
|
switch {
|
||||||
|
case chn == '=':
|
||||||
|
if s.next() == '=' {
|
||||||
|
return NEE, "!=="
|
||||||
|
} else {
|
||||||
|
s.undo()
|
||||||
return NEQ, "!="
|
return NEQ, "!="
|
||||||
}
|
}
|
||||||
s.unread()
|
case chn == '~':
|
||||||
case '<':
|
return SNI, "!~"
|
||||||
if chn := s.read(); chn == '-' {
|
default:
|
||||||
if chn := s.read(); chn == '>' {
|
s.undo()
|
||||||
return BEDGE, "<->"
|
return EXC, string(ch)
|
||||||
}
|
}
|
||||||
s.unread()
|
case '+':
|
||||||
|
chn := s.next()
|
||||||
|
switch {
|
||||||
|
case chn == '=':
|
||||||
|
return INC, "+="
|
||||||
|
case isNumber(chn):
|
||||||
|
return s.scanNumber(ch, chn)
|
||||||
|
default:
|
||||||
|
s.undo()
|
||||||
|
return ADD, string(ch)
|
||||||
|
}
|
||||||
|
case '-':
|
||||||
|
chn := s.next()
|
||||||
|
switch {
|
||||||
|
case chn == '=':
|
||||||
|
return DEC, "-="
|
||||||
|
case chn == '>':
|
||||||
|
return OEDGE, "->"
|
||||||
|
case chn == '-':
|
||||||
|
return s.scanCommentSingle(ch)
|
||||||
|
case isNumber(chn):
|
||||||
|
return s.scanNumber(ch, chn)
|
||||||
|
default:
|
||||||
|
s.undo()
|
||||||
|
return SUB, string(ch)
|
||||||
|
}
|
||||||
|
case '>':
|
||||||
|
chn := s.next()
|
||||||
|
switch {
|
||||||
|
case chn == '=':
|
||||||
|
return GTE, ">="
|
||||||
|
default:
|
||||||
|
s.undo()
|
||||||
|
return GT, string(ch)
|
||||||
|
}
|
||||||
|
case '<':
|
||||||
|
chn := s.next()
|
||||||
|
switch {
|
||||||
|
case chn == '>':
|
||||||
|
return NEQ, "<>"
|
||||||
|
case chn == '=':
|
||||||
|
return LTE, "<="
|
||||||
|
case chn == '-':
|
||||||
|
if s.next() == '>' {
|
||||||
|
return BEDGE, "<->"
|
||||||
|
} else {
|
||||||
|
s.undo()
|
||||||
return IEDGE, "<-"
|
return IEDGE, "<-"
|
||||||
}
|
}
|
||||||
s.unread()
|
default:
|
||||||
if chn := s.read(); chn == '=' {
|
s.undo()
|
||||||
return LTE, "<="
|
|
||||||
}
|
|
||||||
s.unread()
|
|
||||||
return LT, string(ch)
|
return LT, string(ch)
|
||||||
case '>':
|
|
||||||
if chn := s.read(); chn == '=' {
|
|
||||||
return GTE, ">="
|
|
||||||
}
|
}
|
||||||
s.unread()
|
|
||||||
return GT, string(ch)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ILLEGAL, string(ch)
|
return ILLEGAL, string(ch)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// scanWhitespace consumes the current rune and all contiguous whitespace.
|
// scanBlank consumes the current rune and all contiguous whitespace.
|
||||||
func (s *Scanner) scanWhitespace() (tok Token, lit string) {
|
func (s *Scanner) scanBlank(chp ...rune) (tok Token, lit string) {
|
||||||
|
|
||||||
// Create a buffer and read the current character into it.
|
tok = WS
|
||||||
|
|
||||||
|
// Create a buffer
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
buf.WriteRune(s.read())
|
|
||||||
|
|
||||||
// Read every subsequent whitespace character into the buffer.
|
// Read passed in runes
|
||||||
// Non-whitespace characters and EOF will cause the loop to exit.
|
for _, ch := range chp {
|
||||||
|
buf.WriteRune(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read subsequent characters
|
||||||
for {
|
for {
|
||||||
if ch := s.read(); ch == eof {
|
if ch := s.next(); ch == eof {
|
||||||
break
|
break
|
||||||
} else if !isWhitespace(ch) {
|
} else if !isWhitespace(ch) {
|
||||||
s.unread()
|
s.undo()
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
buf.WriteRune(ch)
|
buf.WriteRune(ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return WS, buf.String()
|
return tok, buf.String()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// scanCommentSingle consumes the current rune and all contiguous whitespace.
|
||||||
|
func (s *Scanner) scanCommentSingle(chp ...rune) (tok Token, lit string) {
|
||||||
|
|
||||||
|
tok = WS
|
||||||
|
|
||||||
|
// Create a buffer
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
// Read passed in runes
|
||||||
|
for _, ch := range chp {
|
||||||
|
buf.WriteRune(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read subsequent characters
|
||||||
|
for {
|
||||||
|
if ch := s.next(); ch == '\n' || ch == '\r' {
|
||||||
|
buf.WriteRune(ch)
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
buf.WriteRune(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tok, buf.String()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// scanCommentMultiple consumes the current rune and all contiguous whitespace.
|
||||||
|
func (s *Scanner) scanCommentMultiple(chp ...rune) (tok Token, lit string) {
|
||||||
|
|
||||||
|
tok = WS
|
||||||
|
|
||||||
|
// Create a buffer
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
// Read passed in runes
|
||||||
|
for _, ch := range chp {
|
||||||
|
buf.WriteRune(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read subsequent characters
|
||||||
|
for {
|
||||||
|
if ch := s.next(); ch == eof {
|
||||||
|
break
|
||||||
|
} else if ch == '*' {
|
||||||
|
if chn := s.next(); chn == '/' {
|
||||||
|
buf.WriteRune(chn)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
buf.WriteRune(ch)
|
||||||
|
} else {
|
||||||
|
buf.WriteRune(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tok, buf.String()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) scanParams(chp ...rune) (Token, string) {
|
||||||
|
|
||||||
|
tok, lit := s.scanIdent(chp...)
|
||||||
|
|
||||||
|
if is(tok, IDENT) {
|
||||||
|
return BOUNDPARAM, lit
|
||||||
|
}
|
||||||
|
|
||||||
|
return tok, lit
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// scanIdent consumes the current rune and all contiguous ident runes.
|
// scanIdent consumes the current rune and all contiguous ident runes.
|
||||||
func (s *Scanner) scanIdent() (tok Token, lit string) {
|
func (s *Scanner) scanIdent(chp ...rune) (tok Token, lit string) {
|
||||||
|
|
||||||
// Create a buffer and read the current character into it.
|
tok = IDENT
|
||||||
|
|
||||||
|
// Create a buffer
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
buf.WriteRune(s.read())
|
|
||||||
|
|
||||||
// Read every subsequent ident character into the buffer.
|
// Read passed in runes
|
||||||
// Non-ident characters and EOF will cause the loop to exit.
|
for _, ch := range chp {
|
||||||
|
buf.WriteRune(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read subsequent characters
|
||||||
for {
|
for {
|
||||||
if ch := s.read(); ch == eof {
|
if ch := s.next(); ch == eof {
|
||||||
break
|
break
|
||||||
} else if !isIdentChar(ch) {
|
} else if !isIdentChar(ch) {
|
||||||
s.unread()
|
s.undo()
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
buf.WriteRune(ch)
|
buf.WriteRune(ch)
|
||||||
|
@ -207,22 +369,25 @@ func (s *Scanner) scanIdent() (tok Token, lit string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise return as a regular identifier.
|
// Otherwise return as a regular identifier.
|
||||||
return IDENT, buf.String()
|
return tok, buf.String()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scanner) scanNumber() (tok Token, lit string) {
|
func (s *Scanner) scanNumber(chp ...rune) (tok Token, lit string) {
|
||||||
|
|
||||||
tok = NUMBER
|
tok = NUMBER
|
||||||
|
|
||||||
// Create a buffer and read the current character into it.
|
// Create a buffer
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
buf.WriteRune(s.read())
|
|
||||||
|
|
||||||
// Read every subsequent ident character into the buffer.
|
// Read passed in runes
|
||||||
// Non-ident characters and EOF will cause the loop to exit.
|
for _, ch := range chp {
|
||||||
|
buf.WriteRune(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read subsequent characters
|
||||||
for {
|
for {
|
||||||
if ch := s.read(); ch == eof {
|
if ch := s.next(); ch == eof {
|
||||||
break
|
break
|
||||||
} else if isNumber(ch) {
|
} else if isNumber(ch) {
|
||||||
buf.WriteRune(ch)
|
buf.WriteRune(ch)
|
||||||
|
@ -238,7 +403,7 @@ func (s *Scanner) scanNumber() (tok Token, lit string) {
|
||||||
}
|
}
|
||||||
buf.WriteRune(ch)
|
buf.WriteRune(ch)
|
||||||
} else {
|
} else {
|
||||||
s.unread()
|
s.undo()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,9 +412,9 @@ func (s *Scanner) scanNumber() (tok Token, lit string) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scanner) scanQuoted() (Token, string) {
|
func (s *Scanner) scanQuoted(chp ...rune) (Token, string) {
|
||||||
|
|
||||||
tok, lit := s.scanString()
|
tok, lit := s.scanString(chp...)
|
||||||
|
|
||||||
if is(tok, STRING) {
|
if is(tok, STRING) {
|
||||||
return IDENT, lit
|
return IDENT, lit
|
||||||
|
@ -259,13 +424,9 @@ func (s *Scanner) scanQuoted() (Token, string) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scanner) scanString() (tok Token, lit string) {
|
func (s *Scanner) scanString(chp ...rune) (tok Token, lit string) {
|
||||||
|
|
||||||
tok = STRING
|
beg := chp[0]
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
|
|
||||||
beg := s.read()
|
|
||||||
end := beg
|
end := beg
|
||||||
|
|
||||||
if beg == '"' {
|
if beg == '"' {
|
||||||
|
@ -280,8 +441,16 @@ func (s *Scanner) scanString() (tok Token, lit string) {
|
||||||
end = '⟩'
|
end = '⟩'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tok = STRING
|
||||||
|
|
||||||
|
// Create a buffer
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
// Ignore passed in runes
|
||||||
|
|
||||||
|
// Read subsequent characters
|
||||||
for {
|
for {
|
||||||
if ch := s.read(); ch == end {
|
if ch := s.next(); ch == end {
|
||||||
break
|
break
|
||||||
} else if ch == eof {
|
} else if ch == eof {
|
||||||
return ILLEGAL, buf.String()
|
return ILLEGAL, buf.String()
|
||||||
|
@ -292,13 +461,13 @@ func (s *Scanner) scanString() (tok Token, lit string) {
|
||||||
tok = REGION
|
tok = REGION
|
||||||
buf.WriteRune(ch)
|
buf.WriteRune(ch)
|
||||||
} else if ch == '\\' {
|
} else if ch == '\\' {
|
||||||
chn := s.read()
|
switch chn := s.next(); chn {
|
||||||
switch chn {
|
|
||||||
default:
|
default:
|
||||||
buf.WriteRune(chn)
|
buf.WriteRune(chn)
|
||||||
case 'b':
|
case 'b':
|
||||||
continue
|
continue
|
||||||
case 't':
|
case 't':
|
||||||
|
tok = REGION
|
||||||
buf.WriteRune('\t')
|
buf.WriteRune('\t')
|
||||||
case 'r':
|
case 'r':
|
||||||
tok = REGION
|
tok = REGION
|
||||||
|
@ -328,13 +497,41 @@ func (s *Scanner) scanString() (tok Token, lit string) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scanner) scanObject() (tok Token, lit string) {
|
func (s *Scanner) scanRegexp(chp ...rune) (tok Token, lit string) {
|
||||||
|
|
||||||
tok = IDENT
|
tok = IDENT
|
||||||
|
|
||||||
|
// Create a buffer
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
||||||
beg := s.read()
|
// Ignore passed in runes
|
||||||
|
|
||||||
|
// Read subsequent characters
|
||||||
|
for {
|
||||||
|
if ch := s.next(); ch == chp[0] {
|
||||||
|
break
|
||||||
|
} else if ch == eof {
|
||||||
|
return ILLEGAL, buf.String()
|
||||||
|
} else if ch == '\\' {
|
||||||
|
chn := s.next()
|
||||||
|
buf.WriteRune(ch)
|
||||||
|
buf.WriteRune(chn)
|
||||||
|
} else {
|
||||||
|
buf.WriteRune(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := regexp.Compile(buf.String()); err == nil {
|
||||||
|
return REGEX, buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return tok, buf.String()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) scanObject(chp ...rune) (tok Token, lit string) {
|
||||||
|
|
||||||
|
beg := chp[0]
|
||||||
end := beg
|
end := beg
|
||||||
sub := 0
|
sub := 0
|
||||||
|
|
||||||
|
@ -346,8 +543,20 @@ func (s *Scanner) scanObject() (tok Token, lit string) {
|
||||||
end = ']'
|
end = ']'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tok = IDENT
|
||||||
|
|
||||||
|
// Create a buffer
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
// Read passed in runes
|
||||||
|
for _, ch := range chp {
|
||||||
|
buf.WriteRune(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read subsequent characters
|
||||||
for {
|
for {
|
||||||
if ch := s.read(); ch == end && sub == 0 {
|
if ch := s.next(); ch == end && sub == 0 {
|
||||||
|
buf.WriteRune(ch)
|
||||||
break
|
break
|
||||||
} else if ch == beg {
|
} else if ch == beg {
|
||||||
sub++
|
sub++
|
||||||
|
@ -358,8 +567,7 @@ func (s *Scanner) scanObject() (tok Token, lit string) {
|
||||||
} else if ch == eof {
|
} else if ch == eof {
|
||||||
return ILLEGAL, buf.String()
|
return ILLEGAL, buf.String()
|
||||||
} else if ch == '\\' {
|
} else if ch == '\\' {
|
||||||
chn := s.read()
|
switch chn := s.next(); chn {
|
||||||
switch chn {
|
|
||||||
default:
|
default:
|
||||||
return ILLEGAL, buf.String()
|
return ILLEGAL, buf.String()
|
||||||
case 'b', 't', 'r', 'n', 'f', '"', '\\':
|
case 'b', 't', 'r', 'n', 'f', '"', '\\':
|
||||||
|
@ -371,39 +579,49 @@ func (s *Scanner) scanObject() (tok Token, lit string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
str := buf.String()
|
|
||||||
|
|
||||||
str = strings.Replace(str, "\n", "", -1)
|
|
||||||
str = strings.Replace(str, "\r", "", -1)
|
|
||||||
str = strings.Trim(str, " ")
|
|
||||||
|
|
||||||
if beg == '[' {
|
|
||||||
return ARRAY, string(beg) + str + string(end)
|
|
||||||
}
|
|
||||||
|
|
||||||
if beg == '{' {
|
if beg == '{' {
|
||||||
if len(str) == 0 || str[0] == '"' {
|
return JSON, buf.String()
|
||||||
return JSON, string(beg) + str + string(end)
|
|
||||||
}
|
}
|
||||||
|
if beg == '[' {
|
||||||
|
return ARRAY, buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
return tok, str
|
return ILLEGAL, buf.String()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// read reads the next rune from the bufferred reader.
|
// next reads the next rune from the bufferred reader.
|
||||||
// Returns the rune(0) if an error occurs (or io.EOF is returned).
|
// Returns the rune(0) if an error occurs (or io.EOF is returned).
|
||||||
func (s *Scanner) read() rune {
|
func (s *Scanner) next() rune {
|
||||||
ch, _, err := s.r.ReadRune()
|
|
||||||
|
if len(s.n) > 0 {
|
||||||
|
var r rune
|
||||||
|
r, s.n = s.n[len(s.n)-1], s.n[:len(s.n)-1]
|
||||||
|
s.p = append(s.p, r)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
r, _, err := s.r.ReadRune()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return eof
|
return eof
|
||||||
}
|
}
|
||||||
return ch
|
s.p = append(s.p, r)
|
||||||
|
return r
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// undo places the previously read rune back on the reader.
|
||||||
|
func (s *Scanner) undo() {
|
||||||
|
|
||||||
|
if len(s.p) > 0 {
|
||||||
|
var r rune
|
||||||
|
r, s.p = s.p[len(s.p)-1], s.p[:len(s.p)-1]
|
||||||
|
s.n = append(s.n, r)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// unread places the previously read rune back on the reader.
|
|
||||||
func (s *Scanner) unread() {
|
|
||||||
_ = s.r.UnreadRune()
|
_ = s.r.UnreadRune()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// isWhitespace returns true if the rune is a space, tab, or newline.
|
// isWhitespace returns true if the rune is a space, tab, or newline.
|
||||||
|
@ -411,24 +629,19 @@ func isWhitespace(ch rune) bool {
|
||||||
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'
|
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'
|
||||||
}
|
}
|
||||||
|
|
||||||
// isLetter returns true if the rune is a letter.
|
|
||||||
func isLetter(ch rune) bool {
|
|
||||||
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
|
|
||||||
}
|
|
||||||
|
|
||||||
// isNumber returns true if the rune is a number.
|
// isNumber returns true if the rune is a number.
|
||||||
func isNumber(ch rune) bool {
|
func isNumber(ch rune) bool {
|
||||||
return (ch >= '0' && ch <= '9')
|
return (ch >= '0' && ch <= '9')
|
||||||
}
|
}
|
||||||
|
|
||||||
// isSeparator returns true if the rune is a separator expression.
|
// isLetter returns true if the rune is a letter.
|
||||||
func isSeparator(ch rune) bool {
|
func isLetter(ch rune) bool {
|
||||||
return (ch == '.')
|
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
|
||||||
}
|
}
|
||||||
|
|
||||||
// isIdentChar returns true if the rune can be used in an unquoted identifier.
|
// isIdentChar returns true if the rune is allowed in a IDENT.
|
||||||
func isIdentChar(ch rune) bool {
|
func isIdentChar(ch rune) bool {
|
||||||
return isLetter(ch) || isNumber(ch) || isSeparator(ch) || ch == '_' || ch == '*' || ch == '?'
|
return isLetter(ch) || isNumber(ch) || ch == '.' || ch == '_' || ch == '*'
|
||||||
}
|
}
|
||||||
|
|
||||||
// eof represents a marker rune for the end of the reader.
|
// eof represents a marker rune for the end of the reader.
|
||||||
|
|
|
@ -61,6 +61,10 @@ func (p *Parser) parseSelectStatement(explain bool) (stmt *SelectStatement, err
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if stmt.Echo, err = p.parseEcho(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
|
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -113,8 +117,7 @@ func (p *Parser) parseOrder() (mul []*Order, err error) {
|
||||||
|
|
||||||
tok, lit, exi = p.mightBe(ASC, DESC)
|
tok, lit, exi = p.mightBe(ASC, DESC)
|
||||||
if !exi {
|
if !exi {
|
||||||
tok = ASC
|
tok, lit = ASC, "ASC"
|
||||||
lit = "ASC"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
one.Dir, err = declare(tok, lit)
|
one.Dir, err = declare(tok, lit)
|
||||||
|
|
907
sql/sql_test.go
907
sql/sql_test.go
File diff suppressed because it is too large
Load diff
|
@ -24,7 +24,7 @@ func (p *Parser) parseDefineTableStatement(explain bool) (stmt *DefineTableState
|
||||||
stmt.NS = p.c.Get("NS").(string)
|
stmt.NS = p.c.Get("NS").(string)
|
||||||
stmt.DB = p.c.Get("DB").(string)
|
stmt.DB = p.c.Get("DB").(string)
|
||||||
|
|
||||||
if stmt.What, err = p.parseTables(); err != nil {
|
if stmt.What, err = p.parseNames(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ func (p *Parser) parseRemoveTableStatement(explain bool) (stmt *RemoveTableState
|
||||||
stmt.NS = p.c.Get("NS").(string)
|
stmt.NS = p.c.Get("NS").(string)
|
||||||
stmt.DB = p.c.Get("DB").(string)
|
stmt.DB = p.c.Get("DB").(string)
|
||||||
|
|
||||||
if stmt.What, err = p.parseTables(); err != nil {
|
if stmt.What, err = p.parseNames(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
86
sql/token.go
86
sql/token.go
|
@ -31,7 +31,7 @@ const (
|
||||||
|
|
||||||
DATE // 1970-01-01
|
DATE // 1970-01-01
|
||||||
TIME // 1970-01-01T00:00:00+00:00
|
TIME // 1970-01-01T00:00:00+00:00
|
||||||
PATH // :friend
|
PATH // person->like->person
|
||||||
JSON // {"test":true}
|
JSON // {"test":true}
|
||||||
IDENT // something
|
IDENT // something
|
||||||
STRING // "something"
|
STRING // "something"
|
||||||
|
@ -41,10 +41,12 @@ const (
|
||||||
REGEX // /.*/
|
REGEX // /.*/
|
||||||
ARRAY // [0,1,2]
|
ARRAY // [0,1,2]
|
||||||
DURATION // 13h
|
DURATION // 13h
|
||||||
|
BOUNDPARAM // $1
|
||||||
|
|
||||||
EAT // @
|
EAT // @
|
||||||
DOT // .
|
DOT // .
|
||||||
COMMA // ,
|
COMMA // ,
|
||||||
|
QMARK // ?
|
||||||
LPAREN // (
|
LPAREN // (
|
||||||
RPAREN // )
|
RPAREN // )
|
||||||
LBRACK // [
|
LBRACK // [
|
||||||
|
@ -66,15 +68,19 @@ const (
|
||||||
DEC // -=
|
DEC // -=
|
||||||
|
|
||||||
EQ // =
|
EQ // =
|
||||||
|
EEQ // ==
|
||||||
|
EXC // !
|
||||||
NEQ // !=
|
NEQ // !=
|
||||||
|
NEE // !==
|
||||||
|
ANY // ?=
|
||||||
LT // <
|
LT // <
|
||||||
LTE // <=
|
LTE // <=
|
||||||
GT // >
|
GT // >
|
||||||
GTE // >=
|
GTE // >=
|
||||||
EQR // =~
|
SIN // ∋
|
||||||
NER // !~
|
SNI // ∌
|
||||||
SEQ // ∋
|
INS // ∈
|
||||||
SNE // ∌
|
NIS // ∉
|
||||||
|
|
||||||
OEDGE // ->
|
OEDGE // ->
|
||||||
IEDGE // <-
|
IEDGE // <-
|
||||||
|
@ -89,17 +95,27 @@ const (
|
||||||
ACCEPT
|
ACCEPT
|
||||||
AFTER
|
AFTER
|
||||||
ALL
|
ALL
|
||||||
|
ALLCONTAINEDIN
|
||||||
AND
|
AND
|
||||||
AS
|
AS
|
||||||
ASC
|
ASC
|
||||||
AT
|
AT
|
||||||
BEFORE
|
BEFORE
|
||||||
|
BEGIN
|
||||||
BOTH
|
BOTH
|
||||||
BY
|
BY
|
||||||
|
CANCEL
|
||||||
CODE
|
CODE
|
||||||
|
COLLECT
|
||||||
COLUMNS
|
COLUMNS
|
||||||
|
COMMIT
|
||||||
|
CONTAINS
|
||||||
|
CONTAINSALL
|
||||||
|
CONTAINSNONE
|
||||||
|
CONTAINSSOME
|
||||||
CONTENT
|
CONTENT
|
||||||
CREATE
|
CREATE
|
||||||
|
CUSTOM
|
||||||
DATABASE
|
DATABASE
|
||||||
DEFAULT
|
DEFAULT
|
||||||
DEFINE
|
DEFINE
|
||||||
|
@ -113,6 +129,7 @@ const (
|
||||||
EXPUNGE
|
EXPUNGE
|
||||||
FALSE
|
FALSE
|
||||||
FIELD
|
FIELD
|
||||||
|
FOR
|
||||||
FROM
|
FROM
|
||||||
FULL
|
FULL
|
||||||
GROUP
|
GROUP
|
||||||
|
@ -122,14 +139,21 @@ const (
|
||||||
INDEX
|
INDEX
|
||||||
INSERT
|
INSERT
|
||||||
INTO
|
INTO
|
||||||
|
IS
|
||||||
|
LET
|
||||||
LIMIT
|
LIMIT
|
||||||
MANDATORY
|
MANDATORY
|
||||||
|
MATCH
|
||||||
MAX
|
MAX
|
||||||
MERGE
|
MERGE
|
||||||
MIN
|
MIN
|
||||||
|
MISSING
|
||||||
MODIFY
|
MODIFY
|
||||||
|
MULTI
|
||||||
NAMESPACE
|
NAMESPACE
|
||||||
NONE
|
NONE
|
||||||
|
NONECONTAINEDIN
|
||||||
|
NOT
|
||||||
NOTNULL
|
NOTNULL
|
||||||
NOW
|
NOW
|
||||||
NULL
|
NULL
|
||||||
|
@ -144,17 +168,22 @@ const (
|
||||||
REMOVE
|
REMOVE
|
||||||
RESYNC
|
RESYNC
|
||||||
RETURN
|
RETURN
|
||||||
|
ROLLBACK
|
||||||
|
RULES
|
||||||
SELECT
|
SELECT
|
||||||
SET
|
SET
|
||||||
|
SOMECONTAINEDIN
|
||||||
START
|
START
|
||||||
TABLE
|
TABLE
|
||||||
TO
|
TO
|
||||||
|
TRANSACTION
|
||||||
TRUE
|
TRUE
|
||||||
TYPE
|
TYPE
|
||||||
UNIQUE
|
UNIQUE
|
||||||
UPDATE
|
UPDATE
|
||||||
UPSERT
|
UPSERT
|
||||||
USE
|
USE
|
||||||
|
VALIDATE
|
||||||
VERSION
|
VERSION
|
||||||
VOID
|
VOID
|
||||||
WHERE
|
WHERE
|
||||||
|
@ -182,10 +211,12 @@ var tokens = [...]string{
|
||||||
REGEX: "REGEX",
|
REGEX: "REGEX",
|
||||||
ARRAY: "ARRAY",
|
ARRAY: "ARRAY",
|
||||||
DURATION: "DURATION",
|
DURATION: "DURATION",
|
||||||
|
BOUNDPARAM: "BOUNDPARAM",
|
||||||
|
|
||||||
EAT: "@",
|
EAT: "@",
|
||||||
DOT: ".",
|
DOT: ".",
|
||||||
COMMA: ",",
|
COMMA: ",",
|
||||||
|
QMARK: "?",
|
||||||
LPAREN: "(",
|
LPAREN: "(",
|
||||||
RPAREN: ")",
|
RPAREN: ")",
|
||||||
LBRACK: "[",
|
LBRACK: "[",
|
||||||
|
@ -203,32 +234,50 @@ var tokens = [...]string{
|
||||||
DEC: "-=",
|
DEC: "-=",
|
||||||
|
|
||||||
EQ: "=",
|
EQ: "=",
|
||||||
|
EEQ: "==",
|
||||||
|
EXC: "!",
|
||||||
NEQ: "!=",
|
NEQ: "!=",
|
||||||
|
NEE: "!==",
|
||||||
|
ANY: "?=",
|
||||||
LT: "<",
|
LT: "<",
|
||||||
LTE: "<=",
|
LTE: "<=",
|
||||||
GT: ">",
|
GT: ">",
|
||||||
GTE: ">=",
|
GTE: ">=",
|
||||||
EQR: "=~",
|
SIN: "∋",
|
||||||
NER: "!~",
|
SNI: "∌",
|
||||||
SEQ: "∋",
|
INS: "∈",
|
||||||
SNE: "∌",
|
NIS: "∉",
|
||||||
|
|
||||||
|
OEDGE: "->",
|
||||||
|
IEDGE: "<-",
|
||||||
|
BEDGE: "<->",
|
||||||
|
|
||||||
// keywords
|
// keywords
|
||||||
|
|
||||||
ACCEPT: "ACCEPT",
|
ACCEPT: "ACCEPT",
|
||||||
AFTER: "AFTER",
|
AFTER: "AFTER",
|
||||||
ALL: "ALL",
|
ALL: "ALL",
|
||||||
|
ALLCONTAINEDIN: "ALLCONTAINEDIN",
|
||||||
AND: "AND",
|
AND: "AND",
|
||||||
AS: "AS",
|
AS: "AS",
|
||||||
ASC: "ASC",
|
ASC: "ASC",
|
||||||
AT: "AT",
|
AT: "AT",
|
||||||
BEFORE: "BEFORE",
|
BEFORE: "BEFORE",
|
||||||
|
BEGIN: "BEGIN",
|
||||||
BOTH: "BOTH",
|
BOTH: "BOTH",
|
||||||
BY: "BY",
|
BY: "BY",
|
||||||
|
CANCEL: "CANCEL",
|
||||||
CODE: "CODE",
|
CODE: "CODE",
|
||||||
|
COLLECT: "COLLECT",
|
||||||
COLUMNS: "COLUMNS",
|
COLUMNS: "COLUMNS",
|
||||||
|
COMMIT: "COMMIT",
|
||||||
|
CONTAINS: "CONTAINS",
|
||||||
|
CONTAINSALL: "CONTAINSALL",
|
||||||
|
CONTAINSNONE: "CONTAINSNONE",
|
||||||
|
CONTAINSSOME: "CONTAINSSOME",
|
||||||
CONTENT: "CONTENT",
|
CONTENT: "CONTENT",
|
||||||
CREATE: "CREATE",
|
CREATE: "CREATE",
|
||||||
|
CUSTOM: "CUSTOM",
|
||||||
DATABASE: "DATABASE",
|
DATABASE: "DATABASE",
|
||||||
DEFAULT: "DEFAULT",
|
DEFAULT: "DEFAULT",
|
||||||
DEFINE: "DEFINE",
|
DEFINE: "DEFINE",
|
||||||
|
@ -242,6 +291,7 @@ var tokens = [...]string{
|
||||||
EXPUNGE: "EXPUNGE",
|
EXPUNGE: "EXPUNGE",
|
||||||
FALSE: "FALSE",
|
FALSE: "FALSE",
|
||||||
FIELD: "FIELD",
|
FIELD: "FIELD",
|
||||||
|
FOR: "FOR",
|
||||||
FROM: "FROM",
|
FROM: "FROM",
|
||||||
FULL: "FULL",
|
FULL: "FULL",
|
||||||
GROUP: "GROUP",
|
GROUP: "GROUP",
|
||||||
|
@ -251,14 +301,21 @@ var tokens = [...]string{
|
||||||
INDEX: "INDEX",
|
INDEX: "INDEX",
|
||||||
INSERT: "INSERT",
|
INSERT: "INSERT",
|
||||||
INTO: "INTO",
|
INTO: "INTO",
|
||||||
|
IS: "IS",
|
||||||
|
LET: "LET",
|
||||||
LIMIT: "LIMIT",
|
LIMIT: "LIMIT",
|
||||||
MANDATORY: "MANDATORY",
|
MANDATORY: "MANDATORY",
|
||||||
|
MATCH: "MATCH",
|
||||||
MAX: "MAX",
|
MAX: "MAX",
|
||||||
MERGE: "MERGE",
|
MERGE: "MERGE",
|
||||||
MIN: "MIN",
|
MIN: "MIN",
|
||||||
|
MISSING: "MISSING",
|
||||||
MODIFY: "MODIFY",
|
MODIFY: "MODIFY",
|
||||||
|
MULTI: "MULTI",
|
||||||
NAMESPACE: "NAMESPACE",
|
NAMESPACE: "NAMESPACE",
|
||||||
NONE: "NONE",
|
NONE: "NONE",
|
||||||
|
NONECONTAINEDIN: "NONECONTAINEDIN",
|
||||||
|
NOT: "NOT",
|
||||||
NOTNULL: "NOTNULL",
|
NOTNULL: "NOTNULL",
|
||||||
NOW: "NOW",
|
NOW: "NOW",
|
||||||
NULL: "NULL",
|
NULL: "NULL",
|
||||||
|
@ -272,17 +329,22 @@ var tokens = [...]string{
|
||||||
REMOVE: "REMOVE",
|
REMOVE: "REMOVE",
|
||||||
RESYNC: "RESYNC",
|
RESYNC: "RESYNC",
|
||||||
RETURN: "RETURN",
|
RETURN: "RETURN",
|
||||||
|
ROLLBACK: "ROLLBACK",
|
||||||
|
RULES: "RULES",
|
||||||
SELECT: "SELECT",
|
SELECT: "SELECT",
|
||||||
SET: "SET",
|
SET: "SET",
|
||||||
|
SOMECONTAINEDIN: "SOMECONTAINEDIN",
|
||||||
START: "START",
|
START: "START",
|
||||||
TABLE: "TABLE",
|
TABLE: "TABLE",
|
||||||
TO: "TO",
|
TO: "TO",
|
||||||
|
TRANSACTION: "TRANSACTION",
|
||||||
TRUE: "TRUE",
|
TRUE: "TRUE",
|
||||||
TYPE: "TYPE",
|
TYPE: "TYPE",
|
||||||
UNIQUE: "UNIQUE",
|
UNIQUE: "UNIQUE",
|
||||||
UPDATE: "UPDATE",
|
UPDATE: "UPDATE",
|
||||||
UPSERT: "UPSERT",
|
UPSERT: "UPSERT",
|
||||||
USE: "USE",
|
USE: "USE",
|
||||||
|
VALIDATE: "VALIDATE",
|
||||||
VERSION: "VERSION",
|
VERSION: "VERSION",
|
||||||
VOID: "VOID",
|
VOID: "VOID",
|
||||||
WHERE: "WHERE",
|
WHERE: "WHERE",
|
||||||
|
@ -325,7 +387,11 @@ func (tok Token) precedence() int {
|
||||||
return 1
|
return 1
|
||||||
case AND:
|
case AND:
|
||||||
return 2
|
return 2
|
||||||
case EQ, NEQ, EQR, NER, LT, LTE, GT, GTE:
|
case EQ, NEQ, EEQ, NEE,
|
||||||
|
LT, LTE, GT, GTE,
|
||||||
|
ANY, SIN, SNI, INS, NIS,
|
||||||
|
CONTAINSALL, CONTAINSNONE, CONTAINSSOME,
|
||||||
|
ALLCONTAINEDIN, NONECONTAINEDIN, SOMECONTAINEDIN:
|
||||||
return 3
|
return 3
|
||||||
case ADD, SUB:
|
case ADD, SUB:
|
||||||
return 4
|
return 4
|
||||||
|
|
57
sql/trans.go
Normal file
57
sql/trans.go
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
// Copyright © 2016 Abcum Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package sql
|
||||||
|
|
||||||
|
func (p *Parser) parseBeginStatement(explain bool) (stmt *BeginStatement, err error) {
|
||||||
|
|
||||||
|
stmt = &BeginStatement{}
|
||||||
|
|
||||||
|
_, _, _ = p.mightBe(TRANSACTION)
|
||||||
|
|
||||||
|
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseCancelStatement(explain bool) (stmt *CancelStatement, err error) {
|
||||||
|
|
||||||
|
stmt = &CancelStatement{}
|
||||||
|
|
||||||
|
_, _, _ = p.mightBe(TRANSACTION)
|
||||||
|
|
||||||
|
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseCommitStatement(explain bool) (stmt *CommitStatement, err error) {
|
||||||
|
|
||||||
|
stmt = &CommitStatement{}
|
||||||
|
|
||||||
|
_, _, _ = p.mightBe(TRANSACTION)
|
||||||
|
|
||||||
|
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
12
sql/type.go
12
sql/type.go
|
@ -14,21 +14,21 @@
|
||||||
|
|
||||||
package sql
|
package sql
|
||||||
|
|
||||||
func (p *Parser) parseType() (exp Ident, err error) {
|
func (p *Parser) parseType() (exp string, err error) {
|
||||||
|
|
||||||
allowed := []string{"any", "url", "uuid", "color", "email", "phone", "array", "object", "domain", "string", "number", "custom", "boolean", "datetime", "latitude", "longitude"}
|
allowed := []string{"any", "url", "uuid", "color", "email", "phone", "array", "object", "domain", "string", "number", "custom", "boolean", "datetime", "latitude", "longitude"}
|
||||||
|
|
||||||
tok, lit, err := p.shouldBe(IDENT)
|
_, lit, err := p.shouldBe(IDENT, CUSTOM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Ident(""), err
|
return string(""), &ParseError{Found: lit, Expected: allowed}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !contains(lit, allowed) {
|
if !contains(lit, allowed) {
|
||||||
return Ident(""), &ParseError{Found: lit, Expected: allowed}
|
return string(""), &ParseError{Found: lit, Expected: allowed}
|
||||||
}
|
}
|
||||||
|
|
||||||
val, err := declare(tok, lit)
|
val, err := declare(STRING, lit)
|
||||||
|
|
||||||
return val.(Ident), err
|
return val.(string), err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
13
sql/util.go
13
sql/util.go
|
@ -80,6 +80,9 @@ func declare(tok Token, lit string) (interface{}, error) {
|
||||||
case VOID:
|
case VOID:
|
||||||
return &Void{}, nil
|
return &Void{}, nil
|
||||||
|
|
||||||
|
case MISSING:
|
||||||
|
return &Void{}, nil
|
||||||
|
|
||||||
case EMPTY:
|
case EMPTY:
|
||||||
return &Empty{}, nil
|
return &Empty{}, nil
|
||||||
|
|
||||||
|
@ -93,10 +96,10 @@ func declare(tok Token, lit string) (interface{}, error) {
|
||||||
return &Desc{}, nil
|
return &Desc{}, nil
|
||||||
|
|
||||||
case ID:
|
case ID:
|
||||||
return Ident(lit), nil
|
return &Ident{lit}, nil
|
||||||
|
|
||||||
case IDENT:
|
case IDENT:
|
||||||
return Ident(lit), nil
|
return &Ident{lit}, nil
|
||||||
|
|
||||||
case NOW:
|
case NOW:
|
||||||
return time.Now(), nil
|
return time.Now(), nil
|
||||||
|
@ -111,7 +114,7 @@ func declare(tok Token, lit string) (interface{}, error) {
|
||||||
return regexp.Compile(lit)
|
return regexp.Compile(lit)
|
||||||
|
|
||||||
case NUMBER:
|
case NUMBER:
|
||||||
return strconv.ParseInt(lit, 10, 64)
|
return strconv.ParseFloat(lit, 64)
|
||||||
|
|
||||||
case DOUBLE:
|
case DOUBLE:
|
||||||
return strconv.ParseFloat(lit, 64)
|
return strconv.ParseFloat(lit, 64)
|
||||||
|
@ -120,7 +123,7 @@ func declare(tok Token, lit string) (interface{}, error) {
|
||||||
return time.ParseDuration(lit)
|
return time.ParseDuration(lit)
|
||||||
|
|
||||||
case ARRAY:
|
case ARRAY:
|
||||||
var j interface{}
|
var j []interface{}
|
||||||
json.Unmarshal([]byte(lit), &j)
|
json.Unmarshal([]byte(lit), &j)
|
||||||
if j == nil {
|
if j == nil {
|
||||||
return j, fmt.Errorf("Invalid JSON: %s", lit)
|
return j, fmt.Errorf("Invalid JSON: %s", lit)
|
||||||
|
@ -128,7 +131,7 @@ func declare(tok Token, lit string) (interface{}, error) {
|
||||||
return j, nil
|
return j, nil
|
||||||
|
|
||||||
case JSON:
|
case JSON:
|
||||||
var j interface{}
|
var j map[string]interface{}
|
||||||
json.Unmarshal([]byte(lit), &j)
|
json.Unmarshal([]byte(lit), &j)
|
||||||
if j == nil {
|
if j == nil {
|
||||||
return j, fmt.Errorf("Invalid JSON: %s", lit)
|
return j, fmt.Errorf("Invalid JSON: %s", lit)
|
||||||
|
|
Loading…
Reference in a new issue