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