From 2852564b725731d8fce6f95b7769880122e9f30a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Herg=C3=A8s?= Date: Wed, 23 Jul 2025 15:32:52 +0200 Subject: [PATCH 1/4] feat(function): simple implementation of parsing functions --- function.go | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 function.go diff --git a/function.go b/function.go new file mode 100644 index 0000000..3de388a --- /dev/null +++ b/function.go @@ -0,0 +1,41 @@ +package gomath + +import ( + "errors" + "fmt" + "strings" +) + +var ( + ErrInvalidFunction = errors.New("invalid function") + ErrInvalidFunctionCall = errors.New("invalid function call") +) + +type Function func(map[string]string) (Result, error) + +func NewFunction(s string) (Function, error) { + splits := strings.Split(s, "->") + if len(splits) != 2 { + return nil, errors.Join(ErrInvalidFunction, errors.New("a function is defined by 'args -> expression'")) + } + before := splits[0] + expression := splits[1] + var params []string + for _, p := range strings.Split(before, ",") { + params = append(params, strings.TrimSpace(p)) + } + return func(args map[string]string) (Result, error) { + if len(params) != len(args) { + return nil, errors.Join(ErrInvalidFunctionCall, errors.New("not all parameters have been defined")) + } + cp := expression + for _, p := range params { + v, ok := args[p] + if !ok { + return nil, errors.Join(ErrInvalidFunctionCall, fmt.Errorf("missing argument for %s", p)) + } + cp = strings.ReplaceAll(cp, p, v) + } + return Parse(cp) + }, nil +} From d3db6916111f73cc77d267445054330532b777bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Herg=C3=A8s?= Date: Wed, 23 Jul 2025 15:36:45 +0200 Subject: [PATCH 2/4] test(function): simple test --- function.go | 8 ++++---- function_test.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 function_test.go diff --git a/function.go b/function.go index 3de388a..de8034b 100644 --- a/function.go +++ b/function.go @@ -13,13 +13,13 @@ var ( type Function func(map[string]string) (Result, error) -func NewFunction(s string) (Function, error) { +func NewFunction(s string) (Function, int, error) { splits := strings.Split(s, "->") if len(splits) != 2 { - return nil, errors.Join(ErrInvalidFunction, errors.New("a function is defined by 'args -> expression'")) + return nil, 0, errors.Join(ErrInvalidFunction, errors.New("a function is defined by 'args -> expression'")) } before := splits[0] - expression := splits[1] + expression := strings.TrimSpace(splits[1]) var params []string for _, p := range strings.Split(before, ",") { params = append(params, strings.TrimSpace(p)) @@ -37,5 +37,5 @@ func NewFunction(s string) (Function, error) { cp = strings.ReplaceAll(cp, p, v) } return Parse(cp) - }, nil + }, len(params), nil } diff --git a/function_test.go b/function_test.go new file mode 100644 index 0000000..efb043c --- /dev/null +++ b/function_test.go @@ -0,0 +1,35 @@ +package gomath + +import "testing" + +func TestNewFunction(t *testing.T) { + f, n, err := NewFunction("x -> x^2") + if err != nil { + t.Fatal(err) + } + if n != 1 { + t.Errorf("got %d, want 1", n) + } + result, err := f(map[string]string{"x": "5"}) + if err != nil { + t.Fatal(err) + } + if result.String() != "25" { + t.Errorf("got %s, want 25", result.String()) + } + + f, n, err = NewFunction("x, y -> x^y") + if err != nil { + t.Fatal(err) + } + if n != 2 { + t.Errorf("got %d, want 2", n) + } + result, err = f(map[string]string{"x": "5", "y": "2"}) + if err != nil { + t.Fatal(err) + } + if result.String() != "25" { + t.Errorf("got %s, want 25", result.String()) + } +} From 9c7d47db8fc3ebb056a7f5d12cfb1e1897472c54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Herg=C3=A8s?= Date: Wed, 23 Jul 2025 16:02:03 +0200 Subject: [PATCH 3/4] docs(info): update usage in readme --- README.md | 55 +++++++++++++++++++++++++++++++++++++---------------- function.go | 9 +++++++++ 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 7410b20..e3c30df 100644 --- a/README.md +++ b/README.md @@ -10,32 +10,55 @@ $ go get -u github.com/nyttikord/gomath@latest ``` You can replace `latest` with any valid tags. -### Calculate +### General parsing -To parse an expression and calculate it, use `gomath.ParseAndCalculate(string, *gomath.Options) (string, error)`. +To parse an expression, use `gomath.Parse(string) (gomath.Result, error)`. The string is a valid expression, like `1+2` or `2(1/3+4)^5`. -It returns the result of the expression in a string according to the given options. + +The result will give you everything needed to perform operations like the exact representation or the ability to convert +it to $\LaTeX$ code. ```go -res, err := gomath.ParseAndCalculate("1+2", &gomath.Options{}) -err == nil // true -res == "3" // true +res, err := gomath.Parse("1/2+1") +// check the error +res.String == "3/2" // true +res.IsExact // true because 1.5 is the exact representation of 3/2 +res.LaTeX == `\frac{1}{2} + 1` // true +res.Approx(5) == "1.50000" // true ``` -You can modify the result's type with `gomath.Options`. -Set `Decimal` to `true` if you want to have a decimal approximation. -You can specify the number of digits with `Precision`. +You can also call `gomath.ParseAndCalculate(string, *gomath.Options) (string, error)` to directly get the string +representation with the given options or `gomath.ParseAndConvertToLatex(string, *gomath.Options) (string, error)` to get +the $\LaTeX$ code. + +### Creating a function -### Convert to LaTeX +You can create a function with `gomath.NewFunction(string) (gomath.Function, int, error)`. +The string is a valid expression representing a function. +It is composed by the arguments and the expression of the function. +You must separate each argument with a coma (`,`) and separate the arguments and the expression with `->`. +``` +x, y -> x^y +``` +is a valid expression representing a function. +The returned int is the number of arguments. -To parse an expression and convert it into $\LaTeX$, use `gomath.ParseAndConvertToLatex(string, *gomath.Options) (string, error)`. -The string is a valid expression like `1+2` or `2(1/3+4)^5`. -It returns the result of the expression in a string according to the given options. +Then, you can evaluate the function by passing a map containing every argument. +gomath will send you the result (an instance of `gomath.Result`) of the evaluation. ```go -res, err := gomath.ParseAndConvertToLatex("(1+2/3)/2", &gomath.Options{}) -err == nil // true -res == `\frac{1 + \frac{2}{3}}{2}` // true +f, _, err := gomath.NewFunction("x, y -> x^y") +if err != nil { + panic(err) // will not panic because the expression is valid +} +res, err := f(map[string]string{ + "x": "5", + "y": "2" +}) +if err != nil { + panic(err) // will not panic because the expression is valid and every argument is set +} +res.String == "25" // true ``` ### Special case diff --git a/function.go b/function.go index de8034b..fe28432 100644 --- a/function.go +++ b/function.go @@ -13,6 +13,15 @@ var ( type Function func(map[string]string) (Result, error) +// NewFunction creates a new Function by parsing the given string. It must follow this scheme: +// +// arg1, arg2, arg3... -> expr +// +// Example: +// +// gomath.NewFunction("x, y -> x^y") +// +// It returns the Function and the number of arguments. func NewFunction(s string) (Function, int, error) { splits := strings.Split(s, "->") if len(splits) != 2 { From c70d1ae400ef009f2c3f1b078766894114e47fef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Herg=C3=A8s?= Date: Sat, 26 Jul 2025 18:22:12 +0200 Subject: [PATCH 4/4] docs(usage): better result explaination in readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e3c30df..e098597 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ You can replace `latest` with any valid tags. To parse an expression, use `gomath.Parse(string) (gomath.Result, error)`. The string is a valid expression, like `1+2` or `2(1/3+4)^5`. -The result will give you everything needed to perform operations like the exact representation or the ability to convert -it to $\LaTeX$ code. +The result will give you everything needed to perform operations, like getting the exact representation of the result, +or the ability to convert ```go res, err := gomath.Parse("1/2+1")