;; @module expand-string.lsp ;; @author Ralph Ronnquist, Real Thing Entertainment Pty. Ltd. ;; @location http://www.realthing.com.au/files/newlisp/expand-string.lsp ;; @version 1.1 ;; @description Inclusion module providing string templating using expansion. ; ;; This is an inclusion module that provides an ;; function to process a string template and replace key tokens as ;; declared in a rules list of token-to-replacement associations with ;; their associated values. It offers a similar function to ;; but for strings, but uses string pattern match (without ;; tokenization) to determine the replacement points, and evaluates ;; the value parts to make the replacements. ;; ;; @syntax (expand-string ) ; ;; Processes the given text for the occurrences of the rule keys, and ;; replaces these with the values obtained by evaluating the ;; associated value expressions. The result is the new string with ;; replacements. Note that a value expression may affect variable ;; , which is the rest of the input following the expanding key, ;; to optionally consume additional text in the replacement. See ;; function <.expand-map> for an example. ;; ;; @syntax (expand-file file rules) ;; Reads the file and expand it using with the given ;; rules. ;; ;; @syntax (.expand-eval ) ; ;; This function is intended as expansion value function for an ;; rule, to implement template expression ;; evaluation. The parameter tells the context for symbol ;; creations. The optional parameter tells the end of the ;; replacement fragment. This function extracts the text fragment ;; until the nearest text, then evaluates this with ;; , makes the result a string, and uses that as value to ;; replace the whole block. See below how a ;; rule using this function may look. ;; ;; @syntax (.expand-map ) ; ;; This function is intended as expansion value function for an ;; rule, to implement template fragment ;; repetition. The optional parameter tells the context for ;; symbol creations. The optional parameter tells the end of the ;; fragment portion, which is "</MAP>" by default. The function ;; pulls two s-expression from the template using . The ;; first is a list of keys, and the second a list of binding lists for ;; those keys. The rest of the fragment is then expanded recursively, ;; repeatedly, with the keys having their subsequent bindings, and the ;; block is replaced by the concatenation of these results. See ;; below how a rule using this function may ;; look. ;; ;; @syntax default-expand-rules ;; This constant holds a few default rules for using repetition end ;; expression evaluation. Currently set to the following: ;;
;; (constant 'default-expand-rules
;;           '(("<MAP1>" (.expand-map MAIN "</MAP1>"))
;;             ("<MAP2>" (.expand-map MAIN "</MAP2>"))
;;             ("<MAP3>" (.expand-map MAIN "</MAP3>"))
;;             ("<MAP>" (.expand-map MAIN "</MAP>"))
;;             ("<EVAL>" (.expand-eval MAIN "</EVAL>"))))
;; 
These default rules obviously favours HTML templates. ;;
§

;; Example: The following is an illustration of ;; using <.expand-map>: ;;
(expand-string
;;          "<MAP>(A B) '((1 2) (3 4)) A B B A</MAP>"
;;          '(("<MAP>" (.expand-map)) ))
;; 
;; The example results in the string " 1 2 2 1 3 4 4 3". ;; ;; Note that the binding lists expression is evaluated in the given ;; context, or MAIN, if nil is given. Thus, the rule above is ;; equivalent with the following: (.expand-map MAIN "</MAP>") ;; ;; Note also that the fragment blocks cannot be nested. To achieve ;; nested repetition, use several tag pairs, as in the following rule set: ;;
 '(("<MAP1>" (.expand-map nil "</MAP1>"))
;;   ("<MAP2>" (.expand-map nil "</MAP2>"))
;;   ("<MAP3>" (.expand-map nil "</MAP3>")) )
;; In that case, the outer expansion keys may be used in the inner ;; repetition although they are not actually bound to the values. ;; ;; Example: ;;
(expand-string
;;          {<EVAL>(first (exec "uname -mrs"))</EVAL>}
;;          default-expand-rules )
;; This example results in the machine details as reported by the ;; program with the <-mrs> command line argument. ;; ;; Example: This example illustrates HTML rendering, with a ;; template file that includes certain keys for expansion. In this ;; case I have a list if paragraps as value of variable , and ;; want them inserted nicely into an HTML page. Note that the spaces ;; following the two s-expressions in the .. construct are ;; compulsory, and they get consumed by the function. ; ;;
 @PAGEDOCTYPE@
;; <html><head><title>@TITLE@</title></head>
;; <body><h1>@TITLE@</h1>
;; <MAP>(text) texts <p>text</p></MAP>
;; </body></html>
; ;; This template would be used in a context that provides suitable ;; expansion rules for the "@PAGEDOCTYPE@" and "@TITLE@" keys, as well ;; as the default "<MAP>" expansion rule. ############################################################ (define (rule-key rule) (replace "[\\?*.()]" (first rule) (string "\\" $it) 0)) (define (expand-string txt (rules default-expand-rules)) (if (null? rules) txt (let ((pat (string "(" (join (map string (map rule-key rules)) "|") ")")) (out "") (i 0)) (while (setf i (find pat txt 0)) (extend out (0 i txt)) (setf txt ((+ i (length $1)) txt)) (extend out (string (eval (lookup $1 rules))))) (extend out txt)))) (define (expand-file file (rules default-expand-rules)) (expand-string (read-file file) rules)) (define (.expand-map ctx (end "")) ; uses txt rules (let ((A (map term (read-expr txt (or ctx MAIN) nil 0))) (dlist (read-expr txt (or ctx MAIN) nil $count)) (frag ($count (- (find end txt nil $count) $count) txt)) (out "")) (setf txt ((+ $count (length frag) (length end)) txt)) (dolist (d (eval dlist)) (extend out (expand-string frag (extend (map list A d) rules)))) out)) (define (.expand-eval ctx (end "")) ; uses txt rules (let ((frag (0 (find end txt nil 0) txt))) (setf txt ((+ (length frag) (length end)) txt)) (string (eval-string frag)))) (constant 'default-expand-rules '(("" (.expand-map MAIN "")) ("" (.expand-map MAIN "")) ("" (.expand-map MAIN "")) ("" (.expand-map MAIN "")) ("" (.expand-eval MAIN "")) ("")))) (global 'expand-string 'expand-file '.expand-map 'default-expand-rules) "expand-string.lsp"