Categories
TeaScript

Release of TeaScript 0.16.0 🐞

TeaScript 0.16.0 was published on the 22nd December in 2025 and can be downloaded for free:

TeaScript C++ LibraryTeaScript_CppLibrary_v0.16.0.zip
TeaScript Host WindowsTeaScript_v0.16.0_win64.zip
TeaScript Host Linux (Ubuntu 22.04)TeaScript_v0.16.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.

Read the changelog.txt for a detailed list of all changes.

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.15.0 β˜•β€“ Web Server / Web Client module preview, full JSON read/write support.
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.16.0 release comes with the following new main features:

TeaScript now has a first step towards a clean and modern error handling. Hence the 🐞 ladybird symbol for this release (error β‰ˆ bug β‰ˆ ladybird).

The error handling consists of 2 major things:

  • a new and distinct Error type
  • a catch statement similar as in and highly inspired by Zig.

Furthermore this release adds

All new features are explained below.

Error type

TeaScript now has a distinct Error type. Actually, an Error contains of an error code and an error message.

Here is some basic example TeaScript code introducing the Error type.

// create an Error instance
def some_error := make_runtime_error( "This is an error!" )

typeof some_error // yields Error

// errors can be printed normally
println( some_error ) // prints: This is an error!

// access the attributes of the Error instance
_error_get_code( some_error )    // 1 (== Runtime Error)
_error_get_name( some_error )    // "Runtime Error"
_error_get_message( some_error ) // "This is an error!"


// there is a handy alternativ way to create an error:
// cast from a String
func divide( a, b )
{
    if( b == 0 ) {
        return "Division by Zero!" as Error // returns a runtime error
    }
    
    a / b
}

def result := divide( 10, 0 )

if( result is Error ) { // this can be done better with 'catch' (see below)
     println( "An error occurred: %(result)" )
     _Exit void // exit Script without any 'exit/return value'
}
// use result ...

API change and API version increment

Please note, that all Core Library functions, which were returning a Bool(false) on error but are not returning a Bool on success, are now returning an Error instead! (see Breaking changes below.)

These major API changes lead to a increased API version number (for the first time in TeaScript’s history). The internal Core Library API version is now 1 (in TeaScript _api_version, in C++ teascript::CoreLibrary::API_Version).

Catch Statement

TeaScript now has a new catch statement for easy and convenient error handling. The catch statement is similar as in Zig.

The catch statement acts as a binary operator. If its left hand side (lhs) statement yields an Error or a NaV (Not A Value), then it will execute the right hand side (rhs) statement. Otherwise, it will just return the lhs value (rhs won’t be executed then).

Optionally the error can be accessed at the right hand side, if a name is given in round brackets after the catch keyword. For the case the lhs yields a NaV a “Not A Value” Error (with code==2) will be constructed.

Some example code illustrating the catch statement is present here example_v0.16.tea and here corelibrary_test06.tea and below:

// _strtonum returns an Error if the String cannot be parsed to an integer.
// Here catch is used for yielding default values on error.
def x := _strtonum( "1" ) catch -1    // no Error, x is 1

def y := _strtonum( "xyz" ) catch -1  // Error, y is -1

// catch can be chained
def z := _strtonum( "xyz" ) catch _strtonum( "3" ) catch -1 // z is 3


// catch the error and do sth. with it.
def a := _strtonum( "uvw" ) catch( err ) { 
    println("Error: %(err)\nReturning default: 100!" )
    100 
} 

// catch can be used for early return on errors
func test(s1, s2)
{
    def n1 := _strtonum( s1 ) catch( err ) return err
    def n2 := _strtonum( s2 ) catch( err ) return err
    if( n2 == 0 ) { return "n2 is zero!" as Error }

    // reaching here only if no Error occurred!
    n1 / n2
}

def baz := test( "4", "2" )     // baz is 2
def foo := test( "10", "xyz" )  // foo is Error

