github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/core/asm/compiler.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-aigar library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>. 17 18 package asm 19 20 import ( 21 "fmt" 22 "math/big" 23 "os" 24 "strings" 25 26 "github.com/AigarNetwork/aigar/common/math" 27 "github.com/AigarNetwork/aigar/core/vm" 28 ) 29 30 // Compiler contains information about the parsed source 31 // and holds the tokens for the program. 32 type Compiler struct { 33 tokens []token 34 binary []interface{} 35 36 labels map[string]int 37 38 pc, pos int 39 40 debug bool 41 } 42 43 // newCompiler returns a new allocated compiler. 44 func NewCompiler(debug bool) *Compiler { 45 return &Compiler{ 46 labels: make(map[string]int), 47 debug: debug, 48 } 49 } 50 51 // Feed feeds tokens in to ch and are interpreted by 52 // the compiler. 53 // 54 // feed is the first pass in the compile stage as it 55 // collects the used labels in the program and keeps a 56 // program counter which is used to determine the locations 57 // of the jump dests. The labels can than be used in the 58 // second stage to push labels and determine the right 59 // position. 60 func (c *Compiler) Feed(ch <-chan token) { 61 var prev token 62 for i := range ch { 63 switch i.typ { 64 case number: 65 num := math.MustParseBig256(i.text).Bytes() 66 if len(num) == 0 { 67 num = []byte{0} 68 } 69 c.pc += len(num) 70 case stringValue: 71 c.pc += len(i.text) - 2 72 case element: 73 c.pc++ 74 case labelDef: 75 c.labels[i.text] = c.pc 76 c.pc++ 77 case label: 78 c.pc += 4 79 if prev.typ == element && isJump(prev.text) { 80 c.pc++ 81 } 82 } 83 84 c.tokens = append(c.tokens, i) 85 prev = i 86 } 87 if c.debug { 88 fmt.Fprintln(os.Stderr, "found", len(c.labels), "labels") 89 } 90 } 91 92 // Compile compiles the current tokens and returns a 93 // binary string that can be interpreted by the EVM 94 // and an error if it failed. 95 // 96 // compile is the second stage in the compile phase 97 // which compiles the tokens to EVM instructions. 98 func (c *Compiler) Compile() (string, []error) { 99 var errors []error 100 // continue looping over the tokens until 101 // the stack has been exhausted. 102 for c.pos < len(c.tokens) { 103 if err := c.compileLine(); err != nil { 104 errors = append(errors, err) 105 } 106 } 107 108 // turn the binary to hex 109 var bin string 110 for _, v := range c.binary { 111 switch v := v.(type) { 112 case vm.OpCode: 113 bin += fmt.Sprintf("%x", []byte{byte(v)}) 114 case []byte: 115 bin += fmt.Sprintf("%x", v) 116 } 117 } 118 return bin, errors 119 } 120 121 // next returns the next token and increments the 122 // position. 123 func (c *Compiler) next() token { 124 token := c.tokens[c.pos] 125 c.pos++ 126 return token 127 } 128 129 // compileLine compiles a single line instruction e.g. 130 // "push 1", "jump @label". 131 func (c *Compiler) compileLine() error { 132 n := c.next() 133 if n.typ != lineStart { 134 return compileErr(n, n.typ.String(), lineStart.String()) 135 } 136 137 lvalue := c.next() 138 switch lvalue.typ { 139 case eof: 140 return nil 141 case element: 142 if err := c.compileElement(lvalue); err != nil { 143 return err 144 } 145 case labelDef: 146 c.compileLabel() 147 case lineEnd: 148 return nil 149 default: 150 return compileErr(lvalue, lvalue.text, fmt.Sprintf("%v or %v", labelDef, element)) 151 } 152 153 if n := c.next(); n.typ != lineEnd { 154 return compileErr(n, n.text, lineEnd.String()) 155 } 156 157 return nil 158 } 159 160 // compileNumber compiles the number to bytes 161 func (c *Compiler) compileNumber(element token) (int, error) { 162 num := math.MustParseBig256(element.text).Bytes() 163 if len(num) == 0 { 164 num = []byte{0} 165 } 166 c.pushBin(num) 167 return len(num), nil 168 } 169 170 // compileElement compiles the element (push & label or both) 171 // to a binary representation and may error if incorrect statements 172 // where fed. 173 func (c *Compiler) compileElement(element token) error { 174 // check for a jump. jumps must be read and compiled 175 // from right to left. 176 if isJump(element.text) { 177 rvalue := c.next() 178 switch rvalue.typ { 179 case number: 180 // TODO figure out how to return the error properly 181 c.compileNumber(rvalue) 182 case stringValue: 183 // strings are quoted, remove them. 184 c.pushBin(rvalue.text[1 : len(rvalue.text)-2]) 185 case label: 186 c.pushBin(vm.PUSH4) 187 pos := big.NewInt(int64(c.labels[rvalue.text])).Bytes() 188 pos = append(make([]byte, 4-len(pos)), pos...) 189 c.pushBin(pos) 190 case lineEnd: 191 c.pos-- 192 default: 193 return compileErr(rvalue, rvalue.text, "number, string or label") 194 } 195 // push the operation 196 c.pushBin(toBinary(element.text)) 197 return nil 198 } else if isPush(element.text) { 199 // handle pushes. pushes are read from left to right. 200 var value []byte 201 202 rvalue := c.next() 203 switch rvalue.typ { 204 case number: 205 value = math.MustParseBig256(rvalue.text).Bytes() 206 if len(value) == 0 { 207 value = []byte{0} 208 } 209 case stringValue: 210 value = []byte(rvalue.text[1 : len(rvalue.text)-1]) 211 case label: 212 value = big.NewInt(int64(c.labels[rvalue.text])).Bytes() 213 value = append(make([]byte, 4-len(value)), value...) 214 default: 215 return compileErr(rvalue, rvalue.text, "number, string or label") 216 } 217 218 if len(value) > 32 { 219 return fmt.Errorf("%d type error: unsupported string or number with size > 32", rvalue.lineno) 220 } 221 222 c.pushBin(vm.OpCode(int(vm.PUSH1) - 1 + len(value))) 223 c.pushBin(value) 224 } else { 225 c.pushBin(toBinary(element.text)) 226 } 227 228 return nil 229 } 230 231 // compileLabel pushes a jumpdest to the binary slice. 232 func (c *Compiler) compileLabel() { 233 c.pushBin(vm.JUMPDEST) 234 } 235 236 // pushBin pushes the value v to the binary stack. 237 func (c *Compiler) pushBin(v interface{}) { 238 if c.debug { 239 fmt.Printf("%d: %v\n", len(c.binary), v) 240 } 241 c.binary = append(c.binary, v) 242 } 243 244 // isPush returns whether the string op is either any of 245 // push(N). 246 func isPush(op string) bool { 247 return strings.ToUpper(op) == "PUSH" 248 } 249 250 // isJump returns whether the string op is jump(i) 251 func isJump(op string) bool { 252 return strings.ToUpper(op) == "JUMPI" || strings.ToUpper(op) == "JUMP" 253 } 254 255 // toBinary converts text to a vm.OpCode 256 func toBinary(text string) vm.OpCode { 257 return vm.StringToOp(strings.ToUpper(text)) 258 } 259 260 type compileError struct { 261 got string 262 want string 263 264 lineno int 265 } 266 267 func (err compileError) Error() string { 268 return fmt.Sprintf("%d syntax error: unexpected %v, expected %v", err.lineno, err.got, err.want) 269 } 270 271 func compileErr(c token, got, want string) error { 272 return compileError{ 273 got: got, 274 want: want, 275 lineno: c.lineno, 276 } 277 }