Gazlab logo Gazlab

About Gazlab and the Gazprea Compiler

Open the Gazprea compiler explorer

About Gazprea

Gazprea is derived from a language originally designed at the IBM Hardware Acceleration Laboratory in Markham, Ontario, Canada. Built for fast finance and business operations, it emphasizes computational efficiency and ease of use. The language was later adapted for educational purposes, serving as the foundation for compiler design courses at universities including the University of Alberta.

What makes Gazprea interesting is its dual nature: it's designed to be a quick, easy-to-write scripting language, yet it compiles to native code for maximum performance. The language features four primitive types (integers, reals, characters, and booleans), each supporting scalar, vector, and matrix forms. Variables can be declared as const (immutable by default) or var (mutable), with type inference allowing clean, concise code without explicit type annotations:

// Type inference in action
var i = 42;              // integer (inferred)
const r = 3.14;          // real (inferred)
var arr = [1, 2, 3];     // integer[*] array (inferred)
var t = (1, 2.0, 'a');   // tuple(integer, real, character)

// Explicit types with qualifiers
const integer x = 10;    // immutable integer
var real y = 5.5;        // mutable real

Gazprea's type system is particularly rich, supporting tuples (ordered collections with optional named fields), structs, arrays, vectors, and strings. The language includes all standard operations and control flow statements, but what really sets it apart are its composite operations - generator expressions and filter expressions that compile to highly optimized code, often outperforming traditional loop-based implementations.

One of Gazprea's most distinctive features is its separation of functions and procedures: functions are pure (no side effects, no I/O), while procedures can modify state and perform I/O. This design enforces functional programming principles while still allowing imperative programming when needed, giving developers the best of both worlds:

// Pure function - no side effects, no I/O
function factorial(integer n) returns integer {
  if (n <= 1) return 1;
  else return n * factorial(n - 1);
}

// Procedure - can have side effects and I/O
procedure main() returns integer {
  var integer x = 5;
  var integer result = factorial(x);
  result -> std_output;  // I/O allowed in procedures
  return 0;
}

CMPUT 415 at the University of Alberta

CMPUT 415 (Compiler Design) at the University of Alberta (UAlberta) is a capstone course that brings together everything I learned in my computing science degree: software design, programming, data structures, algorithms, theoretical computing, documentation, and machine architecture. This course gave me the opportunity to build a complete compiler from scratch, serving as the ultimate integration of my undergraduate knowledge.

Working in a team of four, we spent an entire semester implementing a full compiler pipeline for Gazprea. This wasn't just an academic exercise - we were building a production-quality compiler that handles everything from lexical analysis and parsing to semantic analysis, type checking, and code generation. The course served as the capstone of my undergraduate program, requiring me to synthesize knowledge from dozens of previous courses.

What made CMPUT 415 particularly valuable wasn't just the compiler implementation itself, but the emphasis on real-world software engineering practices. We learned proper planning and organization, effective issue tracking, and test-driven development. I gained experience managing a large-scale software project, collaborating effectively in a team, and navigating the complexity of building a real-world compiler system.

The course specification for Gazprea (used in Fall 2025 and subsequent semesters at the University of Alberta) is remarkably comprehensive, covering the full language syntax, semantics, type system, and built-in functions. This specification became our bible - the definitive reference we consulted constantly while implementing our compiler.

My Compiler Implementation

This project represents my complete implementation of a compiler frontend and backend for Gazprea. I built the compiler in C++, leveraging ANTLR4 (an adaptive LL(*) parser generator) to generate a robust parser from the Gazprea grammar specification. One of my favorite features is the ability to visualize the Abstract Syntax Tree (AST) with Graphviz at any point during compilation, which proved invaluable for debugging and understanding the compilation process.

Here's a simple Gazprea program that my compiler processes:

procedure main() returns integer {
  // Calculate sum of squares using a generator
  integer[10] squares = [i in 1..10 | i * i];
  var integer sum = 0;

  loop i in 1..10 {
    sum = sum + squares[i];
  }

  "Sum of squares from 1 to 10: " -> std_output;
  sum -> std_output;
  '
' -> std_output;

  return 0;
}

My compiler transforms this source code through multiple stages: lexical analysis → parsing → AST construction → semantic analysis → type checking → MLIR generation → LLVM IR → native code.

The compilation pipeline is sophisticated: my compiler generates MLIR (Multi-Level Intermediate Representation) bytecode using MLIR's C++ Interface, which is then lowered to raw LLVM IR. The LLVM IR is compiled to object files using llc, producing native executable code. To support Gazprea's runtime features, I developed custom runtime libraries in C that handle built-in functions, memory management, and runtime error handling.

I designed the compiler architecture following traditional compiler design principles with a clear separation of concerns. The codebase includes a comprehensive AST class hierarchy, visitor patterns for tree traversal, multiple analysis passes, and sophisticated symbol tables for type management. I implemented a pass management system inspired by LLVM's approach, with support for annotating passes with metadata, though I kept it simpler by not tracking pass invalidations.

Language Features and Type System

Implementing Gazprea's type system was one of the most challenging and rewarding parts of the project. The language supports a rich variety of types, as shown in this example:

// Primitive types
const integer x = 42;
const real pi = 3.14159;
const character c = 'A';
const boolean flag = true;

// Arrays and vectors
integer[5] fixed = [1, 2, 3, 4, 5];
var vector<integer> dynamic;

// Tuples
tuple(integer, real, string) point = (10, 20.5, "origin");

// Structs
struct Point (integer x, integer y) p = Point(3, 4);
p.x -> std_output;  // Access struct fields

The language supports:

  • Primitive Types: Boolean, Character, Integer, and Real types, each with scalar, vector, and matrix variants
  • Composite Types: Tuples with optional named fields, structs for named aggregates, and fixed-size arrays
  • Dynamic Types: Vectors (dynamically-sized arrays) and strings (character vectors with string literal support)
  • Type Qualifiers: const (default, immutable) and var (mutable) with type inference
  • Type Operations: Automatic type promotion, explicit casting, and type aliasing

The array operations are particularly powerful: 1-indexed access with negative indexing support, slicing with range operators (..), stride operations (by), concatenation (||), element-wise operations, and dot product (**). Matrix operations include true matrix multiplication and built-in dimension functions (rows(), columns()):

integer[5] arr = [10, 20, 30, 40, 50];

// Indexing (1-indexed with negative support)
integer first = arr[1];      // 10
integer last = arr[-1];      // 50

// Slicing
integer[*] slice = arr[2..4];  // [20, 30] (exclusive end)
integer[*] all = arr[..];      // all elements

// Range with stride
integer[*] evens = 1..10 by 2;  // [1, 3, 5, 7, 9]

// Concatenation
integer[*] combined = [1, 2] || [3, 4];  // [1, 2, 3, 4]

// Element-wise operations
integer[*] doubled = [1, 2, 3] * 2;  // [2, 4, 6]

// Dot product
integer dot = [1, 2, 3] ** [4, 5, 6];  // 32

// Matrix multiplication
integer[2][2] A = [[1, 2], [3, 4]];
integer[2][2] B = [[5, 6], [7, 8]];
integer[2][2] C = A ** B;  // true matrix multiplication

Perhaps the most elegant feature is Gazprea's generator expressions (array comprehensions) like [i in 1..10 | i * i]. These generators, combined with filter expressions and domain expressions, allow the compiler to generate highly optimized code that often outperforms traditional loop-based implementations. This was particularly satisfying to implement correctly:

// 1D generator - creates array of squares
integer[10] squares = [i in 1..10 | i * i];
// Result: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

// 2D generator - creates matrix
integer[3][3] mult = [i in 1..3, j in 1..3 | i * j];
// Result: [[1, 2, 3], [2, 4, 6], [3, 6, 9]]

// Nested generators
integer[*] nested = [i in [j in 1..5 | j * 2] | i + 1];
// Result: [3, 5, 7, 9, 11]

Technical Implementation

Building this compiler required implementing every major component of a modern compiler:

  • Lexical Analysis & Parsing: I used ANTLR4 to generate an LL(*) parser that processes Gazprea source code according to the grammar specification. The parser handles the full language syntax, including edge cases and error recovery.
  • AST Construction: I built a custom AST builder that transforms the parse tree into a strongly-typed Abstract Syntax Tree. Each language construct has its own node type, ensuring type safety throughout the compilation process.
  • Semantic Analysis: I implemented multiple analysis passes including scope analysis, comprehensive type checking, type resolution, and qualifier enforcement. Each pass builds on the previous one, creating a robust semantic understanding of the program.
  • Type System: I developed a sophisticated type system that handles type promotion, casting, and conversion. The system correctly handles complex scenarios like tuple compatibility, array promotions, and type inference.
  • Code Generation: I implemented MLIR/LLVM IR generation with specialized services for different language constructs. This includes handling aggregates, binary operations, function calls, loops, matrices, and subroutines - each with their own optimization opportunities.
  • Error Handling: I built a comprehensive error reporting system that provides clear, actionable error messages for syntax errors, semantic errors, type errors, and runtime errors. Good error messages were crucial for usability.

The pass management system I built allows multiple analysis passes to execute in sequence, with each pass potentially depending on information computed by previous passes. I ensured the compiler properly handles forward declarations, namespace resolution, and comprehensive symbol table management - features that are easy to overlook but essential for correctness.

