; 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.arcThis code is in the public domain.