psych 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. data/.autotest +18 -0
  2. data/.gemtest +0 -0
  3. data/CHANGELOG.rdoc +3 -0
  4. data/Manifest.txt +87 -0
  5. data/README.rdoc +50 -0
  6. data/Rakefile +66 -0
  7. data/ext/psych/emitter.c +517 -0
  8. data/ext/psych/emitter.h +8 -0
  9. data/ext/psych/extconf.rb +22 -0
  10. data/ext/psych/parser.c +384 -0
  11. data/ext/psych/parser.h +6 -0
  12. data/ext/psych/psych.c +34 -0
  13. data/ext/psych/psych.h +20 -0
  14. data/ext/psych/to_ruby.c +41 -0
  15. data/ext/psych/to_ruby.h +8 -0
  16. data/ext/psych/yaml_tree.c +24 -0
  17. data/ext/psych/yaml_tree.h +8 -0
  18. data/lib/psych.rb +263 -0
  19. data/lib/psych/coder.rb +94 -0
  20. data/lib/psych/core_ext.rb +39 -0
  21. data/lib/psych/deprecated.rb +82 -0
  22. data/lib/psych/handler.rb +221 -0
  23. data/lib/psych/json.rb +6 -0
  24. data/lib/psych/json/ruby_events.rb +19 -0
  25. data/lib/psych/json/stream.rb +15 -0
  26. data/lib/psych/json/tree_builder.rb +12 -0
  27. data/lib/psych/json/yaml_events.rb +29 -0
  28. data/lib/psych/nodes.rb +77 -0
  29. data/lib/psych/nodes/alias.rb +18 -0
  30. data/lib/psych/nodes/document.rb +60 -0
  31. data/lib/psych/nodes/mapping.rb +56 -0
  32. data/lib/psych/nodes/node.rb +52 -0
  33. data/lib/psych/nodes/scalar.rb +67 -0
  34. data/lib/psych/nodes/sequence.rb +81 -0
  35. data/lib/psych/nodes/stream.rb +37 -0
  36. data/lib/psych/omap.rb +4 -0
  37. data/lib/psych/parser.rb +47 -0
  38. data/lib/psych/scalar_scanner.rb +105 -0
  39. data/lib/psych/set.rb +4 -0
  40. data/lib/psych/stream.rb +36 -0
  41. data/lib/psych/streaming.rb +22 -0
  42. data/lib/psych/tree_builder.rb +94 -0
  43. data/lib/psych/visitors.rb +6 -0
  44. data/lib/psych/visitors/depth_first.rb +26 -0
  45. data/lib/psych/visitors/emitter.rb +44 -0
  46. data/lib/psych/visitors/json_tree.rb +21 -0
  47. data/lib/psych/visitors/to_ruby.rb +267 -0
  48. data/lib/psych/visitors/visitor.rb +19 -0
  49. data/lib/psych/visitors/yaml_tree.rb +373 -0
  50. data/test/psych/helper.rb +63 -0
  51. data/test/psych/json/test_stream.rb +109 -0
  52. data/test/psych/nodes/test_enumerable.rb +43 -0
  53. data/test/psych/test_alias_and_anchor.rb +26 -0
  54. data/test/psych/test_array.rb +19 -0
  55. data/test/psych/test_boolean.rb +36 -0
  56. data/test/psych/test_class.rb +17 -0
  57. data/test/psych/test_coder.rb +184 -0
  58. data/test/psych/test_date_time.rb +17 -0
  59. data/test/psych/test_deprecated.rb +210 -0
  60. data/test/psych/test_document.rb +46 -0
  61. data/test/psych/test_emitter.rb +94 -0
  62. data/test/psych/test_encoding.rb +179 -0
  63. data/test/psych/test_engine_manager.rb +57 -0
  64. data/test/psych/test_exception.rb +39 -0
  65. data/test/psych/test_hash.rb +30 -0
  66. data/test/psych/test_json_tree.rb +65 -0
  67. data/test/psych/test_merge_keys.rb +72 -0
  68. data/test/psych/test_nil.rb +18 -0
  69. data/test/psych/test_null.rb +19 -0
  70. data/test/psych/test_object.rb +27 -0
  71. data/test/psych/test_omap.rb +68 -0
  72. data/test/psych/test_parser.rb +297 -0
  73. data/test/psych/test_psych.rb +168 -0
  74. data/test/psych/test_scalar.rb +11 -0
  75. data/test/psych/test_scalar_scanner.rb +69 -0
  76. data/test/psych/test_serialize_subclasses.rb +38 -0
  77. data/test/psych/test_set.rb +49 -0
  78. data/test/psych/test_stream.rb +49 -0
  79. data/test/psych/test_string.rb +49 -0
  80. data/test/psych/test_struct.rb +51 -0
  81. data/test/psych/test_symbol.rb +17 -0
  82. data/test/psych/test_to_yaml_properties.rb +63 -0
  83. data/test/psych/test_tree_builder.rb +79 -0
  84. data/test/psych/test_yaml.rb +1256 -0
  85. data/test/psych/visitors/test_depth_first.rb +49 -0
  86. data/test/psych/visitors/test_emitter.rb +144 -0
  87. data/test/psych/visitors/test_to_ruby.rb +325 -0
  88. data/test/psych/visitors/test_yaml_tree.rb +155 -0
  89. metadata +232 -0
