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:
162
stdlib/html.lux
162
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<M> =
|
||||
pub type Html<M> =
|
||||
| Element(String, List<Attr<M>>, List<Html<M>>)
|
||||
| Text(String)
|
||||
| Empty
|
||||
|
||||
// Attributes that can be applied to elements
|
||||
type Attr<M> =
|
||||
pub type Attr<M> =
|
||||
| Class(String)
|
||||
| Id(String)
|
||||
| Style(String, String)
|
||||
@@ -46,243 +46,243 @@ type Attr<M> =
|
||||
// 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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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 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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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 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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
fn br<M>(): Html<M> =
|
||||
pub fn br<M>(): Html<M> =
|
||||
Element("br", [], [])
|
||||
|
||||
fn hr<M>(): Html<M> =
|
||||
pub fn hr<M>(): Html<M> =
|
||||
Element("hr", [], [])
|
||||
|
||||
// ============================================================================
|
||||
// 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)
|
||||
|
||||
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)
|
||||
|
||||
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 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)
|
||||
|
||||
fn input<M>(attrs: List<Attr<M>>): Html<M> =
|
||||
pub fn input<M>(attrs: List<Attr<M>>): Html<M> =
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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 builders - Media
|
||||
// ============================================================================
|
||||
|
||||
fn img<M>(attrs: List<Attr<M>>): Html<M> =
|
||||
pub fn img<M>(attrs: List<Attr<M>>): Html<M> =
|
||||
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)
|
||||
|
||||
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 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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
// ============================================================================
|
||||
// Text and empty nodes
|
||||
// ============================================================================
|
||||
|
||||
fn text<M>(content: String): Html<M> =
|
||||
pub fn text<M>(content: String): Html<M> =
|
||||
Text(content)
|
||||
|
||||
fn empty<M>(): Html<M> =
|
||||
pub fn empty<M>(): Html<M> =
|
||||
Empty
|
||||
|
||||
// ============================================================================
|
||||
// Attribute helpers
|
||||
// ============================================================================
|
||||
|
||||
fn class<M>(name: String): Attr<M> =
|
||||
pub fn class<M>(name: String): Attr<M> =
|
||||
Class(name)
|
||||
|
||||
fn id<M>(name: String): Attr<M> =
|
||||
pub fn id<M>(name: String): Attr<M> =
|
||||
Id(name)
|
||||
|
||||
fn style<M>(property: String, value: String): Attr<M> =
|
||||
pub fn style<M>(property: String, value: String): Attr<M> =
|
||||
Style(property, value)
|
||||
|
||||
fn href<M>(url: String): Attr<M> =
|
||||
pub fn href<M>(url: String): Attr<M> =
|
||||
Href(url)
|
||||
|
||||
fn src<M>(url: String): Attr<M> =
|
||||
pub fn src<M>(url: String): Attr<M> =
|
||||
Src(url)
|
||||
|
||||
fn alt<M>(description: String): Attr<M> =
|
||||
pub fn alt<M>(description: String): Attr<M> =
|
||||
Alt(description)
|
||||
|
||||
fn inputType<M>(t: String): Attr<M> =
|
||||
pub fn inputType<M>(t: String): Attr<M> =
|
||||
Type(t)
|
||||
|
||||
fn value<M>(v: String): Attr<M> =
|
||||
pub fn value<M>(v: String): Attr<M> =
|
||||
Value(v)
|
||||
|
||||
fn placeholder<M>(p: String): Attr<M> =
|
||||
pub fn placeholder<M>(p: String): Attr<M> =
|
||||
Placeholder(p)
|
||||
|
||||
fn disabled<M>(d: Bool): Attr<M> =
|
||||
pub fn disabled<M>(d: Bool): Attr<M> =
|
||||
Disabled(d)
|
||||
|
||||
fn checked<M>(c: Bool): Attr<M> =
|
||||
pub fn checked<M>(c: Bool): Attr<M> =
|
||||
Checked(c)
|
||||
|
||||
fn name<M>(n: String): Attr<M> =
|
||||
pub fn name<M>(n: String): Attr<M> =
|
||||
Name(n)
|
||||
|
||||
fn onClick<M>(msg: M): Attr<M> =
|
||||
pub fn onClick<M>(msg: M): Attr<M> =
|
||||
OnClick(msg)
|
||||
|
||||
fn onInput<M>(h: fn(String): M): Attr<M> =
|
||||
pub fn onInput<M>(h: fn(String): M): Attr<M> =
|
||||
OnInput(h)
|
||||
|
||||
fn onSubmit<M>(msg: M): Attr<M> =
|
||||
pub fn onSubmit<M>(msg: M): Attr<M> =
|
||||
OnSubmit(msg)
|
||||
|
||||
fn onChange<M>(h: fn(String): M): Attr<M> =
|
||||
pub fn onChange<M>(h: fn(String): M): Attr<M> =
|
||||
OnChange(h)
|
||||
|
||||
fn onMouseEnter<M>(msg: M): Attr<M> =
|
||||
pub fn onMouseEnter<M>(msg: M): Attr<M> =
|
||||
OnMouseEnter(msg)
|
||||
|
||||
fn onMouseLeave<M>(msg: M): Attr<M> =
|
||||
pub fn onMouseLeave<M>(msg: M): Attr<M> =
|
||||
OnMouseLeave(msg)
|
||||
|
||||
fn onFocus<M>(msg: M): Attr<M> =
|
||||
pub fn onFocus<M>(msg: M): Attr<M> =
|
||||
OnFocus(msg)
|
||||
|
||||
fn onBlur<M>(msg: M): Attr<M> =
|
||||
pub fn onBlur<M>(msg: M): Attr<M> =
|
||||
OnBlur(msg)
|
||||
|
||||
fn onKeyDown<M>(h: fn(String): M): Attr<M> =
|
||||
pub fn onKeyDown<M>(h: fn(String): M): Attr<M> =
|
||||
OnKeyDown(h)
|
||||
|
||||
fn onKeyUp<M>(h: fn(String): M): Attr<M> =
|
||||
pub fn onKeyUp<M>(h: fn(String): M): Attr<M> =
|
||||
OnKeyUp(h)
|
||||
|
||||
fn data<M>(name: String, value: String): Attr<M> =
|
||||
pub fn data<M>(name: String, value: String): Attr<M> =
|
||||
DataAttr(name, value)
|
||||
|
||||
// ============================================================================
|
||||
@@ -290,11 +290,11 @@ fn data<M>(name: String, value: String): Attr<M> =
|
||||
// ============================================================================
|
||||
|
||||
// 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
|
||||
|
||||
// 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 []
|
||||
|
||||
// ============================================================================
|
||||
@@ -302,7 +302,7 @@ fn attrIf<M>(condition: Bool, attr: Attr<M>): List<Attr<M>> =
|
||||
// ============================================================================
|
||||
|
||||
// Render an attribute to a string
|
||||
fn renderAttr<M>(attr: Attr<M>): String =
|
||||
pub fn renderAttr<M>(attr: Attr<M>): String =
|
||||
match attr {
|
||||
Class(name) => " class=\"" + name + "\"",
|
||||
Id(name) => " id=\"" + name + "\"",
|
||||
@@ -333,24 +333,24 @@ fn renderAttr<M>(attr: Attr<M>): String =
|
||||
}
|
||||
|
||||
// Render attributes list to string
|
||||
fn renderAttrs<M>(attrs: List<Attr<M>>): String =
|
||||
List.foldl(attrs, "", fn(acc, attr) => acc + renderAttr(attr))
|
||||
pub fn renderAttrs<M>(attrs: List<Attr<M>>): 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<M>(html: Html<M>): String =
|
||||
pub fn render<M>(html: Html<M>): 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 + "</" + tag + ">"
|
||||
}
|
||||
},
|
||||
@@ -359,7 +359,7 @@ fn render<M>(html: Html<M>): 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<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([
|
||||
[Element("meta", [DataAttr("charset", "UTF-8")], [])],
|
||||
[Element("meta", [Name("viewport"), Value("width=device-width, initial-scale=1.0")], [])],
|
||||
|
||||
Reference in New Issue
Block a user