<commit_before>package interpreter

import (
	"bytes"
	"clingon"
	"container/vector"
	"exp/eval"
	"fmt"
	"go/token"
	"io/ioutil"
	"os"
	"spectrum"
	"spectrum/formats"
	"strings"
	"sync"
	"time"
)

type UserInterfaceSettings interface {
	ResizeVideo(scale2x, fullscreen bool)
	ShowPaintedRegions(enable bool)
	EnableAudio(enable bool)
	SetAudioFreq(freq uint) // 0 means "default frequency"
	SetAudioQuality(hqAudio bool)
}

// ==============
// Some variables
// ==============

// These variables are set only once, before starting new goroutines,
// so there is no need for controlling concurrent access via a sync.Mutex
var (
	app                 *spectrum.Application
	cmdLineArg          string // The 1st non-flag command-line argument, or empty string
	speccy              *spectrum.Spectrum48k
	w                   *eval.World
	buffer              *bytes.Buffer // Stores the output of executed script-functions
	IgnoreStartupScript = false
)

// These variables are mutable. They require locking.
var (
	uiSettings UserInterfaceSettings
)

var mutex sync.Mutex

const (
	SCRIPT_DIRECTORY = "scripts"
	STARTUP_SCRIPT   = "startup"
)

type Interpreter struct{}

func (i *Interpreter) Run(console *clingon.Console, command string) os.Error {
	if command == "" {
		command = "help()"
	}

	buffer = bytes.NewBufferString("")

	err := i.run(w, "", command)
	console.Print(buffer.String())
	return err
}

// Runs the specified Go source code in the context of 'w'
func (i *Interpreter) run(w *eval.World, path_orEmpty string, sourceCode string) os.Error {
	var err os.Error
	var code eval.Code

	fileSet := token.NewFileSet()
	if len(path_orEmpty) > 0 {
		fileSet.AddFile(path_orEmpty, fileSet.Base(), len(sourceCode))
	}

	code, err = w.Compile(fileSet, sourceCode)
	if err != nil {
		return err
	}

	_, err = code.Run()
	if err != nil {
		return err
	}

	return nil
}

// ================
// Various commands
// ================

var help_keys vector.StringVector
var help_vals vector.StringVector

// Signature: func help()
func wrapper_help(t *eval.Thread, in []eval.Value, out []eval.Value) {
	fmt.Fprintf(buffer, "\nAvailable commands:\n")

	maxKeyLen := 1
	for i := 0; i < help_keys.Len(); i++ {
		if len(help_keys[i]) > maxKeyLen {
			maxKeyLen = len(help_keys[i])
		}
	}

	for i := 0; i < help_keys.Len(); i++ {
		fmt.Fprintf(buffer, "  %s", help_keys[i])
		for j := len(help_keys[i]); j < maxKeyLen; j++ {
			fmt.Fprintf(buffer, " ")
		}
		fmt.Fprintf(buffer, "  %s\n", help_vals[i])
	}
}

// Signature: func exit()
func wrapper_exit(t *eval.Thread, in []eval.Value, out []eval.Value) {
	// Implementation note:
	//   The following test has to be there only in cases in which something can go wrong.
	//   For example if the user tried to execute "exit(); audio(false)" then GoSpeccy would panic.
	//   An alternative way would be to actually terminate the whole program at the 1st statement - so that
	//   ["audio(false)" or whatever else] is not executed - alas this is somewhat problematic,
	//   since once the script "exit(); audio(false)" runs, it cannot be stopped halfway
	//   through its execution. Using "runtime.Goexit()" would solve this issue, but only partially,
	//   since it is potentially possible for the statement "audio(false)" to be hidden in a defer statement.
	//   So, the best option (until somebody implements a better one) is to convert the problematic commands
	//   into statements that are doing nothing while the application is in the process of being exited.
	if app.TerminationInProgress() || app.Terminated() {
		return
	}
	app.RequestExit()
}

// Signature: func reset()
func wrapper_reset(t *eval.Thread, in []eval.Value, out []eval.Value) {
	if app.TerminationInProgress() || app.Terminated() {
		return
	}
	speccy.CommandChannel <- spectrum.Cmd_Reset{nil}
}

// Signature: func addSearchPath(path string)
func wrapper_addSearchPath(t *eval.Thread, in []eval.Value, out []eval.Value) {
	path := in[0].(eval.StringValue).Get(t)
	spectrum.AddCustomSearchPath(path)
}

