mirror of
https://github.com/hanzoai/numscript.git
synced 2026-10-30 18:26:57 +00:00
Modules declare go 1.27.1 and the build images follow. go fix applied the modernizers, and the interpreter's parseArg, evaluateExprAs and evaluateOptExprAs are now generic methods on the parser and program state they take. omitzero is not applied, since it changes what the JSON says. Co-Authored-By: Claude Opus 5 <[email protected]>
252 lines
5.4 KiB
Go
252 lines
5.4 KiB
Go
package interpreter
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
"strings"
|
|
|
|
"github.com/formancehq/numscript/internal/analysis"
|
|
"github.com/formancehq/numscript/internal/parser"
|
|
)
|
|
|
|
type Value interface {
|
|
value()
|
|
String() string
|
|
}
|
|
|
|
type String string
|
|
type Asset string
|
|
type Portion big.Rat
|
|
type AccountAddress string
|
|
type MonetaryInt big.Int
|
|
type Monetary struct {
|
|
Amount MonetaryInt
|
|
Asset Asset
|
|
}
|
|
|
|
func (String) value() {}
|
|
func (AccountAddress) value() {}
|
|
func (MonetaryInt) value() {}
|
|
func (Monetary) value() {}
|
|
func (Portion) value() {}
|
|
func (Asset) value() {}
|
|
|
|
func NewAccountAddress(src string) (AccountAddress, InterpreterError) {
|
|
if !checkAccountName(src) {
|
|
return AccountAddress(""), InvalidAccountName{Name: src}
|
|
}
|
|
return AccountAddress(src), nil
|
|
}
|
|
|
|
func NewAsset(src string) (Asset, InterpreterError) {
|
|
if !checkAssetName(src) {
|
|
return Asset(""), InvalidAsset{Name: src}
|
|
}
|
|
return Asset(src), nil
|
|
}
|
|
|
|
func (v MonetaryInt) MarshalJSON() ([]byte, error) {
|
|
bigInt := big.Int(v)
|
|
s := fmt.Sprintf(`"%s"`, bigInt.String())
|
|
return []byte(s), nil
|
|
}
|
|
|
|
func (v Portion) MarshalJSON() ([]byte, error) {
|
|
r := big.Rat(v)
|
|
s := fmt.Sprintf(`"%s"`, r.String())
|
|
return []byte(s), nil
|
|
}
|
|
|
|
func (v Monetary) MarshalJSON() ([]byte, error) {
|
|
m := fmt.Sprintf("\"%s %s\"", v.Asset, v.Amount.String())
|
|
return []byte(m), nil
|
|
}
|
|
|
|
func (v String) String() string {
|
|
return string(v)
|
|
}
|
|
|
|
func (v AccountAddress) String() string {
|
|
return string(v)
|
|
}
|
|
|
|
func (v MonetaryInt) String() string {
|
|
i := big.Int(v)
|
|
return i.String()
|
|
}
|
|
|
|
func (v Monetary) String() string {
|
|
return fmt.Sprintf("%s %s", v.Asset, v.Amount)
|
|
}
|
|
|
|
func (p Portion) String() string {
|
|
r := big.Rat(p)
|
|
return r.String()
|
|
}
|
|
|
|
func (v Asset) String() string {
|
|
return string(v)
|
|
}
|
|
|
|
func expectMonetary(v Value, r parser.Range) (*Monetary, InterpreterError) {
|
|
switch v := v.(type) {
|
|
case Monetary:
|
|
return &v, nil
|
|
|
|
default:
|
|
return nil, TypeError{Expected: analysis.TypeMonetary, Value: v, Range: r}
|
|
}
|
|
}
|
|
|
|
func expectMonetaryOfAsset(expectedAsset string) func(Value, parser.Range) (*big.Int, InterpreterError) {
|
|
return func(v Value, r parser.Range) (*big.Int, InterpreterError) {
|
|
m, err := expectMonetary(v, r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
asset := string(m.Asset)
|
|
|
|
if asset != expectedAsset {
|
|
return nil, MismatchedCurrencyError{Expected: expectedAsset, Got: asset}
|
|
}
|
|
|
|
i := big.Int(m.Amount)
|
|
return &i, nil
|
|
}
|
|
}
|
|
|
|
func expectNumber(v Value, r parser.Range) (*big.Int, InterpreterError) {
|
|
switch v := v.(type) {
|
|
case MonetaryInt:
|
|
return (*big.Int)(&v), nil
|
|
|
|
default:
|
|
return nil, TypeError{Expected: analysis.TypeNumber, Value: v, Range: r}
|
|
}
|
|
}
|
|
|
|
func expectString(v Value, r parser.Range) (*string, InterpreterError) {
|
|
switch v := v.(type) {
|
|
case String:
|
|
return (*string)(&v), nil
|
|
|
|
default:
|
|
return nil, TypeError{Expected: analysis.TypeString, Value: v, Range: r}
|
|
}
|
|
}
|
|
|
|
func expectAsset(v Value, r parser.Range) (*string, InterpreterError) {
|
|
switch v := v.(type) {
|
|
case Asset:
|
|
return (*string)(&v), nil
|
|
|
|
default:
|
|
return nil, TypeError{Expected: analysis.TypeAsset, Value: v, Range: r}
|
|
}
|
|
}
|
|
|
|
func expectAccount(v Value, r parser.Range) (*string, InterpreterError) {
|
|
switch v := v.(type) {
|
|
case AccountAddress:
|
|
return (*string)(&v), nil
|
|
|
|
default:
|
|
return nil, TypeError{Expected: analysis.TypeAccount, Value: v, Range: r}
|
|
}
|
|
}
|
|
|
|
func expectPortion(v Value, r parser.Range) (*big.Rat, InterpreterError) {
|
|
switch v := v.(type) {
|
|
case Portion:
|
|
return (*big.Rat)(&v), nil
|
|
|
|
default:
|
|
return nil, TypeError{Expected: analysis.TypePortion, Value: v, Range: r}
|
|
}
|
|
}
|
|
|
|
func expectAnything(v Value, _ parser.Range) (*Value, InterpreterError) {
|
|
return &v, nil
|
|
}
|
|
|
|
func expectOneOf[T any](combinators ...func(v Value, r parser.Range) (*T, InterpreterError)) func(v Value, r parser.Range) (*T, InterpreterError) {
|
|
return func(v Value, r parser.Range) (*T, InterpreterError) {
|
|
if len(combinators) == 0 {
|
|
// this should be unreachable
|
|
panic("Invalid argument: no combinators given")
|
|
}
|
|
|
|
var errs []TypeError
|
|
for _, combinator := range combinators {
|
|
out, err := combinator(v, r)
|
|
if err == nil {
|
|
return out, nil
|
|
}
|
|
|
|
typeErr, ok := err.(TypeError)
|
|
if !ok {
|
|
return nil, err
|
|
}
|
|
errs = append(errs, typeErr)
|
|
}
|
|
|
|
// e.g. typeErr.map(e => e.Expected).join("|")
|
|
var expected strings.Builder
|
|
for index, typeErr := range errs {
|
|
if index != 0 {
|
|
expected.WriteString("|")
|
|
}
|
|
expected.WriteString(typeErr.Expected)
|
|
}
|
|
|
|
return nil, TypeError{
|
|
Range: r,
|
|
Value: v,
|
|
Expected: expected.String(),
|
|
}
|
|
}
|
|
}
|
|
|
|
func expectMapped[T any, U any](
|
|
combinator func(v Value, r parser.Range) (*T, InterpreterError),
|
|
mapper func(value T) U,
|
|
) func(v Value, r parser.Range) (*U, InterpreterError) {
|
|
return func(v Value, r parser.Range) (*U, InterpreterError) {
|
|
out, err := combinator(v, r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
mapped := mapper(*out)
|
|
return &mapped, nil
|
|
}
|
|
}
|
|
|
|
func NewMonetary(asset string, n int64) Monetary {
|
|
return Monetary{
|
|
Asset: Asset(asset),
|
|
Amount: NewMonetaryInt(n),
|
|
}
|
|
}
|
|
|
|
func NewMonetaryInt(n int64) MonetaryInt {
|
|
bi := big.NewInt(n)
|
|
return MonetaryInt(*bi)
|
|
}
|
|
|
|
func (m MonetaryInt) Add(other MonetaryInt) MonetaryInt {
|
|
bi := big.Int(m)
|
|
otherBi := big.Int(other)
|
|
|
|
sum := new(big.Int).Add(&bi, &otherBi)
|
|
return MonetaryInt(*sum)
|
|
}
|
|
|
|
func (m MonetaryInt) Sub(other MonetaryInt) MonetaryInt {
|
|
bi := big.Int(m)
|
|
otherBi := big.Int(other)
|
|
|
|
sum := new(big.Int).Sub(&bi, &otherBi)
|
|
return MonetaryInt(*sum)
|
|
}
|