diff options
-rw-r--r-- | ext/psych/extconf.rb | 18 | ||||
-rw-r--r-- | ext/psych/parser.c | 60 | ||||
-rw-r--r-- | ext/psych/parser.h | 6 | ||||
-rw-r--r-- | ext/psych/psych.c | 27 | ||||
-rw-r--r-- | ext/psych/psych.h | 11 | ||||
-rw-r--r-- | lib/psych.rb | 18 | ||||
-rw-r--r-- | lib/psych/parser.rb | 22 | ||||
-rw-r--r-- | lib/psych/parser/handler.rb | 22 | ||||
-rw-r--r-- | test/helper.rb | 2 | ||||
-rw-r--r-- | test/psych/test_parser.rb | 49 | ||||
-rw-r--r-- | test/test_psych.rb | 12 |
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 |