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  }