Simplify parsing @table:thing definitions

This commit is contained in:
Tobie Morgan Hitchcock 2017-04-14 13:13:31 +01:00
parent a3da779190
commit 2de5a8fa3f
5 changed files with 70 additions and 119 deletions

View file

@ -635,6 +635,8 @@ func NewThing(TB string, ID interface{}) *Thing {
return &Thing{TB: TB, ID: int64(cnv)}
}
return &Thing{TB: TB, ID: cnv}
} else if cnv, err := strconv.ParseBool(str); err == nil {
return &Thing{TB: TB, ID: cnv}
} else if cnv, err := time.Parse(RFCDate, str); err == nil {
return &Thing{TB: TB, ID: cnv.UTC()}
} else if cnv, err := time.Parse(RFCTime, str); err == nil {

View file

@ -30,7 +30,7 @@ func (p *parser) parseWhat() (mul []Expr, err error) {
tok, lit, err := p.shouldBe(IDENT, THING, PARAM)
if err != nil {
return nil, &ParseError{Found: lit, Expected: []string{"table name or record id"}}
return nil, &ParseError{Found: lit, Expected: []string{"table, or thing"}}
}
if p.is(tok, IDENT) {

View file

@ -62,7 +62,11 @@ func (p *parser) parseLetStatement() (stmt *LetStatement, err error) {
p.v[stmt.Name.ID] = stmt.What
case time.Time, time.Duration:
p.v[stmt.Name.ID] = stmt.What
case *Null, *Void, *Empty, *Table, *Thing, *Param:
case Array, Object:
p.v[stmt.Name.ID] = stmt.What
case *Null, *Void, *Empty:
p.v[stmt.Name.ID] = stmt.What
case *Table, *Thing, *Param, *Ident, *Value:
p.v[stmt.Name.ID] = stmt.What
}

View file

@ -17,6 +17,7 @@ package sql
import (
"bufio"
"bytes"
"fmt"
"io"
"regexp"
"strings"
@ -425,140 +426,38 @@ func (s *scanner) scanThing(chp ...rune) (tok Token, lit string, val interface{}
tok = THING
// Store whether params
var tbp bool
var idp bool
// Store section values
var tbv string
var idv interface{}
// Create a buffer
var buf bytes.Buffer
var beg bytes.Buffer
var mid bytes.Buffer
var end bytes.Buffer
// Read passed in runes
for _, ch := range chp {
buf.WriteRune(ch)
}
for {
if ch := s.next(); ch == eof {
break
} else if isThingChar(ch) {
tok, lit, _ = s.scanIdent(ch)
beg.WriteString(lit)
break
} else if ch == '$' {
tbp = true // The TB is a param
tok, lit, _ = s.scanParams(ch)
beg.WriteString(lit)
break
} else if ch == '`' {
tok, lit, _ = s.scanQuoted(ch)
beg.WriteString(lit)
break
} else if ch == '{' {
tok, lit, _ = s.scanQuoted(ch)
beg.WriteString(lit)
break
} else if ch == '⟨' {
tok, lit, _ = s.scanQuoted(ch)
beg.WriteString(lit)
break
if tok, tbv, _ = s.part(); tok == ILLEGAL {
buf.WriteString(tbv)
return ILLEGAL, buf.String(), val
} else {
s.undo()
break
}
buf.WriteString(tbv)
}
if beg.Len() < 1 || tok == ILLEGAL {
return ILLEGAL, buf.String() + beg.String() + mid.String() + end.String(), val
}
for {
if ch := s.next(); ch != ':' {
s.undo()
break
if ch := s.next(); ch == ':' {
buf.WriteRune(ch)
} else {
mid.WriteRune(ch)
break
}
return ILLEGAL, buf.String(), val
}
if mid.Len() < 1 {
return ILLEGAL, buf.String() + beg.String() + mid.String() + end.String(), val
}
for {
if ch := s.next(); ch == eof {
break
} else if isThingChar(ch) {
tok, lit, _ = s.scanIdent(ch)
end.WriteString(lit)
break
} else if ch == '$' {
idp = true // The ID is a param
tok, lit, _ = s.scanParams(ch)
end.WriteString(lit)
break
} else if ch == '`' {
tok, lit, _ = s.scanQuoted(ch)
end.WriteString(lit)
break
} else if ch == '{' {
tok, lit, _ = s.scanQuoted(ch)
end.WriteString(lit)
break
} else if ch == '⟨' {
tok, lit, _ = s.scanQuoted(ch)
end.WriteString(lit)
break
if tok, lit, idv = s.part(); tok == ILLEGAL {
buf.WriteString(lit)
return ILLEGAL, buf.String(), val
} else {
s.undo()
break
}
buf.WriteString(lit)
}
if end.Len() < 1 || tok == ILLEGAL {
return ILLEGAL, buf.String() + beg.String() + mid.String() + end.String(), val
}
tbv = beg.String()
idv = end.String()
if tbp { // The TB is a param
if p, ok := s.p.v[tbv]; ok {
switch v := p.(type) {
case string:
tbv = v
default:
return ILLEGAL, buf.String() + beg.String() + mid.String() + end.String(), val
}
} else {
return ILLEGAL, buf.String() + beg.String() + mid.String() + end.String(), val
}
}
if idp { // The ID is a param
if p, ok := s.p.v[idv.(string)]; ok {
switch v := p.(type) {
case bool, int64, float64, string, []interface{}, map[string]interface{}:
idv = v
default:
return ILLEGAL, buf.String() + beg.String() + mid.String() + end.String(), val
}
} else {
return ILLEGAL, buf.String() + beg.String() + mid.String() + end.String(), val
}
}
val = NewThing(tbv, idv)
// Otherwise return as a regular thing.
return THING, buf.String() + beg.String() + mid.String() + end.String(), val
return THING, buf.String(), NewThing(tbv, idv)
}
@ -795,6 +694,51 @@ func (s *scanner) scanObject(chp ...rune) (tok Token, lit string, val interface{
}
func (s *scanner) part() (tok Token, lit string, val interface{}) {
if ch := s.next(); isLetter(ch) {
tok, lit, _ = s.scanIdent(ch)
} else if isNumber(ch) {
tok, lit, _ = s.scanNumber(ch)
} else if ch == '`' {
tok, lit, _ = s.scanQuoted(ch)
} else if ch == '{' {
tok, lit, _ = s.scanQuoted(ch)
} else if ch == '⟨' {
tok, lit, _ = s.scanQuoted(ch)
} else if ch == '$' {
tok, lit, _ = s.scanParams(ch)
if p, ok := s.p.v[lit]; ok {
switch v := p.(type) {
case bool, int64, float64, string:
val, lit = v, fmt.Sprint(v)
case *Ident:
lit = v.ID
case *Value:
lit = v.ID
default:
tok = ILLEGAL
}
} else {
tok = ILLEGAL
}
} else {
s.undo()
tok = ILLEGAL
}
if tok != IDENT && tok != PARAM && tok != NUMBER && tok != DOUBLE {
tok = ILLEGAL
}
if val == nil {
val = lit
}
return
}
// next reads the next rune from the bufferred reader.
// Returns the rune(0) if an error occurs (or io.EOF is returned).
func (s *scanner) next() rune {

View file

@ -211,6 +211,7 @@ var tokens = [...]string{
DATE: "DATE",
TIME: "TIME",
JSON: "JSON",
EXPR: "EXPR",
IDENT: "IDENT",
THING: "THING",
STRING: "STRING",