@@ -0,0 +1,18 @@
1
+ module Psych
2
+ module Nodes
3
+ ###
4
+ # This class represents a {YAML Alias}[http://yaml.org/spec/1.1/#alias].
5
+ # It points to an +anchor+.
6
+ #
7
+ # A Psych::Nodes::Alias is a terminal node and may have no children.
8
+ class Alias < Psych::Nodes::Node
9
+ # The anchor this alias links to
10
+ attr_accessor :anchor
11
+
12
+ # Create a new Alias that points to an +anchor+
13
+ def initialize anchor
14
+ @anchor = anchor
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,60 @@
1
+ module Psych
2
+ module Nodes
3
+ ###
4
+ # This represents a YAML Document. This node must be a child of
5
+ # Psych::Nodes::Stream. A Psych::Nodes::Document must have one child,
6
+ # and that child may be one of the following:
7
+ #
8
+ # * Psych::Nodes::Sequence
9
+ # * Psych::Nodes::Mapping
10
+ # * Psych::Nodes::Scalar
11
+ class Document < Psych::Nodes::Node
12
+ # The version of the YAML document
13
+ attr_accessor :version
14
+
15
+ # A list of tag directives for this document
16
+ attr_accessor :tag_directives
17
+
18
+ # Was this document implicitly created?
19
+ attr_accessor :implicit
20
+
21
+ # Is the end of the document implicit?
22
+ attr_accessor :implicit_end
23
+
24
+ ###
25
+ # Create a new Psych::Nodes::Document object.
26
+ #
27
+ # +version+ is a list indicating the YAML version.
28
+ # +tags_directives+ is a list of tag directive declarations
29
+ # +implicit+ is a flag indicating whether the document will be implicitly
30
+ # started.
31
+ #
32
+ # == Example:
33
+ # This creates a YAML document object that represents a YAML 1.1 document
34
+ # with one tag directive, and has an implicit start:
35
+ #
36
+ # Psych::Nodes::Document.new(
37
+ # [1,1],
38
+ # [["!", "tag:tenderlovemaking.com,2009:"]],
39
+ # true
40
+ # )
41
+ #
42
+ # == See Also
43
+ # See also Psych::Handler#start_document
44
+ def initialize version = [], tag_directives = [], implicit = false
45
+ super()
46
+ @version = version
47
+ @tag_directives = tag_directives
48
+ @implicit = implicit
49
+ @implicit_end = true
50
+ end
51
+
52
+ ###
53
+ # Returns the root node. A Document may only have one root node:
54
+ # http://yaml.org/spec/1.1/#id898031
55
+ def root
56
+ children.first
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,56 @@
1
+ module Psych
2
+ module Nodes
3
+ ###
4
+ # This class represents a {YAML Mapping}[http://yaml.org/spec/1.1/#mapping].
5
+ #
6
+ # A Psych::Nodes::Mapping node may have 0 or more children, but must have
7
+ # an even number of children. Here are the valid children a
8
+ # Psych::Nodes::Mapping node may have:
9
+ #
10
+ # * Psych::Nodes::Sequence
11
+ # * Psych::Nodes::Mapping
12
+ # * Psych::Nodes::Scalar
13
+ # * Psych::Nodes::Alias
14
+ class Mapping < Psych::Nodes::Node
15
+ # Any Map Style
16
+ ANY = 0
17
+
18
+ # Block Map Style
19
+ BLOCK = 1
20
+
21
+ # Flow Map Style
22
+ FLOW = 2
23
+
24
+ # The optional anchor for this mapping
25
+ attr_accessor :anchor
26
+
27
+ # The optional tag for this mapping
28
+ attr_accessor :tag
29
+
30
+ # Is this an implicit mapping?
31
+ attr_accessor :implicit
32
+
33
+ # The style of this mapping
34
+ attr_accessor :style
35
+
36
+ ###
37
+ # Create a new Psych::Nodes::Mapping object.
38
+ #
39
+ # +anchor+ is the anchor associated with the map or +nil+.
40
+ # +tag+ is the tag associated with the map or +nil+.
41
+ # +implicit+ is a boolean indicating whether or not the map was implicitly
42
+ # started.
43
+ # +style+ is an integer indicating the mapping style.
44
+ #
45
+ # == See Also
46
+ # See also Psych::Handler#start_mapping
47
+ def initialize anchor = nil, tag = nil, implicit = true, style = BLOCK
48
+ super()
49
+ @anchor = anchor
50
+ @tag = tag
51
+ @implicit = implicit
52
+ @style = style
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,52 @@
1
+ require 'stringio'
2
+
3
+ module Psych
4
+ module Nodes
5
+ ###
6
+ # The base class for any Node in a YAML parse tree. This class should
7
+ # never be instantiated.
8
+ class Node
9
+ include Enumerable
10
+
11
+ # The children of this node
12
+ attr_reader :children
13
+
14
+ # An associated tag
15
+ attr_reader :tag
16
+
17
+ # Create a new Psych::Nodes::Node
18
+ def initialize
19
+ @children = []
20
+ end
21
+
22
+ ###
23
+ # Iterate over each node in the tree. Yields each node to +block+ depth
24
+ # first.
25
+ def each &block
26
+ return enum_for :each unless block_given?
27
+ Visitors::DepthFirst.new(block).accept self
28
+ end
29
+
30
+ ###
31
+ # Convert this node to Ruby.
32
+ #
33
+ # See also Psych::Visitors::ToRuby
34
+ def to_ruby
35
+ Visitors::ToRuby.new.accept self
36
+ end
37
+ alias :transform :to_ruby
38
+
39
+ ###
40
+ # Convert this node to YAML.
41
+ #
42
+ # See also Psych::Visitors::Emitter
43
+ def to_yaml io = nil, options = {}
44
+ real_io = io || StringIO.new
45
+
46
+ Visitors::Emitter.new(real_io, options).accept self
47
+ return real_io.string unless io
48
+ io
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,67 @@
1
+ module Psych
2
+ module Nodes
3
+ ###
4
+ # This class represents a {YAML Scalar}[http://yaml.org/spec/1.1/#id858081].
5
+ #
6
+ # This node type is a terminal node and should not have any children.
7
+ class Scalar < Psych::Nodes::Node
8
+ # Any style scalar, the emitter chooses
9
+ ANY = 0
10
+
11
+ # Plain scalar style
12
+ PLAIN = 1
13
+
14
+ # Single quoted style
15
+ SINGLE_QUOTED = 2
16
+
17
+ # Double quoted style
18
+ DOUBLE_QUOTED = 3
19
+
20
+ # Literal style
21
+ LITERAL = 4
22
+
23
+ # Folded style
24
+ FOLDED = 5
25
+
26
+ # The scalar value
27
+ attr_accessor :value
28
+
29
+ # The anchor value (if there is one)
30
+ attr_accessor :anchor
31
+
32
+ # The tag value (if there is one)
33
+ attr_accessor :tag
34
+
35
+ # Is this a plain scalar?
36
+ attr_accessor :plain
37
+
38
+ # Is this scalar quoted?
39
+ attr_accessor :quoted
40
+
41
+ # The style of this scalar
42
+ attr_accessor :style
43
+
44
+ ###
45
+ # Create a new Psych::Nodes::Scalar object.
46
+ #
47
+ # +value+ is the string value of the scalar
48
+ # +anchor+ is an associated anchor or nil
49
+ # +tag+ is an associated tag or nil
50
+ # +plain+ is a boolean value
51
+ # +quoted+ is a boolean value
52
+ # +style+ is an integer idicating the string style
53
+ #
54
+ # == See Also
55
+ #
56
+ # See also Psych::Handler#scalar
57
+ def initialize value, anchor = nil, tag = nil, plain = true, quoted = false, style = ANY
58
+ @value = value
59
+ @anchor = anchor
60
+ @tag = tag
61
+ @plain = plain
62
+ @quoted = quoted
63
+ @style = style
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,81 @@
1
+ module Psych
2
+ module Nodes
3
+ ###
4
+ # This class represents a
5
+ # {YAML sequence}[http://yaml.org/spec/1.1/#sequence/syntax].
6
+ #
7
+ # A YAML sequence is basically a list, and looks like this:
8
+ #
9
+ # %YAML 1.1
10
+ # ---
11
+ # - I am
12
+ # - a Sequence
13
+ #
14
+ # A YAML sequence may have an anchor like this:
15
+ #
16
+ # %YAML 1.1
17
+ # ---
18
+ # &A [
19
+ # "This sequence",
20
+ # "has an anchor"
21
+ # ]
22
+ #
23
+ # A YAML sequence may also have a tag like this:
24
+ #
25
+ # %YAML 1.1
26
+ # ---
27
+ # !!seq [
28
+ # "This sequence",
29
+ # "has a tag"
30
+ # ]
31
+ #
32
+ # This class represents a sequence in a YAML document. A
33
+ # Psych::Nodes::Sequence node may have 0 or more children. Valid children
34
+ # for this node are:
35
+ #
36
+ # * Psych::Nodes::Sequence
37
+ # * Psych::Nodes::Mapping
38
+ # * Psych::Nodes::Scalar
39
+ # * Psych::Nodes::Alias
40
+ class Sequence < Psych::Nodes::Node
41
+ # Any Styles, emitter chooses
42
+ ANY = 0
43
+
44
+ # Block style sequence
45
+ BLOCK = 1
46
+
47
+ # Flow style sequence
48
+ FLOW = 2
49
+
50
+ # The anchor for this sequence (if any)
51
+ attr_accessor :anchor
52
+
53
+ # The tag name for this sequence (if any)
54
+ attr_accessor :tag
55
+
56
+ # Is this sequence started implicitly?
57
+ attr_accessor :implicit
58
+
59
+ # The sequece style used
60
+ attr_accessor :style
61
+
62
+ ###
63
+ # Create a new object representing a YAML sequence.
64
+ #
65
+ # +anchor+ is the anchor associated with the sequence or nil.
66
+ # +tag+ is the tag associated with the sequence or nil.
67
+ # +implicit+ a boolean indicating whether or not the sequence was
68
+ # implicitly started.
69
+ # +style+ is an integer indicating the list style.
70
+ #
71
+ # See Psych::Handler#start_sequence
72
+ def initialize anchor = nil, tag = nil, implicit = true, style = BLOCK
73
+ super()
74
+ @anchor = anchor
75
+ @tag = tag
76
+ @implicit = implicit
77
+ @style = style
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,37 @@
1
+ module Psych
2
+ module Nodes
3
+ ###
4
+ # Represents a YAML stream. This is the root node for any YAML parse
5
+ # tree. This node must have one or more child nodes. The only valid
6
+ # child node for a Psych::Nodes::Stream node is Psych::Nodes::Document.
7
+ class Stream < Psych::Nodes::Node
8
+
9
+ # Encodings supported by Psych (and libyaml)
10
+
11
+ # Any encoding
12
+ ANY = Psych::Parser::ANY
13
+
14
+ # UTF-8 encoding
15
+ UTF8 = Psych::Parser::UTF8
16
+
17
+ # UTF-16LE encoding
18
+ UTF16LE = Psych::Parser::UTF16LE
19
+
20
+ # UTF-16BE encoding
21
+ UTF16BE = Psych::Parser::UTF16BE
22
+
23
+ # The encoding used for this stream
24
+ attr_accessor :encoding
25
+
26
+ ###
27
+ # Create a new Psych::Nodes::Stream node with an +encoding+ that
28
+ # defaults to Psych::Nodes::Stream::UTF8.
29
+ #
30
+ # See also Psych::Handler#start_stream
31
+ def initialize encoding = UTF8
32
+ super()
33
+ @encoding = encoding
34
+ end
35
+ end
36
+ end
37
+ end
data/lib/psych/omap.rb ADDED
@@ -0,0 +1,4 @@
1
+ module Psych
2
+ class Omap < ::Hash
3
+ end
4
+ end
@@ -0,0 +1,47 @@
1
+ module Psych
2
+ ###
3
+ # YAML event parser class. This class parses a YAML document and calls
4
+ # events on the handler that is passed to the constructor. The events can
5
+ # be used for things such as constructing a YAML AST or deserializing YAML
6
+ # documents. It can even be fed back to Psych::Emitter to emit the same
7
+ # document that was parsed.
8
+ #
9
+ # See Psych::Handler for documentation on the events that Psych::Parser emits.
10
+ #
11
+ # Here is an example that prints out ever scalar found in a YAML document:
12
+ #
13
+ # # Handler for detecting scalar values
14
+ # class ScalarHandler < Psych::Handler
15
+ # def scalar value, anchor, tag, plain, quoted, style
16
+ # puts value
17
+ # end
18
+ # end
19
+ #
20
+ # parser = Psych::Parser.new(ScalarHandler.new)
21
+ # parser.parse(yaml_document)
22
+ #
23
+ # Here is an example that feeds the parser back in to Psych::Emitter. The
24
+ # YAML document is read from STDIN and written back out to STDERR:
25
+ #
26
+ # parser = Psych::Parser.new(Psych::Emitter.new($stderr))
27
+ # parser.parse($stdin)
28
+ #
29
+ # Psych uses Psych::Parser in combination with Psych::TreeBuilder to
30
+ # construct an AST of the parsed YAML document.
31
+
32
+ class Parser
33
+ class Mark < Struct.new(:index, :line, :column)
34
+ end
35
+
36
+ # The handler on which events will be called
37
+ attr_accessor :handler
38
+
39
+ ###
40
+ # Creates a new Psych::Parser instance with +handler+. YAML events will
41
+ # be called on +handler+. See Psych::Parser for more details.
42
+
43
+ def initialize handler = Handler.new
44
+ @handler = handler
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,105 @@
1
+ require 'strscan'
2
+
3
+ module Psych
4
+ ###
5
+ # Scan scalars for built in types
6
+ class ScalarScanner
7
+ # Taken from http://yaml.org/type/timestamp.html
8
+ TIME = /^\d{4}-\d{1,2}-\d{1,2}([Tt]|\s+)\d{1,2}:\d\d:\d\d(\.\d*)?(\s*Z|[-+]\d{1,2}(:\d\d)?)?/
9
+
10
+ # Create a new scanner
11
+ def initialize
12
+ @string_cache = {}
13
+ end
14
+
15
+ # Tokenize +string+ returning the ruby object
16
+ def tokenize string
17
+ return nil if string.empty?
18
+ return string if @string_cache.key?(string)
19
+
20
+ case string
21
+ when /^[A-Za-z~]/
22
+ if string.length > 5
23
+ @string_cache[string] = true
24
+ return string
25
+ end
26
+
27
+ case string
28
+ when /^[^ytonf~]/i
29
+ @string_cache[string] = true
30
+ string
31
+ when '~', /^null$/i
32
+ nil
33
+ when /^(yes|true|on)$/i
34
+ true
35
+ when /^(no|false|off)$/i
36
+ false
37
+ else
38
+ @string_cache[string] = true
39
+ string
40
+ end
41
+ when TIME
42
+ parse_time string
43
+ when /^\d{4}-\d{1,2}-\d{1,2}$/
44
+ require 'date'
45
+ Date.strptime(string, '%Y-%m-%d')
46
+ when /^\.inf$/i
47
+ 1 / 0.0
48
+ when /^-\.inf$/i
49
+ -1 / 0.0
50
+ when /^\.nan$/i
51
+ 0.0 / 0.0
52
+ when /^:./
53
+ if string =~ /^:(["'])(.*)\1/
54
+ $2.sub(/^:/, '').to_sym
55
+ else
56
+ string.sub(/^:/, '').to_sym
57
+ end
58
+ when /^[-+]?[1-9][0-9_]*(:[0-5]?[0-9])+$/
59
+ i = 0
60
+ string.split(':').each_with_index do |n,e|
61
+ i += (n.to_i * 60 ** (e - 2).abs)
62
+ end
63
+ i
64
+ when /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+\.[0-9_]*$/
65
+ i = 0
66
+ string.split(':').each_with_index do |n,e|
67
+ i += (n.to_f * 60 ** (e - 2).abs)
68
+ end
69
+ i
70
+ else
71
+ return Integer(string.gsub(/[,_]/, '')) rescue ArgumentError
72
+ return Float(string.gsub(/[,_]/, '')) rescue ArgumentError
73
+ @string_cache[string] = true
74
+ string
75
+ end
76
+ end
77
+
78
+ ###
79
+ # Parse and return a Time from +string+
80
+ def parse_time string
81
+ date, time = *(string.split(/[ tT]/, 2))
82
+ (yy, m, dd) = date.split('-').map { |x| x.to_i }
83
+ md = time.match(/(\d+:\d+:\d+)(\.\d*)?\s*(Z|[-+]\d+(:\d\d)?)?/)
84
+
85
+ (hh, mm, ss) = md[1].split(':').map { |x| x.to_i }
86
+ us = (md[2] ? Rational(md[2].sub(/^\./, '0.')) : 0) * 1000000
87
+
88
+ time = Time.utc(yy, m, dd, hh, mm, ss, us)
89
+
90
+ return time if 'Z' == md[3]
91
+ return Time.at(time.to_i, us) unless md[3]
92
+
93
+ tz = md[3].match(/^([+\-]?\d{1,2})\:?(\d{1,2})?$/)[1..-1].compact.map { |digit| Integer(digit, 10) }
94
+ offset = tz.first * 3600
95
+
96
+ if offset < 0
97
+ offset -= ((tz[1] || 0) * 60)
98
+ else
99
+ offset += ((tz[1] || 0) * 60)
100
+ end
101
+
102
+ Time.at((time - offset).to_i, us)
103
+ end
104
+ end
105
+ end