~ubuntu-branches/ubuntu/lucid/puppet/lucid-security

« back to all changes in this revision

Viewing changes to lib/puppet/external/pson/pure/parser.rb

  • Committer: Bazaar Package Importer
  • Author(s): Chuck Short
  • Date: 2009-12-23 00:48:10 UTC
  • mfrom: (1.1.10 upstream) (3.1.7 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091223004810-3i4oryds922g5n59
Tags: 0.25.1-3ubuntu1
* Merge from debian testing.  Remaining changes:
  - debian/rules:
    + Don't start puppet when first installing puppet.
  - debian/puppet.conf, lib/puppet/defaults.rb:
    + Move templates to /etc/puppet
  - lib/puppet/defaults.rb:
    + Fix /var/lib/puppet/state ownership.
  - man/man8/puppet.conf.8: 
    + Fix broken URL in manpage.
  - debian/control:
    + Update maintainer accordint to spec.
    + Puppetmaster Recommends -> Suggests
    + Created puppet-testsuite as a seperate. Allow the users to run puppet's 
      testsuite.
  - tests/Rakefile: Fix rakefile so that the testsuite can acutally be ran.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
require 'strscan'
 
2
 
 
3
module PSON
 
4
  module Pure
 
5
    # This class implements the PSON parser that is used to parse a PSON string
 
6
    # into a Ruby data structure.
 
7
    class Parser < StringScanner
 
8
      STRING                = /" ((?:[^\x0-\x1f"\\] |
 
9
                                   # escaped special characters:
 
10
                                  \\["\\\/bfnrt] |
 
11
                                  \\u[0-9a-fA-F]{4} |
 
12
                                   # match all but escaped special characters:
 
13
                                  \\[\x20-\x21\x23-\x2e\x30-\x5b\x5d-\x61\x63-\x65\x67-\x6d\x6f-\x71\x73\x75-\xff])*)
 
14
                              "/nx
 
15
      INTEGER               = /(-?0|-?[1-9]\d*)/
 
16
      FLOAT                 = /(-?
 
17
                                (?:0|[1-9]\d*)
 
18
                                (?:
 
19
                                  \.\d+(?i:e[+-]?\d+) |
 
20
                                  \.\d+ |
 
21
                                  (?i:e[+-]?\d+)
 
22
                                )
 
23
                                )/x
 
24
      NAN                   = /NaN/
 
25
      INFINITY              = /Infinity/
 
26
      MINUS_INFINITY        = /-Infinity/
 
27
      OBJECT_OPEN           = /\{/
 
28
      OBJECT_CLOSE          = /\}/
 
29
      ARRAY_OPEN            = /\[/
 
30
      ARRAY_CLOSE           = /\]/
 
31
      PAIR_DELIMITER        = /:/
 
32
      COLLECTION_DELIMITER  = /,/
 
33
      TRUE                  = /true/
 
34
      FALSE                 = /false/
 
35
      NULL                  = /null/
 
36
      IGNORE                = %r(
 
37
        (?:
 
38
         //[^\n\r]*[\n\r]| # line comments
 
39
         /\*               # c-style comments
 
40
         (?:
 
41
          [^*/]|        # normal chars
 
42
          /[^*]|        # slashes that do not start a nested comment
 
43
          \*[^/]|       # asterisks that do not end this comment
 
44
          /(?=\*/)      # single slash before this comment's end 
 
45
         )*
 
46
           \*/               # the End of this comment
 
47
           |[ \t\r\n]+       # whitespaces: space, horicontal tab, lf, cr
 
48
        )+
 
49
      )mx
 
50
 
 
51
      UNPARSED = Object.new
 
52
 
 
53
      # Creates a new PSON::Pure::Parser instance for the string _source_.
 
54
      #
 
55
      # It will be configured by the _opts_ hash. _opts_ can have the following
 
56
      # keys:
 
57
      # * *max_nesting*: The maximum depth of nesting allowed in the parsed data
 
