// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. // commands.go package main import ( "fmt" "io" "os" "strings" "git.portale-stac.it/go-pkg/expr" "git.portale-stac.it/go-pkg/utils" ) type commandFunction func(opt *Options, ctx expr.ExprContext, args []string) (err error) type command struct { name string description string code commandFunction } func (cmd *command) exec(opt *Options, ctx expr.ExprContext, args []string) (err error) { return cmd.code(opt, ctx, args) } type commandHandler struct { cmdIndex []string commands map[string]*command } func NewCommandHandler() *commandHandler { return &commandHandler{ cmdIndex: make([]string, 0, 20), commands: make(map[string]*command), } } // func (h *commandHandler) setContext(ctx expr.ExprContext) { // h.ctx = ctx // } func (h *commandHandler) add(name, description string, f commandFunction) { h.cmdIndex = append(h.cmdIndex, name) h.commands[name] = &command{name: name, description: description, code: f} } func (h *commandHandler) get(cmdLine string) (cmd *command, args []string) { if len(cmdLine) > 0 { tokens := strings.Split(cmdLine, " ") name := tokens[0] args = make([]string, 0, len(tokens)-1) if cmd = h.commands[name]; cmd != nil && len(tokens) > 1 { for _, tk := range tokens[1:] { if tk != "" { args = append(args, tk) } } } } return } // ------ var cmdHandler *commandHandler func (h *commandHandler) help() { fmt.Fprintln(os.Stderr, `--- REPL commands:`) for _, name := range h.cmdIndex { cmd := h.commands[name] fmt.Fprintf(os.Stderr, "%12s -- %s\n", cmd.name, cmd.description) } fmt.Fprint(os.Stderr, ` --- Command line options: -b <builtin> Import builtin modules. <builtin> can be a list of module names or a glob-pattern. Use the special value 'all' or the pattern '*' to import all modules. -e <expression> Evaluate <expression> instead of standard-input -i Force REPL operation when all -e occurences have been processed -h, --help, help Show this help menu -m, --modules List all builtin modules --noout Disable printing of expression results -p Print prefix form -t Print tree form -v, --version Show program version `) } // -------- func cmdExit(opt *Options, ctx expr.ExprContext, args []string) (err error) { return io.EOF } func cmdHelp(opt *Options, ctx expr.ExprContext, args []string) (err error) { cmdHandler.help() return } func cmdMultiLine(opt *Options, ctx expr.ExprContext, args []string) (err error) { if opt.formOpt&expr.MultiLine == 0 { opt.formOpt |= expr.MultiLine } else { opt.formOpt &= ^expr.MultiLine } return } func cmdTty(opt *Options, ctx expr.ExprContext, args []string) (err error) { if opt.formOpt&expr.TTY == 0 { opt.formOpt |= expr.TTY } else { opt.formOpt &= ^expr.TTY } return } func execFile(opt *Options, ctx expr.ExprContext, fileName string) (err error) { var fh *os.File if fh, err = os.Open(fileName); err == nil { goBatch(opt, ctx, fh) fh.Close() } return } func cmdSource(opt *Options, ctx expr.ExprContext, args []string) (err error) { var target string for _, arg := range args { if len(arg) == 0 { continue } // TODO migliorare questa parte: eventualmente valutare un'espressione if target, err = checkStringLiteral(arg); err != nil { break } if target, err = utils.ExpandPath(target); err != nil { break } if isPattern(target) { var fileNames []string if fileNames, err = matchPathPattern(target); err == nil { for _, fileName := range fileNames { if err = execFile(opt, ctx, fileName); err != nil { break } } } } else { err = execFile(opt, ctx, target) } if err != nil { break } } return } func cmdModules(opt *Options, ctx expr.ExprContext, args []string) (err error) { expr.IterateBuiltinModules(func(name, description string, imported bool) bool { var check rune = ' ' if imported { check = '*' } fmt.Printf("%c %20q: %s\n", check, name, description) return true }) return } func cmdBase(opt *Options, ctx expr.ExprContext, args []string) (err error) { if len(args) == 0 { fmt.Println(opt.base) } else if args[0] == "2" { opt.baseVerb = "0b%b" opt.base = 2 } else if args[0] == "8" { opt.baseVerb = "0o%o" opt.base = 8 } else if args[0] == "10" { opt.baseVerb = "%d" opt.base = 10 } else if args[0] == "16" { opt.baseVerb = "0x%x" opt.base = 16 } else { err = fmt.Errorf("invalid number base %s", args[0]) } return } func cmdOutput(opt *Options, ctx expr.ExprContext, args []string) (err error) { var outputArg string if len(args) == 0 { outputArg = "status" } else { outputArg = strings.ToLower(args[0]) } switch outputArg { case "on": opt.output = true case "off": opt.output = false case "status": if opt.output { fmt.Println("on") } else { fmt.Println("off") } default: err = fmt.Errorf("output: unknown option %q", outputArg) } return } //------------------ func setupCommands() { cmdHandler = NewCommandHandler() cmdHandler.add("base", "Set the integer output base: 2, 8, 10, or 16", cmdBase) cmdHandler.add("exit", "Exit the program", cmdExit) cmdHandler.add("help", "Show command list", cmdHelp) cmdHandler.add("ml", "Enable/Disable multi-line output", cmdMultiLine) cmdHandler.add("mods", "List builtin modules", cmdModules) cmdHandler.add("output", "Enable/Disable printing expression results. Options 'on', 'off', 'status'", cmdOutput) cmdHandler.add("source", "Load a file as input", cmdSource) cmdHandler.add("tty", "Enable/Disable ansi output", cmdTty) }