// handle error by exiting script
def bar := test( "qwert", "2" ) catch (err) {
    fail_with_message( err ) // prints err and exit
}

Shared Assign function parameters by default

Function parameters without assign specifier are shared assign by default now.
Furthermore, explicit shared function parameters without ‘const’ or ‘def’ specifier are set implicit to ‘auto’ now. They will be either ‘const’ or ‘def’ depending on the passed parameter.

With this it is possible to write const correct functions accepting mutable as well as const parameters the same time which generates a great generic programming building block.

This behavior is experimental but activated by default. You can switch it off by

  • define TEASCRIPT_DEFAULT_SHARED_PARAMETERS and/or TEASCRIPT_DEFAULT_SHARED_AUTO_PARAMETERS to false. (see Dialect.hpp)
  • set parameters_are_default_shared and/or shared_parameters_are_default_auto in the relevant Dialect class instance. (see Dialect.hpp)
  • for TeaScript Host Application use --old-deepcopy-parameters (this option will be removed in the future)

Some quick examples for illustration:

func test( a ) {}         
// will result in: 
    func test( const a @= ) {}  // (NEW)

func test( const a ) {}
// will result in: 
    func test( const a @= ) {}  // (NEW)

func test( def a ) {}
// NEW: will result in: 
    func test( def a @= ) {}    // (NEW)

func test( a := ) {}      
// will result in: 
    func test( const a := ) {}  // (OLD, still same)

// IMPORTANT:
func test( a @= ) {}
// will result in: 
    func test( auto a @= ) {}  // (NEW) whereby 'auto' is just used internally
//note: auto is not a keyword yet!


// for example the convenient stack_push function from The Core Library is implemented like this
func stack_push( def stack, val @= )
{
    _tuple_append( stack, val )
}

// with the new rules the val value will become 'auto val @=' internally.
def   some_mutable := 567
const some_const   := 123

def my_stack := _tuple_create()

stack_push( my_stack, some_mutable )
stack_push( my_stack, some_const )    // can be called without runtime error now

// Furthermore, now the values inside the stack still have the same constness.

debug my_stack.0 // possible output: my_stack.0 (i64, mutable, 0x15823da9e00, sc:3) : 567

debug my_stack.1 // possible output: my_stack.1 (i64, const, 0x15823da92c0, sc:3) : 123

my_stack.1 := 10 // will result in Eval Error: Const assign: Variable is const! Cannot assign to const variables!

my_stack.0 := 1  // works, value is 1 now

BSON Support

TeaScript has now BSON Support. This feature is experimental and only available if nlohmann::json is used as the JsonAdapter in TeaScript.

The pre-compiled free TeaScript Host Application has this feature enabled (download link above).

For the TeaScript C++ Library or if you want/need to compile the Host Application by yourself, you must change the used JsonAdapter first as described here: How to change the Json Adapter.

You can then import / export from JSON to (BSON) Buffer and vice versa. See at the end of corelibrary_test05.tea and below:

// create some json (tuple)
def json := readjsonstring( "{ \"key\": \"secret\", \"id\" : 123 }" )

// to BSON
def bson_buffer := writebsonbuffer( json )

// bson_buffer is of type Buffer (std::vector<unsigned char> from C++ view)

println( bson_buffer )
// prints: [29, 0, 0, 0, 16, 105, 100, 0, 123, 0, 0, 0, 2, 107, 101, 121, 0, 7, 0, 0, 0, 115, 101, 99, 114, 101, 116, 0, 0]

// could store as binary file
writefile( tempdir() % "bson_blob.bin", bson_buffer, false )

// or do whatever you want with it...

// ... back to JSON
def json_new := readbsonbuffer( bson_buffer )
tuple_print( json_new, "root", 2 )
//prints:
//root: <Tuple>
//root.id: 123
//root.key: "secret"

Breaking Changes

