-
Notifications
You must be signed in to change notification settings - Fork 31
/
risor.go
115 lines (104 loc) · 3.14 KB
/
risor.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package risor
import (
"context"
"fmt"
"github.com/risor-io/risor/compiler"
"github.com/risor-io/risor/importer"
"github.com/risor-io/risor/object"
"github.com/risor-io/risor/parser"
"github.com/risor-io/risor/vm"
)
// Option describes a function used to configure a Risor evaluation.
type Option func(*Config)
// WithGlobals provides global variables that are made available to Risor
// evaluations. This option is additive, so multiple WithGlobals options
// may be supplied. If the same key is supplied multiple times, the last
// supplied value is used.
func WithGlobals(globals map[string]any) Option {
return func(cfg *Config) {
for k, v := range globals {
cfg.Globals[k] = v
}
}
}
// WithGlobal supplies a single named global variable to the Risor evaluation.
func WithGlobal(name string, value any) Option {
return func(cfg *Config) {
cfg.Globals[name] = value
}
}
// WithoutDefaultGlobals opts out of all default global builtins and modules.
func WithoutDefaultGlobals() Option {
return func(cfg *Config) {
cfg.DefaultGlobals = map[string]object.Object{}
}
}
// WithImporter supplies an Importer that will be used to execute import statements.
func WithImporter(i importer.Importer) Option {
return func(cfg *Config) {
cfg.Importer = i
}
}
// WithLocalImporter enables importing Risor modules from the given directory.
func WithLocalImporter(path string) Option {
return func(cfg *Config) {
cfg.LocalImportPath = path
}
}
// Eval evaluates the given source code and returns the result.
func Eval(ctx context.Context, source string, options ...Option) (object.Object, error) {
cfg := NewConfig()
for _, opt := range options {
opt(cfg)
}
// Parse the source code to create the AST
ast, err := parser.Parse(ctx, source)
if err != nil {
return nil, err
}
// Compile the AST to bytecode, appending these new instructions after any
// instructions that were previously compiled
main, err := compiler.Compile(ast, cfg.CompilerOpts()...)
if err != nil {
return nil, err
}
// Eval the bytecode in a VM then return the top-of-stack (TOS) value
return vm.Run(ctx, main, cfg.VMOpts()...)
}
// EvalCode evaluates the precompiled code and returns the result.
func EvalCode(ctx context.Context, main *compiler.Code, options ...Option) (object.Object, error) {
cfg := NewConfig()
for _, opt := range options {
opt(cfg)
}
// Eval the bytecode in a VM then return the top-of-stack (TOS) value
return vm.Run(ctx, main, cfg.VMOpts()...)
}
// Call evaluates the precompiled code and then calls the named function.
// The supplied arguments are passed in the function call. The result of
// the function call is returned.
func Call(
ctx context.Context,
main *compiler.Code,
functionName string,
args []object.Object,
options ...Option,
) (object.Object, error) {
cfg := NewConfig()
for _, opt := range options {
opt(cfg)
}
vm := vm.New(main, cfg.VMOpts()...)
if err := vm.Run(ctx); err != nil {
return nil, err
}
obj, err := vm.Get(functionName)
if err != nil {
return nil, err
}
fn, ok := obj.(*object.Function)
if !ok {
return nil, fmt.Errorf("object is not a function (got: %s)", obj.Type())
}
return vm.Call(ctx, fn, args)
}