From 2de5a8fa3f9e56d78b25089975684563c7218866 Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Fri, 14 Apr 2017 13:13:31 +0100 Subject: [PATCH] Simplify parsing @table:thing definitions --- sql/ast.go | 2 + sql/exprs.go | 2 +- sql/let.go | 6 +- sql/scanner.go | 178 +++++++++++++++++-------------------------------- sql/tokens.go | 1 + 5 files changed, 70 insertions(+), 119 deletions(-) diff --git a/sql/ast.go b/sql/ast.go index c97ddf19..760ff7ae 100644 --- a/sql/ast.go +++ b/sql/ast.go @@ -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 { diff --git a/sql/exprs.go b/sql/exprs.go index 9479f8ae..f1f32cda 100644 --- a/sql/exprs.go +++ b/sql/exprs.go @@ -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) { diff --git a/sql/let.go b/sql/let.go index 40de6d71..6d9e6112 100644 --- a/sql/let.go +++ b/sql/let.go @@ -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 } diff --git a/sql/scanner.go b/sql/scanner.go index 11054db4..b5dcd77f 100644 --- a/sql/scanner.go +++ b/sql/scanner.go @@ -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 - } else { - s.undo() - break - } + if tok, tbv, _ = s.part(); tok == ILLEGAL { + buf.WriteString(tbv) + return ILLEGAL, buf.String(), val + } else { + buf.WriteString(tbv) } - if beg.Len() < 1 || tok == ILLEGAL { - return ILLEGAL, buf.String() + beg.String() + mid.String() + end.String(), val + if ch := s.next(); ch == ':' { + buf.WriteRune(ch) + } else { + return ILLEGAL, buf.String(), val } - for { - if ch := s.next(); ch != ':' { - s.undo() - break - } else { - mid.WriteRune(ch) - break - } + if tok, lit, idv = s.part(); tok == ILLEGAL { + buf.WriteString(lit) + return ILLEGAL, buf.String(), val + } else { + buf.WriteString(lit) } - 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 - } else { - s.undo() - break - } - } - - 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 { diff --git a/sql/tokens.go b/sql/tokens.go index 4a8d3974..86261ee6 100644 --- a/sql/tokens.go +++ b/sql/tokens.go @@ -211,6 +211,7 @@ var tokens = [...]string{ DATE: "DATE", TIME: "TIME", JSON: "JSON", + EXPR: "EXPR", IDENT: "IDENT", THING: "THING", STRING: "STRING",