mirror of
https://github.com/hanzo-fi/numscript.git
synced 2026-10-30 20:54:52 +00:00
239 lines
4.7 KiB
Go
239 lines
4.7 KiB
Go
package interpreter
|
|
|
|
import (
|
|
"math/big"
|
|
|
|
"github.com/hanzo-fi/numscript/internal/analysis"
|
|
"github.com/hanzo-fi/numscript/internal/flags"
|
|
"github.com/hanzo-fi/numscript/internal/parser"
|
|
)
|
|
|
|
func evaluateFnCall(env *evalEnv, type_ *string, fnCall parser.FnCall) (Value, InterpreterError) {
|
|
if type_ == nil {
|
|
if err := env.checkFeatureFlag(flags.ExperimentalMidScriptFunctionCall); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
args, err := evaluateExpressions(env, fnCall.Args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch fnCall.Caller.Name {
|
|
case analysis.FnVarOriginMeta:
|
|
if type_ == nil {
|
|
return nil, InvalidNestedMeta{}
|
|
}
|
|
|
|
rawValue, err := meta(env, fnCall.Range, args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return parseVar(*type_, rawValue, fnCall.Range)
|
|
|
|
case analysis.FnVarOriginBalance:
|
|
monetary, err := balance(env, fnCall.Range, args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return monetary, nil
|
|
|
|
case analysis.FnVarOriginOverdraft:
|
|
monetary, err := overdraft(env, fnCall.Range, args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return monetary, nil
|
|
|
|
case analysis.FnVarOriginGetAsset:
|
|
return getAsset(env, fnCall.Range, args)
|
|
case analysis.FnVarOriginGetAmount:
|
|
return getAmount(env, fnCall.Range, args)
|
|
case analysis.FnVarOriginScoped:
|
|
return scoped(env, fnCall.Range, args)
|
|
|
|
default:
|
|
return nil, UnboundFunctionErr{Name: fnCall.Caller.Name}
|
|
}
|
|
}
|
|
|
|
func overdraft(
|
|
env *evalEnv,
|
|
r parser.Range,
|
|
args []Value,
|
|
) (Monetary, InterpreterError) {
|
|
err := env.checkFeatureFlag(flags.ExperimentalOverdraftFunctionFeatureFlag)
|
|
if err != nil {
|
|
return Monetary{}, err
|
|
}
|
|
|
|
// TODO more precise args range location
|
|
p := NewArgsParser(args)
|
|
account := parseArg(p, r, expectAccount)
|
|
asset := parseArg(p, r, expectAsset)
|
|
err = p.parse()
|
|
if err != nil {
|
|
return Monetary{}, err
|
|
}
|
|
|
|
// overdraft call doesn't handle colors
|
|
balance_, err := env.getBalance(account, asset)
|
|
if err != nil {
|
|
return Monetary{}, err
|
|
}
|
|
|
|
balanceIsPositive := balance_.Cmp(big.NewInt(0)) == 1
|
|
if balanceIsPositive {
|
|
return Monetary{
|
|
Amount: NewMonetaryInt(0),
|
|
Asset: asset,
|
|
}, nil
|
|
}
|
|
|
|
overdraft := new(big.Int).Neg(balance_)
|
|
return Monetary{
|
|
Amount: MonetaryInt(*overdraft),
|
|
Asset: asset,
|
|
}, nil
|
|
}
|
|
|
|
func meta(
|
|
env *evalEnv,
|
|
rng parser.Range,
|
|
args []Value,
|
|
) (string, InterpreterError) {
|
|
// TODO more precise location
|
|
p := NewArgsParser(args)
|
|
account := parseArg(p, rng, expectAccount)
|
|
key := parseArg(p, rng, expectString)
|
|
err := p.parse()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
value, ok, err := env.getMetadata(account, string(key))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if !ok {
|
|
return "", MetadataNotFound{
|
|
Account: account.Name,
|
|
Scope: account.Scope,
|
|
Key: string(key),
|
|
Range: rng,
|
|
}
|
|
}
|
|
|
|
return value, nil
|
|
}
|
|
|
|
func balance(
|
|
env *evalEnv,
|
|
r parser.Range,
|
|
args []Value,
|
|
) (Monetary, InterpreterError) {
|
|
// TODO more precise args range location
|
|
p := NewArgsParser(args)
|
|
account := parseArg(p, r, expectAccount)
|
|
asset := parseArg(p, r, expectAsset)
|
|
err := p.parse()
|
|
if err != nil {
|
|
return Monetary{}, err
|
|
}
|
|
|
|
// body
|
|
|
|
// balance call doesn't handle colors
|
|
balance, err := env.getBalance(account, asset)
|
|
if err != nil {
|
|
return Monetary{}, err
|
|
}
|
|
|
|
if balance.Cmp(big.NewInt(0)) == -1 {
|
|
return Monetary{}, NegativeBalanceError{
|
|
Account: account.Name,
|
|
Scope: account.Scope,
|
|
Amount: *balance,
|
|
}
|
|
}
|
|
|
|
balanceCopy := new(big.Int).Set(balance)
|
|
|
|
m := Monetary{
|
|
Asset: Asset(asset),
|
|
Amount: MonetaryInt(*balanceCopy),
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
func getAsset(
|
|
env *evalEnv,
|
|
r parser.Range,
|
|
args []Value,
|
|
) (Value, InterpreterError) {
|
|
err := env.checkFeatureFlag(flags.ExperimentalGetAssetFunctionFeatureFlag)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p := NewArgsParser(args)
|
|
mon := parseArg(p, r, expectMonetary)
|
|
err = p.parse()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return mon.Asset, nil
|
|
}
|
|
|
|
func getAmount(
|
|
env *evalEnv,
|
|
r parser.Range,
|
|
args []Value,
|
|
) (Value, InterpreterError) {
|
|
err := env.checkFeatureFlag(flags.ExperimentalGetAmountFunctionFeatureFlag)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p := NewArgsParser(args)
|
|
mon := parseArg(p, r, expectMonetary)
|
|
err = p.parse()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return mon.Amount, nil
|
|
}
|
|
|
|
func scoped(
|
|
env *evalEnv,
|
|
r parser.Range,
|
|
args []Value,
|
|
) (Value, InterpreterError) {
|
|
err := env.checkFeatureFlag(flags.ExperimentalScopedFunction)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p := NewArgsParser(args)
|
|
acc := parseArg(p, r, expectAccount)
|
|
scope := parseArg(p, r, expectString)
|
|
err = p.parse()
|
|
|
|
scopeStr := string(scope)
|
|
|
|
// Precondition: scope is valid idenfitier
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !checkScopeName(scopeStr) {
|
|
return nil, InvalidScope{Scope: scopeStr}
|
|
}
|
|
|
|
return AccountAddress{Name: acc.Name, Scope: scopeStr}, nil
|
|
}
|