<commit_before>package main

import "errors"
import "fmt"
import "io/ioutil"
import "strings"
import "os"
import "path/filepath"
import "github.com/howeyc/fsnotify"
import "mk/Apollo/events"

var MainContext *Context

// modules by name
var modules map[string]*Module = make(map[string]*Module)

// modules by path
var modulesByPath map[string]*Module = make(map[string]*Module)
var moduleSearchPaths = []string{"modules"}
var watcher *fsnotify.Watcher
var watcherDone chan bool

type Context struct {
	symbols map[string]Data
	parent  *Context
	usages  []Usage
}

type Usage struct {
	context *Context
	prefix  string
}

type Module struct {
	name    string
	source  string
	context *Context
}

func (module *Module) Refresh() {
	module.Reload()

	// reimport this module into all usage contexts
	for _, usage := range module.context.usages {
		usage.context.Reimport(module.context, usage.prefix)
	}
}

func (c *Context) String() string {
	return "Context"
}

func (c *Context) Equals(other Data) bool {
	return false
}

func (c *Context) GetType() DataType {
	return ContextType
}

// Gets a module by name. Loads the module beforehand if necessary
func GetModule(name string, env *Context) *Module {
	module, ok := modules[name]
	if !ok {
		module = LoadModule(name, env)
		modules[name] = module
	}

	return module
}

func FindModuleFile(name string) string {
	for _, modulePath := range moduleSearchPaths {
		path := modulePath + "/" + strings.Replace(name, ".", "/", -1) + ".glisp"
		absPath, err := filepath.Abs(path)
		if err != nil {
			panic(err.Error())
		}
		if _, err := os.Stat(absPath); err == nil {
			return absPath
		}
	}

	return ""
}

func (module *Module) Reload() {
	bytes, err := ioutil.ReadFile(module.source)
	if err != nil {
		panic(fmt.Sprintf("Failed to reload module %s: %s", module.name, err.Error()))
	}

	text := "(do " + string(bytes) + ""
	_, err = EvaluateString(text, module.context)
	if err != nil {
		panic(err.Error())
	}
}

func LoadModule(name string, env *Context) *Module {
	path := FindModuleFile(name)
	if path == "" {
		panic(fmt.Sprintf("Module %s could not be found in search path", name))
	}
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		panic(fmt.Sprintf("Failed to load module %s: %s", name, err.Error()))
	}

	text := "(do " + string(bytes) + ""
	context := NewContext()
	context.parent = env

	_, err = EvaluateString(text, context)
	if err == nil {
		module := new(Module)
		module.name = name
		module.context = context
		module.source = path

		err := watcher.Watch(filepath.Dir(path))
		if err != nil {
			fmt.Print(err.Error())
		}

		modulesByPath[path] = module

		return module
	} else {
		panic(err.Error())
	}
}

func NewContext() *Context {
	return &Context{
		make(map[string]Data),
		nil,
		make([]Usage, 0),
	}
}

func (c *Context) Define(symbol Symbol, value Data) {
	c.symbols[symbol.Value] = value
}

func (c *Context) IsDefined(symbol Symbol) bool {
	_, defined := c.symbols[symbol.Value]
	if !defined && c.parent != nil {
		return c.parent.IsDefined(symbol)
	}
	return defined
}

func (c *Context) LookUp(symbol Symbol) Data {
	val, defined := c.symbols[symbol.Value]
	if defined {
		return val
	} else if c.parent != nil {
		return c.parent.LookUp(symbol)
	} else {
		return nil
	}
}

func (c *Context) Reimport(other *Context, prefix string) {
	for key, value := range other.symbols {
		c.symbols[prefix+key] = value
	}
}

func (c *Context) Import(other *Context, prefix string) {
	for key, value := range other.symbols {
		c.symbols[prefix+key] = value
	}

	other.usages = append(other.usages, Usage{c, prefix})
}

