diff --git a/calc/calc.go b/calc/calc.go index 423e537..61b13f2 100644 --- a/calc/calc.go +++ b/calc/calc.go @@ -99,6 +99,7 @@ func (c *Calc) StoreStack(k string) (err option.Option[error]) { err = option.Some(ErrIsEmpty(errStack)) } else { c.registry.Set(k, c.Stack()...) + c.Reset() } return diff --git a/calc/cli/cli.go b/calc/cli/cli.go index b0ad8aa..d068070 100644 --- a/calc/cli/cli.go +++ b/calc/cli/cli.go @@ -1,9 +1,7 @@ package cli import ( - "errors" "fmt" - "regexp" "strings" "gitea.zaclys.com/bvaudour/gob/number" @@ -11,13 +9,41 @@ import ( "gitea.zaclys.net/bvaudour/calc/calc" ) -type Cli struct { - c *calc.Calc - macros map[string]string +type mstate struct { needMacro bool macro []string - needAlt bool - alt []string + cmd []calc.MacroElement +} + +func (s *mstate) reset() { + s.needMacro = false + s.macro = s.macro[:0] + s.cmd = s.cmd[:0] +} + +func (s mstate) strMacro() string { + if len(s.macro) == 0 { + return "[ ]" + } + + return fmt.Sprintf("[ %s ]", strings.Join(s.macro, " ")) +} + +type astate struct { + needAlt bool + alt []calc.MacroElement +} + +func (s *astate) reset() { + s.needAlt = false + s.alt = s.alt[:0] +} + +type Cli struct { + c *calc.Calc + macros map[string]string + mstate + astate } func New() *Cli { @@ -28,142 +54,48 @@ func New() *Cli { 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() +func (c *Cli) reset() { + c.mstate.reset() + c.astate.reset() c.c.Reset() } func (c *Cli) parse(arg string) option.Result[calc.MacroElement] { var me calc.MacroElement + needExec := !c.needAlt && !c.needMacro - 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 { + var exec = func(err option.Option[error]) option.Result[calc.MacroElement] { + var elt calc.MacroElement + if e, ok := err.Get(); ok { + return option.Err[calc.MacroElement](e) + } + return option.Ok(elt) + } + + if cb, ok := otherFunctions[arg]; ok { + if needExec { + return exec(cb()) + } me = calc.MacroCommand(otherToCalcFunc(cb)) - } else if cb, exists := searchCalcFunction(arg); exists { + } else if cb, ok := searchCalcFunction(arg); ok { + if needExec { + return exec(cb(c.c)) + } me = calc.MacroCommand(cb) - } else if cb, exists := searchCliFunction(arg); exists { + } else if cb, ok, now := searchCliFunction(arg); ok { + if needExec || now { + return exec(cb(c)) + } 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)) + return exec(option.Some(fmt.Errorf("Commande inconnue: %s", arg))) + } else if needExec { + c.c.Push(n) + } else { + me = calc.MacroNumber(n) } - me = calc.MacroNumber(n) } return option.Ok(me) @@ -174,26 +106,25 @@ func (c *Cli) Exec(arg string) { if err, ok := result.Err(); ok { printError(err) - c.resetAll() + c.reset() 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 element, ok := result.Ok(); ok && !element.IsNil() { + if c.needMacro { + c.macro = append(c.macro, arg) + c.cmd = append(c.cmd, element) + } else if c.needAlt { + c.alt = append(c.alt, element) } } if c.needAlt && len(c.alt) == 2 { - if err, ok := c.endAlt().Get(); ok { - printError(err) - c.resetAll() + err := c.c.If(c.alt[0], c.alt[1]) + c.astate.reset() + if e, ok := err.Get(); ok { + printError(e) + c.reset() } } } diff --git a/calc/cli/errors.go b/calc/cli/errors.go new file mode 100644 index 0000000..11d4d7e --- /dev/null +++ b/calc/cli/errors.go @@ -0,0 +1,11 @@ +package cli + +import ( + "errors" +) + +var ( + errMacroOpen = errors.New("Les macros imbriquées ne sont pas supportées.") + errAltOpen = errors.New("L’alternative est déjà commencée.") + errMacroClosed = errors.New("La macro n’est pas ouverte.") +) diff --git a/calc/cli/functions.go b/calc/cli/functions.go index d49f315..751ee99 100644 --- a/calc/cli/functions.go +++ b/calc/cli/functions.go @@ -17,6 +17,18 @@ type integer interface { type CliFunc func(*Cli) option.Option[error] type WordCliFunc func(string) CliFunc +func cliToCalcFunc(c *Cli, cb CliFunc) calc.CalcFunc { + return func(_ *calc.Calc) option.Option[error] { + return cb(c) + } +} + +func otherToCalcFunc(cb func() option.Option[error]) calc.CalcFunc { + return func(_ *calc.Calc) option.Option[error] { + return cb() + } +} + func stack2Str(numbers calc.Stack) string { sl := make([]string, len(numbers)) for i, n := range numbers { @@ -141,61 +153,6 @@ func printMacro(k string) CliFunc { } } -func resetMacro(k string) CliFunc { - return func(c *Cli) option.Option[error] { - return c.removeMacro(k) - } -} - -func storeMacro(k string) CliFunc { - return func(c *Cli) option.Option[error] { - return c.endMacro(k) - } -} - -func addMacro(v string) CliFunc { - return func(c *Cli) option.Option[error] { - return c.addMacro(v) - } -} - -func setPrecision(c *calc.Calc, precision option.Option[uint64], fix bool) (err option.Option[error]) { - c.SetPrecision(precision, option.Some(fix)) - return -} - -func fixPrecision(fix bool) calc.CalcFunc { - return func(c *calc.Calc) (err option.Option[error]) { - return setPrecision(c, option.None[uint64](), fix) - } -} - -func precision[N integer](fix bool) calc.IntCalcFunc[N] { - return func(args ...N) calc.CalcFunc { - return func(c *calc.Calc) (err option.Option[error]) { - var p option.Option[uint64] - if len(args) > 1 { - p = option.Some(uint64(args[0])) - } - return setPrecision(c, p, fix) - } - } -} - -func beginMacro(c *Cli) option.Option[error] { - return c.beginMacro() -} - -func conditional(c *Cli) option.Option[error] { - return c.beginAlt() -} - -func addConditional(v string) CliFunc { - return func(c *Cli) option.Option[error] { - return c.addAlt(v) - } -} - func quit() (err option.Option[error]) { os.Exit(0) return @@ -216,14 +173,81 @@ func execMacro(k string) calc.CalcFunc { } } -func cliToCalcFunc(c *Cli, cb CliFunc) calc.CalcFunc { - return func(_ *calc.Calc) option.Option[error] { - return cb(c) +func setPrecision(c *calc.Calc, precision option.Option[uint64], fix bool) (err option.Option[error]) { + c.SetPrecision(precision, option.Some(fix)) + return +} + +func fixPrecision(fix bool) calc.CalcFunc { + return func(c *calc.Calc) (err option.Option[error]) { + return setPrecision(c, option.None[uint64](), fix) } } -func otherToCalcFunc(cb func() option.Option[error]) calc.CalcFunc { - return func(_ *calc.Calc) option.Option[error] { - return cb() +func precision[N integer](fix bool) calc.IntCalcFunc[N] { + return func(args ...N) calc.CalcFunc { + return func(c *calc.Calc) (err option.Option[error]) { + var p option.Option[uint64] + if len(args) > 0 { + p = option.Some(uint64(args[0])) + } + + return setPrecision(c, p, fix) + } + } +} + +func startMacro(c *Cli) (err option.Option[error]) { + if c.needMacro { + return option.Some(errMacroOpen) + } + c.needMacro = true + + return +} + +func startAlt(c *Cli) (err option.Option[error]) { + if c.needAlt { + return option.Some(errAltOpen) + } + c.needAlt = true + + return +} + +func resetMacro(k string) CliFunc { + return func(c *Cli) (err option.Option[error]) { + if err = c.c.ResetMacro(k); !err.IsDefined() { + delete(c.macros, k) + } + + return + } +} + +func storeMacro(k string, macro string, cmd []calc.MacroElement) CliFunc { + return func(c *Cli) (err option.Option[error]) { + c.c.StoreMacro(k, cmd...) + c.macros[k] = macro + + return + } +} + +func endMacro(k string) CliFunc { + return func(c *Cli) (err option.Option[error]) { + if !c.needMacro { + return option.Some(errMacroClosed) + } + + cb := storeMacro(k, c.strMacro(), c.cmd) + c.mstate.reset() + + if !c.needAlt { + return cb(c) + } + c.alt = append(c.alt, calc.MacroCommand(cliToCalcFunc(c, cb))) + + return } } diff --git a/calc/cli/mapping.go b/calc/cli/mapping.go index baea4c7..9356de4 100644 --- a/calc/cli/mapping.go +++ b/calc/cli/mapping.go @@ -37,6 +37,7 @@ var ( "inv": calc.Inv, "+": calc.Add, "-": calc.Sub, + "−": calc.Sub, "*": calc.Mul, "×": calc.Mul, "/": calc.Div, @@ -92,9 +93,9 @@ var ( } intCalcFunctions = map[string]calc.IntCalcFunc[uint64]{ - `^c\d+$`: func(bases ...uint64) calc.CalcFunc { - if len(bases) > 0 { - return calc.ToBase(bases[0]) + `^c\d+$`: func(base ...uint64) calc.CalcFunc { + if len(base) > 0 { + return calc.ToBase(base[0]) } return func(c *calc.Calc) (err option.Option[error]) { return } }, @@ -108,14 +109,19 @@ var ( cliFunctions = map[string]CliFunc{ ";P": printMacros, - "[": beginMacro, - "?": conditional, + "[": startMacro, + "?": startAlt, } wordCliFunctions = map[string]WordCliFunc{ `^;p\w+$`: printMacro, `^;c\w+$`: resetMacro, - `^\]\w+$`: storeMacro, + `^\]\w+$`: endMacro, + } + + needExec = map[string]bool{ + "[": true, + `^\]\w+$`: true, } ) @@ -146,7 +152,7 @@ func searchIntCalcFunction(arg string) (cb calc.CalcFunc, ok bool) { return } -func searchWordCliFunction(arg string) (cb CliFunc, ok bool) { +func searchWordCliFunction(arg string) (cb CliFunc, ok, execNow bool) { for k, f := range wordCliFunctions { if ok = regexp.MustCompile(k).MatchString(arg); ok { if arg[0] == ']' { @@ -154,6 +160,7 @@ func searchWordCliFunction(arg string) (cb CliFunc, ok bool) { } else { cb = f(arg[2:]) } + execNow = needExec[k] break } } @@ -171,9 +178,11 @@ func searchCalcFunction(arg string) (cb calc.CalcFunc, ok bool) { return } -func searchCliFunction(arg string) (cb CliFunc, ok bool) { +func searchCliFunction(arg string) (cb CliFunc, ok, execNow bool) { if cb, ok = cliFunctions[arg]; !ok { - cb, ok = searchWordCliFunction(arg) + cb, ok, execNow = searchWordCliFunction(arg) + } else { + execNow = needExec[arg] } return diff --git a/calc/cli/resources/help.txt b/calc/cli/resources/help.txt index 064d40e..39e814e 100644 --- a/calc/cli/resources/help.txt +++ b/calc/cli/resources/help.txt @@ -17,7 +17,7 @@ {B}()[+|-]{0} Nombre en base b {T}Options d’impression{0} - {B}po{0} Imprime les options d’affichage + {B}op{0} Imprime les options d’affichage {B}of{0} Affichage des nombres à virgules avec une précision fixe de n décimales {B}oa{0} Affichage des nombres à virgules avec une précision auto (max n décimales) Les zéros non significatifs sont supprimés. @@ -57,7 +57,7 @@ {B}--{0} Soustrait 1 {B}inv{0} inverse le nombre {B}+{0} Addition - {B}-{0} Soustraction + {B}-, −{0} Soustraction {B}*, ×{0} Multiplication {B}/, ÷{0} Division {B}//{0} Division entière @@ -84,8 +84,8 @@ {B}c{0} Conversion en base n {B}ci[]{0} Conversion en entier. Si la base est définie, force la conversion de la base. {B}cd[]{0} Conversion en nombre décimal. Si la base est définie, force la conversion de la base. - {B}cf[]{0} Conversion en nombre scientifique. Si la base est définie, force la conversion de la base. - {B}cs[]{0} Conversion en fraction. Si la base est définie, force la conversion de la base. + {B}cs[]{0} Conversion en nombre scientifique. Si la base est définie, force la conversion de la base. + {B}cf[]{0} Conversion en fraction. Si la base est définie, force la conversion de la base. {T}Arithmétique sur les bits{0} {B}>>{0} Déplace les bits vers la droite diff --git a/calc/functions.go b/calc/functions.go index 7b9fa81..6848bf5 100644 --- a/calc/functions.go +++ b/calc/functions.go @@ -114,7 +114,7 @@ var ( Sum = Reduce(number.Sum) Min = Reduce(number.Min) Max = Reduce(number.Max) - Mean = Reduce(number.Min) + Mean = Reduce(number.Mean) Median = Reduce(number.Median) Mode = Reduce(number.Mode) Variance = Reduce(number.Variance) diff --git a/go.mod b/go.mod index 6a71a35..c93b91f 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,6 @@ module gitea.zaclys.net/bvaudour/calc go 1.22.0 require ( - gitea.zaclys.com/bvaudour/gob v0.0.0-20240221091259-5b437644bd67 // indirect - gitea.zaclys.com/bvaudour/readline v0.0.0-20240221095017-721dfd1e4fe2 // indirect + gitea.zaclys.com/bvaudour/gob v0.0.0-20240225133050-ec6a3ba492cc // indirect + gitea.zaclys.com/bvaudour/readline v0.0.0-20240225134040-b1215d26587d // indirect ) diff --git a/go.sum b/go.sum index 03aa86e..936cfbb 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,4 @@ -gitea.zaclys.com/bvaudour/gob v0.0.0-20240221091259-5b437644bd67 h1:Fe9g2grCWVsKCZktydlUNff4ihjRIWO2U7EBVHosdOw= -gitea.zaclys.com/bvaudour/gob v0.0.0-20240221091259-5b437644bd67/go.mod h1:Gw6x0KNKoXv6AMtRkaI+iWM2enVzwHikUSskuEzWQz4= -gitea.zaclys.com/bvaudour/readline v0.0.0-20240221095017-721dfd1e4fe2 h1:ImUUypyhZwtwYNRytCve7Os4ADayq8pgI9iVi7FkrZM= -gitea.zaclys.com/bvaudour/readline v0.0.0-20240221095017-721dfd1e4fe2/go.mod h1:KQcGQpPnklyZYVgjOj8KqiBUqEOoBj2L0Bp9VdIdmPI= +gitea.zaclys.com/bvaudour/gob v0.0.0-20240225133050-ec6a3ba492cc h1:taHLOnrgyIlGYSs9cu+FXChAYtIeHoJg2D8to+UDgoA= +gitea.zaclys.com/bvaudour/gob v0.0.0-20240225133050-ec6a3ba492cc/go.mod h1:Gw6x0KNKoXv6AMtRkaI+iWM2enVzwHikUSskuEzWQz4= +gitea.zaclys.com/bvaudour/readline v0.0.0-20240225134040-b1215d26587d h1:Xch3Agxkk/s8XHDv2QG2hb5O69nhuvlKQn7BedS+gSw= +gitea.zaclys.com/bvaudour/readline v0.0.0-20240225134040-b1215d26587d/go.mod h1:XUeoO+H5R/sX02vWsvILkqpYM4WRWQWHfsNl0GoDKBs=