Skip to main content

UwULang Specification

Banner

Learn more about UwULang at uwulang.com, where you can also try it out!

Thanks to our sponsors before we start:

Also thanks to Daniel Cristofani who created Brainfuck!

Background​

UwULang is the soon to be #1 programming language in the world. It is a turing-complete language modelled after the measured UwUness of a programming language called BrainFuck

Scope and Justification​

It's cool and the people need it πŸ₯Ί

Overview​

The UwULang turning machine:

----------------------||---||----------------------
| 0 | 0 | 0 | 0 | 0 || 0 || 0 | 0 | 0 | 0 | 0 |
----------------------||---||----------------------
tape ---^ ^ ^
cell -------- |
head (pointer) ----

The "tape" shown above is the memory of the computer. The "cells" in the tape contain values. The currently selected cell is at the "head" and is sometimes called the "pointer".

Instructions to manipulate the tape are fed into the machine. They are not stored in the tape itself. The instructions specify how the machine should move the tape. The cell under the head can change and the value of that cell can be updated, replaced or outputted. The full instruction set is described in detail in the Instructions section.

Instructions​

UwULang has 8 basic instructions:

  • πŸ‘† - increment
  • πŸ‘‡ - decrement
  • πŸ‘‰- go right
  • πŸ‘ˆ- go left
  • πŸ₯Ί- print char
  • 😳- get char
  • πŸ₯΄- random short
  • πŸ˜’- jump to 😑if curr == 0
  • 😑- jump back to πŸ˜’ if curr != 0

These are specified in more detail in the sections below.

Memory Layout​

The UwULang tape is made of an "infinite" collection of 1 byte cells. Each cell represents a single, unsigned 8-bit number. Cells start initialized at zero.

Since the numbers are unsigned, there is no need for any complex integer implementation. If the upper limit of the cell is reached, it wraps back to zero. If zero is decremented, it must wrap back to 11111111. Normal binary number arithmetic rules applies.

Here this tape is implemented as a linked list of cells but you are free to implement it however you want as long as the rules are followed and the behaviour is consistent.

Arithmetic and Wrapping Behaviour Examples​

Increment:

Current value: 00000011
Instruction: πŸ‘†
Next value: 00000100

Current value: 11111110
Instruction: πŸ‘†
Next value: 11111111

Current value: 11111111
Instruction: πŸ‘†
Next value: 00000000

Decrement:

Current value: 00000010
Instruction: πŸ‘‡
Next value: 00000001

Current value: 00000001
Instruction: πŸ‘‡
Next value: 00000000

Current value: 00000000
Instruction: πŸ‘‡
Next value: 11111111

The Program Counter and Address Pointer​

The Program Counter (PC) indicates where the processor is in its program. The majority of the time, this value will be incremented by one after every instruction. The two exceptions to this are the two jump instructions which cause the PC to change based on the value of the current cell indicated by the address pointer. The program begins at the first instruction. The processor stops running when it is out of instructions to run.

The Address Pointer (The "Pointer") indicates the "address" of the current cell in memory.

For a turing machine to really be capable of modelling everything a computer can do, the tape must be infinite. Ensure that the tape can grow in either direction regardless of the current position. It should be possible to move left at the "beginning" of the tape and "right" at the end.

Implementation Note: When implemented in software, this is usually just an index into an array used to store the memory of the program.

For practical reasons, a truly infinite tape is not usually possible.

Move Right (πŸ‘‰)​

Moves the pointer to the next cell (to the right of the current cell). It may be necessary to expand the memory buffer in order to make sure the tape is infinite.

Wrapping is not recommended. It is better to abort if previously used cells are going to be overwritten.

danger

Seriously, do not overwrite cells that were previously used. That means that when you reach the end of your available memory, you should not loop back and start overwriting the cells from the beginning.

Move Left (πŸ‘ˆ)​

Moves the pointer to the previous cell (to the left of the current cell).

This instruction is almost identical in implementation to the move right instruction. See the description of the Move Right instruction for more details.

Notice

