aboutsummaryrefslogtreecommitdiff
path: root/commonmark.rb
blob: 74ebb11c0518c86103f205c4220cbdd4b2070bc7 (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(arg)
  69. @stream.write(arg)
  70. end
  71. def render(node)
  72. if node.kind_of?(Array)
  73. node.each { |x| self.render(x) }
  74. else
  75. case node.type
  76. when :document
  77. self.document(node.children)
  78. if @stringwriter
  79. return @stream.string
  80. end
  81. when :paragraph
  82. self.blocksep
  83. self.paragraph(node.children)
  84. @need_blocksep = true
  85. when :setext_header, :atx_header
  86. self.blocksep
  87. self.header(node.header_level, node.children)
  88. @need_blocksep = true
  89. when :str
  90. self.str(node.string_content)
  91. else
  92. # raise "unimplemented " + node.type.to_s
  93. end
  94. @last_node = node.type
  95. end
  96. end
  97. def blocksep
  98. if @need_blocksep
  99. self.out("\n\n")
  100. end
  101. end
  102. def document(children)
  103. self.render(children)
  104. end
  105. def header(level, children)
  106. self.render(children)
  107. end
  108. def paragraph(children)
  109. self.render(children)
  110. end
  111. def str(content)
  112. self.out(content)
  113. end
  114. end
  115. class HtmlRenderer < Renderer
  116. def header(level, children)
  117. self.outf("<h%d>", level)
  118. self.render(children)
  119. self.outf("</h%d>", level)
  120. end
  121. def paragraph(children)
  122. self.out("<p>")
  123. self.render(children)
  124. self.out("</p>")
  125. end
  126. def str(content)
  127. self.out(CGI.escapeHTML(content))
  128. end
  129. end
  130. doc = Node.from_markdown(STDIN.read())
  131. renderer = HtmlRenderer.new(STDOUT)
  132. renderer.render(doc)
  133. # def markdown_to_html(s)
  134. # len = s.bytes.length
  135. # CMark::cmark_markdown_to_html(s, len)
  136. # end
  137. # print markdown_to_html(STDIN.read())