This idea may work, or may not
This commit is contained in:
		@@ -9,6 +9,8 @@ type Token struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
    K_SPACES = "\t\n\r "
 | 
					    K_SPACES = "\t\n\r "
 | 
				
			||||||
 | 
					    // Must be defined in pairs with
 | 
				
			||||||
 | 
					    // open bracket on left and close bracket on right
 | 
				
			||||||
    K_BRACKETS = "()[]{}"
 | 
					    K_BRACKETS = "()[]{}"
 | 
				
			||||||
    K_QUOTES = "'\"`"
 | 
					    K_QUOTES = "'\"`"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -46,6 +48,6 @@ var KEYWORDS = []string {
 | 
				
			|||||||
    "WITH",
 | 
					    "WITH",
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var STATEMENTS = map[string] func() ( IStatement, error ) {
 | 
					var STATEMENTS = map[string] func( *Lexer ) ( IStatement, error ) {
 | 
				
			||||||
    "BUY": _buyStatement,
 | 
					    "BUY": _buyStatement,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										115
									
								
								mmql/lexer.go
									
									
									
									
									
								
							
							
						
						
									
										115
									
								
								mmql/lexer.go
									
									
									
									
									
								
							@@ -1,14 +1,119 @@
 | 
				
			|||||||
package mmql
 | 
					package mmql
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "fmt"
 | 
					import (
 | 
				
			||||||
 | 
					    "fmt"
 | 
				
			||||||
 | 
					    "io"
 | 
				
			||||||
 | 
					    "strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type LexerExpect struct {
 | 
				
			||||||
 | 
					    statements bool
 | 
				
			||||||
 | 
					    keywords bool
 | 
				
			||||||
 | 
					    brackets bool
 | 
				
			||||||
 | 
					    quotes bool
 | 
				
			||||||
 | 
					    key string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Lexer struct {
 | 
					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 {
 | 
					    this.reader = strings.NewReader( line )
 | 
				
			||||||
        fmt.Printf( "%d: %s\n", i, string( r ) )
 | 
					
 | 
				
			||||||
 | 
					    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
 | 
					package mmql
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "testing"
 | 
					import (
 | 
				
			||||||
 | 
					    // "fmt"
 | 
				
			||||||
 | 
					    "testing"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestParse( t *testing.T ) {
 | 
					func TestParse( t *testing.T ) {
 | 
				
			||||||
    lexer := Lexer{}
 | 
					    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
 | 
					BUY 1 SHARES OF BTC_ETF
 | 
				
			||||||
  FOR 10 USD
 | 
					  FOR 10 USD
 | 
				
			||||||
  FROM "MyBrokerAccount"
 | 
					  FROM "MyBrokerAccount"
 | 
				
			||||||
@@ -13,5 +43,7 @@ BUY 1 SHARES OF BTC_ETF
 | 
				
			|||||||
    1 BTC OF USD_BTC SOLD
 | 
					    1 BTC OF USD_BTC SOLD
 | 
				
			||||||
        FROM "CoinBase"
 | 
					        FROM "CoinBase"
 | 
				
			||||||
` )
 | 
					` )
 | 
				
			||||||
 | 
					    if err != nil {
 | 
				
			||||||
 | 
					        t.Error( err )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,31 @@
 | 
				
			|||||||
package mmql
 | 
					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 {
 | 
					type ActionStatement struct {
 | 
				
			||||||
    IStatement
 | 
					    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]
 | 
					    THEN EXPECT: [SOLD|BOUGHT] FROM [ExchangeType]
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func _buyStatement() ( IStatement, error ) {
 | 
					func _buyStatement( lexer *Lexer ) ( IStatement, error ) {
 | 
				
			||||||
    a := ActionStatement{}
 | 
					    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
 | 
					    var b IStatement = a
 | 
				
			||||||
    return b, nil
 | 
					    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
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
		Reference in New Issue
	
	Block a user