Skip to content

Commit

Permalink
improve function handling, provide helpers to make writing funtions e…
Browse files Browse the repository at this point in the history
…asier
  • Loading branch information
xrstf committed Dec 3, 2023
1 parent 02d1472 commit fb45c79
Show file tree
Hide file tree
Showing 37 changed files with 795 additions and 940 deletions.
22 changes: 21 additions & 1 deletion aliases.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
package rudi

import (
"go.xrstf.de/rudi/pkg/builtin"
"go.xrstf.de/rudi/pkg/coalescing"
"go.xrstf.de/rudi/pkg/eval/builtin"
"go.xrstf.de/rudi/pkg/eval/types"
"go.xrstf.de/rudi/pkg/eval/util"
)

// Context is the evaluation context for a Rudi program, consisting of
Expand Down Expand Up @@ -59,3 +60,22 @@ func NewVariables() Variables {
func NewDocument(data any) (Document, error) {
return types.NewDocument(data)
}

// RawFunction is a function that receives its raw, unevaluated child expressions as arguments.
// This is the lowest level a function can be, allowing to selectively evaluate the arguments to
// control side effects.
type RawFunction = util.RawFunction

// NewRawFunction wraps a raw function to be used in Rudi.
func NewRawFunction(f RawFunction, description string) util.Function {
return util.NewRawFunction(f, description)
}

// LiteralFunction is a function that receives all of its arguments already evaluated, but not yet
// coalesced into specific types.
type LiteralFunction = util.LiteralFunction

// NewLiteralFunction wraps a literal function to be used in Rudi.
func NewLiteralFunction(f LiteralFunction, description string) util.Function {
return util.NewLiteralFunction(f, description)
}
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Welcome to the Rudi documentation :smile:
## Math Functions

* [`*`](functions/math-mult.md) – returns the product of all of its arguments
* [`+`](functions/math-sum.md) – returns the sum of all of its arguments
* [`+`](functions/math-add.md) – returns the sum of all of its arguments
* [`-`](functions/math-sub.md) – returns arg1 - arg2 - .. - argN
* [`/`](functions/math-div.md) – returns arg1 / arg2 / .. / argN

Expand Down
6 changes: 3 additions & 3 deletions docs/embed.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"fmt"
"strings"

"go.xrstf.de/rudi/pkg/eval/builtin"
"go.xrstf.de/rudi/pkg/builtin"
)

//go:embed *
Expand Down Expand Up @@ -44,7 +44,7 @@ func Topics() []Topic {
}