If you are at the first (head) cell, moving left will not do anything

Increment (πŸ‘†)​

Increments the value of the current cell by 1. Wrap the value back to zero if the value overflows the byte (255). See the Memory Layout section for more information about cell sizes and arithmetic.

Decrement (πŸ‘‡)​

Decrements the value of the current cell by 1. Wrap the value back to the maximum (255) if the value goes below zero. See the Memory Layout section for more information about cell sizes and arithmetic.

Write (πŸ₯Ί)​

Display ascii character in standard out

Currently,

  • For full UTF-8 compatibility, it will be necessary to temporarily buffer output and combine the bytes into characters which can then be outputted but we are not doing that for now.

Read (😳)​

Reads the next byte from an input stream and replaces the value of the current cell with that new value. The implementation and representation of the input stream is left up to the implementer. All that is necessary is that the stream produces single bytes and that the cell value is replaced with that new value.

If there is no more input to read, the cell should be set to zero in order to signify the End-of-File (EOF). This gives the program a chance to respond to the EOF.

Jump If Zero (πŸ˜’)​

Jumps to the matching 😑 instruction if the value of the current cell is zero (curr == 0). If the value of the current cell is not zero, the program moves on as normal. This has the effect of entering a "loop" body when there is a non-zero value in the current cell. By jumping if the value is zero, some instructions can be skipped based on the value of the current cell.

This is one of two instructions that can modify the PC.

It is important to jump to the matching 😑 instruction so that these jumps can be nested when necessary. If a matching 😑 is not found, the program should abort with an error message.

Example:

πŸ‘† πŸ˜’ πŸ‘‰ πŸ‘† πŸ˜’ πŸ₯Ί 😑 😑
1 2 3 4 5 6 7 8
  1. Add one to the current cell
  2. Jump to instruction 8 if the current cell is zero
  3. Move one cell to the right
  4. Add one to the current cell
  5. Jump to instruction 7 if the current cell is zero
  6. Output the value of the current cell
  7. Jump to instruction 5 if the current cell is not zero
  8. Jump to instruction 2 if the current cell is not zero

Jump Unless Zero (😑)​

Jumps to the matching πŸ˜’ instruction if the value of the current cell is not zero. This has the effect of jumping back to the beginning of a "loop" while the current cell is non-zero. If the current cell is zero, the program continues past this instruction without doing anything.

This is the second of two instructions that can modify the PC.

It is important to jump to the matching πŸ˜’ instruction so that these jumps can be nested when necessary. If a matching πŸ˜’ is not found, the program should abort with an error message.

See the Jump If Zero section for more information and an example.

Random (πŸ₯΄)​

Returns a random short (up to 128) and stores it in the current cell. Implementation of this is optional.

Notice on Random

This is not a standard instruction in UwULang. It is only included here for completeness.

Other Characters​

In general, other characters found in a UwULang file should just be ignored. Those characters could be documentation, or something else entirely. This includes newline and whitespace characters.

Checks​

