gitlab.com/Raven-IO/raven-delve@v1.22.4/pkg/terminal/starbind/repl.go (about) 1 package starbind 2 3 // Code in this file is derived from go.starlark.net/repl/repl.go 4 // Which is licensed under the following copyright: 5 // 6 // Copyright (c) 2017 The Bazel Authors. All rights reserved. 7 // 8 // Redistribution and use in source and binary forms, with or without 9 // modification, are permitted provided that the following conditions are 10 // met: 11 // 12 // 1. Redistributions of source code must retain the above copyright 13 // notice, this list of conditions and the following disclaimer. 14 // 15 // 2. Redistributions in binary form must reproduce the above copyright 16 // notice, this list of conditions and the following disclaimer in the 17 // documentation and/or other materials provided with the 18 // distribution. 19 // 20 // 3. Neither the name of the copyright holder nor the names of its 21 // contributors may be used to endorse or promote products derived 22 // from this software without specific prior written permission. 23 // 24 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 27 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 28 // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 29 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 30 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 32 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 34 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 36 import ( 37 "fmt" 38 "io" 39 "os" 40 41 "go.starlark.net/starlark" 42 "go.starlark.net/syntax" 43 44 "github.com/go-delve/liner" 45 ) 46 47 // REPL executes a read, eval, print loop. 48 func (env *Env) REPL() error { 49 thread := env.newThread() 50 globals := starlark.StringDict{} 51 for k, v := range env.env { 52 globals[k] = v 53 } 54 55 rl := liner.NewLiner() 56 defer rl.Close() 57 for { 58 if err := isCancelled(thread); err != nil { 59 return err 60 } 61 if err := rep(rl, thread, globals, env.out); err != nil { 62 if err == io.EOF { 63 break 64 } 65 return err 66 } 67 } 68 fmt.Fprintln(env.out) 69 return env.exportGlobals(globals) 70 } 71 72 const ( 73 normalPrompt = ">>> " 74 extraPrompt = "... " 75 76 exitCommand = "exit" 77 ) 78 79 // rep reads, evaluates, and prints one item. 80 // 81 // It returns an error (possibly readline.ErrInterrupt) 82 // only if readline failed. Starlark errors are printed. 83 func rep(rl *liner.State, thread *starlark.Thread, globals starlark.StringDict, out EchoWriter) error { 84 defer out.Flush() 85 eof := false 86 87 prompt := normalPrompt 88 readline := func() ([]byte, error) { 89 line, err := rl.Prompt(prompt) 90 out.Echo(prompt + line) 91 if line == exitCommand { 92 eof = true 93 return nil, io.EOF 94 } 95 rl.AppendHistory(line) 96 prompt = extraPrompt 97 if err != nil { 98 if err == io.EOF { 99 eof = true 100 } 101 return nil, err 102 } 103 return []byte(line + "\n"), nil 104 } 105 106 // parse 107 f, err := syntax.ParseCompoundStmt("<stdin>", readline) 108 if err != nil { 109 if eof { 110 return io.EOF 111 } 112 printError(err) 113 return nil 114 } 115 116 if expr := soleExpr(f); expr != nil { 117 //TODO: check for 'exit' 118 // eval 119 v, err := starlark.EvalExpr(thread, expr, globals) 120 if err != nil { 121 printError(err) 122 return nil 123 } 124 125 // print 126 if v != starlark.None { 127 fmt.Fprintln(out, v) 128 } 129 } else { 130 // compile 131 prog, err := starlark.FileProgram(f, globals.Has) 132 if err != nil { 133 printError(err) 134 return nil 135 } 136 137 // execute (but do not freeze) 138 res, err := prog.Init(thread, globals) 139 if err != nil { 140 printError(err) 141 } 142 143 // The global names from the previous call become 144 // the predeclared names of this call. 145 // If execution failed, some globals may be undefined. 146 for k, v := range res { 147 globals[k] = v 148 } 149 } 150 151 return nil 152 } 153 154 func soleExpr(f *syntax.File) syntax.Expr { 155 if len(f.Stmts) == 1 { 156 if stmt, ok := f.Stmts[0].(*syntax.ExprStmt); ok { 157 return stmt.X 158 } 159 } 160 return nil 161 } 162 163 // printError prints the error to stderr, 164 // or its backtrace if it is a Starlark evaluation error. 165 func printError(err error) { 166 if evalErr, ok := err.(*starlark.EvalError); ok { 167 fmt.Fprintln(os.Stderr, evalErr.Backtrace()) 168 } else { 169 fmt.Fprintln(os.Stderr, err) 170 } 171 } 172 173 // MakeLoad returns a simple sequential implementation of module loading 174 // suitable for use in the REPL. 175 // Each function returned by MakeLoad accesses a distinct private cache. 176 func MakeLoad() func(thread *starlark.Thread, module string) (starlark.StringDict, error) { 177 type entry struct { 178 globals starlark.StringDict 179 err error 180 } 181 182 var cache = make(map[string]*entry) 183 184 return func(thread *starlark.Thread, module string) (starlark.StringDict, error) { 185 e, ok := cache[module] 186 if e == nil { 187 if ok { 188 // request for package whose loading is in progress 189 return nil, fmt.Errorf("cycle in load graph") 190 } 191 192 // Add a placeholder to indicate "load in progress". 193 cache[module] = nil 194 195 // Load it. 196 thread := &starlark.Thread{Name: "exec " + module, Load: thread.Load} 197 globals, err := starlark.ExecFile(thread, module, nil, nil) 198 e = &entry{globals, err} 199 200 // Update the cache. 201 cache[module] = e 202 } 203 return e.globals, e.err 204 } 205 }