tyler-trie 0.1.3 → 0.2.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/VERSION.yml +2 -2
- data/ext/trie/trie.c +111 -2
- data/spec/trie_spec.rb +115 -0
- metadata +2 -2
data/VERSION.yml
CHANGED
data/ext/trie/trie.c
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
#include <stdio.h>
|
|
5
5
|
#include <string.h>
|
|
6
6
|
|
|
7
|
+
VALUE cTrie, cTrieNode;
|
|
8
|
+
|
|
7
9
|
static TrieChar* stringToTrieChar(VALUE string) {
|
|
8
10
|
return (TrieChar*) RSTRING(string)->ptr;
|
|
9
11
|
}
|
|
@@ -206,15 +208,111 @@ static VALUE trie_walk_to_terminal(VALUE self, VALUE args) {
|
|
|
206
208
|
iterator++;
|
|
207
209
|
}
|
|
208
210
|
|
|
211
|
+
sb_trie_state_free(state);
|
|
212
|
+
|
|
209
213
|
return Qnil;
|
|
210
214
|
}
|
|
211
215
|
|
|
212
216
|
static VALUE trie_get_path(VALUE self) {
|
|
213
217
|
return rb_iv_get(self, "@path");
|
|
214
218
|
}
|
|
215
|
-
|
|
216
|
-
VALUE cTrie;
|
|
217
219
|
|
|
220
|
+
static void trie_node_free(SBTrieState *state) {
|
|
221
|
+
if(state)
|
|
222
|
+
sb_trie_state_free(state);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
static VALUE trie_node_alloc(VALUE klass) {
|
|
226
|
+
SBTrieState *state;
|
|
227
|
+
VALUE obj;
|
|
228
|
+
|
|
229
|
+
obj = Data_Wrap_Struct(klass, 0, trie_node_free, state);
|
|
230
|
+
|
|
231
|
+
return obj;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
static VALUE trie_root(VALUE self) {
|
|
235
|
+
SBTrie *sb_trie;
|
|
236
|
+
Data_Get_Struct(self, SBTrie, sb_trie);
|
|
237
|
+
|
|
238
|
+
VALUE trie_node = trie_node_alloc(cTrieNode);
|
|
239
|
+
|
|
240
|
+
// replace the pretend SBTrieState created in TrieNode's alloc with a real one
|
|
241
|
+
RDATA(trie_node)->data = sb_trie_root(sb_trie);
|
|
242
|
+
|
|
243
|
+
rb_iv_set(trie_node, "@state", Qnil);
|
|
244
|
+
rb_iv_set(trie_node, "@full_state", rb_str_new2(""));
|
|
245
|
+
return trie_node;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
static VALUE trie_node_get_state(VALUE self) {
|
|
249
|
+
return rb_iv_get(self, "@state");
|
|
250
|
+
}
|
|
251
|
+
static VALUE trie_node_get_full_state(VALUE self) {
|
|
252
|
+
return rb_iv_get(self, "@full_state");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
static VALUE trie_node_walk_bang(VALUE self, VALUE rchar) {
|
|
256
|
+
SBTrieState *state;
|
|
257
|
+
Data_Get_Struct(self, SBTrieState, state);
|
|
258
|
+
|
|
259
|
+
if(RSTRING(rchar)->len != 1)
|
|
260
|
+
return Qnil;
|
|
261
|
+
|
|
262
|
+
char ch = RSTRING(rchar)->ptr[0];
|
|
263
|
+
int result = sb_trie_state_walk(state, (TrieChar)ch);
|
|
264
|
+
|
|
265
|
+
if(result) {
|
|
266
|
+
rb_iv_set(self, "@state", rchar);
|
|
267
|
+
VALUE full_state = rb_iv_get(self, "@full_state");
|
|
268
|
+
rb_str_append(full_state, rchar);
|
|
269
|
+
rb_iv_set(self, "@full_state", full_state);
|
|
270
|
+
return self;
|
|
271
|
+
} else
|
|
272
|
+
return Qnil;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
static VALUE trie_node_value(VALUE self) {
|
|
276
|
+
SBTrieState *state, *dup;
|
|
277
|
+
Data_Get_Struct(self, SBTrieState, state);
|
|
278
|
+
|
|
279
|
+
dup = sb_trie_state_clone(state);
|
|
280
|
+
|
|
281
|
+
sb_trie_state_walk(dup, (TrieChar)'\0');
|
|
282
|
+
TrieData trie_data = sb_trie_state_get_data(dup);
|
|
283
|
+
sb_trie_state_free(dup);
|
|
284
|
+
|
|
285
|
+
return TRIE_DATA_ERROR == trie_data ? Qnil : INT2FIX(trie_data);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
static VALUE trie_node_terminal(VALUE self) {
|
|
289
|
+
SBTrieState *state;
|
|
290
|
+
Data_Get_Struct(self, SBTrieState, state);
|
|
291
|
+
|
|
292
|
+
return sb_trie_state_is_terminal(state) ? Qtrue : Qnil;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
static VALUE trie_node_leaf(VALUE self) {
|
|
296
|
+
SBTrieState *state;
|
|
297
|
+
Data_Get_Struct(self, SBTrieState, state);
|
|
298
|
+
|
|
299
|
+
return sb_trie_state_is_leaf(state) ? Qtrue : Qnil;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
static VALUE trie_node_clone(VALUE self) {
|
|
303
|
+
SBTrieState *state;
|
|
304
|
+
Data_Get_Struct(self, SBTrieState, state);
|
|
305
|
+
|
|
306
|
+
VALUE new_node = trie_node_alloc(cTrieNode);
|
|
307
|
+
RDATA(new_node)->data = sb_trie_state_clone(state);
|
|
308
|
+
|
|
309
|
+
rb_iv_set(new_node, "@state", rb_iv_get(self, "@state"));
|
|
310
|
+
rb_iv_set(new_node, "@full_state", rb_iv_get(self, "@full_state"));
|
|
311
|
+
|
|
312
|
+
return new_node;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
|
|
218
316
|
void Init_trie() {
|
|
219
317
|
cTrie = rb_define_class("Trie", rb_cObject);
|
|
220
318
|
rb_define_alloc_func(cTrie, trie_alloc);
|
|
@@ -227,4 +325,15 @@ void Init_trie() {
|
|
|
227
325
|
rb_define_method(cTrie, "close", trie_close, 0);
|
|
228
326
|
rb_define_method(cTrie, "children", trie_children, 1);
|
|
229
327
|
rb_define_method(cTrie, "walk_to_terminal", trie_walk_to_terminal, -2);
|
|
328
|
+
rb_define_method(cTrie, "root", trie_root, 0);
|
|
329
|
+
|
|
330
|
+
cTrieNode = rb_define_class("TrieNode", rb_cObject);
|
|
331
|
+
rb_define_alloc_func(cTrieNode, trie_node_alloc);
|
|
332
|
+
rb_define_method(cTrieNode, "state", trie_node_get_state, 0);
|
|
333
|
+
rb_define_method(cTrieNode, "full_state", trie_node_get_full_state, 0);
|
|
334
|
+
rb_define_method(cTrieNode, "walk!", trie_node_walk_bang, 1);
|
|
335
|
+
rb_define_method(cTrieNode, "value", trie_node_value, 0);
|
|
336
|
+
rb_define_method(cTrieNode, "terminal?", trie_node_terminal, 0);
|
|
337
|
+
rb_define_method(cTrieNode, "leaf?", trie_node_leaf, 0);
|
|
338
|
+
rb_define_method(cTrieNode, "clone", trie_node_clone, 0);
|
|
230
339
|
}
|
data/spec/trie_spec.rb
CHANGED
|
@@ -99,4 +99,119 @@ describe Trie do
|
|
|
99
99
|
@trie.walk_to_terminal('anderson',true).should == ['and', 15]
|
|
100
100
|
end
|
|
101
101
|
end
|
|
102
|
+
|
|
103
|
+
describe :root do
|
|
104
|
+
it 'returns a TrieNode' do
|
|
105
|
+
@trie.root.should be_an_instance_of(TrieNode)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it 'returns a different TrieNode each time' do
|
|
109
|
+
@trie.root.should_not == @trie.root
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
describe TrieNode do
|
|
115
|
+
before :each do
|
|
116
|
+
@trie = Trie.new(TRIE_PATH);
|
|
117
|
+
@trie.add('rocket',1)
|
|
118
|
+
@trie.add('rock',2)
|
|
119
|
+
@trie.add('frederico',3)
|
|
120
|
+
@node = @trie.root
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
after :each do
|
|
124
|
+
@trie.close
|
|
125
|
+
File.delete('spec/test-trie/trie.br')
|
|
126
|
+
File.delete('spec/test-trie/trie.tl')
|
|
127
|
+
File.delete('spec/test-trie/trie.sbm')
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
describe :state do
|
|
131
|
+
it 'returns the most recent state character' do
|
|
132
|
+
@node.walk!('r')
|
|
133
|
+
@node.state.should == 'r'
|
|
134
|
+
@node.walk!('o')
|
|
135
|
+
@node.state.should == 'o'
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'is nil when no walk has occurred' do
|
|
139
|
+
@node.state.should == nil
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
describe :full_state do
|
|
144
|
+
it 'returns the current string' do
|
|
145
|
+
@node.walk!('r').walk!('o').walk!('c')
|
|
146
|
+
@node.full_state.should == 'roc'
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it 'is a blank string when no walk has occurred' do
|
|
150
|
+
@node.full_state.should == ''
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
describe :walk! do
|
|
155
|
+
it 'returns the updated object when the walk succeeds' do
|
|
156
|
+
other = @node.walk!('r')
|
|
157
|
+
other.should == @node
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it 'returns nil when the walk fails' do
|
|
161
|
+
@node.walk!('q').should be_nil
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
describe :value do
|
|
166
|
+
it 'returns nil when the node is not terminal' do
|
|
167
|
+
@node.walk!('r')
|
|
168
|
+
@node.value.should be_nil
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it 'returns a value when the node is terminal' do
|
|
172
|
+
@node.walk!('r').walk!('o').walk!('c').walk!('k')
|
|
173
|
+
@node.value.should == 2
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
describe :terminal? do
|
|
178
|
+
it 'returns true when the node is a word end' do
|
|
179
|
+
@node.walk!('r').walk!('o').walk!('c').walk!('k')
|
|
180
|
+
@node.should be_terminal
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
it 'returns nil when the node is not a word end' do
|
|
184
|
+
@node.walk!('r').walk!('o').walk!('c')
|
|
185
|
+
@node.should_not be_terminal
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
describe :leaf? do
|
|
190
|
+
it 'returns true when this is the end of a branch of the trie' do
|
|
191
|
+
@node.walk!('r').walk!('o').walk!('c').walk!('k').walk!('e').walk!('t')
|
|
192
|
+
@node.should be_leaf
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
it 'returns nil when there are more splits on this branch' do
|
|
196
|
+
@node.walk!('r').walk!('o').walk!('c').walk!('k')
|
|
197
|
+
@node.should_not be_leaf
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
describe :clone do
|
|
202
|
+
it 'creates a new instance of this node which is not this node' do
|
|
203
|
+
new_node = @node.clone
|
|
204
|
+
new_node.should_not == @node
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
it 'matches the state of the current node' do
|
|
208
|
+
new_node = @node.clone
|
|
209
|
+
new_node.state.should == @node.state
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
it 'matches the full_state of the current node' do
|
|
213
|
+
new_node = @node.clone
|
|
214
|
+
new_node.full_state.should == @node.full_state
|
|
215
|
+
end
|
|
216
|
+
end
|
|
102
217
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tyler-trie
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tyler McMullen
|
|
@@ -9,7 +9,7 @@ autorequire:
|
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
11
|
|
|
12
|
-
date: 2009-03-
|
|
12
|
+
date: 2009-03-10 00:00:00 -07:00
|
|
13
13
|
default_executable:
|
|
14
14
|
dependencies: []
|
|
15
15
|
|