summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Barnette <jbarnette@gmail.com>2009-09-29 10:48:00 -0700
committerJohn Barnette <jbarnette@gmail.com>2009-09-29 10:48:00 -0700
commit28fcaf328982d06ff006d2b6b13ef75d125cab23 (patch)
tree020a6180df7556372ef1682b04b472577e29222d
parent6d840a576b6cc627a34cd5c46a07a6aee8d54c1c (diff)
parentd2bf942ac38b1b6e91116a06855a00d32b95d27b (diff)
downloadpsych-28fcaf328982d06ff006d2b6b13ef75d125cab23.zip
Merge branch 'master' of git@github.com:tenderlove/psych
-rw-r--r--ext/psych/emitter.c192
-rw-r--r--ext/psych/emitter.h8
-rw-r--r--ext/psych/psych.c1
-rw-r--r--ext/psych/psych.h1
-rw-r--r--lib/psych.rb8
-rw-r--r--lib/psych/emitter.rb4
-rw-r--r--lib/psych/nodes/document.rb2
-rw-r--r--lib/psych/nodes/node.rb8
-rw-r--r--lib/psych/nodes/scalar.rb2
-rw-r--r--lib/psych/tree_builder.rb5
-rw-r--r--lib/psych/visitors.rb1
-rw-r--r--lib/psych/visitors/emitter.rb31
-rw-r--r--lib/psych/visitors/to_ruby.rb14
-rw-r--r--test/test_psych.rb6
-rw-r--r--test/visitors/test_emitter.rb63
-rw-r--r--test/visitors/test_to_ruby.rb24
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