Compare commits

...

2 Commits

2 changed files with 100 additions and 1 deletions

View File

@ -9,6 +9,95 @@ programming language which compiles to JavaScript?
The answer, it turns out, is yes!
## Example
Here's a simple example: Let's define a simple fibbonacci function:
```json
[
"slow_fibb",
{
"0": 0,
"1": 1,
"n": ["+", ["slow_fibb", ["+", "n", -2]], ["slow_fibb", ["+", "n", -1]]]
}
]
```
Our fibbonacci function, called `"slow_fibb"` has two base cases: If `1` is
passed, it returns 1, and if `2` is passed, it returns `2`. Otherwise, it
recursively calls `slow_fibb` on `n-1` and `n-2`, then adds the results
together.
If you need to take some time to understand what's going on here, feel free to
pause and work through it. Further down in this README, you can find a
walkthrough of what various different language features do, and what the syntax
looks like. If you're familiar with ML-style languages and Lisps, parts might
feel familiar to you. If you're more used to imperative languages like C or
Python, one thing to keep in mind is that all operations are prefix. This
means that to add together 1 and 2, we write `["+", 1, 2]` rather than `[1, "+", 2]`.
Got it (mostly) figured out? Sick! Now let's try running our code through the
compiler (with pretty-printing turned on for our sake) and see what we get:
```bash
$ python3 compile.py slow_fibb.json | npx uglify-js -c evaluate -m --beautify
function slow_fibb(b) {
return 0 == b ? 0 : 1 == b ? 1 : slow_fibb(b - 2) + slow_fibb(b - 1);
}
```
Just what we ordered! But let's try it out to make sure it works.
```js
> function slow_fibb(b) {
... return 0 == b ? 0 : 1 == b ? 1 : slow_fibb(b - 2) + slow_fibb(b - 1);
... }
undefined
> slow_fibb(10)
55
```
Perfect!
Ready for something a little more complex? How about an `O(n)` fibbonacci function:
```json
[
"fast_fibb",
[
"fibb_helper",
{
"a": {"b": {
"0": "a",
"1": "b",
"S S n": ["fibb_helper", ["+", "a", "b"], ["+", ["+", "a", "b"], "b"], "n"]
}}
},
["fibb_helper", 0, 1]
]
]
```
Wondering about those `S` patterns? Those denote a predecessor. So `S S n` only
matches integers greater than 1, and binds `n` to 2 less than the input.
Alright, let's try compiling that!
```bash
$ python compile.py fast_fibb.json | npx uglify-js -c evaluate -m --beautify
const fast_fibb = function n(i) {
return f => b => 0 == b ? i : 1 == b ? f : n(i + f)(i + f + f)(b - 2);
}(0)(1);
```
Nice!
Want to mess around with things yourself? Both of these fibbonacci functions
can be found in the [`fibb.json`](./fibb.json) file in this repository, and all
you need to run it is a recent-ish version of python! Check out the next
section for more details.
## Setup
This project has no dependencies, just Python3! That means all you have to do to run is install Python3 and clone the repository. Once you have the code, you can either run:

12
ir.py
View File

@ -38,8 +38,18 @@ class ReplHole:
return '[]'
def render(self) -> str:
def render_var(var_name: str, var_expr: Expression) -> str:
match var_expr:
case LetBinding(prev_func_name, MonoFunc(formal, func_body), Variable(mb_prev_func_name)):
if mb_prev_func_name == prev_func_name:
func_body_with_new_name = func_body.subst(Variable(var_name), prev_func_name)
return f'function {var_name}({formal}) {{return {func_body_with_new_name.codegen()}}}'
else:
raise Exception('This useless LetBinding should have been removed by the optimization stage')
case _:
return f'const {var_name} = ({var_expr.codegen()});'
return '\n'.join(
f'const {var_name} = ({var_expr.codegen()});'
render_var(var_name, var_expr)
for (var_name, var_expr) in self.val_bindings[::-1]
if var_name not in types_.BUILTINS_CONTEXT
)