Skip to content
Oeiuwq Faith Blog OpenSource Porfolio

synrc/shen

🐉 SHEN: Erlang JavaScript Compiler

synrc/shen.json
{
"createdAt": "2013-07-20T00:48:24Z",
"defaultBranch": "master",
"description": "🐉 SHEN: Erlang JavaScript Compiler",
"fullName": "synrc/shen",
"homepage": "",
"language": "Erlang",
"name": "shen",
"pushedAt": "2025-07-31T14:19:29Z",
"stargazersCount": 235,
"topics": [],
"updatedAt": "2025-08-27T16:36:04Z",
"url": "https://github.com/synrc/shen"
}

Simple

We support following stack by Erlang JavaScript compiler:

  • matches.js — Erlang-like matching syntax
  • tailrec.js — optimize tail calls
  • beacon.js — FRP event streaming

The only real practical fast solution is to translate Erlang AST into JavaScript using JavaScript helpers like matches.js and tailrec.js.

  • Compilation to JavaScript, node.js, Browser, Client-Side FRP
  • Macros, String Templates, Embedding Mode, Server-Side, N2O

In case of Client-Logic, Off-line clients, Client-side FRP Event System use can export you functions in module with -js attribute. All function will be stored to the same filename with js extension.

fac.erl:

-module(fac).
-compile({parse_transform, shen}).
-compile(export_all).
-js([start/0,fac/1]).
start() ->
N = fac(5),
console:log("factorial ~p", [J, N]).
fac(0) -> 1;
fac(N) -> N * fac(N-1).

Compile with Erlang:

$ erlc shen.erl
$ erlc -pa . fac.erl

And you will get fac.js:

var pattern = require("matches").pattern;
var start = pattern({
'': function() {
j = 5;
n = fac(j);
return console.log('factorial ~p',[j,[n,[]]]);
}});
var fac = pattern({
'0': function(x1) {
return 1;
},
'n': function(n) {
return n * fac(n - 1);
}});
start();

Now you can check:

$ node fac.js
factorial ~p [ 5, [ 120, [] ] ]

Let say we want to generate JavaScript in our Erlang code which is very useful for Server-Logic Event System, so you can write programs in Erlang and expand them into JavaScript using -jsmacro attribute. Specified functions will be expanded to JavaScript during compilation.

-module(fac).
-compile({parse_transform, shen}).
-compile(export_all).
-jsmacro([macro/3]).
macro(A,B,C) ->
ws:send('Bert':encodebuf(
[{source,'Bert':binary(A)},
{x,C},
{pickle,'Bert':binary(B)},
{linked,C}])).
main() ->
A = "1",
B = "2",
Script = macro(A,B,"3"),
io:format("JS Macro: ~s",[Script]).

Lets try it:

7> fac:main().
JS Macro: ws.send(Bert.encodebuf({source:Bert.binary(1),x:3,pickle:Bert.binary(2),linked:3}));
ok
  1. multiple clauses for lambdas
  2. list comprehensions
  3. more JavaScript/OTP mappings
  4. if statement :-)
* Maxim Sokhatsky
* Andrew Zadorozhny

OM A HUM