Here is the usage, which shows how many backends this supports (pretty impressive IMO).
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
I do think the syntax is a little bit jarring as someone who is used to LISP's omission of keywords like "then" and "do". Also, LISPers usually don't put parens on their own lines.
This isn't a Lisp, this is a paren based serialization of the syntax tree of an Algol like language.
Even the readme says as much: 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.
This is actual standard Common Lisp and basically the same (minus slightly different syntax) as the quicksort example from the link:
(defun qksort-inplace (a lo hi)
(declare (type integer lo hi)
(type (array float) a))
(if (>= lo hi)
(return-from qksort-inplace))
(let ((pivot (aref a lo))
(left lo)
(right hi))
(declare (type float pivot)
(type integer lo hi))
(loop while (<= left right) do
(loop while (< (aref a left) pivot) do
(setf left (+ left 1)))
(loop while (> (aref a right) pivot) do
(setf right (- right 1)))
(if (<= left right)
(let ((tmp (aref a left)))
(setf (aref a left) (aref A right)
(aref a right) tmp
left (+ left 1)
right (- right 1)))))
(qksort-inplace A lo right)
(qksort-inplace A left hi)))
Can be is not is. One can rewrite that in a functional not implace style. Less so for a language with a c like call stack. Which is why the op wrote it like that instead of using tail call recursion.
Writing a Lisp transpiler is a great exercise, and becomes more involved but more also educational as the source and target language become more and more distant. Here's my Scheme to JS transpiler that supports higher order functions, variadic lambdas, set! and recursion.[0] The function that performs the conversion is https://github.com/siraben/lisp-to-js/blob/0251892e55d1a88f9...
- Lisp -> JS is quite easy, good practice for AST representation and working with trees
- Lisp -> C is more challenging, you'll need to perform
closure conversion and generate C structures to hold the environment, see[1] as an example for another functional language
This is very interesting as the base level AST for a multi-step transpiler. Just pile transformations on top and grow your own language that transpiles to "everything".
I'm so astonished at the output of Lingdong that I find myself doubting that this is one person and may be a collective. I'm not making theories, I'm merely expressing my admiration of these works and these abilities.
Very interesting project! That is the first attempt I've seen at something I call "idiomatic code translation" - transpiling with result not only to be executed, but to be read and naturally included in existing codebases.
I've had similar idea on my mind since few years, but as for now the only output is a document explaining the goals [1], I'm still in exploration phase, as I want something more abstract, with more advanced language concepts implemented using macros (as in Turnstile+ does with dependent typing [2]).
I was just ruminating with a friend the other day how much I want a typed lisp with Clojure syntax and semantics, and TypeScript’s type system. This checks one of those boxes almost entirely (syntax), and it makes me wonder if it could be wrapped or retrofitted to provide the rest. Probably not the type system without significant work. Still, cool project nonetheless!
I agree! It’s made whole classes of problems easily solvable with great compile time validation. I just want it in a lisp with multiple bracket shapes and a good immutability story.
For what it’s worth, having gone the opposite direction (dynamic imperative langs -> clojure -> typescript), I highly recommend sticking with it for at least a little while. The dynamic type system must surely be painful if you’ve already enjoyed a good static type system, but getting a good instinct for how rarely code needs to be stateful and how easily maintainable even complex FP can be... really pays off when you come back to a language like TS.
Backend language support wise, it makes me think of Haxe [0]; would be easy to add a Haxe backend for Wax. Definitely Wax is a lot simpler to add backends for and all Haxe targets are garbage collected so might be an interesting experiment.
As expected, the transpiled code doesn't look great, with lots of unnecessary parentheses. That said, the number of back-ends is very impressive. Good job!
> Is this the first time to lisp related but non-functional language
This is a common misconception.
People conflate Lisp and purely functional programming as if Lisps are and were always purely functional, but this isn't historically accurate. Many early Lisps were deeply imperative - to an extreme that would seem crazy to use today - including even dynamic scoping!
>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.
Can someone change the title? Just because it uses parens does not mean it's in the lisp family.