Changes in CoreLibrary functionality

clock_utc() has a breaking change on Windows and on Linux with gcc >= 13.
clock_utc() now returns always the UTC time in system clock representation.
On Windows and on Linux with GCC >= 13 this is a breaking change since the leap seconds are not counted anymore.
The previous wrong behavior resulted due to a misunderstanding of the underlying C++ API.
Now there isn’t an annoying offset to the system clock time anymore.

strtrim() first parameter was a const @= by accident. changed to correct def @=.
Now the function will not take a const string as input parameter anymore!
This worked because of a const correct bug, which has been fixed now (see C++ API level breaking changes below).

Following functions are now returning Error instead of Bool(false):
_strtonum/_strtonumex
to_i64/to_f64
_strfromascii
readfile
read[toml|json]file
read[toml|json]string
write[toml|json]string
read/writebsonbuffer
all _buf_get_x functions

Changes on C++ API level

T & ValueObject::GetValue() was not const correct for the case the inner value was const.
In that case also a non const reference was returned and the const value could be modified. This has been fixed now.
Calling GetValue() from a non const value object which has a const inner value will throw an exception::bad_value_cast now.
This can result in unexpected exceptions in old code!
To fix this, you can call GetValue< Type const >() instead.
A more clean variant would be to replace all GetValue() calls by one of the new GetConstValue()|GetMutableValue() or GetValueCopy().

For temporarily restore the old but wrong behavior define this macro TEASCRIPT_DISABLE_GETVALUE_CONSTCHECK.
This switch will be removed in the next release!

tuple::foreach_element/foreach_named_element are working now with either a const ValueObject or a mutable one.
If the functor takes mutable objects all nested const objects (if any) will be skipped.
This is a breaking change but the old behavior was faulty as it allowed to access const elements as mutable reference.

fixed typo in Collection::RemoveValueByKeyWithPlaceholder()

Deprecation

The following deprecated parts have been finally removed from this release:

The Core Library function CoreLibrary::ExitScript() has been finally removed.
Please, use _Exit val instead or directly throw teascript::control::Exit_Script from C++.

–old-mutable-parameters command line option has been finally removed.
For the case you want use a legacy dialect of TeaScript with default mutable parameters you need to change the used Dialect instance, see Dialect.hpp

The following parts are now deprecated and will be removed in some future release:

class LibraryFunction0<> to LibraryFunction5<> are deprecated now.
Please, use the new and generic one fits for all LibraryFunction<> instead.

_f64toi64 is now deprecated.
Please, use the cast operator as instead (recommended) (or alternatively to_i64).

Misc

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 old clang versions

TeaScript is a C++20 library and relies on C++20 language and standard library features. Unfortunately, many old clang versions in combination with libc++ don’t 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:

  • use clang 20 or newer.
  • 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

  • Enums – it would be interesting to see if it is possible to implement an enum equivalent in TeaScript which will be transformed to real constant integrals (and not variables with the enum value name!) after parsing and compiling to TeaStackVM code. But still, after loading the program, the enum constants need to be resolved by their name as well!
  • switch/match statement. Enums (and much more) could be really useful only when dispatched via a switch or match statement. The goal here would be to not only ease programmers life by offering syntactic sugar for if-else cascades but also bringing a significant performance advantage.
  • records – a mixture between C structs and C++ classes. Internally the definition of a ‘record’ records the creation of a Named Tuple which then can be used as an object of this record.
// possible future code
def Point := record {
  def x := 1
  def y := 2
}

def p := Point()   // record instance have call operator.

p.x    // 1
p.y    // 2

// [...] more to come...

Currently, I think, there is not a high demand for Enums yet. Before implement those, TeaScript needs a switch / match statement first.

From my current view the records are the most interesting for me to implement next, but this may change.

What do you think about this actual TeaScript release?

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. πŸ™‚

Leave a Reply

Your email address will not be published. Required fields are marked *