-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathevaluate.go
More file actions
113 lines (96 loc) · 3.34 KB
/
evaluate.go
File metadata and controls
113 lines (96 loc) · 3.34 KB
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
package FormulaEvaluator
import (
_ "embed"
"encoding/json"
"fmt"
"github.com/samber/mo"
v8 "rogchap.com/v8go"
)
type Target int
const (
Javascript Target = iota
Postgrsql
)
//go:embed dist/evaluate.cjs
var evaluateSource string
var iso = v8.NewIsolate()
// map the value of a Result[T] to a Result[U] using the given function.
func resultMap[T any, U any](r mo.Result[T], f func(T) U) mo.Result[U] {
if r.IsError() {
return mo.Err[U](r.Error())
} else {
return mo.Ok(f(r.MustGet()))
}
}
// chain the result of of Result[T] to a function that returns a Result[U].
func resultChain[T any, U any](r mo.Result[T], f func(T) mo.Result[U]) mo.Result[U] {
if r.IsError() {
return mo.Err[U](r.Error())
} else {
return f(r.MustGet())
}
}
// add two results together ignoring the first result.
func resultApply[T any, U any](r1 mo.Result[T], r2 mo.Result[U]) mo.Result[U] {
if r1.IsError() {
return mo.Err[U](r1.Error())
} else {
return r2
}
}
func targetString(target Target) string {
switch target {
case Javascript:
return "javascript"
case Postgrsql:
return "postgresql"
default:
panic("unreachable")
}
}
// sets a global variable with the given name and given parsed JSON value.
func setGlobalValue(ctx *v8.Context, name string, jsonValue string) mo.Result[*v8.Value] {
valueResult := mo.TupleToResult(v8.JSONParse(ctx, jsonValue))
return resultMap(valueResult, func(value *v8.Value) *v8.Value {
global := ctx.Global()
global.Set(name, value)
return value
})
}
func run(ctx *v8.Context, script string) mo.Result[string] {
v8Result := mo.TupleToResult(ctx.RunScript(evaluateSource, "evaluate.js"))
v8Result = resultApply(v8Result, mo.TupleToResult(ctx.RunScript(script, "main.js")))
return resultMap(v8Result, func(val *v8.Value) string {
return val.String()
})
}
type VariableConfig = struct {
Name string `json:"name"`
Type string `json:"type"`
Value string `json:"value"`
}
func Evaluate(formula string, target Target) (string, error) {
ctx := v8.NewContext(iso)
return run(ctx, fmt.Sprintf("evaluate('%s', '%s')", formula, targetString(target))).Get()
}
func EvaluateWithVariables(formula string, target Target, variableConfigs []VariableConfig) (string, error) {
ctx := v8.NewContext(iso)
jsonStrResult := mo.TupleToResult(json.Marshal(variableConfigs))
valueResult := resultChain(jsonStrResult, func(jsonStr []byte) mo.Result[*v8.Value] {
return setGlobalValue(ctx, "variableConfigs", string(jsonStr))
})
return resultApply(valueResult, run(ctx, fmt.Sprintf("evaluate('%s', '%s', variableConfigs)", formula, targetString(target)))).Get()
}
func EvaluateAST(jsonAST string, target Target) (string, error) {
ctx := v8.NewContext(iso)
valueResult := setGlobalValue(ctx, "ast", jsonAST)
return resultApply(valueResult, run(ctx, fmt.Sprintf("evaluateAST(ast, '%s')", targetString(target)))).Get()
}
func EvaluateASTWithVariables(jsonAST string, target Target, variableConfigs []VariableConfig) (string, error) {
ctx := v8.NewContext(iso)
jsonStrResult := mo.TupleToResult(json.Marshal(variableConfigs))
valueResult := resultChain(jsonStrResult, func(jsonStr []byte) mo.Result[*v8.Value] {
return resultApply(setGlobalValue(ctx, "ast", jsonAST), setGlobalValue(ctx, "variableConfigs", string(jsonStr)))
})
return resultApply(valueResult, run(ctx, fmt.Sprintf("evaluateAST(ast, '%s', variableConfigs)", targetString(target)))).Get()
}