go-calc/calc/cli/cli.go

206 lines
4.6 KiB
Go
Raw Normal View History

2024-02-21 10:42:56 +00:00
package cli
import (
"errors"
"fmt"
"regexp"
"strings"
"gitea.zaclys.com/bvaudour/gob/number"
"gitea.zaclys.com/bvaudour/gob/option"
"gitea.zaclys.net/bvaudour/calc/calc"
)
type Cli struct {
c *calc.Calc
macros map[string]string
needMacro bool
macro []string
needAlt bool
alt []string
}
func New() *Cli {
c := Cli{
c: calc.New(),
macros: make(map[string]string),
}
return &c
}
func (c *Cli) beginMacro() (err option.Option[error]) {
if c.needMacro {
return option.Some(errors.New("Les macros imbriquées ne sont pas supportées"))
}
c.needMacro = true
return
}
func (c *Cli) addMacro(arg string) (err option.Option[error]) {
if !c.needMacro {
return option.Some(errors.New("La macro na pas commencé…"))
}
c.macro = append(c.macro, arg)
return
}
func (c *Cli) endMacro(k string) (err option.Option[error]) {
macro, fields, needMacro := "[ ]", make([]string, len(c.macro)), c.needMacro
copy(fields, c.macro)
c.resetMacro()
if len(fields) > 0 {
macro = fmt.Sprintf("[ %s ]", strings.Join(fields, " "))
}
if !needMacro {
return option.Some(errors.New("La macro na pas commencé…"))
} else if c.needAlt {
return c.addAlt(fmt.Sprintf("%s%s", macro, k))
} else if len(fields) == 0 {
c.macros[k] = macro
c.c.StoreMacro(k, calc.MacroCommand(calc.NoAction))
return
}
cmds := make([]calc.MacroElement, len(fields))
for i, f := range fields {
result := c.parse(f)
if me, ok := result.Ok(); ok {
cmds[i] = me
} else if e, ok := result.Err(); ok {
return option.Some(e)
}
}
c.macros[k] = macro
c.c.StoreMacro(k, cmds...)
return
}
func (c *Cli) removeMacro(k string) (err option.Option[error]) {
if err = c.c.ResetMacro(k); !err.IsDefined() {
delete(c.macros, k)
}
return
}
func (c *Cli) removeMacros() (err option.Option[error]) {
if err = c.c.ResetMacros(); !err.IsDefined() {
c.macros = make(map[string]string)
}
return
}
func (c *Cli) beginAlt() (err option.Option[error]) {
if c.needAlt {
err = option.Some(errors.New("Les alternatives imbriquées ne sont pas supportées"))
} else {
c.needAlt = true
}
return
}
func (c *Cli) addAlt(arg string) (err option.Option[error]) {
if !c.needAlt {
return option.Some(errors.New("Lalternative nest pas commencée…"))
} else if len(c.alt) == 2 {
return option.Some(errors.New("Lalternative est pleine…"))
}
c.alt = append(c.alt, arg)
return
}
func (c *Cli) endAlt() (err option.Option[error]) {
if !c.needAlt || len(c.alt) != 2 {
return option.Some(errors.New("Lalternative nest pas pleine ou nest pas commencée…"))
}
r1, r2 := c.parse(c.alt[0]), c.parse(c.alt[1])
if me1, ok := r1.Ok(); ok {
if me2, ok := r2.Ok(); ok {
return c.c.If(me1, me2)
}
} else if e1, ok := r1.Err(); ok {
return option.Some(e1)
} else if e2, ok := r2.Err(); ok {
return option.Some(e2)
}
return
}
func (c *Cli) resetAlt() { c.needAlt, c.alt = false, c.alt[:0] }
func (c *Cli) resetMacro() { c.needMacro, c.macro = false, c.macro[:0] }
func (c *Cli) resetAll() {
c.resetAlt()
c.resetMacro()
c.c.Reset()
}
func (c *Cli) parse(arg string) option.Result[calc.MacroElement] {
var me calc.MacroElement
if arg == "[" {
me = calc.MacroCommand(cliToCalcFunc(c, beginMacro))
} else if regexp.MustCompile(`^\]\w+$`).MatchString(arg) {
me = calc.MacroCommand(cliToCalcFunc(c, storeMacro(arg[1:])))
} else if c.needMacro {
me = calc.MacroCommand(cliToCalcFunc(c, addMacro(arg)))
} else if arg == "?" {
me = calc.MacroCommand(cliToCalcFunc(c, conditional))
} else if c.needAlt {
me = calc.MacroCommand(cliToCalcFunc(c, addConditional(arg)))
} else if cb, exists := otherFunctions[arg]; exists {
me = calc.MacroCommand(otherToCalcFunc(cb))
} else if cb, exists := searchCalcFunction(arg); exists {
me = calc.MacroCommand(cb)
} else if cb, exists := searchCliFunction(arg); exists {
me = calc.MacroCommand(cliToCalcFunc(c, cb))
} else {
n, ok := number.Parse(arg).Get()
if !ok {
return option.Err[calc.MacroElement](fmt.Errorf("Commande inconnue: %s", arg))
}
me = calc.MacroNumber(n)
}
return option.Ok(me)
}
func (c *Cli) Exec(arg string) {
result := c.parse(arg)
if err, ok := result.Err(); ok {
printError(err)
c.resetAll()
return
}
if element, ok := result.Ok(); ok {
if n, ok := element.Left(); ok {
c.c.Push(n)
} else if cb, ok := element.Right(); ok {
if err, ok := cb(c.c).Get(); ok {
printError(err)
c.resetAll()
return
}
}
}
if c.needAlt && len(c.alt) == 2 {
if err, ok := c.endAlt().Get(); ok {
printError(err)
c.resetAll()
}
}
}
func (c *Cli) ExecAll(args string) {
for _, arg := range strings.Fields(args) {
c.Exec(arg)
}
}