Skip to content

About

Compiler for cowlang language

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

cowlang

cowlang is a small programming language and compiler written from scratch in Java.

The compiler uses a hand-written lexer and recursive-descent parser to build an abstract syntax tree, then translates the program into LLVM IR. The generated LLVM can be assembled and compiled into a native executable using LLVM and Clang.

The project is primarily built as a way to learn how compilers work internally, from source code all the way down to native machine code.

Example

x <- 3 > 4;
x <- x + (4 > 3);

yell x;

Output:

1

Comparisons evaluate to integers:

false -> 0
true  -> 1

Language

cowlang currently supports:

  • Integer literals
  • Variables
  • Assignment using <-
  • Arithmetic expressions
  • Comparison expressions
  • Parentheses
  • yell for output with a newline
  • whisper for output without a newline
  • when / otherwise control flow
  • // comments

Assignment

x <- 10;
y <- x + 5;

Variables are created when they are first assigned.

Arithmetic

x <- 10 + 5;
y <- x * 2;
z <- (x + y) / 3;

Supported operators:

+
-
*
/

Normal operator precedence is used.

For example:

x <- 10 + 5 * 2;

is interpreted as:

10 + (5 * 2)

Comparisons

x <- 10 > 5;
y <- 3 == 4;

Supported comparison operators:

==
!=
<
<=
>
>=

Comparison results are represented as integers in cowlang:

true  = 1
false = 0

This means comparisons can be used inside other expressions:

x <- 5 + (10 > 3);

which evaluates to:

6

Output

yell prints a value followed by a newline:

yell 42;

whisper prints without a newline:

whisper 42;

Compiler architecture

The compiler currently consists of four main stages:

cowlang source code
        |
        v
      Lexer
        |
        v
      Tokens
        |
        v
      Parser
        |
        v
       AST
        |
        v
 LLVM IR Generator
        |
        v
    LLVM IR (.ll)
        |
        v
      LLVM
        |
        v
      Clang
        |
        v
 Native executable

Lexer

The lexer converts source code into tokens.

For example:

x <- 10 + 5;

becomes conceptually:

IDENTIFIER("x")
ASSIGN("<-")
INTEGER("10")
PLUS("+")
INTEGER("5")
SEMICOLON(";")

Parser

The parser is a hand-written recursive-descent parser.

Expression precedence is handled through separate parsing levels for comparisons, addition/subtraction and multiplication/division.

For example:

x <- 10 + 20 * 3;

produces an AST equivalent to:

Assignment
└── x
    └── Add
        ├── 10
        └── Multiply
            ├── 20
            └── 3

LLVM IR generation

The backend traverses the AST and emits textual LLVM IR.

A cowlang expression such as:

x <- 3 > 4;

can generate LLVM IR similar to:

%tmp.0 = icmp sgt i32 3, 4
%tmp.1 = zext i1 %tmp.0 to i32

%var.x = alloca i32
store i32 %tmp.1, ptr %var.x

LLVM comparisons return an i1, which is converted to i32 so that cowlang can represent boolean results as 0 and 1.

Building

Requirements

The project requires:

  • Java
  • Gradle
  • LLVM
  • Clang

The Gradle wrapper is included in the repository.

Build the compiler

On Linux/macOS:

./gradlew build

On Windows:

.\gradlew.bat build

Running the compiler

A cowlang source file uses the .cow extension.

Example:

app/examples/hello.cow

The Java compiler can be run through Gradle.

On Linux:

./gradlew run --args="examples/hello.cow"

On Windows:

.\gradlew.bat run --args="examples\hello.cow"

The compiler generates an LLVM .ll file.

Native compilation

The generated LLVM IR can be assembled using llvm-as:

llvm-as program.ll -o program.bc

The resulting LLVM bitcode can then be compiled using Clang:

clang program.bc -o program

Run the resulting executable:

./program

On Windows, the resulting file can instead be compiled as an .exe.

Compiler scripts

The repository contains helper scripts intended to automate the compilation pipeline.

Windows

compile.ps1

The PowerShell script automates the process from:

.cow
 -> .ll
 -> .bc
 -> .exe

Linux

compile.sh

The Bash version provides the equivalent compilation workflow on Linux.

Current status

The compiler can currently compile basic integer programs from cowlang source code into LLVM IR and then into native executables.

Working features include:

  • Lexical analysis
  • Recursive-descent parsing
  • Abstract syntax tree generation
  • Integer variables
  • Arithmetic expressions
  • Expression precedence
  • Parenthesized expressions
  • Comparison expressions
  • Integer output
  • LLVM IR generation
  • Native compilation through LLVM and Clang
  • Conditional control flow using LLVM basic blocks and branches

Some syntax is already represented in the frontend but does not yet have complete LLVM code generation.

Planned features

Future work includes:

  • while loops
  • loop N times
  • Functions using ~>
  • Function parameters
  • return
  • String code generation
  • Variable scopes
  • Semantic analysis
  • Improved type checking
  • Better compiler diagnostics
  • AST and token dump options
  • Compiler optimizations
  • Automated tests

Goals

cowlang is not intended to compete with production programming languages.

The goal of the project is to explore and implement the major components of a compiler:

  • Lexical analysis
  • Parsing
  • Syntax trees
  • Semantic analysis
  • Symbol handling
  • Intermediate representations
  • Control flow
  • Code generation
  • Native compilation

Rather than relying on a parser generator or an existing compiler frontend, the major compiler stages are implemented manually in order to better understand how they work.

Resources

LLVM Documentation

The official LLVM documentation is the best reference for LLVM IR syntax, instructions, types, control flow, and other language details:

It is especially useful when implementing the LLVM backend and looking up instructions such as icmp, br, zext, load, and store.

LLVM Tutorial

A useful resource for learning how to build a first compiler frontend with LLVM is the official LLVM tutorial:

The tutorial covers many of the same core concepts used in this project, including lexing, parsing, AST construction, control flow, and LLVM IR generation.

Compiler Fundamentals

For a deeper understanding of compiler design and implementation:

  • Andrew W. Appel — Modern Compiler Implementation in C (1998)

Although the book uses C rather than Java (there exists a book by the same author for Java, but I do not own that book unfortunately), its discussion of compiler fundamentals is largely language-agnostic. It covers topics such as lexical analysis, parsing, abstract syntax trees, semantic analysis and more.

About

Compiler for cowlang language

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages