Introduction

I wanted to write a programming language interpreter. But first, I needed to come up with a toy programming language. Thus, Aetherflo.

The language is intentionally eccentric and inelegant—I wanted it to look different from popular languages, because I wanted to focus on writing the interpreter, not the syntax, and because I recognize that underneath the syntax, the heart of all programming languages are alike. In fact, I asked a friend unbiased with programming language experience to jot whatever came to his head on a piece of paper. With some tweaking, we arrived on what looked to be a feasible, yet ugly, syntax.

The purpose of Aetherflo is to stand as a minimal programming language interpreter, both for educational purposes and to use as a baseline for other interpreters. Despite having a very strange syntax, tweaking the language to use a completely different syntax would only involve changing the lexer and grammar, while the bulk of the interpreter could remain the same.

Try Aetherflo

To compile the interpreter and run a sample app:

git clone https://github.com/notfed/aetherflo.git aetherflo
cd aetherflo
make
./floc < samples/sample.flo

The first line will compile the interpreter. The second will run the following sample aetherflo script:

{1}|:x
def incx() = {
  &x
  {x+1}|:x
  (x<=100)??(
      incx()
  )
}
incx()

This will print the numbers from 1 to 100.

About the Language

The Aetherflo language supports:

  • Integer Variables
  • Integer Expressions (supporting standard arithmetic and boolean operators)
  • Conditionals
  • Printing variables to standard output
  • Functions/Recursion
  • Closures

To declare a variable, and/or assign the result of an expression to a variable named id, do:

{ expression } | : id

Each expression evaluates to an integer. Standard arithmetic is supported with the +, -, *, and / operators. In addition, boolean logic is supported with operators ==, !=, <, <=, >, and >=.

To define a procedure named id:

def id(arg1,arg2,...) { statements }

Procedures contain scope. All variables and procedures belong to the parent procedure’s (or global) scope when declared for the first time.

To invoke a procedure named id:

id(arg1,arg2,...)

Note that procedures can be nested, and in this case, will close over all parent procedure’s in-scope variables or procedures.

To conditionally invoke one or more statements:

( expression ) ? ? ( statements )

Each conditional statement will evaluate the given expression, and conditionally execute the given sub-statement.

To print the value of an expression to standard output:

& expression