Programming languages come in various forms, each designed to address specific types of problems or to improve upon existing languages in terms of performance, ease of use, or readability. However, some languages are created as experimental or esoteric languages, primarily for fun or to challenge conventional programming paradigms. One such language is Whitespace.
Introduction to Whitespace
Whitespace is an esoteric programming language developed by Edwin Brady and Chris Morris and released on April 1, 2003. Unlike conventional programming languages that utilize visible characters, Whitespace relies exclusively on whitespace characters: spaces, tabs, and line feeds. In Whitespace, all other characters are ignored, making the actual code invisible to the naked eye if interspersed with regular text. This unique feature makes Whitespace both intriguing and challenging.
Key Features of Whitespace
- Invisible Syntax: The code comprises only spaces, tabs, and line feeds.
- Turing Complete: Despite its odd syntax, Whitespace is capable of performing any computation that a Turing machine can, making it a Turing complete language.
- Stack-Based: Whitespace primarily uses a stack-based architecture, where most operations involve pushing, popping, and manipulating data on a stack.
- Heap Access: It supports heap access for more complex data storage needs.
- Control Flow: Whitespace provides mechanisms for structured control flow, including conditionals and loops.
The Philosophy Behind Whitespace
Whitespace was created partially as a joke and partially as an intellectual exercise. By using only whitespace characters, the creators aimed to highlight how dependent traditional programming is on visible syntax. Additionally, Whitespace challenges programmers to think about code in a fundamentally different way, emphasizing the importance of whitespace often overlooked in other languages.
Basic Concepts and Syntax
Characters and Commands
In Whitespace, there are three types of characters:
- Space (): Represented by a single space.
- Tab (
\t
): Represented by the tab character. - Line Feed (
\n
): Represented by the newline character.
Each command in Whitespace is a sequence of these characters. Commands are divided into four categories based on their initial character:
- Stack Manipulation (Space): Commands that manipulate the stack.
- Arithmetic (Tab-Space): Commands that perform arithmetic operations.
- Heap Access (Tab-Tab): Commands that interact with the heap.
- Flow Control (Line Feed): Commands that manage the program’s control flow.
- I/O (Tab-Line Feed): Commands for input and output operations.
Stack Manipulation
Stack manipulation is fundamental in Whitespace. Here are some common stack commands:
- Push (Space [number]): Pushes a number onto the stack.
- Duplicate (Space LF Space): Duplicates the top item on the stack.
- Swap (Space LF Tab): Swaps the top two items on the stack.
- Discard (Space LF LF): Discards the top item of the stack.
Arithmetic Operations
Arithmetic operations follow the stack manipulation commands:
- Addition (Tab Space Space Space): Pops the top two items, adds them, and pushes the result.
- Subtraction (Tab Space Space Tab): Pops the top two items, subtracts the second from the first, and pushes the result.
- Multiplication (Tab Space Space LF): Pops the top two items, multiplies them, and pushes the result.
- Division (Tab Space Tab Space): Pops the top two items, divides the second by the first, and pushes the result.
- Modulo (Tab Space Tab Tab): Pops the top two items, calculates the modulo, and pushes the result.
Heap Access
Heap access allows for more dynamic data storage:
- Store (Tab Tab Space): Pops the top two items; uses the second item as the address and stores the first item at that address in the heap.
- Retrieve (Tab Tab Tab): Pops the top item and retrieves the value stored at that address in the heap, pushing it onto the stack.
Flow Control
Flow control commands manage the program’s execution flow:
- Label (LF Space Space): Marks a location in the program with a label.
- Call Subroutine (LF Space Tab): Calls a subroutine at a specified label.
- Jump (LF Space LF): Jumps to a specified label unconditionally.
- Jump if Zero (LF Tab Space): Jumps to a label if the top of the stack is zero.
- Jump if Negative (LF Tab Tab): Jumps to a label if the top of the stack is negative.
- End Subroutine (LF Tab LF): Ends a subroutine and returns to the caller.
- End Program (LF LF LF): Ends the program.
Input and Output
Whitespace provides basic I/O commands:
- Output Character (Tab LF Space): Pops the top item and outputs it as a character.
- Output Number (Tab LF Tab): Pops the top item and outputs it as a number.
- Read Character (Tab Tab Space): Reads a character from input and stores it at the address on top of the stack.
- Read Number (Tab Tab Tab): Reads a number from input and stores it at the address on top of the stack.
Writing and Executing Whitespace Programs
Writing Programs
Writing a Whitespace program involves understanding how to combine the commands to perform desired operations. Let’s start with a simple example: pushing and printing a number.
- Push Number: To push the number 5 onto the stack, the command would be
Space Space Space Space Tab LF
. Breaking it down:
Space Space
(Command prefix for push)Space Space Tab LF
(Binary representation of 5, which is101
)
- Output Number: To output the top item of the stack as a number, use
Tab LF Tab
.
Combining these commands, a Whitespace program that pushes and prints the number 5 looks like this:
[Space][Space][Space][Space][Tab][LF][Tab][LF][Tab]
Executing Programs
To execute Whitespace programs, you need an interpreter that can parse the whitespace characters and execute the corresponding commands. Various interpreters are available online, and you can also write your own if you want to deepen your understanding of how Whitespace works.
Example: Hello World Program
Writing a “Hello, World!” program in Whitespace is more complex due to the need to handle characters individually. Here’s a step-by-step breakdown:
- Push Characters: Push each character of “Hello, World!” onto the stack in reverse order.
- Output Characters: Use the output character command to print each character.
Given the complexity and the sheer length of such a program, here’s a simplified version that demonstrates pushing and printing a few characters:
[Space][Space][Space][Tab][LF] // Push 10 (newline)
[Tab][LF][Space] // Output character
[Space][Space][Space][Space][Tab][Tab] // Push 33 (!)
[Tab][LF][Space] // Output character
Conclusion
Whitespace is a fascinating programming language that turns the conventional understanding of programming on its head by using only spaces, tabs, and line feeds. While it may not be practical for real-world applications, it serves as an excellent tool for understanding the importance of whitespace in programming and for exploring the boundaries of programming language design.
Whether you’re an experienced programmer looking for a fun challenge or a beginner wanting to broaden your horizons, experimenting with Whitespace can be a rewarding experience. It pushes you to think differently and appreciate the subtleties of programming in a whole new light.