string-template.ss
(module string-template mzscheme
  ;; A implementation of a template engine.  At the moment,
  ;; very minimal, as this just supports interpolation, with
  ;; and without separators.
  ;;
  ;; Danny Yoo (dyoo@cs.wpi.edu / dyoo@hkn.eecs.berkeley.edu)
  ;;
  ;; The intent is to rip off, er..., adapt stuff from
  ;; Terrence Parr's StringTemplate design.  Not sure how much
  ;; of it I'll be implementing.
  ;;
  ;; TODO: handle attribute expressions
  ;; TODO: handle template groups
  ;; TODO: handle iteration
  ;; TODO: error traps
  
  (require (lib "plt-match.ss")
           "parsing.ss"
           (prefix s: "structs.ss")
           (lib "list.ss"))
  
  
  (provide (rename -make-template make-template)
           struct:template template
           ;; put other structural stuff here?
           (struct exn:template:missing-attribute (attribute-name))
           
           
           template->string
           display-template)
  
  
  (define-struct template (text document))
  
  (define-struct (exn:template:missing-attribute exn:fail) (attribute-name))
  
  ;; make-template: string -> template
  ;; Constructs a template, parsing tmpl-string in the process.
  (define (-make-template tmpl-string)
    (let ([inp (open-input-string tmpl-string)])
      (port-count-lines! inp)
      (make-template tmpl-string (parse inp))))
  
  
  ;; template->string: template hash-table -> string
  ;; Instantiates a template as a string, using a-hash-table
  ;; to find name/value bindings.  The keys of the hash
  ;; table are strings.  (Unstable: this might change since symbols
  ;; work better as hash keys.)
  (define (template->string a-template a-hash-table)
    (let ([outp (open-output-string)])
      (display-template a-template a-hash-table outp)
      (get-output-string outp)))
  
  ;; display-template: template hash-table output-port -> void
  ;; Instantiates a template, writing output to the out-p output port.
  (define (display-template a-template a-hash-table out-p)
    (display-template-document (template-document a-template) a-hash-table out-p))
  
  
  
  
  
  ;                                                                         
  ;                                                                         
  ;                                                                         
  ;    ;;;;;                                                        ;;;;    
  ;      ;                ;                                            ;    
  ;      ;                ;                                            ;    
  ;      ;     ; ;;;;   ;;;;;;     ;;;      ; ;;;  ; ;;;;     ;;;;     ;    
  ;      ;     ;;   ;;    ;       ;   ;     ;;   ; ;;   ;;   ;   ;;    ;    
  ;      ;     ;     ;    ;      ;     ;    ;      ;     ;        ;    ;    
  ;      ;     ;     ;    ;      ;     ;    ;      ;     ;   ;;;;;;    ;    
  ;      ;     ;     ;    ;      ;;;;;;;    ;      ;     ;  ;;    ;    ;    
  ;      ;     ;     ;    ;      ;          ;      ;     ;  ;     ;    ;    
  ;      ;     ;     ;    ;       ;    ;    ;      ;     ;  ;    ;;    ;    
  ;    ;;;;;   ;     ;     ;;;     ;;;;     ;      ;     ;   ;;;; ;     ;;; 
  ;                                                                         
  ;                                                                         
  ;                                                                         
  ;                                                                        ;  

  ;; lookup: string hash-table -> any
  (define (lookup id a-hash-table)
    (hash-table-get
     a-hash-table id
     (lambda ()
       (raise (make-exn:template:missing-attribute
               (format "missing attribute ~s" id)
               (current-continuation-marks)
               id)))))

  ;; display-template-document: document hash-table output-port -> void
  (define (display-template-document a-document a-hash-table out-p)
    (match a-document
      [(struct s:document (elts))
       (for-each (lambda (elt)
                   (display-element elt a-hash-table out-p))
                 elts)]))
  
  
  ;; display-element: element hash-table output-port -> void
  (define (display-element an-element a-hash-table out-p)
    (match an-element
      [(struct s:normal-text (t))
       (display t out-p)]
      
      [(struct s:variable-reference (id))
       (display (lookup id a-hash-table) out-p)]
      
      [(struct s:variable-reference/separator (id sep))
       (let ([elts (lookup id a-hash-table)])
         (cond
           [(empty? elts) (void)]
           [else
            (display (first elts) out-p)
            (for-each (lambda (elt)
                        (display-element sep a-hash-table out-p)
                        (display elt out-p))
                      (rest elts))]))])))