Skip to content

Commit

Permalink
Add build tag: expr_debug (#521)
Browse files Browse the repository at this point in the history
  • Loading branch information
antonmedv authored Jan 13, 2024
1 parent 1b3c10c commit db94b96
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 86 deletions.
11 changes: 11 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,14 @@ jobs:
go-version: ${{ matrix.go-version }}
- name: Test
run: go test ./...

debug:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Go 1.18
uses: actions/setup-go@v4
with:
go-version: 1.18
- name: Test
run: go test -tags=expr_debug -run=TestDebugger -v ./vm
10 changes: 7 additions & 3 deletions debug/debugger.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import (
"strings"
"time"

. "github.com/expr-lang/expr/vm"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"

. "github.com/expr-lang/expr/vm"
)

func StartDebugger(program *Program, env any) {
Expand Down Expand Up @@ -112,14 +113,17 @@ func StartDebugger(program *Program, env any) {
}

stack.Clear()
for i, value := range vm.Stack() {
for i, value := range vm.Stack {
stack.SetCellSimple(i, 0, fmt.Sprintf("% *d: ", 2, i))
stack.SetCellSimple(i, 1, fmt.Sprintf("%#v", value))
}
stack.ScrollToEnd()

scope.Clear()
s := vm.Scope()
var s *Scope
if len(vm.Scopes) > 0 {
s = vm.Scopes[len(vm.Scopes)-1]
}
if s != nil {
type pair struct {
key string
Expand Down
5 changes: 5 additions & 0 deletions vm/debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//go:build expr_debug

package vm

const debug = true
5 changes: 5 additions & 0 deletions vm/debug_off.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//go:build !expr_debug

package vm

const debug = false
40 changes: 40 additions & 0 deletions vm/debug_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//go:build expr_debug

package vm_test

import (
"testing"

"github.com/stretchr/testify/require"

"github.com/expr-lang/expr/compiler"
"github.com/expr-lang/expr/parser"
"github.com/expr-lang/expr/vm"
)

func TestDebugger(t *testing.T) {
input := `[1, 2]`

node, err := parser.Parse(input)
require.NoError(t, err)

program, err := compiler.Compile(node, nil)
require.NoError(t, err)

debug := vm.Debug()
go func() {
debug.Step()
debug.Step()
debug.Step()
debug.Step()
}()
go func() {
for range debug.Position() {
}
}()

_, err = debug.Run(program, nil)
require.NoError(t, err)
require.Len(t, debug.Stack, 0)
require.Nil(t, debug.Scopes)
}
11 changes: 11 additions & 0 deletions vm/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package vm

import (
"reflect"
)

type Function = func(params ...any) (any, error)

var MemoryBudget uint = 1e6

var errorType = reflect.TypeOf((*error)(nil)).Elem()
100 changes: 44 additions & 56 deletions vm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ import (
"github.com/expr-lang/expr/vm/runtime"
)

var MemoryBudget uint = 1e6
var errorType = reflect.TypeOf((*error)(nil)).Elem()

type Function = func(params ...any) (any, error)

func Run(program *Program, env any) (any, error) {
if program == nil {
return nil, fmt.Errorf("program is nil")
Expand All @@ -27,15 +22,24 @@ func Run(program *Program, env any) (any, error) {
return vm.Run(program, env)
}

func Debug() *VM {
vm := &VM{
debug: true,
step: make(chan struct{}, 0),
curr: make(chan int, 0),
}
return vm
}

type VM struct {
stack []any
Stack []any
Scopes []*Scope
ip int
scopes []*Scope
memory uint
memoryBudget uint
debug bool
step chan struct{}
curr chan int
memory uint
memoryBudget uint
}

type Scope struct {
Expand All @@ -47,15 +51,6 @@ type Scope struct {
Acc any
}

func Debug() *VM {
vm := &VM{
debug: true,
step: make(chan struct{}, 0),
curr: make(chan int, 0),
}
return vm
}

func (vm *VM) Run(program *Program, env any) (_ any, err error) {
defer func() {
if r := recover(); r != nil {
Expand All @@ -74,22 +69,22 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) {
}
}()

if vm.stack == nil {
vm.stack = make([]any, 0, 2)
if vm.Stack == nil {
vm.Stack = make([]any, 0, 2)
} else {
vm.stack = vm.stack[0:0]
vm.Stack = vm.Stack[0:0]
}

if vm.scopes != nil {
vm.scopes = vm.scopes[0:0]
if vm.Scopes != nil {
vm.Scopes = vm.Scopes[0:0]
}

vm.memoryBudget = MemoryBudget
vm.memory = 0
vm.ip = 0

for vm.ip < len(program.Bytecode) {
if vm.debug {
if debug && vm.debug {
<-vm.step
}

Expand Down Expand Up @@ -204,7 +199,7 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) {
}

case OpJumpIfEnd:
scope := vm.Scope()
scope := vm.scope()
if scope.Index >= scope.Len {
vm.ip += arg
}
Expand Down Expand Up @@ -399,7 +394,7 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) {

case OpValidateArgs:
fn := vm.pop().(Function)
mem, err := fn(vm.stack[len(vm.stack)-arg:]...)
mem, err := fn(vm.Stack[len(vm.Stack)-arg:]...)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -443,49 +438,49 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) {
vm.push(runtime.Deref(a))

case OpIncrementIndex:
vm.Scope().Index++
vm.scope().Index++

case OpDecrementIndex:
scope := vm.Scope()
scope := vm.scope()
scope.Index--

case OpIncrementCount:
scope := vm.Scope()
scope := vm.scope()
scope.Count++

case OpGetIndex:
vm.push(vm.Scope().Index)
vm.push(vm.scope().Index)

case OpSetIndex:
scope := vm.Scope()
scope := vm.scope()
scope.Index = vm.pop().(int)

case OpGetCount:
scope := vm.Scope()
scope := vm.scope()
vm.push(scope.Count)

case OpGetLen:
scope := vm.Scope()
scope := vm.scope()
vm.push(scope.Len)

case OpGetGroupBy:
vm.push(vm.Scope().GroupBy)
vm.push(vm.scope().GroupBy)

case OpGetAcc:
vm.push(vm.Scope().Acc)
vm.push(vm.scope().Acc)

case OpSetAcc:
vm.Scope().Acc = vm.pop()
vm.scope().Acc = vm.pop()

case OpPointer:
scope := vm.Scope()
scope := vm.scope()
vm.push(scope.Array.Index(scope.Index).Interface())

case OpThrow:
panic(vm.pop().(error))

case OpGroupBy:
scope := vm.Scope()
scope := vm.scope()
if scope.GroupBy == nil {
scope.GroupBy = make(map[any][]any)
}
Expand All @@ -496,46 +491,46 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) {
case OpBegin:
a := vm.pop()
array := reflect.ValueOf(a)
vm.scopes = append(vm.scopes, &Scope{
vm.Scopes = append(vm.Scopes, &Scope{
Array: array,
Len: array.Len(),
})

case OpEnd:
vm.scopes = vm.scopes[:len(vm.scopes)-1]
vm.Scopes = vm.Scopes[:len(vm.Scopes)-1]

default:
panic(fmt.Sprintf("unknown bytecode %#x", op))
}

if vm.debug {
if debug && vm.debug {
vm.curr <- vm.ip
}
}

if vm.debug {
if debug && vm.debug {
close(vm.curr)
close(vm.step)
}

if len(vm.stack) > 0 {
if len(vm.Stack) > 0 {
return vm.pop(), nil
}

return nil, nil
}

func (vm *VM) push(value any) {
vm.stack = append(vm.stack, value)
vm.Stack = append(vm.Stack, value)
}

func (vm *VM) current() any {
return vm.stack[len(vm.stack)-1]
return vm.Stack[len(vm.Stack)-1]
}

func (vm *VM) pop() any {
value := vm.stack[len(vm.stack)-1]
vm.stack = vm.stack[:len(vm.stack)-1]
value := vm.Stack[len(vm.Stack)-1]
vm.Stack = vm.Stack[:len(vm.Stack)-1]
return value
}

Expand All @@ -546,15 +541,8 @@ func (vm *VM) memGrow(size uint) {
}
}

func (vm *VM) Stack() []any {
return vm.stack
}

func (vm *VM) Scope() *Scope {
if len(vm.scopes) > 0 {
return vm.scopes[len(vm.scopes)-1]
}
return nil
func (vm *VM) scope() *Scope {
return vm.Scopes[len(vm.Scopes)-1]
}

func (vm *VM) Step() {
Expand Down
27 changes: 0 additions & 27 deletions vm/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,33 +20,6 @@ func TestRun_NilProgram(t *testing.T) {
require.Error(t, err)
}

func TestRun_Debugger(t *testing.T) {
input := `[1, 2]`

node, err := parser.Parse(input)
require.NoError(t, err)

program, err := compiler.Compile(node, nil)
require.NoError(t, err)

debug := vm.Debug()
go func() {
debug.Step()
debug.Step()
debug.Step()
debug.Step()
}()
go func() {
for range debug.Position() {
}
}()

_, err = debug.Run(program, nil)
require.NoError(t, err)
require.Len(t, debug.Stack(), 0)
require.Nil(t, debug.Scope())
}

func TestRun_ReuseVM(t *testing.T) {
node, err := parser.Parse(`map(1..2, {#})`)
require.NoError(t, err)
Expand Down

0 comments on commit db94b96

Please sign in to comment.