Class: Parser

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/mulparse/parser.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(src, lang_config) ⇒ Parser

Returns a new instance of Parser.



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
# File 'lib/mulparse/parser.rb', line 24

def initialize( src, lang_config )

    # Initialize a parser and set lang_config and other parsing configurations

    @src = src # src must be loaded from a text stream not a binary one

    if lang_config.class == String then
        
        # Open predefined lang-config
        
        @lang_config = YAML.load( IO.read(
            "#{File.absolute_path( File.expand_path( File.join( File.dirname( __FILE__ ), '..' ) ) )}" \
            "/mulparse/lang-configs/#{lang_config}.yaml" 
        ) )

    else
        # Else use user provided lang-config
        @lang_config = lang_config
    end

    @position = 0

    # Store the current Component
    @current = Component.new( finish: true )

end

Instance Attribute Details

#srcObject (readonly)

Returns the value of attribute src.



22
23
24
# File 'lib/mulparse/parser.rb', line 22

def src
  @src
end

Instance Method Details

#each(&block) ⇒ Object



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
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/mulparse/parser.rb', line 51

def each( &block )

    # Implements each method defined in Enumerable.
    # This method passes the next component parsed
    # in the srouce. The component is in the form
    # of a Component.

    loop do
    
        # Either finish the currentCN or open a new one on each iteration

        # Get the next start delimiter and its corresponding lcEntry

        next_start, start_entry = get_next_start()

        # Keep track of the existance of the finish attribute
        has_finish = @current.finish && @current.finish != true # A true value indicates the root Component

        if has_finish then

            # If finsih regex provided prepare closing regex

            closing_regex = @current.finish

            if closing_regex.is_a?( String ) then
                
                # Format in mirror to the closing_regex if mirror is provided

                if @current.hash then

                    # Process hash argument if provided

                    closing_regex = Regexp.new( closing_regex % Hash.new { |_, key|
                        Regexp.escape( @current.hash[ @current.start_match[ key ] ] || @current.start_match[ key ] )
                    } )

                else

                    # Else process normally

                    closing_regex = Regexp.new( closing_regex % Hash.new { |_, key|
                        Regexp.escape( @current.start_match[ key ] )
                    } )

                end

            end

            # Match for next closing delimiter
            next_finish = closing_regex.match( @src, @position )
            
            raise UnmatchedException.new(
                @current.name,
                @src[ 0..@current.start_match.end( 0 ) ].count( "\n" ) + 1
            ) unless next_finish

            # Ensure "escape" capture is valid

            until next_finish[ :escape ].length.even?

                # Match until length of "escape" capture is even
                next_finish = closing_regex.match( @src, next_finish.end( 0 ) )
            
            end if closing_regex.names.include?( "escape" )

        end

        if !next_start then
            
            # If done parsing yield last Component

            @current.finish_match = next_finish
            yield @current

            break

        end

        break if !next_start # Break if pasrsing is complete

        if @current&.parent && ( has_finish &&
            ( @current.unestable || next_finish.begin( 0 ) < next_start.begin( 0 ) ) ) then
        
            # If current Component should be finished then finish it

            begin
                @position = next_finish.end( 0 ) # Update position
            rescue NoMethodError
                
                # Raise UnmatchedException if next_finish is nil

                raise UnmatchedException.new(
                    @current.name,
                    @src[ 0..@current.start_match.end( 0 ) ].count( "\n" ) + 1
                )
                
            end

            # Update parsing data
            @current.finish_match = next_finish

            # Yield finished Component
            yield @current

            # Set current Component to the previous's parent
            @current = @current.parent

        else

            # Else new Component is found set it as current

            @current = Component.new( next_start, @current, start_entry )
            @position = next_start.end( 0 ) # Update position

            # Yield if Component uses syntactic sugar unestability
            
            unless @current.finish && @current.finish != true  then
                yield @current
                @current = @current.parent
            end

        end

    end

end

#lineObject

External/Internal Utility Methods



180
181
182
183
184
185
# File 'lib/mulparse/parser.rb', line 180

def line

    # Return number of lines already parsed
    return @src[ 0..@position ].count( "\n" )

end