Genewrath
Genewrath is an esolang by User:BoundedBeans where all data is stored in generic types.
Reading of code
Carriage returns and line feeds are completely ignored, and they can be used in the code freely. They can be treated as if they are not in the code; for example, if a carriage return is put in a variable name, it can be accessed with or without the carriage return, or with additional carriage returns or newlines anywhere.
Data types
There are four 'main' types, I, B, N, and X. I is a 4-tuple of an integer and three type arguments. B is a tuple of a boolean and a stack of items of the one type argument. N only has one value, which is treated as null. N takes no type arguments. N must only exist as a type argument, not as the type of a variable, function parameter, or function return value. N
is invalid syntax; the only way to specify N is to omit the type arguments. X is a function (because F
is taken by the false B literal), and takes one or more type arguments. The first type argument is the return type, any remaining ones are arguments. The return type and arguments must both have explicit type arguments, that is, they cannot be N (for example, X<I<BIB>B<I>I<BBB>>
is valid but X<IBI>
is not). X also cannot have its type arguments omitted (an X returning or taking in an N is not valid).
Types are specified by the letters I
and B
. If the next character is another I, B, N, or X, the type arguments are treated as omitted, meaning the type arguments are treated as N
. If the next character is an opening angle bracket, the types inside of the brackets are the type arguments. Angle brackets can be nested, and the top level must always be generic.
X types have a special null value for each type parameter combination. These are known as X nulls (collectively), or X<something> nulls (for type X<something>
).
A few examples of syntax:
I<B<I>B<B>I> I<I<BBI>I<III>I<BIB>> B<B<I>>
Default values are an aspect of a type which is mainly used for the initialization of current value special variables.
The default value of an I is the integer 0 and the default values of all its type arguments.
The default value of a B is the boolean false and an empty stack.
The default value of an N is N
.
The default value of an X type is its corresponding X null.
Expressions
I literals are represented by :[[integer]|[t1]|[t2]|[t3]]
. Such as :[95|N|F[]|[2|N|N|N]]
B literals are either T
or F
followed by [[stack-top]|[next-element]|[etc.]|[stack-bottom]]
. Such as F[:[8|FB[]|FB[]|TB[]]|:[8|TB[]|FB[]|TB[F[N|N]]]]
. B literals with an empty stack must have the type argument listed after the "T" or "F", such as TI<BIB>[]
.
The value of type N can be represented as N
. This prevents infinite nesting.
X nulls are represented as Z[type]
, such as ZX<I<BIB>B<B>>
/
Variables can be referred to by their name enclosed in hashtags, such as #MyBoolean#
.
Elements of I<something> or I expressions can be referred to as [expression]/[index]/
, where index is either 1, 2, 3, or 4. The boolean of a B<something> or B value can be accessed by [expression]/1/
(which gets immediately turned into either :[0|F[]|F[]|F[]]
, while a value popped from their stack can be accessed with [expression]/2/
.
c
is a command to call functions, but it can also be used inside of expressions by using a capital C
.
%[[integer]]
means :[[integer]|N|N|N]
, for example %[7]
means :[7|N|N|N]
. It has the type I. It will need to be contained in something else since the top level of a variable must not have null type arguments.
"[string]"
means a value that when passed to the o
command will output the string. "Hello, world!"
means a value that when passed to the o
command will print "Hello, world!". It has the type B<I>
. The escape sequence @XX
where X is an uppercase hexadecimal digit, represents the character with that ASCII code. The characters @
, "
, carriage return, and line feed must be escaped, and all other characters don't have to, but for control characters and non-printable characters, it is good style and recommended to escape them so no one has to deal with control characters embedded in text.
An I<something> expression can be [I expression]+
or [I expression]-
. This increments or decrements the value. By doing this in a loop, you can add, subtract, multiply, divide, modulo, etc.
Scopes
The f
and {}
commands enclose a scope, and the full program is also one big scope. In scopes, any variables created inside are local to the enclosing block, and are not usable from outside of it. Variables inside a scope may not share a name with a variable in any enclosing scope. Assigning or accessing a variable in an enclosing scope changes that, it does not create a new local variable, and declaring variables with duplicate names as one in the enclosing scope is invalid. Pairs of scopes where neither one contains the other can contain variables of the same name, and these are treated as separate variables.
Scopes also have another trait, which is that a variable cannot be accessed before it is declared. This means that a variable or function declaration.
Functions
Function parameters are contained within ~
and `
. In function declarations, they should be listed as d statements but with no literal. In function calls, the parameters should be put in as literals, separated by |
.
For example:
Declaration:
f#my function#I<BBI>~d#args#B<I<BBI>>d#args2#I<IIB>`!(code)?
Call:
c#my function#~T[FB<B>[]|TB<B>[F[N]|T[N]]]|:[5|TB[]|:[78|N|N|N]|FB[]]`
When function parameters are passed, their values are copied, they do not get a duplicate reference of it. All variables declared in functions are local, and functions cannot access global variables.
Functions are treated as variables, with type X<something>. To declare a function with separate code, the f
command must be used, but variables created with the d
command can be used to hold functions, and functions can return or take in other functions.
Functions enclose a new scope. The function is not visible to its own code, since it is declared in the same command as the code. However, if you want recursion, you can declare an X-type variable with an X null first, then declare a function with code in a dummy variable, then assign the dummy variable to the X-type variable. The function body will be able to access the X-type variable, but when it actually gets called, it will access the current value (the function with the body) rather than the X null it was declared with.
Variables
Variable names are notated by #(name)#. Inside, the name can contain any characters except hashtags. Variable names can also be assigned to a type, allowing shorter and more meaningful names for types.
In addition to the variables, there is also a set of global variables named @current:[type]
used to check loops. Their type is the type used in their name. Of these variables, only those assigned or accessed in the program actually exist at the start. No matter what scope they are assigned in, they are treated as global. These are already declared and thus they can't be declared again. They use the default value of their type.
Statements
{[type][code]}
|
Run [code] as long as the current value of the given [type] is an I<something> with a non-zero integer value, or a B<something> with the boolean value T. |
d#[variable-name]#[type][expression]
|
Declare the variable [variable-name] as type [type] and initialize it to [expression]. |
r#[variable-name]#[expression]
|
Assign [expression] to the variable [variable-name]. |
f#[variable-name]#[return-type]~[arguments]`![code]?
|
Declare the function [variable-name] as taking in [arguments], returning [return-type], and running [code] to get there. [arguments] should be a series of d statements without [expression]s.
|
c#[variable-name]#~[arguments]`
|
Call the function [variable-name] with [arguments]. |
g[expression]
|
Return [expression] from the currently executing function. |
p#[variable-name]#[expression]
|
The variable [variable-name]] must be a B type. [expression] will be pushed to its stack. |
o[expression]
|
Output a value. If the value is a true B<I> , it will print out the ascii values of the I's. Other values are implementation-dependent.
|
i#[variable-name]#
|
If the variable [variable-name] is currently a true B<I> , input a byte, push it onto the stack. Other values are implementation-dependent.
|
t#[variable-name]#[type]
|
Creates an alternate name #[variable-name]# for the type.
|
x[expression]
|
Comment, usually the literal will be a string. |
Examples
Hello world
o"Hello world!"
Hello world without strings
oT[:[72|N|N|N]|:[101|N|N| N]|:[108|N|N|N]|:[108|N|N |N]|:[111|N|N|N]|:[32|N|N |N]|:[119|N|N|N]|:[111|N| N|N]|:[114|N|N|N]|:[108|N |N|N]|:[100|N|N|N]|:[32|N |N|N]]x"F[:[10|N|N|N]/1/"
Truth-machine
f#subtract#~dI<BBB>#arg1# NdI<BBB>#arg2#N`!r#@curre nt:I<BBB>##arg2#{r#arg1## arg1#-r#arg2##arg2#-r#cur rent##arg2#}g##?d#truth#B <I>T[]i#truth#d#is 1#I<BB B>:[C~:[#truth#/2/|N|N|N] |:[%[48]/1/|N|N|N]`/1/|N| N|N]r#current##is 1#{o"1" }o"0"x""x""x""x""x""x"xx"