File: parser.rb

package info (click to toggle)
ruby-iniparse 1.4.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 472 kB
  • sloc: ruby: 2,409; makefile: 3
file content (110 lines) | stat: -rw-r--r-- 2,933 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
module IniParse
  class Parser

    # Returns the line types.
    #
    # ==== Returns
    # Array
    #
    def self.parse_types
      @@parse_types ||= []
    end

    # Sets the line types. Handy if you want to add your own custom Line
    # classes.
    #
    # ==== Parameters
    # types<Array[IniParse::Lines::Line]>:: An array containing Line classes.
    #
    def self.parse_types=(types)
      parse_types.replace(types)
    end

    self.parse_types = [ IniParse::Lines::Section,
      IniParse::Lines::Option, IniParse::Lines::Blank ]

    # Creates a new Parser instance for parsing string +source+.
    #
    # ==== Parameters
    # source<String>:: The source string.
    #
    def initialize(source)
      @source = source.dup
    end

    # Parses the source string and returns the resulting data structure.
    #
    # ==== Returns
    # IniParse::Document
    #
    def parse
      IniParse::Generator.gen do |generator|
        @source.split("\n", -1).each do |line|
          generator.send(*Parser.parse_line(line))
        end
      end
    end

    class << self
      # Takes a raw line from an INI document, striping out any inline
      # comment, and indent, then returns the appropriate tuple so that the
      # Generator instance can add the line to the Document.
      #
      # ==== Raises
      # IniParse::ParseError: If the line could not be parsed.
      #
      def parse_line(line)
        sanitized, opts = strip_indent(*strip_comment(line, {}))

        parsed = nil
        @@parse_types.each do |type|
          break if (parsed = type.parse(sanitized, opts))
        end

        if parsed.nil?
          raise IniParse::ParseError,
            "A line of your INI document could not be parsed to a " \
            "LineType: '#{line}'."
        end

        parsed
      end

      #######
      private
      #######

      # Strips in inline comment from a line (or value), removes trailing
      # whitespace and sets the comment options as applicable.
      def strip_comment(line, opts)
        if m = /^(^)(?:(;|\#)(\s*)(.*))$$/.match(line) ||
           m = /^(.*?)(?:\s+(;|\#)(\s*)(.*))$/.match(line) # Comment lines.
          opts[:comment] = m[4].rstrip
          opts[:comment_prefix] = m[3]
          opts[:comment_sep] = m[2]
          # Remove the line content (since an option value may contain a
          # semi-colon) _then_ get the index of the comment separator.
          opts[:comment_offset] =
            line[(m[1].length..-1)].index(m[2]) + m[1].length

          line = m[1]
        else
          line.rstrip!
        end

        [line, opts]
      end

      # Removes any leading whitespace from a line, and adds it to the options
      # hash.
      def strip_indent(line, opts)
        if m = /^(\s+).*$/.match(line)
          line.lstrip!
          opts[:indent] = m[1]
        end

        [line, opts]
      end
    end
  end # Parser
end # IniParse