Class: PropertyList::AsciiParser

Inherits:
Object
  • Object
show all
Defined in:
lib/property-list/ascii_parser.rb

Overview

:nodoc:

Instance Method Summary collapse

Constructor Details

#initialize(src) ⇒ AsciiParser

Returns a new instance of AsciiParser.



8
9
10
# File 'lib/property-list/ascii_parser.rb', line 8

def initialize src
  @lexer = StringScanner.new src.strip
end

Instance Method Details

#parseObject



12
13
14
15
16
17
18
19
# File 'lib/property-list/ascii_parser.rb', line 12

def parse
  res = parse_object
  skip_space_and_comment
  if !@lexer.eos? or res.nil?
    syntax_error "Unrecognized token"
  end
  res
end

#parse_arrayObject



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/property-list/ascii_parser.rb', line 91

def parse_array
  @lexer.pos += 1
  array = []
  while (skip_space_and_comment; @lexer.peek(1) != ')')
    obj = parse_object
    if obj.nil?
      syntax_error "Failed to parse array element"
    end
    array << obj
    skip_space_and_comment
    @lexer.scan(/,/)
  end
  if @lexer.getch != ')'
    syntax_error "Unclosed array"
  end
  array
end

#parse_dataObject



204
205
206
207
208
209
210
211
212
# File 'lib/property-list/ascii_parser.rb', line 204

def parse_data
  if (h = @lexer.scan /[0-9a-f\s]*\>/i)
    h = h.gsub /[\s\>]/, ''
    data = [h].pack 'H*'
    StringIO.new data
  else
    syntax_error "Expect hex value"
  end
end

#parse_dictObject



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/property-list/ascii_parser.rb', line 49

def parse_dict
  @lexer.pos += 1
  hash = {}
  while (skip_space_and_comment; @lexer.peek(1) != '}')
    k = \
      case @lexer.peek(1)
      when '"'
        parse_string '"'
      when "'"
        parse_string "'"
      when /\w/
        parse_unquoted_string
      end
    if !k
      syntax_error "Expect dictionary key"
    end

    skip_space_and_comment
    if !@lexer.scan(/=/)
      syntax_error "Expect '=' after dictionary key"
    end
    skip_space_and_comment

    v = parse_object
    if v.nil?
      syntax_error "Expect dictionary value"
    end

    skip_space_and_comment
    if !@lexer.scan(/;/)
      syntax_error "Expect ';' after dictionary value"
    end
    skip_space_and_comment

    hash[k] = v
  end
  if @lexer.getch != '}'
    syntax_error "Unclosed hash"
  end
  hash
end

#parse_extension_valueObject



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/property-list/ascii_parser.rb', line 160

def parse_extension_value
  @lexer.pos += 1

  case @lexer.peek(2)
  when '*D' # date
    @lexer.pos += 2
    if (d = @lexer.scan /\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [+\-]\d{4}\>/)
      Date.strptime d.chop, "%Y-%m-%d %H:%M:%S %z"
    else
      syntax_error "Expect date value"
    end

  when '*I' # integer
    @lexer.pos += 2
    if (i = @lexer.scan /[\+\-]?\d+\>/)
      i.chop.to_i
    else
      syntax_error "Expect integer value"
    end

  when '*R' # real
    @lexer.pos += 2
    if (r = @lexer.scan /[\+\-]?\d+(\.\d+)?([eE][\+\-]?\d+)?\>/)
      r.chop.to_f
    else
      syntax_error "Expect real value"
    end

  when '*B' # boolean
    @lexer.pos += 2
    case @lexer.scan(/[YN]\>/)
    when 'Y>'
      true
    when 'N>'
      false
    else
      syntax_error "Expect boolean value"
    end

  else
    parse_data
  end
end

#parse_objectObject



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/property-list/ascii_parser.rb', line 31

def parse_object
  skip_space_and_comment
  case @lexer.peek(1)
  when '{'
    parse_dict
  when '('
    parse_array
  when '"'
    parse_string '"'
  when "'"
    parse_string "'" # NOTE: not in GNU extension
  when '<'
    parse_extension_value
  when /[\w\.\/]/
    parse_unquoted_string
  end
end

#parse_string(delim) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/property-list/ascii_parser.rb', line 109

def parse_string delim
  @lexer.pos += 1

  # TODO (TextMate only, xcode cannot parse it) when delim is ', '' is the escape

  chars = []
  while (ch = @lexer.getch) != delim
    case ch
    when '\\'
      case @lexer.getch
      when '\\'
        chars << '\\'
      when '"'
        chars << '"'
      when "'"
        chars << "'"
      when 'b'
        chars << "\b"
      when 'n'
        chars << "\n"
      when 'r'
        chars << "\r"
      when 't'
        chars << "\t"
      when 'U', 'u'
        if (hex = @lexer.scan /[0-9a-h]{4}/i)
          chars << [hex.to_i(16)].pack('U')
        else
          syntax_error "Expect 4 digit hex code"
        end
      when /(\d)/
        oct_init = $1
        if (oct = @lexer.scan /[0-7]{2}/)
          chars << [(oct_init + oct).to_i(8)].pack('U')
        else
          syntax_error "Expect 3 digit oct code"
        end
      else
        syntax_error "Bad escape"
      end
    else
      chars << ch
    end
  end
  chars.join
end

#parse_unquoted_stringObject



156
157
158
# File 'lib/property-list/ascii_parser.rb', line 156

def parse_unquoted_string
  @lexer.scan /[\w\.\/]+/
end

#skip_space_and_commentObject



21
22
23
24
25
26
27
28
29
# File 'lib/property-list/ascii_parser.rb', line 21

def skip_space_and_comment
  @lexer.skip(%r{(?:
    [\x0A\x0D\u2028\u2029\x09\x0B\x0C\x20]+ # newline and space
    |
    //[^\x0A\x0D\u2028\u2029]* # one-line comment
    |
    /\*(?:.*?)\*/ # multi-line comment
  )+}mx)
end

#syntax_error(msg) ⇒ Object

Raises:



214
215
216
217
218
219
# File 'lib/property-list/ascii_parser.rb', line 214

def syntax_error msg
  pre = @lexer.string[0...@lexer.pos]
  line = pre.count("\n") + 1
  col = pre.size - (pre.rindex("\n") || -1)
  raise SyntaxError, msg + " at line: #{line} col: #{col} #{@lexer.inspect}", caller
end