Memory management was handled through custom runtime libraries that provide allocation, deallocation, and garbage collection support for Gazprea's dynamic data structures. The runtime includes support for all built-in functions like length(), shape(), reverse(), format(), and stream state management.

Testing and Quality Assurance

One of the most important lessons I learned was the value of comprehensive testing. My compiler includes thousands of test cases covering every language feature, edge cases, and error conditions. The test suite includes unit tests for individual components, integration tests for compiler passes, and end-to-end compilation tests that verify the entire pipeline.

I set up continuous integration and continuous deployment (CI/CD) pipelines that automatically run the full test suite on every change. This gave me confidence that modifications wouldn't break existing functionality. I embraced test-driven development (TDD) practices throughout the project - tests weren't just for verifying functionality, but for ensuring that refactoring and new features wouldn't break existing code.

The comprehensive test suite became my safety net, allowing me to refactor and improve the codebase with confidence. This experience fundamentally changed how I approach software development - I now use TDD everywhere.

Gazlab: The Web Interface

To showcase the compiler and make it accessible, I built Gazlab - a web-based compiler explorer that provides an interactive environment for writing, compiling, and executing Gazprea programs. Built with modern web technologies including TypeScript, Monaco Editor (the same editor that powers VS Code), and a Node.js backend, Gazlab offers real-time compilation feedback, syntax highlighting, and comprehensive error reporting.

You can try it right here! Write Gazprea code in the editor, and Gazlab will compile and execute it in real-time. Here's a complete example you can run:

// Fibonacci sequence using recursion
function fib(integer n) returns integer {
  if (n <= 1) return n;
  else return fib(n - 1) + fib(n - 2);
}

procedure main() returns integer {
  "First 10 Fibonacci numbers:
" -> std_output;
  loop i in 0..9 {
    fib(i) -> std_output;
    " " -> std_output;
  }
  '
' -> std_output;
  return 0;
}

Gazlab allows users to write Gazprea code, compile it instantly, view the output, and debug compilation errors - all in the browser. I included features like code templates, syntax highlighting, word wrap, theme customization, and the ability to share code via URL. Gazlab serves dual purposes: it's both a learning tool for students exploring Gazprea and a demonstration platform showcasing the compiler's capabilities.

Building Gazlab was a great way to apply my web development skills while creating something that makes the compiler more accessible and useful. The real-time compilation feedback and interactive environment make it easy to experiment with Gazprea code and see immediate results.

Lessons Learned and Impact

Building a complete compiler for Gazprea as part of CMPUT 415 at the University of Alberta was one of the most challenging and rewarding experiences of my undergraduate career. It provided invaluable experience in large-scale software engineering, teaching me critical lessons about planning, organization, team collaboration, and the complexity of building production-quality software systems.

One of the most important realizations was that implementing the "normal case" for a compiler feature is often straightforward, but handling errors and edge cases is where the real challenge lies. The project emphasized the critical importance of comprehensive error handling, proper type system design, and thorough testing. These skills have proven directly applicable to my professional software development work.

This project taught me that building a working compiler is achievable, but building a perfect compiler is an ongoing challenge. It gave me a deep understanding of how programming languages work, how compilers transform source code into executable programs, and how modern compiler infrastructure (like LLVM and MLIR) enables efficient code generation. The experience fundamentally shaped how I think about programming languages, software architecture, and system design.

Working on this compiler was hands down the most interesting and engaging project I completed during my studies at the University of Alberta. It combined theory and practice in a way that made abstract concepts concrete, and it gave me confidence that I could tackle complex, real-world software engineering challenges.

More pages

Frequently asked questions

What is Gazlab?

Gazlab is a free in-browser compiler explorer for Gazprea. Write, compile, and run Gazprea programs online with real-time output and errors, without installing the full toolchain.

What is Gazprea?

Gazprea is derived from a language originally designed at the IBM Hardware Acceleration Laboratory in Markham, ON. It reads like a scripting language but compiles for speed, and is used in CMPUT 415 at the University of Alberta.

What is CMPUT 415?

CMPUT 415 is Compiler Design at the University of Alberta. The course covers compilers and interpreters, lexical and syntax analysis, type checking, code generation, and optimization. Students build a Gazprea compiler with ANTLR4, MLIR, and LLVM.

Who built Gazlab?

Gazlab was built by Nandan Ramesh around the Gazprea compiler project for CMPUT 415 at the University of Alberta.

Is Gazlab affiliated with the University of Alberta?

Gazlab is an unofficial student project. It is not an official University of Alberta or IBM product. It is meant to help people explore Gazprea as taught in CMPUT 415.