OSDN Git Service

add value calculate with lock statement (#2)
[bytom/equity.git] / compiler / checks.go
1 package compiler
2
3 import "fmt"
4
5 func checkRecursive(contract *Contract) bool {
6         for _, clause := range contract.Clauses {
7                 for _, stmt := range clause.statements {
8                         if l, ok := stmt.(*lockStatement); ok {
9                                 if c, ok := l.program.(*callExpr); ok {
10                                         if references(c.fn, contract.Name) {
11                                                 return true
12                                         }
13                                 }
14                         }
15                 }
16         }
17         return false
18 }
19
20 func prohibitSigParams(contract *Contract) error {
21         for _, p := range contract.Params {
22                 if p.Type == sigType {
23                         return fmt.Errorf("contract parameter \"%s\" has type Signature, but contract parameters cannot have type Signature", p.Name)
24                 }
25         }
26         return nil
27 }
28
29 func requireAllParamsUsedInClauses(params []*Param, clauses []*Clause) error {
30         for _, p := range params {
31                 used := false
32                 for _, c := range clauses {
33                         err := requireAllParamsUsedInClause([]*Param{p}, c)
34                         if err == nil {
35                                 used = true
36                                 break
37                         }
38                 }
39                 if !used {
40                         return fmt.Errorf("parameter \"%s\" is unused", p.Name)
41                 }
42         }
43         return nil
44 }
45
46 func requireAllParamsUsedInClause(params []*Param, clause *Clause) error {
47         for _, p := range params {
48                 used := false
49                 for _, stmt := range clause.statements {
50                         switch s := stmt.(type) {
51                         case *verifyStatement:
52                                 used = references(s.expr, p.Name)
53                         case *lockStatement:
54                                 used = references(s.lockedAmount, p.Name) || references(s.lockedAsset, p.Name) || references(s.program, p.Name)
55                         case *unlockStatement:
56                                 used = references(s.unlockedAmount, p.Name) || references(s.unlockedAsset, p.Name)
57                         }
58                         if used {
59                                 break
60                         }
61                 }
62
63                 if !used {
64                         return fmt.Errorf("parameter \"%s\" is unused in clause \"%s\"", p.Name, clause.Name)
65                 }
66         }
67         return nil
68 }
69
70 func references(expr expression, name string) bool {
71         switch e := expr.(type) {
72         case *binaryExpr:
73                 return references(e.left, name) || references(e.right, name)
74         case *unaryExpr:
75                 return references(e.expr, name)
76         case *callExpr:
77                 if references(e.fn, name) {
78                         return true
79                 }
80                 for _, a := range e.args {
81                         if references(a, name) {
82                                 return true
83                         }
84                 }
85                 return false
86         case varRef:
87                 return string(e) == name
88         case listExpr:
89                 for _, elt := range []expression(e) {
90                         if references(elt, name) {
91                                 return true
92                         }
93                 }
94                 return false
95         }
96         return false
97 }
98
99 func requireAllValuesDisposedOnce(contract *Contract, clause *Clause) error {
100         err := valueDisposedOnce(contract.Value, clause)
101         if err != nil {
102                 return err
103         }
104         return nil
105 }
106
107 func valueDisposedOnce(value ValueInfo, clause *Clause) error {
108         var count int
109         for _, s := range clause.statements {
110                 switch stmt := s.(type) {
111                 case *unlockStatement:
112                         if references(stmt.unlockedAmount, value.Amount) && references(stmt.unlockedAsset, value.Asset) {
113                                 count++
114                         }
115                 case *lockStatement:
116                         if references(stmt.lockedAmount, value.Amount) && references(stmt.lockedAsset, value.Asset) {
117                                 count++
118                         }
119                 }
120         }
121         switch count {
122         case 0:
123                 return fmt.Errorf("valueAmount \"%s\" or valueAsset \"%s\" not disposed in clause \"%s\"", value.Amount, value.Asset, clause.Name)
124         case 1:
125                 return nil
126         default:
127                 return fmt.Errorf("valueAmount \"%s\" or valueAsset \"%s\" disposed multiple times in clause \"%s\"", value.Amount, value.Asset, clause.Name)
128         }
129 }
130
131 func referencedBuiltin(expr expression) *builtin {
132         if v, ok := expr.(varRef); ok {
133                 for _, b := range builtins {
134                         if string(v) == b.name {
135                                 return &b
136                         }
137                 }
138         }
139         return nil
140 }
141
142 func assignIndexes(clause *Clause) {
143         var nextIndex int64
144         for _, s := range clause.statements {
145                 switch stmt := s.(type) {
146                 case *lockStatement:
147                         stmt.index = nextIndex
148                         nextIndex++
149
150                 case *unlockStatement:
151                         nextIndex++
152                 }
153         }
154 }
155
156 func typeCheckClause(contract *Contract, clause *Clause, env *environ) error {
157         for _, s := range clause.statements {
158                 switch stmt := s.(type) {
159                 case *verifyStatement:
160                         if t := stmt.expr.typ(env); t != boolType {
161                                 return fmt.Errorf("expression in verify statement in clause \"%s\" has type \"%s\", must be Boolean", clause.Name, t)
162                         }
163
164                 case *lockStatement:
165                         if t := stmt.lockedAmount.typ(env); !(t == intType || t == amountType) {
166                                 return fmt.Errorf("lockedAmount expression \"%s\" in lock statement in clause \"%s\" has type \"%s\", must be Integer", stmt.lockedAmount, clause.Name, t)
167                         }
168                         if t := stmt.lockedAsset.typ(env); t != assetType {
169                                 return fmt.Errorf("lockedAsset expression \"%s\" in lock statement in clause \"%s\" has type \"%s\", must be Asset", stmt.lockedAsset, clause.Name, t)
170                         }
171                         if t := stmt.program.typ(env); t != progType {
172                                 return fmt.Errorf("program in lock statement in clause \"%s\" has type \"%s\", must be Program", clause.Name, t)
173                         }
174
175                 case *unlockStatement:
176                         if t := stmt.unlockedAmount.typ(env); !(t == intType || t == amountType) {
177                                 return fmt.Errorf("unlockedAmount expression \"%s\" in unlock statement of clause \"%s\" has type \"%s\", must be Integer", stmt.unlockedAmount, clause.Name, t)
178                         }
179                         if t := stmt.unlockedAsset.typ(env); t != assetType {
180                                 return fmt.Errorf("unlockedAsset expression \"%s\" in unlock statement of clause \"%s\" has type \"%s\", must be Asset", stmt.unlockedAsset, clause.Name, t)
181                         }
182                         if stmt.unlockedAmount.String() != contract.Value.Amount || stmt.unlockedAsset.String() != contract.Value.Asset {
183                                 return fmt.Errorf("amount \"%s\" of asset \"%s\" expression in unlock statement of clause \"%s\" must be the contract valueAmount \"%s\" of valueAsset \"%s\"",
184                                         stmt.unlockedAmount.String(), stmt.unlockedAsset.String(), clause.Name, contract.Value.Amount, contract.Value.Asset)
185                         }
186                 }
187         }
188         return nil
189 }