Graff is a plain-text ASCII art programming language interoperable with JavaScript.
All programs proceed West to East.
To follow any graff, trace the arrow of any node exiting the East side to the next step in the program.
This is an experiment in designing a 2D programming language that is still plain-text.
So far, there is only an incomplete specification.
- If you want to bikeshed the speicification, open an issue!
- If you want to ask a question, also feel free to in an issue.
- If you want to tackle writing a parser, please do! I will likely give you write access.
I use asciiflow.com for all my diagrams.
Anyone who helps contribute will be listed here.
┌───────────────────────┐
│ │
│ ┌─────────────┼───────────────────────────────────────────────┐
│ │ │ │
│ │ ┌──────┐ │ │
│ └───►│ $1+1 ├─┘ ┌─────────────┐ │
│ └──┬───┘ ┌───────────────┬─────►│ console.log ├──┘
│ │ │ │ └─────────────┘
│ ┌───────────┘ │ │
│ ▼ │ │
└──►┌───┐ ┌──────────────┐ │ │
┌───┐ │$0 ├──►│ ├────┘ │
───►│ 1 ├────►└───┘ │ $0 │ │
└───┘ ├──────────────┤ │
│ │ ┌───┐ │
│ $0>100 ├──────►│ │ │
│ │ └───┘ │
├──────────────┤ │
│ │ ┌──────────┐ │
│ $0%15==0 ├──────►│"FizzBuzz"├─┤
│ │ └──────────┘ │
├──────────────┤ │
│ │ ┌──────┐ │
│ $0%5==0 ├──────►│"Fizz"├─────┤
│ │ └──────┘ │
├──────────────┤ │
│ │ ┌──────┐ │
│ $0%3==0 ├──────►│"Buzz"├─────┘
│ │ └──────┘
└──────────────┘ ┌────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────┐ │
│ │ │ │
│ ┌──────┐ ┌──────────────►│ 1 ├────────────────────────►│
├─────►│ ├──────┘ └─────┘ │
│ │ =1 │ ┌──────┐ ┌─────┐ │
│ │ ├─────────┤ ├─────────────►│ ├───────────────►│ ┌───────────────┐
│ └──────┘ │ =0 │ │ 0 │ │ │ │
│ │ ├───────┐ └─────┘ ├───►│ console.log │
│ └──────┘ │ │ │ │
│ │ ┌─────┐ │ │ │
┌────────────────┐ │ └─────►│ + ├───────────────►│ └───────────────┘
│ │ │ └┬───┬┘ │
───►│ process.argv[0]├─────►│ ┌────────┐ ┌───────┐ │ │ │
│ │ │ │ │ │ │ │ │ │
└────────────────┘ │◄──────────────┤ -1 │◄───┤ Self │◄───────┘ │ │
│ │ │ │ │ │ │
│ └────────┘ └───────┘ │ │
│ │ │
│ │ │
│ │ │
│ ┌────────┐ ┌───────┐ │ │
│ │ │ │ │ │ │
│◄──────────────┤ -2 │◄───┤ Self │◄───────────┘ │
│ │ │ │ │ │
│ └────────┘ └───────┘ │
│ │
│ │
│ │
│ │
└────────────────────────────────────────────────────────────────────┘- Intermediate Compiler (IR to JS; see src/compiler)
- [-] Virtual Machines
- JS Script VM
- JS Module VM
-
importsupport (partial)
-
- AST (AST to IR)
- Parser (Source to AST)
- Static Analysis
Graffs are intended to be easy to reason about. Starting at any node, it should be easy to trace the program execution forward as well as backwards.
Graff mixes imperative programming nodes with functional programming nodes. Separating side-effect and (hopefully) non-side-effect nodes helps reason about a graff. You can tell which type of node based on the arrows.
-
Arrows pointing out from the East are imperative. We call thse Forward Nodes.
┌──────┐ ──►│ foo ├──► └──────┘Think do this, then that, like imperative control flow.
-
Arrows pointing in from the East are functional. We call these Reverse Nodes.
┌──────┐ ◄──┤ foo │◄── └──────┘Think that needs this, like function arguments.
Everything else is syntactic sugar to minimize syntax for common operations and use cases.
Translations are approximate. The exact compiler specification does not exist.
┌───────────────────────┐
─────►│ console.log("Hello") ├
└───────────────────────┘
Is equivalent to console.log("Hello"), which has the same result as the following:
┌──────────┐ ┌──────────────┐
─────►│ "Hello" ├───────►│ console.log │
└──────────┘ └──────────────┘
which is equivalent to:
var $0 = "Hello"
console.log($0) ┌───┐
──►│ 1 ├─┐ ┌───┐
└───┘ └─►│ + ├────►
└─┬─┘
┌───┐ │
│ 2 │◄─────┘
└───┘
Is equivalent to:
var $0 = 1
var $1 = 2
var $3 = $0 + $1 ┌────────┐ ┌───────────────────────────────────────┐
│ 0──────►│console.log($0, "Is Greater than Zero")│
─────►│ >0 │ └───────────────────────────────────────┘
│ 0───┐
└────────┘ │ ┌───────────────────────────────────────────┐
└──►│console.log($0, "Is not greater than zero")│
└───────────────────────────────────────────┘
For an arbitrary input $0, is approximately equivalent to:
var $0 = /* something */
if ($0 > 0) {
console.log($0, "Is Greater than Zero")
} else {
console.log($0, "Is not greater than zero")
}Although in practice, it may be implemented differently. See Implementation for details.
┌────┐ ┌───────┐
│ │ │ │ ┌───────────┐
──►│ [] ├─►│ .push 0───►│console.log│
│ │ │ │ └───────────┘
└────┘ └──┬────┘
│
┌───────┐ │
│foo.get│◄──┘
└───────┘
Is approximately:
var $0 = []
$0.push(foo.get())
console.log($0)Hopefully you're starting to get the idea. There are precice rules for what arguments go where based on the arrow positions and directions.
-
Get the source:
git clone https://github.com/groundwater/GraffJS cd GraffJS npm install -
Run the test:
npm test -
Run a single file:
npm start $PATH_TO_FILE
Please see SPEC.md.