diff --git a/sql/exprs.go b/sql/exprs.go index 7ccc7e20..556e9a5b 100644 --- a/sql/exprs.go +++ b/sql/exprs.go @@ -57,114 +57,6 @@ func (p *Parser) parseNames() (mul []string, err error) { // // -------------------------------------------------- -func (p *Parser) parseTable() (*Table, error) { - - _, lit, err := p.shouldBe(IDENT, NUMBER, DATE) - if err != nil { - return nil, &ParseError{Found: lit, Expected: []string{"table name"}} - } - - return &Table{lit}, err - -} - -func (p *Parser) parseTables() (mul []*Table, err error) { - - for { - - one, err := p.parseTable() - 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) parseThing() (one *Thing, err error) { - - var tok Token - var lit string - var val interface{} - - one = &Thing{} - - _, _, err = p.shouldBe(EAT) - if err != nil { - return nil, err - } - - _, one.TB, err = p.shouldBe(IDENT, NUMBER, DATE) - if err != nil { - return nil, &ParseError{Found: one.TB, Expected: []string{"table name"}} - } - - _, _, err = p.shouldBe(COLON) - if err != nil { - return nil, err - } - - tok, lit, err = p.shouldBe(IDENT, NUMBER, DOUBLE, DATE, TIME) - if err != nil { - return nil, &ParseError{Found: lit, Expected: []string{"table id"}} - } - - switch tok { - case IDENT: - val = lit - default: - val, err = declare(tok, lit) - } - - if err != nil { - return nil, err - } - - one.ID = val - - return - -} - -func (p *Parser) parseThings() (mul []Expr, err error) { - - for { - - one, err := p.parseThing() - 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) parseIdent() (*Ident, error) { _, lit, err := p.shouldBe(IDENT) @@ -178,6 +70,19 @@ func (p *Parser) parseIdent() (*Ident, error) { } +func (p *Parser) parseThing() (*Thing, error) { + + _, lit, err := p.shouldBe(THING) + if err != nil { + return nil, &ParseError{Found: lit, Expected: []string{"record id"}} + } + + val, err := p.declare(THING, lit) + + return val.(*Thing), err + +} + func (p *Parser) parseArray() ([]interface{}, error) { _, lit, err := p.shouldBe(ARRAY) diff --git a/sql/scanner.go b/sql/scanner.go index 2fc1b44f..fe5de7b5 100644 --- a/sql/scanner.go +++ b/sql/scanner.go @@ -70,12 +70,12 @@ func (s *Scanner) Scan() (tok Token, lit string, val interface{}) { return MUL, string(ch), val case '÷': return DIV, string(ch), val - case '@': - return EAT, string(ch), val case ',': return COMMA, string(ch), val case '.': return DOT, string(ch), val + case '@': + return s.scanThing(ch) case '"': return s.scanString(ch) case '\'': @@ -374,6 +374,109 @@ func (s *Scanner) scanIdent(chp ...rune) (tok Token, lit string, val interface{} } +// scanThing consumes the current rune and all contiguous ident runes. +func (s *Scanner) scanThing(chp ...rune) (tok Token, lit string, val interface{}) { + + tok = THING + + // 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 isLetter(ch) { + tok, lit, _ = s.scanIdent(ch) + beg.WriteString(lit) + } else if isNumber(ch) { + tok, lit, _ = s.scanNumber(ch) + beg.WriteString(lit) + } else if ch == '`' { + tok, lit, _ = s.scanQuoted(ch) + beg.WriteString(lit) + } else if ch == '{' { + tok, lit, _ = s.scanQuoted(ch) + beg.WriteString(lit) + } else if ch == '⟨' { + tok, lit, _ = s.scanQuoted(ch) + beg.WriteString(lit) + } else { + s.undo() + break + } + } + + if beg.Len() < 1 { + return ILLEGAL, buf.String() + beg.String() + mid.String() + end.String(), val + } + + for { + if ch := s.next(); ch != ':' { + s.undo() + break + } else { + mid.WriteRune(ch) + break + } + } + + 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 isLetter(ch) { + tok, lit, _ = s.scanIdent(ch) + end.WriteString(lit) + } else if isNumber(ch) { + tok, lit, _ = s.scanNumber(ch) + end.WriteString(lit) + } else if ch == '`' { + tok, lit, _ = s.scanQuoted(ch) + beg.WriteString(lit) + } else if ch == '{' { + tok, lit, _ = s.scanQuoted(ch) + end.WriteString(lit) + } else if ch == '⟨' { + tok, lit, _ = s.scanQuoted(ch) + end.WriteString(lit) + } else { + s.undo() + break + } + } + + if end.Len() < 1 { + return ILLEGAL, buf.String() + beg.String() + mid.String() + end.String(), val + } + + val = &Thing{ + TB: beg.String(), + ID: end.String(), + } + + switch tok { + case DATE, TIME, NUMBER, DOUBLE: + val.(*Thing).ID, _ = s.p.declare(tok, end.String()) + case REGION: + return ILLEGAL, buf.String() + beg.String() + mid.String() + end.String(), val + } + + // Otherwise return as a regular thing. + return THING, buf.String() + beg.String() + mid.String() + end.String(), val + +} + func (s *Scanner) scanNumber(chp ...rune) (tok Token, lit string, val interface{}) { tok = NUMBER @@ -442,6 +545,10 @@ func (s *Scanner) scanString(chp ...rune) (tok Token, lit string, val interface{ end = '⟩' } + if beg == '{' { + end = '}' + } + tok = STRING // Create a buffer diff --git a/sql/what.go b/sql/what.go index b8182939..d4ba7f80 100644 --- a/sql/what.go +++ b/sql/what.go @@ -18,27 +18,23 @@ func (p *Parser) parseWhat() (mul []Expr, err error) { for { - _, _, exi := p.mightBe(EAT) + tok, lit, err := p.shouldBe(IDENT, THING) + if err != nil { + return nil, &ParseError{Found: lit, Expected: []string{"table name or record id"}} + } - if exi == false { - one, err := p.parseTable() - if err != nil { - return nil, err - } + if p.is(tok, IDENT) { + one, _ := p.declare(TABLE, lit) mul = append(mul, one) } - if exi == true { - p.unscan() - one, err := p.parseThing() - if err != nil { - return nil, err - } + if p.is(tok, THING) { + one, _ := p.declare(THING, lit) mul = append(mul, one) } // If the next token is not a comma then break the loop. - if _, _, exi = p.mightBe(COMMA); !exi { + if _, _, exi := p.mightBe(COMMA); !exi { break }