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) } }