From 6c473d17de677379346ac9e5029879b3422c24cb Mon Sep 17 00:00:00 2001 From: ascandone Date: Mon, 23 Sep 2024 15:54:45 +0200 Subject: [PATCH] wrapped all files within /internal folder 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 --- internal/interpreter/interpreter.go | 118 ++++++++---------- .../parser/__snapshots__/parser_test.snap | 90 ++++++++++--- internal/parser/ast.go | 6 +- internal/parser/parser.go | 28 +++-- 4 files changed, 145 insertions(+), 97 deletions(-) diff --git a/internal/interpreter/interpreter.go b/internal/interpreter/interpreter.go index 28ca934..39d60ae 100644 --- a/internal/interpreter/interpreter.go +++ b/internal/interpreter/interpreter.go @@ -63,12 +63,12 @@ type ExecutionResult struct { AccountsMetadata AccountsMetadata `json:"accountsMeta"` } -func parsePercentage(p string) big.Rat { +func parsePercentage(p string) *big.Rat { num, den, err := parser.ParsePercentageRatio(p) if err != nil { panic(err) } - return *big.NewRat(int64(num), int64(den)) + return new(big.Rat).SetFrac(num, den) } func parseMonetary(source string) (Monetary, InterpreterError) { @@ -100,7 +100,7 @@ func parseVar(type_ string, rawValue string, r parser.Range) (Value, Interpreter case analysis.TypeAccount: return AccountAddress(rawValue), nil case analysis.TypePortion: - return Portion(parsePercentage(rawValue)), nil + return Portion(*parsePercentage(rawValue)), nil case analysis.TypeAsset: return Asset(rawValue), nil case analysis.TypeNumber: @@ -327,7 +327,7 @@ func (st *programState) runSendStatement(statement parser.SendStatement) ([]Post } st.CurrentAsset = string(monetary.Asset) - monetaryAmt := big.Int(monetary.Amount) + monetaryAmt := (*big.Int)(&monetary.Amount) if monetaryAmt.Cmp(big.NewInt(0)) == -1 { return nil, NegativeAmountErr{Amount: monetary.Amount} } @@ -420,7 +420,7 @@ func (s *programState) sendAll(source parser.Source) (*big.Int, InterpreterError } // We switch to the default sending evaluation for this subsource - return s.trySendingUpTo(source.From, *monetary) + return s.trySendingUpTo(source.From, monetary) case *parser.SourceAllotment: return nil, InvalidAllotmentInSendAll{} @@ -432,15 +432,15 @@ func (s *programState) sendAll(source parser.Source) (*big.Int, InterpreterError } // Fails if it doesn't manage to send exactly "amount" -func (s *programState) trySendingExact(source parser.Source, amount big.Int) InterpreterError { +func (s *programState) trySendingExact(source parser.Source, amount *big.Int) InterpreterError { sentAmt, err := s.trySendingUpTo(source, amount) if err != nil { return err } - if sentAmt.Cmp(&amount) != 0 { + if sentAmt.Cmp(amount) != 0 { return MissingFundsErr{ Asset: s.CurrentAsset, - Needed: amount, + Needed: *amount, Available: *sentAmt, Range: source.GetRange(), } @@ -448,7 +448,7 @@ func (s *programState) trySendingExact(source parser.Source, amount big.Int) Int return nil } -func (s *programState) trySendingToAccount(accountLiteral parser.Literal, amount big.Int, overdraft *big.Int) (*big.Int, InterpreterError) { +func (s *programState) trySendingToAccount(accountLiteral parser.Literal, amount *big.Int, overdraft *big.Int) (*big.Int, InterpreterError) { account, err := evaluateLitExpecting(s, accountLiteral, expectAccount) if err != nil { return nil, err @@ -457,30 +457,28 @@ func (s *programState) trySendingToAccount(accountLiteral parser.Literal, amount overdraft = nil } - var actuallySentAmt big.Int + var actuallySentAmt *big.Int if overdraft == nil { // unbounded overdraft: we send the required amount - actuallySentAmt.Set(&amount) + actuallySentAmt = new(big.Int).Set(amount) } else { balance := s.getCachedBalance(*account, s.CurrentAsset) // that's the amount we are allowed to send (balance + overdraft) - var safeSendAmt big.Int - safeSendAmt.Add(balance, overdraft) - - actuallySentAmt = *utils.MinBigInt(&safeSendAmt, &amount) + safeSendAmt := new(big.Int).Add(balance, overdraft) + actuallySentAmt = utils.MinBigInt(safeSendAmt, amount) } s.Senders = append(s.Senders, Sender{ Name: *account, - Monetary: &actuallySentAmt, + Monetary: actuallySentAmt, }) - return &actuallySentAmt, nil + return actuallySentAmt, nil } // Tries sending "amount" and returns the actually sent amt. // Doesn't fail (unless nested sources fail) -func (s *programState) trySendingUpTo(source parser.Source, amount big.Int) (*big.Int, InterpreterError) { +func (s *programState) trySendingUpTo(source parser.Source, amount *big.Int) (*big.Int, InterpreterError) { switch source := source.(type) { case *parser.SourceAccount: return s.trySendingToAccount(source.Literal, amount, big.NewInt(0)) @@ -497,44 +495,40 @@ func (s *programState) trySendingUpTo(source parser.Source, amount big.Int) (*bi return s.trySendingToAccount(source.Address, amount, cap) case *parser.SourceInorder: - var totalLeft big.Int - totalLeft.Set(&amount) + totalLeft := new(big.Int).Set(amount) for _, source := range source.Sources { sentAmt, err := s.trySendingUpTo(source, totalLeft) if err != nil { return nil, err } - totalLeft.Sub(&totalLeft, sentAmt) + totalLeft.Sub(totalLeft, sentAmt) } - - var sentAmt big.Int - sentAmt.Sub(&amount, &totalLeft) - return &sentAmt, nil + return new(big.Int).Sub(amount, totalLeft), nil case *parser.SourceAllotment: var items []parser.AllotmentValue for _, i := range source.Items { items = append(items, i.Allotment) } - allot, err := s.makeAllotment(amount.Int64(), items) + allot, err := s.makeAllotment(amount, items) if err != nil { return nil, err } for i, allotmentItem := range source.Items { - err := s.trySendingExact(allotmentItem.From, *big.NewInt(allot[i])) + err := s.trySendingExact(allotmentItem.From, allot[i]) if err != nil { return nil, err } } - return &amount, nil + return amount, nil case *parser.SourceCapped: cap, err := evaluateLitExpecting(s, source.Cap, expectMonetaryOfAsset(s.CurrentAsset)) if err != nil { return nil, err } - cappedAmount := utils.MinBigInt(&amount, cap) - return s.trySendingUpTo(source.From, *cappedAmount) + cappedAmount := utils.MinBigInt(amount, cap) + return s.trySendingUpTo(source.From, cappedAmount) default: utils.NonExhaustiveMatchPanic[any](source) @@ -563,14 +557,14 @@ func (s *programState) receiveFrom(destination parser.Destination, amount *big.I items = append(items, i.Allotment) } - allot, err := s.makeAllotment(amount.Int64(), items) + allot, err := s.makeAllotment(amount, items) if err != nil { return err } receivedTotal := big.NewInt(0) for i, allotmentItem := range destination.Items { - amtToReceive := big.NewInt(allot[i]) + amtToReceive := allot[i] err := s.receiveFromKeptOrDest(allotmentItem.To, amtToReceive) if err != nil { return err @@ -581,15 +575,14 @@ func (s *programState) receiveFrom(destination parser.Destination, amount *big.I return nil case *parser.DestinationInorder: - var remainingAmount big.Int - remainingAmount.Set(amount) + remainingAmount := new(big.Int).Set(amount) - handler := func(keptOrDest parser.KeptOrDestination, amountToReceive big.Int) InterpreterError { - err := s.receiveFromKeptOrDest(keptOrDest, &amountToReceive) + handler := func(keptOrDest parser.KeptOrDestination, amountToReceive *big.Int) InterpreterError { + err := s.receiveFromKeptOrDest(keptOrDest, amountToReceive) if err != nil { return err } - remainingAmount.Sub(&remainingAmount, &amountToReceive) + remainingAmount.Sub(remainingAmount, amountToReceive) return err } @@ -605,16 +598,16 @@ func (s *programState) receiveFrom(destination parser.Destination, amount *big.I break } - err = handler(destinationClause.To, *utils.MinBigInt(cap, &remainingAmount)) + err = handler(destinationClause.To, utils.MinBigInt(cap, remainingAmount)) if err != nil { return err } } - var cp big.Int // if remainingAmount bad things with pointers happen.. somehow - cp.Set(&remainingAmount) - return handler(destination.Remaining, cp) + remainingAmountCopy := new(big.Int).Set(remainingAmount) + // passing "remainingAmount" directly breaks the code + return handler(destination.Remaining, remainingAmountCopy) default: utils.NonExhaustiveMatchPanic[any](destination) @@ -641,19 +634,18 @@ func (s *programState) receiveFromKeptOrDest(keptOrDest parser.KeptOrDestination } -func (s *programState) makeAllotment(monetary int64, items []parser.AllotmentValue) ([]int64, InterpreterError) { - // TODO runtime error when totalAllotment != 1? +func (s *programState) makeAllotment(monetary *big.Int, items []parser.AllotmentValue) ([]*big.Int, InterpreterError) { totalAllotment := big.NewRat(0, 1) - var allotments []big.Rat + var allotments []*big.Rat remainingAllotmentIndex := -1 for i, item := range items { switch allotment := item.(type) { case *parser.RatioLiteral: - rat := big.NewRat(int64(allotment.Numerator), int64(allotment.Denominator)) + rat := allotment.ToRatio() totalAllotment.Add(totalAllotment, rat) - allotments = append(allotments, *rat) + allotments = append(allotments, rat) case *parser.VariableLiteral: rat, err := evaluateLitExpecting(s, allotment, expectPortion) if err != nil { @@ -661,45 +653,44 @@ func (s *programState) makeAllotment(monetary int64, items []parser.AllotmentVal } totalAllotment.Add(totalAllotment, rat) - allotments = append(allotments, *rat) + allotments = append(allotments, rat) case *parser.RemainingAllotment: remainingAllotmentIndex = i - var rat big.Rat - allotments = append(allotments, rat) + allotments = append(allotments, new(big.Rat)) // TODO check there are not duplicate remaining clause } } if remainingAllotmentIndex != -1 { - var rat big.Rat - rat.Sub(big.NewRat(1, 1), totalAllotment) - allotments[remainingAllotmentIndex] = rat + allotments[remainingAllotmentIndex] = new(big.Rat).Sub(big.NewRat(1, 1), totalAllotment) } else if totalAllotment.Cmp(big.NewRat(1, 1)) != 0 { return nil, InvalidAllotmentSum{ActualSum: *totalAllotment} } - parts := make([]int64, len(allotments)) + parts := make([]*big.Int, len(allotments)) - var totalAllocated int64 + totalAllocated := big.NewInt(0) for i, allot := range allotments { - var product big.Rat - product.Mul(&allot, big.NewRat(monetary, 1)) + monetaryRat := new(big.Rat).SetInt(monetary) + product := new(big.Rat).Mul(allot, monetaryRat) - floored := product.Num().Int64() / product.Denom().Int64() + floored := new(big.Int).Div(product.Num(), product.Denom()) parts[i] = floored - totalAllocated += floored + totalAllocated.Add(totalAllocated, floored) + } for i := range parts { - if totalAllocated >= monetary { + if /* totalAllocated >= monetary */ totalAllocated.Cmp(monetary) != -1 { break } - parts[i]++ - totalAllocated++ + parts[i].Add(parts[i], big.NewInt(1)) + // totalAllocated++ + totalAllocated.Add(totalAllocated, big.NewInt(1)) } return parts, nil @@ -768,12 +759,11 @@ func balance( } } - var balanceCopy big.Int - balanceCopy.Set(balance) + balanceCopy := new(big.Int).Set(balance) m := Monetary{ Asset: Asset(*asset), - Amount: MonetaryInt(balanceCopy), + Amount: MonetaryInt(*balanceCopy), } return &m, nil } diff --git a/internal/parser/__snapshots__/parser_test.snap b/internal/parser/__snapshots__/parser_test.snap index 929cf57..f67761c 100755 --- a/internal/parser/__snapshots__/parser_test.snap +++ b/internal/parser/__snapshots__/parser_test.snap @@ -153,8 +153,14 @@ parser.Program{ Start: parser.Position{Character:13, Line:1}, End: parser.Position{Character:16, Line:1}, }, - Numerator: 0x1, - Denominator: 0x3, + Numerator: &big.Int{ + neg: false, + abs: {0x1}, + }, + Denominator: &big.Int{ + neg: false, + abs: {0x3}, + }, }, From: &parser.SourceAccount{ Literal: &parser.AccountLiteral{ @@ -229,8 +235,14 @@ parser.Program{ Start: parser.Position{Character:4, Line:2}, End: parser.Position{Character:7, Line:2}, }, - Numerator: 0x2a, - Denominator: 0x64, + Numerator: &big.Int{ + neg: false, + abs: {0x2a}, + }, + Denominator: &big.Int{ + neg: false, + abs: {0x64}, + }, }, From: &parser.SourceAccount{ Literal: &parser.AccountLiteral{ @@ -252,8 +264,14 @@ parser.Program{ Start: parser.Position{Character:1, Line:3}, End: parser.Position{Character:4, Line:3}, }, - Numerator: 0x1, - Denominator: 0x2, + Numerator: &big.Int{ + neg: false, + abs: {0x1}, + }, + Denominator: &big.Int{ + neg: false, + abs: {0x2}, + }, }, From: &parser.SourceAccount{ Literal: &parser.AccountLiteral{ @@ -349,8 +367,14 @@ parser.Program{ Start: parser.Position{Character:13, Line:1}, End: parser.Position{Character:18, Line:1}, }, - Numerator: 0xf2, - Denominator: 0x2710, + Numerator: &big.Int{ + neg: false, + abs: {0xf2}, + }, + Denominator: &big.Int{ + neg: false, + abs: {0x2710}, + }, }, From: &parser.SourceAccount{ Literal: &parser.AccountLiteral{ @@ -434,8 +458,14 @@ parser.Program{ Start: parser.Position{Character:18, Line:2}, End: parser.Position{Character:21, Line:2}, }, - Numerator: 0x1, - Denominator: 0x2, + Numerator: &big.Int{ + neg: false, + abs: {0x1}, + }, + Denominator: &big.Int{ + neg: false, + abs: {0x2}, + }, }, To: &parser.DestinationTo{ Destination: &parser.DestinationAccount{ @@ -938,8 +968,14 @@ parser.Program{ Start: parser.Position{Character:3, Line:3}, End: parser.Position{Character:6, Line:3}, }, - Numerator: 0x1, - Denominator: 0x2, + Numerator: &big.Int{ + neg: false, + abs: {0x1}, + }, + Denominator: &big.Int{ + neg: false, + abs: {0x2}, + }, }, To: &parser.DestinationTo{ Destination: &parser.DestinationAccount{ @@ -1316,8 +1352,14 @@ parser.Program{ Start: parser.Position{Character:1, Line:4}, End: parser.Position{Character:4, Line:4}, }, - Numerator: 0x1, - Denominator: 0x2, + Numerator: &big.Int{ + neg: false, + abs: {0x1}, + }, + Denominator: &big.Int{ + neg: false, + abs: {0x2}, + }, }, &parser.VariableLiteral{ Range: parser.Range{ @@ -1675,8 +1717,14 @@ parser.Program{ Start: parser.Position{Character:18, Line:2}, End: parser.Position{Character:21, Line:2}, }, - Numerator: 0x1, - Denominator: 0x2, + Numerator: &big.Int{ + neg: false, + abs: {0x1}, + }, + Denominator: &big.Int{ + neg: false, + abs: {0x2}, + }, }, To: &parser.DestinationKept{ Range: parser.Range{ @@ -1735,8 +1783,14 @@ parser.Program{ Start: parser.Position{Character:4, Line:4}, End: parser.Position{Character:9, Line:4}, }, - Numerator: 0x1, - Denominator: 0x6, + Numerator: &big.Int{ + neg: false, + abs: {0x1}, + }, + Denominator: &big.Int{ + neg: false, + abs: {0x6}, + }, }, To: &parser.DestinationTo{ Destination: &parser.DestinationAccount{ diff --git a/internal/parser/ast.go b/internal/parser/ast.go index 7ff825d..99132be 100644 --- a/internal/parser/ast.go +++ b/internal/parser/ast.go @@ -55,8 +55,8 @@ type ( RatioLiteral struct { Range Range - Numerator uint64 - Denominator uint64 + Numerator *big.Int + Denominator *big.Int } VariableLiteral struct { @@ -66,7 +66,7 @@ type ( ) func (r RatioLiteral) ToRatio() *big.Rat { - return big.NewRat(int64(r.Numerator), int64(r.Denominator)) + return new(big.Rat).SetFrac(r.Numerator, r.Denominator) } type RemainingAllotment struct { diff --git a/internal/parser/parser.go b/internal/parser/parser.go index a7342e7..fdc449c 100644 --- a/internal/parser/parser.go +++ b/internal/parser/parser.go @@ -2,6 +2,7 @@ package parser import ( "math" + "math/big" "strconv" "strings" @@ -230,18 +231,20 @@ func parseSource(sourceCtx parser.ISourceContext) Source { } } +func unsafeParseBigInt(s string) *big.Int { + s = strings.TrimSpace(s) + i, ok := new(big.Int).SetString(s, 10) + if !ok { + panic("invalid int: " + s) + } + return i +} + func parseRatio(source string, range_ Range) *RatioLiteral { split := strings.Split(source, "/") - num, err := strconv.ParseUint(strings.TrimSpace(split[0]), 0, 64) - if err != nil { - panic(err) - } - - den, err := strconv.ParseUint(strings.TrimSpace(split[1]), 0, 64) - if err != nil { - panic(err) - } + num := unsafeParseBigInt(split[0]) + den := unsafeParseBigInt(split[1]) return &RatioLiteral{ Range: range_, @@ -250,11 +253,12 @@ func parseRatio(source string, range_ Range) *RatioLiteral { } } -func ParsePercentageRatio(source string) (uint64, uint64, error) { +// TODO actually handle big int +func ParsePercentageRatio(source string) (*big.Int, *big.Int, error) { str := strings.TrimSuffix(source, "%") num, err := strconv.ParseUint(strings.Replace(str, ".", "", -1), 0, 64) if err != nil { - return 0, 0, err + return nil, nil, err } var denominator uint64 @@ -267,7 +271,7 @@ func ParsePercentageRatio(source string) (uint64, uint64, error) { denominator = 100 } - return num, denominator, nil + return big.NewInt(int64(num)), big.NewInt(int64(denominator)), nil } func parsePercentageRatio(source string, range_ Range) *RatioLiteral {