When implementing a UwULang interpreter, it is important to run the sanity tests on ./test/* just to make sure that your interpreter is working correctly. If you pass the tests, you can be pretty confident that your interpreter is working correctly. You can also try other programs written in bf and converted to UwULang using UwUfier

Hello World Example​

πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ˜’πŸ‘‰πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ˜’πŸ‘‰πŸ‘†πŸ‘†πŸ‘‰πŸ‘†πŸ‘†πŸ‘†πŸ‘‰πŸ‘†πŸ‘†πŸ‘†πŸ‘‰πŸ‘†πŸ‘ˆπŸ‘ˆπŸ‘ˆπŸ‘ˆπŸ‘‡πŸ˜‘πŸ‘‰πŸ‘†πŸ‘‰πŸ‘†πŸ‘‰πŸ‘‡πŸ‘‰πŸ‘‰πŸ‘†πŸ˜’πŸ‘ˆπŸ˜‘πŸ‘ˆπŸ‘‡πŸ˜‘πŸ‘‰πŸ‘‰πŸ₯ΊπŸ‘‰πŸ‘‡πŸ‘‡πŸ‘‡πŸ₯ΊπŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ₯ΊπŸ₯ΊπŸ‘†πŸ‘†πŸ‘†πŸ₯ΊπŸ‘‰πŸ‘‰πŸ₯ΊπŸ‘ˆπŸ‘‡πŸ₯ΊπŸ‘ˆπŸ₯ΊπŸ‘†πŸ‘†πŸ‘†πŸ₯ΊπŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ₯ΊπŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ₯ΊπŸ‘‰πŸ‘‰πŸ‘†πŸ₯ΊπŸ‘‰πŸ‘†πŸ‘†πŸ₯Ί

Standard output:

Hello World!

First 10000 Squares​

πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ˜’πŸ‘‰πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘ˆπŸ‘‡πŸ˜‘πŸ‘‰πŸ˜’πŸ‘ˆπŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘‰πŸ‘‡πŸ˜‘πŸ‘†πŸ‘ˆπŸ‘†πŸ˜’πŸ‘‰πŸ˜’πŸ‘‰πŸ‘†πŸ‘‰πŸ‘†πŸ‘ˆπŸ‘ˆπŸ‘‡πŸ˜‘πŸ‘†πŸ‘†πŸ‘‰πŸ‘‰πŸ˜’πŸ‘ˆπŸ‘ˆπŸ‘†πŸ‘‰πŸ‘‰πŸ‘‡πŸ˜‘πŸ‘‰πŸ‘‰πŸ‘‰πŸ˜’πŸ‘‡πŸ˜‘πŸ‘†πŸ‘†πŸ‘‰πŸ˜’πŸ‘‡πŸ˜‘πŸ‘†πŸ‘‰πŸ‘‰πŸ‘‰πŸ‘†πŸ˜’πŸ˜’πŸ‘‡πŸ˜‘πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘‰πŸ‘‰πŸ‘‰πŸ˜‘πŸ‘ˆπŸ‘ˆπŸ‘ˆπŸ˜’πŸ˜’πŸ‘ˆπŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘ˆπŸ‘†πŸ‘†πŸ‘‰πŸ‘‰πŸ‘‡πŸ˜‘πŸ‘†πŸ‘ˆπŸ₯ΊπŸ‘ˆπŸ˜’πŸ‘‰πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘ˆπŸ‘‡πŸ˜‘πŸ‘ˆπŸ˜‘πŸ‘ˆπŸ‘ˆπŸ˜’πŸ‘‰πŸ‘‰πŸ‘‰πŸ‘‰πŸ‘‰πŸ˜’πŸ‘‰πŸ‘‰πŸ‘‰πŸ˜’πŸ‘‡πŸ˜‘πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘ˆπŸ˜’πŸ‘‰πŸ‘‡πŸ‘ˆπŸ‘‡πŸ˜‘πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘†πŸ‘‰πŸ˜’πŸ‘‡πŸ˜’πŸ‘ˆπŸ‘‡πŸ‘‰πŸ‘‡πŸ˜‘πŸ‘†πŸ˜’πŸ‘ˆπŸ‘ˆπŸ‘ˆπŸ˜‘πŸ˜‘πŸ‘ˆπŸ˜’πŸ‘‰πŸ‘†πŸ‘ˆπŸ‘‡πŸ˜‘πŸ‘‰πŸ˜‘πŸ‘ˆπŸ‘ˆπŸ‘‡πŸ˜‘πŸ‘ˆπŸ‘ˆπŸ‘‡πŸ˜‘

Standard output:

0
1
4
9
...
10000

Extension​

In a classical Turing machine, the input is assumed to be loaded onto the tape/memory. If you want to implement the preload tape with a --preload feel free to do so. This should take in a csv with unsigned short ints (range from 0 to 127 in comma separate list no spaces). This will load all the numbers onto the tape.

Metadata​

UwULang License

KeyValue
Version0.1.0
AuthorAndrew Li (andrewli.site)
LicenseMIT

Template from github.com/brain-lang/brainfuck/blob/master/brainfuck.md