← Back to Home

Viz-Compiler — Language Reference & Developer Manual

Official Documentation for VizLang and the Compiler Pipeline

Viz-Compiler is a custom-built native compiler for VizLang, a minimal C-like language designed as a complete, pedagogically clear compiler construction reference. It compiles VizLang source through every classical stage — Lexing → Parsing → Semantic Analysis → IR Generation → Optimization → x86-64 Native Code — exposing each phase's internal representation via interactive web visualizers.

Academic Disclaimer: Viz-Compiler is built to demonstrate compiler theory. Design decisions consistently choose readability and observability over performance. The backend utilizes a strict "Memory-to-Memory" stack model rather than complex register allocation.

Table of Contents


1. Project Goals & Architecture

Goals

Compiler Pipeline

Source (.viz) → Lexer (Flex) → Parser (Bison) → Semantic (AST Walk) 
  → IR Gen (Quads) → Optimizer (Prop/Fold/Strength/DCE) → x86-64 ASM

2. VizLang Specification

VizLang is a strict subset of C. Every valid VizLang program is structurally close to C, but relies on integers, strict scoping, and explicit memory pointers.

2.1 Data Types & Variables

VizLang operates exclusively on 64-bit (8-byte) integers. To perfectly align with the x86-64 hardware stack, every variable, pointer, and array element occupies exactly 8 bytes of memory. Variables must be explicitly declared.

// 64-bit Integer (8 bytes)
int score = 100;
int x; 
x = 50;
                

2.2 Pointers & Memory

VizLang supports explicit memory addressing using the address-of (&) and dereference (*) operators. Pointers also consume exactly 8 bytes.

int target = 42;
int *ptr = ⌖ // Get 8-byte memory address
*ptr = 99;          // Mutate target via pointer
                

2.3 Arrays

Arrays are allocated as contiguous blocks on the stack frame. The size must be an integer literal. There is no runtime bounds checking.

int arr[5];                  // Uninitialized fixed-size array
int primes[3] = {2, 3, 5};   // Array with initializer list
arr[0] = primes[2];          // Indexing (Read & Write)
            

2.4 Operators

CategoryOperatorsNotes
Arithmetic+ - * /Standard integer math. Evaluated left-to-right.
Relational== != < > <= >=Evaluates to 1 (true) or 0 (false).
Logical&& ||C-style short-circuit evaluation supported by IR generator.
Memory& *Address-of and Dereference operators.

3. Functions & Recursion

VizLang supports complex procedural generation through functions. Functions must declare their return type (int or void). The semantic analyzer guarantees that all code paths in an int function reach a return statement.

3.1 Pass-by-Value (Standard)

Standard arguments (like integers) are passed by value. The function receives a copy of the data. Modifying the argument inside the function does not affect the original variable.

// Recursive Factorial Function
int factorial(int n) {
    if (n <= 1) { return 1; }
    // 'n' is a local copy for this frame
    return n * factorial(n - 1); 
}
                

3.2 Pass-by-Reference (Array Decay)

When an array is passed as an argument, the semantic analyzer forces Array Decay. Instead of copying the entire array, it is passed by reference as an 8-byte memory pointer.

// Notice the empty brackets: arr[]
// This tells the compiler it is a pointer
void mutateArray(int arr[], int index) {
    arr[index] = 999; 
    // This modifies the caller's array!
}
                

3.3 Explicit Pass-by-Reference (Pointers)

To modify a standard variable inside a function, you must pass its memory address using the Address-of (&) operator. The function receives it as a Pointer (*) and mutates it using the Dereference operator.

// Function expects an 8-byte memory pointer
void addBonus(int *scorePtr) {
    *scorePtr = *scorePtr + 500; // Mutate caller's data
}

int main() {
    int myScore = 100;
    
    // Pass the memory address, NOT the value
    addBonus(&myScore); 
    
    print(myScore); // Outputs 600
    return 0;
}
            

4. Built-in Hooks & Loops

Because Viz-Compiler is an educational tool, it includes special grammar rules designed to teach fundamental computer science concepts.

4.1 Control Flow

// If-Else Branching
if (x == 10) { 
    print("Ten"); 
} else { 
    print("Other"); 
}

// Standard Loops
while (x < 10) { x = x + 1; }

for (int i = 0; i < 5; i = i + 1) { 
    print(i); 
}

// The Repeat Loop (Evaluates limit exactly once)
repeat (5 * 2) {
    print("This prints 10 times!");
}
                

4.2 Native Action Hooks

These commands bypass standard function calls and are injected directly as distinct IR instructions or external assembly jumps.

// I/O Operations
print(x);        // Prints variable 
print("Text");   // Prints string literal
input(x);        // Reads integer from stdin

// Hardware & Memory Hooks
printarray(arr); // Dumps entire array contents
sort(arr, 5);    // Here 5 was the Size of the array .
                

5. IR Instruction Set Reference

The Intermediate Representation (IR) flattens the AST into 3-Address Code (Quads) generated by ir.c. This acts as the bridge to Assembly.

OpcodeArg1Arg2ResultSemantics
IR_MOVSourceDestResult = Arg1
IR_ADD / SUBLeftRightDestResult = Arg1 +/- Arg2
IR_MUL / DIVLeftRightDestResult = Arg1 */÷ Arg2
IR_SHL / SHRValueShiftDestResult = Arg1 <</>> Arg2 (Used by Optimizer)
IR_LT / GT / EQLeftRightDestSets Result to 1 or 0 based on comparison
IR_IF_FALSEConditionLabelJump to Result if Arg1 == 0
IR_GOTOLabelUnconditional jump to Arg1
IR_LABELNameJump target marker
IR_CALLFunc NameDestExecutes function, stores return in Result
IR_PARAM / ARGVar/ValueIndexPushes argument to stack before a CALL
IR_ARR_DECLNameSizeAllocates memory for array
IR_ARR_LOADArray NameIndexDestResult = Array[Index]
IR_ARR_STOREArray NameIndexValueArray[Index] = Result (Value)

6. Optimization Reference

The optimize_ir() pipeline in ir.c performs a multi-pass sweep over the IR Quads to improve execution speed before Code Generation.

7. Complete Code Example

This is a complete, valid VizLang program demonstrating functions, recursion, array decay, and mathematical logic. Our compiler can translate this all the way down to executable x86-64 assembly!

// Recursive Binary Search Implementation
int binarySearch(int arr[], int l, int r, int key) {
    int mid;
    
    // Base Case: Not found
    if (l > r) { 
        return 0 - 1; 
    }
    
    // Find middle (Optimized to bit-shift by IR)
    mid = (l + r) / 2;
    
    // Check if key is present at mid
    if (arr[mid] == key) { 
        return mid; 
    } else {
        // Recurse left or right
        if (key < arr[mid]) { 
            return binarySearch(arr, l, mid - 1, key); 
        } else { 
            return binarySearch(arr, mid + 1, r, key); 
        }
    }
}

int main() {
    int data[5] = {10, 20, 30, 40, 50};
    int target = 30;
    int result;

    print("Searching for target...");
    
    // Call the function
    result = binarySearch(data, 0, 4, target);
    
    if (result != 0 - 1) {
        print("Found at index:");
        print(result);
    } else {
        print("Not Found");
    }
    
    return 0;
}