206 lines
4.6 KiB
Go
206 lines
4.6 KiB
Go
|
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 n’a 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 n’a 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("L’alternative n’est pas commencée…"))
|
|||
|
} else if len(c.alt) == 2 {
|
|||
|
return option.Some(errors.New("L’alternative 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("L’alternative n’est pas pleine ou n’est 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)
|
|||
|
}
|
|||
|
}
|