func EvaluateString(code string, context *Context) (Data, error) {<commit_msg>hot swapping function definitions<commit_after>package main

import "errors"
import "fmt"
import "io/ioutil"
import "strings"
import "os"
import "path/filepath"
import "github.com/howeyc/fsnotify"
import "mk/Apollo/events"

var MainContext *Context

// modules by name
var modules map[string]*Module = make(map[string]*Module)

// modules by path
var modulesByPath map[string]*Module = make(map[string]*Module)
var moduleSearchPaths = []string{"modules"}
var watcher *fsnotify.Watcher
var watcherDone chan bool

type Context struct {
	symbols map[string]Data
	parent  *Context
	usages  []Usage
}

type Usage struct {
	context *Context
	prefix  string
}

type Module struct {
	name    string
	source  string
	context *Context
}

func (module *Module) Refresh() {
	module.Reload()

	// reimport this module into all usage contexts
	for _, usage := range module.context.usages {
		usage.context.Reimport(module.context, usage.prefix)
	}
}

func (c *Context) String() string {
	return "Context"
}

func (c *Context) Equals(other Data) bool {
	return false
}

func (c *Context) GetType() DataType {
	return ContextType
}

// Gets a module by name. Loads the module beforehand if necessary
func GetModule(name string, env *Context) *Module {
	module, ok := modules[name]
	if !ok {
		module = LoadModule(name, env)
		modules[name] = module
	}

	return module
}

func FindModuleFile(name string) string {
	for _, modulePath := range moduleSearchPaths {
		path := modulePath + "/" + strings.Replace(name, ".", "/", -1) + ".glisp"
		absPath, err := filepath.Abs(path)
		if err != nil {
			panic(err.Error())
		}
		if _, err := os.Stat(absPath); err == nil {
			return absPath
		}
	}

	return ""
}

func (module *Module) Reload() {
	bytes, err := ioutil.ReadFile(module.source)
	if err != nil {
		panic(fmt.Sprintf("Failed to reload module %s: %s", module.name, err.Error()))
	}

	text := "(do " + string(bytes) + ""
	_, err = EvaluateString(text, module.context)
	if err != nil {
		panic(err.Error())
	}
}

func LoadModule(name string, env *Context) *Module {
	path := FindModuleFile(name)
	if path == "" {
		panic(fmt.Sprintf("Module %s could not be found in search path", name))
	}
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		panic(fmt.Sprintf("Failed to load module %s: %s", name, err.Error()))
	}

	text := "(do " + string(bytes) + ""
	context := NewContext()
	context.parent = env

	_, err = EvaluateString(text, context)
	if err == nil {
		module := new(Module)
		module.name = name
		module.context = context
		module.source = path

		err := watcher.Watch(filepath.Dir(path))
		if err != nil {
			fmt.Print(err.Error())
		}

		modulesByPath[path] = module

		return module
	} else {
		panic(err.Error())
	}
}

func NewContext() *Context {
	return &Context{
		make(map[string]Data),
		nil,
		make([]Usage, 0),
	}
}

func (c *Context) Define(symbol Symbol, value Data) {
	c.symbols[symbol.Value] = value
}

func (c *Context) IsDefined(symbol Symbol) bool {
	_, defined := c.symbols[symbol.Value]
	if !defined && c.parent != nil {
		return c.parent.IsDefined(symbol)
	}
	return defined
}

func (c *Context) LookUp(symbol Symbol) Data {
	val, defined := c.symbols[symbol.Value]
	if defined {
		return val
	} else if c.parent != nil {
		return c.parent.LookUp(symbol)
	} else {
		return nil
	}
}

func (c *Context) Reimport(other *Context, prefix string) {
	for key, value := range other.symbols {
		if newFunction, ok := value.(*Function); ok {
			if current, ok := c.symbols[prefix+key]; ok {
				if currentFunction, ok := current.(*Function); ok && currentFunction.Name == newFunction.Name {
					// replace function definition in place
					currentFunction.Dispatchers = newFunction.Dispatchers
				}

			}
		}

		c.symbols[prefix+key] = value
	}
}

func (c *Context) Import(other *Context, prefix string) {
	for key, value := range other.symbols {
		c.symbols[prefix+key] = value
	}

	other.usages = append(other.usages, Usage{c, prefix})
}

func EvaluateString(code string, context *Context) (Data, error) {
{'fraction_non_alphanumeric': 0.141502079002079, 'fraction_numerical': 0.0005197505197505198, 'mean_word_length': 3.9276568501920615, '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': '4345934', 'n_tokens_mistral': 2763, 'n_tokens_neox': 2630, 'n_words': 741}