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 +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: []
|