joker 0.0.1 → 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.
- data/HISTORY.markdown +5 -0
- data/README.markdown +8 -3
- data/Rakefile +5 -18
- data/VERSION +1 -1
- data/ext/joker_native/Joker.c +72 -0
- data/ext/joker_native/Joker.h +53 -0
- data/ext/joker_native/Wildcard.c +22 -0
- data/ext/joker_native/Wildcard.h +40 -0
- data/ext/joker_native/compile.c +158 -0
- data/ext/joker_native/compile.h +16 -0
- data/ext/joker_native/extconf.rb +6 -0
- data/ext/joker_native/match.c +272 -0
- data/ext/joker_native/match.h +21 -0
- data/lib/joker.rb +6 -70
- data/tasks/c_extensions.rake +29 -0
- data/tasks/jeweler.rake +28 -0
- data/tasks/rdoc.rake +9 -0
- data/tasks/shortcuts.rake +22 -0
- data/tasks/test.rake +6 -0
- data/test/c/test_compile.c +215 -0
- data/test/c/test_match.c +131 -0
- data/test/{test_joker.rb → ruby/test_joker.rb} +0 -18
- metadata +19 -3
@@ -0,0 +1,21 @@
|
|
1
|
+
#ifndef MATCH_H_GUARD
|
2
|
+
#define MATCH_H_GUARD
|
3
|
+
|
4
|
+
#include "Wildcard.h"
|
5
|
+
#include "stdbool.h"
|
6
|
+
|
7
|
+
|
8
|
+
/*
|
9
|
+
* Matches a given Wildcard against a given string and returns
|
10
|
+
* true on success and false otherwise.
|
11
|
+
*
|
12
|
+
*/
|
13
|
+
bool Wildcard_match(
|
14
|
+
Wildcard * wildcard, // The wildcard to match
|
15
|
+
const char * cstring, // The string to match against
|
16
|
+
const long int len, // The length of the string
|
17
|
+
bool casefold); // Whether to ignore character case
|
18
|
+
|
19
|
+
|
20
|
+
#endif
|
21
|
+
|
data/lib/joker.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
# Joker is a simple Wildcard implementation that works much like Regexps.
|
3
3
|
#
|
4
4
|
|
5
|
+
require File.join( File.dirname( File.expand_path(__FILE__)), 'joker_native')
|
6
|
+
|
5
7
|
#
|
6
8
|
# Implements wildcards for Ruby. Modeled after the Regexp class.
|
7
9
|
#
|
@@ -54,6 +56,7 @@ class Wildcard
|
|
54
56
|
#
|
55
57
|
# Creates a new Wildcard from the given string.
|
56
58
|
# If casefold is true, the Wildcard will ignore case.
|
59
|
+
# TODO
|
57
60
|
#
|
58
61
|
def initialize( wildcard_string, casefold = false )
|
59
62
|
@source = wildcard_string
|
@@ -71,7 +74,7 @@ class Wildcard
|
|
71
74
|
# special meaning in a Wildcard.
|
72
75
|
#
|
73
76
|
def quote( string )
|
74
|
-
string.gsub(%r{[\\?*\[]}) { |char| "\\#{char}" }
|
77
|
+
string.gsub(%r{[\\?*\[\]]}) { |char| "\\#{char}" }
|
75
78
|
end
|
76
79
|
|
77
80
|
alias_method :[], :new
|
@@ -81,7 +84,7 @@ class Wildcard
|
|
81
84
|
end
|
82
85
|
|
83
86
|
def inspect
|
84
|
-
%{Wildcard[#{
|
87
|
+
%{Wildcard[#{self.source.inspect}]#{self.casefold ? 'i' : ''}}
|
85
88
|
end
|
86
89
|
|
87
90
|
#
|
@@ -94,33 +97,6 @@ class Wildcard
|
|
94
97
|
self =~ $_
|
95
98
|
end
|
96
99
|
|
97
|
-
#
|
98
|
-
# Matches the Wildcard against the given string.
|
99
|
-
#
|
100
|
-
# NOTE: Since a wildcard has to match the whole string,
|
101
|
-
# this method only returns true or false, not the position
|
102
|
-
# of the match.
|
103
|
-
#
|
104
|
-
# Wildcard['*fairy*'] =~ 'I love fairycake' #=> true
|
105
|
-
# 'I love fairycake' =~ Wildcard['*dairy*'] #=> false
|
106
|
-
#
|
107
|
-
def =~( string )
|
108
|
-
!!(@regexp =~ string)
|
109
|
-
end
|
110
|
-
|
111
|
-
#
|
112
|
-
# The case operator. Allows you to use Wildcards in case
|
113
|
-
# expressions:
|
114
|
-
#
|
115
|
-
# case 'I love fairycake'
|
116
|
-
# when Wildcard['*fairy*'] then puts 'fairy!'
|
117
|
-
# else puts 'no fairy...'
|
118
|
-
# end
|
119
|
-
#
|
120
|
-
def ===( object )
|
121
|
-
!!(@regexp =~ object)
|
122
|
-
end
|
123
|
-
|
124
100
|
#
|
125
101
|
# Compares to Wildcards for equality.
|
126
102
|
#
|
@@ -129,51 +105,11 @@ class Wildcard
|
|
129
105
|
#
|
130
106
|
def eql?( that )
|
131
107
|
return false unless that.is_a?(Wildcard)
|
132
|
-
|
108
|
+
self.source == that.source && self.casefold == that.casefold
|
133
109
|
end
|
134
110
|
|
135
111
|
alias_method :==, :eql?
|
136
112
|
alias_method :casefold?, :casefold
|
137
113
|
|
138
|
-
private
|
139
|
-
|
140
|
-
#
|
141
|
-
# Converts the wildcard string into a Regexp.
|
142
|
-
# A simple parser, I just threw it in there, no
|
143
|
-
# optimizations.
|
144
|
-
#
|
145
|
-
def compile
|
146
|
-
ptr = 0
|
147
|
-
compiled = '^'
|
148
|
-
while ptr < @source.length
|
149
|
-
snip = @source[ptr..-1]
|
150
|
-
if snip.scan(%r{^\\\\}).first
|
151
|
-
compiled << '\\\\'
|
152
|
-
ptr += 2
|
153
|
-
elsif snip.scan(%r{^\\\?}).first
|
154
|
-
compiled << '\\?'
|
155
|
-
ptr += 2
|
156
|
-
elsif snip.scan(%r{^\\\*}).first
|
157
|
-
compiled << '\\*'
|
158
|
-
ptr += 2
|
159
|
-
elsif snip.scan(%r{^\?}).first
|
160
|
-
compiled << '.'
|
161
|
-
ptr += 1
|
162
|
-
elsif snip.scan(%r{^\*}).first
|
163
|
-
compiled << '.*'
|
164
|
-
ptr += 1
|
165
|
-
elsif group = snip.scan(%r{^\[(?:\\\]|[^\]])+\]}).first
|
166
|
-
ptr += group.length
|
167
|
-
group = group[1..-2] # remove []
|
168
|
-
group = group.gsub(%r{\\\]}) { ']' }
|
169
|
-
compiled << '[' << Regexp.quote(group) << ']'
|
170
|
-
else
|
171
|
-
compiled << Regexp.quote(@source[ptr..ptr])
|
172
|
-
ptr += 1
|
173
|
-
end
|
174
|
-
end
|
175
|
-
compiled + '$'
|
176
|
-
end
|
177
|
-
|
178
114
|
end
|
179
115
|
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rake/extensiontask'
|
2
|
+
require 'rake/extensiontesttask'
|
3
|
+
|
4
|
+
extension_task =
|
5
|
+
Rake::ExtensionTask.new('joker_native', $gemspec) do |ext|
|
6
|
+
ext.cross_compile = true
|
7
|
+
ext.cross_platform = 'x86-mswin32'
|
8
|
+
ext.test_files = FileList['test/c/*']
|
9
|
+
end
|
10
|
+
|
11
|
+
CLEAN.include 'lib/**/*.so'
|
12
|
+
|
13
|
+
|
14
|
+
# workaround for rake-compiler which needs the gemspec to have a
|
15
|
+
# version and yaml-dump-loads the
|
16
|
+
# gemspec which leads to errors since procs can't be loaded
|
17
|
+
Rake::Task.tasks.each do |task_name|
|
18
|
+
case task_name.to_s
|
19
|
+
when /^native/
|
20
|
+
task_name.prerequisites.unshift("gemspec", "fix_rake_compiler_gemspec_dump")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
task :fix_rake_compiler_gemspec_dump do
|
25
|
+
%w{files extra_rdoc_files test_files}.each do |accessor|
|
26
|
+
$gemspec.send(accessor).instance_eval { @exclude_procs = Array.new }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
data/tasks/jeweler.rake
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
task :release do
|
3
|
+
sh "vim HISTORY.markdown"
|
4
|
+
sh "vim README.markdown"
|
5
|
+
sh "git commit -a -m 'prerelease adjustments'; true"
|
6
|
+
end
|
7
|
+
|
8
|
+
task :build => :gemspec
|
9
|
+
|
10
|
+
require 'jeweler'
|
11
|
+
jeweler_tasks = Jeweler::Tasks.new do |gem|
|
12
|
+
gem.name = 'joker'
|
13
|
+
gem.summary = 'Joker is a simple wildcard implementation that works much like Regexps'
|
14
|
+
gem.description = gem.summary
|
15
|
+
gem.email = 'karottenreibe@gmail.com'
|
16
|
+
gem.homepage = 'http://karottenreibe.github.com/joker'
|
17
|
+
gem.authors = ['Fabian Streitel']
|
18
|
+
gem.rubyforge_project = 'k-gems'
|
19
|
+
gem.extensions = FileList['ext/**/extconf.rb']
|
20
|
+
|
21
|
+
gem.files.include('lib/joker_native.*') # add native stuff
|
22
|
+
end
|
23
|
+
|
24
|
+
$gemspec = jeweler_tasks.gemspec
|
25
|
+
|
26
|
+
Jeweler::RubyforgeTasks.new
|
27
|
+
Jeweler::GemcutterTasks.new
|
28
|
+
|
data/tasks/rdoc.rake
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
desc("Build linux and windows specific gems")
|
3
|
+
task :gems do
|
4
|
+
sh "rake clean build:native"
|
5
|
+
sh "rake clean build:cross"
|
6
|
+
sh "rake clean build"
|
7
|
+
end
|
8
|
+
|
9
|
+
task "build:native" => [:no_extconf, :native, :build] do
|
10
|
+
file = "pkg/joker-#{`cat VERSION`.chomp}.gem"
|
11
|
+
mv file, "#{file.ext}-i686-linux.gem"
|
12
|
+
end
|
13
|
+
|
14
|
+
task "build:cross" => [:no_extconf, :cross, :native, :build] do
|
15
|
+
file = "pkg/joker-#{`cat VERSION`.chomp}.gem"
|
16
|
+
mv file, "#{file.ext}-x86-mingw32.gem"
|
17
|
+
end
|
18
|
+
|
19
|
+
task :no_extconf do
|
20
|
+
$gemspec.extensions = []
|
21
|
+
end
|
22
|
+
|
data/tasks/test.rake
ADDED
@@ -0,0 +1,215 @@
|
|
1
|
+
#include <stdarg.h>
|
2
|
+
#include <stddef.h>
|
3
|
+
#include <stdbool.h>
|
4
|
+
#include <setjmp.h>
|
5
|
+
#include <cmockery.h>
|
6
|
+
#include <stdio.h>
|
7
|
+
#include <string.h>
|
8
|
+
#include <ruby.h>
|
9
|
+
#include "compile.h"
|
10
|
+
#include "Wildcard.h"
|
11
|
+
|
12
|
+
//
|
13
|
+
// Possible scenarios:
|
14
|
+
// * string empty
|
15
|
+
// string not empty
|
16
|
+
// * contains group
|
17
|
+
// contains fixed
|
18
|
+
// contains wildcard
|
19
|
+
// contains kleene
|
20
|
+
// * contains "**"
|
21
|
+
// * contains escape at EOS
|
22
|
+
// contains escape at Fixed
|
23
|
+
// contains escape at Group
|
24
|
+
//
|
25
|
+
|
26
|
+
|
27
|
+
static void generic_test(cstring, length, expected) // {{{1
|
28
|
+
const char * cstring;
|
29
|
+
const int length;
|
30
|
+
const char * expected;
|
31
|
+
{
|
32
|
+
Wildcard * wildcard;
|
33
|
+
int i;
|
34
|
+
|
35
|
+
wildcard = Wildcard_compile(cstring, strlen(cstring));
|
36
|
+
assert_int_not_equal( (int)NULL, (int)wildcard );
|
37
|
+
assert_int_not_equal( (int)NULL, (int)wildcard->first );
|
38
|
+
assert_int_not_equal( (int)NULL, (int)wildcard->last );
|
39
|
+
assert_int_equal( length, wildcard->length );
|
40
|
+
|
41
|
+
for (i = 0; i < length; i += 2) {
|
42
|
+
assert_int_equal((int)*(expected + i), (int)*(wildcard->first + i));
|
43
|
+
}
|
44
|
+
|
45
|
+
Wildcard_free(wildcard);
|
46
|
+
}
|
47
|
+
|
48
|
+
|
49
|
+
static void test_empty(state) // {{{1
|
50
|
+
void ** state;
|
51
|
+
{
|
52
|
+
Wildcard * wildcard;
|
53
|
+
int i;
|
54
|
+
|
55
|
+
wildcard = Wildcard_compile("", 0);
|
56
|
+
assert_int_not_equal( (int)NULL, (int)wildcard );
|
57
|
+
assert_int_equal( (int)NULL, (int)wildcard->first );
|
58
|
+
assert_int_equal( (int)NULL, (int)wildcard->last );
|
59
|
+
assert_int_equal( 0, wildcard->length );
|
60
|
+
|
61
|
+
Wildcard_free(wildcard);
|
62
|
+
}
|
63
|
+
|
64
|
+
|
65
|
+
static void test_fixed(state) // {{{1
|
66
|
+
void ** state;
|
67
|
+
{
|
68
|
+
const char expected[8] = {
|
69
|
+
Fixed, 'u',
|
70
|
+
Fixed, 'i',
|
71
|
+
Fixed, 'a',
|
72
|
+
Fixed, 'e',
|
73
|
+
};
|
74
|
+
|
75
|
+
generic_test("uiae", 8, expected);
|
76
|
+
}
|
77
|
+
|
78
|
+
|
79
|
+
static void test_group(state) // {{{1
|
80
|
+
void ** state;
|
81
|
+
{
|
82
|
+
const char expected[4] = {
|
83
|
+
Group, 'i',
|
84
|
+
Group, 'a',
|
85
|
+
};
|
86
|
+
|
87
|
+
generic_test("[ia]", 4, expected);
|
88
|
+
}
|
89
|
+
|
90
|
+
|
91
|
+
static void test_wild(state) // {{{1
|
92
|
+
void ** state;
|
93
|
+
{
|
94
|
+
const char expected[2] = {
|
95
|
+
Wild, '?',
|
96
|
+
};
|
97
|
+
|
98
|
+
generic_test("?", 2, expected);
|
99
|
+
}
|
100
|
+
|
101
|
+
|
102
|
+
static void test_kleene(state) // {{{1
|
103
|
+
void ** state;
|
104
|
+
{
|
105
|
+
const char expected[2] = {
|
106
|
+
Kleene, '*',
|
107
|
+
};
|
108
|
+
|
109
|
+
generic_test("*", 2, expected);
|
110
|
+
generic_test("**", 2, expected);
|
111
|
+
generic_test("****", 2, expected);
|
112
|
+
}
|
113
|
+
|
114
|
+
|
115
|
+
static void test_mixed(state) // {{{1
|
116
|
+
void ** state;
|
117
|
+
{
|
118
|
+
const char expected[10] = {
|
119
|
+
Fixed, 'u',
|
120
|
+
Wild, '?',
|
121
|
+
Kleene, '*',
|
122
|
+
Group, 'e',
|
123
|
+
Group, '?',
|
124
|
+
};
|
125
|
+
|
126
|
+
generic_test("u?*[e?]", 10, expected);
|
127
|
+
}
|
128
|
+
|
129
|
+
|
130
|
+
static void test_escaping(state) // {{{1
|
131
|
+
void ** state;
|
132
|
+
{
|
133
|
+
ruby_init(); // since we will be calling rb_warning
|
134
|
+
|
135
|
+
// escaping in fixed parts
|
136
|
+
{
|
137
|
+
char expected[4] = {
|
138
|
+
Fixed, '?',
|
139
|
+
Fixed, 'a',
|
140
|
+
};
|
141
|
+
|
142
|
+
// escaping of escapable characters
|
143
|
+
generic_test("\\?", 2, expected);
|
144
|
+
|
145
|
+
expected[1] = '*';
|
146
|
+
generic_test("\\*", 2, expected);
|
147
|
+
|
148
|
+
expected[1] = '[';
|
149
|
+
generic_test("\\[", 2, expected);
|
150
|
+
|
151
|
+
expected[1] = ']';
|
152
|
+
generic_test("\\]", 2, expected);
|
153
|
+
|
154
|
+
expected[1] = '\\';
|
155
|
+
generic_test("\\\\", 2, expected);
|
156
|
+
|
157
|
+
// special warning-generating escaping mechanism
|
158
|
+
expected[1] = ']';
|
159
|
+
generic_test("]", 2, expected);
|
160
|
+
|
161
|
+
// escaping of non-escapable characters
|
162
|
+
expected[1] = '\\';
|
163
|
+
generic_test("\\a", 4, expected);
|
164
|
+
}
|
165
|
+
// escaping in group parts
|
166
|
+
{
|
167
|
+
char expected[4] = {
|
168
|
+
Group, '[',
|
169
|
+
Group, 'a',
|
170
|
+
};
|
171
|
+
|
172
|
+
// escaping of escapable characters
|
173
|
+
generic_test("[\\[]", 2, expected);
|
174
|
+
|
175
|
+
expected[1] = ']';
|
176
|
+
generic_test("[\\]]", 2, expected);
|
177
|
+
|
178
|
+
expected[1] = '\\';
|
179
|
+
generic_test("[\\\\]", 2, expected);
|
180
|
+
|
181
|
+
// special warning-generating escaping mechanism
|
182
|
+
expected[1] = '[';
|
183
|
+
generic_test("[[]", 2, expected);
|
184
|
+
|
185
|
+
// escaping of non-escapable characters
|
186
|
+
expected[1] = '\\';
|
187
|
+
generic_test("[\\a]", 4, expected);
|
188
|
+
|
189
|
+
expected[3] = '?';
|
190
|
+
generic_test("[\\?]", 4, expected);
|
191
|
+
}
|
192
|
+
// escaping at EOS
|
193
|
+
{
|
194
|
+
char expected[4] = {
|
195
|
+
Fixed, '\\',
|
196
|
+
};
|
197
|
+
|
198
|
+
generic_test("\\", 2, expected);
|
199
|
+
}
|
200
|
+
}
|
201
|
+
|
202
|
+
|
203
|
+
int main() { // {{{1
|
204
|
+
const UnitTest tests[] = {
|
205
|
+
unit_test(test_empty),
|
206
|
+
unit_test(test_fixed),
|
207
|
+
unit_test(test_group),
|
208
|
+
unit_test(test_wild),
|
209
|
+
unit_test(test_kleene),
|
210
|
+
unit_test(test_mixed),
|
211
|
+
unit_test(test_escaping),
|
212
|
+
};
|
213
|
+
return run_tests(tests);
|
214
|
+
}
|
215
|
+
|