readably embed other languages in Arc

(implicit guillemet pr)

; turn adjacent characters into a string

(def coalesce-chars (lst)
  (xloop (lst lst a nil)
    (if (no lst)
         (rev a)
         (let (chars rest) (span [is (type _) 'char] lst)
           (if chars
                (next rest (cons (coerce chars 'string) a))
                (next cdr.lst (cons car.lst a)))))))

(def slurp-template-field (closing port)
  (read:string:accum a
    (whiler c (readc port) [or (no _) (is _ closing)]
      (a c))))

(def slurp-template (port (o accum))
  (cons 'template
        (coalesce-chars:accum a
          (whiler c (readc port) [or (no _) (is _ #\”)]
            (a (if (is c #\«)
                    (list 'guillemet (slurp-template-field #\» port))
                   (is c #\⌊)
                    (list 'pr (slurp-template-field #\⌋ port))

(extend-readtable #\“ slurp-template)

(mac template xs
  `(do ,@(map [if (isa _ 'string)
                   `(pr ,_)

My goal for this template hack is twofold: to be able to readably embed other languages in Arc, and to not get in the way of evolving towards generating the other languages’ code via macros, as Arc’s html.arc does for HTML.

For the readable part, I cheat by using Unicode. Embedding one ASCII language in another ASCII language requires escaping characters, which often results in an unreadable mishmash. As the languages I want to embed aren’t for the most part using Unicode yet... ha! I use Unicode curly quotes and guillemet characters.

(Which will work fine until someday I’ll need to embed some code which itself contains curly quotes or guillemets, and then I’ll have to do something else).

The “...” construct is a shorthand for (pr "..."), but with printing the contents literally without any escaping.

arc> (do (pr "He said, \"hi\"") (prn) nil)
He said, "hi"
arc> (do “He said, "hi"” (prn) nil)
He said, "hi"

Within “...”, a guillemet quote «foo» causes foo to be spliced in as Arc code and printed.

arc> (do “The result is «(+ 3 4)»” (prn) nil)
The result is 7

The «...» expression is compiled within the lexical scope of the expression, and so has access to local variables.

arc> (let a 3
       (do “The result is «(+ a 4)»” (prn) nil))
The result is 7

While the Arc reader will only read one expression within the «...», I use the delimited form to prevent the reader from reading from pulling more into the expression than I want. I could hypothetically use a single character to read the following Arc form:

“The result is →(+ a 4)”

but then

“Hello →name!”

would print the value of the variable “name!”, instead of the variable “name” followed by an exclamation mark. By using «...» I can choose “Hello «name»!” or “Hello «name!»” without ambiguity.

The action of the guillemet is determined by the guillemet implicit variable, which defaults to a simple pr. Using w/guillemet you can modify the action:

arc> (w/guillemet [pr "*" _ "*"]
       (do “The result is «(+ 3 4)»” (prn) nil))
The result is *7*


This hack depends on arc3.1, implicit2, xloop0, span0, and extend-readtable0.

Get this hack

Using the hackinator:

$ hack \ \ \ \ \ \ \ \ \ \ \ \


Same as Arc.

Contact me

Twitter: awwx
Email: andrew.wilcox [at]