Build a complete static site generator in Lux that faithfully clones blu.cx (elmstatic). Generates 14 post pages, section indexes, tag pages, and a home page with snippets grid from markdown content. ISSUES.md documents 15 Lux language limitations found during the project. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
285 lines
7.7 KiB
Markdown
285 lines
7.7 KiB
Markdown
# 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**:
|
|
```lux
|
|
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:
|
|
```lux
|
|
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**:
|
|
```lux
|
|
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**:
|
|
```lux
|
|
let pair = ("hello", 42);
|
|
let x = pair.0; // ERROR: Expected identifier
|
|
```
|
|
|
|
**Workaround**: Use ADTs (algebraic data types) with accessor functions:
|
|
```lux
|
|
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**:
|
|
```lux
|
|
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:
|
|
```lux
|
|
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**:
|
|
```lux
|
|
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:
|
|
```lux
|
|
fn addOne(x: Int): Int = x + 1
|
|
List.map(items, addOne)
|
|
```
|
|
|
|
---
|
|
|
|
## Issue 7: Effectful callbacks in `List.map`/`forEach`/`fold`
|
|
|
|
**Category**: Type checker limitation
|
|
**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).
|
|
|
|
**Reproduction**:
|
|
```lux
|
|
fn readFile(path: String): String with {File} = File.read(path)
|
|
let contents = List.map(paths, fn(p: String): String => readFile(p));
|
|
// ERROR: Effect mismatch: expected {File}, got {}
|
|
```
|
|
|
|
**Workaround**: Use manual recursion instead of List.map/forEach:
|
|
```lux
|
|
fn mapRead(paths: List<String>): List<String> with {File} =
|
|
match List.head(paths) {
|
|
None => [],
|
|
Some(p) => {
|
|
let content = readFile(p);
|
|
match List.tail(paths) {
|
|
Some(rest) => List.concat([content], mapRead(rest)),
|
|
None => [content]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Issue 8: `String.indexOf` and `String.lastIndexOf` missing from type checker
|
|
|
|
**Category**: Type checker gap → **Fixed**
|
|
**Severity**: Medium
|
|
|
|
These functions exist in the interpreter but were not registered in the type checker, causing "Module 'String' has no member" errors.
|
|
|
|
**Fix**: Added type registrations in src/types.rs:
|
|
- `String.indexOf(String, String) -> Option<Int>`
|
|
- `String.lastIndexOf(String, String) -> Option<Int>`
|
|
|
|
---
|
|
|
|
## 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`:
|
|
```lux
|
|
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(_)`:
|
|
```lux
|
|
let json = match Json.parse(raw) { Ok(j) => j, Err(_) => ... };
|
|
```
|
|
|
|
---
|
|
|
|
## Issue 14: No `File.copy`
|
|
|
|
**Category**: Missing feature
|
|
**Severity**: Low
|
|
|
|
Must shell out to copy files/directories.
|
|
|
|
**Workaround**: `Process.exec("cp -r static/* _site/")`.
|
|
|
|
---
|
|
|
|
## 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 | Open |
|
|
| 8 | String.indexOf/lastIndexOf types | 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 | Open |
|
|
| 15 | No file globbing | Low | Open |
|