Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
A Tiny Lispy Transpiler (github.com/lingdong-)
110 points by oumua_don17 on Dec 27, 2020 | hide | past | favorite | 24 comments


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.


Lisp is imperative.

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.


> instead of using tail call recursion

If your language supports that. TCO is not a standard feature of Lisp.


Regular recursion however is.


Why did this get downvoted? This is correct description of the project. It is even written in readme.


Maybe because people being pedantic gatekeepers about what is and isn't a true lisp is tedious.


Gatekeepers like the guy who wrote it. Words mean things.


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

[0] https://github.com/siraben/lisp-to-js

[1] https://github.com/jozefg/pcf/blob/master/explanation.md


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.

https://lingdong.works/


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]).

[1] https://github.com/krcz/zygote [2] https://www.ccs.neu.edu/home/stchang/popl2020/index.html


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!


As a tangent, Typescript's type system has become really powerful, expressive and quite pleasant to work with.


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.


I agree! I’m learning Clojure at the minute and it’s just confirming how important static typing is to me now after TS.

I’m using and I get that it’s powerful but TS just hits that sweet spot of being immensely useful with out getting too much in the way.


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.

[0] https://haxe.org


Online playground: https://waxc.netlify.app/

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. Shock to see it does not support functional style.


> 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!


closing parenthesis on a new line


From the Readme:

>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.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: