mirror of
https://github.com/MontFerret/ferret.git
synced 2025-08-13 19:52:52 +02:00
Remove outdated compiler benchmarks, refactor function registration in namespace, and migrate math stdlib functions to simplified argument handling. Add new benchmarks for function calls and sorting scenarios.
This commit is contained in:
@@ -421,19 +421,47 @@ func (ec *ExprCompiler) CompileFunctionCallWith(ctx fql.IFunctionCallContext, pr
|
||||
|
||||
return seq[0]
|
||||
default:
|
||||
dest := ec.ctx.Registers.Allocate(core.Temp)
|
||||
ec.ctx.Emitter.EmitLoadConst(dest, ec.ctx.Symbols.AddConstant(name))
|
||||
|
||||
if !protected {
|
||||
ec.ctx.Emitter.EmitAs(vm.OpCall, dest, seq)
|
||||
} else {
|
||||
ec.ctx.Emitter.EmitAs(vm.OpProtectedCall, dest, seq)
|
||||
}
|
||||
|
||||
return dest
|
||||
return ec.compileUserFunctionCallWith(name, protected, seq)
|
||||
}
|
||||
}
|
||||
|
||||
func (ec *ExprCompiler) compileUserFunctionCallWith(name runtime.String, protected bool, seq core.RegisterSequence) vm.Operand {
|
||||
dest := ec.ctx.Registers.Allocate(core.Temp)
|
||||
ec.ctx.Emitter.EmitLoadConst(dest, ec.ctx.Symbols.AddConstant(name))
|
||||
|
||||
var opcode vm.Opcode
|
||||
var protectedOpcode vm.Opcode
|
||||
|
||||
switch len(seq) {
|
||||
case 0:
|
||||
opcode = vm.OpCall0
|
||||
protectedOpcode = vm.OpProtectedCall0
|
||||
case 1:
|
||||
opcode = vm.OpCall1
|
||||
protectedOpcode = vm.OpProtectedCall1
|
||||
case 2:
|
||||
opcode = vm.OpCall2
|
||||
protectedOpcode = vm.OpProtectedCall2
|
||||
case 3:
|
||||
opcode = vm.OpCall3
|
||||
protectedOpcode = vm.OpProtectedCall3
|
||||
case 4:
|
||||
opcode = vm.OpCall4
|
||||
protectedOpcode = vm.OpProtectedCall4
|
||||
default:
|
||||
opcode = vm.OpCall
|
||||
protectedOpcode = vm.OpProtectedCall
|
||||
}
|
||||
|
||||
if !protected {
|
||||
ec.ctx.Emitter.EmitAs(opcode, dest, seq)
|
||||
} else {
|
||||
ec.ctx.Emitter.EmitAs(protectedOpcode, dest, seq)
|
||||
}
|
||||
|
||||
return dest
|
||||
}
|
||||
|
||||
func (ec *ExprCompiler) CompileArgumentList(ctx fql.IArgumentListContext) core.RegisterSequence {
|
||||
var seq core.RegisterSequence
|
||||
|
||||
|
@@ -35,7 +35,7 @@ func sortDirection(dir antlr.TerminalNode) runtime.SortDirection {
|
||||
return runtime.SortDirectionAsc
|
||||
}
|
||||
|
||||
func copyFromNamespace(fns *runtime.Functions, namespace string) error {
|
||||
func copyFromNamespace(fns runtime.Functions, namespace string) error {
|
||||
// In the name of the function "A::B::C", the namespace is "A::B",
|
||||
// not "A::B::".
|
||||
//
|
||||
@@ -53,15 +53,28 @@ func copyFromNamespace(fns *runtime.Functions, namespace string) error {
|
||||
|
||||
noprefix := strings.Replace(name, namespace, "", 1)
|
||||
|
||||
if _, exists := fns.Get(noprefix); exists {
|
||||
if exists := fns.Has(noprefix); exists {
|
||||
return errors.Errorf(
|
||||
`collision occurred: "%s" already registered`,
|
||||
noprefix,
|
||||
)
|
||||
}
|
||||
|
||||
fn, _ := fns.Get(name)
|
||||
fns.Set(noprefix, fn)
|
||||
if fn, exists := fns.F().Get(name); exists {
|
||||
fns.F().Unset(name).Set(noprefix, fn)
|
||||
} else if fn, exists := fns.F0().Get(name); exists {
|
||||
fns.F0().Unset(name).Set(noprefix, fn)
|
||||
} else if fn, exists := fns.F1().Get(name); exists {
|
||||
fns.F1().Unset(name).Set(noprefix, fn)
|
||||
} else if fn, exists := fns.F2().Get(name); exists {
|
||||
fns.F2().Unset(name).Set(noprefix, fn)
|
||||
} else if fn, exists := fns.F3().Get(name); exists {
|
||||
fns.F3().Unset(name).Set(noprefix, fn)
|
||||
} else if fn, exists := fns.F4().Get(name); exists {
|
||||
fns.F4().Unset(name).Set(noprefix, fn)
|
||||
} else {
|
||||
return errors.Errorf(`function "%s" not found`, name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -5,8 +5,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var fnNameValidation = regexp.MustCompile("^[a-zA-Z]+[a-zA-Z0-9_]*(::[a-zA-Z]+[a-zA-Z0-9_]*)*$")
|
||||
@@ -34,53 +32,12 @@ func (nc *NamespaceContainer) Namespace(name string) runtime.Namespace {
|
||||
return NewNamespace(nc.funcs, nc.makeFullName(name))
|
||||
}
|
||||
|
||||
func (nc *NamespaceContainer) MustRegisterFunction(name string, fun runtime.Function) {
|
||||
if err := nc.RegisterFunction(name, fun); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (nc *NamespaceContainer) RegisterFunction(name string, fun runtime.Function) error {
|
||||
nsName := nc.makeFullName(name)
|
||||
|
||||
_, exists := nc.funcs.Get(nsName)
|
||||
|
||||
if exists {
|
||||
return errors.Errorf("function already exists: %s", name)
|
||||
}
|
||||
|
||||
// validation the name
|
||||
if strings.Contains(name, separator) {
|
||||
return errors.Errorf("invalid function name: %s", name)
|
||||
}
|
||||
|
||||
if !fnNameValidation.MatchString(nsName) {
|
||||
return errors.Errorf("invalid function or namespace name: %s", nsName)
|
||||
}
|
||||
|
||||
nc.funcs.Set(nsName, fun)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nc *NamespaceContainer) RemoveFunction(name string) {
|
||||
nc.funcs.Unset(nc.makeFullName(name))
|
||||
}
|
||||
|
||||
func (nc *NamespaceContainer) MustRegisterFunctions(funcs runtime.Functions) {
|
||||
if err := nc.RegisterFunctions(funcs); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (nc *NamespaceContainer) RegisterFunctions(funcs runtime.Functions) error {
|
||||
for _, name := range funcs.Names() {
|
||||
fun, _ := funcs.Get(name)
|
||||
|
||||
if err := nc.RegisterFunction(name, fun); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
nc.funcs.SetAll(funcs)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -2,118 +2,68 @@ package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// MaxArgs defines the maximum number of arguments that a function can accept.
|
||||
const MaxArgs = 65536
|
||||
|
||||
type (
|
||||
// Functions is a container for functions.
|
||||
Functions map[string]Function
|
||||
Functions interface {
|
||||
Has(name string) bool
|
||||
F() FunctionCollection[Function]
|
||||
F0() FunctionCollection[Function0]
|
||||
F1() FunctionCollection[Function1]
|
||||
F2() FunctionCollection[Function2]
|
||||
F3() FunctionCollection[Function3]
|
||||
F4() FunctionCollection[Function4]
|
||||
SetAll(otherFns Functions) Functions
|
||||
Unset(name string) Functions
|
||||
UnsetAll() Functions
|
||||
Names() []string
|
||||
}
|
||||
|
||||
// Function is a common interface for all functions of FQL.
|
||||
FunctionsBuilder interface {
|
||||
Set(name string, fn Function) FunctionsBuilder
|
||||
Set0(name string, fn Function0) FunctionsBuilder
|
||||
Set1(name string, fn Function1) FunctionsBuilder
|
||||
Set2(name string, fn Function2) FunctionsBuilder
|
||||
Set3(name string, fn Function3) FunctionsBuilder
|
||||
Set4(name string, fn Function4) FunctionsBuilder
|
||||
Build() Functions
|
||||
}
|
||||
|
||||
FunctionConstraint interface {
|
||||
Function | Function0 | Function1 | Function2 | Function3 | Function4
|
||||
}
|
||||
|
||||
FunctionCollection[T FunctionConstraint] interface {
|
||||
Has(name string) bool
|
||||
Set(name string, fn T) FunctionCollection[T]
|
||||
SetAll(otherFns FunctionCollection[T]) FunctionCollection[T]
|
||||
Get(name string) (T, bool)
|
||||
GetAll() map[string]T
|
||||
Unset(name string) FunctionCollection[T]
|
||||
UnsetAll() FunctionCollection[T]
|
||||
Names() []string
|
||||
Size() int
|
||||
}
|
||||
|
||||
// Function is a common interface for functions with variable number of arguments.
|
||||
// All functions receive a context and a slice of values, returning a value and an error.
|
||||
Function = func(ctx context.Context, args ...Value) (Value, error)
|
||||
|
||||
// Function0 is a common interface for functions with no arguments.
|
||||
Function0 = func(ctx context.Context) (Value, error)
|
||||
|
||||
// Function1 is a common interface for functions with a single argument.
|
||||
Function1 = func(ctx context.Context, arg Value) (Value, error)
|
||||
|
||||
// Function2 is a common interface for functions with two arguments.
|
||||
Function2 = func(ctx context.Context, arg1, arg2 Value) (Value, error)
|
||||
|
||||
// Function3 is a common interface for functions with three arguments.
|
||||
Function3 = func(ctx context.Context, arg1, arg2, arg3 Value) (Value, error)
|
||||
|
||||
// Function4 is a common interface for functions with four arguments.
|
||||
Function4 = func(ctx context.Context, arg1, arg2, arg3, arg4 Value) (Value, error)
|
||||
)
|
||||
|
||||
func ErrorArg(err error, pos int) error {
|
||||
return Errorf(
|
||||
ErrInvalidArgumentType,
|
||||
"expected argument %d to be: %s",
|
||||
pos+1, err.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
func ValidateArgs(args []Value, minimum, maximum int) error {
|
||||
count := len(args)
|
||||
|
||||
if count < minimum || count > maximum {
|
||||
return Error(
|
||||
ErrInvalidArgumentNumber,
|
||||
fmt.Sprintf(
|
||||
"expected number of arguments %d-%d, but got %d",
|
||||
minimum,
|
||||
maximum,
|
||||
len(args)))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateArgType(args []Value, pos int, assertion TypeAssertion) error {
|
||||
if pos >= len(args) {
|
||||
return nil
|
||||
}
|
||||
|
||||
arg := args[pos]
|
||||
|
||||
err := assertion(arg)
|
||||
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ErrorArg(err, pos)
|
||||
}
|
||||
|
||||
// NewFunctions returns new empty Functions.
|
||||
func NewFunctions() Functions {
|
||||
return make(map[string]Function)
|
||||
}
|
||||
|
||||
// NewFunctionsFromMap creates new Functions from map, where
|
||||
// key is the name of the function and value is the function.
|
||||
func NewFunctionsFromMap(funcs map[string]Function) Functions {
|
||||
fns := NewFunctions()
|
||||
|
||||
for name, fn := range funcs {
|
||||
fns.Set(name, fn)
|
||||
}
|
||||
|
||||
return fns
|
||||
}
|
||||
|
||||
// Has returns true if the function with the given name exists.
|
||||
func (fns Functions) Has(name string) bool {
|
||||
_, exists := fns[name]
|
||||
return exists
|
||||
}
|
||||
|
||||
// Get returns the function with the given name. If the function
|
||||
// does not exist it returns nil, false.
|
||||
func (fns Functions) Get(name string) (Function, bool) {
|
||||
fn, exists := fns[name]
|
||||
return fn, exists
|
||||
}
|
||||
|
||||
// MustGet returns the function with the given name. If the function
|
||||
// does not exist it panics.
|
||||
func (fns Functions) MustGet(name string) Function {
|
||||
return fns[name]
|
||||
}
|
||||
|
||||
// Set sets the function with the given name. If the function
|
||||
// with the such name already exists it will be overwritten.
|
||||
func (fns Functions) Set(name string, fn Function) {
|
||||
fns[name] = fn
|
||||
}
|
||||
|
||||
// Unset delete the function with the given name.
|
||||
func (fns Functions) Unset(name string) {
|
||||
delete(fns, name)
|
||||
}
|
||||
|
||||
// Names returns the names of the internal functions.
|
||||
func (fns Functions) Names() []string {
|
||||
names := make([]string, 0, len(fns))
|
||||
|
||||
for name := range fns {
|
||||
names = append(names, name)
|
||||
}
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
// Unwrap returns the internal map of functions.
|
||||
func (fns Functions) Unwrap() map[string]Function {
|
||||
return fns
|
||||
}
|
||||
|
51
pkg/runtime/function_builder.go
Normal file
51
pkg/runtime/function_builder.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package runtime
|
||||
|
||||
type functionBuilder struct {
|
||||
functions Functions
|
||||
}
|
||||
|
||||
func NewFunctionsBuilder() FunctionsBuilder {
|
||||
return &functionBuilder{
|
||||
functions: NewFunctions(),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *functionBuilder) Set(name string, fn Function) FunctionsBuilder {
|
||||
f.functions.F().Set(name, fn)
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *functionBuilder) Set0(name string, fn Function0) FunctionsBuilder {
|
||||
f.functions.F0().Set(name, fn)
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *functionBuilder) Set1(name string, fn Function1) FunctionsBuilder {
|
||||
f.functions.F1().Set(name, fn)
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *functionBuilder) Set2(name string, fn Function2) FunctionsBuilder {
|
||||
f.functions.F2().Set(name, fn)
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *functionBuilder) Set3(name string, fn Function3) FunctionsBuilder {
|
||||
f.functions.F3().Set(name, fn)
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *functionBuilder) Set4(name string, fn Function4) FunctionsBuilder {
|
||||
f.functions.F4().Set(name, fn)
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *functionBuilder) Build() Functions {
|
||||
return f.functions
|
||||
}
|
97
pkg/runtime/function_collection.go
Normal file
97
pkg/runtime/function_collection.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package runtime
|
||||
|
||||
type functionCollection[T FunctionConstraint] struct {
|
||||
values map[string]T
|
||||
}
|
||||
|
||||
// NewFunctionCollection creates a new function collection of the specified type
|
||||
func NewFunctionCollection[T FunctionConstraint]() FunctionCollection[T] {
|
||||
return &functionCollection[T]{
|
||||
values: make(map[string]T),
|
||||
}
|
||||
}
|
||||
|
||||
// NewFunctionCollectionFromMap creates a new function collection from an existing map
|
||||
func NewFunctionCollectionFromMap[T FunctionConstraint](values map[string]T) FunctionCollection[T] {
|
||||
fc := &functionCollection[T]{
|
||||
values: make(map[string]T, len(values)),
|
||||
}
|
||||
|
||||
for name, fn := range values {
|
||||
fc.values[name] = fn
|
||||
}
|
||||
|
||||
return fc
|
||||
}
|
||||
|
||||
func (f *functionCollection[T]) Has(name string) bool {
|
||||
_, exists := f.values[name]
|
||||
|
||||
return exists
|
||||
|
||||
}
|
||||
|
||||
func (f *functionCollection[T]) Set(name string, fn T) FunctionCollection[T] {
|
||||
f.values[name] = fn
|
||||
|
||||
return f
|
||||
|
||||
}
|
||||
|
||||
func (f *functionCollection[T]) SetAll(otherFns FunctionCollection[T]) FunctionCollection[T] {
|
||||
if otherFns == nil {
|
||||
return f
|
||||
}
|
||||
|
||||
for name, fn := range otherFns.GetAll() {
|
||||
f.values[name] = fn
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *functionCollection[T]) Get(name string) (T, bool) {
|
||||
fn, exists := f.values[name]
|
||||
|
||||
return fn, exists
|
||||
}
|
||||
|
||||
func (f *functionCollection[T]) GetAll() map[string]T {
|
||||
// Return a copy to prevent external modification
|
||||
result := make(map[string]T, len(f.values))
|
||||
|
||||
for name, fn := range f.values {
|
||||
result[name] = fn
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
func (f *functionCollection[T]) Unset(name string) FunctionCollection[T] {
|
||||
delete(f.values, name)
|
||||
|
||||
return f
|
||||
|
||||
}
|
||||
|
||||
func (f *functionCollection[T]) UnsetAll() FunctionCollection[T] {
|
||||
f.values = make(map[string]T)
|
||||
|
||||
return f
|
||||
|
||||
}
|
||||
|
||||
func (f *functionCollection[T]) Names() []string {
|
||||
names := make([]string, 0, len(f.values))
|
||||
|
||||
for name := range f.values {
|
||||
names = append(names, name)
|
||||
}
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
func (f *functionCollection[T]) Size() int {
|
||||
return len(f.values)
|
||||
}
|
50
pkg/runtime/function_helper.go
Normal file
50
pkg/runtime/function_helper.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package runtime
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ErrorArg creates an error for an invalid argument at the specified position.
|
||||
// The position is 0-based internally but reported as 1-based to users.
|
||||
func ErrorArg(err error, pos int) error {
|
||||
return Errorf(
|
||||
ErrInvalidArgumentType,
|
||||
"expected argument %d to be: %s",
|
||||
pos+1, err.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
// ValidateArgs validates that the number of arguments is within the specified range.
|
||||
// It returns an error if the argument count is outside the [minimum, maximum] range.
|
||||
func ValidateArgs(args []Value, minimum, maximum int) error {
|
||||
count := len(args)
|
||||
|
||||
if count < minimum || count > maximum {
|
||||
return Error(
|
||||
ErrInvalidArgumentNumber,
|
||||
fmt.Sprintf(
|
||||
"expected number of arguments %d-%d, but got %d",
|
||||
minimum,
|
||||
maximum,
|
||||
len(args)))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateArgType validates that the argument at the specified position matches
|
||||
// the given type assertion. If the position is beyond the arguments array,
|
||||
// no validation is performed (returns nil).
|
||||
func ValidateArgType(args []Value, pos int, assertion TypeAssertion) error {
|
||||
if pos >= len(args) {
|
||||
return nil
|
||||
}
|
||||
|
||||
arg := args[pos]
|
||||
|
||||
err := assertion(arg)
|
||||
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ErrorArg(err, pos)
|
||||
}
|
171
pkg/runtime/function_registry.go
Normal file
171
pkg/runtime/function_registry.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package runtime
|
||||
|
||||
// Functions is a container for functions that organizes them by their argument count.
|
||||
// It provides separate storage for functions with fixed argument counts (0-4) and
|
||||
// functions with variable argument counts for optimal performance.
|
||||
type functionRegistry struct {
|
||||
variadic FunctionCollection[Function] // Functions with variable number of arguments
|
||||
zero FunctionCollection[Function0] // Functions with no arguments
|
||||
single FunctionCollection[Function1] // Functions with a single argument
|
||||
double FunctionCollection[Function2] // Functions with two arguments
|
||||
tripple FunctionCollection[Function3] // Functions with three arguments
|
||||
quadruple FunctionCollection[Function4] // Functions with four arguments
|
||||
}
|
||||
|
||||
// NewFunctions creates and returns a new empty Functions container.
|
||||
func NewFunctions() Functions {
|
||||
return &functionRegistry{}
|
||||
}
|
||||
|
||||
func NewFunctionsFromMap(funcs map[string]Function) Functions {
|
||||
return &functionRegistry{
|
||||
variadic: NewFunctionCollectionFromMap(funcs),
|
||||
zero: NewFunctionCollection[Function0](),
|
||||
single: NewFunctionCollection[Function1](),
|
||||
double: NewFunctionCollection[Function2](),
|
||||
tripple: NewFunctionCollection[Function3](),
|
||||
quadruple: NewFunctionCollection[Function4](),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *functionRegistry) Has(name string) bool {
|
||||
return f.F().Has(name) ||
|
||||
f.F0().Has(name) ||
|
||||
f.F1().Has(name) ||
|
||||
f.F2().Has(name) ||
|
||||
f.F3().Has(name) ||
|
||||
f.F4().Has(name)
|
||||
|
||||
}
|
||||
|
||||
func (f *functionRegistry) F() FunctionCollection[Function] {
|
||||
if f.variadic == nil {
|
||||
f.variadic = NewFunctionCollection[Function]()
|
||||
}
|
||||
|
||||
return f.variadic
|
||||
}
|
||||
|
||||
func (f *functionRegistry) F0() FunctionCollection[Function0] {
|
||||
if f.zero == nil {
|
||||
f.zero = NewFunctionCollection[Function0]()
|
||||
}
|
||||
|
||||
return f.zero
|
||||
}
|
||||
|
||||
func (f *functionRegistry) F1() FunctionCollection[Function1] {
|
||||
if f.single == nil {
|
||||
f.single = NewFunctionCollection[Function1]()
|
||||
}
|
||||
|
||||
return f.single
|
||||
}
|
||||
|
||||
func (f *functionRegistry) F2() FunctionCollection[Function2] {
|
||||
if f.double == nil {
|
||||
f.double = NewFunctionCollection[Function2]()
|
||||
}
|
||||
|
||||
return f.double
|
||||
}
|
||||
|
||||
func (f *functionRegistry) F3() FunctionCollection[Function3] {
|
||||
if f.tripple == nil {
|
||||
f.tripple = NewFunctionCollection[Function3]()
|
||||
}
|
||||
|
||||
return f.tripple
|
||||
}
|
||||
|
||||
func (f *functionRegistry) F4() FunctionCollection[Function4] {
|
||||
if f.quadruple == nil {
|
||||
f.quadruple = NewFunctionCollection[Function4]()
|
||||
}
|
||||
|
||||
return f.quadruple
|
||||
}
|
||||
|
||||
func (f *functionRegistry) SetAll(otherFns Functions) Functions {
|
||||
if otherFns == nil {
|
||||
return f
|
||||
}
|
||||
|
||||
// Copy functions from each collection
|
||||
f.F().SetAll(otherFns.F())
|
||||
f.F0().SetAll(otherFns.F0())
|
||||
f.F1().SetAll(otherFns.F1())
|
||||
f.F2().SetAll(otherFns.F2())
|
||||
f.F3().SetAll(otherFns.F3())
|
||||
f.F4().SetAll(otherFns.F4())
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *functionRegistry) Unset(name string) Functions {
|
||||
if f.F().Has(name) {
|
||||
f.variadic.Unset(name)
|
||||
} else if f.F0().Has(name) {
|
||||
f.zero.Unset(name)
|
||||
} else if f.F1().Has(name) {
|
||||
f.single.Unset(name)
|
||||
} else if f.F2().Has(name) {
|
||||
f.double.Unset(name)
|
||||
} else if f.F3().Has(name) {
|
||||
f.tripple.Unset(name)
|
||||
} else if f.F4().Has(name) {
|
||||
f.quadruple.Unset(name)
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *functionRegistry) UnsetAll() Functions {
|
||||
if f.variadic != nil {
|
||||
f.variadic.UnsetAll()
|
||||
}
|
||||
|
||||
if f.zero != nil {
|
||||
f.zero.UnsetAll()
|
||||
}
|
||||
|
||||
if f.single != nil {
|
||||
f.single.UnsetAll()
|
||||
}
|
||||
|
||||
if f.double != nil {
|
||||
f.double.UnsetAll()
|
||||
}
|
||||
|
||||
if f.tripple != nil {
|
||||
f.tripple.UnsetAll()
|
||||
}
|
||||
|
||||
if f.quadruple != nil {
|
||||
f.quadruple.UnsetAll()
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *functionRegistry) Names() []string {
|
||||
// Pre-calculate capacity to avoid reallocations
|
||||
capacity := f.F().Size() +
|
||||
f.F0().Size() +
|
||||
f.F1().Size() +
|
||||
f.F2().Size() +
|
||||
f.F3().Size() +
|
||||
f.F4().Size()
|
||||
|
||||
names := make([]string, 0, capacity)
|
||||
|
||||
// Collect names from all function collections
|
||||
names = append(names, f.variadic.Names()...)
|
||||
names = append(names, f.zero.Names()...)
|
||||
names = append(names, f.single.Names()...)
|
||||
names = append(names, f.double.Names()...)
|
||||
names = append(names, f.tripple.Names()...)
|
||||
names = append(names, f.quadruple.Names()...)
|
||||
|
||||
return names
|
||||
}
|
@@ -2,7 +2,6 @@ package runtime
|
||||
|
||||
type Namespace interface {
|
||||
Namespace(name string) Namespace
|
||||
RegisterFunction(name string, fun Function) error
|
||||
RegisterFunctions(funs Functions) error
|
||||
RegisteredFunctions() []string
|
||||
RemoveFunction(name string)
|
||||
|
@@ -10,14 +10,10 @@ import (
|
||||
// ABS returns the absolute value of a given number.
|
||||
// @param {Int | Float} number - Input number.
|
||||
// @return {Float} - The absolute value of a given number.
|
||||
func Abs(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
func Abs(_ context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
return runtime.NewFloat(math.Abs(toFloat(args[0]))), nil
|
||||
return runtime.NewFloat(math.Abs(toFloat(arg))), nil
|
||||
}
|
||||
|
@@ -10,14 +10,10 @@ import (
|
||||
// ACOS returns the arccosine, in radians, of a given number.
|
||||
// @param {Int | Float} number - Input number.
|
||||
// @return {Float} - The arccosine, in radians, of a given number.
|
||||
func Acos(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
func Acos(_ context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
return runtime.NewFloat(math.Acos(toFloat(args[0]))), nil
|
||||
return runtime.NewFloat(math.Acos(toFloat(arg))), nil
|
||||
}
|
||||
|
@@ -10,14 +10,10 @@ import (
|
||||
// ASIN returns the arcsine, in radians, of a given number.
|
||||
// @param {Int | Float} number - Input number.
|
||||
// @return {Float} - The arcsine, in radians, of a given number.
|
||||
func Asin(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
func Asin(_ context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
return runtime.NewFloat(math.Asin(toFloat(args[0]))), nil
|
||||
return runtime.NewFloat(math.Asin(toFloat(arg))), nil
|
||||
}
|
||||
|
@@ -10,14 +10,10 @@ import (
|
||||
// ATAN returns the arctangent, in radians, of a given number.
|
||||
// @param {Int | Float} number - Input number.
|
||||
// @return {Float} - The arctangent, in radians, of a given number.
|
||||
func Atan(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
func Atan(_ context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
return runtime.NewFloat(math.Atan(toFloat(args[0]))), nil
|
||||
return runtime.NewFloat(math.Atan(toFloat(arg))), nil
|
||||
}
|
||||
|
@@ -11,21 +11,17 @@ import (
|
||||
// @param {Int | Float} number1 - Input number.
|
||||
// @param {Int | Float} number2 - Input number.
|
||||
// @return {Float} - The arc tangent of y/x, using the signs of the two to determine the quadrant of the return value.
|
||||
func Atan2(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 2, 2); err != nil {
|
||||
func Atan2(_ context.Context, arg1, arg2 runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg1); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
if err := runtime.AssertNumber(arg2); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[1]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
argf1 := toFloat(arg1)
|
||||
argf2 := toFloat(arg2)
|
||||
|
||||
arg1 := toFloat(args[0])
|
||||
arg2 := toFloat(args[1])
|
||||
|
||||
return runtime.NewFloat(math.Atan2(arg1, arg2)), nil
|
||||
return runtime.NewFloat(math.Atan2(argf1, argf2)), nil
|
||||
}
|
||||
|
@@ -9,12 +9,8 @@ import (
|
||||
// AVERAGE Returns the average (arithmetic mean) of the values in array.
|
||||
// @param {Int[] | Float[]} array - arrayList of numbers.
|
||||
// @return {Float} - The average of the values in array.
|
||||
func Average(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
arr, err := runtime.CastList(args[0])
|
||||
func Average(ctx context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
arr, err := runtime.CastList(arg)
|
||||
|
||||
if err != nil {
|
||||
return runtime.None, err
|
||||
|
@@ -10,14 +10,10 @@ import (
|
||||
// CEIL returns the least integer value greater than or equal to a given value.
|
||||
// @param {Int | Float} number - Input number.
|
||||
// @return {Int} - The least integer value greater than or equal to a given value.
|
||||
func Ceil(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
func Ceil(_ context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
return runtime.NewInt(int(math.Ceil(toFloat(args[0])))), nil
|
||||
return runtime.NewInt(int(math.Ceil(toFloat(arg)))), nil
|
||||
}
|
||||
|
@@ -10,14 +10,10 @@ import (
|
||||
// COS returns the cosine of a given number.
|
||||
// @param {Int | Float} number - Input number.
|
||||
// @return {Float} - The cosine of a given number.
|
||||
func Cos(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
func Cos(_ context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
return runtime.NewFloat(math.Cos(toFloat(args[0]))), nil
|
||||
return runtime.NewFloat(math.Cos(toFloat(arg))), nil
|
||||
}
|
||||
|
@@ -9,16 +9,12 @@ import (
|
||||
// DEGREES returns the angle converted from radians to degrees.
|
||||
// @param {Int | Float} number - The input number.
|
||||
// @return {Float} - The angle in degrees
|
||||
func Degrees(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
func Degrees(_ context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
r := toFloat(args[0])
|
||||
r := toFloat(arg)
|
||||
|
||||
return runtime.NewFloat(r * RadToDeg), nil
|
||||
}
|
||||
|
@@ -10,14 +10,10 @@ import (
|
||||
// EXP returns Euler's constant (2.71828...) raised to the power of value.
|
||||
// @param {Int | Float} number - Input number.
|
||||
// @return {Float} - Euler's constant raised to the power of value.
|
||||
func Exp(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
func Exp(_ context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
return runtime.NewFloat(math.Exp(toFloat(args[0]))), nil
|
||||
return runtime.NewFloat(math.Exp(toFloat(arg))), nil
|
||||
}
|
||||
|
@@ -10,14 +10,10 @@ import (
|
||||
// EXP2 returns 2 raised to the power of value.
|
||||
// @param {Int | Float} number - Input number.
|
||||
// @return {Float} - 2 raised to the power of value.
|
||||
func Exp2(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
func Exp2(_ context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
return runtime.NewFloat(math.Exp2(toFloat(args[0]))), nil
|
||||
return runtime.NewFloat(math.Exp2(toFloat(arg))), nil
|
||||
}
|
||||
|
@@ -10,14 +10,10 @@ import (
|
||||
// FLOOR returns the greatest integer value less than or equal to a given value.
|
||||
// @param {Int | Float} number - Input number.
|
||||
// @return {Int} - The greatest integer value less than or equal to a given value.
|
||||
func Floor(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
func Floor(_ context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
return runtime.NewInt(int(math.Floor(toFloat(args[0])))), nil
|
||||
return runtime.NewInt(int(math.Floor(toFloat(arg)))), nil
|
||||
}
|
||||
|
@@ -15,41 +15,43 @@ const (
|
||||
|
||||
func RegisterLib(ns runtime.Namespace) error {
|
||||
return ns.RegisterFunctions(
|
||||
runtime.NewFunctionsFromMap(map[string]runtime.Function{
|
||||
"ABS": Abs,
|
||||
"ACOS": Acos,
|
||||
"ASIN": Asin,
|
||||
"ATAN": Atan,
|
||||
"ATAN2": Atan2,
|
||||
"AVERAGE": Average,
|
||||
"CEIL": Ceil,
|
||||
"COS": Cos,
|
||||
"DEGREES": Degrees,
|
||||
"EXP": Exp,
|
||||
"EXP2": Exp2,
|
||||
"FLOOR": Floor,
|
||||
"LOG": Log,
|
||||
"LOG2": Log2,
|
||||
"LOG10": Log10,
|
||||
"MAX": Max,
|
||||
"MEDIAN": Median,
|
||||
"MIN": Min,
|
||||
"PERCENTILE": Percentile,
|
||||
"PI": Pi,
|
||||
"POW": Pow,
|
||||
"RADIANS": Radians,
|
||||
"RAND": Rand,
|
||||
"RANGE": Range,
|
||||
"ROUND": Round,
|
||||
"SIN": Sin,
|
||||
"SQRT": Sqrt,
|
||||
"STDDEV_POPULATION": StandardDeviationPopulation,
|
||||
"STDDEV_SAMPLE": StandardDeviationSample,
|
||||
"SUM": Sum,
|
||||
"TAN": Tan,
|
||||
"VARIANCE_POPULATION": PopulationVariance,
|
||||
"VARIANCE_SAMPLE": SampleVariance,
|
||||
}))
|
||||
runtime.
|
||||
NewFunctionsBuilder().
|
||||
Set1("ABS", Abs).
|
||||
Set1("ACOS", Acos).
|
||||
Set1("ASIN", Asin).
|
||||
Set1("ATAN", Atan).
|
||||
Set2("ATAN2", Atan2).
|
||||
Set1("AVERAGE", Average).
|
||||
Set1("CEIL", Ceil).
|
||||
Set1("COS", Cos).
|
||||
Set1("DEGREES", Degrees).
|
||||
Set1("EXP", Exp).
|
||||
Set1("EXP2", Exp2).
|
||||
Set1("FLOOR", Floor).
|
||||
Set1("LOG", Log).
|
||||
Set1("LOG2", Log2).
|
||||
Set1("LOG10", Log10).
|
||||
Set1("MAX", Max).
|
||||
Set1("MEDIAN", Median).
|
||||
Set1("MIN", Min).
|
||||
Set("PERCENTILE", Percentile).
|
||||
Set0("PI", Pi).
|
||||
Set2("POW", Pow).
|
||||
Set1("RADIANS", Radians).
|
||||
Set("RAND", Rand).
|
||||
Set("RANGE", Range).
|
||||
Set1("ROUND", Round).
|
||||
Set1("SIN", Sin).
|
||||
Set1("SQRT", Sqrt).
|
||||
Set1("STDDEV_POPULATION", StandardDeviationPopulation).
|
||||
Set1("STDDEV_SAMPLE", StandardDeviationSample).
|
||||
Set1("SUM", Sum).
|
||||
Set1("TAN", Tan).
|
||||
Set1("VARIANCE_POPULATION", PopulationVariance).
|
||||
Set1("VARIANCE_SAMPLE", SampleVariance).
|
||||
Build(),
|
||||
)
|
||||
}
|
||||
|
||||
func toFloat(arg runtime.Value) float64 {
|
||||
|
@@ -10,14 +10,10 @@ import (
|
||||
// LOG returns the natural logarithm of a given value.
|
||||
// @param {Int | Float} number - Input number.
|
||||
// @return {Float} - The natural logarithm of a given value.
|
||||
func Log(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
func Log(_ context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
return runtime.NewFloat(math.Log(toFloat(args[0]))), nil
|
||||
return runtime.NewFloat(math.Log(toFloat(arg))), nil
|
||||
}
|
||||
|
@@ -10,14 +10,10 @@ import (
|
||||
// LOG10 returns the decimal logarithm of a given value.
|
||||
// @param {Int | Float} number - Input number.
|
||||
// @return {Float} - The decimal logarithm of a given value.
|
||||
func Log10(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
func Log10(_ context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
return runtime.NewFloat(math.Log10(toFloat(args[0]))), nil
|
||||
return runtime.NewFloat(math.Log10(toFloat(arg))), nil
|
||||
}
|
||||
|
@@ -10,14 +10,10 @@ import (
|
||||
// LOG2 returns the binary logarithm of a given value.
|
||||
// @param {Int | Float} number - Input number.
|
||||
// @return {Float} - The binary logarithm of a given value.
|
||||
func Log2(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
func Log2(_ context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
return runtime.NewFloat(math.Log2(toFloat(args[0]))), nil
|
||||
return runtime.NewFloat(math.Log2(toFloat(arg))), nil
|
||||
}
|
||||
|
@@ -9,12 +9,8 @@ import (
|
||||
// MAX returns the greatest (arithmetic mean) of the values in array.
|
||||
// @param {Int[] | Float[]} array - arrayList of numbers.
|
||||
// @return {Float} - The greatest of the values in array.
|
||||
func Max(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
arr, err := runtime.CastList(args[0])
|
||||
func Max(ctx context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
arr, err := runtime.CastList(arg)
|
||||
|
||||
if err != nil {
|
||||
return runtime.None, err
|
||||
|
@@ -10,12 +10,8 @@ import (
|
||||
// MEDIAN returns the median of the values in array.
|
||||
// @param {Int[] | Float[]} array - arrayList of numbers.
|
||||
// @return {Float} - The median of the values in array.
|
||||
func Median(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
arr, err := runtime.CastList(args[0])
|
||||
func Median(ctx context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
arr, err := runtime.CastList(arg)
|
||||
|
||||
if err != nil {
|
||||
return runtime.None, err
|
||||
|
@@ -9,12 +9,8 @@ import (
|
||||
// MIN returns the smallest (arithmetic mean) of the values in array.
|
||||
// @param {Int[] | Float[]} array - arrayList of numbers.
|
||||
// @return {Float} - The smallest of the values in array.
|
||||
func Min(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
arr, err := runtime.CastList(args[0])
|
||||
func Min(ctx context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
arr, err := runtime.CastList(arg)
|
||||
|
||||
if err != nil {
|
||||
return runtime.None, err
|
||||
|
@@ -9,12 +9,6 @@ import (
|
||||
|
||||
// PI returns Pi value.
|
||||
// @return {Float} - Pi value.
|
||||
func Pi(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
err := runtime.ValidateArgs(args, 0, 0)
|
||||
|
||||
if err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
func Pi(_ context.Context) (runtime.Value, error) {
|
||||
return runtime.NewFloat(math.Pi), nil
|
||||
}
|
||||
|
@@ -11,18 +11,14 @@ import (
|
||||
// @param {Int | Float} base - The base value.
|
||||
// @param {Int | Float} exp - The exponent value.
|
||||
// @return {Float} - The exponentiated value.
|
||||
func Pow(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 2, 2); err != nil {
|
||||
func Pow(_ context.Context, arg1, arg2 runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg1); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
if err := runtime.AssertNumber(arg2); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[1]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
return runtime.NewFloat(math.Pow(toFloat(args[0]), toFloat(args[1]))), nil
|
||||
return runtime.NewFloat(math.Pow(toFloat(arg1), toFloat(arg2))), nil
|
||||
}
|
||||
|
@@ -9,16 +9,12 @@ import (
|
||||
// RADIANS returns the angle converted from degrees to radians.
|
||||
// @param {Int | Float} number - The input number.
|
||||
// @return {Float} - The angle in radians.
|
||||
func Radians(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
func Radians(_ context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
r := toFloat(args[0])
|
||||
r := toFloat(arg)
|
||||
|
||||
return runtime.NewFloat(r * DegToRad), nil
|
||||
}
|
||||
|
@@ -10,14 +10,10 @@ import (
|
||||
// ROUND returns the nearest integer, rounding half away from zero.
|
||||
// @param {Int | Float} number - Input number.
|
||||
// @return {Int} - The nearest integer, rounding half away from zero.
|
||||
func Round(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
func Round(_ context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
return runtime.NewInt(int(math.Round(toFloat(args[0])))), nil
|
||||
return runtime.NewInt(int(math.Round(toFloat(arg)))), nil
|
||||
}
|
||||
|
@@ -10,14 +10,10 @@ import (
|
||||
// SIN returns the sine of the radian argument.
|
||||
// @param {Int | Float} number - Input number.
|
||||
// @return {Float} - The sin, in radians, of a given number.
|
||||
func Sin(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
func Sin(_ context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
return runtime.NewFloat(math.Sin(toFloat(args[0]))), nil
|
||||
return runtime.NewFloat(math.Sin(toFloat(arg))), nil
|
||||
}
|
||||
|
@@ -10,14 +10,10 @@ import (
|
||||
// SQRT returns the square root of a given number.
|
||||
// @param {Int | Float} value - A number.
|
||||
// @return {Float} - The square root.
|
||||
func Sqrt(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
func Sqrt(_ context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
return runtime.NewFloat(math.Sqrt(toFloat(args[0]))), nil
|
||||
return runtime.NewFloat(math.Sqrt(toFloat(arg))), nil
|
||||
}
|
||||
|
@@ -10,12 +10,8 @@ import (
|
||||
// STDDEV_POPULATION returns the population standard deviation of the values in a given array.
|
||||
// @param {Int[] | Float[]} numbers - arrayList of numbers.
|
||||
// @return {Float} - The population standard deviation.
|
||||
func StandardDeviationPopulation(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
arr, err := runtime.CastList(args[0])
|
||||
func StandardDeviationPopulation(ctx context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
arr, err := runtime.CastList(arg)
|
||||
|
||||
if err != nil {
|
||||
return runtime.None, err
|
||||
|
@@ -10,12 +10,8 @@ import (
|
||||
// STDDEV_SAMPLE returns the sample standard deviation of the values in a given array.
|
||||
// @param {Int[] | Float[]} numbers - arrayList of numbers.
|
||||
// @return {Float} - The sample standard deviation.
|
||||
func StandardDeviationSample(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
arr, err := runtime.CastList(args[0])
|
||||
func StandardDeviationSample(ctx context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
arr, err := runtime.CastList(arg)
|
||||
|
||||
if err != nil {
|
||||
return runtime.None, err
|
||||
|
@@ -9,12 +9,8 @@ import (
|
||||
// SUM returns the sum of the values in a given array.
|
||||
// @param {Int[] | Float[]} numbers - arrayList of numbers.
|
||||
// @return {Float} - The sum of the values.
|
||||
func Sum(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
arr, err := runtime.CastList(args[0])
|
||||
func Sum(ctx context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
arr, err := runtime.CastList(arg)
|
||||
|
||||
if err != nil {
|
||||
return runtime.None, err
|
||||
|
@@ -10,14 +10,10 @@ import (
|
||||
// TAN returns the tangent of a given number.
|
||||
// @param {Int | Float} number - A number.
|
||||
// @return {Float} - The tangent.
|
||||
func Tan(_ context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
func Tan(_ context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.AssertNumber(arg); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
if err := runtime.AssertNumber(args[0]); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
return runtime.NewFloat(math.Tan(toFloat(args[0]))), nil
|
||||
return runtime.NewFloat(math.Tan(toFloat(arg))), nil
|
||||
}
|
||||
|
@@ -10,12 +10,8 @@ import (
|
||||
// VARIANCE_POPULATION returns the population variance of the values in a given array.
|
||||
// @param {Int[] | Float[]} numbers - arrayList of numbers.
|
||||
// @return {Float} - The population variance.
|
||||
func PopulationVariance(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
arr, err := runtime.CastList(args[0])
|
||||
func PopulationVariance(ctx context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
arr, err := runtime.CastList(arg)
|
||||
|
||||
if err != nil {
|
||||
return runtime.None, err
|
||||
|
@@ -10,12 +10,8 @@ import (
|
||||
// VARIANCE_SAMPLE returns the sample variance of the values in a given array.
|
||||
// @param {Int[] | Float[]} numbers - arrayList of numbers.
|
||||
// @return {Float} - The sample variance.
|
||||
func SampleVariance(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if err := runtime.ValidateArgs(args, 1, 1); err != nil {
|
||||
return runtime.None, err
|
||||
}
|
||||
|
||||
arr, err := runtime.CastList(args[0])
|
||||
func SampleVariance(ctx context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
arr, err := runtime.CastList(arg)
|
||||
|
||||
if err != nil {
|
||||
return runtime.None, err
|
||||
|
@@ -12,14 +12,14 @@ type (
|
||||
EnvironmentOption func(env *Environment)
|
||||
|
||||
Environment struct {
|
||||
functions map[string]runtime.Function
|
||||
functions runtime.Functions
|
||||
params map[string]runtime.Value
|
||||
logging logging.Options
|
||||
}
|
||||
)
|
||||
|
||||
var noopEnv = &Environment{
|
||||
functions: make(map[string]runtime.Function),
|
||||
functions: runtime.NewFunctions(),
|
||||
params: make(map[string]runtime.Value),
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func newEnvironment(opts []EnvironmentOption) *Environment {
|
||||
}
|
||||
|
||||
env := &Environment{
|
||||
functions: make(map[string]runtime.Function),
|
||||
functions: runtime.NewFunctions(),
|
||||
params: make(map[string]runtime.Value),
|
||||
logging: logging.Options{
|
||||
Writer: os.Stdout,
|
||||
@@ -43,23 +43,3 @@ func newEnvironment(opts []EnvironmentOption) *Environment {
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
func (env *Environment) GetFunction(name string) runtime.Function {
|
||||
return env.functions[name]
|
||||
}
|
||||
|
||||
func (env *Environment) HasFunction(name string) bool {
|
||||
_, exists := env.functions[name]
|
||||
|
||||
return exists
|
||||
}
|
||||
|
||||
func (env *Environment) GetParam(name string) runtime.Value {
|
||||
return env.params[name]
|
||||
}
|
||||
|
||||
func (env *Environment) HasParam(name string) bool {
|
||||
_, exists := env.params[name]
|
||||
|
||||
return exists
|
||||
}
|
||||
|
@@ -20,21 +20,23 @@ func WithParam(name string, value interface{}) EnvironmentOption {
|
||||
}
|
||||
}
|
||||
|
||||
func WithFunctions(functions map[string]runtime.Function) EnvironmentOption {
|
||||
func WithFunctions(functions runtime.Functions) EnvironmentOption {
|
||||
return func(env *Environment) {
|
||||
if env.functions == nil {
|
||||
env.functions = make(map[string]runtime.Function)
|
||||
}
|
||||
|
||||
for name, function := range functions {
|
||||
env.functions[name] = function
|
||||
}
|
||||
env.functions.SetAll(functions)
|
||||
}
|
||||
}
|
||||
|
||||
func WithFunction(name string, function runtime.Function) EnvironmentOption {
|
||||
return func(env *Environment) {
|
||||
env.functions[name] = function
|
||||
env.functions.F().Set(name, function)
|
||||
}
|
||||
}
|
||||
|
||||
func WithFunctionSetter(setter func(fns runtime.Functions)) EnvironmentOption {
|
||||
return func(env *Environment) {
|
||||
if setter != nil {
|
||||
setter(env.functions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
35
pkg/vm/helpers.go
Normal file
35
pkg/vm/helpers.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
)
|
||||
|
||||
func validateParams(env *Environment, program *Program) error {
|
||||
if len(program.Params) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// There might be no errors.
|
||||
// Thus, we allocate this slice lazily, on a first error.
|
||||
var missedParams []string
|
||||
|
||||
for _, n := range program.Params {
|
||||
_, exists := env.params[n]
|
||||
|
||||
if !exists {
|
||||
if missedParams == nil {
|
||||
missedParams = make([]string, 0, len(program.Params))
|
||||
}
|
||||
|
||||
missedParams = append(missedParams, "@"+n)
|
||||
}
|
||||
}
|
||||
|
||||
if len(missedParams) > 0 {
|
||||
return runtime.Error(ErrMissedParam, strings.Join(missedParams, ", "))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@@ -73,6 +73,16 @@ const (
|
||||
// Function Operations
|
||||
OpCall
|
||||
OpProtectedCall
|
||||
OpCall0
|
||||
OpProtectedCall0
|
||||
OpCall1
|
||||
OpProtectedCall1
|
||||
OpCall2
|
||||
OpProtectedCall2
|
||||
OpCall3
|
||||
OpProtectedCall3
|
||||
OpCall4
|
||||
OpProtectedCall4
|
||||
|
||||
// Collection Creation
|
||||
OpList // Create an array
|
||||
|
272
pkg/vm/vm.go
272
pkg/vm/vm.go
@@ -3,7 +3,6 @@ package vm
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"github.com/MontFerret/ferret/pkg/vm/internal"
|
||||
@@ -25,19 +24,9 @@ func New(program *Program) *VM {
|
||||
}
|
||||
|
||||
func (vm *VM) Run(ctx context.Context, opts []EnvironmentOption) (runtime.Value, error) {
|
||||
tryCatch := func(pos int) (Catch, bool) {
|
||||
for _, pair := range vm.program.CatchTable {
|
||||
if pos >= pair[0] && pos <= pair[1] {
|
||||
return pair, true
|
||||
}
|
||||
}
|
||||
|
||||
return Catch{}, false
|
||||
}
|
||||
|
||||
env := newEnvironment(opts)
|
||||
|
||||
if err := vm.validateParams(env); err != nil {
|
||||
if err := validateParams(env, vm.program); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -148,7 +137,7 @@ loop:
|
||||
|
||||
if err == nil {
|
||||
reg[dst] = r.Match(reg[src1])
|
||||
} else if _, catch := tryCatch(vm.pc); catch {
|
||||
} else if _, catch := vm.tryCatch(vm.pc); catch {
|
||||
reg[dst] = runtime.False
|
||||
} else {
|
||||
return nil, err
|
||||
@@ -159,7 +148,7 @@ loop:
|
||||
|
||||
if err == nil {
|
||||
reg[dst] = !r.Match(reg[src1])
|
||||
} else if _, catch := tryCatch(vm.pc); catch {
|
||||
} else if _, catch := vm.tryCatch(vm.pc); catch {
|
||||
reg[dst] = runtime.False
|
||||
} else {
|
||||
return nil, err
|
||||
@@ -250,40 +239,13 @@ loop:
|
||||
}
|
||||
|
||||
case OpCall, OpProtectedCall:
|
||||
fnName := reg[dst].String()
|
||||
fn := vm.env.GetFunction(fnName)
|
||||
|
||||
if fn == nil {
|
||||
if op == OpProtectedCall {
|
||||
reg[dst] = runtime.None
|
||||
continue
|
||||
} else {
|
||||
return nil, runtime.Error(ErrFunctionNotFound, fnName)
|
||||
}
|
||||
}
|
||||
|
||||
var size int
|
||||
|
||||
if src1 > 0 {
|
||||
size = src2.Register() - src1.Register() + 1
|
||||
}
|
||||
|
||||
start := int(src1)
|
||||
end := int(src1) + size
|
||||
args := make([]runtime.Value, size)
|
||||
|
||||
// Iterate over registers starting from src1 and up to the src2
|
||||
for i := start; i < end; i++ {
|
||||
args[i-start] = reg[i]
|
||||
}
|
||||
|
||||
out, err := fn(ctx, args...)
|
||||
out, err := vm.call(ctx, dst, src1, src2)
|
||||
|
||||
if err == nil {
|
||||
reg[dst] = out
|
||||
} else if op == OpProtectedCall {
|
||||
reg[dst] = runtime.None
|
||||
} else if catch, ok := tryCatch(vm.pc); ok {
|
||||
} else if catch, ok := vm.tryCatch(vm.pc); ok {
|
||||
reg[dst] = runtime.None
|
||||
|
||||
if catch[2] > 0 {
|
||||
@@ -292,6 +254,91 @@ loop:
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
case OpCall0, OpProtectedCall0:
|
||||
out, err := vm.call0(ctx, dst)
|
||||
|
||||
if err == nil {
|
||||
reg[dst] = out
|
||||
} else if op == OpProtectedCall0 {
|
||||
reg[dst] = runtime.None
|
||||
} else if catch, ok := vm.tryCatch(vm.pc); ok {
|
||||
reg[dst] = runtime.None
|
||||
|
||||
if catch[2] > 0 {
|
||||
vm.pc = catch[2]
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case OpCall1, OpProtectedCall1:
|
||||
out, err := vm.call1(ctx, dst, src1)
|
||||
|
||||
if err == nil {
|
||||
reg[dst] = out
|
||||
} else if op == OpProtectedCall1 {
|
||||
reg[dst] = runtime.None
|
||||
} else if catch, ok := vm.tryCatch(vm.pc); ok {
|
||||
reg[dst] = runtime.None
|
||||
|
||||
if catch[2] > 0 {
|
||||
vm.pc = catch[2]
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case OpCall2, OpProtectedCall2:
|
||||
out, err := vm.call2(ctx, dst, src1, src2)
|
||||
|
||||
if err == nil {
|
||||
reg[dst] = out
|
||||
} else if op == OpProtectedCall2 {
|
||||
reg[dst] = runtime.None
|
||||
} else if catch, ok := vm.tryCatch(vm.pc); ok {
|
||||
reg[dst] = runtime.None
|
||||
|
||||
if catch[2] > 0 {
|
||||
vm.pc = catch[2]
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case OpCall3, OpProtectedCall3:
|
||||
out, err := vm.call3(ctx, dst, src1, src2)
|
||||
|
||||
if err == nil {
|
||||
reg[dst] = out
|
||||
} else if op == OpProtectedCall3 {
|
||||
reg[dst] = runtime.None
|
||||
} else if catch, ok := vm.tryCatch(vm.pc); ok {
|
||||
reg[dst] = runtime.None
|
||||
|
||||
if catch[2] > 0 {
|
||||
vm.pc = catch[2]
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case OpCall4, OpProtectedCall4:
|
||||
out, err := vm.call4(ctx, dst, src1, src2)
|
||||
|
||||
if err == nil {
|
||||
reg[dst] = out
|
||||
} else if op == OpProtectedCall4 {
|
||||
reg[dst] = runtime.None
|
||||
} else if catch, ok := vm.tryCatch(vm.pc); ok {
|
||||
reg[dst] = runtime.None
|
||||
|
||||
if catch[2] > 0 {
|
||||
vm.pc = catch[2]
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case OpLength:
|
||||
val, ok := reg[src1].(runtime.Measurable)
|
||||
|
||||
@@ -299,7 +346,7 @@ loop:
|
||||
length, err := val.Length(ctx)
|
||||
|
||||
if err != nil {
|
||||
if _, catch := tryCatch(vm.pc); catch {
|
||||
if _, catch := vm.tryCatch(vm.pc); catch {
|
||||
length = 0
|
||||
} else {
|
||||
return nil, err
|
||||
@@ -307,7 +354,7 @@ loop:
|
||||
}
|
||||
|
||||
reg[dst] = length
|
||||
} else if _, catch := tryCatch(vm.pc); catch {
|
||||
} else if _, catch := vm.tryCatch(vm.pc); catch {
|
||||
reg[dst] = runtime.ZeroInt
|
||||
} else {
|
||||
return runtime.None, runtime.TypeErrorOf(reg[src1],
|
||||
@@ -328,7 +375,7 @@ loop:
|
||||
err := val.Close()
|
||||
|
||||
if err != nil {
|
||||
if _, catch := tryCatch(vm.pc); !catch {
|
||||
if _, catch := vm.tryCatch(vm.pc); !catch {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -356,7 +403,7 @@ loop:
|
||||
ds := reg[dst].(runtime.List)
|
||||
|
||||
if err := ds.Add(ctx, reg[src1]); err != nil {
|
||||
if _, catch := tryCatch(vm.pc); catch {
|
||||
if _, catch := vm.tryCatch(vm.pc); catch {
|
||||
continue
|
||||
} else {
|
||||
return nil, err
|
||||
@@ -366,7 +413,7 @@ loop:
|
||||
tr := reg[dst].(internal.Transformer)
|
||||
|
||||
if err := tr.Add(ctx, reg[src1], reg[src2]); err != nil {
|
||||
if _, catch := tryCatch(vm.pc); catch {
|
||||
if _, catch := vm.tryCatch(vm.pc); catch {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -385,7 +432,7 @@ loop:
|
||||
|
||||
reg[dst] = internal.NewIterator(iterator)
|
||||
default:
|
||||
if _, catch := tryCatch(vm.pc); catch {
|
||||
if _, catch := vm.tryCatch(vm.pc); catch {
|
||||
// Fall back to an empty iterator
|
||||
reg[dst] = internal.NoopIter
|
||||
} else {
|
||||
@@ -438,7 +485,7 @@ loop:
|
||||
observable, eventName, options, err := vm.castSubscribeArgs(reg[dst], reg[src1], reg[src2])
|
||||
|
||||
if err != nil {
|
||||
if _, catch := tryCatch(vm.pc); catch {
|
||||
if _, catch := vm.tryCatch(vm.pc); catch {
|
||||
continue
|
||||
} else {
|
||||
return nil, err
|
||||
@@ -451,7 +498,7 @@ loop:
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if _, catch := tryCatch(vm.pc); catch {
|
||||
if _, catch := vm.tryCatch(vm.pc); catch {
|
||||
continue
|
||||
} else {
|
||||
return nil, err
|
||||
@@ -468,7 +515,7 @@ loop:
|
||||
t, err := runtime.CastInt(reg[src1])
|
||||
|
||||
if err != nil {
|
||||
if _, catch := tryCatch(vm.pc); catch {
|
||||
if _, catch := vm.tryCatch(vm.pc); catch {
|
||||
continue
|
||||
} else {
|
||||
return nil, err
|
||||
@@ -483,7 +530,7 @@ loop:
|
||||
dur, err := runtime.ToInt(ctx, reg[dst])
|
||||
|
||||
if err != nil {
|
||||
if _, catch := tryCatch(vm.pc); catch {
|
||||
if _, catch := vm.tryCatch(vm.pc); catch {
|
||||
continue
|
||||
} else {
|
||||
return nil, err
|
||||
@@ -502,32 +549,109 @@ loop:
|
||||
return vm.registers[NoopOperand], nil
|
||||
}
|
||||
|
||||
func (vm *VM) validateParams(env *Environment) error {
|
||||
if len(vm.program.Params) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// There might be no errors.
|
||||
// Thus, we allocate this slice lazily, on a first error.
|
||||
var missedParams []string
|
||||
|
||||
for _, n := range vm.program.Params {
|
||||
_, exists := env.params[n]
|
||||
|
||||
if !exists {
|
||||
if missedParams == nil {
|
||||
missedParams = make([]string, 0, len(vm.program.Params))
|
||||
}
|
||||
|
||||
missedParams = append(missedParams, "@"+n)
|
||||
func (vm *VM) tryCatch(pos int) (Catch, bool) {
|
||||
for _, pair := range vm.program.CatchTable {
|
||||
if pos >= pair[0] && pos <= pair[1] {
|
||||
return pair, true
|
||||
}
|
||||
}
|
||||
|
||||
if len(missedParams) > 0 {
|
||||
return runtime.Error(ErrMissedParam, strings.Join(missedParams, ", "))
|
||||
return Catch{}, false
|
||||
}
|
||||
|
||||
func (vm *VM) call(ctx context.Context, dst, src1, src2 Operand) (runtime.Value, error) {
|
||||
fnName := vm.registers[dst].String()
|
||||
fn, found := vm.env.functions.F().Get(fnName)
|
||||
|
||||
if !found {
|
||||
return nil, runtime.Error(ErrFunctionNotFound, fnName)
|
||||
}
|
||||
|
||||
return nil
|
||||
var size int
|
||||
|
||||
if src1 > 0 {
|
||||
size = src2.Register() - src1.Register() + 1
|
||||
}
|
||||
|
||||
start := int(src1)
|
||||
end := int(src1) + size
|
||||
args := make([]runtime.Value, size)
|
||||
|
||||
// Iterate over registers starting from src1 and up to the src2
|
||||
for i := start; i < end; i++ {
|
||||
args[i-start] = vm.registers[i]
|
||||
}
|
||||
|
||||
return fn(ctx, args...)
|
||||
}
|
||||
|
||||
func (vm *VM) call0(ctx context.Context, dst Operand) (runtime.Value, error) {
|
||||
fnName := vm.registers[dst].String()
|
||||
fn, found := vm.env.functions.F0().Get(fnName)
|
||||
|
||||
if found {
|
||||
return fn(ctx)
|
||||
}
|
||||
|
||||
// Fall back to a variadic function call
|
||||
return vm.call(ctx, dst, NoopOperand, NoopOperand)
|
||||
}
|
||||
|
||||
func (vm *VM) call1(ctx context.Context, dst, src1 Operand) (runtime.Value, error) {
|
||||
fnName := vm.registers[dst].String()
|
||||
fn, found := vm.env.functions.F1().Get(fnName)
|
||||
|
||||
if found {
|
||||
return fn(ctx, vm.registers[src1])
|
||||
}
|
||||
|
||||
// Fall back to a variadic function call
|
||||
return vm.call(ctx, dst, src1, src1)
|
||||
}
|
||||
|
||||
func (vm *VM) call2(ctx context.Context, dst, src1, src2 Operand) (runtime.Value, error) {
|
||||
fnName := vm.registers[dst].String()
|
||||
fn, found := vm.env.functions.F2().Get(fnName)
|
||||
|
||||
if found {
|
||||
return fn(ctx, vm.registers[src1], vm.registers[src2])
|
||||
}
|
||||
|
||||
// Fall back to a variadic function call
|
||||
return vm.call(ctx, dst, src1, src2)
|
||||
}
|
||||
|
||||
func (vm *VM) call3(ctx context.Context, dst, src1, src2 Operand) (runtime.Value, error) {
|
||||
fnName := vm.registers[dst].String()
|
||||
fn, found := vm.env.functions.F3().Get(fnName)
|
||||
|
||||
if found {
|
||||
arg1 := vm.registers[src1]
|
||||
arg2 := vm.registers[src1+1]
|
||||
arg3 := vm.registers[src1+2]
|
||||
|
||||
return fn(ctx, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// Fall back to a variadic function call
|
||||
return vm.call(ctx, dst, src1, src2)
|
||||
}
|
||||
|
||||
func (vm *VM) call4(ctx context.Context, dst, src1, src2 Operand) (runtime.Value, error) {
|
||||
fnName := vm.registers[dst].String()
|
||||
fn, found := vm.env.functions.F4().Get(fnName)
|
||||
|
||||
if found {
|
||||
arg1 := vm.registers[src1]
|
||||
arg2 := vm.registers[src1+1]
|
||||
arg3 := vm.registers[src1+2]
|
||||
arg4 := vm.registers[src1+3]
|
||||
|
||||
return fn(ctx, arg1, arg2, arg3, arg4)
|
||||
}
|
||||
|
||||
// Fall back to a variadic function call
|
||||
return vm.call(ctx, dst, src1, src2)
|
||||
}
|
||||
|
||||
func (vm *VM) loadIndex(ctx context.Context, src, arg runtime.Value) (runtime.Value, error) {
|
||||
|
@@ -16,7 +16,7 @@ func RunBenchmarkWith(b *testing.B, c *compiler.Compiler, expression string, opt
|
||||
}
|
||||
|
||||
options := []vm.EnvironmentOption{
|
||||
vm.WithFunctions(c.Functions().Unwrap()),
|
||||
vm.WithFunctions(c.Functions()),
|
||||
}
|
||||
options = append(options, opts...)
|
||||
|
||||
@@ -25,7 +25,7 @@ func RunBenchmarkWith(b *testing.B, c *compiler.Compiler, expression string, opt
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
for b.Loop() {
|
||||
_, err := instance.Run(ctx, opts)
|
||||
|
||||
if err != nil {
|
||||
|
17
test/integration/benchmarks/bench_for_sort_test.go
Normal file
17
test/integration/benchmarks/bench_for_sort_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package benchmarks_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/MontFerret/ferret/test/integration/base"
|
||||
)
|
||||
|
||||
func BenchmarkForSort(b *testing.B) {
|
||||
RunBenchmark(b, `
|
||||
LET strs = ["foo", "bar", "qaz", "abc"]
|
||||
|
||||
FOR s IN strs
|
||||
SORT s
|
||||
RETURN s
|
||||
`)
|
||||
}
|
119
test/integration/benchmarks/bench_func_test.go
Normal file
119
test/integration/benchmarks/bench_func_test.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package benchmarks_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"github.com/MontFerret/ferret/pkg/vm"
|
||||
|
||||
. "github.com/MontFerret/ferret/test/integration/base"
|
||||
)
|
||||
|
||||
func BenchmarkFunctionCall(b *testing.B) {
|
||||
RunBenchmark(b, `
|
||||
RETURN TEST(1,2,3,4,5,6)
|
||||
`, vm.WithFunction("TEST", func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
return runtime.True, nil
|
||||
}))
|
||||
}
|
||||
|
||||
func BenchmarkFunctionCall0(b *testing.B) {
|
||||
RunBenchmark(b, `
|
||||
RETURN TEST()
|
||||
`, vm.WithFunctions(runtime.NewFunctionsBuilder().
|
||||
Set0("TEST", func(ctx context.Context) (runtime.Value, error) {
|
||||
return runtime.String("test0"), nil
|
||||
}).
|
||||
Build()))
|
||||
}
|
||||
|
||||
func BenchmarkFunctionCall0Fallback(b *testing.B) {
|
||||
RunBenchmark(b, `
|
||||
RETURN TEST()
|
||||
`, vm.WithFunctions(runtime.NewFunctionsBuilder().
|
||||
Set("TEST", func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
return runtime.String("test"), nil
|
||||
}).
|
||||
Build()))
|
||||
}
|
||||
|
||||
func BenchmarkFunctionCall1(b *testing.B) {
|
||||
RunBenchmark(b, `
|
||||
RETURN TEST(1)
|
||||
`, vm.WithFunctions(runtime.NewFunctionsBuilder().
|
||||
Set1("TEST", func(ctx context.Context, arg runtime.Value) (runtime.Value, error) {
|
||||
return runtime.String("test"), nil
|
||||
}).
|
||||
Build()))
|
||||
}
|
||||
|
||||
func BenchmarkFunctionCall1Fallback(b *testing.B) {
|
||||
RunBenchmark(b, `
|
||||
RETURN TEST(1)
|
||||
`, vm.WithFunctions(runtime.NewFunctionsBuilder().
|
||||
Set("TEST", func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
return runtime.String("test"), nil
|
||||
}).
|
||||
Build()))
|
||||
}
|
||||
|
||||
func BenchmarkFunctionCall2(b *testing.B) {
|
||||
RunBenchmark(b, `
|
||||
RETURN TEST(1, 1)
|
||||
`, vm.WithFunctions(runtime.NewFunctionsBuilder().
|
||||
Set2("TEST", func(ctx context.Context, arg1, arg2 runtime.Value) (runtime.Value, error) {
|
||||
return runtime.String("test"), nil
|
||||
}).
|
||||
Build()))
|
||||
}
|
||||
|
||||
func BenchmarkFunctionCall2Fallback(b *testing.B) {
|
||||
RunBenchmark(b, `
|
||||
RETURN TEST(1, 1)
|
||||
`, vm.WithFunctions(runtime.NewFunctionsBuilder().
|
||||
Set("TEST", func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
return runtime.String("test"), nil
|
||||
}).
|
||||
Build()))
|
||||
}
|
||||
|
||||
func BenchmarkFunctionCall3(b *testing.B) {
|
||||
RunBenchmark(b, `
|
||||
RETURN TEST(1, 1, 1)
|
||||
`, vm.WithFunctions(runtime.NewFunctionsBuilder().
|
||||
Set3("TEST", func(ctx context.Context, arg1, arg2, arg3 runtime.Value) (runtime.Value, error) {
|
||||
return runtime.String("test"), nil
|
||||
}).
|
||||
Build()))
|
||||
}
|
||||
|
||||
func BenchmarkFunctionCall3Fallback(b *testing.B) {
|
||||
RunBenchmark(b, `
|
||||
RETURN TEST(1, 1, 1)
|
||||
`, vm.WithFunctions(runtime.NewFunctionsBuilder().
|
||||
Set("TEST", func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
return runtime.String("test"), nil
|
||||
}).
|
||||
Build()))
|
||||
}
|
||||
|
||||
func BenchmarkFunctionCall4(b *testing.B) {
|
||||
RunBenchmark(b, `
|
||||
RETURN TEST(1, 1, 1, 1)
|
||||
`, vm.WithFunctions(runtime.NewFunctionsBuilder().
|
||||
Set4("TEST", func(ctx context.Context, arg1, arg2, arg3, arg4 runtime.Value) (runtime.Value, error) {
|
||||
return runtime.String("test"), nil
|
||||
}).
|
||||
Build()))
|
||||
}
|
||||
|
||||
func BenchmarkFunctionCall4Fallback(b *testing.B) {
|
||||
RunBenchmark(b, `
|
||||
RETURN TEST(1, 1, 1, 1)
|
||||
`, vm.WithFunctions(runtime.NewFunctionsBuilder().
|
||||
Set("TEST", func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
return runtime.String("test"), nil
|
||||
}).
|
||||
Build()))
|
||||
}
|
@@ -1,182 +0,0 @@
|
||||
package benchmarks_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkStringLiteral(b *testing.B) {
|
||||
test.RunBenchmark(b, `
|
||||
RETURN "
|
||||
FOO
|
||||
BAR
|
||||
"
|
||||
`)
|
||||
}
|
||||
|
||||
func BenchmarkEmptyArray(b *testing.B) {
|
||||
test.RunBenchmark(b, `RETURN []`)
|
||||
}
|
||||
|
||||
func BenchmarkStaticArray(b *testing.B) {
|
||||
test.RunBenchmark(b, `RETURN [1,2,3,4,5,6,7,8,9,10]`)
|
||||
}
|
||||
|
||||
func BenchmarkEmptyObject(b *testing.B) {
|
||||
test.RunBenchmark(b, `RETURN {}`)
|
||||
}
|
||||
|
||||
func BenchmarkUnaryOperatorExcl(b *testing.B) {
|
||||
test.RunBenchmark(b, `RETURN !TRUE`)
|
||||
}
|
||||
|
||||
func BenchmarkUnaryOperatorQ(b *testing.B) {
|
||||
test.RunBenchmark(b, `
|
||||
LET foo = TRUE
|
||||
RETURN !foo ? TRUE : FALSE
|
||||
`)
|
||||
}
|
||||
|
||||
func BenchmarkUnaryOperatorN(b *testing.B) {
|
||||
test.RunBenchmark(b, `
|
||||
LET v = 1
|
||||
RETURN -v
|
||||
`)
|
||||
}
|
||||
|
||||
func BenchmarkTernaryOperator(b *testing.B) {
|
||||
test.RunBenchmark(b, `
|
||||
LET a = "a"
|
||||
LET b = "b"
|
||||
LET c = FALSE
|
||||
RETURN c ? a : b;
|
||||
|
||||
`)
|
||||
}
|
||||
|
||||
func BenchmarkTernaryOperatorDef(b *testing.B) {
|
||||
test.RunBenchmark(b, `
|
||||
LET a = "a"
|
||||
LET b = "b"
|
||||
LET c = FALSE
|
||||
RETURN c ? : a;
|
||||
|
||||
`)
|
||||
}
|
||||
|
||||
func BenchmarkForEmpty(b *testing.B) {
|
||||
test.RunBenchmark(b, `
|
||||
FOR i IN []
|
||||
RETURN i
|
||||
`)
|
||||
}
|
||||
|
||||
func BenchmarkForStaticArray(b *testing.B) {
|
||||
test.RunBenchmark(b, `
|
||||
FOR i IN [1,2,3,4,5,6,7,8,9,10]
|
||||
RETURN i
|
||||
`)
|
||||
}
|
||||
|
||||
func BenchmarkForRange(b *testing.B) {
|
||||
test.RunBenchmark(b, `
|
||||
FOR i IN 1..10
|
||||
RETURN i
|
||||
`)
|
||||
}
|
||||
|
||||
func BenchmarkForObject(b *testing.B) {
|
||||
test.RunBenchmark(b, `
|
||||
FOR i IN {"1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9":9, "10":10}
|
||||
RETURN i
|
||||
`)
|
||||
}
|
||||
|
||||
func BenchmarkForNested(b *testing.B) {
|
||||
test.RunBenchmark(b, `
|
||||
FOR prop IN ["a"]
|
||||
FOR val IN [1, 2, 3]
|
||||
RETURN {[prop]: val}
|
||||
`)
|
||||
}
|
||||
|
||||
func BenchmarkForTernary(b *testing.B) {
|
||||
test.RunBenchmark(b, `
|
||||
LET foo = FALSE
|
||||
RETURN foo ? TRUE : (FOR i IN 1..5 RETURN i*2)
|
||||
`)
|
||||
}
|
||||
|
||||
func BenchmarkForSort(b *testing.B) {
|
||||
test.RunBenchmark(b, `
|
||||
LET users = [
|
||||
{
|
||||
active: true,
|
||||
age: 31,
|
||||
gender: "m"
|
||||
},
|
||||
{
|
||||
active: true,
|
||||
age: 29,
|
||||
gender: "f"
|
||||
},
|
||||
{
|
||||
active: true,
|
||||
age: 36,
|
||||
gender: "m"
|
||||
}
|
||||
]
|
||||
FOR u IN users
|
||||
SORT u.age
|
||||
RETURN u
|
||||
`)
|
||||
}
|
||||
|
||||
func BenchmarkForSort2(b *testing.B) {
|
||||
test.RunBenchmark(b, `
|
||||
LET users = [
|
||||
{
|
||||
active: true,
|
||||
age: 31,
|
||||
gender: "m"
|
||||
},
|
||||
{
|
||||
active: true,
|
||||
age: 29,
|
||||
gender: "f"
|
||||
},
|
||||
{
|
||||
active: true,
|
||||
age: 36,
|
||||
gender: "m"
|
||||
}
|
||||
]
|
||||
FOR u IN users
|
||||
SORT u.age, u.gender
|
||||
RETURN u
|
||||
`)
|
||||
}
|
||||
|
||||
func BenchmarkForSortDesc(b *testing.B) {
|
||||
test.RunBenchmark(b, `
|
||||
LET users = [
|
||||
{
|
||||
active: true,
|
||||
age: 31,
|
||||
gender: "m"
|
||||
},
|
||||
{
|
||||
active: true,
|
||||
age: 29,
|
||||
gender: "f"
|
||||
},
|
||||
{
|
||||
active: true,
|
||||
age: 36,
|
||||
gender: "m"
|
||||
}
|
||||
]
|
||||
FOR u IN users
|
||||
SORT u.age DESC
|
||||
RETURN u
|
||||
`)
|
||||
}
|
@@ -123,7 +123,7 @@ func RunUseCasesWith(t *testing.T, c *compiler.Compiler, useCases []UseCase, opt
|
||||
}
|
||||
|
||||
options := []vm.EnvironmentOption{
|
||||
vm.WithFunctions(c.Functions().Unwrap()),
|
||||
vm.WithFunctions(c.Functions()),
|
||||
}
|
||||
options = append(options, opts...)
|
||||
|
||||
|
@@ -272,7 +272,7 @@ LET users = [
|
||||
COUNT_B()
|
||||
RETURN i + x
|
||||
`, []any{5, 6, 5}),
|
||||
}, vm.WithFunctions(map[string]runtime.Function{
|
||||
}, vm.WithFunctions(runtime.NewFunctionsFromMap(map[string]runtime.Function{
|
||||
"COUNT_A": func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
counterA++
|
||||
|
||||
@@ -283,5 +283,5 @@ LET users = [
|
||||
|
||||
return runtime.None, nil
|
||||
},
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
@@ -1,8 +1,12 @@
|
||||
package vm_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime"
|
||||
"github.com/MontFerret/ferret/pkg/vm"
|
||||
|
||||
. "github.com/MontFerret/ferret/test/integration/base"
|
||||
)
|
||||
|
||||
@@ -37,6 +41,20 @@ func TestFunctionCall(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestFunctionCall0(t *testing.T) {
|
||||
RunUseCases(t, []UseCase{
|
||||
Case("RETURN TEST0()", "test0", "Should call a function with no arguments"),
|
||||
Case("RETURN TEST()", "test", "Should call a function with no arguments using fallback"),
|
||||
}, vm.WithFunctions(runtime.NewFunctionsBuilder().
|
||||
Set("TEST", func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
return runtime.String("test"), nil
|
||||
}).
|
||||
Set0("TEST0", func(ctx context.Context) (runtime.Value, error) {
|
||||
return runtime.String("test0"), nil
|
||||
}).
|
||||
Build()))
|
||||
}
|
||||
|
||||
func TestBuiltinFunctions(t *testing.T) {
|
||||
RunUseCases(t, []UseCase{
|
||||
Case("RETURN LENGTH([1,2,3])", 3),
|
||||
|
@@ -182,7 +182,7 @@ func TestMemberReservedWords(t *testing.T) {
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
out, err := Exec(prog, true, vm.WithFunctions(c.Functions().Unwrap()))
|
||||
out, err := Exec(prog, true, vm.WithFunctions(c.Functions()))
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(out, ShouldEqual, expected.String())
|
||||
|
@@ -29,7 +29,7 @@ func TestForDoWhile(t *testing.T) {
|
||||
FOR x IN 1..y
|
||||
RETURN i * x
|
||||
`, []any{0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 2, 4, 6, 8, 0, 3, 6, 9, 12, 0, 4, 8, 12, 16}),
|
||||
}, vm.WithFunctions(map[string]runtime.Function{
|
||||
}, vm.WithFunctions(runtime.NewFunctionsFromMap(map[string]runtime.Function{
|
||||
"COUNTER": func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
counter++
|
||||
return runtime.NewInt(counter), nil
|
||||
@@ -38,5 +38,5 @@ func TestForDoWhile(t *testing.T) {
|
||||
counter2++
|
||||
return runtime.NewInt(counter), nil
|
||||
},
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
@@ -26,10 +26,10 @@ func TestForTernaryWhileExpression(t *testing.T) {
|
||||
LET foo = FALSE
|
||||
RETURN foo ? TRUE : (FOR i WHILE COUNTER() < 10 RETURN i*2)`,
|
||||
[]any{0, 2, 4, 6, 8, 10, 12, 14, 16, 18}),
|
||||
}, vm.WithFunctions(map[string]runtime.Function{
|
||||
}, vm.WithFunctions(runtime.NewFunctionsFromMap(map[string]runtime.Function{
|
||||
"COUNTER": func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
counter++
|
||||
return runtime.NewInt(counter), nil
|
||||
},
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@ func TestForWhile(t *testing.T) {
|
||||
FOR x IN 1..y
|
||||
RETURN i * x
|
||||
`, []any{0, 1, 2, 2, 4, 6, 3, 6, 9, 12, 4, 8, 12, 16, 20}),
|
||||
}, vm.WithFunctions(map[string]runtime.Function{
|
||||
}, vm.WithFunctions(runtime.NewFunctionsFromMap(map[string]runtime.Function{
|
||||
"UNTIL": func(ctx context.Context, args ...runtime.Value) (runtime.Value, error) {
|
||||
if untilCounter < int(runtime.ToIntSafe(ctx, args[0])) {
|
||||
untilCounter++
|
||||
@@ -37,5 +37,5 @@ func TestForWhile(t *testing.T) {
|
||||
counter++
|
||||
return runtime.NewInt(counter), nil
|
||||
},
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
Reference in New Issue
Block a user