aboutsummaryrefslogtreecommitdiff
path: root/commonmark.rb
blob: 06787b3779b73ef5cf404fd875b752cea3de6f51 (plain)
  1. require 'ffi'
  2. require 'stringio'
  3. require 'cgi'
  4. module CMark
  5. extend FFI::Library
  6. ffi_lib ['libcmark', 'cmark']
  7. typedef :pointer, :node
  8. enum :node_type, [:document, :blockquote, :list, :list_item,
  9. :fenced_code, :indented_code, :html, :paragraph,
  10. :atx_header, :setext_header, :hrule, :reference_def,
  11. :str, :softbreak, :linebreak, :code, :inline_html,
  12. :emph, :strong, :link, :image]
  13. attach_function :cmark_free_nodes, [:node], :void
  14. attach_function :cmark_node_unlink, [:node], :void
  15. attach_function :cmark_markdown_to_html, [:string, :int], :string
  16. attach_function :cmark_parse_document, [:string, :int], :node
  17. attach_function :cmark_node_first_child, [:node], :node
  18. attach_function :cmark_node_parent, [:node], :node
  19. attach_function :cmark_node_next, [:node], :node
  20. attach_function :cmark_node_previous, [:node], :node
  21. attach_function :cmark_node_get_type, [:node], :node_type
  22. attach_function :cmark_node_get_string_content, [:node], :string
  23. attach_function :cmark_node_get_header_level, [:node], :int
  24. end
  25. class Node
  26. attr_accessor :type, :children, :string_content, :header_level
  27. def initialize(pointer)
  28. if pointer.null?
  29. return nil
  30. end
  31. @pointer = pointer
  32. @type = CMark::cmark_node_get_type(pointer)
  33. @children = []
  34. first_child = CMark::cmark_node_first_child(pointer)
  35. b = first_child
  36. while !b.null?
  37. @children << Node.new(b)
  38. b = CMark::cmark_node_next(b)
  39. end
  40. @string_content = CMark::cmark_node_get_string_content(pointer)
  41. @header_level = CMark::cmark_node_get_header_level(pointer)
  42. if @type == :document
  43. self.free
  44. end
  45. end
  46. def self.from_markdown(s)
  47. len = s.bytes.length
  48. Node.new(CMark::cmark_parse_document(s, len))
  49. end
  50. def free
  51. CMark::cmark_free_nodes(@pointer)
  52. end
  53. end
  54. class Renderer
  55. def initialize(stream = nil)
  56. if stream
  57. @stream = stream
  58. @stringwriter = false
  59. else
  60. @stringwriter = true
  61. @stream = StringIO.new
  62. end
  63. @need_blocksep = false
  64. end
  65. def outf(format, *args)
  66. @stream.printf(format, *args)
  67. end
  68. def out(*args)
  69. args.each do |arg|
  70. if arg.kind_of?(String)
  71. @stream.write(arg)
  72. elsif arg.kind_of?(Node)
  73. self.render(arg)
  74. elsif arg.kind_of?(Array)
  75. arg.each { |x| self.out(x) }
  76. else arg.kind_of?(Numeric)
  77. @stream.write(arg)
  78. end
  79. end
  80. end
  81. def render(node)
  82. case node.type
  83. when :document
  84. self.document(node.children)
  85. if @stringwriter
  86. return @stream.string
  87. end
  88. when :paragraph
  89. self.startblock
  90. self.paragraph(node.children)
  91. self.endblock
  92. when :setext_header, :atx_header
  93. self.startblock
  94. self.header(node.header_level, node.children)
  95. self.endblock
  96. when :str
  97. self.str(node.string_content)
  98. else
  99. # raise "unimplemented " + node.type.to_s
  100. end
  101. end
  102. def document(children)
  103. self.out(children)
  104. end
  105. def startblock
  106. if @need_blocksep
  107. self.blocksep
  108. end
  109. end
  110. def endblock
  111. @need_blocksep = true
  112. end
  113. def blocksep
  114. self.out("\n\n")
  115. end
  116. end
  117. class HtmlRenderer < Renderer
  118. def blocksep
  119. self.out("\n")
  120. end
  121. def header(level, children)
  122. self.out("<h", level, ">", children, "</h", level, ">")
  123. end
  124. def paragraph(children)
  125. self.out("<p>", children, "</p>")
  126. end
  127. def str(content)
  128. self.out(CGI.escapeHTML(content))
  129. end
  130. end
  131. doc = Node.from_markdown(STDIN.read())
  132. renderer = HtmlRenderer.new(STDOUT)
  133. renderer.render(doc)
  134. # def markdown_to_html(s)
  135. # len = s.bytes.length
  136. # CMark::cmark_markdown_to_html(s, len)
  137. # end
  138. # print markdown_to_html(STDIN.read())