lnlang is an esoteric programming language where the code consists of numbered lines that dictate the program's execution flow. Each line can contain function calls that return numeric values, and the sum of these results is stored for each line after execution.
You can find the interpreter here
- Each line starts with a number and is followed by a list of function calls.
- Functions can be called with numeric arguments and always return a numeric value.
- The sum of function results is stored for each line after it's executed.
This line is ignored 1 example(1, 2, 3) test(69) 2 hello() 3 world() helloworld() test(42) 4 This counts as a valid line, but it does not contain any valid function calls, so it is interpreted as an empty line.
In the example above, the first line is ignored because it doesn't start with a number. Line 1 calls the example and test functions, the next line calls the hello function, and the next calls the world, helloworld, and test functions. Line 4 is considered a valid line but doesn't have any function calls.
(there are not existing functions and used only as an example here)
- Function calls can be prefixed with ~ (bitwise NOT) or ! (logical NOT) modifiers.
- Modifiers are applied to the returned result of the function.
01 !nop() !nop() 02 get(1) get(1) 03 print(2)
In the example above, the nop function is called twice with the logical NOT modifier applied on the first line. Since nop() always returns zero, we can use logical NOT modifier to convert 0 to 1. The results of both calls are added together, resulting in 2.
The get(ln) function retrieves the result of a specified line. In this case, get(1) returns 2 because line 1 resulted in 2. Calling get(ln) multiple times on the same line is equivalent to n1 + n2 + ....
The print(ln) function outputs the result of a provided line (n) as a number to stdout.
Translated into pseudocode, the example above can be represented as follows:
a = !0 + !0 // 1 + 1 -> 2 b = a + a print(b)
goto (or jump)
In lnlang, the order of line numbers is not enforced. If a line's number is not one more than the previous line's number, it jumps to the line number that is one higher than the current line number after executing the line.
Here's an example:
01 hello() 02 world() 04 asd() // The line above is numbered 4, which breaks the sequential order. 03 this_is_ignored() 04 this_is_ignored_as_well() // So it performs a jump to the line numbered 5. 05 hello_world()
(these are not existing functions and used only as an example here)
This feature allows for the creation of basic loops.
For example, the following program counts from one to infinity:
01 !nop() 02 print(1) 00 ^^ jumps to line 01
In the example above a jump to the start is performed when line 00 is encountered. If the same line number is encountered multiple times, its result is added to the previously stored result instead of overriding it. As a result, the value stored for line 01 keeps increasing, leading to an infinite counting loop.
An important feature in lnlang is the call(ln) function, which executes a specific line without triggering a jump to it. Additionally, lnlang incorporates a callstack, enabling recursive operations and more complex program flows.
01 call(3) 02 stop() 03 do_something() 04 this_wont_be_executed()
In the provided example, line 1 runs call(3) to invoke line 3 for execution. Consequently, the program proceeds to execute the do_something() function. However, the execution does not continue to line 4 since no jump was initiated. This behavior provides fine-grained control over program flow and allows for more flexible and intricate operations.
This function cannot be used to get a result of a line and always returns zero.
You can use the assert(ln) function for conditional control flow in lnlang. This function checks the value of the provided line and skips the current and the next line if the value equals to zero.
01 nop() 02 assert(1) this_wont_be_executed() 03 this_wont_be_executed_as_well() 04 helloworld()
In this example, line 1 is set to 0, so the assert(1) condition fails, causing lines 2 and 3 to be skipped and not executed. Line 4, helloworld(), will still be executed regardless of the condition result.
lnlang does not have a built-in operation for subtracting numbers. However, subtraction can be achieved using the bitwise NOT operation.
We can negate a number by first applying bitwise NOT operation to it and then adding one. Bitwise NOT of 5 is -6, so adding one results in negative 5, and the same applies to converting negative numbers to positive numbers.
We can just add the first number to the negated second number.
Here's an example implementation:
01 input() 02 input() 03 get(1) ~get(2) !nop() 04 print(3)
Translated into pseudocode, the example can be represented as follows:
a = input() b = input() c = a + (~b) + 1 print(c)
Multiplication and Division
There is no built-in operation for multiplication and division but it's possible to implement them using loops, subtraction and addition.
Here's an example program that takes two numbers from stdin, multiplies them and outputs the result:
01 input() 02 input() 03 get(1) 04 !nop() 05 eq(4, 2) 06 assert(5) 07 print(3) stop() 02
(good luck with division lol)
lnlang has several functions available to perform logical operations: eq, lt, and gt. These functions operate on two arguments, which reference line numbers.
- The eq function compares the values of the two lines. It returns 1 if the values are equal and 0 otherwise.
- The lt function checks if the value of the first line is smaller than the value of the second line.
- The gt function checks if the value of the first line is greater than the value of the second one.
List of functions
|nop()||Performs no operation and always returns zero.|
|get(ln)||Retrieves and returns the result stored for a specific line.|
|print(ln)||Outputs the value of the provided line as a number to stdout. Returns the number of outputted digits.|
|input(ln)||Reads a number from stdin and returns it.|
|write(ln)||Writes the character with the corresponding unicode code of the provided line's value to stdout. Always returns 1.|
|read(ln)||Reads one character from stdin and returns its corresponding unicode charcode.|
|assert(ln)||Checks if the value of the provided line, skips the current and the next lines if it is zero.|
|call(ln)||Executes the specified line without performing a jump to it. Always returns 0.|
|eq(a, b)||Compares the values of two lines. Returns 1 if they are equal and 0 otherwise.|
|lt(a, b)||Checks if the first line is less than the second line|
|gt(a, b)||Checks if the first line is greater than the second line|
|stop()||Halts the execution of the program.|
// read 1 char from stdin // this is an infinite loop, so we need to reset the first line so it doesn't add the inputted character to the previous. 01 ~get(1) !nop() read() 02 write(1) 00
// this is horrible help // 12 for faster addition 01 !nop() !nop() !nop() !nop() !nop() !nop() !nop() !nop() !nop() !nop() !nop() !nop() // H 02 get(1) get(1) get(1) get(1) get(1) get(1) // e 03 get(2) get(1) get(1) !nop() !nop() !nop() !nop() !nop() // l 04 get(2) get(1) get(1) get(1) // o 05 get(4) !nop() !nop() !nop() // 8 for faster addition 06 !nop() !nop() !nop() !nop() !nop() !nop() !nop() !nop() // comma 07 get(1) get(1) get(1) get(6) // space 08 get(1) get(1) get(6) // W 09 get(2) get(1) !nop() !nop() !nop() // r 10 get(5) !nop() !nop() !nop() // d 11 get(3) ~nop() // ! 12 get(8) !nop() // newline 13 get(1) ~nop() ~nop() // print 14 write(2) write(3) write(4) write(4) write(5) write(7) write(8) write(9) write(5) write(10) write(4) write(11) write(12) write(13)
// Take two numbers from stdin 01 input() 02 input() // add first input to sum 03 get(1) // increment 04 !nop() // print sum & exit if the increment equals to the second input 05 eq(4, 2) 06 assert(5) 07 print(3) stop() // repeat 02
// Take an input and apply logical NOT to it // so it becomes 1 if it's zero and 0 if it's any other number. 01 !input() // Exit if the input was 0 02 assert(1) 03 print(0) stop() // Print 1 infinitely 04 ~get(4) !nop() !nop() 03 print(4)