mirror of
https://github.com/ChaiScript/ChaiScript.git
synced 2026-04-30 19:09:26 +08:00
Merge pull request #637 from leftibot/fix/issue-12-document-reflection
[WIP] Issue #12 — Document reflection
This commit is contained in:
commit
2e2740f90d
120
cheatsheet.md
120
cheatsheet.md
@ -593,6 +593,126 @@ use("filename") // evals file exactly once and returns value of last statement
|
||||
|
||||
Both `use` and `eval_file` search the 'usepaths' passed to the ChaiScript constructor
|
||||
|
||||
## Reflection and Introspection
|
||||
|
||||
ChaiScript provides built-in reflection capabilities for inspecting types, functions, and objects at runtime.
|
||||
|
||||
### Type Inspection
|
||||
|
||||
```
|
||||
type_name(x) // returns the type name of a value as a string
|
||||
is_type(x, "typename") // returns true if x is of the named type
|
||||
type("typename") // returns a Type_Info object for the named type
|
||||
|
||||
// Examples
|
||||
type_name(1) // "int"
|
||||
type_name("hello") // "string"
|
||||
is_type(1, "int") // true
|
||||
is_type(1, "string") // false
|
||||
```
|
||||
|
||||
### Object Inspection Methods
|
||||
|
||||
Every object in ChaiScript supports these methods:
|
||||
|
||||
```
|
||||
x.get_type_info() // returns a Type_Info object for the value
|
||||
x.is_type("string") // returns true if x is of the named type
|
||||
x.is_type(string_type) // returns true if x matches the Type_Info
|
||||
x.is_var_const() // returns true if x is immutable
|
||||
x.is_var_null() // returns true if x is a null pointer
|
||||
x.is_var_pointer() // returns true if x is stored as a pointer
|
||||
x.is_var_reference() // returns true if x is stored as a reference
|
||||
x.is_var_undef() // returns true if x is undefined
|
||||
```
|
||||
|
||||
### Type_Info
|
||||
|
||||
`Type_Info` objects describe a type. You can get them via `type("typename")` or `x.get_type_info()`.
|
||||
|
||||
```
|
||||
var ti = type("int")
|
||||
ti.name() // ChaiScript registered name, e.g. "int"
|
||||
ti.cpp_name() // mangled C++ type name
|
||||
ti.cpp_bare_name() // C++ name without const/pointer/reference
|
||||
ti.bare_equal(other) // true if types match ignoring const/ptr/ref
|
||||
ti.is_type_const() // true if type is const
|
||||
ti.is_type_reference() // true if type is a reference
|
||||
ti.is_type_void() // true if type is void
|
||||
ti.is_type_undef() // true if type is undefined
|
||||
ti.is_type_pointer() // true if type is a pointer
|
||||
ti.is_type_arithmetic() // true if type is arithmetic (int, double, etc.)
|
||||
```
|
||||
|
||||
Built-in type constants are available: `int_type`, `double_type`, `string_type`, `bool_type`, `Object_type`, `Function_type`, `vector_type`, `map_type`.
|
||||
|
||||
### Function Introspection
|
||||
|
||||
Function objects support these introspection methods:
|
||||
|
||||
```
|
||||
f.get_arity() // number of parameters (-1 for variadic)
|
||||
f.get_param_types() // Vector of Type_Info (first element is return type)
|
||||
f.get_contained_functions() // Vector of overloaded functions (empty if not a conglomerate)
|
||||
f.has_guard() // true if the function has a guard condition
|
||||
f.get_guard() // returns the guard function (throws if none)
|
||||
f.get_annotation() // returns the annotation description
|
||||
f.call([param1, param2]) // call the function with a vector of parameters
|
||||
|
||||
// Examples
|
||||
def my_func(a, b) { return a + b; }
|
||||
my_func.get_arity() // 2
|
||||
my_func.has_guard() // false
|
||||
|
||||
def guarded(x) : x > 0 { return x; }
|
||||
guarded.has_guard() // true
|
||||
guarded.get_guard().get_arity() // 1
|
||||
|
||||
// Calling functions dynamically
|
||||
`+`.call([1, 2]) // 3
|
||||
```
|
||||
|
||||
### System Introspection
|
||||
|
||||
```
|
||||
get_functions() // returns a Map of all registered functions (name -> function)
|
||||
get_objects() // returns a Map of all scripting objects (name -> value)
|
||||
function_exists("f") // returns true if a function named "f" is registered
|
||||
call_exists(`f`, args) // returns true if f can be called with the given args
|
||||
dump_system() // prints all registered functions to stdout
|
||||
dump_object(x) // prints information about a value to stdout
|
||||
|
||||
// Examples
|
||||
var funcs = get_functions()
|
||||
funcs["print"] // the print function object
|
||||
function_exists("print") // true
|
||||
call_exists(`+`, 1, 2) // true
|
||||
```
|
||||
|
||||
### Dynamic_Object Reflection
|
||||
|
||||
ChaiScript-defined classes are Dynamic_Objects internally. They support:
|
||||
|
||||
```
|
||||
obj.get_type_name() // returns the ChaiScript class name (e.g. "MyClass")
|
||||
obj.get_attrs() // returns a Map of all attributes
|
||||
obj.has_attr("name") // returns true if the attribute exists
|
||||
obj.get_attr("name") // returns the value of the attribute
|
||||
obj.set_explicit(true) // disables dynamic attribute creation
|
||||
obj.is_explicit() // returns true if explicit mode is enabled
|
||||
|
||||
// Example
|
||||
class MyClass {
|
||||
var x
|
||||
def MyClass() { this.x = 10; }
|
||||
}
|
||||
var m = MyClass()
|
||||
m.get_type_name() // "MyClass"
|
||||
m.get_attrs() // map containing "x" -> 10
|
||||
type_name(m) // "Dynamic_Object" (the underlying C++ type)
|
||||
m.is_type("MyClass") // true (checks the ChaiScript class name)
|
||||
```
|
||||
|
||||
## JSON
|
||||
|
||||
* `from_json` converts a JSON string into its strongly typed (map, vector, int, double, string) representations
|
||||
|
||||
@ -426,6 +426,9 @@ namespace ChaiScript_Language {
|
||||
/// \brief Returns true if the type is "void"
|
||||
bool is_type_void() const;
|
||||
|
||||
/// \brief Returns true if the type is an arithmetic type (int, double, etc.)
|
||||
bool is_type_arithmetic() const;
|
||||
|
||||
/// \brief Returns the ChaiScript registered name for the type if one exists.
|
||||
string name() const;
|
||||
};
|
||||
@ -764,6 +767,90 @@ namespace ChaiScript_Language {
|
||||
/// \endcode
|
||||
bool call_exists(Function f, ...);
|
||||
|
||||
/// \brief Returns the type name of the given object as a string
|
||||
///
|
||||
/// Example:
|
||||
/// \code
|
||||
/// eval> type_name(1)
|
||||
/// int
|
||||
/// eval> type_name("hello")
|
||||
/// string
|
||||
/// \endcode
|
||||
///
|
||||
/// \sa Type_Info::name()
|
||||
/// \sa Object::get_type_info()
|
||||
string type_name(Object o);
|
||||
|
||||
/// \brief Returns true if the object is of the named type
|
||||
///
|
||||
/// Example:
|
||||
/// \code
|
||||
/// eval> is_type(1, "int")
|
||||
/// true
|
||||
/// eval> is_type(1, "string")
|
||||
/// false
|
||||
/// \endcode
|
||||
///
|
||||
/// \sa Object::is_type()
|
||||
bool is_type(Object o, string type_name);
|
||||
|
||||
/// \brief Returns true if a function with the given name is registered
|
||||
///
|
||||
/// Example:
|
||||
/// \code
|
||||
/// eval> function_exists("print")
|
||||
/// true
|
||||
/// eval> function_exists("nonexistent")
|
||||
/// false
|
||||
/// \endcode
|
||||
bool function_exists(string name);
|
||||
|
||||
/// \brief Returns a Map of all registered functions, keyed by function name
|
||||
///
|
||||
/// Example:
|
||||
/// \code
|
||||
/// eval> var funcs = get_functions()
|
||||
/// eval> funcs["print"].get_arity()
|
||||
/// 1
|
||||
/// \endcode
|
||||
///
|
||||
/// \sa Function
|
||||
Map get_functions();
|
||||
|
||||
/// \brief Returns a Map of all scripting objects (variables), keyed by name
|
||||
///
|
||||
/// Example:
|
||||
/// \code
|
||||
/// eval> var x = 42
|
||||
/// eval> var objs = get_objects()
|
||||
/// eval> objs.count("x")
|
||||
/// 1
|
||||
/// \endcode
|
||||
Map get_objects();
|
||||
|
||||
/// \brief Returns a Type_Info for the named type
|
||||
///
|
||||
/// Example:
|
||||
/// \code
|
||||
/// eval> type("int").name()
|
||||
/// int
|
||||
/// \endcode
|
||||
///
|
||||
/// \param type_name The name of the type to look up
|
||||
/// \param throw_on_fail If true (default), throws if the type is not found
|
||||
/// \sa Type_Info
|
||||
Type_Info type(string type_name, bool throw_on_fail = true);
|
||||
|
||||
/// \brief Prints all registered functions to stdout
|
||||
///
|
||||
/// Useful for debugging. Outputs a list of all functions registered in the system.
|
||||
void dump_system();
|
||||
|
||||
/// \brief Prints information about the given object to stdout
|
||||
///
|
||||
/// Useful for debugging. Outputs the type and value of the object.
|
||||
void dump_object(Object o);
|
||||
|
||||
/// \brief Reverses a Range object so that the elements are accessed in reverse
|
||||
Range retro(Range);
|
||||
|
||||
|
||||
123
unittests/reflection_documentation.chai
Normal file
123
unittests/reflection_documentation.chai
Normal file
@ -0,0 +1,123 @@
|
||||
|
||||
// Tests for ChaiScript reflection / introspection capabilities
|
||||
// Ensures all documented reflection functions work correctly
|
||||
|
||||
// --- Global reflection functions ---
|
||||
|
||||
// type_name: returns the type name of a value
|
||||
assert_equal("int", type_name(1))
|
||||
assert_equal("string", type_name("hello"))
|
||||
assert_equal("bool", type_name(true))
|
||||
assert_equal("double", type_name(1.0))
|
||||
|
||||
// is_type: checks if a value is of the given type
|
||||
assert_true(is_type(1, "int"))
|
||||
assert_true(is_type("hello", "string"))
|
||||
assert_false(is_type(1, "string"))
|
||||
|
||||
// function_exists: checks if a named function is registered
|
||||
assert_true(function_exists("print"))
|
||||
assert_true(function_exists("type_name"))
|
||||
assert_true(function_exists("function_exists"))
|
||||
assert_false(function_exists("this_function_does_not_exist_xyz"))
|
||||
|
||||
// get_functions: returns a Map of all registered functions
|
||||
var funcs = get_functions()
|
||||
assert_true(funcs.size() > 0)
|
||||
assert_true(funcs.count("print") > 0)
|
||||
assert_true(funcs.count("type_name") > 0)
|
||||
|
||||
// get_objects: returns a Map of all scripting objects
|
||||
var my_test_var = 42
|
||||
var objs = get_objects()
|
||||
assert_true(objs.size() > 0)
|
||||
assert_true(objs.count("my_test_var") > 0)
|
||||
|
||||
// type: returns a Type_Info for a named type
|
||||
var ti = type("int")
|
||||
assert_equal("int", ti.name())
|
||||
|
||||
// call_exists: checks if a function call with given params exists
|
||||
assert_true(call_exists(`+`, 1, 2))
|
||||
assert_true(call_exists(`+`, "a", "b"))
|
||||
|
||||
// --- Object methods ---
|
||||
|
||||
// get_type_info: returns Type_Info for a value
|
||||
var s = "hello"
|
||||
assert_equal("string", s.get_type_info().name())
|
||||
assert_equal("int", 1.get_type_info().name())
|
||||
|
||||
// is_type on objects
|
||||
assert_true("hello".is_type("string"))
|
||||
assert_true(1.is_type("int"))
|
||||
assert_false(1.is_type("string"))
|
||||
|
||||
// is_type with Type_Info
|
||||
assert_true("hello".is_type(string_type))
|
||||
assert_true(1.is_type(int_type))
|
||||
|
||||
// is_var_* methods
|
||||
var x = 5
|
||||
assert_false(x.is_var_const())
|
||||
assert_false(x.is_var_null())
|
||||
assert_false(x.is_var_undef())
|
||||
|
||||
// --- Type_Info methods ---
|
||||
var int_ti = type("int")
|
||||
assert_equal("int", int_ti.name())
|
||||
assert_false(int_ti.is_type_const())
|
||||
assert_false(int_ti.is_type_void())
|
||||
assert_false(int_ti.is_type_undef())
|
||||
assert_false(int_ti.is_type_reference())
|
||||
assert_false(int_ti.is_type_pointer())
|
||||
|
||||
// bare_equal: compares types ignoring const/pointer/reference
|
||||
assert_true(int_ti.bare_equal(1.get_type_info()))
|
||||
|
||||
// --- Function introspection ---
|
||||
|
||||
def my_reflection_test_func(a, b) { return a + b; }
|
||||
|
||||
// get_arity
|
||||
assert_equal(2, my_reflection_test_func.get_arity())
|
||||
|
||||
// get_param_types
|
||||
var param_types = my_reflection_test_func.get_param_types()
|
||||
assert_true(param_types.size() > 0)
|
||||
|
||||
// get_contained_functions
|
||||
assert_equal(0, my_reflection_test_func.get_contained_functions().size())
|
||||
|
||||
// has_guard
|
||||
assert_false(my_reflection_test_func.has_guard())
|
||||
|
||||
// Guarded function
|
||||
def my_guarded_func(x) : x > 0 { return x; }
|
||||
assert_true(my_guarded_func.has_guard())
|
||||
var g = my_guarded_func.get_guard()
|
||||
assert_equal(1, g.get_arity())
|
||||
|
||||
// call: invoke a function with a vector of parameters
|
||||
assert_equal(3, `+`.call([1, 2]))
|
||||
|
||||
// --- Dynamic_Object reflection ---
|
||||
var obj = Dynamic_Object()
|
||||
obj.name = "test"
|
||||
obj.value = 42
|
||||
var attrs = obj.get_attrs()
|
||||
assert_true(attrs.count("name") > 0)
|
||||
assert_true(attrs.count("value") > 0)
|
||||
|
||||
// --- Class reflection ---
|
||||
class ReflectionTestClass {
|
||||
var x
|
||||
def ReflectionTestClass() { this.x = 10; }
|
||||
def get_x() { return this.x; }
|
||||
}
|
||||
|
||||
var rtc = ReflectionTestClass()
|
||||
assert_equal("ReflectionTestClass", rtc.get_type_name())
|
||||
assert_true(rtc.is_type("ReflectionTestClass"))
|
||||
assert_equal("Dynamic_Object", type_name(rtc))
|
||||
assert_equal("ReflectionTestClass", rtc.get_type_name())
|
||||
Loading…
x
Reference in New Issue
Block a user