match_skeleton 1.0.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 +7 -0
- data/.gitignore +10 -0
- data/ChangeLog +3 -0
- data/News +3 -0
- data/README.ja.rdoc +188 -0
- data/Rakefile +10 -0
- data/lib/match_skeleton/match_data.rb +22 -0
- data/lib/match_skeleton.rb +218 -0
- data/test/test_match_skeleton.rb +353 -0
- metadata +57 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5ee807aa1f8e0b7050849503c16ac08f4ef6bc3f
|
4
|
+
data.tar.gz: 36b5a97d0a8e12c5d74924373d4d6accf3198987
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 13d436fe8338c8ccd41b6a503b5daa033dab55e6bb66dfab71fb8211a59270acac9cf60b576ed455b36f413e23ad99bd26775ac6b6425b39997668181684c231
|
7
|
+
data.tar.gz: 66d95d123d62cc28348e681f987e94b664761e73c4ae8cacb578c848270fa17e9cc28c8b22876f71faa495d92191e79046ca2c8943e4d305a9eff028aceabb12
|
data/.gitignore
ADDED
data/ChangeLog
ADDED
data/README.ja.rdoc
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
|
2
|
+
= MatchSkeleton - Equivalent to MatchData with less memory use
|
3
|
+
|
4
|
+
This package provides the class {MatchSkeleton}.
|
5
|
+
It behaves almost exactly like {MatchData}, except it uses a greatly
|
6
|
+
less and practically negligible internal memory.
|
7
|
+
|
8
|
+
The built-in {MatchData} class holds a copy of the original
|
9
|
+
{String}, from which the matched data are derived, in a "freeze"
|
10
|
+
state. It is useful because no one can by chance destructively modify
|
11
|
+
the original string. However, if is memory-savvy. Even if
|
12
|
+
{MatchData} is the match of the first few characters of a 1-GB string,
|
13
|
+
it internally holds the entire 1-GB of the original string. If a code
|
14
|
+
running holds 1000 of them, it needs a memory use of 1TB(!).
|
15
|
+
|
16
|
+
This class holds the same information of the matched data as
|
17
|
+
{MatchData} but as positional indices, and the original String object only.
|
18
|
+
Therefore, as long as the original String is not destructively
|
19
|
+
modified by an external process, any method of this class returns the
|
20
|
+
equal result as {MatchData}. Note that while the instance of this
|
21
|
+
class is kept, the original string is not garbage-collected by the
|
22
|
+
Ruby interpreter.
|
23
|
+
|
24
|
+
One additional function this class offers is the instance variable and
|
25
|
+
method <tt>pos_begin</tt>. It can be set by a user and hold the
|
26
|
+
information ({Integer}) at which position the {Regexp} match has started to
|
27
|
+
generate the matched data.
|
28
|
+
|
29
|
+
Note this library, when required, also modifies the operator {MatchData#==}
|
30
|
+
so that it can be compared with {MatchSkeleton} objects.
|
31
|
+
Now the operator is simply based on duck-typing of
|
32
|
+
{#string}, {#regexp}, and {#pre_match}.
|
33
|
+
{MatchData#eql?} is not affected, and therefore any comparison between
|
34
|
+
{MatchData} and {MatchSkeleton} objects with {MatchData#eql?} or
|
35
|
+
{MatchSkeleton#eql?} returns false.
|
36
|
+
|
37
|
+
== Install
|
38
|
+
|
39
|
+
The standard procedure to install a gem should work:
|
40
|
+
gem install match_skeleton
|
41
|
+
Or, get it from
|
42
|
+
{https://rubygems.org/gems/match_skeleton}
|
43
|
+
|
44
|
+
Then just require as
|
45
|
+
require 'match_skeleton'
|
46
|
+
|
47
|
+
Have fun!
|
48
|
+
|
49
|
+
|
50
|
+
== Simple Examples
|
51
|
+
|
52
|
+
Here are some simple examples.
|
53
|
+
|
54
|
+
require 'match_skeleton'
|
55
|
+
|
56
|
+
str = "Something_Big."
|
57
|
+
pos = 3
|
58
|
+
mtch = str.match(/(.)big/i, pos)
|
59
|
+
mskn = MatchSkeleton.new(mtch, str, pos_begin: pos)
|
60
|
+
|
61
|
+
mtch == mskn # => true
|
62
|
+
mskn.string # => "Something_Big."
|
63
|
+
mskn[1] # => "_"
|
64
|
+
mskn.pos_begin # => 3
|
65
|
+
|
66
|
+
mskn.post_match # => "."
|
67
|
+
mtch.post_match # => "."
|
68
|
+
str[-1,1] = '%'
|
69
|
+
mskn.post_match # => "%" # The original string has been destructively modified. Do not do that...
|
70
|
+
mtch.post_match # => "."
|
71
|
+
mtch == mskn # => false
|
72
|
+
|
73
|
+
== Known bugs
|
74
|
+
|
75
|
+
* None.
|
76
|
+
|
77
|
+
This library has been tested in Ruby 2.0 or later only.
|
78
|
+
It may work in Ruby 1.8, but has not been tested.
|
79
|
+
|
80
|
+
Extensive tests have been performed, as included in the package.
|
81
|
+
|
82
|
+
== Acknowledgement
|
83
|
+
|
84
|
+
This work is supported by {Wise Babel Ltd}[http://www.wisebabel.com/].
|
85
|
+
{http://www.wisebabel.com/}
|
86
|
+
|
87
|
+
== Copyright etc
|
88
|
+
|
89
|
+
Author:: Masa Sakano < imagine a_t sakano dot co dot uk >
|
90
|
+
License:: MIT.
|
91
|
+
Warranty:: No warranty whatsoever.
|
92
|
+
Versions:: The versions of this package follow Semantic Versioning (2.0.0) http://semver.org/
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
= MatchSkeleton - MatchDataに等価でメモリを節約するクラス
|
97
|
+
|
98
|
+
クラス {MatchSkeleton} のGemです。
|
99
|
+
{MatchData}とほぼ完全に等価な動作をします。ただし、メモリ使用量は
|
100
|
+
比較してはるかに少なくなり、ほぼ無視できます。
|
101
|
+
|
102
|
+
組込{MatchData}クラスは、正規表現マッチした元の文字列(String)のコピーを
|
103
|
+
"freeze"した状態で保持します。おかげで、外部のプロセスが元文字列を
|
104
|
+
破壊的に変更して{MatchData}のオブジェクトも影響を受ける、ということは
|
105
|
+
ありません。しかし、それはメモリ使用量が巨大になり得ます。たとえば、
|
106
|
+
{MatchData}オブジェクトが1GBの文字列の最初の数文字だけのマッチだったと
|
107
|
+
しても、同オブジェクトは、その1GBの文字列を内部的に保持します。
|
108
|
+
もし動作中のコードがそんなオブジェクトを1000個保持していたら、
|
109
|
+
それだけで 1TB(!)のメモリを消費することになります。
|
110
|
+
|
111
|
+
このクラスは、{MatchData}と同じ情報を保持しますが、それは内部的に、
|
112
|
+
インデックスと(コピーではない)原文字列そのものだけです。したがって、
|
113
|
+
原文字列が外部のプロセスで破壊的に変更されない限り、このクラスの
|
114
|
+
メソッドの返り値は、{MatchData}と同じです。念のため、このクラスの
|
115
|
+
インスタンスが生きている限り、原文字列がガーベージ・コレクトされる
|
116
|
+
ことはありません。
|
117
|
+
|
118
|
+
一点、このクラス独自のパラメーターとして、インスタンス変数かつ
|
119
|
+
メソッドの<tt>pos_begin</tt>が導入されています。正規表現マッチの
|
120
|
+
開始ポジション({Integer})を(ユーザーが設定することで)保持します。
|
121
|
+
|
122
|
+
なお、このライブラリを require した時、演算子 {MatchData#==}
|
123
|
+
が変更され、{MatchSkeleton} と比較できるようになります。
|
124
|
+
結果、同演算子は、ダック・タイピングとして、
|
125
|
+
{#string}, {#regexp}, および {#pre_match}に依存するようになります。
|
126
|
+
一方、{MatchData#eql?} は影響を受けません。すなわち、
|
127
|
+
{MatchData#eql?} あるいは {MatchSkeleton#eql?}によって、
|
128
|
+
{MatchData} と {MatchSkeleton} オブジェクトを比較すると常に
|
129
|
+
偽(false)を返します。
|
130
|
+
|
131
|
+
|
132
|
+
== インストール
|
133
|
+
|
134
|
+
gem を使ってインストールできます。
|
135
|
+
gem install match_skeleton
|
136
|
+
もしくは以下から入手して下さい。
|
137
|
+
{https://rubygems.org/gems/match_skeleton}
|
138
|
+
|
139
|
+
あとは、以下で一発です。
|
140
|
+
require 'match_skeleton'
|
141
|
+
|
142
|
+
お楽しみあれ!
|
143
|
+
|
144
|
+
|
145
|
+
== 単純な使用例
|
146
|
+
|
147
|
+
以下に幾つかの基本的な使用例を列挙します。
|
148
|
+
|
149
|
+
require 'match_skeleton'
|
150
|
+
|
151
|
+
str = "Something_Big."
|
152
|
+
pos = 3
|
153
|
+
mtch = str.match(/(.)big/i, pos)
|
154
|
+
mskn = MatchSkeleton.new(mtch, str, pos_begin: pos)
|
155
|
+
|
156
|
+
mtch == mskn # => true
|
157
|
+
mskn.string # => "Something_Big."
|
158
|
+
mskn[1] # => "_"
|
159
|
+
mskn.pos_begin # => 3
|
160
|
+
|
161
|
+
mskn.post_match # => "."
|
162
|
+
mtch.post_match # => "."
|
163
|
+
str[-1,1] = '%'
|
164
|
+
mskn.post_match # => "%" # 元文字列が破壊的に変更された結果。そんなことはしないように……。
|
165
|
+
mtch.post_match # => "."
|
166
|
+
mtch == mskn # => false
|
167
|
+
|
168
|
+
== 既知のバグ
|
169
|
+
|
170
|
+
* なし。
|
171
|
+
|
172
|
+
このライブラリは Ruby 2.0 以上でのみ挙動試験されています。
|
173
|
+
1.8以前でも動くかもしれませんが、テストされていません。
|
174
|
+
|
175
|
+
パッケージに含まれている通り、網羅的なテストが実行されています。
|
176
|
+
|
177
|
+
== 謝辞
|
178
|
+
|
179
|
+
この開発は、ワイズバベル社({Wise Babel Ltd}[http://www.wisebabel.com/en])に支援されています。
|
180
|
+
http://www.wisebabel.com/ja
|
181
|
+
|
182
|
+
== 著作権他情報
|
183
|
+
|
184
|
+
著者:: Masa Sakano < imagine a_t sakano dot co dot uk >
|
185
|
+
利用許諾条項:: MIT.
|
186
|
+
保証:: 一切無し。
|
187
|
+
バージョン:: Semantic Versioning (2.0.0) http://semver.org/
|
188
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# =Class MatchData
|
4
|
+
#
|
5
|
+
# Modifies the operator {#==} so it can be compared with {MatchSkeleton}.
|
6
|
+
#
|
7
|
+
class MatchData
|
8
|
+
|
9
|
+
# Backup alias for {MatchData#==}
|
10
|
+
alias_method :equal_before_match_skeleton, :== if ! self.method_defined?(:equal_before_match_skeleton)
|
11
|
+
|
12
|
+
# Compares with {MatchSkeleton}, in addition to {MatchData}
|
13
|
+
#
|
14
|
+
# @param [#string, #regexp, #pre_match] All the methods have to be there. Practically, {MatchSkeleton} and {MatchData}
|
15
|
+
# @return [Boolean]
|
16
|
+
def ==(obj)
|
17
|
+
(string == obj.string) &&
|
18
|
+
(regexp == obj.regexp) &&
|
19
|
+
(pre_match == obj.pre_match)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'match_skeleton/match_data'
|
4
|
+
|
5
|
+
# =Class MatchSkeleton
|
6
|
+
#
|
7
|
+
# To represent {MatchData} with much less memory use
|
8
|
+
#
|
9
|
+
class MatchSkeleton
|
10
|
+
|
11
|
+
# The same as {MatchData#string} but it is identical to the original string.
|
12
|
+
# If the original string is modified destructively, this too is modified.
|
13
|
+
attr_reader :string
|
14
|
+
|
15
|
+
# The same as {MatchData#regexp}.
|
16
|
+
attr_reader :regexp
|
17
|
+
|
18
|
+
# The position {Regexp} match has started.
|
19
|
+
# For example, both
|
20
|
+
# /x/.match('0000x', 0)
|
21
|
+
# /x/.match('0000x', 3)
|
22
|
+
# give the same (equal) {MatchData}. This instance variable {#pos_begin}
|
23
|
+
# holds the position (0 or 3 in the cases above), if set explicitly.
|
24
|
+
attr_accessor :pos_begin
|
25
|
+
|
26
|
+
|
27
|
+
# @param md [MatchData]
|
28
|
+
# @param string [String] If not specified, it is taken from the first argument (however, that would spoil the whole point of using this class!)
|
29
|
+
# @param pos_begin: [Integer] The position {Regexp} match has started.
|
30
|
+
def initialize(md, string=nil, pos_begin: nil)
|
31
|
+
size_str = md.string.size
|
32
|
+
if string && string.size != size_str
|
33
|
+
raise ArgumentError, 'The first parameter is obligatory.'
|
34
|
+
end
|
35
|
+
@string = (string || md.string)
|
36
|
+
@regexp = md.regexp
|
37
|
+
@pre_match = (0..(md.pre_match.size-1)) # {Range}
|
38
|
+
@post_match = ((size_str-md.post_match.size)..(size_str-1)) # {Range}
|
39
|
+
|
40
|
+
# @offsets is Hash with the keys of both Integer and possibly Symbol
|
41
|
+
# if names exist.
|
42
|
+
names = md.names
|
43
|
+
ar_off = (0..(md.size-1)).map do |n|
|
44
|
+
ar = md.offset(n)
|
45
|
+
#if names[0]=='foo'
|
46
|
+
#print "DEBUG: ar=#{ar.inspect}\n"
|
47
|
+
#printf "DEBUG: n=(%d)range=%s\n", n, (ar.first...ar.last).inspect
|
48
|
+
#end
|
49
|
+
(ar.first...ar.last)
|
50
|
+
end
|
51
|
+
@offsets = {}
|
52
|
+
ar_off.each_with_index do |ev, ei|
|
53
|
+
@offsets[ei] = ev
|
54
|
+
ej = ei - 1
|
55
|
+
@offsets[names[ej]] = ev if (ej >= 0 && names[ej])
|
56
|
+
#print "DEBUG: names=#{names[ei].inspect}\n"
|
57
|
+
#p names[ei], ev
|
58
|
+
#p md.offset(:foo)
|
59
|
+
end
|
60
|
+
#printf "DEBUG: offsets=%s\n", @offsets.inspect if !names.empty?
|
61
|
+
|
62
|
+
|
63
|
+
@pos_begin = pos_begin
|
64
|
+
end
|
65
|
+
|
66
|
+
# Comparable with {MatchSkeleton} and {MatchData}
|
67
|
+
#
|
68
|
+
# A difference in {#pos_begin} is not taken into account.
|
69
|
+
#
|
70
|
+
# @param [Object] The methods of {#string}, {#regexp}, {#pre_match} have to be defined and return the same to be true. Practically, only {MatchSkeleton} and {MatchData} may return true.
|
71
|
+
# @return [Boolean]
|
72
|
+
# @see #eql?
|
73
|
+
def ==(obj)
|
74
|
+
(string == obj.string) &&
|
75
|
+
(regexp == obj.regexp) &&
|
76
|
+
(pre_match == obj.pre_match)
|
77
|
+
end
|
78
|
+
|
79
|
+
# The same as {MatchData#[]}
|
80
|
+
#
|
81
|
+
# @param i [Integer, Range, Symbol, String]
|
82
|
+
# @param j [Integer, NilClass]
|
83
|
+
# @return [String, Array, NilClass]
|
84
|
+
# @raise [IndexError] if an invalid argument(s) is given.
|
85
|
+
def [](i, j=nil)
|
86
|
+
if j
|
87
|
+
to_a[i, j]
|
88
|
+
elsif defined?(i.to_sym)
|
89
|
+
values_at(i)[0]
|
90
|
+
else
|
91
|
+
to_a[i]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# The same as {MatchData#begin}
|
96
|
+
#
|
97
|
+
# @param n [Integer]
|
98
|
+
# @return [Integer]
|
99
|
+
def begin(n)
|
100
|
+
offset(n)[0]
|
101
|
+
end
|
102
|
+
|
103
|
+
# The same as {MatchData#captures}
|
104
|
+
#
|
105
|
+
# @return [Array]
|
106
|
+
def captures
|
107
|
+
to_a[1..-1]
|
108
|
+
end
|
109
|
+
|
110
|
+
# The same as {MatchData#end}
|
111
|
+
#
|
112
|
+
# @param n [Integer]
|
113
|
+
# @return [Integer]
|
114
|
+
def end(n)
|
115
|
+
offset(n)[1]
|
116
|
+
end
|
117
|
+
|
118
|
+
# The same as {#==} but stricter comparison.
|
119
|
+
#
|
120
|
+
# The comparison between {MatchSkeleton} and {MatchData} returns false and
|
121
|
+
# that between {MatchSkeleton} with different {#pos_begin} or
|
122
|
+
# those with strings of different Object-IDs also return false.
|
123
|
+
#
|
124
|
+
# @param [Object]
|
125
|
+
# @return [Boolean]
|
126
|
+
# @see #==
|
127
|
+
def eql?(obj)
|
128
|
+
return false if self != obj
|
129
|
+
return false if !defined?(obj.pos_begin)
|
130
|
+
return false if (string.object_id != obj.string.object_id)
|
131
|
+
return false if pos_begin != obj.pos_begin
|
132
|
+
return true
|
133
|
+
end
|
134
|
+
|
135
|
+
# Similar to {MatchData#inspect}
|
136
|
+
#
|
137
|
+
# @return [String]
|
138
|
+
def inspect
|
139
|
+
core = ''
|
140
|
+
ar = (names.empty? ? captures : names)
|
141
|
+
ar.each_with_index do |ev, ei|
|
142
|
+
core << sprintf(" %d:%s", ei, ev.inspect)
|
143
|
+
end
|
144
|
+
sprintf("#<%s %s%s>", self.class.to_s, self[0].inspect, core)
|
145
|
+
end
|
146
|
+
|
147
|
+
# The same as {MatchData#names} and {Regexp#names}
|
148
|
+
#
|
149
|
+
# @return [Array<String>]
|
150
|
+
def names
|
151
|
+
regexp.names
|
152
|
+
end
|
153
|
+
|
154
|
+
# The same as {MatchData#offset}
|
155
|
+
#
|
156
|
+
# @param n [Integer]
|
157
|
+
# @return [Array<integer>]
|
158
|
+
def offset(n)
|
159
|
+
if defined?(n.to_sym)
|
160
|
+
n = n.to_s
|
161
|
+
raise IndexError, sprintf("undefined group name reference: %s", n) if !names.include?(n)
|
162
|
+
end
|
163
|
+
[@offsets[n].first,
|
164
|
+
@offsets[n].last]
|
165
|
+
end
|
166
|
+
|
167
|
+
# The same as {MatchData#pre_match}
|
168
|
+
#
|
169
|
+
# @return [String]
|
170
|
+
def pre_match
|
171
|
+
@string[@pre_match]
|
172
|
+
end
|
173
|
+
|
174
|
+
# The same as {MatchData#post_match}
|
175
|
+
#
|
176
|
+
# @return [String]
|
177
|
+
def post_match
|
178
|
+
@string[@post_match]
|
179
|
+
end
|
180
|
+
|
181
|
+
# The same as {MatchData#size}
|
182
|
+
#
|
183
|
+
# @return [Integer]
|
184
|
+
def size
|
185
|
+
to_a.size
|
186
|
+
end
|
187
|
+
alias_method :length, :size if ! self.method_defined?(:length)
|
188
|
+
|
189
|
+
# The same as {MatchData#to_a}
|
190
|
+
#
|
191
|
+
# @return [Array]
|
192
|
+
def to_a
|
193
|
+
#print 'DEBUG: '; p @offsets
|
194
|
+
indices = @offsets.keys.sort
|
195
|
+
indices.delete_if { |i| !defined?(i.divmod) }
|
196
|
+
indices.map { |i| string[@offsets[i]] }
|
197
|
+
end
|
198
|
+
|
199
|
+
# The same as {MatchData#to_s}
|
200
|
+
#
|
201
|
+
# @return [String]
|
202
|
+
def to_s
|
203
|
+
self[0]
|
204
|
+
end
|
205
|
+
|
206
|
+
# The same as {MatchData#values_at}
|
207
|
+
#
|
208
|
+
# @param *rest [Integer, Symbol, String]
|
209
|
+
# @return [Array]
|
210
|
+
def values_at(*rest)
|
211
|
+
rest.map do |i|
|
212
|
+
key = @offsets[i.to_s]
|
213
|
+
# printf "DEBUG(%s): offsets=%s string=%s i=%s key=%s r=%s\n", __method__, @offsets.inspect,string.inspect,i.inspect,key.inspect,(string[key].inspect rescue 'nil') if !names.empty?
|
214
|
+
raise IndexError, sprintf("undefined group name reference: %s", i) if !key
|
215
|
+
string[key]
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
@@ -0,0 +1,353 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
$stdout.sync=true
|
4
|
+
$stderr.sync=true
|
5
|
+
# print '$LOAD_PATH=';p $LOAD_PATH
|
6
|
+
|
7
|
+
arlibrelpath = []
|
8
|
+
arlibdir = %w(match_skeleton)
|
9
|
+
arlibbase = [''] # match_skeleton.rb is read.
|
10
|
+
|
11
|
+
arlibbase.each do |elibbase|
|
12
|
+
arlibdir.each do |elibdir|
|
13
|
+
|
14
|
+
arAllPaths = []
|
15
|
+
er=nil
|
16
|
+
pathnow = nil
|
17
|
+
# (['../lib/', 'lib/', ''].map{|i| i+elibbase+'/'} + ['']).each do |dir|
|
18
|
+
['../lib', 'lib', ''].each do |dirroot|
|
19
|
+
begin
|
20
|
+
s = [dirroot, elibdir, elibbase].join('/').sub(%r@^/@, '').sub(%r@/$@, '')
|
21
|
+
# eg., %w(../lib/rangeary lib/rangeary rangeary)
|
22
|
+
next if s.empty?
|
23
|
+
arAllPaths.push(s)
|
24
|
+
require s
|
25
|
+
pathnow = s
|
26
|
+
break
|
27
|
+
rescue LoadError => er
|
28
|
+
end
|
29
|
+
end # (['../lib/', 'lib/', ''].map{|i| i+elibbase+'/'} + '').each do |dir|
|
30
|
+
|
31
|
+
if pathnow.nil?
|
32
|
+
warn "Warning: All the attempts to load the following files have failed. Abort..."
|
33
|
+
warn arAllPaths.inspect
|
34
|
+
warn " NOTE: It may be because a require statement in that file failed,
|
35
|
+
rather than requiring the file itself.
|
36
|
+
Check with % ruby -r#{File.basename(elibbase)} -e p
|
37
|
+
or maybe add env RUBYLIB=$RUBYLIB:`pwd`"
|
38
|
+
# p $LOADED_FEATURES.grep(/#{Regexp.quote(File.basename(elibbase)+'.rb')}$/)
|
39
|
+
raise er
|
40
|
+
else
|
41
|
+
arlibrelpath.push pathnow
|
42
|
+
end
|
43
|
+
end # arlibdir.each do |elibdir|
|
44
|
+
end # arlibbase.each do |elibbase|
|
45
|
+
|
46
|
+
print "NOTE: Library relative paths: "; p arlibrelpath
|
47
|
+
print "NOTE: Library full paths:\n"
|
48
|
+
arlibbase.each do |elibbase|
|
49
|
+
fname = (elibbase.empty? ? arlibdir[0] : elibbase) # Not complete.
|
50
|
+
p $LOADED_FEATURES.grep(/#{Regexp.quote(File.basename(fname)+'.rb')}$/)
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
#################################################
|
55
|
+
# Unit Test
|
56
|
+
#################################################
|
57
|
+
|
58
|
+
require 'minitest/autorun'
|
59
|
+
|
60
|
+
class TestMatchSkeleton < Minitest::Test
|
61
|
+
T = true
|
62
|
+
F = false
|
63
|
+
Inf = Float::INFINITY
|
64
|
+
BS ||= "\u005c"
|
65
|
+
BS_QUOTE ||= Regexp.quote(BS)
|
66
|
+
|
67
|
+
def setup
|
68
|
+
# @ib = 1
|
69
|
+
end
|
70
|
+
|
71
|
+
def teardown # teardown is not often used.
|
72
|
+
# @foo = nil
|
73
|
+
end
|
74
|
+
|
75
|
+
# # User method
|
76
|
+
# def conjRE(r1, r2)
|
77
|
+
# Escape_Character.class_eval{ conjunctionRangeExtd(r1, r2) }
|
78
|
+
# end
|
79
|
+
|
80
|
+
def test_readme_example01
|
81
|
+
str = "Something_Big."
|
82
|
+
pos = 3
|
83
|
+
mtch = str.match(/(.)big/i, pos)
|
84
|
+
mskn = MatchSkeleton.new(mtch, str, pos_begin: pos)
|
85
|
+
|
86
|
+
assert_equal true, (mtch == mskn)
|
87
|
+
assert_equal "Something_Big.", mskn.string
|
88
|
+
assert_equal "_", mskn[1]
|
89
|
+
assert_equal 3, mskn.pos_begin
|
90
|
+
|
91
|
+
assert_equal ".", mskn.post_match
|
92
|
+
assert_equal ".", mtch.post_match
|
93
|
+
str[-1,1] = '%'
|
94
|
+
assert_equal '%', mskn.post_match
|
95
|
+
assert_equal ".", mtch.post_match
|
96
|
+
assert_equal false, (mtch == mskn)
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_string01
|
100
|
+
# string(), regexp()
|
101
|
+
s = 'abcdefghij'
|
102
|
+
id = s.object_id
|
103
|
+
re = /./i
|
104
|
+
md = s.match(re)
|
105
|
+
ms1 = MatchSkeleton.new(md, s)
|
106
|
+
|
107
|
+
assert_equal s, ms1.string
|
108
|
+
assert_equal id, ms1.string.object_id
|
109
|
+
assert_equal re, ms1.regexp
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_pos_begin01
|
113
|
+
s = 'abcdefghij'
|
114
|
+
re = /g/i
|
115
|
+
pos = 3
|
116
|
+
md = s.match(re, pos)
|
117
|
+
ms1 = MatchSkeleton.new(md, s, pos_begin: pos)
|
118
|
+
|
119
|
+
assert_equal pos, ms1.pos_begin
|
120
|
+
ms1.pos_begin = 5
|
121
|
+
assert_equal 5, ms1.pos_begin
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_equal01
|
125
|
+
s = 'abcdefghij'
|
126
|
+
re1 = /g/
|
127
|
+
pos = 3
|
128
|
+
|
129
|
+
md1 = s.match(re1, pos)
|
130
|
+
ms1 = MatchSkeleton.new(md1, s, pos_begin: pos)
|
131
|
+
assert_equal true, (ms1 == md1)
|
132
|
+
assert_equal true, (md1 == ms1)
|
133
|
+
assert_equal false, (ms1.eql?(md1))
|
134
|
+
|
135
|
+
# pos_begin does not count.
|
136
|
+
ms1p = ms1.dup
|
137
|
+
assert_equal ms1.string.object_id, ms1p.string.object_id
|
138
|
+
assert_equal true, (ms1 == ms1p)
|
139
|
+
assert_equal true, (ms1.eql?(ms1p))
|
140
|
+
ms1p.pos_begin = 6
|
141
|
+
assert_equal true, (ms1 == ms1p)
|
142
|
+
assert_equal false, (ms1.eql?(ms1p))
|
143
|
+
|
144
|
+
# Different Object-ID
|
145
|
+
s2 = s.dup
|
146
|
+
md2 = s2.match(re1, pos)
|
147
|
+
ms21= MatchSkeleton.new(md2, s, pos_begin: pos) # Equal but non-identical MatchData, but identical string s (with the identical Object-IDs)
|
148
|
+
assert_equal true, (ms1 == ms21)
|
149
|
+
assert_equal true, (ms1 == md2)
|
150
|
+
assert_equal true, (md2 == ms1)
|
151
|
+
assert_equal true, (ms21== md2)
|
152
|
+
assert_equal true, (md2 == ms21)
|
153
|
+
assert_equal true, (ms21.eql?(ms1))
|
154
|
+
|
155
|
+
ms2 = MatchSkeleton.new(md2, s2, pos_begin: pos) # Equal MatchData, but non-identical string s (with the different Object-IDs)
|
156
|
+
assert_equal true, (ms1 == ms2)
|
157
|
+
assert_equal true, (ms1 == md2)
|
158
|
+
assert_equal true, (ms2 == md2)
|
159
|
+
assert_equal true, (md2 == ms2)
|
160
|
+
assert_equal false, (ms2.eql?(ms1))
|
161
|
+
|
162
|
+
# Slightly different Regexp
|
163
|
+
re3 = /g/i
|
164
|
+
md3 = s.match(re3, pos)
|
165
|
+
ms3 = MatchSkeleton.new(md3, s, pos_begin: pos)
|
166
|
+
assert_equal false, (ms3 == ms1)
|
167
|
+
|
168
|
+
# Different pre_match
|
169
|
+
re4 = /./
|
170
|
+
pos4 = 2
|
171
|
+
md4 = s.match(re4, pos4)
|
172
|
+
ms4 = MatchSkeleton.new(md4, s)
|
173
|
+
pos5 = 6
|
174
|
+
md5 = s.match(re4, pos5)
|
175
|
+
ms5 = MatchSkeleton.new(md5, s)
|
176
|
+
assert_equal false, (ms4 == ms5)
|
177
|
+
end
|
178
|
+
|
179
|
+
def test_squarebracket_single
|
180
|
+
s = "THX1138."
|
181
|
+
md1 = /(.)(.)(\d+)(\d)/.match(s) #=> #<MatchData "HX1138" 1:"H" 2:"X" 3:"113" 4:"8">
|
182
|
+
ms1 = MatchSkeleton.new(md1, s)
|
183
|
+
assert_equal md1[0], ms1[0]
|
184
|
+
assert_equal "HX1138", ms1[0]
|
185
|
+
assert_equal md1[1], ms1[1]
|
186
|
+
assert_equal "H", ms1[1]
|
187
|
+
assert_equal md1[3], ms1[3]
|
188
|
+
assert_equal "113", ms1[3]
|
189
|
+
end
|
190
|
+
|
191
|
+
def test_squarebracket_double
|
192
|
+
s = "THX1138."
|
193
|
+
md1 = /(.)(.)(\d+)(\d)/.match(s) #=> #<MatchData "HX1138" 1:"H" 2:"X" 3:"113" 4:"8">
|
194
|
+
ms1 = MatchSkeleton.new(md1, s)
|
195
|
+
assert_equal md1[1,2], ms1[1, 2]
|
196
|
+
assert_equal ["H", "X"], ms1[1, 2]
|
197
|
+
assert_equal md1[-3, 2], ms1[-3, 2]
|
198
|
+
assert_equal ["X", "113"], ms1[-3, 2]
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_squarebracket_range
|
202
|
+
s = "THX1138."
|
203
|
+
md1 = /(.)(.)(\d+)(\d)/.match(s) #=> #<MatchData "HX1138" 1:"H" 2:"X" 3:"113" 4:"8">
|
204
|
+
ms1 = MatchSkeleton.new(md1, s)
|
205
|
+
assert_equal md1[1..3], ms1[1..3]
|
206
|
+
assert_equal ["H", "X", "113"], ms1[1..3]
|
207
|
+
end
|
208
|
+
|
209
|
+
def test_squarebracket_name
|
210
|
+
s = "ccaaab"
|
211
|
+
md1 = /(?<foo>a+)b/.match(s) #=> #<MatchData "aaab" foo:"aaa">
|
212
|
+
ms1 = MatchSkeleton.new(md1, s)
|
213
|
+
assert_equal md1["foo"], ms1["foo"]
|
214
|
+
assert_equal "aaa", ms1["foo"]
|
215
|
+
assert_equal md1[:foo], ms1[:foo]
|
216
|
+
assert_equal "aaa", ms1[:foo]
|
217
|
+
assert_raises(IndexError) { md1.begin(:naiyo) }
|
218
|
+
assert_raises(IndexError) { ms1.begin(:naiyo) }
|
219
|
+
end
|
220
|
+
|
221
|
+
def test_begin_end_int01
|
222
|
+
s ="THX1138."
|
223
|
+
md1 = /(.)(.)(\d+)(\d)/.match(s)
|
224
|
+
ms1 = MatchSkeleton.new(md1, s)
|
225
|
+
assert_equal md1.begin(0), ms1.begin(0)
|
226
|
+
assert_equal md1.begin(2), ms1.begin(2)
|
227
|
+
assert_equal md1.end(0), ms1.end(0)
|
228
|
+
assert_equal md1.end(2), ms1.end(2)
|
229
|
+
assert_equal 1, ms1.begin(0)
|
230
|
+
assert_equal 2, ms1.begin(2)
|
231
|
+
assert_equal 7, ms1.end(0)
|
232
|
+
assert_equal 3, ms1.end(2)
|
233
|
+
end
|
234
|
+
|
235
|
+
def test_begin_end_int02
|
236
|
+
s ="hoge"
|
237
|
+
md1 = /(?<foo>.)(.)(?<bar>.)/.match(s)
|
238
|
+
ms1 = MatchSkeleton.new(md1, s)
|
239
|
+
assert_equal md1.begin(:foo), ms1.begin(:foo)
|
240
|
+
assert_equal md1.begin(:bar), ms1.begin(:bar)
|
241
|
+
assert_equal md1.end(:bar), ms1.end(:bar)
|
242
|
+
assert_equal md1.end(:foo), ms1.end(:foo)
|
243
|
+
assert_equal 0, ms1.begin(:foo)
|
244
|
+
assert_equal 2, ms1.begin(:bar)
|
245
|
+
assert_equal 1, ms1.end(:foo)
|
246
|
+
assert_equal 3, ms1.end(:bar)
|
247
|
+
assert_raises(IndexError) { ms1.begin(:naiyo) }
|
248
|
+
assert_raises(IndexError) { ms1.end(:naiyo) }
|
249
|
+
end
|
250
|
+
|
251
|
+
def test_captures01
|
252
|
+
s ="THX1138."
|
253
|
+
md1 = /(.)(.)(\d+)(\d)/.match(s)
|
254
|
+
ms1 = MatchSkeleton.new(md1, s)
|
255
|
+
assert_equal md1.captures.size, ms1.captures.size
|
256
|
+
assert_equal md1.captures, ms1.captures
|
257
|
+
assert_equal md1.captures[0], ms1.captures[0]
|
258
|
+
assert_equal ms1.captures[3], ms1.captures[3]
|
259
|
+
assert_equal 4, ms1.captures.size
|
260
|
+
assert_equal ms1.to_a[1..-1], ms1.captures
|
261
|
+
assert_equal 'H', ms1.captures[0]
|
262
|
+
assert_equal '8', ms1.captures[3]
|
263
|
+
end
|
264
|
+
|
265
|
+
def test_inspect01
|
266
|
+
s ="foo"
|
267
|
+
md1 = /.$/.match(s)
|
268
|
+
ms1 = MatchSkeleton.new(md1, s)
|
269
|
+
assert_equal '#<MatchSkeleton "o">', ms1.inspect
|
270
|
+
end
|
271
|
+
|
272
|
+
def test_names01
|
273
|
+
s = "hoge"
|
274
|
+
md1 = /(?<foo>.)(?<bar>.)(?<baz>.)/.match(s)
|
275
|
+
ms1 = MatchSkeleton.new(md1, s)
|
276
|
+
assert_equal md1.names, ms1.names
|
277
|
+
assert_equal ["foo", "bar", "baz"], ms1.names
|
278
|
+
|
279
|
+
s = "a"
|
280
|
+
md1 = /(?<x>.)(?<y>.)?/.match(s) #=> #<MatchData "a" x:"a" y:nil>
|
281
|
+
ms1 = MatchSkeleton.new(md1, s)
|
282
|
+
assert_equal md1.names, ms1.names
|
283
|
+
assert_equal ["x", "y"], ms1.names
|
284
|
+
end
|
285
|
+
|
286
|
+
def test_offset_int01
|
287
|
+
s = "THX1138."
|
288
|
+
md1 = /(.)(.)(\d+)(\d)/.match(s)
|
289
|
+
ms1 = MatchSkeleton.new(md1, s)
|
290
|
+
assert_equal md1.offset(0), ms1.offset(0)
|
291
|
+
assert_equal md1.offset(4), ms1.offset(4)
|
292
|
+
assert_equal [1, 7], ms1.offset(0)
|
293
|
+
assert_equal [6, 7], ms1.offset(4)
|
294
|
+
end
|
295
|
+
|
296
|
+
def test_offset_name01
|
297
|
+
s = "hoge"
|
298
|
+
md1 = /(?<foo>.)(.)(?<bar>.)/.match(s)
|
299
|
+
ms1 = MatchSkeleton.new(md1, s)
|
300
|
+
assert_equal md1.offset(:foo), ms1.offset(:foo)
|
301
|
+
assert_equal md1.offset(:bar), ms1.offset(:bar)
|
302
|
+
assert_equal [0, 1], ms1.offset(:foo)
|
303
|
+
assert_equal [2, 3], ms1.offset(:bar)
|
304
|
+
assert_raises(IndexError) { ms1.offset(:naiyo) }
|
305
|
+
end
|
306
|
+
|
307
|
+
def test_post_match01
|
308
|
+
s = "THX1138: The Movie"
|
309
|
+
md1 = /(.)(.)(\d+)(\d)/.match(s)
|
310
|
+
ms1 = MatchSkeleton.new(md1, s)
|
311
|
+
assert_equal md1.post_match, ms1.post_match
|
312
|
+
assert_equal ": The Movie", ms1.post_match
|
313
|
+
end
|
314
|
+
|
315
|
+
def test_pre_match01
|
316
|
+
s = "THX1138."
|
317
|
+
md1 = /(.)(.)(\d+)(\d)/.match(s)
|
318
|
+
ms1 = MatchSkeleton.new(md1, s)
|
319
|
+
assert_equal md1.pre_match, ms1.pre_match
|
320
|
+
assert_equal "T", ms1.pre_match
|
321
|
+
end
|
322
|
+
|
323
|
+
def test_size01
|
324
|
+
s = "THX1138."
|
325
|
+
md1 = /(.)(.)(\d+)(\d)/.match(s)
|
326
|
+
ms1 = MatchSkeleton.new(md1, s)
|
327
|
+
assert_equal md1.size, ms1.size
|
328
|
+
assert_equal md1.length, ms1.length
|
329
|
+
assert_equal 5, ms1.size
|
330
|
+
assert_equal 5, ms1.length
|
331
|
+
end
|
332
|
+
|
333
|
+
def test_to_a01
|
334
|
+
s = "THX1138."
|
335
|
+
md1 = /(.)(.)(\d+)(\d)/.match(s)
|
336
|
+
ms1 = MatchSkeleton.new(md1, s)
|
337
|
+
assert_equal md1.to_a, ms1.to_a
|
338
|
+
assert_equal ["HX1138", "H", "X", "113", "8"], ms1.to_a
|
339
|
+
end
|
340
|
+
|
341
|
+
def test_to_s01
|
342
|
+
s = "THX1138."
|
343
|
+
md1 = /(.)(.)(\d+)(\d)/.match(s)
|
344
|
+
ms1 = MatchSkeleton.new(md1, s)
|
345
|
+
assert_equal md1.to_s, ms1.to_s
|
346
|
+
assert_equal "HX1138", ms1.to_s
|
347
|
+
assert_equal ms1.to_a[0], ms1.to_s
|
348
|
+
assert_equal ms1[0], ms1.to_s
|
349
|
+
end
|
350
|
+
|
351
|
+
end # class TestMatchSkeleton < Minitest::Test
|
352
|
+
|
353
|
+
|
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: match_skeleton
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Masa Sakano
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-05-19 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: MatchSkeleton is a class equivalent to MatchData with negligible memory
|
14
|
+
use. As long as the original string is not destructively modified, it behaves almost
|
15
|
+
icentical to MatchData.
|
16
|
+
email: info@wisebabel.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files:
|
20
|
+
- README.ja.rdoc
|
21
|
+
files:
|
22
|
+
- ".gitignore"
|
23
|
+
- ChangeLog
|
24
|
+
- News
|
25
|
+
- README.ja.rdoc
|
26
|
+
- Rakefile
|
27
|
+
- lib/match_skeleton.rb
|
28
|
+
- lib/match_skeleton/match_data.rb
|
29
|
+
- test/test_match_skeleton.rb
|
30
|
+
homepage: https://www.wisebabel.com/
|
31
|
+
licenses:
|
32
|
+
- MIT
|
33
|
+
metadata: {}
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options:
|
36
|
+
- "--charset=UTF-8"
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '2.0'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 2.5.1
|
52
|
+
signing_key:
|
53
|
+
specification_version: 4
|
54
|
+
summary: MatchSkeleton - class equivalent to MatchData with negligible memory use
|
55
|
+
test_files:
|
56
|
+
- test/test_match_skeleton.rb
|
57
|
+
has_rdoc:
|