<commit_before>package vm

import (
	"fmt"
	"github.com/goby-lang/goby/compiler"
	"github.com/goby-lang/goby/compiler/bytecode"
	"github.com/goby-lang/goby/compiler/parser"
	"github.com/goby-lang/goby/vm/errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

type thread struct {
	// a stack that holds call frames
	callFrameStack *callFrameStack
	// call frame pointer
	cfp int
	// data stack
	stack *stack
	// stack pointer
	sp int

	vm *VM
}

func (t *thread) isMainThread() bool {
	return t == t.vm.mainThread
}

func (t *thread) getBlock(name string, filename filename) *instructionSet {
	// The "name" here is actually an index of block
	// for example <Block:1>'s name is "1"
	is, ok := t.vm.blockTables[filename][name]

	if !ok {
		panic(fmt.Sprintf("Can't find block %s", name))
	}

	return is
}

func (t *thread) getMethodIS(name string, filename filename) (*instructionSet, bool) {
	iss, ok := t.vm.isTables[bytecode.MethodDef][name]

	if !ok {
		return nil, false
	}

	is := iss[t.vm.methodISIndexTables[filename].Data[name]]

	t.vm.methodISIndexTables[filename].Data[name]++

	return is, ok
}

func (t *thread) getClassIS(name string, filename filename) *instructionSet {
	iss, ok := t.vm.isTables[bytecode.ClassDef][name]

	if !ok {
		panic(fmt.Sprintf("Can't find class %s's instructions", name))
	}

	is := iss[t.vm.classISIndexTables[filename].Data[name]]

	t.vm.classISIndexTables[filename].Data[name]++

	return is
}

func (t *thread) execGobyLib(libName string) (err error) {
	libPath := filepath.Join(t.vm.projectRoot, "lib", libName)
	file, err := ioutil.ReadFile(libPath)

	if err != nil {
		t.vm.mainThread.pushErrorObject(errors.InternalError, -1, err.Error())
	}

	err = t.execRequiredFile(libPath, file)
	return
}

func (t *thread) execRequiredFile(filepath string, file []byte) (err error) {
	instructionSets, err := compiler.CompileToInstructions(string(file), parser.NormalMode)

	if err != nil {
		return
	}

	oldMethodTable := isTable{}
	oldClassTable := isTable{}

	// Copy current file's instruction sets.
	for name, is := range t.vm.isTables[bytecode.MethodDef] {
		oldMethodTable[name] = is
	}

	for name, is := range t.vm.isTables[bytecode.ClassDef] {
		oldClassTable[name] = is
	}

	// This creates new execution environments for required file, including new instruction set table.
	// So we need to copy old instruction sets and restore them later, otherwise current program's instruction set would be overwrite.
	t.vm.ExecInstructions(instructionSets, filepath)

	// Restore instruction sets.
	t.vm.isTables[bytecode.MethodDef] = oldMethodTable
	t.vm.isTables[bytecode.ClassDef] = oldClassTable

	return
}

func (t *thread) captureAndHandlePanic() {
	switch e := recover().(type) {
	case *Error:
		if t.vm.mode == NormalMode {
			fmt.Println(e.Message())
		} else {
			panic(e)
		}
	case error:
		fmt.Println(e.Error())
	case nil:
		return
	default:
		panic(e)
	}
}

func (t *thread) startFromTopFrame() {
	defer t.captureAndHandlePanic()
	cf := t.callFrameStack.top()
	t.evalCallFrame(cf)
}
<commit_msg>VM should exit when code failed.<commit_after>package vm

import (
	"fmt"
	"github.com/goby-lang/goby/compiler"
	"github.com/goby-lang/goby/compiler/bytecode"
	"github.com/goby-lang/goby/compiler/parser"
	"github.com/goby-lang/goby/vm/errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

type thread struct {
	// a stack that holds call frames
	callFrameStack *callFrameStack
	// call frame pointer
	cfp int
	// data stack
	stack *stack
	// stack pointer
	sp int

	vm *VM
}

func (t *thread) isMainThread() bool {
	return t == t.vm.mainThread
}

func (t *thread) getBlock(name string, filename filename) *instructionSet {
	// The "name" here is actually an index of block
	// for example <Block:1>'s name is "1"
	is, ok := t.vm.blockTables[filename][name]

	if !ok {
		panic(fmt.Sprintf("Can't find block %s", name))
	}

	return is
}

func (t *thread) getMethodIS(name string, filename filename) (*instructionSet, bool) {
	iss, ok := t.vm.isTables[bytecode.MethodDef][name]

	if !ok {
		return nil, false
	}

	is := iss[t.vm.methodISIndexTables[filename].Data[name]]

	t.vm.methodISIndexTables[filename].Data[name]++

	return is, ok
}

func (t *thread) getClassIS(name string, filename filename) *instructionSet {
	iss, ok := t.vm.isTables[bytecode.ClassDef][name]

	if !ok {
		panic(fmt.Sprintf("Can't find class %s's instructions", name))
	}

	is := iss[t.vm.classISIndexTables[filename].Data[name]]

	t.vm.classISIndexTables[filename].Data[name]++

	return is
}

func (t *thread) execGobyLib(libName string) (err error) {
	libPath := filepath.Join(t.vm.projectRoot, "lib", libName)
	file, err := ioutil.ReadFile(libPath)

	if err != nil {
		t.vm.mainThread.pushErrorObject(errors.InternalError, -1, err.Error())
	}

	err = t.execRequiredFile(libPath, file)
	return
}

func (t *thread) execRequiredFile(filepath string, file []byte) (err error) {
	instructionSets, err := compiler.CompileToInstructions(string(file), parser.NormalMode)

	if err != nil {
		return
	}

	oldMethodTable := isTable{}
	oldClassTable := isTable{}

	// Copy current file's instruction sets.
	for name, is := range t.vm.isTables[bytecode.MethodDef] {
		oldMethodTable[name] = is
	}

	for name, is := range t.vm.isTables[bytecode.ClassDef] {
		oldClassTable[name] = is
	}

	// This creates new execution environments for required file, including new instruction set table.
	// So we need to copy old instruction sets and restore them later, otherwise current program's instruction set would be overwrite.
	t.vm.ExecInstructions(instructionSets, filepath)

	// Restore instruction sets.
	t.vm.isTables[bytecode.MethodDef] = oldMethodTable
	t.vm.isTables[bytecode.ClassDef] = oldClassTable

	return
}

func (t *thread) captureAndHandlePanic() {
	switch e := recover().(type) {
	case *Error:
		if t.vm.mode == NormalMode {
			fmt.Println(e.Message())
			if t.isMainThread() {
				os.Exit(1)
			}
		} else {
			panic(e)
		}
	case error:
		fmt.Println(e.Error())
	case nil:
		return
	default:
		panic(e)
	}
}

func (t *thread) startFromTopFrame() {
	defer t.captureAndHandlePanic()
	cf := t.callFrameStack.top()
	t.evalCallFrame(cf)
}

{'fraction_non_alphanumeric': 0.13754465735628452, 'fraction_numerical': 0.0011367327054238388, 'mean_word_length': 4.184343434343434, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2584999', 'n_tokens_mistral': 2290, 'n_tokens_neox': 2184, 'n_words': 542}