FixScript is an extensible scripting language designed for simple implementation and strong support for both backward and forward compatibility.
You can use FixScript both as a standalone and embedded programming language. You can build standalone native executables for any supported platform from every platform.
The best results are obtained when combined with the C language to get a very powerful tool. This way you can use the best of the two worlds, using the language for the high-level stuff and C doing the interoperability and fast stuff while having a good integration between both languages.
You can learn more about FixScript in the Introduction to FixScript blog post.
Features
- simple and efficient implementation
- JIT compiler available for x86 and x86_64 CPU architectures (an interpreter is used for other architectures)
- object-oriented by convention (avoids the overheads of inheritance in script languages and the more descriptive names of functions and fields make more clear what data structures are being manipulated in absence of static types)
- full class syntax (including type checking) is also available as a token processor
- available types: 32-bit integers, 32-bit floats (without denormals), (compressed) resizable arrays, unicode strings, shared arrays, hash tables, function references, weak references, and native handles
- can also work with 64-bit (or more) integers and 64-bit floats using intrinsic functions
- mark-and-sweep garbage collector (each thread having own heap with ability to explicitly share data)
- written in C as a single file and a header (approx. 780KB source file including JIT, 26kLOC, 400KB object code)
- familiar C-like syntax with ability to create new syntaxes using token processors (macros)
- minimal amount of built-in functions, suited well for embedded and portable usage
- ability to speed-up specific functions written in FixScript by using optional native functions without sacrifying backward compatibility
- ability to return second value, primarilly to report errors, when the error value is not retrieved it fallbacks through functions similar to exception handling
- designed for backward and forward compatibility of both the scripts and the host application, the language and built-in functions are fixed
- designed to catch more errors at compile-time than is usual in script languages
- designed for multithreading (although in specific ways)
- friendly to native compilation (most operators work with a single type only)
- integer overflow emits an error
- ability to set execution time limit
- ability to reload scripts in-place to change behaviors of functions (live update)
- licensed under ZLIB license (doesn't need any notices in binary distributions)
The libraries extend the features further:
- simple to use macro system
- static typing and direct support for classes
- multithreading
- direct declaration of C and JS functions for calling
Status
The first public release was released after 2 years of development at the end of 2019 and the language is currently in the Beta phase. This means that it's pretty much in it's final shape, but some (minor) incompatibilities can occur. Also some additions are still to be expected. Currently most of the work is being done in creating and improving of the common libraries as well as various improvements and bugfixes.
The plan is to use the language in real world usages for a longer time to catch some potential pitfalls before the final stabilization (many were already resolved). That will occur once the 1.0 version is reached. After that point, the language will be frozen forever, providing a super stable base to build on. The language is extensible in various ways so this doesn't limit possible improvements into the future.
After the 1.0 version, the language (including the built-in functions, serialization format, etc.) will be unchanged, however the implementation is expected to be improved over the time. Specifically the C API/ABI will remain backward compatible within major versions, adding new functions in minor versions, and no API/ABI changes in patch versions. It is expected that every major version will remain supported indefinitelly at basic level (bugfixes only).
The language is by default using a JIT compiler (or an interpreter when not available for given CPU architecture). An optimizing native compiler is also in development based on native codegen that was developed as part of a different project. The goal is to make the language fast while maintaining good ratio between performance and implementation simplicity.
Examples
The base language looks like this:
const @PI = 3.141592654; const { BASE_first, BASE_second, BASE_SIZE }; const { SUBCLASS_third = BASE_SIZE, SUBCLASS_SIZE }; function base_create(first, second) { var base = object_create(BASE_SIZE); base->BASE_first = first; base->BASE_second = second; return base; } function subclass_create(first, second, third) { var subclass = object_extend(base_create(first, second), SUBCLASS_SIZE); subclass->BASE_first = 0; subclass->SUBCLASS_third = third; return subclass; } function test() { log({"the value of PI: ", PI}); var array = []; for (var i=0; i<length(array); i++) { ... } var hash = {}; for (var i=0; i<length(hash); i++) { var (k, v) = hash_entry(hash, i); ... } return {float(1) + {2.0 * 3.0}}; }
However you can create and use libraries for extending the base syntax, for example the same code using the implementation of classes:
use "classes"; const @PI = 3.141592654; class Base { var first: Integer; var second: Integer; constructor create(first: Integer, second: Integer) { this.first = first; this.second = second; } } class Subclass: Base { var third: Float[]; constructor create(first: Integer, second: Integer, third: Float[]) { super::create(first, second); this.first = 0; this.third = third; } } function test() { log("the value of PI: " + PI); var array: Integer[] = []; for (var i=0; i<array.length; i++) { ... } var hash: Integer[String] = {}; foreach (var k, v in hash) { ... } return 1 + 2.0 * 3.0; }
You can find more examples in the Introduction to FixScript blog post.