Files
blu-lux/ISSUES.md
2026-02-19 21:11:44 -05:00

7.9 KiB

Lux Language Issues — blu-site SSG Project

Issues discovered while building a static site generator in Lux. Each entry includes a reproduction case and the workaround used.


Issue 1: Html.render() / Html.document() not available in interpreter

Category: Missing feature Severity: High

The Html module only works in the JS backend, not the interpreter. All HTML must be generated via string concatenation.

Workaround: Build all HTML as concatenated strings.


Issue 2: String interpolation {} has no escape mechanism

Category: Missing feature → Fixed (added \{ and \} escape sequences) Severity: High

Any { inside a string literal triggers interpolation mode, and there was no way to include literal braces. This breaks when generating JavaScript like function() { ... } or JSON like {}.

Reproduction:

let s = "function() { return 1; }"  // Parse error: treats { } as interpolation

Fix: Added \{ and \} as escape sequences in the lexer (src/lexer.rs). Also fixed the formatter (src/formatter.rs) to re-escape { and } in string literals so that lux fmt doesn't break escaped braces. Now:

let s = "function() \{ return 1; \}"  // Works, produces literal braces

Issue 3: Module-qualified constructors not supported in pattern matching

Category: Parser limitation Severity: High

Cannot use module.Constructor in match patterns. The parser expects => but finds ..

Reproduction:

import frontmatter
let result = frontmatter.parse(content);
match result {
    frontmatter.ParseResult(front, body) => ...  // ERROR: Expected =>, found .
}

Workaround: Consolidated everything into a single file. Alternatively, use import foo.* (wildcard imports), though this can cause name conflicts.


Issue 4: Tuple field access (.0, .1) not supported

Category: Missing feature Severity: High

The parser's field access only works on Records (with named fields), not tuples. The . operator expects an identifier.

Reproduction:

let pair = ("hello", 42);
let x = pair.0;  // ERROR: Expected identifier

Workaround: Use ADTs (algebraic data types) with accessor functions:

type Pair = | Pair(String, Int)
fn first(p: Pair): String = match p { Pair(a, _) => a }

Issue 5: Multi-line function arguments cause parse errors

Category: Parser limitation Severity: High

Function call arguments cannot span multiple lines. The parser sees \n where it expects , or ).

Reproduction:

let result = someFunction(
    arg1,
    arg2
);  // ERROR: Expected ,, found \n

Workaround: Keep all function arguments on a single line, or use let bindings for intermediate values:

let a = arg1;
let b = arg2;
let result = someFunction(a, b);

Issue 6: Multi-line lambdas in function call arguments fail

Category: Parser limitation Severity: High

Lambda bodies that span multiple lines inside function call arguments cause parse errors.

Reproduction:

List.map(items, fn(x: Int): Int =>
    x + 1  // ERROR: Expected ,, found \n
);

Workaround: Either keep lambdas on one line or extract to named functions:

fn addOne(x: Int): Int = x + 1
List.map(items, addOne)

Issue 7: Effectful callbacks in List.map/forEach/fold

Category: Type checker limitation → Fixed Severity: Medium

The type checker requires pure callbacks for higher-order List functions, but effectful callbacks are often needed (e.g., reading files in a map operation).

Fix: Added effect propagation in src/typechecker.rs: callback arguments with effect annotations now propagate their effects to the enclosing function's inferred effect set, in infer_call, infer_effect_op (module access path), and infer_effect_op (effect op path).


Issue 8: String.indexOf and String.lastIndexOf broken in C backend

Category: C backend bug → Fixed Severity: Medium

Type registrations were added previously, but C compilation of code using String.indexOf/lastIndexOf failed with type errors. Three root causes:

  1. Global let bindings always declared as static LuxInt regardless of value type
  2. Option<Int> inner type not tracked through function parameters, causing match extraction to default to LuxString
  3. indexOf/lastIndexOf stored ints as (void*)(intptr_t) but extraction expected boxed pointers (inconsistent with parseInt)

Fix: Fixed in src/codegen/c_backend.rs:

  • emit_global_let now infers type from value expression
  • Added var_option_inner_types map; function params with Option<T> annotations are tracked
  • indexOf/lastIndexOf now use lux_box_int consistently; extraction dereferences via *(LuxInt*)

Issue 9: No List.sort / List.sortBy

Category: Missing feature Severity: Medium

No built-in sorting for lists. Must implement manually.

Workaround: Insertion sort via List.fold:

fn sortByDateDesc(items: List<Page>): List<Page> =
    List.fold(items, [], sortInsert)

fn insertByDate(sorted: List<Page>, item: Page): List<Page> = {
    match List.head(sorted) {
        None => [item],
        Some(first) =>
            if pgDate(item) >= pgDate(first) then
                List.concat([item], sorted)
            else
                match List.tail(sorted) {
                    Some(rest) => List.concat([first], insertByDate(rest, item)),
                    None => [first, item]
                }
    }
}

Issue 10: No HashMap/Map type

Category: Missing feature Severity: Medium

No associative data structure available. Tag grouping required manual list scanning.

Workaround: Use List<(key, value)> with List.filter/List.find for lookups. O(n) per lookup.


Issue 11: No regex support

Category: Missing feature Severity: Medium

Inline markdown parsing (bold, italic, links, code) required manual character-by-character scanning with index tracking.

Workaround: Recursive processInlineFrom(text, i, len, acc) function scanning character by character.


Issue 12: No multiline string literals

Category: Missing feature Severity: Medium

HTML templates require very long single-line strings with many + concatenations and \" escapes.

Workaround: Concatenate multiple single-line strings with +.


Issue 13: Json.parse returns Result<Json, String>

Category: Documentation gap Severity: Low

Not immediately obvious that Json.parse wraps the result in Ok/Err. Error message "Json.get expects Json, got Constructor" is confusing.

Workaround: Pattern match on Ok(j) / Err(_):

let json = match Json.parse(raw) { Ok(j) => j, Err(_) => ... };

Issue 14: No File.copy

Category: Missing feature → Fixed Severity: Low

Fix: Added File.copy(source, dest) to types.rs, interpreter.rs (using std::fs::copy), and C backend (fread/fwrite buffer copy).


Issue 15: No file globbing

Category: Missing feature Severity: Low

Must manually scan directories and filter by extension.

Workaround: File.readDir(dir) + List.filter(entries, fn(e) => String.endsWith(e, ".md")).


Summary

# Issue Severity Status
1 Html module interpreter-only High Open
2 No \{ \} string escapes High Fixed
3 Module-qualified pattern matching High Open
4 Tuple field access .0 .1 High Open
5 Multi-line function arguments High Open
6 Multi-line lambdas in calls High Open
7 Effectful callbacks in List HOFs Medium Fixed
8 String.indexOf/lastIndexOf C backend Medium Fixed
9 No List.sort Medium Open
10 No HashMap/Map Medium Open
11 No regex Medium Open
12 No multiline strings Medium Open
13 Json.parse return type docs Low Open
14 No File.copy Low Fixed
15 No file globbing Low Open