Commit initial

This commit is contained in:
Benjamin VAUDOUR 2024-02-21 11:42:56 +01:00
commit f0bc5f2273
17 changed files with 1596 additions and 0 deletions

11
LICENSE Normal file
View 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
View 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 laide, saisissez `h` puis appuyez sur la touche `Entrée`.
Exemple :
```
1 1 + p
```
(effectue lopé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 nimporte 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 denregistrer des suites de commandes complexes.

182
calc/calc.go Normal file
View 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
View 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 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)
}
}

229
calc/cli/functions.go Normal file
View 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
View 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
View 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
View File

@ -0,0 +1,110 @@
{T}Affichage de laide{0}
{B}h{0} Affiche laide
{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 dimpression{0}
{B}po{0} Imprime les options daffichage
{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 lordre des deux derniers éléments
{B}R{0} Inversion de lordre 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
View File

@ -0,0 +1,26 @@
package calc
import (
"fmt"
)
const (
errEmpty = "%s est vide"
errNotExist = "%s nexiste 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 nest pas définie"
errParser = "La fonction de parsage nest 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 dentrée: %s\n", err)
}
}
}