diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index a74e89a2..fa46b9be 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -56,21 +56,25 @@ We will review your pull request and provide feedback. To set up a local development environment: -1. Ensure you have the required dependencies installed (e.g., cmake, ninja, gcc or your preferred compiler). +1. Ensure you have a C compiler installed, such as `cc`, `gcc`, or `clang`. -2. Clone the Luma repository: +1. Clone the Luma repository: ```bash -git clone https://github.com/your-username/luma.git -cd luma +git clone https://github.com/Luma-Programming-Language/Luma.git +cd Luma ``` -3. Build the project following instructions in the README (or specific build scripts). +1. Build the project with the bootstrap script: -4. Run to ensure everything is working: +```bash +./scripts/bootstrap-build.sh +``` + +1. Run the compiler to ensure everything is working: ```bash -./luma +./bin/luma --help ``` ## Style Guide @@ -86,7 +90,7 @@ cd luma Tests are important to maintain code quality. Before submitting a pull request: - Write new tests for your features or bug fixes -- Run the full test suite to make sure everything passes +- Run the full test suite with `./bin/luma test/test.lx -name testing` - Avoid breaking existing tests ## License diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 7d1ff221..42bc372d 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -6,7 +6,7 @@ Luma is a self-hosted compiler — it transpiles to C and just needs a C compile Grab the archive for your platform from the [latest release](releases/v0.3.5.md). -### Linux / macOS +### Linux / macOS (Prebuilt Binary) ```bash tar -xzf luma-v0.3.5-linux-x86_64.tar.gz # or luma-v0.3.5-macos-x86_64.tar.gz @@ -17,7 +17,7 @@ sudo ./install.sh # system-wide, requires sudo ./install.sh # user-local install, no sudo needed ``` -### Windows +### Windows (Prebuilt Binary) 1. Extract `luma-v0.3.5-windows-x86_64.zip`. 2. Run `install.bat` — as Administrator for a system-wide install, or without for a user-local one. @@ -68,7 +68,7 @@ If none of the three has the file, you'll get a "module not found — was its fi If you'd rather not run the installer script: -### Linux / macOS +### Linux / macOS (Manual Installation) **System-wide:** @@ -90,9 +90,9 @@ cp -r std/* ~/.luma/std/ export PATH="$PATH:$HOME/.local/bin" ``` -### Windows +### Windows (Manual Installation) -1. Create `C:\Program Files\luma\bin` and `\std` (system-wide) or `%USERPROFILE%\.luma\bin` and `\std` (user-local). +1. Create `C:\Program Files\luma\bin` and `C:\Program Files\luma\std` (system-wide) or `%USERPROFILE%\.luma\bin` and `%USERPROFILE%\.luma\std` (user-local). 2. Copy `luma.exe` into the `bin` directory. 3. Copy the contents of `std/` into the `std` directory. 4. Add the `bin` directory to your `PATH` environment variable. @@ -105,7 +105,7 @@ export PATH="$PATH:$HOME/.local/bin" luma --version ``` -``` +```text Luma Compiler v0.3.5 ``` @@ -113,7 +113,7 @@ Luma Compiler v0.3.5 ## Troubleshooting -**"module not found — was its file passed with -l?"** +### "module not found — was its file passed with -l?" Either the file wasn't passed with `-l` at all, or it's not sitting in any of the three [Standard Library Paths](#standard-library-paths) tiers above. Double-check the install actually landed where you expect (`ls ~/.luma/std/` or `ls /usr/local/lib/luma/std/`), and that the path you're passing to `-l` matches what's actually on disk relative to your current directory. @@ -126,7 +126,7 @@ export PATH="$PATH:$HOME/.local/bin" # user-local install export PATH="$PATH:/usr/local/bin" # system-wide install (usually already on PATH) ``` -**PATH issues (Windows)** +### PATH issues (Windows) Search "Environment Variables" in the Start menu, edit your PATH, and add `%USERPROFILE%\.luma\bin` or `C:\Program Files\luma\bin`, then restart your terminal. diff --git a/docs/README.md b/docs/README.md index d9b4c418..051f3901 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,9 +2,7 @@ *A low-level compiled language for people who want C's control without giving up their afternoon to memory bugs.* -

- Luma Logo -

