From 90aafc8bb403baf8d87bafda0354aa829c27e964 Mon Sep 17 00:00:00 2001 From: Daniel Kang Date: Sun, 3 Feb 2019 15:50:40 -0800 Subject: [PATCH] Add 'is_function' and 'is_callable' builtin functions --- docs/builtins.md | 24 ++++++++++++++++-------- objects/builtin_type_checks.go | 26 ++++++++++++++++++++++++++ objects/builtins.go | 8 ++++++++ runtime/vm_builtin_test.go | 21 +++++++++++++++++++++ 4 files changed, 71 insertions(+), 8 deletions(-) diff --git a/docs/builtins.md b/docs/builtins.md index 68b3ba1..a4f8c6b 100644 --- a/docs/builtins.md +++ b/docs/builtins.md @@ -185,32 +185,40 @@ v := bytes(100) ## is_string -Returns `true` if the object is string. Or it returns `false`. +Returns `true` if the object's type is string. Or it returns `false`. ## is_int -Returns `true` if the object is int. Or it returns `false`. +Returns `true` if the object's type is int. Or it returns `false`. ## is_bool -Returns `true` if the object is bool. Or it returns `false`. +Returns `true` if the object's type is bool. Or it returns `false`. ## is_float -Returns `true` if the object is float. Or it returns `false`. +Returns `true` if the object's type is float. Or it returns `false`. ## is_char -Returns `true` if the object is char. Or it returns `false`. +Returns `true` if the object's type is char. Or it returns `false`. ## is_bytes -Returns `true` if the object is bytes. Or it returns `false`. +Returns `true` if the object's type is bytes. Or it returns `false`. ## is_error -Returns `true` if the object is error. Or it returns `false`. +Returns `true` if the object's type is error. Or it returns `false`. ## is_undefined -Returns `true` if the object is undefined. Or it returns `false`. \ No newline at end of file +Returns `true` if the object's type is undefined. Or it returns `false`. + +## is_function + +Returns `true` if the object's type is function or closure. Or it returns `false`. Note that `is_function` returns `false` for builtin functions and user-provided callable objects. + +## is_callable + +Returns `true` if the object is callable (e.g. function, closure, builtin function, or user-provided callable objects). Or it returns `false`. \ No newline at end of file diff --git a/objects/builtin_type_checks.go b/objects/builtin_type_checks.go index e39cc9f..960f782 100644 --- a/objects/builtin_type_checks.go +++ b/objects/builtin_type_checks.go @@ -155,3 +155,29 @@ func builtinIsUndefined(args ...Object) (Object, error) { return FalseValue, nil } + +func builtinIsFunction(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + + switch args[0].(type) { + case *CompiledFunction, *Closure: + return TrueValue, nil + } + + return FalseValue, nil +} + +func builtinIsCallable(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + + switch args[0].(type) { + case *CompiledFunction, *Closure, Callable: // BuiltinFunction is Callable + return TrueValue, nil + } + + return FalseValue, nil +} diff --git a/objects/builtins.go b/objects/builtins.go index 30049e9..6755393 100644 --- a/objects/builtins.go +++ b/objects/builtins.go @@ -112,6 +112,14 @@ var Builtins = []NamedBuiltinFunc{ Name: "is_undefined", Func: builtinIsUndefined, }, + { + Name: "is_function", + Func: builtinIsFunction, + }, + { + Name: "is_callable", + Func: builtinIsCallable, + }, { Name: "to_json", Func: builtinToJSON, diff --git a/runtime/vm_builtin_test.go b/runtime/vm_builtin_test.go index 0eef432..841f789 100644 --- a/runtime/vm_builtin_test.go +++ b/runtime/vm_builtin_test.go @@ -172,4 +172,25 @@ func TestBuiltinFunction(t *testing.T) { expect(t, `out = type_name(bytes( 1))`, "bytes") expect(t, `out = type_name(undefined)`, "undefined") expect(t, `out = type_name(error("err"))`, "error") + expect(t, `out = type_name(func() {})`, "compiled-function") + expect(t, `a := func(x) { return func() { return x } }; out = type_name(a(5))`, "closure") // closure + + // is_function + expect(t, `out = is_function(1)`, false) + expect(t, `out = is_function(func() {})`, true) + expect(t, `out = is_function(func(x) { return x })`, true) + expect(t, `out = is_function(len)`, false) // builtin function + expect(t, `a := func(x) { return func() { return x } }; out = is_function(a)`, true) // function + expect(t, `a := func(x) { return func() { return x } }; out = is_function(a(5))`, true) // closure + expectWithSymbols(t, `out = is_function(x)`, false, SYM{"x": &StringArray{Value: []string{"foo", "bar"}}}) // user object + + // is_callable + expect(t, `out = is_callable(1)`, false) + expect(t, `out = is_callable(func() {})`, true) + expect(t, `out = is_callable(func(x) { return x })`, true) + expect(t, `out = is_callable(len)`, true) // builtin function + expect(t, `a := func(x) { return func() { return x } }; out = is_callable(a)`, true) // function + expect(t, `a := func(x) { return func() { return x } }; out = is_callable(a(5))`, true) // closure + expectWithSymbols(t, `out = is_callable(x)`, true, SYM{"x": &StringArray{Value: []string{"foo", "bar"}}}) // user object + }