Home

Awesome

wax

wax

wax is a tiny language designed to transpile to other languages easily. Currently supported backends: C, C++, Java, TypeScript, Python, C#, Swift, Lua, as well as directly to WebAssembly.

Playground | Quickstart | Examples | IDE

The goal of wax is to be a "common subset" of most major imperative programming languages. By lacking the unique fancy features in each of these languages and being as boring as possible, wax transpiles to all of them seamlessly, producing outputs that are:

These of course, from the programmers' perspective, come at the cost of losing some of the interesting features offered by other languages. Nevertheless, wax retains the crucial bits for a programming language to be productive.

The syntax of wax is inspired by WebAssembly Text Format (wat), hence the name. Though it uses S-expressions reminiscent of the Lisp family, it is actually quite imperative and most resemblant of C in its design. The idea of transpiling to many languages is inspired by Haxe.

wax is currently experimental, so there might be bugs as well as aspects to be improved, in which case PR and Issues are very much appreciated.

Hello World

(func main (result int)
  (print "hello world!")
  (return 0)
)

Newlines and indentations are entirely cosmetic. You can use any type of brackets anywhere (() [] {}). You can mix different types of brackets if you somehow prefer that.

[func main [result int] [print "hello world!"] [return 0]]

{func main {result int} {print "hello world!"} {return 0}}

{func main [result int] 
	(print "hello world!") 
	(return 0)
}

Here's an in-place quicksort to get a quick taste of the language:

;; sort array in-place for index range [lo,hi] inclusive
(func qksort_inplace (param A (arr float)) (param lo int) (param hi int)
	(if (>= lo hi) (then
		(return)
	))
	(let pivot float (get A lo))
	(let left  int lo)
	(let right int hi)
	(while (<= left right) (do
		(while (< (get A left) pivot) (do
			(set left (+ left 1))
		))
		(while (> (get A right) pivot) (do
			(set right (- right 1))
		))
		(if (<= left right) (then
			(let tmp float (get A left))
			(set A left (get A right))
			(set A right tmp)
			(set left  (+ left 1))
			(set right (- right 1))
		))
	))
	(call qksort_inplace A lo right)
	(call qksort_inplace A left hi)
)

(func qksort (param A (arr float))
	(if (! (# A)) (then
		(return)
	))
	(call qksort_inplace A 0 (- (# A) 1))
)

As you might have noticed, writing in wax is pretty much like writing an abstract syntax tree directly!

There're many more examples, check them out here or on the online playground.

Overview

The Compiler

This repo contains a reference implementation of wax called waxc, written from scratch in C99.

 _____                                           
|||'  |                                          
|''   |                                          
|_WAX_| Compiler                                 

built Oct 27 2020                               

USAGE: waxc [options] code.wax                   

OPTIONS:                                         
--c     path/out.c     transpile to c            
--java  path/out.java  transpile to java         
--ts    path/out.ts    transpile to typescript   
--py    path/out.py    transpile to python       
--cs    path/out.cs    transpile to c#           
--cpp   path/out.cpp   transpile to c++          
--swift path/out.swift transpile to swift  
--lua   path/out.lua   transpile to lua 
--wat   path/out.wat   transpile to webassembly         
--json  path/out.json  syntax tree to JSON file  
--tokens               print tokenization        
--ast                  print abstract syntax tree
--silent               don't print info          
--help                 print this message 

Example

To compile the fib.wax example included in the example folder to C, and print the abstract syntax tree to terminal:

./waxc examples/fib.wax --c fib.c --ast

Now compile the C output with gcc and run the example:

gcc fib.c
./a.out

Compile to all targets and compile all outputs with target languages' compilers and run all outputs of target languages' compilers:

./waxc examples/fib.wax \
--c fib.c   --java  fib.java  --ts  fib.ts    --py fib.py  --cs  fib.cs  --swift  fib.swift --lua fib.lua;
gcc fib.c;    javac fib.java;   tsc fib.ts;                  csc fib.cs;   swiftc fib.swift;
./a.out;      java  fib;       node fib.js; python fib.py;  mono fib.exe;       ./fib;        lua fib.lua;

Compiling to C++ requires flag -std=c++11:

./waxc examples/fib.wax --cpp fib.cpp;
g++ fib.cpp -std=c++11;
./a.out;

Compiling to WebAssembly

waxc also supports compiling to WebAssembly Text Format (.wat). As the output needs to be further transformed to binary (.wasm) and wrapped with JS for calling, there's a couple more steps:

1. Compile to wat with waxc:

./waxc examples/fib.wax --wat fib.wat

2. Compile wat to wasm, using wat2wasm from wabt:

./wat2wasm fib.wat

3. Optional: Optimize with wasm-opt from binaryen for massive speedups, since (currently) waxc is not an optimizing compiler.

./wasm-opt fib.wasm -o fib.O4.wasm -O4

4. Now that the wasm is ready, you probably need some JS to call it, which basically involves WebAssembly.instantiate(bytes,imports) with console.log (and Math if you used (@include math)) as imports. Luckily you can find a readymade wrapper in tools/waxwasmwrap.js. To use:

Node:

const wrapper = require("tools/waxwasmwrap.js");
wrapper("fib.wasm",function(lib){
  lib.main();
});

Browser:

WAXWASMWRAP("fib.wasm",function(lib){
  lib.main();
});

All user-defined functions are exported under their original names, so you can call

lib.fib(42);

and so on.

Compiling the Compiler

You need:

To compile:

gcc src/wax.c -o waxc

That's it, no dependencies.

Alternatively you can run the Makefile:

VSCode Extension

Syntax Highlighting + Transpile + Compile + Run + Render

Get Started

Now that the compiler is compiled, head over to QUICKSTART.md for a tour of language features!