// Signature: func load(path string)
func wrapper_load(t *eval.Thread, in []eval.Value, out []eval.Value) {
	if app.TerminationInProgress() || app.Terminated() {
		return
	}
<commit_msg>Fix the reset test (now the reset command waits until the system ROM is loaded)<commit_after>package interpreter

import (
	"bytes"
	"clingon"
	"container/vector"
	"exp/eval"
	"fmt"
	"go/token"
	"io/ioutil"
	"os"
	"spectrum"
	"spectrum/formats"
	"strings"
	"sync"
	"time"
)

type UserInterfaceSettings interface {
	ResizeVideo(scale2x, fullscreen bool)
	ShowPaintedRegions(enable bool)
	EnableAudio(enable bool)
	SetAudioFreq(freq uint) // 0 means "default frequency"
	SetAudioQuality(hqAudio bool)
}

// ==============
// Some variables
// ==============

// These variables are set only once, before starting new goroutines,
// so there is no need for controlling concurrent access via a sync.Mutex
var (
	app                 *spectrum.Application
	cmdLineArg          string // The 1st non-flag command-line argument, or empty string
	speccy              *spectrum.Spectrum48k
	w                   *eval.World
	buffer              *bytes.Buffer // Stores the output of executed script-functions
	IgnoreStartupScript = false
)

// These variables are mutable. They require locking.
var (
	uiSettings UserInterfaceSettings
)

var mutex sync.Mutex

const (
	SCRIPT_DIRECTORY = "scripts"
	STARTUP_SCRIPT   = "startup"
)

type Interpreter struct{}

func (i *Interpreter) Run(console *clingon.Console, command string) os.Error {
	if command == "" {
		command = "help()"
	}

	buffer = bytes.NewBufferString("")

	err := i.run(w, "", command)
	console.Print(buffer.String())
	return err
}

// Runs the specified Go source code in the context of 'w'
func (i *Interpreter) run(w *eval.World, path_orEmpty string, sourceCode string) os.Error {
	var err os.Error
	var code eval.Code

	fileSet := token.NewFileSet()
	if len(path_orEmpty) > 0 {
		fileSet.AddFile(path_orEmpty, fileSet.Base(), len(sourceCode))
	}

	code, err = w.Compile(fileSet, sourceCode)
	if err != nil {
		return err
	}

	_, err = code.Run()
	if err != nil {
		return err
	}

	return nil
}

// ================
// Various commands
// ================

var help_keys vector.StringVector
var help_vals vector.StringVector

// Signature: func help()
func wrapper_help(t *eval.Thread, in []eval.Value, out []eval.Value) {
	fmt.Fprintf(buffer, "\nAvailable commands:\n")

	maxKeyLen := 1
	for i := 0; i < help_keys.Len(); i++ {
		if len(help_keys[i]) > maxKeyLen {
			maxKeyLen = len(help_keys[i])
		}
	}

	for i := 0; i < help_keys.Len(); i++ {
		fmt.Fprintf(buffer, "  %s", help_keys[i])
		for j := len(help_keys[i]); j < maxKeyLen; j++ {
			fmt.Fprintf(buffer, " ")
		}
		fmt.Fprintf(buffer, "  %s\n", help_vals[i])
	}
}

// Signature: func exit()
func wrapper_exit(t *eval.Thread, in []eval.Value, out []eval.Value) {
	// Implementation note:
	//   The following test has to be there only in cases in which something can go wrong.
	//   For example if the user tried to execute "exit(); audio(false)" then GoSpeccy would panic.
	//   An alternative way would be to actually terminate the whole program at the 1st statement - so that
	//   ["audio(false)" or whatever else] is not executed - alas this is somewhat problematic,
	//   since once the script "exit(); audio(false)" runs, it cannot be stopped halfway
	//   through its execution. Using "runtime.Goexit()" would solve this issue, but only partially,
	//   since it is potentially possible for the statement "audio(false)" to be hidden in a defer statement.
	//   So, the best option (until somebody implements a better one) is to convert the problematic commands
	//   into statements that are doing nothing while the application is in the process of being exited.
	if app.TerminationInProgress() || app.Terminated() {
		return
	}
	app.RequestExit()
}

// Signature: func reset()
func wrapper_reset(t *eval.Thread, in []eval.Value, out []eval.Value) {
	if app.TerminationInProgress() || app.Terminated() {
		return
	}
	romLoaded := make(chan (<-chan bool))
	speccy.CommandChannel <- spectrum.Cmd_Reset{romLoaded}
	<-(<-romLoaded)
}

// Signature: func addSearchPath(path string)
func wrapper_addSearchPath(t *eval.Thread, in []eval.Value, out []eval.Value) {
	path := in[0].(eval.StringValue).Get(t)
	spectrum.AddCustomSearchPath(path)
}

// Signature: func load(path string)
func wrapper_load(t *eval.Thread, in []eval.Value, out []eval.Value) {
	if app.TerminationInProgress() || app.Terminated() {
		return
	}

{'fraction_non_alphanumeric': 0.13495782567947517, 'fraction_numerical': 0.002577319587628866, 'mean_word_length': 3.716574585635359, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 7, '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': '7125475', 'n_tokens_mistral': 2871, 'n_tokens_neox': 2737, 'n_words': 871}