380 lines
8.9 KiB
Go
380 lines
8.9 KiB
Go
package engine
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
"strconv"
|
|
|
|
stmtd "github.com/tgckpg/mmqlengine/mmql/statements"
|
|
)
|
|
|
|
type LexerExpect struct {
|
|
Statements bool
|
|
Keywords bool
|
|
Brackets bool
|
|
Quotes bool
|
|
Key string
|
|
}
|
|
|
|
type Lexer struct {
|
|
SupportedStmts *map[string] func( *Lexer ) ( stmtd.IStatement, error )
|
|
reader *strings.Reader
|
|
readToken bool
|
|
}
|
|
|
|
func ( this *Lexer ) Parse( line string ) ( stmtd.IStatement, error ) {
|
|
|
|
this.reader = strings.NewReader( line )
|
|
|
|
ex := LexerExpect{
|
|
Statements: true,
|
|
Keywords: false,
|
|
Brackets: true,
|
|
Quotes: true,
|
|
}
|
|
|
|
return this.ReadStatement( &ex )
|
|
}
|
|
|
|
func ( this *Lexer ) SetReader( reader *strings.Reader ) {
|
|
this.reader = reader
|
|
}
|
|
|
|
func ( this *Lexer ) GetPos() int {
|
|
return this.reader.Len()
|
|
}
|
|
|
|
/**
|
|
* Reads
|
|
* 123,456,789.00001
|
|
* 1234.567
|
|
* 1234
|
|
* 10.5k
|
|
*/
|
|
func ( this *Lexer ) ReadDecimal() ( f float64, err error ) {
|
|
reader := this.reader
|
|
defer this.Rollback( reader.Len(), &err )
|
|
|
|
var b strings.Builder
|
|
|
|
dot := false
|
|
comma := false
|
|
k := false
|
|
end := false
|
|
for {
|
|
r, _, _err := reader.ReadRune()
|
|
err = _err
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
if comma {
|
|
err = fmt.Errorf( "Unexpected ',' char" )
|
|
return
|
|
}
|
|
if b.Len() == 0 {
|
|
err = fmt.Errorf( "Nothing left to read" )
|
|
return
|
|
}
|
|
}
|
|
break
|
|
}
|
|
|
|
if end && !strings.ContainsRune( K_SPACES, r ) {
|
|
if k {
|
|
b.WriteRune( 'k' )
|
|
}
|
|
err = fmt.Errorf(
|
|
"Cannot parse '%s' in ReadDecimal",
|
|
this.ReadUntilNot( K_STATEMENT_CHARS, true, r ),
|
|
)
|
|
return
|
|
}
|
|
|
|
if r == ',' {
|
|
if comma || dot {
|
|
err = fmt.Errorf( "Unexpected ',' char in ReadDecimal" )
|
|
return
|
|
}
|
|
comma = true
|
|
continue
|
|
}
|
|
|
|
if strings.ContainsRune( K_SPACES, r ) {
|
|
if comma {
|
|
err = fmt.Errorf( "Unexpected ',' char in ReadDecimal" )
|
|
return
|
|
}
|
|
if 0 < b.Len() {
|
|
break
|
|
}
|
|
continue
|
|
}
|
|
|
|
if r == '.' {
|
|
if !dot {
|
|
b.WriteRune( r )
|
|
dot = true
|
|
continue
|
|
}
|
|
err = fmt.Errorf( "Unexpected '.' char in ReadDecimal" )
|
|
return
|
|
}
|
|
|
|
if r == 'k' {
|
|
k = true
|
|
end = true
|
|
continue
|
|
}
|
|
|
|
if '0' <= r && r <= '9' {
|
|
b.WriteRune( r )
|
|
comma = false
|
|
} else {
|
|
err = fmt.Errorf(
|
|
"Cannot parse '%s' in ReadDecimal",
|
|
this.ReadUntilNot( K_STATEMENT_CHARS, true, r ),
|
|
)
|
|
return
|
|
}
|
|
}
|
|
|
|
f, err = strconv.ParseFloat( b.String(), 64 )
|
|
if k {
|
|
f *= 1000
|
|
}
|
|
return
|
|
}
|
|
|
|
func ( this *Lexer ) Rollback( oPos int, err *error ) {
|
|
if *err != nil {
|
|
this.reader.Seek( int64( this.reader.Len() - oPos ), io.SeekCurrent )
|
|
}
|
|
}
|
|
|
|
func ( this *Lexer ) ReadKeyword( Keyword string ) ( err error ) {
|
|
|
|
reader := this.reader
|
|
kReader := strings.NewReader( Keyword )
|
|
|
|
defer this.Rollback( reader.Len(), &err )
|
|
|
|
var b strings.Builder
|
|
for {
|
|
r, _, _err := reader.ReadRune()
|
|
err = _err
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
if 0 < b.Len() {
|
|
err = nil
|
|
} else {
|
|
err = fmt.Errorf( "Nothing left to read" )
|
|
return
|
|
}
|
|
}
|
|
break
|
|
}
|
|
|
|
if strings.ContainsRune( K_SPACES, r ) && b.Len() == 0 {
|
|
continue
|
|
}
|
|
|
|
k, _, _err := kReader.ReadRune()
|
|
if _err != nil {
|
|
if _err != io.EOF {
|
|
err = _err
|
|
} else {
|
|
err = nil
|
|
}
|
|
return
|
|
}
|
|
|
|
if k != r {
|
|
err = fmt.Errorf(
|
|
"Expected \"%s\", got \"%s\"",
|
|
Keyword,
|
|
this.ReadUntilNot( K_STATEMENT_CHARS, true, r ),
|
|
)
|
|
return
|
|
}
|
|
b.WriteRune( r )
|
|
}
|
|
return
|
|
}
|
|
|
|
func ( this *Lexer ) ReadAlpha() ( s string, err error ) {
|
|
reader := this.reader
|
|
|
|
defer this.Rollback( reader.Len(), &err )
|
|
|
|
var b strings.Builder
|
|
|
|
for {
|
|
r, _, _err := reader.ReadRune()
|
|
err = _err
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
if 0 < b.Len() {
|
|
err = nil
|
|
} else {
|
|
err = fmt.Errorf( "Nothing left to read" )
|
|
return
|
|
}
|
|
}
|
|
break
|
|
}
|
|
|
|
if strings.ContainsRune( K_SPACES, r ) {
|
|
if 0 < b.Len() {
|
|
break
|
|
}
|
|
continue
|
|
}
|
|
|
|
if ( 'A' <= r && r <= 'Z' ) || ( 'a' <= r && r <= 'z' ) {
|
|
b.WriteRune( r )
|
|
}
|
|
}
|
|
s = b.String()
|
|
return
|
|
}
|
|
|
|
func ( this *Lexer ) ReadStatement( expecting *LexerExpect ) ( stmtd.IStatement, error ) {
|
|
reader := this.reader
|
|
|
|
var stmt stmtd.IStatement
|
|
|
|
for {
|
|
r, _, err := reader.ReadRune()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if expecting.Quotes && strings.ContainsRune( K_QUOTES, r ) {
|
|
s, err := this.ReadUntilClose( r, true, '\\' )
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
stmt = stmtd.QuotedValue{
|
|
RawStatement: &stmtd.RawStatement{ Value: s },
|
|
Quote: string( r ),
|
|
}
|
|
return stmt, nil
|
|
}
|
|
|
|
bracketIndex := strings.IndexRune( K_BRACKETS, r )
|
|
if expecting.Brackets && bracketIndex % 2 == 0 {
|
|
cl := rune( K_BRACKETS[ bracketIndex + 1 ] )
|
|
s, err := this.ReadUntilClose( cl, false, 'A' )
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var b strings.Builder
|
|
b.WriteRune( r )
|
|
b.WriteRune( cl )
|
|
|
|
stmt = stmtd.BracketedValue{
|
|
RawStatement: &stmtd.RawStatement{ Value: s },
|
|
Brackets: b.String(),
|
|
}
|
|
return stmt, nil
|
|
}
|
|
|
|
s := expecting.Key
|
|
if s == "" && strings.ContainsRune( K_STATEMENT_CHARS, r ) {
|
|
s = this.ReadUntilNot( K_STATEMENT_CHARS, true, r )
|
|
}
|
|
|
|
if expecting.Statements && s != "" {
|
|
|
|
if this.SupportedStmts == nil {
|
|
return nil, fmt.Errorf( "Statement not supported: %s", s )
|
|
}
|
|
|
|
if f, ok := (*this.SupportedStmts)[ strings.ToUpper( s ) ]; ok {
|
|
|
|
// If we are using key, we need to move 1 rune back
|
|
if expecting.Key != "" {
|
|
err = reader.UnreadRune()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return f( this )
|
|
} else {
|
|
var supported strings.Builder
|
|
comma := false
|
|
for k := range *this.SupportedStmts {
|
|
if comma {
|
|
supported.WriteString( ", " )
|
|
} else {
|
|
comma = true
|
|
}
|
|
supported.WriteString( k )
|
|
}
|
|
return nil, fmt.Errorf( "Statement not supported: %s, supported statements are: %s", s, supported.String() )
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil, fmt.Errorf( "Nothing to read" )
|
|
}
|
|
|
|
func ( this *Lexer ) ReadUntilNot( charRange string, writeHead bool, head rune ) string {
|
|
var b strings.Builder
|
|
|
|
if writeHead {
|
|
b.WriteRune( head )
|
|
}
|
|
|
|
reader := this.reader
|
|
for {
|
|
r, _, err := reader.ReadRune()
|
|
|
|
if err == nil && strings.ContainsRune( charRange, r ) {
|
|
b.WriteRune( r )
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
return b.String()
|
|
}
|
|
|
|
func ( this *Lexer ) ReadUntilClose( closing rune, canEsc bool, escRune rune ) ( string, error ) {
|
|
var b strings.Builder
|
|
|
|
reader := this.reader
|
|
for {
|
|
r, _, err := reader.ReadRune()
|
|
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
return "", fmt.Errorf( "Unexpected end of data after opening %s", string( closing ) )
|
|
}
|
|
return "", err
|
|
}
|
|
|
|
if r == closing {
|
|
break
|
|
} else if canEsc && r == escRune {
|
|
r2, _, err := reader.ReadRune()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if r2 != closing {
|
|
b.WriteRune( r )
|
|
b.WriteRune( r2 )
|
|
continue
|
|
}
|
|
}
|
|
|
|
b.WriteRune( r )
|
|
}
|
|
|
|
return b.String(), nil
|
|
}
|