html.arc has certainly been useful to me, but two things about it have sometimes been a bother: 1. Using 'tag, 'gentag etc. as the basic building blocks breaks the elegant isomorphism s-expressions have with HTML. 2. The way attributes are handled (i.e. only valid if they're in the table) requires too much micromanagement of the attributes table. I've taken a stab at an html.arc that addresses these concerns, though I've inevitably created new ones in the process. The way this works is that an 'html macro serves as a keyword for entering an environment where code is treated as HTML instead of as normal Arc: ; return values will be omitted for readability,
; since we're printing to stdout
arc> (html ((a href "http://arclanguage.org/") "Arc Forum"))
<a href="http://arclanguage.org/">Arc Forum</a>
arc> (html (html
(head
((script src "arc.js")))
(body
"hello world!")))
<html><head><script src="arc.js"/></head><body>hello world!</body></html>
Invoking the 'html macro causes the cars of included conses to be treated as tags with their cdrs as tag bodies: arc> (html (some-tag "and its body"))
<some-tag>and its body</some-tag>
If the cadr is a string (as above), it's printed as literal text between the corresponding start and end tags. If it's another cons, then the process recurses and you get nested tags: arc> (html (1st-tag (2nd-tag (3rd-tag "3rd-tag's body"))))
<1st-tag><2nd-tag><3rd-tag>3rd-tag's body</3rd-tag></2nd-tag></1st-tag>
Note that you just need the string literal "3rd-tag's body", rather than (pr "3rd-tag's body") as it would be using the original html.arc.To generate attributes, you only need to make the car a list instead of an atom; the first element will be treated as the tag name and the rest as a series of attribute pairs: arc> (html ((mytag attr1 "attr1's val" attr2 "attr2's val")))
<mytag attr1="attr1's val" attr2="attr2's val"/>
And as the above illustrates, when there is no cdr, an empty tag is generated (i.e. <mytag/>)instead of a start-tag/end-tag pair (<mytag></mytag>).That just about covers basic usage for this html.arc. Now just a few words on the more interesting (and problematic) stuff. As I said before, the 'html macro invokes a new environment where everything is considered HTML. If you want to escape into Arc and have your functions and macros back, use the 'arc keyword: arc> (html (html
(body
(arc (if (is 1 1) (pr "then it all makes sense")
(is 1 nil) (pr "then I'm awfully confused"))))))
<html><body>then it all makes sense</body></html>
How about defining macros, a la 'whitepage and 'link from the original html.arc? You could call the 'html macro within each of those macro definitions, but then they're only good as top-level tags or if you escape into arc each time you use them. To help address the problem, I provide the 'html-mac facility for defining HTML-specific macros. For example, we define 'link, (html-mac link (text (o dest text))
`((a href ,dest) ,text))
which then acts as an exception to the regular tag-processing algorithm: arc> (html (link "Arc Forum" "http://arclanguage.org"))
<a href="http://arclanguage.org">Arc Forum</a>
(If 'link hadn't been predefined with html-mac, it would have printed as: <link>Arc Forumhttp://arclanguage.org</link> )
This html.arc lacks some of the refinements of the original, but I am enjoying being free from the attributes table and able to write in something that looks more like HTML. I'm most uncertain about: this design choice of essentially creating another namespace; the kludginess surrounding having to start every page with "(html (html ..."; and needing the 'arc keyword to escape into Arc proper. Looking forward to everyone's feedback and hoping this might be useful to some of you.Complete source is posted below, and here's a good related thread: http://arclanguage.org/item?id=5565 ;; html.arc: another approach
(def cdar (xs)
(cdr (car xs)))
; string escaping utils, mostly for use in conjunction with
; js.arc and arc.js from http://arclanguage.org/item?id=11918
(let html-nestlev 0
(def html-q ()
(if (is html-nestlev 0)
(pr #\")
(pr """))
; how to warn without muddying stdout?
;(if (> html-nestlev 1)
; (warn "maximum html nest level exceeded"))
)
(def html-openq ()
(html-q)
++.html-nestlev)
(def html-closeq ()
--.html-nestlev
(html-q)))
(mac html-w/quotes body
`(do (html-openq)
,@body
(html-closeq)))
(def attrs (as)
(each a pair.as
(pr #\ car.a #\=)
(html-w/quotes
(htmlf cadr.a))))
(def start-tag (t . as)
(pr #\< t)
attrs.as
(pr #\>))
(def end-tag (t)
(pr #\< #\/ t #\>))
(def empty-tag (t . as)
(pr #\< t)
attrs.as
(pr #\/ #\>))
(def tag (t as . body)
(apply start-tag t as)
(if (acons car.body)
(apply htmlfs body)
(apply pr body))
(end-tag t))
; the html-macros are kept in this table
(= html-macs* (table))
(mac html-mac (name args . body)
`(= (html-macs* ',name) (fn ,args (htmlf ,@body))))
; 'link is just the only one I've bothered to define so far
(html-mac link (text (o dest text))
`((a href ,dest) ,text))
; program's central function
(def htmlf (s)
(if no.s nil
atom.s pr.s
(caris s 'arc) (apply eval cdr.s)
(caris s 'js) (apply jsfs cdr.s)
(html-macs* car.s) (apply (html-macs* car.s) cdr.s)
(acons car.s) (if (no cdr.s)
(apply empty-tag caar.s cdar.s)
(apply tag caar.s cdar.s cdr.s))
(if (no cdr.s)
(apply empty-tag car.s nil)
(apply tag car.s nil cdr.s))))
(def htmlfs args
(each a args
htmlf.a))
; entry point
(mac html args
`(apply htmlfs ',args))
|