Aetherflo - A toy programming language interpreter
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