Skip to content

Commit

Permalink
implement a mutable JavaScript interpreter
Browse files Browse the repository at this point in the history
  • Loading branch information
CrowdHailer committed Aug 19, 2024
1 parent 0a1e56f commit 3728488
Show file tree
Hide file tree
Showing 10 changed files with 633 additions and 9 deletions.
74 changes: 65 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,73 @@
# Eat Your Greens
# Eat Your Greens (EYG)

**Experiments in building "better" languages and tools; for some measure of better.**

The name is a reference to the idea that eating your greens is good for you but the benefit is only realised at some later time. The idea in most these projects is to be very explicit about something, for example side effects. In doing so can we make tooling that is much better at giving insights about a program and in fact give back more than the initial constraints took away.
## Philosophy

I have experimented with this priciple in building actor systems, datalog engines but most work is now focused on [Eyg](https://eyg.run). A language for programs that are fully explicit in all side-effects and therefore easier to run any where.
"Eat Your Greens" is a reference to the idea that eating vegetables is good for you, however the benefit is only realised at some later time.

[![video introduction](https://videoapi-muybridge.vimeocdn.com/animated-thumbnails/image/4c0396bb-b75b-4e16-80fe-72a18e8dc725.gif?ClientID=vimeo-core-prod&Date=1690784024&Signature=e9dff0472fadd261013891cb2ab7d332486d3185)](https://vimeo.com/848449632?share=copy)
Projects in this repo introduce extra contraints, over regular programming languages and tools. By doing so more guarantees about the system can be given.

## Development
For example; EYG the language has only managed effects which gives the guarantee a program will never crash.

I post videos (2-10 min) of features as I develop them https://petersaxton.uk/log/.
They are mostly intended as notes for myself, but are the closest thing you will find to a documentation, or roadmap.
I have experimented with this priciple to build actor systems, datalog engines and others.
For more context on these experiments you can view my devlog, short videos of experiments as they develop https://petersaxton.uk/log/.

If you want to chat I hang out in the [Gleam Discord](https://gleam.run/community/).
If you to build anything in a safe functional expressive language, I would suggest use [Gleam](https://gleam.run/) instead. After all it's what I use.
However most work in this repository is now focused on EYG the language.

## Getting started

### Run an EYG program

```sh
# Write the hello world program to the file `hello.json`.
# Code in EYG is structured (not a text file) we'll get back to this.
echo '{"0":"a","f":{"0":"p","l":"Log"},"a":{"0":"s","v":"Hello, World!"}}' > hello.json

# Run the hello world program using the JavaScript interpreter in your shell.
cat hello.json | npx eyg-run
```

This example requires `node` and `npx` is installed on your machine.

EYG can be run in a variety of places using different interpreters/compilers.
Or it can be run in any environment by implementing your own.

EYG is a very small language and designed to be easy to implement.
The JS interpreter (used in the introduction above) is <500 lines of code.
View [the source](./packages/javascript_interpreter/src/interpreter.mjs) for details.

Other interpreters in this repo:

- Gleam
- Go

### Writing an EYG program

#### Use the EYG structured editor.

https://eyg.run/drafting/

This tool is used to manipulate the program tree directly.
It is nice because you can never write an invalid program.
However, you can't type text as you are used to.

#### Write the JSON by hand.

Follow the [spec](./ir/README.md) for the EYG Intermediate Representations(IR) to write your program by hand.

This is not recommented, but is a useful thing to do if you want to try writing your own tooling.

#### Bring your own syntax and parser.

If you find syntax interesting or like opinions your can bring the syntax you choose.
Any syntax that is parsed to an EYG Intermediate Representations(IR) can be used.

Most [language tutorial](https://craftinginterpreters.com/contents.html) start with parsing syntax if you want a place to start.
Once you have a working parser you can use EYG to have a working program.

Something like this would be great.

```sh
cat hello.eyg | yourparser | npx @eyg-run
```
56 changes: 56 additions & 0 deletions packages/javascript_interpreter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# JavaScript Interpreter for [EYG](eyg.run)

A simple interpreter for EYG programs in any JavaScript environment.

This interpreter design aims for simplicity while being "fast enough".
All features of the language are implemented;
including extensible Records, extensible Unions and Algebraic effects.

The interpreter implementation is <500 lines of code.
To explore the interpreter I recommend starting at `./src/interpreter.mjs`.

## Usage - node

EYG programs can be run using the default node runtime, as follows:

```bash
echo '{"0":"a","f":{"0":"p","l":"Log"},"a":{"0":"s","v":"Hello, World!"}}' > hello.json
cat hello.json | npx eyg-run
```

This runtime includes the following effects.
- `Log(String -> {})`


**The interpreter does not perform any type checking.**

## Build your own runtime

EYG quickly allows you to support the specifics of the platform you run it in.
The following code presents the same effects to the program as the default node version,
but instead of logging to the console `Log` effects use the `window.alert` functionality.

```js
import { exec, Record, native } from "https://esm.run/eyg-run";

const extrinsic = {
Log(message) {
window.alert(message)
return (Record())
}
}

async function run() {
let response = await fetch("./hello.json")
let source = await response.json()

let result = await exec(source, extrinsic)
console.log(native(result))
}
run()
```

Any extrinsic effects can be made available to EYG programs.
They can be as specific as needed for different platforms.

For example EYG could be embedded in a todo app with such effects as `CreateTask`, `MarkAsDone` etc.
20 changes: 20 additions & 0 deletions packages/javascript_interpreter/bin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env node

import * as fs from "fs";
import { exec, native, Record } from "./src/index.mjs";

const input = process.stdin.isTTY ? "" : fs.readFileSync(process.stdin.fd, 'utf-8');
const source = JSON.parse(input)

const extrinsic = {
Log(message) {
console.log(message);
return Record()
}
}
const value = await exec(source, extrinsic)

console.log(native(value));



23 changes: 23 additions & 0 deletions packages/javascript_interpreter/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions packages/javascript_interpreter/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "eyg-run",
"main": "./src/index.mjs",
"bin": {
"eyg": "./bin.js"
},
"type": "module",
"dependencies": {
"immutable": "^4.3.7"
},
"version": "0.2.0"
}
3 changes: 3 additions & 0 deletions packages/javascript_interpreter/src/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { native } from "./value.mjs"
export { exec } from "./runner.mjs"
export { List, Record } from "./value.mjs"
Loading

0 comments on commit 3728488

Please sign in to comment.