58
      #   structures. Disable depth checking with :max_nesting => false|nil|0,
 
59
      #   it defaults to 19.
 
60
      # * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
 
61
      #   defiance of RFC 4627 to be parsed by the Parser. This option defaults
 
62
      #   to false.
 
63
      # * *create_additions*: If set to false, the Parser doesn't create
 
64
      #   additions even if a matchin class and create_id was found. This option
 
65
      #   defaults to true.
 
66
      # * *object_class*: Defaults to Hash
 
67
      # * *array_class*: Defaults to Array
 
68
      def initialize(source, opts = {})
 
69
        super
 
70
        if !opts.key?(:max_nesting) # defaults to 19
 
71
          @max_nesting = 19
 
72
        elsif opts[:max_nesting]
 
73
          @max_nesting = opts[:max_nesting]
 
74
        else
 
75
          @max_nesting = 0
 
76
        end
 
77
        @allow_nan = !!opts[:allow_nan]
 
78
        ca = true
 
79
        ca = opts[:create_additions] if opts.key?(:create_additions)
 
80
        @create_id = ca ? PSON.create_id : nil
 
81
        @object_class = opts[:object_class] || Hash
 
82
        @array_class = opts[:array_class] || Array
 
83
      end
 
84
 
 
85
      alias source string
 
86
 
 
87
      # Parses the current PSON string _source_ and returns the complete data
 
88
      # structure as a result.
 
89
      def parse
 
90
        reset
 
91
        obj = nil
 
92
        until eos?
 
93
          case
 
94
          when scan(OBJECT_OPEN)
 
95
            obj and raise ParserError, "source '#{peek(20)}' not in PSON!"
 
96
            @current_nesting = 1
 
97
            obj = parse_object
 
98
          when scan(ARRAY_OPEN)
 
99
            obj and raise ParserError, "source '#{peek(20)}' not in PSON!"
 
100
            @current_nesting = 1
 
101
            obj = parse_array
 
102
          when skip(IGNORE)
 
103
            ;
 
104
          else
 
105
            raise ParserError, "source '#{peek(20)}' not in PSON!"
 
106
          end
 
107
        end
 
108
        obj or raise ParserError, "source did not contain any PSON!"
 
109
        obj
 
110
      end
 
111
 
 
112
      private
 
113
 
 
114
      # Unescape characters in strings.
 
115
      UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr }
 
116
      UNESCAPE_MAP.update({
 
117
        ?"  => '"',
 
118
        ?\\ => '\\',
 
119
        ?/  => '/',
 
120
        ?b  => "\b",
 
121
        ?f  => "\f",
 
122
        ?n  => "\n",
 
123
        ?r  => "\r",
 
124
        ?t  => "\t",
 
125
        ?u  => nil, 
 
126
      })
 
127
 
 
128
      def parse_string
 
129
        if scan(STRING)
 
130
          return '' if self[1].empty?
 
131
          string = self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c|
 
132
            if u = UNESCAPE_MAP[$&[1]]
 
133
              u
 
134
            else # \uXXXX
 
135
              bytes = ''
 
136
              i = 0
 
137
              while c[6 * i] == ?\\ && c[6 * i + 1] == ?u
 
138
                bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16)
 
139
                i += 1
 
140
              end
 
141
              PSON::UTF16toUTF8.iconv(bytes)
 
142
            end
 
143
          end
 
144
          if string.respond_to?(:force_encoding)
 
145
            string.force_encoding(Encoding::UTF_8)
 
146
          end
 
147
          string
 
148
        else
 
149
          UNPARSED
 
150
        end
 
151
      rescue Iconv::Failure => e
 
152
        raise GeneratorError, "Caught #{e.class}: #{e}"
 
153
      end
 
154
 
 
155
      def parse_value
 
156
        case
 
157
        when scan(FLOAT)
 
158
          Float(self[1])
 
159
        when scan(INTEGER)
 
