; 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 (name closing port) (list name (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 #\«) (slurp-template-field 'guillemet #\» port) (is c #\⌊) (slurp-template-field 'floor #\⌋ port) c)))))) (ac-scheme (current-readtable (make-readtable (current-readtable) #\‡ 'non-terminating-macro (lambda (ch port src line col pos) (_slurp-template port))))) (mac lex1 (template) `(tostring ,@(map (fn (x) (if (isa x 'string) `(pr ,x) (and (acons x) (is (car x) 'floor)) `(pr ,(read (cadr x))))) (cdr template)))) (mac lex2 (converter template) (w/uniq gc `(tostring:let ,gc ,converter ,@(map (fn (x) (if (isa x 'string) `(pr ,x) (and (acons x) (is (car x) 'guillemet)) `(pr (,gc ,(read (cadr x)))) (and (acons x) (is (car x) 'floor)) `(pr ,(read (cadr x))))) (cdr template))))) (mac lex args (if (is (len args) 1) `(lex1 ,@args) (is (len args) 2) `(lex2 ,@args) (err "lex takes one or two arguments")))
I haven’t really figured out how I want templates to work yet... often I want to be inserting Arc expressions, something like
(let name "Fred" ‡Hello «name»‡)
but other times the data I want to insert is already in a table, and it’s a pain to get the data from the table into lexical variables just so I can insert them into the template, so I want something like my previous template2 code.
To give me something to play with, with this hack the reader expands ‡...‡ into a (template ...) form, like this:
arc> (read "‡one «two» ⌊three⌋ four‡") (template "one " (guillemet "two") " " (floor "three") " four")
Like how the reader expands a comma into an unquote form to be used in a quasiquotation expression, but “unquote
” itself is not a function or macro since it is the quasiquotation expander that goes in and finds the unquote forms:
arc> (read ",x") (unquote x)
arc> (unquote x)reference to undefined identifier: _unquote
“template
” isn’t defined to be anything,
arc> ‡hello‡reference to undefined identifier: _template
but it serves as a handle for other macros to work on. For example, lex
causes ⌊...⌋ to be treated as an Arc expression:
arc> (let name "Fred" (lex ‡Hello ⌊name⌋‡)) "Hello Fred"
lex
takes an optional argument, a conversion function to apply to «...» forms:
arc> (let name "Fred" (lex strperl ‡hello(«name»);‡)) "hello('Fred');"
I don’t expect this to be my final version, but now I have some flexibility to play around with.
Using the hackinator:
$ hack \ ycombinator.com/arc/arc3.1.tar \ awwx.ws/ac0.patch \ awwx.ws/ac1.arc \ awwx.ws/xloop0.arc \ awwx.ws/span0.arc \ awwx.ws/template3.arc
This code is in the public domain.