Permalink
Browse files

first working version of extension with tests

  • Loading branch information...
1 parent 1792fab commit dd18f062afd1062a56778b58af7c53be4ed59ad7 @rtomayko committed May 21, 2008
Showing with 220 additions and 1 deletion.
  1. +10 −1 Rakefile
  2. +13 −0 bin/rpeg-markdown
  3. +3 −0 ext/extconf.rb
  4. +90 −0 ext/markdown.c
  5. +46 −0 ext/markdown_buffer.c
  6. +6 −0 ext/markdown_buffer.h
  7. +6 −0 ext/markdown_output.c
  8. +21 −0 lib/markdown.rb
  9. +25 −0 test.rb
View
@@ -3,7 +3,6 @@ require 'rake/clean'
DLEXT = Config::CONFIG['DLEXT']
-
desc 'Gather required peg-markdown sources into extension directory'
task :gather do |t|
sh 'cd peg-markdown && make markdown_parser.c'
@@ -29,3 +28,13 @@ end
desc 'Build the peg-markdown extension'
task :build => "lib/markdown.#{DLEXT}"
+
+task 'test:unit' => [ :build ] do |t|
+ ruby 'test.rb'
+end
+
+task 'test:conformance' => [ :build ] do |t|
+ chdir('peg-markdown/MarkdownTest_1.0.3') do
+ sh "./MarkdownTest.pl --script=../../bin/rpeg-markdown --tidy"
+ end
+end
View
@@ -0,0 +1,13 @@
+#!/usr/bin/env ruby
+
+begin
+ require 'markdown'
+rescue LoadError => boom
+ local_path = File.expand_path(File.dirname(__FILE__))
+ $: << "#{local_path}/../lib"
+ require 'markdown'
+end
+
+STDIN.reopen(ARGV[0], 'rb') if ARGV.any?
+markdown = Markdown.new(STDIN.read)
+STDOUT.write(markdown.to_html)
View
@@ -1,3 +1,6 @@
require 'mkmf'
+
+$CFLAGS = "-Wall"
+
dir_config('markdown')
create_makefile('markdown')
View
@@ -0,0 +1,90 @@
+#include "ruby.h"
+#include "markdown_peg.h"
+#include "markdown_buffer.h"
+
+static VALUE rb_cMarkdown;
+
+static ID id_text;
+static ID id_smart;
+static ID id_notes;
+
+#define TABSTOP 4
+#define INCREMENT 4096 /* size of chunks in which to allocate memory */
+
+static VALUE
+markdown_to_html(VALUE self)
+{
+ element parsed_input;
+ VALUE output_buffer;
+
+ char *inputbuf, *curchar;
+ int charstotab, buflength, maxlength;
+
+ /* grab char pointer to markdown input text */
+ VALUE text = rb_funcall(self, id_text, 0);
+ Check_Type(text, T_STRING);
+ char * ptext = StringValuePtr(text);
+
+ buflength = 0;
+ maxlength = RSTRING(text)->len >= INCREMENT ?
+ RSTRING(text)->len :
+ INCREMENT;
+ inputbuf = malloc(maxlength);
+ curchar = inputbuf;
+
+ charstotab = TABSTOP;
+ while ((*curchar = *ptext++) != '\0') {
+ switch (*curchar) {
+ case '\t':
+ while (charstotab > 0)
+ *curchar = ' ', curchar++, buflength++, charstotab--;
+ break;
+ case '\n':
+ curchar++, buflength++, charstotab = TABSTOP;
+ break;
+ default:
+ curchar++, buflength++, charstotab--;
+ }
+ if (charstotab == 0)
+ charstotab = TABSTOP;
+ if (buflength > maxlength - TABSTOP - 3) {
+ maxlength += INCREMENT;
+ inputbuf = realloc(inputbuf, maxlength);
+ curchar = inputbuf + buflength;
+ if (inputbuf == NULL) {
+ /* TODO: no memory */
+ }
+ }
+ }
+ *curchar++ = '\n';
+ *curchar++ = '\n';
+ *curchar = '\0';
+ buflength+= 2;
+
+ /* flip extension bits */
+ int extensions = 0;
+ if ( rb_funcall(self, id_smart, 0) == Qtrue )
+ extensions = extensions | EXT_SMART ;
+ if ( rb_funcall(self, id_notes, 0) == Qtrue )
+ extensions = extensions | EXT_NOTES ;
+
+ /* parse markdown input into sematic element tree */
+ parsed_input = markdown(inputbuf, extensions);
+
+ /* allocate output buffer and generate output */
+ output_buffer = rb_markdown_buffer_init(buflength * 2);
+ print_element(parsed_input, HTML_FORMAT);
+ rb_markdown_buffer_free();
+ return output_buffer;
+}
+
+void Init_markdown()
+{
+ /* Initialize frequently used Symbols */
+ id_text = rb_intern("text");
+ id_smart = rb_intern("smart");
+ id_notes = rb_intern("notes");
+
+ rb_cMarkdown = rb_define_class("Markdown", rb_cObject);
+ rb_define_method(rb_cMarkdown, "to_html", markdown_to_html, 0);
+}
View
@@ -0,0 +1,46 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include "ruby.h"
+#include "markdown_buffer.h"
+
+static VALUE markdown_buffer;
+static int registered = 0;
+
+VALUE rb_markdown_buffer_init(long size) {
+ if ( registered != 1 ) {
+ rb_gc_register_address(&markdown_buffer);
+ registered = 1;
+ }
+ markdown_buffer = rb_str_buf_new(size);
+ return markdown_buffer;
+}
+
+void rb_markdown_buffer_free(void) {
+ markdown_buffer = Qnil;
+}
+
+int rb_markdown_buffer_printf(const char * format, ...)
+{
+ va_list args;
+ int length;
+ char * buf = NULL;
+
+ va_start(args, format);
+ length = vasprintf(&buf, format, args);
+ va_end(args);
+
+ if ( buf != NULL ) {
+ rb_str_buf_cat(markdown_buffer, buf, length);
+ free(buf);
+ } else {
+ /* TODO: handle out of memory condition */
+ }
+
+ return length;
+}
+
+char rb_markdown_buffer_putchar(char c)
+{
+ rb_str_buf_cat(markdown_buffer, &c, 1);
+ return c;
+}
View
@@ -0,0 +1,6 @@
+#include "ruby.h"
+
+VALUE rb_markdown_buffer_init(long size);
+void rb_markdown_buffer_free(void);
+int rb_markdown_buffer_printf(const char * format, ...);
+char rb_markdown_buffer_putchar(char c);
View
@@ -22,6 +22,12 @@
#include <assert.h>
#include "markdown_peg.h"
+/***********************************************************************/
+#include "markdown_buffer.h"
+#define printf rb_markdown_buffer_printf
+#define putchar rb_markdown_buffer_putchar
+/***********************************************************************/
+
/* TODO remove */
static extensions = 0;
View
@@ -0,0 +1,21 @@
+require 'markdown.so'
+
+class Markdown
+
+ # Original Markdown formatted text.
+ attr_reader :text
+
+ # Whether smarty-like quote translation should be performed.
+ attr_accessor :smart
+
+ # Whether footnotes should be
+ attr_accessor :notes
+
+ def initialize(text, *extensions)
+ @smart = false
+ @notes = false
+ @text = text
+ extensions.each { |e| send("#{e}=", true) }
+ end
+
+end
View
25 test.rb
@@ -0,0 +1,25 @@
+$: << File.join(File.dirname(__FILE__), "lib")
+
+require 'test/unit'
+require 'markdown'
+
+class MarkdownTest < Test::Unit::TestCase
+
+ def test_that_extension_methods_are_present_on_markdown_class
+ assert Markdown.instance_methods.include?('to_html'),
+ "Markdown class should respond to #to_html"
+ end
+
+ def test_converting_simple_string_to_html
+ markdown = Markdown.new('Hello World.')
+ assert_respond_to markdown, :to_html
+ assert_equal "\n\n<p>Hello World.</p>", markdown.to_html
+ end
+
+ def test_converting_markup_string_to_html
+ markdown = Markdown.new('_Hello World_!')
+ assert_respond_to markdown, :to_html
+ assert_equal '<p><em>Hello World</em>!</p>', markdown.to_html
+ end
+
+end

0 comments on commit dd18f06

Please sign in to comment.