- 1 Concepts
- 2 Commands
- 3 Functions
- 4 Examples
- 5 Interpreters
Ly is a stack-based language, so all operations are performed on a stack. However, the program operates on not just one one stack, but on an infinite strip of stacks. It also has a backup cell that can store one value for later use. Execution is mostly linear, with the exception of the
In Ly, execution is mostly linear, with a single execution pointer that performs the action dictated by the character it is currently on, before moving to the right.
However, there are two instructions that can move the pointer:
], which check if the top value on the stack is 0, and jump accordingly between each other. This allows for looping and conditional behavior.
In Ly, a stack is a simple one-dimensional list of integers and floats. The language operates on an infinite list of stacks.
There are several operations for stacks such as arithmetic, reversal, popping and I/O, but values in a stack below the two highest ones cannot be accessed without reversing the stack (except with the
I instruction). This can prove problematic in some cases where you need a place to store values. That's where other stacks come in. The
> operators can be used to change stacks at will, allowing greater control over your programs. The
> operators also complete the language's set of instructions to be able to emulate brainfuck, proving Turing completion of the language.
The backup cell
The backup cell is accessible all the time no matter what the current state of the stacks are. The
l command will copy (not move) the current top value on the stack to the backup cell, and the
s will copy it back. This can be incredibly useful for storing a value for later use.
Ly has four I/O commands, two for input(
n) and two for output. (
The string I/O commands (
o) will input and output characters as ASCII codes.
The number I/O commands (
u) will input and output numbers directly.
n takes input as space-separated digits, to make the job of taking input as a list of numbers much easier.
Ly also has the ability to create functions, pieces of reusable code that can be called at will. Functions are ran independently from the main program, allowing great flexibility with programs and even code that can be reused between programs. Functions, like with many other languages, can also be called with functions, allowing them to be dynamic and powerful.
The official Ly interpreter will throw an error if any of the following things occurs:
- Invalid arithmetic operations, such as dividing by zero
- Trying to pop from the stack when it is empty (and implicit input fails)
- The program has unbalanced brackets
- Trying to load from the backup cell when it is empty
- Receiving a non-integer as input with
n(or implicit input)
Error messages look like so:
Error occurred at program index 6 (zero-indexed, includes comments) EmptyStackError: cannot pop from an empty stack
Implicit input and output is an optional feature of Ly mainly aimed towards code golfers.
Implicit input will occur whenever an instruction attempts to pop from the stack when it is empty.
Instead of raising an error, the interpreter will read a number (character I/O is not supported implicitly) from STDIN and push it to the stack.
This is slightly different from the
n instruction in that it reads the input backwards for convenience. For example, an input of
43 54 will return
[43, 54] and not
If there is no more input to take implicitly (EOF is reached or an empty line of input is given),
0 will be used instead.
Implicit input is never pushed to the stack; it is delivered straight to instructions that attempt to pop from the stack.
If an invalid input is passed implicitly, an
EmptyStackError is raised.
In addition to normal implicit input, instructions that perform an action on the entire stack will implicitly dump STDIN (take lines of input until EOF/empty line) onto the stack.
Implicit output occurs at the end of a program if that program is still running.
Thus, ending execution with the
; instruction before the end of the program will disable implicit output.
Implicit output will print the entire stack that is under the stack pointer, with a space in between numbers.
This language is still under very active development, and these commands are subject to change. In these explanations, "the stack" refers to the current stack.
Literals and operators
||Push the corresponding value onto the stack. You can push multi-digit numbers if you surround them in parentheses.|
|| Addition, subtraction, multiplication, division and modulo, respectively. Pop |
||String literals; push every character found to the stack until a closing quote is hit.|
|| Pop |
||Equals, greater than and less than, respectively. Pop a number from the stack, and push 1 if the top value on the stack operator that number and 0 otherwise.|
||Pop a number from the stack, and push the number of digits in that number.|
|| Pop a number from the stack, and push each of its digits individually. For example: |
|| Pop the stack, and push all of the numbers in it as a single number. For example: |
||Duplicate the top value on the stack.|
||Pop the top value of the stack.|
||Swap the top two values on the stack.|
||Reverse the current stack.|
||Pop a number from the stack. If it is 0, push 1. Otherwise, push 0.|
|| Pop a number from the stack and reverse its sign, e.g. |
||Move left on the stack strip.|
||Move right on the stack strip.|
||Push the length of the stack to the stack.|
||Sort the stack from lowest to highest.|
|| Pop a value off the stack, find the value on the stack with that index (Ly uses 0-indexing), and push it to the stack. If the index is out of range, an error is raised. e.g. |
||Pop the top of the stack and output the character with that ASCII code point.|
||Pop the top of the stack and output it as a number.|
|| Read a line of input and push each character to the stack separately. Ly does not have strings, so input is taken as ASCII code points instead. e.g. |
||Read one line of input input and push it to the stack as an integer. If an invalid input is given, an error is raised.|
||If the top value of the stack is 0 or the stack is empty, jump to the corresponding left bracket. Otherwise, do nothing.|
||If the top value of the stack is not 0 and the stack is not empty, jump to the corresponding right bracket. Otherwise, do nothing.|
||Break out of the current loop and move the execution pointer to the instruction immediately after the end of the loop.|
||Copy the top value on the stack to the backup cell. See The backup cell.|
||Push the current value of the backup cell.|
||End execution. This instruction is only required to end execution early; it is not required to add it at the end of every program.|
In Ly, commands can be modified using the
& symbol so that their effect will take place on the entire stack, as opposed to only the top value.
||Push the sum of the stack. The stack will not be popped in this process.|
||Take input and write it to the stack, as a number, until EOF is reached or an empty input is given.|
||Pop the entire stack, and print it in the correct order.|
||Pop the entire stack, and print it, as space-separated numbers, in the correct order.|
||Pop the entire stack.|
||Duplicate the entire stack on top of itself.|
|| Back up the whole stack to the backup cell. The stack will not be popped in this process. A backed up stack can be loaded as normal with the |
A major feature of the Ly language is its functions.
Defining a function
Defining a function is simple:
<name> is a single character denoting the name of your function, and
<code> is the body of your function.
Inside a function, code is executed just like a completely seperate Ly program. The only connection between a function and the main program is the input and output commands.
Instead of performing I/O from STDIN and STDOUT, a function manages I/O through the stack of the parent program.
An example function looks like this:
This is a simple prime number checker program wrapped up in a function
P that takes a single number as a parameter and pushes either
1 to the stack depending on if the number passed was a prime or not.
NOTE: If you give a function the same name as an existing function or built-in instruction, the old function/instruction will be overwritten.
Calling functions is just as simple as creating them:
<name> again is the name of the function.
The function is run like a normal program, with two differences:
- Whenever the function outputs, it is pushed to the stack of the parent program
- Whenever the function takes input, it is popped from the stack of the parent program
Here is an example call of the prime number function:
This will pop a number from the stack, and push
1 if it was prime and
All examples are using the official
$ ./ly.py helloworld.ly Hello, world!
Prime number checker
Takes in a number as input, and outputs 1 if it is prime and 0 otherwise.
$ ./ly.py prime.ly Enter program input: 7 1 $ ./ly.py prime.ly Enter program input: 8 0
$ ./ly.py helloworld.ly Enter program input: 0 0 $ ./ly.py helloworld.ly Enter program input: 1 1111111111111111111111111111111111111111111111111111111111111111111111111111111...
$ ./ly.py helloworld.ly -t 0.01 1123581321...
$ ./ly.py helloworld.ly -t 0.01 1 1 2 3 5 8 13 21 ...
The official interpreter, and currently the only one. Hosted here.