From 6d9439298248087e77d5d6b5a7c6cc0f55c487fa Mon Sep 17 00:00:00 2001 From: refaktor Date: Thu, 30 May 2024 20:16:57 +0200 Subject: [PATCH] Improved Eyr dialect, adde lang flag to CLI --- evaldo/builtins.go | 2 +- evaldo/builtins_eyr.go | 110 ++++++++++++-------- evaldo/builtins_math.go | 2 +- evaldo/repl.go | 223 +++++++++++++++++++++++++++++----------- main.go | 21 ++-- 5 files changed, 244 insertions(+), 114 deletions(-) diff --git a/evaldo/builtins.go b/evaldo/builtins.go index 70cc414d..b805d511 100644 --- a/evaldo/builtins.go +++ b/evaldo/builtins.go @@ -2261,7 +2261,7 @@ var builtins = map[string]*env.Builtin{ } }*/ - DoRyeRepl(ps, ShowResults) + DoRyeRepl(ps, "do", ShowResults) fmt.Println("-------------------------------------------------------------") // ps.Ser = ser return ps.Res diff --git a/evaldo/builtins_eyr.go b/evaldo/builtins_eyr.go index 478c2ae9..90556a91 100755 --- a/evaldo/builtins_eyr.go +++ b/evaldo/builtins_eyr.go @@ -14,6 +14,8 @@ import ( // while loop pogleda naslednji arg, če je literal nastavi arg in poveča argc če je argc nargs potem pokliče frame in iz stacka potegne naslednjega, če ni potem zalopa // če je builtin potem pusha trenuten frame na stack in kreira novega +const STACK_SIZE int = 1000 + type EyrStack struct { D []env.Object I int @@ -21,7 +23,7 @@ type EyrStack struct { func NewEyrStack() *EyrStack { st := EyrStack{} - st.D = make([]env.Object, 100) + st.D = make([]env.Object, STACK_SIZE) st.I = 0 return &st } @@ -34,23 +36,21 @@ func (s *EyrStack) IsEmpty() bool { // Push adds a new number to the stack func (s *EyrStack) Push(x env.Object) { //// *s = append(*s, x) + if s.I+1 >= STACK_SIZE { + fmt.Printf("stack overflow\n") + os.Exit(0) + } s.D[s.I] = x s.I++ // appending takes a lot of time .. pushing values ... - // try creating stack in advance and then just setting values - // and see the difference TODO NEXT } // Pop removes and returns the top element of stack. func (s *EyrStack) Pop() env.Object { if s.IsEmpty() { fmt.Printf("stack underflow\n") - os.Exit(1) + os.Exit(0) } - - /// i := len(*s) - 1 - /// x := (*s)[i] - /// *s = (*s)[:i] s.I-- x := s.D[s.I] return x @@ -59,11 +59,9 @@ func (s *EyrStack) Pop() env.Object { func Eyr_CallBuiltin(bi env.Builtin, ps *env.ProgramState, arg0_ env.Object, toLeft bool, stack *EyrStack) *env.ProgramState { arg0 := bi.Cur0 //env.Object(bi.Cur0) var arg1 env.Object // := bi.Cur1 + var arg2 env.Object if bi.Argsn > 0 && bi.Cur0 == nil { - //fmt.Println(" ARG 1 ") - //fmt.Println(ps.Ser.GetPos()) - // evalExprFn(ps, true) if checkFlagsBi(bi, ps, 0) { return ps } @@ -73,23 +71,35 @@ func Eyr_CallBuiltin(bi env.Builtin, ps *env.ProgramState, arg0_ env.Object, toL arg0 = stack.Pop() if bi.Argsn == 1 { ps.Res = bi.Fn(ps, arg0, nil, nil, nil, nil) - stack.Push(ps.Res) + // stack.Push(ps.Res) } } if bi.Argsn > 1 && bi.Cur1 == nil { - //evalExprFn(ps, true) // <---- THESE DETERMINE IF IT CONSUMES WHOLE EXPRESSION OR NOT IN CASE OF PIPEWORDS .. HM*... MAYBE WOULD COULD HAVE A WORD MODIFIER?? a: 2 |add 5 a:: 2 |add 5 print* --TODO if checkFlagsBi(bi, ps, 1) { return ps } if ps.ErrorFlag || ps.ReturnFlag { return ps } - //fmt.Println(ps.Res) arg1 = stack.Pop() if bi.Argsn == 2 { ps.Res = bi.Fn(ps, arg1, arg0, nil, nil, nil) - stack.Push(ps.Res) + // stack.Push(ps.Res) + } + } + if bi.Argsn > 2 && bi.Cur2 == nil { + if checkFlagsBi(bi, ps, 0) { + return ps + } + if ps.ErrorFlag || ps.ReturnFlag { + return ps + } + + arg2 = stack.Pop() + if bi.Argsn == 3 { + ps.Res = bi.Fn(ps, arg2, arg1, arg0, nil, nil) + //stack.Push(ps.Res) } } return ps @@ -99,28 +109,16 @@ func Eyr_EvalObject(es *env.ProgramState, object env.Object, leftVal env.Object, //fmt.Print("EVAL OBJECT") switch object.Type() { case env.BuiltinType: - //fmt.Println(" BUIL**") - //fmt.Println(es.Ser.GetPos()) - //fmt.Println(" BUILTIN**") bu := object.(env.Builtin) if bakein { es.Ser.Put(bu) } //es.Ser.SetPos(es.Ser.Pos() - 1) - //es.Ser.SetPos(es.Ser.Pos() + 1) - // OBJECT INJECTION EXPERIMENT - // es.Ser.Put(bu) - if checkFlagsBi(bu, es, 333) { return es } return Eyr_CallBuiltin(bu, es, leftVal, toLeft, stack) - - //es.Res.Trace("After builtin call") - //return es default: - //d object.Trace("DEFAULT**") es.Res = object - //es.Res.Trace("After object returned") return es } } @@ -129,12 +127,10 @@ func Eyr_EvalWord(es *env.ProgramState, word env.Object, leftVal env.Object, toL // LOCAL FIRST found, object, ctx := findWordValue(es, word) if found { - trace("****33") - return Eyr_EvalObject(es, object, leftVal, toLeft, ctx, stack, true) //ww0128a * - //es.Res.Trace("After eval Object") - //return es + es = Eyr_EvalObject(es, object, leftVal, toLeft, ctx, stack, true) //ww0128a * + stack.Push(es.Res) + return es } else { - trace("****34") es.ErrorFlag = true if !es.FailureFlag { es.Res = *env.NewError2(5, "Word not found: "+word.Inspect(*es.Idx)) @@ -143,6 +139,13 @@ func Eyr_EvalWord(es *env.ProgramState, word env.Object, leftVal env.Object, toL } } +func Eyr_EvalLSetword(ps *env.ProgramState, word env.LSetword, leftVal env.Object, toLeft bool, stack *EyrStack) *env.ProgramState { + idx := word.Index + val := stack.Pop() + ps.Ctx.Mod(idx, val) + return ps +} + func Eyr_EvalExpression(es *env.ProgramState, stack *EyrStack) *env.ProgramState { object := es.Ser.Pop() trace2("Before entering expression") @@ -159,27 +162,27 @@ func Eyr_EvalExpression(es *env.ProgramState, stack *EyrStack) *env.ProgramState case env.WordType: rr := Eyr_EvalWord(es, object.(env.Word), nil, false, stack) return rr - case env.OpwordType: + case env.OpwordType: // + and orther operators are basically opwords too rr := Eyr_EvalWord(es, object.(env.Opword), nil, false, stack) return rr + case env.LSetwordType: + rr := Eyr_EvalLSetword(es, object.(env.LSetword), nil, false, stack) + return rr case env.BuiltinType: - //fmt.Println("yoyo") return Eyr_EvalObject(es, object, nil, false, nil, stack, false) //ww0128a * - //rr := Eyr_EvalWord(es, object.(env.Word), nil, false, stack) - //return rr default: es.ErrorFlag = true - es.Res = env.NewError("Not known type") + es.Res = env.NewError("Not known type for Eyr") } } else { es.ErrorFlag = true - es.Res = env.NewError("Not known type") + es.Res = env.NewError("Not known type (nil)") } return es } -func Eyr_EvalBlock(es *env.ProgramState, stack *EyrStack) *env.ProgramState { +func Eyr_EvalBlock(es *env.ProgramState, stack *EyrStack, full bool) *env.ProgramState { for es.Ser.Pos() < es.Ser.Len() { es = Eyr_EvalExpression(es, stack) if checkFlagsAfterBlock(es, 101) { @@ -189,6 +192,11 @@ func Eyr_EvalBlock(es *env.ProgramState, stack *EyrStack) *env.ProgramState { return es } } + if full { + es.Res = *env.NewBlock(*env.NewTSeries(stack.D[0:stack.I])) + } else { + es.Res = stack.Pop() + } return es } @@ -203,7 +211,25 @@ var Builtins_eyr = map[string]*env.Builtin{ stack := NewEyrStack() ser := ps.Ser ps.Ser = bloc.Series - Eyr_EvalBlock(ps, stack) + Eyr_EvalBlock(ps, stack, false) + ps.Ser = ser + return ps.Res + default: + return MakeArgError(ps, 1, []env.Type{env.BlockType}, "eyr") + } + }, + }, + + "eyr\\full": { + Argsn: 1, + Doc: "Evaluates Rye block as Eyr (postfix) stack based code.", + Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { + switch bloc := arg0.(type) { + case env.Block: + stack := NewEyrStack() + ser := ps.Ser + ps.Ser = bloc.Series + Eyr_EvalBlock(ps, stack, true) ps.Ser = ser return ps.Res default: @@ -212,7 +238,7 @@ var Builtins_eyr = map[string]*env.Builtin{ }, }, - "eyr-loop": { + "eyr\\loop": { Argsn: 2, Doc: "Evaluates Rye block in loop as Eyr code (postfix stack based) N times.", Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { @@ -224,7 +250,7 @@ var Builtins_eyr = map[string]*env.Builtin{ ps.Ser = bloc.Series stack := NewEyrStack() for i := 0; int64(i) < cond.Value; i++ { - ps = Eyr_EvalBlock(ps, stack) + ps = Eyr_EvalBlock(ps, stack, false) ps.Ser.Reset() } ps.Ser = ser diff --git a/evaldo/builtins_math.go b/evaldo/builtins_math.go index f6443cf6..c6a8b001 100644 --- a/evaldo/builtins_math.go +++ b/evaldo/builtins_math.go @@ -516,7 +516,7 @@ var Builtins_math = map[string]*env.Builtin{ stack := NewEyrStack() ser := ps.Ser ps.Ser = block.Series - Eyr_EvalBlock(ps, stack) + Eyr_EvalBlock(ps, stack, false) ps.Ser = ser return ps.Res default: diff --git a/evaldo/repl.go b/evaldo/repl.go index 42d6831a..fcc1d269 100644 --- a/evaldo/repl.go +++ b/evaldo/repl.go @@ -30,7 +30,7 @@ type ShellEd struct { } func genPrompt(shellEd *ShellEd, line string) (string, string) { - if shellEd.Mode != "" { + if shellEd != nil && shellEd.Mode != "" { a := shellEd.Askfor if len(a) > 0 { x := a[0] @@ -148,9 +148,167 @@ func MoveCursorBackward(bias int) { fmt.Printf("\033[%dD", bias) } -// +func DoRyeRepl(es *env.ProgramState, dialect string, showResults bool) { // here because of some odd options we were experimentally adding + //codestr := "" + //codelines := strings.Split(codestr, ",\n") -func DoRyeRepl(es *env.ProgramState, showResults bool) { + line := liner.NewLiner() + defer line.Close() + + line.SetCtrlCAborts(true) + + line.SetCompleter(func(line string) (c []string) { + for i := 0; i < es.Idx.GetWordCount(); i++ { + if strings.HasPrefix(es.Idx.GetWord(i), strings.ToLower(line)) { + c = append(c, es.Idx.GetWord(i)) + } + } + return + }) + + if f, err := os.Open(history_fn); err == nil { + if _, err := line.ReadHistory(f); err != nil { + log.Print("Error reading history file: ", err) + } + f.Close() + } + + line2 := "" + + var prevResult env.Object + + stack := NewEyrStack() + + for { + prompt, _ := genPrompt(nil, line2) + + 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*;`) + line1 := comment.Split(code, 2) //--- just very temporary solution for some comments in repl. Later should probably be part of loader ... maybe? + lineReal := strings.Trim(line1[0], "\t") + + if multiline { + line2 += lineReal + "\n" + } else { + line2 += lineReal + + block, genv := loader.LoadString(line2, false) + block1 := block.(env.Block) + es = env.AddToProgramState(es, block1.Series, genv) + + // EVAL THE DO DIALECT + if dialect == "do" { + EvalBlockInj(es, prevResult, true) + } else if dialect == "eyr" { + Eyr_EvalBlock(es, stack, true) + } + + MaybeDisplayFailureOrError(es, genv) + + if !es.ErrorFlag && es.Res != nil { + prevResult = es.Res + if showResults { + fmt.Println("\033[38;5;37m" + es.Res.Inspect(*genv) + "\x1b[0m") + } + } + + es.ReturnFlag = false + es.ErrorFlag = false + es.FailureFlag = false + + line2 = "" + } + + line.AppendHistory(code) + } else if err == liner.ErrPromptAborted { + break + } else { + log.Print("Error reading line: ", err) + break + } + } + + if f, err := os.Create(history_fn); err != nil { + log.Print("Error writing history file: ", err) + } else { + if _, err := line.WriteHistory(f); err != nil { + log.Print("Error writing history file: ", err) + } + f.Close() + } +} + +func MaybeDisplayFailureOrError(es *env.ProgramState, genv *env.Idxs) { + if es.FailureFlag { + fmt.Println("\x1b[33m" + "Failure" + "\x1b[0m") + } + if es.ErrorFlag { + fmt.Println("\x1b[31m" + es.Res.Print(*genv)) + switch err := es.Res.(type) { + case env.Error: + fmt.Println(err.CodeBlock.PositionAndSurroundingElements(*genv)) + fmt.Println("Error not pointer so bug. #temp") + case *env.Error: + fmt.Println("At location:") + fmt.Print(err.CodeBlock.PositionAndSurroundingElements(*genv)) + } + fmt.Println("\x1b[0m") + } +} + +func MaybeDisplayFailureOrErrorWASM(es *env.ProgramState, genv *env.Idxs, printfn func(string)) { + if es.FailureFlag { + printfn("\x1b[33m" + "Failure" + "\x1b[0m") + } + if es.ErrorFlag { + printfn("\x1b[31;3m" + es.Res.Print(*genv)) + switch err := es.Res.(type) { + case env.Error: + printfn(err.CodeBlock.PositionAndSurroundingElements(*genv)) + printfn("Error not pointer so bug. #temp") + case *env.Error: + printfn("At location:") + printfn(err.CodeBlock.PositionAndSurroundingElements(*genv)) + } + printfn("\x1b[0m") + } +} + +/* THIS WAS DISABLED TEMP FOR WASM MODE .. 20250116 func DoGeneralInput(es *env.ProgramState, prompt string) { + line := liner.NewLiner() + defer line.Close() + if code, err := line.SimplePrompt(prompt); err == nil { + es.Res = *env.NewString(code) + } else { + log.Print("Error reading line: ", err) + } +} + +func DoGeneralInputField(es *env.ProgramState, prompt string) { + line := liner.NewLiner() + defer line.Close() + if code, err := line.SimpleTextField(prompt, 5); err == nil { + es.Res = *env.NewString(code) + } else { + log.Print("Error reading line: ", err) + } +} +*/ + +func DoRyeRepl_OLD(es *env.ProgramState, showResults bool) { // here because of some odd options we were experimentally adding codestr := "a: 100\nb: \"jim\"\nprint 10 + 20 + b" codelines := strings.Split(codestr, ",\n") @@ -244,6 +402,8 @@ func DoRyeRepl(es *env.ProgramState, showResults bool) { block, genv := loader.LoadString(line2, false) block1 := block.(env.Block) es = env.AddToProgramState(es, block1.Series, genv) + + // EVAL THE DO DIALECT EvalBlockInj(es, prevResult, true) if arg != "" { @@ -308,60 +468,3 @@ func DoRyeRepl(es *env.ProgramState, showResults bool) { f.Close() } } - -func MaybeDisplayFailureOrError(es *env.ProgramState, genv *env.Idxs) { - if es.FailureFlag { - fmt.Println("\x1b[33m" + "Failure" + "\x1b[0m") - } - if es.ErrorFlag { - fmt.Println("\x1b[31m" + es.Res.Print(*genv)) - switch err := es.Res.(type) { - case env.Error: - fmt.Println(err.CodeBlock.PositionAndSurroundingElements(*genv)) - fmt.Println("Error not pointer so bug. #temp") - case *env.Error: - fmt.Println("At location:") - fmt.Print(err.CodeBlock.PositionAndSurroundingElements(*genv)) - } - fmt.Println("\x1b[0m") - } -} - -func MaybeDisplayFailureOrErrorWASM(es *env.ProgramState, genv *env.Idxs, printfn func(string)) { - if es.FailureFlag { - printfn("\x1b[33m" + "Failure" + "\x1b[0m") - } - if es.ErrorFlag { - printfn("\x1b[31;3m" + es.Res.Print(*genv)) - switch err := es.Res.(type) { - case env.Error: - printfn(err.CodeBlock.PositionAndSurroundingElements(*genv)) - printfn("Error not pointer so bug. #temp") - case *env.Error: - printfn("At location:") - printfn(err.CodeBlock.PositionAndSurroundingElements(*genv)) - } - printfn("\x1b[0m") - } -} - -/* THIS WAS DISABLED TEMP FOR WASM MODE .. 20250116 func DoGeneralInput(es *env.ProgramState, prompt string) { - line := liner.NewLiner() - defer line.Close() - if code, err := line.SimplePrompt(prompt); err == nil { - es.Res = *env.NewString(code) - } else { - log.Print("Error reading line: ", err) - } -} - -func DoGeneralInputField(es *env.ProgramState, prompt string) { - line := liner.NewLiner() - defer line.Close() - if code, err := line.SimpleTextField(prompt, 5); err == nil { - es.Res = *env.NewString(code) - } else { - log.Print("Error reading line: ", err) - } -} -*/ diff --git a/main.go b/main.go index b9fb1565..ed661c47 100644 --- a/main.go +++ b/main.go @@ -54,6 +54,7 @@ var CODE []any var ( // fileName = flag.String("fiimle", "", "Path to the Rye file (default: none)") do = flag.String("do", "", "Evaluates code after it loads a file or last save.") + lang = flag.String("lang", "do", "Select a dialect / language (do, eyr, ...)") silent = flag.Bool("silent", false, "Console doesn't display return values") // quit = flag.Bool("quit", false, "Quits after executing.") console = flag.Bool("console", false, "Enters console after a file is evaluated.") @@ -111,7 +112,7 @@ func main() { ryeFile := dotsToMainRye(".") main_rye_file(ryeFile, false, true, *console, code) } else { - main_rye_repl(os.Stdin, os.Stdout, true, false) + main_rye_repl(os.Stdin, os.Stdout, true, false, *lang) } } else { // Check for --help flag @@ -128,7 +129,7 @@ func main() { ryeFile := findLastConsoleSave() main_rye_file(ryeFile, false, true, true, code) } else if args[0] == "here" { - main_rye_repl(os.Stdin, os.Stdout, true, true) + main_rye_repl(os.Stdin, os.Stdout, true, true, *lang) } else { ryeFile := dotsToMainRye(args[0]) main_rye_file(ryeFile, false, true, *console, code) @@ -137,7 +138,7 @@ func main() { if *do != "" { main_rye_file("", false, true, *console, code) } else { - main_rye_repl(os.Stdin, os.Stdout, true, false) + main_rye_repl(os.Stdin, os.Stdout, true, false, *lang) } } } @@ -152,15 +153,15 @@ func main_OLD() { // be formalized and we should use a proper library to handle all cases consistently, offer standard help, etc if len(os.Args) == 1 { - main_rye_repl(os.Stdin, os.Stdout, true, false) + main_rye_repl(os.Stdin, os.Stdout, true, false, "do") } else if len(os.Args) == 2 { if os.Args[1] == "shell" { main_rysh() } else if os.Args[1] == "here" { - main_rye_repl(os.Stdin, os.Stdout, true, true) + main_rye_repl(os.Stdin, os.Stdout, true, true, "do") } else if os.Args[1] == "--silent" { evaldo.ShowResults = false - main_rye_repl(os.Stdin, os.Stdout, true, false) + main_rye_repl(os.Stdin, os.Stdout, true, false, "do") } else if os.Args[1] == "cont" { ryeFile := findLastConsoleSave() main_rye_file(ryeFile, false, true, true, "") @@ -463,7 +464,7 @@ func main_rye_file(file string, sig bool, subc bool, interactive bool, code stri evaldo.MaybeDisplayFailureOrError(ps, ps.Idx) if interactive { - evaldo.DoRyeRepl(ps, evaldo.ShowResults) + evaldo.DoRyeRepl(ps, "do", evaldo.ShowResults) } case env.Error: @@ -523,7 +524,7 @@ func main_rye_file_OLD(file string, sig bool, subc bool, interactive bool, code evaldo.MaybeDisplayFailureOrError(es, genv) if interactive { - evaldo.DoRyeRepl(es, evaldo.ShowResults) + evaldo.DoRyeRepl(es, "do", evaldo.ShowResults) } case env.Error: @@ -570,7 +571,7 @@ func main_cgi_file(file string, sig bool) { } } -func main_rye_repl(_ io.Reader, _ io.Writer, subc bool, here bool) { +func main_rye_repl(_ io.Reader, _ io.Writer, subc bool, here bool, lang string) { input := " " // "name: \"Rye\" version: \"0.011 alpha\"" // userHomeDir, _ := os.UserHomeDir() // profile_path := filepath.Join(userHomeDir, ".rye-profile") @@ -614,7 +615,7 @@ func main_rye_repl(_ io.Reader, _ io.Writer, subc bool, here bool) { } } - evaldo.DoRyeRepl(es, evaldo.ShowResults) + evaldo.DoRyeRepl(es, lang, evaldo.ShowResults) } func main_rysh() {