- This is still a work in progress. It may be changed in the future.
Blockfunge is a two-dimensional esoteric programming language designed in 2019 by User:CraftSpider. Blockfunge is, as the name suggests, a derivative of Befunge, but instead of a primarily stack based memory system, Blockfunge contains support for named variables and function calls.
A Blockfunge program is layed out on a 2-dimensional graph of any size. The graph is a grid of characters, arranged into ASCII blocks, each containing characters representing either an instruction or part of a token, and with a name defined by an arrow pointing towards the block, with a string attached to the other end.
When writing a program, there are three types of blocks:
A function block contains executable code, a type block contains a constructor function and a nested Blockfunge graph, and an extern block should be empty and named after another Blockfunge file/library on the system. All blocks names must be primarily alphanumeric, with the exceptions that a Type block name should be prefixed with
# and an extern name should be prefixed with
@. Both a function and a type may optionally end with a pair of square brackets surrounding a number, specifying the number of arguments they take.
Execution proceeds by means of an instruction pointer (IP). This pointer is the current location being executed by the system, and has inertia. It will continue travelling in a given direction until it encounters an instruction that changes its direction. A function exits if the IP travels into/past its boundaries. At the beginning of a program, the IP is set to the top-left of the function named 'main', and begins travelling right. A lack of a function named 'main' in the executed file is a syntax error, though it does not need to be present in library files, and is ignored if present during extern imports.
As execution progresses, any alphanumeric character that is hit by the pointer is added to a token register (TR). As soon as the pointer hits a non-alphanumeric character, this register is added to the token stack (TS). The TS is similar to the standard stack in Befunge, except that it can contain both literal and indirect values. Strings and Integers are added to the stack as literal values, but tokens may also be variable or function names, and their evaluation is deferred until their usage. The IP, TS, and TR are relative to the current function scope, and so a newly called function begins with an empty stack and register, and a pointer initialized at the top left travelling right. The exception to this rule being function or types that take arguments, their stack should start with at most N values, popped and evaluated from their calling function stack. The return of a function is the value on top of its stack (if non-empty), this value is added to the calling function's stack. If no return value is desired, the function stack should be cleared before return.
[TODO: Type Creation and Instantiation rules]
||IP direction up|
||IP direction down|
||IP direction right|
||IP direction left|
||Open Gate, Allows flow control through if top of TS is truthy, otherwise reverses IP|
||Closed Gate, Allows flow control through if top of TS is falsey, otherwise reverses IP|
||IP reverse direction|
||IP skip next instruction|
||IP conditionally skip next instruction, if top of TS is falsey|
||Call/Invoke operator. Attempts to invoke a function matching the name on top of the TS|
||Addition operator. Pop two values, return A + B|
||Subtraction operator. Pop two values, return A - B|
||Multiplication operator. Pop two values, return A * B|
||Division operator. Pop two values, return A / B|
||Module operator. Pop two values, return A mod B|
||Boolean negation. Pop one value, return !A|
||Boolean and. Pop two values, return (A and B)|
||Boolean or. Pop two values, return (A or B)|
||Boolean xor. Pop two values, return (A xor B)|
||Comparison operator. Pop two values, return (A == B)|
||Set operator. Pop one token and one value from top of stack. Set token A equal to value B|
||Sub-object access. Pop one value and one token from top of stack. Get the value of the variable B on object or module A|
||Enter string mode. All characters till the next |
||Get stacksize. Push the current stacksize onto the top of the stack|
||Stringize top, convert the top of the stack into a string literal|
||Tokenize top, convert the top of the stack into a token (variable name)|
||Pop stack, unconditionally discard the top of the stack|
||Get character, Pops two values. Gets the character at position (A, B) and pushes it onto the stack. Not that A and B are relative to the current block, and cannot affect other blocks|
||Set character, Pops three values. Sets position (A, B) = C. Note that A and B are relative to the current block, and cannot affect other blocks|
@stdlib v /\ \/ add v /-----\ | + | Silly minimal function, simply adds its arguments \-----/ #type v main /--------------------\ v | var1= var2 v | /-----------\ | 3rav < | |1 2 v | |--------------------| | > add* | | getVar1 meth2 | | | | v v | | | | /-----\ /-------\| | | | |var1 | | || \-----------/ | \-----/ | || | \-------/| \--------------------/
Some implementation notes:
- Though the specification allows graphs of any size, an interpreter may decide on what it considers a max reasonable size and reject any graph larger than that size
- The characters
}are intentionally left unused by the spec, and interpreters may assign them any meaning they wish
- The character
)is reserved, and should not be assigned any meaning
The author provides a simple reference implementation of the language in Python. As of the most recent update, this implementation should meet the specification for functions and operators, but does not yet implement extern or type declarations. Though they are parsed, the declarations have no effect and will likely crash the interpreter if referenced.