235 lines
5.7 KiB
Go
235 lines
5.7 KiB
Go
// 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)
|
|
}
|