├── machine.go ├── script ├── generate.go ├── generate.sh ├── compiler │ ├── error_test.go │ ├── program.go │ ├── allotment.go │ ├── error.go │ ├── destination.go │ ├── source.go │ └── compiler.go ├── parser │ ├── NumScript.tokens │ ├── NumScriptLexer.tokens │ ├── NumScript.interp │ ├── numscript_listener.go │ ├── numscript_base_listener.go │ ├── NumScriptLexer.interp │ └── numscript_lexer.go └── NumScript.g4 ├── codecov.yml ├── .golangci.yml ├── docs ├── types.md └── instructions.md ├── .gitignore ├── core ├── number.go ├── address.go ├── asset.go ├── account.go ├── allotment_test.go ├── allotment.go ├── value.go ├── portion.go ├── json.go ├── portion_test.go ├── funding_test.go ├── monetary.go ├── json_test.go └── funding.go ├── .github └── workflows │ ├── main.yml │ └── pr_open.yml ├── .pre-commit-config.yaml ├── vm ├── stack.go ├── program │ ├── resource.go │ ├── instructions.go │ └── program.go ├── machine_kept_test.go ├── machine_overdraft_test.go └── machine.go ├── go.mod ├── README.md ├── Taskfile.yml ├── LICENSE ├── examples └── basic.go └── go.sum /machine.go: -------------------------------------------------------------------------------- 1 | package machine 2 | -------------------------------------------------------------------------------- /script/generate.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | //go:generate ./generate.sh 4 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "docs" 3 | - "examples" 4 | - ".github" 5 | - ".devcontainer" 6 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters: 2 | enable: 3 | - gofmt 4 | - gci 5 | - goimports 6 | 7 | run: 8 | timeout: 5m 9 | -------------------------------------------------------------------------------- /docs/types.md: -------------------------------------------------------------------------------- 1 | # Formance Machine Types 2 | 3 | ``` 4 | # Integers 5 | unsigned 128 bits integers 6 | 7 | # Monetary values 8 | {USD/2 100} 9 | ``` 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | script/.antlr 3 | docs/*.example.md 4 | coverage.out 5 | machine 6 | main.go 7 | machine.go 8 | .idea 9 | vendor 10 | .DS_Store 11 | script/antlr-* 12 | -------------------------------------------------------------------------------- /core/number.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | type Number = *MonetaryInt 4 | 5 | func NewNumber(i int64) Number { 6 | return NewMonetaryInt(i) 7 | } 8 | 9 | func ParseNumber(s string) (Number, error) { 10 | return ParseMonetaryInt(s) 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Main 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | test: 10 | uses: formancehq/gh-workflows/.github/workflows/golang-test.yml@main 11 | 12 | lint: 13 | uses: formancehq/gh-workflows/.github/workflows/golang-lint.yml@main 14 | -------------------------------------------------------------------------------- /docs/instructions.md: -------------------------------------------------------------------------------- 1 | # Formance Machine Instruction Set 2 | 3 | ``` 4 | INIT 5 | LOAD address 6 | BEGIN 7 | BALANCE asset, address 8 | IPUSH value 9 | MPUSH value 10 | RPUSH value 11 | GET register 12 | SET register 13 | IADD 14 | ISUB 15 | IMUL 16 | MADD 17 | MSUB 18 | RMUL 19 | RDD 20 | RSUB 21 | TXSTART size 22 | TXEND 23 | SEND source, destination, value 24 | FLUSH 25 | COMMIT 26 | ABORT 27 | ``` 28 | -------------------------------------------------------------------------------- /.github/workflows/pr_open.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request - Open 2 | 3 | on: 4 | pull_request: 5 | types: [assigned, opened, synchronize, reopened] 6 | 7 | jobs: 8 | pr-style: 9 | uses: formancehq/gh-workflows/.github/workflows/pr-style.yml@main 10 | 11 | test: 12 | uses: formancehq/gh-workflows/.github/workflows/golang-test.yml@main 13 | 14 | lint: 15 | uses: formancehq/gh-workflows/.github/workflows/golang-lint.yml@main 16 | -------------------------------------------------------------------------------- /core/address.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import "encoding/binary" 4 | 5 | // Address represents an address in the machine's resources, which include 6 | // constants (literals) and variables passed to the program 7 | type Address uint16 8 | 9 | func NewAddress(x uint16) Address { 10 | return Address(x) 11 | } 12 | 13 | func (a Address) ToBytes() []byte { 14 | bytes := make([]byte, 2) 15 | binary.LittleEndian.PutUint16(bytes, uint16(a)) 16 | return bytes 17 | } 18 | -------------------------------------------------------------------------------- /script/generate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | if [[ "${TRACE-0}" == "1" ]]; then 7 | set -o xtrace 8 | fi 9 | 10 | cd "$(dirname "$0")" 11 | 12 | ANTLR_VERSION='4.10.1' 13 | 14 | main() { 15 | curl --continue-at - https://www.antlr.org/download/antlr-$ANTLR_VERSION-complete.jar -O 16 | java -Xmx500M -cp "./antlr-$ANTLR_VERSION-complete.jar" org.antlr.v4.Tool -Dlanguage=Go -o parser NumScript.g4 17 | } 18 | 19 | main "$@" 20 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/compilerla/conventional-pre-commit 3 | rev: v2.1.1 4 | hooks: 5 | - id: conventional-pre-commit 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | rev: v4.3.0 8 | hooks: 9 | - id: trailing-whitespace 10 | - id: end-of-file-fixer 11 | - id: check-yaml 12 | - id: check-added-large-files 13 | - id: check-merge-conflict 14 | - repo: https://github.com/golangci/golangci-lint 15 | rev: v1.50.1 16 | hooks: 17 | - id: golangci-lint 18 | -------------------------------------------------------------------------------- /vm/stack.go: -------------------------------------------------------------------------------- 1 | package vm 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/formancehq/machine/core" 7 | ) 8 | 9 | func (m *Machine) popValue() core.Value { 10 | l := len(m.Stack) 11 | x := m.Stack[l-1] 12 | m.Stack = m.Stack[:l-1] 13 | return x 14 | } 15 | 16 | func pop[T core.Value](m *Machine) T { 17 | x := m.popValue() 18 | if v, ok := x.(T); ok { 19 | return v 20 | } 21 | panic(fmt.Errorf("unexpected type '%T' on stack", x)) 22 | } 23 | 24 | func (m *Machine) pushValue(v core.Value) { 25 | m.Stack = append(m.Stack, v) 26 | } 27 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/formancehq/machine 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220626175859-9abda183db8e 7 | github.com/logrusorgru/aurora v2.0.3+incompatible 8 | github.com/pkg/errors v0.9.1 9 | github.com/stretchr/testify v1.8.0 10 | ) 11 | 12 | require ( 13 | github.com/davecgh/go-spew v1.1.1 // indirect 14 | github.com/kr/pretty v0.3.0 // indirect 15 | github.com/pmezard/go-difflib v1.0.0 // indirect 16 | github.com/rogpeppe/go-internal v1.8.0 // indirect 17 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 18 | gopkg.in/yaml.v3 v3.0.1 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /core/asset.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | ) 7 | 8 | const assetPattern = "^[A-Z/0-9]+$" 9 | 10 | var assetRegexp = regexp.MustCompile(assetPattern) 11 | 12 | type Asset string 13 | 14 | func (Asset) GetType() Type { return TypeAsset } 15 | func (a Asset) String() string { 16 | return fmt.Sprintf("%v", string(a)) 17 | } 18 | 19 | type HasAsset interface { 20 | GetAsset() Asset 21 | } 22 | 23 | func (a Asset) GetAsset() Asset { return a } 24 | 25 | func ParseAsset(ass Asset) error { 26 | if !assetRegexp.MatchString(string(ass)) { 27 | return fmt.Errorf("asset should respect pattern '%s'", assetPattern) 28 | } 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /core/account.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | ) 7 | 8 | const accountPattern = "^[a-zA-Z_]+[a-zA-Z0-9_:]*$" 9 | 10 | var accountRegexp = regexp.MustCompile(accountPattern) 11 | 12 | type Account string 13 | 14 | func (Account) GetType() Type { return TypeAccount } 15 | func (a Account) String() string { 16 | return fmt.Sprintf("@%v", string(a)) 17 | } 18 | 19 | func ParseAccount(acc Account) error { 20 | // TODO: handle properly in ledger v1.10 21 | if acc == "" { 22 | return nil 23 | } 24 | if !accountRegexp.MatchString(string(acc)) { 25 | return fmt.Errorf("accounts should respect pattern %s", accountPattern) 26 | } 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Formance Machine [![test](https://github.com/formancehq/machine/actions/workflows/main.yml/badge.svg)](https://github.com/formancehq/machine/actions/workflows/main.yml) 2 | 3 | A virtual machine for moving money. 4 | 5 | This repo bundles: 6 | * The Formance Machine VM 7 | * A Numscript parser 8 | * A Numscript compiler 9 | 10 | # Example 11 | 12 | ``` 13 | send [USD/2 1099] ( 14 | source = { 15 | @users:001:wallet 16 | @users:001:credit 17 | } 18 | destination = { 19 | 85% to @drivers:033 20 | 15% to @platform:fees 21 | } 22 | ) 23 | ``` 24 | 25 | # Documentation 26 | 27 | You can find the complete Formance documentation at [docs.formance.com](https://docs.formance.com) 28 | -------------------------------------------------------------------------------- /script/compiler/error_test.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestEndCharacter(t *testing.T) { 8 | src := ` 9 | send [CREDIT 200] ( 10 | source = @a 11 | destination = { 12 | 500% to @b 13 | 50% to @c 14 | } 15 | ) 16 | ` 17 | 18 | _, err := Compile(src) 19 | if err == nil { 20 | t.Fatal("expected error and got none") 21 | } 22 | 23 | if _, ok := err.(*CompileErrorList); !ok { 24 | t.Fatal("error had wrong type") 25 | } 26 | 27 | compErr := err.(*CompileErrorList).Errors[0] 28 | 29 | if compErr.StartL != 5 { 30 | t.Fatalf("start line was %v", compErr.StartL) 31 | } 32 | if compErr.StartC != 3 { 33 | t.Fatalf("start character was %v", compErr.StartC) 34 | } 35 | if compErr.EndL != 5 { 36 | t.Fatalf("end line was %v", compErr.EndL) 37 | } 38 | if compErr.EndC != 7 { 39 | t.Fatalf("end character was %v", compErr.EndC) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Taskfile.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | vars: 4 | PKG: ./... 5 | COVERAGE_FILE: coverage.out 6 | FAILFAST: -failfast 7 | TIMEOUT: 1m 8 | RUN: "''" 9 | 10 | tasks: 11 | default: 12 | cmds: 13 | - task: generate 14 | - task: lint 15 | - task: tests:local 16 | 17 | tests: 18 | cmds: 19 | - go test -v {{.FAILFAST}} -coverpkg {{.PKG}} -coverprofile {{.COVERAGE_FILE}} -covermode atomic -timeout {{.TIMEOUT}} {{.PKG}} 20 | 21 | tests:local: 22 | cmds: 23 | - > 24 | go test -v {{.FAILFAST}} -coverpkg {{.PKG}} -coverprofile coverage.out -covermode atomic 25 | -run {{.RUN}} -timeout {{.TIMEOUT}} {{.PKG}} | 26 | sed ''/PASS/s//$(printf "\033[32mPASS\033[0m")/'' | 27 | sed ''/FAIL/s//$(printf "\033[31mFAIL\033[0m")/'' | 28 | sed ''/RUN/s//$(printf "\033[34mRUN\033[0m")/'' 29 | 30 | lint: 31 | cmds: 32 | - golangci-lint run -v --fix {{.PKG}} 33 | 34 | generate: 35 | cmds: 36 | - go generate {{.PKG}} 37 | 38 | bench: 39 | cmds: 40 | - go test -test.bench=. {{.PKG}} 41 | -------------------------------------------------------------------------------- /script/compiler/program.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "github.com/formancehq/machine/core" 5 | "github.com/formancehq/machine/vm/program" 6 | ) 7 | 8 | func (p *parseVisitor) AppendInstruction(instruction byte) { 9 | p.instructions = append(p.instructions, instruction) 10 | } 11 | 12 | func (p *parseVisitor) PushAddress(addr core.Address) { 13 | p.instructions = append(p.instructions, program.OP_APUSH) 14 | bytes := addr.ToBytes() 15 | p.instructions = append(p.instructions, bytes...) 16 | } 17 | 18 | func (p *parseVisitor) PushInteger(val core.Number) error { 19 | addr, err := p.AllocateResource(program.Constant{Inner: val}) 20 | if err != nil { 21 | return err 22 | } 23 | p.instructions = append(p.instructions, program.OP_APUSH) 24 | bytes := addr.ToBytes() 25 | p.instructions = append(p.instructions, bytes...) 26 | return nil 27 | } 28 | 29 | func (p *parseVisitor) Bump(n int64) error { 30 | err := p.PushInteger(core.NewNumber(n)) 31 | if err != nil { 32 | return err 33 | } 34 | p.instructions = append(p.instructions, program.OP_BUMP) 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Numaire, Inc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /script/parser/NumScript.tokens: -------------------------------------------------------------------------------- 1 | T__0=1 2 | T__1=2 3 | T__2=3 4 | T__3=4 5 | NEWLINE=5 6 | WHITESPACE=6 7 | MULTILINE_COMMENT=7 8 | LINE_COMMENT=8 9 | VARS=9 10 | META=10 11 | SET_TX_META=11 12 | SET_ACCOUNT_META=12 13 | PRINT=13 14 | FAIL=14 15 | SEND=15 16 | SOURCE=16 17 | FROM=17 18 | MAX=18 19 | DESTINATION=19 20 | TO=20 21 | ALLOCATE=21 22 | OP_ADD=22 23 | OP_SUB=23 24 | LPAREN=24 25 | RPAREN=25 26 | LBRACK=26 27 | RBRACK=27 28 | LBRACE=28 29 | RBRACE=29 30 | EQ=30 31 | TY_ACCOUNT=31 32 | TY_NUMBER=32 33 | TY_MONETARY=33 34 | TY_PORTION=34 35 | TY_STRING=35 36 | STRING=36 37 | PORTION=37 38 | REMAINING=38 39 | KEPT=39 40 | BALANCE=40 41 | NUMBER=41 42 | PERCENT=42 43 | VARIABLE_NAME=43 44 | ACCOUNT=44 45 | ASSET=45 46 | '*'=1 47 | 'allowing overdraft up to'=2 48 | 'allowing unbounded overdraft'=3 49 | ','=4 50 | 'vars'=9 51 | 'meta'=10 52 | 'set_tx_meta'=11 53 | 'set_account_meta'=12 54 | 'print'=13 55 | 'fail'=14 56 | 'send'=15 57 | 'source'=16 58 | 'from'=17 59 | 'max'=18 60 | 'destination'=19 61 | 'to'=20 62 | 'allocate'=21 63 | '+'=22 64 | '-'=23 65 | '('=24 66 | ')'=25 67 | '['=26 68 | ']'=27 69 | '{'=28 70 | '}'=29 71 | '='=30 72 | 'account'=31 73 | 'number'=32 74 | 'monetary'=33 75 | 'portion'=34 76 | 'string'=35 77 | 'remaining'=38 78 | 'kept'=39 79 | 'balance'=40 80 | '%'=42 81 | -------------------------------------------------------------------------------- /script/parser/NumScriptLexer.tokens: -------------------------------------------------------------------------------- 1 | T__0=1 2 | T__1=2 3 | T__2=3 4 | T__3=4 5 | NEWLINE=5 6 | WHITESPACE=6 7 | MULTILINE_COMMENT=7 8 | LINE_COMMENT=8 9 | VARS=9 10 | META=10 11 | SET_TX_META=11 12 | SET_ACCOUNT_META=12 13 | PRINT=13 14 | FAIL=14 15 | SEND=15 16 | SOURCE=16 17 | FROM=17 18 | MAX=18 19 | DESTINATION=19 20 | TO=20 21 | ALLOCATE=21 22 | OP_ADD=22 23 | OP_SUB=23 24 | LPAREN=24 25 | RPAREN=25 26 | LBRACK=26 27 | RBRACK=27 28 | LBRACE=28 29 | RBRACE=29 30 | EQ=30 31 | TY_ACCOUNT=31 32 | TY_NUMBER=32 33 | TY_MONETARY=33 34 | TY_PORTION=34 35 | TY_STRING=35 36 | STRING=36 37 | PORTION=37 38 | REMAINING=38 39 | KEPT=39 40 | BALANCE=40 41 | NUMBER=41 42 | PERCENT=42 43 | VARIABLE_NAME=43 44 | ACCOUNT=44 45 | ASSET=45 46 | '*'=1 47 | 'allowing overdraft up to'=2 48 | 'allowing unbounded overdraft'=3 49 | ','=4 50 | 'vars'=9 51 | 'meta'=10 52 | 'set_tx_meta'=11 53 | 'set_account_meta'=12 54 | 'print'=13 55 | 'fail'=14 56 | 'send'=15 57 | 'source'=16 58 | 'from'=17 59 | 'max'=18 60 | 'destination'=19 61 | 'to'=20 62 | 'allocate'=21 63 | '+'=22 64 | '-'=23 65 | '('=24 66 | ')'=25 67 | '['=26 68 | ']'=27 69 | '{'=28 70 | '}'=29 71 | '='=30 72 | 'account'=31 73 | 'number'=32 74 | 'monetary'=33 75 | 'portion'=34 76 | 'string'=35 77 | 'remaining'=38 78 | 'kept'=39 79 | 'balance'=40 80 | '%'=42 81 | -------------------------------------------------------------------------------- /vm/program/resource.go: -------------------------------------------------------------------------------- 1 | package program 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/formancehq/machine/core" 7 | ) 8 | 9 | type Resource interface { 10 | GetType() core.Type 11 | } 12 | 13 | type Constant struct { 14 | Inner core.Value 15 | } 16 | 17 | func (c Constant) GetType() core.Type { return c.Inner.GetType() } 18 | func (c Constant) String() string { return fmt.Sprintf("%v", c.Inner) } 19 | 20 | type Variable struct { 21 | Typ core.Type 22 | Name string 23 | } 24 | 25 | func (p Variable) GetType() core.Type { return p.Typ } 26 | func (p Variable) String() string { return fmt.Sprintf("<%v %v>", p.Typ, p.Name) } 27 | 28 | type VariableAccountMetadata struct { 29 | Typ core.Type 30 | Name string 31 | Account core.Address 32 | Key string 33 | } 34 | 35 | func (m VariableAccountMetadata) GetType() core.Type { return m.Typ } 36 | func (m VariableAccountMetadata) String() string { 37 | return fmt.Sprintf("<%v %v meta(%v, %v)>", m.Typ, m.Name, m.Account, m.Key) 38 | } 39 | 40 | type VariableAccountBalance struct { 41 | Name string 42 | Account core.Address 43 | Asset string 44 | } 45 | 46 | func (a VariableAccountBalance) GetType() core.Type { return core.TypeMonetary } 47 | func (a VariableAccountBalance) String() string { 48 | return fmt.Sprintf("<%v %v balance(%v, %v)>", core.TypeMonetary, a.Name, a.Account, a.Asset) 49 | } 50 | -------------------------------------------------------------------------------- /examples/basic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/formancehq/machine/core" 7 | "github.com/formancehq/machine/script/compiler" 8 | "github.com/formancehq/machine/vm" 9 | ) 10 | 11 | func main() { 12 | program, err := compiler.Compile(` 13 | // This is a comment 14 | vars { 15 | account $dest 16 | } 17 | send [COIN 99] ( 18 | source = { 19 | 15% from { 20 | @alice 21 | @bob 22 | } 23 | remaining from @bob 24 | } 25 | destination = $dest 26 | )`) 27 | if err != nil { 28 | panic(err) 29 | } 30 | fmt.Print(program) 31 | 32 | machine := vm.NewMachine(*program) 33 | machine.Debug = true 34 | 35 | if err = machine.SetVars(map[string]core.Value{ 36 | "dest": core.Account("charlie"), 37 | }); err != nil { 38 | panic(err) 39 | } 40 | 41 | initialBalances := map[string]map[string]*core.MonetaryInt{ 42 | "alice": {"COIN": core.NewMonetaryInt(10)}, 43 | "bob": {"COIN": core.NewMonetaryInt(100)}, 44 | } 45 | 46 | { 47 | ch, err := machine.ResolveResources() 48 | if err != nil { 49 | panic(err) 50 | } 51 | for req := range ch { 52 | if req.Error != nil { 53 | panic(req.Error) 54 | } 55 | } 56 | } 57 | 58 | { 59 | ch, err := machine.ResolveBalances() 60 | if err != nil { 61 | panic(err) 62 | } 63 | for req := range ch { 64 | val := initialBalances[req.Account][req.Asset] 65 | if req.Error != nil { 66 | panic(req.Error) 67 | } 68 | req.Response <- val 69 | } 70 | } 71 | 72 | exitCode, err := machine.Execute() 73 | if err != nil { 74 | panic(err) 75 | } 76 | 77 | fmt.Println("Exit code:", exitCode) 78 | fmt.Println(machine.Postings) 79 | fmt.Println(machine.TxMeta) 80 | } 81 | -------------------------------------------------------------------------------- /core/allotment_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestAllocate(t *testing.T) { 12 | allotment, err := NewAllotment([]Portion{ 13 | {Specific: big.NewRat(4, 5)}, 14 | {Specific: big.NewRat(2, 25)}, 15 | {Specific: big.NewRat(3, 25)}, 16 | }) 17 | require.NoError(t, err) 18 | 19 | parts := allotment.Allocate(NewMonetaryInt(15)) 20 | expectedParts := []*MonetaryInt{NewMonetaryInt(13), NewMonetaryInt(1), NewMonetaryInt(1)} 21 | if len(parts) != len(expectedParts) { 22 | t.Fatalf("unexpected output %v != %v", parts, expectedParts) 23 | } 24 | for i := range parts { 25 | if !parts[i].Equal(expectedParts[i]) { 26 | t.Fatalf("unexpected output %v != %v", parts, expectedParts) 27 | } 28 | } 29 | } 30 | 31 | func TestAllocateEmptyRemainder(t *testing.T) { 32 | allotment, err := NewAllotment([]Portion{ 33 | {Specific: big.NewRat(1, 2)}, 34 | {Specific: big.NewRat(1, 2)}, 35 | {Remaining: true}, 36 | }) 37 | require.NoError(t, err) 38 | 39 | parts := allotment.Allocate(NewMonetaryInt(15)) 40 | expectedParts := []*MonetaryInt{NewMonetaryInt(8), NewMonetaryInt(7), NewMonetaryInt(0)} 41 | if len(parts) != len(expectedParts) { 42 | t.Fatalf("unexpected output %v != %v", parts, expectedParts) 43 | } 44 | for i := range parts { 45 | if !parts[i].Equal(expectedParts[i]) { 46 | t.Fatalf("unexpected output %v != %v", parts, expectedParts) 47 | } 48 | } 49 | 50 | } 51 | 52 | func TestInvalidAllotments(t *testing.T) { 53 | _, err := NewAllotment([]Portion{ 54 | {Remaining: true}, 55 | {Specific: big.NewRat(2, 25)}, 56 | {Remaining: true}, 57 | }) 58 | assert.Errorf(t, err, "allowed two remainings") 59 | 60 | _, err = NewAllotment([]Portion{ 61 | {Specific: big.NewRat(1, 2)}, 62 | {Specific: big.NewRat(1, 2)}, 63 | {Specific: big.NewRat(1, 2)}, 64 | }) 65 | assert.Errorf(t, err, "allowed more than 100%") 66 | } 67 | -------------------------------------------------------------------------------- /core/allotment.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "math/big" 7 | ) 8 | 9 | type Allotment []big.Rat 10 | 11 | func (Allotment) GetType() Type { return TypeAllotment } 12 | 13 | func NewAllotment(portions []Portion) (*Allotment, error) { 14 | n := len(portions) 15 | total := big.NewRat(0, 1) 16 | var remainingIdx *int 17 | allotment := make([]big.Rat, n) 18 | for i := 0; i < n; i++ { 19 | if portions[i].Remaining { 20 | if remainingIdx != nil { 21 | return nil, errors.New("two uses of `remaining` in the same allotment") 22 | } 23 | allotment[i] = big.Rat{} // temporary 24 | idx := i 25 | remainingIdx = &idx 26 | } else { 27 | rat := *portions[i].Specific 28 | allotment[i] = rat 29 | total.Add(total, &rat) 30 | } 31 | } 32 | if total.Cmp(big.NewRat(1, 1)) == 1 { 33 | return nil, errors.New("sum of portions exceeded 100%") 34 | } 35 | if remainingIdx != nil { 36 | remaining := big.NewRat(1, 1) 37 | remaining.Sub(remaining, total) 38 | allotment[*remainingIdx] = *remaining 39 | } 40 | result := Allotment(allotment) 41 | return &result, nil 42 | } 43 | 44 | func (a Allotment) String() string { 45 | out := "{ " 46 | for i, ratio := range a { 47 | out += fmt.Sprintf("%v", &ratio) 48 | if i != len(a)-1 { 49 | out += " : " 50 | } 51 | } 52 | return out + " }" 53 | } 54 | 55 | func (a Allotment) Allocate(amount *MonetaryInt) []*MonetaryInt { 56 | amtBigint := big.Int(*amount) 57 | parts := make([]*MonetaryInt, len(a)) 58 | totalAllocated := NewMonetaryInt(0) 59 | // for every part in the allotment, calculate the floored value 60 | for i, allot := range a { 61 | var res big.Int 62 | res.Mul(&amtBigint, allot.Num()) 63 | res.Div(&res, allot.Denom()) 64 | mi := MonetaryInt(res) 65 | parts[i] = &mi 66 | totalAllocated = totalAllocated.Add(parts[i]) 67 | } 68 | for i := range parts { 69 | if totalAllocated.Lt(amount) { 70 | parts[i] = parts[i].Add(NewMonetaryInt(1)) 71 | totalAllocated = totalAllocated.Add(NewMonetaryInt(1)) 72 | } 73 | } 74 | return parts 75 | } 76 | -------------------------------------------------------------------------------- /core/value.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | type Type byte 9 | 10 | const ( 11 | TypeAccount = Type(iota + 1) // address of an account 12 | TypeAsset // name of an asset 13 | TypeNumber // 64bit unsigned integer 14 | TypeString // string 15 | TypeMonetary // [asset number] 16 | TypePortion // rational number between 0 and 1 both exclusive 17 | TypeAllotment // list of portions 18 | TypeAmount // either ALL or a SPECIFIC number 19 | TypeFunding // (asset, []{amount, account}) 20 | ) 21 | 22 | func (t Type) String() string { 23 | switch t { 24 | case TypeAccount: 25 | return "account" 26 | case TypeAsset: 27 | return "asset" 28 | case TypeNumber: 29 | return "number" 30 | case TypeString: 31 | return "string" 32 | case TypeMonetary: 33 | return "monetary" 34 | case TypePortion: 35 | return "portion" 36 | case TypeAllotment: 37 | return "allotment" 38 | case TypeAmount: 39 | return "amount" 40 | default: 41 | return "invalid type" 42 | } 43 | } 44 | 45 | type Value interface { 46 | GetType() Type 47 | } 48 | 49 | type String string 50 | 51 | func (String) GetType() Type { return TypeString } 52 | func (s String) String() string { 53 | return fmt.Sprintf("\"%v\"", string(s)) 54 | } 55 | 56 | func ValueEquals(lhs, rhs Value) bool { 57 | if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) { 58 | return false 59 | } 60 | if lhsn, ok := lhs.(*MonetaryInt); ok { 61 | rhsn := rhs.(*MonetaryInt) 62 | return lhsn.Equal(rhsn) 63 | } else if lhsm, ok := lhs.(Monetary); ok { 64 | rhsm := rhs.(Monetary) 65 | return lhsm.Asset == rhsm.Asset && lhsm.Amount.Equal(rhsm.Amount) 66 | } else if lhsa, ok := lhs.(Allotment); ok { 67 | rhsa := rhs.(Allotment) 68 | if len(lhsa) != len(rhsa) { 69 | return false 70 | } 71 | for i := range lhsa { 72 | if lhsa[i].Cmp(&rhsa[i]) != 0 { 73 | return false 74 | } 75 | } 76 | } else if lhsp, ok := lhs.(Portion); ok { 77 | rhsp := rhs.(Portion) 78 | return lhsp.Equals(rhsp) 79 | } else if lhsf, ok := lhs.(Funding); ok { 80 | rhsf := rhs.(Funding) 81 | return lhsf.Equals(rhsf) 82 | } else if lhs != rhs { 83 | return false 84 | } 85 | return true 86 | } 87 | -------------------------------------------------------------------------------- /core/portion.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "math/big" 7 | "regexp" 8 | ) 9 | 10 | type Portion struct { 11 | Remaining bool `json:"remaining"` 12 | Specific *big.Rat `json:"specific"` 13 | } 14 | 15 | func (Portion) GetType() Type { return TypePortion } 16 | 17 | func NewPortionRemaining() Portion { 18 | return Portion{ 19 | Remaining: true, 20 | Specific: nil, 21 | } 22 | } 23 | 24 | func NewPortionSpecific(r big.Rat) (*Portion, error) { 25 | if r.Cmp(big.NewRat(0, 1)) == -1 || r.Cmp(big.NewRat(1, 1)) == 1 { 26 | return nil, errors.New("portion must be between 0% and 100% inclusive") 27 | } 28 | return &Portion{ 29 | Remaining: false, 30 | Specific: &r, 31 | }, nil 32 | } 33 | 34 | func ValidatePortionSpecific(p Portion) error { 35 | if p.Remaining { 36 | return errors.New("remaining should not be true for a specific portion") 37 | } 38 | if p.Specific == nil { 39 | return errors.New("specific portion should not be nil") 40 | } 41 | if p.Specific.Cmp(big.NewRat(0, 1)) == -1 || p.Specific.Cmp(big.NewRat(1, 1)) == 1 { 42 | return errors.New("specific portion must be between 0% and 100% inclusive") 43 | } 44 | return nil 45 | } 46 | 47 | func (lhs Portion) Equals(rhs Portion) bool { 48 | if lhs.Remaining != rhs.Remaining { 49 | return false 50 | } 51 | if !lhs.Remaining && lhs.Specific.Cmp(rhs.Specific) != 0 { 52 | return false 53 | } 54 | return true 55 | } 56 | 57 | func ParsePortionSpecific(input string) (*Portion, error) { 58 | var res *big.Rat 59 | var ok bool 60 | 61 | re := regexp.MustCompile(`^([0-9]+)(?:[.]([0-9]+))?[%]$`) 62 | percentMatch := re.FindStringSubmatch(input) 63 | if len(percentMatch) != 0 { 64 | integral := percentMatch[1] 65 | fractional := percentMatch[2] 66 | res, ok = new(big.Rat).SetString(integral + "." + fractional) 67 | if !ok { 68 | return nil, errors.New("invalid percent format") 69 | } 70 | res.Mul(res, big.NewRat(1, 100)) 71 | } else { 72 | re = regexp.MustCompile(`^([0-9]+)\s?[/]\s?([0-9]+)$`) 73 | fractionMatch := re.FindStringSubmatch(input) 74 | if len(fractionMatch) != 0 { 75 | numerator := fractionMatch[1] 76 | denominator := fractionMatch[2] 77 | res, ok = new(big.Rat).SetString(numerator + "/" + denominator) 78 | if !ok { 79 | return nil, errors.New("invalid fractional format") 80 | } 81 | } 82 | } 83 | if res == nil { 84 | return nil, errors.New("invalid format") 85 | } 86 | return NewPortionSpecific(*res) 87 | } 88 | 89 | func (p Portion) String() string { 90 | if p.Remaining { 91 | return "remaining" 92 | } 93 | return fmt.Sprintf("%v", p.Specific) 94 | } 95 | -------------------------------------------------------------------------------- /script/compiler/allotment.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "math/big" 7 | 8 | "github.com/antlr/antlr4/runtime/Go/antlr" 9 | "github.com/formancehq/machine/core" 10 | "github.com/formancehq/machine/script/parser" 11 | "github.com/formancehq/machine/vm/program" 12 | ) 13 | 14 | func (p *parseVisitor) VisitAllotment(c antlr.ParserRuleContext, portions []parser.IAllotmentPortionContext) *CompileError { 15 | total := big.NewRat(0, 1) 16 | hasVariable := false 17 | hasRemaining := false 18 | for i := len(portions) - 1; i >= 0; i-- { 19 | c := portions[i] 20 | switch c := c.(type) { 21 | case *parser.AllotmentPortionConstContext: 22 | portion, err := core.ParsePortionSpecific(c.GetText()) 23 | if err != nil { 24 | return LogicError(c, err) 25 | } 26 | rat := *portion.Specific 27 | total.Add(&rat, total) 28 | addr, err := p.AllocateResource(program.Constant{Inner: *portion}) 29 | if err != nil { 30 | return LogicError(c, err) 31 | } 32 | p.PushAddress(*addr) 33 | case *parser.AllotmentPortionVarContext: 34 | ty, _, err := p.VisitVariable(c.GetPor(), true) 35 | if err != nil { 36 | return err 37 | } 38 | if ty != core.TypePortion { 39 | return LogicError(c, 40 | fmt.Errorf("wrong type: expected type portion for variable: %v", ty), 41 | ) 42 | } 43 | hasVariable = true 44 | case *parser.AllotmentPortionRemainingContext: 45 | if hasRemaining { 46 | return LogicError(c, 47 | errors.New("two uses of `remaining` in the same allocation"), 48 | ) 49 | } 50 | addr, err := p.AllocateResource(program.Constant{Inner: core.NewPortionRemaining()}) 51 | if err != nil { 52 | return LogicError(c, err) 53 | } 54 | p.PushAddress(*addr) 55 | hasRemaining = true 56 | } 57 | } 58 | if total.Cmp(big.NewRat(1, 1)) == 1 { 59 | return LogicError(c, 60 | errors.New("the sum of known portions is greater than 100%"), 61 | ) 62 | } 63 | if total.Cmp(big.NewRat(1, 1)) == -1 && !hasRemaining { 64 | return LogicError(c, 65 | errors.New("the sum of portions might be less than 100%"), 66 | ) 67 | } 68 | if total.Cmp(big.NewRat(1, 1)) == 0 && hasVariable { 69 | return LogicError(c, 70 | errors.New("the sum of portions might be greater than 100%"), 71 | ) 72 | } 73 | if total.Cmp(big.NewRat(1, 1)) == 0 && hasRemaining { 74 | return LogicError(c, 75 | errors.New("known portions are already equal to 100%"), 76 | ) 77 | } 78 | err := p.PushInteger(core.NewNumber(int64(len(portions)))) 79 | if err != nil { 80 | return LogicError(c, err) 81 | } 82 | p.AppendInstruction(program.OP_MAKE_ALLOTMENT) 83 | return nil 84 | } 85 | -------------------------------------------------------------------------------- /vm/machine_kept_test.go: -------------------------------------------------------------------------------- 1 | package vm 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/formancehq/machine/core" 7 | ) 8 | 9 | func TestKeptDestinationAllotment(t *testing.T) { 10 | tc := NewTestCase() 11 | tc.compile(t, `send [GEM 100] ( 12 | source = { 13 | @a 14 | @world 15 | } 16 | destination = { 17 | 50% kept 18 | 25% to @x 19 | 25% to @y 20 | } 21 | )`) 22 | tc.setBalance("a", "GEM", 1) 23 | tc.expected = CaseResult{ 24 | Printed: []core.Value{}, 25 | Postings: []Posting{ 26 | { 27 | Asset: "GEM", 28 | Amount: core.NewMonetaryInt(1), 29 | Source: "a", 30 | Destination: "x", 31 | }, 32 | { 33 | Asset: "GEM", 34 | Amount: core.NewMonetaryInt(24), 35 | Source: "world", 36 | Destination: "x", 37 | }, 38 | { 39 | Asset: "GEM", 40 | Amount: core.NewMonetaryInt(25), 41 | Source: "world", 42 | Destination: "y", 43 | }, 44 | }, 45 | ExitCode: EXIT_OK, 46 | } 47 | test(t, tc) 48 | } 49 | 50 | func TestKeptComplex(t *testing.T) { 51 | tc := NewTestCase() 52 | tc.compile(t, `send [GEM 100] ( 53 | source = { 54 | @foo 55 | @bar 56 | @baz 57 | } 58 | destination = { 59 | 50% to { 60 | max [GEM 8] to { 61 | 50% kept 62 | 25% to @arst 63 | 25% kept 64 | } 65 | remaining to @thing 66 | } 67 | 20% to @qux 68 | 5% kept 69 | remaining to @quz 70 | } 71 | )`) 72 | tc.setBalance("foo", "GEM", 20) 73 | tc.setBalance("bar", "GEM", 40) 74 | tc.setBalance("baz", "GEM", 40) 75 | tc.expected = CaseResult{ 76 | Printed: []core.Value{}, 77 | Postings: []Posting{ 78 | { 79 | Asset: "GEM", 80 | Amount: core.NewMonetaryInt(2), 81 | Source: "foo", 82 | Destination: "arst", 83 | }, 84 | { 85 | Asset: "GEM", 86 | Amount: core.NewMonetaryInt(18), 87 | Source: "foo", 88 | Destination: "thing", 89 | }, 90 | { 91 | Asset: "GEM", 92 | Amount: core.NewMonetaryInt(24), 93 | Source: "bar", 94 | Destination: "thing", 95 | }, 96 | { 97 | Asset: "GEM", 98 | Amount: core.NewMonetaryInt(16), 99 | Source: "bar", 100 | Destination: "qux", 101 | }, 102 | { 103 | Asset: "GEM", 104 | Amount: core.NewMonetaryInt(4), 105 | Source: "baz", 106 | Destination: "qux", 107 | }, 108 | { 109 | Asset: "GEM", 110 | Amount: core.NewMonetaryInt(25), 111 | Source: "baz", 112 | Destination: "quz", 113 | }, 114 | }, 115 | ExitCode: EXIT_OK, 116 | } 117 | test(t, tc) 118 | } 119 | -------------------------------------------------------------------------------- /core/json.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/pkg/errors" 8 | ) 9 | 10 | type ValueJSON struct { 11 | Type string `json:"type"` 12 | Value json.RawMessage `json:"value"` 13 | } 14 | 15 | func TypeFromName(name string) (Type, bool) { 16 | switch name { 17 | case "account": 18 | return TypeAccount, true 19 | case "asset": 20 | return TypeAsset, true 21 | case "number": 22 | return TypeNumber, true 23 | case "portion": 24 | return TypePortion, true 25 | case "monetary": 26 | return TypeMonetary, true 27 | default: 28 | return 0, false 29 | } 30 | } 31 | 32 | func NewValueFromTypedJSON(rawInput json.RawMessage) (*Value, error) { 33 | var input ValueJSON 34 | if err := json.Unmarshal(rawInput, &input); err != nil { 35 | return nil, err 36 | } 37 | 38 | typ, ok := TypeFromName(input.Type) 39 | if !ok { 40 | return nil, fmt.Errorf("unknown type: %v", input.Type) 41 | } 42 | 43 | return NewValueFromJSON(typ, input.Value) 44 | } 45 | 46 | func NewValueFromJSON(typ Type, data json.RawMessage) (*Value, error) { 47 | var value Value 48 | switch typ { 49 | case TypeAccount: 50 | var account Account 51 | if err := json.Unmarshal(data, &account); err != nil { 52 | return nil, err 53 | } 54 | if err := ParseAccount(account); err != nil { 55 | return nil, errors.Wrapf(err, "value %s", string(account)) 56 | } 57 | value = account 58 | case TypeAsset: 59 | var asset Asset 60 | if err := json.Unmarshal(data, &asset); err != nil { 61 | return nil, err 62 | } 63 | value = asset 64 | case TypeNumber: 65 | var number Number 66 | if err := json.Unmarshal(data, &number); err != nil { 67 | return nil, err 68 | } 69 | value = number 70 | case TypeMonetary: 71 | var monTmp struct { 72 | Asset string `json:"asset"` 73 | Amount *MonetaryInt `json:"amount"` 74 | } 75 | if err := json.Unmarshal(data, &monTmp); err != nil { 76 | return nil, err 77 | } 78 | mon := Monetary{ 79 | Asset: Asset(monTmp.Asset), 80 | Amount: monTmp.Amount, 81 | } 82 | if err := ParseMonetary(mon); err != nil { 83 | return nil, errors.Wrapf(err, "value %s", mon.String()) 84 | } 85 | value = mon 86 | case TypePortion: 87 | var s string 88 | if err := json.Unmarshal(data, &s); err != nil { 89 | return nil, err 90 | } 91 | res, err := ParsePortionSpecific(s) 92 | if err != nil { 93 | return nil, err 94 | } 95 | value = *res 96 | case TypeString: 97 | var s String 98 | if err := json.Unmarshal(data, &s); err != nil { 99 | return nil, err 100 | } 101 | value = s 102 | default: 103 | return nil, fmt.Errorf("invalid type '%v'", typ) 104 | } 105 | 106 | return &value, nil 107 | } 108 | -------------------------------------------------------------------------------- /core/portion_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "math/big" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | func TestBetween0And1Inclusive(t *testing.T) { 10 | tests := []struct { 11 | in string 12 | want *big.Rat 13 | wantErr bool 14 | }{ 15 | { 16 | in: "0%", 17 | want: big.NewRat(0, 1), 18 | }, 19 | { 20 | in: "0.0%", 21 | want: big.NewRat(0, 1), 22 | }, 23 | { 24 | in: "0/1", 25 | want: big.NewRat(0, 1), 26 | }, 27 | { 28 | in: "0/25", 29 | want: big.NewRat(0, 1), 30 | }, 31 | { 32 | in: "0/100", 33 | want: big.NewRat(0, 1), 34 | }, 35 | { 36 | in: "1%", 37 | want: big.NewRat(1, 100), 38 | }, 39 | { 40 | in: "1/100", 41 | want: big.NewRat(1, 100), 42 | }, 43 | { 44 | in: "10/1000", 45 | want: big.NewRat(1, 100), 46 | }, 47 | { 48 | in: "50/100", 49 | want: big.NewRat(50, 100), 50 | }, 51 | { 52 | in: "50%", 53 | want: big.NewRat(50, 100), 54 | }, 55 | { 56 | in: "50.0%", 57 | want: big.NewRat(50, 100), 58 | }, 59 | { 60 | in: "1/1", 61 | want: big.NewRat(1, 1), 62 | }, 63 | { 64 | in: "100/100", 65 | want: big.NewRat(1, 1), 66 | }, 67 | { 68 | in: "100.0%", 69 | want: big.NewRat(1, 1), 70 | }, 71 | { 72 | in: "100%", 73 | want: big.NewRat(1, 1), 74 | }, 75 | // Now for the failures. We don't check negative numbers in this test because 76 | // those are a parsing failure, not a range failure. 77 | { 78 | in: "100.1%", 79 | wantErr: true, 80 | }, 81 | { 82 | in: "101%", 83 | wantErr: true, 84 | }, 85 | { 86 | in: "101/100", 87 | wantErr: true, 88 | }, 89 | { 90 | in: "2/1", 91 | wantErr: true, 92 | }, 93 | { 94 | in: "3/2", 95 | wantErr: true, 96 | }, 97 | } 98 | 99 | for _, test := range tests { 100 | t.Run(test.in, func(t *testing.T) { 101 | got, err := ParsePortionSpecific(test.in) 102 | if test.wantErr { 103 | if err == nil { 104 | t.Fatal("should have errored") 105 | } 106 | if !strings.Contains(err.Error(), "between") { 107 | t.Fatal("wrong error") 108 | } 109 | return 110 | } 111 | if err != nil { 112 | t.Fatalf("ParsePortionSpecific(%q): %v", test.in, err) 113 | } 114 | if test.want.Cmp(got.Specific) != 0 { 115 | t.Fatalf("ParsePortionSpecific(%q) = %q, want %q", test.in, got, test.want) 116 | } 117 | }) 118 | } 119 | } 120 | 121 | func TestInvalidFormat(t *testing.T) { 122 | _, err := ParsePortionSpecific("this is not a portion") 123 | if err == nil { 124 | t.Fatal("should have errored") 125 | } 126 | if !strings.Contains(err.Error(), "format") { 127 | t.Fatal("wrong error") 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /vm/program/instructions.go: -------------------------------------------------------------------------------- 1 | package program 2 | 3 | const ( 4 | OP_APUSH = byte(iota + 1) 5 | OP_IPUSH 6 | OP_BUMP // *N => *N 7 | OP_DELETE // 8 | OP_IADD // => 9 | OP_ISUB // => 10 | OP_PRINT // 11 | OP_FAIL // 12 | OP_ASSET // => 13 | OP_MONETARY_NEW // => 14 | OP_MONETARY_ADD // => // panics if not same asset 15 | OP_MAKE_ALLOTMENT // *N => 16 | OP_TAKE_ALL // => 17 | OP_TAKE_ALWAYS // => // takes amount from account unconditionally 18 | OP_TAKE // => // fails with EXIT_INSUFFICIENT_FUNDS if not enough 19 | OP_TAKE_MAX // => // Doesn't fail on insufficient funds. Either missing or remaining is zero. 20 | OP_FUNDING_ASSEMBLE // *N => (first has highest priority) 21 | OP_FUNDING_SUM // => 22 | OP_FUNDING_REVERSE // => 23 | OP_REPAY // 24 | OP_ALLOC // => *N 25 | OP_SEND // 26 | OP_TX_META // 27 | OP_ACCOUNT_META // 28 | ) 29 | 30 | func OpcodeName(op byte) string { 31 | switch op { 32 | case OP_APUSH: 33 | return "OP_APUSH" 34 | case OP_IPUSH: 35 | return "OP_IPUSH" 36 | case OP_BUMP: 37 | return "OP_BUMP" 38 | case OP_DELETE: 39 | return "OP_DELETE" 40 | case OP_IADD: 41 | return "OP_IADD" 42 | case OP_ISUB: 43 | return "OP_ISUB" 44 | case OP_PRINT: 45 | return "OP_PRINT" 46 | case OP_FAIL: 47 | return "OP_FAIL" 48 | case OP_ASSET: 49 | return "OP_ASSET" 50 | case OP_MONETARY_NEW: 51 | return "OP_MONETARY_NEW" 52 | case OP_MONETARY_ADD: 53 | return "OP_MONETARY_ADD" 54 | case OP_MAKE_ALLOTMENT: 55 | return "OP_MAKE_ALLOTMENT" 56 | case OP_TAKE_ALL: 57 | return "OP_TAKE_ALL" 58 | case OP_TAKE_ALWAYS: 59 | return "OP_TAKE_ALWAYS" 60 | case OP_TAKE: 61 | return "OP_TAKE" 62 | case OP_TAKE_MAX: 63 | return "OP_TAKE_MAX" 64 | case OP_FUNDING_ASSEMBLE: 65 | return "OP_FUNDING_ASSEMBLE" 66 | case OP_FUNDING_SUM: 67 | return "OP_FUNDING_SUM" 68 | case OP_FUNDING_REVERSE: 69 | return "OP_FUNDING_REVERSE" 70 | case OP_REPAY: 71 | return "OP_REPAY" 72 | case OP_ALLOC: 73 | return "OP_ALLOC" 74 | case OP_SEND: 75 | return "OP_SEND" 76 | case OP_TX_META: 77 | return "OP_TX_META" 78 | case OP_ACCOUNT_META: 79 | return "OP_ACCOUNT_META" 80 | default: 81 | return "Unknown opcode" 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220626175859-9abda183db8e h1:bt6SW1eSSvdmmsG0KqyxYXorcTnFBTX7hfVR1+68+jg= 2 | github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220626175859-9abda183db8e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= 3 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 8 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 9 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 10 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 11 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 12 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 13 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 14 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 15 | github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= 16 | github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= 17 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 18 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 19 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 20 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 21 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 22 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 23 | github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= 24 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= 25 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 26 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 27 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 28 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 29 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 30 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 31 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 32 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 33 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 34 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 35 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 36 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 37 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 38 | -------------------------------------------------------------------------------- /core/funding_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestFundingTake(t *testing.T) { 8 | f := Funding{ 9 | Asset: "COIN", 10 | Parts: []FundingPart{ 11 | { 12 | Account: "aaa", 13 | Amount: NewMonetaryInt(70), 14 | }, 15 | { 16 | Account: "bbb", 17 | Amount: NewMonetaryInt(30), 18 | }, 19 | { 20 | Account: "ccc", 21 | Amount: NewMonetaryInt(50), 22 | }, 23 | }, 24 | } 25 | result, remainder, err := f.Take(NewMonetaryInt(80)) 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | expectedResult := Funding{ 30 | Asset: "COIN", 31 | Parts: []FundingPart{ 32 | { 33 | Account: "aaa", 34 | Amount: NewMonetaryInt(70), 35 | }, 36 | { 37 | Account: "bbb", 38 | Amount: NewMonetaryInt(10), 39 | }, 40 | }, 41 | } 42 | if !ValueEquals(result, expectedResult) { 43 | t.Fatalf("unexpected result: %v", result) 44 | } 45 | expectedRemainder := Funding{ 46 | Asset: "COIN", 47 | Parts: []FundingPart{ 48 | { 49 | Account: "bbb", 50 | Amount: NewMonetaryInt(20), 51 | }, 52 | { 53 | Account: "ccc", 54 | Amount: NewMonetaryInt(50), 55 | }, 56 | }, 57 | } 58 | if !ValueEquals(remainder, expectedRemainder) { 59 | t.Fatalf("unexpected remainder: %v", remainder) 60 | } 61 | } 62 | 63 | func TestFundingTakeMaxUnder(t *testing.T) { 64 | f := Funding{ 65 | Asset: "COIN", 66 | Parts: []FundingPart{ 67 | { 68 | Account: "aaa", 69 | Amount: NewMonetaryInt(30), 70 | }, 71 | }, 72 | } 73 | result, remainder := f.TakeMax(NewMonetaryInt(80)) 74 | if !ValueEquals(result, Funding{ 75 | Asset: "COIN", 76 | Parts: []FundingPart{ 77 | { 78 | Account: "aaa", 79 | Amount: NewMonetaryInt(30), 80 | }, 81 | }, 82 | }) { 83 | t.Fatalf("unexpected result: %v", result) 84 | } 85 | if !ValueEquals(remainder, Funding{ 86 | Asset: "COIN", 87 | }) { 88 | t.Fatalf("unexpected remainder: %v", remainder) 89 | } 90 | } 91 | 92 | func TestFundingTakeMaxAbove(t *testing.T) { 93 | f := Funding{ 94 | Asset: "COIN", 95 | Parts: []FundingPart{ 96 | { 97 | Account: "aaa", 98 | Amount: NewMonetaryInt(90), 99 | }, 100 | }, 101 | } 102 | result, remainder := f.TakeMax(NewMonetaryInt(80)) 103 | if !ValueEquals(result, Funding{ 104 | Asset: "COIN", 105 | Parts: []FundingPart{ 106 | { 107 | Account: "aaa", 108 | Amount: NewMonetaryInt(80), 109 | }, 110 | }, 111 | }) { 112 | t.Fatalf("unexpected result: %v", result) 113 | } 114 | if !ValueEquals(remainder, Funding{ 115 | Asset: "COIN", 116 | Parts: []FundingPart{ 117 | { 118 | Account: "aaa", 119 | Amount: NewMonetaryInt(10), 120 | }, 121 | }, 122 | }) { 123 | t.Fatalf("unexpected remainder: %v", remainder) 124 | } 125 | } 126 | 127 | func TestFundingReversal(t *testing.T) { 128 | f := Funding{ 129 | Asset: "COIN", 130 | Parts: []FundingPart{ 131 | { 132 | Account: "aaa", 133 | Amount: NewMonetaryInt(10), 134 | }, 135 | { 136 | Account: "bbb", 137 | Amount: NewMonetaryInt(20), 138 | }, 139 | { 140 | Account: "ccc", 141 | Amount: NewMonetaryInt(30), 142 | }, 143 | }, 144 | } 145 | rev := f.Reverse() 146 | if !ValueEquals(rev, Funding{ 147 | Asset: "COIN", 148 | Parts: []FundingPart{ 149 | { 150 | Account: "ccc", 151 | Amount: NewMonetaryInt(30), 152 | }, 153 | { 154 | Account: "bbb", 155 | Amount: NewMonetaryInt(20), 156 | }, 157 | { 158 | Account: "aaa", 159 | Amount: NewMonetaryInt(10), 160 | }, 161 | }, 162 | }) { 163 | t.Fatalf("unexpected result: %v", rev) 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /script/compiler/error.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "strings" 7 | 8 | "github.com/antlr/antlr4/runtime/Go/antlr" 9 | "github.com/logrusorgru/aurora" 10 | ) 11 | 12 | type CompileError struct { 13 | StartL, StartC int 14 | EndL, EndC int 15 | Msg string 16 | } 17 | 18 | type CompileErrorList struct { 19 | Errors []CompileError 20 | Source string 21 | } 22 | 23 | func (c *CompileErrorList) Error() string { 24 | source := strings.ReplaceAll(c.Source, "\t", " ") 25 | lines := strings.SplitAfter(strings.ReplaceAll(source, "\r\n", "\n"), "\n") 26 | lines[len(lines)-1] += "\n" 27 | 28 | txtBarGood := aurora.Blue("|") 29 | 30 | s := "" 31 | for _, e := range c.Errors { 32 | lnPad := int(math.Log10(float64(e.EndL))) + 1 // line number padding 33 | // error indicator 34 | s += fmt.Sprintf("%v error:%v:%v\n", aurora.Red("-->"), e.StartL, e.StartC) 35 | // initial empty line 36 | s += fmt.Sprintf("%v %v\n", strings.Repeat(" ", lnPad), txtBarGood) 37 | // offending lines 38 | for l := e.StartL; l <= e.EndL; l++ { // "print fail" 39 | line := lines[l-1] 40 | before := "" 41 | after := "" 42 | start := 0 43 | if l == e.StartL { 44 | before = line[:e.StartC] 45 | line = line[e.StartC:] 46 | start = e.StartC 47 | } 48 | if l == e.EndL { 49 | idx := e.EndC - start + 1 50 | if idx >= len(line) { // because newline was erased 51 | idx = len(line) - 1 52 | } 53 | after = line[idx:] 54 | line = line[:idx] 55 | } 56 | s += aurora.Red(fmt.Sprintf("%0*d | ", lnPad, l)).String() 57 | s += fmt.Sprintf("%v%v%v", 58 | aurora.BrightBlack(before), line, aurora.BrightBlack(after)) 59 | } 60 | // message 61 | start := strings.IndexFunc(lines[e.EndL-1], func(r rune) bool { 62 | return r != ' ' 63 | }) 64 | span := e.EndC - start + 1 65 | if e.StartL == e.EndL { 66 | start = e.StartC 67 | span = e.EndC - e.StartC 68 | } 69 | if span == 0 { 70 | span = 1 71 | } 72 | s += fmt.Sprintf("%v %v %v%v %v\n", 73 | strings.Repeat(" ", lnPad), 74 | txtBarGood, 75 | strings.Repeat(" ", start), 76 | aurora.Red(strings.Repeat("^", span)), 77 | e.Msg) 78 | } 79 | return s 80 | } 81 | 82 | type ErrorListener struct { 83 | *antlr.DefaultErrorListener 84 | Errors []CompileError 85 | } 86 | 87 | func (l *ErrorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, startL, startC int, msg string, e antlr.RecognitionException) { 88 | length := 1 89 | if token, ok := offendingSymbol.(antlr.Token); ok { 90 | length = len(token.GetText()) 91 | } 92 | endL := startL 93 | endC := startC + length - 1 // -1 so that end character is inside the offending token 94 | l.Errors = append(l.Errors, CompileError{ 95 | StartL: startL, 96 | StartC: startC, 97 | EndL: endL, 98 | EndC: endC, 99 | Msg: msg, 100 | }) 101 | } 102 | 103 | func LogicError(c antlr.ParserRuleContext, err error) *CompileError { 104 | endC := c.GetStop().GetColumn() + len(c.GetStop().GetText()) 105 | // fmt.Println(c.GetStart().GetLine(), 106 | // c.GetStart().GetColumn(), 107 | // c.GetStop().GetLine(), 108 | // endC) 109 | return &CompileError{ 110 | StartL: c.GetStart().GetLine(), 111 | StartC: c.GetStart().GetColumn(), 112 | EndL: c.GetStop().GetLine(), 113 | EndC: endC, 114 | Msg: err.Error(), 115 | } 116 | } 117 | 118 | const InternalErrorMsg = "internal compiler error, please report to the issue tracker" 119 | 120 | func InternalError(c antlr.ParserRuleContext) *CompileError { 121 | return &CompileError{ 122 | StartL: c.GetStart().GetLine(), 123 | StartC: c.GetStart().GetColumn(), 124 | EndL: c.GetStop().GetLine(), 125 | EndC: c.GetStop().GetColumn(), 126 | Msg: InternalErrorMsg, 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /vm/program/program.go: -------------------------------------------------------------------------------- 1 | package program 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/json" 6 | "fmt" 7 | 8 | "github.com/formancehq/machine/core" 9 | "github.com/pkg/errors" 10 | ) 11 | 12 | type Program struct { 13 | Instructions []byte 14 | Resources []Resource 15 | NeededBalances map[core.Address]map[core.Address]struct{} 16 | } 17 | 18 | func (p Program) String() string { 19 | out := "Program:\nINSTRUCTIONS\n" 20 | for i := 0; i < len(p.Instructions); i++ { 21 | out += fmt.Sprintf("%02d----- ", i) 22 | switch p.Instructions[i] { 23 | case OP_APUSH: 24 | out += "OP_APUSH " 25 | address := binary.LittleEndian.Uint16(p.Instructions[i+1 : i+3]) 26 | out += fmt.Sprintf("#%d\n", address) 27 | i += 2 28 | case OP_IPUSH: 29 | out += "OP_IPUSH " 30 | out += fmt.Sprintf("%d\n", binary.LittleEndian.Uint64(p.Instructions[i+1:i+9])) 31 | i += 8 32 | default: 33 | out += OpcodeName(p.Instructions[i]) + "\n" 34 | } 35 | } 36 | 37 | out += fmt.Sprintln("RESOURCES") 38 | i := 0 39 | for i = 0; i < len(p.Resources); i++ { 40 | out += fmt.Sprintf("%02d ", i) 41 | out += fmt.Sprintf("%v\n", p.Resources[i]) 42 | } 43 | return out 44 | } 45 | 46 | func (p *Program) ParseVariables(vars map[string]core.Value) (map[string]core.Value, error) { 47 | variables := make(map[string]core.Value) 48 | for _, res := range p.Resources { 49 | if variable, ok := res.(Variable); ok { 50 | if val, ok := vars[variable.Name]; ok && val.GetType() == variable.Typ { 51 | variables[variable.Name] = val 52 | switch val.GetType() { 53 | case core.TypeAccount: 54 | if err := core.ParseAccount(val.(core.Account)); err != nil { 55 | return nil, errors.Wrapf(err, "invalid variable $%s value '%s'", 56 | variable.Name, string(val.(core.Account))) 57 | } 58 | case core.TypeMonetary: 59 | if err := core.ParseMonetary(val.(core.Monetary)); err != nil { 60 | return nil, errors.Wrapf(err, "invalid variable $%s value '%s'", 61 | variable.Name, val.(core.Monetary).String()) 62 | } 63 | case core.TypePortion: 64 | if err := core.ValidatePortionSpecific(val.(core.Portion)); err != nil { 65 | return nil, errors.Wrapf(err, "invalid variable $%s value '%s'", 66 | variable.Name, val.(core.Portion).String()) 67 | } 68 | case core.TypeString: 69 | if _, ok := val.(core.String); !ok { 70 | return nil, fmt.Errorf("invalid variable $%s value '%s'", 71 | variable.Name, val) 72 | } 73 | case core.TypeNumber: 74 | if _, ok := val.(core.Number); !ok { 75 | return nil, fmt.Errorf("invalid variable $%s value '%s'", 76 | variable.Name, val) 77 | } 78 | } 79 | delete(vars, variable.Name) 80 | } else { 81 | return nil, fmt.Errorf("missing variables: %q", variable.Name) 82 | } 83 | } 84 | } 85 | for name := range vars { 86 | return nil, fmt.Errorf("extraneous variable: %q", name) 87 | } 88 | return variables, nil 89 | } 90 | 91 | func (p *Program) ParseVariablesJSON(vars map[string]json.RawMessage) (map[string]core.Value, error) { 92 | variables := make(map[string]core.Value) 93 | for _, res := range p.Resources { 94 | if param, ok := res.(Variable); ok { 95 | data, ok := vars[param.Name] 96 | if !ok { 97 | return nil, fmt.Errorf("missing variable $%s", param.Name) 98 | } 99 | val, err := core.NewValueFromJSON(param.Typ, data) 100 | if err != nil { 101 | return nil, fmt.Errorf( 102 | "invalid JSON value for variable $%s of type %v: %w", 103 | param.Name, param.Typ, err) 104 | } 105 | variables[param.Name] = *val 106 | delete(vars, param.Name) 107 | } 108 | } 109 | for name := range vars { 110 | return nil, fmt.Errorf("extraneous variable $%s", name) 111 | } 112 | return variables, nil 113 | } 114 | -------------------------------------------------------------------------------- /core/monetary.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | 7 | "github.com/pkg/errors" 8 | ) 9 | 10 | type Monetary struct { 11 | Asset Asset `json:"asset"` 12 | Amount *MonetaryInt `json:"amount"` 13 | } 14 | 15 | func (Monetary) GetType() Type { return TypeMonetary } 16 | 17 | func (m Monetary) String() string { 18 | if m.Amount == nil { 19 | return fmt.Sprintf("[%s nil]", m.Asset) 20 | } 21 | amt := *m.Amount 22 | return fmt.Sprintf("[%v %s]", m.Asset, amt.String()) 23 | } 24 | 25 | func (m Monetary) GetAsset() Asset { return m.Asset } 26 | 27 | func ParseMonetary(mon Monetary) error { 28 | if err := ParseAsset(mon.Asset); err != nil { 29 | return errors.Wrapf(err, "asset '%s'", mon.Asset) 30 | } 31 | if mon.Amount == nil { 32 | return errors.Errorf("nil amount") 33 | } 34 | if mon.Amount.Ltz() { 35 | return errors.Errorf("negative amount") 36 | } 37 | return nil 38 | } 39 | 40 | type MonetaryInt big.Int 41 | 42 | func (MonetaryInt) GetType() Type { return TypeNumber } 43 | 44 | func (a *MonetaryInt) Add(b *MonetaryInt) *MonetaryInt { 45 | if a == nil { 46 | a = NewMonetaryInt(0) 47 | } 48 | 49 | if b == nil { 50 | b = NewMonetaryInt(0) 51 | } 52 | 53 | return (*MonetaryInt)(big.NewInt(0).Add((*big.Int)(a), (*big.Int)(b))) 54 | } 55 | 56 | func (a *MonetaryInt) Sub(b *MonetaryInt) *MonetaryInt { 57 | if a == nil { 58 | a = NewMonetaryInt(0) 59 | } 60 | 61 | if b == nil { 62 | b = NewMonetaryInt(0) 63 | } 64 | 65 | return (*MonetaryInt)(big.NewInt(0).Sub((*big.Int)(a), (*big.Int)(b))) 66 | } 67 | 68 | func (a *MonetaryInt) Neg() *MonetaryInt { 69 | return (*MonetaryInt)(big.NewInt(0).Neg((*big.Int)(a))) 70 | } 71 | 72 | func (a *MonetaryInt) OrZero() *MonetaryInt { 73 | if a == nil { 74 | return NewMonetaryInt(0) 75 | } 76 | 77 | return a 78 | } 79 | 80 | func (a *MonetaryInt) Lte(b *MonetaryInt) bool { 81 | return (*big.Int)(a).Cmp((*big.Int)(b)) <= 0 82 | } 83 | 84 | func (a *MonetaryInt) Gte(b *MonetaryInt) bool { 85 | return (*big.Int)(a).Cmp((*big.Int)(b)) >= 0 86 | } 87 | 88 | func (a *MonetaryInt) Lt(b *MonetaryInt) bool { 89 | return (*big.Int)(a).Cmp((*big.Int)(b)) < 0 90 | } 91 | 92 | func (a *MonetaryInt) Ltz() bool { 93 | return (*big.Int)(a).Cmp(big.NewInt(0)) < 0 94 | } 95 | 96 | func (a *MonetaryInt) Gt(b *MonetaryInt) bool { 97 | return (*big.Int)(a).Cmp((*big.Int)(b)) > 0 98 | } 99 | 100 | func (a *MonetaryInt) Eq(b *MonetaryInt) bool { 101 | return (*big.Int)(a).Cmp((*big.Int)(b)) == 0 102 | } 103 | 104 | func (a *MonetaryInt) Equal(b *MonetaryInt) bool { 105 | return (*big.Int)(a).Cmp((*big.Int)(b)) == 0 106 | } 107 | 108 | func (a *MonetaryInt) Cmp(b *MonetaryInt) int { 109 | return (*big.Int)(a).Cmp((*big.Int)(b)) 110 | } 111 | 112 | func (a *MonetaryInt) Uint64() uint64 { 113 | return (*big.Int)(a).Uint64() 114 | } 115 | 116 | func (a *MonetaryInt) String() string { 117 | if a == nil { 118 | return "0" 119 | } 120 | 121 | return (*big.Int)(a).String() 122 | } 123 | 124 | func (a *MonetaryInt) UnmarshalJSON(b []byte) error { 125 | return (*big.Int)(a).UnmarshalJSON(b) 126 | } 127 | 128 | func (a *MonetaryInt) MarshalJSON() ([]byte, error) { 129 | if a == nil { 130 | return []byte("0"), nil 131 | } 132 | return (*big.Int)(a).MarshalJSON() 133 | } 134 | 135 | func (a *MonetaryInt) MarshalText() ([]byte, error) { 136 | return (*big.Int)(a).MarshalText() 137 | } 138 | 139 | func (a *MonetaryInt) UnmarshalText(b []byte) error { 140 | return (*big.Int)(a).UnmarshalText(b) 141 | } 142 | 143 | func NewMonetaryInt(i int64) *MonetaryInt { 144 | return (*MonetaryInt)(big.NewInt(i)) 145 | } 146 | 147 | func ParseMonetaryInt(s string) (*MonetaryInt, error) { 148 | i, ok := big.NewInt(0).SetString(s, 10) 149 | if !ok { 150 | return nil, errors.New("invalid monetary int") 151 | } 152 | return (*MonetaryInt)(i), nil 153 | } 154 | -------------------------------------------------------------------------------- /core/json_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "encoding/json" 5 | "math/big" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestAccountTypedJSON(t *testing.T) { 13 | j := json.RawMessage(`{ 14 | "type": "account", 15 | "value": "users:001" 16 | }`) 17 | value, err := NewValueFromTypedJSON(j) 18 | require.NoError(t, err) 19 | 20 | if !ValueEquals(*value, Account("users:001")) { 21 | t.Fatalf("unexpected value: %v", *value) 22 | } 23 | } 24 | 25 | func TestAssetTypedJSON(t *testing.T) { 26 | j := json.RawMessage(`{ 27 | "type": "asset", 28 | "value": "EUR/2" 29 | }`) 30 | value, err := NewValueFromTypedJSON(j) 31 | require.NoError(t, err) 32 | 33 | if !ValueEquals(*value, Asset("EUR/2")) { 34 | t.Fatalf("unexpected value: %v", *value) 35 | } 36 | } 37 | 38 | func TestNumberTypedJSON(t *testing.T) { 39 | j := json.RawMessage(`{ 40 | "type": "number", 41 | "value": 89849865111111111111111111111111111555555555555555555555555555555555555555555555555999999999999999999999 42 | }`) 43 | value, err := NewValueFromTypedJSON(j) 44 | require.NoError(t, err) 45 | 46 | num, err := ParseNumber("89849865111111111111111111111111111555555555555555555555555555555555555555555555555999999999999999999999") 47 | require.NoError(t, err) 48 | 49 | if !ValueEquals(*value, num) { 50 | t.Fatalf("unexpected value: %v", *value) 51 | } 52 | } 53 | 54 | func TestMonetaryTypedJSON(t *testing.T) { 55 | j := json.RawMessage(`{ 56 | "type": "monetary", 57 | "value": { 58 | "asset": "EUR/2", 59 | "amount": 123456 60 | } 61 | }`) 62 | value, err := NewValueFromTypedJSON(j) 63 | require.NoError(t, err) 64 | 65 | if !ValueEquals(*value, Monetary{ 66 | Asset: "EUR/2", 67 | Amount: NewMonetaryInt(123456), 68 | }) { 69 | t.Fatalf("unexpected value: %v", *value) 70 | } 71 | } 72 | 73 | func TestPortionTypedJSON(t *testing.T) { 74 | j := json.RawMessage(`{ 75 | "type": "portion", 76 | "value": "90%" 77 | }`) 78 | value, err := NewValueFromTypedJSON(j) 79 | require.NoError(t, err) 80 | 81 | portion, err := NewPortionSpecific(*big.NewRat(90, 100)) 82 | require.NoError(t, err) 83 | 84 | if !ValueEquals(*value, *portion) { 85 | t.Fatalf("unexpected value: %v", *value) 86 | } 87 | } 88 | 89 | func TestInvalidTypedJSON(t *testing.T) { 90 | j := json.RawMessage(`{ 91 | "value": { 92 | "asset": "EUR/2", 93 | "amount": 123456 94 | } 95 | }`) 96 | _, err := NewValueFromTypedJSON(j) 97 | require.Error(t, err) 98 | } 99 | 100 | func TestMarshalJSON(t *testing.T) { 101 | t.Run("account", func(t *testing.T) { 102 | by, err := json.Marshal(Account("platform")) 103 | require.NoError(t, err) 104 | assert.Equal(t, `"platform"`, string(by)) 105 | }) 106 | t.Run("asset", func(t *testing.T) { 107 | by, err := json.Marshal(Asset("COIN")) 108 | require.NoError(t, err) 109 | assert.Equal(t, `"COIN"`, string(by)) 110 | }) 111 | t.Run("number", func(t *testing.T) { 112 | by, err := json.Marshal( 113 | Number(big.NewInt(42))) 114 | require.NoError(t, err) 115 | assert.Equal(t, `42`, string(by)) 116 | }) 117 | t.Run("string", func(t *testing.T) { 118 | by, err := json.Marshal(String("test")) 119 | require.NoError(t, err) 120 | assert.Equal(t, `"test"`, string(by)) 121 | }) 122 | t.Run("monetary", func(t *testing.T) { 123 | by, err := json.Marshal( 124 | Monetary{ 125 | Asset: "COIN", 126 | Amount: NewMonetaryInt(42), 127 | }) 128 | require.NoError(t, err) 129 | assert.Equal(t, `{"asset":"COIN","amount":42}`, string(by)) 130 | }) 131 | t.Run("portion", func(t *testing.T) { 132 | by, err := json.Marshal( 133 | Portion{ 134 | Remaining: true, 135 | Specific: big.NewRat(10, 12), 136 | }) 137 | require.NoError(t, err) 138 | assert.Equal(t, `{"remaining":true,"specific":"5/6"}`, string(by)) 139 | }) 140 | } 141 | -------------------------------------------------------------------------------- /script/NumScript.g4: -------------------------------------------------------------------------------- 1 | grammar NumScript; 2 | 3 | NEWLINE: [\r\n]+; 4 | WHITESPACE: [ \t]+ -> skip; 5 | 6 | MULTILINE_COMMENT: '/*' (MULTILINE_COMMENT|.)*? '*/' -> skip; 7 | LINE_COMMENT: '//' .*? NEWLINE -> skip; 8 | VARS: 'vars'; 9 | META: 'meta'; 10 | SET_TX_META: 'set_tx_meta'; 11 | SET_ACCOUNT_META: 'set_account_meta'; 12 | PRINT: 'print'; 13 | FAIL: 'fail'; 14 | SEND: 'send'; 15 | SOURCE: 'source'; 16 | FROM: 'from'; 17 | MAX: 'max'; 18 | DESTINATION: 'destination'; 19 | TO: 'to'; 20 | ALLOCATE: 'allocate'; 21 | OP_ADD: '+'; 22 | OP_SUB: '-'; 23 | LPAREN: '('; 24 | RPAREN: ')'; 25 | LBRACK: '['; 26 | RBRACK: ']'; 27 | LBRACE: '{'; 28 | RBRACE: '}'; 29 | EQ: '='; 30 | TY_ACCOUNT: 'account'; 31 | TY_NUMBER: 'number'; 32 | TY_MONETARY: 'monetary'; 33 | TY_PORTION: 'portion'; 34 | TY_STRING: 'string'; 35 | STRING: '"' [a-zA-Z0-9_\- ]* '"'; 36 | PORTION: 37 | ( [0-9]+ [ ]? '/' [ ]? [0-9]+ 38 | | [0-9]+ ('.' [0-9]+)? '%' 39 | ); 40 | REMAINING: 'remaining'; 41 | KEPT: 'kept'; 42 | BALANCE: 'balance'; 43 | NUMBER: [0-9]+; 44 | PERCENT: '%'; 45 | VARIABLE_NAME: '$' [a-z_]+ [a-z0-9_]*; 46 | ACCOUNT: '@' [a-zA-Z_]+ [a-zA-Z0-9_:]*; 47 | ASSET: [A-Z/0-9]+; 48 | 49 | monetary: LBRACK asset=ASSET amt=NUMBER RBRACK; 50 | 51 | monetaryAll: LBRACK asset=ASSET '*' RBRACK; 52 | 53 | literal 54 | : ACCOUNT # LitAccount 55 | | ASSET # LitAsset 56 | | NUMBER # LitNumber 57 | | STRING # LitString 58 | | PORTION # LitPortion 59 | | monetary # LitMonetary 60 | ; 61 | 62 | variable: VARIABLE_NAME; 63 | 64 | expression 65 | : lhs=expression op=(OP_ADD|OP_SUB) rhs=expression # ExprAddSub 66 | | lit=literal # ExprLiteral 67 | | var_=variable # ExprVariable 68 | ; 69 | 70 | allotmentPortion 71 | : PORTION # allotmentPortionConst 72 | | por=variable # allotmentPortionVar 73 | | REMAINING # allotmentPortionRemaining 74 | ; 75 | 76 | destinationInOrder: LBRACE NEWLINE 77 | (MAX amounts+=expression dests+=keptOrDestination NEWLINE)+ 78 | REMAINING remainingDest=keptOrDestination NEWLINE 79 | RBRACE; 80 | destinationAllotment: LBRACE NEWLINE 81 | (portions+=allotmentPortion dests+=keptOrDestination NEWLINE)+ 82 | RBRACE; 83 | 84 | keptOrDestination 85 | : TO destination # isDestination 86 | | KEPT # isKept 87 | ; 88 | 89 | destination 90 | : expression # DestAccount 91 | | destinationInOrder # DestInOrder 92 | | destinationAllotment # DestAllotment 93 | ; 94 | 95 | sourceAccountOverdraft 96 | : 'allowing overdraft up to' specific=expression # SrcAccountOverdraftSpecific 97 | | 'allowing unbounded overdraft' # SrcAccountOverdraftUnbounded 98 | ; 99 | 100 | sourceAccount: account=expression (overdraft=sourceAccountOverdraft)?; 101 | sourceInOrder: LBRACE NEWLINE (sources+=source NEWLINE)+ RBRACE; 102 | sourceMaxed: MAX max=expression FROM src=source; 103 | source 104 | : sourceAccount # SrcAccount 105 | | sourceMaxed # SrcMaxed 106 | | sourceInOrder # SrcInOrder 107 | ; 108 | 109 | sourceAllotment: LBRACE NEWLINE (portions+=allotmentPortion FROM sources+=source NEWLINE)+ RBRACE; 110 | valueAwareSource 111 | : source # Src 112 | | sourceAllotment # SrcAllotment 113 | ; 114 | 115 | statement 116 | : PRINT expr=expression # Print 117 | | SET_TX_META '(' key=STRING ',' value=expression ')' # SetTxMeta 118 | | SET_ACCOUNT_META '(' acc=expression ',' key=STRING ',' value=expression ')' # SetAccountMeta 119 | | FAIL # Fail 120 | | SEND (mon=expression | monAll=monetaryAll) LPAREN NEWLINE 121 | ( SOURCE '=' src=valueAwareSource NEWLINE DESTINATION '=' dest=destination 122 | | DESTINATION '=' dest=destination NEWLINE SOURCE '=' src=valueAwareSource) NEWLINE RPAREN # Send 123 | ; 124 | 125 | type_: TY_ACCOUNT | TY_NUMBER | TY_STRING | TY_MONETARY | TY_PORTION; 126 | 127 | origin 128 | : META '(' account=expression ',' key=STRING ')' # OriginAccountMeta 129 | | BALANCE '(' account=expression ',' asset=ASSET ')' # OriginAccountBalance 130 | ; 131 | 132 | varDecl: ty=type_ name=variable (EQ orig=origin)?; 133 | 134 | varListDecl: VARS LBRACE NEWLINE (v+=varDecl NEWLINE+)+ RBRACE NEWLINE; 135 | 136 | script: 137 | NEWLINE* 138 | vars=varListDecl? 139 | stmts+=statement 140 | (NEWLINE stmts+=statement)* 141 | NEWLINE* 142 | EOF 143 | ; 144 | -------------------------------------------------------------------------------- /core/funding.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | type FundingPart struct { 9 | Amount *MonetaryInt 10 | Account Account 11 | } 12 | 13 | func (Funding) GetType() Type { return TypeFunding } 14 | 15 | func (f Funding) GetAsset() Asset { return f.Asset } 16 | 17 | func (lhs FundingPart) Equals(rhs FundingPart) bool { 18 | return lhs.Account == rhs.Account && lhs.Amount.Equal(rhs.Amount) 19 | } 20 | 21 | type Funding struct { 22 | Asset Asset 23 | Parts []FundingPart 24 | } 25 | 26 | func (lhs Funding) Equals(rhs Funding) bool { 27 | if lhs.Asset != rhs.Asset { 28 | return false 29 | } 30 | if len(lhs.Parts) != len(rhs.Parts) { 31 | return false 32 | } 33 | for i := range lhs.Parts { 34 | if !lhs.Parts[i].Equals(rhs.Parts[i]) { 35 | return false 36 | } 37 | } 38 | return true 39 | } 40 | 41 | func (f Funding) String() string { 42 | out := fmt.Sprintf("[%v", string(f.Asset)) 43 | for _, part := range f.Parts { 44 | out += fmt.Sprintf(" %v %v", part.Account, part.Amount) 45 | } 46 | return out + "]" 47 | } 48 | 49 | func (f Funding) Take(amount *MonetaryInt) (Funding, Funding, error) { 50 | result := Funding{ 51 | Asset: f.Asset, 52 | } 53 | remainder := Funding{ 54 | Asset: f.Asset, 55 | } 56 | remainingToWithdraw := amount 57 | i := 0 58 | for remainingToWithdraw.Gt(NewMonetaryInt(0)) && i < len(f.Parts) { 59 | amtToWithdraw := f.Parts[i].Amount 60 | // if this part has excess balance, put it in the remainder & only take what's needed 61 | if amtToWithdraw.Gt(remainingToWithdraw) { 62 | rem := amtToWithdraw.Sub(remainingToWithdraw) 63 | amtToWithdraw = remainingToWithdraw 64 | remainder.Parts = append(remainder.Parts, FundingPart{ 65 | Account: f.Parts[i].Account, 66 | Amount: rem, 67 | }) 68 | } 69 | remainingToWithdraw = remainingToWithdraw.Sub(amtToWithdraw) 70 | result.Parts = append(result.Parts, FundingPart{ 71 | Account: f.Parts[i].Account, 72 | Amount: amtToWithdraw, 73 | }) 74 | i++ 75 | } 76 | for i < len(f.Parts) { 77 | remainder.Parts = append(remainder.Parts, FundingPart{ 78 | Account: f.Parts[i].Account, 79 | Amount: f.Parts[i].Amount, 80 | }) 81 | i++ 82 | } 83 | if !remainingToWithdraw.Eq(NewMonetaryInt(0)) { 84 | return Funding{}, Funding{}, errors.New("insufficient funding") 85 | } 86 | return result, remainder, nil 87 | } 88 | 89 | func (f Funding) TakeMax(amount *MonetaryInt) (Funding, Funding) { 90 | result := Funding{ 91 | Asset: f.Asset, 92 | } 93 | remainder := Funding{ 94 | Asset: f.Asset, 95 | } 96 | remainingToWithdraw := amount 97 | i := 0 98 | for remainingToWithdraw.Gt(NewMonetaryInt(0)) && i < len(f.Parts) { 99 | amtToWithdraw := f.Parts[i].Amount 100 | // if this part has excess balance, put it in the remainder & only take what's needed 101 | if amtToWithdraw.Gt(remainingToWithdraw) { 102 | rem := amtToWithdraw.Sub(remainingToWithdraw) 103 | amtToWithdraw = remainingToWithdraw 104 | remainder.Parts = append(remainder.Parts, FundingPart{ 105 | Account: f.Parts[i].Account, 106 | Amount: rem, 107 | }) 108 | } 109 | remainingToWithdraw = remainingToWithdraw.Sub(amtToWithdraw) 110 | result.Parts = append(result.Parts, FundingPart{ 111 | Account: f.Parts[i].Account, 112 | Amount: amtToWithdraw, 113 | }) 114 | i++ 115 | } 116 | for i < len(f.Parts) { 117 | remainder.Parts = append(remainder.Parts, FundingPart{ 118 | Account: f.Parts[i].Account, 119 | Amount: f.Parts[i].Amount, 120 | }) 121 | i++ 122 | } 123 | return result, remainder 124 | } 125 | 126 | func (f Funding) Concat(other Funding) (Funding, error) { 127 | if f.Asset != other.Asset { 128 | return Funding{}, errors.New("tried to concat different assets") 129 | } 130 | res := Funding{ 131 | Asset: f.Asset, 132 | Parts: f.Parts, 133 | } 134 | if len(res.Parts) > 0 && len(other.Parts) > 0 && res.Parts[len(res.Parts)-1].Account == other.Parts[0].Account { 135 | res.Parts[len(res.Parts)-1].Amount = res.Parts[len(res.Parts)-1].Amount.Add(other.Parts[0].Amount) 136 | res.Parts = append(res.Parts, other.Parts[1:]...) 137 | } else { 138 | res.Parts = append(res.Parts, other.Parts...) 139 | } 140 | return res, nil 141 | } 142 | 143 | func (f Funding) Total() *MonetaryInt { 144 | total := NewMonetaryInt(0) 145 | for _, part := range f.Parts { 146 | total = total.Add(part.Amount) 147 | } 148 | return total 149 | } 150 | 151 | func (f Funding) Reverse() Funding { 152 | newParts := []FundingPart{} 153 | for i := len(f.Parts) - 1; i >= 0; i-- { 154 | newParts = append(newParts, f.Parts[i]) 155 | } 156 | newFunding := Funding{ 157 | Asset: f.Asset, 158 | Parts: newParts, 159 | } 160 | return newFunding 161 | } 162 | -------------------------------------------------------------------------------- /script/compiler/destination.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/formancehq/machine/core" 7 | "github.com/formancehq/machine/script/parser" 8 | "github.com/formancehq/machine/vm/program" 9 | ) 10 | 11 | func (p *parseVisitor) VisitDestination(c parser.IDestinationContext) *CompileError { 12 | err := p.VisitDestinationRecursive(c) 13 | if err != nil { 14 | return err 15 | } 16 | p.AppendInstruction(program.OP_REPAY) 17 | return nil 18 | } 19 | 20 | func (p *parseVisitor) VisitDestinationRecursive(c parser.IDestinationContext) *CompileError { 21 | switch c := c.(type) { 22 | case *parser.DestAccountContext: 23 | p.AppendInstruction(program.OP_FUNDING_SUM) 24 | p.AppendInstruction(program.OP_TAKE) 25 | ty, _, err := p.VisitExpr(c.Expression(), true) 26 | if err != nil { 27 | return err 28 | } 29 | if ty != core.TypeAccount { 30 | return LogicError(c, 31 | errors.New("wrong type: expected account as destination"), 32 | ) 33 | } 34 | p.AppendInstruction(program.OP_SEND) 35 | return nil 36 | case *parser.DestInOrderContext: 37 | dests := c.DestinationInOrder().GetDests() 38 | amounts := c.DestinationInOrder().GetAmounts() 39 | n := len(dests) 40 | 41 | // initialize the `kept` accumulator 42 | p.AppendInstruction(program.OP_FUNDING_SUM) 43 | p.AppendInstruction(program.OP_ASSET) 44 | err := p.PushInteger(core.NewNumber(0)) 45 | if err != nil { 46 | return LogicError(c, err) 47 | } 48 | p.AppendInstruction(program.OP_MONETARY_NEW) 49 | 50 | err = p.Bump(1) 51 | if err != nil { 52 | return LogicError(c, err) 53 | } 54 | 55 | for i := 0; i < n; i++ { 56 | ty, _, compErr := p.VisitExpr(amounts[i], true) 57 | if compErr != nil { 58 | return compErr 59 | } 60 | if ty != core.TypeMonetary { 61 | return LogicError(c, errors.New("wrong type: expected monetary as max")) 62 | } 63 | p.AppendInstruction(program.OP_TAKE_MAX) 64 | err := p.Bump(2) 65 | if err != nil { 66 | return LogicError(c, err) 67 | } 68 | p.AppendInstruction(program.OP_DELETE) 69 | compErr = p.VisitKeptOrDestination(dests[i]) 70 | if compErr != nil { 71 | return compErr 72 | } 73 | p.AppendInstruction(program.OP_FUNDING_SUM) 74 | err = p.Bump(3) 75 | if err != nil { 76 | return LogicError(c, err) 77 | } 78 | p.AppendInstruction(program.OP_MONETARY_ADD) 79 | err = p.Bump(1) 80 | if err != nil { 81 | return LogicError(c, err) 82 | } 83 | err = p.Bump(2) 84 | if err != nil { 85 | return LogicError(c, err) 86 | } 87 | err = p.PushInteger(core.NewNumber(2)) 88 | if err != nil { 89 | return LogicError(c, err) 90 | } 91 | p.AppendInstruction(program.OP_FUNDING_ASSEMBLE) 92 | } 93 | p.AppendInstruction(program.OP_FUNDING_REVERSE) 94 | err = p.Bump(1) 95 | if err != nil { 96 | return LogicError(c, err) 97 | } 98 | p.AppendInstruction(program.OP_TAKE) 99 | p.AppendInstruction(program.OP_FUNDING_REVERSE) 100 | err = p.Bump(1) 101 | if err != nil { 102 | return LogicError(c, err) 103 | } 104 | p.AppendInstruction(program.OP_FUNDING_REVERSE) 105 | cerr := p.VisitKeptOrDestination(c.DestinationInOrder().GetRemainingDest()) 106 | if cerr != nil { 107 | return cerr 108 | } 109 | err = p.Bump(1) 110 | if err != nil { 111 | return LogicError(c, err) 112 | } 113 | err = p.PushInteger(core.NewNumber(2)) 114 | if err != nil { 115 | return LogicError(c, err) 116 | } 117 | p.AppendInstruction(program.OP_FUNDING_ASSEMBLE) 118 | return nil 119 | case *parser.DestAllotmentContext: 120 | err := p.VisitDestinationAllotment(c.DestinationAllotment()) 121 | return err 122 | default: 123 | return InternalError(c) 124 | } 125 | } 126 | 127 | func (p *parseVisitor) VisitKeptOrDestination(c parser.IKeptOrDestinationContext) *CompileError { 128 | switch c := c.(type) { 129 | case *parser.IsKeptContext: 130 | return nil 131 | case *parser.IsDestinationContext: 132 | err := p.VisitDestinationRecursive(c.Destination()) 133 | return err 134 | default: 135 | return InternalError(c) 136 | } 137 | } 138 | 139 | func (p *parseVisitor) VisitDestinationAllotment(c parser.IDestinationAllotmentContext) *CompileError { 140 | p.AppendInstruction(program.OP_FUNDING_SUM) 141 | err := p.VisitAllotment(c, c.GetPortions()) 142 | if err != nil { 143 | return err 144 | } 145 | p.AppendInstruction(program.OP_ALLOC) 146 | err = p.VisitAllocDestination(c.GetDests()) 147 | if err != nil { 148 | return err 149 | } 150 | return nil 151 | } 152 | 153 | func (p *parseVisitor) VisitAllocDestination(dests []parser.IKeptOrDestinationContext) *CompileError { 154 | err := p.Bump(int64(len(dests))) 155 | if err != nil { 156 | return LogicError(dests[0], err) 157 | } 158 | for _, dest := range dests { 159 | err = p.Bump(1) 160 | if err != nil { 161 | return LogicError(dest, err) 162 | } 163 | p.AppendInstruction(program.OP_TAKE) 164 | compErr := p.VisitKeptOrDestination(dest) 165 | if compErr != nil { 166 | return compErr 167 | } 168 | err = p.Bump(1) 169 | if err != nil { 170 | return LogicError(dest, err) 171 | } 172 | err = p.PushInteger(core.NewNumber(2)) 173 | if err != nil { 174 | return LogicError(dest, err) 175 | } 176 | p.AppendInstruction(program.OP_FUNDING_ASSEMBLE) 177 | } 178 | return nil 179 | } 180 | -------------------------------------------------------------------------------- /vm/machine_overdraft_test.go: -------------------------------------------------------------------------------- 1 | package vm 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/formancehq/machine/core" 7 | ) 8 | 9 | func TestOverdraftNotEnough(t *testing.T) { 10 | tc := NewTestCase() 11 | tc.compile(t, `send [GEM 100] ( 12 | source = @foo allowing overdraft up to [GEM 10] 13 | destination = @world 14 | )`) 15 | tc.setBalance("foo", "GEM", 89) 16 | tc.expected = CaseResult{ 17 | Printed: []core.Value{}, 18 | Postings: []Posting{}, 19 | ExitCode: EXIT_FAIL_INSUFFICIENT_FUNDS, 20 | } 21 | test(t, tc) 22 | } 23 | 24 | func TestOverdraftEnough(t *testing.T) { 25 | tc := NewTestCase() 26 | tc.compile(t, `send [GEM 100] ( 27 | source = @foo allowing overdraft up to [GEM 10] 28 | destination = @world 29 | )`) 30 | tc.setBalance("foo", "GEM", 90) 31 | tc.expected = CaseResult{ 32 | Printed: []core.Value{}, 33 | Postings: []Posting{ 34 | { 35 | Asset: "GEM", 36 | Amount: core.NewMonetaryInt(100), 37 | Source: "foo", 38 | Destination: "world", 39 | }, 40 | }, 41 | ExitCode: EXIT_OK, 42 | } 43 | test(t, tc) 44 | } 45 | 46 | func TestOverdraftUnbounded(t *testing.T) { 47 | tc := NewTestCase() 48 | tc.compile(t, `send [GEM 1000] ( 49 | source = @foo allowing unbounded overdraft 50 | destination = @world 51 | )`) 52 | tc.setBalance("foo", "GEM", 90) 53 | tc.expected = CaseResult{ 54 | Printed: []core.Value{}, 55 | Postings: []Posting{ 56 | { 57 | Asset: "GEM", 58 | Amount: core.NewMonetaryInt(1000), 59 | Source: "foo", 60 | Destination: "world", 61 | }, 62 | }, 63 | ExitCode: EXIT_OK, 64 | } 65 | test(t, tc) 66 | } 67 | 68 | func TestOverdraftSourceAllotmentSuccess(t *testing.T) { 69 | tc := NewTestCase() 70 | tc.compile(t, `send [GEM 100] ( 71 | source = { 72 | 50% from @foo allowing overdraft up to [GEM 10] 73 | 50% from { 74 | @bar allowing overdraft up to [GEM 20] 75 | @baz allowing unbounded overdraft 76 | } 77 | } 78 | destination = @world 79 | )`) 80 | tc.setBalance("foo", "GEM", 40) 81 | tc.setBalance("bar", "GEM", 20) 82 | tc.setBalance("baz", "GEM", 0) 83 | tc.expected = CaseResult{ 84 | Printed: []core.Value{}, 85 | Postings: []Posting{ 86 | { 87 | Asset: "GEM", 88 | Amount: core.NewMonetaryInt(50), 89 | Source: "foo", 90 | Destination: "world", 91 | }, 92 | { 93 | Asset: "GEM", 94 | Amount: core.NewMonetaryInt(40), 95 | Source: "bar", 96 | Destination: "world", 97 | }, 98 | { 99 | Asset: "GEM", 100 | Amount: core.NewMonetaryInt(10), 101 | Source: "baz", 102 | Destination: "world", 103 | }, 104 | }, 105 | ExitCode: EXIT_OK, 106 | } 107 | test(t, tc) 108 | } 109 | 110 | func TestOverdraftSourceInOrderSuccess(t *testing.T) { 111 | tc := NewTestCase() 112 | tc.compile(t, `send [GEM 100] ( 113 | source = { 114 | max [GEM 50] from { 115 | @foo allowing overdraft up to [GEM 10] 116 | @bar allowing overdraft up to [GEM 20] 117 | @baz allowing unbounded overdraft 118 | } 119 | @qux allowing unbounded overdraft 120 | } 121 | destination = @world 122 | )`) 123 | tc.setBalance("foo", "GEM", 0) 124 | tc.setBalance("bar", "GEM", 0) 125 | tc.setBalance("baz", "GEM", 0) 126 | tc.setBalance("qux", "GEM", 0) 127 | tc.expected = CaseResult{ 128 | Printed: []core.Value{}, 129 | Postings: []Posting{ 130 | { 131 | Asset: "GEM", 132 | Amount: core.NewMonetaryInt(10), 133 | Source: "foo", 134 | Destination: "world", 135 | }, 136 | { 137 | Asset: "GEM", 138 | Amount: core.NewMonetaryInt(20), 139 | Source: "bar", 140 | Destination: "world", 141 | }, 142 | { 143 | Asset: "GEM", 144 | Amount: core.NewMonetaryInt(20), 145 | Source: "baz", 146 | Destination: "world", 147 | }, 148 | { 149 | Asset: "GEM", 150 | Amount: core.NewMonetaryInt(50), 151 | Source: "qux", 152 | Destination: "world", 153 | }, 154 | }, 155 | ExitCode: EXIT_OK, 156 | } 157 | test(t, tc) 158 | } 159 | 160 | func TestOverdraftBalanceTracking(t *testing.T) { 161 | tc := NewTestCase() 162 | tc.compile(t, `send [GEM 100] ( 163 | source = @foo allowing unbounded overdraft 164 | destination = @world 165 | ) 166 | send [GEM 200] ( 167 | source = @foo allowing overdraft up to [GEM 300] 168 | destination = @world 169 | ) 170 | send [GEM 300] ( 171 | source = @foo allowing unbounded overdraft 172 | destination = @world 173 | ) 174 | `) 175 | tc.setBalance("foo", "GEM", 0) 176 | tc.expected = CaseResult{ 177 | Printed: []core.Value{}, 178 | Postings: []Posting{ 179 | { 180 | Asset: "GEM", 181 | Amount: core.NewMonetaryInt(100), 182 | Source: "foo", 183 | Destination: "world", 184 | }, 185 | { 186 | Asset: "GEM", 187 | Amount: core.NewMonetaryInt(200), 188 | Source: "foo", 189 | Destination: "world", 190 | }, 191 | { 192 | Asset: "GEM", 193 | Amount: core.NewMonetaryInt(300), 194 | Source: "foo", 195 | Destination: "world", 196 | }, 197 | }, 198 | ExitCode: EXIT_OK, 199 | } 200 | test(t, tc) 201 | } 202 | 203 | func TestWorldIsUnbounded(t *testing.T) { 204 | tc := NewTestCase() 205 | tc.compile(t, `send [GEM 100] ( 206 | source = @world 207 | destination = @foo 208 | ) 209 | send [GEM 200] ( 210 | source = @world 211 | destination = @foo 212 | ) 213 | `) 214 | tc.expected = CaseResult{ 215 | Printed: []core.Value{}, 216 | Postings: []Posting{ 217 | { 218 | Asset: "GEM", 219 | Amount: core.NewMonetaryInt(100), 220 | Source: "world", 221 | Destination: "foo", 222 | }, 223 | { 224 | Asset: "GEM", 225 | Amount: core.NewMonetaryInt(200), 226 | Source: "world", 227 | Destination: "foo", 228 | }, 229 | }, 230 | ExitCode: EXIT_OK, 231 | } 232 | test(t, tc) 233 | } 234 | 235 | func TestOverdraftComplexFailure(t *testing.T) { 236 | tc := NewTestCase() 237 | tc.compile(t, `send [GEM 100] ( 238 | source = { 239 | 50% from @foo allowing overdraft up to [GEM 10] 240 | 50% from { 241 | @bar allowing overdraft up to [GEM 20] 242 | @baz 243 | } 244 | } 245 | destination = @world 246 | )`) 247 | tc.setBalance("foo", "GEM", 40) 248 | tc.setBalance("bar", "GEM", 20) 249 | tc.setBalance("baz", "GEM", 0) 250 | tc.expected = CaseResult{ 251 | Printed: []core.Value{}, 252 | Postings: []Posting{}, 253 | ExitCode: EXIT_FAIL_INSUFFICIENT_FUNDS, 254 | } 255 | test(t, tc) 256 | } 257 | 258 | func TestNegativeBalance(t *testing.T) { 259 | tc := NewTestCase() 260 | tc.compile(t, `send [GEM 100] ( 261 | source = @foo 262 | destination = @world 263 | )`) 264 | tc.setBalance("foo", "GEM", -50) 265 | tc.expected = CaseResult{ 266 | Printed: []core.Value{}, 267 | Postings: []Posting{}, 268 | ExitCode: EXIT_FAIL_INSUFFICIENT_FUNDS, 269 | } 270 | test(t, tc) 271 | } 272 | -------------------------------------------------------------------------------- /script/compiler/source.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/formancehq/machine/core" 8 | "github.com/formancehq/machine/script/parser" 9 | "github.com/formancehq/machine/vm/program" 10 | ) 11 | 12 | type FallbackAccount core.Address 13 | 14 | // VisitValueAwareSource returns the resource addresses of all the accounts 15 | func (p *parseVisitor) VisitValueAwareSource(c parser.IValueAwareSourceContext, pushAsset func(), monAddr *core.Address) (map[core.Address]struct{}, *CompileError) { 16 | neededAccounts := map[core.Address]struct{}{} 17 | isAll := monAddr == nil 18 | switch c := c.(type) { 19 | case *parser.SrcContext: 20 | accounts, _, unbounded, compErr := p.VisitSource(c.Source(), pushAsset, isAll) 21 | if compErr != nil { 22 | return nil, compErr 23 | } 24 | for k, v := range accounts { 25 | neededAccounts[k] = v 26 | } 27 | if !isAll { 28 | p.PushAddress(*monAddr) 29 | err := p.TakeFromSource(unbounded) 30 | if err != nil { 31 | return nil, LogicError(c, err) 32 | } 33 | } 34 | case *parser.SrcAllotmentContext: 35 | if isAll { 36 | return nil, LogicError(c, errors.New("cannot take all balance of an allotment source")) 37 | } 38 | p.PushAddress(*monAddr) 39 | p.VisitAllotment(c.SourceAllotment(), c.SourceAllotment().GetPortions()) 40 | p.AppendInstruction(program.OP_ALLOC) 41 | 42 | sources := c.SourceAllotment().GetSources() 43 | n := len(sources) 44 | for i := 0; i < n; i++ { 45 | accounts, _, fallback, compErr := p.VisitSource(sources[i], pushAsset, isAll) 46 | if compErr != nil { 47 | return nil, compErr 48 | } 49 | for k, v := range accounts { 50 | neededAccounts[k] = v 51 | } 52 | err := p.Bump(int64(i + 1)) 53 | if err != nil { 54 | return nil, LogicError(c, err) 55 | } 56 | err = p.TakeFromSource(fallback) 57 | if err != nil { 58 | return nil, LogicError(c, err) 59 | } 60 | } 61 | err := p.PushInteger(core.NewNumber(int64(n))) 62 | if err != nil { 63 | return nil, LogicError(c, err) 64 | } 65 | p.AppendInstruction(program.OP_FUNDING_ASSEMBLE) 66 | } 67 | return neededAccounts, nil 68 | } 69 | 70 | func (p *parseVisitor) TakeFromSource(fallback *FallbackAccount) error { 71 | if fallback == nil { 72 | p.AppendInstruction(program.OP_TAKE) 73 | err := p.Bump(1) 74 | if err != nil { 75 | return err 76 | } 77 | p.AppendInstruction(program.OP_REPAY) 78 | } else { 79 | p.AppendInstruction(program.OP_TAKE_MAX) 80 | err := p.Bump(1) 81 | if err != nil { 82 | return err 83 | } 84 | p.AppendInstruction(program.OP_REPAY) 85 | p.PushAddress(core.Address(*fallback)) 86 | err = p.Bump(2) 87 | if err != nil { 88 | return err 89 | } 90 | p.AppendInstruction(program.OP_TAKE_ALWAYS) 91 | err = p.PushInteger(core.NewNumber(2)) 92 | if err != nil { 93 | return err 94 | } 95 | p.AppendInstruction(program.OP_FUNDING_ASSEMBLE) 96 | } 97 | return nil 98 | } 99 | 100 | // VisitSource returns the resource addresses of all the accounts, 101 | // the addresses of accounts already emptied, 102 | // and possibly a fallback account if the source has an unbounded overdraft allowance or contains @world 103 | func (p *parseVisitor) VisitSource(c parser.ISourceContext, pushAsset func(), isAll bool) (map[core.Address]struct{}, map[core.Address]struct{}, *FallbackAccount, *CompileError) { 104 | neededAccounts := map[core.Address]struct{}{} 105 | emptiedAccounts := map[core.Address]struct{}{} 106 | var fallback *FallbackAccount 107 | switch c := c.(type) { 108 | case *parser.SrcAccountContext: 109 | ty, accAddr, compErr := p.VisitExpr(c.SourceAccount().GetAccount(), true) 110 | if compErr != nil { 111 | return nil, nil, nil, compErr 112 | } 113 | if ty != core.TypeAccount { 114 | return nil, nil, nil, LogicError(c, errors.New("wrong type: expected account or allocation as destination")) 115 | } 116 | if p.isWorld(*accAddr) { 117 | f := FallbackAccount(*accAddr) 118 | fallback = &f 119 | } 120 | 121 | overdraft := c.SourceAccount().GetOverdraft() 122 | if overdraft == nil { 123 | // no overdraft: use zero monetary 124 | pushAsset() 125 | err := p.PushInteger(core.NewNumber(0)) 126 | if err != nil { 127 | return nil, nil, nil, LogicError(c, err) 128 | } 129 | p.AppendInstruction(program.OP_MONETARY_NEW) 130 | p.AppendInstruction(program.OP_TAKE_ALL) 131 | } else { 132 | if p.isWorld(*accAddr) { 133 | return nil, nil, nil, LogicError(c, errors.New("@world is already set to an unbounded overdraft")) 134 | } 135 | switch c := overdraft.(type) { 136 | case *parser.SrcAccountOverdraftSpecificContext: 137 | ty, _, compErr := p.VisitExpr(c.GetSpecific(), true) 138 | if compErr != nil { 139 | return nil, nil, nil, compErr 140 | } 141 | if ty != core.TypeMonetary { 142 | return nil, nil, nil, LogicError(c, errors.New("wrong type: expected monetary")) 143 | } 144 | p.AppendInstruction(program.OP_TAKE_ALL) 145 | case *parser.SrcAccountOverdraftUnboundedContext: 146 | pushAsset() 147 | err := p.PushInteger(core.NewNumber(0)) 148 | if err != nil { 149 | return nil, nil, nil, LogicError(c, err) 150 | } 151 | p.AppendInstruction(program.OP_MONETARY_NEW) 152 | p.AppendInstruction(program.OP_TAKE_ALL) 153 | f := FallbackAccount(*accAddr) 154 | fallback = &f 155 | } 156 | } 157 | neededAccounts[*accAddr] = struct{}{} 158 | emptiedAccounts[*accAddr] = struct{}{} 159 | 160 | if fallback != nil && isAll { 161 | return nil, nil, nil, LogicError(c, errors.New("cannot take all balance of an unbounded source")) 162 | } 163 | 164 | case *parser.SrcMaxedContext: 165 | accounts, _, subsourceFallback, compErr := p.VisitSource(c.SourceMaxed().GetSrc(), pushAsset, false) 166 | if compErr != nil { 167 | return nil, nil, nil, compErr 168 | } 169 | ty, _, compErr := p.VisitExpr(c.SourceMaxed().GetMax(), true) 170 | if compErr != nil { 171 | return nil, nil, nil, compErr 172 | } 173 | if ty != core.TypeMonetary { 174 | return nil, nil, nil, LogicError(c, errors.New("wrong type: expected monetary as max")) 175 | } 176 | for k, v := range accounts { 177 | neededAccounts[k] = v 178 | } 179 | p.AppendInstruction(program.OP_TAKE_MAX) 180 | err := p.Bump(1) 181 | if err != nil { 182 | return nil, nil, nil, LogicError(c, err) 183 | } 184 | p.AppendInstruction(program.OP_REPAY) 185 | if subsourceFallback != nil { 186 | p.PushAddress(core.Address(*subsourceFallback)) 187 | err := p.Bump(2) 188 | if err != nil { 189 | return nil, nil, nil, LogicError(c, err) 190 | } 191 | p.AppendInstruction(program.OP_TAKE_ALL) 192 | err = p.PushInteger(core.NewNumber(2)) 193 | if err != nil { 194 | return nil, nil, nil, LogicError(c, err) 195 | } 196 | p.AppendInstruction(program.OP_FUNDING_ASSEMBLE) 197 | } else { 198 | err := p.Bump(1) 199 | if err != nil { 200 | return nil, nil, nil, LogicError(c, err) 201 | } 202 | p.AppendInstruction(program.OP_DELETE) 203 | } 204 | case *parser.SrcInOrderContext: 205 | sources := c.SourceInOrder().GetSources() 206 | n := len(sources) 207 | for i := 0; i < n; i++ { 208 | accounts, emptied, subsourceFallback, compErr := p.VisitSource(sources[i], pushAsset, isAll) 209 | if compErr != nil { 210 | return nil, nil, nil, compErr 211 | } 212 | fallback = subsourceFallback 213 | if subsourceFallback != nil && i != n-1 { 214 | return nil, nil, nil, LogicError(c, errors.New("an unbounded subsource can only be in last position")) 215 | } 216 | for k, v := range accounts { 217 | neededAccounts[k] = v 218 | } 219 | for k, v := range emptied { 220 | if _, ok := emptiedAccounts[k]; ok { 221 | return nil, nil, nil, LogicError(sources[i], fmt.Errorf("%v is already empty at this stage", p.resources[k])) 222 | } 223 | emptiedAccounts[k] = v 224 | } 225 | } 226 | err := p.PushInteger(core.NewNumber(int64(n))) 227 | if err != nil { 228 | return nil, nil, nil, LogicError(c, err) 229 | } 230 | p.AppendInstruction(program.OP_FUNDING_ASSEMBLE) 231 | } 232 | return neededAccounts, emptiedAccounts, fallback, nil 233 | } 234 | -------------------------------------------------------------------------------- /script/parser/NumScript.interp: -------------------------------------------------------------------------------- 1 | token literal names: 2 | null 3 | '*' 4 | 'allowing overdraft up to' 5 | 'allowing unbounded overdraft' 6 | ',' 7 | null 8 | null 9 | null 10 | null 11 | 'vars' 12 | 'meta' 13 | 'set_tx_meta' 14 | 'set_account_meta' 15 | 'print' 16 | 'fail' 17 | 'send' 18 | 'source' 19 | 'from' 20 | 'max' 21 | 'destination' 22 | 'to' 23 | 'allocate' 24 | '+' 25 | '-' 26 | '(' 27 | ')' 28 | '[' 29 | ']' 30 | '{' 31 | '}' 32 | '=' 33 | 'account' 34 | 'number' 35 | 'monetary' 36 | 'portion' 37 | 'string' 38 | null 39 | null 40 | 'remaining' 41 | 'kept' 42 | 'balance' 43 | null 44 | '%' 45 | null 46 | null 47 | null 48 | 49 | token symbolic names: 50 | null 51 | null 52 | null 53 | null 54 | null 55 | NEWLINE 56 | WHITESPACE 57 | MULTILINE_COMMENT 58 | LINE_COMMENT 59 | VARS 60 | META 61 | SET_TX_META 62 | SET_ACCOUNT_META 63 | PRINT 64 | FAIL 65 | SEND 66 | SOURCE 67 | FROM 68 | MAX 69 | DESTINATION 70 | TO 71 | ALLOCATE 72 | OP_ADD 73 | OP_SUB 74 | LPAREN 75 | RPAREN 76 | LBRACK 77 | RBRACK 78 | LBRACE 79 | RBRACE 80 | EQ 81 | TY_ACCOUNT 82 | TY_NUMBER 83 | TY_MONETARY 84 | TY_PORTION 85 | TY_STRING 86 | STRING 87 | PORTION 88 | REMAINING 89 | KEPT 90 | BALANCE 91 | NUMBER 92 | PERCENT 93 | VARIABLE_NAME 94 | ACCOUNT 95 | ASSET 96 | 97 | rule names: 98 | monetary 99 | monetaryAll 100 | literal 101 | variable 102 | expression 103 | allotmentPortion 104 | destinationInOrder 105 | destinationAllotment 106 | keptOrDestination 107 | destination 108 | sourceAccountOverdraft 109 | sourceAccount 110 | sourceInOrder 111 | sourceMaxed 112 | source 113 | sourceAllotment 114 | valueAwareSource 115 | statement 116 | type_ 117 | origin 118 | varDecl 119 | varListDecl 120 | script 121 | 122 | 123 | atn: 124 | [4, 1, 45, 284, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 63, 8, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 3, 4, 70, 8, 4, 1, 4, 1, 4, 1, 4, 5, 4, 75, 8, 4, 10, 4, 12, 4, 78, 9, 4, 1, 5, 1, 5, 1, 5, 3, 5, 83, 8, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 4, 6, 92, 8, 6, 11, 6, 12, 6, 93, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 4, 7, 107, 8, 7, 11, 7, 12, 7, 108, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 3, 8, 116, 8, 8, 1, 9, 1, 9, 1, 9, 3, 9, 121, 8, 9, 1, 10, 1, 10, 1, 10, 3, 10, 126, 8, 10, 1, 11, 1, 11, 3, 11, 130, 8, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 4, 12, 137, 8, 12, 11, 12, 12, 12, 138, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 3, 14, 151, 8, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 4, 15, 160, 8, 15, 11, 15, 12, 15, 161, 1, 15, 1, 15, 1, 16, 1, 16, 3, 16, 168, 8, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 3, 17, 192, 8, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 3, 17, 212, 8, 17, 1, 17, 1, 17, 1, 17, 3, 17, 217, 8, 17, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 3, 19, 235, 8, 19, 1, 20, 1, 20, 1, 20, 1, 20, 3, 20, 241, 8, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 4, 21, 248, 8, 21, 11, 21, 12, 21, 249, 4, 21, 252, 8, 21, 11, 21, 12, 21, 253, 1, 21, 1, 21, 1, 21, 1, 22, 5, 22, 260, 8, 22, 10, 22, 12, 22, 263, 9, 22, 1, 22, 3, 22, 266, 8, 22, 1, 22, 1, 22, 1, 22, 5, 22, 271, 8, 22, 10, 22, 12, 22, 274, 9, 22, 1, 22, 5, 22, 277, 8, 22, 10, 22, 12, 22, 280, 9, 22, 1, 22, 1, 22, 1, 22, 0, 1, 8, 23, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 0, 2, 1, 0, 22, 23, 1, 0, 31, 35, 295, 0, 46, 1, 0, 0, 0, 2, 51, 1, 0, 0, 0, 4, 62, 1, 0, 0, 0, 6, 64, 1, 0, 0, 0, 8, 69, 1, 0, 0, 0, 10, 82, 1, 0, 0, 0, 12, 84, 1, 0, 0, 0, 14, 100, 1, 0, 0, 0, 16, 115, 1, 0, 0, 0, 18, 120, 1, 0, 0, 0, 20, 125, 1, 0, 0, 0, 22, 127, 1, 0, 0, 0, 24, 131, 1, 0, 0, 0, 26, 142, 1, 0, 0, 0, 28, 150, 1, 0, 0, 0, 30, 152, 1, 0, 0, 0, 32, 167, 1, 0, 0, 0, 34, 216, 1, 0, 0, 0, 36, 218, 1, 0, 0, 0, 38, 234, 1, 0, 0, 0, 40, 236, 1, 0, 0, 0, 42, 242, 1, 0, 0, 0, 44, 261, 1, 0, 0, 0, 46, 47, 5, 26, 0, 0, 47, 48, 5, 45, 0, 0, 48, 49, 5, 41, 0, 0, 49, 50, 5, 27, 0, 0, 50, 1, 1, 0, 0, 0, 51, 52, 5, 26, 0, 0, 52, 53, 5, 45, 0, 0, 53, 54, 5, 1, 0, 0, 54, 55, 5, 27, 0, 0, 55, 3, 1, 0, 0, 0, 56, 63, 5, 44, 0, 0, 57, 63, 5, 45, 0, 0, 58, 63, 5, 41, 0, 0, 59, 63, 5, 36, 0, 0, 60, 63, 5, 37, 0, 0, 61, 63, 3, 0, 0, 0, 62, 56, 1, 0, 0, 0, 62, 57, 1, 0, 0, 0, 62, 58, 1, 0, 0, 0, 62, 59, 1, 0, 0, 0, 62, 60, 1, 0, 0, 0, 62, 61, 1, 0, 0, 0, 63, 5, 1, 0, 0, 0, 64, 65, 5, 43, 0, 0, 65, 7, 1, 0, 0, 0, 66, 67, 6, 4, -1, 0, 67, 70, 3, 4, 2, 0, 68, 70, 3, 6, 3, 0, 69, 66, 1, 0, 0, 0, 69, 68, 1, 0, 0, 0, 70, 76, 1, 0, 0, 0, 71, 72, 10, 3, 0, 0, 72, 73, 7, 0, 0, 0, 73, 75, 3, 8, 4, 4, 74, 71, 1, 0, 0, 0, 75, 78, 1, 0, 0, 0, 76, 74, 1, 0, 0, 0, 76, 77, 1, 0, 0, 0, 77, 9, 1, 0, 0, 0, 78, 76, 1, 0, 0, 0, 79, 83, 5, 37, 0, 0, 80, 83, 3, 6, 3, 0, 81, 83, 5, 38, 0, 0, 82, 79, 1, 0, 0, 0, 82, 80, 1, 0, 0, 0, 82, 81, 1, 0, 0, 0, 83, 11, 1, 0, 0, 0, 84, 85, 5, 28, 0, 0, 85, 91, 5, 5, 0, 0, 86, 87, 5, 18, 0, 0, 87, 88, 3, 8, 4, 0, 88, 89, 3, 16, 8, 0, 89, 90, 5, 5, 0, 0, 90, 92, 1, 0, 0, 0, 91, 86, 1, 0, 0, 0, 92, 93, 1, 0, 0, 0, 93, 91, 1, 0, 0, 0, 93, 94, 1, 0, 0, 0, 94, 95, 1, 0, 0, 0, 95, 96, 5, 38, 0, 0, 96, 97, 3, 16, 8, 0, 97, 98, 5, 5, 0, 0, 98, 99, 5, 29, 0, 0, 99, 13, 1, 0, 0, 0, 100, 101, 5, 28, 0, 0, 101, 106, 5, 5, 0, 0, 102, 103, 3, 10, 5, 0, 103, 104, 3, 16, 8, 0, 104, 105, 5, 5, 0, 0, 105, 107, 1, 0, 0, 0, 106, 102, 1, 0, 0, 0, 107, 108, 1, 0, 0, 0, 108, 106, 1, 0, 0, 0, 108, 109, 1, 0, 0, 0, 109, 110, 1, 0, 0, 0, 110, 111, 5, 29, 0, 0, 111, 15, 1, 0, 0, 0, 112, 113, 5, 20, 0, 0, 113, 116, 3, 18, 9, 0, 114, 116, 5, 39, 0, 0, 115, 112, 1, 0, 0, 0, 115, 114, 1, 0, 0, 0, 116, 17, 1, 0, 0, 0, 117, 121, 3, 8, 4, 0, 118, 121, 3, 12, 6, 0, 119, 121, 3, 14, 7, 0, 120, 117, 1, 0, 0, 0, 120, 118, 1, 0, 0, 0, 120, 119, 1, 0, 0, 0, 121, 19, 1, 0, 0, 0, 122, 123, 5, 2, 0, 0, 123, 126, 3, 8, 4, 0, 124, 126, 5, 3, 0, 0, 125, 122, 1, 0, 0, 0, 125, 124, 1, 0, 0, 0, 126, 21, 1, 0, 0, 0, 127, 129, 3, 8, 4, 0, 128, 130, 3, 20, 10, 0, 129, 128, 1, 0, 0, 0, 129, 130, 1, 0, 0, 0, 130, 23, 1, 0, 0, 0, 131, 132, 5, 28, 0, 0, 132, 136, 5, 5, 0, 0, 133, 134, 3, 28, 14, 0, 134, 135, 5, 5, 0, 0, 135, 137, 1, 0, 0, 0, 136, 133, 1, 0, 0, 0, 137, 138, 1, 0, 0, 0, 138, 136, 1, 0, 0, 0, 138, 139, 1, 0, 0, 0, 139, 140, 1, 0, 0, 0, 140, 141, 5, 29, 0, 0, 141, 25, 1, 0, 0, 0, 142, 143, 5, 18, 0, 0, 143, 144, 3, 8, 4, 0, 144, 145, 5, 17, 0, 0, 145, 146, 3, 28, 14, 0, 146, 27, 1, 0, 0, 0, 147, 151, 3, 22, 11, 0, 148, 151, 3, 26, 13, 0, 149, 151, 3, 24, 12, 0, 150, 147, 1, 0, 0, 0, 150, 148, 1, 0, 0, 0, 150, 149, 1, 0, 0, 0, 151, 29, 1, 0, 0, 0, 152, 153, 5, 28, 0, 0, 153, 159, 5, 5, 0, 0, 154, 155, 3, 10, 5, 0, 155, 156, 5, 17, 0, 0, 156, 157, 3, 28, 14, 0, 157, 158, 5, 5, 0, 0, 158, 160, 1, 0, 0, 0, 159, 154, 1, 0, 0, 0, 160, 161, 1, 0, 0, 0, 161, 159, 1, 0, 0, 0, 161, 162, 1, 0, 0, 0, 162, 163, 1, 0, 0, 0, 163, 164, 5, 29, 0, 0, 164, 31, 1, 0, 0, 0, 165, 168, 3, 28, 14, 0, 166, 168, 3, 30, 15, 0, 167, 165, 1, 0, 0, 0, 167, 166, 1, 0, 0, 0, 168, 33, 1, 0, 0, 0, 169, 170, 5, 13, 0, 0, 170, 217, 3, 8, 4, 0, 171, 172, 5, 11, 0, 0, 172, 173, 5, 24, 0, 0, 173, 174, 5, 36, 0, 0, 174, 175, 5, 4, 0, 0, 175, 176, 3, 8, 4, 0, 176, 177, 5, 25, 0, 0, 177, 217, 1, 0, 0, 0, 178, 179, 5, 12, 0, 0, 179, 180, 5, 24, 0, 0, 180, 181, 3, 8, 4, 0, 181, 182, 5, 4, 0, 0, 182, 183, 5, 36, 0, 0, 183, 184, 5, 4, 0, 0, 184, 185, 3, 8, 4, 0, 185, 186, 5, 25, 0, 0, 186, 217, 1, 0, 0, 0, 187, 217, 5, 14, 0, 0, 188, 191, 5, 15, 0, 0, 189, 192, 3, 8, 4, 0, 190, 192, 3, 2, 1, 0, 191, 189, 1, 0, 0, 0, 191, 190, 1, 0, 0, 0, 192, 193, 1, 0, 0, 0, 193, 194, 5, 24, 0, 0, 194, 211, 5, 5, 0, 0, 195, 196, 5, 16, 0, 0, 196, 197, 5, 30, 0, 0, 197, 198, 3, 32, 16, 0, 198, 199, 5, 5, 0, 0, 199, 200, 5, 19, 0, 0, 200, 201, 5, 30, 0, 0, 201, 202, 3, 18, 9, 0, 202, 212, 1, 0, 0, 0, 203, 204, 5, 19, 0, 0, 204, 205, 5, 30, 0, 0, 205, 206, 3, 18, 9, 0, 206, 207, 5, 5, 0, 0, 207, 208, 5, 16, 0, 0, 208, 209, 5, 30, 0, 0, 209, 210, 3, 32, 16, 0, 210, 212, 1, 0, 0, 0, 211, 195, 1, 0, 0, 0, 211, 203, 1, 0, 0, 0, 212, 213, 1, 0, 0, 0, 213, 214, 5, 5, 0, 0, 214, 215, 5, 25, 0, 0, 215, 217, 1, 0, 0, 0, 216, 169, 1, 0, 0, 0, 216, 171, 1, 0, 0, 0, 216, 178, 1, 0, 0, 0, 216, 187, 1, 0, 0, 0, 216, 188, 1, 0, 0, 0, 217, 35, 1, 0, 0, 0, 218, 219, 7, 1, 0, 0, 219, 37, 1, 0, 0, 0, 220, 221, 5, 10, 0, 0, 221, 222, 5, 24, 0, 0, 222, 223, 3, 8, 4, 0, 223, 224, 5, 4, 0, 0, 224, 225, 5, 36, 0, 0, 225, 226, 5, 25, 0, 0, 226, 235, 1, 0, 0, 0, 227, 228, 5, 40, 0, 0, 228, 229, 5, 24, 0, 0, 229, 230, 3, 8, 4, 0, 230, 231, 5, 4, 0, 0, 231, 232, 5, 45, 0, 0, 232, 233, 5, 25, 0, 0, 233, 235, 1, 0, 0, 0, 234, 220, 1, 0, 0, 0, 234, 227, 1, 0, 0, 0, 235, 39, 1, 0, 0, 0, 236, 237, 3, 36, 18, 0, 237, 240, 3, 6, 3, 0, 238, 239, 5, 30, 0, 0, 239, 241, 3, 38, 19, 0, 240, 238, 1, 0, 0, 0, 240, 241, 1, 0, 0, 0, 241, 41, 1, 0, 0, 0, 242, 243, 5, 9, 0, 0, 243, 244, 5, 28, 0, 0, 244, 251, 5, 5, 0, 0, 245, 247, 3, 40, 20, 0, 246, 248, 5, 5, 0, 0, 247, 246, 1, 0, 0, 0, 248, 249, 1, 0, 0, 0, 249, 247, 1, 0, 0, 0, 249, 250, 1, 0, 0, 0, 250, 252, 1, 0, 0, 0, 251, 245, 1, 0, 0, 0, 252, 253, 1, 0, 0, 0, 253, 251, 1, 0, 0, 0, 253, 254, 1, 0, 0, 0, 254, 255, 1, 0, 0, 0, 255, 256, 5, 29, 0, 0, 256, 257, 5, 5, 0, 0, 257, 43, 1, 0, 0, 0, 258, 260, 5, 5, 0, 0, 259, 258, 1, 0, 0, 0, 260, 263, 1, 0, 0, 0, 261, 259, 1, 0, 0, 0, 261, 262, 1, 0, 0, 0, 262, 265, 1, 0, 0, 0, 263, 261, 1, 0, 0, 0, 264, 266, 3, 42, 21, 0, 265, 264, 1, 0, 0, 0, 265, 266, 1, 0, 0, 0, 266, 267, 1, 0, 0, 0, 267, 272, 3, 34, 17, 0, 268, 269, 5, 5, 0, 0, 269, 271, 3, 34, 17, 0, 270, 268, 1, 0, 0, 0, 271, 274, 1, 0, 0, 0, 272, 270, 1, 0, 0, 0, 272, 273, 1, 0, 0, 0, 273, 278, 1, 0, 0, 0, 274, 272, 1, 0, 0, 0, 275, 277, 5, 5, 0, 0, 276, 275, 1, 0, 0, 0, 277, 280, 1, 0, 0, 0, 278, 276, 1, 0, 0, 0, 278, 279, 1, 0, 0, 0, 279, 281, 1, 0, 0, 0, 280, 278, 1, 0, 0, 0, 281, 282, 5, 0, 0, 1, 282, 45, 1, 0, 0, 0, 25, 62, 69, 76, 82, 93, 108, 115, 120, 125, 129, 138, 150, 161, 167, 191, 211, 216, 234, 240, 249, 253, 261, 265, 272, 278] 125 | -------------------------------------------------------------------------------- /script/parser/numscript_listener.go: -------------------------------------------------------------------------------- 1 | // Code generated from NumScript.g4 by ANTLR 4.10.1. DO NOT EDIT. 2 | 3 | package parser // NumScript 4 | 5 | import "github.com/antlr/antlr4/runtime/Go/antlr" 6 | 7 | // NumScriptListener is a complete listener for a parse tree produced by NumScriptParser. 8 | type NumScriptListener interface { 9 | antlr.ParseTreeListener 10 | 11 | // EnterMonetary is called when entering the monetary production. 12 | EnterMonetary(c *MonetaryContext) 13 | 14 | // EnterMonetaryAll is called when entering the monetaryAll production. 15 | EnterMonetaryAll(c *MonetaryAllContext) 16 | 17 | // EnterLitAccount is called when entering the LitAccount production. 18 | EnterLitAccount(c *LitAccountContext) 19 | 20 | // EnterLitAsset is called when entering the LitAsset production. 21 | EnterLitAsset(c *LitAssetContext) 22 | 23 | // EnterLitNumber is called when entering the LitNumber production. 24 | EnterLitNumber(c *LitNumberContext) 25 | 26 | // EnterLitString is called when entering the LitString production. 27 | EnterLitString(c *LitStringContext) 28 | 29 | // EnterLitPortion is called when entering the LitPortion production. 30 | EnterLitPortion(c *LitPortionContext) 31 | 32 | // EnterLitMonetary is called when entering the LitMonetary production. 33 | EnterLitMonetary(c *LitMonetaryContext) 34 | 35 | // EnterVariable is called when entering the variable production. 36 | EnterVariable(c *VariableContext) 37 | 38 | // EnterExprAddSub is called when entering the ExprAddSub production. 39 | EnterExprAddSub(c *ExprAddSubContext) 40 | 41 | // EnterExprLiteral is called when entering the ExprLiteral production. 42 | EnterExprLiteral(c *ExprLiteralContext) 43 | 44 | // EnterExprVariable is called when entering the ExprVariable production. 45 | EnterExprVariable(c *ExprVariableContext) 46 | 47 | // EnterAllotmentPortionConst is called when entering the allotmentPortionConst production. 48 | EnterAllotmentPortionConst(c *AllotmentPortionConstContext) 49 | 50 | // EnterAllotmentPortionVar is called when entering the allotmentPortionVar production. 51 | EnterAllotmentPortionVar(c *AllotmentPortionVarContext) 52 | 53 | // EnterAllotmentPortionRemaining is called when entering the allotmentPortionRemaining production. 54 | EnterAllotmentPortionRemaining(c *AllotmentPortionRemainingContext) 55 | 56 | // EnterDestinationInOrder is called when entering the destinationInOrder production. 57 | EnterDestinationInOrder(c *DestinationInOrderContext) 58 | 59 | // EnterDestinationAllotment is called when entering the destinationAllotment production. 60 | EnterDestinationAllotment(c *DestinationAllotmentContext) 61 | 62 | // EnterIsDestination is called when entering the isDestination production. 63 | EnterIsDestination(c *IsDestinationContext) 64 | 65 | // EnterIsKept is called when entering the isKept production. 66 | EnterIsKept(c *IsKeptContext) 67 | 68 | // EnterDestAccount is called when entering the DestAccount production. 69 | EnterDestAccount(c *DestAccountContext) 70 | 71 | // EnterDestInOrder is called when entering the DestInOrder production. 72 | EnterDestInOrder(c *DestInOrderContext) 73 | 74 | // EnterDestAllotment is called when entering the DestAllotment production. 75 | EnterDestAllotment(c *DestAllotmentContext) 76 | 77 | // EnterSrcAccountOverdraftSpecific is called when entering the SrcAccountOverdraftSpecific production. 78 | EnterSrcAccountOverdraftSpecific(c *SrcAccountOverdraftSpecificContext) 79 | 80 | // EnterSrcAccountOverdraftUnbounded is called when entering the SrcAccountOverdraftUnbounded production. 81 | EnterSrcAccountOverdraftUnbounded(c *SrcAccountOverdraftUnboundedContext) 82 | 83 | // EnterSourceAccount is called when entering the sourceAccount production. 84 | EnterSourceAccount(c *SourceAccountContext) 85 | 86 | // EnterSourceInOrder is called when entering the sourceInOrder production. 87 | EnterSourceInOrder(c *SourceInOrderContext) 88 | 89 | // EnterSourceMaxed is called when entering the sourceMaxed production. 90 | EnterSourceMaxed(c *SourceMaxedContext) 91 | 92 | // EnterSrcAccount is called when entering the SrcAccount production. 93 | EnterSrcAccount(c *SrcAccountContext) 94 | 95 | // EnterSrcMaxed is called when entering the SrcMaxed production. 96 | EnterSrcMaxed(c *SrcMaxedContext) 97 | 98 | // EnterSrcInOrder is called when entering the SrcInOrder production. 99 | EnterSrcInOrder(c *SrcInOrderContext) 100 | 101 | // EnterSourceAllotment is called when entering the sourceAllotment production. 102 | EnterSourceAllotment(c *SourceAllotmentContext) 103 | 104 | // EnterSrc is called when entering the Src production. 105 | EnterSrc(c *SrcContext) 106 | 107 | // EnterSrcAllotment is called when entering the SrcAllotment production. 108 | EnterSrcAllotment(c *SrcAllotmentContext) 109 | 110 | // EnterPrint is called when entering the Print production. 111 | EnterPrint(c *PrintContext) 112 | 113 | // EnterSetTxMeta is called when entering the SetTxMeta production. 114 | EnterSetTxMeta(c *SetTxMetaContext) 115 | 116 | // EnterSetAccountMeta is called when entering the SetAccountMeta production. 117 | EnterSetAccountMeta(c *SetAccountMetaContext) 118 | 119 | // EnterFail is called when entering the Fail production. 120 | EnterFail(c *FailContext) 121 | 122 | // EnterSend is called when entering the Send production. 123 | EnterSend(c *SendContext) 124 | 125 | // EnterType_ is called when entering the type_ production. 126 | EnterType_(c *Type_Context) 127 | 128 | // EnterOriginAccountMeta is called when entering the OriginAccountMeta production. 129 | EnterOriginAccountMeta(c *OriginAccountMetaContext) 130 | 131 | // EnterOriginAccountBalance is called when entering the OriginAccountBalance production. 132 | EnterOriginAccountBalance(c *OriginAccountBalanceContext) 133 | 134 | // EnterVarDecl is called when entering the varDecl production. 135 | EnterVarDecl(c *VarDeclContext) 136 | 137 | // EnterVarListDecl is called when entering the varListDecl production. 138 | EnterVarListDecl(c *VarListDeclContext) 139 | 140 | // EnterScript is called when entering the script production. 141 | EnterScript(c *ScriptContext) 142 | 143 | // ExitMonetary is called when exiting the monetary production. 144 | ExitMonetary(c *MonetaryContext) 145 | 146 | // ExitMonetaryAll is called when exiting the monetaryAll production. 147 | ExitMonetaryAll(c *MonetaryAllContext) 148 | 149 | // ExitLitAccount is called when exiting the LitAccount production. 150 | ExitLitAccount(c *LitAccountContext) 151 | 152 | // ExitLitAsset is called when exiting the LitAsset production. 153 | ExitLitAsset(c *LitAssetContext) 154 | 155 | // ExitLitNumber is called when exiting the LitNumber production. 156 | ExitLitNumber(c *LitNumberContext) 157 | 158 | // ExitLitString is called when exiting the LitString production. 159 | ExitLitString(c *LitStringContext) 160 | 161 | // ExitLitPortion is called when exiting the LitPortion production. 162 | ExitLitPortion(c *LitPortionContext) 163 | 164 | // ExitLitMonetary is called when exiting the LitMonetary production. 165 | ExitLitMonetary(c *LitMonetaryContext) 166 | 167 | // ExitVariable is called when exiting the variable production. 168 | ExitVariable(c *VariableContext) 169 | 170 | // ExitExprAddSub is called when exiting the ExprAddSub production. 171 | ExitExprAddSub(c *ExprAddSubContext) 172 | 173 | // ExitExprLiteral is called when exiting the ExprLiteral production. 174 | ExitExprLiteral(c *ExprLiteralContext) 175 | 176 | // ExitExprVariable is called when exiting the ExprVariable production. 177 | ExitExprVariable(c *ExprVariableContext) 178 | 179 | // ExitAllotmentPortionConst is called when exiting the allotmentPortionConst production. 180 | ExitAllotmentPortionConst(c *AllotmentPortionConstContext) 181 | 182 | // ExitAllotmentPortionVar is called when exiting the allotmentPortionVar production. 183 | ExitAllotmentPortionVar(c *AllotmentPortionVarContext) 184 | 185 | // ExitAllotmentPortionRemaining is called when exiting the allotmentPortionRemaining production. 186 | ExitAllotmentPortionRemaining(c *AllotmentPortionRemainingContext) 187 | 188 | // ExitDestinationInOrder is called when exiting the destinationInOrder production. 189 | ExitDestinationInOrder(c *DestinationInOrderContext) 190 | 191 | // ExitDestinationAllotment is called when exiting the destinationAllotment production. 192 | ExitDestinationAllotment(c *DestinationAllotmentContext) 193 | 194 | // ExitIsDestination is called when exiting the isDestination production. 195 | ExitIsDestination(c *IsDestinationContext) 196 | 197 | // ExitIsKept is called when exiting the isKept production. 198 | ExitIsKept(c *IsKeptContext) 199 | 200 | // ExitDestAccount is called when exiting the DestAccount production. 201 | ExitDestAccount(c *DestAccountContext) 202 | 203 | // ExitDestInOrder is called when exiting the DestInOrder production. 204 | ExitDestInOrder(c *DestInOrderContext) 205 | 206 | // ExitDestAllotment is called when exiting the DestAllotment production. 207 | ExitDestAllotment(c *DestAllotmentContext) 208 | 209 | // ExitSrcAccountOverdraftSpecific is called when exiting the SrcAccountOverdraftSpecific production. 210 | ExitSrcAccountOverdraftSpecific(c *SrcAccountOverdraftSpecificContext) 211 | 212 | // ExitSrcAccountOverdraftUnbounded is called when exiting the SrcAccountOverdraftUnbounded production. 213 | ExitSrcAccountOverdraftUnbounded(c *SrcAccountOverdraftUnboundedContext) 214 | 215 | // ExitSourceAccount is called when exiting the sourceAccount production. 216 | ExitSourceAccount(c *SourceAccountContext) 217 | 218 | // ExitSourceInOrder is called when exiting the sourceInOrder production. 219 | ExitSourceInOrder(c *SourceInOrderContext) 220 | 221 | // ExitSourceMaxed is called when exiting the sourceMaxed production. 222 | ExitSourceMaxed(c *SourceMaxedContext) 223 | 224 | // ExitSrcAccount is called when exiting the SrcAccount production. 225 | ExitSrcAccount(c *SrcAccountContext) 226 | 227 | // ExitSrcMaxed is called when exiting the SrcMaxed production. 228 | ExitSrcMaxed(c *SrcMaxedContext) 229 | 230 | // ExitSrcInOrder is called when exiting the SrcInOrder production. 231 | ExitSrcInOrder(c *SrcInOrderContext) 232 | 233 | // ExitSourceAllotment is called when exiting the sourceAllotment production. 234 | ExitSourceAllotment(c *SourceAllotmentContext) 235 | 236 | // ExitSrc is called when exiting the Src production. 237 | ExitSrc(c *SrcContext) 238 | 239 | // ExitSrcAllotment is called when exiting the SrcAllotment production. 240 | ExitSrcAllotment(c *SrcAllotmentContext) 241 | 242 | // ExitPrint is called when exiting the Print production. 243 | ExitPrint(c *PrintContext) 244 | 245 | // ExitSetTxMeta is called when exiting the SetTxMeta production. 246 | ExitSetTxMeta(c *SetTxMetaContext) 247 | 248 | // ExitSetAccountMeta is called when exiting the SetAccountMeta production. 249 | ExitSetAccountMeta(c *SetAccountMetaContext) 250 | 251 | // ExitFail is called when exiting the Fail production. 252 | ExitFail(c *FailContext) 253 | 254 | // ExitSend is called when exiting the Send production. 255 | ExitSend(c *SendContext) 256 | 257 | // ExitType_ is called when exiting the type_ production. 258 | ExitType_(c *Type_Context) 259 | 260 | // ExitOriginAccountMeta is called when exiting the OriginAccountMeta production. 261 | ExitOriginAccountMeta(c *OriginAccountMetaContext) 262 | 263 | // ExitOriginAccountBalance is called when exiting the OriginAccountBalance production. 264 | ExitOriginAccountBalance(c *OriginAccountBalanceContext) 265 | 266 | // ExitVarDecl is called when exiting the varDecl production. 267 | ExitVarDecl(c *VarDeclContext) 268 | 269 | // ExitVarListDecl is called when exiting the varListDecl production. 270 | ExitVarListDecl(c *VarListDeclContext) 271 | 272 | // ExitScript is called when exiting the script production. 273 | ExitScript(c *ScriptContext) 274 | } 275 | -------------------------------------------------------------------------------- /script/parser/numscript_base_listener.go: -------------------------------------------------------------------------------- 1 | // Code generated from NumScript.g4 by ANTLR 4.10.1. DO NOT EDIT. 2 | 3 | package parser // NumScript 4 | 5 | import "github.com/antlr/antlr4/runtime/Go/antlr" 6 | 7 | // BaseNumScriptListener is a complete listener for a parse tree produced by NumScriptParser. 8 | type BaseNumScriptListener struct{} 9 | 10 | var _ NumScriptListener = &BaseNumScriptListener{} 11 | 12 | // VisitTerminal is called when a terminal node is visited. 13 | func (s *BaseNumScriptListener) VisitTerminal(node antlr.TerminalNode) {} 14 | 15 | // VisitErrorNode is called when an error node is visited. 16 | func (s *BaseNumScriptListener) VisitErrorNode(node antlr.ErrorNode) {} 17 | 18 | // EnterEveryRule is called when any rule is entered. 19 | func (s *BaseNumScriptListener) EnterEveryRule(ctx antlr.ParserRuleContext) {} 20 | 21 | // ExitEveryRule is called when any rule is exited. 22 | func (s *BaseNumScriptListener) ExitEveryRule(ctx antlr.ParserRuleContext) {} 23 | 24 | // EnterMonetary is called when production monetary is entered. 25 | func (s *BaseNumScriptListener) EnterMonetary(ctx *MonetaryContext) {} 26 | 27 | // ExitMonetary is called when production monetary is exited. 28 | func (s *BaseNumScriptListener) ExitMonetary(ctx *MonetaryContext) {} 29 | 30 | // EnterMonetaryAll is called when production monetaryAll is entered. 31 | func (s *BaseNumScriptListener) EnterMonetaryAll(ctx *MonetaryAllContext) {} 32 | 33 | // ExitMonetaryAll is called when production monetaryAll is exited. 34 | func (s *BaseNumScriptListener) ExitMonetaryAll(ctx *MonetaryAllContext) {} 35 | 36 | // EnterLitAccount is called when production LitAccount is entered. 37 | func (s *BaseNumScriptListener) EnterLitAccount(ctx *LitAccountContext) {} 38 | 39 | // ExitLitAccount is called when production LitAccount is exited. 40 | func (s *BaseNumScriptListener) ExitLitAccount(ctx *LitAccountContext) {} 41 | 42 | // EnterLitAsset is called when production LitAsset is entered. 43 | func (s *BaseNumScriptListener) EnterLitAsset(ctx *LitAssetContext) {} 44 | 45 | // ExitLitAsset is called when production LitAsset is exited. 46 | func (s *BaseNumScriptListener) ExitLitAsset(ctx *LitAssetContext) {} 47 | 48 | // EnterLitNumber is called when production LitNumber is entered. 49 | func (s *BaseNumScriptListener) EnterLitNumber(ctx *LitNumberContext) {} 50 | 51 | // ExitLitNumber is called when production LitNumber is exited. 52 | func (s *BaseNumScriptListener) ExitLitNumber(ctx *LitNumberContext) {} 53 | 54 | // EnterLitString is called when production LitString is entered. 55 | func (s *BaseNumScriptListener) EnterLitString(ctx *LitStringContext) {} 56 | 57 | // ExitLitString is called when production LitString is exited. 58 | func (s *BaseNumScriptListener) ExitLitString(ctx *LitStringContext) {} 59 | 60 | // EnterLitPortion is called when production LitPortion is entered. 61 | func (s *BaseNumScriptListener) EnterLitPortion(ctx *LitPortionContext) {} 62 | 63 | // ExitLitPortion is called when production LitPortion is exited. 64 | func (s *BaseNumScriptListener) ExitLitPortion(ctx *LitPortionContext) {} 65 | 66 | // EnterLitMonetary is called when production LitMonetary is entered. 67 | func (s *BaseNumScriptListener) EnterLitMonetary(ctx *LitMonetaryContext) {} 68 | 69 | // ExitLitMonetary is called when production LitMonetary is exited. 70 | func (s *BaseNumScriptListener) ExitLitMonetary(ctx *LitMonetaryContext) {} 71 | 72 | // EnterVariable is called when production variable is entered. 73 | func (s *BaseNumScriptListener) EnterVariable(ctx *VariableContext) {} 74 | 75 | // ExitVariable is called when production variable is exited. 76 | func (s *BaseNumScriptListener) ExitVariable(ctx *VariableContext) {} 77 | 78 | // EnterExprAddSub is called when production ExprAddSub is entered. 79 | func (s *BaseNumScriptListener) EnterExprAddSub(ctx *ExprAddSubContext) {} 80 | 81 | // ExitExprAddSub is called when production ExprAddSub is exited. 82 | func (s *BaseNumScriptListener) ExitExprAddSub(ctx *ExprAddSubContext) {} 83 | 84 | // EnterExprLiteral is called when production ExprLiteral is entered. 85 | func (s *BaseNumScriptListener) EnterExprLiteral(ctx *ExprLiteralContext) {} 86 | 87 | // ExitExprLiteral is called when production ExprLiteral is exited. 88 | func (s *BaseNumScriptListener) ExitExprLiteral(ctx *ExprLiteralContext) {} 89 | 90 | // EnterExprVariable is called when production ExprVariable is entered. 91 | func (s *BaseNumScriptListener) EnterExprVariable(ctx *ExprVariableContext) {} 92 | 93 | // ExitExprVariable is called when production ExprVariable is exited. 94 | func (s *BaseNumScriptListener) ExitExprVariable(ctx *ExprVariableContext) {} 95 | 96 | // EnterAllotmentPortionConst is called when production allotmentPortionConst is entered. 97 | func (s *BaseNumScriptListener) EnterAllotmentPortionConst(ctx *AllotmentPortionConstContext) {} 98 | 99 | // ExitAllotmentPortionConst is called when production allotmentPortionConst is exited. 100 | func (s *BaseNumScriptListener) ExitAllotmentPortionConst(ctx *AllotmentPortionConstContext) {} 101 | 102 | // EnterAllotmentPortionVar is called when production allotmentPortionVar is entered. 103 | func (s *BaseNumScriptListener) EnterAllotmentPortionVar(ctx *AllotmentPortionVarContext) {} 104 | 105 | // ExitAllotmentPortionVar is called when production allotmentPortionVar is exited. 106 | func (s *BaseNumScriptListener) ExitAllotmentPortionVar(ctx *AllotmentPortionVarContext) {} 107 | 108 | // EnterAllotmentPortionRemaining is called when production allotmentPortionRemaining is entered. 109 | func (s *BaseNumScriptListener) EnterAllotmentPortionRemaining(ctx *AllotmentPortionRemainingContext) { 110 | } 111 | 112 | // ExitAllotmentPortionRemaining is called when production allotmentPortionRemaining is exited. 113 | func (s *BaseNumScriptListener) ExitAllotmentPortionRemaining(ctx *AllotmentPortionRemainingContext) { 114 | } 115 | 116 | // EnterDestinationInOrder is called when production destinationInOrder is entered. 117 | func (s *BaseNumScriptListener) EnterDestinationInOrder(ctx *DestinationInOrderContext) {} 118 | 119 | // ExitDestinationInOrder is called when production destinationInOrder is exited. 120 | func (s *BaseNumScriptListener) ExitDestinationInOrder(ctx *DestinationInOrderContext) {} 121 | 122 | // EnterDestinationAllotment is called when production destinationAllotment is entered. 123 | func (s *BaseNumScriptListener) EnterDestinationAllotment(ctx *DestinationAllotmentContext) {} 124 | 125 | // ExitDestinationAllotment is called when production destinationAllotment is exited. 126 | func (s *BaseNumScriptListener) ExitDestinationAllotment(ctx *DestinationAllotmentContext) {} 127 | 128 | // EnterIsDestination is called when production isDestination is entered. 129 | func (s *BaseNumScriptListener) EnterIsDestination(ctx *IsDestinationContext) {} 130 | 131 | // ExitIsDestination is called when production isDestination is exited. 132 | func (s *BaseNumScriptListener) ExitIsDestination(ctx *IsDestinationContext) {} 133 | 134 | // EnterIsKept is called when production isKept is entered. 135 | func (s *BaseNumScriptListener) EnterIsKept(ctx *IsKeptContext) {} 136 | 137 | // ExitIsKept is called when production isKept is exited. 138 | func (s *BaseNumScriptListener) ExitIsKept(ctx *IsKeptContext) {} 139 | 140 | // EnterDestAccount is called when production DestAccount is entered. 141 | func (s *BaseNumScriptListener) EnterDestAccount(ctx *DestAccountContext) {} 142 | 143 | // ExitDestAccount is called when production DestAccount is exited. 144 | func (s *BaseNumScriptListener) ExitDestAccount(ctx *DestAccountContext) {} 145 | 146 | // EnterDestInOrder is called when production DestInOrder is entered. 147 | func (s *BaseNumScriptListener) EnterDestInOrder(ctx *DestInOrderContext) {} 148 | 149 | // ExitDestInOrder is called when production DestInOrder is exited. 150 | func (s *BaseNumScriptListener) ExitDestInOrder(ctx *DestInOrderContext) {} 151 | 152 | // EnterDestAllotment is called when production DestAllotment is entered. 153 | func (s *BaseNumScriptListener) EnterDestAllotment(ctx *DestAllotmentContext) {} 154 | 155 | // ExitDestAllotment is called when production DestAllotment is exited. 156 | func (s *BaseNumScriptListener) ExitDestAllotment(ctx *DestAllotmentContext) {} 157 | 158 | // EnterSrcAccountOverdraftSpecific is called when production SrcAccountOverdraftSpecific is entered. 159 | func (s *BaseNumScriptListener) EnterSrcAccountOverdraftSpecific(ctx *SrcAccountOverdraftSpecificContext) { 160 | } 161 | 162 | // ExitSrcAccountOverdraftSpecific is called when production SrcAccountOverdraftSpecific is exited. 163 | func (s *BaseNumScriptListener) ExitSrcAccountOverdraftSpecific(ctx *SrcAccountOverdraftSpecificContext) { 164 | } 165 | 166 | // EnterSrcAccountOverdraftUnbounded is called when production SrcAccountOverdraftUnbounded is entered. 167 | func (s *BaseNumScriptListener) EnterSrcAccountOverdraftUnbounded(ctx *SrcAccountOverdraftUnboundedContext) { 168 | } 169 | 170 | // ExitSrcAccountOverdraftUnbounded is called when production SrcAccountOverdraftUnbounded is exited. 171 | func (s *BaseNumScriptListener) ExitSrcAccountOverdraftUnbounded(ctx *SrcAccountOverdraftUnboundedContext) { 172 | } 173 | 174 | // EnterSourceAccount is called when production sourceAccount is entered. 175 | func (s *BaseNumScriptListener) EnterSourceAccount(ctx *SourceAccountContext) {} 176 | 177 | // ExitSourceAccount is called when production sourceAccount is exited. 178 | func (s *BaseNumScriptListener) ExitSourceAccount(ctx *SourceAccountContext) {} 179 | 180 | // EnterSourceInOrder is called when production sourceInOrder is entered. 181 | func (s *BaseNumScriptListener) EnterSourceInOrder(ctx *SourceInOrderContext) {} 182 | 183 | // ExitSourceInOrder is called when production sourceInOrder is exited. 184 | func (s *BaseNumScriptListener) ExitSourceInOrder(ctx *SourceInOrderContext) {} 185 | 186 | // EnterSourceMaxed is called when production sourceMaxed is entered. 187 | func (s *BaseNumScriptListener) EnterSourceMaxed(ctx *SourceMaxedContext) {} 188 | 189 | // ExitSourceMaxed is called when production sourceMaxed is exited. 190 | func (s *BaseNumScriptListener) ExitSourceMaxed(ctx *SourceMaxedContext) {} 191 | 192 | // EnterSrcAccount is called when production SrcAccount is entered. 193 | func (s *BaseNumScriptListener) EnterSrcAccount(ctx *SrcAccountContext) {} 194 | 195 | // ExitSrcAccount is called when production SrcAccount is exited. 196 | func (s *BaseNumScriptListener) ExitSrcAccount(ctx *SrcAccountContext) {} 197 | 198 | // EnterSrcMaxed is called when production SrcMaxed is entered. 199 | func (s *BaseNumScriptListener) EnterSrcMaxed(ctx *SrcMaxedContext) {} 200 | 201 | // ExitSrcMaxed is called when production SrcMaxed is exited. 202 | func (s *BaseNumScriptListener) ExitSrcMaxed(ctx *SrcMaxedContext) {} 203 | 204 | // EnterSrcInOrder is called when production SrcInOrder is entered. 205 | func (s *BaseNumScriptListener) EnterSrcInOrder(ctx *SrcInOrderContext) {} 206 | 207 | // ExitSrcInOrder is called when production SrcInOrder is exited. 208 | func (s *BaseNumScriptListener) ExitSrcInOrder(ctx *SrcInOrderContext) {} 209 | 210 | // EnterSourceAllotment is called when production sourceAllotment is entered. 211 | func (s *BaseNumScriptListener) EnterSourceAllotment(ctx *SourceAllotmentContext) {} 212 | 213 | // ExitSourceAllotment is called when production sourceAllotment is exited. 214 | func (s *BaseNumScriptListener) ExitSourceAllotment(ctx *SourceAllotmentContext) {} 215 | 216 | // EnterSrc is called when production Src is entered. 217 | func (s *BaseNumScriptListener) EnterSrc(ctx *SrcContext) {} 218 | 219 | // ExitSrc is called when production Src is exited. 220 | func (s *BaseNumScriptListener) ExitSrc(ctx *SrcContext) {} 221 | 222 | // EnterSrcAllotment is called when production SrcAllotment is entered. 223 | func (s *BaseNumScriptListener) EnterSrcAllotment(ctx *SrcAllotmentContext) {} 224 | 225 | // ExitSrcAllotment is called when production SrcAllotment is exited. 226 | func (s *BaseNumScriptListener) ExitSrcAllotment(ctx *SrcAllotmentContext) {} 227 | 228 | // EnterPrint is called when production Print is entered. 229 | func (s *BaseNumScriptListener) EnterPrint(ctx *PrintContext) {} 230 | 231 | // ExitPrint is called when production Print is exited. 232 | func (s *BaseNumScriptListener) ExitPrint(ctx *PrintContext) {} 233 | 234 | // EnterSetTxMeta is called when production SetTxMeta is entered. 235 | func (s *BaseNumScriptListener) EnterSetTxMeta(ctx *SetTxMetaContext) {} 236 | 237 | // ExitSetTxMeta is called when production SetTxMeta is exited. 238 | func (s *BaseNumScriptListener) ExitSetTxMeta(ctx *SetTxMetaContext) {} 239 | 240 | // EnterSetAccountMeta is called when production SetAccountMeta is entered. 241 | func (s *BaseNumScriptListener) EnterSetAccountMeta(ctx *SetAccountMetaContext) {} 242 | 243 | // ExitSetAccountMeta is called when production SetAccountMeta is exited. 244 | func (s *BaseNumScriptListener) ExitSetAccountMeta(ctx *SetAccountMetaContext) {} 245 | 246 | // EnterFail is called when production Fail is entered. 247 | func (s *BaseNumScriptListener) EnterFail(ctx *FailContext) {} 248 | 249 | // ExitFail is called when production Fail is exited. 250 | func (s *BaseNumScriptListener) ExitFail(ctx *FailContext) {} 251 | 252 | // EnterSend is called when production Send is entered. 253 | func (s *BaseNumScriptListener) EnterSend(ctx *SendContext) {} 254 | 255 | // ExitSend is called when production Send is exited. 256 | func (s *BaseNumScriptListener) ExitSend(ctx *SendContext) {} 257 | 258 | // EnterType_ is called when production type_ is entered. 259 | func (s *BaseNumScriptListener) EnterType_(ctx *Type_Context) {} 260 | 261 | // ExitType_ is called when production type_ is exited. 262 | func (s *BaseNumScriptListener) ExitType_(ctx *Type_Context) {} 263 | 264 | // EnterOriginAccountMeta is called when production OriginAccountMeta is entered. 265 | func (s *BaseNumScriptListener) EnterOriginAccountMeta(ctx *OriginAccountMetaContext) {} 266 | 267 | // ExitOriginAccountMeta is called when production OriginAccountMeta is exited. 268 | func (s *BaseNumScriptListener) ExitOriginAccountMeta(ctx *OriginAccountMetaContext) {} 269 | 270 | // EnterOriginAccountBalance is called when production OriginAccountBalance is entered. 271 | func (s *BaseNumScriptListener) EnterOriginAccountBalance(ctx *OriginAccountBalanceContext) {} 272 | 273 | // ExitOriginAccountBalance is called when production OriginAccountBalance is exited. 274 | func (s *BaseNumScriptListener) ExitOriginAccountBalance(ctx *OriginAccountBalanceContext) {} 275 | 276 | // EnterVarDecl is called when production varDecl is entered. 277 | func (s *BaseNumScriptListener) EnterVarDecl(ctx *VarDeclContext) {} 278 | 279 | // ExitVarDecl is called when production varDecl is exited. 280 | func (s *BaseNumScriptListener) ExitVarDecl(ctx *VarDeclContext) {} 281 | 282 | // EnterVarListDecl is called when production varListDecl is entered. 283 | func (s *BaseNumScriptListener) EnterVarListDecl(ctx *VarListDeclContext) {} 284 | 285 | // ExitVarListDecl is called when production varListDecl is exited. 286 | func (s *BaseNumScriptListener) ExitVarListDecl(ctx *VarListDeclContext) {} 287 | 288 | // EnterScript is called when production script is entered. 289 | func (s *BaseNumScriptListener) EnterScript(ctx *ScriptContext) {} 290 | 291 | // ExitScript is called when production script is exited. 292 | func (s *BaseNumScriptListener) ExitScript(ctx *ScriptContext) {} 293 | -------------------------------------------------------------------------------- /script/compiler/compiler.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/antlr/antlr4/runtime/Go/antlr" 8 | "github.com/formancehq/machine/core" 9 | "github.com/formancehq/machine/script/parser" 10 | "github.com/formancehq/machine/vm/program" 11 | "github.com/pkg/errors" 12 | ) 13 | 14 | type parseVisitor struct { 15 | errListener *ErrorListener 16 | instructions []byte 17 | resources []program.Resource // must not exceed 65536 elements 18 | varIdx map[string]core.Address // maps name to resource index 19 | neededBalances map[core.Address]map[core.Address]struct{} // for each account, set of assets needed 20 | } 21 | 22 | // Allocates constants if it hasn't already been, 23 | // and returns its resource address. 24 | func (p *parseVisitor) findConstant(constant program.Constant) (*core.Address, bool) { 25 | for i := 0; i < len(p.resources); i++ { 26 | if c, ok := p.resources[i].(program.Constant); ok { 27 | if core.ValueEquals(c.Inner, constant.Inner) { 28 | addr := core.Address(i) 29 | return &addr, true 30 | } 31 | } 32 | } 33 | return nil, false 34 | } 35 | 36 | func (p *parseVisitor) AllocateResource(res program.Resource) (*core.Address, error) { 37 | if c, ok := res.(program.Constant); ok { 38 | idx, ok := p.findConstant(c) 39 | if ok { 40 | return idx, nil 41 | } 42 | } 43 | if len(p.resources) >= 65536 { 44 | return nil, errors.New("number of unique constants exceeded 65536") 45 | } 46 | p.resources = append(p.resources, res) 47 | addr := core.NewAddress(uint16(len(p.resources) - 1)) 48 | return &addr, nil 49 | } 50 | 51 | func (p *parseVisitor) isWorld(addr core.Address) bool { 52 | idx := int(addr) 53 | if idx < len(p.resources) { 54 | if c, ok := p.resources[idx].(program.Constant); ok { 55 | if acc, ok := c.Inner.(core.Account); ok { 56 | if string(acc) == "world" { 57 | return true 58 | } 59 | } 60 | } 61 | } 62 | return false 63 | } 64 | 65 | func (p *parseVisitor) VisitVariable(c parser.IVariableContext, push bool) (core.Type, *core.Address, *CompileError) { 66 | name := c.GetText()[1:] // strip '$' prefix 67 | if idx, ok := p.varIdx[name]; ok { 68 | res := p.resources[idx] 69 | if push { 70 | p.PushAddress(idx) 71 | } 72 | return res.GetType(), &idx, nil 73 | } else { 74 | return 0, nil, LogicError(c, errors.New("variable not declared")) 75 | } 76 | } 77 | 78 | func (p *parseVisitor) VisitExpr(c parser.IExpressionContext, push bool) (core.Type, *core.Address, *CompileError) { 79 | switch c := c.(type) { 80 | case *parser.ExprAddSubContext: 81 | ty, _, err := p.VisitExpr(c.GetLhs(), push) 82 | if err != nil { 83 | return 0, nil, err 84 | } 85 | if ty != core.TypeNumber { 86 | return 0, nil, LogicError(c, errors.New("tried to do arithmetic with wrong type")) 87 | } 88 | ty, _, err = p.VisitExpr(c.GetRhs(), push) 89 | if err != nil { 90 | return 0, nil, err 91 | } 92 | if ty != core.TypeNumber { 93 | return 0, nil, LogicError(c, errors.New("tried to do arithmetic with wrong type")) 94 | } 95 | if push { 96 | switch c.GetOp().GetTokenType() { 97 | case parser.NumScriptLexerOP_ADD: 98 | p.AppendInstruction(program.OP_IADD) 99 | case parser.NumScriptLexerOP_SUB: 100 | p.AppendInstruction(program.OP_ISUB) 101 | } 102 | } 103 | return core.TypeNumber, nil, nil 104 | case *parser.ExprLiteralContext: 105 | ty, addr, err := p.VisitLit(c.GetLit(), push) 106 | if err != nil { 107 | return 0, nil, err 108 | } 109 | return ty, addr, nil 110 | case *parser.ExprVariableContext: 111 | ty, addr, err := p.VisitVariable(c.GetVar_(), push) 112 | return ty, addr, err 113 | default: 114 | return 0, nil, InternalError(c) 115 | } 116 | } 117 | 118 | // pushes a value from a literal onto the stack 119 | func (p *parseVisitor) VisitLit(c parser.ILiteralContext, push bool) (core.Type, *core.Address, *CompileError) { 120 | switch c := c.(type) { 121 | case *parser.LitAccountContext: 122 | account := core.Account(c.GetText()[1:]) 123 | addr, err := p.AllocateResource(program.Constant{Inner: account}) 124 | if err != nil { 125 | return 0, nil, LogicError(c, err) 126 | } 127 | if push { 128 | p.PushAddress(*addr) 129 | } 130 | return core.TypeAccount, addr, nil 131 | case *parser.LitAssetContext: 132 | asset := core.Asset(c.GetText()) 133 | addr, err := p.AllocateResource(program.Constant{Inner: asset}) 134 | if err != nil { 135 | return 0, nil, LogicError(c, err) 136 | } 137 | if push { 138 | p.PushAddress(*addr) 139 | } 140 | return core.TypeAsset, addr, nil 141 | case *parser.LitNumberContext: 142 | number, err := core.ParseNumber(c.GetText()) 143 | if err != nil { 144 | return 0, nil, LogicError(c, err) 145 | } 146 | if push { 147 | err := p.PushInteger(number) 148 | if err != nil { 149 | return 0, nil, LogicError(c, err) 150 | } 151 | } 152 | return core.TypeNumber, nil, nil 153 | case *parser.LitStringContext: 154 | addr, err := p.AllocateResource(program.Constant{ 155 | Inner: core.String(strings.Trim(c.GetText(), `"`)), 156 | }) 157 | if err != nil { 158 | return 0, nil, LogicError(c, err) 159 | } 160 | if push { 161 | p.PushAddress(*addr) 162 | } 163 | return core.TypeString, addr, nil 164 | case *parser.LitPortionContext: 165 | portion, err := core.ParsePortionSpecific(c.GetText()) 166 | if err != nil { 167 | return 0, nil, LogicError(c, err) 168 | } 169 | addr, err := p.AllocateResource(program.Constant{Inner: *portion}) 170 | if err != nil { 171 | return 0, nil, LogicError(c, err) 172 | } 173 | if push { 174 | p.PushAddress(*addr) 175 | } 176 | return core.TypePortion, addr, nil 177 | case *parser.LitMonetaryContext: 178 | asset := c.Monetary().GetAsset().GetText() 179 | amt, err := core.ParseMonetaryInt(c.Monetary().GetAmt().GetText()) 180 | if err != nil { 181 | return 0, nil, LogicError(c, err) 182 | } 183 | monetary := core.Monetary{ 184 | Asset: core.Asset(asset), 185 | Amount: amt, 186 | } 187 | addr, err := p.AllocateResource(program.Constant{Inner: monetary}) 188 | if err != nil { 189 | return 0, nil, LogicError(c, err) 190 | } 191 | if push { 192 | p.PushAddress(*addr) 193 | } 194 | return core.TypeMonetary, addr, nil 195 | default: 196 | return 0, nil, InternalError(c) 197 | } 198 | } 199 | 200 | // send statement 201 | func (p *parseVisitor) VisitSend(c *parser.SendContext) *CompileError { 202 | var assetAddr core.Address 203 | var neededAccounts map[core.Address]struct{} 204 | if mon := c.GetMonAll(); mon != nil { 205 | asset := core.Asset(mon.GetAsset().GetText()) 206 | addr, err := p.AllocateResource(program.Constant{Inner: asset}) 207 | if err != nil { 208 | return LogicError(c, err) 209 | } 210 | assetAddr = *addr 211 | accounts, compErr := p.VisitValueAwareSource(c.GetSrc(), func() { 212 | p.PushAddress(*addr) 213 | }, nil) 214 | if compErr != nil { 215 | return compErr 216 | } 217 | neededAccounts = accounts 218 | } 219 | if mon := c.GetMon(); mon != nil { 220 | ty, monAddr, err := p.VisitExpr(c.GetMon(), false) 221 | if err != nil { 222 | return err 223 | } 224 | if ty != core.TypeMonetary { 225 | return LogicError(c, errors.New("wrong type for monetary value")) 226 | } 227 | assetAddr = *monAddr 228 | accounts, err := p.VisitValueAwareSource(c.GetSrc(), func() { 229 | p.PushAddress(*monAddr) 230 | p.AppendInstruction(program.OP_ASSET) 231 | }, monAddr) 232 | if err != nil { 233 | return err 234 | } 235 | neededAccounts = accounts 236 | } 237 | // add source accounts to the needed balances 238 | for acc := range neededAccounts { 239 | if b, ok := p.neededBalances[acc]; ok { 240 | b[assetAddr] = struct{}{} 241 | } else { 242 | p.neededBalances[acc] = map[core.Address]struct{}{ 243 | assetAddr: {}, 244 | } 245 | } 246 | } 247 | err := p.VisitDestination(c.GetDest()) 248 | if err != nil { 249 | return err 250 | } 251 | return nil 252 | } 253 | 254 | // set_tx_meta statement 255 | func (p *parseVisitor) VisitSetTxMeta(ctx *parser.SetTxMetaContext) *CompileError { 256 | _, _, compErr := p.VisitExpr(ctx.GetValue(), true) 257 | if compErr != nil { 258 | return compErr 259 | } 260 | 261 | keyAddr, err := p.AllocateResource(program.Constant{ 262 | Inner: core.String(strings.Trim(ctx.GetKey().GetText(), `"`)), 263 | }) 264 | if err != nil { 265 | return LogicError(ctx, err) 266 | } 267 | p.PushAddress(*keyAddr) 268 | 269 | p.AppendInstruction(program.OP_TX_META) 270 | 271 | return nil 272 | } 273 | 274 | // set_account_meta statement 275 | func (p *parseVisitor) VisitSetAccountMeta(ctx *parser.SetAccountMetaContext) *CompileError { 276 | _, _, compErr := p.VisitExpr(ctx.GetValue(), true) 277 | if compErr != nil { 278 | return compErr 279 | } 280 | 281 | keyAddr, err := p.AllocateResource(program.Constant{ 282 | Inner: core.String(strings.Trim(ctx.GetKey().GetText(), `"`)), 283 | }) 284 | if err != nil { 285 | return LogicError(ctx, err) 286 | } 287 | p.PushAddress(*keyAddr) 288 | 289 | ty, accAddr, compErr := p.VisitExpr(ctx.GetAcc(), false) 290 | if compErr != nil { 291 | return compErr 292 | } 293 | if ty != core.TypeAccount { 294 | return LogicError(ctx, fmt.Errorf( 295 | "variable is of type %s, and should be of type account", ty)) 296 | } 297 | p.PushAddress(*accAddr) 298 | 299 | p.AppendInstruction(program.OP_ACCOUNT_META) 300 | 301 | return nil 302 | } 303 | 304 | // print statement 305 | func (p *parseVisitor) VisitPrint(ctx *parser.PrintContext) *CompileError { 306 | _, _, err := p.VisitExpr(ctx.GetExpr(), true) 307 | if err != nil { 308 | return err 309 | } 310 | 311 | p.AppendInstruction(program.OP_PRINT) 312 | 313 | return nil 314 | } 315 | 316 | // vars declaration block 317 | func (p *parseVisitor) VisitVars(c *parser.VarListDeclContext) *CompileError { 318 | if len(c.GetV()) > 32768 { 319 | return LogicError(c, fmt.Errorf("number of variables exceeded %v", 32768)) 320 | } 321 | 322 | for _, v := range c.GetV() { 323 | name := v.GetName().GetText()[1:] 324 | if _, ok := p.varIdx[name]; ok { 325 | return LogicError(c, errors.New("duplicate variable")) 326 | } 327 | var ty core.Type 328 | switch v.GetTy().GetText() { 329 | case "account": 330 | ty = core.TypeAccount 331 | case "number": 332 | ty = core.TypeNumber 333 | case "string": 334 | ty = core.TypeString 335 | case "monetary": 336 | ty = core.TypeMonetary 337 | case "portion": 338 | ty = core.TypePortion 339 | default: 340 | return InternalError(c) 341 | } 342 | 343 | var addr *core.Address 344 | var err error 345 | if v.GetOrig() == nil { 346 | addr, err = p.AllocateResource(program.Variable{Typ: ty, Name: name}) 347 | if err != nil { 348 | return &CompileError{ 349 | Msg: errors.Wrap(err, 350 | "allocating variable resource").Error(), 351 | } 352 | } 353 | p.varIdx[name] = *addr 354 | continue 355 | } 356 | 357 | switch c := v.GetOrig().(type) { 358 | case *parser.OriginAccountMetaContext: 359 | srcTy, src, compErr := p.VisitExpr(c.GetAccount(), false) 360 | if compErr != nil { 361 | return compErr 362 | } 363 | if srcTy != core.TypeAccount { 364 | return LogicError(c, errors.New( 365 | "variable type should be 'account' to pull account metadata")) 366 | } 367 | key := strings.Trim(c.GetKey().GetText(), `"`) 368 | addr, err = p.AllocateResource(program.VariableAccountMetadata{ 369 | Typ: ty, 370 | Name: name, 371 | Account: *src, 372 | Key: key, 373 | }) 374 | case *parser.OriginAccountBalanceContext: 375 | if ty != core.TypeMonetary { 376 | return LogicError(c, fmt.Errorf( 377 | "variable type should be 'monetary' to pull account balance")) 378 | } 379 | accTy, accAddr, compErr := p.VisitExpr(c.GetAccount(), false) 380 | if compErr != nil { 381 | return compErr 382 | } 383 | if accTy != core.TypeAccount { 384 | return LogicError(c, errors.New( 385 | "variable type should be 'account' to pull account balance")) 386 | } 387 | 388 | asset := core.Asset(c.GetAsset().GetText()) 389 | addr, err = p.AllocateResource(program.VariableAccountBalance{ 390 | Name: name, 391 | Account: *accAddr, 392 | Asset: string(asset), 393 | }) 394 | if err != nil { 395 | return LogicError(c, err) 396 | } 397 | } 398 | if err != nil { 399 | return LogicError(c, err) 400 | } 401 | 402 | p.varIdx[name] = *addr 403 | } 404 | 405 | return nil 406 | } 407 | 408 | func (p *parseVisitor) VisitScript(c parser.IScriptContext) *CompileError { 409 | switch c := c.(type) { 410 | case *parser.ScriptContext: 411 | vars := c.GetVars() 412 | if vars != nil { 413 | switch c := vars.(type) { 414 | case *parser.VarListDeclContext: 415 | if err := p.VisitVars(c); err != nil { 416 | return err 417 | } 418 | default: 419 | return InternalError(c) 420 | } 421 | } 422 | 423 | for _, stmt := range c.GetStmts() { 424 | var err *CompileError 425 | switch c := stmt.(type) { 426 | case *parser.PrintContext: 427 | err = p.VisitPrint(c) 428 | case *parser.FailContext: 429 | p.AppendInstruction(program.OP_FAIL) 430 | case *parser.SendContext: 431 | err = p.VisitSend(c) 432 | case *parser.SetTxMetaContext: 433 | err = p.VisitSetTxMeta(c) 434 | case *parser.SetAccountMetaContext: 435 | err = p.VisitSetAccountMeta(c) 436 | default: 437 | return InternalError(c) 438 | } 439 | if err != nil { 440 | return err 441 | } 442 | } 443 | default: 444 | return InternalError(c) 445 | } 446 | 447 | return nil 448 | } 449 | 450 | type CompileArtifacts struct { 451 | Source string 452 | Tokens []antlr.Token 453 | Errors []CompileError 454 | Program *program.Program 455 | } 456 | 457 | func CompileFull(input string) CompileArtifacts { 458 | artifacts := CompileArtifacts{ 459 | Source: input, 460 | } 461 | 462 | errListener := &ErrorListener{} 463 | 464 | is := antlr.NewInputStream(input) 465 | lexer := parser.NewNumScriptLexer(is) 466 | lexer.RemoveErrorListeners() 467 | lexer.AddErrorListener(errListener) 468 | 469 | stream := antlr.NewCommonTokenStream(lexer, antlr.LexerDefaultTokenChannel) 470 | p := parser.NewNumScriptParser(stream) 471 | p.RemoveErrorListeners() 472 | p.AddErrorListener(errListener) 473 | 474 | p.BuildParseTrees = true 475 | 476 | tree := p.Script() 477 | 478 | artifacts.Tokens = stream.GetAllTokens() 479 | artifacts.Errors = append(artifacts.Errors, errListener.Errors...) 480 | 481 | if len(errListener.Errors) != 0 { 482 | return artifacts 483 | } 484 | 485 | visitor := parseVisitor{ 486 | errListener: errListener, 487 | instructions: make([]byte, 0), 488 | resources: make([]program.Resource, 0), 489 | varIdx: make(map[string]core.Address), 490 | neededBalances: make(map[core.Address]map[core.Address]struct{}), 491 | } 492 | 493 | err := visitor.VisitScript(tree) 494 | if err != nil { 495 | artifacts.Errors = append(artifacts.Errors, *err) 496 | return artifacts 497 | } 498 | 499 | artifacts.Program = &program.Program{ 500 | Instructions: visitor.instructions, 501 | Resources: visitor.resources, 502 | NeededBalances: visitor.neededBalances, 503 | } 504 | 505 | return artifacts 506 | } 507 | 508 | func Compile(input string) (*program.Program, error) { 509 | artifacts := CompileFull(input) 510 | if len(artifacts.Errors) > 0 { 511 | err := CompileErrorList{ 512 | Errors: artifacts.Errors, 513 | Source: artifacts.Source, 514 | } 515 | return nil, &err 516 | } 517 | 518 | return artifacts.Program, nil 519 | } 520 | -------------------------------------------------------------------------------- /script/parser/NumScriptLexer.interp: -------------------------------------------------------------------------------- 1 | token literal names: 2 | null 3 | '*' 4 | 'allowing overdraft up to' 5 | 'allowing unbounded overdraft' 6 | ',' 7 | null 8 | null 9 | null 10 | null 11 | 'vars' 12 | 'meta' 13 | 'set_tx_meta' 14 | 'set_account_meta' 15 | 'print' 16 | 'fail' 17 | 'send' 18 | 'source' 19 | 'from' 20 | 'max' 21 | 'destination' 22 | 'to' 23 | 'allocate' 24 | '+' 25 | '-' 26 | '(' 27 | ')' 28 | '[' 29 | ']' 30 | '{' 31 | '}' 32 | '=' 33 | 'account' 34 | 'number' 35 | 'monetary' 36 | 'portion' 37 | 'string' 38 | null 39 | null 40 | 'remaining' 41 | 'kept' 42 | 'balance' 43 | null 44 | '%' 45 | null 46 | null 47 | null 48 | 49 | token symbolic names: 50 | null 51 | null 52 | null 53 | null 54 | null 55 | NEWLINE 56 | WHITESPACE 57 | MULTILINE_COMMENT 58 | LINE_COMMENT 59 | VARS 60 | META 61 | SET_TX_META 62 | SET_ACCOUNT_META 63 | PRINT 64 | FAIL 65 | SEND 66 | SOURCE 67 | FROM 68 | MAX 69 | DESTINATION 70 | TO 71 | ALLOCATE 72 | OP_ADD 73 | OP_SUB 74 | LPAREN 75 | RPAREN 76 | LBRACK 77 | RBRACK 78 | LBRACE 79 | RBRACE 80 | EQ 81 | TY_ACCOUNT 82 | TY_NUMBER 83 | TY_MONETARY 84 | TY_PORTION 85 | TY_STRING 86 | STRING 87 | PORTION 88 | REMAINING 89 | KEPT 90 | BALANCE 91 | NUMBER 92 | PERCENT 93 | VARIABLE_NAME 94 | ACCOUNT 95 | ASSET 96 | 97 | rule names: 98 | T__0 99 | T__1 100 | T__2 101 | T__3 102 | NEWLINE 103 | WHITESPACE 104 | MULTILINE_COMMENT 105 | LINE_COMMENT 106 | VARS 107 | META 108 | SET_TX_META 109 | SET_ACCOUNT_META 110 | PRINT 111 | FAIL 112 | SEND 113 | SOURCE 114 | FROM 115 | MAX 116 | DESTINATION 117 | TO 118 | ALLOCATE 119 | OP_ADD 120 | OP_SUB 121 | LPAREN 122 | RPAREN 123 | LBRACK 124 | RBRACK 125 | LBRACE 126 | RBRACE 127 | EQ 128 | TY_ACCOUNT 129 | TY_NUMBER 130 | TY_MONETARY 131 | TY_PORTION 132 | TY_STRING 133 | STRING 134 | PORTION 135 | REMAINING 136 | KEPT 137 | BALANCE 138 | NUMBER 139 | PERCENT 140 | VARIABLE_NAME 141 | ACCOUNT 142 | ASSET 143 | 144 | channel names: 145 | DEFAULT_TOKEN_CHANNEL 146 | HIDDEN 147 | 148 | mode names: 149 | DEFAULT_MODE 150 | 151 | atn: 152 | [4, 0, 45, 442, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 4, 4, 151, 8, 4, 11, 4, 12, 4, 152, 1, 5, 4, 5, 156, 8, 5, 11, 5, 12, 5, 157, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 5, 6, 167, 8, 6, 10, 6, 12, 6, 170, 9, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 5, 7, 181, 8, 7, 10, 7, 12, 7, 184, 9, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, 1, 23, 1, 23, 1, 24, 1, 24, 1, 25, 1, 25, 1, 26, 1, 26, 1, 27, 1, 27, 1, 28, 1, 28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 5, 35, 344, 8, 35, 10, 35, 12, 35, 347, 9, 35, 1, 35, 1, 35, 1, 36, 4, 36, 352, 8, 36, 11, 36, 12, 36, 353, 1, 36, 3, 36, 357, 8, 36, 1, 36, 1, 36, 3, 36, 361, 8, 36, 1, 36, 4, 36, 364, 8, 36, 11, 36, 12, 36, 365, 1, 36, 4, 36, 369, 8, 36, 11, 36, 12, 36, 370, 1, 36, 1, 36, 4, 36, 375, 8, 36, 11, 36, 12, 36, 376, 3, 36, 379, 8, 36, 1, 36, 3, 36, 382, 8, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 4, 40, 408, 8, 40, 11, 40, 12, 40, 409, 1, 41, 1, 41, 1, 42, 1, 42, 4, 42, 416, 8, 42, 11, 42, 12, 42, 417, 1, 42, 5, 42, 421, 8, 42, 10, 42, 12, 42, 424, 9, 42, 1, 43, 1, 43, 4, 43, 428, 8, 43, 11, 43, 12, 43, 429, 1, 43, 5, 43, 433, 8, 43, 10, 43, 12, 43, 436, 9, 43, 1, 44, 4, 44, 439, 8, 44, 11, 44, 12, 44, 440, 2, 168, 182, 0, 45, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, 45, 1, 0, 10, 2, 0, 10, 10, 13, 13, 2, 0, 9, 9, 32, 32, 6, 0, 32, 32, 45, 45, 48, 57, 65, 90, 95, 95, 97, 122, 1, 0, 48, 57, 1, 0, 32, 32, 2, 0, 95, 95, 97, 122, 3, 0, 48, 57, 95, 95, 97, 122, 3, 0, 65, 90, 95, 95, 97, 122, 4, 0, 48, 58, 65, 90, 95, 95, 97, 122, 2, 0, 47, 57, 65, 90, 461, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 1, 91, 1, 0, 0, 0, 3, 93, 1, 0, 0, 0, 5, 118, 1, 0, 0, 0, 7, 147, 1, 0, 0, 0, 9, 150, 1, 0, 0, 0, 11, 155, 1, 0, 0, 0, 13, 161, 1, 0, 0, 0, 15, 176, 1, 0, 0, 0, 17, 189, 1, 0, 0, 0, 19, 194, 1, 0, 0, 0, 21, 199, 1, 0, 0, 0, 23, 211, 1, 0, 0, 0, 25, 228, 1, 0, 0, 0, 27, 234, 1, 0, 0, 0, 29, 239, 1, 0, 0, 0, 31, 244, 1, 0, 0, 0, 33, 251, 1, 0, 0, 0, 35, 256, 1, 0, 0, 0, 37, 260, 1, 0, 0, 0, 39, 272, 1, 0, 0, 0, 41, 275, 1, 0, 0, 0, 43, 284, 1, 0, 0, 0, 45, 286, 1, 0, 0, 0, 47, 288, 1, 0, 0, 0, 49, 290, 1, 0, 0, 0, 51, 292, 1, 0, 0, 0, 53, 294, 1, 0, 0, 0, 55, 296, 1, 0, 0, 0, 57, 298, 1, 0, 0, 0, 59, 300, 1, 0, 0, 0, 61, 302, 1, 0, 0, 0, 63, 310, 1, 0, 0, 0, 65, 317, 1, 0, 0, 0, 67, 326, 1, 0, 0, 0, 69, 334, 1, 0, 0, 0, 71, 341, 1, 0, 0, 0, 73, 381, 1, 0, 0, 0, 75, 383, 1, 0, 0, 0, 77, 393, 1, 0, 0, 0, 79, 398, 1, 0, 0, 0, 81, 407, 1, 0, 0, 0, 83, 411, 1, 0, 0, 0, 85, 413, 1, 0, 0, 0, 87, 425, 1, 0, 0, 0, 89, 438, 1, 0, 0, 0, 91, 92, 5, 42, 0, 0, 92, 2, 1, 0, 0, 0, 93, 94, 5, 97, 0, 0, 94, 95, 5, 108, 0, 0, 95, 96, 5, 108, 0, 0, 96, 97, 5, 111, 0, 0, 97, 98, 5, 119, 0, 0, 98, 99, 5, 105, 0, 0, 99, 100, 5, 110, 0, 0, 100, 101, 5, 103, 0, 0, 101, 102, 5, 32, 0, 0, 102, 103, 5, 111, 0, 0, 103, 104, 5, 118, 0, 0, 104, 105, 5, 101, 0, 0, 105, 106, 5, 114, 0, 0, 106, 107, 5, 100, 0, 0, 107, 108, 5, 114, 0, 0, 108, 109, 5, 97, 0, 0, 109, 110, 5, 102, 0, 0, 110, 111, 5, 116, 0, 0, 111, 112, 5, 32, 0, 0, 112, 113, 5, 117, 0, 0, 113, 114, 5, 112, 0, 0, 114, 115, 5, 32, 0, 0, 115, 116, 5, 116, 0, 0, 116, 117, 5, 111, 0, 0, 117, 4, 1, 0, 0, 0, 118, 119, 5, 97, 0, 0, 119, 120, 5, 108, 0, 0, 120, 121, 5, 108, 0, 0, 121, 122, 5, 111, 0, 0, 122, 123, 5, 119, 0, 0, 123, 124, 5, 105, 0, 0, 124, 125, 5, 110, 0, 0, 125, 126, 5, 103, 0, 0, 126, 127, 5, 32, 0, 0, 127, 128, 5, 117, 0, 0, 128, 129, 5, 110, 0, 0, 129, 130, 5, 98, 0, 0, 130, 131, 5, 111, 0, 0, 131, 132, 5, 117, 0, 0, 132, 133, 5, 110, 0, 0, 133, 134, 5, 100, 0, 0, 134, 135, 5, 101, 0, 0, 135, 136, 5, 100, 0, 0, 136, 137, 5, 32, 0, 0, 137, 138, 5, 111, 0, 0, 138, 139, 5, 118, 0, 0, 139, 140, 5, 101, 0, 0, 140, 141, 5, 114, 0, 0, 141, 142, 5, 100, 0, 0, 142, 143, 5, 114, 0, 0, 143, 144, 5, 97, 0, 0, 144, 145, 5, 102, 0, 0, 145, 146, 5, 116, 0, 0, 146, 6, 1, 0, 0, 0, 147, 148, 5, 44, 0, 0, 148, 8, 1, 0, 0, 0, 149, 151, 7, 0, 0, 0, 150, 149, 1, 0, 0, 0, 151, 152, 1, 0, 0, 0, 152, 150, 1, 0, 0, 0, 152, 153, 1, 0, 0, 0, 153, 10, 1, 0, 0, 0, 154, 156, 7, 1, 0, 0, 155, 154, 1, 0, 0, 0, 156, 157, 1, 0, 0, 0, 157, 155, 1, 0, 0, 0, 157, 158, 1, 0, 0, 0, 158, 159, 1, 0, 0, 0, 159, 160, 6, 5, 0, 0, 160, 12, 1, 0, 0, 0, 161, 162, 5, 47, 0, 0, 162, 163, 5, 42, 0, 0, 163, 168, 1, 0, 0, 0, 164, 167, 3, 13, 6, 0, 165, 167, 9, 0, 0, 0, 166, 164, 1, 0, 0, 0, 166, 165, 1, 0, 0, 0, 167, 170, 1, 0, 0, 0, 168, 169, 1, 0, 0, 0, 168, 166, 1, 0, 0, 0, 169, 171, 1, 0, 0, 0, 170, 168, 1, 0, 0, 0, 171, 172, 5, 42, 0, 0, 172, 173, 5, 47, 0, 0, 173, 174, 1, 0, 0, 0, 174, 175, 6, 6, 0, 0, 175, 14, 1, 0, 0, 0, 176, 177, 5, 47, 0, 0, 177, 178, 5, 47, 0, 0, 178, 182, 1, 0, 0, 0, 179, 181, 9, 0, 0, 0, 180, 179, 1, 0, 0, 0, 181, 184, 1, 0, 0, 0, 182, 183, 1, 0, 0, 0, 182, 180, 1, 0, 0, 0, 183, 185, 1, 0, 0, 0, 184, 182, 1, 0, 0, 0, 185, 186, 3, 9, 4, 0, 186, 187, 1, 0, 0, 0, 187, 188, 6, 7, 0, 0, 188, 16, 1, 0, 0, 0, 189, 190, 5, 118, 0, 0, 190, 191, 5, 97, 0, 0, 191, 192, 5, 114, 0, 0, 192, 193, 5, 115, 0, 0, 193, 18, 1, 0, 0, 0, 194, 195, 5, 109, 0, 0, 195, 196, 5, 101, 0, 0, 196, 197, 5, 116, 0, 0, 197, 198, 5, 97, 0, 0, 198, 20, 1, 0, 0, 0, 199, 200, 5, 115, 0, 0, 200, 201, 5, 101, 0, 0, 201, 202, 5, 116, 0, 0, 202, 203, 5, 95, 0, 0, 203, 204, 5, 116, 0, 0, 204, 205, 5, 120, 0, 0, 205, 206, 5, 95, 0, 0, 206, 207, 5, 109, 0, 0, 207, 208, 5, 101, 0, 0, 208, 209, 5, 116, 0, 0, 209, 210, 5, 97, 0, 0, 210, 22, 1, 0, 0, 0, 211, 212, 5, 115, 0, 0, 212, 213, 5, 101, 0, 0, 213, 214, 5, 116, 0, 0, 214, 215, 5, 95, 0, 0, 215, 216, 5, 97, 0, 0, 216, 217, 5, 99, 0, 0, 217, 218, 5, 99, 0, 0, 218, 219, 5, 111, 0, 0, 219, 220, 5, 117, 0, 0, 220, 221, 5, 110, 0, 0, 221, 222, 5, 116, 0, 0, 222, 223, 5, 95, 0, 0, 223, 224, 5, 109, 0, 0, 224, 225, 5, 101, 0, 0, 225, 226, 5, 116, 0, 0, 226, 227, 5, 97, 0, 0, 227, 24, 1, 0, 0, 0, 228, 229, 5, 112, 0, 0, 229, 230, 5, 114, 0, 0, 230, 231, 5, 105, 0, 0, 231, 232, 5, 110, 0, 0, 232, 233, 5, 116, 0, 0, 233, 26, 1, 0, 0, 0, 234, 235, 5, 102, 0, 0, 235, 236, 5, 97, 0, 0, 236, 237, 5, 105, 0, 0, 237, 238, 5, 108, 0, 0, 238, 28, 1, 0, 0, 0, 239, 240, 5, 115, 0, 0, 240, 241, 5, 101, 0, 0, 241, 242, 5, 110, 0, 0, 242, 243, 5, 100, 0, 0, 243, 30, 1, 0, 0, 0, 244, 245, 5, 115, 0, 0, 245, 246, 5, 111, 0, 0, 246, 247, 5, 117, 0, 0, 247, 248, 5, 114, 0, 0, 248, 249, 5, 99, 0, 0, 249, 250, 5, 101, 0, 0, 250, 32, 1, 0, 0, 0, 251, 252, 5, 102, 0, 0, 252, 253, 5, 114, 0, 0, 253, 254, 5, 111, 0, 0, 254, 255, 5, 109, 0, 0, 255, 34, 1, 0, 0, 0, 256, 257, 5, 109, 0, 0, 257, 258, 5, 97, 0, 0, 258, 259, 5, 120, 0, 0, 259, 36, 1, 0, 0, 0, 260, 261, 5, 100, 0, 0, 261, 262, 5, 101, 0, 0, 262, 263, 5, 115, 0, 0, 263, 264, 5, 116, 0, 0, 264, 265, 5, 105, 0, 0, 265, 266, 5, 110, 0, 0, 266, 267, 5, 97, 0, 0, 267, 268, 5, 116, 0, 0, 268, 269, 5, 105, 0, 0, 269, 270, 5, 111, 0, 0, 270, 271, 5, 110, 0, 0, 271, 38, 1, 0, 0, 0, 272, 273, 5, 116, 0, 0, 273, 274, 5, 111, 0, 0, 274, 40, 1, 0, 0, 0, 275, 276, 5, 97, 0, 0, 276, 277, 5, 108, 0, 0, 277, 278, 5, 108, 0, 0, 278, 279, 5, 111, 0, 0, 279, 280, 5, 99, 0, 0, 280, 281, 5, 97, 0, 0, 281, 282, 5, 116, 0, 0, 282, 283, 5, 101, 0, 0, 283, 42, 1, 0, 0, 0, 284, 285, 5, 43, 0, 0, 285, 44, 1, 0, 0, 0, 286, 287, 5, 45, 0, 0, 287, 46, 1, 0, 0, 0, 288, 289, 5, 40, 0, 0, 289, 48, 1, 0, 0, 0, 290, 291, 5, 41, 0, 0, 291, 50, 1, 0, 0, 0, 292, 293, 5, 91, 0, 0, 293, 52, 1, 0, 0, 0, 294, 295, 5, 93, 0, 0, 295, 54, 1, 0, 0, 0, 296, 297, 5, 123, 0, 0, 297, 56, 1, 0, 0, 0, 298, 299, 5, 125, 0, 0, 299, 58, 1, 0, 0, 0, 300, 301, 5, 61, 0, 0, 301, 60, 1, 0, 0, 0, 302, 303, 5, 97, 0, 0, 303, 304, 5, 99, 0, 0, 304, 305, 5, 99, 0, 0, 305, 306, 5, 111, 0, 0, 306, 307, 5, 117, 0, 0, 307, 308, 5, 110, 0, 0, 308, 309, 5, 116, 0, 0, 309, 62, 1, 0, 0, 0, 310, 311, 5, 110, 0, 0, 311, 312, 5, 117, 0, 0, 312, 313, 5, 109, 0, 0, 313, 314, 5, 98, 0, 0, 314, 315, 5, 101, 0, 0, 315, 316, 5, 114, 0, 0, 316, 64, 1, 0, 0, 0, 317, 318, 5, 109, 0, 0, 318, 319, 5, 111, 0, 0, 319, 320, 5, 110, 0, 0, 320, 321, 5, 101, 0, 0, 321, 322, 5, 116, 0, 0, 322, 323, 5, 97, 0, 0, 323, 324, 5, 114, 0, 0, 324, 325, 5, 121, 0, 0, 325, 66, 1, 0, 0, 0, 326, 327, 5, 112, 0, 0, 327, 328, 5, 111, 0, 0, 328, 329, 5, 114, 0, 0, 329, 330, 5, 116, 0, 0, 330, 331, 5, 105, 0, 0, 331, 332, 5, 111, 0, 0, 332, 333, 5, 110, 0, 0, 333, 68, 1, 0, 0, 0, 334, 335, 5, 115, 0, 0, 335, 336, 5, 116, 0, 0, 336, 337, 5, 114, 0, 0, 337, 338, 5, 105, 0, 0, 338, 339, 5, 110, 0, 0, 339, 340, 5, 103, 0, 0, 340, 70, 1, 0, 0, 0, 341, 345, 5, 34, 0, 0, 342, 344, 7, 2, 0, 0, 343, 342, 1, 0, 0, 0, 344, 347, 1, 0, 0, 0, 345, 343, 1, 0, 0, 0, 345, 346, 1, 0, 0, 0, 346, 348, 1, 0, 0, 0, 347, 345, 1, 0, 0, 0, 348, 349, 5, 34, 0, 0, 349, 72, 1, 0, 0, 0, 350, 352, 7, 3, 0, 0, 351, 350, 1, 0, 0, 0, 352, 353, 1, 0, 0, 0, 353, 351, 1, 0, 0, 0, 353, 354, 1, 0, 0, 0, 354, 356, 1, 0, 0, 0, 355, 357, 7, 4, 0, 0, 356, 355, 1, 0, 0, 0, 356, 357, 1, 0, 0, 0, 357, 358, 1, 0, 0, 0, 358, 360, 5, 47, 0, 0, 359, 361, 7, 4, 0, 0, 360, 359, 1, 0, 0, 0, 360, 361, 1, 0, 0, 0, 361, 363, 1, 0, 0, 0, 362, 364, 7, 3, 0, 0, 363, 362, 1, 0, 0, 0, 364, 365, 1, 0, 0, 0, 365, 363, 1, 0, 0, 0, 365, 366, 1, 0, 0, 0, 366, 382, 1, 0, 0, 0, 367, 369, 7, 3, 0, 0, 368, 367, 1, 0, 0, 0, 369, 370, 1, 0, 0, 0, 370, 368, 1, 0, 0, 0, 370, 371, 1, 0, 0, 0, 371, 378, 1, 0, 0, 0, 372, 374, 5, 46, 0, 0, 373, 375, 7, 3, 0, 0, 374, 373, 1, 0, 0, 0, 375, 376, 1, 0, 0, 0, 376, 374, 1, 0, 0, 0, 376, 377, 1, 0, 0, 0, 377, 379, 1, 0, 0, 0, 378, 372, 1, 0, 0, 0, 378, 379, 1, 0, 0, 0, 379, 380, 1, 0, 0, 0, 380, 382, 5, 37, 0, 0, 381, 351, 1, 0, 0, 0, 381, 368, 1, 0, 0, 0, 382, 74, 1, 0, 0, 0, 383, 384, 5, 114, 0, 0, 384, 385, 5, 101, 0, 0, 385, 386, 5, 109, 0, 0, 386, 387, 5, 97, 0, 0, 387, 388, 5, 105, 0, 0, 388, 389, 5, 110, 0, 0, 389, 390, 5, 105, 0, 0, 390, 391, 5, 110, 0, 0, 391, 392, 5, 103, 0, 0, 392, 76, 1, 0, 0, 0, 393, 394, 5, 107, 0, 0, 394, 395, 5, 101, 0, 0, 395, 396, 5, 112, 0, 0, 396, 397, 5, 116, 0, 0, 397, 78, 1, 0, 0, 0, 398, 399, 5, 98, 0, 0, 399, 400, 5, 97, 0, 0, 400, 401, 5, 108, 0, 0, 401, 402, 5, 97, 0, 0, 402, 403, 5, 110, 0, 0, 403, 404, 5, 99, 0, 0, 404, 405, 5, 101, 0, 0, 405, 80, 1, 0, 0, 0, 406, 408, 7, 3, 0, 0, 407, 406, 1, 0, 0, 0, 408, 409, 1, 0, 0, 0, 409, 407, 1, 0, 0, 0, 409, 410, 1, 0, 0, 0, 410, 82, 1, 0, 0, 0, 411, 412, 5, 37, 0, 0, 412, 84, 1, 0, 0, 0, 413, 415, 5, 36, 0, 0, 414, 416, 7, 5, 0, 0, 415, 414, 1, 0, 0, 0, 416, 417, 1, 0, 0, 0, 417, 415, 1, 0, 0, 0, 417, 418, 1, 0, 0, 0, 418, 422, 1, 0, 0, 0, 419, 421, 7, 6, 0, 0, 420, 419, 1, 0, 0, 0, 421, 424, 1, 0, 0, 0, 422, 420, 1, 0, 0, 0, 422, 423, 1, 0, 0, 0, 423, 86, 1, 0, 0, 0, 424, 422, 1, 0, 0, 0, 425, 427, 5, 64, 0, 0, 426, 428, 7, 7, 0, 0, 427, 426, 1, 0, 0, 0, 428, 429, 1, 0, 0, 0, 429, 427, 1, 0, 0, 0, 429, 430, 1, 0, 0, 0, 430, 434, 1, 0, 0, 0, 431, 433, 7, 8, 0, 0, 432, 431, 1, 0, 0, 0, 433, 436, 1, 0, 0, 0, 434, 432, 1, 0, 0, 0, 434, 435, 1, 0, 0, 0, 435, 88, 1, 0, 0, 0, 436, 434, 1, 0, 0, 0, 437, 439, 7, 9, 0, 0, 438, 437, 1, 0, 0, 0, 439, 440, 1, 0, 0, 0, 440, 438, 1, 0, 0, 0, 440, 441, 1, 0, 0, 0, 441, 90, 1, 0, 0, 0, 21, 0, 152, 157, 166, 168, 182, 345, 353, 356, 360, 365, 370, 376, 378, 381, 409, 417, 422, 429, 434, 440, 1, 6, 0, 0] 153 | -------------------------------------------------------------------------------- /vm/machine.go: -------------------------------------------------------------------------------- 1 | /* 2 | Provides `Machine`, which executes programs and outputs postings. 3 | 1: Create New Machine 4 | 2: Set Variables (with `core.Value`s or JSON) 5 | 3: Resolve Resources (answer requests on channel) 6 | 4: Resolve Balances (answer requests on channel) 7 | 6: Execute 8 | */ 9 | package vm 10 | 11 | import ( 12 | "encoding/binary" 13 | "encoding/json" 14 | "errors" 15 | "fmt" 16 | "math/big" 17 | 18 | "github.com/formancehq/machine/core" 19 | "github.com/formancehq/machine/vm/program" 20 | "github.com/logrusorgru/aurora" 21 | ) 22 | 23 | const ( 24 | EXIT_OK = byte(iota + 1) 25 | EXIT_FAIL 26 | EXIT_FAIL_INVALID 27 | EXIT_FAIL_INSUFFICIENT_FUNDS 28 | ) 29 | 30 | type Machine struct { 31 | P uint 32 | Program program.Program 33 | Vars map[string]core.Value 34 | UnresolvedResources []program.Resource 35 | Resources []core.Value // Constants and Variables 36 | resolveCalled bool 37 | Balances map[core.Account]map[core.Asset]*core.MonetaryInt // keeps tracks of balances throughout execution 38 | setBalanceCalled bool 39 | Stack []core.Value 40 | Postings []Posting // accumulates postings throughout execution 41 | TxMeta map[string]core.Value // accumulates transaction meta throughout execution 42 | AccountsMeta map[core.Account]map[string]core.Value // accumulates accounts meta throughout execution 43 | Printer func(chan core.Value) 44 | printChan chan core.Value 45 | Debug bool 46 | } 47 | 48 | type Posting struct { 49 | Source string `json:"source"` 50 | Destination string `json:"destination"` 51 | Amount *core.MonetaryInt `json:"amount"` 52 | Asset string `json:"asset"` 53 | } 54 | 55 | type Metadata map[string]any 56 | 57 | func NewMachine(p program.Program) *Machine { 58 | printChan := make(chan core.Value) 59 | 60 | m := Machine{ 61 | Program: p, 62 | UnresolvedResources: p.Resources, 63 | Resources: make([]core.Value, 0), 64 | printChan: printChan, 65 | Printer: StdOutPrinter, 66 | Postings: make([]Posting, 0), 67 | TxMeta: map[string]core.Value{}, 68 | AccountsMeta: map[core.Account]map[string]core.Value{}, 69 | } 70 | 71 | return &m 72 | } 73 | 74 | func StdOutPrinter(c chan core.Value) { 75 | for v := range c { 76 | fmt.Println("OUT:", v) 77 | } 78 | } 79 | 80 | func (m *Machine) GetTxMetaJSON() Metadata { 81 | meta := make(Metadata) 82 | for k, v := range m.TxMeta { 83 | valJSON, err := json.Marshal(v) 84 | if err != nil { 85 | panic(err) 86 | } 87 | v, err := json.Marshal(core.ValueJSON{ 88 | Type: v.GetType().String(), 89 | Value: valJSON, 90 | }) 91 | if err != nil { 92 | panic(err) 93 | } 94 | meta[k] = v 95 | } 96 | return meta 97 | } 98 | 99 | func (m *Machine) GetAccountsMetaJSON() Metadata { 100 | res := Metadata{} 101 | for account, meta := range m.AccountsMeta { 102 | for k, v := range meta { 103 | if _, ok := res[account.String()]; !ok { 104 | res[account.String()] = map[string][]byte{} 105 | } 106 | valJSON, err := json.Marshal(v) 107 | if err != nil { 108 | panic(err) 109 | } 110 | v, err := json.Marshal(core.ValueJSON{ 111 | Type: v.GetType().String(), 112 | Value: valJSON, 113 | }) 114 | if err != nil { 115 | panic(err) 116 | } 117 | res[account.String()].(map[string][]byte)[k] = v 118 | } 119 | } 120 | 121 | return res 122 | } 123 | 124 | func (m *Machine) getResource(addr core.Address) (*core.Value, bool) { 125 | a := int(addr) 126 | if a >= len(m.Resources) { 127 | return nil, false 128 | } 129 | return &m.Resources[a], true 130 | } 131 | 132 | func (m *Machine) withdrawAll(account core.Account, asset core.Asset, overdraft *core.MonetaryInt) (*core.Funding, error) { 133 | if accBalances, ok := m.Balances[account]; ok { 134 | if balance, ok := accBalances[asset]; ok { 135 | amountTaken := core.NewMonetaryInt(0) 136 | if balance.Add(overdraft).Gt(core.NewMonetaryInt(0)) { 137 | amountTaken = balance.Add(overdraft) 138 | accBalances[asset] = overdraft.Neg() 139 | } 140 | 141 | return &core.Funding{ 142 | Asset: asset, 143 | Parts: []core.FundingPart{{ 144 | Account: account, 145 | Amount: amountTaken, 146 | }}, 147 | }, nil 148 | } 149 | } 150 | return nil, fmt.Errorf("missing %v balance from %v", asset, account) 151 | } 152 | 153 | func (m *Machine) withdrawAlways(account core.Account, mon core.Monetary) (*core.Funding, error) { 154 | if accBalance, ok := m.Balances[account]; ok { 155 | if balance, ok := accBalance[mon.Asset]; ok { 156 | accBalance[mon.Asset] = balance.Sub(mon.Amount) 157 | return &core.Funding{ 158 | Asset: mon.Asset, 159 | Parts: []core.FundingPart{{ 160 | Account: account, 161 | Amount: mon.Amount, 162 | }}, 163 | }, nil 164 | } 165 | } 166 | return nil, fmt.Errorf("missing %v balance from %v", mon.Asset, account) 167 | } 168 | 169 | func (m *Machine) credit(account core.Account, funding core.Funding) { 170 | if account == "world" { 171 | return 172 | } 173 | if accBalance, ok := m.Balances[account]; ok { 174 | if _, ok := accBalance[funding.Asset]; ok { 175 | for _, part := range funding.Parts { 176 | balance := accBalance[funding.Asset] 177 | accBalance[funding.Asset] = balance.Add(part.Amount) 178 | } 179 | } 180 | } 181 | } 182 | 183 | func (m *Machine) repay(funding core.Funding) { 184 | for _, part := range funding.Parts { 185 | if part.Account == "world" { 186 | continue 187 | } 188 | balance := m.Balances[part.Account][funding.Asset] 189 | m.Balances[part.Account][funding.Asset] = balance.Add(part.Amount) 190 | } 191 | } 192 | 193 | func (m *Machine) tick() (bool, byte) { 194 | op := m.Program.Instructions[m.P] 195 | 196 | if m.Debug { 197 | fmt.Println("STATE ---------------------------------------------------------------------") 198 | fmt.Printf(" %v\n", aurora.Blue(m.Stack)) 199 | fmt.Printf(" %v\n", aurora.Cyan(m.Balances)) 200 | fmt.Printf(" %v\n", program.OpcodeName(op)) 201 | } 202 | 203 | switch op { 204 | case program.OP_APUSH: 205 | bytes := m.Program.Instructions[m.P+1 : m.P+3] 206 | v, ok := m.getResource(core.Address(binary.LittleEndian.Uint16(bytes))) 207 | if !ok { 208 | return true, EXIT_FAIL 209 | } 210 | m.Stack = append(m.Stack, *v) 211 | m.P += 2 212 | 213 | case program.OP_IPUSH: 214 | bytes := m.Program.Instructions[m.P+1 : m.P+9] 215 | v := core.Number(big.NewInt(int64(binary.LittleEndian.Uint64(bytes)))) 216 | m.Stack = append(m.Stack, v) 217 | m.P += 8 218 | 219 | case program.OP_BUMP: 220 | n := big.Int(*pop[core.Number](m)) 221 | idx := len(m.Stack) - int(n.Uint64()) - 1 222 | v := m.Stack[idx] 223 | m.Stack = append(m.Stack[:idx], m.Stack[idx+1:]...) 224 | m.Stack = append(m.Stack, v) 225 | 226 | case program.OP_DELETE: 227 | n := m.popValue() 228 | if n.GetType() == core.TypeFunding { 229 | return true, EXIT_FAIL_INVALID 230 | } 231 | 232 | case program.OP_IADD: 233 | b := pop[core.Number](m) 234 | a := pop[core.Number](m) 235 | m.pushValue(a.Add(b)) 236 | 237 | case program.OP_ISUB: 238 | b := pop[core.Number](m) 239 | a := pop[core.Number](m) 240 | m.pushValue(a.Sub(b)) 241 | 242 | case program.OP_PRINT: 243 | a := m.popValue() 244 | m.printChan <- a 245 | 246 | case program.OP_FAIL: 247 | return true, EXIT_FAIL 248 | 249 | case program.OP_ASSET: 250 | v := m.popValue() 251 | switch v := v.(type) { 252 | case core.Asset: 253 | m.pushValue(v) 254 | case core.Monetary: 255 | m.pushValue(v.Asset) 256 | case core.Funding: 257 | m.pushValue(v.Asset) 258 | default: 259 | return true, EXIT_FAIL_INVALID 260 | } 261 | 262 | case program.OP_MONETARY_NEW: 263 | amount := pop[core.Number](m) 264 | asset := pop[core.Asset](m) 265 | m.pushValue(core.Monetary{ 266 | Asset: asset, 267 | Amount: amount, 268 | }) 269 | 270 | case program.OP_MONETARY_ADD: 271 | b := pop[core.Monetary](m) 272 | a := pop[core.Monetary](m) 273 | if a.Asset != b.Asset { 274 | return true, EXIT_FAIL_INVALID 275 | } 276 | m.pushValue(core.Monetary{ 277 | Asset: a.Asset, 278 | Amount: a.Amount.Add(b.Amount), 279 | }) 280 | 281 | case program.OP_MAKE_ALLOTMENT: 282 | n := pop[core.Number](m) 283 | portions := make([]core.Portion, n.Uint64()) 284 | for i := uint64(0); i < n.Uint64(); i++ { 285 | p := pop[core.Portion](m) 286 | portions[i] = p 287 | } 288 | allotment, err := core.NewAllotment(portions) 289 | if err != nil { 290 | return true, EXIT_FAIL_INVALID 291 | } 292 | m.pushValue(*allotment) 293 | 294 | case program.OP_TAKE_ALL: 295 | overdraft := pop[core.Monetary](m) 296 | account := pop[core.Account](m) 297 | funding, err := m.withdrawAll(account, overdraft.Asset, overdraft.Amount) 298 | if err != nil { 299 | return true, EXIT_FAIL_INVALID 300 | } 301 | m.pushValue(*funding) 302 | 303 | case program.OP_TAKE_ALWAYS: 304 | mon := pop[core.Monetary](m) 305 | account := pop[core.Account](m) 306 | funding, err := m.withdrawAlways(account, mon) 307 | if err != nil { 308 | return true, EXIT_FAIL_INVALID 309 | } 310 | m.pushValue(*funding) 311 | 312 | case program.OP_TAKE: 313 | mon := pop[core.Monetary](m) 314 | funding := pop[core.Funding](m) 315 | if funding.Asset != mon.Asset { 316 | return true, EXIT_FAIL_INVALID 317 | } 318 | result, remainder, err := funding.Take(mon.Amount) 319 | if err != nil { 320 | return true, EXIT_FAIL_INSUFFICIENT_FUNDS 321 | } 322 | m.pushValue(remainder) 323 | m.pushValue(result) 324 | 325 | case program.OP_TAKE_MAX: 326 | mon := pop[core.Monetary](m) 327 | funding := pop[core.Funding](m) 328 | if funding.Asset != mon.Asset { 329 | return true, EXIT_FAIL_INVALID 330 | } 331 | missing := core.NewMonetaryInt(0) 332 | total := funding.Total() 333 | if mon.Amount.Gt(total) { 334 | missing = mon.Amount.Sub(total) 335 | } 336 | result, remainder := funding.TakeMax(mon.Amount) 337 | m.pushValue(core.Monetary{ 338 | Asset: mon.Asset, 339 | Amount: missing, 340 | }) 341 | m.pushValue(remainder) 342 | m.pushValue(result) 343 | 344 | case program.OP_FUNDING_ASSEMBLE: 345 | num := pop[core.Number](m) 346 | n := int(num.Uint64()) 347 | if n == 0 { 348 | return true, EXIT_FAIL_INVALID 349 | } 350 | first := pop[core.Funding](m) 351 | result := core.Funding{ 352 | Asset: first.Asset, 353 | } 354 | fundings_rev := make([]core.Funding, n) 355 | fundings_rev[0] = first 356 | for i := 1; i < n; i++ { 357 | f := pop[core.Funding](m) 358 | if f.Asset != result.Asset { 359 | return true, EXIT_FAIL_INVALID 360 | } 361 | fundings_rev[i] = f 362 | } 363 | for i := 0; i < n; i++ { 364 | res, err := result.Concat(fundings_rev[n-1-i]) 365 | if err != nil { 366 | return true, EXIT_FAIL_INVALID 367 | } 368 | result = res 369 | } 370 | m.pushValue(result) 371 | 372 | case program.OP_FUNDING_SUM: 373 | funding := pop[core.Funding](m) 374 | sum := funding.Total() 375 | m.pushValue(funding) 376 | m.pushValue(core.Monetary{ 377 | Asset: funding.Asset, 378 | Amount: sum, 379 | }) 380 | 381 | case program.OP_FUNDING_REVERSE: 382 | funding := pop[core.Funding](m) 383 | result := funding.Reverse() 384 | m.pushValue(result) 385 | 386 | case program.OP_ALLOC: 387 | allotment := pop[core.Allotment](m) 388 | monetary := pop[core.Monetary](m) 389 | total := monetary.Amount 390 | parts := allotment.Allocate(total) 391 | for i := len(parts) - 1; i >= 0; i-- { 392 | m.pushValue(core.Monetary{ 393 | Asset: monetary.Asset, 394 | Amount: parts[i], 395 | }) 396 | } 397 | 398 | case program.OP_REPAY: 399 | m.repay(pop[core.Funding](m)) 400 | 401 | case program.OP_SEND: 402 | dest := pop[core.Account](m) 403 | funding := pop[core.Funding](m) 404 | m.credit(dest, funding) 405 | for _, part := range funding.Parts { 406 | src := part.Account 407 | amt := part.Amount 408 | if amt.Eq(core.NewMonetaryInt(0)) { 409 | continue 410 | } 411 | m.Postings = append(m.Postings, Posting{ 412 | Source: string(src), 413 | Destination: string(dest), 414 | Asset: string(funding.Asset), 415 | Amount: amt, 416 | }) 417 | } 418 | 419 | case program.OP_TX_META: 420 | k := pop[core.String](m) 421 | v := m.popValue() 422 | m.TxMeta[string(k)] = v 423 | 424 | case program.OP_ACCOUNT_META: 425 | a := pop[core.Account](m) 426 | k := pop[core.String](m) 427 | v := m.popValue() 428 | if m.AccountsMeta[a] == nil { 429 | m.AccountsMeta[a] = map[string]core.Value{} 430 | } 431 | m.AccountsMeta[a][string(k)] = v 432 | 433 | default: 434 | return true, EXIT_FAIL_INVALID 435 | } 436 | 437 | m.P += 1 438 | 439 | if int(m.P) >= len(m.Program.Instructions) { 440 | return true, EXIT_OK 441 | } 442 | 443 | return false, 0 444 | } 445 | 446 | func (m *Machine) Execute() (byte, error) { 447 | go m.Printer(m.printChan) 448 | defer close(m.printChan) 449 | 450 | if len(m.Resources) != len(m.UnresolvedResources) { 451 | return 0, errors.New("resources haven't been initialized") 452 | } else if m.Balances == nil { 453 | return 0, errors.New("balances haven't been initialized") 454 | } 455 | 456 | for { 457 | finished, exitCode := m.tick() 458 | if finished { 459 | if exitCode == EXIT_OK && len(m.Stack) != 0 { 460 | return EXIT_FAIL_INVALID, nil 461 | } else { 462 | return exitCode, nil 463 | } 464 | } 465 | } 466 | } 467 | 468 | type BalanceRequest struct { 469 | Account string 470 | Asset string 471 | Response chan *core.MonetaryInt 472 | Error error 473 | } 474 | 475 | func (m *Machine) ResolveBalances() (chan BalanceRequest, error) { 476 | if len(m.Resources) != len(m.UnresolvedResources) { 477 | return nil, errors.New("tried to resolve balances before resources") 478 | } 479 | if m.setBalanceCalled { 480 | return nil, errors.New("tried to call ResolveBalances twice") 481 | } 482 | m.setBalanceCalled = true 483 | resChan := make(chan BalanceRequest) 484 | go func() { 485 | defer close(resChan) 486 | m.Balances = make(map[core.Account]map[core.Asset]*core.MonetaryInt) 487 | // for every account that we need balances of, check if it's there 488 | for addr, neededAssets := range m.Program.NeededBalances { 489 | account, ok := m.getResource(addr) 490 | if !ok { 491 | resChan <- BalanceRequest{ 492 | Error: errors.New("invalid program (resolve balances: invalid address of account)"), 493 | } 494 | return 495 | } 496 | if account, ok := (*account).(core.Account); ok { 497 | m.Balances[account] = make(map[core.Asset]*core.MonetaryInt) 498 | // for every asset, send request 499 | for addr := range neededAssets { 500 | mon, ok := m.getResource(addr) 501 | if !ok { 502 | resChan <- BalanceRequest{ 503 | Error: errors.New("invalid program (resolve balances: invalid address of monetary)"), 504 | } 505 | return 506 | } 507 | if ha, ok := (*mon).(core.HasAsset); ok { 508 | asset := ha.GetAsset() 509 | if string(account) == "world" { 510 | m.Balances[account][asset] = core.NewMonetaryInt(0) 511 | continue 512 | } 513 | respChan := make(chan *core.MonetaryInt) 514 | resChan <- BalanceRequest{ 515 | Account: string(account), 516 | Asset: string(asset), 517 | Response: respChan, 518 | } 519 | resp, ok := <-respChan 520 | close(respChan) 521 | if !ok { 522 | resChan <- BalanceRequest{ 523 | Error: errors.New("error on response channel"), 524 | } 525 | return 526 | } 527 | m.Balances[account][asset] = resp 528 | } else { 529 | resChan <- BalanceRequest{ 530 | Error: errors.New("invalid program (resolve balances: not an asset)"), 531 | } 532 | return 533 | } 534 | } 535 | } else { 536 | resChan <- BalanceRequest{ 537 | Error: errors.New("incorrect program (resolve balances: not an account)"), 538 | } 539 | return 540 | } 541 | } 542 | }() 543 | return resChan, nil 544 | } 545 | 546 | type ResourceRequest struct { 547 | Account string 548 | Key string 549 | Asset string 550 | Response chan core.Value 551 | Error error 552 | } 553 | 554 | func (m *Machine) ResolveResources() (chan ResourceRequest, error) { 555 | if m.resolveCalled { 556 | return nil, errors.New("tried to call ResolveResources twice") 557 | } 558 | m.resolveCalled = true 559 | resChan := make(chan ResourceRequest) 560 | go func() { 561 | defer close(resChan) 562 | for len(m.Resources) != len(m.UnresolvedResources) { 563 | idx := len(m.Resources) 564 | res := m.UnresolvedResources[idx] 565 | var val core.Value 566 | switch res := res.(type) { 567 | case program.Constant: 568 | val = res.Inner 569 | case program.Variable: 570 | var ok bool 571 | val, ok = m.Vars[res.Name] 572 | if !ok { 573 | resChan <- ResourceRequest{ 574 | Error: fmt.Errorf("missing variable '%s'", res.Name), 575 | } 576 | return 577 | } 578 | case program.VariableAccountMetadata: 579 | sourceAccount, ok := m.getResource(res.Account) 580 | if !ok { 581 | resChan <- ResourceRequest{ 582 | Error: fmt.Errorf( 583 | "variable '%s': tried to request metadata of an account which has not yet been solved", 584 | res.Name), 585 | } 586 | return 587 | } 588 | if (*sourceAccount).GetType() != core.TypeAccount { 589 | resChan <- ResourceRequest{ 590 | Error: fmt.Errorf( 591 | "variable '%s': tried to request metadata on wrong entity: %v instead of account", 592 | res.Name, (*sourceAccount).GetType()), 593 | } 594 | return 595 | } 596 | account := (*sourceAccount).(core.Account) 597 | resp := make(chan core.Value) 598 | resChan <- ResourceRequest{ 599 | Account: string(account), 600 | Key: res.Key, 601 | Response: resp, 602 | } 603 | val = <-resp 604 | close(resp) 605 | if val == nil { 606 | resChan <- ResourceRequest{ 607 | Error: fmt.Errorf("variable '%s': tried to set nil as resource", res.Name), 608 | } 609 | return 610 | } 611 | if val.GetType() != res.Typ { 612 | resChan <- ResourceRequest{ 613 | Error: fmt.Errorf("variable '%s': wrong type: expected %v, got %v", 614 | res.Name, res.Typ, val.GetType()), 615 | } 616 | return 617 | } 618 | case program.VariableAccountBalance: 619 | sourceAccount, ok := m.getResource(res.Account) 620 | if !ok { 621 | resChan <- ResourceRequest{ 622 | Error: fmt.Errorf( 623 | "variable '%s': tried to request balance of an account which has not yet been solved", 624 | res.Name), 625 | } 626 | return 627 | } 628 | if (*sourceAccount).GetType() != core.TypeAccount { 629 | resChan <- ResourceRequest{ 630 | Error: fmt.Errorf( 631 | "variable '%s': tried to request balance on wrong entity: %v instead of account", 632 | res.Name, (*sourceAccount).GetType()), 633 | } 634 | return 635 | } 636 | account := (*sourceAccount).(core.Account) 637 | resp := make(chan core.Value) 638 | resChan <- ResourceRequest{ 639 | Account: string(account), 640 | Asset: res.Asset, 641 | Response: resp, 642 | } 643 | amount := <-resp 644 | close(resp) 645 | if amount == nil { 646 | resChan <- ResourceRequest{ 647 | Error: fmt.Errorf("variable '%s': received nil amount", res.Name), 648 | } 649 | return 650 | } 651 | if amount.GetType() != core.TypeNumber { 652 | resChan <- ResourceRequest{ 653 | Error: fmt.Errorf( 654 | "variable '%s': tried to request balance: wrong type received: expected %v, got %v", 655 | res.Name, core.TypeNumber, amount.GetType()), 656 | } 657 | return 658 | } 659 | amt := amount.(core.Number) 660 | if amt.Ltz() { 661 | resChan <- ResourceRequest{ 662 | Error: fmt.Errorf( 663 | "variable '%s': tried to request the balance of account %s for asset %s: received %s: monetary amounts must be non-negative", 664 | res.Name, account, res.Asset, amt), 665 | } 666 | return 667 | } 668 | val = core.Monetary{ 669 | Asset: core.Asset(res.Asset), 670 | Amount: amt, 671 | } 672 | default: 673 | panic(fmt.Errorf("type %T not implemented", res)) 674 | } 675 | m.Resources = append(m.Resources, val) 676 | } 677 | }() 678 | return resChan, nil 679 | } 680 | 681 | func (m *Machine) SetVars(vars map[string]core.Value) error { 682 | v, err := m.Program.ParseVariables(vars) 683 | if err != nil { 684 | return err 685 | } 686 | m.Vars = v 687 | return nil 688 | } 689 | 690 | func (m *Machine) SetVarsFromJSON(vars map[string]json.RawMessage) error { 691 | v, err := m.Program.ParseVariablesJSON(vars) 692 | if err != nil { 693 | return err 694 | } 695 | m.Vars = v 696 | return nil 697 | } 698 | -------------------------------------------------------------------------------- /script/parser/numscript_lexer.go: -------------------------------------------------------------------------------- 1 | // Code generated from NumScript.g4 by ANTLR 4.10.1. DO NOT EDIT. 2 | 3 | package parser 4 | 5 | import ( 6 | "fmt" 7 | "sync" 8 | "unicode" 9 | 10 | "github.com/antlr/antlr4/runtime/Go/antlr" 11 | ) 12 | 13 | // Suppress unused import error 14 | var _ = fmt.Printf 15 | var _ = sync.Once{} 16 | var _ = unicode.IsLetter 17 | 18 | type NumScriptLexer struct { 19 | *antlr.BaseLexer 20 | channelNames []string 21 | modeNames []string 22 | // TODO: EOF string 23 | } 24 | 25 | var numscriptlexerLexerStaticData struct { 26 | once sync.Once 27 | serializedATN []int32 28 | channelNames []string 29 | modeNames []string 30 | literalNames []string 31 | symbolicNames []string 32 | ruleNames []string 33 | predictionContextCache *antlr.PredictionContextCache 34 | atn *antlr.ATN 35 | decisionToDFA []*antlr.DFA 36 | } 37 | 38 | func numscriptlexerLexerInit() { 39 | staticData := &numscriptlexerLexerStaticData 40 | staticData.channelNames = []string{ 41 | "DEFAULT_TOKEN_CHANNEL", "HIDDEN", 42 | } 43 | staticData.modeNames = []string{ 44 | "DEFAULT_MODE", 45 | } 46 | staticData.literalNames = []string{ 47 | "", "'*'", "'allowing overdraft up to'", "'allowing unbounded overdraft'", 48 | "','", "", "", "", "", "'vars'", "'meta'", "'set_tx_meta'", "'set_account_meta'", 49 | "'print'", "'fail'", "'send'", "'source'", "'from'", "'max'", "'destination'", 50 | "'to'", "'allocate'", "'+'", "'-'", "'('", "')'", "'['", "']'", "'{'", 51 | "'}'", "'='", "'account'", "'number'", "'monetary'", "'portion'", "'string'", 52 | "", "", "'remaining'", "'kept'", "'balance'", "", "'%'", 53 | } 54 | staticData.symbolicNames = []string{ 55 | "", "", "", "", "", "NEWLINE", "WHITESPACE", "MULTILINE_COMMENT", "LINE_COMMENT", 56 | "VARS", "META", "SET_TX_META", "SET_ACCOUNT_META", "PRINT", "FAIL", 57 | "SEND", "SOURCE", "FROM", "MAX", "DESTINATION", "TO", "ALLOCATE", "OP_ADD", 58 | "OP_SUB", "LPAREN", "RPAREN", "LBRACK", "RBRACK", "LBRACE", "RBRACE", 59 | "EQ", "TY_ACCOUNT", "TY_NUMBER", "TY_MONETARY", "TY_PORTION", "TY_STRING", 60 | "STRING", "PORTION", "REMAINING", "KEPT", "BALANCE", "NUMBER", "PERCENT", 61 | "VARIABLE_NAME", "ACCOUNT", "ASSET", 62 | } 63 | staticData.ruleNames = []string{ 64 | "T__0", "T__1", "T__2", "T__3", "NEWLINE", "WHITESPACE", "MULTILINE_COMMENT", 65 | "LINE_COMMENT", "VARS", "META", "SET_TX_META", "SET_ACCOUNT_META", "PRINT", 66 | "FAIL", "SEND", "SOURCE", "FROM", "MAX", "DESTINATION", "TO", "ALLOCATE", 67 | "OP_ADD", "OP_SUB", "LPAREN", "RPAREN", "LBRACK", "RBRACK", "LBRACE", 68 | "RBRACE", "EQ", "TY_ACCOUNT", "TY_NUMBER", "TY_MONETARY", "TY_PORTION", 69 | "TY_STRING", "STRING", "PORTION", "REMAINING", "KEPT", "BALANCE", "NUMBER", 70 | "PERCENT", "VARIABLE_NAME", "ACCOUNT", "ASSET", 71 | } 72 | staticData.predictionContextCache = antlr.NewPredictionContextCache() 73 | staticData.serializedATN = []int32{ 74 | 4, 0, 45, 442, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 75 | 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 76 | 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 77 | 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 78 | 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 79 | 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 80 | 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 81 | 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 82 | 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 1, 0, 1, 0, 1, 1, 1, 1, 1, 83 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 84 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 85 | 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 86 | 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 87 | 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 4, 4, 151, 8, 4, 11, 4, 12, 4, 152, 88 | 1, 5, 4, 5, 156, 8, 5, 11, 5, 12, 5, 157, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 89 | 1, 6, 1, 6, 5, 6, 167, 8, 6, 10, 6, 12, 6, 170, 9, 6, 1, 6, 1, 6, 1, 6, 90 | 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 5, 7, 181, 8, 7, 10, 7, 12, 7, 184, 91 | 9, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 92 | 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 93 | 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 94 | 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 95 | 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 96 | 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 97 | 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 98 | 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 99 | 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 100 | 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, 1, 23, 1, 23, 1, 24, 101 | 1, 24, 1, 25, 1, 25, 1, 26, 1, 26, 1, 27, 1, 27, 1, 28, 1, 28, 1, 29, 1, 102 | 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 103 | 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 104 | 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 105 | 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 5, 106 | 35, 344, 8, 35, 10, 35, 12, 35, 347, 9, 35, 1, 35, 1, 35, 1, 36, 4, 36, 107 | 352, 8, 36, 11, 36, 12, 36, 353, 1, 36, 3, 36, 357, 8, 36, 1, 36, 1, 36, 108 | 3, 36, 361, 8, 36, 1, 36, 4, 36, 364, 8, 36, 11, 36, 12, 36, 365, 1, 36, 109 | 4, 36, 369, 8, 36, 11, 36, 12, 36, 370, 1, 36, 1, 36, 4, 36, 375, 8, 36, 110 | 11, 36, 12, 36, 376, 3, 36, 379, 8, 36, 1, 36, 3, 36, 382, 8, 36, 1, 37, 111 | 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 112 | 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 113 | 1, 39, 1, 40, 4, 40, 408, 8, 40, 11, 40, 12, 40, 409, 1, 41, 1, 41, 1, 114 | 42, 1, 42, 4, 42, 416, 8, 42, 11, 42, 12, 42, 417, 1, 42, 5, 42, 421, 8, 115 | 42, 10, 42, 12, 42, 424, 9, 42, 1, 43, 1, 43, 4, 43, 428, 8, 43, 11, 43, 116 | 12, 43, 429, 1, 43, 5, 43, 433, 8, 43, 10, 43, 12, 43, 436, 9, 43, 1, 44, 117 | 4, 44, 439, 8, 44, 11, 44, 12, 44, 440, 2, 168, 182, 0, 45, 1, 1, 3, 2, 118 | 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 119 | 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 120 | 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 121 | 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 122 | 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, 45, 1, 0, 10, 2, 0, 10, 10, 13, 123 | 13, 2, 0, 9, 9, 32, 32, 6, 0, 32, 32, 45, 45, 48, 57, 65, 90, 95, 95, 97, 124 | 122, 1, 0, 48, 57, 1, 0, 32, 32, 2, 0, 95, 95, 97, 122, 3, 0, 48, 57, 95, 125 | 95, 97, 122, 3, 0, 65, 90, 95, 95, 97, 122, 4, 0, 48, 58, 65, 90, 95, 95, 126 | 97, 122, 2, 0, 47, 57, 65, 90, 461, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 127 | 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 128 | 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 129 | 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 130 | 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 131 | 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 132 | 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 133 | 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 134 | 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 135 | 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 136 | 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 137 | 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 138 | 1, 0, 0, 0, 1, 91, 1, 0, 0, 0, 3, 93, 1, 0, 0, 0, 5, 118, 1, 0, 0, 0, 7, 139 | 147, 1, 0, 0, 0, 9, 150, 1, 0, 0, 0, 11, 155, 1, 0, 0, 0, 13, 161, 1, 0, 140 | 0, 0, 15, 176, 1, 0, 0, 0, 17, 189, 1, 0, 0, 0, 19, 194, 1, 0, 0, 0, 21, 141 | 199, 1, 0, 0, 0, 23, 211, 1, 0, 0, 0, 25, 228, 1, 0, 0, 0, 27, 234, 1, 142 | 0, 0, 0, 29, 239, 1, 0, 0, 0, 31, 244, 1, 0, 0, 0, 33, 251, 1, 0, 0, 0, 143 | 35, 256, 1, 0, 0, 0, 37, 260, 1, 0, 0, 0, 39, 272, 1, 0, 0, 0, 41, 275, 144 | 1, 0, 0, 0, 43, 284, 1, 0, 0, 0, 45, 286, 1, 0, 0, 0, 47, 288, 1, 0, 0, 145 | 0, 49, 290, 1, 0, 0, 0, 51, 292, 1, 0, 0, 0, 53, 294, 1, 0, 0, 0, 55, 296, 146 | 1, 0, 0, 0, 57, 298, 1, 0, 0, 0, 59, 300, 1, 0, 0, 0, 61, 302, 1, 0, 0, 147 | 0, 63, 310, 1, 0, 0, 0, 65, 317, 1, 0, 0, 0, 67, 326, 1, 0, 0, 0, 69, 334, 148 | 1, 0, 0, 0, 71, 341, 1, 0, 0, 0, 73, 381, 1, 0, 0, 0, 75, 383, 1, 0, 0, 149 | 0, 77, 393, 1, 0, 0, 0, 79, 398, 1, 0, 0, 0, 81, 407, 1, 0, 0, 0, 83, 411, 150 | 1, 0, 0, 0, 85, 413, 1, 0, 0, 0, 87, 425, 1, 0, 0, 0, 89, 438, 1, 0, 0, 151 | 0, 91, 92, 5, 42, 0, 0, 92, 2, 1, 0, 0, 0, 93, 94, 5, 97, 0, 0, 94, 95, 152 | 5, 108, 0, 0, 95, 96, 5, 108, 0, 0, 96, 97, 5, 111, 0, 0, 97, 98, 5, 119, 153 | 0, 0, 98, 99, 5, 105, 0, 0, 99, 100, 5, 110, 0, 0, 100, 101, 5, 103, 0, 154 | 0, 101, 102, 5, 32, 0, 0, 102, 103, 5, 111, 0, 0, 103, 104, 5, 118, 0, 155 | 0, 104, 105, 5, 101, 0, 0, 105, 106, 5, 114, 0, 0, 106, 107, 5, 100, 0, 156 | 0, 107, 108, 5, 114, 0, 0, 108, 109, 5, 97, 0, 0, 109, 110, 5, 102, 0, 157 | 0, 110, 111, 5, 116, 0, 0, 111, 112, 5, 32, 0, 0, 112, 113, 5, 117, 0, 158 | 0, 113, 114, 5, 112, 0, 0, 114, 115, 5, 32, 0, 0, 115, 116, 5, 116, 0, 159 | 0, 116, 117, 5, 111, 0, 0, 117, 4, 1, 0, 0, 0, 118, 119, 5, 97, 0, 0, 119, 160 | 120, 5, 108, 0, 0, 120, 121, 5, 108, 0, 0, 121, 122, 5, 111, 0, 0, 122, 161 | 123, 5, 119, 0, 0, 123, 124, 5, 105, 0, 0, 124, 125, 5, 110, 0, 0, 125, 162 | 126, 5, 103, 0, 0, 126, 127, 5, 32, 0, 0, 127, 128, 5, 117, 0, 0, 128, 163 | 129, 5, 110, 0, 0, 129, 130, 5, 98, 0, 0, 130, 131, 5, 111, 0, 0, 131, 164 | 132, 5, 117, 0, 0, 132, 133, 5, 110, 0, 0, 133, 134, 5, 100, 0, 0, 134, 165 | 135, 5, 101, 0, 0, 135, 136, 5, 100, 0, 0, 136, 137, 5, 32, 0, 0, 137, 166 | 138, 5, 111, 0, 0, 138, 139, 5, 118, 0, 0, 139, 140, 5, 101, 0, 0, 140, 167 | 141, 5, 114, 0, 0, 141, 142, 5, 100, 0, 0, 142, 143, 5, 114, 0, 0, 143, 168 | 144, 5, 97, 0, 0, 144, 145, 5, 102, 0, 0, 145, 146, 5, 116, 0, 0, 146, 169 | 6, 1, 0, 0, 0, 147, 148, 5, 44, 0, 0, 148, 8, 1, 0, 0, 0, 149, 151, 7, 170 | 0, 0, 0, 150, 149, 1, 0, 0, 0, 151, 152, 1, 0, 0, 0, 152, 150, 1, 0, 0, 171 | 0, 152, 153, 1, 0, 0, 0, 153, 10, 1, 0, 0, 0, 154, 156, 7, 1, 0, 0, 155, 172 | 154, 1, 0, 0, 0, 156, 157, 1, 0, 0, 0, 157, 155, 1, 0, 0, 0, 157, 158, 173 | 1, 0, 0, 0, 158, 159, 1, 0, 0, 0, 159, 160, 6, 5, 0, 0, 160, 12, 1, 0, 174 | 0, 0, 161, 162, 5, 47, 0, 0, 162, 163, 5, 42, 0, 0, 163, 168, 1, 0, 0, 175 | 0, 164, 167, 3, 13, 6, 0, 165, 167, 9, 0, 0, 0, 166, 164, 1, 0, 0, 0, 166, 176 | 165, 1, 0, 0, 0, 167, 170, 1, 0, 0, 0, 168, 169, 1, 0, 0, 0, 168, 166, 177 | 1, 0, 0, 0, 169, 171, 1, 0, 0, 0, 170, 168, 1, 0, 0, 0, 171, 172, 5, 42, 178 | 0, 0, 172, 173, 5, 47, 0, 0, 173, 174, 1, 0, 0, 0, 174, 175, 6, 6, 0, 0, 179 | 175, 14, 1, 0, 0, 0, 176, 177, 5, 47, 0, 0, 177, 178, 5, 47, 0, 0, 178, 180 | 182, 1, 0, 0, 0, 179, 181, 9, 0, 0, 0, 180, 179, 1, 0, 0, 0, 181, 184, 181 | 1, 0, 0, 0, 182, 183, 1, 0, 0, 0, 182, 180, 1, 0, 0, 0, 183, 185, 1, 0, 182 | 0, 0, 184, 182, 1, 0, 0, 0, 185, 186, 3, 9, 4, 0, 186, 187, 1, 0, 0, 0, 183 | 187, 188, 6, 7, 0, 0, 188, 16, 1, 0, 0, 0, 189, 190, 5, 118, 0, 0, 190, 184 | 191, 5, 97, 0, 0, 191, 192, 5, 114, 0, 0, 192, 193, 5, 115, 0, 0, 193, 185 | 18, 1, 0, 0, 0, 194, 195, 5, 109, 0, 0, 195, 196, 5, 101, 0, 0, 196, 197, 186 | 5, 116, 0, 0, 197, 198, 5, 97, 0, 0, 198, 20, 1, 0, 0, 0, 199, 200, 5, 187 | 115, 0, 0, 200, 201, 5, 101, 0, 0, 201, 202, 5, 116, 0, 0, 202, 203, 5, 188 | 95, 0, 0, 203, 204, 5, 116, 0, 0, 204, 205, 5, 120, 0, 0, 205, 206, 5, 189 | 95, 0, 0, 206, 207, 5, 109, 0, 0, 207, 208, 5, 101, 0, 0, 208, 209, 5, 190 | 116, 0, 0, 209, 210, 5, 97, 0, 0, 210, 22, 1, 0, 0, 0, 211, 212, 5, 115, 191 | 0, 0, 212, 213, 5, 101, 0, 0, 213, 214, 5, 116, 0, 0, 214, 215, 5, 95, 192 | 0, 0, 215, 216, 5, 97, 0, 0, 216, 217, 5, 99, 0, 0, 217, 218, 5, 99, 0, 193 | 0, 218, 219, 5, 111, 0, 0, 219, 220, 5, 117, 0, 0, 220, 221, 5, 110, 0, 194 | 0, 221, 222, 5, 116, 0, 0, 222, 223, 5, 95, 0, 0, 223, 224, 5, 109, 0, 195 | 0, 224, 225, 5, 101, 0, 0, 225, 226, 5, 116, 0, 0, 226, 227, 5, 97, 0, 196 | 0, 227, 24, 1, 0, 0, 0, 228, 229, 5, 112, 0, 0, 229, 230, 5, 114, 0, 0, 197 | 230, 231, 5, 105, 0, 0, 231, 232, 5, 110, 0, 0, 232, 233, 5, 116, 0, 0, 198 | 233, 26, 1, 0, 0, 0, 234, 235, 5, 102, 0, 0, 235, 236, 5, 97, 0, 0, 236, 199 | 237, 5, 105, 0, 0, 237, 238, 5, 108, 0, 0, 238, 28, 1, 0, 0, 0, 239, 240, 200 | 5, 115, 0, 0, 240, 241, 5, 101, 0, 0, 241, 242, 5, 110, 0, 0, 242, 243, 201 | 5, 100, 0, 0, 243, 30, 1, 0, 0, 0, 244, 245, 5, 115, 0, 0, 245, 246, 5, 202 | 111, 0, 0, 246, 247, 5, 117, 0, 0, 247, 248, 5, 114, 0, 0, 248, 249, 5, 203 | 99, 0, 0, 249, 250, 5, 101, 0, 0, 250, 32, 1, 0, 0, 0, 251, 252, 5, 102, 204 | 0, 0, 252, 253, 5, 114, 0, 0, 253, 254, 5, 111, 0, 0, 254, 255, 5, 109, 205 | 0, 0, 255, 34, 1, 0, 0, 0, 256, 257, 5, 109, 0, 0, 257, 258, 5, 97, 0, 206 | 0, 258, 259, 5, 120, 0, 0, 259, 36, 1, 0, 0, 0, 260, 261, 5, 100, 0, 0, 207 | 261, 262, 5, 101, 0, 0, 262, 263, 5, 115, 0, 0, 263, 264, 5, 116, 0, 0, 208 | 264, 265, 5, 105, 0, 0, 265, 266, 5, 110, 0, 0, 266, 267, 5, 97, 0, 0, 209 | 267, 268, 5, 116, 0, 0, 268, 269, 5, 105, 0, 0, 269, 270, 5, 111, 0, 0, 210 | 270, 271, 5, 110, 0, 0, 271, 38, 1, 0, 0, 0, 272, 273, 5, 116, 0, 0, 273, 211 | 274, 5, 111, 0, 0, 274, 40, 1, 0, 0, 0, 275, 276, 5, 97, 0, 0, 276, 277, 212 | 5, 108, 0, 0, 277, 278, 5, 108, 0, 0, 278, 279, 5, 111, 0, 0, 279, 280, 213 | 5, 99, 0, 0, 280, 281, 5, 97, 0, 0, 281, 282, 5, 116, 0, 0, 282, 283, 5, 214 | 101, 0, 0, 283, 42, 1, 0, 0, 0, 284, 285, 5, 43, 0, 0, 285, 44, 1, 0, 0, 215 | 0, 286, 287, 5, 45, 0, 0, 287, 46, 1, 0, 0, 0, 288, 289, 5, 40, 0, 0, 289, 216 | 48, 1, 0, 0, 0, 290, 291, 5, 41, 0, 0, 291, 50, 1, 0, 0, 0, 292, 293, 5, 217 | 91, 0, 0, 293, 52, 1, 0, 0, 0, 294, 295, 5, 93, 0, 0, 295, 54, 1, 0, 0, 218 | 0, 296, 297, 5, 123, 0, 0, 297, 56, 1, 0, 0, 0, 298, 299, 5, 125, 0, 0, 219 | 299, 58, 1, 0, 0, 0, 300, 301, 5, 61, 0, 0, 301, 60, 1, 0, 0, 0, 302, 303, 220 | 5, 97, 0, 0, 303, 304, 5, 99, 0, 0, 304, 305, 5, 99, 0, 0, 305, 306, 5, 221 | 111, 0, 0, 306, 307, 5, 117, 0, 0, 307, 308, 5, 110, 0, 0, 308, 309, 5, 222 | 116, 0, 0, 309, 62, 1, 0, 0, 0, 310, 311, 5, 110, 0, 0, 311, 312, 5, 117, 223 | 0, 0, 312, 313, 5, 109, 0, 0, 313, 314, 5, 98, 0, 0, 314, 315, 5, 101, 224 | 0, 0, 315, 316, 5, 114, 0, 0, 316, 64, 1, 0, 0, 0, 317, 318, 5, 109, 0, 225 | 0, 318, 319, 5, 111, 0, 0, 319, 320, 5, 110, 0, 0, 320, 321, 5, 101, 0, 226 | 0, 321, 322, 5, 116, 0, 0, 322, 323, 5, 97, 0, 0, 323, 324, 5, 114, 0, 227 | 0, 324, 325, 5, 121, 0, 0, 325, 66, 1, 0, 0, 0, 326, 327, 5, 112, 0, 0, 228 | 327, 328, 5, 111, 0, 0, 328, 329, 5, 114, 0, 0, 329, 330, 5, 116, 0, 0, 229 | 330, 331, 5, 105, 0, 0, 331, 332, 5, 111, 0, 0, 332, 333, 5, 110, 0, 0, 230 | 333, 68, 1, 0, 0, 0, 334, 335, 5, 115, 0, 0, 335, 336, 5, 116, 0, 0, 336, 231 | 337, 5, 114, 0, 0, 337, 338, 5, 105, 0, 0, 338, 339, 5, 110, 0, 0, 339, 232 | 340, 5, 103, 0, 0, 340, 70, 1, 0, 0, 0, 341, 345, 5, 34, 0, 0, 342, 344, 233 | 7, 2, 0, 0, 343, 342, 1, 0, 0, 0, 344, 347, 1, 0, 0, 0, 345, 343, 1, 0, 234 | 0, 0, 345, 346, 1, 0, 0, 0, 346, 348, 1, 0, 0, 0, 347, 345, 1, 0, 0, 0, 235 | 348, 349, 5, 34, 0, 0, 349, 72, 1, 0, 0, 0, 350, 352, 7, 3, 0, 0, 351, 236 | 350, 1, 0, 0, 0, 352, 353, 1, 0, 0, 0, 353, 351, 1, 0, 0, 0, 353, 354, 237 | 1, 0, 0, 0, 354, 356, 1, 0, 0, 0, 355, 357, 7, 4, 0, 0, 356, 355, 1, 0, 238 | 0, 0, 356, 357, 1, 0, 0, 0, 357, 358, 1, 0, 0, 0, 358, 360, 5, 47, 0, 0, 239 | 359, 361, 7, 4, 0, 0, 360, 359, 1, 0, 0, 0, 360, 361, 1, 0, 0, 0, 361, 240 | 363, 1, 0, 0, 0, 362, 364, 7, 3, 0, 0, 363, 362, 1, 0, 0, 0, 364, 365, 241 | 1, 0, 0, 0, 365, 363, 1, 0, 0, 0, 365, 366, 1, 0, 0, 0, 366, 382, 1, 0, 242 | 0, 0, 367, 369, 7, 3, 0, 0, 368, 367, 1, 0, 0, 0, 369, 370, 1, 0, 0, 0, 243 | 370, 368, 1, 0, 0, 0, 370, 371, 1, 0, 0, 0, 371, 378, 1, 0, 0, 0, 372, 244 | 374, 5, 46, 0, 0, 373, 375, 7, 3, 0, 0, 374, 373, 1, 0, 0, 0, 375, 376, 245 | 1, 0, 0, 0, 376, 374, 1, 0, 0, 0, 376, 377, 1, 0, 0, 0, 377, 379, 1, 0, 246 | 0, 0, 378, 372, 1, 0, 0, 0, 378, 379, 1, 0, 0, 0, 379, 380, 1, 0, 0, 0, 247 | 380, 382, 5, 37, 0, 0, 381, 351, 1, 0, 0, 0, 381, 368, 1, 0, 0, 0, 382, 248 | 74, 1, 0, 0, 0, 383, 384, 5, 114, 0, 0, 384, 385, 5, 101, 0, 0, 385, 386, 249 | 5, 109, 0, 0, 386, 387, 5, 97, 0, 0, 387, 388, 5, 105, 0, 0, 388, 389, 250 | 5, 110, 0, 0, 389, 390, 5, 105, 0, 0, 390, 391, 5, 110, 0, 0, 391, 392, 251 | 5, 103, 0, 0, 392, 76, 1, 0, 0, 0, 393, 394, 5, 107, 0, 0, 394, 395, 5, 252 | 101, 0, 0, 395, 396, 5, 112, 0, 0, 396, 397, 5, 116, 0, 0, 397, 78, 1, 253 | 0, 0, 0, 398, 399, 5, 98, 0, 0, 399, 400, 5, 97, 0, 0, 400, 401, 5, 108, 254 | 0, 0, 401, 402, 5, 97, 0, 0, 402, 403, 5, 110, 0, 0, 403, 404, 5, 99, 0, 255 | 0, 404, 405, 5, 101, 0, 0, 405, 80, 1, 0, 0, 0, 406, 408, 7, 3, 0, 0, 407, 256 | 406, 1, 0, 0, 0, 408, 409, 1, 0, 0, 0, 409, 407, 1, 0, 0, 0, 409, 410, 257 | 1, 0, 0, 0, 410, 82, 1, 0, 0, 0, 411, 412, 5, 37, 0, 0, 412, 84, 1, 0, 258 | 0, 0, 413, 415, 5, 36, 0, 0, 414, 416, 7, 5, 0, 0, 415, 414, 1, 0, 0, 0, 259 | 416, 417, 1, 0, 0, 0, 417, 415, 1, 0, 0, 0, 417, 418, 1, 0, 0, 0, 418, 260 | 422, 1, 0, 0, 0, 419, 421, 7, 6, 0, 0, 420, 419, 1, 0, 0, 0, 421, 424, 261 | 1, 0, 0, 0, 422, 420, 1, 0, 0, 0, 422, 423, 1, 0, 0, 0, 423, 86, 1, 0, 262 | 0, 0, 424, 422, 1, 0, 0, 0, 425, 427, 5, 64, 0, 0, 426, 428, 7, 7, 0, 0, 263 | 427, 426, 1, 0, 0, 0, 428, 429, 1, 0, 0, 0, 429, 427, 1, 0, 0, 0, 429, 264 | 430, 1, 0, 0, 0, 430, 434, 1, 0, 0, 0, 431, 433, 7, 8, 0, 0, 432, 431, 265 | 1, 0, 0, 0, 433, 436, 1, 0, 0, 0, 434, 432, 1, 0, 0, 0, 434, 435, 1, 0, 266 | 0, 0, 435, 88, 1, 0, 0, 0, 436, 434, 1, 0, 0, 0, 437, 439, 7, 9, 0, 0, 267 | 438, 437, 1, 0, 0, 0, 439, 440, 1, 0, 0, 0, 440, 438, 1, 0, 0, 0, 440, 268 | 441, 1, 0, 0, 0, 441, 90, 1, 0, 0, 0, 21, 0, 152, 157, 166, 168, 182, 345, 269 | 353, 356, 360, 365, 370, 376, 378, 381, 409, 417, 422, 429, 434, 440, 1, 270 | 6, 0, 0, 271 | } 272 | deserializer := antlr.NewATNDeserializer(nil) 273 | staticData.atn = deserializer.Deserialize(staticData.serializedATN) 274 | atn := staticData.atn 275 | staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState)) 276 | decisionToDFA := staticData.decisionToDFA 277 | for index, state := range atn.DecisionToState { 278 | decisionToDFA[index] = antlr.NewDFA(state, index) 279 | } 280 | } 281 | 282 | // NumScriptLexerInit initializes any static state used to implement NumScriptLexer. By default the 283 | // static state used to implement the lexer is lazily initialized during the first call to 284 | // NewNumScriptLexer(). You can call this function if you wish to initialize the static state ahead 285 | // of time. 286 | func NumScriptLexerInit() { 287 | staticData := &numscriptlexerLexerStaticData 288 | staticData.once.Do(numscriptlexerLexerInit) 289 | } 290 | 291 | // NewNumScriptLexer produces a new lexer instance for the optional input antlr.CharStream. 292 | func NewNumScriptLexer(input antlr.CharStream) *NumScriptLexer { 293 | NumScriptLexerInit() 294 | l := new(NumScriptLexer) 295 | l.BaseLexer = antlr.NewBaseLexer(input) 296 | staticData := &numscriptlexerLexerStaticData 297 | l.Interpreter = antlr.NewLexerATNSimulator(l, staticData.atn, staticData.decisionToDFA, staticData.predictionContextCache) 298 | l.channelNames = staticData.channelNames 299 | l.modeNames = staticData.modeNames 300 | l.RuleNames = staticData.ruleNames 301 | l.LiteralNames = staticData.literalNames 302 | l.SymbolicNames = staticData.symbolicNames 303 | l.GrammarFileName = "NumScript.g4" 304 | // TODO: l.EOF = antlr.TokenEOF 305 | 306 | return l 307 | } 308 | 309 | // NumScriptLexer tokens. 310 | const ( 311 | NumScriptLexerT__0 = 1 312 | NumScriptLexerT__1 = 2 313 | NumScriptLexerT__2 = 3 314 | NumScriptLexerT__3 = 4 315 | NumScriptLexerNEWLINE = 5 316 | NumScriptLexerWHITESPACE = 6 317 | NumScriptLexerMULTILINE_COMMENT = 7 318 | NumScriptLexerLINE_COMMENT = 8 319 | NumScriptLexerVARS = 9 320 | NumScriptLexerMETA = 10 321 | NumScriptLexerSET_TX_META = 11 322 | NumScriptLexerSET_ACCOUNT_META = 12 323 | NumScriptLexerPRINT = 13 324 | NumScriptLexerFAIL = 14 325 | NumScriptLexerSEND = 15 326 | NumScriptLexerSOURCE = 16 327 | NumScriptLexerFROM = 17 328 | NumScriptLexerMAX = 18 329 | NumScriptLexerDESTINATION = 19 330 | NumScriptLexerTO = 20 331 | NumScriptLexerALLOCATE = 21 332 | NumScriptLexerOP_ADD = 22 333 | NumScriptLexerOP_SUB = 23 334 | NumScriptLexerLPAREN = 24 335 | NumScriptLexerRPAREN = 25 336 | NumScriptLexerLBRACK = 26 337 | NumScriptLexerRBRACK = 27 338 | NumScriptLexerLBRACE = 28 339 | NumScriptLexerRBRACE = 29 340 | NumScriptLexerEQ = 30 341 | NumScriptLexerTY_ACCOUNT = 31 342 | NumScriptLexerTY_NUMBER = 32 343 | NumScriptLexerTY_MONETARY = 33 344 | NumScriptLexerTY_PORTION = 34 345 | NumScriptLexerTY_STRING = 35 346 | NumScriptLexerSTRING = 36 347 | NumScriptLexerPORTION = 37 348 | NumScriptLexerREMAINING = 38 349 | NumScriptLexerKEPT = 39 350 | NumScriptLexerBALANCE = 40 351 | NumScriptLexerNUMBER = 41 352 | NumScriptLexerPERCENT = 42 353 | NumScriptLexerVARIABLE_NAME = 43 354 | NumScriptLexerACCOUNT = 44 355 | NumScriptLexerASSET = 45 356 | ) 357 | --------------------------------------------------------------------------------