srl_ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +4 -0
  3. data/.rubocop.yml +3 -0
  4. data/.yardopts +6 -0
  5. data/Gemfile +6 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +66 -0
  8. data/Rakefile +16 -0
  9. data/bin/srl_ruby +58 -0
  10. data/lib/regex/abstract_method.rb +35 -0
  11. data/lib/regex/alternation.rb +27 -0
  12. data/lib/regex/anchor.rb +45 -0
  13. data/lib/regex/atomic_expression.rb +16 -0
  14. data/lib/regex/capturing_group.rb +51 -0
  15. data/lib/regex/char_class.rb +38 -0
  16. data/lib/regex/char_range.rb +51 -0
  17. data/lib/regex/char_shorthand.rb +50 -0
  18. data/lib/regex/character.rb +204 -0
  19. data/lib/regex/compound_expression.rb +57 -0
  20. data/lib/regex/concatenation.rb +29 -0
  21. data/lib/regex/expression.rb +60 -0
  22. data/lib/regex/lookaround.rb +50 -0
  23. data/lib/regex/match_option.rb +34 -0
  24. data/lib/regex/monadic_expression.rb +28 -0
  25. data/lib/regex/multiplicity.rb +91 -0
  26. data/lib/regex/non_capturing_group.rb +27 -0
  27. data/lib/regex/polyadic_expression.rb +60 -0
  28. data/lib/regex/quantifiable.rb +22 -0
  29. data/lib/regex/repetition.rb +29 -0
  30. data/lib/regex/wildcard.rb +23 -0
  31. data/lib/srl_ruby/ast_builder.rb +384 -0
  32. data/lib/srl_ruby/grammar.rb +106 -0
  33. data/lib/srl_ruby/regex_repr.rb +13 -0
  34. data/lib/srl_ruby/tokenizer.rb +147 -0
  35. data/lib/srl_ruby/version.rb +3 -0
  36. data/lib/srl_ruby.rb +4 -0
  37. data/spec/integration_spec.rb +451 -0
  38. data/spec/regex/character_spec.rb +166 -0
  39. data/spec/regex/multiplicity_spec.rb +79 -0
  40. data/spec/spec_helper.rb +16 -0
  41. data/spec/srl_ruby/srl_ruby_spec.rb +7 -0
  42. data/spec/srl_ruby/tokenizer_spec.rb +147 -0
  43. data/srl_ruby.gemspec +58 -0
  44. metadata +150 -0
