Options struct moved to options.go and dev-expr commands moved to commands.go

This commit is contained in:
Celestino Amoroso 2024-10-10 10:22:01 +02:00
parent e30d90befb
commit f8d83ee5b7
5 changed files with 354 additions and 344 deletions

View File

@ -1 +1 @@
1.11.0 41
1.12.0 15

234
commands.go Normal file
View File

@ -0,0 +1,234 @@
// 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)
}

347
main.go
View File

@ -30,80 +30,8 @@ const (
historyFile = "~/.expr_history"
)
type commandFunction func(ctx expr.ExprContext, args []string) (err error)
type command struct {
name string
description string
code commandFunction
}
func (cmd *command) exec(ctx expr.ExprContext, args []string) (err error) {
return cmd.code(ctx, args)
}
type commandHandler struct {
commands map[string]*command
// ctx expr.ExprContext
}
func NewCommandHandler() *commandHandler {
return &commandHandler{
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.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
}
// ------
type Options struct {
printTree bool
printPrefix bool
forceInteractive bool
builtin []any
expressions []io.Reader
formOpt expr.FmtOpt
baseVerb string
base int
output bool
rcCount int
}
func NewOptions() *Options {
return &Options{
expressions: make([]io.Reader, 0),
builtin: make([]any, 0),
formOpt: expr.Base10,
baseVerb: "%d",
base: 10,
output: true,
rcCount: 0,
}
}
func errOptValueRequired(opt string) error {
return fmt.Errorf("option %q requires a value", opt)
}
@ -112,104 +40,6 @@ func about() string {
return PROGNAME + " -- " + VERSION + "; Expr package " + EXPR_VERSION
}
func (opt *Options) loadRc() {
var rcPath string
var fh *os.File
var err error
if rcPath, err = utils.ExpandPath("~/.dev-expr.rc"); err != nil {
return
}
if fh, err = os.Open(rcPath); err == nil {
opt.expressions = append(opt.expressions, fh)
opt.rcCount++
}
}
func (opt *Options) parseArgs() (err error) {
for i := 1; i < len(os.Args) && err == nil; i++ {
arg := os.Args[i]
switch arg {
case "-i":
opt.forceInteractive = true
case "-t":
opt.printTree = true
case "-p":
opt.printPrefix = true
case "-e":
if i+1 < len(os.Args) {
i++
spec := os.Args[i]
if strings.HasPrefix(spec, "@") {
var f *os.File
if f, err = os.Open(spec[1:]); err == nil {
opt.expressions = append(opt.expressions, f)
} else {
return
}
} else {
opt.expressions = append(opt.expressions, strings.NewReader(spec))
}
} else {
err = errOptValueRequired(arg)
}
case "-b":
if i+1 < len(os.Args) {
i++
specs := strings.Split(os.Args[i], ",")
if len(specs) == 1 {
opt.builtin = append(opt.builtin, specs[0])
} else {
opt.builtin = append(opt.builtin, specs)
}
} else {
err = errOptValueRequired(arg)
}
case "-m", "--modules":
expr.IterateBuiltinModules(func(name, description string, _ bool) bool {
fmt.Printf("%20q: %s\n", name, description)
return true
})
os.Exit(0)
case "--noout":
opt.output = false
case "-h", "--help", "help":
cmdHandler.help()
os.Exit(0)
case "-v", "--version", "version", "about":
fmt.Println(about())
os.Exit(0)
default:
err = fmt.Errorf("invalid option nr %d %q", i+1, arg)
}
}
return
}
// ------
var cmdHandler *commandHandler
func (h *commandHandler) help() {
fmt.Fprintln(os.Stderr, `--- REPL commands:`)
for _, cmd := range h.commands {
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 importBuiltins(opt *Options) (err error) {
for _, spec := range opt.builtin {
if moduleSpec, ok := spec.(string); ok {
@ -266,7 +96,7 @@ func goInteractiveReadline(opt *Options, ctx expr.ExprContext, r io.Reader) {
if source != "" && !strings.HasPrefix(source, "//") {
if cmd, args := cmdHandler.get(source); cmd != nil {
rl.SaveToHistory(source)
if err = cmd.exec(ctx, args); err != nil {
if err = cmd.exec(opt, ctx, args); err != nil {
fmt.Fprintln(os.Stderr, "Eval Error:", err)
}
} else {
@ -296,7 +126,7 @@ func goInteractive(opt *Options, ctx expr.ExprContext, r io.Reader) {
// fmt.Printf("source=%q\n", source)
if source != "" && !strings.HasPrefix(source, "//") {
if cmd, args := cmdHandler.get(source); cmd != nil {
if err = cmd.exec(ctx, args); err != nil {
if err = cmd.exec(opt, ctx, args); err != nil {
break
}
} else {
@ -323,7 +153,7 @@ func goBatch(opt *Options, ctx expr.ExprContext, r io.Reader) {
// fmt.Printf("source=%q\n", source)
if source != "" && !strings.HasPrefix(source, "//") {
if cmd, args := cmdHandler.get(source); cmd != nil {
if err = cmd.exec(ctx, args); err != nil {
if err = cmd.exec(opt, ctx, args); err != nil {
fmt.Fprintln(os.Stderr, "Eval Error:", err)
break
}
@ -456,10 +286,9 @@ func registerLocalFunctions(ctx expr.ExprContext) {
}
var opt *Options
func main() {
opt = NewOptions()
setupCommands()
opt := NewOptions()
opt.loadRc()
if err := opt.parseArgs(); err != nil {
fmt.Fprintln(os.Stderr, err)
@ -492,169 +321,3 @@ func main() {
printResult(opt, ctx.GetLast())
}
}
// --------
func cmdExit(ctx expr.ExprContext, args []string) (err error) {
return io.EOF
}
func cmdHelp(ctx expr.ExprContext, args []string) (err error) {
cmdHandler.help()
return
}
func cmdMultiLine(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(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(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(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(ctx, fileName); err != nil {
break
}
}
}
} else {
err = execFile(ctx, target)
}
if err != nil {
break
}
}
return
}
func _cmdSource(ctx expr.ExprContext, args []string) (err error) {
var fh *os.File
for _, arg := range args {
length := len(arg)
if length == 0 {
continue
}
if length >= 2 {
if (arg[0] == '"' && arg[length-1] == '"') || arg[0] == '\'' && arg[length-1] == '\'' {
arg = arg[1 : length-1]
}
}
if fh, err = os.Open(arg); err == nil {
goBatch(opt, ctx, fh)
fh.Close()
} else {
break
}
}
return
}
func cmdModules(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(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(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 init() {
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)
}

113
options.go Normal file
View File

@ -0,0 +1,113 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// options.go
package main
import (
"fmt"
"io"
"os"
"strings"
"git.portale-stac.it/go-pkg/expr"
"git.portale-stac.it/go-pkg/utils"
)
type Options struct {
printTree bool
printPrefix bool
forceInteractive bool
builtin []any
expressions []io.Reader
formOpt expr.FmtOpt
baseVerb string
base int
output bool
rcCount int
}
func NewOptions() *Options {
return &Options{
expressions: make([]io.Reader, 0),
builtin: make([]any, 0),
formOpt: expr.Base10,
baseVerb: "%d",
base: 10,
output: true,
rcCount: 0,
}
}
func (opt *Options) loadRc() {
var rcPath string
var fh *os.File
var err error
if rcPath, err = utils.ExpandPath("~/.dev-expr.rc"); err != nil {
return
}
if fh, err = os.Open(rcPath); err == nil {
opt.expressions = append(opt.expressions, fh)
opt.rcCount++
}
}
func (opt *Options) parseArgs() (err error) {
for i := 1; i < len(os.Args) && err == nil; i++ {
arg := os.Args[i]
switch arg {
case "-i":
opt.forceInteractive = true
case "-t":
opt.printTree = true
case "-p":
opt.printPrefix = true
case "-e":
if i+1 < len(os.Args) {
i++
spec := os.Args[i]
if strings.HasPrefix(spec, "@") {
var f *os.File
if f, err = os.Open(spec[1:]); err == nil {
opt.expressions = append(opt.expressions, f)
} else {
return
}
} else {
opt.expressions = append(opt.expressions, strings.NewReader(spec))
}
} else {
err = errOptValueRequired(arg)
}
case "-b":
if i+1 < len(os.Args) {
i++
specs := strings.Split(os.Args[i], ",")
if len(specs) == 1 {
opt.builtin = append(opt.builtin, specs[0])
} else {
opt.builtin = append(opt.builtin, specs)
}
} else {
err = errOptValueRequired(arg)
}
case "-m", "--modules":
expr.IterateBuiltinModules(func(name, description string, _ bool) bool {
fmt.Printf("%20q: %s\n", name, description)
return true
})
os.Exit(0)
case "--noout":
opt.output = false
case "-h", "--help", "help":
cmdHandler.help()
os.Exit(0)
case "-v", "--version", "version", "about":
fmt.Println(about())
os.Exit(0)
default:
err = fmt.Errorf("invalid option nr %d %q", i+1, arg)
}
}
return
}

View File

@ -1 +1 @@
1.11.0
1.12.0