297 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Scheme
		
	
	
	
			
		
		
	
	
			297 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Scheme
		
	
	
	
(define-record-type <interp>
 | 
						|
                    (interp ref escape?)
 | 
						|
                    interp?
 | 
						|
                    (ref interp-ref)
 | 
						|
                    (escape? interp-escape?) ;; should html be escaped
 | 
						|
                    )
 | 
						|
 | 
						|
(define-record-type <section>
 | 
						|
                    (section ref invert? content raw-content)
 | 
						|
                    section?
 | 
						|
                    (ref section-ref)
 | 
						|
                    (invert? section-invert?) ;; normal section if false, {{^ section if true
 | 
						|
                    (content section-content) ;; compiled inner content
 | 
						|
                    (raw-content section-raw-content) ;; uncompiled inner content as a string; used for lambdas
 | 
						|
                    )
 | 
						|
 | 
						|
(define-record-type <partial>
 | 
						|
                    (partial name indent)
 | 
						|
                    partial?
 | 
						|
                    (name partial-name)
 | 
						|
                    (indent partial-indent))
 | 
						|
 | 
						|
(define-record-type <newline>
 | 
						|
                    (new-line content)
 | 
						|
                    new-line?
 | 
						|
                    (content new-line-content))
 | 
						|
 | 
						|
(define (parse tokens)
 | 
						|
  (let* ((tokens (replace-standalone tokens))
 | 
						|
         (tokens (remove-non-visible tokens))
 | 
						|
         (tokens (convert-string-tokens tokens))
 | 
						|
         (tokens (parse-interp+sections tokens)))
 | 
						|
    tokens))
 | 
						|
 | 
						|
(define (tpl->string tokens)
 | 
						|
  (define (->string item out)
 | 
						|
    (cond
 | 
						|
      ((string? item) (write-string item out))
 | 
						|
      ((new-line? item) (write-string (new-line-content item) out))
 | 
						|
      ((section? item)
 | 
						|
       (let ((tagname (list->tagname (section-ref item))))
 | 
						|
         (write-string (if (section-invert? item) "{{^" "{{#") out)
 | 
						|
         (write-string tagname out)
 | 
						|
         (write-string "}}" out)
 | 
						|
         (for-each
 | 
						|
           (lambda (item*)
 | 
						|
             (->string item* out))
 | 
						|
           (section-content item))
 | 
						|
         (write-string "{{/" out)
 | 
						|
         (write-string tagname out)
 | 
						|
         (write-string "}}" out)))
 | 
						|
      ((interp? item)
 | 
						|
       (let ((tagname (list->tagname (interp-ref item))))
 | 
						|
        (write-string (if (interp-escape? item) "{{" "{{&") out)
 | 
						|
        (write-string tagname out)
 | 
						|
        (write-string "}}" out)))))
 | 
						|
  (define out (open-output-string))
 | 
						|
  (for-each
 | 
						|
    (lambda (item) (->string item out))
 | 
						|
    tokens)
 | 
						|
  (get-output-string out))
 | 
						|
 | 
						|
;;TODO remove this
 | 
						|
(define (debug-tokens tokens)
 | 
						|
  (for-each
 | 
						|
    (lambda (t)
 | 
						|
      (cond
 | 
						|
        ((token-str? t) (display (string-append "#<<token-str> " (token-str-content t) "> ")))
 | 
						|
        ((token-nl? t) (display "#<<token-nl>> "))
 | 
						|
        ((token-section-open? t) (display (string-append "#<<token-open> " (token-section-open-tag t) "> ")))
 | 
						|
        ((token-section-close? t) (display "#<<token-close>> "))
 | 
						|
        ((token-ws? t) (display (string-append "#<<token-ws> " (number->string (token-ws-count t)) "> ")))
 | 
						|
        ((token-interp? t) (display (string-append "#<<token-interp> " (token-interp-tag t) "> ")))
 | 
						|
        (else (display t)))) 
 | 
						|
    tokens
 | 
						|
    )
 | 
						|
  
 | 
						|
  )
 | 
						|
 | 
						|
(define (standalone/remove? token)
 | 
						|
  (or (token-comment? token)
 | 
						|
      (token-delimchager? token)))
 | 
						|
 | 
						|
(define (standalone/trim? token)
 | 
						|
  (or (token-section-open? token)
 | 
						|
      (token-section-close? token)))
 | 
						|
 | 
						|
(define (replace-standalone tokens)
 | 
						|
  (let loop ((tokens tokens)
 | 
						|
             (result/inv '())
 | 
						|
             (first #t))
 | 
						|
    
 | 
						|
    (cond
 | 
						|
      ((null? tokens) (reverse result/inv))
 | 
						|
      
 | 
						|
      ((and first
 | 
						|
            (or (match-follows tokens standalone/remove? token-ws? token-nl?)
 | 
						|
                (match-follows tokens standalone/remove? token-nl?)
 | 
						|
                (match-follows tokens token-ws? standalone/remove? token-ws? token-nl?)
 | 
						|
                (match-follows tokens token-ws? standalone/remove? token-nl?))) =>
 | 
						|
       (lambda (tokens*)
 | 
						|
         (loop tokens*
 | 
						|
               result/inv
 | 
						|
               #t)))
 | 
						|
      
 | 
						|
      ((and first
 | 
						|
            (or (match-follows tokens token-ws? standalone/remove? token-ws? eof-object?)
 | 
						|
                (match-follows tokens token-ws? standalone/remove? eof-object?)
 | 
						|
                (match-follows tokens standalone/remove? token-ws? eof-object?)
 | 
						|
                (match-follows tokens standalone/remove? eof-object?))) =>
 | 
						|
       (lambda (tokens*)
 | 
						|
         (loop '()
 | 
						|
               result/inv
 | 
						|
               #t)))
 | 
						|
      
 | 
						|
      ((and first
 | 
						|
            (or (match-follows tokens token-ws? standalone/trim? token-ws? token-nl?)
 | 
						|
                (match-follows tokens token-ws? standalone/trim? token-nl?)
 | 
						|
                (match-follows tokens token-ws? standalone/trim? token-ws? eof-object?)
 | 
						|
                (match-follows tokens token-ws? standalone/trim? eof-object?))) => 
 | 
						|
       (lambda (tokens*)
 | 
						|
         (loop tokens*
 | 
						|
               (append (list (cadr tokens))
 | 
						|
                       result/inv)
 | 
						|
               #t)))
 | 
						|
      
 | 
						|
      ((and first
 | 
						|
            (or (match-follows tokens standalone/trim? token-ws? token-nl?)
 | 
						|
                (match-follows tokens standalone/trim? token-nl?)
 | 
						|
                (match-follows tokens standalone/trim? token-ws? eof-object?)
 | 
						|
                (match-follows tokens standalone/trim? eof-object?))) => 
 | 
						|
       (lambda (tokens*)
 | 
						|
         (loop tokens*
 | 
						|
               (append (list (car tokens))
 | 
						|
                       result/inv)
 | 
						|
               #t)))
 | 
						|
      
 | 
						|
      ((and first
 | 
						|
            (or (match-follows tokens token-ws? token-partial? token-ws? token-nl?)
 | 
						|
                (match-follows tokens token-ws? token-partial? token-nl?)
 | 
						|
                (match-follows tokens token-ws? token-partial? token-ws? eof-object?)
 | 
						|
                (match-follows tokens token-ws? token-partial? eof-object?))) =>
 | 
						|
       (lambda (tokens*)
 | 
						|
         (loop tokens*
 | 
						|
               (append (list (partial (token-partial-tag (cadr tokens))
 | 
						|
                                      (token-ws-count (car tokens))))
 | 
						|
                       result/inv)
 | 
						|
               #t)))
 | 
						|
      
 | 
						|
      ((and first
 | 
						|
            (or (match-follows tokens token-partial? token-ws? token-nl?)
 | 
						|
                (match-follows tokens token-partial? token-nl?)
 | 
						|
                (match-follows tokens token-partial? token-ws? eof-object?)
 | 
						|
                (match-follows tokens token-partial? eof-object?))) =>
 | 
						|
       (lambda (tokens*)
 | 
						|
         (loop tokens*
 | 
						|
               (append (list (partial (token-partial-tag (car tokens))
 | 
						|
                                      0))
 | 
						|
                       result/inv)
 | 
						|
               #t)))
 | 
						|
      
 | 
						|
      ((match-follows tokens token-partial?) => (lambda (tokens*)
 | 
						|
                                                  (loop tokens*
 | 
						|
                                                        (cons (partial (token-partial-tag (car tokens))
 | 
						|
                                                                       0)
 | 
						|
                                                              result/inv)
 | 
						|
                                                        #f)))
 | 
						|
      
 | 
						|
      (else (loop (cdr tokens)
 | 
						|
                  (cons (car tokens) result/inv)
 | 
						|
                  (token-nl? (car tokens)))))))
 | 
						|
 | 
						|
(define (convert-string-tokens tokens)
 | 
						|
  (let loop ((tokens tokens)
 | 
						|
             (out #f)
 | 
						|
             (result/inv '()))
 | 
						|
    (cond
 | 
						|
      ((null? tokens)
 | 
						|
       (let ((result-final/inv (if out
 | 
						|
                                   (cons (get-output-string out)
 | 
						|
                                         result/inv)
 | 
						|
                                   result/inv)))
 | 
						|
         (reverse result-final/inv)))
 | 
						|
      ((or (token-str? (car tokens))
 | 
						|
           (token-ws? (car tokens)))
 | 
						|
       (let* ((token (car tokens))
 | 
						|
              (out* (if out 
 | 
						|
                        out
 | 
						|
                        (open-output-string)))
 | 
						|
              (str (if (token-str? token)
 | 
						|
                       (token-str-content token)
 | 
						|
                       (make-string (token-ws-count token) #\space))))
 | 
						|
         (write-string str out*)
 | 
						|
         (loop (cdr tokens)
 | 
						|
               out*
 | 
						|
               result/inv)))
 | 
						|
      (else (let* ((token (car tokens))
 | 
						|
                   (value (cond
 | 
						|
                            ((token-nl? token) (new-line (list->string (token-nl-chars token))))
 | 
						|
                            (else token)))
 | 
						|
                   (new-result/inv (if out
 | 
						|
                                       (cons (get-output-string out)
 | 
						|
                                             result/inv)
 | 
						|
                                       result/inv)))
 | 
						|
              (loop (cdr tokens)
 | 
						|
                    #f
 | 
						|
                    (cons value new-result/inv)))))))
 | 
						|
 | 
						|
(define (parse-interp+sections tokens)
 | 
						|
  (define (parse-interp+sections* tokens expected-close-tag)
 | 
						|
    (let loop ((tokens tokens)
 | 
						|
               (result/inv '()))
 | 
						|
      (cond
 | 
						|
        ((null? tokens)
 | 
						|
         (if expected-close-tag
 | 
						|
             (error "Unexpected eof")
 | 
						|
             (values '() (reverse result/inv))))
 | 
						|
        ((token-section-close? (car tokens))
 | 
						|
         (if (equal? expected-close-tag (token-section-close-tag (car tokens)))
 | 
						|
             (values (cdr tokens) (reverse result/inv))
 | 
						|
             (error "Closing token mismatch")))
 | 
						|
        ((token-section-open? (car tokens))
 | 
						|
         (let* ((token (car tokens))
 | 
						|
                (tag (token-section-open-tag token))
 | 
						|
                (ref (tagname->list tag)))
 | 
						|
          (define-values (tokens* result*)
 | 
						|
                         (parse-interp+sections* (cdr tokens)
 | 
						|
                                                 tag))
 | 
						|
          (define value (section ref 
 | 
						|
                                 (token-section-open-inverted? token)
 | 
						|
                                 result*
 | 
						|
                                 #f))
 | 
						|
          (loop tokens*
 | 
						|
                (cons value result/inv))))
 | 
						|
        ((token-interp? (car tokens))
 | 
						|
         (let* ((token (car tokens))
 | 
						|
                (tag (token-interp-tag token))
 | 
						|
                (ref (tagname->list tag)))
 | 
						|
           (define value (interp ref (token-interp-escape? token)))
 | 
						|
           (loop (cdr tokens)
 | 
						|
                 (cons value result/inv))))
 | 
						|
        (else (loop (cdr tokens)
 | 
						|
                    (cons (car tokens)
 | 
						|
                          result/inv))))))
 | 
						|
  (define-values (tokens* result)
 | 
						|
                 (parse-interp+sections* tokens #f))
 | 
						|
  result)
 | 
						|
 | 
						|
(define (remove-non-visible tokens)
 | 
						|
  (filter
 | 
						|
    (lambda (token)
 | 
						|
      (not (or (token-comment? token)
 | 
						|
               (token-delimchager? token))))
 | 
						|
    tokens))
 | 
						|
 | 
						|
(define (match-follows in . preds)
 | 
						|
  (let loop ((in* in)
 | 
						|
             (preds* preds))
 | 
						|
    (cond
 | 
						|
      ((null? preds*) in*)
 | 
						|
      ((null? in*) (and (null? (cdr preds*))
 | 
						|
                        (eq? (car preds*) eof-object?)
 | 
						|
                        '()))
 | 
						|
      (((car preds*) (car in*)) 
 | 
						|
       (loop (cdr in*)
 | 
						|
             (cdr preds*)))
 | 
						|
      (else #f))))
 | 
						|
 | 
						|
(define (tagname->list str)
 | 
						|
  (define (prepend-part parts part)
 | 
						|
    (when (null? part)
 | 
						|
      (error "Trailing period in tag name"))
 | 
						|
    (cons (list->string (reverse part))
 | 
						|
          parts))
 | 
						|
  (if (equal? "." str)
 | 
						|
      '(".")
 | 
						|
      (let loop ((in (string->list str))
 | 
						|
                 (parts '())
 | 
						|
                 (part '()))
 | 
						|
        (cond
 | 
						|
          ((null? in)
 | 
						|
           (reverse (prepend-part parts part)))
 | 
						|
          ((char=? #\. (car in))
 | 
						|
           (loop (cdr in)
 | 
						|
                 (prepend-part parts part)
 | 
						|
                 '()))
 | 
						|
          (else (loop (cdr in)
 | 
						|
                      parts
 | 
						|
                      (cons (car in) part)))))))
 | 
						|
 | 
						|
(define (list->tagname lst)
 | 
						|
  (apply string-append
 | 
						|
         (cdr (apply append
 | 
						|
                     (map
 | 
						|
                       (lambda (el) (list "." el))
 | 
						|
                       lst)))))
 |