RifL: a playing card stack language
#lang RifL | package:RifL |
RifL is a tactile auto-imperative esoteric coding language.
Tactile means that RifL can be fully represented with real-world objects: RifL is written in playing cards. You can spread cards out on a table and perform RifL code by hand.
Auto-imperative means that RifL code represents a state, and then augments its own state. The cards encode information and can also encode instruction to transform that information.
Esoteric means that the language is not meant for practical use. RifL is intended as a educational tool, to teach to students who learn best physically. It is also meant as an affordable method of introducing programming concepts. RifL can help teach computer memory and algorithms.
If you wish to run RifL code physically, you will need a bunch of decks of cards (they don’t need to all be the same brand, size, or even complete decks, just make sure you have Jokers), as well as masking tape, a sharpie, a coin or token, and the documentation below.
The first chapter, Introduction, teaches you how to read, write, and execute RifL code both physically and on a computer. The second chapter, Data, explains how RifL turns cards into numbers, words, and references. The third chapter, Royal, thoroughly covers how RifL can manipulate that data. The final chapter, Extras, has RifL code examples, past and future development information, and acknowledgements.
1 Introduction
The best way to start learning RifL is from a top down view. You will need to be familiar with a 52 card deck (along with Jokers), so if you are unfamiliar, go and grab one now and look through it. This chapter overviews how RifL operates, and teaches you to read, write, and execute RifL code both physically and on a computer.
1.1 Card Notation
To write and read RifL code, a notation system for cards is needed. All cards have a pip value, followed by a suit letter (except for Jokers which have no suit). The pips are:
Pip
Ace
2
3
4
5
6
7
8
9
10
Jack
Queen
King
Joker
Symbol
A
2
3
4
5
6
7
8
9
10
J
Q
K
R
The four suits are:
Suit
spades
clubs
hearts
diamonds
Symbol
s
c
h
d
Some example cards:
As > The Ace of Spades |
10d > The Ten of Diamonds |
Aces can also be written as 1, and Tens can be written as 0. This documentation will default to using 1 for aces, and 0 for tens, so keep this in mind when reading further.
1s > The Ace of Spades |
0d > The Ten of Diamonds |
All of the cards we have discussed so far as face-up. Cards in RifL can also be face-down. Face-down cards are written with a F in front.
FJc > The face-down Jack of Clubs |
F0d > The face-down Ten of Diamonds |
A face-down Joker can be written as FR, or shorthanded as just F. This documentation will default to writing a face-down Joker as F.
FR > A face-down Joker |
F > Also a face-down Joker |
Most of the time RifL code does not rely on the value of face-down cards, so it’s ok to just write F whenever we need a face-down card. When running RifL physically, in most cases you can use whatever extra cards you have as face-down cards, except in special cases where the Kh is used.
1.2 Cards as Numbers
RifL represents numbers using the number cards, that is from Aces to tens. Aces will represent the digit 1, Two cards the digit 2, and so on. However Ten cards will represent the digit 0. The cards are laid out left to right, with the rightmost card representing the ones places, the second card the tens place, the third card the hundreds place, and so on.
1s 2s > 12, the number twelve |
1s 2s 3s > 123, the number one-hundred and twenty-three |
9s 0s > 90, the number ninety |
Leading zeros can be ignored.
0s > 0 |
0s 6s > 6 |
0s 5s 0s 6s > 506 |
0s 0s > 0 |
1.3 Decks
RifL code is separated into decks of cards, with the top card written at the left, and the bottom card written on the right.
5s, 0s, 6s |
In this deck, the Five of spades is the top card, the Six of spades is the bottom card. This deck has one argument, the number 506.
Each deck has a name, written as a series of cards, followed by a ":". The cards in a deck’s name must all be of the same suit, and a deck’s name cannot use leading zeros, with the exception of a name that is a single zero. The name must all be written on the same line. The name of a deck can be thought of as a number and a suit.
#lang RifL 0s: 5s, 0s, 6s > The zero spade deck, with the number 506 in it 1s: 0s, 6s > The 1 spade deck, with the number 6 in it 3s 2s: 4s, 6s > The 32 spade deck, with the number 46 in it
Within the naming restrictions, each deck’s name is up to the programmer, but no two decks in a RifL program can have the same name. It is standard to start at low deck numbers and proceed upwards as needed.
1.4 Arguments
Decks can have multiple arguments, each separated by face-down cards. Face-down cards work much the same way the spaces between words and numbers work, signifying an end to one piece of information and the beginning of the next.
#lang RifL 0s: 5s, F, 0s, F, 6s
In this zero spade deck, the Five of spades is the top card, the Six of spades is the bottom card. This deck represents the number 5, followed by the number 0, followed by the number 6. This deck is not the number 506, but three separate and distinct arguments.
The number of face-down cards between arguments does not matter unless your code uses the Kh. The following two decks are essentially identical:
#lang RifL 0s: 5s, F, 0s, F, 6s 1s: F, 5s, F, F, F, 0s, F, F, 6s F, F
Each argument is either:
Data is a specific type of argument; either a sequence of number cards, or a single Joker. Each data argument can be interpreted in different ways, depending on the context.
Royal Cards and Jokers are special in regards to spacing. They don’t need face-down cards between them and other arguments. The following two decks are essentially identical:
#lang RifL 0s: 5s, F, R, F, R, F, Ks, F, Qc, F, 1s 1s: 5s, R, R, Ks, Qc, 1s
Face-down cards between Jokers and Royal cards is optional, however it is almost always best practice to separate everything out with face-down cards, but when the Kh is later covered, this flexibility will become useful.
1.4.1 Instructions
Royal cards, that is Jacks, Queens and Kings, are instructions to manipulate information in decks. RifL resolves these instructions from the top of a deck to the bottom.
To illustrate this, the Qc will stand as an example. The Qc is the math Royal card, used to add, subtract, multiply, and divide numbers. Subtracting the number 3 from 5 in RifL looks as follows:
#lang RifL 0s: 5s F 3s F 1c F Qc
The Qc takes three dataarguments. From the top of the 0s deck to bottom, the Qcinterprets the first two arguments are numbers (5 and 3 in this example), while the third argument’s pip is ignored and only the suit matters (clubs in this example). This third argument determines if the Qc adds, subtracts, multiplies, or divides the first two arguments. A club tells the Qc to subtract.
#lang RifL 0s: 5s F 3s F 1c F Qc > Represents 5 - 3
Each Royal card requires a certain number of dataarguments. Royal cards can never use other Royal cards as their arguments. The arguments given to a Royal card will always be data: either a series of number cards, or a single Joker.
1.4.2 Postfix Notation
The logical operators most people are familiar with are written in Infix Notation. RifL is coded in what is known as Postfix Notation. In Postfix, the operators are written after the operands.
Infix
Postfix
5 - 3
5 3 -
2 * 5 - 3
2 5 * 3 -
13 - (2 * 5)
13 2 5 * -
This system may feel unintuitive at first, but it is very useful. It is always clear in postfix notation the order of operations. With infix notation, parentheses are needed to clarify if the order is different than reading left to right. Postfix never encounters this problem, and so it is much better for writing code.
The Qcinterprets a heart in its third argument as multiplication, so the above three examples written in RifL look like this:
#lang RifL 0s: 5s F 3s F 1c Qc > 5 3 - 1s: 2s F 5s F 1h Qc F 3s F 1c Qc > 2 5 * 3 - 2s: 1s 3s F 2s F 5s F 1h Qc F 1c Qc > 13 2 5 * -
1.5 The Stack
To resolve instructionss, RifL uses a special deck called the stack. Programmers can’t write code in the stack, its only used durring run time. When run, RifL proceeds in steps. Each step, RifL looks at the top of the current deck, and if it is not a Royal card, puts that card on top of the stack. If the top card of the current deck is a Royal card, RifL pulls arguments from the top of the stack to fill the Royal card’s needed arguments, and then follows that Royal card’s instructionss. After following the Royal’sinstructionss, none of the used arguments nor the Royal card goes on top of the stack. RifL would process 5 - 3 as follows:
>Stack: > Starts empty 0s: 5s F 3s F 1c Qc > Step 1 >Stack: 5s 0s: F 3s F 1c Qc > Step 2 >Stack: F 5s 0s: 3s F 1c Qc > Step 3 >Stack: 3s F 5s 0s: F 1c Qc > Step 4 >Stack: F 3s F 5s 0s: 1c Qc > Step 5 >Stack: 1c F 3s F 5s 0s: Qc > Step 6 >Stack: 2s 0s: > Empty
The Qc inserts the answer, 2, back onto the stack. RifL stops running when its current deck is empty. Notice how the stack holds the arguments in backwards order. When the Qc is at the top of the current deck, RifL retrieves the first three pieces of information from the stack, and reverses them. For this reason, multi-digit numbers are reversed in the stack.
13 - (2 * 5) would resolve like this:
>Stack: > Starts Empty 2s: 1s 3s F 2s F 5s F 1h Qc F 1c Qc > Step 8 >Stack: 1h F 5s F 2s F 3s 1s 2s: Qc F 1c Qc > Step 9 >Stack: 0s 1s F 3s 1s > The number 10 then the number 13 2s: F 1c Qc > Step 11 >Stack: 1c F 0s 1s F 3s 1s 2s: Qc > Step 12 >Stack: 3s > the number 3 2s: > Empty
Notice how the top Qc resolves before the bottom Qc. The top Qc never goes into the stack, and as such, is not used as an argument for the bottom Qc. Instead, The result of the top Qc, ten (5 * 2), is used as an argument for the bottom Qc.
1.6 Structure of Code
The decks are laid on a table, with the following structure:
Each space on the table is a deck name, starting at 0 on the bottom row and increasing indefinitely upwards. Each deck is given its name based on which space of the table it occupies. Spaces on the table are empty by default until a deck is put there.
#lang RifL 0s: 5s 0s 6s > This deck is in the bottom left space of the table 1s: > This deck is one above the 0s deck 0c: > This deck is one space to the right of the 0s deck
When running RifL code, the pointer determines the current deck. The current deck is the deck which RifL pulls from the top of. The pointer always starts at the 0s deck, and can change over the runtime of a RifL program. If the table starts with an empty 0s deck, the program will immediately terminate, since RifL programs end when the current deck is empty.
Some Royal cards can refer not only to decks in the table, but also the stack. The stack has the card reference R. The Royal card specifications later in the documentation will always declare if Royal cards that refer to decks can refer to the stack.
1.7 Writing RifL Code
When writing RifL, each non-empty deck will have its name, followed by a colon, followed by the cards in that deck from top to bottom:
#lang RifL 0s: 2c, 4c, Js > The deck zero of spades, with some cards in it 2c 4c: 1d, F, 1s, Jd > The deck 24 of clubs, with some cards in it
Cards can optionally have a comma after them, depending if you find that more readable.
Decks can be broken up into any number of lines, but a new deck must always start on a new line, and a deck’s name, including the colon, must all be on the same line.
0s: 1s 3s F 2s F 5s > The number 13, 2, 5 F 1h Qc > Multiply F 1c Qc > Subtract 1s: R R R > The 1 of spades deck
Anything that follows a > will not be read by the RifL executor, until either a line break or a <. These are called comments. Anything between a ( and a ) is also a comment.
#lang RifL >This is a comment 0s: 3c >That was a deck >This is also a comment< 1s: 3c >That was a deck (This is also a comment)
1.8 Debugging
If you are running RifL digitally, it can sometimes be hard to figure out what is going wrong when your code is not behaving as expected. Three debugging modes have been included to help with this. To use one of these modes, at the beginning of the RifL code (after "#lang RifL" but before any decks), you can type any of the following:
step-by-step |
royal-by-royal |
end-state |
Step by step will print out the RifL table after every step of running the code, or when RifL encounters an error. This is the most costly of the debugging modes.
Royal by royal will print out the RifL table when the top card of the current deck is a Royal card, after the the last step, or when RifL encounters an error.
End state will print out the RifL table after the last step or when RifL encounters an error. This is the least costly of the debugging modes.
If you have multiple debugging modes enabled, they must be written in the order shown above. Anytime multiple debugging modes would print the RifL table at the same time, the RifL table is only printed once. Commenting out a debugging mode turns it off.
1.9 Physically Running
All the same rules of RifL apply when running RifL physically. A good way to run RifL by hand is to draw the table out on a large piece of paper, or take masking tape to mark out the grid of the table, and a sharpie to write the name of each grid space that you delineate on the masking tape. Grid spaces need to be large enough to place decks of cards onto. You’ll need a space to one side of the grid to use as your stack. Additionally, a token of some sort is useful to mark which grid space the pointer is at. Finally, you will need the list of what each Royal card does.
To begin, set up all the cards in the code in their corresponding deck spaces. Place the token on the 0s deck space. Most RifL programs will need extra sets of cards, so keep a handful of decks ready. When running physically, Face-down cards can be any card, unless the code you are running uses the Kh.
At each step of RifL, take the top card of the deck the pointer is at, and put it on top of the stack. If the top card of the pointer deck is a Royal card, instead look up how many arguments it needs. One by one take cards from the top of the stack, and put them on top of a temporary pile. Do this until you get the required number of arguments for the Royal card, and the top card of the stack is a face-down card, a Joker, or the stack is empty. Then follow the instructionss of the Royal card. Once you are done following the instructionss, take the Royal card and any cards in that temporary pile and put them in a discard to the side.
If at any time a Royal card requires an argument from the stack, and there are no more arguments in the stack, or the wrong kind of arguments, the code has hit an error and the code stops. If at any time a Royal card makes its way into the stack, the code hits an error and stops.
2 Data
RifL code is broken up into arguments. Each argument is either:
Data is a specific type of argument; either a sequence of number cards, or a single Joker. Each data argument can be interpreted in different ways, depending on the context. This chapter will cover all the different ways data can be interpreted, as well as a guide to the most common contexts in which that interpretation will happen.
2.1 Leading Suit
Many Royal cards rely on the suit of a data argument. However, a sequence of number cards can have different suits in it:
0s 1c 2h 3d |
To resolve this, when the documentation refers to the suit of a sequence of number cards, the suit of the top card of the sequence is all that matters. In the above example, the suit of the sequence would be spades, since the top cards is 0s.
A detail to remember is that arguments that go into the stack get reversed. To account for this, the leading suit of an argument in the stack is determined by the bottom card of the sequence, not the top.
2.2 Jokers
Jokers, like the number cards, represent information, but are wild cards, and are interpreted differently in different contexts, and are not valid in all contexts. Jokers is somewhat comparable to the ’null value found in other programming languages, although that comparison sells short the usefulness of the Joker.
Jokers act like a 5th suit. When the documentation uses the suit of an argument, the interpretation of a Joker will also be listed. Sometimes a Joker in certain contexts will result in an error, which stops the code from running
2.3 Interpretations
2.3.1 Cards
This is the default interpretation if none is listed. No interpretation is made. The argument is taken literally as the cards in the sequence. This is most often used when the suit of an argument is the only thing that matters.
2.3.2 Name
Interprets the card sequence as the name of a deck space on the table. The sequence R represents the stack. Name interpretations are either stack inclusive and will accept R, others are stack exclusive and will not accept R.
1s > the 1 of spades deck |
2c 0c 3c > the 203 of clubs deck |
R > the stack |
A card sequence that has more than one suit type in it is invalid for a name. A card sequence that starts with a ten card is invalid for a name, unless the sequence is a single ten card.
2c 0s 3s > error |
0s 1s > error |
0s 0s > error |
0s > 0 of spades deck |
2.3.3 Integer
Interprets the card sequence as a zero or positive whole number, unless the leading suit is clubs, in which case the number is a negative zero or negative whole number.
3s > 3 |
3c > -3 |
3h > 3 |
3d > 3 |
0s 4c > 4 |
0c 4s > -4 |
0s > 0 |
0c > -0 |
R is invalid for Integers.
R > error |
When converting integers into cards, RifL converts zero and positive integers into all spades, but converts negative numbers into all clubs.
2.3.4 Real Number
Interprets the card sequence as zero or a positive whole number, unless the leading suit is spades or clubs, and the suit of a card in the sequence is diamonds. In that case, the first diamonds card represents the decimal point (and not a digit), and all cards after the first diamonds card are the fractional part of the number. If the leading suit of a real number is clubs, the number is negative.
3s > 3 |
3s 0d > 3 |
3s 0d 0s > 3 |
3s 0d 1s 4s 1s 5s 9s > 3.14159 |
3c 0d 1c 4c 1c 5c 9c > -3.14159 |
3h 0d 1h 4h 1h 5h 9h > 3,014,159 |
3d 0d 1d 4d 1d 5d 9d > 3,014,159 |
0s 0d 1c > 0.1 |
0s 0d 0d 1c > 0.01 |
0d 0d 1c > 1 |
R is invalid for a real number.
R > error |
When converting numbers into cards, RifL always converts whole numbers into the Integer card form. Positive non-whole numbers get converted into spades, with the decimal point being a 0d. Negative non-whole numbers get converted into clubs, with the decimal point being a 0d.
2.3.5 Boolean
The 0d, and any data argument with only tens cards that has a leading suit of diamonds evaluates as #false. R evaluates as null. All other arguments evalutate as #true.
0s > #t |
0c > #t |
0h > #t |
0d > #f |
0d 0s 0h 0c > #f |
0d 0s 0h 1c > #t |
1d > #t |
R > null |
When converting booleans into cards, RifL converts true into 1d, and false into 0d.
2.3.6 Character
Interprets the argument as zero or a positive whole number, and uses that number to reference the ASCII.
6h 5h > 'A' |
1h 2h 2h > 'z' |
R > '' |
Character interpretation is only used with card sequences that have a leading suit of hearts, or with Jokers.
When converting characters into cards, RifL converts the character into its ASCII number, and then turns that number into a hearts sequence.
3 Royal
3.1 Instruction Notation
Each Royal card below lists its form, which shows the number of arguments it takes, and gives each argument a name. Named arguments are written between square brackets.
After the form, the instructions, that is the digital/physical process to follow is listed. argument might be written with data types before them, which is an instruction to convert the argument into that data type.
Anytime an argument gets put on top of the stack, the argument must first be reversed in order.
3.2 Jacks
3.2.1 Js
Form: [Deck], Js
Instruction: Move the pointer to [Name (stack exclusive) Deck].
3.2.2 Jc
Form: [X], [Y], Jc
Instruction: Starting at the current deck space, count to the right [Integer X] times, and up [Integer Y] times. Diamonds wrap around to spades. Negative [Integer X] moves left. Negative [Integer Y] moves down. Push the name of the landed on deck space onto the stack. If the landed on deck space is below the bottom row of the table, throw an error.
3.2.3 Jh
Form: [Deck], Jh
Instruction: Count the number of arguments of [Name (stack inclusive) Deck]. Turn that integer into cards, and put it on top of the stack.
3.2.4 Jd
Form: [Deck], [Operation], Jd
Instruction: The leading suit of [Operation] changes the function.
Spades: Print Cards Output the [Name (stack inclusive) Deck] as a list of cards. Outputting a deck does not change it.
Clubs: Print Data Output the [Name (stack inclusive) Deck] as interpreted data. If the named deck is the stack, it gets outputted in reversed order. Data with the leading suit spades or clubs gets interpreted as a real number. Data with the leading suit hearts and Jokers gets interpreted as a character. Data with the leading suit diamonds gets interpreted as a boolean. Royal cards do not get outputted. Outputting a deck does not change it.
Hearts: Read Cards Take an input of cards and puts it on top of the [Name (stack inclusive) Deck]. If the named deck is the stack, reverse the inputted cards before putting them on top of the stack.
Diamonds: Read Data Take an input of characters, and transform it into cards, and put it on top of the named deck. If the named deck is the stack, reverse the cards before putting it on top of the stack. FR is put between each datum read in.
However, there are special strings of characters that do not get interpreted as characters:
A string of characters that are digits and has up to one ’.’ in it is intepreted as a real number.
A string of characters that starts with ’-’, followed by digits, with up to one ’.’ in it is intepreted as a real number.
The string "#t" and "#f" gets interpreted as a boolean.
Joker: Error
This card is likely to be updated in the future. Use tens cards for [Operation] for future proofing.
3.3 Queens
3.3.1 Qs
Form: [A], [B], [Operation] Qs
Instruction: The leading suit of [Operation] changes the function.
Spades: Greater-than/OR: If [A] & [B] have the same leading suit, put the larger of [Real A] & [Real B] onto stack. If they are both equal to 0, the one with less cards is the larger value.
If [A] & [B] have different leading suits, put the one with the "larger" leading suit. spades > clubs > hearts > diamonds > Jokers
Clubs: Less-than/AND: If [A] & [B] have the same leading suit, put the smaller of [Real A] & [Real B] onto stack. If they are both equal to 0, the one with more cards is the smaller value.
If [A] & [B] have different leading suits, put the one with the "smaller" leading suit. spades > clubs > hearts > diamonds > Jokers
Hearts: Equal/XNOR: If [A] is exactly the same as [B], put [A] on top of the stack. Otherwise, put 0d on top of the stack.
Diamonds: Equivalent/NOT: If both [Boolean A] & [Boolean B] are false, put Ad on top of the stack. Otherwise, if [Integer A] is the same as [Integer B], put [A] on top of the stack. Otherwise, put 0d on top of the stack.
Joker: Error
3.3.2 Qc
Form: [A], [B], [Operation] Qc
Instruction: The leading suit of [Operation] changes the function.
Spades: Add: Put [Real A] + [Real B] on top of the stack.
Clubs: Subtract: Put [Real A] - [Real B] on top of the stack.
Hearts: Multiply: Put [Real A] * [Real B] on top of the stack.
Diamonds: Divide: Put [Real A] / [Real B] on top of the stack. If [Real B] is zero, throw an error.
Joker: Concatenate: Put [A] on top of the stack, then put [B] on top of the stack. Do not put any face-down cards between them.
3.3.3 Qh
Form: [A], [B], [Proposition] Qh
Instruction: If [Boolean Proposition] is #t, put [A] on top of the stack. If [Boolean Proposition] is #f, put [B] on top of the stack. If [Boolean Proposition] is null, do nothing.
3.3.4 Qd
Form: [Sequence], [Modifier] Qd
Instruction: If [Sequence] or [Modifier] is a Joker, throw an error. Otherwise, make a copy of [Sequence], except the suit of every card in the copy of [Sequence] is the same as the leading suit of [Modifier]. Put the copy of [Sequence] on top of the stack.
This card is likely to be updated in the future, such that [Modifier] had multiple functions. Use tens cards for [Modifier] for future proofing.
3.4 Kings
3.4.1 Ks
Form: [Origin], [Destination], [Operation] Ks
Instruction: Find the [Integer Operation] argument of [Name (stack inclusive) Origin]. If there are not enough arguments in [Name (stack inclusive) Origin], throw an error. The leading suit of [Operation] changes the function.
Spades: Copy One: If [Destination] is a Joker, do nothing. Otherwise copy the found argument and the face-down cards directly below it and put it on top of [Name (stack exclusive) Destination]. If [Name (stack inclusive) Origin] is the stack, instead copy the found argument and the face-down cards directly above it, reverse all of those cards, and put it on top of [Name (stack exclusive) Destination].
Clubs: Copy Up To: If [Destination] is a Joker, do nothing. Otherwise, copy all the cards above and including the found argument, and put it on top of [Name (stack exclusive Destination]. If [Name (stack inclusive) Origin] is the stack, reverse all of those cards before putting it on top of [Name (stack exclusive Destination].
Hearts: Move One: Remove the the found argument and the face-down cards directly below it from [Name (stack inclusive) Origin]: if [Destination] is a Joker, the removed cards go nowhere, otherwise put them on top of [Name (stack exclusive) Destination]. If [Name (stack inclusive) Origin] is the stack, reverse the found cards before moving them.
Diamonds: Move Up To: Remove all the cards above and including the found argument, as well as the face down cards directly below it. If [Destination] is a Joker, the removed cards go nowhere, otherwise put them on top of [Name (stack exclusive) Destination]. If [Name (stack inclusive) Origin] is the stack, reverse all of those cards before putting them on top of [Name (stack exclusive) Destination].
Joker: Copy Whole: If [Destination] is a Joker, delete [Name (stack inclusive) Origin]. Otherwise copy [Name (stack inclusive) Origin] and put it on top of [Name (stack exclusive) Destination]. If [Name (stack inclusive) Origin] is the stack, reverse the copy before putting in on top of [Name (stack exclusive) Destination]
3.4.2 Kc
Currently does nothing. Will be added in the future.
3.4.3 Kh
Form: [Deck], Kh
Instruction: Flip [Name (stack inclusive) Deck] over.
3.4.4 Kd
Form: [Deck], Kd
Instruction: Shuffle [Name (stack inclusive) Deck].
4 Extras
4.1 Program Examples
The programs below use both the ’1’ form of aces as well as the ’A’ form, and the ’0’ form of tens as well as the ’10’ form, depending on the programer’s preferences.
4.1.1 Hello World!
#lang RifL 0s: 3h 3h F Ah 10h 10h F Ah 10h 8h F Ah Ah 4h F Ah Ah Ah F 8h 7h F > "!dlroW" 3h 2h F > space Ah Ah Ah F Ah 10h 8h F Ah 10h 8h F Ah 10h Ah F 7h 2h > "olleh" F R F Ac Jd
4.1.2 Truth Machine
Takes an input. If input starts with 0 or #f, print input & terminate. Otherwise print input infinitely.
#lang RifL >If input starts with 0 or #f, print input & terminate. Otherwise print input infinitley. 10s: R F, 10s, F, >Results of if statement 10d, F, >for concatenation 2c, F, Ad, Jd >Take data input onto 2c 2c, F, 10s, F, 10s, Ks >Copy top arg 2c onto 10s >10d, F, input,< R, Qc >Concat 10d + input >R F, 10s, F, 10d, input,< Qh >If top arg input is not 0, 10s. Else R. >R or 10s< Kh > flip either deck 10s or R >If below section is flipped, prints out deck 2c and terminates 10s, FJd, 10s, R, Ks > Copy 10s deck onto 10s deck 2c, FAc, Ac, Jd > Print out deck 2c 10s, F2c, 10s, R, Ks > Copy 10s deck onto 10s deck
4.1.3 Fibonacci
#lang RifL 0s: 2c, F Ad, Jd > input data to 2c Ah, F, 0s, R Ks > copy Ah onto 0s Ac: 0s, F, 1s, F > 0th and 1st term of Fib 2c: >input deck Ah: >(if input less than 2) Ad, F, 2d, F, > load if results 2c, F 0s, R Ks > copy input to 0s >input< F 1s, F, As, Qs > greater of input and 1 >result< F, 1s, F, Ad Qs > is input less than 2? >Ad, F, 2d, F, result< Qh >if result, Ad, else 2d >result< F, 0s R Ks > copy result deck onto 0s Ad: >(true case: print result) Ac, F, 0s, F, > load for Ks 2c, F, 0s, R, Ks > copy input to 0s >Ac, F, 0s, F, input< Ks > get input arg of Ac onto 0s R, Ac, Jd > print out input arg of Ac 2d: Ac, F, 3c, F, 1s Ks > copy 2nd args of Ac onto deck 3c Ac, F, 0s, F, 1d Ks > move full 2s onto 0s >2s args< F, As, Qc > add args of 2s together F, R, Ac F 0h Ks > move result onto Ac 3c, F, Ac, F, 0h, Ks > move 3s onto 2s 2c, F, 0s, F, 0h, Ks > move input to 0s >input< F, 1s, F Ac Qc > input - 1 R, 2c, F, 0h, Ks > move result onto 2c Ah, F, 0s, R Ks > copy Ah onto 0s
4.1.4 Quine
#lang RifL 10s: 10s FR As R Ks As FR 2s FR 2d Ad Ks As FR 2s R Ks 2s FR As R Ks 3h 2h FR 5h 8h FR Ah Ah 5h FR As 10s FR R Ac Jd As FR As Jd 10s FR As R Ks
4.1.5 Brainfuck
An interpreter for the esoteric language Brainfuck. It has unlimited cells, but does not have negative cells. Each cell is 8bit with wrap around, meaning it can input and output the unicode characters from 0 to 255. Because of RifL’s strange data input interpretation (which will receive updates in the future), numbers and the strings "#t" or "#f" may cause undefined behavior. The interpreter can take multiple input characters at a time, and will only ask for a new input when it has run out of inputted characters to use. EOF inputs result in no change to the current cell.
#lang RifL >Brainfuck >cells 0s: 0c, Js, 0s > Move to 0c as starting deck, initilize cell 0 with 0s As: 0s > initialize cell 1 with 0s >parsing------------------------------------------------ ( \/<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< \/ ^ ^ ^ ^ 10c <- 0c/Ac -> 2c"+,-." -> 3c"[" -> 5c"]" -> 7c"<" -> 8c">" -> 9c'other' ^ \/ \/ ^ 4c 6c ^ \/ \/ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<< ) (first-time parse control: Inputs bf code, initializes code size & code index = 0 And if code is larger than 0, go to parse loop control. ) 0c: 4d, F, Ad, Jd > new start, take data input >If code index = code size, stop 1c 0c, F, 2c, F,> load if results 4d, Jh > get code size R, 3d, F, 0s Ks > copy code size to 3d F, 2d, F, 0c, R Ks > copy code index to here >code size F code index< F Ad Qs > code size = code index equivilant >0c, F, 2c, F, (index=size)< Qh >0c or 2c< F, 0c, R Ks > copy 2c onto 0c if code index is less than code size (parse loop control: ++code-index, if code-index < code-size, check +,-. ) Ac: >If code index = code size, stop 1c 0c, F, 2c F> load if results 2d, F, 0c, F 0h, Ks > move code index here >code index,< F, As, F, As, Qc > code index + 1 F, R, 2d, F, 0s, Ks > copy code index + 1 to 2d F 3d, F, 0c, R Ks > copy code size to here >index F code size< F Ad Qs > code index = code size equivilant >0c, F, 2c, F, (index=size)< Qh >0c or 2c< F, 0c, R Ks > copy 2c onto here if code index is less than code size (check +,-. : If character at code-index in code is +,-. goto parse loop control, else goto check [ ) 2c: Ac, F, 3c, F > load if 4d, F, 0c, F, 2d, F, 0c, F, R Ks, Ks > copy cur char onto 0c >cur char< F, 4h 3h, F, Ac Qs> lesser of 43 and cur char >result< F, 4h 3h, F, Ah, Qs > greater or equal to 43 F, 4d, F, 0c, F, 2d, F, 0c, F, R Ks, Ks > copy cur char onto 0c >cur char< F, 4h 6h, F, As Qs> greater of 46 and cur char >result< F, 4h 6h, F, Ah, Qs > less than or equal to 46 >greater or equal 43< >F< >less or equal 46< F, Ac Qs> AND >Ac, F, 3c,< >Ad or 0d< Qh > if cur char is between, Ac, else 3c >Ac or 3c< F, 0c, R, Ks > copy Ac or 3c onto 0c (check [ : If character at code-index in code is [ go to handle [, else goto check ] ) 3c: 4c, F, 5c, F, > load if 4d, F, 0c, F, 2d, F, 0c, F, R Ks, Ks > copy cur char onto 0c F, 9h 1h, F, Ah, Qs > cur char == 91 >4c, F, 5c, F,< >91 or 0d< Qh > if cur char == 91 4c, else 5c >4c or 5c< F, 0c, R, Ks > copy 4c or 5c onto 0c >handle [ : Save code index of [ on "["-stack (deck 5d) 4c: 2d, F 0c, R Ks > copy code index to 0c F, Ah Qd > index to hearts F, R, 5d, F 0h Ks> move heart index to 5d Ac, F, 0c, R Ks > copy Ac onto 0c (check ] : If character at code-index is code is ] goto to handle ], else goto ) 5c: 6c, F, 7c, F, > load if 4d, F, 0c, F, 2d, F, 0c, F, R Ks, Ks > copy cur char onto 0c F, 9h 3h, F, Ah, Qs > cur char == 93 >6c, F, 7c, F,< >93 or 0d< Qh > if cur char == 93 6c, else 7c >6c or 7c< F, 0c, R, Ks > copy 6c or 7c onto 0c (handle ] : Pop last [ index from "["-stack, store both as decks in the heart column, holding their pair's index ) 6c: >Error handling if no matching [ 6d, F, 0c, F, > load error deck and 0c 5d, Jh > Get size of "["-stack >"["-stack size< F, 0s, F Ad, Qs > size equivilant to 0? >6d, F, 0c, F< >stack 0?< Qh > If stack 0? then 6d, else 0c >6d or 0c< Js > Move to 6d or stay >Setting [] pair references in hearts column F, 5d, F, 0c, F, 0s, Ks > copy last [ pos onto 0c F, Ah, Qd > to hearts R > load 2d, F, 0c, R, Ks F, Ah, Qd > copy ] pos to hearts >R, F, index hearts< F, 0h, Ks > move last [ pos+1 onto deck hearts ] >>>> 2d, F, 0c, R, Ks F, Ah, Qd > copy ] pos+1 to hearts R > load 5d, F, 0c, F, 0h Ks > move last [ pos to 0c >R< >last [ pos< F, 0h, Ks > copy ] pos+1 to deck [ Ac, F, 0c, R Ks > copy Ac onto 0c (check < : If character at code-index is < goto parse loop control, else goto check > ) 7c: Ac, F, 8c, F, > load if 4d, F, 0c, F, 2d, F, 0c, F, R Ks, Ks > copy cur char onto 0c F, 6h 0h, F, Ah, Qs > cur char == 60 >Ac, F, 8c, F,< >61 or 0d< Qh > if cur char == 61 Ac, else 8c >Ac or 8c< F, 0c, R, Ks > copy Ac or 8c onto 0c (check > : If character at code-index is > goto parse loop control, else goto comment ) 8c: Ac, F, 9c, F, > load if 4d, F, 0c, F, 2d, F, 0c, F, R Ks, Ks > copy cur char onto 0c F, 6h 2h, F, Ah, Qs > cur char == 62 >Ac, F, 9c, F,< >62 or 0d< Qh > if cur char == 62 Ac, else 9c >Ac or 9c< F, 0c, R, Ks > copy Ac or 9c onto 0c (comment: delete current character, --code-index --code-size, goto parse loop control. ) 9c: 4d, F, R, > load deletion 2d, F, 0c, R Ks > copy code index >index< F, Ah, Qd > turn index to hearts >4d, F, R,< >index h< Ks > delete cur char 2d, F, 0c, F, 0h, Ks > move code index 0c >index< F, As, F, Ac, Qc > index - 1 F, R, 2d, F, 0h, Ks > move index - 1 to 2d 3d, F, 0c, F 0h, Ks > move code size 0c >index< F, As, F, Ac, Qc > size - 1 F, R, 3d, F, 0h, Ks > move size - 1 to 3d Ac, F, 0c, R Ks > copy Ac onto 0c >Reset: Reset code-index, begin executing 1c 0c: >Error handling if remaining [ 2d, F, 0c, F, 0h Ks > copy code index here >code index< F, Ac, F, As, Qc > --code index R, F, 2d, F, 0h, Ks >move code index to 2d 0c, F, 6d, F, > load 0c and error deck 5d, Jh > Get size of "["-stack >"["-stack size< F, 0s, F Ad, Qs > size equivilant to 0? >0c, F, 6d, F,< >stack 0?< Qh > If stack 0? then 0c, else 6d >0c or 6d< Js > stay or move to 6d 2d, R, 0h Ks > delete code index Ac > -1 R, 2d, F, 0h, Ks > set code index = -1 1c 1c F, 0c, R Ks > copy 11c onto 0c >executing--------------------------------------------------------------- 1c 1c: >If code index = code size, stop 0c, F, 1c 2c F > load if results 2d, F, 0c, F 0h, Ks > move code index here >code index,< F, As, F, As, Qc > code index + 1 F, R, 2d, F, 0s, Ks > copy code index + 1 to 2d F 3d, F, 0c, R Ks > copy code size to here >index F code size< F Ad Qs > code index = code size equivilant >0c, F, 1c 2c, F, (index=size)< Qh >0c or 1c 2c< F, 0c, R Ks > copy 1c 2c onto here if code index is less than code size 1c 2c: 4d, F, 0c, F, 2d, F, 0c, F, R Ks, Ks > copy cur char onto 0c F, Ac, Qd > turn cur char to clubs >cur char as deck,< F, 0c, R, Ks > copy cur char deck onto 0c >cur char happens< 1c 1c F, 0c, R, Ks > copy 11c onto 0c >if over 255, need to set to 0 4c 3c: ( + 43 ) 0s, F, >load if 0d, F, 0c, R, Ks > copy cell index to 0c >cell index,< F, 0c, F, 0h, Ks > move cell index [data] to 0c >cur data< F, As, F, As, Qc > cur-data++ F, R, 0c, F, 0s, Ks > copy cur data >cur data< >cur data2< F, 2s 5s 6s, F, Ad, Qs > equivilant to 256? >0s, F,< >cur data< F, >256 or #f< Qh >if true, 0, else data R, > load move 0d, F, 0c, R, Ks > copy cell index to 0c >R, cell-index,< F, 0h, Ks > move top of R to cell index >replace \/ 4c 1c: 4c 1c, F, 4c 1c, R Ks > copy this onto this 0d, F, 4c 1c, R, Ks > copy cell index to 0c >cell index< R, R, Ks > delete cell [index] 5d, F, > load 0d, F, 4c 1c, R, Ks > copy cell index to 0c >5d, F, cell-index< F, 0h, Ks > move top 5d to cell [index] 0c, Js 4c 1c, F, 4c 1c, R Ks > copy this onto this 4c 2c: 4c 2c, F, 4c 2c, R Ks > copy this onto this 5d, R, R, Ks > delete 5d 5d, F, Ad, Jd > input data onto 5d 0c, Js > move back to 0c 4c 2c, F, 4c 2c, R Ks > copy this onto this >>need to handle eofs 4c 4c: ( , 44 ) 4c 2c, F, 0c, F, > load ifs 5d, Jh> get # args 5d >5d< F, 0s, F, Ad, Qs > # args = 0 >4c 2c, F, 0c, F,< >true or false< Qh > if true, get input, then replace, if false, just replace Js > move to 42c or stay here >check 5d eof 0c, F, 4c 1c, F, > load ifs 5d, Jh, >get 5d size >5d size< F, 0s, F, Ah, Qs >5d size equal 0? >0c, F, 4c 1c, F,< >5d size or 0d< Qh >if true, 0c otherwise 4c 1c >0c or 4c 1c< Js > goto 0c or 4c 1c 4c 5c: ( - 45 ) 2s 5s 5s, F, > load if 0d, F, 0c, R, Ks > copy cell index to 0c >cell index,< F, 0c, F, 0h, Ks > move cell index [data] to 0c >cur data< F, As, F, Ac, Qc > cur-data-- F, R, 0c, F, 0s, Ks > copy cur data >cur data< >cur data2< F, Ac, F, Ah, Qs > equal to -1? >2s 5s 5s, F,< >cur data< F, >-1 or #f< Qh >if true, 255, else data R, > load move 0d, F, 0c, R, Ks > copy cell index to 0c >R, cell-index,< F, 0h, Ks > move top of R to cell index 4c 6c: ( . 46 ) 0d, F, 0c, R, Ks > copy cell index to 0c >cell index< F, 0c, F, 0s, Ks > copy cell [index] to 0c >cur data< F, Ah, Qd > turn cur-data to heart R, 4c 7c, F, 0h, Ks > move cur-data copy to 4c 7c 4c 7c F, Ac, Jd > print cur-cell 4c, 7c, R, R, Ks > delete stack 6c 0c: ( < 60 ) >Cell Underflow error handling 9d, F, 0c, F, > load error deck and 0c 0d, F, 0c, F, 0s Ks > copy cell-index here >cell-index< F, 0s, F Ad, Qs > cell-index equivilant to 0? >9d, F, 0c, F,< >cell-index 0?< Qh > If cell-index 0? then 9d, else 0c >9d or 0c< Js > stay or move to 9d >Move cell index down 1 0d, F, 0c, F, 0h, Ks > move cell index to 0c F, As, F, Ac, Qc > cell index-- R, 0d, F, 0h, Ks > move top stack onto 0d 6c 2c: ( > 62 ) 0d, F, 0c, F, 0h, Ks > move cell index to 0c F, As, F, As, Qc > cell index++ R, 0d, F, 0h, Ks > move top stack onto 0d 6c 3c, F, 6c 4c, F,> load if 0d, F, 0c, F, R, Ks > Copy cell index to 0c F, 1d, F, 0c, F, R, Ks > Copy max cell index to 0c >cell index, F, max cell,< F, Ad, Qs > max cell = cell index? >6c 3c, F, 6c 4c, F, max=index< Qh > if max=index -63, else -64 >-63 or -64< F, 0c, R, Ks > Copy 63 or 64 onto 0c 6c 3c: 1d, F, 0c, F, 0h, Ks > move max cell index to 0c F, As, F, As, Qc > max cell++ R, 1d, F 0h, Ks > move new max cell to 1d 0s, R, > load 1d, F, 0c, R, Ks > copy max cell to 0c >R, max cell< F, 0h, Ks > move 0s from stack to max-cell 9c 1c: ( [ 91 ) 9c 2c F, 9c 0c F, > load 0d, F, 0c, R, Ks > get copy of cell index >cell index< F, 0c, R, Ks > get value at deck [cell index] >value< F, 0s, F, Ad Qs > is value = 0 >9c 2c F, 9c 0c F, val=0< Qh > if so, 92c, else 90 F, 0c, R Ks > copy either 92c or 90c onto 0c 9c 2c: 2d, F, 0c, F 0h, Ks > move code index to 0c >code index< F, Ah Qd > hearted >[ key< F, 0c, R, Ks > get value at deck [code index] R, 2d, F, 0h, Ks > set code index to resulting number 9c 3c: ( ] 93 ) 9c 5c F, 9c 4c F, 0d, F, 0c, R, Ks > get copy of cell index >cell index< F, 0c, R, Ks > get value at deck [cell index] >value< F, 0s, F, Ad Qs > is value = 0 >9c 5c F, 9c 4c F, val=0< Qh > if so, 95c, else 94c F, 0c, R Ks > copy either 95c or 94c onto 0c 9c 4c: 2d, F, 0c, F 0h, Ks > move code index to 0c >code index< F, Ah Qd > hearted >[ key< F, 0c, R, Ks > get value at deck [code index] R, 2d, F, 0h, Ks > set code index to resulting number >variables & constants 0d: 0s > cell index 1d: As > largest cell tracker. +1 if reach new max 2d: 0s > code index 3d: > code size 4d: > bf code 5d: > "[" stack >Errors 6d: >hanging "]" error 7d, F, Ac, Jd > print 7d 4d, F, 6d, F, > load 2d, F, 6d, F, 0s, Ks > copy code index to here >4d, F, 6d, F,< >index< Ks > copy index char to here F, R, F, Ac, Jd, F > Print cur char >print char 8d, F, Ac, Jd > print 8d 2d, F, Ac, Jd > print code index >"Hanging "" 7d: 7h 2h FR 9h 7h FR Ah Ah 10h FR Ah 10h 3h FR Ah 10h 5h FR Ah Ah 10h FR Ah 10h 3h FR 3h 2h FR 3h 4h FR >"" at index " 8d: 3h 4h FR 3h 2h FR 9h 7h FR Ah Ah 6h FR 3h 2h FR Ah 10h 5h FR Ah Ah 10h FR Ah 10h 10h FR Ah 10h Ah FR Ah 2h 10h FR 3h 2h FR 9d: >cell underflow error Ad 0d, F, Ac, Jd > print Ad 0d 2d, F, Ac, Jd > print code index >"Cell underflow at index " Ad 0d: 6h 7h FR Ah 10h Ah FR Ah 10h 8h FR Ah 10h 8h FR 3h 2h FR Ah Ah 7h FR Ah Ah 10h FR Ah 10h 10h FR Ah 10h Ah FR Ah Ah 4h FR Ah 10h 2h FR Ah 10h 8h FR Ah Ah Ah FR Ah Ah 9h FR 3h 2h FR 9h 7h FR Ah Ah 6h FR 3h 2h FR Ah 10h 5h FR Ah Ah 10h FR Ah 10h 10h FR Ah 10h Ah FR Ah 2h 10h FR 3h 2h FR
4.2 Development
RifL was started in my Sophomore year of college, with the goal of creating an esoteric language that could be run physically. It was partially finished there, and the first build was completed 3 years later. The parser was built in Brag up until 1.02, but was rebuilt 1.2 in plain beautiful racket.
I intend RifL to receive updates in the future, for a few reasons:
First, the functionality of RifL is not complete. I intentionally left some Royal cards incomplete or not implemented at all. This is the first language I have designed, and as such, I know that letting some of the functionality grow out of user needs rather than design ideas is healthy. In a language like RifL that can have only 12 functions, leaving myself some open space is required to do this.
Second, the current debugging systems RifL gives you to step through code is crude and inefficient. In the future I will provide more natural feeling ways to see what RifL code is doing while it is running. Luckily, you can just run RifL code physically for yourself to see what is happening. That is always the best approach for debugging small portions of RifL.
4.3 Aknowledgments
Keith O’Hara, my computer science professor of my design of programming languages, as well as my thesis advisor, and all around great guy. If you ever get a chance to learn from this man, take that opportunity. He is what so few manage to be: a good programer, an excellent teacher, and a great person. If you see him, tell him I say hi.
Matthew Butterick and his great online textbook, beautiful racket, as well as the Beautiful Racket and Brag libraries. If you are interested in building a coding language in Racket, I recommend you start here.
Greg Hendershott, a good online teacher and expert in the weird and strange world of Racket and Racket macros. I have not yet had the pleasure of meeting this Racket wizard in his tower, but if you do, listen carefully.
Corbob, for taking the time to review the documentation, as well as giving suggestions on the debugging functionalities.