Exercises
These exercises extend the type system and code generation. Each builds on your understanding of static typing and LLVM.
Exercise 1: Add Float Type
Add float type with 64-bit floating point:
def area(radius: float) -> float {
return 3.14159 * radius * radius
}
area(2.0)
Hints:
- Add
Type::Floatand map to LLVMf64 - Parse float literals:
Float = { ASCII_DIGIT+ ~ "." ~ ASCII_DIGIT+ } - Use
build_float_add,build_float_mul, etc. in codegen - Handle type coercion:
int + floatshould promote tofloat
Checkpoint: 2.0 * 3.0 returns 6.0
Exercise 2: Add Modulo Operator
Add the % operator:
def is_even(n: int) -> bool {
return n % 2 == 0
}
Hints:
- Add
BinaryOp::Mod - Use
build_int_signed_remfor signed modulo - Type rule:
int % int -> int
Checkpoint: 10 % 3 returns 1
Exercise 3: Add Short-Circuit Operators
Add && and || with short-circuit evaluation:
def safe_div(a: int, b: int) -> int {
if (b != 0 && a / b > 0) {
return a / b
}
return 0
}
Hints:
- Cannot use simple
build_and- need control flow - For
a && b: evaluatea, if false jump to end withfalse, else evaluateb - Generate basic blocks:
eval_left,eval_right,merge - Use phi node to merge results
Checkpoint: false && (1/0 > 0) should not crash (division never executes)
Exercise 4: Add Type Aliases
Allow type aliases:
type Distance = int
def manhattan(x: Distance, y: Distance) -> Distance {
return x + y
}
Hints:
- Store aliases in a
HashMap<String, Type> - Resolve aliases during parsing or type checking
- Aliases should be interchangeable with their underlying type
Checkpoint: Distance and int should be compatible
Exercise 5: Add Array Type
Add fixed-size arrays:
def sum(arr: [int; 3]) -> int {
return arr[0] + arr[1] + arr[2]
}
Hints:
- Add
Type::Array { element: Box<Type>, size: usize } - LLVM: use
context.i64_type().array_type(size) - Access:
build_extract_valueor GEP with indices - Type check: ensure index is in bounds (statically if possible)
Checkpoint: Array access compiles and runs correctly
Stretch Goals
- String type: Pointer to null-terminated bytes
- Type inference for locals:
let x = 5infersint - Generic functions:
def identity<T>(x: T) -> T { return x } - Struct types:
struct Point { x: int, y: int } - Pattern matching on ints:
match n { 0 => ..., 1 => ..., _ => ... }
What You Practiced
- Extending the type system with new types
- Generating LLVM IR for new constructs
- Type checking rules and inference
- The pattern: Type → TypeCheck → Codegen