1(module (readit parser)2 (make-meta meta-state meta-key meta-title parse-fields3 parse-readit readit-ref? readit-set? parse-indent parse-note parse-notes)4 (import scheme (chicken base) comparse srfi-1 srfi-14)56 (define-record-type metadata7 (make-meta state key title)8 metadata?9 (state meta-state)10 (key meta-key)11 (title meta-title))1213 (define-record-printer (metadata input port)14 (define (join . strings)15 (fold (lambda (str output)16 (string-append17 (if (zero? (string-length output))18 output19 (string-append output " ")) str)) "" strings))2021 (display (join22 (string (meta-state input))23 (string-append "[" (symbol->string (meta-key input)) "]:")24 (meta-title input))))2526 (define symbol-charset27 (char-set-union28 char-set:letter29 char-set:digit30 (->char-set "!$%&*+-./:<=>?@^_~")))3132 (define symbol-charset-start33 (char-set-difference symbol-charset char-set:digit))3435 ;;;;36 ;; Utility functions37 ;;;;3839 (define parse-symbol40 (bind (as-string (all-of41 (in symbol-charset-start)42 (zero-or-more (in symbol-charset))))43 (lambda (str) (result (string->symbol str)))))4445 (define (parse-any-except char . chars)46 (as-string (one-or-more47 (in (char-set-complement (list->char-set48 (cons char chars)))))))4950 (define parse-indent51 (any-of52 (is #\tab)53 (repeated (is #\space) min: 4 max: 4)))5455 (define parse-blanks56 (zero-or-more (in char-set:blank)))5758 (define parse-text59 (parse-any-except #\newline))6061 ;;;;62 ;; Parser for literals63 ;;;;6465 (define (parse-escaped ctrl-chars)66 (define parse-char67 (any-of68 (sequence* ((_ (is #\\))69 (i item))70 (result i))71 (in (char-set-complement (list->char-set ctrl-chars)))))7273 (as-string (one-or-more parse-char)))7475 (define (parse-vector parser)76 (define (parse-vector*)77 (one-or-more78 (sequence* ((elem parser)79 (_ (maybe (is #\,)))80 (_ parse-blanks))81 (result elem))))8283 (bind (parse-vector*)84 (lambda (lst)85 (result (list->vector lst)))))8687 (define parse-set88 (enclosed-by (is #\{)89 (parse-vector (parse-escaped '(#\, #\})))90 (is #\})))9192 (define parse-ref93 (enclosed-by (is #\[)94 (parse-vector parse-symbol)95 (is #\])))9697 ;;;;98 ;; Parsers for entry parts99 ;;;;100101 (define parse-state102 (in (string->char-set "-x")))103104 (define parse-key105 (enclosed-by (is #\[) parse-symbol (is #\])))106107 (define parse-title108 parse-text)109110 ;;;;111 ;; Parsers for optional field values112 ;;;;113114 (define parse-field-name115 (parse-any-except #\:))116117 (define parse-field-value118 (any-of119 parse-set120 parse-ref121 parse-text))122123 (define parse-field124 (sequence* ((_ parse-indent)125 (_ (is #\*))126 (_ parse-blanks)127 (name parse-field-name)128 (_ (is #\:))129 (_ parse-blanks)130 (value parse-field-value)131 (_ parse-blanks)132 (_ (is #\newline)))133 (result (cons name value))))134135 (define parse-fields136 (zero-or-more parse-field))137138 ;;;;139 ;; Parser for optional notes140 ;;;;141142 (define parse-note143 (sequence* ((_ parse-indent)144 (text parse-text)145 (_ (one-or-more (is #\newline))))146 (result (string-append text "\n"))))147148 (define parse-notes149 (as-string (zero-or-more parse-note)))150151 ;;;;152 ;; Combine utility parsers153 ;;;;154155 (define parse-info156 (sequence* ((fields (maybe parse-fields '()))157 (notes (maybe (preceded-by158 (is #\newline)159 parse-notes) "")))160 (result (list fields notes))))161162 (define parse-entry163 (sequence* ((state parse-state)164 (_ parse-blanks)165 (key parse-key)166 (_ (is #\:))167 (_ parse-blanks)168 (title parse-title)169 (_ (is #\newline))170 (info (maybe parse-info (list '() '()))))171 (result (cons (make-meta state key title) info))))172173 (define parse-entries174 (one-or-more (preceded-by175 (zero-or-more (in char-set:whitespace))176 parse-entry)))177178 ;;;;179 ;; Interface functions180 ;;;;181182 (define (readit-ref? obj)183 (and (vector? obj)184 (>= (vector-length obj) 1)185 (every symbol? (vector->list obj))))186187 (define (readit-set? obj)188 (and (vector? obj)189 (>= (vector-length obj) 1)190 (every string? (vector->list obj))))191192 (define (parse-readit input)193 (parse (sequence* ((r parse-entries)194 (_ end-of-input))195 (result r)) input)))