+![Luma Logo](../assets/luma.png) [Why?](#why) • [Language Tour](#language-tour) • [Self-Hosted](#self-hosted) • [Getting Started](#getting-started) • [Join Us](#join-us) @@ -117,7 +115,7 @@ That's a small slice. The full language reference is in [`docs/docs.md`](https:/ The compiler is written in Luma. It compiles itself, and every commit proves it can: an existing `luma` binary builds the current compiler source, that output builds the same source again, and the two results are diffed byte-for-byte. If a compiler can't reproduce itself exactly from its own source, something's wrong, so that check runs before anything else does, on every push and every PR. -Releases are cross-compiled from that same self-hosted compiler: a single Linux CI run produces Linux, Windows, and macOS binaries, and the Windows/macOS ones actually get downloaded and executed on real runners before a release goes out. See [`docs/releases/`](releases/) for what's shipped and when. +Releases are built from that same self-hosted compiler: Linux and Windows binaries are produced on Linux, while macOS binaries are built on a macOS CI runner. The Windows and macOS binaries are downloaded and executed on real runners before a release goes out. See [`docs/releases/`](releases/) for what's shipped and when. --- @@ -134,13 +132,13 @@ Latest release: **[v0.3.5](releases/v0.3.5.md)** - A language server (`luma --lsp`) with diagnostics, hover, and completion - Cross-platform builds for Linux, Windows, and macOS, verified in CI -**What's not there yet:** generic structs (generic *functions* — `fn` — are implemented, see [docs.md's Generics section](docs.md#generics)), and a few rough edges in the static analyzer around conditional allocation paths. See the Known Limitations section of the [latest release notes](releases/v0.3.5.md) for the current honest list. +**What's not there yet:** a few rough edges in the static analyzer around conditional allocation paths. Generic functions and generic structs are implemented; see [the Generics section](docs.md#generics). See [Current Limitations](docs.md#current-limitations) for the current list. --- ## Getting Started -Building from source just needs a C compiler no LLVM, no Meson, nothing else to install first: +Building from source just needs a C compiler, not LLVM or Meson: ```bash git clone https://github.com/Luma-Programming-Language/Luma.git @@ -199,7 +197,4 @@ cc output/main.c -lm -o main --- -

- Built with ❤️ by the Luma community -

- +Built with ❤️ by the Luma community diff --git a/docs/docs.md b/docs/docs.md index b08440d6..dc0f0742 100644 --- a/docs/docs.md +++ b/docs/docs.md @@ -39,20 +39,21 @@ pub const main -> fn (argc: int, argv: **byte) int { let origin: Point = Point { x: 0, y: 0 }; let destination: Point = Point { x: 3, y: 4 }; let current_status: Status = Status::Active; - + outputln("Distance: ", origin.distance_to(destination)); - + switch (current_status) { Status::Active -> outputln("System is running"); Status::Inactive -> outputln("System is stopped"); Status::Pending -> outputln("System is starting"); } - + return 0; } ``` This example shows: + - Module declaration with `@module` - Struct definitions with methods - Enum definitions @@ -67,8 +68,9 @@ This example shows: Luma provides a straightforward type system with both primitive and compound types. -### Primitive Types -``` +### Primitive Types (Quick Reference) + +```text int - Signed integer (64-bit) uint - Unsigned integer (64-bit) float - Floating point (32-bit) @@ -80,23 +82,27 @@ void - No value (used for function return types and generic pointers) ``` **Note on String Types:** + - String literals like `"hello"` are of type `*byte` (null-terminated character arrays) - All string operations in the standard library use `*byte` - There is no separate `str` type in Luma ### Type Modifiers & Operators -``` + +```text *T - Pointer type (declares a pointer to type T) [T; N] - Array type (fixed-size array of N elements of type T) ``` **Pointer Operators:** -``` + +```text *expr - Dereference operator (access value pointed to) &expr - Address-of operator (get pointer to value) ``` **Example:** + ```luma let x: int = 42; // x is an int let ptr: *int = &x; // ptr is a pointer to int, holds address of x @@ -106,6 +112,7 @@ let value: int = *ptr; // value is 42 (dereferenced ptr) ### Enumerations Enums provide type-safe constants with underlying integer values: + ```luma const Direction -> enum { North, // = 0 @@ -123,6 +130,7 @@ let dir_value: int = cast(Direction::North); // 0 ### Structures Structures group related data with optional access control: + ```luma const Point -> struct { x: int, @@ -148,13 +156,14 @@ priv: ``` ### Using Types + ```luma const origin: Point = Point { x: 0, y: 0 }; -const player: Player = Player { - name: "Alice", +const player: Player = Player { + name: "Alice", score: 100, - internal_id: 12345 + internal_id: 12345 }; // Access fields @@ -349,6 +358,7 @@ pub const main -> fn (argc: int, argv: **byte) int { `destroy` only frees what the struct itself owns (`name`) — the struct's own allocation (`alice` the pointer) is a separate responsibility, freed by whoever called `create`, same as any other `#returns_ownership` pointer. Nothing in Luma calls `destroy` automatically; there's no destructor-on-scope-exit — pair it with `defer` explicitly, as shown. ### Type Compatibility + ```luma // Same types let x: int = 42; @@ -369,8 +379,8 @@ See [Type Casting System](#type-casting-system) for full details on conversions. ## Generics -**Generic functions are implemented.** Generic structs (`struct`) are not -yet — see the note at the end of this section. +**Generic functions and generic structs are implemented.** Generic structs use +type parameters in angle brackets, such as `struct`. ### Generic Functions @@ -432,7 +442,7 @@ effects and falls through to ordinary `<` (less-than). This is also why the type arguments are mandatory rather than optional — inference would remove the very shape (`(`) the parser depends on to disambiguate. -### Monomorphization +### Performance Monomorphization Luma uses **monomorphization**: the compiler generates a separate, concrete function for each distinct set of type arguments actually used, the first @@ -453,8 +463,8 @@ declaration itself. `add`'s `a + b` isn't checked against every possible right at that call site (`+` isn't defined for `*byte`), the same as if you'd written a `*byte`-specific function with `+` in it directly. -**Not yet supported:** generic structs (`struct`, e.g. a `Box` -container) — only generic functions are implemented so far. +Generic structs such as `struct` containers are supported alongside generic +functions. --- @@ -462,7 +472,7 @@ container) — only generic functions are implemented so far. Luma uses the `const` keyword as a **unified declaration mechanism** for all top-level bindings. Whether you're declaring variables, functions, types, or enums, `const` provides a consistent syntax that enforces immutability at the binding level. -### Basic Syntax +### Declaration Examples ```luma const NUM: int = 42; // Immutable variable @@ -473,7 +483,7 @@ const add -> fn (a: int, b: int) int { // Function definition } ``` -(`fn` bindings work today — see [Generics](#generics). `struct` is aspirational syntax, not yet implemented.) +(`fn` and `struct` bindings work today — see [Generics](#generics).) ### Why This Design? @@ -505,20 +515,21 @@ Inside functions, use `let` to declare local variables: const main -> fn (argc: int, argv: **byte) int { let x: int = 10; // Mutable local variable x = 20; // Can be reassigned - + let y: int = 5; y = y + 1; // Can be modified - + let counter: int = 0; loop (counter < 10) { counter = counter + 1; // Mutating in loop } - + return 0; } ``` **Key difference:** + - `const` at top-level = immutable binding (cannot reassign) - `let` in functions = mutable variable (can reassign and modify) @@ -573,10 +584,10 @@ pub const main -> fn (argc: int, argv: **byte) int { const main -> fn (argc: int, argv: **byte) int { let result: int = add(5, 3); outputln("5 + 3 = ", result); - + greet(); print_number(42); - + return 0; } ``` @@ -821,7 +832,7 @@ const WeekDay -> enum { const classify_day -> fn (day: WeekDay) void { switch (day) { - WeekDay::Monday, WeekDay::Tuesday, WeekDay::Wednesday, + WeekDay::Monday, WeekDay::Tuesday, WeekDay::Wednesday, WeekDay::Thursday, WeekDay::Friday -> outputln("Weekday => ", day); WeekDay::Saturday, WeekDay::Sunday -> @@ -916,7 +927,7 @@ pub const main -> fn (argc: int, argv: **byte) int { `@use` only declares the dependency — it doesn't locate the file. Every module you `@use` also has to be passed to the compiler explicitly with `-l`: -``` +```sh luma main.lx -l std/libc.lx std/cstring.lx -name main ``` @@ -924,7 +935,7 @@ luma main.lx -l std/libc.lx std/cstring.lx -name main All standard library modules use the `std_` prefix: -``` +```txt std_math - Mathematical functions and constants std_memory - Low-level memory operations std_cstring - Null-terminated (*byte) string utilities: strlen, strcmp, copy, dup, ... @@ -1112,11 +1123,11 @@ Both functions are **variadic** - they accept any number of arguments of any typ const main -> fn (argc: int, argv: **byte) int { output("Hello", " ", "World"); // Hello World outputln("The answer is:", 42); // The answer is: 42\n - + let x: int = 10; let y: float = 3.14; outputln("x = ", x, ", y = ", y); // x = 10, y = 3.14\n - + return 0; } ``` @@ -1180,11 +1191,11 @@ const main -> fn (argc: int, argv: **byte) int { outputln("int: ", sizeof); // 8 outputln("byte: ", sizeof); // 1 outputln("double: ", sizeof); // 8 - + // Use in allocations let buffer: *int = cast<*int>(alloc(100 * sizeof)); defer free(buffer); - + return 0; } ``` @@ -1208,15 +1219,15 @@ const main -> fn (argc: int, argv: **byte) int { // Integer to float let i: int = 42; let f: float = cast(i); // 42.0 - + // Float to integer (truncates) let pi: double = 3.14159; let rounded: int = cast(pi); // 3 - + // Between integer types let small: byte = cast(65); // 'A' let large: int = cast(small); // 65 - + return 0; } ``` @@ -1230,12 +1241,12 @@ const main -> fn (argc: int, argv: **byte) int { let typed: *int = cast<*int>(raw); *typed = 42; free(raw); - + // Between pointer types let int_ptr: *int = cast<*int>(alloc(sizeof)); let void_ptr: *void = cast<*void>(int_ptr); free(int_ptr); - + return 0; } ``` @@ -1246,16 +1257,16 @@ const main -> fn (argc: int, argv: **byte) int { const main -> fn (argc: int, argv: **byte) int { let ptr: *byte = cast<*byte>(alloc(10)); defer free(ptr); - + // Pointer to integer let addr: int = cast(ptr); - + // Add offset (pointer arithmetic) let offset_addr: int = addr + 5; - + // Back to pointer let offset_ptr: *byte = cast<*byte>(offset_addr); - + return 0; } ``` @@ -1284,16 +1295,16 @@ const PRIMES: [int; 5] = [2, 3, 5, 7, 11]; const main -> fn (argc: int, argv: **byte) int { // Uninitialized (contains garbage) let data: [int; 5]; - + // Initialize with literal let primes: [int; 5] = [2, 3, 5, 7, 11]; - + // Initialize element by element let scores: [int; 3]; scores[0] = 95; scores[1] = 87; scores[2] = 92; - + return 0; } ``` @@ -1303,19 +1314,19 @@ const main -> fn (argc: int, argv: **byte) int { ```luma const main -> fn (argc: int, argv: **byte) int { let numbers: [int; 5] = [10, 20, 30, 40, 50]; - + // Read elements let first: int = numbers[0]; // 10 let last: int = numbers[4]; // 50 - + // Write elements numbers[2] = 99; - + // Loop through array loop [i: int = 0](i < 5) : (++i) { outputln("numbers[", i, "] = ", numbers[i]); } - + return 0; } ``` @@ -1333,7 +1344,7 @@ const main -> fn (argc: int, argv: **byte) int { // String literal - type is *byte let message: *byte = "Hello, World!"; outputln(message); - + return 0; } ``` @@ -1347,7 +1358,7 @@ const main -> fn (argc: int, argv: **byte) int { let letter: byte = 'A'; // Character literal let newline: byte = '\n'; // Escape sequence let tab: byte = '\t'; // Tab character - + return 0; } ``` @@ -1378,19 +1389,19 @@ Luma supports pointer arithmetic for low-level memory manipulation. const main -> fn (argc: int, argv: **byte) int { let arr: *int = cast<*int>(alloc(5 * sizeof)); defer free(arr); - + // Initialize loop [i: int = 0](i < 5) : (++i) { arr[i] = i * 10; } - + // Pointer arithmetic: convert to int, add offset, convert back let addr: int = cast(arr); let new_addr: int = addr + (2 * sizeof); let new_ptr: *int = cast<*int>(new_addr); - + outputln(*new_ptr); // arr[2] = 20 - + return 0; } ``` @@ -1424,7 +1435,7 @@ const Person -> struct { pub: name: *byte, age: int, - + priv: ssn: *byte, internal_id: int @@ -1451,11 +1462,11 @@ sizeof -> int // Size of type const main -> fn (argc: int, argv: **byte) int { // Allocate memory let ptr: *int = cast<*int>(alloc(sizeof)); - + // Use the memory *ptr = 42; outputln("Value: ", *ptr); - + // Clean up free(ptr); return 0; @@ -1470,15 +1481,15 @@ Ensure cleanup with `defer` statements that execute when leaving scope: const process_data -> fn () void { let buffer: *int = cast<*int>(alloc(100 * sizeof)); defer free(buffer); // Guaranteed to run when function exits - + let file: *File = open_file("data.txt"); defer close_file(file); // Will run even if early return - + // Complex processing... if (error_condition) { return; // defer statements still execute } - + // More processing... // defer statements execute here automatically } @@ -1495,6 +1506,7 @@ defer { ``` **Key Benefits:** + - Ensures cleanup code runs regardless of how the function exits - Keeps allocation and deallocation code close together - Prevents resource leaks from early returns @@ -1536,10 +1548,10 @@ const consume_buffer -> fn (buffer: *int) void { const main -> fn (argc: int, argv: **byte) int { let data: *int = cast<*int>(alloc(sizeof)); *data = 42; - + consume_buffer(data); // Ownership transferred // Note: do not use `data` after this point - + return 0; } ``` @@ -1551,6 +1563,7 @@ Luma's compiler includes a static analyzer that tracks memory at compile time to #### What the Analyzer Tracks **Verified at Compile Time:** + - **Memory Leaks**: Detects `alloc()` calls without corresponding `free()` - **Double-Free**: Prevents freeing the same pointer twice - **Use-After-Free**: Catches access to freed memory within the same function @@ -1617,12 +1630,13 @@ const create_arena -> fn () Arena { The analyzer currently has limitations in these areas: 1. **Struct Field Granularity**: When tracking `a.buf = alloc(...)`, the analyzer tracks the entire struct `a`, not the specific field `a.buf`. This works for single-pointer structs but may cause issues with: + ```luma const Container -> struct { data1: *int, data2: *int }; - + let c: Container; c.data1 = alloc(10); // Tracked as "c" c.data2 = alloc(20); // Also tracked as "c" - potential confusion @@ -1630,6 +1644,7 @@ The analyzer currently has limitations in these areas: ``` 2. **Conditional Allocations**: The analyzer may report false positives for conditional paths: + ```luma let ptr: *int; if (condition) { @@ -1639,6 +1654,7 @@ The analyzer currently has limitations in these areas: ``` 3. **Allocations in Loops**: Each loop iteration's allocations should be independent, but edge cases may exist: + ```luma loop [i: int = 0](i < 10) : (++i) { let temp: *int = alloc(4); @@ -1648,11 +1664,12 @@ The analyzer currently has limitations in these areas: ``` 4. **Early Returns with Defer**: While generally working, complex control flow with multiple early returns may need testing: + ```luma const process -> fn () int { let a: *int = alloc(sizeof); defer free(a); - + if (error) { return -1; } // Defer should fire if (warning) { return 0; } // Defer should fire return 1; // Defer should fire @@ -1660,6 +1677,7 @@ The analyzer currently has limitations in these areas: ``` 5. **Stack vs Heap**: The analyzer doesn't currently detect returning pointers to stack variables: + ```luma const dangerous -> fn () *int { let local: int = 42; @@ -1668,6 +1686,7 @@ The analyzer currently has limitations in these areas: ``` 6. **Arrays of Pointers**: Complex allocation patterns may not be fully tracked: + ```luma let arr: [*int; 5]; loop [i: int = 0](i < 5) : (++i) { @@ -1696,6 +1715,7 @@ Understanding performance is crucial for systems programming. Luma follows the "zero-cost abstraction" principle: abstractions should have no runtime overhead. **Generics are zero-cost** (see [Generics](#generics)): + ```luma const add -> fn (a: T, b: T) T { return a + b; @@ -1724,17 +1744,20 @@ const max -> fn (a: T, b: T) T { ``` **Benefits:** + - No runtime overhead - Full optimization per type - No vtables or dynamic dispatch **Trade-offs:** + - Larger binary size (one copy per type) - Longer compilation time ### Memory Layout **Struct layout is predictable:** + ```luma const Point -> struct { x: int, // Offset 0, 8 bytes @@ -1743,6 +1766,7 @@ const Point -> struct { ``` **Array layout is contiguous:** + ```luma let arr: [int; 10]; // 80 contiguous bytes // arr[0] at offset 0, arr[1] at offset 8, arr[2] at offset 16... @@ -1751,6 +1775,7 @@ let arr: [int; 10]; // 80 contiguous bytes ### Memory Allocation Performance **Stack allocation is fast:** + ```luma const fast_function -> fn () void { let buffer: [int; 1024]; // Stack allocated - instant @@ -1759,6 +1784,7 @@ const fast_function -> fn () void { ``` **Heap allocation has overhead:** + ```luma const slower_function -> fn () void { let buffer: *int = cast<*int>(alloc(1024 * sizeof)); @@ -1770,11 +1796,13 @@ const slower_function -> fn () void { ### Optimization Guidelines **1. Prefer stack allocation when possible:** + ```luma let temp: [int; 100]; // Good for small, fixed-size data ``` **2. Minimize pointer indirection:** + ```luma // Better: direct access let ptr: *int; @@ -1785,6 +1813,7 @@ let value2: int = 42; // No memory load ``` **3. Batch operations:** + ```luma // Good: one large allocation let buffer: *int = cast<*int>(alloc(1000 * sizeof)); @@ -1795,6 +1824,7 @@ free(buffer); ``` **4. Avoid unnecessary copying:** + ```luma // Bad: pass large struct by value const process -> fn (data: LargeStruct) void { } @@ -1806,7 +1836,7 @@ const process_fast -> fn (data: *LargeStruct) void { } ### Performance Summary | Operation | Cost | Notes | -|-----------|------|-------| +| ----------- | ------ | ------- | | Stack variable | ~0 | Instant | | Heap allocation | High | System call | | Pointer dereference | Low | One memory access | @@ -1860,7 +1890,7 @@ Luma provides several safety features to prevent common bugs: ### Keywords -``` +```txt const let if elif else loop break continue return defer struct enum pub priv cast @@ -1870,7 +1900,7 @@ using static input system as ### Directives -``` +```luma @module "name" // Declare module name @use "name" as alias // Import module @os { "linux" -> { } } // Platform-conditional code @@ -1879,7 +1909,7 @@ using static input system as ### Attributes -``` +```luma #returns_ownership // Function returns allocated memory (caller must free) #takes_ownership // Function takes ownership of a pointer argument #lib_import("lib.so") // Per-function library override (POSIX) @@ -1888,7 +1918,7 @@ using static input system as ### Operators -``` +```txt Arithmetic: + - * / % ++ -- Comparison: == != < > <= >= Logical: && || ! @@ -1899,7 +1929,7 @@ Access: . :: [] * & ### Primitive Types -``` +```txt int double bool *T [T; N] uint float byte void ``` diff --git a/docs/ideas.md b/docs/ideas.md index 40b2ca02..0bea107c 100644 --- a/docs/ideas.md +++ b/docs/ideas.md @@ -34,7 +34,7 @@ LLVM ERROR: Broken module found, compilation aborted! ## Linked List Ideas ```luma -;; Syntax will change on somethins +;; Syntax will change on something const Link = struct { tag: int, value = union { @@ -102,10 +102,10 @@ extern "C" { pub const main = fn () int { io.printf("Hello from C!\n"); - + let file: *io.FILE = io.fopen("test.txt", "w"); defer io.fclose(file); - + io.fprintf(file, "Writing from Luma!\n"); return 0; } @@ -123,7 +123,7 @@ impl [func list...] -> [struct list...] { } } -## The goals of the impl is to implement functions for structs +## The goal of impl is to implement functions for structs ## It should have the ability to conditionally make functions @@ -139,27 +139,27 @@ impl [func list...] -> [struct list...] { ## a #compile tag from a function within a conditional will be optionally compiled, and so only one available at runtime -## Why the two? One use case for @run_time is to allow dynamic function assignment. Lets say you must work with an api +## Why the two? One use case for @run_time is to allow dynamic function assignment. Let's say you must work with an API ## This api does not respond with the same data, same type of data and so on. This means you can write multiple capture() functions -## Yes this is function overloading, but conditionally, and can be programmed for the potential context the appliction will be in +## Yes this is function overloading, but conditionally, and can be programmed for the potential context the application will be in ## For @compile_time, it optionally compiles one of the implementations of the function. Say you need portability, you can use the same -## source code and target specific architectures. This can be thought of #IF_WINDOWS bullshit from C, you can conditionaly compile +## source code and target specific architectures. This can be thought of #IF_WINDOWS bullshit from C, you can conditionally compile -## one function or another, but in a nice and effecient way +## one function or another, but in a nice and efficient way ## the ? and None type someType: ?; # is a None, or a real type. -## Thats what it does. Gives a way to init without a type. Can also be used to identify things. if (someType? == None) {return "Nothing found";} +## That's what it does. It provides a way to initialize without a type. It can also be used to identify things. if (someType? == None) {return "Nothing found";} ## It is our solution to NULL, but it provides a more meaning. Because it can also be a value if (someType?) {return "value found";} -## This is very straigh forward in its concept. someType? returns the value inside, or it returns the None type +## This is very straightforward in its concept. someType? returns the value inside, or it returns the None type ## the set type @@ -173,7 +173,7 @@ someType: ?; # is a None, or a real type. ## a, b, c : () = func1 -## changes the the position for the returning set +## changes the position for the returning set ## a, b, c : (float, int, int) = func1 @@ -187,7 +187,7 @@ someType: ?; # is a None, or a real type. // -OB : object files cc_o = luma-1 -OB -V2; -// -x86_64 could be used to verify the executable and compatability for translate() +// -x86_64 could be used to verify the executable and compatibility for translate() cc = luma-1 -O3 -x86_64; // the output for artefacts and exe @@ -227,13 +227,13 @@ clean_all -> (where) { output("Removed all artefacts\n"); } -## compile: and clean: are labels, there are the external commands a user can run (lpbs compile, lpbs clean) +## compile: and clean: are labels; they are the external commands a user can run (lpbs compile, lpbs clean) -## Lets break it down. Post-Processing is about understanding end context from the src code and a solution +## Let's break it down. Post-processing is about understanding the end context from the source code and finding a solution ## The LPB System should generate bindings for the end result -## It should manage ffi for C and providing that compatability +## It should manage FFI for C and provide that compatibility ## It should provide a way to create, manage, and work with shared libraries or static libraries @@ -280,7 +280,7 @@ pub const main = fn () int { ## This creates: ## std/stdio.lx -## std/stdlib.lx +## std/stdlib.lx ## std/math.lx ## std/string.lx ## etc. diff --git a/docs/releases/v0.3.4.md b/docs/releases/v0.3.4.md index 3d2ec4da..aba2220a 100644 --- a/docs/releases/v0.3.4.md +++ b/docs/releases/v0.3.4.md @@ -6,7 +6,7 @@ Since v0.2.0 we've rewritten the compiler from scratch, in Luma itself. This rel --- -# Self-Hosting +## Self-Hosting The old compiler was C on top of LLVM. The new one is Luma, transpiling to C, which `cc` (or a cross toolchain) turns into a native binary. `luma` compiles `luma`. @@ -30,9 +30,9 @@ Tagging a release now produces verified builds for Linux, Windows, and macOS aut --- -# What's New +## What's New -## Scoped `switch using` +### Scoped `switch using` Bare case labels instead of repeating an enum's namespace on every arm: @@ -48,7 +48,7 @@ switch using Color (c) { You can still write a label fully qualified if you want — the shorthand is optional, and exhaustiveness checking works exactly the same either way. -## Struct Embedding +### Struct Embedding Structs can embed another struct now, which promotes its fields and methods onto the containing struct: @@ -99,7 +99,7 @@ move -> fn (dx: float, dy: float) void { }, ``` -## Static Struct Methods +### Static Struct Methods Methods that don't get an implicit `self`, called on the type instead of an instance: @@ -124,7 +124,7 @@ let b: Point = Point::at(3.0, 4.0); --- -# Cross-Platform +## Cross-Platform `-t`/`--target-os` actually cross-compiles now. It used to just pick which `@os {}` branches got emitted, while the build step still shelled out to whatever `cc` was on the host — so asking for `windows64` on Linux got you a Linux binary with the wrong platform code baked into it. Not great. The build step now routes non-native targets through [`zig cc`](https://ziglang.org), which brings its own minimal libc and headers per platform, so you get a real Windows or macOS binary from one Linux machine without setting up mingw or osxcross. @@ -137,7 +137,7 @@ smoke-test-macos: success --- -# Hardening +## Hardening Pushing cross-compilation all the way through surfaced four real bugs, none of which actually had anything to do with cross-compiling. They were just sitting in the code paths nothing had exercised hard before: @@ -150,7 +150,7 @@ All four are fixed now, with regression tests. Kind of embarrassing to have ship --- -# Static Analyzer +## Static Analyzer Fixed three classes of false-positive leak reports: @@ -162,7 +162,7 @@ While chasing those down we also found two real use-after-free/double-free bugs --- -# Developer Experience +## Developer Experience - New `-debug` flag. - `bootstrap/luma-seed` is now a committed, working compiler binary, so a fresh checkout can build itself without needing Luma installed some other way first. @@ -170,7 +170,7 @@ While chasing those down we also found two real use-after-free/double-free bugs --- -# Installation +## Installation Building from source no longer needs LLVM or Meson, just `cc`: @@ -185,13 +185,13 @@ sudo ./scripts/install.sh Pre-built binaries: -* Linux x86_64 -* Windows x86_64 -* macOS x86_64 +- Linux x86_64 +- Windows x86_64 +- macOS x86_64 --- -# Breaking Changes +## Breaking Changes ⚠️ The compiler's internals were rewritten from scratch. Existing programs should behave the same as they did under v0.2.0, but this is a new implementation, not a port of the old one; if something regressed for you, please open an issue. @@ -199,32 +199,32 @@ Pre-built binaries: --- -# Known Limitations +## Known Limitations -### Cross-Platform +### Cross-Platform Limitations -* Windows and macOS builds are cross-compiled from Linux and verified by actually running on real runners, but the compiler hasn't bootstrapped natively on either OS yet (compiling itself, on that OS, with that OS's own toolchain). That's still on the list. -* zig's macOS target is libc-level only, no Cocoa/AppKit or other framework linking. Doesn't affect the compiler itself, but matters if you're linking Luma code against native macOS frameworks. +- Windows and macOS builds are cross-compiled from Linux and verified by actually running on real runners, but the compiler hasn't bootstrapped natively on either OS yet (compiling itself, on that OS, with that OS's own toolchain). That's still on the list. +- zig's macOS target is libc-level only, no Cocoa/AppKit or other framework linking. Doesn't affect the compiler itself, but matters if you're linking Luma code against native macOS frameworks. -### Static Analyzer +### Static Analyzer Limitations -* Struct methods declared in a `priv:` block aren't typechecked yet. -* Conditional allocation paths can still produce false positives. +- Struct methods declared in a `priv:` block aren't typechecked yet. +- Conditional allocation paths can still produce false positives. ### Language -* A multi-variable C-style `for` loop initializer (`for (long long i = 0, long long j = 0; ...)`) emits invalid C. -* No generics yet. +- A multi-variable C-style `for` loop initializer (`for (long long i = 0, long long j = 0; ...)`) emits invalid C. +- No generics yet. --- -# Community +## Community Issues: -https://github.com/Luma-Programming-Language/Luma/issues +[GitHub Issues](https://github.com/Luma-Programming-Language/Luma/issues) Discord: -https://discord.gg/gqnwasvqd9 +[Discord](https://discord.gg/gqnwasvqd9) --- diff --git a/docs/releases/v0.3.5.md b/docs/releases/v0.3.5.md index 38b04ba3..937208df 100644 --- a/docs/releases/v0.3.5.md +++ b/docs/releases/v0.3.5.md @@ -2,21 +2,21 @@ --- -# No More Zig +## No More Zig -v0.3.4's Windows and macOS cross-compilation went through `zig cc`. It was the easiest way to get both platforms working from one Linux machine, but it meant depending on an entire separate language and toolchain just to build Luma. Which does not feel right. +v0.3.4's Windows and macOS cross-compilation went through `zig cc`. It was the easiest way to get both platforms working from one Linux machine, but it meant depending on an entire separate language and toolchain just to build Luma, which did not feel right. -**Windows** now cross-compiles with [mingw-w64](https://www.mingw-w64.org) instead. a real GNU C cross-compiler. `-t windows64` works exactly like it did before; it just shells out to `x86_64-w64-mingw32-gcc` now instead of `zig cc`. +**Windows** now cross-compiles with [mingw-w64](https://www.mingw-w64.org) instead: a real GNU C cross-compiler. `-t windows64` works exactly like it did before; it just shells out to `x86_64-w64-mingw32-gcc` now instead of `zig cc`. -**macOS** doesn't have an equivalent unfortunately the only real cross-compiler for it is `osxcross`, and that needs Apple's actual SDK extracted out of a Xcode install, which isn't something we want this project depending on either. So macOS releases are built differently now: `luma` transpiles the compiler to C on Linux (with the new `-c`/`--no-compile` flag), and a `macos-latest` CI runner compiles that C with its own preinstalled `clang`. +**macOS** doesn't have an equivalent. Unfortunately, the only real cross-compiler for it is `osxcross`, and that needs Apple's actual SDK extracted from an Xcode installation, which isn't something we want this project depending on either. So macOS releases are built differently now: `luma` transpiles the compiler to C on Linux (with the new `-c`/`--no-compile` flag), and a `macos-latest` CI runner compiles that C with its own preinstalled `clang`. If you want to do the same thing yourself: `scripts/cross-build.sh` is Windows-only now, and there's a new `scripts/transpile.sh` for the "emit C, compile it natively somewhere else" path. --- -# `-c` / `--no-compile` +## `-c` / `--no-compile` -New flag! It stops the compiler right after writing the transpiled C, before it ever shells out to a C compiler: +The new flag stops the compiler right after writing the transpiled C, before it ever shells out to a C compiler: ```bash luma main.lx -c -name main # writes output/main.c, nothing else @@ -26,11 +26,11 @@ Implies `-save`, since there's no point in emitting C you don't keep. --- -# Fixed: native Windows builds looking for a cross-compiler +## Fixed: native Windows builds looking for a cross-compiler When building a Windows binary with `-t windows64` and then actually running on a Windows machine: -``` +```powershell PS> luma.exe .\main.lx -name main 'zig' is not recognized as an internal or external command... ``` @@ -41,7 +41,7 @@ Fixed by tracking whether `-t`/`--target-os` was actually passed on the current --- -# Fixed: non-root installs couldn't find their own standard library +## Fixed: non-root installs couldn't find their own standard library `install.sh`/`install.bat` have always offered a non-root/non-admin install mode that writes to `~/.luma/` (or `%USERPROFILE%\.luma\` on Windows). The compiler's path resolution never actually knew that location existed. It only checked the exact path you gave it, then the system-wide install path. A non-root install therefore produced a `luma` that couldn't find its own standard library unless you happened to be running it from a directory with a local `std/`. @@ -49,7 +49,7 @@ Added the missing tier. Path resolution is now, in order: the given path, then ` --- -# Installation +## Installation ```bash git clone https://github.com/Luma-Programming-Language/Luma.git @@ -62,19 +62,19 @@ sudo ./scripts/install.sh Pre-built binaries: -* Linux x86_64 -* Windows x86_64 (cross-compiled with mingw-w64, smoke-tested on a real Windows runner) -* macOS x86_64 (built natively on macOS in CI) +- Linux x86_64 +- Windows x86_64 (cross-compiled with mingw-w64, smoke-tested on a real Windows runner) +- macOS x86_64 (built natively on macOS in CI) --- -# Community +## Community Issues: -https://github.com/Luma-Programming-Language/Luma/issues +[GitHub Issues](https://github.com/Luma-Programming-Language/Luma/issues) Discord: -https://discord.gg/gqnwasvqd9 +[Discord](https://discord.gg/gqnwasvqd9) --- diff --git a/docs/todo.md b/docs/todo.md index 81ff61e2..6cc3e11c 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -71,7 +71,7 @@ These AST node types are fully implemented in code generation: - [ ] Consider ownership transfer semantics - [ ] Allowing structs to point to itself -- name struct {some: *name}; -#### Control Flow Analysis +#### Control Flow Analysis - [ ] **Conditional path tracking** - [ ] Detect leaks in conditional branches (`if/else` without free in all paths) @@ -127,39 +127,39 @@ These AST node types are fully implemented in code generation: ### Parsing -- [ ] Add parsing for templates (`fn[T]`, `struct[T]`) -- [ ] Add parsing for type aliases using `type` keyword -- [ ] Add parsing for modules and imports refinements +- [x] Add parsing for generics (`fn`, `struct`) +- [ ] Add parsing for type aliases using `type` keyword +- [ ] Add parsing for modules and imports refinements - [ ] Design and implement **union syntax** - [ ] Consider Go/Odin-style loop syntax improvements ### Semantic Analysis -- [ ] Type inference for generics -- [ ] Detect unused imports and symbols +- [ ] Type inference for generics +- [ ] Detect unused imports and symbols ### Codegen -- [ ] Implement codegen for `switch` or `match` constructs -- [ ] Support more LLVM optimizations -- [ ] **Add structs and enums support** in codegen +- [x] Implement codegen for `switch` constructs +- [ ] Support more C codegen optimizations +- [x] **Add structs and enums support** in codegen - [ ] **Add unions support** in codegen -- [ ] **Add in memcpy and streq** streq === strcmp +- [ ] Add `memcpy` and `streq` support (`streq` is equivalent to `strcmp`) ### Lexer & Parser -- [ ] Add tokens and grammar for unions +- [ ] Add tokens and grammar for unions ### Type Checker -- [ ] Implement type checking for structs +- [x] Implement type checking for structs - [ ] Implement type checking for unions --- ## 🚀 Future Features Ideas (Maybe) -- [ ] Investigate pattern matching +- [ ] Investigate pattern matching - [ ] Build minimal standard library - [ ] Consider ownership/borrowing system for advanced memory safety - [ ] Explore compile-time memory layout optimization