Commit initial
This commit is contained in:
commit
f0bc5f2273
11
LICENSE
Normal file
11
LICENSE
Normal file
@ -0,0 +1,11 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
88
README.md
Normal file
88
README.md
Normal file
@ -0,0 +1,88 @@
|
||||
# calc
|
||||
|
||||
Calc est une calculatrice en ligne de commande écrite en Go et fonctionnant en notation polonaise inverse.
|
||||
|
||||
## Installation
|
||||
|
||||
Vous devez avoir go installé sur votre système. Pour installer calc :
|
||||
|
||||
```
|
||||
go install gitea.zaclys.com/bvaudour/calc
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Saisissez simplement `calc` dans votre terminal. Pour afficher l’aide, saisissez `h` puis appuyez sur la touche `Entrée`.
|
||||
|
||||
Exemple :
|
||||
|
||||
```
|
||||
1 1 + p
|
||||
```
|
||||
|
||||
(effectue l’opération `1+1` puis affiche le résultat)
|
||||
|
||||
## Opérateurs supportés
|
||||
|
||||
### Opérateurs mathématiques de base
|
||||
|
||||
- Addition
|
||||
- Soustraction
|
||||
- Multiplication
|
||||
- Division entière
|
||||
- Division exacte
|
||||
- Reste
|
||||
- Incrémentation
|
||||
- Décrémentation
|
||||
- Valeur absolue
|
||||
- Inverse
|
||||
- Puissance au carré
|
||||
- Puissance quelconque
|
||||
- Racine carrée
|
||||
- Racine quelconque
|
||||
- Factorielle
|
||||
- Valeur absolue
|
||||
|
||||
### Comparaison de nombres
|
||||
|
||||
- Comparaison
|
||||
- Égalité
|
||||
- Inégalité
|
||||
- Supériorité
|
||||
- Supériorité stricte
|
||||
- Infériorité
|
||||
- Infériorité stricte
|
||||
|
||||
### Opération sur les bits
|
||||
|
||||
- Déplacement à gauche
|
||||
- Déplacement à droite
|
||||
|
||||
### Statistiques
|
||||
|
||||
- Minimum
|
||||
- Maximum
|
||||
- Moyenne
|
||||
- Médiane
|
||||
- Écart-type
|
||||
- Mode
|
||||
- Variance
|
||||
|
||||
## Format des nombres
|
||||
|
||||
Calc supporte 4 types de nombres :
|
||||
|
||||
- Entiers
|
||||
- Décimaux
|
||||
- Fractions
|
||||
- Nombres scientifiques
|
||||
|
||||
Les nombres peuvent être spécifiés en n’importe quelle base, de 2 à 36.
|
||||
|
||||
## Registre
|
||||
|
||||
Le registre permet de stocker en mémoire des nombres ou piles de nombres, et les réinjecter dans la pile.
|
||||
|
||||
## Macro
|
||||
|
||||
Les macros permettent d’enregistrer des suites de commandes complexes.
|
182
calc/calc.go
Normal file
182
calc/calc.go
Normal file
@ -0,0 +1,182 @@
|
||||
package calc
|
||||
|
||||
import (
|
||||
"gitea.zaclys.com/bvaudour/gob/number"
|
||||
"gitea.zaclys.com/bvaudour/gob/option"
|
||||
)
|
||||
|
||||
type Calc struct {
|
||||
stack Stack
|
||||
registry Registry
|
||||
macro Macro
|
||||
}
|
||||
|
||||
type CalcFunc func(*Calc) option.Option[error]
|
||||
type IntCalcFunc[N integer] func(...N) CalcFunc
|
||||
type WordCalcFunc func(string) CalcFunc
|
||||
type CalcErrFunc func(*Calc) []error
|
||||
|
||||
func New() *Calc {
|
||||
return &Calc{
|
||||
registry: make(Registry),
|
||||
macro: make(Macro),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Calc) SetPrecision(precision option.Option[uint64], fix option.Option[bool]) {
|
||||
if p, ok := precision.Get(); ok {
|
||||
number.FloatingPrecision = p
|
||||
}
|
||||
|
||||
if f, ok := fix.Get(); ok {
|
||||
number.FixedPrecision = f
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Calc) Precision() (precision uint64, fix bool) {
|
||||
return number.FloatingPrecision, number.FixedPrecision
|
||||
}
|
||||
|
||||
func (c *Calc) Push(numbers ...number.Number) { c.stack.Push(numbers...) }
|
||||
func (c *Calc) Stack() Stack { return c.stack.Clone() }
|
||||
func (c *Calc) GetStack() (result option.Result[Stack]) {
|
||||
if c.stack.IsEmpty() {
|
||||
result = option.Err[Stack](ErrIsEmpty(errStack))
|
||||
} else {
|
||||
result = option.Ok(c.Stack())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
func (c *Calc) Len() { c.Push(number.IntOf(c.stack.Len())) }
|
||||
|
||||
func (c *Calc) Last() option.Result[number.Number] { return c.stack.Last() }
|
||||
|
||||
func (c *Calc) Pop() (err option.Option[error]) {
|
||||
if e, ok := c.stack.Pop().Err(); ok {
|
||||
err = option.Some(e)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Calc) Reset() option.Option[error] { return c.stack.Reset() }
|
||||
func (c *Calc) Duplicate() option.Option[error] { return c.stack.Duplicate() }
|
||||
func (c *Calc) DuplicateStack() option.Option[error] { return c.stack.DuplicateAll() }
|
||||
func (c *Calc) Reverse() option.Option[error] { return c.stack.Reverse() }
|
||||
func (c *Calc) ReverseStack() option.Option[error] { return c.stack.ReverseAll() }
|
||||
func (c *Calc) Sort() option.Option[error] { return c.stack.Sort() }
|
||||
func (c *Calc) SortDesc() option.Option[error] { return c.stack.SortDesc() }
|
||||
|
||||
func (c *Calc) Registries() Registry { return c.registry.Clone() }
|
||||
func (c *Calc) GetRegistries() (result option.Result[Registry]) {
|
||||
if len(c.registry) == 0 {
|
||||
result = option.Err[Registry](ErrIsEmpty(errMemory))
|
||||
} else {
|
||||
result = option.Ok(c.Registries())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Calc) Registry(k string) option.Result[Stack] { return c.registry.Get(k) }
|
||||
func (c *Calc) ResetRegistry(k string) option.Option[error] { return c.registry.Reset(k) }
|
||||
func (c *Calc) ResetRegistries() option.Option[error] { return c.registry.ResetAll() }
|
||||
|
||||
func (c *Calc) Store(k string) (err option.Option[error]) {
|
||||
pop := c.stack.Pop()
|
||||
if n, ok := pop.Ok(); ok {
|
||||
c.registry.Set(k, n)
|
||||
} else if e, ok := pop.Err(); ok {
|
||||
err = option.Some(e)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Calc) StoreStack(k string) (err option.Option[error]) {
|
||||
if c.stack.IsEmpty() {
|
||||
err = option.Some(ErrIsEmpty(errStack))
|
||||
} else {
|
||||
c.registry.Set(k, c.Stack()...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Calc) Load(k string) (err option.Option[error]) {
|
||||
result := c.registry.Get(k)
|
||||
if s, ok := result.Ok(); ok {
|
||||
c.Push(s...)
|
||||
} else if e, ok := result.Err(); ok {
|
||||
err = option.Some(e)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Calc) Macros() Macro { return c.macro.Clone() }
|
||||
func (c *Calc) GetMacros() (result option.Result[Macro]) {
|
||||
if len(c.macro) == 0 {
|
||||
result = option.Err[Macro](ErrIsEmpty(errMemory))
|
||||
} else {
|
||||
result = option.Ok(c.Macros())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Calc) Macro(k string) (result option.Result[MacroStack]) { return c.macro.Get(k) }
|
||||
func (c *Calc) StoreMacro(k string, args ...MacroElement) {
|
||||
c.macro.Set(k, args)
|
||||
}
|
||||
|
||||
func (c *Calc) Exec(s MacroStack) (errors []error) {
|
||||
for _, e := range s {
|
||||
if n, ok := e.Left(); ok {
|
||||
c.Push(n)
|
||||
} else if f, ok := e.Right(); ok {
|
||||
if err, ok := f(c).Get(); ok {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Calc) ExecMacro(k string) (errors []error) {
|
||||
result := c.macro.Get(k)
|
||||
if s, ok := result.Ok(); ok {
|
||||
errors = c.Exec(s)
|
||||
} else if err, ok := result.Err(); ok {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Calc) ResetMacro(k string) option.Option[error] { return c.macro.Reset(k) }
|
||||
func (c *Calc) ResetMacros() option.Option[error] { return c.macro.ResetAll() }
|
||||
func (c *Calc) NoAction() (error option.Option[error]) { return }
|
||||
|
||||
func (c *Calc) If(e1, e2 MacroElement) (err option.Option[error]) {
|
||||
result := c.stack.Pop()
|
||||
if n, ok := result.Ok(); ok {
|
||||
if n.IsZero() || n.IsNan() {
|
||||
if n2, ok := e2.Left(); ok {
|
||||
c.Push(n2)
|
||||
} else if c2, ok := e2.Right(); ok {
|
||||
c2(c)
|
||||
}
|
||||
} else if n1, ok := e1.Left(); ok {
|
||||
c.Push(n1)
|
||||
} else if c1, ok := e1.Right(); ok {
|
||||
c1(c)
|
||||
}
|
||||
} else if e, ok := result.Err(); ok {
|
||||
err = option.Some(e)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
205
calc/cli/cli.go
Normal file
205
calc/cli/cli.go
Normal file
@ -0,0 +1,205 @@
|
||||
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)
|
||||
}
|
||||
}
|
229
calc/cli/functions.go
Normal file
229
calc/cli/functions.go
Normal file
@ -0,0 +1,229 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"gitea.zaclys.com/bvaudour/gob/option"
|
||||
"gitea.zaclys.net/bvaudour/calc/calc"
|
||||
)
|
||||
|
||||
type integer interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
|
||||
}
|
||||
|
||||
type CliFunc func(*Cli) option.Option[error]
|
||||
type WordCliFunc func(string) CliFunc
|
||||
|
||||
func stack2Str(numbers calc.Stack) string {
|
||||
sl := make([]string, len(numbers))
|
||||
for i, n := range numbers {
|
||||
sl[i] = n.String()
|
||||
}
|
||||
return strings.Join(sl, " ")
|
||||
}
|
||||
|
||||
func printResult(arg any) (result option.Option[error]) {
|
||||
if _, e := fmt.Printf("\033[1;33m%s\033[m\n", arg); e != nil {
|
||||
result = option.Some(e)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func printError(err error) (result option.Option[error]) {
|
||||
if _, e := fmt.Printf("\033[1;31m%s\033[m\n", err); e != nil {
|
||||
result = option.Some(e)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func printLine(k string, arg any) (result option.Option[error]) {
|
||||
if _, e := fmt.Printf("\033[1;32m%s\033[m → \033[1;33m%s\033[m\n", k, arg); e != nil {
|
||||
result = option.Some(e)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func printHelp() (result option.Option[error]) {
|
||||
if _, e := fmt.Print(help); e != nil {
|
||||
result = option.Some(e)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func printOption(c *calc.Calc) option.Option[error] {
|
||||
p, f := c.Precision()
|
||||
t := "auto"
|
||||
if f {
|
||||
t = "fixe"
|
||||
}
|
||||
|
||||
return printResult(fmt.Sprintf("%d (%s)", p, t))
|
||||
}
|
||||
|
||||
func printLast(c *calc.Calc) (err option.Option[error]) {
|
||||
result := c.Last()
|
||||
if n, ok := result.Ok(); ok {
|
||||
err = printResult(n)
|
||||
} else if e, ok := result.Err(); ok {
|
||||
err = option.Some(e)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func printStack(c *calc.Calc) (err option.Option[error]) {
|
||||
result := c.GetStack()
|
||||
if s, ok := result.Ok(); ok {
|
||||
err = printResult(stack2Str(s))
|
||||
} else if e, ok := result.Err(); ok {
|
||||
err = option.Some(e)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func printRegistries(c *calc.Calc) (err option.Option[error]) {
|
||||
result := c.GetRegistries()
|
||||
if r, ok := result.Ok(); ok {
|
||||
for k, s := range r {
|
||||
printLine(k, stack2Str(s))
|
||||
}
|
||||
} else if e, ok := result.Err(); ok {
|
||||
err = option.Some(e)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func printRegistry(k string) calc.CalcFunc {
|
||||
return func(c *calc.Calc) (err option.Option[error]) {
|
||||
result := c.Registry(k)
|
||||
if s, ok := result.Ok(); ok {
|
||||
err = printResult(stack2Str(s))
|
||||
} else if e, ok := result.Err(); ok {
|
||||
err = option.Some(e)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func printMacros(c *Cli) (err option.Option[error]) {
|
||||
result := c.c.GetMacros()
|
||||
if e, ok := result.Err(); ok {
|
||||
err = option.Some(e)
|
||||
} else {
|
||||
for k, m := range c.macros {
|
||||
printLine(k, m)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func printMacro(k string) CliFunc {
|
||||
return func(c *Cli) (err option.Option[error]) {
|
||||
result := c.c.Macro(k)
|
||||
if e, ok := result.Err(); ok {
|
||||
err = option.Some(e)
|
||||
} else {
|
||||
err = printResult(c.macros[k])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func execMacro(k string) calc.CalcFunc {
|
||||
return func(c *calc.Calc) (err option.Option[error]) {
|
||||
errs := c.ExecMacro(k)
|
||||
if len(errs) > 0 {
|
||||
sErrors := make([]string, len(errs))
|
||||
for i, e := range errs {
|
||||
sErrors[i] = e.Error()
|
||||
}
|
||||
err = option.Some(errors.New(strings.Join(sErrors, "\n")))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
18
calc/cli/init.go
Normal file
18
calc/cli/init.go
Normal file
@ -0,0 +1,18 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//go:embed resources/help.txt
|
||||
var embedHelp []byte
|
||||
|
||||
var help string
|
||||
|
||||
func init() {
|
||||
help = string(embedHelp)
|
||||
help = strings.ReplaceAll(help, "{T}", "\033[1;33m")
|
||||
help = strings.ReplaceAll(help, "{0}", "\033[m")
|
||||
help = strings.ReplaceAll(help, "{B}", "\033[33m")
|
||||
}
|
180
calc/cli/mapping.go
Normal file
180
calc/cli/mapping.go
Normal file
@ -0,0 +1,180 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"gitea.zaclys.com/bvaudour/gob/option"
|
||||
"gitea.zaclys.net/bvaudour/calc/calc"
|
||||
)
|
||||
|
||||
var (
|
||||
otherFunctions = map[string]func() option.Option[error]{
|
||||
"h": printHelp,
|
||||
"q": quit,
|
||||
}
|
||||
|
||||
calcFunctions = map[string]calc.CalcFunc{
|
||||
"op": printOption,
|
||||
"of": fixPrecision(true),
|
||||
"oa": fixPrecision(false),
|
||||
"p": printLast,
|
||||
"P": printStack,
|
||||
"c": calc.Pop,
|
||||
"C": calc.Reset,
|
||||
"d": calc.Duplicate,
|
||||
"D": calc.DuplicateStack,
|
||||
"r": calc.Reverse,
|
||||
"R": calc.ReverseStack,
|
||||
"s": calc.Sort,
|
||||
"S": calc.SortDesc,
|
||||
":P": printRegistries,
|
||||
":C": calc.ResetRegistries,
|
||||
";C": calc.ResetMacros,
|
||||
"|": calc.Abs,
|
||||
"++": calc.Inc,
|
||||
"--": calc.Dec,
|
||||
"inv": calc.Inv,
|
||||
"+": calc.Add,
|
||||
"-": calc.Sub,
|
||||
"*": calc.Mul,
|
||||
"×": calc.Mul,
|
||||
"/": calc.Div,
|
||||
"÷": calc.Div,
|
||||
"//": calc.Quo,
|
||||
"%": calc.Rem,
|
||||
"²": calc.Square,
|
||||
"^": calc.Pow,
|
||||
"v": calc.Sqrt,
|
||||
"√": calc.Sqrt,
|
||||
"vn": calc.Sqrtn,
|
||||
"!": calc.Fact,
|
||||
"<=>": calc.Cmp,
|
||||
"=": calc.Eq,
|
||||
"<>": calc.Ne,
|
||||
"≠": calc.Ne,
|
||||
">": calc.Gt,
|
||||
">=": calc.Ge,
|
||||
"≥": calc.Ge,
|
||||
"<=": calc.Le,
|
||||
"≤": calc.Le,
|
||||
"cb": calc.ToBase(2),
|
||||
"co": calc.ToBase(8),
|
||||
"cx": calc.ToBase(16),
|
||||
"ci": calc.ToInteger[uint](),
|
||||
"cd": calc.ToDecimal[uint](),
|
||||
"cs": calc.ToScientific[uint](),
|
||||
"cf": calc.ToFraction[uint](),
|
||||
">>": calc.Rsh,
|
||||
"<<": calc.Lsh,
|
||||
"min": calc.Min,
|
||||
"max": calc.Max,
|
||||
"sum": calc.Sum,
|
||||
"moy": calc.Mean,
|
||||
"med": calc.Median,
|
||||
"mod": calc.Mode,
|
||||
"var": calc.Variance,
|
||||
"dev": calc.StdDeviation,
|
||||
"l": func(c *calc.Calc) (err option.Option[error]) {
|
||||
c.Len()
|
||||
return
|
||||
},
|
||||
"n": calc.NoAction,
|
||||
}
|
||||
|
||||
wordCalcFunctions = map[string]calc.WordCalcFunc{
|
||||
`^:p\w+$`: printRegistry,
|
||||
`^:c\w+$`: calc.ResetRegistry,
|
||||
`^:l\w+$`: calc.Load,
|
||||
`^:s\w+$`: calc.Store,
|
||||
`^:S\w+$`: calc.StoreStack,
|
||||
`^;x\w+$`: execMacro,
|
||||
}
|
||||
|
||||
intCalcFunctions = map[string]calc.IntCalcFunc[uint64]{
|
||||
`^c\d+$`: func(bases ...uint64) calc.CalcFunc {
|
||||
if len(bases) > 0 {
|
||||
return calc.ToBase(bases[0])
|
||||
}
|
||||
return func(c *calc.Calc) (err option.Option[error]) { return }
|
||||
},
|
||||
`^ci\d+$`: calc.ToInteger[uint64],
|
||||
`^cd\d+$`: calc.ToDecimal[uint64],
|
||||
`^cs\d+$`: calc.ToScientific[uint64],
|
||||
`^cf\d+$`: calc.ToFraction[uint64],
|
||||
`^of\d+$`: precision[uint64](true),
|
||||
`^oa\d+$`: precision[uint64](false),
|
||||
}
|
||||
|
||||
cliFunctions = map[string]CliFunc{
|
||||
";P": printMacros,
|
||||
"[": beginMacro,
|
||||
"?": conditional,
|
||||
}
|
||||
|
||||
wordCliFunctions = map[string]WordCliFunc{
|
||||
`^;p\w+$`: printMacro,
|
||||
`^;c\w+$`: resetMacro,
|
||||
`^\]\w+$`: storeMacro,
|
||||
}
|
||||
)
|
||||
|
||||
func searchWordCalcFunction(arg string) (cb calc.CalcFunc, ok bool) {
|
||||
for k, f := range wordCalcFunctions {
|
||||
if ok = regexp.MustCompile(k).MatchString(arg); ok {
|
||||
cb = f(arg[2:])
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func searchIntCalcFunction(arg string) (cb calc.CalcFunc, ok bool) {
|
||||
for k, f := range intCalcFunctions {
|
||||
if ok = regexp.MustCompile(k).MatchString(arg); ok {
|
||||
var d uint64
|
||||
var err error
|
||||
if d, err = strconv.ParseUint(arg[1:], 10, 64); err != nil {
|
||||
d, _ = strconv.ParseUint(arg[2:], 10, 64)
|
||||
}
|
||||
cb = f(d)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func searchWordCliFunction(arg string) (cb CliFunc, ok bool) {
|
||||
for k, f := range wordCliFunctions {
|
||||
if ok = regexp.MustCompile(k).MatchString(arg); ok {
|
||||
if arg[0] == ']' {
|
||||
cb = f(arg[1:])
|
||||
} else {
|
||||
cb = f(arg[2:])
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func searchCalcFunction(arg string) (cb calc.CalcFunc, ok bool) {
|
||||
if cb, ok = calcFunctions[arg]; !ok {
|
||||
if cb, ok = searchWordCalcFunction(arg); !ok {
|
||||
cb, ok = searchIntCalcFunction(arg)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func searchCliFunction(arg string) (cb CliFunc, ok bool) {
|
||||
if cb, ok = cliFunctions[arg]; !ok {
|
||||
cb, ok = searchWordCliFunction(arg)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
110
calc/cli/resources/help.txt
Normal file
110
calc/cli/resources/help.txt
Normal file
@ -0,0 +1,110 @@
|
||||
{T}Affichage de l’aide{0}
|
||||
{B}h{0} Affiche l’aide
|
||||
|
||||
{T}Format des nombres{0}
|
||||
{B}[+|-]<n>{0} Nombre entier
|
||||
{B}[+|-]<n>.<n>{0} Nombre décimal
|
||||
{B}<decimal|entier>E<entier>{0} Nombre décimal en notation scientifique (base 10)
|
||||
{B}<decimal|entier en base>×<b>^[+|-]<e>{0}
|
||||
Nombre décimal en notation scientifique (base quelconque)
|
||||
{B}<n>/<n>{0} Nombre rationnel
|
||||
{B}0b<n>{0} Nombre en base 2
|
||||
{B}1b<n>{0} Nombre en base 2 négatif
|
||||
{B}0o<n>{0} Nombre en base 8
|
||||
{B}1o<n>{0} Nombre en base 8 négatif
|
||||
{B}0x<n>{0} Nombre en base 16
|
||||
{B}1x<n>{0} Nombre en base 16 négatif
|
||||
{B}(<b>)[+|-]<n>{0} Nombre en base b
|
||||
|
||||
{T}Options d’impression{0}
|
||||
{B}po{0} Imprime les options d’affichage
|
||||
{B}of<n>{0} Affichage des nombres à virgules avec une précision fixe de n décimales
|
||||
{B}oa<n>{0} Affichage des nombres à virgules avec une précision auto (max n décimales)
|
||||
Les zéros non significatifs sont supprimés.
|
||||
|
||||
{T}Gestion de la pile{0}
|
||||
{B}p{0} Impression du dernier élément de la pile
|
||||
{B}P{0} Impression de toute la pile
|
||||
{B}c{0} Suppression du dernier élément
|
||||
{B}C{0} Réinitialisation de toute la pile
|
||||
{B}d{0} Duplication du dernier élément
|
||||
{B}D{0} Duplication de toute la pile
|
||||
{B}r{0} Inversion de l’ordre des deux derniers éléments
|
||||
{B}R{0} Inversion de l’ordre de tous les éléments de la pile
|
||||
{B}s{0} Tri des éléments de la pile par ordre croissant
|
||||
{B}S{0} Tri des éléments de la pile par ordre décroissant
|
||||
|
||||
{T}Gestion des registres{0}
|
||||
{B}:P{0} Impression des registres
|
||||
{B}:p<r>{0} Impression du registre r
|
||||
{B}:C{0} Efface tous les registres
|
||||
{B}:c<r>{0} Efface le registre r
|
||||
{B}:l<r>{0} Charge le registre r dans la pile
|
||||
{B}:s<r>{0} Stocke le dernier élément de la pile dans le registre r et le supprime
|
||||
{B}:S<r>{0} Stocke le contenu de la pile dans le registre r et la réinitialise
|
||||
|
||||
{T}Gestion des macros{0}
|
||||
{B};P{0} Impression des macros
|
||||
{B};p<m>{0} Impression de la macro m
|
||||
{B};C{0} Efface toutes les macros
|
||||
{B};c<m>{0} Efface La macro m
|
||||
{B}[ <chain> ]<m>{0} Stocke la chaîne en tant que macro dans m
|
||||
{B};x<m>{0} Exécute la macro m
|
||||
|
||||
{T}Arithmétique élémentaire{0}
|
||||
{B}|{0} Valeur absolue
|
||||
{B}++{0} Ajoute 1
|
||||
{B}--{0} Soustrait 1
|
||||
{B}inv{0} inverse le nombre
|
||||
{B}+{0} Addition
|
||||
{B}-{0} Soustraction
|
||||
{B}*, ×{0} Multiplication
|
||||
{B}/, ÷{0} Division
|
||||
{B}//{0} Division entière
|
||||
{B}%{0} Modulo
|
||||
{B}²{0} Puissance carrée
|
||||
{B}^{0} Puissance
|
||||
{B}v, √{0} Racine carrée
|
||||
{B}vn{0} Racine n-ième (deux nombres sont consommés)
|
||||
{B}!{0} Factorielle
|
||||
|
||||
{T}Fonctions de comparaison{0}
|
||||
{B}<=>{0} Comparaison
|
||||
{B}={0} Égalité
|
||||
{B}<>, ≠{0} Différence
|
||||
{B}>{0} Supériorité stricte
|
||||
{B}<{0} Infériorité stricte
|
||||
{B}>=, ≥{0} Supériorité
|
||||
{B}<=, ≤{0} Infériorité
|
||||
|
||||
{T}Conversions{0}
|
||||
{B}cb{0} Conversion en base 2
|
||||
{B}co{0} Conversion en base 8
|
||||
{B}cx{0} Conversion en base 16
|
||||
{B}c<n>{0} Conversion en base n
|
||||
{B}ci[<b>]{0} Conversion en entier. Si la base est définie, force la conversion de la base.
|
||||
{B}cd[<b>]{0} Conversion en nombre décimal. Si la base est définie, force la conversion de la base.
|
||||
{B}cf[<b>]{0} Conversion en nombre scientifique. Si la base est définie, force la conversion de la base.
|
||||
{B}cs[<b>]{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
|
||||
{B}<<{0} Déplace les bits vers la gauche
|
||||
|
||||
{T}Fonctions statistiques{0}
|
||||
{B}min{0} Minimum
|
||||
{B}max{0} Maximum
|
||||
{B}sum{0} Somme
|
||||
{B}moy{0} Moyenne
|
||||
{B}med{0} Médiane
|
||||
{B}mod{0} Mode
|
||||
{B}var{0} Variance
|
||||
{B}dev{0} Écart-type
|
||||
|
||||
{T}Actions spéciales{0}
|
||||
{B}l{0} Ajoute la taille de la pile dans la pile
|
||||
{B}?{0} Dépile le dernier élément de la pile pour sélectionner le prochain argument à utiliser
|
||||
- Si la pile est vide ou que l’élément dépilé vaut 0 ou <NaN>, saute le prochain argument et exécute le suivant
|
||||
- Sinon, exécute le prochain arguement et saute le suivant
|
||||
{B}n{0} Ne fait aucune action
|
||||
{B}q{0} Termine le mode interactif
|
26
calc/errors.go
Normal file
26
calc/errors.go
Normal file
@ -0,0 +1,26 @@
|
||||
package calc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
errEmpty = "%s est vide"
|
||||
errNotExist = "%s n’existe pas"
|
||||
errNeed2Nbs = "%s contient moins de 2 éléments"
|
||||
errUnknown = "commande inconnue : '%s'"
|
||||
errStack = "La pile"
|
||||
errReg = "Le registre '%s'"
|
||||
errMemory = "La mémoire"
|
||||
errMacro = "La macro '%s'"
|
||||
errStayArgs = "la liste des arguments restants à parser"
|
||||
errCallback = "La fonction de callback n’est pas définie"
|
||||
errParser = "La fonction de parsage n’est pas définie"
|
||||
)
|
||||
|
||||
func ErrIsEmpty(s string) error { return fmt.Errorf(errEmpty, s) }
|
||||
func ErrNotExist(s string) error { return fmt.Errorf(errNotExist, s) }
|
||||
func ErrLenLt2(s string) error { return fmt.Errorf(errNeed2Nbs, s) }
|
||||
func ErrIsUnknow(s string) error { return fmt.Errorf(errUnknown, s) }
|
||||
func reg(s string) string { return fmt.Sprintf(errReg, s) }
|
||||
func mac(s string) string { return fmt.Sprintf(errMacro, s) }
|
130
calc/functions.go
Normal file
130
calc/functions.go
Normal file
@ -0,0 +1,130 @@
|
||||
package calc
|
||||
|
||||
import (
|
||||
"gitea.zaclys.com/bvaudour/gob/number"
|
||||
"gitea.zaclys.com/bvaudour/gob/option"
|
||||
)
|
||||
|
||||
func Push(numbers ...number.Number) CalcFunc {
|
||||
return func(c *Calc) (err option.Option[error]) {
|
||||
c.Push(numbers...)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Gestion de la pile
|
||||
func Pop(c *Calc) option.Option[error] { return c.Pop() }
|
||||
func Reset(c *Calc) option.Option[error] { return c.Reset() }
|
||||
func Duplicate(c *Calc) option.Option[error] { return c.Duplicate() }
|
||||
func DuplicateStack(c *Calc) option.Option[error] { return c.DuplicateStack() }
|
||||
func Reverse(c *Calc) option.Option[error] { return c.Reverse() }
|
||||
func ReverseStack(c *Calc) option.Option[error] { return c.ReverseStack() }
|
||||
func Sort(c *Calc) option.Option[error] { return c.Sort() }
|
||||
func SortDesc(c *Calc) option.Option[error] { return c.SortDesc() }
|
||||
|
||||
// Gestion du registre
|
||||
func ResetRegistry(k string) CalcFunc {
|
||||
return func(c *Calc) option.Option[error] {
|
||||
return c.ResetRegistry(k)
|
||||
}
|
||||
}
|
||||
func ResetRegistries(c *Calc) option.Option[error] { return c.ResetRegistries() }
|
||||
func Store(k string) CalcFunc {
|
||||
return func(c *Calc) option.Option[error] {
|
||||
return c.Store(k)
|
||||
}
|
||||
}
|
||||
func StoreStack(k string) CalcFunc {
|
||||
return func(c *Calc) option.Option[error] {
|
||||
return c.StoreStack(k)
|
||||
}
|
||||
}
|
||||
func Load(k string) CalcFunc {
|
||||
return func(c *Calc) option.Option[error] {
|
||||
return c.Load(k)
|
||||
}
|
||||
}
|
||||
|
||||
// Gestion des macros
|
||||
func ResetMacro(k string) CalcFunc {
|
||||
return func(c *Calc) option.Option[error] {
|
||||
return c.ResetMacro(k)
|
||||
}
|
||||
}
|
||||
func ResetMacros(c *Calc) option.Option[error] { return c.ResetMacros() }
|
||||
func StoreMacro(k string, args ...MacroElement) CalcFunc {
|
||||
return func(c *Calc) (err option.Option[error]) {
|
||||
c.StoreMacro(k, args...)
|
||||
return
|
||||
}
|
||||
}
|
||||
func Exec(s MacroStack) CalcErrFunc {
|
||||
return func(c *Calc) []error {
|
||||
return c.Exec(s)
|
||||
}
|
||||
}
|
||||
func ExecMacro(k string) CalcErrFunc {
|
||||
return func(c *Calc) []error {
|
||||
return c.ExecMacro(k)
|
||||
}
|
||||
}
|
||||
func If(e1, e2 MacroElement) CalcFunc {
|
||||
return func(c *Calc) option.Option[error] {
|
||||
return c.If(e1, e2)
|
||||
}
|
||||
}
|
||||
|
||||
// Aucune action
|
||||
func NoAction(c *Calc) option.Option[error] { return c.NoAction() }
|
||||
|
||||
var (
|
||||
// Opérations mathématiques
|
||||
Abs = Op1(number.Abs)
|
||||
Neg = Op1(number.Neg)
|
||||
Inc = Op1(number.Inc)
|
||||
Dec = Op1(number.Dec)
|
||||
Inv = Op1(number.Inv)
|
||||
Add = Op2(number.Add)
|
||||
Sub = Op2(number.Sub)
|
||||
Mul = Op2(number.Mul)
|
||||
Div = Op2(number.Div)
|
||||
Quo = Op2(number.Quo)
|
||||
Rem = Op2(number.Rem)
|
||||
Square = Op1(number.Square)
|
||||
Pow = Op2(number.Pow)
|
||||
Sqrt = Op1(number.Sqrt)
|
||||
Sqrtn = Op2(number.Sqrtn)
|
||||
Fact = Op1(number.Fact)
|
||||
|
||||
// Opérations de comparaison
|
||||
Cmp = Op2(number.Cmp)
|
||||
Eq = Op2(number.Eq)
|
||||
Ne = Op2(number.Ne)
|
||||
Gt = Op2(number.Gt)
|
||||
Lt = Op2(number.Lt)
|
||||
Ge = Op2(number.Ge)
|
||||
Le = Op2(number.Le)
|
||||
|
||||
// Opérations sur les bits
|
||||
Lsh = Op2(number.Lsh)
|
||||
Rsh = Op2(number.Rsh)
|
||||
Bit = Op2(number.Bit)
|
||||
|
||||
// Fonctions statistiques
|
||||
Sum = Reduce(number.Sum)
|
||||
Min = Reduce(number.Min)
|
||||
Max = Reduce(number.Max)
|
||||
Mean = Reduce(number.Min)
|
||||
Median = Reduce(number.Median)
|
||||
Mode = Reduce(number.Mode)
|
||||
Variance = Reduce(number.Variance)
|
||||
StdDeviation = Reduce(number.StdDeviation)
|
||||
)
|
||||
|
||||
// Conversions
|
||||
func ToBase[N integer](base N) CalcFunc { return Op1(ConvertToBase(base)) }
|
||||
func ToInteger[N integer](base ...N) CalcFunc { return Op1(ConvertToInteger(base...)) }
|
||||
func ToDecimal[N integer](base ...N) CalcFunc { return Op1(ConvertToDecimal(base...)) }
|
||||
func ToFraction[N integer](base ...N) CalcFunc { return Op1(ConvertToFraction(base...)) }
|
||||
func ToScientific[N integer](base ...N) CalcFunc { return Op1(ConvertToScientific(base...)) }
|
||||
func Round(precision uint64, base ...uint) CalcFunc { return Op1(ConvertRound(precision, base...)) }
|
101
calc/macro.go
Normal file
101
calc/macro.go
Normal file
@ -0,0 +1,101 @@
|
||||
package calc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitea.zaclys.com/bvaudour/gob/number"
|
||||
"gitea.zaclys.com/bvaudour/gob/option"
|
||||
)
|
||||
|
||||
type MacroElement = option.Choice[number.Number, CalcFunc]
|
||||
|
||||
type MacroStack []MacroElement
|
||||
|
||||
type Macro map[string]MacroStack
|
||||
|
||||
func MacroNumber(n number.Number) MacroElement { return option.Left[number.Number, CalcFunc](n) }
|
||||
|
||||
func MacroCommand(c CalcFunc) MacroElement { return option.Right[number.Number, CalcFunc](c) }
|
||||
|
||||
func (s MacroStack) Clone() MacroStack {
|
||||
out := make(MacroStack, len(s))
|
||||
copy(out, s)
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func NewMacroStack(args ...any) option.Result[MacroStack] {
|
||||
var s MacroStack
|
||||
for _, e := range args {
|
||||
if n, ok := e.(number.Number); ok {
|
||||
s = append(s, MacroNumber(n))
|
||||
} else if c, ok := e.(CalcFunc); ok {
|
||||
s = append(s, MacroCommand(c))
|
||||
} else {
|
||||
return option.Err[MacroStack](ErrIsUnknow(fmt.Sprint(e)))
|
||||
}
|
||||
}
|
||||
|
||||
return option.Ok(s)
|
||||
}
|
||||
|
||||
func (m Macro) Exists(k string) (ok bool) {
|
||||
_, ok = m[k]
|
||||
return
|
||||
}
|
||||
|
||||
func (m Macro) Set(k string, s MacroStack) {
|
||||
m[k] = s
|
||||
}
|
||||
|
||||
func (m Macro) SetStack(k string, args ...any) (err option.Option[error]) {
|
||||
result := NewMacroStack(args...)
|
||||
if s, ok := result.Ok(); ok {
|
||||
m.Set(k, s)
|
||||
} else if e, ok := result.Err(); ok {
|
||||
err = option.Some(e)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m Macro) Get(k string) (result option.Result[MacroStack]) {
|
||||
if s, ok := m[k]; ok {
|
||||
result = option.Ok(s.Clone())
|
||||
} else {
|
||||
result = option.Err[MacroStack](ErrNotExist(mac(k)))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m Macro) Reset(k string) (err option.Option[error]) {
|
||||
if m.Exists(k) {
|
||||
delete(m, k)
|
||||
} else {
|
||||
err = option.Some(ErrNotExist(mac(k)))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m Macro) ResetAll() (err option.Option[error]) {
|
||||
if len(m) > 0 {
|
||||
for k := range m {
|
||||
delete(m, k)
|
||||
}
|
||||
} else {
|
||||
err = option.Some(ErrIsEmpty(errMemory))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m Macro) Clone() Macro {
|
||||
out := make(Macro)
|
||||
for k, s := range m {
|
||||
out[k] = s.Clone()
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
88
calc/operations.go
Normal file
88
calc/operations.go
Normal file
@ -0,0 +1,88 @@
|
||||
package calc
|
||||
|
||||
import (
|
||||
"gitea.zaclys.com/bvaudour/gob/number"
|
||||
"gitea.zaclys.com/bvaudour/gob/option"
|
||||
)
|
||||
|
||||
type integer interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
|
||||
}
|
||||
|
||||
func Op1(cb number.Op1Func) CalcFunc {
|
||||
return func(c *Calc) (err option.Option[error]) {
|
||||
result := c.stack.Pop()
|
||||
if n, ok := result.Ok(); ok {
|
||||
c.Push(cb(n))
|
||||
} else if e, ok := result.Err(); ok {
|
||||
c.Reset()
|
||||
err = option.Some(e)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func Op2(cb number.Op2Func) CalcFunc {
|
||||
return func(c *Calc) (err option.Option[error]) {
|
||||
result := c.stack.Pop2()
|
||||
if n, ok := result.Ok(); ok {
|
||||
c.Push(cb(n.N1, n.N2))
|
||||
} else if e, ok := result.Err(); ok {
|
||||
c.Reset()
|
||||
err = option.Some(e)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func Reduce(cb number.ReduceFunc) CalcFunc {
|
||||
return func(c *Calc) (err option.Option[error]) {
|
||||
if c.stack.IsEmpty() {
|
||||
err = option.Some(ErrIsEmpty(errStack))
|
||||
} else {
|
||||
n := cb(c.Stack()...)
|
||||
c.Reset()
|
||||
c.Push(n)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func ConvertToBase[N integer](base N) number.Op1Func {
|
||||
return func(n number.Number) number.Number {
|
||||
return number.ToBase(n, base)
|
||||
}
|
||||
}
|
||||
|
||||
func ConvertToInteger[N integer](base ...N) number.Op1Func {
|
||||
return func(n number.Number) number.Number {
|
||||
return number.ToInteger(n, base...)
|
||||
}
|
||||
}
|
||||
|
||||
func ConvertToDecimal[N integer](base ...N) number.Op1Func {
|
||||
return func(n number.Number) number.Number {
|
||||
return number.ToDecimal(n, base...)
|
||||
}
|
||||
}
|
||||
|
||||
func ConvertToFraction[N integer](base ...N) number.Op1Func {
|
||||
return func(n number.Number) number.Number {
|
||||
return number.ToFraction(n, base...)
|
||||
}
|
||||
}
|
||||
|
||||
func ConvertToScientific[N integer](base ...N) number.Op1Func {
|
||||
return func(n number.Number) number.Number {
|
||||
return number.ToScientific(n, base...)
|
||||
}
|
||||
}
|
||||
|
||||
func ConvertRound(precision uint64, base ...uint) number.Op1Func {
|
||||
return func(n number.Number) number.Number {
|
||||
return number.Round(n, precision, base...)
|
||||
}
|
||||
}
|
58
calc/registry.go
Normal file
58
calc/registry.go
Normal file
@ -0,0 +1,58 @@
|
||||
package calc
|
||||
|
||||
import (
|
||||
"gitea.zaclys.com/bvaudour/gob/number"
|
||||
"gitea.zaclys.com/bvaudour/gob/option"
|
||||
)
|
||||
|
||||
type Registry map[string]Stack
|
||||
|
||||
func (r Registry) Exists(k string) (ok bool) {
|
||||
_, ok = r[k]
|
||||
return
|
||||
}
|
||||
|
||||
func (r Registry) Set(k string, numbers ...number.Number) {
|
||||
r[k] = numbers
|
||||
}
|
||||
|
||||
func (r Registry) Get(k string) (result option.Result[Stack]) {
|
||||
if s, ok := r[k]; ok {
|
||||
result = option.Ok(s.Clone())
|
||||
} else {
|
||||
result = option.Err[Stack](ErrNotExist(reg(k)))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r Registry) Reset(k string) (err option.Option[error]) {
|
||||
if r.Exists(k) {
|
||||
delete(r, k)
|
||||
} else {
|
||||
err = option.Some(ErrNotExist(reg(k)))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r Registry) ResetAll() (err option.Option[error]) {
|
||||
if len(r) > 0 {
|
||||
for k := range r {
|
||||
delete(r, k)
|
||||
}
|
||||
} else {
|
||||
err = option.Some(ErrIsEmpty(errMemory))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r Registry) Clone() Registry {
|
||||
out := make(Registry)
|
||||
for k, s := range r {
|
||||
out[k] = s.Clone()
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
136
calc/stack.go
Normal file
136
calc/stack.go
Normal file
@ -0,0 +1,136 @@
|
||||
package calc
|
||||
|
||||
import (
|
||||
"gitea.zaclys.com/bvaudour/gob/number"
|
||||
"gitea.zaclys.com/bvaudour/gob/option"
|
||||
)
|
||||
|
||||
type Number2 struct {
|
||||
N1, N2 number.Number
|
||||
}
|
||||
|
||||
type Stack []number.Number
|
||||
|
||||
func (s *Stack) Len() int { return len(*s) }
|
||||
|
||||
func (s *Stack) IsEmpty() bool { return s.Len() == 0 }
|
||||
|
||||
func (s *Stack) Clone() (out Stack) {
|
||||
out = make(Stack, s.Len())
|
||||
copy(out, *s)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Stack) Last() option.Result[number.Number] {
|
||||
i := s.Len() - 1
|
||||
|
||||
if i >= 0 {
|
||||
return option.Ok((*s)[i])
|
||||
}
|
||||
return option.Err[number.Number](ErrIsEmpty(errStack))
|
||||
}
|
||||
|
||||
func (s *Stack) Last2() option.Result[Number2] {
|
||||
i := s.Len() - 2
|
||||
|
||||
if i >= 0 {
|
||||
return option.Ok(Number2{
|
||||
N1: (*s)[i],
|
||||
N2: (*s)[i+1],
|
||||
})
|
||||
}
|
||||
return option.Err[Number2](ErrLenLt2(errStack))
|
||||
}
|
||||
|
||||
func (s *Stack) Pop() (n option.Result[number.Number]) {
|
||||
if n = s.Last(); n.IsOk() {
|
||||
*s = (*s)[:s.Len()-1]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Stack) Pop2() (n option.Result[Number2]) {
|
||||
if n = s.Last2(); n.IsOk() {
|
||||
*s = (*s)[:s.Len()-2]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Stack) Reset() (err option.Option[error]) {
|
||||
if s.Len() > 0 {
|
||||
*s = (*s)[:0]
|
||||
} else {
|
||||
err = option.Some(ErrIsEmpty(errStack))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Stack) Push(numbers ...number.Number) { *s = append(*s, numbers...) }
|
||||
|
||||
func (s *Stack) Duplicate() (err option.Option[error]) {
|
||||
result := s.Last()
|
||||
if n, ok := result.Ok(); ok {
|
||||
s.Push(n)
|
||||
} else if e, ok := result.Err(); ok {
|
||||
err = option.Some(e)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Stack) DuplicateAll() (err option.Option[error]) {
|
||||
if s.IsEmpty() {
|
||||
err = option.Some(ErrIsEmpty(errStack))
|
||||
} else {
|
||||
s.Push((*s)...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Stack) Swap(i, j int) { (*s)[i], (*s)[j] = (*s)[j], (*s)[i] }
|
||||
|
||||
func (s *Stack) Reverse() (err option.Option[error]) {
|
||||
i := s.Len() - 2
|
||||
if i >= 0 {
|
||||
s.Swap(i, i+1)
|
||||
} else {
|
||||
err = option.Some(ErrLenLt2(errStack))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Stack) ReverseAll() (err option.Option[error]) {
|
||||
if s.IsEmpty() {
|
||||
err = option.Some(ErrIsEmpty(errStack))
|
||||
} else {
|
||||
*s = number.Reverse((*s)...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Stack) Sort() (err option.Option[error]) {
|
||||
if s.IsEmpty() {
|
||||
err = option.Some(ErrIsEmpty(errStack))
|
||||
} else {
|
||||
*s = number.Sort((*s)...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Stack) SortDesc() (err option.Option[error]) {
|
||||
if s.IsEmpty() {
|
||||
err = option.Some(ErrIsEmpty(errStack))
|
||||
} else {
|
||||
*s = number.SortDesc((*s)...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
8
go.mod
Normal file
8
go.mod
Normal file
@ -0,0 +1,8 @@
|
||||
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
|
||||
)
|
4
go.sum
Normal file
4
go.sum
Normal file
@ -0,0 +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=
|
22
main.go
Normal file
22
main.go
Normal file
@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"gitea.zaclys.com/bvaudour/readline"
|
||||
"gitea.zaclys.net/bvaudour/calc/calc/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
calc, rl := cli.New(), readline.New()
|
||||
for {
|
||||
result := rl.Prompt("> ")
|
||||
if args, ok := result.Ok(); ok {
|
||||
calc.ExecAll(args)
|
||||
} else if err, ok := result.Err(); ok {
|
||||
fmt.Println(result)
|
||||
fmt.Fprintf(os.Stderr, "Erreur d’entrée: %s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user