9 ms·
Produce HTML from S-Expressions
- lelanthran 3y agoI'd like to thank everyone for their feedback and criticisms; I've received some excellent suggestions for improvement. I've tried to engage with everyone - I apologise to those I didn't engage with or answer adequately.
- sph 3y ago(h1 :disabled Hello world!) (div :class="alert" So long, and (b thanks) for all the (em fish)) I hate that syntax for attributes. :class="alert" is strictly worse than ((class . "alert")) or any other variation that actually leverages s-expressions instead of merging key and value into the same symbol. Ew. Not a fan of the bare words instead of strings either, which means this cannot be naively evaluated by a Lisp. I wouldn't say this is producing html from s-exps, rather that it is vaguely inspired by them.
- ptx 3y agoWith this merged attribute syntax, since each key-value combination is a separate symbol and since symbols are interned, doesn't this mean that all the attribute values (and not just the keys) end up interned? Do Lisp implementations usually garbage-collect interned symbols when they're no longer used?
- kazinator 3y agoFirstly, obviously yes for un-interned. TXR Lisp: This is the TXR Lisp interactive listener of TXR 291. Quit with :quit or Ctrl-D on an empty line. Ctrl-X ? for cheatsheet. I'm confused; is this piano recital Rimsky-Korsakov or Wernicke–Korsakoff? 1> (progn [finalize (gensym "foo-") prinl] nil) nil 2> (sys:gc) #:foo-0012 t 3> (sys:gc) t We make a gensym whose name starts with foo-. We register a GC finalizer for it which is the prinl function (print, with newline). Then when we manually invoke GC, it gets collected; we see the print. Internet symbols cannot be collected, normally, because they are always used: they are registered in their package. TXR Lisp has weak packages, though. A weak package will purge a symbol that is not reachable other than through the package. 1> (make-package "abc" t) ;; t argument here means weak #<package: abc> 2> (progn [finalize (intern "xyz" *1) prinl] nil) nil 3> (sys:gc) abc:xyz t 4> (package-symbols *1) nil 5> Don't know if other Lisp people thunk of this one, but there it is. I don't think I exposed this weak property through the defpackage macro. It's intended for specific scenarios, like reading data in the context of a package.
- KRAKRISMOTT 3y agoLike this amirite https://github.com/weavejester/hiccup https://github.com/weavejester/hiccup
- xemoka 3y agoThe thi.ng umbrella has a hiccup variant for JS that looks pretty neat too: https://thi.ng/hiccup https://thi.ng/hiccup
- lelanthran 3y ago> :class="alert" is strictly worse than ((class . "alert")) or any other variation that actually leverages s-expressions instead of merging key and value into the same symbol. Ew. I hear you, but I wasn't aiming for leveraging the power of macros. Using `((class . "alert"))` does not provide any benefits over :class="alert". > Not a fan of the bare words instead of strings either, which means this cannot be naively evaluated by a Lisp. I traded off ergonomics against "Evaluation by a Lisp processor".Instead of (div "My first " (b "hello") (em " world")) I'd rather do (div My first (b hello) (em world)) Thank you for your input.
- kazinator 3y agoIf My and first just stand for themselves, you're not interpolating variables; where is the templating? Also, what if you don't want a space between the bold hello and emphasized world? Or between first and <b>hello</b>?
- lelanthran 3y ago> If My and first just stand for themselves, you're not interpolating variables; where is the templating? Templating wasn't mentioned (yet. looking at the feedback I got, some avenues are open for some sort of templating). > Also, what if you don't want a space between the bold hello and emphasized world? Then you don't put one in? $ echo "(div My first (b hello)(em world)) " | ./l2h -s <div>My first <b>hello</b><em>world</em></div> > Or between first and <b>hello</b>? Again, don't put one in: echo "(div My first(b hello) (em world))" | ./l2h -s <div>My first<b>hello</b> <em>world</em></div> I think that that is is intuitive as it can get for people who know HTML: space-emission matches what you would expect for HTML. If you look at the git history, I spent a lot of time making sure that the parsing of whitespace and newlines made sense from an HTML perspective, not from a Lisp perspective.
- kazinator 3y agoThat's not S-exprs then because there is no symbolic difference between )( and ) ( or between foo( and foo (. But it's a better representation for HTML, that we would have had if Berners-Lee hadn't been a supreme goofball.
- messe 3y ago> Not a fan of the bare words instead of strings either It is consistent with HTML however, especially the fact that both collapse whitespace.
- giraffe_lady 3y agoPollen's tagged xexp is probably the most sophisticated approach to this I've seen. Though hiccup is about as good and easier to work with. https://docs.racket-lang.org/txexpr/index.html https://docs.racket-lang.org/txexpr/index.html
- Zambyte 3y ago> Not a fan of the bare words instead of strings either, which means this cannot be naively evaluated by a Lisp. Most Lisps (all that I know of) have a way to convert symbols to strings. The hard part is resolving the ambiguity between what should be text, and what should be treated as an HTML node. Like, what if I wanted to actually write the string "(b thanks)" on a page? R7RS could probably do this with the pipe identifier syntax[0], but I think R7RS is fairly unique in its capability for identifiers (and thus symbols) with such a wide range of supported characters. [0] https://standards.scheme.org/corrected-r7rs/r7rs-Z-H-4.html#TAG:__tex2page_sec_2.1 https://standards.scheme.org/corrected-r7rs/r7rs-Z-H-4.html#...
- sph 3y agoBare words are fine until you have a variable with the same name as your bare word, so you need additional syntax to determine if you're referencing a variable, or it is just text.
- lelanthran 3y ago> Bare words are fine until you have a variable with the same name as your bare word, so you need additional syntax to determine if you're referencing a variable, or it is just text. This doesn't have variables. Or functions.
- mtreis86 3y agoThe space you embed it into might
- em-bee 3y agoyou would normally quote the whole s-expression to make sure it is interpreted as data only. '(look at this: no variables)
- Zambyte 3y agoTo expand on this, Lisps also usually have a quasiquotation system to make it easy to embed data from the host system into an s-exp. `(look ,(get-parent): some variables like ,x)
- fabiofzero 3y agoBasically Haml then.
- iLemming 3y agoHaml is not homoiconic. It's like saying "the flask is basically a teapot". Sure, they both can serve similar purposes, but categorically they are very different things. They look different, they are usually made of different materials, and they are used very differently. So, no, it's not "Basically Haml"
- brabel 3y agoThere are so many of these already: https://cliki.net/html%20generator https://cliki.net/html%20generator And still I can't find one that I like (which is why I also wrote one myself :D).
- fouc 3y agoReminds me of hyperscript like in https://mithril.js.org https://mithril.js.org m("h1", {class: "title"}, "My first app")
- efortis 3y agoYep. Also it’s worth mentioning that that’s the signature of React.createElement Here’s a simplified implementation: https://github.com/uxtely/js-utils/tree/main/react-create-element https://github.com/uxtely/js-utils/tree/main/react-create-el...
- bjoli 3y agoI your aready using a scheme you can use SXML. The tooling makes XML fun.
- mschaef 3y ago> The tooling makes XML fun. High praise indeed.
- bjoli 3y agoWell, it is because you can do most transformations using a tree fold. You get the speed and flexibility of SAX but with the nice interface of DOM parsing. Apart from scheme, I have only done XML things in Python and C#, and that was a less than stellar experience.
- junon 3y agoI actually really love this. I much prefer this over HTML syntax. Add a way to do custom tags and imports and this becomes a very, very neat static site generation tool.
- lelanthran 3y ago> Add a way to do custom tags I don't know what that means - it currently uses the first element of the list as the tagname, so you can use whatever tags you want to. > and imports and this becomes a very, very neat static site generation tool. I've toyed with the idea of adding more functionality (like imports, environment variables, conditionals, etc). Not too sure I want to go in that direction.
- junon 3y agoThe ability to create tag templates basically. You'd define tags that you could parameterize using attributes, and then within the custom tag "body" would be more tags definitions. If the parser comes across a tag it doesn't know, it emits it verbatim. If it comes across a tag that is defined as a custom tag, then it replaces the tag expression with the expanded tag template body ("expanded" here meaning the parameters are substituted). Similar to JSX, make an implicit parameter called "children" that holds all child elements, and then allow the templates to refer to attributes with a special expression (e.g. `(attr children)` or something, or maybe just `(:children)` if that doesn't break syntax rules).
- lelanthran 3y ago> The ability to create tag templates basically. You'd define tags that you could parameterize using attributes, and then within the custom tag "body" would be more tags definitions. This sounds like a good idea (could call it a `macro` but it might annoy readers who expect Lisp-type macros). Something to DRY the input a little, maybe. I don't want it veering off too much into LispLand because then it's bound to be annoying to devs who wanted a more Lisp-conformant tool.
- mschaef 3y ago
- datastack 3y agoNice, thanks for sharing. Curious what your motivation was for making it. Perhaps include a "why" at the start of the repo. As evidenced by some comments, depending on whether the goal was to write HTML with a lisp compatible syntax, vs, a more concise but simple xml alternative, it creates different expectations. Personally I'm just interested from a parser/ compiler perspective.
- lelanthran 3y ago> Curious what your motivation was for making it. Perhaps include a "why" at the start of the repo. As evidenced by some comments, depending on whether the goal was to write HTML with a lisp compatible syntax, vs, a more concise but simple xml alternative, it creates different expectations. You are quite correct, my goal appeared to be "Lisp interpreted HTML", my actual goal is "Write HTML tag trees easier".
- leumassuehtam 3y agoShameless plug of a similar idea to embed lisp into LaTeX files. https://github.com/keyehzy/lispylatex https://github.com/keyehzy/lispylatex
- deleted 3y ago[deleted]
- neilv 3y agoThe Scheme community seemed to standardize on Oleg Kiselyov's SXML format for XML and HTML: https://en.wikipedia.org/wiki/SXML https://en.wikipedia.org/wiki/SXML The initial driver for adoption was that Oleg made an excellent XML parser (SSAX) that used SXML. Then a bunch of people (including me) built new tools to work with SXML, and changed existing tools to use it. Later, I realized that SXML permitting unnecessary list nesting, which initially seemed sloppy and inefficient, actually has a very useful efficiency property, once you're using immutable lists: you can compose larger XML with a small allocation and no mutation like: (list some-huge-tree another-huge-tree) Racket-specific starting point: https://docs.racket-lang.org/sxml-intro/index.html https://docs.racket-lang.org/sxml-intro/index.html
- pharmakom 3y agoIs there a standard format for a-expressions? I want to use them but I cannot find a spec like for JSON
- jll29 3y agoIf one uses the XML tree of the XHTML as the primary internal representation instead of S-expressions, no conversion is needed at all, as XHTML can be viewed in the browser simply by associating a CSS style sheet via "rel". Nothing is a faster converter than avoiding a conversion step altogether... but that is not why I did it; I used that technique in my Ph.D. thesis back in 2007 [1,2] to avoid directories full with hundreds of auxiliary files (xml/ html.tmp/ ..), which may be confusing to manage, slow to copy, and eventually lead to the file system running out of i-nodes. EDIT: [1] https://www.sciencedirect.com/science/article/abs/pii/S0198971505000578 https://www.sciencedirect.com/science/article/abs/pii/S01989... [2] pp. 132-134 in https://era.ed.ac.uk/bitstream/handle/1842/1849/leidner-2007-phd.pdf;jsessionid=70AD79221944A3A1750AEBE893F8D757?sequence=1 https://era.ed.ac.uk/bitstream/handle/1842/1849/leidner-2007...
- pgt 3y agoHiccup syntax for Clojure uses hash maps (curly braces) for attrs, e.g. `{:style {:background "red" :margin "1em"}}` See Reagent which uses Hiccup syntax: https://reagent-project.github.io/ https://reagent-project.github.io/ (defn simple-component [] [:div [:p "I am a component!"] [:p.someclass "I have " [:strong "bold"] [:span {:style {:color "red"}} " and red "] "text."]])
- tannhaeuser 3y agoFYI in traditional SGML DTD-driven HTML parsing, "disabled" in <div disabled> represents the attribute value rather than the attribute name. The value has to be declared in an enumeration and must be different from all other enumerated values (of other elements) for the parser to infer the attribute and emit canonical markup such as <div disabled="disabled"> In HTML, it just so happens that the attribute name in what the WHATWG HTML spec calls a "Boolean attribute" is always the same as the single allowed value. However, WHATWG managed to break that in the latest spec, allowing "until-found" as additional value for the "hidden" attribute [1]. [1]: https://sgmljs.net/docs/html230116.html https://sgmljs.net/docs/html230116.html
- jgalt212 3y agoIf you use js-based hiccup instead of JSX, SPA frameworks would not need a compile step.