Some basic structures
This commit is contained in:
parent
31f6e7165d
commit
bbde3e80f7
@ -1,11 +1,4 @@
|
|||||||
package mmql
|
package engine
|
||||||
|
|
||||||
type TokenType int
|
|
||||||
|
|
||||||
type Token struct {
|
|
||||||
Type TokenType
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
K_SPACES = "\t\n\r "
|
K_SPACES = "\t\n\r "
|
||||||
@ -15,16 +8,7 @@ const (
|
|||||||
K_QUOTES = "'\"`"
|
K_QUOTES = "'\"`"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const K_STATEMENT_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"
|
||||||
UnknownToken TokenType = iota
|
|
||||||
KeywordToken
|
|
||||||
BracketToken
|
|
||||||
SquareBracketToken
|
|
||||||
CurlyBracketToken
|
|
||||||
SingleQuote
|
|
||||||
DoubleQuote
|
|
||||||
AccuteAccentQuote
|
|
||||||
)
|
|
||||||
|
|
||||||
var KEYWORDS = []string {
|
var KEYWORDS = []string {
|
||||||
"ANYWHERE",
|
"ANYWHERE",
|
||||||
@ -47,7 +31,3 @@ var KEYWORDS = []string {
|
|||||||
"UPDATE",
|
"UPDATE",
|
||||||
"WITH",
|
"WITH",
|
||||||
}
|
}
|
||||||
|
|
||||||
var STATEMENTS = map[string] func( *Lexer ) ( IStatement, error ) {
|
|
||||||
"BUY": _buyStatement,
|
|
||||||
}
|
|
311
mmql/engine/lexer.go
Normal file
311
mmql/engine/lexer.go
Normal 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
119
mmql/engine/lexer_test.go
Normal 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 )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
119
mmql/lexer.go
119
mmql/lexer.go
@ -1,119 +0,0 @@
|
|||||||
package mmql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type LexerExpect struct {
|
|
||||||
statements bool
|
|
||||||
keywords bool
|
|
||||||
brackets bool
|
|
||||||
quotes bool
|
|
||||||
key string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Lexer struct {
|
|
||||||
reader *strings.Reader
|
|
||||||
readToken bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func ( this *Lexer ) Parse( line string ) ( IStatement, error ) {
|
|
||||||
|
|
||||||
this.reader = strings.NewReader( line )
|
|
||||||
|
|
||||||
ex := LexerExpect{
|
|
||||||
statements: true,
|
|
||||||
keywords: false,
|
|
||||||
brackets: true,
|
|
||||||
quotes: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.ReadStatement( &ex )
|
|
||||||
}
|
|
||||||
|
|
||||||
func ( this *Lexer ) ReadStatement( expecting *LexerExpect ) ( IStatement, error ) {
|
|
||||||
reader := this.reader
|
|
||||||
|
|
||||||
var stmt 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 = QuotedValue{
|
|
||||||
RawStatement: &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 = BracketedValue{
|
|
||||||
RawStatement: &RawStatement{ Value: s },
|
|
||||||
Brackets: b.String(),
|
|
||||||
}
|
|
||||||
return stmt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if expecting.statements {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf( "Nothing to read" )
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
package mmql
|
|
||||||
|
|
||||||
import (
|
|
||||||
// "fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParse( t *testing.T ) {
|
|
||||||
lexer := Lexer{}
|
|
||||||
s, err := lexer.Parse( "\"ABC\"" )
|
|
||||||
if err != nil {
|
|
||||||
t.Error( err )
|
|
||||||
}
|
|
||||||
|
|
||||||
got := (*any( s ).( 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 ).( 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 )
|
|
||||||
}
|
|
||||||
|
|
||||||
s, err = lexer.Parse( `
|
|
||||||
BUY 1 SHARES OF BTC_ETF
|
|
||||||
FOR 10 USD
|
|
||||||
FROM "MyBrokerAccount"
|
|
||||||
WITH LIMIT OF PURCHASING_POWER( "MyBrokerAccount", "QQQ" )
|
|
||||||
FOR EVERY
|
|
||||||
1 BTC OF USD_BTC SOLD
|
|
||||||
FROM "CoinBase"
|
|
||||||
` )
|
|
||||||
if err != nil {
|
|
||||||
t.Error( err )
|
|
||||||
}
|
|
||||||
}
|
|
32
mmql/statements/actions/amount.go
Normal file
32
mmql/statements/actions/amount.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package actions
|
||||||
|
|
||||||
|
import (
|
||||||
|
// "fmt"
|
||||||
|
engine "github.com/tgckpg/mmqlengine/mmql/engine"
|
||||||
|
stmtd "github.com/tgckpg/mmqlengine/mmql/statements"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
EXPECT: [Number] [Units]
|
||||||
|
OR
|
||||||
|
EXPECT: [FUNCTION]( ...params )
|
||||||
|
*/
|
||||||
|
|
||||||
|
func AmountStatement( lexer *engine.Lexer ) ( istmt stmtd.IStatement, err error ) {
|
||||||
|
stmt := stmtd.AmountStatement{}
|
||||||
|
|
||||||
|
val, err := lexer.ReadDecimal()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stmt.Value = val
|
||||||
|
|
||||||
|
unit, err := lexer.ReadAlpha()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stmt.Unit = unit
|
||||||
|
|
||||||
|
istmt = stmt
|
||||||
|
return
|
||||||
|
}
|
41
mmql/statements/actions/buy.go
Normal file
41
mmql/statements/actions/buy.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package actions
|
||||||
|
|
||||||
|
import (
|
||||||
|
engine "github.com/tgckpg/mmqlengine/mmql/engine"
|
||||||
|
stmtd "github.com/tgckpg/mmqlengine/mmql/statements"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
EXPECT: BUY
|
||||||
|
THEN EXPECT: [AmountStatement] OF [ProductStatement]
|
||||||
|
THEN EXPECT: FOR [AmountStatement]
|
||||||
|
THEN EXPECT: FROM [ExchangeType]
|
||||||
|
THEN OPT EXPECT: [ExchangeType]
|
||||||
|
THEN OPT EXPECT: [AND|OR]
|
||||||
|
THEN EXPECT: [ActionExpression]
|
||||||
|
THEN OPT EXPECT: FOR EVERY
|
||||||
|
THEN EXPECT: [AmountType] OF [ProductType]
|
||||||
|
THEN EXPECT: [SOLD|BOUGHT] FROM [ExchangeType]
|
||||||
|
*/
|
||||||
|
|
||||||
|
func BuyStatement( lexer *engine.Lexer ) ( stmtd.IStatement, error ) {
|
||||||
|
orderStmt := stmtd.OrderStatement{}
|
||||||
|
orderStmt.Action = "BUY"
|
||||||
|
|
||||||
|
readAmount := engine.LexerExpect{
|
||||||
|
Statements: true,
|
||||||
|
Keywords: false,
|
||||||
|
Brackets: false,
|
||||||
|
Quotes: false,
|
||||||
|
Key: "AMOUNT",
|
||||||
|
}
|
||||||
|
|
||||||
|
amountStmt, err := lexer.ReadStatement( &readAmount )
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
orderStmt.For( amountStmt )
|
||||||
|
|
||||||
|
var istmt stmtd.IStatement = orderStmt
|
||||||
|
return istmt, err
|
||||||
|
}
|
30
mmql/statements/actions/buy_test.go
Normal file
30
mmql/statements/actions/buy_test.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package actions
|
||||||
|
|
||||||
|
import (
|
||||||
|
// "fmt"
|
||||||
|
"testing"
|
||||||
|
engine "github.com/tgckpg/mmqlengine/mmql/engine"
|
||||||
|
stmtd "github.com/tgckpg/mmqlengine/mmql/statements"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBuyStatement( t *testing.T ) {
|
||||||
|
lexer := engine.Lexer{}
|
||||||
|
supportedStmts := map[string] func( *engine.Lexer ) ( stmtd.IStatement, error ) {
|
||||||
|
"BUY": BuyStatement,
|
||||||
|
"AMOUNT": AmountStatement,
|
||||||
|
}
|
||||||
|
lexer.SupportedStmts = &supportedStmts
|
||||||
|
|
||||||
|
_, err := lexer.Parse( `
|
||||||
|
BUY 1 SHARES OF BTC_ETF
|
||||||
|
FOR 10 USD
|
||||||
|
FROM "MyBrokerAccount"
|
||||||
|
WITH LIMIT OF PURCHASING_POWER( "MyBrokerAccount", "QQQ" )
|
||||||
|
FOR EVERY
|
||||||
|
1 BTC OF USD_BTC SOLD
|
||||||
|
FROM "CoinBase"
|
||||||
|
` )
|
||||||
|
if err != nil {
|
||||||
|
t.Error( err )
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package mmql
|
package statements
|
||||||
|
|
||||||
type IStatement interface { }
|
type IStatement interface { }
|
||||||
|
|
||||||
@ -19,12 +19,18 @@ type BracketedValue struct {
|
|||||||
Brackets string
|
Brackets string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ActionStatement struct {
|
type AmountStatement struct {
|
||||||
IStatement
|
IStatement
|
||||||
|
Value float64
|
||||||
|
Unit string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IActionStatement interface { }
|
||||||
|
|
||||||
type OrderStatement struct {
|
type OrderStatement struct {
|
||||||
*ActionStatement
|
IStatement
|
||||||
|
IActionStatement
|
||||||
|
Action string
|
||||||
}
|
}
|
||||||
|
|
||||||
func ( this *OrderStatement ) For( stmt IStatement ) {
|
func ( this *OrderStatement ) For( stmt IStatement ) {
|
@ -1,35 +0,0 @@
|
|||||||
package mmql
|
|
||||||
|
|
||||||
/*
|
|
||||||
EXPECT: BUY
|
|
||||||
THEN EXPECT: [AmountType] OF [ProductType]
|
|
||||||
THEN EXPECT: FOR [AmountType]
|
|
||||||
THEN EXPECT: FROM [ExchangeType]
|
|
||||||
THEN OPT EXPECT: [ExchangeType]
|
|
||||||
THEN OPT EXPECT: [AND|OR]
|
|
||||||
THEN EXPECT: [ActionExpression]
|
|
||||||
THEN OPT EXPECT: FOR EVERY
|
|
||||||
THEN EXPECT: [AmountType] OF [ProductType]
|
|
||||||
THEN EXPECT: [SOLD|BOUGHT] FROM [ExchangeType]
|
|
||||||
*/
|
|
||||||
|
|
||||||
func _buyStatement( lexer *Lexer ) ( IStatement, error ) {
|
|
||||||
a := OrderStatement{}
|
|
||||||
|
|
||||||
ex := LexerExpect{
|
|
||||||
statements: true,
|
|
||||||
keywords: false,
|
|
||||||
brackets: false,
|
|
||||||
quotes: false,
|
|
||||||
key: "AMOUNT",
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt, err := lexer.ReadStatement( &ex )
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
a.For( stmt )
|
|
||||||
|
|
||||||
var b IStatement = a
|
|
||||||
return b, nil
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user