Voxel is a loosely-typed programming language that is designed to be powerful, versatile, easy-to-use and portable. It has a C-like syntax and has many features that can be found in other modern programming languages.
Portability is a key goal of Voxel — especially so that programs written in it can run on low-powered hardware, such as microcontrollers. Voxel achieves this by compiling source code into a bytecode format, called VxC (Voxel Code). Voxel can generate space-efficient bytecode by using a number of optimisations, including name mangling and dead code elimination (DCE) via static code analysis.
Voxel only generates the necessary bytecode to provide the supporting features needed by the main program's code. As an example, here's a hex dump of all the bytecode needed to output "Hello, world!":
56 78 43 01 22 48 65 6c 6c 6f 2c 20 77 6f 72 6c VxC."Hello, worl
64 21 0a 00 33 01 2e 69 6f 5f 6f 75 74 00 70 00 d!..3..io_out.p.
Examples of Voxel programs can be found in the examples
and test
directories.
- Functions — named and anonymous
- Closures that allow access to non-global variables outside a function's scope
- Spread and rest syntax (
...
) to dynamically provide argument values to functions and allow functions to become variadic
- Expressions that follow a defined operator precedence
- Short-circuit evaluation of logical operators using
&&
and||
(evaluation of subsequent operands is aborted if result's value is guaranteed to betrue
orfalse
) - Eager evaluation of logical operators using
&&&
and|||
(all operands are evaluated — this produces simpler bytecode but may perform unnecessary computations at runtime) - Assignment operators that allow variables, list items and properties to be updated based on their previous value
- Short-circuit evaluation of logical operators using
- Lists and their common operations (such as access through index accessors and
push
andpop
methods) - Objects
- Object-oriented programming features including classes and prototype-based multiple inheritance
- Methods that have access to their associated object by using
this
keyword, including constructor methods that are called by usingnew
- Getters and setters to simplify object interfaces and perform data validation (by prefixing class method declarations with
get
orset
) - Calling parent methods by using the
super
keyword to call the constructor method (super()
) or other methods (super.methodName()
)
- Imports of other Voxel files and libraries
- Modular, namespaced approach where each imported file is called in the correct dependency order and where symbol names across files won't collide
- Relative imports by specifying a path relative to the Voxel file
- Circular imports where A can import B and B can import A
- Exceptions to throw and handle errors with
throw
andtry
/catch
- Weak references for fine control of memory management, with
weak
method to obtain weak reference andderef
method to obtain the referenced value - Enumerations (enums) to represent a set of discrete named values and to act as error codes
- Automatic value assignment for enumeration entries that have not been explicitly assigned a value
- Enumeration entry lookup to find the original identifier string by enumeration entry value
- Static macros for libraries to determine whether symbols and properties are used or not to enable efficient code generation
- Dead code elimination to produce space-efficient VxC bytecode
- Truthiness estimation so that unreachable code in
if
statements andwhile
loops (due to condition always beingtrue
orfalse
) is removed - Tree shaking to remove functions that are never called and variables that are never read
- Potential side effect detection when deciding how unused variables should be removed so that their assigned value is still evaluated if its expression contains a function call or getter method
- Truthiness estimation so that unreachable code in
- Multithreading through the
threads
library, which allows multiple functions to be executed concurrently - String pattern matching through the
patterns
library, which is an alternative to regular expressions found in other programming languages
- Stack-based virtual machine that enables powerful manipulation of data and easy passing of data as arguments to commands
- Position-independent code to allow easy linkage of libraries without absolute address references
- Read-only execution to reduce potential for exploits whereby untrusted code is loaded and executed
- Sandboxed, untrusted code could be allowed to execute by creating a syscall in C that instantiates a new Voxel context with
voxel_newContext
and loads in the byte buffer from a syscall argument as code for execution
- Sandboxed, untrusted code could be allowed to execute by creating a syscall in C that instantiates a new Voxel context with
- Compact code with a space-efficient design
- Easy-to-read instruction set with all instruction tokens as printable ASCII characters
- Loose typing of data with type casting where necessary
Before compiling projects, you currently must first have Deno installed. We are currently in the process of transitioning VxBuild into a self-hosting compiler.
To compile Voxel source code (.vxl files) into VxC bytecode (.vxc files), run:
deno run --allow-read --allow-write tools/vxbuild-js/vxbuild.js examples/hello.vxl -o examples/build/hello.vxc
To build the Voxel runtime, run:
./build.sh --runtime
You can then run a .vxc file like this:
runtime/build/voxel code.vxc
To build the libvoxel integration example, then run the hello
example code, run:
./build.sh --examples && examples/build/hello
To test all features of Voxel, run:
./test.sh
This will perform all tests listed in the test
directory against their expected outputs and for memory leaks (the latter runs a test's program in an infinite loop and measures the difference in memory consumption). Tests are automatically performed for new commits on pull requests and must all pass before a pull request can be merged.