surrealpatch/sql/scanner.go

650 lines
11 KiB
Go
Raw Normal View History

2016-02-26 17:27:07 +00:00
// 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 (
"bufio"
"bytes"
"io"
2016-09-06 13:30:59 +00:00
"regexp"
2016-02-26 17:27:07 +00:00
"strings"
"time"
)
// Scanner represents a lexical scanner.
type Scanner struct {
b []rune // any runes before
a []rune // any runes after
p *Parser
2016-02-26 17:27:07 +00:00
r *bufio.Reader
}
// NewScanner returns a new instance of Scanner.
func NewScanner(p *Parser, r io.Reader) *Scanner {
return &Scanner{p: p, r: bufio.NewReader(r)}
2016-02-26 17:27:07 +00:00
}
// Scan returns the next token and literal value.
func (s *Scanner) Scan() (tok Token, lit string) {
// Read the next rune.
2016-09-06 13:30:59 +00:00
ch := s.next()
2016-02-26 17:27:07 +00:00
// If we see whitespace then consume all contiguous whitespace.
if isWhitespace(ch) {
2016-09-06 13:30:59 +00:00
return s.scanBlank(ch)
2016-02-26 17:27:07 +00:00
}
// If we see a letter then consume as an string.
if isLetter(ch) {
2016-09-06 13:30:59 +00:00
return s.scanIdent(ch)
2016-02-26 17:27:07 +00:00
}
// If we see a number then consume as a number.
if isNumber(ch) {
2016-09-06 13:30:59 +00:00
return s.scanNumber(ch)
2016-02-26 17:27:07 +00:00
}
// Otherwise read the individual character.
switch ch {
case eof:
return EOF, ""
case '*':
return ALL, string(ch)
2016-09-06 13:30:59 +00:00
case '×':
return MUL, string(ch)
case '∙':
return MUL, string(ch)
case '÷':
return DIV, string(ch)
2016-02-26 17:27:07 +00:00
case '@':
return EAT, string(ch)
case ',':
return COMMA, string(ch)
case '.':
return DOT, string(ch)
case '"':
2016-09-06 13:30:59 +00:00
return s.scanString(ch)
2016-02-26 17:27:07 +00:00
case '\'':
2016-09-06 13:30:59 +00:00
return s.scanString(ch)
2016-02-26 17:27:07 +00:00
case '`':
2016-09-06 13:30:59 +00:00
return s.scanQuoted(ch)
2016-05-23 12:32:02 +00:00
case '⟨':
2016-09-06 13:30:59 +00:00
return s.scanQuoted(ch)
2016-02-26 17:27:07 +00:00
case '{':
2016-09-06 13:30:59 +00:00
return s.scanObject(ch)
2016-02-26 17:27:07 +00:00
case '[':
2016-09-06 13:30:59 +00:00
return s.scanObject(ch)
case '$':
return s.scanParams(ch)
2016-02-26 17:27:07 +00:00
case ':':
return COLON, string(ch)
case ';':
return SEMICOLON, string(ch)
case '(':
return LPAREN, string(ch)
case ')':
return RPAREN, string(ch)
2016-09-06 13:30:59 +00:00
case '¬':
return NEQ, string(ch)
case '≤':
return LTE, string(ch)
case '≥':
return GTE, string(ch)
case '~':
return SIN, string(ch)
case '∋':
return SIN, string(ch)
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)
2016-07-21 21:45:35 +00:00
case '/':
chn := s.next()
switch {
case chn == '*':
return s.scanCommentMultiple(ch)
2016-09-06 13:30:59 +00:00
case chn == ' ':
s.undo()
return DIV, string(ch)
2016-07-21 21:45:35 +00:00
default:
2016-09-06 13:30:59 +00:00
s.undo()
2016-07-21 21:45:35 +00:00
return s.scanRegexp(ch)
2016-07-04 10:37:37 +00:00
}
2016-09-06 13:30:59 +00:00
case '=':
chn := s.next()
switch {
case chn == '~':
return SIN, "=~"
case chn == '=':
return EEQ, "=="
default:
s.undo()
return EQ, string(ch)
2016-02-26 17:27:07 +00:00
}
2016-09-06 13:30:59 +00:00
case '?':
chn := s.next()
switch {
case chn == '=':
return ANY, "?="
default:
s.undo()
return QMARK, string(ch)
2016-02-26 17:27:07 +00:00
}
2016-09-06 13:30:59 +00:00
case '!':
chn := s.next()
switch {
case chn == '=':
if s.next() == '=' {
return NEE, "!=="
} else {
s.undo()
return NEQ, "!="
2016-07-04 10:37:37 +00:00
}
2016-09-06 13:30:59 +00:00
case chn == '~':
return SNI, "!~"
default:
s.undo()
return EXC, string(ch)
2016-07-04 10:37:37 +00:00
}
2016-09-06 13:30:59 +00:00
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)
2016-02-26 17:27:07 +00:00
}
case '>':
2016-09-06 13:30:59 +00:00
chn := s.next()
switch {
case chn == '=':
2016-02-26 17:27:07 +00:00
return GTE, ">="
2016-09-06 13:30:59 +00:00
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, "<-"
}
default:
s.undo()
return LT, string(ch)
2016-02-26 17:27:07 +00:00
}
}
return ILLEGAL, string(ch)
}
2016-09-06 13:30:59 +00:00
// scanBlank consumes the current rune and all contiguous whitespace.
func (s *Scanner) scanBlank(chp ...rune) (tok Token, lit string) {
tok = WS
2016-02-26 17:27:07 +00:00
2016-09-06 13:30:59 +00:00
// Create a buffer
2016-02-26 17:27:07 +00:00
var buf bytes.Buffer
2016-09-06 13:30:59 +00:00
// Read passed in runes
for _, ch := range chp {
buf.WriteRune(ch)
}
// Read subsequent characters
2016-02-26 17:27:07 +00:00
for {
2016-09-06 13:30:59 +00:00
if ch := s.next(); ch == eof {
2016-02-26 17:27:07 +00:00
break
} else if !isWhitespace(ch) {
2016-09-06 13:30:59 +00:00
s.undo()
2016-02-26 17:27:07 +00:00
break
} else {
buf.WriteRune(ch)
}
}
2016-09-06 13:30:59 +00:00
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...)
return BOUNDPARAM, lit
if s.p.is(tok, IDENT) {
2016-09-06 13:30:59 +00:00
}
return tok, lit
2016-02-26 17:27:07 +00:00
}
// scanIdent consumes the current rune and all contiguous ident runes.
2016-09-06 13:30:59 +00:00
func (s *Scanner) scanIdent(chp ...rune) (tok Token, lit string) {
tok = IDENT
2016-02-26 17:27:07 +00:00
2016-09-06 13:30:59 +00:00
// Create a buffer
2016-02-26 17:27:07 +00:00
var buf bytes.Buffer
2016-09-06 13:30:59 +00:00
// Read passed in runes
for _, ch := range chp {
buf.WriteRune(ch)
}
// Read subsequent characters
2016-02-26 17:27:07 +00:00
for {
2016-09-06 13:30:59 +00:00
if ch := s.next(); ch == eof {
2016-02-26 17:27:07 +00:00
break
} else if !isIdentChar(ch) {
2016-09-06 13:30:59 +00:00
s.undo()
2016-02-26 17:27:07 +00:00
break
} else {
buf.WriteRune(ch)
}
}
// If the string matches a keyword then return that keyword.
if tok := keywords[strings.ToUpper(buf.String())]; tok > 0 {
return tok, buf.String()
}
2016-05-23 12:32:02 +00:00
if _, err := time.ParseDuration(buf.String()); err == nil {
return DURATION, buf.String()
}
2016-02-26 17:27:07 +00:00
// Otherwise return as a regular identifier.
2016-09-06 13:30:59 +00:00
return tok, buf.String()
2016-02-26 17:27:07 +00:00
}
2016-09-06 13:30:59 +00:00
func (s *Scanner) scanNumber(chp ...rune) (tok Token, lit string) {
2016-05-23 12:32:02 +00:00
tok = NUMBER
2016-09-06 13:30:59 +00:00
// Create a buffer
2016-05-23 12:32:02 +00:00
var buf bytes.Buffer
2016-09-06 13:30:59 +00:00
// Read passed in runes
for _, ch := range chp {
buf.WriteRune(ch)
}
// Read subsequent characters
2016-05-23 12:32:02 +00:00
for {
2016-09-06 13:30:59 +00:00
if ch := s.next(); ch == eof {
2016-05-23 12:32:02 +00:00
break
} else if isNumber(ch) {
buf.WriteRune(ch)
} else if isLetter(ch) {
tok = IDENT
buf.WriteRune(ch)
} else if ch == '.' {
if tok == DOUBLE {
tok = IDENT
}
if tok == NUMBER {
tok = DOUBLE
}
buf.WriteRune(ch)
} else {
2016-09-06 13:30:59 +00:00
s.undo()
2016-05-23 12:32:02 +00:00
break
}
}
return tok, buf.String()
}
2016-09-06 13:30:59 +00:00
func (s *Scanner) scanQuoted(chp ...rune) (Token, string) {
2016-02-26 17:27:07 +00:00
2016-09-06 13:30:59 +00:00
tok, lit := s.scanString(chp...)
2016-02-26 17:27:07 +00:00
return IDENT, lit
if s.p.is(tok, STRING) {
2016-02-26 17:27:07 +00:00
}
return tok, lit
}
2016-09-06 13:30:59 +00:00
func (s *Scanner) scanString(chp ...rune) (tok Token, lit string) {
2016-02-26 17:27:07 +00:00
2016-09-06 13:30:59 +00:00
beg := chp[0]
2016-05-23 12:32:02 +00:00
end := beg
if beg == '"' {
end = '"'
}
if beg == '`' {
end = '`'
}
if beg == '⟨' {
end = '⟩'
}
2016-02-26 17:27:07 +00:00
2016-09-06 13:30:59 +00:00
tok = STRING
// Create a buffer
var buf bytes.Buffer
// Ignore passed in runes
// Read subsequent characters
2016-02-26 17:27:07 +00:00
for {
2016-09-06 13:30:59 +00:00
if ch := s.next(); ch == end {
2016-02-26 17:27:07 +00:00
break
} else if ch == eof {
return ILLEGAL, buf.String()
} else if ch == '\n' {
tok = REGION
buf.WriteRune(ch)
2016-05-23 12:32:02 +00:00
} else if ch == '\r' {
tok = REGION
buf.WriteRune(ch)
2016-02-26 17:27:07 +00:00
} else if ch == '\\' {
2016-09-06 13:30:59 +00:00
switch chn := s.next(); chn {
2016-05-23 12:32:02 +00:00
default:
buf.WriteRune(chn)
case 'b':
continue
case 't':
2016-09-06 13:30:59 +00:00
tok = REGION
2016-05-23 12:32:02 +00:00
buf.WriteRune('\t')
case 'r':
tok = REGION
buf.WriteRune('\r')
case 'n':
tok = REGION
2016-02-26 17:27:07 +00:00
buf.WriteRune('\n')
}
} else {
buf.WriteRune(ch)
}
}
2016-05-23 12:32:02 +00:00
if _, err := time.ParseDuration(buf.String()); err == nil {
return DURATION, buf.String()
2016-02-26 17:27:07 +00:00
}
if _, err := time.Parse("2006-01-02", buf.String()); err == nil {
return DATE, buf.String()
}
if _, err := time.Parse(time.RFC3339, buf.String()); err == nil {
return TIME, buf.String()
}
return tok, buf.String()
}
2016-09-06 13:30:59 +00:00
func (s *Scanner) scanRegexp(chp ...rune) (tok Token, lit string) {
2016-02-26 17:27:07 +00:00
tok = IDENT
2016-09-06 13:30:59 +00:00
// Create a buffer
2016-02-26 17:27:07 +00:00
var buf bytes.Buffer
2016-09-06 13:30:59 +00:00
// 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]
2016-02-26 17:27:07 +00:00
end := beg
2016-05-23 12:32:02 +00:00
sub := 0
2016-02-26 17:27:07 +00:00
if beg == '{' {
end = '}'
}
if beg == '[' {
end = ']'
}
2016-09-06 13:30:59 +00:00
tok = IDENT
// Create a buffer
var buf bytes.Buffer
// Read passed in runes
for _, ch := range chp {
buf.WriteRune(ch)
}
// Read subsequent characters
2016-02-26 17:27:07 +00:00
for {
2016-09-06 13:30:59 +00:00
if ch := s.next(); ch == end && sub == 0 {
buf.WriteRune(ch)
2016-02-26 17:27:07 +00:00
break
2016-05-23 12:32:02 +00:00
} else if ch == beg {
sub++
buf.WriteRune(ch)
} else if ch == end {
sub--
buf.WriteRune(ch)
2016-02-26 17:27:07 +00:00
} else if ch == eof {
return ILLEGAL, buf.String()
} else if ch == '\\' {
2016-09-06 13:30:59 +00:00
switch chn := s.next(); chn {
2016-05-23 12:32:02 +00:00
default:
return ILLEGAL, buf.String()
case 'b', 't', 'r', 'n', 'f', '"', '\\':
buf.WriteRune(ch)
buf.WriteRune(chn)
2016-02-26 17:27:07 +00:00
}
} else {
buf.WriteRune(ch)
}
}
2016-05-23 12:32:02 +00:00
if beg == '{' {
2016-09-06 13:30:59 +00:00
return JSON, buf.String()
}
if beg == '[' {
return ARRAY, buf.String()
2016-05-23 12:32:02 +00:00
}
2016-09-06 13:30:59 +00:00
return ILLEGAL, buf.String()
2016-02-26 17:27:07 +00:00
}
2016-09-06 13:30:59 +00:00
// next reads the next rune from the bufferred reader.
2016-02-26 17:27:07 +00:00
// Returns the rune(0) if an error occurs (or io.EOF is returned).
2016-09-06 13:30:59 +00:00
func (s *Scanner) next() rune {
if len(s.a) > 0 {
2016-09-06 13:30:59 +00:00
var r rune
r, s.a = s.a[len(s.a)-1], s.a[:len(s.a)-1]
s.b = append(s.b, r)
2016-09-06 13:30:59 +00:00
return r
}
r, _, err := s.r.ReadRune()
2016-02-26 17:27:07 +00:00
if err != nil {
return eof
}
s.b = append(s.b, r)
2016-09-06 13:30:59 +00:00
return r
2016-02-26 17:27:07 +00:00
}
2016-09-06 13:30:59 +00:00
// undo places the previously read rune back on the reader.
func (s *Scanner) undo() {
if len(s.b) > 0 {
2016-09-06 13:30:59 +00:00
var r rune
r, s.b = s.b[len(s.b)-1], s.b[:len(s.b)-1]
s.a = append(s.a, r)
2016-09-06 13:30:59 +00:00
return
}
2016-02-26 17:27:07 +00:00
_ = s.r.UnreadRune()
2016-09-06 13:30:59 +00:00
2016-02-26 17:27:07 +00:00
}
// isWhitespace returns true if the rune is a space, tab, or newline.
func isWhitespace(ch rune) bool {
2016-05-23 12:32:02 +00:00
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'
2016-02-26 17:27:07 +00:00
}
// isNumber returns true if the rune is a number.
func isNumber(ch rune) bool {
return (ch >= '0' && ch <= '9')
}
2016-09-06 13:30:59 +00:00
// isLetter returns true if the rune is a letter.
func isLetter(ch rune) bool {
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
2016-02-26 17:27:07 +00:00
}
2016-09-06 13:30:59 +00:00
// isIdentChar returns true if the rune is allowed in a IDENT.
2016-02-26 17:27:07 +00:00
func isIdentChar(ch rune) bool {
2016-09-06 13:30:59 +00:00
return isLetter(ch) || isNumber(ch) || ch == '.' || ch == '_' || ch == '*'
2016-02-26 17:27:07 +00:00
}
// eof represents a marker rune for the end of the reader.
var eof = rune(0)