Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modwords - added import\live #216

Merged
merged 17 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 61 additions & 1 deletion env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import (
"fmt"
"sort"
"strings"
"sync"

"github.com/fsnotify/fsnotify"
)

/* type Envi interface {
Expand Down Expand Up @@ -244,7 +247,7 @@ func (e *RyeCtx) Get2(word int) (Object, bool, *RyeCtx) {

func (e *RyeCtx) Set(word int, val Object) Object {
if _, exists := e.state[word]; exists {
return NewError("Can't set already set word, try using modword")
return NewError("Can't set already set word, try using modword! FIXME !")
} else {
e.state[word] = val
return val
Expand Down Expand Up @@ -296,6 +299,8 @@ type ProgramState struct {
InErrHandler bool
ScriptPath string // holds the path to the script that is being imported (doed) currently
WorkingPath string // holds the path to CWD (can be changed in program with specific functions)
AllowMod bool
LiveObj *LiveEnv
}

func NewProgramState(ser TSeries, idx *Idxs) *ProgramState {
Expand All @@ -317,6 +322,8 @@ func NewProgramState(ser TSeries, idx *Idxs) *ProgramState {
false,
"",
"",
false,
NewLiveEnv(),
}
return &ps
}
Expand All @@ -340,6 +347,8 @@ func NewProgramStateNEW() *ProgramState {
false,
"",
"",
false,
NewLiveEnv(),
}
return &ps
}
Expand Down Expand Up @@ -368,3 +377,54 @@ func SetValue(ps *ProgramState, word string, val Object) {
}
}
}

// LiveEnv -- a experiment in live realoading

type LiveEnv struct {
Active bool
Watcher *fsnotify.Watcher
PsMutex sync.Mutex
Updates []string
}

func NewLiveEnv() *LiveEnv {
watcher, err := fsnotify.NewWatcher()
if err != nil {
fmt.Println("Error creating watcher:", err)
return nil
}
// defer watcher.Close()

// Watch current directory for changes in any Go source file (*.go)

liveEnv := &LiveEnv{true, watcher, sync.Mutex{}, make([]string, 0)}

go func() {
for {
select {
case event := <-watcher.Events:
if event.Op&fsnotify.Write == fsnotify.Write {
// fmt.Println("LiveEnv file changed:", event.Name)
liveEnv.PsMutex.Lock()
liveEnv.Updates = append(liveEnv.Updates, event.Name)
liveEnv.PsMutex.Unlock()
}
case err := <-watcher.Errors:
fmt.Println("LiveEnv error watching files:", err)
}
}
}()

return liveEnv
}

func (le *LiveEnv) Add(file string) {
err := le.Watcher.Add(".")
if err != nil {
fmt.Println("LiveEnv: Error adding directory to watch:", err)
}
}

func (le *LiveEnv) ClearUpdates() {
le.Updates = make([]string, 0)
}
166 changes: 150 additions & 16 deletions evaldo/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func MakeRyeError(env1 *env.ProgramState, val env.Object, er *env.Error) *env.Er
return env.NewError4(int(val.Value), "", er, nil)
case env.Block: // todo .. make Error type .. make error construction micro dialect, return the error wrapping error that caused it
// TODO -- this is only temporary it takes numeric value as first and string as second arg
// TODONOW implement the dialect
code := val.Series.Get(0)
message := val.Series.Get(1)
if code.Type() == env.IntegerType && message.Type() == env.StringType {
Expand Down Expand Up @@ -332,6 +333,44 @@ func (s RyeListSort) Less(i, j int) bool {
return greaterThanNew(env.ToRyeValue(s[j]), env.ToRyeValue(s[i]))
}

func LoadScriptLocalFile(ps *env.ProgramState, s1 env.Uri) (env.Object, string) {
var str string
fileIdx, _ := ps.Idx.GetIndex("file")
fullpath := filepath.Join(filepath.Dir(ps.ScriptPath), s1.GetPath())
if s1.Scheme.Index == fileIdx {
b, err := os.ReadFile(fullpath)
if err != nil {
return MakeBuiltinError(ps, err.Error(), "import"), ps.ScriptPath
}
str = string(b) // convert content to a 'string'
}
script_ := ps.ScriptPath
ps.ScriptPath = fullpath
block_ := loader.LoadStringNEW(str, false, ps)
return block_, script_
}

func EvaluateLoadedValue(ps *env.ProgramState, block_ env.Object, script_ string, allowMod bool) env.Object {
switch block := block_.(type) {
case env.Block:
ser := ps.Ser
ps.Ser = block.Series
ps.AllowMod = allowMod
EvalBlock(ps)
ps.AllowMod = false
ps.Ser = ser
return ps.Res
case env.Error:
ps.ScriptPath = script_
ps.ErrorFlag = true
return MakeBuiltinError(ps, block.Message, "import")
default:
fmt.Println(block)
panic("Not block and not error in import builtin.") // TODO -- Think how best to handle this
// return env.Void{}
}
}

var ShowResults bool

var builtins = map[string]*env.Builtin{
Expand Down Expand Up @@ -376,6 +415,8 @@ var builtins = map[string]*env.Builtin{
return MakeBuiltinError(ps, err.Error(), "to-integer")
}
return *env.NewInteger(int64(iValue))
case env.Decimal:
return *env.NewInteger(int64(addr.Value))
default:
return MakeArgError(ps, 1, []env.Type{env.StringType}, "to-integer")
}
Expand Down Expand Up @@ -1580,20 +1621,24 @@ var builtins = map[string]*env.Builtin{
Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
switch s1 := arg0.(type) {
case env.Uri:
var str string
fileIdx, _ := ps.Idx.GetIndex("file")
fullpath := filepath.Join(filepath.Dir(ps.ScriptPath), s1.GetPath())
if s1.Scheme.Index == fileIdx {
b, err := os.ReadFile(fullpath)
if err != nil {
return MakeBuiltinError(ps, err.Error(), "import")
}
str = string(b) // convert content to a 'string'
}
script_ := ps.ScriptPath
ps.ScriptPath = fullpath
block_ := loader.LoadStringNEW(str, false, ps)
switch block := block_.(type) {
block_, script_ := LoadScriptLocalFile(ps, s1)
/*
var str string
fileIdx, _ := ps.Idx.GetIndex("file")
fullpath := filepath.Join(filepath.Dir(ps.ScriptPath), s1.GetPath())
if s1.Scheme.Index == fileIdx {
b, err := os.ReadFile(fullpath)
if err != nil {
return MakeBuiltinError(ps, err.Error(), "import")
}
str = string(b) // convert content to a 'string'
}
script_ := ps.ScriptPath
ps.ScriptPath = fullpath
block_ := loader.LoadStringNEW(str, false, ps)
*/
ps.Res = EvaluateLoadedValue(ps, block_, script_, false)
/* switch block := block_.(type) {
case env.Block:
ser := ps.Ser
ps.Ser = block.Series
Expand All @@ -1606,7 +1651,25 @@ var builtins = map[string]*env.Builtin{
default:
fmt.Println(block)
panic("Not block and not error in import builtin.") // TODO -- Think how best to handle this
}
} */
ps.ScriptPath = script_
//ps = env.AddToProgramState(ps, block.Series, genv)
return ps.Res
default:
return MakeArgError(ps, 1, []env.Type{env.UriType}, "import")
}
},
},

