TeaScript 0.15.0 was published on the 1st September in 2024 and can be downloaded for free:
New Patch Release TeaScript C++ Library 0.15.1 (03. November 2024) – This patch release fixes the header only compile mode which was broken for Engine/CoroutineScriptEngine.
TeaScript C++ Library | TeaScript_CppLibrary_v0.15.1.zip |
TeaScript Host Windows | TeaScript_v0.15.0_win64.zip |
TeaScript Host Linux (Ubuntu 22.04) | TeaScript_v0.15.0_linux64.tgz |
All details, features and changes for this new release are following in the blog post below.
Infos and Links
The download page with more infos, links and basic instructions:
☛☛☛ Download Page. ☚☚☚
Browse the source code of the TeaScript C++ Library on Github.
Are you new to TeaScript?
Then you may read here first: Overview and Highlights of TeaScript.
Watch demos, tutorials and more on the YouTube Channel.
What was new?
The previous release blog posts are nice for a tutorial like introduction of the new features and for getting an overview. If you missed some, here is the collection.
Release of TeaScript 0.14.0 🖥– TeaStackVM, Compiler, Dis-/Assembler, Suspend+Continue, Yield, improved debugging.
Release of TeaScript 0.13.0 🗍 – Buffer, U8, U64, bit ops, UTF-8 Iterator, hex integrals, MPL-2.0.
Release of TeaScript 0.12.0 🌈 – Colored Output, Format String, Forall Loop, Sequences, interactive debugging.
Release of TeaScript 0.11.0 🎂 – TOML Support, Subscript Operator, Raw String Literals.
Release of TeaScript 0.10.0 🌞 – Tuples/Named Tuples, Passthrough Type, CoreLib config.
What is new?
The TeaScript 0.15.0 release comes with the following new main features:
- Web module as a preview for http client / server functionality with enhanced build-in JSON support.
☛ Watch the web feature demo on YouTube. - Full JSON support for read and write operations (String/File ⟷ (Named) Tuple).
- Provided C++ JSON adapter for nlohmann::json, RapidJSON, Boost.Json and PicoJson.
- Added missing write support for TOML (now TOML is complete as well).
- Import/export of C++ JSON / TOML objects from/to ValueObject of TeaScript (as a Named Tuple) (TeaScript C++ Library only)
Web Client / Server Preview
The pre-compiled Windows and Linux packages of the TeaScript Host Application have this feature enabled by default (download links at the top of the page). For the TeaScript C++ Library it is an opt-in feature and must be enabled first (see below).
The web preview module adds functionality for issuing http requests (client) or receive and reply to them (server). Internally TeaScript is utilizing the power of the awesome Boost.Beast and Boost.Asio C++ libraries. (Well, for this preview maybe only 1% of the available power is used already… 😉 )
This feature comes along with automatic enhanced JSON support for payloads with Content-Type “application/json”.
You can try it easily by your self with the provided web_client.tea and web_server.tea example script files.
Here is another simple example (error handling omitted):
// issue a http GET request (here to a test page for JSON as payload)
def res := web_get( "headers.jsontest.com" )
// access http status code and its reason (may print "200: OK")
println( res.code % ": " % res.reason )
// print all header entries of the reply
tuple_print( res.header, "header", 1 )
// the above may print:
// [...]
// header.Date: "Sat, 31 Aug 2024 13:34:26 GMT"
// header.Server: "Google Frontend"
// header.Content-Length: "236"
// print the payload of the reply as String
println( res.payload ) // this will print a json formatted string
// if the payload was sent as Content-Type "application/json"
// TeaScript automatically builds a Tuple out of it.
if( is_defined res.json ) { // json Tuple is present
// we can access all elements/objects directly.
// In this example the server just echoed the header of
// the request back as a json formatted string.
// print the Host value
println( res.json.Host )
// print the User-Agent
println( res.json["User-Agent"] )
// dot access possible as well!
println( res.json."User-Agent" )
}
Issuing a http POST is easy as well:
// easily create the json payload
def json_payload := json_make_object( ("name", "John"), ("age", 31) )
// issue the http POST with our json payload (plain String also works as paramter)
def reply := web_post( "postman-echo.com", json_payload, "/post" )
// now we can access the result/reply message as before.
// lets print the whole json object from the reply...
tuple_print( reply.json, "json", 10 )
// may print:
// [...]
// json.args: <Tuple>
// json.data: <Tuple>
// json.data.age: 31
// json.data.name: "John"
// [...]
// json.headers.connection: "close"
// json.headers.content-length: "24"
// json.headers.content-type: "application/json"
// json.headers.host: "postman-echo.com"
// json.headers.user-agent: "TeaScript/0.15.0"
// [...]
For a web server example, please, have a look at web_server.tea.
Howto enable the web preview for the C++ Library
The web preview module is not header only and it has a dependency to Boost.Beast / Boost.Asio. Because of that, the following steps have to be done in order to use it in the TeaScript C++ Library:
- Add WebPreview.cpp from “TeaScriptRoot”/modules/source/ to your project.
- Add the include directory “TeaScriptRoot”/modules/include/ to your project.
- Add the include for the Boost Library to your project (You don’t need to compile Boost, TeaScript is using only header only libs from there)
- (optionally): If you want the default
Engine
of TeaScript is always loading the Web Preview module, define the preprocessor macroTEASCRIPT_ENGINE_USE_WEB_PREVIEW
to1
(or change its default value in Engine.hpp to1
). Otherwise you must manually load the web preview module into the desiredContext
instance.
You can also watch a short How-To on YouTube: https://youtu.be/SeRO21U1vMk
JSON Support
TeaScript now has build-in JSON support. You can convert a JSON formatted String into a Tuple object and vice versa. The Json can be written to/read from a file as well. Also, TeaScript offers a bunch of utility functions for access and manipulating JSON compatible Tuple objects.
The Json support is enabled by default and is served by the tiny PicoJson library, which is shipped with the TeaScript source.
If you use or want use the TeaScript C++ Library, it is likely that your C++ project is using a different Json library. For that case, you can change the used Json library of TeaScript. This release comes with available Json adapters for nlohmann::json, RapidJSON and Boost.Json.
While the internally used Json Adapter of TeaScript is exactly one and must be chosen at compile time, all available Json adapters can be used at runtime for manually import/export Json objects of the concrete adapter. This is for the unlucky case (but I have seen many already!) that not one Json library is in use but many.
You might have a look at the corelibrary_test05.tea code to see how the JSON support could be used (and how its functionality is tested).
Here is another simple example:
// json formatted array string to Tuple
def json := readjsonstring( "[1, 2, 3, 4]" )
// use the utility functions for json arrays
println( json_is_array( json ) ) // true
println( json_array_empty( json ) ) // false
println( json_array_size( json ) ) // 4
// access
println( json[1] ) // 2
// manipulate
json_array_append( json, 5 ) // value 5 appended
json_array_remove( json, 1 ) // index 1 removed
// to json formatted string
def str := writejsonstring( json )
println( str ) // "[1,3,4,5]"
// use a more complex json object
const object_str := """"
{
"name": "John",
"age": 31,
"additional": [1, true, "Hello"]
}
""""
def json_object := readjsonstring( object_str )
println( json_object.name ) // "John"
println( json_is_array( json_object.additional ) ) // true
println( "%(json_object.additional.2) World!" ) // "Hello World!"
// create json compatible tuples:
def new_json := json_make_object( ("foo", "bar"), ("myarray", json_make_array(1, "test" ) ) )
// build a json formatted string
def new_str := writejsonstring( new_json ) // "{"foo":"bar","myarray":[1,"test"]}"
There are convenience functions for read/write Json formatted Strings from/to files via readjsonfile()
and writejsonfile()
respectively.
Have a look in the Core Library Documentation for all json utility functions or use :search json
from within the REPL of the TeaScript Host Application.
Technical noteworthiness
TeaScript does not have distinct arrays and dictionaries but (only) Tuples which can act as both of them and much more.
If a Tuple shall be used as a Json value and it contains key value pairs then it is considered to be a json object. But if it contains only values, then it is considered to be a json array.
But if a Tuple object is empty, it is not possible to distinguish between an empty json array and an empty json object. For this case TeaScript inserts a marker inside a Tuple which comes from a json import or should be used for a json export and was considered to be an json array.
Because of that it is really important to use the provided utility functions for json arrays. They will take care of the specialties under the hood.
Howto change the Json Adapter
The Json adapters are not header only and have dependencies to other Json C++ libraries. Because of that, the following steps have to be done:
- Add the include directory for the desired Json library to your project, e.g., /nlohmann_3.11.3/single_include/
- Add the include directory for TeaScript extensions to your project: “TeaScriptRoot”/extensions/include/
- Add the corresponding adapter to your project, e.g., “TeaScriptRoot”/extensions/source/JsonAdapterNlohmann.cpp
- Define the preprocessor macro
TEASCRIPT_JSON_FLAVOR
toTEASCRIPT_JSON_NLOHMANN
. Either via project settings or inside JsonSupport.hpp.
Thats it. After this changes, TeaScript will use nlohmann::json for the Json support.
Complete TOML support
Before, TeaScript was able to read TOML files/strings already. Now it can also write them with writetomlstring()
and writetomlfile()
.
The TOML feature is optional. In order to activate it, you must download the tomlplusplus library and add the include directory to your project. Thats all, TeaScript will automatically detect it.
The same kind of utility functions were added to the TOML support as it was for JSON. Have a look in the Core Library Documentation for all toml utility functions or use :search toml
from within the REPL of the TeaScript Host Application.
Please note, the same noteworthiness as for json applies to toml. So, in order to support possibly empty arrays, you must use the corresponding utility functions, like toml_is_array
, toml_array_empty
, toml_array_size
, toml_array_append
, toml_array_remove
and so on.
Import/Export of C++ Json objects
Within C++ you can import and export C++ Json objects of all available adapters/json libraries.
For the time being you need a valid instance of the corresponding TeaScript Context
. If you use the Engine
class, the easiest way is to derive from it and use the Context member from there.
The demo app uses this feature in the web preview code. You can view the source of the function here: teascript_demo.cpp webpreview_code().
Here is some little example code using nlohmann::json
:
// somewhere in the code where a TeaScript context is available...
// just make some json object
nlohmann::json value;
value["name"] = "John";
value["myarray"] = { 4, 5, 6 };
// import it
ValueObject val = teascript::JsonAdapterNlohmann::ToValueObject( context, value );
// add it as variable 'json'. ('context' and the context of the engine should be the same.)
engine.AddSharedValueObject( "json", val );
// export is even more simple
// assuming there is a 'json2' variable which is a json compatible Tuple...
auto const valobj = engine.GetVar( "json2" );
// and export it to nlohmann::json.
nlohmann::json value2;
teascript::JsonAdapterNlohmann::FromValueObject( valobj, value2 );
Shortcut for creating dictionaries
Tuples with named elements are used as the map/dictionary container in TeaScript. Before you had to use the Uniform Definition Syntax for create them or use the _tuple_named_append/insert
functions.
Now, a third way is available, which enables creating dictionaries easily with one call: _tuple_named_create()
This functions excepts an arbitrary amount of parameters. Each parameter must be a tuple with 2 elements. The first element must be always of type String. This will be the key of the entry. The second element can be of any type and will be the value of the entry.
Here is some example code showing the 3 available possibilities:
// old way, use _tuple_named_append
def dic1 := _tuple_create() // empty tuple to start with.
_tuple_named_append( dic1, "elem1", 1 ) // added "elem1" with value 1
_tuple_named_append( dic1, "elem2", "foo" ) // added "elem2" with value "foo"
// old way (but still great!), the Uniform Definition Syntax.
def dic1.elem3 := true // added "elem3" with value true
def dic1.baz := _buf(10) // added "baz" with Buffer of size 10
// Now we do the same with the new _tuple_named_create()
def dic2 := _tuple_named_create( ("elem1", 1), ("elem2", "foo"), ("elem3", true), ("baz", _buf(10) ) )
println( dic1 == dic2 ) // prints: true
Easy to use C++ LibraryFunction object
If you want to extend TeaScript via C++ functions there were 2 possibilities available.
The first is the high-level API via a UserCallbackFunc and a call to RegisterUserCallback()
of the Engine class.
The advantage of this is, a callback can be installed and invoked very easily. The disadvantage is, the parameters will not be passed directly but you must pull it from the Context and manually check for its inner value type.
The other way was to use the low-level API via the LibraryFunction0
to LibraryFunction5
, which is also used internally for the CoreLibrary
. The usage of this is neither easy nor straightforward. Also, there are some limitations and partly missing features.
Therefore, the TeaScript C++ Library now offers a generic LibraryFunction
as a low level alternative for registering a C++ function for be invocable from TeaScript. This new generic variant is more easy to use and comes without the old limitations and missing sub-features.
View the source here: LibraryFunction.hpp.
Here is a small example:
// imagine there is a custom module for TeaScript
// please NOTE: The module interface is considered EXPERIMENTAL
class MyModule : public IModule
{
public:
// these are the functions of the module....
static ValueObject Test( Context &rContext, ValueObject const &rRequest );
static bool Test2( long long x, std::string const & y );
static bool Test3( Context &rContext, std::string const &x, double a );
static void Test4();
static bool Test5();
static bool Test6( Context &rContext );
static std::string Test7( long long a );
void Load( Context &rInto, config::eConfig const, bool const ) override;
std::string_view GetName() const override
{
using namespace std::string_view_literals;
return "MyModule"sv;
}
};
// then inside the cpp
void MyModule::Load( Context &rInto, config::eConfig const, bool const )
{
auto const cfg = ValueConfig( ValueShared, ValueConst, rInto.GetTypeSystem() );
// every function can be registered straight forward via the same call!
{
auto func = std::make_shared< LibraryFunction<decltype(Test)> >( &Test );
ValueObject val{std::move( func ), cfg};
rInto.AddValueObject( "Test", val );
}
{
auto func = std::make_shared< LibraryFunction<decltype(Test2)> >( &Test2 );
ValueObject val{std::move( func ), cfg};
rInto.AddValueObject( "Test2", val );
}
{
auto func = std::make_shared< LibraryFunction<decltype(Test3)> >( &Test3 );
ValueObject val{std::move( func ), cfg};
rInto.AddValueObject( "Test3", val );
}
{
auto func = std::make_shared< LibraryFunction<decltype(Test4)> >( &Test4 );
ValueObject val{std::move( func ), cfg};
rInto.AddValueObject( "Test4", val );
}
{
auto func = std::make_shared< LibraryFunction<decltype(Test5)> >( &Test5 );
ValueObject val{std::move( func ), cfg};
rInto.AddValueObject( "Test5", val );
}
{
auto func = std::make_shared< LibraryFunction<decltype(Test6)> >( &Test6 );
ValueObject val{std::move( func ), cfg};
rInto.AddValueObject( "Test6", val );
}
{
auto func = std::make_shared< LibraryFunction<decltype(Test7)> >( &Test7 );
ValueObject val{std::move( func ), cfg};
rInto.AddValueObject( "Test7", val );
}
}
Misc
Deprecation
The following deprecated parts have been finally removed from this release:
exit( exit_code )
Use either _exit( Any )
or the new _Exit
statement instead.
A script will always have a result value (or NaV
(Not A Value)).
class Engine
HasExitCode()
/GetExitCode()
and mExitCode
A script will always have a result value (or NaV
(Not A Value)).
For Visual Studio Users
Depending on your project / code size it might be possible that you must add the /bigobj
flag to the “Additional Options” settings.
Unfortunately Microsoft still has not set this as the new default and every now and then a project runs into this annoying issue.
As you can read on StackOverflow the setting can just be activated if the code reaches a specific size.
If you also find it very annoying you might vote or write a comment in the corresponding topic in the Microsoft developer community as I did as well.
Limitations with clang
TeaScript is a C++20 library and relies on C++20 language and standard library features. Unfortunately, as of today clang in combination with libc++
does still not support std::stop_source
and std::stop_token
which is a C++20 library feature.
If you need the TesScript feature of sending a suspend request to a running TeaScript program inside the Tea StackVM by another thread you cannot use this feature right out of the box.
There are 3 possible solutions beside in don’t use this feature at all:
- try clang 18 with
libc++
and-fexperimental-library
(I did not test it). - use clang with
libstdc++
instead. - use g++ instead (ideally g++13 or newer).
Please, see also the Known_Issues.txt for other known issues on Linux / with clang.
Linux support / g++13
Starting with TeaScript 0.14.0 release all known issues specific for Linux are solved if you use g++13 or newer.
Therefore, I highly recommend to use g++13 (or newer) on Linux.
However, the pre-compiled Linux download bundle of the TeaScript Host Application is still built with g++11 for the best possible platform compatibility.
Please, see also the Known_Issues.txt for the list of known issues.
More Infos
More information about the TeaScript language is available here:
✯ Overview and Highlights
✯ TeaScript language documentation
✯ Core Library documentation
☛☛☛ Try and download TeaScript here. ☚☚☚
Outlook
There are some top new features on my list to implement next
- A distinct Error value which can be returned by a function. The Error type will not only have an integer value but also a custom message.
- A switch or match over types and values for further dispatching depending on the return type/value.
(Actually, I like the approach of Zig)
- A switch or match over types and values for further dispatching depending on the return type/value.
- Next step(s) of the module support.
import
(or load?) statement for import/load modules from within the script.- modules can be added on C++ level or loaded from the filesystem as a .tea or .tsb file.
- A later, future step will be to load compiled C++ code from a plugin folder for add further functionalities (a working prototype exists already).
- Somehow add first asynchronous support from within the script, e.g., for better dispatching possibilities of a (web) server.
- Have a cache folder for compiled scripts (.tsb files, TeaScript Binary) for save parsing and compile time during script launching.
From the time perspective, I think, the next release will likely to be possible on end of December 2024 or during January 2025.
I will be extremely happy for any feedback, suggestions, questions and other kind of constructive feedback.
I hope you will enjoy with this TeaScript release. 🙂