Imported File#ftype spec from rubyspecs.
[rbx.git] / lib / rbyaml / reader.rb
blob939841415055e1c23d8ff73bca333a72dd990431
1 # This is a more or less straight translation of PyYAML3000 to Ruby
3 # the big difference in this implementation is that unicode support is not here...
5 require 'rbyaml/error'
7 module RbYAML
9   # Reader:
10   # - checks if characters are in allowed range,
11   # - adds '\0' to the end.
12   # Reader accepts
13   #  - a String object
14   #  - a duck-typed IO object
15   module Reader
16     def initialize_reader(stream)
17       @stream = nil
18       @stream_pointer = 0
19       @eof = true
20       @buffer = ""
21       @pointer = 0
22       @index = 0
23       @line = 0
24       @column = 0
25       if String === stream
26         @name = "<string>"
27         @raw_buffer = stream
28       else
29         @stream = stream
30         @name = stream.respond_to?(:path) ? stream.path : stream.inspect
31         @eof = false
32         @raw_buffer = ""
33       end
34     end
36     def peek(index=0)
37       update(index+1) if @pointer+index+1 >= @buffer.length
38       @buffer[@pointer+index]
39     end
40     
41     def prefix(length=1)
42       update(length) if @pointer+length >= @buffer.length
43       @buffer[@pointer...@pointer+length]
44     end
46     def forward(length=1)
47       update(length+1) if @pointer+length+1 >= @buffer.length
48       length.times { |k|
49         ch = @buffer[@pointer]
50         @pointer += 1
51         @index += 1
52         if "\n\x85".include?(ch) || (ch == ?\r && @buffer[@pointer+1] != ?\n)
53           @line += 1
54           @column = 0
55         else
56           @column += 1
57         end
58       }
59     end
60     
61     def get_mark
62       if @stream.nil?
63         Mark.new(@name,@index,@line,@column,@buffer,@pointer)
64       else
65         Mark.new(@name,@index,@line,@column,nil,nil)
66       end
67     end
68     
69     NON_PRINTABLE = /[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\xFF]/
70     def check_printable(data)
71       if NON_PRINTABLE =~ data
72         position = @index+@buffer.length-@pointer+($~.offset(0)[0])
73         raise ReaderError.new(@name, position, $&,"unicode","special characters are not allowed"),"special characters are not allowed"
74       end
75     end
77     def update(length)
78       return if @raw_buffer.nil?
79       @buffer = @buffer[@pointer..-1]
80       @pointer = 0
81       while @buffer.length < length
82         unless @eof
83           update_raw
84         end
85         data = @raw_buffer
86         converted = data.length
87         check_printable(data)
88         @buffer << data
89         @raw_buffer = @raw_buffer[converted..-1]
90         if @eof
91           @buffer << ?\0
92           @raw_buffer = nil
93           break
94         end
95       end
96     end
98     def update_raw(size=1024)
99       data = @stream.read(size)
100       if data && !data.empty?
101         @raw_buffer << data
102         @stream_pointer += data.length
103       else
104         @eof = true
105       end
106     end
107   end
109   class ReaderError < YAMLError
110     def initialize(name, position, character, encoding, reason)
111       @name = name
112       @position = position
113       @character = character
114       @encoding = encoding
115       @reason = reason
116     end
118     def to_s
119       if String === @character
120         "'#{@encoding}' codec can't decode byte #x%02x: #{@reason}\n  in \"#{@name}\", position #{@position}" % @character.to_i
121       else
122         "unacceptable character #x%04x: #{@reason}\n  in \"#{@name}\", position #{@position}" % @character.to_i
123       end
124     end
125   end