"import\\live": { // **
Argsn: 1,
Doc: "Imports a file, loads and does it from script local path.",
Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
switch s1 := arg0.(type) {
case env.Uri:
block_, script_ := LoadScriptLocalFile(ps, s1)
ps.Res = EvaluateLoadedValue(ps, block_, script_, false)
ps.LiveObj.Add(s1.GetPath()) // add to watcher
ps.ScriptPath = script_
//ps = env.AddToProgramState(ps, block.Series, genv)
return ps.Res
Expand Down Expand Up @@ -1648,7 +1711,78 @@ var builtins = map[string]*env.Builtin{
},
},

"load-sig": {
// TODO -- refactor load variants so they use common function LoadString and LoadFile

"load\\mod": { // **
Argsn: 1,
Doc: "Loads a string into Rye values. During load it allows modification of words.",
Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
switch s1 := arg0.(type) {
case env.String:
block, _ := loader.LoadString(s1.Value, false)
//ps = env.AddToProgramState(ps, block.Series, genv)
return block
case env.Uri:
var str string
fileIdx, _ := ps.Idx.GetIndex("file")
if s1.Scheme.Index == fileIdx {
b, err := os.ReadFile(s1.GetPath())
if err != nil {
return makeError(ps, err.Error())
}
str = string(b) // convert content to a 'string'
}
scrip := ps.ScriptPath
ps.AllowMod = true
ps.ScriptPath = s1.GetPath()
block := loader.LoadStringNEW(str, false, ps)
ps.AllowMod = false
ps.ScriptPath = scrip
//ps = env.AddToProgramState(ps, block.Series, genv)
return block
default:
ps.FailureFlag = true
return env.NewError("Must be string or file TODO")
}
},
},

"load\\live": { // **
Argsn: 1,
Doc: "Loads a string into Rye values. During load it allows modification of words.",
Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
switch s1 := arg0.(type) {
case env.String:
block, _ := loader.LoadString(s1.Value, false)
//ps = env.AddToProgramState(ps, block.Series, genv)
return block
case env.Uri:
var str string
fileIdx, _ := ps.Idx.GetIndex("file")
if s1.Scheme.Index == fileIdx {
b, err := os.ReadFile(s1.GetPath())
ps.LiveObj.Add(s1.GetPath()) // add to watcher
if err != nil {
return makeError(ps, err.Error())
}
str = string(b) // convert content to a 'string'
}
scrip := ps.ScriptPath
ps.AllowMod = true
ps.ScriptPath = s1.GetPath()
block := loader.LoadStringNEW(str, false, ps)
ps.AllowMod = false
ps.ScriptPath = scrip
//ps = env.AddToProgramState(ps, block.Series, genv)
return block
default:
ps.FailureFlag = true
return env.NewError("Must be string or file TODO")
}
},
},

