Stability
JaiScript 0.5 is a beta: the language and its contract are complete, and the version stays 0.x until 1.0. This page sorts the language and the embedding API into what is settled and what a release may still change.
- A settled rule is the contract. Where the implementation departs from one, the implementation is the bug and changes to match.
- Everything under May change can move in a minor release, and so can anything this page does not mention.
Versions
A version is 0.minor.patch (0.5.2). On this page a release that may change something is a minor
release: 0.5 to 0.6.
- Within one minor version (0.5.2 to 0.5.3) the CMake package declares the releases compatible:
find_package(JaiScript 0.5)accepts any 0.5.z and refuses 0.6. - Each release is a tag in the repository
(
v0.5.2). The repository keeps no changelog file; the history between two tags is the record of what changed. - Precompiled
.jaibitefiles and saved archives are tied to the build that wrote them, at any version step (below).
Settled: the language
| Area | The rule |
|---|---|
| Value semantics | Assignment, by-value parameters and returns copy. Copying an instance, array or map copies its values and shares its handles. Aliasing exists only where a declaration asks: T& or shared_ptr<T>. A non-const reference binds storage of its own type; a const T& also binds a temporary, which lives as long as the reference; a store through a reference follows the target's declared type. |
| The type ladder | A typed slot is enforced, auto is inferred then enforced, var stays dynamic. Declarations, assignments, by-value parameters, typed returns and field stores convert through one table, with at most one converting constructor. |
| Strict checking | The checker always runs before anything executes. A typed-to-typed mistake it can see fails the load; a value that came through var fails at run time, where catch sees it. [[warn_mode]] and [[strict_mode]] apply to a region, extern declares a host name, cast<T>, cast<T&> and cast<shared_ptr<T>> are the explicit boundary. A program that runs clean is never rejected. |
| Typed containers | array<T> and map<K, V> hold what they declare: a store converts by the element rules or raises. array<var> is the dynamic form. A copy into a var or a slice keeps the element type, map keys never coerce, array<Base> holds subclasses, handle-typed elements take null. Maps iterate and serialize in key order. |
| Integers and chars | int is 64-bit. Overflow raises a catchable error (or wraps in a build that defines JAISCRIPT_WRAP_ON_OVERFLOW) and is never undefined. Shifts operate on the 64-bit pattern. char is a byte that enters arithmetic as 0..255 and converts only through cast. |
| Evaluation order | A call evaluates its callee, then its arguments left to right; a by-value argument keeps the value it had when evaluated. A binary operator evaluates its left operand first. Errors surface in that order. |
| Captures | A lambda captures locals and parameters when it is created: [] nothing, [=]/[x] a copy, [&]/[&x] the variable itself (it outlives its frame), [this] the instance. Named lists are exhaustive. An anonymous function(...) captures as [&]. A global is never captured: the lambda reads it when it runs. A named function declared inside a function body captures nothing. |
| Names and scopes | Blocks scope as in C++. In a method a bare name resolves to a local, then a member, then a global. A namespace is a scope, and a catch variable belongs to its block. |
| Access control | Two levels: public: reaches everyone, private: the declaring class and its derived classes; protected: is a synonym of private:. |
| Operators | A class overloads binary + - * / % & | ^ << >> < > <= >= == != <=>, unary - + ~, and =, [], (). a op= b is a = a op b. Truthiness is bool to_bool(), and !v is !to_bool(v). == compares arrays, maps and instances member by member (a user operator== first); same_object asks identity. obj.method without a call is an error; reflect::bind makes the callable. |
| Statements | switch breaks by default and fallthrough; continues. ++ and -- work on every lvalue. An assignment expression's value is the value the destination stored. A declaration without an initializer takes its type's default. |
| Strings | A string is bytes; utf8_string holds the same bytes and counts characters. |
| Modules | include is textual and runs each time it is reached. import runs a module once per engine, may appear wherever a statement may, and re-runs a module whose file changed when reached again. |
| Coroutines | coroutine prefixes any function or method; it runs to each yield and is driven by a range-for or next()/next(n). |
| Hot reload | Redefining a class migrates its live instances in place, and migration never fails. A source that does not parse or check does not load; the previous definition keeps running. |
| Edge rules | contains, index_of, has and switch cases compare with == (switch (1) matches case 1.0). to_string(x) uses a class's own to_string(). 0.0 and -0.0 are one map key. Reading a missing map key raises, as does a compound assignment on one; m[k] = v inserts. A JSON round trip keeps chars and non-string map keys, and from_json propagates an error from a class's constructor. A bare throw; with no active exception raises the catchable Bare throw with no active exception thrown. A compound assignment with an operand of the wrong type raises one text at every destination. A derived value in a base-typed slot keeps its class. |
| Two backends, one behavior | The interpreter and the VM produce the same values, output and error text for every program. The one exception is recursion depth: where non-tail recursion meets the recursion error, and the VM reusing a self tail call's frame. |
Settled: embedding
- An engine is an object.
jai::engine::make()returns one. There is no global state: engines share nothing and run on separate threads without locks. Quickstart - Registration is the allowlist. Beyond the core builtins, a script reaches only what its engine registered. Three doors
- The backend is chosen before an engine's first execute. Backends
- A script cannot outlast its limits. Exceeding the execution budget is terminal: no
catchsees it. The memory cap raises once per execute as an ordinary catchable error, so a script can free memory and continue; a second raise in the same execute is terminal. Safety Rails - Each way of calling script fails one way.
executeandexecute_filethrow on every failure (jai::parse_error,jai::static_check_error, or ajai::runtime_error) and never report one through their return value. A call that returns achecked_result(as_function(), a coroutine handle'snext) reports every failure in it. Astd::functionfromconvert_script_functionthrowsjai::runtime_error, and a signal receiver's error throws out of the emit. Error surfaces - Precompiled equals source. Running a
.jaibitebehaves exactly like executing the source it was parsed from. jaibite
May change
| What | How it may change | What holds |
|---|---|---|
| The C++ API's shape | signatures, header paths and helper type names | the model above, and the meaning of every call the embedding pages and the reference describe. Anything in a detail namespace (jai::detail::engine_access included) is the library's own and can change or go. |
| Performance | what each construct costs, which shapes the VM compiles to its fastest forms, every published figure | a performance change never changes what a program computes |
The .jaibite format version | it moves whenever the parser's output changes, and a bite loads only in a build with the same version | ship bites built by the release that loads them; the automatic disk cache re-parses a stale file |
| What the checker reports at load | a mistake that raises at run time can become a load error | a program that runs clean keeps loading clean |
| Error text wording | the catalogued texts can be reworded | both backends raise the same text |
| Serialized data | the layout to_json, to_binary and the C++ archives write | nothing across releases: data saved by one is not promised to load in the next |
| Parallel admission | which parallel_for, parallel_transform and parallel_reduce bodies are admitted | a body not admitted is refused with a catchable error, never run serially; results are deterministic at any worker count |
| String mutators | whether to_upper, trim and the other mutating string methods change their receiver in place | |
| Script-extensible objects | extend(obj, Class) and its host side | |
| Coroutine lambdas | absent; they may be added |