Introduction
PooLang is a tiny, interpreted language written in Rust. It was designed to be a lightweight, pedagogical language featuring a full pipeline: lexical analysis, parsing into an Abstract Syntax Tree (AST), and tree-walking interpretation.
The name Poo is inspired by the Burmese word for Guinea Pig, reflecting the language's friendly and compact nature.
Core Philosophies
- Simplicity: Minimal keywords and straightforward syntax.
- Rust-Inspired: Familiar concepts like immutability by default and
mutfor mutability. - Transparent Pipeline: Designed to be easy to study for those interested in how interpreters work.
Features at a Glance
- Explicit Variables: Uses the
pookeyword. - Assignment: Uses the unique
<<operator. - Control Flow: Robust support for
if/elif/else,while, andforloops. - Functions: Define reusable logic with
poof.
Getting Started
Installation
Using Automated Installers
The fastest way to install PooLang is via our universal installers:
Linux / macOS
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/shayyz-code/poolang/releases/latest/download/poo-installer.sh | sh
Windows
powershell -c "irm https://github.com/shayyz-code/poolang/releases/latest/download/poo-installer.ps1 | iex"
From Source
If you have Rust and Cargo installed, you can build from source:
git clone https://github.com/shayyz-code/poolang.git
cd poolang
cargo build --release
The binary will be available at ./target/release/poo.
Running your first program
Create a file named hello.poo:
use std::pout;
pout("Hello, Guinea Pig!");
Run it using the PooLang CLI:
poo hello.poo
Next Steps
Check out the Variables & Types section to learn more about PooLang syntax.
Variables & Types
Variable Declarations
PooLang uses the poo keyword to declare variables. By default, variables are immutable, meaning their value cannot be changed after assignment.
poo age << 5;
If you need to reassign a value, you must use the mut keyword:
poo mut score << 0;
score << 10; # This is allowed
Assignment Operator
Note that PooLang uses the << operator for assignment instead of the traditional =. This helps distinguish PooLang code at a glance.
Supported Types
- Integer: Whole numbers (e.g.,
42,-10). - Float: Decimal numbers (e.g.,
3.14,2.0). - String: Text enclosed in double quotes (e.g.,
"Hello"). - Boolean: Truth values (
trueorfalse). - Vector: A collection of values (e.g.,
[1, 2, 3]).
Type Inference
PooLang automatically infers the type of a variable based on the value assigned to it. You do not need to explicitly declare types for variables.
Operators & Math
PooLang supports standard arithmetic operations with proper mathematical precedence (PEMDAS).
Arithmetic Operators
| Operator | Description | Example |
|---|---|---|
+ | Addition | 5 + 2 |
- | Subtraction | 10 - 4 |
* | Multiplication | 3 * 3 |
/ | Division | 20 / 5 |
Precedence Example
poo result << 10 + 5 * 2; # result is 20 (5*2=10, 10+10=20)
Comparison Operators
These operators return a Boolean (true or false).
| Operator | Description |
|---|---|
== | Equal to |
!= | Not equal to |
> | Greater than |
< | Less than |
>= | Greater than or equal to |
<= | Less than or equal to |
Logical Operators
| Operator | Description |
|---|---|
&& | Logical AND |
| ` | |
! | Logical NOT |
Comments
Conditionals
PooLang uses if, elif, and else to control the flow of execution based on boolean conditions.
If Statements
The simplest form is a single if block:
if x > 10 {
pout("x is large");
}
If-Else
You can provide an alternative path using else:
if x > 10 {
pout("x is large");
} else {
pout("x is small");
}
Elif (Else If)
For multiple conditions, use elif:
if x > 100 {
pout("Huge");
} elif x > 10 {
pout("Large");
} else {
pout("Small");
}
Scoping
Variables declared inside a conditional block are scoped to that block and are not accessible outside.
if true {
poo temp << 1;
}
# temp is no longer accessible here
Loops
PooLang provides two primary ways to repeat logic: while loops and for loops.
While Loops
The while loop continues to execute as long as its condition remains true.
poo mut count << 0;
while count < 5 {
pout("Count: ", count);
count << count + 1;
}
For Loops
PooLang supports for loops that iterate over a range or a collection.
Range Iteration
You can iterate over a range of numbers using the .. syntax:
for i in 0..5 {
pout(i); # Prints 0, 1, 2, 3, 4
}
Vector Iteration
You can also iterate over elements in a vector:
poo items << [10, 20, 30];
for item in items {
pout(item);
}
Scoping
The loop variable (like i or item) is only accessible within the body of the loop.
Functions
Functions in PooLang allow you to encapsulate logic into reusable blocks. They are defined using the poof keyword.
Defining a Function
A basic function looks like this:
poof sayHello() {
pout("Hello!");
}
Return Types
PooLang uses the >> operator to specify the return type of a function.
poof getNumber() >> int {
return 42;
}
Common return types include int, float, string, bool, and void (if no value is returned).
Parameters
You can pass data into functions via parameters:
poof add(a, b) >> int {
return a + b;
}
poo sum << add(5, 5); # sum is 10
Function Scope
Functions create their own scope. Variables declared inside a function are not accessible outside. Functions can, however, access variables in the global scope if they are defined before the function is called.
Standard Library
PooLang includes a small built-in standard library to handle common tasks like output.
Printing to Console
The pout function is used to print values to the standard output.
use std::pout;
pout("Hello, World");
pout("The result is: ", 42);
Import Syntax
Note the use std::pout; line. While pout is often available by default in many contexts, using the explicit use statement ensures it is imported into your current namespace.
Future Plans
The standard library is currently minimal. Planned additions include:
std::math: Advanced math functions (sin, cos, etc.)std::fs: File system operationsstd::str: String manipulation utilities
Lexer
The Lexer (also known as a Tokenizer) is the first stage of the PooLang pipeline. Its job is to take raw source code (a string of characters) and turn it into a sequence of meaningful Tokens.
How it works
The Lexer scans the input character by character and groups them into tokens like:
- Keywords:
poo,mut,if,while - Operators:
<<,+,-,*,/ - Literals: Integers (
10), Strings ("hi"), Booleans (true) - Identifiers: Variable and function names
Implementation
The implementation is located in src/lexer.rs. It uses a simple state-machine approach to recognize different patterns of text.
Example
Input:
poo x << 5;
Tokens produced:
Keyword(Poo)Identifier("x")Operator(Assign)Literal(Integer(5))Semicolon
Parser
The Parser takes the stream of tokens produced by the Lexer and organizes them into a hierarchical structure called an Abstract Syntax Tree (AST).
How it works
PooLang uses a Recursive Descent parser. It looks at the current token and decides which rule of the language grammar to apply next.
For example, if the parser see the poo token, it knows it is starting a variable declaration and expects an identifier followed by an assignment operator.
Abstract Syntax Tree (AST)
The AST is a tree-like representation of your code. For example, the expression 1 + 2 * 3 would be parsed into a tree where + is the root, 1 is the left child, and another tree representing 2 * 3 is the right child.
The AST node definitions can be found in src/ast.rs.
Implementation
The parser logic resides in src/parser.rs. It handles:
- Statement parsing
- Expression parsing (with operator precedence)
- Error reporting for syntax errors
Interpreter
The Interpreter is the final stage of the PooLang pipeline. It takes the Abstract Syntax Tree (AST) produced by the Parser and executes the logic it represents.
How it works
PooLang uses a Tree-Walking Interpreter. It traverses the AST recursively, "visiting" each node and performing the corresponding action in the real world (e.g., adding two numbers, updating a variable in memory).
Environment and State
The interpreter maintains an Environment (or Scope) which maps variable names to their current values. When you declare a variable with poo, it is added to the current environment.
Implementation
The interpreter is located in src/interpreter.rs. Key features include:
- Value system: Defines how integers, floats, and strings are stored in memory.
- Scope management: Handles nested scopes for loops and functions.
- Runtime Errors: Detects issues like dividing by zero or using an undefined variable.