arugula 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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: []