Skip to content
This repository was archived by the owner on Mar 16, 2025. It is now read-only.

Commit 66b1b02

Browse files
committed
allow more than one expression in user-defined functions (automatically assume a "(do...)")
1 parent 0111ec3 commit 66b1b02

4 files changed

Lines changed: 31 additions & 11 deletions

File tree

80 Bytes
Binary file not shown.

docs/stdlib/rudifunc/func.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ embedding Rudi into other Go applications.
2323
`func!` creates a new function, which can be used in all subseqeuent statements
2424
in the same Rudi program. Function creation is bound to its parent scope, so
2525
defining a function within an `if` statement for example will make the function
26-
available onside inside that statement, not globally.
26+
available only inside that statement, not globally.
2727

2828
Dynamically defined functions cannot overwrite statically defined functions, i.e.
2929
you cannot define a custom `concat` function using `func!` if `concat` already
@@ -34,6 +34,10 @@ Since defining a new function is a side effect, `func` must always be used with
3434
the bang modifier (`func!`). The behaviour of Rudi programs that use `func`
3535
without the bang modifier is undefined.
3636

37+
Functions can contain an arbitrary number of expressions, which will be evaluated
38+
in sequence and share a single context. This means user-defined functions form
39+
a "sub-program" the same way the [`do`](core-do.md) function does.
40+
3741
## Examples
3842

3943
```
@@ -56,12 +60,11 @@ inject a Go function statically into Rudi instead of defining it at runtime.
5660

5761
## Forms
5862

59-
### `(func! name:identifier params:vector body:expression)``null`
63+
### `(func! name:identifier params:vector body:expression)``null`
6064

6165
* `name` is an identifier giving the function its name.
6266
* `params` is a vector containing identifiers that hold the parameter names.
63-
* `body` is a single expression (use `do` for multiple statements) that forms
64-
the function body.
67+
* `body` is one or more expressions that form the function body.
6568

6669
This form will create a new function called `name` with as many parameters as
6770
`params` has identifiers. `params` can be empty, but must otherwise contain only

pkg/builtin/rudifunc/docs/func.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ embedding Rudi into other Go applications.
2323
`func!` creates a new function, which can be used in all subseqeuent statements
2424
in the same Rudi program. Function creation is bound to its parent scope, so
2525
defining a function within an `if` statement for example will make the function
26-
available onside inside that statement, not globally.
26+
available only inside that statement, not globally.
2727

2828
Dynamically defined functions cannot overwrite statically defined functions, i.e.
2929
you cannot define a custom `concat` function using `func!` if `concat` already
@@ -34,6 +34,10 @@ Since defining a new function is a side effect, `func` must always be used with
3434
the bang modifier (`func!`). The behaviour of Rudi programs that use `func`
3535
without the bang modifier is undefined.
3636

37+
Functions can contain an arbitrary number of expressions, which will be evaluated
38+
in sequence and share a single context. This means user-defined functions form
39+
a "sub-program" the same way the [`do`](core-do.md) function does.
40+
3741
## Examples
3842

3943
```
@@ -56,12 +60,11 @@ inject a Go function statically into Rudi instead of defining it at runtime.
5660

5761
## Forms
5862

59-
### `(func! name:identifier params:vector body:expression)``null`
63+
### `(func! name:identifier params:vector body:expression)``null`
6064

6165
* `name` is an identifier giving the function its name.
6266
* `params` is a vector containing identifiers that hold the parameter names.
63-
* `body` is a single expression (use `do` for multiple statements) that forms
64-
the function body.
67+
* `body` is one or more expressions that form the function body.
6568

6669
This form will create a new function called `name` with as many parameters as
6770
`params` has identifiers. `params` can be empty, but must otherwise contain only

pkg/builtin/rudifunc/functions.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ var (
1919

2020
// funcFunction should never be called without the bang modifier, as without it, the created function
2121
// just instantly vanishes into thin air.
22-
func funcFunction(ctx types.Context, name ast.Expression, namingVector ast.Expression, body ast.Expression) (any, error) {
22+
func funcFunction(ctx types.Context, name ast.Expression, namingVector ast.Expression, body ...ast.Expression) (any, error) {
2323
nameIdent, ok := name.(ast.Identifier)
2424
if !ok {
2525
return nil, fmt.Errorf("first argument must be an identifier that specifies the function name, but got %T instead", name)
@@ -60,7 +60,7 @@ func funcBangHandler(ctx types.Context, originalArgs []ast.Expression, value any
6060
type rudispaceFunc struct {
6161
name string
6262
params []string
63-
body ast.Expression
63+
body []ast.Expression
6464
}
6565

6666
var _ types.Function = rudispaceFunc{}
@@ -84,7 +84,21 @@ func (f rudispaceFunc) Evaluate(ctx types.Context, args []ast.Expression) (any,
8484
funcArgs[paramName] = arg
8585
}
8686

87-
_, result, err := ctx.Runtime().EvalExpression(ctx.WithVariables(funcArgs), f.body)
87+
// user-defined functions form a sub-program and all statements share the same context
88+
funcCtx := ctx.WithVariables(funcArgs)
89+
runtime := ctx.Runtime()
90+
91+
var (
92+
result any
93+
err error
94+
)
95+
96+
for _, expr := range f.body {
97+
funcCtx, result, err = runtime.EvalExpression(funcCtx, expr)
98+
if err != nil {
99+
return nil, err
100+
}
101+
}
88102

89103
return result, err
90104
}

0 commit comments

Comments
 (0)