"load\\sig": {
Argsn: 1,
Doc: "Checks the signature, if OK then loads a string into Rye values.",
Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
Expand Down
2 changes: 1 addition & 1 deletion evaldo/builtins_io.go
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ func __https_request__do(ps *env.ProgramState, arg0 env.Object, arg1 env.Object,
case env.Native:
client := &http.Client{}
resp, err := client.Do(req.Value.(*http.Request))
defer resp.Body.Close()
defer resp.Body.Close() // TODO -- comment this and figure out goling bodyclose
if err != nil {
return MakeBuiltinError(ps, err.Error(), "__https_request__do")
}
Expand Down
28 changes: 18 additions & 10 deletions evaldo/evaldo.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,11 +300,15 @@ func MaybeEvalOpwordOnRight(nextObj env.Object, ps *env.ProgramState, limited bo
}
//ProcOpword(nextObj, es)
idx := opword.Index
ok := ps.Ctx.SetNew(idx, ps.Res, ps.Idx)
if !ok {
ps.Res = *env.NewError("Can't set already set word " + ps.Idx.GetWord(idx) + ", try using modword")
ps.ErrorFlag = true
return ps
if ps.AllowMod {
ps.Ctx.Mod(idx, ps.Res)
} else {
ok := ps.Ctx.SetNew(idx, ps.Res, ps.Idx)
if !ok {
ps.Res = *env.NewError("Can't set already set word " + ps.Idx.GetWord(idx) + ", try using modword")
ps.ErrorFlag = true
return ps
}
}
ps.Ser.Next()
ps.SkipFlag = false
Expand Down Expand Up @@ -538,11 +542,15 @@ func EvalSetword(ps *env.ProgramState, word env.Setword) *env.ProgramState {
// es1 := EvalExpression(es)
ps1, _ := EvalExpressionInj(ps, nil, false)
idx := word.Index
ok := ps1.Ctx.SetNew(idx, ps1.Res, ps.Idx)
if !ok {
ps.Res = *env.NewError("Can't set already set word " + ps.Idx.GetWord(idx) + ", try using modword")
ps.FailureFlag = true
ps.ErrorFlag = true
if ps.AllowMod {
ps1.Ctx.Mod(idx, ps.Res)
} else {
ok := ps1.Ctx.SetNew(idx, ps1.Res, ps.Idx)
if !ok {
ps.Res = *env.NewError("Can't set already set word " + ps.Idx.GetWord(idx) + ", try using modword")
ps.FailureFlag = true
ps.ErrorFlag = true
}
}
return ps
}
Expand Down
9 changes: 9 additions & 0 deletions evaldo/repl.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,15 @@ func DoRyeRepl(es *env.ProgramState, showResults bool) {
if code, err := line.Prompt(prompt); err == nil {
// strip comment

es.LiveObj.PsMutex.Lock()
for _, update := range es.LiveObj.Updates {
fmt.Println("\033[35m((Reloading " + update + "))\033[0m")
block_, script_ := LoadScriptLocalFile(es, *env.NewUri1(es.Idx, "file://"+update))
es.Res = EvaluateLoadedValue(es, block_, script_, true)
}
es.LiveObj.ClearUpdates()
es.LiveObj.PsMutex.Unlock()

multiline := len(code) > 1 && code[len(code)-1:] == " "

comment := regexp.MustCompile(`\s*;`)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ require (
github.com/blevesearch/zapx/v15 v15.3.13 // indirect
github.com/blevesearch/zapx/v16 v16.0.12 // indirect
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ github.com/drewlanenga/govector v0.0.0-20220726163947-b958ac08bc93 h1:2VXZHsypUG
github.com/drewlanenga/govector v0.0.0-20220726163947-b958ac08bc93/go.mod h1:AbP/uRrjZFATEwl0P2DHePteIMZRWHEJBWBmMmLdCkk=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df h1:Bao6dhmbTA1KFVxmJ6nBoMuOJit2yjEgLJpIMYpop0E=
Expand Down