This idea may work, or may not
This commit is contained in:
parent
c9ecbdde08
commit
31f6e7165d
@ -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,
|
||||
}
|
||||
|
115
mmql/lexer.go
115
mmql/lexer.go
@ -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
|
||||
}
|
||||
|
@ -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 )
|
||||
}
|
||||
}
|
||||
|
@ -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 ) {
|
||||
}
|
||||
|
@ -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 {
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
50
specifications/0002_statement_structurs.md
Normal file
50
specifications/0002_statement_structurs.md
Normal 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
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
Loading…
Reference in New Issue
Block a user