-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
moved main file and updated goreleaser yaml
WIP more test for get balances changed public API removed duplicate struct added ctx param to interpreter run re-export function export more types removed some todos added more tests for balance moved main file run go mod tidy updated gorelease config handle big ints
- Loading branch information
Showing
16 changed files
with
925 additions
and
293 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
package interpreter | ||
|
||
import ( | ||
"slices" | ||
|
||
"github.com/formancehq/numscript/internal/parser" | ||
"github.com/formancehq/numscript/internal/utils" | ||
"golang.org/x/exp/maps" | ||
) | ||
|
||
// traverse the script to batch in advance required balance queries | ||
|
||
func (st *programState) findBalancesQueriesInStatement(statement parser.Statement) InterpreterError { | ||
switch statement := statement.(type) { | ||
case *parser.FnCall: | ||
return nil | ||
|
||
case *parser.SendStatement: | ||
// set the current asset | ||
switch sentValue := statement.SentValue.(type) { | ||
case *parser.SentValueAll: | ||
asset, err := evaluateLitExpecting(st, sentValue.Asset, expectAsset) | ||
if err != nil { | ||
return err | ||
} | ||
st.CurrentAsset = *asset | ||
|
||
case *parser.SentValueLiteral: | ||
monetary, err := evaluateLitExpecting(st, sentValue.Monetary, expectMonetary) | ||
if err != nil { | ||
return err | ||
} | ||
st.CurrentAsset = string(monetary.Asset) | ||
|
||
default: | ||
utils.NonExhaustiveMatchPanic[any](sentValue) | ||
} | ||
|
||
// traverse source | ||
return st.findBalancesQueries(statement.Source) | ||
|
||
default: | ||
utils.NonExhaustiveMatchPanic[any](statement) | ||
return nil | ||
} | ||
} | ||
|
||
func (st *programState) batchQuery(account string, asset string) { | ||
if account == "world" { | ||
return | ||
} | ||
|
||
previousValues := st.CurrentBalanceQuery[account] | ||
if !slices.Contains[[]string, string](previousValues, account) { | ||
st.CurrentBalanceQuery[account] = append(previousValues, asset) | ||
} | ||
} | ||
|
||
func (st *programState) runBalancesQuery() error { | ||
filteredQuery := BalanceQuery{} | ||
for accountName, queriedCurrencies := range st.CurrentBalanceQuery { | ||
|
||
cachedCurrenciesForAccount := defaultMapGet(st.CachedBalances, accountName, func() AccountBalance { | ||
return AccountBalance{} | ||
}) | ||
|
||
for _, queriedCurrency := range queriedCurrencies { | ||
isAlreadyCached := slices.Contains(maps.Keys(cachedCurrenciesForAccount), queriedCurrency) | ||
if !isAlreadyCached { | ||
filteredQuery[accountName] = queriedCurrencies | ||
} | ||
} | ||
|
||
} | ||
|
||
// avoid updating balances if we don't need to fetch new data | ||
if len(filteredQuery) == 0 { | ||
return nil | ||
} | ||
|
||
balances, err := st.Store.GetBalances(st.ctx, filteredQuery) | ||
if err != nil { | ||
return err | ||
} | ||
// reset batch query | ||
st.CurrentBalanceQuery = BalanceQuery{} | ||
|
||
st.CachedBalances = balances | ||
return nil | ||
} | ||
|
||
func (st *programState) findBalancesQueries(source parser.Source) InterpreterError { | ||
switch source := source.(type) { | ||
case *parser.SourceAccount: | ||
account, err := evaluateLitExpecting(st, source.Literal, expectAccount) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
st.batchQuery(*account, st.CurrentAsset) | ||
return nil | ||
|
||
case *parser.SourceOverdraft: | ||
// Skip balance tracking when balance is overdraft | ||
if source.Bounded == nil { | ||
return nil | ||
} | ||
|
||
account, err := evaluateLitExpecting(st, source.Address, expectAccount) | ||
if err != nil { | ||
return err | ||
} | ||
st.batchQuery(*account, st.CurrentAsset) | ||
return nil | ||
|
||
case *parser.SourceInorder: | ||
for _, subSource := range source.Sources { | ||
err := st.findBalancesQueries(subSource) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
|
||
case *parser.SourceCapped: | ||
// TODO can this be optimized in some cases? | ||
return st.findBalancesQueries(source.From) | ||
|
||
case *parser.SourceAllotment: | ||
for _, item := range source.Items { | ||
err := st.findBalancesQueries(item.From) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
|
||
default: | ||
utils.NonExhaustiveMatchPanic[error](source) | ||
return nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package interpreter | ||
|
||
import ( | ||
"math/big" | ||
|
||
"github.com/formancehq/numscript/internal/parser" | ||
"github.com/formancehq/numscript/internal/utils" | ||
) | ||
|
||
func (st *programState) evaluateLit(literal parser.Literal) (Value, InterpreterError) { | ||
switch literal := literal.(type) { | ||
case *parser.AssetLiteral: | ||
return Asset(literal.Asset), nil | ||
case *parser.AccountLiteral: | ||
return AccountAddress(literal.Name), nil | ||
case *parser.StringLiteral: | ||
return String(literal.String), nil | ||
case *parser.RatioLiteral: | ||
return Portion(*literal.ToRatio()), nil | ||
case *parser.NumberLiteral: | ||
return MonetaryInt(*big.NewInt(int64(literal.Number))), nil | ||
case *parser.MonetaryLiteral: | ||
asset, err := evaluateLitExpecting(st, literal.Asset, expectAsset) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
amount, err := evaluateLitExpecting(st, literal.Amount, expectNumber) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return Monetary{Asset: Asset(*asset), Amount: MonetaryInt(*amount)}, nil | ||
|
||
case *parser.VariableLiteral: | ||
value, ok := st.ParsedVars[literal.Name] | ||
if !ok { | ||
return nil, UnboundVariableErr{ | ||
Name: literal.Name, | ||
Range: literal.Range, | ||
} | ||
} | ||
return value, nil | ||
default: | ||
utils.NonExhaustiveMatchPanic[any](literal) | ||
return nil, nil | ||
} | ||
} | ||
|
||
func evaluateLitExpecting[T any](st *programState, literal parser.Literal, expect func(Value, parser.Range) (*T, InterpreterError)) (*T, InterpreterError) { | ||
value, err := st.evaluateLit(literal) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
res, err := expect(value, literal.GetRange()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return res, nil | ||
} | ||
|
||
func (st *programState) evaluateLiterals(literals []parser.Literal) ([]Value, InterpreterError) { | ||
var values []Value | ||
for _, argLit := range literals { | ||
value, err := st.evaluateLit(argLit) | ||
if err != nil { | ||
return nil, err | ||
} | ||
values = append(values, value) | ||
} | ||
return values, nil | ||
} |
Oops, something went wrong.