diff --git a/src/parser.rs b/src/parser.rs index b96953a..4529940 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2266,12 +2266,15 @@ impl Parser { fn parse_list_expr(&mut self) -> Result { let start = self.current_span(); self.expect(TokenKind::LBracket)?; + self.skip_newlines(); let mut elements = Vec::new(); while !self.check(TokenKind::RBracket) { elements.push(self.parse_expr()?); + self.skip_newlines(); if !self.check(TokenKind::RBracket) { self.expect(TokenKind::Comma)?; + self.skip_newlines(); } } diff --git a/stdlib/html.lux b/stdlib/html.lux index 0a49ac0..b31e68d 100644 --- a/stdlib/html.lux +++ b/stdlib/html.lux @@ -11,13 +11,13 @@ // Html type represents a DOM structure // Parameterized by Msg - the type of messages emitted by event handlers -type Html = +pub type Html = | Element(String, List>, List>) | Text(String) | Empty // Attributes that can be applied to elements -type Attr = +pub type Attr = | Class(String) | Id(String) | Style(String, String) @@ -46,243 +46,243 @@ type Attr = // Element builders - Container elements // ============================================================================ -fn div(attrs: List>, children: List>): Html = +pub fn div(attrs: List>, children: List>): Html = Element("div", attrs, children) -fn span(attrs: List>, children: List>): Html = +pub fn span(attrs: List>, children: List>): Html = Element("span", attrs, children) -fn section(attrs: List>, children: List>): Html = +pub fn section(attrs: List>, children: List>): Html = Element("section", attrs, children) -fn article(attrs: List>, children: List>): Html = +pub fn article(attrs: List>, children: List>): Html = Element("article", attrs, children) -fn header(attrs: List>, children: List>): Html = +pub fn header(attrs: List>, children: List>): Html = Element("header", attrs, children) -fn footer(attrs: List>, children: List>): Html = +pub fn footer(attrs: List>, children: List>): Html = Element("footer", attrs, children) -fn nav(attrs: List>, children: List>): Html = +pub fn nav(attrs: List>, children: List>): Html = Element("nav", attrs, children) -fn main(attrs: List>, children: List>): Html = +pub fn main(attrs: List>, children: List>): Html = Element("main", attrs, children) -fn aside(attrs: List>, children: List>): Html = +pub fn aside(attrs: List>, children: List>): Html = Element("aside", attrs, children) // ============================================================================ // Element builders - Text elements // ============================================================================ -fn h1(attrs: List>, children: List>): Html = +pub fn h1(attrs: List>, children: List>): Html = Element("h1", attrs, children) -fn h2(attrs: List>, children: List>): Html = +pub fn h2(attrs: List>, children: List>): Html = Element("h2", attrs, children) -fn h3(attrs: List>, children: List>): Html = +pub fn h3(attrs: List>, children: List>): Html = Element("h3", attrs, children) -fn h4(attrs: List>, children: List>): Html = +pub fn h4(attrs: List>, children: List>): Html = Element("h4", attrs, children) -fn h5(attrs: List>, children: List>): Html = +pub fn h5(attrs: List>, children: List>): Html = Element("h5", attrs, children) -fn h6(attrs: List>, children: List>): Html = +pub fn h6(attrs: List>, children: List>): Html = Element("h6", attrs, children) -fn p(attrs: List>, children: List>): Html = +pub fn p(attrs: List>, children: List>): Html = Element("p", attrs, children) -fn pre(attrs: List>, children: List>): Html = +pub fn pre(attrs: List>, children: List>): Html = Element("pre", attrs, children) -fn code(attrs: List>, children: List>): Html = +pub fn code(attrs: List>, children: List>): Html = Element("code", attrs, children) -fn blockquote(attrs: List>, children: List>): Html = +pub fn blockquote(attrs: List>, children: List>): Html = Element("blockquote", attrs, children) // ============================================================================ // Element builders - Inline elements // ============================================================================ -fn a(attrs: List>, children: List>): Html = +pub fn a(attrs: List>, children: List>): Html = Element("a", attrs, children) -fn strong(attrs: List>, children: List>): Html = +pub fn strong(attrs: List>, children: List>): Html = Element("strong", attrs, children) -fn em(attrs: List>, children: List>): Html = +pub fn em(attrs: List>, children: List>): Html = Element("em", attrs, children) -fn small(attrs: List>, children: List>): Html = +pub fn small(attrs: List>, children: List>): Html = Element("small", attrs, children) -fn br(): Html = +pub fn br(): Html = Element("br", [], []) -fn hr(): Html = +pub fn hr(): Html = Element("hr", [], []) // ============================================================================ // Element builders - Lists // ============================================================================ -fn ul(attrs: List>, children: List>): Html = +pub fn ul(attrs: List>, children: List>): Html = Element("ul", attrs, children) -fn ol(attrs: List>, children: List>): Html = +pub fn ol(attrs: List>, children: List>): Html = Element("ol", attrs, children) -fn li(attrs: List>, children: List>): Html = +pub fn li(attrs: List>, children: List>): Html = Element("li", attrs, children) // ============================================================================ // Element builders - Forms // ============================================================================ -fn form(attrs: List>, children: List>): Html = +pub fn form(attrs: List>, children: List>): Html = Element("form", attrs, children) -fn input(attrs: List>): Html = +pub fn input(attrs: List>): Html = Element("input", attrs, []) -fn textarea(attrs: List>, children: List>): Html = +pub fn textarea(attrs: List>, children: List>): Html = Element("textarea", attrs, children) -fn button(attrs: List>, children: List>): Html = +pub fn button(attrs: List>, children: List>): Html = Element("button", attrs, children) -fn label(attrs: List>, children: List>): Html = +pub fn label(attrs: List>, children: List>): Html = Element("label", attrs, children) -fn select(attrs: List>, children: List>): Html = +pub fn select(attrs: List>, children: List>): Html = Element("select", attrs, children) -fn option(attrs: List>, children: List>): Html = +pub fn option(attrs: List>, children: List>): Html = Element("option", attrs, children) // ============================================================================ // Element builders - Media // ============================================================================ -fn img(attrs: List>): Html = +pub fn img(attrs: List>): Html = Element("img", attrs, []) -fn video(attrs: List>, children: List>): Html = +pub fn video(attrs: List>, children: List>): Html = Element("video", attrs, children) -fn audio(attrs: List>, children: List>): Html = +pub fn audio(attrs: List>, children: List>): Html = Element("audio", attrs, children) // ============================================================================ // Element builders - Tables // ============================================================================ -fn table(attrs: List>, children: List>): Html = +pub fn table(attrs: List>, children: List>): Html = Element("table", attrs, children) -fn thead(attrs: List>, children: List>): Html = +pub fn thead(attrs: List>, children: List>): Html = Element("thead", attrs, children) -fn tbody(attrs: List>, children: List>): Html = +pub fn tbody(attrs: List>, children: List>): Html = Element("tbody", attrs, children) -fn tr(attrs: List>, children: List>): Html = +pub fn tr(attrs: List>, children: List>): Html = Element("tr", attrs, children) -fn th(attrs: List>, children: List>): Html = +pub fn th(attrs: List>, children: List>): Html = Element("th", attrs, children) -fn td(attrs: List>, children: List>): Html = +pub fn td(attrs: List>, children: List>): Html = Element("td", attrs, children) // ============================================================================ // Text and empty nodes // ============================================================================ -fn text(content: String): Html = +pub fn text(content: String): Html = Text(content) -fn empty(): Html = +pub fn empty(): Html = Empty // ============================================================================ // Attribute helpers // ============================================================================ -fn class(name: String): Attr = +pub fn class(name: String): Attr = Class(name) -fn id(name: String): Attr = +pub fn id(name: String): Attr = Id(name) -fn style(property: String, value: String): Attr = +pub fn style(property: String, value: String): Attr = Style(property, value) -fn href(url: String): Attr = +pub fn href(url: String): Attr = Href(url) -fn src(url: String): Attr = +pub fn src(url: String): Attr = Src(url) -fn alt(description: String): Attr = +pub fn alt(description: String): Attr = Alt(description) -fn inputType(t: String): Attr = +pub fn inputType(t: String): Attr = Type(t) -fn value(v: String): Attr = +pub fn value(v: String): Attr = Value(v) -fn placeholder(p: String): Attr = +pub fn placeholder(p: String): Attr = Placeholder(p) -fn disabled(d: Bool): Attr = +pub fn disabled(d: Bool): Attr = Disabled(d) -fn checked(c: Bool): Attr = +pub fn checked(c: Bool): Attr = Checked(c) -fn name(n: String): Attr = +pub fn name(n: String): Attr = Name(n) -fn onClick(msg: M): Attr = +pub fn onClick(msg: M): Attr = OnClick(msg) -fn onInput(h: fn(String): M): Attr = +pub fn onInput(h: fn(String): M): Attr = OnInput(h) -fn onSubmit(msg: M): Attr = +pub fn onSubmit(msg: M): Attr = OnSubmit(msg) -fn onChange(h: fn(String): M): Attr = +pub fn onChange(h: fn(String): M): Attr = OnChange(h) -fn onMouseEnter(msg: M): Attr = +pub fn onMouseEnter(msg: M): Attr = OnMouseEnter(msg) -fn onMouseLeave(msg: M): Attr = +pub fn onMouseLeave(msg: M): Attr = OnMouseLeave(msg) -fn onFocus(msg: M): Attr = +pub fn onFocus(msg: M): Attr = OnFocus(msg) -fn onBlur(msg: M): Attr = +pub fn onBlur(msg: M): Attr = OnBlur(msg) -fn onKeyDown(h: fn(String): M): Attr = +pub fn onKeyDown(h: fn(String): M): Attr = OnKeyDown(h) -fn onKeyUp(h: fn(String): M): Attr = +pub fn onKeyUp(h: fn(String): M): Attr = OnKeyUp(h) -fn data(name: String, value: String): Attr = +pub fn data(name: String, value: String): Attr = DataAttr(name, value) // ============================================================================ @@ -290,11 +290,11 @@ fn data(name: String, value: String): Attr = // ============================================================================ // Conditionally include an element -fn when(condition: Bool, element: Html): Html = +pub fn when(condition: Bool, element: Html): Html = if condition then element else Empty // Conditionally apply attributes -fn attrIf(condition: Bool, attr: Attr): List> = +pub fn attrIf(condition: Bool, attr: Attr): List> = if condition then [attr] else [] // ============================================================================ @@ -302,7 +302,7 @@ fn attrIf(condition: Bool, attr: Attr): List> = // ============================================================================ // Render an attribute to a string -fn renderAttr(attr: Attr): String = +pub fn renderAttr(attr: Attr): String = match attr { Class(name) => " class=\"" + name + "\"", Id(name) => " id=\"" + name + "\"", @@ -333,24 +333,24 @@ fn renderAttr(attr: Attr): String = } // Render attributes list to string -fn renderAttrs(attrs: List>): String = - List.foldl(attrs, "", fn(acc, attr) => acc + renderAttr(attr)) +pub fn renderAttrs(attrs: List>): String = + List.fold(attrs, "", fn(acc, attr) => acc + renderAttr(attr)) // Self-closing tags -fn isSelfClosing(tag: String): Bool = +pub fn isSelfClosing(tag: String): Bool = tag == "br" || tag == "hr" || tag == "img" || tag == "input" || tag == "meta" || tag == "link" || tag == "area" || tag == "base" || tag == "col" || tag == "embed" || tag == "source" || tag == "track" || tag == "wbr" // Render Html to string -fn render(html: Html): String = +pub fn render(html: Html): String = match html { Element(tag, attrs, children) => { let attrStr = renderAttrs(attrs) if isSelfClosing(tag) then "<" + tag + attrStr + " />" 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 + "" } }, @@ -359,7 +359,7 @@ fn render(html: Html): String = } // Escape HTML special characters -fn escapeHtml(s: String): String = { +pub fn escapeHtml(s: String): String = { // Simple replacement - a full implementation would handle all entities let s1 = String.replace(s, "&", "&") let s2 = String.replace(s1, "<", "<") @@ -369,7 +369,7 @@ fn escapeHtml(s: String): String = { } // Render a full HTML document -fn document(title: String, headExtra: List>, bodyContent: List>): String = { +pub fn document(title: String, headExtra: List>, bodyContent: List>): String = { let headElements = List.concat([ [Element("meta", [DataAttr("charset", "UTF-8")], [])], [Element("meta", [Name("viewport"), Value("width=device-width, initial-scale=1.0")], [])], diff --git a/website/lux-site/LUX_WEAKNESSES.md b/website/lux-site/LUX_WEAKNESSES.md index 8e6a097..6a57195 100644 --- a/website/lux-site/LUX_WEAKNESSES.md +++ b/website/lux-site/LUX_WEAKNESSES.md @@ -2,49 +2,62 @@ 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:** -```lux -import html // Doesn't make html functions available -``` +**Root Cause:** Two issues were found: +1. Parser didn't skip newlines inside list expressions, causing parse errors in multi-line lists +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:** ``` Module error: Module error in '
': 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 -### 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:** Needs verification +**Status:** VERIFIED WORKING - String.replace is implemented in the interpreter. --- -### 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. @@ -54,17 +67,17 @@ Module error: Module error in '
': 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 -### 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. @@ -76,7 +89,7 @@ Module error: Module error in '
': 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. @@ -94,7 +107,7 @@ let html = `
${content}
` --- -### 8. No Markdown Parser +### 9. No Markdown Parser **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 5. **Let bindings** - Variable assignment works 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 -Since the module system isn't working, the website should be: -1. **Hand-written HTML** (already done in `dist/index.html`) +The module system now works! The website can be: +1. **Generated from Lux** using the html module for HTML rendering 2. **CSS separate file** (already done in `static/style.css`) 3. **Lux code examples** embedded as text in HTML ### For Future -Once these issues are fixed, the website can be: -1. **Generated from Lux** using the components and pages modules +1. **Add FileSystem effect** for writing generated HTML to files 2. **Markdown-based documentation** parsed and rendered by Lux 3. **Live playground** using WASM compilation @@ -137,12 +151,15 @@ Once these issues are fixed, the website can be: | Feature | Status | Notes | |---------|--------|-------| -| String concatenation | ✅ Works | `"<" + tag + ">"` | -| Conditionals | ✅ Works | `if x then y else z` | -| Console.print | ✅ Works | Basic output | -| Module imports | ❌ Broken | Parse errors | -| Html module | ❌ Blocked | Depends on imports | -| FileSystem | ❓ Unknown | Not tested | +| String concatenation | Works | `"<" + tag + ">"` | +| Conditionals | Works | `if x then y else z` | +| Console.print | Works | Basic output | +| Module imports | Works | `import stdlib/html` | +| Html module | Works | Full HTML generation | +| 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 | |------|---------| | 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 |