summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/psych/extconf.rb18
-rw-r--r--ext/psych/parser.c60
-rw-r--r--ext/psych/parser.h6
-rw-r--r--ext/psych/psych.c27
-rw-r--r--ext/psych/psych.h11
-rw-r--r--lib/psych.rb18
-rw-r--r--lib/psych/parser.rb22
-rw-r--r--lib/psych/parser/handler.rb22
-rw-r--r--test/helper.rb2
-rw-r--r--test/psych/test_parser.rb49
-rw-r--r--test/test_psych.rb12
11 files changed, 240 insertions, 7 deletions
diff --git a/ext/psych/extconf.rb b/ext/psych/extconf.rb
index 5e50432..a859c98 100644
--- a/ext/psych/extconf.rb
+++ b/ext/psych/extconf.rb
@@ -2,8 +2,24 @@ require 'mkmf'
$CFLAGS << " -O3 -Wall -Wcast-qual -Wwrite-strings -Wconversion -Wmissing-noreturn -Winline"
+LIBDIR = Config::CONFIG['libdir']
+INCLUDEDIR = Config::CONFIG['includedir']
+
+LIB_DIRS = [
+ '/opt/local/lib',
+ '/usr/local/lib',
+ LIBDIR,
+ '/usr/lib',
+]
+
libyaml = dir_config('libyaml', '/opt/local/include', '/opt/local/lib')
-find_header('yaml.h')
+unless find_header('yaml.h')
+ abort "yaml.y is missing. try 'port install libyaml +universal' or 'yum install libyaml-devel'"
+end
+
+unless find_library('yaml', 'yaml_get_version')
+ abort "libyaml is missing. try 'port install libyaml +universal' or 'yum install libyaml-devel'"
+end
create_makefile('psych/psych')
diff --git a/ext/psych/parser.c b/ext/psych/parser.c
new file mode 100644
index 0000000..b984739
--- /dev/null
+++ b/ext/psych/parser.c
@@ -0,0 +1,60 @@
+#include <psych.h>
+
+static VALUE parse_string(VALUE self, VALUE string)
+{
+ yaml_parser_t parser;
+ yaml_event_t event;
+
+ yaml_parser_initialize(&parser);
+
+ yaml_parser_set_input_string(
+ &parser,
+ StringValuePtr(string),
+ RSTRING_LEN(string)
+ );
+
+ int done = 0;
+
+ VALUE handler = rb_iv_get(self, "@handler");
+
+ while(!done) {
+ if(!yaml_parser_parse(&parser, &event)) {
+ yaml_parser_delete(&parser);
+ rb_raise(rb_eRuntimeError, "couldn't parse YAML");
+ }
+
+ switch(event.type) {
+ case YAML_STREAM_START_EVENT:
+ rb_funcall(handler, rb_intern("start_stream"), 1,
+ INT2NUM((long)event.data.stream_start.encoding)
+ );
+ break;
+ case YAML_DOCUMENT_START_EVENT:
+ {
+ VALUE version = event.data.document_start.version_directive ?
+ rb_ary_new3(
+ (long)2,
+ INT2NUM((long)event.data.document_start.version_directive->major),
+ INT2NUM((long)event.data.document_start.version_directive->minor)
+ ) : rb_ary_new();
+ rb_funcall(handler, rb_intern("start_document"), 1, version);
+ }
+ break;
+ case YAML_STREAM_END_EVENT:
+ rb_funcall(handler, rb_intern("end_stream"), 0);
+ done = 1;
+ break;
+ }
+ }
+
+ return self;
+}
+
+VALUE cPsychParser;
+
+void Init_psych_parser()
+{
+ cPsychParser = rb_define_class_under(mPsych, "Parser", rb_cObject);
+
+ rb_define_private_method(cPsychParser, "parse_string", parse_string, 1);
+}
diff --git a/ext/psych/parser.h b/ext/psych/parser.h
new file mode 100644
index 0000000..c4536ed
--- /dev/null
+++ b/ext/psych/parser.h
@@ -0,0 +1,6 @@
+#ifndef PSYCH_PARSER_H
+#define PSYCH_PARSER_H
+
+void Init_parser();
+
+#endif
diff --git a/ext/psych/psych.c b/ext/psych/psych.c
new file mode 100644
index 0000000..dfba7eb
--- /dev/null
+++ b/ext/psych/psych.c
@@ -0,0 +1,27 @@
+#include <psych.h>
+
+static VALUE libyaml_version(VALUE module)
+{
+ int major, minor, patch;
+
+ yaml_get_version(&major, &minor, &patch);
+
+ VALUE list[3] = {
+ INT2NUM((long)major),
+ INT2NUM((long)minor),
+ INT2NUM((long)patch)
+ };
+
+ return rb_ary_new4((long)3, list);
+}
+
+VALUE mPsych;
+
+void Init_psych()
+{
+ mPsych = rb_define_module("Psych");
+
+ rb_define_singleton_method(mPsych, "libyaml_version", libyaml_version, 0);
+
+ Init_psych_parser();
+}
diff --git a/ext/psych/psych.h b/ext/psych/psych.h
new file mode 100644
index 0000000..656bc73
--- /dev/null
+++ b/ext/psych/psych.h
@@ -0,0 +1,11 @@
+#ifndef PSYCH_H
+#define PSYCH_H
+
+#include <ruby.h>
+#include <yaml.h>
+
+#include <parser.h>
+
+extern VALUE mPsych;
+
+#endif
diff --git a/lib/psych.rb b/lib/psych.rb
index 31cd7f6..df00156 100644
--- a/lib/psych.rb
+++ b/lib/psych.rb
@@ -1,3 +1,17 @@
-class Psych
- VERSION = '1.0.0'
+require 'psych/parser'
+require 'psych/psych'
+
+module Psych
+ VERSION = '1.0.0'
+ LIBYAML_VERSION = Psych.libyaml_version.join '.'
+
+ # Encodings supported by Psych (and libyaml)
+ ANY_ENCODING = 1
+ UTF8_ENCODING = 2
+ UTF16LE_ENCODING = 3
+ UTF16BE_ENCODING = 4
+
+ def self.parse thing
+ Psych::Parser.new.parse thing
+ end
end
diff --git a/lib/psych/parser.rb b/lib/psych/parser.rb
new file mode 100644
index 0000000..736521f
--- /dev/null
+++ b/lib/psych/parser.rb
@@ -0,0 +1,22 @@
+require 'psych/parser/handler'
+
+module Psych
+ ###
+ # YAML parser class.
+ #
+ # Example:
+ #
+ # parser = Psych::Parser.new
+ # parser.parse(some_yaml)
+ class Parser
+ attr_accessor :handler
+
+ def initialize handler = Handler.new
+ @handler = handler
+ end
+
+ def parse string
+ parse_string string
+ end
+ end
+end
diff --git a/lib/psych/parser/handler.rb b/lib/psych/parser/handler.rb
new file mode 100644
index 0000000..c3e6f1d
--- /dev/null
+++ b/lib/psych/parser/handler.rb
@@ -0,0 +1,22 @@
+module Psych
+ class Parser
+ ###
+ # Default event handlers used in conjunction with Psych::Parser
+ class Handler
+ ###
+ # Called with +encoding+ when the YAML stream starts.
+ def start_stream encoding
+ end
+
+ ###
+ # Called when the document starts with the declared +version+
+ def start_document version = []
+ end
+
+ ###
+ # Called when the YAML stream ends
+ def end_stream
+ end
+ end
+ end
+end
diff --git a/test/helper.rb b/test/helper.rb
new file mode 100644
index 0000000..8e77fed
--- /dev/null
+++ b/test/helper.rb
@@ -0,0 +1,2 @@
+require "test/unit"
+require "psych"
diff --git a/test/psych/test_parser.rb b/test/psych/test_parser.rb
new file mode 100644
index 0000000..748d67b
--- /dev/null
+++ b/test/psych/test_parser.rb
@@ -0,0 +1,49 @@
+require 'helper'
+
+module Psych
+ class TestParser < Test::Unit::TestCase
+ class EventCatcher < Parser::Handler
+ attr_reader :calls
+ def initialize
+ @calls = []
+ end
+
+ (Parser::Handler.instance_methods(true) -
+ Object.instance_methods).each do |m|
+ class_eval %{
+ def #{m} *args
+ super
+ @calls << [:#{m}, args]
+ end
+ }
+ end
+ end
+
+ def setup
+ @parser = Psych::Parser.new EventCatcher.new
+ end
+
+ def test_end_stream
+ @parser.parse("--- foo\n")
+ assert_called :end_stream
+ end
+
+ def test_start_stream
+ @parser.parse("--- foo\n")
+ assert_called :start_stream
+ end
+
+ def test_start_document
+ @parser.parse("%YAML 1.1\n---\n\"foo\"\n")
+ assert_called :start_document, [[1,1]]
+ end
+
+ def assert_called call, with = nil, parser = @parser
+ if with
+ assert parser.handler.calls.any? { |x| x == [call, with] }
+ else
+ assert parser.handler.calls.any? { |x| x.first == call }
+ end
+ end
+ end
+end
diff --git a/test/test_psych.rb b/test/test_psych.rb
index 33a3cc0..dbf892a 100644
--- a/test/test_psych.rb
+++ b/test/test_psych.rb
@@ -1,8 +1,12 @@
-require "test/unit"
-require "psych"
+require 'helper'
class TestPsych < Test::Unit::TestCase
- def test_sanity
- flunk "write tests or I will kneecap you"
+ def test_simple
+ assert_equal 'foo', Psych.parse("--- foo\n")
+ end
+
+ def test_libyaml_version
+ assert Psych.libyaml_version
+ assert_equal Psych.libyaml_version.join('.'), Psych::LIBYAML_VERSION
end
end