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