* Renamed 'lua' to 'eval' * Renamed 'luascript' to 'script' * Added 'describe' to describe LIQ and lists tags that have defined schemas Tests for all of this
160 lines
2.9 KiB
Go
160 lines
2.9 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/chzyer/readline"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func ReplCommand() *cobra.Command {
|
|
return &cobra.Command{
|
|
Use: "repl",
|
|
Short: "Interactive Lua REPL",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
conn, err := connFromFlags(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return startRepl(conn)
|
|
},
|
|
}
|
|
}
|
|
|
|
func isIncomplete(code string) bool {
|
|
depth := 0
|
|
for _, word := range strings.Fields(code) {
|
|
switch word {
|
|
case "do", "function", "if", "repeat":
|
|
depth++
|
|
case "end", "until":
|
|
depth--
|
|
}
|
|
}
|
|
for _, ch := range code {
|
|
switch ch {
|
|
case '(', '[', '{':
|
|
depth++
|
|
case ')', ']', '}':
|
|
depth--
|
|
}
|
|
}
|
|
return depth > 0
|
|
}
|
|
|
|
func startRepl(conn *SpaceConnection) error {
|
|
rl, err := readline.NewEx(&readline.Config{
|
|
Prompt: "lua> ",
|
|
InterruptPrompt: "^C",
|
|
EOFPrompt: "",
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer rl.Close()
|
|
|
|
fmt.Println("SilverBullet Lua REPL. Type .exit or Ctrl-D to quit.")
|
|
|
|
var scriptMode bool
|
|
var scriptBuffer string
|
|
var multiLineBuffer string
|
|
|
|
for {
|
|
line, err := rl.Readline()
|
|
if err == readline.ErrInterrupt {
|
|
if multiLineBuffer != "" || scriptMode {
|
|
multiLineBuffer = ""
|
|
scriptMode = false
|
|
scriptBuffer = ""
|
|
rl.SetPrompt("lua> ")
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
|
|
trimmed := strings.TrimSpace(line)
|
|
|
|
if trimmed == ".exit" {
|
|
break
|
|
}
|
|
|
|
if trimmed == ".script" {
|
|
scriptMode = true
|
|
scriptBuffer = ""
|
|
fmt.Println("Entering script mode. Type .end to execute.")
|
|
rl.SetPrompt("...> ")
|
|
continue
|
|
}
|
|
|
|
if scriptMode {
|
|
if trimmed == ".end" {
|
|
scriptMode = false
|
|
rl.SetPrompt("lua> ")
|
|
if strings.TrimSpace(scriptBuffer) != "" {
|
|
result, err := conn.EvalLuaScript(scriptBuffer)
|
|
if err != nil {
|
|
fmt.Fprintf(rl.Stderr(), "Error: %s\n", err)
|
|
} else {
|
|
FormatOutput(os.Stdout, result, OutputText)
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
scriptBuffer += line + "\n"
|
|
continue
|
|
}
|
|
|
|
if strings.HasPrefix(trimmed, ".timeout ") {
|
|
var val int
|
|
if _, err := fmt.Sscanf(trimmed, ".timeout %d", &val); err != nil || val <= 0 {
|
|
fmt.Fprintln(rl.Stderr(), "Invalid timeout value")
|
|
} else {
|
|
conn.Timeout = time.Duration(val) * time.Second
|
|
fmt.Printf("Timeout set to %ds\n", val)
|
|
}
|
|
continue
|
|
}
|
|
|
|
if multiLineBuffer != "" {
|
|
multiLineBuffer += "\n" + line
|
|
} else {
|
|
multiLineBuffer = line
|
|
}
|
|
|
|
if isIncomplete(multiLineBuffer) {
|
|
rl.SetPrompt("...> ")
|
|
continue
|
|
}
|
|
|
|
code := multiLineBuffer
|
|
multiLineBuffer = ""
|
|
rl.SetPrompt("lua> ")
|
|
|
|
if strings.TrimSpace(code) == "" {
|
|
continue
|
|
}
|
|
|
|
isMultiLine := strings.Contains(code, "\n")
|
|
var result any
|
|
if isMultiLine {
|
|
result, err = conn.EvalLuaScript(code)
|
|
} else {
|
|
result, err = conn.EvalLua(code)
|
|
}
|
|
if err != nil {
|
|
fmt.Fprintf(rl.Stderr(), "Error: %s\n", err)
|
|
} else {
|
|
FormatOutput(os.Stdout, result, OutputText)
|
|
}
|
|
}
|
|
fmt.Println()
|
|
return nil
|
|
}
|