arugula 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 450b105a13d2bee3427f3ab008d1a0172e0bbacf
4
- data.tar.gz: 091ebc89a888684923c2c05175c7370e43844a6f
3
+ metadata.gz: f59b134599b696a71d41c30d0bfc84fbab54e2c0
4
+ data.tar.gz: d3d49f304685d6f458284a712780a3274bc43b97
5
5
  SHA512:
6
- metadata.gz: afb71d903455aa9d1508078919cccc34fd7cb1fb5ad7e15d3e39ab8170f9d31cfd12d21633098088b57e0003e99d267dd097d5acf8aca10901afac03c1266e9e
7
- data.tar.gz: 27130b99003c17307830266fc1f2b29045d109c9bfaec2fc505f03e4978c3347e35e4945caebe4fe7437b16d7608c3c3510f910a0afc0443450fa26b598cfc5f
6
+ metadata.gz: 6cf01040b54906eaf6c9b4b4e6eff08843ad11fd6c4eb7da26865e068fb627ae9b9defd4382295120874e58bf68c48753f7c8dc80c2c2faf4bff7e1da542f883
7
+ data.tar.gz: 83b14f9ecf7a4cf2a8feef267d5599c999575db458bd35f3e08302414d4c16580d5234aa92c1d6e1edf09b544f49fc226fb0fc0b4626f650eaaf419b92a349a6
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- arugula (0.2.1)
4
+ arugula (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/lib/arugula.rb CHANGED
@@ -2,16 +2,29 @@
2
2
  class Arugula
3
3
  require 'arugula/version'
4
4
 
5
+ attr_reader :captures
6
+
7
+ autoload :MatchData, 'arugula/match_data'
5
8
  autoload :Parser, 'arugula/parser'
6
9
 
7
10
  def initialize(pattern)
8
- @root = Parser.new(pattern).parse!
11
+ @root, @captures = Parser.new(pattern).parse!
9
12
  end
10
13
 
11
14
  def match?(str, index = 0)
15
+ match_data = match(str, index)
16
+ match_data && match_data.start_index
17
+ end
18
+
19
+ def match(str, index = 0)
20
+ match_data = MatchData.new(self, str)
12
21
  loop do
13
- match, = @root.match(str, index)
14
- return index if match
22
+ match, end_index = @root.match(str, index, match_data)
23
+ if match
24
+ match_data.start_index = index
25
+ match_data.end_index = end_index
26
+ return match_data.freeze
27
+ end
15
28
  index += 1
16
29
  return if index > str.size
17
30
  end
@@ -0,0 +1,38 @@
1
+ class Arugula
2
+ class MatchData
3
+ def initialize(regexp, string)
4
+ # require "awesome_print"
5
+ # ap regexp, raw: true
6
+ @regexp = regexp
7
+ @string = string.dup.freeze
8
+ @captures = Hash[regexp.captures.map { |c| [c.name, nil] }]
9
+ end
10
+
11
+ def add_capture(name, start_index, end_index)
12
+ @captures[name] = start_index...end_index
13
+ end
14
+
15
+ attr_accessor :start_index
16
+ attr_accessor :end_index
17
+
18
+ def to_s
19
+ @string[start_index...end_index]
20
+ end
21
+
22
+ def inspect
23
+ captures_part = @captures.map do |name, range|
24
+ " #{name}:#{@string[range].dump}"
25
+ end.join
26
+ "#<MatchData #{to_s.dump}#{captures_part}>"
27
+ end
28
+
29
+ def to_a
30
+ @captures.map { |_name, range| @string[range] }.unshift(to_s)
31
+ end
32
+
33
+ def freeze
34
+ @captures.freeze
35
+ super
36
+ end
37
+ end
38
+ end
@@ -6,6 +6,7 @@ class Arugula
6
6
  def initialize(str)
7
7
  @pattern = str.dup
8
8
  @states = [AndPart.new]
9
+ @captures = []
9
10
  end
10
11
 
11
12
  def state
@@ -14,13 +15,13 @@ class Arugula
14
15
 
15
16
  def parse!
16
17
  consume until pattern.empty?
17
- @states.first
18
+ [@states.first, @captures]
18
19
  end
19
20
 
20
21
  Part.all.each do |part|
21
22
  type = part.type
22
23
  define_method(:"#{type}_type?") do
23
- state && state.class.type == type
24
+ return true if state && state.class.type == type
24
25
  end
25
26
  end
26
27
 
@@ -56,6 +57,15 @@ class Arugula
56
57
  end
57
58
  elsif characterclass_type?
58
59
  push_part(:literal, tok)
60
+ elsif tok == '('
61
+ push_capture
62
+ elsif tok == ')'
63
+ pop_part until capture_type?
64
+ pop_part
65
+ elsif tok == '|'
66
+ pop_part until state == @states.first || or_type? || capture_type?
67
+ wrap_state(:or) unless or_type?
68
+ push_part(:and)
59
69
  elsif tok == '.'
60
70
  push_part(:dot)
61
71
  elsif tok == '*'
@@ -85,5 +95,11 @@ class Arugula
85
95
  @states.pop until @states.last.respond_to?(:parts)
86
96
  @states.pop
87
97
  end
98
+
99
+ def push_capture
100
+ push_part(:capture, @captures.size.succ.to_s)
101
+ @captures << state
102
+ push_part(:and)
103
+ end
88
104
  end
89
105
  end
data/lib/arugula/parts.rb CHANGED
@@ -24,41 +24,44 @@ class Arugula
24
24
  literal.gsub('\\', '\\\\')
25
25
  end
26
26
 
27
- def match(str, index)
27
+ def match(str, index, _match_data)
28
28
  length = literal.size
29
29
  matches = str[index, length] == literal
30
30
  [matches, index + (matches ? length : 0)]
31
31
  end
32
32
  end
33
33
 
34
- class AndPart < Part
34
+ module MatchAll
35
35
  attr_accessor :parts
36
36
  def initialize
37
37
  @parts = []
38
38
  end
39
39
 
40
- def to_s
41
- parts.join
42
- end
43
-
44
- def match(str, index)
40
+ def match(str, index, match_data)
45
41
  parts.each do |part|
46
- match, index = part.match(str, index)
42
+ match, index = part.match(str, index, match_data)
47
43
  return false, index unless match
48
44
  end
49
45
  [true, index]
50
46
  end
51
47
  end
52
48
 
49
+ class AndPart < Part
50
+ include MatchAll
51
+ def to_s
52
+ parts.join
53
+ end
54
+ end
55
+
53
56
  module MatchAny
54
57
  attr_accessor :parts
55
58
  def initialize
56
59
  @parts = []
57
60
  end
58
61
 
59
- def match(str, index)
62
+ def match(str, index, match_data)
60
63
  parts.each do |part|
61
- match, match_index = part.match(str, index)
64
+ match, match_index = part.match(str, index, match_data)
62
65
  return true, match_index if match
63
66
  end
64
67
  [false, index]
@@ -67,6 +70,11 @@ class Arugula
67
70
 
68
71
  class OrPart < Part
69
72
  include MatchAny
73
+ def initialize(*parts)
74
+ super()
75
+ @parts += parts
76
+ end
77
+
70
78
  def to_s
71
79
  parts.join '|'
72
80
  end
@@ -88,7 +96,7 @@ class Arugula
88
96
  "#{@range.begin}-#{@range.end}"
89
97
  end
90
98
 
91
- def match(str, index)
99
+ def match(str, index, _match_data)
92
100
  matches = @range.member?(str[index])
93
101
  [matches, index + (matches ? 1 : 0)]
94
102
  end
@@ -114,8 +122,9 @@ class Arugula
114
122
  @metachar = metachar.to_sym
115
123
  end
116
124
 
117
- def match(str, index)
118
- [MATCHERS[@metachar][str, index], index + OFFSETS[@metachar]]
125
+ def match(str, index, _match_data)
126
+ matches = MATCHERS[@metachar][str, index]
127
+ [matches, index + (matches ? OFFSETS[@metachar] : 0)]
119
128
  end
120
129
 
121
130
  def to_s
@@ -123,15 +132,24 @@ class Arugula
123
132
  end
124
133
  end
125
134
 
126
- class MatchPart < Part
127
- attr_accessor :parts
128
- def initialize
129
- @parts = AndPart.new
135
+ class CapturePart < Part
136
+ include MatchAll
137
+ attr_reader :name
138
+
139
+ def initialize(name)
140
+ @name = name
141
+ super()
130
142
  end
131
143
 
132
144
  def to_s
133
145
  "(#{parts.join})"
134
146
  end
147
+
148
+ def match(str, index, match_data)
149
+ matches, end_index = super
150
+ match_data.add_capture(@name, index, end_index) if matches
151
+ [matches, end_index]
152
+ end
135
153
  end
136
154
 
137
155
  class EOLPart < Part
@@ -139,9 +157,9 @@ class Arugula
139
157
  '$'
140
158
  end
141
159
 
142
- def match(str, index)
160
+ def match(str, index, _match_data)
143
161
  matches = str[index] == "\n" || index == str.size
144
- return true, index + 1 if matches
162
+ return true, index if matches
145
163
  [false, index]
146
164
  end
147
165
  end
@@ -150,7 +168,7 @@ class Arugula
150
168
  '^'
151
169
  end
152
170
 
153
- def match(str, index)
171
+ def match(str, index, _match_data)
154
172
  matches = (index == 0) || (str[index - 1] == "\n")
155
173
  [matches, index]
156
174
  end
@@ -169,9 +187,9 @@ class Arugula
169
187
  "#{wrapped}*"
170
188
  end
171
189
 
172
- def match(str, index)
190
+ def match(str, index, match_data)
173
191
  loop do
174
- matches, index = wrapped.match(str, index)
192
+ matches, index = wrapped.match(str, index, match_data)
175
193
  return true, index unless matches
176
194
  end
177
195
  end
@@ -183,10 +201,10 @@ class Arugula
183
201
  "#{wrapped}+"
184
202
  end
185
203
 
186
- def match(str, index)
204
+ def match(str, index, match_data)
187
205
  has_matched = false
188
206
  loop do
189
- matches, index = wrapped.match(str, index)
207
+ matches, index = wrapped.match(str, index, match_data)
190
208
  has_matched = true if matches
191
209
  return has_matched, index unless matches
192
210
  end
@@ -198,7 +216,7 @@ class Arugula
198
216
  '.'
199
217
  end
200
218
 
201
- def match(str, index)
219
+ def match(str, index, _match_data)
202
220
  matches = index < str.size
203
221
  [matches, index + (matches ? 1 : 0)]
204
222
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  class Arugula
3
- VERSION = '0.2.1'.freeze
3
+ VERSION = '0.3.0'.freeze
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arugula
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Giddins
@@ -14,42 +14,42 @@ dependencies:
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.11'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.11'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '10.5'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.5'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ~>
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
47
  version: '3.4'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ~>
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.4'
55
55
  description:
@@ -59,10 +59,10 @@ executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
61
61
  files:
62
- - .gitignore
63
- - .rspec
64
- - .rubocop.yml
65
- - .travis.yml
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".rubocop.yml"
65
+ - ".travis.yml"
66
66
  - CODE_OF_CONDUCT.md
67
67
  - Gemfile
68
68
  - Gemfile.lock
@@ -76,6 +76,7 @@ files:
76
76
  - bin/rubocop
77
77
  - bin/setup
78
78
  - lib/arugula.rb
79
+ - lib/arugula/match_data.rb
79
80
  - lib/arugula/parser.rb
80
81
  - lib/arugula/parts.rb
81
82
  - lib/arugula/version.rb
@@ -89,12 +90,12 @@ require_paths:
89
90
  - lib
90
91
  required_ruby_version: !ruby/object:Gem::Requirement
91
92
  requirements:
92
- - - '>='
93
+ - - ">="
93
94
  - !ruby/object:Gem::Version
94
95
  version: '0'
95
96
  required_rubygems_version: !ruby/object:Gem::Requirement
96
97
  requirements:
97
- - - '>='
98
+ - - ">="
98
99
  - !ruby/object:Gem::Version
99
100
  version: '0'
100
101
  requirements: []