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)))))
 |