Skip to content

Commit 25d9279

Browse files
authored
Add functions (#54)
1 parent 86cd0b4 commit 25d9279

File tree

7 files changed

+278
-160
lines changed

7 files changed

+278
-160
lines changed

internal/analysis/check.go

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"slices"
66
"strings"
77

8+
"github.com/formancehq/numscript/internal/flags"
89
"github.com/formancehq/numscript/internal/parser"
910
"github.com/formancehq/numscript/internal/utils"
1011
)
@@ -34,9 +35,10 @@ type FnCallResolution interface {
3435
}
3536

3637
type VarOriginFnCallResolution struct {
37-
Params []string
38-
Docs string
39-
Return string
38+
Params []string
39+
Docs string
40+
Return string
41+
VersionConstraints []VersionClause
4042
}
4143
type StatementFnCallResolution struct {
4244
Params []string
@@ -57,6 +59,8 @@ const FnSetAccountMeta = "set_account_meta"
5759
const FnVarOriginMeta = "meta"
5860
const FnVarOriginBalance = "balance"
5961
const FnVarOriginOverdraft = "overdraft"
62+
const FnVarOriginGetAsset = "get_asset"
63+
const FnVarOriginGetAmount = "get_amount"
6064

6165
var Builtins = map[string]FnCallResolution{
6266
FnSetTxMeta: StatementFnCallResolution{
@@ -81,6 +85,34 @@ var Builtins = map[string]FnCallResolution{
8185
Params: []string{TypeAccount, TypeAsset},
8286
Return: TypeMonetary,
8387
Docs: "get absolute amount of the overdraft of an account. Returns zero if balance is not negative",
88+
VersionConstraints: []VersionClause{
89+
{
90+
Version: parser.NewVersionInterpreter(0, 0, 15),
91+
FeatureFlag: flags.ExperimentalOverdraftFunctionFeatureFlag,
92+
},
93+
},
94+
},
95+
FnVarOriginGetAsset: VarOriginFnCallResolution{
96+
Params: []string{TypeMonetary},
97+
Return: TypeAsset,
98+
Docs: "get the asset of the given monetary",
99+
VersionConstraints: []VersionClause{
100+
{
101+
Version: parser.NewVersionInterpreter(0, 0, 16),
102+
FeatureFlag: flags.ExperimentalGetAssetFunctionFeatureFlag,
103+
},
104+
},
105+
},
106+
FnVarOriginGetAmount: VarOriginFnCallResolution{
107+
Params: []string{TypeMonetary},
108+
Return: TypeNumber,
109+
Docs: "get the amount of the given monetary",
110+
VersionConstraints: []VersionClause{
111+
{
112+
Version: parser.NewVersionInterpreter(0, 0, 16),
113+
FeatureFlag: flags.ExperimentalGetAmountFunctionFeatureFlag,
114+
},
115+
},
84116
},
85117
}
86118

@@ -305,14 +337,14 @@ func (res *CheckResult) checkDuplicateVars(variableName parser.Variable, decl pa
305337
}
306338

307339
func (res *CheckResult) checkFnCall(fnCall parser.FnCall) string {
308-
res.checkOvedraftFunctionVersion(fnCall)
309-
310340
returnType := TypeAny
311341

312342
if resolution, ok := Builtins[fnCall.Caller.Name]; ok {
313343
if resolution, ok := resolution.(VarOriginFnCallResolution); ok {
314344
res.fnCallResolution[fnCall.Caller] = resolution
315345
returnType = resolution.Return
346+
347+
res.requireVersion(fnCall.Range, resolution.VersionConstraints...)
316348
}
317349
}
318350

internal/analysis/version_check.go

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,6 @@ func (res *CheckResult) checkOneofVersion(rng parser.Range) {
3131
)
3232
}
3333

34-
func (res *CheckResult) checkOvedraftFunctionVersion(fnCall parser.FnCall) {
35-
if fnCall.Caller.Name != FnVarOriginOverdraft {
36-
return
37-
}
38-
39-
res.requireVersion(fnCall.Range,
40-
VersionClause{
41-
Version: parser.NewVersionInterpreter(0, 0, 15),
42-
FeatureFlag: flags.ExperimentalOverdraftFunctionFeatureFlag,
43-
},
44-
)
45-
}
46-
4734
func (res *CheckResult) checkAccountInterpolationVersion(expr parser.AccountInterpLiteral) {
4835
isInterpolation := slices.ContainsFunc(expr.Parts, func(part parser.AccountNamePart) bool {
4936
_, isVar := part.(*parser.Variable)

internal/flags/flags.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@ type FeatureFlag = string
44

55
const (
66
ExperimentalOverdraftFunctionFeatureFlag FeatureFlag = "experimental-overdraft-function"
7+
ExperimentalGetAssetFunctionFeatureFlag FeatureFlag = "experimental-get-asset-function"
8+
ExperimentalGetAmountFunctionFeatureFlag FeatureFlag = "experimental-get-amount-function"
79
ExperimentalOneofFeatureFlag FeatureFlag = "experimental-oneof"
810
ExperimentalAccountInterpolationFlag FeatureFlag = "experimental-account-interpolation"
911
ExperimentalMidScriptFunctionCall FeatureFlag = "experimental-mid-script-function-call"
1012
)
1113

1214
var AllFlags []string = []string{
1315
ExperimentalOverdraftFunctionFeatureFlag,
16+
ExperimentalGetAssetFunctionFeatureFlag,
17+
ExperimentalGetAmountFunctionFeatureFlag,
1418
ExperimentalOneofFeatureFlag,
1519
ExperimentalAccountInterpolationFlag,
1620
ExperimentalMidScriptFunctionCall,
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package interpreter
2+
3+
import (
4+
"math/big"
5+
6+
"github.com/formancehq/numscript/internal/flags"
7+
"github.com/formancehq/numscript/internal/parser"
8+
)
9+
10+
func overdraft(
11+
s *programState,
12+
r parser.Range,
13+
args []Value,
14+
) (*Monetary, InterpreterError) {
15+
err := s.checkFeatureFlag(flags.ExperimentalOverdraftFunctionFeatureFlag)
16+
if err != nil {
17+
return nil, err
18+
}
19+
20+
// TODO more precise args range location
21+
p := NewArgsParser(args)
22+
account := parseArg(p, r, expectAccount)
23+
asset := parseArg(p, r, expectAsset)
24+
err = p.parse()
25+
if err != nil {
26+
return nil, err
27+
}
28+
29+
balance_, err := getBalance(s, *account, *asset)
30+
if err != nil {
31+
return nil, err
32+
}
33+
34+
balanceIsPositive := balance_.Cmp(big.NewInt(0)) == 1
35+
if balanceIsPositive {
36+
return &Monetary{
37+
Amount: NewMonetaryInt(0),
38+
Asset: Asset(*asset),
39+
}, nil
40+
}
41+
42+
overdraft := new(big.Int).Neg(balance_)
43+
return &Monetary{
44+
Amount: MonetaryInt(*overdraft),
45+
Asset: Asset(*asset),
46+
}, nil
47+
}
48+
49+
func meta(
50+
s *programState,
51+
rng parser.Range,
52+
args []Value,
53+
) (string, InterpreterError) {
54+
// TODO more precise location
55+
p := NewArgsParser(args)
56+
account := parseArg(p, rng, expectAccount)
57+
key := parseArg(p, rng, expectString)
58+
err := p.parse()
59+
if err != nil {
60+
return "", err
61+
}
62+
63+
meta, fetchMetaErr := s.Store.GetAccountsMetadata(s.ctx, MetadataQuery{
64+
*account: []string{*key},
65+
})
66+
if fetchMetaErr != nil {
67+
return "", QueryMetadataError{WrappedError: fetchMetaErr}
68+
}
69+
s.CachedAccountsMeta = meta
70+
71+
// body
72+
accountMeta := s.CachedAccountsMeta[*account]
73+
value, ok := accountMeta[*key]
74+
75+
if !ok {
76+
return "", MetadataNotFound{Account: *account, Key: *key, Range: rng}
77+
}
78+
79+
return value, nil
80+
}
81+
82+
func balance(
83+
s *programState,
84+
r parser.Range,
85+
args []Value,
86+
) (*Monetary, InterpreterError) {
87+
// TODO more precise args range location
88+
p := NewArgsParser(args)
89+
account := parseArg(p, r, expectAccount)
90+
asset := parseArg(p, r, expectAsset)
91+
err := p.parse()
92+
if err != nil {
93+
return nil, err
94+
}
95+
96+
// body
97+
98+
balance, err := getBalance(s, *account, *asset)
99+
if err != nil {
100+
return nil, err
101+
}
102+
103+
if balance.Cmp(big.NewInt(0)) == -1 {
104+
return nil, NegativeBalanceError{
105+
Account: *account,
106+
Amount: *balance,
107+
}
108+
}
109+
110+
balanceCopy := new(big.Int).Set(balance)
111+
112+
m := Monetary{
113+
Asset: Asset(*asset),
114+
Amount: MonetaryInt(*balanceCopy),
115+
}
116+
return &m, nil
117+
}
118+
119+
func getAsset(
120+
s *programState,
121+
r parser.Range,
122+
args []Value,
123+
) (Value, InterpreterError) {
124+
err := s.checkFeatureFlag(flags.ExperimentalGetAssetFunctionFeatureFlag)
125+
if err != nil {
126+
return nil, err
127+
}
128+
129+
p := NewArgsParser(args)
130+
mon := parseArg(p, r, expectMonetary)
131+
err = p.parse()
132+
if err != nil {
133+
return nil, err
134+
}
135+
136+
return mon.Asset, nil
137+
}
138+
139+
func getAmount(
140+
s *programState,
141+
r parser.Range,
142+
args []Value,
143+
) (Value, InterpreterError) {
144+
err := s.checkFeatureFlag(flags.ExperimentalGetAmountFunctionFeatureFlag)
145+
if err != nil {
146+
return nil, err
147+
}
148+
149+
p := NewArgsParser(args)
150+
mon := parseArg(p, r, expectMonetary)
151+
err = p.parse()
152+
if err != nil {
153+
return nil, err
154+
}
155+
156+
return mon.Amount, nil
157+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package interpreter
2+
3+
import "github.com/formancehq/numscript/internal/parser"
4+
5+
func setTxMeta(st *programState, r parser.Range, args []Value) InterpreterError {
6+
p := NewArgsParser(args)
7+
key := parseArg(p, r, expectString)
8+
meta := parseArg(p, r, expectAnything)
9+
err := p.parse()
10+
if err != nil {
11+
return err
12+
}
13+
14+
st.TxMeta[*key] = *meta
15+
return nil
16+
}
17+
18+
func setAccountMeta(st *programState, r parser.Range, args []Value) InterpreterError {
19+
p := NewArgsParser(args)
20+
account := parseArg(p, r, expectAccount)
21+
key := parseArg(p, r, expectString)
22+
meta := parseArg(p, r, expectAnything)
23+
err := p.parse()
24+
if err != nil {
25+
return err
26+
}
27+
28+
accountMeta := defaultMapGet(st.SetAccountsMeta, *account, func() AccountMetadata {
29+
return AccountMetadata{}
30+
})
31+
32+
accountMeta[*key] = (*meta).String()
33+
34+
return nil
35+
}

0 commit comments

Comments
 (0)