Skip to content

Commit

Permalink
import\live added
Browse files Browse the repository at this point in the history
  • Loading branch information
refaktor committed May 21, 2024
1 parent 3b28759 commit 9f94fc1
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 30 deletions.
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
4 changes: 2 additions & 2 deletions examples/xmlprint/xmlprint.rye
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

doc
doc!
`This is a simple xmlprint dialect that exposes just one function xmlprint
It prints the XML, handles the line padding and check if the tags match`

Expand Down Expand Up @@ -46,7 +46,7 @@ private {
}

; Only this function will get returned and set to xmlgen word
fn\par { values block "Accepts dict of values and a block of xtags extags and code" } current-ctx { process block values 0 'no }
fn\par { values block "Accepts a value that gets injected in code and a block of xtags extags and code" } current-ctx { process block values 0 'no }

} :xmlprint

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
Loading

0 comments on commit 9f94fc1

Please sign in to comment.