Some basic structures

This commit is contained in:
2022-10-22 00:03:06 +08:00
parent 31f6e7165d
commit bbde3e80f7
10 changed files with 544 additions and 228 deletions

View File

@@ -0,0 +1,33 @@
package engine
const (
K_SPACES = "\t\n\r "
// Must be defined in pairs with
// open bracket on left and close bracket on right
K_BRACKETS = "()[]{}"
K_QUOTES = "'\"`"
)
const K_STATEMENT_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"
var KEYWORDS = []string {
"ANYWHERE",
"BOUGHT",
"BUY",
"CREATE",
"EVERY",
"EXCHANGE",
"FOR",
"FROM",
"GET",
"LIMIT",
"LIST",
"OF",
"SELL",
"SOLD",
"TRIGGER",
"TRIGGERS",
"TYPE",
"UPDATE",
"WITH",
}

311
mmql/engine/lexer.go Normal file
View File

@@ -0,0 +1,311 @@
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
}
/**
* Reads
* 123,456,789.00001
* 1234.567
* 1234
* 10.5k
*/
func ( this *Lexer ) ReadDecimal() ( f float64, err error ) {
reader := this.reader
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' )
}
b.WriteRune( r )
err = fmt.Errorf( "Cannot parse '%s'", b.String() )
return
}
if r == ',' {
if comma || dot {
err = fmt.Errorf( "Unexpected ',' char" )
return
}
comma = true
continue
}
if strings.ContainsRune( K_SPACES, r ) {
if comma {
err = fmt.Errorf( "Unexpected ',' char" )
return
}
if 0 < b.Len() {
break
}
continue
}
if r == '.' {
if !dot {
b.WriteRune( r )
dot = true
continue
}
err = fmt.Errorf( "Unexpected '.' char" )
return
}
if r == 'k' {
k = true
end = true
continue
}
if '0' <= r && r <= '9' {
b.WriteRune( r )
comma = false
} else {
b.WriteRune( r )
err = fmt.Errorf( "Cannot parse '%s'", b.String() )
return
}
}
f, err = strconv.ParseFloat( b.String(), 64 )
if k {
f *= 1000
}
return
}
func ( this *Lexer ) ReadAlpha() ( s string, err error ) {
reader := this.reader
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
}

119
mmql/engine/lexer_test.go Normal file
View File

@@ -0,0 +1,119 @@
package engine
import (
// "fmt"
"strings"
"testing"
stmtd "github.com/tgckpg/mmqlengine/mmql/statements"
)
func TestParse( t *testing.T ) {
lexer := Lexer{}
s, err := lexer.Parse( "\"ABC\"" )
if err != nil {
t.Error( err )
}
got := (*any( s ).( stmtd.QuotedValue ).RawStatement).Value
if got != "ABC" {
t.Errorf( "Expected ABC, got %s", got )
}
s, err = lexer.Parse( "\"ABC" )
if err == nil {
t.Errorf( "Expected error, got value %s", s )
}
s, err = lexer.Parse( "( ABC )" )
got = (*any( s ).( stmtd.BracketedValue ).RawStatement).Value
if got != " ABC " {
t.Errorf( "Expected ABC, got %s", got )
}
s, err = lexer.Parse( "( ABC" )
if err == nil {
t.Errorf( "Expected error, got value %s", s )
}
}
func TestDecimal( t *testing.T ) {
lexer := Lexer{}
read_oks := map[string] float64 {
"1,000k": 1000000,
"10.8": 10.8,
"10,000.60": 10000.6,
"10,000.60 this_string_should_not_be_read": 10000.6,
"10,000 this_string_should_not_be_read": 10000,
"10.": 10,
".2": 0.2,
".2k": 200,
"1 g": 1,
" 1 g": 1,
}
for k := range read_oks {
lexer.SetReader( strings.NewReader( k ) )
f, err := lexer.ReadDecimal()
if err != nil {
t.Errorf( "Expected number for %s, got %s", k, err )
continue
}
expects, _ := read_oks[ k ]
if f != expects {
t.Errorf( "Expected %f, got %f", expects, f )
}
}
read_fails := []string {
"1,000ki",
"10,000.60s",
"10,,0",
"100s",
"10, 000",
"10,",
}
for _, v := range read_fails {
lexer.SetReader( strings.NewReader( v ) )
f, err := lexer.ReadDecimal()
if err == nil {
t.Errorf( "Expected error for %s, got %f", v, f )
}
}
}
func TestAlpha( t *testing.T ) {
lexer := Lexer{}
read_oks := map[string] string {
"abcd efgh": "abcd",
" abcd": "abcd",
}
for k := range read_oks {
lexer.SetReader( strings.NewReader( k ) )
f, err := lexer.ReadAlpha()
if err != nil {
t.Errorf( "Expected string for \"%s\", got %s", k, err )
continue
}
expects, _ := read_oks[ k ]
if f != expects {
t.Errorf( "Expected %s, got %s", expects, f )
}
}
read_fails := []string {
"1", " ",
}
for _, v := range read_fails {
lexer.SetReader( strings.NewReader( v ) )
f, err := lexer.ReadAlpha()
if err == nil {
t.Errorf( "Expected error for %s, got %s", v, f )
}
}
}