160
          Integer(self[1])
 
161
        when scan(TRUE)
 
162
          true
 
163
        when scan(FALSE)
 
164
          false
 
165
        when scan(NULL)
 
166
          nil
 
167
        when (string = parse_string) != UNPARSED
 
168
          string
 
169
        when scan(ARRAY_OPEN)
 
170
          @current_nesting += 1
 
171
          ary = parse_array
 
172
          @current_nesting -= 1
 
173
          ary
 
174
        when scan(OBJECT_OPEN)
 
175
          @current_nesting += 1
 
176
          obj = parse_object
 
177
          @current_nesting -= 1
 
178
          obj
 
179
        when @allow_nan && scan(NAN)
 
180
          NaN
 
181
        when @allow_nan && scan(INFINITY)
 
182
          Infinity
 
183
        when @allow_nan && scan(MINUS_INFINITY)
 
184
          MinusInfinity
 
185
        else
 
186
          UNPARSED
 
187
        end
 
188
      end
 
189
 
 
190
      def parse_array
 
191
        raise NestingError, "nesting of #@current_nesting is too deep" if
 
192
          @max_nesting.nonzero? && @current_nesting > @max_nesting
 
193
        result = @array_class.new
 
194
        delim = false
 
195
        until eos?
 
196
          case
 
197
          when (value = parse_value) != UNPARSED
 
198
            delim = false
 
199
            result << value
 
200
            skip(IGNORE)
 
201
            if scan(COLLECTION_DELIMITER)
 
202
              delim = true
 
203
            elsif match?(ARRAY_CLOSE)
 
204
              ;
 
205
            else
 
206
              raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
 
207
            end
 
208
          when scan(ARRAY_CLOSE)
 
209
            if delim
 
210
              raise ParserError, "expected next element in array at '#{peek(20)}'!"
 
211
            end
 
212
            break
 
213
          when skip(IGNORE)
 
214
            ;
 
215
          else
 
216
            raise ParserError, "unexpected token in array at '#{peek(20)}'!"
 
217
          end
 
218
        end
 
219
        result
 
220
      end
 
221
 
 
222
      def parse_object
 
223
        raise NestingError, "nesting of #@current_nesting is too deep" if
 
224
          @max_nesting.nonzero? && @current_nesting > @max_nesting
 
225
        result = @object_class.new
 
226
        delim = false
 
227
        until eos?
 
228
          case
 
229
          when (string = parse_string) != UNPARSED
 
230
            skip(IGNORE)
 
231
            unless scan(PAIR_DELIMITER)
 
232
              raise ParserError, "expected ':' in object at '#{peek(20)}'!"
 
233
            end
 
234
            skip(IGNORE)
 
235
            unless (value = parse_value).equal? UNPARSED
 
236
              result[string] = value
 
237
              delim = false
 
238
              skip(IGNORE)
 
239
              if scan(COLLECTION_DELIMITER)
 
240
                delim = true
 
241
              elsif match?(OBJECT_CLOSE)
 
242
                ;
 
243
              else
 
244
                raise ParserError, "expected ',' or '}' in object at '#{peek(20)}'!"
 
245
              end
 
246
            else
 
247
              raise ParserError, "expected value in object at '#{peek(20)}'!"
 
248
            end
 
249
          when scan(OBJECT_CLOSE)
 
250
            if delim
 
251
              raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!"
 
252
            end
 
253
            if @create_id and klassname = result[@create_id]
 
254
              klass = PSON.deep_const_get klassname
 
255
              break unless klass and klass.pson_creatable?
 
256
              result = klass.pson_create(result)
 
257
            end
 
258
            break
 
259
          when skip(IGNORE)
 
260
            ;
 
261
          else
 
262
            raise ParserError, "unexpected token in object at '#{peek(20)}'!"
 
263
          end
 
264
        end
 
265
        result
 
266
      end
 
267
    end
 
268
  end
 
269
end