aboutsummaryrefslogtreecommitdiff
path: root/commonmark.rb
blob: 7c9c6d2bc9c89eefc14a94e7d7ce30d36dbf3841 (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. end
  24. class Node
  25. attr_accessor :type, :children, :string_content, :pointer
  26. def initialize(pointer)
  27. if pointer.null?
  28. return nil
  29. end
  30. @pointer = pointer
  31. @type = CMark::cmark_node_get_type(pointer)
  32. @children = []
  33. first_child = CMark::cmark_node_first_child(pointer)
  34. b = first_child
  35. while !b.null?
  36. @children << Node.new(b)
  37. b = CMark::cmark_node_next(b)
  38. end
  39. @string_content = CMark::cmark_node_get_string_content(pointer)
  40. if @type == :document
  41. self.free
  42. end
  43. end
  44. def self.from_markdown(s)
  45. len = s.bytes.length
  46. Node.new(CMark::cmark_parse_document(s, len))
  47. end
  48. def free
  49. CMark::cmark_free_nodes(@pointer)
  50. end
  51. end
  52. class Renderer
  53. def initialize(stream = nil)
  54. if stream
  55. @stream = stream
  56. @stringwriter = false
  57. else
  58. @stringwriter = true
  59. @stream = StringIO.new
  60. end
  61. end
  62. def outf(format, *args)
  63. @stream.printf(format, *args)
  64. end
  65. def out(arg)
  66. @stream.puts(arg)
  67. end
  68. def render(node)
  69. case node.type
  70. when :document
  71. self.document(node.children)
  72. if @stringwriter
  73. @stream.string
  74. end
  75. when :paragraph
  76. self.paragraph(node.children)
  77. when :str
  78. self.str(node.string_content)
  79. else
  80. # raise "unimplemented " + node.type.to_s
  81. end
  82. end
  83. def document(children)
  84. children.each { |x| render(x) }
  85. end
  86. def paragraph(children)
  87. children.each { |x| render(x) }
  88. end
  89. def str(content)
  90. self.out(content)
  91. end
  92. end
  93. class HtmlRenderer < Renderer
  94. def paragraph(children)
  95. self.out("<p>")
  96. children.each { |x| render(x) }
  97. self.out("</p>\n")
  98. end
  99. def str(content)
  100. self.out(CGI.escapeHTML(content))
  101. end
  102. end
  103. doc = Node.from_markdown(STDIN.read())
  104. renderer = HtmlRenderer.new(STDOUT)
  105. renderer.render(doc)
  106. # def markdown_to_html(s)
  107. # len = s.bytes.length
  108. # CMark::cmark_markdown_to_html(s, len)
  109. # end
  110. # print markdown_to_html(STDIN.read())