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 6c 6f 67 00 70 00 d!..3..log.p.
Since this program does not make use of Voxel's auxiliary features, all code from core.vxl in this example has been excluded through static code analysis and is therefore never compiled into the bytecode.
The following Voxel code produced the above bytecode:
syscall log("Hello, world!\n");
More examples of Voxel programs can be found in the examples and test directories.
- Functions — named and anonymous
- Closures that allow references to non-global variables outside a function's scope
- 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 betrueorfalse) - Eager evaluation of logical operators using
&&&and|||(all operands are evaluated — this produces simpler bytecode but may perform unnecessary computations at runtime)
- Short-circuit evaluation of logical operators using
- Lists and their common operations (such as access through index accessors and
pushandpopmethods) - Objects
- Object-oriented programming features including classes and prototype-based multiple inheritance
- Methods that have access to their associated object by using
thiskeyword, 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
getorset) - Calling parent methods by using the
superkeyword 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
throwandtry/catch - 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
ifstatements andwhileloops (due to condition always beingtrueorfalse) 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
threadslibrary, which allows multiple functions to be executed concurrently
- 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_newContextand 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 must first have Deno installed.
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.vxcTo build the Voxel runtime, run:
./build.sh --runtimeYou can then run a .vxc file like this:
runtime/build/voxel code.vxcTo build the libvoxel integration example, then run the hello example code, run:
./build.sh --examples && examples/build/helloTo test all features of Voxel, run:
./test.shThis 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).