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 +4 -4
- data/Gemfile.lock +1 -1
- data/lib/arugula.rb +16 -3
- data/lib/arugula/match_data.rb +38 -0
- data/lib/arugula/parser.rb +18 -2
- data/lib/arugula/parts.rb +43 -25
- data/lib/arugula/version.rb +1 -1
- metadata +14 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f59b134599b696a71d41c30d0bfc84fbab54e2c0
|
4
|
+
data.tar.gz: d3d49f304685d6f458284a712780a3274bc43b97
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6cf01040b54906eaf6c9b4b4e6eff08843ad11fd6c4eb7da26865e068fb627ae9b9defd4382295120874e58bf68c48753f7c8dc80c2c2faf4bff7e1da542f883
|
7
|
+
data.tar.gz: 83b14f9ecf7a4cf2a8feef267d5599c999575db458bd35f3e08302414d4c16580d5234aa92c1d6e1edf09b544f49fc226fb0fc0b4626f650eaaf419b92a349a6
|
data/Gemfile.lock
CHANGED
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
|
-
|
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
|
data/lib/arugula/parser.rb
CHANGED
@@ -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
|
-
|
34
|
+
module MatchAll
|
35
35
|
attr_accessor :parts
|
36
36
|
def initialize
|
37
37
|
@parts = []
|
38
38
|
end
|
39
39
|
|
40
|
-
def
|
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
|
-
|
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
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
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
|
data/lib/arugula/version.rb
CHANGED
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.
|
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: []
|