<commit_before>package runtime

import (
	"io"
	"os"

	"github.com/bruston/roost/parser"
)

type Value interface {
	Type() ValueType
	Value() interface{}
}

type Stack struct {
	data []Value
	top  int
}

func (s *Stack) Push(v Value) {
	s.top++
	s.data[s.top] = v
}

func (s *Stack) Pop() Value {
	v := s.data[s.top]
	s.data[s.top] = nil
	s.top--
	return v
}

func (s *Stack) Peek() Value { return s.data[s.top] }

func (s *Stack) Dup() { s.Push(s.data[s.top]) }

func (s *Stack) Drop() { s.Pop() }

func (s *Stack) Swap() {
	s.data[s.top], s.data[s.top-1] = s.data[s.top-1], s.data[s.top]
}

func (s *Stack) PushBool(b bool) { s.Push(BoolValue{ValueBool, b}) }

func (s *Stack) PushNum(n float64) { s.Push(NumValue{ValueNum, n}) }

func (s *Stack) PushString(str string) { s.Push(StringValue{ValueString, str}) }

func (s *Stack) PushBlob(b []byte) { s.Push(&BlobValue{ValueBlob, b}) }

func NewStack(size int) *Stack { return &Stack{data: make([]Value, size), top: -1} }

type Env struct {
	Stack   *Stack
	Return  *Stack
	Stdin   io.Reader
	Stdout  io.Writer
	Stderr  io.Writer
	Builtin map[string]FuncValue
	Vars    map[string]Value
	Words   map[string]*parser.NodeWordDef
}

func New(stackSize int) *Env {
	return &Env{
		Stack:   NewStack(stackSize),
		Return:  NewStack(stackSize),
		Stdin:   os.Stdin,
		Stdout:  os.Stdout,
		Stderr:  os.Stderr,
		Builtin: Builtin,
		Vars:    make(map[string]Value),
		Words:   make(map[string]*parser.NodeWordDef),
	}
}

func (ev *Evaluator) Visit(node parser.Node) parser.Visitor {
	switch n := node.(type) {
	case *parser.NodeWordDef:
		ev.env.Words[n.Identifier] = n
	case parser.NodeWord:
		if word, ok := ev.env.Words[n.Identifier]; ok {<commit_msg>Recover stack underflow panics<commit_after>package runtime

import (
	"fmt"
	"io"
	"os"

	"github.com/bruston/roost/parser"
)

type Value interface {
	Type() ValueType
	Value() interface{}
}

type Stack struct {
	data []Value
	top  int
}

func (s *Stack) Push(v Value) {
	s.top++
	s.data[s.top] = v
}

func (s *Stack) Pop() Value {
	v := s.data[s.top]
	s.data[s.top] = nil
	s.top--
	return v
}

func (s *Stack) Peek() Value { return s.data[s.top] }

func (s *Stack) Dup() { s.Push(s.data[s.top]) }

func (s *Stack) Drop() { s.Pop() }

func (s *Stack) Swap() {
	s.data[s.top], s.data[s.top-1] = s.data[s.top-1], s.data[s.top]
}

func (s *Stack) PushBool(b bool) { s.Push(BoolValue{ValueBool, b}) }

func (s *Stack) PushNum(n float64) { s.Push(NumValue{ValueNum, n}) }

func (s *Stack) PushString(str string) { s.Push(StringValue{ValueString, str}) }

func (s *Stack) PushBlob(b []byte) { s.Push(&BlobValue{ValueBlob, b}) }

func NewStack(size int) *Stack { return &Stack{data: make([]Value, size), top: -1} }

type Env struct {
	Stack   *Stack
	Return  *Stack
	Stdin   io.Reader
	Stdout  io.Writer
	Stderr  io.Writer
	Builtin map[string]FuncValue
	Vars    map[string]Value
	Words   map[string]*parser.NodeWordDef
}

func New(stackSize int) *Env {
	return &Env{
		Stack:   NewStack(stackSize),
		Return:  NewStack(stackSize),
		Stdin:   os.Stdin,
		Stdout:  os.Stdout,
		Stderr:  os.Stderr,
		Builtin: Builtin,
		Vars:    make(map[string]Value),
		Words:   make(map[string]*parser.NodeWordDef),
	}
}

func (ev *Evaluator) Visit(node parser.Node) parser.Visitor {
	defer func() {
		if r := recover(); r != nil {
			fmt.Fprintln(ev.env.Stderr, "%s", r)
		}
	}()
	switch n := node.(type) {
	case *parser.NodeWordDef:
		ev.env.Words[n.Identifier] = n
	case parser.NodeWord:
		if word, ok := ev.env.Words[n.Identifier]; ok {
{'fraction_non_alphanumeric': 0.1721194879089616, 'fraction_numerical': 0.002844950213371266, 'mean_word_length': 3.790190735694823, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '7347277', 'n_tokens_mistral': 1495, 'n_tokens_neox': 1450, 'n_words': 342}