This idea may work, or may not

This commit is contained in:
斟酌 鵬兄 2022-10-21 20:49:35 +08:00
parent c9ecbdde08
commit 31f6e7165d
6 changed files with 239 additions and 22 deletions

View File

@ -9,6 +9,8 @@ type Token struct {
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 = "'\"`"
)
@ -46,6 +48,6 @@ var KEYWORDS = []string {
"WITH",
}
var STATEMENTS = map[string] func() ( IStatement, error ) {
var STATEMENTS = map[string] func( *Lexer ) ( IStatement, error ) {
"BUY": _buyStatement,
}

View File

@ -1,14 +1,119 @@
package mmql
import "fmt"
import (
"fmt"
"io"
"strings"
)
type LexerExpect struct {
statements bool
keywords bool
brackets bool
quotes bool
key string
}
type Lexer struct {
expect TokenType
reader *strings.Reader
readToken bool
}
func ( this *Lexer ) Parse( line string ) {
func ( this *Lexer ) Parse( line string ) ( IStatement, error ) {
for i, r := range line {
fmt.Printf( "%d: %s\n", i, string( r ) )
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
}

View File

@ -1,10 +1,40 @@
package mmql
import "testing"
import (
// "fmt"
"testing"
)
func TestParse( t *testing.T ) {
lexer := Lexer{}
lexer.Parse( `
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"
@ -13,5 +43,7 @@ BUY 1 SHARES OF BTC_ETF
1 BTC OF USD_BTC SOLD
FROM "CoinBase"
` )
if err != nil {
t.Error( err )
}
}

View File

@ -1,8 +1,31 @@
package mmql
type IStatement interface {
type IStatement interface { }
type RawStatement struct {
IStatement
Value string
}
type QuotedValue struct {
IStatement
RawStatement *RawStatement
Quote string
}
type BracketedValue struct {
IStatement
RawStatement *RawStatement
Brackets string
}
type ActionStatement struct {
IStatement
}
type OrderStatement struct {
*ActionStatement
}
func ( this *OrderStatement ) For( stmt IStatement ) {
}

View File

@ -13,18 +13,23 @@ THEN OPT EXPECT: FOR EVERY
THEN EXPECT: [SOLD|BOUGHT] FROM [ExchangeType]
*/
func _buyStatement() ( IStatement, error ) {
a := ActionStatement{}
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
}
/*
func _buyStatement( lexer *Lexer ) ( ActionStatement, error ) {
token := lexer.GetToken()
if token.Type == KeywordToken && token.Value == "BUY" {
} else {
}
}
*/

View File

@ -0,0 +1,50 @@
#### Example Statement
```
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"
```
#### Yield Structure
```
{
Statement: BUY
Product: {
Name: BTC_ETF
Amount: {
Value: 1
Unit: SHARES
}
}
For: {
Value: 10
Unit: USD
}
From: MyBrokerAccount
Limit: {
Amount: {
Func: {
Name: PURCHASING_POWER
Params [
MyBrokerAccount
QQQ
]
}
}
}
When: [{
Event: SOLD
Params: {
Amount: {
Value: 1
Unit: BTC
}
From: CoinBase
}
}]
}
```