diff options
author | John Barnette <jbarnette@gmail.com> | 2009-09-29 10:48:00 -0700 |
---|---|---|
committer | John Barnette <jbarnette@gmail.com> | 2009-09-29 10:48:00 -0700 |
commit | 28fcaf328982d06ff006d2b6b13ef75d125cab23 (patch) | |
tree | 020a6180df7556372ef1682b04b472577e29222d | |
parent | 6d840a576b6cc627a34cd5c46a07a6aee8d54c1c (diff) | |
parent | d2bf942ac38b1b6e91116a06855a00d32b95d27b (diff) | |
download | psych-28fcaf328982d06ff006d2b6b13ef75d125cab23.zip |
Merge branch 'master' of git@github.com:tenderlove/psych
-rw-r--r-- | ext/psych/emitter.c | 192 | ||||
-rw-r--r-- | ext/psych/emitter.h | 8 | ||||
-rw-r--r-- | ext/psych/psych.c | 1 | ||||
-rw-r--r-- | ext/psych/psych.h | 1 | ||||
-rw-r--r-- | lib/psych.rb | 8 | ||||
-rw-r--r-- | lib/psych/emitter.rb | 4 | ||||
-rw-r--r-- | lib/psych/nodes/document.rb | 2 | ||||
-rw-r--r-- | lib/psych/nodes/node.rb | 8 | ||||
-rw-r--r-- | lib/psych/nodes/scalar.rb | 2 | ||||
-rw-r--r-- | lib/psych/tree_builder.rb | 5 | ||||
-rw-r--r-- | lib/psych/visitors.rb | 1 | ||||
-rw-r--r-- | lib/psych/visitors/emitter.rb | 31 | ||||
-rw-r--r-- | lib/psych/visitors/to_ruby.rb | 14 | ||||
-rw-r--r-- | test/test_psych.rb | 6 | ||||
-rw-r--r-- | test/visitors/test_emitter.rb | 63 | ||||
-rw-r--r-- | test/visitors/test_to_ruby.rb | 24 |
16 files changed, 358 insertions, 12 deletions
diff --git a/ext/psych/emitter.c b/ext/psych/emitter.c new file mode 100644 index 0000000..7b8a43a --- /dev/null +++ b/ext/psych/emitter.c @@ -0,0 +1,192 @@ +#include <psych.h> + +VALUE cPsychEmitter; + +static void emit(yaml_emitter_t * emitter, yaml_event_t * event) +{ + if(!yaml_emitter_emit(emitter, event)) + rb_raise(rb_eRuntimeError, emitter->problem); +} + +static int writer(void *ctx, unsigned char *buffer, size_t size) +{ + VALUE io = (VALUE)ctx; + VALUE str = rb_str_new((const char *)buffer, (long)size); + VALUE wrote = rb_funcall(io, rb_intern("write"), 1, str); + return (int)NUM2INT(wrote); +} + +static void dealloc(yaml_emitter_t * emitter) +{ + yaml_emitter_delete(emitter); + free(emitter); +} + +static VALUE allocate(VALUE klass) +{ + yaml_emitter_t * emitter = malloc(sizeof(yaml_emitter_t)); + yaml_emitter_initialize(emitter); + return Data_Wrap_Struct(cPsychEmitter, 0, dealloc, emitter); +} + +static VALUE initialize(VALUE self, VALUE io) +{ + yaml_emitter_t * emitter; + Data_Get_Struct(self, yaml_emitter_t, emitter); + + yaml_emitter_set_output(emitter, writer, (void *)io); + + return self; +} + +static VALUE start_stream(VALUE self, VALUE encoding) +{ + yaml_emitter_t * emitter; + Data_Get_Struct(self, yaml_emitter_t, emitter); + + yaml_event_t event; + yaml_stream_start_event_initialize(&event, (yaml_encoding_t)NUM2INT(encoding)); + + emit(emitter, &event); + + return self; +} + +static VALUE end_stream(VALUE self) +{ + yaml_emitter_t * emitter; + Data_Get_Struct(self, yaml_emitter_t, emitter); + + yaml_event_t event; + yaml_stream_end_event_initialize(&event); + + emit(emitter, &event); + + return self; +} + +static VALUE start_document(VALUE self, VALUE version, VALUE tags, VALUE imp) +{ + yaml_emitter_t * emitter; + Data_Get_Struct(self, yaml_emitter_t, emitter); + + yaml_version_directive_t version_directive; + + if(RARRAY_LEN(version) > 0) { + VALUE major = rb_ary_entry(version, (long)0); + VALUE minor = rb_ary_entry(version, (long)1); + + version_directive.major = NUM2INT(major); + version_directive.minor = NUM2INT(minor); + } + + yaml_event_t event; + yaml_document_start_event_initialize( + &event, + (RARRAY_LEN(version) > 0) ? &version_directive : NULL, + NULL, + NULL, + imp == Qtrue ? 1 : 0 + ); + + emit(emitter, &event); + + return self; +} + +static VALUE end_document(VALUE self, VALUE imp) +{ + yaml_emitter_t * emitter; + Data_Get_Struct(self, yaml_emitter_t, emitter); + + yaml_event_t event; + yaml_document_end_event_initialize(&event, imp == Qtrue ? 1 : 0); + + emit(emitter, &event); + + return self; +} + +static VALUE scalar( + VALUE self, + VALUE value, + VALUE anchor, + VALUE tag, + VALUE plain, + VALUE quoted, + VALUE style +) { + yaml_emitter_t * emitter; + Data_Get_Struct(self, yaml_emitter_t, emitter); + + yaml_event_t event; + yaml_scalar_event_initialize( + &event, + (yaml_char_t *)(Qnil == anchor ? NULL : StringValuePtr(anchor)), + (yaml_char_t *)(Qnil == tag ? NULL : StringValuePtr(tag)), + (yaml_char_t*)StringValuePtr(value), + (int)RSTRING_LEN(value), + Qtrue == plain ? 1 : 0, + Qtrue == quoted ? 1 : 0, + (yaml_scalar_style_t)NUM2INT(style) + ); + + emit(emitter, &event); + + return self; +} + +static VALUE start_sequence( + VALUE self, + VALUE anchor, + VALUE tag, + VALUE implicit, + VALUE style +) { + yaml_emitter_t * emitter; + Data_Get_Struct(self, yaml_emitter_t, emitter); + + yaml_event_t event; + yaml_sequence_start_event_initialize( + &event, + (yaml_char_t *)(Qnil == anchor ? NULL : StringValuePtr(anchor)), + (yaml_char_t *)(Qnil == anchor ? NULL : StringValuePtr(tag)), + Qtrue == implicit ? 1 : 0, + (yaml_sequence_style_t)NUM2INT(style) + ); + + emit(emitter, &event); + + return self; +} + +static VALUE end_sequence(VALUE self) +{ + yaml_emitter_t * emitter; + Data_Get_Struct(self, yaml_emitter_t, emitter); + + yaml_event_t event; + yaml_sequence_end_event_initialize(&event); + + emit(emitter, &event); + + return self; +} + +void Init_psych_emitter() +{ + VALUE psych = rb_define_module("Psych"); + VALUE handler = rb_define_class_under(psych, "Handler", rb_cObject); + cPsychEmitter = rb_define_class_under(psych, "Emitter", handler); + + rb_define_alloc_func(cPsychEmitter, allocate); + + rb_define_method(cPsychEmitter, "initialize", initialize, 1); + rb_define_method(cPsychEmitter, "start_stream", start_stream, 1); + rb_define_method(cPsychEmitter, "end_stream", end_stream, 0); + rb_define_method(cPsychEmitter, "start_document", start_document, 3); + rb_define_method(cPsychEmitter, "end_document", end_document, 1); + rb_define_method(cPsychEmitter, "scalar", scalar, 6); + rb_define_method(cPsychEmitter, "start_sequence", start_sequence, 4); + rb_define_method(cPsychEmitter, "end_sequence", end_sequence, 0); +} diff --git a/ext/psych/emitter.h b/ext/psych/emitter.h new file mode 100644 index 0000000..560451e --- /dev/null +++ b/ext/psych/emitter.h @@ -0,0 +1,8 @@ +#ifndef PSYCH_EMITTER_H +#define PSYCH_EMITTER_H + +#include <psych.h> + +void Init_psych_emitter(); + +#endif diff --git a/ext/psych/psych.c b/ext/psych/psych.c index dfba7eb..9d40fe3 100644 --- a/ext/psych/psych.c +++ b/ext/psych/psych.c @@ -24,4 +24,5 @@ void Init_psych() rb_define_singleton_method(mPsych, "libyaml_version", libyaml_version, 0); Init_psych_parser(); + Init_psych_emitter(); } diff --git a/ext/psych/psych.h b/ext/psych/psych.h index 656bc73..eea82f8 100644 --- a/ext/psych/psych.h +++ b/ext/psych/psych.h @@ -5,6 +5,7 @@ #include <yaml.h> #include <parser.h> +#include <emitter.h> extern VALUE mPsych; diff --git a/lib/psych.rb b/lib/psych.rb index bdc2458..bbb8662 100644 --- a/lib/psych.rb +++ b/lib/psych.rb @@ -19,7 +19,11 @@ module Psych VERSION = '1.0.0' LIBYAML_VERSION = Psych.libyaml_version.join '.' - def self.parse thing - Psych::Parser.new.parse thing + ### + # Load +yaml+ in to a Ruby data structure + def self.load yaml + parser = Psych::Parser.new(TreeBuilder.new) + parser.parse yaml + parser.handler.root.children.first.to_ruby end end diff --git a/lib/psych/emitter.rb b/lib/psych/emitter.rb new file mode 100644 index 0000000..b06ad3a --- /dev/null +++ b/lib/psych/emitter.rb @@ -0,0 +1,4 @@ +module Psych + class Emitter < Psych::Handler + end +end diff --git a/lib/psych/nodes/document.rb b/lib/psych/nodes/document.rb index 0bd58b2..be786c6 100644 --- a/lib/psych/nodes/document.rb +++ b/lib/psych/nodes/document.rb @@ -10,7 +10,7 @@ module Psych # Was this document implicitly created? attr_accessor :implicit - def initialize version = [], tag_directives = [], implicit = true + def initialize version = [], tag_directives = [], implicit = false super() @version = version @tag_directives = tag_directives diff --git a/lib/psych/nodes/node.rb b/lib/psych/nodes/node.rb index b5ae131..a63fc60 100644 --- a/lib/psych/nodes/node.rb +++ b/lib/psych/nodes/node.rb @@ -1,3 +1,5 @@ +require 'stringio' + module Psych module Nodes ### @@ -15,6 +17,12 @@ module Psych def to_ruby Visitors::ToRuby.new.accept self end + + def to_yaml + io = StringIO.new + Visitors::Emitter.new(io).accept self + io.string + end end end end diff --git a/lib/psych/nodes/scalar.rb b/lib/psych/nodes/scalar.rb index b84e0ed..fac4d69 100644 --- a/lib/psych/nodes/scalar.rb +++ b/lib/psych/nodes/scalar.rb @@ -29,7 +29,7 @@ module Psych # The style of this scalar attr_accessor :style - def initialize value, anchor = nil, tag = nil, plain = true, quoted = true, style = ANY + def initialize value, anchor = nil, tag = nil, plain = true, quoted = false, style = ANY super() @value = value @anchor = anchor diff --git a/lib/psych/tree_builder.rb b/lib/psych/tree_builder.rb index c158368..efbeaf9 100644 --- a/lib/psych/tree_builder.rb +++ b/lib/psych/tree_builder.rb @@ -23,31 +23,26 @@ module Psych }.each do |node| class_eval %{ def start_#{node.downcase}(*args) - super n = Nodes::#{node}.new(*args) @stack.last.children << n @stack.push n end def end_#{node.downcase}(*args) - super @stack.pop end } end def start_stream encoding - super @stack.push Nodes::Stream.new(encoding) end def scalar(*args) - super @stack.last.children << Nodes::Scalar.new(*args) end def alias(*args) - super @stack.last.children << Nodes::Alias.new(*args) end end diff --git a/lib/psych/visitors.rb b/lib/psych/visitors.rb index 45b4b58..df58e2e 100644 --- a/lib/psych/visitors.rb +++ b/lib/psych/visitors.rb @@ -1,2 +1,3 @@ require 'psych/visitors/visitor' require 'psych/visitors/to_ruby' +require 'psych/visitors/emitter' diff --git a/lib/psych/visitors/emitter.rb b/lib/psych/visitors/emitter.rb new file mode 100644 index 0000000..a0ff9d9 --- /dev/null +++ b/lib/psych/visitors/emitter.rb @@ -0,0 +1,31 @@ +module Psych + module Visitors + class Emitter < Psych::Visitors::Visitor + def initialize io + @handler = Psych::Emitter.new io + end + + visitor_for(Nodes::Stream) do |o| + @handler.start_stream o.encoding + o.children.each { |c| c.accept self } + @handler.end_stream + end + + visitor_for(Nodes::Document) do |o| + @handler.start_document o.version, o.tag_directives, o.implicit + o.children.each { |c| c.accept self } + @handler.end_document o.implicit + end + + visitor_for(Nodes::Scalar) do |o| + @handler.scalar o.value, o.anchor, o.tag, o.plain, o.quoted, o.style + end + + visitor_for(Nodes::Sequence) do |o| + @handler.start_sequence o.anchor, o.tag, o.implicit, o.style + o.children.each { |c| c.accept self } + @handler.end_sequence + end + end + end +end diff --git a/lib/psych/visitors/to_ruby.rb b/lib/psych/visitors/to_ruby.rb index a4e6152..8848c16 100644 --- a/lib/psych/visitors/to_ruby.rb +++ b/lib/psych/visitors/to_ruby.rb @@ -3,7 +3,13 @@ module Psych ### # This class walks a YAML AST, converting each node to ruby class ToRuby < Psych::Visitors::Visitor + def initialize + super + @st = {} + end + visitor_for(Nodes::Scalar) do |o| + @st[o.anchor] = o.value if o.anchor o.value end @@ -18,6 +24,14 @@ module Psych visitor_for(Nodes::Document) do |o| o.root.accept self end + + visitor_for(Nodes::Stream) do |o| + o.children.map { |c| c.accept self } + end + + visitor_for(Nodes::Alias) do |o| + @st[o.anchor] + end end end end diff --git a/test/test_psych.rb b/test/test_psych.rb index 865024f..96d0f69 100644 --- a/test/test_psych.rb +++ b/test/test_psych.rb @@ -2,9 +2,9 @@ require 'minitest/autorun' require 'psych' class TestPsych < MiniTest::Unit::TestCase - #def test_simple - # assert_equal 'foo', Psych.parse("--- foo\n") - #end + def test_simple + assert_equal 'foo', Psych.load("--- foo\n") + end def test_libyaml_version assert Psych.libyaml_version diff --git a/test/visitors/test_emitter.rb b/test/visitors/test_emitter.rb new file mode 100644 index 0000000..b7b9110 --- /dev/null +++ b/test/visitors/test_emitter.rb @@ -0,0 +1,63 @@ +require 'minitest/autorun' +require 'psych' + +module Psych + module Visitors + class TestEmitter < MiniTest::Unit::TestCase + def setup + @io = StringIO.new + @visitor = Visitors::Emitter.new @io + end + + def test_stream + s = Nodes::Stream.new + @visitor.accept s + assert_equal '', @io.string + end + + def test_document + s = Nodes::Stream.new + doc = Nodes::Document.new [1,1] + scalar = Nodes::Scalar.new 'hello world' + + doc.children << scalar + s.children << doc + + @visitor.accept s + + assert_match(/1.1/, @io.string) + assert_equal @io.string, s.to_yaml + end + + def test_scalar + s = Nodes::Stream.new + doc = Nodes::Document.new + scalar = Nodes::Scalar.new 'hello world' + + doc.children << scalar + s.children << doc + + @visitor.accept s + + assert_match(/hello/, @io.string) + assert_equal @io.string, s.to_yaml + end + + def test_sequence + s = Nodes::Stream.new + doc = Nodes::Document.new + scalar = Nodes::Scalar.new 'hello world' + seq = Nodes::Sequence.new + + seq.children << scalar + doc.children << seq + s.children << doc + + @visitor.accept s + + assert_match(/- hello/, @io.string) + assert_equal @io.string, s.to_yaml + end + end + end +end diff --git a/test/visitors/test_to_ruby.rb b/test/visitors/test_to_ruby.rb index c295caf..cc499b7 100644 --- a/test/visitors/test_to_ruby.rb +++ b/test/visitors/test_to_ruby.rb @@ -34,6 +34,30 @@ module Psych doc.children << Nodes::Scalar.new('foo') assert_equal 'foo', doc.to_ruby end + + def test_stream + a = Nodes::Document.new + a.children << Nodes::Scalar.new('foo') + + b = Nodes::Document.new + b.children << Nodes::Scalar.new('bar') + + stream = Nodes::Stream.new + stream.children << a + stream.children << b + + assert_equal %w{ foo bar }, stream.to_ruby + end + + def test_alias + seq = Nodes::Sequence.new + seq.children << Nodes::Scalar.new('foo', 'A') + seq.children << Nodes::Alias.new('A') + + list = seq.to_ruby + assert_equal %w{ foo foo }, list + assert_equal list[0].object_id, list[1].object_id + end end end end |