github.com/expr-lang/expr@v1.16.9/vm/program.go (about) 1 package vm 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "reflect" 8 "regexp" 9 "strings" 10 "text/tabwriter" 11 12 "github.com/expr-lang/expr/ast" 13 "github.com/expr-lang/expr/builtin" 14 "github.com/expr-lang/expr/file" 15 "github.com/expr-lang/expr/vm/runtime" 16 ) 17 18 // Program represents a compiled expression. 19 type Program struct { 20 Bytecode []Opcode 21 Arguments []int 22 Constants []any 23 24 source file.Source 25 node ast.Node 26 locations []file.Location 27 variables int 28 functions []Function 29 debugInfo map[string]string 30 span *Span 31 } 32 33 // NewProgram returns a new Program. It's used by the compiler. 34 func NewProgram( 35 source file.Source, 36 node ast.Node, 37 locations []file.Location, 38 variables int, 39 constants []any, 40 bytecode []Opcode, 41 arguments []int, 42 functions []Function, 43 debugInfo map[string]string, 44 span *Span, 45 ) *Program { 46 return &Program{ 47 source: source, 48 node: node, 49 locations: locations, 50 variables: variables, 51 Constants: constants, 52 Bytecode: bytecode, 53 Arguments: arguments, 54 functions: functions, 55 debugInfo: debugInfo, 56 span: span, 57 } 58 } 59 60 // Source returns origin file.Source. 61 func (program *Program) Source() file.Source { 62 return program.source 63 } 64 65 // Node returns origin ast.Node. 66 func (program *Program) Node() ast.Node { 67 return program.node 68 } 69 70 // Locations returns a slice of bytecode's locations. 71 func (program *Program) Locations() []file.Location { 72 return program.locations 73 } 74 75 // Disassemble returns opcodes as a string. 76 func (program *Program) Disassemble() string { 77 var buf bytes.Buffer 78 w := tabwriter.NewWriter(&buf, 0, 0, 2, ' ', 0) 79 program.DisassembleWriter(w) 80 _ = w.Flush() 81 return buf.String() 82 } 83 84 // DisassembleWriter takes a writer and writes opcodes to it. 85 func (program *Program) DisassembleWriter(w io.Writer) { 86 ip := 0 87 for ip < len(program.Bytecode) { 88 pp := ip 89 op := program.Bytecode[ip] 90 arg := program.Arguments[ip] 91 ip += 1 92 93 code := func(label string) { 94 _, _ = fmt.Fprintf(w, "%v\t%v\n", pp, label) 95 } 96 jump := func(label string) { 97 _, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\t(%v)\n", pp, label, arg, ip+arg) 98 } 99 jumpBack := func(label string) { 100 _, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\t(%v)\n", pp, label, arg, ip-arg) 101 } 102 argument := func(label string) { 103 _, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\n", pp, label, arg) 104 } 105 argumentWithInfo := func(label string, prefix string) { 106 _, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\t%v\n", pp, label, arg, program.debugInfo[fmt.Sprintf("%s_%d", prefix, arg)]) 107 } 108 constant := func(label string) { 109 var c any 110 if arg < len(program.Constants) { 111 c = program.Constants[arg] 112 } else { 113 c = "out of range" 114 } 115 if r, ok := c.(*regexp.Regexp); ok { 116 c = r.String() 117 } 118 if field, ok := c.(*runtime.Field); ok { 119 c = fmt.Sprintf("{%v %v}", strings.Join(field.Path, "."), field.Index) 120 } 121 if method, ok := c.(*runtime.Method); ok { 122 c = fmt.Sprintf("{%v %v}", method.Name, method.Index) 123 } 124 _, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\t%v\n", pp, label, arg, c) 125 } 126 builtinArg := func(label string) { 127 _, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\t%v\n", pp, label, arg, builtin.Builtins[arg].Name) 128 } 129 130 switch op { 131 case OpInvalid: 132 code("OpInvalid") 133 134 case OpPush: 135 constant("OpPush") 136 137 case OpInt: 138 argument("OpInt") 139 140 case OpPop: 141 code("OpPop") 142 143 case OpStore: 144 argumentWithInfo("OpStore", "var") 145 146 case OpLoadVar: 147 argumentWithInfo("OpLoadVar", "var") 148 149 case OpLoadConst: 150 constant("OpLoadConst") 151 152 case OpLoadField: 153 constant("OpLoadField") 154 155 case OpLoadFast: 156 constant("OpLoadFast") 157 158 case OpLoadMethod: 159 constant("OpLoadMethod") 160 161 case OpLoadFunc: 162 argumentWithInfo("OpLoadFunc", "func") 163 164 case OpLoadEnv: 165 code("OpLoadEnv") 166 167 case OpFetch: 168 code("OpFetch") 169 170 case OpFetchField: 171 constant("OpFetchField") 172 173 case OpMethod: 174 constant("OpMethod") 175 176 case OpTrue: 177 code("OpTrue") 178 179 case OpFalse: 180 code("OpFalse") 181 182 case OpNil: 183 code("OpNil") 184 185 case OpNegate: 186 code("OpNegate") 187 188 case OpNot: 189 code("OpNot") 190 191 case OpEqual: 192 code("OpEqual") 193 194 case OpEqualInt: 195 code("OpEqualInt") 196 197 case OpEqualString: 198 code("OpEqualString") 199 200 case OpJump: 201 jump("OpJump") 202 203 case OpJumpIfTrue: 204 jump("OpJumpIfTrue") 205 206 case OpJumpIfFalse: 207 jump("OpJumpIfFalse") 208 209 case OpJumpIfNil: 210 jump("OpJumpIfNil") 211 212 case OpJumpIfNotNil: 213 jump("OpJumpIfNotNil") 214 215 case OpJumpIfEnd: 216 jump("OpJumpIfEnd") 217 218 case OpJumpBackward: 219 jumpBack("OpJumpBackward") 220 221 case OpIn: 222 code("OpIn") 223 224 case OpLess: 225 code("OpLess") 226 227 case OpMore: 228 code("OpMore") 229 230 case OpLessOrEqual: 231 code("OpLessOrEqual") 232 233 case OpMoreOrEqual: 234 code("OpMoreOrEqual") 235 236 case OpAdd: 237 code("OpAdd") 238 239 case OpSubtract: 240 code("OpSubtract") 241 242 case OpMultiply: 243 code("OpMultiply") 244 245 case OpDivide: 246 code("OpDivide") 247 248 case OpModulo: 249 code("OpModulo") 250 251 case OpExponent: 252 code("OpExponent") 253 254 case OpRange: 255 code("OpRange") 256 257 case OpMatches: 258 code("OpMatches") 259 260 case OpMatchesConst: 261 constant("OpMatchesConst") 262 263 case OpContains: 264 code("OpContains") 265 266 case OpStartsWith: 267 code("OpStartsWith") 268 269 case OpEndsWith: 270 code("OpEndsWith") 271 272 case OpSlice: 273 code("OpSlice") 274 275 case OpCall: 276 argument("OpCall") 277 278 case OpCall0: 279 argumentWithInfo("OpCall0", "func") 280 281 case OpCall1: 282 argumentWithInfo("OpCall1", "func") 283 284 case OpCall2: 285 argumentWithInfo("OpCall2", "func") 286 287 case OpCall3: 288 argumentWithInfo("OpCall3", "func") 289 290 case OpCallN: 291 argument("OpCallN") 292 293 case OpCallFast: 294 argument("OpCallFast") 295 296 case OpCallSafe: 297 argument("OpCallSafe") 298 299 case OpCallTyped: 300 signature := reflect.TypeOf(FuncTypes[arg]).Elem().String() 301 _, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\t%v\n", pp, "OpCallTyped", arg, signature) 302 303 case OpCallBuiltin1: 304 builtinArg("OpCallBuiltin1") 305 306 case OpArray: 307 code("OpArray") 308 309 case OpMap: 310 code("OpMap") 311 312 case OpLen: 313 code("OpLen") 314 315 case OpCast: 316 argument("OpCast") 317 318 case OpDeref: 319 code("OpDeref") 320 321 case OpIncrementIndex: 322 code("OpIncrementIndex") 323 324 case OpDecrementIndex: 325 code("OpDecrementIndex") 326 327 case OpIncrementCount: 328 code("OpIncrementCount") 329 330 case OpGetIndex: 331 code("OpGetIndex") 332 333 case OpGetCount: 334 code("OpGetCount") 335 336 case OpGetLen: 337 code("OpGetLen") 338 339 case OpGetAcc: 340 code("OpGetAcc") 341 342 case OpSetAcc: 343 code("OpSetAcc") 344 345 case OpSetIndex: 346 code("OpSetIndex") 347 348 case OpPointer: 349 code("OpPointer") 350 351 case OpThrow: 352 code("OpThrow") 353 354 case OpCreate: 355 argument("OpCreate") 356 357 case OpGroupBy: 358 code("OpGroupBy") 359 360 case OpSortBy: 361 code("OpSortBy") 362 363 case OpSort: 364 code("OpSort") 365 366 case OpProfileStart: 367 code("OpProfileStart") 368 369 case OpProfileEnd: 370 code("OpProfileEnd") 371 372 case OpBegin: 373 code("OpBegin") 374 375 case OpEnd: 376 code("OpEnd") 377 378 default: 379 _, _ = fmt.Fprintf(w, "%v\t%#x (unknown)\n", ip, op) 380 } 381 } 382 }