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

  1. Simplicity: Minimal keywords and straightforward syntax.
  2. Rust-Inspired: Familiar concepts like immutability by default and mut for mutability.
  3. Transparent Pipeline: Designed to be easy to study for those interested in how interpreters work.

Features at a Glance

  • Explicit Variables: Uses the poo keyword.
  • Assignment: Uses the unique << operator.
  • Control Flow: Robust support for if/elif/else, while, and for loops.
  • 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

  1. Integer: Whole numbers (e.g., 42, -10).
  2. Float: Decimal numbers (e.g., 3.14, 2.0).
  3. String: Text enclosed in double quotes (e.g., "Hello").
  4. Boolean: Truth values (true or false).
  5. 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

OperatorDescriptionExample
+Addition5 + 2
-Subtraction10 - 4
*Multiplication3 * 3
/Division20 / 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).

OperatorDescription
==Equal to
!=Not equal to
>Greater than
<Less than
>=Greater than or equal to
<=Less than or equal to

Logical Operators

OperatorDescription
&&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 operations
  • std::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:

  1. Keyword(Poo)
  2. Identifier("x")
  3. Operator(Assign)
  4. Literal(Integer(5))
  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.