Skip to content

Tier 1: Core Language Features - Enums, Options, Maps, Generics #15

@mjm918

Description

@mjm918

Overview

This issue tracks the implementation of Tier 1 core language features required to run examples/simple.naml. These are foundational features that many other language constructs depend on.

Features

1. Enums with Variants

Used in: simple.naml lines 152-166

enum UserStatus {
    Active,
    Inactive,
    Suspended(string),        // variant with associated data
    PendingVerification(string),
    Deleted(int)
}

// Usage
var status: UserStatus = UserStatus.Active;
var suspended: UserStatus = UserStatus::Suspended("policy violation");

Implementation Requirements:

  1. Runtime representation - Tagged union structure:

    #[repr(C)]
    pub struct NamlEnum {
        pub header: HeapHeader,
        pub variant_tag: u32,      // which variant
        pub data_size: u32,        // size of associated data
        // associated data follows (if any)
    }
  2. Codegen changes (codegen/cranelift/mod.rs):

    • Handle Expression::Path for enum variant access (UserStatus::Active)
    • Handle Expression::Call for variants with data (UserStatus::Suspended("reason"))
    • Extend switch/case to pattern match on enum tags
    • Implement enum comparison (==, !=)
  3. Runtime functions (runtime/enum.rs):

    • naml_enum_new(type_id, variant_tag, data_ptr, data_size) -> *mut NamlEnum
    • naml_enum_get_tag(enum_ptr) -> u32
    • naml_enum_get_data(enum_ptr) -> *mut u8
    • naml_enum_incref/decref

2. Option Type

Used in: simple.naml lines 141, 173-183, 239, etc.

pub extension: option<string>
var first_name: option<string> = none;
var node: LinkedListNode<T> = self.tail else { return; }
self.first_name.or_default("")
optional.is_some()
optional.is_none()
some(value)

Implementation Requirements:

  1. Runtime representation - Can be implemented as a special enum:

    // Option is tag=0 for none, tag=1 for some
    // When some, data follows the tag
  2. Codegen changes:

    • Handle none literal → naml_option_none()
    • Handle some(value) constructor → naml_option_some(value)
    • Handle .is_some(), .is_none() method calls
    • Handle .or_default(value) method
    • Critical: Handle var x = optional else { ... } pattern:
      if optional.is_none() {
          execute else block (may contain return/break)
      } else {
          x = optional.unwrap()
      }
      
  3. Runtime functions (runtime/option.rs):

    • naml_option_none() -> i64 (returns tagged none)
    • naml_option_some(value: i64) -> i64 (returns tagged some)
    • naml_option_is_some(opt: i64) -> bool
    • naml_option_is_none(opt: i64) -> bool
    • naml_option_unwrap(opt: i64) -> i64
    • naml_option_or_default(opt: i64, default: i64) -> i64

3. Map Type

Used in: simple.naml lines 179, 424, 704-708

pub metadata: map<string, string>
pub headers: map<string, string>
return { "user_id": self.user_id.value, "email": self.email }
self.headers[name] = value
return self.headers[name]

Implementation Requirements:

  1. Runtime representation:

    #[repr(C)]
    pub struct NamlMap {
        pub header: HeapHeader,
        pub capacity: usize,
        pub length: usize,
        pub buckets: *mut MapBucket,  // hash buckets
    }
    
    #[repr(C)]
    pub struct MapBucket {
        pub key: i64,
        pub value: i64,
        pub next: *mut MapBucket,  // for chaining
    }
  2. Codegen changes:

    • Handle Expression::Map for map literals { "key": "value" }
    • Handle Expression::Index for map access map[key]
    • Handle map assignment map[key] = value
    • Handle map in struct fields
  3. Runtime functions (runtime/map.rs):

    • naml_map_new(capacity: usize) -> *mut NamlMap
    • naml_map_set(map: *mut NamlMap, key: i64, value: i64)
    • naml_map_get(map: *mut NamlMap, key: i64) -> i64 (returns option)
    • naml_map_contains(map: *mut NamlMap, key: i64) -> bool
    • naml_map_remove(map: *mut NamlMap, key: i64) -> bool
    • naml_map_len(map: *mut NamlMap) -> i64
    • naml_map_incref/decref
    • naml_string_hash(str: *const u8) -> u64 (for string keys)

4. Generics

Used in: simple.naml lines 5-30, 237-418, 736-836

interface Comparable<T> { ... }
pub struct LinkedList<T> implements Collection<T> { ... }
pub fn map_array<T, U>(arr: [T], f: fn(T) -> U) -> [U]
pub struct BinarySearchTree<T: Comparable<T>> { ... }

Implementation Requirements:

  1. Monomorphization approach (recommended for JIT):

    • At compile time, instantiate generic types/functions for each concrete type used
    • LinkedList<int> becomes a separate compiled struct from LinkedList<string>
  2. Codegen changes:

    • Track generic instantiations during type checking
    • Generate separate code for each instantiation
    • Handle trait bounds (T: Comparable<T>) by verifying method existence
  3. Typechecker changes (typechecker/):

    • Already handles generic type parameters
    • Need to track which concrete instantiations are used
    • Pass instantiation info to codegen

Files to Modify

File Changes
namlc/src/runtime/mod.rs Add option, map, enum modules
namlc/src/runtime/option.rs New file - option type implementation
namlc/src/runtime/map.rs New file - hash map implementation
namlc/src/runtime/enum.rs New file - enum runtime support
namlc/src/codegen/cranelift/mod.rs Handle new expression types
namlc/src/typechecker/mod.rs Track generic instantiations

Testing

Create incremental test files:

// test_option.naml
fn main() {
    var x: option<int> = some(42);
    var y: option<int> = none;
    
    if (x.is_some()) {
        println("x has value");
    }
    
    var val: int = x.or_default(0);
    println(val);
    
    var unwrapped: int = x else {
        println("x was none");
        return;
    };
    println(unwrapped);
}
// test_enum.naml
enum Color {
    Red,
    Green,
    Blue,
    Custom(int, int, int)
}

fn main() {
    var c: Color = Color.Red;
    var custom: Color = Color::Custom(255, 128, 0);
    
    switch (c) {
        case Red: println("red");
        case Green: println("green");
        case Blue: println("blue");
        case Custom(r, g, b): {
            print("rgb: ");
            println(r);
        }
    }
}

Priority

High - These features are prerequisites for most of simple.naml and block Tier 2 and Tier 3 features.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions