surrealpatch/sql/parser.go
2016-02-26 17:27:07 +00:00

183 lines
3.8 KiB
Go

// 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
import (
"io"
"strings"
)
// Parser represents a parser.
type Parser struct {
s *Scanner
buf struct {
tok Token // last read token
lit string // last read literal
n int // buffer size (max=1)
}
}
// Parse parses a string.
func Parse(s string) (*Query, error) {
r := strings.NewReader(s)
p := &Parser{s: NewScanner(r)}
return p.Parse()
}
// NewParser returns a new instance of Parser.
func NewParser(r io.Reader) *Parser {
return &Parser{s: NewScanner(r)}
}
// Parse parses single or multiple SQL queries.
func (p *Parser) Parse() (*Query, error) {
return p.ParseMulti()
}
// ParseMulti parses multiple SQL SELECT statements.
func (p *Parser) ParseMulti() (*Query, error) {
var statements Statements
var semi bool
for {
if tok, _ := p.scanIgnoreWhitespace(); tok == EOF {
return &Query{Statements: statements}, nil
} else if !semi && tok == SEMICOLON {
semi = true
} else {
p.unscan()
s, err := p.ParseSingle()
if err != nil {
return nil, err
}
statements = append(statements, s)
semi = false
}
}
}
// ParseSingle parses a single SQL SELECT statement.
func (p *Parser) ParseSingle() (Statement, error) {
// Inspect the first token.
tok, lit := p.scanIgnoreWhitespace()
switch tok {
case SELECT:
return p.parseSelectStatement()
case CREATE:
return p.parseCreateStatement()
case INSERT:
return p.parseInsertStatement()
case UPSERT:
return p.parseUpsertStatement()
case UPDATE:
return p.parseUpdateStatement()
case MODIFY:
return p.parseModifyStatement()
case DELETE:
return p.parseDeleteStatement()
case RELATE:
return p.parseRelateStatement()
case RECORD:
return p.parseRecordStatement()
case DEFINE:
return p.parseDefineStatement()
case RESYNC:
return p.parseResyncStatement()
case REMOVE:
return p.parseRemoveStatement()
default:
return nil, &ParseError{
Found: lit,
Expected: []string{
"SELECT",
"INSERT",
"UPSERT",
"UPDATE",
"MODIFY",
"DELETE",
"RELATE",
"RECORD",
"DEFINE",
"RESYNC",
"REMOVE",
},
}
}
}
func (p *Parser) mightBe(expected ...Token) (tok Token, lit string, found bool) {
tok, lit = p.scanIgnoreWhitespace()
if found = in(tok, expected); !found {
p.unscan()
}
return
}
func (p *Parser) shouldBe(expected ...Token) (tok Token, lit string, err error) {
tok, lit = p.scanIgnoreWhitespace()
if found := in(tok, expected); !found {
p.unscan()
err = &ParseError{Found: lit, Expected: lookup(expected)}
}
return
}
// scan returns the next token from the underlying scanner.
// If a token has been unscanned then read that instead.
func (p *Parser) scan() (tok Token, lit string) {
// If we have a token on the buffer, then return it.
if p.buf.n != 0 {
p.buf.n = 0
return p.buf.tok, p.buf.lit
}
// Otherwise read the next token from the scanner.
tok, lit = p.s.Scan()
// Save it to the buffer in case we unscan later.
p.buf.tok, p.buf.lit = tok, lit
return
}
// unscan pushes the previously read token back onto the buffer.
func (p *Parser) unscan() { p.buf.n = 1 }
// scanIgnoreWhitespace scans the next non-whitespace token.
func (p *Parser) scanIgnoreWhitespace() (tok Token, lit string) {
tok, lit = p.scan()
if tok == WS {
tok, lit = p.scan()
}
return
}