sexp-html

    HTML's s-expression representation

    s-exp
    html
    Download zip
    Author
    Version
    0.6.0
    License
    Apache-2.0
    Last updated
    last month
    Downloads
    120

    #S-Expression Notation for HTML

    #Example

    • keep comments

      (% I want you to know since you came in my life)

      <!-- I want you to know since you came in my life -->

    • runs of whitespace inside a text term collapse to a single space

      (p One hundred million and two thousand years from now (span (:style font-family: "Alegreya Sans SC", sans-serif) 爱してる) )

      <p>One hundred million and two thousand years from now <span style="font-family: &quot;Alegreya Sans SC&quot;, sans-serif">爱してる</span></p>

    • only parentheses need to be escaped

      (details (:open) (summary every day every night) \(I've been waiting to share my love with you\) you give light into the darkness skies )

      <details open><summary>every day every night</summary> (I've been waiting to share my love with you) you give light into the darkness skies</details>

    • (# ...) emits a verbatim text node: everything after # is literal, including leading spaces and newlines. No whitespace is consumed as a separator.

      (p (span hello) (# world))

      <p><span>hello</span> world</p>

    • whitespace you type around an element is content, so adjacency requires no space

      (p a (b c)) (p a(b c))

      <p>a <b>c</b></p> <p>a<b>c</b></p>

    #Specification

    see SPEC.md.

    #AST transform API

    For custom forms that should become normal HTML-shaped nodes, parse to the public AST, transform it, then render it:

    parse_sexp("(p (icon search))").map(transform).map(render_html_fragment)

    The public AST is normalized to HTML-shaped nodes:

    pub(all) enum SexpNode {
    Text(String)
    Comment(String)
    Raw(String)
    Element(String, Attrs, Array[SexpNode])
    }

    Syntax forms such as (# ...) are expanded during parsing into ordinary Text and Element nodes.

    Use Raw for trusted content that should be inserted directly without escaping:

    render_html_fragment([
    SexpNode::Element("p", Attrs({}), [
    SexpNode::Text("<escaped> "),
    SexpNode::Raw("<em>trusted</em>"),
    ]),
    ])

    sexp-html also provides helpers for common HTML rendering operations:

    let attrs : Attrs = Attrs({})
    attrs.upsert("lang", Some("en"))
    attrs.append_class("page")

    render_open_tag("html", attrs)
    render_html_document(attrs, head, body, Some("html"))

    Attrs

    pub(all) struct Attrs(Map[String, String?])

    A map of HTML attribute names to optional values. None renders as a minimized (valueless) attribute.

    Attrs::append_class

    fn Attrs::append_class(self : Attrs, class_name : String) -> Unit

    Appends a class to the class attribute.

    Attrs::upsert

    fn Attrs::upsert(self : Attrs, name : String, value : String?) -> Unit

    Sets an attribute, overwriting any previous value. None renders minimized.

    SexpNode

    pub(all) enum SexpNode {
    Text(String)
    Comment(String)
    Raw(String)
    Element(String, Attrs, Array[SexpNode])
    }

    A normalized AST node. Text nodes are HTML-escaped when rendered; Raw holds trusted HTML emitted verbatim.

    escape_html_attr_value

    fn escape_html_attr_value(value : String) -> String

    HTML-escapes value for use inside a double-quoted attribute value: in addition to &, <, and >, " becomes &quot;.

    escape_html_text

    fn escape_html_text(text : String) -> String

    HTML-escapes text for use in element text content: &, <, and > become their named character references.

    is_sexp_close_paren

    fn is_sexp_close_paren(ch : Char) -> Bool

    Returns true for the closing delimiter of an S-expression list.

    is_sexp_open_paren

    fn is_sexp_open_paren(ch : Char) -> Bool

    Returns true for the opening delimiter of an S-expression list.

    is_sexp_paren

    fn is_sexp_paren(ch : Char) -> Bool

    Returns true for either S-expression list delimiter.

    is_sexp_text_escape_target

    fn is_sexp_text_escape_target(ch : Char) -> Bool

    Returns true for characters escaped by S-expression text syntax.

    is_void_element_name

    fn is_void_element_name(tag : String) -> Bool

    Returns whether tag is an HTML void element (rendered without a closing tag and invalid with child nodes).

    parse_sexp

    fn parse_sexp(source : String) -> Result[Array[SexpNode], String]

    Parses a full S-expression HTML fragment into normalized AST nodes.

    Returns Err with a parse error at line L, column C: ... message when the source is not a valid fragment.

    render_html_document

    fn render_html_document(attrs : Attrs, head : Array[SexpNode], body : Array[SexpNode], doctype : String?) -> String

    Renders a full HTML document shell with doctype, <head> and <body>.

    render_html_fragment

    fn render_html_fragment(nodes : Array[SexpNode], separator? : String) -> String

    Renders AST nodes to an HTML fragment, joined by separator.

    render_open_tag

    fn render_open_tag(tag : String, attrs : Attrs) -> String

    Renders <tag ...> with the given attributes.

    sexp_to_html

    fn sexp_to_html(source : String, separator? : String) -> Result[String, String]

    Parses an S-expression HTML fragment and renders it to an HTML string.

    Returns Err with a parse error at line L, column C: ... message when the source is not a valid fragment.