Created
September 16, 2021 17:25
-
-
Save emctague/d7c6b37a9e9d9fb863fe0ba0363adcf5 to your computer and use it in GitHub Desktop.
LHTML - Convert S-Expressions to HTML
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; LHTML | |
; ----- | |
; | |
; Generate HTML code from a sequence of lisp s-expressions. | |
; This operates on stdin and prints to stdout. | |
; | |
; (For clisp.) | |
; | |
; Please for the love of all that is good do not use this in production. | |
; The code is barely readable! | |
; | |
; Example input: | |
; | |
; (div :id "my-div" :style "font-family: sans-serif;" | |
; (img :src "icon.png" /) | |
; (p (b "This") "is content that ends up inside the element!")) | |
; | |
; | |
; Example output: | |
; | |
; <div id="my-div" style="font-family: sans-serif;"> | |
; <img src="icon.png"/> | |
; <p> | |
; <b> | |
; This | |
; </b> | |
; is content that ends up inside the element! | |
; </p> | |
; </div> | |
(defun ind (indent) | |
"Print (indent * 4) spaces to standard output" | |
(format t "~va" (* indent 4) "")) | |
; Syntax: | |
; (tag-name [:attribute-name "attribute-value"]* [[child-node]* | [/]]) | |
; - :attribute-name "attribute-value" pairs become attributes on the tag | |
; - child-node values, either strings with text content or nested elements, | |
; are inserted into this element | |
; - '/' token, if provided instead of children, makes this element have no | |
; children or closing tagt | |
(defun decode-element (seq) | |
"Parse an element s-expression into (flag for if '/' is present, list of attribute k/v pairs, list of child nodes)" | |
(let ((prevkey nil) (slashfound nil)) | |
(loop for i in seq | |
if prevkey collect (list prevkey i) into keywords and do (setq prevkey nil) | |
else if (eql i '/) do (setq slashfound t) | |
else if (keywordp i) do (setq prevkey i) | |
else collect i into children | |
finally (return (list slashfound keywords children))))) | |
(defun element (indent elem) | |
"Convert an element s-expression to HTML. See decode-element for formatting." | |
(destructuring-bind (slashfound keywords children) (decode-element (cdr elem)) | |
(format t "<~(~a~)~{ ~{~(~a~)=\"~a\"~}~}~:[~;/~]>~%" (car elem) keywords slashfound) | |
(if (not slashfound) (progn | |
(loop for ch in children do (node (+ indent 1) ch)) | |
(ind indent) | |
(format t "</~(~a~)>~%" (car elem)))))) | |
(defun node (indent elem) | |
"Parse an s-expression into HTML content - either a string or parenthesized expression." | |
(ind indent) | |
(cond | |
((stringp elem) (format t "~a~%" elem)) | |
((listp elem) (element indent elem)) | |
(t (format t "Error: what is ~a?~%" elem)))) | |
; Begin Read Loop | |
(loop for line = (read t NIL) | |
while line do | |
(node 0 line)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment