A surprisingly convenient, moderately esoteric, programming language.
Adjectives that apply to this language (gotta use lots of buzzwords, right?):
- Unary-based: Everything in the language is expressed in base-1 (technically base-1 and a delimiter, since pure base-1 is infeasible storage-wise).
- Functional: There are no loops, only recursion (yes it has tail call optimization)!
- Homoiconic: Code is represented in the same way as data, and can be easily manipulated as such. Code as data! Code is data!
- Stack-oriented: All data is allocated on a single stack, the stack (the function namespace is separate, and in theory it could be used to store data). All data operations are stack operations, somewhat like Forth).
- Reverse polish notation: The operation comes after the data. This comes directly as a result of being stack-oriented.
- Strongly-typed: There is only one type, the integer. The stack (the general purpose memory allocation space) can only contain integers.
Building Instructions
- Download and extract the latest oOonoOo.zip.
- Install SBCL. Note that in principle any implementation of Common Lisp should work (builds are tested working with ECL).
- Run
sbcl --load build.lisp
from the root directory of the project. - (Optional) Move the resulting
ooonooo
to somewhere on your path (eg./usr/local/bin
on Linux).
Usage Instructions
Run the command with the form ooonooo [OPTIONS] [FILE]
.
Where OPTIONS
are one of:
--help
which outputs a help message and exits.--version
which outputs the version number and exits.
Where FILE
is a path to the file you want to run.
If file is not specified, the program runs in interactive mode, otherwise it runs in non-interactive mode.
In interactive mode the stack will start with a valid load command (you just have to Eval) which points to modules/basic-math.ooonooo
.
In non-interactive mode the stack will start empty.
Language Description
A command is notated by the number of 0
characters there are on a single line (hence the unary).
Any other characters are ignored (use them towards your advantage).
General Behavior
If there are 0 0
characters on a line, a NOP instruction is executed.
It does nothing, but still counts as an instruction (important for function/macro).
If there are 1-9 0
characters on a line, special evaluation rules are applied (see Stack Operations for 2-5, Conditions for 6, Eval/Function/Macro for 1, 7-8, and Load for 9).
If there are more than 10 0
characters on a line, then the number of 0
characters -10 is pushed onto the stack.
Stack Operations
Used to manipulate the stack (when there are 2-5 0
characters on a line), they are:
# of 0 s |
Action | Rule |
---|---|---|
2 | Drop | Remove the last number added to the stack. |
3 | Duplicate | Duplicate the last number added to the stack (and add it to the stack). |
4 | Swap | Swap the positions of the last two numbers added to the stack. |
5 | Rotate | Remove the third most recent number on the stack and add it to the stack. |
Conditions
If there are 6 0
characters on a line, then the Branch operation is run.
The Branch instruction consumes 3 values on the stack.
Branch requires the following stack setup.
- A number, the if part.
- A number, the then part.
- A number, the else part.
If the if part is nonzero, then all three values are dropped, and the then part is then added to the stack.
If the if part is zero, then all three values are dropped, and the else part is then added to the stack.
Eval/Function/Macro
If there is 1 0
character on a line, then the Eval instruction is run.
Eval consumes the top element of the stack, finds the function at that location, and executes it.
If there are 7 0
characters on a line, then the Function instruction is run.
Function declares some amount of oOonoOo code to be run when you call it.
Function requires the following stack setup:
- The location in which to intern the function (if it's less than 10, you're doing this wrong).
- A string representing the name of the function (can be empty).
- N, the number of instructions in the function.
- The N instructions that make up the body of the function.
Note that if the last instruction in your function is Eval, the compiler will do tail call optimization.
If there are 8 0
characters on a line, then the Macro instruction is run.
Macro declares common lisp code to be run upon it being called.
Macro requires the following stack setup:
- The location in which to intern the macro (if it's less than 10, you're doing this wrong).
- A string representing the name of the macro (can be empty).
- A string representing the body of the macro (can be empty, but why would it be).
Load
If there are 9 0
characters on a line, then the Load instruction is run.
Load requires the following stack setup:
- The base offset in which to evaluate the file.
- A string containing a path to a valid oOonoOo file.
Load evaluates a file with all locations shifted by the base offset specified (with one exception: 0-9 0
characters are evaluated as if there was a 0 offset).
This allows for proper namespacing of functions/macros, and allows you to reuse low (small) addresses.
Strings
By convention strings are denoted by pushing each character code in reverse order followed by pushing the length of the string onto the stack.
Copyright/Licensing
All code files include an SPDX-License-Identifier
describing which license it falls under.
In general:
modules/basic-*.ooonooo
is licensed under the WTFPL (see theLICENSE.WTFPL
file).- The rest of the code is licensed under the BSD 2-Clause license (see the
LICENSE.BSD2
file).