(use comparse) (define whitespace (in #\space #\tab)) (define nl (is #\newline)) (define end-of-input (none-of item)) (define headline-marks (sequence* ((marks (one-or-more (is #\*))) (_ (one-or-more whitespace))) (result (length marks)))) (define tag-delim (is #\:)) (define tag (preceded-by tag-delim (as-string (repeated (none-of* whitespace item) until: tag-delim)))) (define header-tags (sequence* ((tags (zero-or-more tag)) (_ tag-delim) (_ (zero-or-more whitespace)) (_ (any-of nl end-of-input))) (result tags))) (define headline-text (as-string (repeated item until: (any-of header-tags nl end-of-input)))) (define headline (sequence* ((level headline-marks) (text headline-text) (tags (maybe header-tags))) (let* ((value `(headline (level ,level) (text ,text))) (value (if (pair? tags) (append value `((tags . ,tags))) value))) (result value)))) ;; Examples: ;; ;; * Hi this is a simple headline ;; * Another one with a :tag: ;; * Two tags :one:two: ;; * This should not be a tag :haha: as its inside the title ;; * Nor is this a tag as it contains whitespaces :aha : (use test) (define-syntax test-parser (syntax-rules () ((_ parser in out) (test out (parse parser in))))) (test-parser whitespace " " #\space) (test-parser headline "* Hi this is a simple headline" '(headline (level 1) (text "Hi this is a simple headline"))) (test-parser headline "* Another one with a :tag:" '(headline (level 1) (text "Another one with a ") (tags "tag"))) (test-parser headline "* Two tags :one:two:" '(headline (level 1) (text "Two tags ") (tags "one" "two"))) (test-parser headline "* This should not be a tag :haha: as its inside the title" '(headline (level 1) (text "This should not be a tag :haha: as its inside the title"))) (test-parser headline "* Nor is this a tag as it contains whitespaces :aha :" '(headline (level 1) (text "Nor is this a tag as it contains whitespaces :aha :")))