@@ -0,0 +1,204 @@
1
+ # File: character.rb
2
+
3
+ require_relative 'atomic_expression' # Access the superclass
4
+
5
+ module Regex # This module is used as a namespace
6
+ # A regular expression that matches a specific character in a given character set
7
+ class Character < AtomicExpression
8
+ # Constant with all special 2-characters escape sequences
9
+ DigramSequences = {
10
+ "\\a" => 0x7, # alarm
11
+ "\\n" => 0xA, # newline
12
+ "\\r" => 0xD, # carriage return
13
+ "\\t" => 0x9, # tab
14
+ "\\e" => 0x1B, # escape
15
+ "\\f" => 0xC, # form feed
16
+ "\\v" => 0xB, # vertical feed
17
+ # Single octal digit literals
18
+ "\\0" => 0,
19
+ "\\1" => 1,
20
+ "\\2" => 2,
21
+ "\\3" => 3,
22
+ "\\4" => 4,
23
+ "\\5" => 5,
24
+ "\\6" => 6,
25
+ "\\7" => 7
26
+ }.freeze
27
+
28
+ MetaChars = '\^$+?.'.freeze
29
+
30
+ # The integer value that uniquely identifies the character.
31
+ attr_reader(:codepoint)
32
+
33
+ # The initial text representation of the character (if any).
34
+ attr_reader(:lexeme)
35
+
36
+ # Constructor.
37
+ # [aValue] Initialize the character with a either a String literal or a
38
+ # codepoint value.
39
+ # Examples:
40
+ # Initializing with codepoint value...
41
+ # RegAn::Character.new(0x3a3) # Represents: Σ
42
+ # (Unicode GREEK CAPITAL LETTER SIGMA)
43
+ # RegAn::Character.new(931) # Also represents: Σ (931 dec == 3a3 hex)
44
+ #
45
+ # Initializing with a single character string
46
+ # RegAn::Character.new(?\u03a3) # Also represents: Σ
47
+ # RegAn::Character.new('Σ') # Obviously, represents a Σ
48
+ #
49
+ # Initializing with an escape sequence string
50
+ # Recognized escaped characters are: \a (alarm, 0x07), \n (newline, 0xA),
51
+ # \r (carriage return, 0xD), \t (tab, 0x9), \e (escape, 0x1B),
52
+ # \f (form feed, 0xC)
53
+ # \uXXXX where XXXX is a 4 hex digits integer value, \u{X...}, \ooo (octal)
54
+ # \xXX (hex)
55
+ # Any other escaped character will be treated as a literal character
56
+ # RegAn::Character.new('\n') # Represents a newline
57
+ # RegAn::Character.new('\u03a3') # Represents a Σ
58
+ def initialize(aValue)
59
+ case aValue
60
+ when String
61
+ if aValue.size == 1
62
+ # Literal single character case...
63
+ @codepoint = self.class.char2codepoint(aValue)
64
+ else
65
+ # Should be an escape sequence...
66
+ @codepoint = self.class.esc2codepoint(aValue)
67
+ end
68
+ @lexeme = aValue
69
+
70
+ when Integer
71
+ @codepoint = aValue
72
+ else
73
+ raise StandardError, "Cannot initialize a Character with a '#{aValue}'."
74
+ end
75
+ end
76
+
77
+ # Convertion method that returns a character given a codepoint (integer) value.
78
+ # Example:
79
+ # RegAn::Character::codepoint2char(0x3a3) # Returns: Σ (
80
+ # The Unicode GREEK CAPITAL LETTER SIGMA)
81
+ def self.codepoint2char(aCodepoint)
82
+ return [aCodepoint].pack('U') # Remark: chr() fails with codepoints > 256
83
+ end
84
+
85
+ # Convertion method that returns the codepoint for the given single character.
86
+ # Example:
87
+ # RegAn::Character::char2codepoint('Σ') # Returns: 0x3a3
88
+ def self.char2codepoint(aChar)
89
+ return aChar.ord
90
+ end
91
+
92
+ # Convertion method that returns the codepoint for the given escape
93
+ # sequence (a String).
94
+ # Recognized escaped characters are: \a (alarm, 0x07), \n (newline, 0xA),
95
+ # \r (carriage return, 0xD), \t (tab, 0x9), \e (escape, 0x1B), \f (form feed,
96
+ # 0xC), \v (vertical feed, 0xB)
97
+ # \uXXXX where XXXX is a 4 hex digits integer value, \u{X...}, \ooo (octal)
98
+ # \xXX (hex)
99
+ # Any other escaped character will be treated as a literal character
100
+ # Example:
101
+ # RegAn::Character::esc2codepoint('\n') # Returns: 0xd
102
+ def self.esc2codepoint(anEscapeSequence)
103
+ msg = "Escape sequence #{anEscapeSequence} does not begin with a backslash (\)."
104
+ raise StandardError, msg unless anEscapeSequence[0] == "\\"
105
+ result = (anEscapeSequence.length == 2)? digram2codepoint(anEscapeSequence) : esc_number2codepoint(anEscapeSequence)
106
+
107
+ return result
108
+ end
109
+
110
+ # Return the character as a String object
111
+ def char()
112
+ self.class.codepoint2char(@codepoint)
113
+ end
114
+
115
+ # Returns true iff this Character and parameter 'another' represent the same character.
116
+ # [another] any Object. The way the equality is tested depends on the another's class
117
+ # Example:
118
+ # newOne = Character.new(?\u03a3)
119
+ # newOne == newOne # true. Identity
120
+ # newOne == Character.new(?\u03a3) # true. Both have same codepoint
121
+ # newOne == ?\u03a3 # true. The single character String match exactly the char attribute.
122
+ # newOne == 0x03a3 # true. The Integer is compared to the codepoint value.
123
+ # Will test equality with any Object that knows the to_s method
124
+ def ==(other)
125
+ result = case other
126
+ when Character
127
+ self.to_str == other.to_str
128
+
129
+ when Integer
130
+ self.codepoint == other
131
+
132
+ when String
133
+ other.size > 1 ? false : to_str == other
134
+
135
+ else
136
+ # Unknown type: try with a convertion
137
+ self == other.to_s # Recursive call
138
+ end
139
+
140
+ return result
141
+ end
142
+
143
+ # Return a plain English description of the character
144
+ def explain()
145
+ return "the character '#{to_str}'"
146
+ end
147
+
148
+ protected
149
+
150
+ # Conversion method re-definition.
151
+ # Purpose: Return the String representation of the expression.
152
+ # If the Character was initially from a text (the lexeme), then the lexeme
153
+ # is returned back.
154
+ # Otherwise the character corresponding to the codepoint is returned.
155
+ def text_repr()
156
+ return char if lexeme.nil?
157
+ return lexeme.dup
158
+ end
159
+
160
+ # Convertion method that returns a codepoint for the given two characters
161
+ # (digram) escape sequence.
162
+ # Recognized escaped characters are: \a (alarm, 0x07), \n (newline, 0xA),
163
+ # \r (carriage return, 0xD), \t (tab, 0x9), \e (escape, 0x1B),
164
+ # \f (form feed, 0xC), \v (vertical feed, 0xB)
165
+ # Any other escape sequence will return the codepoint of the escaped
166
+ # character.
167
+ # [aDigram] A sequence of two characters that starts with a backslash.
168
+ def self.digram2codepoint(aDigram)
169
+ # Check that the digram is a special escape sequence
170
+ result = DigramSequences.fetch(aDigram, nil)
171
+
172
+ # If it not a special sequence, then escaped character is
173
+ # considered literally (the backslash is 'dummy')
174
+ result = char2codepoint(aDigram[-1]) if result.nil?
175
+ return result
176
+ end
177
+
178
+ private_class_method :digram2codepoint
179
+
180
+ # Convertion method that returns a codepoint for the given complex
181
+ # escape sequence.
182
+ # [anEscapeSequence] A String with the format:
183
+ # \uXXXX where XXXX is a 4 hex digits integer value,
184
+ # \u{X...} X 1 or more hex digits
185
+ # \ooo (1..3 octal digits literal)
186
+ # \xXX (1..2 hex digits literal)
187
+ def self.esc_number2codepoint(anEscapeSequence)
188
+ unless /^\\(?:(?:(?<prefix>[uxX])\{?(?<hexa>\h+)\}?)|(?<octal>[0-7]{1,3}))$/ =~ anEscapeSequence
189
+ raise StandardError, "Unsupported escape sequence #{anEscapeSequence}."
190
+ else
191
+ # Octal literal case?
192
+ return octal.oct if octal # shorterSeq =~ /[0-7]{1,3}/
193
+
194
+ # Extract the hexadecimal number
195
+ hexliteral = hexa # shorterSeq.sub(/^[xXu]\{?([0-9a-fA-F]+)}?$/, '\1')
196
+ return hexliteral.hex
197
+ end
198
+ end
199
+
200
+ private_class_method :esc_number2codepoint
201
+ end # class
202
+ end # module
203
+
204
+ # End of file
@@ -0,0 +1,57 @@
1
+ # File: compound_expression.rb
2
+
3
+ require_relative 'expression' # Access the superclass
4
+
5
+ module Regex # This module is used as a namespace
6
+ # Abstract class. An element that is part of a regular expression &
7
+ # that has its own child sub-expressions.
8
+ class CompoundExpression < Expression
9
+ # Redefined method. Return false since it may have one or more children.
10
+ def atomic?
11
+ return false
12
+ end
13
+
14
+ =begin
15
+ # Build a depth-first in-order children visitor.
16
+ # The visitor is implemented as an Enumerator.
17
+ def df_visitor()
18
+ root = children # The visit will start from the children of this object
19
+
20
+ visitor = Enumerator.new do |result| # result is a Yielder
21
+ # Initialization part: will run once
22
+ visit_stack = [ root ] # The LIFO queue of nodes to visit
23
+
24
+ begin # Traversal part (as a loop)
25
+ top = visit_stack.pop()
26
+ if top.kind_of?(Array)
27
+ if top.empty?
28
+ next
29
+ else
30
+ currChild = top.pop()
31
+ visit_stack.push top
32
+ end
33
+ else
34
+ currChild = top
35
+ end
36
+
37
+ result << currChild # Return the visited child
38
+
39
+ unless currChild.atomic?
40
+ children_to_enqueue = currChild.children.reverse() # in-order traversal implies LIFO queue
41
+ visit_stack.push(children_to_enqueue)
42
+ end
43
+ end until visit_stack.empty?
44
+ end
45
+ end
46
+ =end
47
+
48
+ protected
49
+
50
+ # Abstract method. Return the text representation of the child (if any)
51
+ def all_child_text()
52
+ abstract_method
53
+ end
54
+ end # class
55
+ end # module
56
+
57
+ # End of file
@@ -0,0 +1,29 @@
1
+ # File: concatenation.rb
2
+
3
+ require_relative 'polyadic_expression' # Access the superclass
4
+
5
+ module Regex # This module is used as a namespace
6
+ # Abstract class. A n-ary matching operator.
7
+ # It succeeds when each child succeeds to match the subject text in the same
8
+ # serial arrangement than defined by this concatenation.
9
+ class Concatenation < PolyadicExpression
10
+ # Constructor.
11
+ def initialize(*theChildren)
12
+ super(theChildren)
13
+ end
14
+
15
+ protected
16
+
17
+ # Conversion method re-definition.
18
+ # Purpose: Return the String representation of the concatented expressions.
19
+ def text_repr()
20
+ outcome = children.inject('') do |result, aChild|
21
+ result << aChild.to_str
22
+ end
23
+
24
+ return outcome
25
+ end
26
+ end # class
27
+ end # module
28
+
29
+ # End of file
@@ -0,0 +1,60 @@
1
+ # File: expression.rb
2
+
3
+ require_relative 'abstract_method'
4
+
5
+ module Regex # This module is used as a namespace
6
+ # Abstract class. The generalization of any valid regular (sub)expression.
7
+ class Expression
8
+ attr_accessor :begin_anchor
9
+ attr_accessor :end_anchor
10
+
11
+ # Constructor
12
+ def initialize(); end
13
+
14
+ # Abstract method. Return true iff the expression is atomic
15
+ # (= may not have any child).
16
+ def atomic?()
17
+ abstract_method
18
+ end
19
+
20
+ # Abstract method. Return the number of values that match this expression.
21
+ # [_parent_options] an Hash of matching options. They are overridden
22
+ # by options with same name that are bound to this object.
23
+ def cardinality(_parent_options)
24
+ abstract_method
25
+ end
26
+
27
+ # Determine the matching options to apply to this object, given the options
28
+ # coming from the parent
29
+ # and options that are local to this object. Local options take precedence.
30
+ # @param theParentOptions [Hash] matching options. They are overridden
31
+ # by options with same name that are bound to this object.
32
+ def options(theParentOptions)
33
+ resulting_options = theParentOptions.merge(@local_options)
34
+ return resulting_options
35
+ end
36
+
37
+ # Template method.
38
+ # Purpose: Return the String representation of the expression.
39
+ def to_str()
40
+ result = ''
41
+ result << prefix
42
+ result << text_repr
43
+ result << suffix
44
+
45
+ return result
46
+ end
47
+
48
+ protected
49
+
50
+ def prefix()
51
+ begin_anchor ? begin_anchor.to_str : ''
52
+ end
53
+
54
+ def suffix()
55
+ end_anchor ? end_anchor.to_str : ''
56
+ end
57
+ end # class
58
+ end # module
59
+
60
+ # End of file
@@ -0,0 +1,50 @@
1
+ # File: Lookaround.rb
2
+
3
+ ########################
4
+ # TODO: make it a binary expression
5
+ ########################
6
+
7
+
8
+ require_relative 'polyadic_expression' # Access the superclass
9
+
10
+ module Regex # This module is used as a namespace
11
+ # Lookaround is a zero-width assertion just like the start and end of line
12
+ # anchors.
13
+ # The difference is that lookarounds will actually match characters, but only
14
+ # return the result of the match: match or no match.
15
+ # That is why they are called "assertions". They do not consume characters
16
+ # from the subject, but only assert whether a match is possible or not.
17
+ class Lookaround < PolyadicExpression
18
+ # The "direction" of the lookaround. Can be ahead or behind. It specifies
19
+ # the relative position of the expression to match compared to
20
+ # the current 'position' in the subject text.
21
+ attr_reader(:dir)
22
+
23
+ # The kind indicates whether the assertion is positive
24
+ # (succeeds when there is a match) or negative
25
+ # (assertion succeeds when there is NO match).
26
+ attr_reader(:kind)
27
+
28
+ # Constructor.
29
+ # [assertedExpression] A sub-expression to match.
30
+ # [theDir] One of the following values: [ :ahead, :behind ]
31
+ # [theKind] One of the following values: [ :positive, :negative ]
32
+ def initialize(assertedExpression, theDir, theKind)
33
+ super([assertedExpression])
34
+ @dir = theDir
35
+ @kind = theKind
36
+ end
37
+
38
+ # Conversion method re-definition.
39
+ # Purpose: Return the String representation of the captured expression.
40
+ def to_str()
41
+ result = children[0].to_str
42
+ dir_syntax = (dir == :ahead) ? '' : '<'
43
+ kind_syntax = (kind == :positive) ? '=' : '!'
44
+ result << '(?' + dir_syntax + kind_syntax + children[1].to_str + ')'
45
+ return result
46
+ end
47
+ end # class
48
+ end # module
49
+
50
+ # End of file
@@ -0,0 +1,34 @@
1
+ # File: MatchOption.rb
2
+
3
+ module Regex # This module is used as a namespace
4
+ # Represents an option that influences the way a regular (sub)expression
5
+ # can perform its matching.
6
+ class MatchOption
7
+ # The symbolic name of the option
8
+ attr_reader(:name)
9
+
10
+ # An indicator that tells whether the option is turned on or off
11
+ attr_reader(:setting)
12
+
13
+ # Constructor.
14
+ def initialize(theName, theSetting)
15
+ @name = theName
16
+ @setting = theSetting
17
+ end
18
+
19
+ # Equality operator
20
+ def ==(other)
21
+ return true if object_id == other.object_id
22
+
23
+ if other.kind_of?(MatchOption)
24
+ isEqual = ((name == other.name) && (setting == other.setting))
25
+ else
26
+ isEqual = false
27
+ end
28
+
29
+ return isEqual
30
+ end
31
+ end # class
32
+ end # module
33
+
34
+ # End of file
@@ -0,0 +1,28 @@
1
+ # File: monadic_expression.rb
2
+
3
+ require_relative 'compound_expression' # Access the superclass
4
+
5
+ module Regex # This module is used as a namespace
6
+ # Abstract class. An element that is part of a regular expression &
7
+ # that can have up to one child sub-expression.
8
+ class MonadicExpression < CompoundExpression
9
+ # The (optional) child sub-expression
10
+ attr_reader(:child)
11
+
12
+ # Constructor.
13
+ def initialize(theChild)
14
+ super()
15
+ @child = theChild
16
+ end
17
+
18
+ protected
19
+
20
+ # Return the text representation of the child (if any)
21
+ def all_child_text()
22
+ result = child.nil? ? '' : child.to_str
23
+
24
+ return result
25
+ end
26
+ end # class
27
+ end # module
28
+ # End of file
@@ -0,0 +1,91 @@
1
+ # File: Multiplicity.rb
2
+
3
+ module SRL
4
+ module Regex # This module is used as a namespace
5
+ # The multiplicity specifies by how much a given expression can be repeated.
6
+ class Multiplicity
7
+ # The lowest acceptable repetition count
8
+ attr_reader(:lower_bound)
9
+
10
+ # The highest possible repetition count
11
+ attr_reader(:upper_bound)
12
+
13
+ # An indicator that specifies how to repeat (:greedy, :lazy, :possessive)
14
+ attr_reader(:policy)
15
+
16
+ # @param aLowerBound [Integer]
17
+ # @param anUpperBound [Integer, Symbol] integer or :more symbol
18
+ # @param aPolicy [Symbol] One of: (:greedy, :lazy, :possessive)
19
+ def initialize(aLowerBound, anUpperBound, aPolicy)
20
+ @lower_bound = valid_lower_bound(aLowerBound)
21
+ @upper_bound = valid_upper_bound(anUpperBound)
22
+ @policy = valid_policy(aPolicy)
23
+ end
24
+
25
+ # Purpose: Return the String representation of the multiplicity.
26
+ def to_str()
27
+ case upper_bound
28
+ when :more
29
+ case lower_bound
30
+ when 0
31
+ subresult = '*'
32
+ when 1
33
+ subresult = '+'
34
+ else
35
+ subresult = "{#{lower_bound},}"
36
+ end
37
+
38
+ when lower_bound
39
+ subresult = "{#{lower_bound}}"
40
+ else
41
+ if [lower_bound, upper_bound] == [0, 1]
42
+ subresult = '?'
43
+ else
44
+ subresult = "{#{lower_bound},#{upper_bound}}"
45
+ end
46
+ end
47
+
48
+ suffix = case policy
49
+ when :greedy
50
+ ''
51
+ when :lazy
52
+ '?'
53
+ when :possessive
54
+ '+'
55
+ end
56
+
57
+ return subresult + suffix
58
+ end
59
+
60
+ private
61
+
62
+ # Validation method. Return the validated lower bound value
63
+ def valid_lower_bound(aLowerBound)
64
+ err_msg = "Invalid lower bound of repetition count #{aLowerBound}"
65
+ raise StandardError, err_msg unless aLowerBound.kind_of?(Integer)
66
+ return aLowerBound
67
+ end
68
+
69
+ # Validation method. Return the validated lower bound value
70
+ def valid_upper_bound(anUpperBound)
71
+ err_msg = "Invalid upper bound of repetition count #{anUpperBound}"
72
+ unless anUpperBound.kind_of?(Integer) || (anUpperBound == :more)
73
+ raise StandardError, err_msg
74
+ end
75
+
76
+ return anUpperBound
77
+ end
78
+
79
+ # Validation method. Return the validated policy value.
80
+ def valid_policy(aPolicy)
81
+ err_msg = "Invalid repetition policy '#{aPolicy}'."
82
+ valid_policies = %i[greedy lazy possessive]
83
+ raise StandardError, err_msg unless valid_policies.include? aPolicy
84
+
85
+ return aPolicy
86
+ end
87
+ end # class
88
+ end # module
89
+ end # module
90
+
91
+ # End of file
@@ -0,0 +1,27 @@
1
+ # File: non_capturing_group.rb
2
+
3
+ require_relative 'monadic_expression' # Access the superclass
4
+
5
+ module Regex # This module is used as a namespace
6
+ # A non-capturing group, in other word it is a pure grouping
7
+ # of sub-expressions
8
+ class NonCapturingGroup < MonadicExpression
9
+ # Constructor.
10
+ # [aChildExpression] A sub-expression to match. When successful
11
+ # the matching text is assigned to the capture variable.
12
+ def initialize(aChildExpression)
13
+ super(aChildExpression)
14
+ end
15
+
16
+ protected
17
+
18
+ # Conversion method re-definition.
19
+ # Purpose: Return the String representation of the captured expression.
20
+ def text_repr()
21
+ result = '(?:' + all_child_text + ')'
22
+ return result
23
+ end
24
+ end # class
25
+ end # module
26
+
27
+ # End of file
@@ -0,0 +1,60 @@
1
+ # File: polyadic_expression.rb
2
+
3
+ require_relative 'compound_expression' # Access the superclass
4
+
5
+ module Regex # This module is used as a namespace
6
+ # Abstract class. An element that is part of a regular expression &
7
+ # that has its own child sub-expressions.
8
+ class PolyadicExpression < CompoundExpression
9
+ # The aggregation of child elements
10
+ attr_reader(:children)
11
+
12
+ # Constructor.
13
+ def initialize(theChildren)
14
+ super()
15
+ @children = theChildren
16
+ end
17
+
18
+ # Append the given child to the list of children.
19
+ # TODO: assess whether to defer to a subclass NAryExpression
20
+ def <<(aChild)
21
+ @children << aChild
22
+
23
+ return self
24
+ end
25
+
26
+ # Build a depth-first in-order children visitor.
27
+ # The visitor is implemented as an Enumerator.
28
+ def df_visitor()
29
+ root = children # The visit will start from the children of this object
30
+
31
+ visitor = Enumerator.new do |result| # result is a Yielder
32
+ # Initialization part: will run once
33
+ visit_stack = [root] # The LIFO queue of nodes to visit
34
+
35
+ begin # Traversal part (as a loop)
36
+ top = visit_stack.pop
37
+ if top.kind_of?(Array)
38
+ next if top.empty?
39
+ currChild = top.pop
40
+ visit_stack.push top
41
+ else
42
+ currChild = top
43
+ end
44
+
45
+ result << currChild # Return the visited child
46
+
47
+ unless currChild.atomic?
48
+ # in-order traversal implies LIFO queue
49
+ children_to_enqueue = currChild.children.reverse
50
+ visit_stack.push(children_to_enqueue)
51
+ end
52
+ end until visit_stack.empty?
53
+ end
54
+
55
+ return visitor
56
+ end
57
+ end # class
58
+ end # module
59
+
60
+ # End of file
@@ -0,0 +1,22 @@
1
+ # File: quantifiable.rb
2
+
3
+ require_relative 'multiplicity'
4
+
5
+ module Regex # This module is used as a namespace
6
+ module Quantifiable
7
+ # Redefined method. Return true since it may not have any child.
8
+ def quantified?
9
+ return @quantifier.nil? ? false : true
10
+ end
11
+
12
+ def quantifier
13
+ @quantifier
14
+ end
15
+
16
+ def quantifier=(aQuantifier)
17
+ @quantifier = aQuantifier
18
+ end
19
+ end # module
20
+ end # module
21
+
22
+ # End of file
@@ -0,0 +1,29 @@
1
+ # File: repetition.rb
2
+
3
+ require_relative 'monadic_expression' # Access the superclass
4
+
5
+ module Regex # This module is used as a namespace
6
+ # Abstract class. An unary matching operator.
7
+ # It succeeds when the specified repetition of the child expression
8
+ # succeeds to match the subject text in the same serial arrangement
9
+ class Repetition < MonadicExpression
10
+ attr_reader(:multiplicity)
11
+
12
+ # Constructor.
13
+ def initialize(childExpressionToRepeat, aMultiplicity)
14
+ super(childExpressionToRepeat)
15
+ @multiplicity = aMultiplicity
16
+ end
17
+
18
+ protected
19
+
20
+ # Conversion method re-definition.
21
+ # Purpose: Return the String representation of the concatented expressions.
22
+ def text_repr()
23
+ result = all_child_text + multiplicity.to_str
24
+ return result
25
+ end
26
+ end # class
27
+ end # module
28
+
29
+ # End of file