// This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #ifndef CHAISCRIPT_HPP_ #define CHAISCRIPT_HPP_ /// @mainpage /// [ChaiScript](http://www.chaiscript.com") is a scripting language designed specifically for integration with C++. It provides /// seamless integration with C++ on all levels, including shared_ptr objects, functors and exceptions. /// /// The parts of the ChaiScript API that the average user will be concerned with are contained in the /// chaiscript namespace and the chaiscript::ChaiScript class. /// /// The end user parts of the API are extremely simple both in size and ease of use. /// /// Currently, all source control and project management aspects of ChaiScript occur on /// [github](http://www.github.com/ChaiScript/ChaiScript"). /// /// ------------------------------------------------------------ /// /// @sa chaiscript /// @sa chaiscript::ChaiScript /// @sa ChaiScript_Language for Built in Functions /// @sa @ref LangGettingStarted /// @sa @ref LangKeywordRef /// @sa @ref LangInPlaceRef /// @sa @ref LangObjectSystemRef /// @sa http://www.chaiscript.com /// @sa http://www.github.com/ChaiScript/ChaiScript /// /// ----------------------------------------------------------- /// /// @section gettingstarted API Getting Started /// /// - @ref basics /// - @ref compiling /// - @ref eval /// - @ref adding_items /// - @ref operatoroverloading /// - @ref add_class /// - @ref pointer_conversions /// - @ref baseclasses /// - @ref functionobjects /// - @ref threading /// - @ref exceptions /// /// /// @subsection basics Basics /// /// Basic simple example: /// /// ~~~~~~~{.cpp} /// //main.cpp /// #include /// /// double function(int i, double j) /// { /// return i * j; /// } /// /// int main() /// { /// chaiscript::ChaiScript chai; /// chai.add(chaiscript::fun(&function), "function"); /// /// double d = chai.eval("function(3, 4.75);"); /// } /// ~~~~~~~ /// /// ------------------------------------------------------ /// /// @subsection compiling Compiling ChaiScript Applications /// /// ChaiScript is a header only library with only one dependency: The /// operating system provided dynamic library loader, which has to be specified on some platforms. /// /// @subsubsection compilinggcc Compiling with GCC /// /// To compile the above application on a Unix like operating system (MacOS, Linux) with GCC you need to link /// the dynamic loader. For example: /// /// ~~~~~~~~ /// gcc main.cpp -I/path/to/chaiscript/headers -ldl /// ~~~~~~~~ /// /// Alternatively, you may compile without threading support. /// /// ~~~~~~~~ /// gcc main.cpp -I/path/to/chaiscript/headers -ldl -DCHAISCRIPT_NO_THREADS /// ~~~~~~~~ /// /// ------------------------------------------ /// /// @subsection eval Evaluating Scripts /// /// Scripts can be evaluated with the () operator, eval method or eval_file method. /// /// @subsubsection parenoperator () Operator /// /// operator() can be used as a handy shortcut for evaluating ChaiScript snippets. /// /// ~~~~~~~~{.cpp} /// chaiscript::ChaiScript chai; /// chai("print(@"hello world@")"); /// ~~~~~~~~ /// /// @sa chaiscript::ChaiScript::operator()(const std::string &) /// /// @subsubsection evalmethod Method 'eval' /// /// The eval method is somewhat more verbose and can be used to get type safely return values /// from the script. /// /// ~~~~~~~~{.cpp} /// chaiscript::ChaiScript chai; /// chai.eval("callsomefunc()"); /// int result = chai.eval("1 + 3"); /// // result now equals 4 /// ~~~~~~~~ /// /// @sa chaiscript::ChaiScript::eval /// /// @subsubsection evalfilemethod Method 'eval_file' /// /// The 'eval_file' method loads a file from disk and executes the script in it /// /// ~~~~~~~~{.cpp} /// chaiscript::ChaiScript chai; /// chai.eval_file("myfile.chai"); /// std::string result = chai.eval_file("myfile.chai") // extract the last value returned from the file /// ~~~~~~~~ /// /// @sa chaiscript::ChaiScript::eval_file /// /// -------------------------------------------------- /// /// @subsection adding_items Adding Items to ChaiScript /// /// ChaiScript supports 4 basic things that can be added: objects, functions, type infos and Modules /// /// @subsubsection adding_objects Adding Objects /// /// Named objects can be created with the chaiscript::var function. Note: adding a object /// adds it to the current thread scope, not to a global scope. If you have multiple /// threads that need to access the same variables you will need to add them /// separately for each thread, from the thread itself. /// /// ~~~~~~~~~{.cpp} /// using namespace chaiscript; /// ChaiScript chai; /// int i = 5; /// chai.add(var(i), "i"); /// chai("print(i)"); /// ~~~~~~~~~ /// /// Immutable objects can be created with the chaiscript::const_var function. /// /// ~~~~~~~~~{.cpp} /// chai.add(const_var(i), "i"); /// chai("i = 5"); // exception throw, cannot assign const var /// ~~~~~~~~~ /// /// Named variables can only be accessed from the context they are created in. /// If you want a global variable, it must be const, and created with the /// chaiscript::ChaiScript::add_global_const function. /// /// ~~~~~~~~~{.cpp} /// chai.add_global_const(const_var(i), "i"); /// chai("def somefun() { print(i); }; somefun();"); /// ~~~~~~~~~ /// /// @subsubsection adding_functions Adding Functions /// /// Functions, methods and members are all added using the same function: chaiscript::fun. /// /// ~~~~~~~~~{.cpp} /// using namespace chaiscript; /// /// class MyClass { /// public: /// int memberdata; /// void method(); /// void method2(int); /// static void staticmethod(); /// void overloadedmethod(); /// void overloadedmethod(const std::string &); /// }; /// /// ChaiScript chai; /// chai.add(fun(&MyClass::memberdata), "memberdata"); /// chai.add(fun(&MyClass::method), "method"); /// chai.add(fun(&MyClass::staticmethod), "staticmethod"); /// ~~~~~~~~~ /// /// Overloaded methods will need some help, to hint the compiler as to which overload you want: /// /// ~~~~~~~~~{.cpp} /// chai.add(fun(&MyClass::overloadedmethod), "overloadedmethod"); /// chai.add(fun(&MyClass::overloadedmethod), "overloadedmethod"); /// ~~~~~~~~~ /// /// There are also shortcuts built into chaiscript::fun for binding up to the first two parameters of the function. /// /// ~~~~~~~~~{.cpp} /// MyClass obj; /// chai.add(fun(&MyClass::method, &obj), "method"); /// chai("method()"); // equiv to obj.method() /// chai.add(fun(&MyClass::method2, &obj, 3), "method2"); /// chai("method2()"); // equiv to obj.method2(3) /// ~~~~~~~~~ /// /// @subsubsection addingtypeinfo Adding Type Info /// /// ChaiScript will automatically support any type implicitly provided to it in the form /// of objects and function parameters / return types. However, it can be nice to let ChaiScript /// know more details about the types you are giving it. For instance, the "clone" functionality /// cannot work unless there is a copy constructor registered and the name of the type is known /// (so that ChaiScript can look up the copy constructor). /// /// Continuing with the example "MyClass" from above: /// /// ~~~~~~~~{.cpp} /// chai.add(user_type(), "MyClass"); /// ~~~~~~~~ /// /// @subsubsection adding_modules Adding Modules /// /// Modules are holders for collections of ChaiScript registrations. /// /// ~~~~~~~~{.cpp} /// ModulePtr module = get_sum_module(); /// chai.add(module); /// ~~~~~~~~ /// /// @sa chaiscript::Module /// /// ----------------------------------------------------------------------- /// /// @subsection operatoroverloading Operator Overloading /// /// Operators are just like any other function in ChaiScript, to overload an operator, simply register it. /// /// ~~~~~~~~{.cpp} /// class MyClass { /// MyClass operator+(const MyClass &) const; /// }; /// /// chai.add(fun(&MyClass::operator+), "+"); /// /// std::string append_string_int(const std::string &t_lhs, int t_rhs) /// { /// std::stringstream ss; /// ss << t_lhs << t_rhs; /// return ss.str(); /// } /// /// chai.add(fun(append_string_int), "+"); /// ~~~~~~~~ /// /// @sa @ref adding_functions /// /// ----------------------------------------------------------------------- /// /// @subsection add_class Class Helper Utility /// /// Much of the work of adding new classes to ChaiScript can be reduced with the help /// of the add_class helper utility. /// /// ~~~~~~~~{.cpp} /// class Test /// { /// public: /// void function() {} /// std::string function2() { return "Function2"; } /// void function3() {} /// std::string functionOverload(double) { return "double"; } /// std::string functionOverload(int) { return "int"; } /// }; /// /// int main() /// { /// chaiscript::ModulePtr m = chaiscript::ModulePtr(new chaiscript::Module()); /// /// chaiscript::utility::add_class(*m, /// "Test", /// { constructor(), /// constructor() }, /// { {fun(&Test::function), "function"}, /// {fun(&Test::function2), "function2"}, /// {fun(&Test::function2), "function3"} /// {fun(static_cast(&Test::functionOverload)), "functionOverload"} /// {fun(static_cast(&Test::functionOverload)), "functionOverload"} } /// ); /// /// /// chaiscript::ChaiScript chai; /// chai.add(m); /// } /// ~~~~~~~~ /// /// @sa @ref adding_modules /// /// ----------------------------------------------------------------------- /// /// @subsection pointer_conversions Pointer / Object Conversions /// /// As much as possible, ChaiScript attempts to convert between &, *, const &, const *, std::shared_ptr, /// std::shared_ptr, std::reference_wrapper, std::reference_wrapper and value types automatically. /// /// If a chaiscript::var object was created in C++ from a pointer, it cannot be converted to a shared_ptr (this would add invalid reference /// counting). Const may be added, but never removed. /// /// The take away is that you can pretty much expect function calls to Just Work when you need them to. /// /// ~~~~~~~~{.cpp} /// void fun1(const int *); /// void fun2(int *); /// void fun3(int); /// void fun4(int &); /// void fun5(const int &); /// void fun5(std::shared_ptr); /// void fun6(std::shared_ptr); /// void fun7(const std::shared_ptr &); /// void fun8(const std::shared_ptr &); /// void fun9(std::reference_wrapper); /// void fun10(std::reference_wrapper); /// /// int main() /// { /// using namespace chaiscript /// chaiscript::ChaiScript chai; /// chai.add(fun(fun1), "fun1"); /// chai.add(fun(fun2), "fun2"); /// chai.add(fun(fun3), "fun3"); /// chai.add(fun(fun4), "fun4"); /// chai.add(fun(fun5), "fun5"); /// chai.add(fun(fun6), "fun6"); /// chai.add(fun(fun7), "fun7"); /// chai.add(fun(fun8), "fun8"); /// chai.add(fun(fun9), "fun9"); /// chai.add(fun(fun10), "fun10"); /// /// chai("var i = 10;"); /// chai("fun1(i)"); /// chai("fun2(i)"); /// chai("fun3(i)"); /// chai("fun4(i)"); /// chai("fun5(i)"); /// chai("fun6(i)"); /// chai("fun7(i)"); /// chai("fun8(i)"); /// chai("fun9(i)"); /// chai("fun10(i)"); /// } /// ~~~~~~~~ /// /// See the unit test unittests/boxed_cast_test.cpp for a complete breakdown of the automatic casts that /// available and tested. /// /// ----------------------------------------------------------------------- /// /// @subsection baseclasses Base Classes /// /// ChaiScript supports handling of passing a derived class object to a function expecting a base class object. /// For the process to work, the base/derived relationship must be registered with the engine. /// /// ~~~~~~~~{.cpp} /// class Base {}; /// class Derived : public Base {}; /// void myfunction(Base *b); /// /// int main() /// { /// chaiscript::ChaiScript chai; /// chai.add(chaiscript::base_class()); /// Derived d; /// chai.add(chaiscript::var(&d), "d"); /// chai.add(chaiscript::fun(&myfunction), "myfunction"); /// chai("myfunction(d)"); /// } /// ~~~~~~~~ /// /// ----------------------------------------------------------------------- /// /// /// @subsection functionobjects Function Objects /// /// Functions are first class objects in ChaiScript and ChaiScript supports automatic conversion /// between ChaiScript functions and std::function objects. /// /// ~~~~~~~~{.cpp} /// void callafunc(const std::function &t_func) /// { /// t_func("bob"); /// } /// /// int main() /// { /// chaiscript::ChaiScript chai; /// chai.add(chaiscript::fun(&callafunc), "callafunc"); /// chai("callafunc(fun(x) { print(x); })"); // pass a lambda function to the registered function /// // which expects a typed std::function /// /// std::function f = chai.eval >("dump_system"); /// f(); // call the ChaiScript function dump_system, from C++ /// } /// ~~~~~~~~ /// /// ----------------------------------------------------------------------- /// /// /// @subsection threading Threading /// /// Thread safety is automatically handled within the ChaiScript system. Objects can be added /// and scripts executed from multiple threads. For each thread that executes scripts, a new /// context is created and managed by the engine. /// /// Thread safety can be disabled by defining CHAISCRIPT_NO_THREADS when using the library. /// /// Disabling thread safety increases performance in many cases. /// /// ----------------------------------------------------------------------- /// /// /// @subsection exceptions Exception Handling /// /// @subsubsection exceptionsbasics Exception Handling Basics /// /// Exceptions can be thrown in ChaiScript and caught in C++ or thrown in C++ and caught in /// ChaiScript. /// /// ~~~~~~~~{.cpp} /// void throwexception() /// { /// throw std::runtime_error("err"); /// } /// /// int main() /// { /// // Throw in C++, catch in ChaiScript /// chaiscript::ChaiScript chai; /// chai.add(chaiscript::fun(&throwexception), "throwexception"); /// chai("try { throwexception(); } catch (e) { print(e.what()); }"); // prints "err" /// /// // Throw in ChaiScript, catch in C++ /// try { /// chai("throw(1)"); /// } catch (chaiscript::Boxed_Value bv) { /// int i = chaiscript::boxed_cast(bv); /// // i == 1 /// } /// } /// ~~~~~~~~ /// /// @subsubsection exceptionsautomatic Exception Handling Automatic Unboxing /// /// As an alternative to the manual unboxing of exceptions shown above, exception specifications allow the user to tell /// ChaiScript what possible exceptions are expected from the script being executed. /// /// Example: /// ~~~~~~~~{.cpp} /// chaiscript::ChaiScript chai; /// /// try { /// chai.eval("throw(runtime_error(@"error@"))", chaiscript::exception_specification()); /// } catch (const double e) { /// } catch (int) { /// } catch (float) { /// } catch (const std::string &) { /// } catch (const std::exception &e) { /// // This is the one what will be called in the specific throw() above /// } /// ~~~~~~~~ /// /// @sa chaiscript::Exception_Handler for details on automatic exception unboxing /// @sa chaiscript::exception_specification /// @page LangObjectSystemRef ChaiScript Language Object Model Reference /// /// /// ChaiScript has an object system built in, for types defined within the ChaiScript system. /// /// ~~~~~~~~~ /// attr Rectangle::height /// attr Rectangle::width /// def Rectangle::Rectangle() { this.height = 10; this.width = 20 } /// def Rectangle::area() { this.height * this.width } /// /// var rect = Rectangle() /// rect.height = 30 /// print(rect.area()) /// ~~~~~~~~~ /// /// Since ChaiScript 5.4.0 it has been possible to use the "class" keyword to simplify this code. /// /// ~~~~~~~~~ /// class Rectangle { /// attr height /// attr width /// def Rectangle() { this.height = 10; this.width = 20 } /// def area() { this.height * this.width } /// } /// /// var rect = Rectangle() /// rect.height = 30 /// print(rect.area()) /// ~~~~~~~~~ /// /// @sa @ref keywordattr /// @sa @ref keyworddef /// @page LangInPlaceRef ChaiScript Language In-Place Creation Reference /// @section inplacevector Vector /// /// ~~~~~~~~~ /// In-place Vector ::= "[" [expression ("," expression)*] "]" /// ~~~~~~~~~ /// /// @section inplacerangedvector Ranged Vector /// /// ~~~~~~~~~ /// In-place Ranged Vector ::= "[" value ".." value "]" /// ~~~~~~~~~ /// /// Creates a vector over a range (eg. 1..10) /// /// @section inplacemap Map /// /// ~~~~~~~~ /// In-place Map ::= "[" (string ":" expression)+ "]" /// ~~~~~~~~ /// @page LangGettingStarted ChaiScript Language Getting Started /// /// ChaiScript is a simple language that should feel familiar to anyone who knows /// C++ or ECMAScript (JavaScript). /// /// ----------------------------------------------------------------------- /// /// @section chaiscriptloops Loops /// /// Common looping constructs exist in ChaiScript /// /// ~~~~~~~~ /// var i = 0; /// while (i < 10) /// { /// // do something /// ++i; /// } /// ~~~~~~~~ /// /// ~~~~~~~~ /// for (var i = 0; i < 10; ++i) /// { /// // do something /// } /// ~~~~~~~~ /// /// @sa @ref keywordfor /// @sa @ref keywordwhile /// /// ----------------------------------------------------------------------- /// /// @section chaiscriptifs Conditionals /// /// If statements work as expected /// /// ~~~~~~~~ /// var b = true; /// /// if (b) { /// // do something /// } else if (c < 10) { /// // do something else /// } else { /// // or do this /// } /// ~~~~~~~~ /// /// @sa @ref keywordif /// /// ----------------------------------------------------------------------- /// /// @section chaiscriptfunctions Functions /// /// Functions are defined with the def keyword /// /// ~~~~~~~~ /// def myfun(x) { print(x); } /// /// myfun(10); /// ~~~~~~~~ /// /// Functions may have "guards" which determine if which is called. /// /// ~~~~~~~~ /// eval> def myfun2(x) : x < 10 { print("less than 10"); } /// eval> def myfun2(x) : x >= 10 { print("10 or greater"); } /// eval> myfun2(5) /// less than 10 /// eval> myfun2(12) /// 10 or greater /// ~~~~~~~~ /// /// @sa @ref keyworddef /// @sa @ref keywordattr /// @sa @ref LangObjectSystemRef /// /// ----------------------------------------------------------------------- /// /// @section chaiscriptfunctionobjects Function Objects /// /// Functions are first class types in ChaiScript and can be used as variables. /// /// ~~~~~~~~ /// eval> var p = print; /// eval> p(1); /// 1 /// ~~~~~~~~ /// /// They can also be passed to functions. /// /// ~~~~~~~~ /// eval> def callfunc(f, lhs, rhs) { return f(lhs, rhs); } /// eval> def do_something(lhs, rhs) { print("lhs: ${lhs}, rhs: ${rhs}"); } /// eval> callfunc(do_something, 1, 2); /// lhs: 1, rhs: 2 /// ~~~~~~~~ /// /// Operators can also be treated as functions by using the back tick operator. Building on the above example: /// /// ~~~~~~~~ /// eval> callfunc(`+`, 1, 4); /// 5 /// eval> callfunc(`*`, 3, 2); /// 6 /// ~~~~~~~~ /// /// ----------------------------------------------------------------------- /// /// @sa @ref LangKeywordRef /// @sa ChaiScript_Language for Built in Functions /// @page LangKeywordRef ChaiScript Language Keyword Reference /// /// /// ----------------------------------------------------------------------- /// /// @section keywordattr attr /// Defines a ChaiScript object attribute /// /// ~~~~~~~~ /// Attribute Definition ::= "attr" class_name "::" attribute_name /// ~~~~~~~~ /// /// @sa @ref LangObjectSystemRef /// /// /// ----------------------------------------------------------------------- /// /// @section keywordauto auto /// /// Defines a variable /// /// ~~~~~~~~ /// Variable ::= "auto" identifier /// ~~~~~~~~ /// /// Synonym for @ref keywordvar /// /// ----------------------------------------------------------------------- /// /// @section keywordbreak break /// Stops execution of a looping block. /// /// ~~~~~~~~ /// Break Statement ::= "break" /// ~~~~~~~~ /// /// @sa @ref keywordfor /// @sa @ref keywordwhile /// /// /// ----------------------------------------------------------------------- /// /// @section keyworddef def /// Begins a function or method definition /// /// ~~~~~~~~ /// Function Definition ::= "def" identifier "(" [[type] arg ("," [type] arg)*] ")" [":" guard] block /// Method Definition ::= "def" class_name "::" method_name "(" [[type] arg ("," [type] arg)*] ")" [":" guard] block /// ~~~~~~~~ /// /// identifier: name of function. Required. /// args: comma-delimited list of parameter names with optional type specifiers. Optional. /// guards: guarding statement that act as a prerequisite for the function. Optional. /// { }: scoped block as function body. Required. /// /// Functions return values in one of two ways: /// /// By using an explicit return call, optionally passing the value to be returned. /// By implicitly returning the value of the last expression (if it is not a while or for loop). /// /// Method definitions for known types extend those types with new methods. This includes C++ and ChaiScript defined types. /// Method definitions for unknown types implicitly define the named type. /// /// @sa @ref LangObjectSystemRef /// /// /// ----------------------------------------------------------------------- /// /// @section keywordelse else /// @sa @ref keywordif /// /// /// ----------------------------------------------------------------------- /// /// @section keywordfor for /// ~~~~~~~~ /// For Block ::= "for" "(" [initial] ";" stop_condition ";" loop_expression ")" block /// ~~~~~~~~ /// This loop can be broken using the @ref keywordbreak command. /// /// /// ----------------------------------------------------------------------- /// /// @section keywordfun fun /// Begins an anonymous function declaration (sometimes called a lambda). /// /// ~~~~~~~~ /// Lambda ::= "fun" "(" [variable] ("," variable)* ")" block /// ~~~~~~~~ /// /// _Example_ /// /// ~~~~~~~~ /// // Generate an anonymous function object that adds 2 to its parameter /// var f = fun(x) { x + 2; } /// ~~~~~~~~ /// /// @sa @ref keyworddef for more details on ChaiScript functions /// /// /// ----------------------------------------------------------------------- /// /// @section keywordif if /// Begins a conditional block of code that only executes if the condition evaluates as true. /// ~~~~~~~~ /// If Block ::= "if" "(" condition ")" block /// Else If Block ::= "else if" "(" condition ")" block /// Else Block ::= "else" block /// ~~~~~~~~ /// /// _Example_ /// /// ~~~~~~~~ /// if (true) { /// // do something /// } else if (false) { /// // do something else /// } else { /// // otherwise do this /// } /// ~~~~~~~~ /// /// /// ----------------------------------------------------------------------- /// /// @section keywordtry try /// ~~~~~~~~ /// Try Block ::= "try" block /// ("catch" ["(" [type] variable ")"] [":" guards] block)+ /// ["finally" block] /// ~~~~~~~~ /// /// @sa ChaiScript_Language::throw /// /// /// ----------------------------------------------------------------------- /// /// @section keywordwhile while /// /// Begins a conditional block of code that loops 0 or more times, as long as the condition is true /// /// ~~~~~~~~ /// While Block ::= "while" "(" condition ")" block /// ~~~~~~~~ /// /// This loop can be broken using the @ref keywordbreak command. /// /// /// ----------------------------------------------------------------------- /// /// @section keywordvar var /// /// Defines a variable /// /// ~~~~~~~~ /// Variable ::= "var" identifier /// ~~~~~~~~ /// /// Synonym for @ref keywordauto /// @namespace chaiscript /// @brief Namespace chaiscript contains every API call that the average user will be concerned with. /// @namespace chaiscript::detail /// @brief Classes and functions reserved for internal use. Items in this namespace are not supported. #include "chaiscript_basic.hpp" #include "chaiscript_stdlib.hpp" #include "language/chaiscript_parser.hpp" namespace chaiscript { class ChaiScript : public ChaiScript_Basic { public: ChaiScript(std::vector t_modulepaths = {}, std::vector t_usepaths = {}, std::vector t_opts = chaiscript::default_options()) : ChaiScript_Basic(chaiscript::Std_Lib::library(), std::make_unique>(), std::move(t_modulepaths), std::move(t_usepaths), std::move(t_opts)) { } }; } // namespace chaiscript #endif /* CHAISCRIPT_HPP_ */