fix: parser newline handling in lists and stdlib exports

- Fix parse_list_expr to skip newlines between list elements
- Add `pub` keyword to all exported functions in stdlib/html.lux
- Change List.foldl to List.fold (matching built-in name)
- Update weaknesses document with fixed issues

The module import system now works correctly. This enables:
- import stdlib/html to work as expected
- html.div(), html.render() etc. to be accessible
- Multi-line list expressions in Lux source files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-16 06:51:37 -05:00
parent 552e7a4972
commit 4b553031fd
3 changed files with 138 additions and 115 deletions

View File

@@ -2266,12 +2266,15 @@ impl Parser {
fn parse_list_expr(&mut self) -> Result<Expr, ParseError> { fn parse_list_expr(&mut self) -> Result<Expr, ParseError> {
let start = self.current_span(); let start = self.current_span();
self.expect(TokenKind::LBracket)?; self.expect(TokenKind::LBracket)?;
self.skip_newlines();
let mut elements = Vec::new(); let mut elements = Vec::new();
while !self.check(TokenKind::RBracket) { while !self.check(TokenKind::RBracket) {
elements.push(self.parse_expr()?); elements.push(self.parse_expr()?);
self.skip_newlines();
if !self.check(TokenKind::RBracket) { if !self.check(TokenKind::RBracket) {
self.expect(TokenKind::Comma)?; self.expect(TokenKind::Comma)?;
self.skip_newlines();
} }
} }

View File

@@ -11,13 +11,13 @@
// Html type represents a DOM structure // Html type represents a DOM structure
// Parameterized by Msg - the type of messages emitted by event handlers // Parameterized by Msg - the type of messages emitted by event handlers
type Html<M> = pub type Html<M> =
| Element(String, List<Attr<M>>, List<Html<M>>) | Element(String, List<Attr<M>>, List<Html<M>>)
| Text(String) | Text(String)
| Empty | Empty
// Attributes that can be applied to elements // Attributes that can be applied to elements
type Attr<M> = pub type Attr<M> =
| Class(String) | Class(String)
| Id(String) | Id(String)
| Style(String, String) | Style(String, String)
@@ -46,243 +46,243 @@ type Attr<M> =
// Element builders - Container elements // Element builders - Container elements
// ============================================================================ // ============================================================================
fn div<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn div<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("div", attrs, children) Element("div", attrs, children)
fn span<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn span<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("span", attrs, children) Element("span", attrs, children)
fn section<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn section<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("section", attrs, children) Element("section", attrs, children)
fn article<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn article<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("article", attrs, children) Element("article", attrs, children)
fn header<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn header<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("header", attrs, children) Element("header", attrs, children)
fn footer<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn footer<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("footer", attrs, children) Element("footer", attrs, children)
fn nav<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn nav<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("nav", attrs, children) Element("nav", attrs, children)
fn main<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn main<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("main", attrs, children) Element("main", attrs, children)
fn aside<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn aside<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("aside", attrs, children) Element("aside", attrs, children)
// ============================================================================ // ============================================================================
// Element builders - Text elements // Element builders - Text elements
// ============================================================================ // ============================================================================
fn h1<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn h1<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("h1", attrs, children) Element("h1", attrs, children)
fn h2<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn h2<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("h2", attrs, children) Element("h2", attrs, children)
fn h3<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn h3<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("h3", attrs, children) Element("h3", attrs, children)
fn h4<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn h4<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("h4", attrs, children) Element("h4", attrs, children)
fn h5<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn h5<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("h5", attrs, children) Element("h5", attrs, children)
fn h6<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn h6<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("h6", attrs, children) Element("h6", attrs, children)
fn p<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn p<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("p", attrs, children) Element("p", attrs, children)
fn pre<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn pre<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("pre", attrs, children) Element("pre", attrs, children)
fn code<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn code<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("code", attrs, children) Element("code", attrs, children)
fn blockquote<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn blockquote<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("blockquote", attrs, children) Element("blockquote", attrs, children)
// ============================================================================ // ============================================================================
// Element builders - Inline elements // Element builders - Inline elements
// ============================================================================ // ============================================================================
fn a<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn a<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("a", attrs, children) Element("a", attrs, children)
fn strong<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn strong<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("strong", attrs, children) Element("strong", attrs, children)
fn em<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn em<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("em", attrs, children) Element("em", attrs, children)
fn small<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn small<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("small", attrs, children) Element("small", attrs, children)
fn br<M>(): Html<M> = pub fn br<M>(): Html<M> =
Element("br", [], []) Element("br", [], [])
fn hr<M>(): Html<M> = pub fn hr<M>(): Html<M> =
Element("hr", [], []) Element("hr", [], [])
// ============================================================================ // ============================================================================
// Element builders - Lists // Element builders - Lists
// ============================================================================ // ============================================================================
fn ul<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn ul<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("ul", attrs, children) Element("ul", attrs, children)
fn ol<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn ol<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("ol", attrs, children) Element("ol", attrs, children)
fn li<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn li<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("li", attrs, children) Element("li", attrs, children)
// ============================================================================ // ============================================================================
// Element builders - Forms // Element builders - Forms
// ============================================================================ // ============================================================================
fn form<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn form<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("form", attrs, children) Element("form", attrs, children)
fn input<M>(attrs: List<Attr<M>>): Html<M> = pub fn input<M>(attrs: List<Attr<M>>): Html<M> =
Element("input", attrs, []) Element("input", attrs, [])
fn textarea<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn textarea<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("textarea", attrs, children) Element("textarea", attrs, children)
fn button<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn button<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("button", attrs, children) Element("button", attrs, children)
fn label<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn label<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("label", attrs, children) Element("label", attrs, children)
fn select<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn select<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("select", attrs, children) Element("select", attrs, children)
fn option<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn option<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("option", attrs, children) Element("option", attrs, children)
// ============================================================================ // ============================================================================
// Element builders - Media // Element builders - Media
// ============================================================================ // ============================================================================
fn img<M>(attrs: List<Attr<M>>): Html<M> = pub fn img<M>(attrs: List<Attr<M>>): Html<M> =
Element("img", attrs, []) Element("img", attrs, [])
fn video<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn video<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("video", attrs, children) Element("video", attrs, children)
fn audio<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn audio<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("audio", attrs, children) Element("audio", attrs, children)
// ============================================================================ // ============================================================================
// Element builders - Tables // Element builders - Tables
// ============================================================================ // ============================================================================
fn table<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn table<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("table", attrs, children) Element("table", attrs, children)
fn thead<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn thead<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("thead", attrs, children) Element("thead", attrs, children)
fn tbody<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn tbody<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("tbody", attrs, children) Element("tbody", attrs, children)
fn tr<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn tr<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("tr", attrs, children) Element("tr", attrs, children)
fn th<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn th<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("th", attrs, children) Element("th", attrs, children)
fn td<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> = pub fn td<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
Element("td", attrs, children) Element("td", attrs, children)
// ============================================================================ // ============================================================================
// Text and empty nodes // Text and empty nodes
// ============================================================================ // ============================================================================
fn text<M>(content: String): Html<M> = pub fn text<M>(content: String): Html<M> =
Text(content) Text(content)
fn empty<M>(): Html<M> = pub fn empty<M>(): Html<M> =
Empty Empty
// ============================================================================ // ============================================================================
// Attribute helpers // Attribute helpers
// ============================================================================ // ============================================================================
fn class<M>(name: String): Attr<M> = pub fn class<M>(name: String): Attr<M> =
Class(name) Class(name)
fn id<M>(name: String): Attr<M> = pub fn id<M>(name: String): Attr<M> =
Id(name) Id(name)
fn style<M>(property: String, value: String): Attr<M> = pub fn style<M>(property: String, value: String): Attr<M> =
Style(property, value) Style(property, value)
fn href<M>(url: String): Attr<M> = pub fn href<M>(url: String): Attr<M> =
Href(url) Href(url)
fn src<M>(url: String): Attr<M> = pub fn src<M>(url: String): Attr<M> =
Src(url) Src(url)
fn alt<M>(description: String): Attr<M> = pub fn alt<M>(description: String): Attr<M> =
Alt(description) Alt(description)
fn inputType<M>(t: String): Attr<M> = pub fn inputType<M>(t: String): Attr<M> =
Type(t) Type(t)
fn value<M>(v: String): Attr<M> = pub fn value<M>(v: String): Attr<M> =
Value(v) Value(v)
fn placeholder<M>(p: String): Attr<M> = pub fn placeholder<M>(p: String): Attr<M> =
Placeholder(p) Placeholder(p)
fn disabled<M>(d: Bool): Attr<M> = pub fn disabled<M>(d: Bool): Attr<M> =
Disabled(d) Disabled(d)
fn checked<M>(c: Bool): Attr<M> = pub fn checked<M>(c: Bool): Attr<M> =
Checked(c) Checked(c)
fn name<M>(n: String): Attr<M> = pub fn name<M>(n: String): Attr<M> =
Name(n) Name(n)
fn onClick<M>(msg: M): Attr<M> = pub fn onClick<M>(msg: M): Attr<M> =
OnClick(msg) OnClick(msg)
fn onInput<M>(h: fn(String): M): Attr<M> = pub fn onInput<M>(h: fn(String): M): Attr<M> =
OnInput(h) OnInput(h)
fn onSubmit<M>(msg: M): Attr<M> = pub fn onSubmit<M>(msg: M): Attr<M> =
OnSubmit(msg) OnSubmit(msg)
fn onChange<M>(h: fn(String): M): Attr<M> = pub fn onChange<M>(h: fn(String): M): Attr<M> =
OnChange(h) OnChange(h)
fn onMouseEnter<M>(msg: M): Attr<M> = pub fn onMouseEnter<M>(msg: M): Attr<M> =
OnMouseEnter(msg) OnMouseEnter(msg)
fn onMouseLeave<M>(msg: M): Attr<M> = pub fn onMouseLeave<M>(msg: M): Attr<M> =
OnMouseLeave(msg) OnMouseLeave(msg)
fn onFocus<M>(msg: M): Attr<M> = pub fn onFocus<M>(msg: M): Attr<M> =
OnFocus(msg) OnFocus(msg)
fn onBlur<M>(msg: M): Attr<M> = pub fn onBlur<M>(msg: M): Attr<M> =
OnBlur(msg) OnBlur(msg)
fn onKeyDown<M>(h: fn(String): M): Attr<M> = pub fn onKeyDown<M>(h: fn(String): M): Attr<M> =
OnKeyDown(h) OnKeyDown(h)
fn onKeyUp<M>(h: fn(String): M): Attr<M> = pub fn onKeyUp<M>(h: fn(String): M): Attr<M> =
OnKeyUp(h) OnKeyUp(h)
fn data<M>(name: String, value: String): Attr<M> = pub fn data<M>(name: String, value: String): Attr<M> =
DataAttr(name, value) DataAttr(name, value)
// ============================================================================ // ============================================================================
@@ -290,11 +290,11 @@ fn data<M>(name: String, value: String): Attr<M> =
// ============================================================================ // ============================================================================
// Conditionally include an element // Conditionally include an element
fn when<M>(condition: Bool, element: Html<M>): Html<M> = pub fn when<M>(condition: Bool, element: Html<M>): Html<M> =
if condition then element else Empty if condition then element else Empty
// Conditionally apply attributes // Conditionally apply attributes
fn attrIf<M>(condition: Bool, attr: Attr<M>): List<Attr<M>> = pub fn attrIf<M>(condition: Bool, attr: Attr<M>): List<Attr<M>> =
if condition then [attr] else [] if condition then [attr] else []
// ============================================================================ // ============================================================================
@@ -302,7 +302,7 @@ fn attrIf<M>(condition: Bool, attr: Attr<M>): List<Attr<M>> =
// ============================================================================ // ============================================================================
// Render an attribute to a string // Render an attribute to a string
fn renderAttr<M>(attr: Attr<M>): String = pub fn renderAttr<M>(attr: Attr<M>): String =
match attr { match attr {
Class(name) => " class=\"" + name + "\"", Class(name) => " class=\"" + name + "\"",
Id(name) => " id=\"" + name + "\"", Id(name) => " id=\"" + name + "\"",
@@ -333,24 +333,24 @@ fn renderAttr<M>(attr: Attr<M>): String =
} }
// Render attributes list to string // Render attributes list to string
fn renderAttrs<M>(attrs: List<Attr<M>>): String = pub fn renderAttrs<M>(attrs: List<Attr<M>>): String =
List.foldl(attrs, "", fn(acc, attr) => acc + renderAttr(attr)) List.fold(attrs, "", fn(acc, attr) => acc + renderAttr(attr))
// Self-closing tags // Self-closing tags
fn isSelfClosing(tag: String): Bool = pub fn isSelfClosing(tag: String): Bool =
tag == "br" || tag == "hr" || tag == "img" || tag == "input" || tag == "br" || tag == "hr" || tag == "img" || tag == "input" ||
tag == "meta" || tag == "link" || tag == "area" || tag == "base" || tag == "meta" || tag == "link" || tag == "area" || tag == "base" ||
tag == "col" || tag == "embed" || tag == "source" || tag == "track" || tag == "wbr" tag == "col" || tag == "embed" || tag == "source" || tag == "track" || tag == "wbr"
// Render Html to string // Render Html to string
fn render<M>(html: Html<M>): String = pub fn render<M>(html: Html<M>): String =
match html { match html {
Element(tag, attrs, children) => { Element(tag, attrs, children) => {
let attrStr = renderAttrs(attrs) let attrStr = renderAttrs(attrs)
if isSelfClosing(tag) then if isSelfClosing(tag) then
"<" + tag + attrStr + " />" "<" + tag + attrStr + " />"
else { else {
let childrenStr = List.foldl(children, "", fn(acc, child) => acc + render(child)) let childrenStr = List.fold(children, "", fn(acc, child) => acc + render(child))
"<" + tag + attrStr + ">" + childrenStr + "</" + tag + ">" "<" + tag + attrStr + ">" + childrenStr + "</" + tag + ">"
} }
}, },
@@ -359,7 +359,7 @@ fn render<M>(html: Html<M>): String =
} }
// Escape HTML special characters // Escape HTML special characters
fn escapeHtml(s: String): String = { pub fn escapeHtml(s: String): String = {
// Simple replacement - a full implementation would handle all entities // Simple replacement - a full implementation would handle all entities
let s1 = String.replace(s, "&", "&amp;") let s1 = String.replace(s, "&", "&amp;")
let s2 = String.replace(s1, "<", "&lt;") let s2 = String.replace(s1, "<", "&lt;")
@@ -369,7 +369,7 @@ fn escapeHtml(s: String): String = {
} }
// Render a full HTML document // Render a full HTML document
fn document(title: String, headExtra: List<Html<M>>, bodyContent: List<Html<M>>): String = { pub fn document(title: String, headExtra: List<Html<M>>, bodyContent: List<Html<M>>): String = {
let headElements = List.concat([ let headElements = List.concat([
[Element("meta", [DataAttr("charset", "UTF-8")], [])], [Element("meta", [DataAttr("charset", "UTF-8")], [])],
[Element("meta", [Name("viewport"), Value("width=device-width, initial-scale=1.0")], [])], [Element("meta", [Name("viewport"), Value("width=device-width, initial-scale=1.0")], [])],

View File

@@ -2,49 +2,62 @@
This document tracks issues and limitations discovered while building the Lux website in Lux. This document tracks issues and limitations discovered while building the Lux website in Lux.
## Critical Issues ## Fixed Issues
### 1. Module Import System Not Working ### 1. Module Import System Not Working (FIXED)
**Description:** The `import` statement doesn't appear to work for importing standard library modules. **Description:** The `import` statement wasn't working for importing standard library modules.
**Example:** **Root Cause:** Two issues were found:
```lux 1. Parser didn't skip newlines inside list expressions, causing parse errors in multi-line lists
import html // Doesn't make html functions available 2. Functions in stdlib modules weren't marked as `pub` (public), so they weren't exported
```
**Workaround:** Functions must be defined in the same file or copied. **Fix:**
1. Added `skip_newlines()` calls to `parse_list_expr()` in parser.rs
2. Added `pub` keyword to all exported functions in stdlib/html.lux
**Status:** Needs investigation **Status:** FIXED
--- ---
### 2. Parse Error in html.lux (Line 196-197) ### 2. Parse Error in html.lux (FIXED)
**Description:** When trying to load files that import the html module, there's a parse error. **Description:** When trying to load files that import the html module, there was a parse error at line 196-197.
**Error Message:** **Error Message:**
``` ```
Module error: Module error in '<main>': Parse error: Parse error at 196-197: Unexpected token: \n Module error: Module error in '<main>': Parse error: Parse error at 196-197: Unexpected token: \n
``` ```
**Status:** Needs investigation **Root Cause:** The parser's `parse_list_expr()` function didn't handle newlines between list elements.
**Fix:** Added `skip_newlines()` calls after `[`, after each element, and after commas in list expressions.
**Status:** FIXED
---
### 3. List.foldl Renamed to List.fold (FIXED)
**Description:** The html.lux file used `List.foldl` but the built-in List module exports `List.fold`.
**Fix:** Changed `List.foldl` to `List.fold` in stdlib/html.lux.
**Status:** FIXED
--- ---
## Minor Issues ## Minor Issues
### 3. String.replace May Not Exist ### 4. String.replace Verified Working
**Description:** The `escapeHtml` function in html.lux uses `String.replace`, but this function may not be implemented. **Description:** The `escapeHtml` function in html.lux uses `String.replace`.
**Workaround:** Implement character-by-character escaping. **Status:** VERIFIED WORKING - String.replace is implemented in the interpreter.
**Status:** Needs verification
--- ---
### 4. FileSystem Effect Not Fully Implemented ### 5. FileSystem Effect Not Fully Implemented
**Description:** For static site generation, we need `FileSystem.mkdir`, `FileSystem.write`, `FileSystem.copy` operations. These may not be fully implemented. **Description:** For static site generation, we need `FileSystem.mkdir`, `FileSystem.write`, `FileSystem.copy` operations. These may not be fully implemented.
@@ -54,17 +67,17 @@ Module error: Module error in '<main>': Parse error: Parse error at 196-197: Une
--- ---
### 5. List.concat May Have Issues ### 6. List.concat Verified Working
**Description:** The `List.concat` function used in html.lux document generation may not handle nested lists correctly. **Description:** The `List.concat` function is used in html.lux document generation.
**Status:** Needs verification **Status:** VERIFIED WORKING - List.concat is implemented in the interpreter.
--- ---
## Feature Gaps ## Feature Gaps
### 6. No Built-in Static Site Generation ### 7. No Built-in Static Site Generation
**Description:** There's no built-in way to generate static HTML files from Lux. A static site generator effect or module would be helpful. **Description:** There's no built-in way to generate static HTML files from Lux. A static site generator effect or module would be helpful.
@@ -76,7 +89,7 @@ Module error: Module error in '<main>': Parse error: Parse error at 196-197: Une
--- ---
### 7. No Template String Support ### 8. No Template String Support
**Description:** Multi-line strings and template literals (like JavaScript's backticks) would make HTML generation much easier. **Description:** Multi-line strings and template literals (like JavaScript's backticks) would make HTML generation much easier.
@@ -94,7 +107,7 @@ let html = `<div class="${className}">${content}</div>`
--- ---
### 8. No Markdown Parser ### 9. No Markdown Parser
**Description:** A built-in Markdown parser would be valuable for documentation sites. **Description:** A built-in Markdown parser would be valuable for documentation sites.
@@ -112,6 +125,8 @@ These features work correctly and can be used for website generation:
4. **Basic types** - `String`, `Int`, `Bool` work 4. **Basic types** - `String`, `Int`, `Bool` work
5. **Let bindings** - Variable assignment works 5. **Let bindings** - Variable assignment works
6. **Functions** - Function definitions work 6. **Functions** - Function definitions work
7. **Module imports** - Works with `import stdlib/module`
8. **Html module** - Fully functional for generating HTML strings
--- ---
@@ -119,15 +134,14 @@ These features work correctly and can be used for website generation:
### For Website MVP ### For Website MVP
Since the module system isn't working, the website should be: The module system now works! The website can be:
1. **Hand-written HTML** (already done in `dist/index.html`) 1. **Generated from Lux** using the html module for HTML rendering
2. **CSS separate file** (already done in `static/style.css`) 2. **CSS separate file** (already done in `static/style.css`)
3. **Lux code examples** embedded as text in HTML 3. **Lux code examples** embedded as text in HTML
### For Future ### For Future
Once these issues are fixed, the website can be: 1. **Add FileSystem effect** for writing generated HTML to files
1. **Generated from Lux** using the components and pages modules
2. **Markdown-based documentation** parsed and rendered by Lux 2. **Markdown-based documentation** parsed and rendered by Lux
3. **Live playground** using WASM compilation 3. **Live playground** using WASM compilation
@@ -137,12 +151,15 @@ Once these issues are fixed, the website can be:
| Feature | Status | Notes | | Feature | Status | Notes |
|---------|--------|-------| |---------|--------|-------|
| String concatenation | Works | `"<" + tag + ">"` | | String concatenation | Works | `"<" + tag + ">"` |
| Conditionals | Works | `if x then y else z` | | Conditionals | Works | `if x then y else z` |
| Console.print | Works | Basic output | | Console.print | Works | Basic output |
| Module imports | ❌ Broken | Parse errors | | Module imports | Works | `import stdlib/html` |
| Html module | ❌ Blocked | Depends on imports | | Html module | Works | Full HTML generation |
| FileSystem | ❓ Unknown | Not tested | | List.fold | Works | Fold over lists |
| List.concat | Works | Concatenate list of lists |
| String.replace | Works | String replacement |
| FileSystem | Unknown | Not tested |
--- ---
@@ -151,3 +168,6 @@ Once these issues are fixed, the website can be:
| Date | Finding | | Date | Finding |
|------|---------| |------|---------|
| 2026-02-16 | Module import system not working, parse error at line 196-197 in html.lux | | 2026-02-16 | Module import system not working, parse error at line 196-197 in html.lux |
| 2026-02-16 | Fixed: Parser newline handling in list expressions |
| 2026-02-16 | Fixed: Added `pub` to stdlib/html.lux exports |
| 2026-02-16 | Fixed: Changed List.foldl to List.fold |