238 lines
7.4 KiB
Scheme
238 lines
7.4 KiB
Scheme
(define-record-type <token-ws>
|
|
(token-ws count)
|
|
token-ws?
|
|
(count token-ws-count))
|
|
|
|
(define-record-type <token-nl>
|
|
(token-nl chars)
|
|
token-nl?
|
|
(chars token-nl-chars))
|
|
|
|
(define-record-type <token-comment>
|
|
(token-comment)
|
|
token-comment?)
|
|
|
|
(define-record-type <token-str>
|
|
(token-str content)
|
|
token-str?
|
|
(content token-str-content))
|
|
|
|
(define-record-type <token-delimchanger>
|
|
(token-delimchager open close)
|
|
token-delimchager?
|
|
(open token-delimchager-open)
|
|
(close token-delimchager-close))
|
|
|
|
(define-record-type <token-interp>
|
|
(token-interp tag escape?)
|
|
token-interp?
|
|
(tag token-interp-tag)
|
|
(escape? token-interp-escape?))
|
|
|
|
(define-record-type <token-section-open>
|
|
(token-section-open tag inverted?)
|
|
token-section-open?
|
|
(tag token-section-open-tag)
|
|
(inverted? token-section-open-inverted?))
|
|
|
|
(define-record-type <token-section-close>
|
|
(token-section-close tag)
|
|
token-section-close?
|
|
(tag token-section-close-tag))
|
|
|
|
(define-record-type <token-partial>
|
|
(token-partial tag)
|
|
token-partial?
|
|
(tag token-partial-tag))
|
|
|
|
(define (read-tokens str)
|
|
(let loop ((in (string->list str))
|
|
(ws-count 0)
|
|
(str-value '())
|
|
(open-delim '(#\{ #\{))
|
|
(close-delim '(#\} #\}))
|
|
(result/inv '()))
|
|
|
|
(define (resolve-ws/str)
|
|
(cond
|
|
;; unflushed ws and str info
|
|
((and (not (null? str-value))
|
|
(> ws-count 0))
|
|
(append (list (token-ws ws-count)
|
|
(token-str (list->string (reverse str-value))))
|
|
result/inv))
|
|
|
|
;; unflushed str info
|
|
((not (null? str-value))
|
|
(cons (token-str (list->string (reverse str-value)))
|
|
result/inv))
|
|
|
|
;; unflushed ws info
|
|
((> ws-count 0)
|
|
(cons (token-ws ws-count)
|
|
result/inv))
|
|
|
|
;; no unflushed info
|
|
(else result/inv)))
|
|
|
|
;; handle when in is null; ie final function return
|
|
(define (return)
|
|
(define final-result/inv (resolve-ws/str))
|
|
(reverse final-result/inv))
|
|
|
|
;; handle after tag read
|
|
(define (continue-after-tag in token)
|
|
(loop
|
|
in
|
|
0
|
|
'()
|
|
open-delim
|
|
close-delim
|
|
(cons token (resolve-ws/str))))
|
|
|
|
(define (process-interp in)
|
|
(define-values (in* tag)
|
|
(read-tag in close-delim))
|
|
(continue-after-tag in* (token-interp tag #t)))
|
|
|
|
(define (process-triple-mustache in)
|
|
(define-values (in* tag)
|
|
(read-tag in '(#\} #\} #\})))
|
|
(continue-after-tag in* (token-interp tag #f)))
|
|
|
|
(define (process-ampersand in)
|
|
(define-values (in* tag)
|
|
(read-tag in close-delim))
|
|
(continue-after-tag in* (token-interp tag #f)))
|
|
|
|
(define (process-inverted in)
|
|
(define-values (in* tag)
|
|
(read-tag in close-delim))
|
|
(continue-after-tag in* (token-section-open tag #t)))
|
|
|
|
(define (process-section in)
|
|
(define-values (in* tag)
|
|
(read-tag in close-delim))
|
|
(continue-after-tag in* (token-section-open tag #f)))
|
|
|
|
(define (process-close in)
|
|
(define-values (in* tag)
|
|
(read-tag in close-delim))
|
|
(continue-after-tag in* (token-section-close tag)))
|
|
|
|
(define (process-partial in)
|
|
(define-values (in* tag)
|
|
(read-tag in close-delim))
|
|
(continue-after-tag in* (token-partial tag)))
|
|
|
|
(define (process-comment in)
|
|
(let loop* ((in in))
|
|
(cond
|
|
((null? in) (error "Unexpected EOF"))
|
|
((match-follows in close-delim) => (lambda (in*)
|
|
(continue-after-tag in* (token-comment))))
|
|
(else (loop* (cdr in))))))
|
|
|
|
(define (process-delim-change in)
|
|
(let*-values (((in new-open) (read-tag in #f))
|
|
((in new-close) (read-tag in (cons #\= close-delim))))
|
|
(loop in
|
|
0
|
|
'()
|
|
(string->list new-open)
|
|
(string->list new-close)
|
|
(cons (token-delimchager new-open new-close)
|
|
(resolve-ws/str)))))
|
|
|
|
(define (process-open-delim in*)
|
|
(cond
|
|
((match-follows in* '(#\&)) => process-ampersand)
|
|
((match-follows in* '(#\^)) => process-inverted)
|
|
((match-follows in* '(#\#)) => process-section)
|
|
((match-follows in* '(#\/)) => process-close)
|
|
((match-follows in* '(#\>)) => process-partial)
|
|
((match-follows in* '(#\=)) => process-delim-change)
|
|
((match-follows in* '(#\!)) => process-comment)
|
|
(else (process-interp in*))))
|
|
|
|
(define (process-space in*)
|
|
(loop in*
|
|
(+ 1 ws-count)
|
|
str-value
|
|
open-delim
|
|
close-delim
|
|
result/inv))
|
|
|
|
(define (process-eol in* chars)
|
|
(loop in*
|
|
0
|
|
'()
|
|
open-delim
|
|
close-delim
|
|
(cons (token-nl chars)
|
|
(resolve-ws/str))))
|
|
|
|
(define (process-nl in*)
|
|
(process-eol in* '(#\newline)))
|
|
|
|
(define (process-crnl in*)
|
|
(process-eol in* '(#\return #\newline)))
|
|
|
|
(define (process-char)
|
|
(loop (cdr in)
|
|
0
|
|
(append (list (car in))
|
|
(make-list ws-count #\space)
|
|
str-value)
|
|
open-delim
|
|
close-delim
|
|
result/inv))
|
|
|
|
;; loop handler
|
|
(cond
|
|
((null? in) (return))
|
|
((match-follows in '(#\{ #\{ #\{)) => process-triple-mustache)
|
|
((match-follows in open-delim) => process-open-delim)
|
|
((match-follows in '(#\space)) => process-space)
|
|
((match-follows in '(#\newline)) => process-nl)
|
|
((match-follows in '(#\return #\newline)) => process-crnl)
|
|
(else (process-char)))))
|
|
|
|
(define (match-follows in chars)
|
|
(let loop ((in* in)
|
|
(chars* chars))
|
|
(cond
|
|
((null? chars*) in*)
|
|
((null? in*) #f)
|
|
((char=? (car in*) (car chars*))
|
|
(loop (cdr in*)
|
|
(cdr chars*)))
|
|
(else #f))))
|
|
|
|
(define (skip-spaces in)
|
|
(cond
|
|
((null? in) '())
|
|
((char=? (car in) #\space) (skip-spaces (cdr in)))
|
|
(else in)))
|
|
|
|
(define (read-tag in close-delim)
|
|
(define-values
|
|
(tag in*)
|
|
(let loop ((in (skip-spaces in))
|
|
(result '()))
|
|
(define (return)
|
|
(values (list->string (reverse result))
|
|
in))
|
|
(cond
|
|
((null? in) (error "Unexpected EOF"))
|
|
((char=? (car in) #\space) (return))
|
|
((and close-delim (match-follows in close-delim))
|
|
(return))
|
|
(else (loop (cdr in)
|
|
(cons (car in) result))))))
|
|
(cond
|
|
((not close-delim) (values in* tag))
|
|
((match-follows (skip-spaces in*) close-delim) => (lambda (in**)
|
|
(values in** tag)))
|
|
(else (error "Bad tag"))))
|