mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
Add ast comments
Rename program.Opcodes() to program.DisassembleWriter()
This commit is contained in:
+105
-46
@@ -16,179 +16,238 @@ type Node interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
// Patch replaces the node with a new one.
|
||||
// Location information is preserved.
|
||||
// Type information is lost.
|
||||
func Patch(node *Node, newNode Node) {
|
||||
newNode.SetLocation((*node).Location())
|
||||
*node = newNode
|
||||
}
|
||||
|
||||
// base is a base struct for all nodes.
|
||||
type base struct {
|
||||
loc file.Location
|
||||
nodeType reflect.Type
|
||||
}
|
||||
|
||||
// Location returns the location of the node in the source code.
|
||||
func (n *base) Location() file.Location {
|
||||
return n.loc
|
||||
}
|
||||
|
||||
// SetLocation sets the location of the node in the source code.
|
||||
func (n *base) SetLocation(loc file.Location) {
|
||||
n.loc = loc
|
||||
}
|
||||
|
||||
// Type returns the type of the node.
|
||||
func (n *base) Type() reflect.Type {
|
||||
return n.nodeType
|
||||
}
|
||||
|
||||
// SetType sets the type of the node.
|
||||
func (n *base) SetType(t reflect.Type) {
|
||||
n.nodeType = t
|
||||
}
|
||||
|
||||
// NilNode represents nil.
|
||||
type NilNode struct {
|
||||
base
|
||||
}
|
||||
|
||||
// IdentifierNode represents an identifier.
|
||||
type IdentifierNode struct {
|
||||
base
|
||||
Value string
|
||||
FieldIndex []int
|
||||
Method bool // true if method, false if field
|
||||
MethodIndex int // index of method, set only if Method is true
|
||||
Value string // Name of the identifier. Like "foo" in "foo.bar".
|
||||
FieldIndex []int // Index of the field in the list of fields.
|
||||
Method bool // If true then the identifier is a method call.
|
||||
MethodIndex int // Index of the method in the list of methods.
|
||||
}
|
||||
|
||||
// SetFieldIndex sets the field index of the identifier.
|
||||
func (n *IdentifierNode) SetFieldIndex(field []int) {
|
||||
n.FieldIndex = field
|
||||
}
|
||||
|
||||
// SetMethodIndex sets the method index of the identifier.
|
||||
func (n *IdentifierNode) SetMethodIndex(methodIndex int) {
|
||||
n.Method = true
|
||||
n.MethodIndex = methodIndex
|
||||
}
|
||||
|
||||
// IntegerNode represents an integer.
|
||||
type IntegerNode struct {
|
||||
base
|
||||
Value int
|
||||
Value int // Value of the integer.
|
||||
}
|
||||
|
||||
// FloatNode represents a float.
|
||||
type FloatNode struct {
|
||||
base
|
||||
Value float64
|
||||
Value float64 // Value of the float.
|
||||
}
|
||||
|
||||
// BoolNode represents a boolean.
|
||||
type BoolNode struct {
|
||||
base
|
||||
Value bool
|
||||
Value bool // Value of the boolean.
|
||||
}
|
||||
|
||||
// StringNode represents a string.
|
||||
type StringNode struct {
|
||||
base
|
||||
Value string
|
||||
Value string // Value of the string.
|
||||
}
|
||||
|
||||
// ConstantNode represents a constant.
|
||||
// Constants are predefined values like nil, true, false, array, map, etc.
|
||||
// The parser.Parse will never generate ConstantNode, it is only generated
|
||||
// by the optimizer.
|
||||
type ConstantNode struct {
|
||||
base
|
||||
Value any
|
||||
Value any // Value of the constant.
|
||||
}
|
||||
|
||||
// UnaryNode represents a unary operator.
|
||||
type UnaryNode struct {
|
||||
base
|
||||
Operator string
|
||||
Node Node
|
||||
Operator string // Operator of the unary operator. Like "!" in "!foo" or "not" in "not foo".
|
||||
Node Node // Node of the unary operator. Like "foo" in "!foo".
|
||||
}
|
||||
|
||||
// BinaryNode represents a binary operator.
|
||||
type BinaryNode struct {
|
||||
base
|
||||
Regexp *regexp.Regexp
|
||||
Operator string
|
||||
Left Node
|
||||
Right Node
|
||||
Regexp *regexp.Regexp // Regexp of the "matches" operator. Like "f.+" in `foo matches "f.+"`.
|
||||
Operator string // Operator of the binary operator. Like "+" in "foo + bar" or "matches" in "foo matches bar".
|
||||
Left Node // Left node of the binary operator.
|
||||
Right Node // Right node of the binary operator.
|
||||
}
|
||||
|
||||
// ChainNode represents an optional chaining group.
|
||||
// A few MemberNode nodes can be chained together,
|
||||
// and will be wrapped in a ChainNode. Example:
|
||||
// ```
|
||||
// foo.bar?.baz?.qux
|
||||
// ```
|
||||
// The whole chain will be wrapped in a ChainNode.
|
||||
type ChainNode struct {
|
||||
base
|
||||
Node Node
|
||||
Node Node // Node of the chain.
|
||||
}
|
||||
|
||||
// MemberNode represents a member access.
|
||||
// It can be a field access, a method call,
|
||||
// or an array element access.
|
||||
// Example:
|
||||
// ```
|
||||
// foo.bar or foo["bar"]
|
||||
// foo.bar()
|
||||
// array[0]
|
||||
// ```
|
||||
type MemberNode struct {
|
||||
base
|
||||
Node Node
|
||||
Property Node
|
||||
Node Node // Node of the member access. Like "foo" in "foo.bar".
|
||||
Property Node // Property of the member access. For property access it is a StringNode.
|
||||
Name string // Name of the filed or method. Used for error reporting.
|
||||
Optional bool
|
||||
FieldIndex []int
|
||||
Optional bool // If true then the member access is optional. Like "foo?.bar".
|
||||
FieldIndex []int // Index sequence of fields. Generated by type checker.
|
||||
|
||||
// TODO: Combine Method and MethodIndex into a single MethodIndex field of &int type.
|
||||
Method bool
|
||||
MethodIndex int
|
||||
Method bool // If true then the member access is a method call.
|
||||
MethodIndex int // Index of the method in the list of methods. Generated by type checker.
|
||||
}
|
||||
|
||||
// SetFieldIndex sets the field index of the member access.
|
||||
func (n *MemberNode) SetFieldIndex(field []int) {
|
||||
n.FieldIndex = field
|
||||
}
|
||||
|
||||
// SetMethodIndex sets the method index of the member access.
|
||||
func (n *MemberNode) SetMethodIndex(methodIndex int) {
|
||||
n.Method = true
|
||||
n.MethodIndex = methodIndex
|
||||
}
|
||||
|
||||
// SliceNode represents access to a slice of an array.
|
||||
// Example:
|
||||
// ```
|
||||
// array[1:4]
|
||||
// ```
|
||||
type SliceNode struct {
|
||||
base
|
||||
Node Node
|
||||
From Node
|
||||
To Node
|
||||
Node Node // Node of the slice. Like "array" in "array[1:4]".
|
||||
From Node // From an index of the array. Like "1" in "array[1:4]".
|
||||
To Node // To an index of the array. Like "4" in "array[1:4]".
|
||||
}
|
||||
|
||||
// CallNode represents a function or a method call.
|
||||
type CallNode struct {
|
||||
base
|
||||
Callee Node
|
||||
Arguments []Node
|
||||
Typed int
|
||||
Fast bool
|
||||
Func *Function
|
||||
Callee Node // Node of the call. Like "foo" in "foo()".
|
||||
Arguments []Node // Arguments of the call.
|
||||
Typed int // If more than 0 then the call is one of the types from vm.FuncTypes.
|
||||
Fast bool // If true then the call type is "func(...any) any".
|
||||
Func *Function // If not nil then the func is provider via expr.Function.
|
||||
}
|
||||
|
||||
// BuiltinNode represents a builtin function call.
|
||||
type BuiltinNode struct {
|
||||
base
|
||||
Name string
|
||||
Arguments []Node
|
||||
Throws bool
|
||||
Map Node
|
||||
Name string // Name of the builtin function. Like "len" in "len(foo)".
|
||||
Arguments []Node // Arguments of the builtin function.
|
||||
Throws bool // If true then accessing a field or array index can throw an error. Used by optimizer.
|
||||
Map Node // Used by optimizer to fold filter() and map() builtins.
|
||||
}
|
||||
|
||||
// ClosureNode represents a predicate.
|
||||
// Example:
|
||||
// ```
|
||||
// filter(foo, .bar == 1)
|
||||
// ```
|
||||
// The predicate is `.bar == 1`.
|
||||
type ClosureNode struct {
|
||||
base
|
||||
Node Node
|
||||
Node Node // Node of the predicate body.
|
||||
}
|
||||
|
||||
// PointerNode represents a pointer to a current value in predicate.
|
||||
type PointerNode struct {
|
||||
base
|
||||
Name string
|
||||
Name string // Name of the pointer. Like "index" in "#index".
|
||||
}
|
||||
|
||||
// ConditionalNode represents a ternary operator.
|
||||
type ConditionalNode struct {
|
||||
base
|
||||
Cond Node
|
||||
Exp1 Node
|
||||
Exp2 Node
|
||||
Cond Node // Condition of the ternary operator. Like "foo" in "foo ? bar : baz".
|
||||
Exp1 Node // Expression 1 of the ternary operator. Like "bar" in "foo ? bar : baz".
|
||||
Exp2 Node // Expression 2 of the ternary operator. Like "baz" in "foo ? bar : baz".
|
||||
}
|
||||
|
||||
// VariableDeclaratorNode represents a variable declaration.
|
||||
type VariableDeclaratorNode struct {
|
||||
base
|
||||
Name string
|
||||
Value Node
|
||||
Expr Node
|
||||
Name string // Name of the variable. Like "foo" in "let foo = 1; foo + 1".
|
||||
Value Node // Value of the variable. Like "1" in "let foo = 1; foo + 1".
|
||||
Expr Node // Expression of the variable. Like "foo + 1" in "let foo = 1; foo + 1".
|
||||
}
|
||||
|
||||
// ArrayNode represents an array.
|
||||
type ArrayNode struct {
|
||||
base
|
||||
Nodes []Node
|
||||
Nodes []Node // Nodes of the array.
|
||||
}
|
||||
|
||||
// MapNode represents a map.
|
||||
type MapNode struct {
|
||||
base
|
||||
Pairs []Node
|
||||
Pairs []Node // PairNode nodes.
|
||||
}
|
||||
|
||||
// PairNode represents a key-value pair of a map.
|
||||
type PairNode struct {
|
||||
base
|
||||
Key Node
|
||||
Value Node
|
||||
Key Node // Key of the pair.
|
||||
Value Node // Value of the pair.
|
||||
}
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ func StartDebugger(program *Program, env any) {
|
||||
|
||||
index := make(map[int]int)
|
||||
var buf strings.Builder
|
||||
program.Opcodes(&buf)
|
||||
program.DisassembleWriter(&buf)
|
||||
|
||||
for row, line := range strings.Split(buf.String(), "\n") {
|
||||
if line == "" {
|
||||
|
||||
+6
-2
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/antonmedv/expr/vm/runtime"
|
||||
)
|
||||
|
||||
// Program represents a compiled expression.
|
||||
type Program struct {
|
||||
Bytecode []Opcode
|
||||
Arguments []int
|
||||
@@ -26,6 +27,7 @@ type Program struct {
|
||||
debugInfo map[string]string
|
||||
}
|
||||
|
||||
// NewProgram returns a new Program. It's used by the compiler.
|
||||
func NewProgram(
|
||||
source *file.Source,
|
||||
locations []file.Location,
|
||||
@@ -48,15 +50,17 @@ func NewProgram(
|
||||
}
|
||||
}
|
||||
|
||||
// Disassemble returns opcodes as a string.
|
||||
func (program *Program) Disassemble() string {
|
||||
var buf bytes.Buffer
|
||||
w := tabwriter.NewWriter(&buf, 0, 0, 2, ' ', 0)
|
||||
program.Opcodes(w)
|
||||
program.DisassembleWriter(w)
|
||||
_ = w.Flush()
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (program *Program) Opcodes(w io.Writer) {
|
||||
// DisassembleWriter takes a writer and writes opcodes to it.
|
||||
func (program *Program) DisassembleWriter(w io.Writer) {
|
||||
ip := 0
|
||||
for ip < len(program.Bytecode) {
|
||||
pp := ip
|
||||
|
||||
Reference in New Issue
Block a user