ignoredFunctions := map[string]struct{}{
"sum": {},
"add": {},
"sub": {},
"mult": {},
"div": {},
Expand All @@ -58,7 +58,7 @@ func Topics() []Topic {
var sanitized string
switch funcName {
case "+":
sanitized = "sum"
sanitized = "add"
case "-":
sanitized = "sub"
case "*":
Expand Down
2 changes: 1 addition & 1 deletion docs/functions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ applied to all functions (so technically `eq?!` is valid, though weird looking).
## Math Functions

* [`*`](../functions/math-mult.md) – returns the product of all of its arguments
* [`+`](../functions/math-sum.md) – returns the sum of all of its arguments
* [`+`](../functions/math-add.md) – returns the sum of all of its arguments
* [`-`](../functions/math-sub.md) – returns arg1 - arg2 - .. - argN
* [`/`](../functions/math-div.md) – returns arg1 / arg2 / .. / argN

Expand Down
2 changes: 1 addition & 1 deletion docs/functions/math-sum.md → docs/functions/math-add.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# +

`+` sums up all provided arguments. Arguments must evaluate to numeric values.
`sum` is an alias for this function.
`add` is an alias for this function.

## Examples

Expand Down
104 changes: 104 additions & 0 deletions pkg/builtin/comparisons.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// SPDX-FileCopyrightText: 2023 Christoph Mewes
// SPDX-License-Identifier: MIT

package builtin

import (
"errors"

"go.xrstf.de/rudi/pkg/coalescing"
"go.xrstf.de/rudi/pkg/equality"
"go.xrstf.de/rudi/pkg/eval/types"
)

func eqFunction(ctx types.Context, args []any) (any, error) {
return equality.Equal(ctx.Coalesce(), args[0], args[1])
}

func likeFunction(ctx types.Context, args []any) (any, error) {
return equality.Equal(coalescing.NewHumane(), args[0], args[1])
}

func identicalFunction(ctx types.Context, args []any) (any, error) {
return equality.Equal(coalescing.NewStrict(), args[0], args[1])
}

func ltCoalescer(ctx types.Context, args []any) (any, error) {
compared, err := equality.Compare(ctx.Coalesce(), args[0], args[1])
if err != nil {
return nil, err
}

switch compared {
case equality.IsEqual:
return false, nil
case equality.IsSmaller:
return true, nil
case equality.IsGreater:
return false, nil
case equality.Unorderable:
return false, errors.New("cannot order the given arguments")
default:
panic("Unexpected comparison result.")
}
}

func lteCoalescer(ctx types.Context, args []any) (any, error) {
compared, err := equality.Compare(ctx.Coalesce(), args[0], args[1])
if err != nil {
return nil, err
}

switch compared {
case equality.IsEqual:
return true, nil
case equality.IsSmaller:
return true, nil
case equality.IsGreater:
return false, nil
case equality.Unorderable:
return false, errors.New("cannot order the given arguments")
default:
panic("Unexpected comparison result.")
}
}

func gtCoalescer(ctx types.Context, args []any) (any, error) {
compared, err := equality.Compare(ctx.Coalesce(), args[0], args[1])
if err != nil {
return nil, err
}

switch compared {
case equality.IsEqual:
return false, nil
case equality.IsSmaller:
return false, nil
case equality.IsGreater:
return true, nil
case equality.Unorderable:
return false, errors.New("cannot order the given arguments")
default:
panic("Unexpected comparison result.")
}
}

func gteCoalescer(ctx types.Context, args []any) (any, error) {
compared, err := equality.Compare(ctx.Coalesce(), args[0], args[1])
if err != nil {
return nil, err
}

switch compared {
case equality.IsEqual:
return true, nil
case equality.IsSmaller:
return false, nil
case equality.IsGreater:
return true, nil
case equality.Unorderable:
return false, errors.New("cannot order the given arguments")
default:
panic("Unexpected comparison result.")
}
}
File renamed without changes.
61 changes: 5 additions & 56 deletions pkg/eval/builtin/core.go → pkg/builtin/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ import (

// (if COND:Expr YES:Expr NO:Expr?)
func ifFunction(ctx types.Context, args []ast.Expression) (any, error) {
if size := len(args); size < 2 || size > 3 {
return nil, fmt.Errorf("expected 2 or 3 arguments, got %d", size)
}

_, condition, err := eval.EvalExpression(ctx, args[0])
if err != nil {
return nil, fmt.Errorf("condition: %w", err)
Expand Down Expand Up @@ -49,10 +45,6 @@ func ifFunction(ctx types.Context, args []ast.Expression) (any, error) {

// (do STEP:Expr+)
func doFunction(ctx types.Context, args []ast.Expression) (any, error) {
if size := len(args); size < 1 {
return nil, fmt.Errorf("expected 1+ arguments, got %d", size)
}

tupleCtx := ctx

var (
Expand All @@ -73,10 +65,6 @@ func doFunction(ctx types.Context, args []ast.Expression) (any, error) {

// (has? SYM:SymbolWithPathExpression)
func hasFunction(ctx types.Context, args []ast.Expression) (any, error) {
if size := len(args); size != 1 {
return nil, fmt.Errorf("expected 1 argument, got %d", size)
}

var (
expr ast.Expression
pathExpr *ast.PathExpression
Expand Down Expand Up @@ -145,10 +133,6 @@ func hasFunction(ctx types.Context, args []ast.Expression) (any, error) {

// (default TEST:Expression FALLBACK:any)
func defaultFunction(ctx types.Context, args []ast.Expression) (any, error) {
if size := len(args); size != 2 {
return nil, fmt.Errorf("expected 2 arguments, got %d", size)
}

_, result, err := eval.EvalExpression(ctx, args[0])
if err != nil {
return nil, fmt.Errorf("argument #0: %w", err)
Expand All @@ -174,10 +158,6 @@ func defaultFunction(ctx types.Context, args []ast.Expression) (any, error) {

// (try TEST:Expression FALLBACK:any?)
func tryFunction(ctx types.Context, args []ast.Expression) (any, error) {
if size := len(args); size < 1 || size > 2 {
return nil, fmt.Errorf("expected 1 or 2 arguments, got %d", size)
}

_, result, err := eval.EvalExpression(ctx, args[0])
if err != nil {
if len(args) == 1 {
Expand All @@ -196,10 +176,6 @@ func tryFunction(ctx types.Context, args []ast.Expression) (any, error) {
// (set VAR:Variable VALUE:any)
// (set EXPR:PathExpression VALUE:any)
func setFunction(ctx types.Context, args []ast.Expression) (any, error) {
if size := len(args); size != 2 {
return nil, fmt.Errorf("expected 2 arguments, got %d", size)
}

symbol, ok := args[0].(ast.Symbol)
if !ok {
return nil, fmt.Errorf("argument #0 is not a symbol, but %T", args[0])
Expand Down Expand Up @@ -321,20 +297,11 @@ func (deleteFunction) BangHandler(ctx types.Context, sym ast.Symbol, value any)
}

// (empty? VALUE:any)
func isEmptyFunction(ctx types.Context, args []ast.Expression) (any, error) {
if size := len(args); size != 1 {
return nil, fmt.Errorf("expected 1 or 2 arguments, got %d", size)
}

_, result, err := eval.EvalExpression(ctx, args[0])
if err != nil {
return nil, err
}

func isEmptyFunction(ctx types.Context, args []any) (any, error) {
// this function purposefully always uses humane coalescing
boolified, err := coalescing.NewHumane().ToBool(result)
boolified, err := coalescing.NewHumane().ToBool(args[0])
if err != nil {
return nil, fmt.Errorf("argument #0: %w", err)
return nil, err
}

return !boolified, nil
Expand All @@ -356,24 +323,6 @@ func pedanticallyFunction(ctx types.Context, args []ast.Expression) (any, error)
}

func coalescingChangerFunction(ctx types.Context, args []ast.Expression, c coalescing.Coalescer) (any, error) {
if size := len(args); size < 1 {
return nil, fmt.Errorf("expected 1+ arguments, got %d", size)
}

tupleCtx := ctx.WithCoalescer(c)

var (
result any
err error
)

// do not use evalArgs(), as we want to inherit the context between expressions
for i, arg := range args {
tupleCtx, result, err = eval.EvalExpression(tupleCtx, arg)
if err != nil {
return nil, fmt.Errorf("argument #%d: %w", i, err)
}
}

return result, nil
_, result, err := eval.EvalExpression(ctx.WithCoalescer(c), args[0])
return result, err
}
File renamed without changes.
21 changes: 21 additions & 0 deletions pkg/builtin/dates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: 2023 Christoph Mewes
// SPDX-License-Identifier: MIT

package builtin

import (
"time"

"go.xrstf.de/rudi/pkg/eval/types"
)

func nowFunction(ctx types.Context, args []any) (any, error) {
formatString, err := ctx.Coalesce().ToString(args[0])
if err != nil {
return nil, err
}

formatted := time.Now().Format(string(formatString))

return formatted, nil
}
38 changes: 38 additions & 0 deletions pkg/builtin/encoding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-FileCopyrightText: 2023 Christoph Mewes
// SPDX-License-Identifier: MIT

package builtin

import (
"encoding/base64"
"fmt"

"go.xrstf.de/rudi/pkg/eval/types"
)

// (to-base64 VAL:string)
func toBase64Function(ctx types.Context, args []any) (any, error) {
str, err := ctx.Coalesce().ToString(args[0])
if err != nil {
return nil, err
}

encoded := base64.StdEncoding.EncodeToString([]byte(str))

return encoded, nil
}

// (from-base64 VAL:string)
func fromBase64Function(ctx types.Context, args []any) (any, error) {
str, err := ctx.Coalesce().ToString(args[0])
if err != nil {
return nil, err
}

decoded, err := base64.StdEncoding.DecodeString(string(str))
if err != nil {
return nil, fmt.Errorf("not valid base64: %w", err)
}

return string(decoded), nil
}
File renamed without changes.
Loading

0 comments on commit fb45c79

Please sign in to comment.