(module cmark (cmark->sxml commonmark->html cmark->html CMARK_OPT_SAFE CMARK_OPT_DEFAULT CMARK_EVENT_NONE CMARK_EVENT_DONE CMARK_EVENT_ENTER CMARK_EVENT_EXIT CMARK_NODE_NONE CMARK_NODE_DOCUMENT CMARK_NODE_BLOCK_QUOTE CMARK_NODE_LIST CMARK_NODE_ITEM CMARK_NODE_CODE_BLOCK CMARK_NODE_HTML_BLOCK CMARK_NODE_CUSTOM_BLOCK CMARK_NODE_PARAGRAPH CMARK_NODE_HEADING CMARK_NODE_THEMATIC_BREAK CMARK_NODE_FIRST_BLOCK CMARK_NODE_LAST_BLOCK CMARK_NODE_TEXT CMARK_NODE_SOFTBREAK CMARK_NODE_LINEBREAK CMARK_NODE_CODE CMARK_NODE_HTML_INLINE CMARK_NODE_CUSTOM_INLINE CMARK_NODE_EMPH CMARK_NODE_STRONG CMARK_NODE_LINK CMARK_NODE_IMAGE CMARK_NODE_FIRST_INLINE CMARK_NODE_LAST_INLINE CMARK_NO_LIST CMARK_BULLET_LIST CMARK_ORDERED_LIST CMARK_NO_DELIM CMARK_PERIOD_DELIM CMARK_PAREN_DELIM cmark-node-free cmark-node-next cmark-node-previous cmark-node-parent cmark-node-first-child cmark-node-last-child cmark-iter-new cmark-iter-free cmark-iter-next cmark-iter-get-node cmark-iter-get-event-type cmark-iter-get-root cmark-iter-reset cmark-node-get-user-data cmark-node-get-type cmark-node-get-type-string cmark-node-get-literal cmark-node-set-literal cmark-node-get-heading-level cmark-node-set-heading-level cmark-node-get-list-type cmark-node-set-list-type cmark-node-get-list-type cmark-node-get-list-delim cmark-node-set-list-delim cmark-node-get-list-start cmark-node-set-list-start cmark-node-get-list-tight cmark-node-set-list-tight cmark-node-get-fence-info cmark-node-set-fence-info cmark-node-get-url cmark-node-set-url cmark-node-get-title cmark-node-set-title cmark-node-get-on-enter cmark-node-set-on-enter cmark-node-get-on-exit cmark-node-set-on-exit cmark-node-get-start-line cmark-node-get-start-column cmark-node-get-end-line cmark-node-end-column cmark-node-unlink cmark-node-insert-before cmark-node-insert-after cmark-node-replace cmark-node-prepend-child cmark-node-append-child cmark-consolidate-text-nodes cmark-parser-new cmark-parser-free cmark-parser-feed cmark-parser-finish cmark-parse-document cmark-parse-file cmark-render-xml cmark-render-html cmark-render-man cmark-render-commonmark cmark-render-latex cmark-version cmark-version-string) (import scheme (chicken base) (chicken foreign) ;; only for debugging (chicken pretty-print) (chicken format)) ;; void ;; usage_example(cmark_node *root) { ;; cmark_event_type ev_type; ;; cmark_iter *iter = cmark_iter_new(root); ;; while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { ;; cmark_node *cur = cmark_iter_get_node(iter); ;; // do something with cur and ev_type ;; } ;; cmark_iter_free(iter); ;; (foreign-declare "#include \"cmark.h\"") (define CMARK_OPT_SAFE (foreign-value "CMARK_OPT_SAFE" int)) (define CMARK_OPT_DEFAULT (foreign-value "CMARK_OPT_DEFAULT" int)) ;; Event Types (define CMARK_EVENT_NONE (foreign-value "CMARK_EVENT_NONE" int)) (define CMARK_EVENT_DONE (foreign-value "CMARK_EVENT_DONE" int)) (define CMARK_EVENT_ENTER (foreign-value "CMARK_EVENT_ENTER" int)) (define CMARK_EVENT_EXIT (foreign-value "CMARK_EVENT_EXIT" int)) ;; Node Types (define CMARK_NODE_NONE (foreign-value "CMARK_NODE_NONE" int)) ;; Block (define CMARK_NODE_DOCUMENT (foreign-value "CMARK_NODE_DOCUMENT" int)) (define CMARK_NODE_BLOCK_QUOTE (foreign-value "CMARK_NODE_BLOCK_QUOTE" int)) (define CMARK_NODE_LIST (foreign-value "CMARK_NODE_LIST" int)) (define CMARK_NODE_ITEM (foreign-value "CMARK_NODE_ITEM" int)) (define CMARK_NODE_CODE_BLOCK (foreign-value "CMARK_NODE_CODE_BLOCK" int)) (define CMARK_NODE_HTML_BLOCK (foreign-value "CMARK_NODE_HTML_BLOCK" int)) (define CMARK_NODE_CUSTOM_BLOCK (foreign-value "CMARK_NODE_CUSTOM_BLOCK" int)) (define CMARK_NODE_PARAGRAPH (foreign-value "CMARK_NODE_PARAGRAPH" int)) (define CMARK_NODE_HEADING (foreign-value "CMARK_NODE_HEADING" int)) (define CMARK_NODE_THEMATIC_BREAK (foreign-value "CMARK_NODE_THEMATIC_BREAK" int)) ;; CMARK_NODE_FIRST_BLOCK = CMARK_NODE_DOCUMENT (define CMARK_NODE_FIRST_BLOCK (foreign-value "CMARK_NODE_FIRST_BLOCK" int)) ;; CMARK_NODE_LAST_BLOCK = CMARK_NODE_THEMATIC_BREAK (define CMARK_NODE_LAST_BLOCK (foreign-value "CMARK_NODE_LAST_BLOCK" int)) ;; Inline (define CMARK_NODE_TEXT (foreign-value "CMARK_NODE_TEXT" int)) (define CMARK_NODE_SOFTBREAK (foreign-value "CMARK_NODE_SOFTBREAK" int)) (define CMARK_NODE_LINEBREAK (foreign-value "CMARK_NODE_LINEBREAK" int)) (define CMARK_NODE_CODE (foreign-value "CMARK_NODE_CODE" int)) (define CMARK_NODE_HTML_INLINE (foreign-value "CMARK_NODE_HTML_INLINE" int)) (define CMARK_NODE_CUSTOM_INLINE (foreign-value "CMARK_NODE_CUSTOM_INLINE" int)) (define CMARK_NODE_EMPH (foreign-value "CMARK_NODE_EMPH" int)) (define CMARK_NODE_STRONG (foreign-value "CMARK_NODE_STRONG" int)) (define CMARK_NODE_LINK (foreign-value "CMARK_NODE_LINK" int)) (define CMARK_NODE_IMAGE (foreign-value "CMARK_NODE_IMAGE" int)) ;; CMARK_NODE_FIRST_INLINE = CMARK_NODE_TEXT (define CMARK_NODE_FIRST_INLINE (foreign-value "CMARK_NODE_FIRST_INLINE" int)) ;; CMARK_NODE_LAST_INLINE = CMARK_NODE_IMAGE (define CMARK_NODE_LAST_INLINE (foreign-value "CMARK_NODE_LAST_INLINE" int)) ;; List types (define CMARK_NO_LIST (foreign-value "CMARK_NO_LIST" int)) (define CMARK_BULLET_LIST (foreign-value "CMARK_BULLET_LIST" int)) (define CMARK_ORDERED_LIST (foreign-value "CMARK_ORDERED_LIST" int)) ;; Delim types (define CMARK_NO_DELIM (foreign-value "CMARK_NO_DELIM" int)) (define CMARK_PERIOD_DELIM (foreign-value "CMARK_PERIOD_DELIM" int)) (define CMARK_PAREN_DELIM (foreign-value "CMARK_PAREN_DELIM" int)) (define cmark-node-free (foreign-lambda void "cmark_node_free" (c-pointer "cmark_node"))) ;; Tree Traversal (define cmark-node-next (foreign-lambda (c-pointer "cmark_node") "cmark_node_next" (c-pointer "cmark_node"))) (define cmark-node-previous (foreign-lambda (c-pointer "cmark_node") "cmark_node_previous" (c-pointer "cmark_node"))) (define cmark-node-parent (foreign-lambda (c-pointer "cmark_node") "cmark_node_parent" (c-pointer "cmark_node"))) (define cmark-node-first-child (foreign-lambda (c-pointer "cmark_node") "cmark_node_first_child" (c-pointer "cmark_node"))) (define cmark-node-last-child (foreign-lambda (c-pointer "cmark_node") "cmark_node_last_child" (c-pointer "cmark_node"))) ;; Iterator (define cmark-iter-new (foreign-lambda (c-pointer "cmark_iter") "cmark_iter_new" (c-pointer "cmark_node"))) (define cmark-iter-free (foreign-lambda void "cmark_iter_free" (c-pointer "cmark_iter"))) ;; Returns either CMARK_EVENT_ENTER, CMARK_EVENT_EXIT, CMARK_EVENT_DONE (define cmark-iter-next (foreign-lambda int "cmark_iter_next" (c-pointer "cmark_iter"))) (define cmark-iter-get-node (foreign-lambda (c-pointer "cmark_node") "cmark_iter_get_node" (c-pointer "cmark_iter"))) (define cmark-iter-get-event-type (foreign-lambda int "cmark_iter_get_event_type" (c-pointer "cmark_iter"))) (define cmark-iter-get-root (foreign-lambda (c-pointer "cmark_node") "cmark_iter_get_root" (c-pointer "cmark_iter"))) (define cmark-iter-reset (foreign-lambda void "cmark_iter_reset" (c-pointer "cmark_iter") (c-pointer "cmark_node") int)) ;; Accessors ;; is void * valid type? (define cmark-node-get-user-data (foreign-lambda (c-pointer "void") "cmark_node_get_user_data" (c-pointer "cmark_node"))) (define cmark-node-get-type (foreign-lambda int "cmark_node_get_type" (c-pointer "cmark_node"))) ;; returns type string or "" (define cmark-node-get-type-string (foreign-lambda c-string "cmark_node_get_type_string" (c-pointer "cmark_node"))) (define cmark-node-get-literal (foreign-lambda c-string "cmark_node_get_literal" (c-pointer "cmark_node"))) (define cmark-node-set-literal (foreign-lambda int "cmark_node_set_literal" (c-pointer "cmark_node") c-string)) (define cmark-node-get-heading-level (foreign-lambda int "cmark_node_get_heading_level" (c-pointer "cmark_node"))) (define cmark-node-set-heading-level (foreign-lambda int "cmark_node_set_heading_level" (c-pointer "cmark_node") int)) (define cmark-node-get-list-type (foreign-lambda int "cmark_node_get_list_type" (c-pointer "cmark_node"))) (define cmark-node-set-list-type (foreign-lambda int "cmark_node_set_list_type" (c-pointer "cmark_node") int)) (define cmark-node-get-list-delim (foreign-lambda int "cmark_node_get_list_delim" (c-pointer "cmark_node"))) (define cmark-node-set-list-delim (foreign-lambda int "cmark_node_set_list_delim" (c-pointer "cmark_node") int)) (define cmark-node-get-list-start (foreign-lambda int "cmark_node_get_list_start" (c-pointer "cmark_node"))) (define cmark-node-set-list-start (foreign-lambda int "cmark_node_set_list_start" (c-pointer "cmark_node") int)) (define cmark-node-get-list-tight (foreign-lambda int "cmark_node_get_list_tight" (c-pointer "cmark_node"))) (define cmark-node-set-list-tight (foreign-lambda int "cmark_node_set_list_tight" (c-pointer "cmark_node") int)) (define cmark-node-get-fence-info (foreign-lambda c-string "cmark_node_get_fence_info" (c-pointer "cmark_node"))) (define cmark-node-set-fence-info (foreign-lambda int "cmark_node_set_fence_info" (c-pointer "cmark_node") c-string)) (define cmark-node-get-url (foreign-lambda c-string "cmark_node_get_url" (c-pointer "cmark_node"))) (define cmark-node-set-url (foreign-lambda int "cmark_node_set_url" (c-pointer "cmark_node") c-string)) (define cmark-node-get-title (foreign-lambda c-string "cmark_node_get_title" (c-pointer "cmark_node"))) (define cmark-node-set-title (foreign-lambda int "cmark_node_set_title" (c-pointer "cmark_node") c-string)) (define cmark-node-get-on-enter (foreign-lambda c-string "cmark_node_get_on_enter" (c-pointer "cmark_node"))) (define cmark-node-set-on-enter (foreign-lambda int "cmark_node_set_on_enter" (c-pointer "cmark_node") c-string)) (define cmark-node-get-on-exit (foreign-lambda c-string "cmark_node_get_on_exit" (c-pointer "cmark_node"))) (define cmark-node-set-on-exit (foreign-lambda int "cmark_node_set_on_exit" (c-pointer "cmark_node") c-string)) (define cmark-node-get-start-line (foreign-lambda int "cmark_node_get_start_line" (c-pointer "cmark_node"))) (define cmark-node-get-start-column (foreign-lambda int "cmark_node_get_start_column" (c-pointer "cmark_node"))) (define cmark-node-get-end-line (foreign-lambda int "cmark_node_get_end_line" (c-pointer "cmark_node"))) (define cmark-node-end-column (foreign-lambda int "cmark_node_get_end_column" (c-pointer "cmark_node"))) (define cmark-node-unlink (foreign-lambda void "cmark_node_unlink" (c-pointer "cmark_node"))) (define cmark-node-insert-before (foreign-lambda int "cmark_node_insert_before" (c-pointer "cmark_node") (c-pointer "cmark_node"))) (define cmark-node-insert-after (foreign-lambda int "cmark_node_insert_after" (c-pointer "cmark_node") (c-pointer "cmark_node"))) (define cmark-node-replace (foreign-lambda int "cmark_node_replace" (c-pointer "cmark_node") (c-pointer "cmark_node"))) (define cmark-node-prepend-child (foreign-lambda int "cmark_node_prepend_child" (c-pointer "cmark_node") (c-pointer "cmark_node"))) (define cmark-node-append-child (foreign-lambda int "cmark_node_append_child" (c-pointer "cmark_node") (c-pointer "cmark_node"))) (define cmark-consolidate-text-nodes (foreign-lambda void "cmark_consolidate_text_nodes" (c-pointer "cmark_node"))) ;; Parsing (define cmark-parser-new (foreign-lambda (c-pointer "cmark_parser") "cmark_parser_new" int)) (define cmark-parser-free (foreign-lambda void "cmark_parser_free" (c-pointer "cmark_parser"))) (define cmark-parser-feed (foreign-lambda void "cmark_parser_feed" (c-pointer "cmark_parser") c-string size_t)) (define cmark-parser-finish (foreign-lambda (c-pointer "cmark_node") "cmark_parser_finish" (c-pointer "cmark_parser"))) (define cmark-parse-document (foreign-lambda (c-pointer "cmark_node") "cmark_parse_document" c-string size_t int)) ;; TODO: How to pass file ??? (define cmark-parse-file (foreign-lambda (c-pointer "cmark_node") "cmark_parse_file" (c-pointer "FILE") int)) ;; Rendering ;; Make sure to free return c buffer (define cmark-render-xml (foreign-lambda c-string "cmark_render_xml" (c-pointer "cmark_node") int)) (define cmark-render-html (foreign-lambda c-string "cmark_render_html" (c-pointer "cmark_node") int)) (define cmark-render-man (foreign-lambda c-string "cmark_render_man" (c-pointer "cmark_node") int int)) (define cmark-render-commonmark (foreign-lambda c-string "cmark_render_commonmark" (c-pointer "cmark_node") int int)) (define cmark-render-latex (foreign-lambda c-string "cmark_render_latex" (c-pointer "cmark_node") int int)) ;; Version (define cmark-version (foreign-lambda int "cmark_version")) (define cmark-version-string (foreign-lambda c-string "cmark_version_string")) (define (get-header-type node) (format "h~A" (cmark-node-get-heading-level node))) (define (get-html-node-type node) (let ([stype (cmark-node-get-type-string node)]) (string->symbol (cond [(equal? stype "paragraph") "p"] [(equal? stype "image") "img"] [(equal? stype "link") "a"] [(equal? stype "heading") (get-header-type node)] [(equal? stype "list") (if (= (cmark-node-get-list-type node) CMARK_ORDERED_LIST) "ol" "ul")] [(equal? stype "item") "li"] [(equal? stype "emph") "em"] [(equal? stype "code") "code"] [else stype])))) (define (node-summary node) (list (cmark-node-get-type node) (cmark-node-get-start-line node) (cmark-node-get-end-line node))) ;; -1 isnt a real cmark node type ;; 0 isnt a real cmark line number (define (null-node-summary) (list -1 0 0)) (define (single-node? node-type) (or (= node-type CMARK_NODE_TEXT) (= node-type CMARK_NODE_LINEBREAK) (= node-type CMARK_NODE_SOFTBREAK) (= node-type CMARK_NODE_CODE_BLOCK) (= node-type CMARK_NODE_THEMATIC_BREAK) (= node-type CMARK_NODE_CODE))) (define (update-stack stack node node-summary node-type head) (cond [(equal? head node-summary) (cons (list 'end node) stack)] [(single-node? node-type) (cons (list 'single node) stack)] [else (cons (list 'start node) stack)])) (define (update-queue queue node node-summary node-type head) (cond [(equal? head node-summary) (cdr queue)] [(single-node? node-type) queue] [else (cons node-summary queue)])) (define (cmark-iter->stack iter) (let loop ([event (cmark-iter-next iter)] [stack (list)] [queue (list)]) (if (= event CMARK_EVENT_DONE) (reverse stack) (let* ([node (cmark-iter-get-node iter)] [node-sum (node-summary node)] [node-type (cmark-node-get-type node)] [head (if (null? queue) (null-node-summary) (car queue))] [new-stack (update-stack stack node node-sum node-type head)] [new-queue (update-queue queue node node-sum node-type head)]) (loop (cmark-iter-next iter) new-stack new-queue))))) (define-record node-stack stack) (define (node-stack-null? stack) (null? (node-stack-stack stack))) (define (node-stack-car stack) (if (node-stack-null? stack) (list) (car (node-stack-stack stack)))) (define (node-stack-cdr stack) (if (node-stack-null? stack) (list) (cdr (node-stack-stack stack)))) (define (node-stack-pop! stack) (if (node-stack-null? stack) (list) (let ([v (node-stack-car stack)]) (node-stack-stack-set! stack (node-stack-cdr stack)) v))) (define (node-stack-cons stack v) (cons v (node-stack-stack stack))) (define (node-stack-push! stack v) (node-stack-stack-set! stack (node-stack-cons stack v))) (define (node-stack-print stack) (pretty-print (node-stack-stack stack))) (define (node-stack-reverse stack) (reverse (node-stack-stack stack))) (define (node-stack-pop-to-last! stack) (let* ([rstack (node-stack-reverse stack)] [last (car rstack)] [rtail (cdr rstack)]) (node-stack-stack-set! stack (list last)) rtail)) (define (node-stack-pop-all! stack) (let ([rstack (node-stack-reverse stack)]) (node-stack-stack-set! stack (list)) rstack)) (define (delimiter-pair? start end) (if (or (string? start) (string? end)) #f (if (or (<= (length start) 1) (<= (length end) 1)) #f (if (not (and (equal? (car start) 'start) (equal? (car end) 'end))) #f (= (cmark-node-get-type (cadr start)) (cmark-node-get-type (cadr end))))))) (define (node-stack-pop-to-delim! stack end) (let loop ([vs (list)]) (if (node-stack-null? stack) vs (let ([start (node-stack-pop! stack)]) (if (delimiter-pair? start end) vs (loop (cons start vs))))))) (define (print-node-types nodes) (map (lambda (n) (list (car n) (cmark-node-get-type-string (cadr n)))) nodes)) (define (stack-interpret-node node stack) ;;(pretty-print (node-stack-stack stack)) (let* ([label (car node)] [v (cadr node)] [node-type (cmark-node-get-type v)]) (cond [(equal? label 'start) (begin (node-stack-push! stack node) stack)] [(= node-type CMARK_NODE_DOCUMENT) (node-stack-pop-to-delim! stack node)] [(= node-type CMARK_NODE_TEXT) (node-stack-push! stack (cmark-node-get-literal v)) stack] [(= node-type CMARK_NODE_LINEBREAK) (node-stack-push! stack '(br)) stack] [(= node-type CMARK_NODE_SOFTBREAK) (node-stack-push! stack "\n") stack] [(= node-type CMARK_NODE_CODE_BLOCK) (node-stack-push! stack (list 'pre (list 'code (cmark-node-get-literal v)))) stack] [(= node-type CMARK_NODE_THEMATIC_BREAK) (node-stack-push! stack '(hr)) stack] [(= node-type CMARK_NODE_CODE) (node-stack-push! stack (list (get-html-node-type v) (cmark-node-get-literal v))) stack] [(= node-type CMARK_NODE_HEADING) (let ([vs (node-stack-pop-to-delim! stack node)]) (node-stack-push! stack (list (get-html-node-type v) vs)) stack)] [(= node-type CMARK_NODE_PARAGRAPH) (let ([vs (node-stack-pop-to-delim! stack node)]) (node-stack-push! stack (list (get-html-node-type v) vs)) stack)] [(= node-type CMARK_NODE_EMPH) (let ([vs (node-stack-pop-to-delim! stack node)]) (node-stack-push! stack (list (get-html-node-type v) vs)) stack)] [(= node-type CMARK_NODE_STRONG) (let ([vs (node-stack-pop-to-delim! stack node)]) (node-stack-push! stack (list 'strong vs)) stack)] [(= node-type CMARK_NODE_LIST) (let ([vs (node-stack-pop-to-delim! stack node)]) (node-stack-push! stack (list (get-html-node-type v) vs)) stack)] [(= node-type CMARK_NODE_ITEM) (let ([vs (node-stack-pop-to-delim! stack node)]) (node-stack-push! stack (list (get-html-node-type v) vs)) stack)] [(= node-type CMARK_NODE_IMAGE) (let ([vs (node-stack-pop-to-delim! stack node)]) (node-stack-push! stack (list (get-html-node-type v) (if (equal? (cmark-node-get-title v) "") (list '@ (list 'src (list (cmark-node-get-url v))) (list 'alt vs)) (list '@ (list 'src (list (cmark-node-get-url v))) (list 'alt vs) (list 'title (cmark-node-get-title v)))))) stack)] [(= node-type CMARK_NODE_LINK) (let* ([vs (node-stack-pop-to-delim! stack node)]) (node-stack-push! stack (list (get-html-node-type v) (if (equal? (cmark-node-get-title v) "") (list '@ (list 'href (cmark-node-get-url v))) (list '@ (list 'href (cmark-node-get-url v)) (list 'title (cmark-node-get-title v)))) vs)) stack)] [(= node-type CMARK_NODE_BLOCK_QUOTE) (let ([vs (node-stack-pop-to-delim! stack node)]) (node-stack-push! stack (list 'blockquote vs)) stack)] [else stack]))) (define (print-node-types nodes) (pretty-print (map (lambda (n) (list (car n) (cmark-node-get-type-string (cadr n)))) nodes))) (define (cmark-stack->sxml code) ;;(print-node-types code) (let loop ([stack (make-node-stack (list))] [code code]) (if (null? code) stack (loop (stack-interpret-node (car code) stack) (cdr code))))) (define (cmark->sxml s) (let* ([root (cmark-parse-document s (string-length s) CMARK_OPT_DEFAULT)] [iter (cmark-iter-new root)] [code (cmark-iter->stack iter)] [sxml (cmark-stack->sxml code)]) (cmark-iter-free iter) (cmark-node-free root) sxml)) (define cmark-markdown-to-html (foreign-lambda c-string "cmark_markdown_to_html" c-string size_t int)) (define (commonmark->html input #!key (safe #t)) (let ((opt (if safe CMARK_OPT_SAFE CMARK_OPT_DEFAULT))) (cmark-markdown-to-html input (string-length input) opt))) (define (cmark->html input #!key (safe #t)) (let ((opt (if safe CMARK_OPT_SAFE CMARK_OPT_DEFAULT))) (cmark-markdown-to-html input (string-length input) opt))) )