Class: RegexOperator

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby_grammar_builder/regex_operator.rb

Overview

RegexOperator is used to provide complicated combining behavior that is not possible to implement in PatternBase#do_evaluate_self

Each PatternBase when evaluated produces a RegexOperator and a regexstring RegexOperator::evaluate takes that array and produces a single regexstring

Direct Known Subclasses

AlternationOperator, ConcatOperator

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#association:left, :right

right associative is processed first

Returns:

  • (:left, :right)

    is the operator left of right associative



47
48
49
# File 'lib/ruby_grammar_builder/regex_operator.rb', line 47

def association
  @association
end

#precedencenumber

Returns The precedence of the operator, lower numbers are processed earlier.

Returns:

  • (number)

    The precedence of the operator, lower numbers are processed earlier



44
45
46
# File 'lib/ruby_grammar_builder/regex_operator.rb', line 44

def precedence
  @precedence
end

Class Method Details

.evaluate(arr) ⇒ String

Evaluate the array of RegexStrings and RegexOperators

Parameters:

Returns:

  • (String)

    The evaluated string



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
# File 'lib/ruby_grammar_builder/regex_operator.rb', line 56

def self.evaluate(arr)
    # grab all operators and sort by precedence and association
    ops = arr.reject { |v| v.is_a?(String) }.sort do |a, b|
        if a.precedence == b.precedence
            next 0 if a.association == b.association
            next -1 if a.association == :left

            next 1
        end

        a.precedence - b.precedence
    end

    ops.each do |op|
        # TODO: consolidate left and right paths
        split = []
        if op.association == :right
            elems = break_right(arr) { |elem| elem == op }
            next if elems[0].empty?

            split = [elems[0][0..-2], elems[1]]
        else
            elems = break_left(arr) { |elem| elem == op }
            next if elems[1].empty?

            split = [elems[0], elems[1][1..-1]]
        end
        arr = op.do_evaluate_self(split[0], split[1])
    end
    if arr.length != 1
        puts "evaluate did not result in a length of 1"
        raise "see above error"
    end

    arr.first
end

Instance Method Details

#==(other) ⇒ Boolean

Compares for equality

Parameters:

Returns:

  • (Boolean)

    are they equal



117
118
119
120
121
122
123
# File 'lib/ruby_grammar_builder/regex_operator.rb', line 117

def ==(other)
    return false unless other.instance_of?(self.class)
    return false unless @precedence == other.precedence
    return false unless @association == other.association

    true
end

#do_evaluate_self(arr_left, arr_right) ⇒ Array<RegexOperator,String>

This method is abstract.

override to provide evaluate the operator

Note:

arr_left and arr_right contain the entire parse array use #fold_left and #fold_right to collect only the portions that this operator is responsible for

<Description>

Parameters:

Returns:

Raises:

  • (NotImplementedError)


106
107
108
# File 'lib/ruby_grammar_builder/regex_operator.rb', line 106

def do_evaluate_self(arr_left, arr_right) # rubocop:disable Lint/UnusedMethodArgument
    raise NotImplementedError
end

#fold_left(arr) ⇒ Array<(String,Array<String,RegexOperator>)>

Note:

the leftover portion is not suitable for evaluation (it begins or ends with a RegexOperator or is an empty string)

<Description>

Parameters:

Returns:



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/ruby_grammar_builder/regex_operator.rb', line 134

def fold_left(arr)
    # go left until:
    #  - the precedence of self is greater than the token being tested
    #  - the precedence is the same and the association of self is :left
    fold = (arr.reverse.take_while do |t|
        next true if t.is_a? String
        next true if t.precedence > @precedence

        next false if t.precedence < @precedence
        next false if @association == :left

        true
    end).reverse

    if fold.empty? || !fold[0].is_a?(String) || !fold[-1].is_a?(String)
        puts "fold_left generated an invalid fold expression"
        raise "see above error"
    end

    # [0..-(fold.length+1)] peels the elements that are a part of fold off the end
    # of arr
    [RegexOperator.evaluate(fold), arr[0..-(fold.length+1)]]
end

#fold_right(arr) ⇒ Array<(String,Array<String,RegexOperator>)>

Note:

the leftover portion is not suitable for evaluation (it begins or ends with a RegexOperator or is an empty string)

<Description>

Parameters:

Returns:



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/ruby_grammar_builder/regex_operator.rb', line 161

def fold_right(arr)
    # go right until:
    #  - the precedence of self is greater than the token being tested
    #  - the precedence is the same and the association of self is :right
    fold = arr.take_while do |t|
        next true if t.is_a? String
        next true if t.precedence > @precedence

        next false if t.precedence < @precedence
        next false if @association == :right

        true
    end

    if fold.empty? || !fold[0].is_a?(String) || !fold[-1].is_a?(String)
        puts "fold_right generated an invalid fold expression"
        raise "see above error"
    end

    [RegexOperator.evaluate(fold), arr[(fold.length)..-1]]
end