Skip to content
This repository
Newer
Older
100644 362 lines (311 sloc) 8.225 kb
19f18d3b » Laurent Sansonetti
2010-02-25 a new Symbol class, unicode-aware + refactored/cleaned symbol generation
1 /*
2 * MacRuby Symbols.
3 *
4 * This file is covered by the Ruby license. See COPYING for more details.
5 *
6 * Copyright (C) 2010, Apple Inc. All rights reserved.
7 */
8
9 #include <wctype.h>
10
11 #include "ruby.h"
12 #include "ruby/encoding.h"
13 #include "encoding.h"
14 #include "symbol.h"
15 #include "ruby/node.h"
16 #include "vm.h"
17
18 VALUE rb_cSymbol;
19
20 static CFMutableDictionaryRef sym_id = NULL, id_str = NULL;
21 static long last_id = 0;
22
23 typedef struct {
24 VALUE klass;
25 rb_str_t *str;
26 ID id;
27 SEL sel;
28 } rb_sym_t;
29
30 #define RSYM(obj) ((rb_sym_t *)(obj))
31
32 static rb_sym_t *
33 sym_alloc(rb_str_t *str, ID id)
34 {
35 rb_sym_t *sym = (rb_sym_t *)malloc(sizeof(rb_sym_t));
36 assert(rb_cSymbol != 0);
37 sym->klass = rb_cSymbol;
38 GC_RETAIN(str); // never released
39 sym->str = str;
40 sym->id = id;
41 sym->sel = NULL; // lazy
42 return sym;
43 }
44
45 ID
46 rb_intern_str(VALUE str)
47 {
48 UChar *chars = NULL;
49 long chars_len = 0;
50 bool need_free = false;
51 rb_str_get_uchars(str, &chars, &chars_len, &need_free);
52 assert(chars_len > 0);
53
54 const unsigned long name_hash = rb_str_hash_uchars(chars, chars_len);
55 ID id = (ID)CFDictionaryGetValue(sym_id, (const void *)name_hash);
56 if (id != 0) {
57 goto return_id;
58 }
59
60 rb_sym_t *sym = NULL;
61
62 switch (chars[0]) {
63 case '$':
64 id = ID_GLOBAL;
65 break;
66
67 case '@':
68 if (chars_len > 1 && chars[1] == '@') {
69 id = ID_CLASS;
70 }
71 else {
72 id = ID_INSTANCE;
73 }
74 break;
75
76 default:
77 if (chars_len > 1 && chars[chars_len - 1] == '=') {
78 // Attribute assignment.
79 id = rb_intern_str(rb_str_substr(str, 0, chars_len - 1));
80 if (!is_attrset_id(id)) {
81 id = rb_id_attrset(id);
82 goto id_register;
83 }
84 id = ID_ATTRSET;
85 }
86 else if (iswupper(chars[0])) {
87 id = ID_CONST;
88 }
89 else {
90 id = ID_LOCAL;
91 }
92 break;
93 }
94
95 id |= ++last_id << ID_SCOPE_SHIFT;
96
97 id_register:
98 //printf("register %s hash %ld id %ld\n", RSTRING_PTR(str), name_hash, id);
99 sym = sym_alloc(RSTR(str), id);
100 CFDictionarySetValue(sym_id, (const void *)name_hash, (const void *)id);
101 CFDictionarySetValue(id_str, (const void *)id, (const void *)sym);
102
103 return_id:
104 if (need_free && chars != NULL) {
105 free(chars);
106 }
107 return id;
108 }
109
110 VALUE
111 rb_id2str(ID id)
112 {
113 VALUE sym = (VALUE)CFDictionaryGetValue(id_str, (const void *)id);
114 if (sym != 0) {
115 //printf("lookup %ld -> %s\n", id, rb_sym2name(sym));
116 return sym;
117 }
118
119 if (is_attrset_id(id)) {
120 // Attribute assignment.
121 ID id2 = (id & ~ID_SCOPE_MASK) | ID_LOCAL;
122
123 while ((sym = rb_id2str(id2)) == 0) {
124 if (!is_local_id(id2)) {
125 //printf("lookup %ld -> FAIL\n", id);
126 return 0;
127 }
128 id2 = (id & ~ID_SCOPE_MASK) | ID_CONST;
129 }
130
131 VALUE str = rb_str_dup((VALUE)RSYM(sym)->str);
132 rb_str_cat(str, "=", 1);
133 rb_intern_str(str);
134
135 // Retry one more time.
136 sym = (VALUE)CFDictionaryGetValue(id_str, (const void *)id);
137 if (sym != 0) {
138 //printf("lookup %ld -> %s\n", id, rb_sym2name(sym));
139 return sym;
140 }
141 }
142 //printf("lookup %ld -> FAIL\n", id);
143 return 0;
144 }
145
146 ID
147 rb_intern3(const char *name, long len, rb_encoding *enc)
148 {
149 VALUE str = rb_enc_str_new(name, len, enc);
150 return rb_intern_str(str);
151 }
152
153 ID
154 rb_intern2(const char *name, long len)
155 {
156 return rb_intern_str(rb_str_new(name, len));
157 }
158
159 ID
160 rb_intern(const char *name)
161 {
162 return rb_intern_str(rb_str_new2(name));
163 }
164
165 ID
166 rb_sym2id(VALUE sym)
167 {
168 return RSYM(sym)->id;
169 }
170
171 VALUE
172 rb_name2sym(const char *name)
173 {
174 return rb_id2str(rb_intern(name));
175 }
176
177 VALUE
178 rb_sym2str(VALUE sym)
179 {
180 return (VALUE)RSYM(sym)->str;
181 }
182
183 VALUE
184 rb_sym_to_s(VALUE sym)
185 {
186 return rb_str_dup(rb_sym2str(sym));
187 }
188
189 const char *
190 rb_sym2name(VALUE sym)
191 {
192 return RSTRING_PTR(RSYM(sym)->str);
193 }
194
195 /*
196 * call-seq:
197 * Symbol.all_symbols => array
198 *
199 * Returns an array of all the symbols currently in Ruby's symbol
200 * table.
201 *
202 * Symbol.all_symbols.size #=> 903
203 * Symbol.all_symbols[1,20] #=> [:floor, :ARGV, :Binding, :symlink,
204 * :chown, :EOFError, :$;, :String,
205 * :LOCK_SH, :"setuid?", :$<,
206 * :default_proc, :compact, :extend,
207 * :Tms, :getwd, :$=, :ThreadGroup,
208 * :wait2, :$>]
209 */
210
211 static VALUE
212 rsym_all_symbols(VALUE klass, SEL sel)
213 {
214 VALUE ary = rb_ary_new();
215 const long count = CFDictionaryGetCount(id_str);
216 if (count >= 0) {
217 const void **values = (const void **)malloc(sizeof(void *) * count);
218 CFDictionaryGetKeysAndValues(id_str, NULL, values);
219 for (long i = 0; i < count; i++) {
220 rb_ary_push(ary, (VALUE)values[i]);
221 }
222 free(values);
223 }
224 return ary;
225 }
226
227 void
228 Init_PreSymbol(void)
229 {
230 sym_id = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
231 id_str = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
232 last_id = 1000;
233
234 // Pre-register parser symbols.
235 for (int i = 0; rb_op_tbl[i].token != 0; i++) {
236 VALUE str = rb_str_new2(rb_op_tbl[i].name);
237
238 UChar *chars = NULL;
239 long chars_len = 0;
240 bool need_free = false;
241 rb_str_get_uchars(str, &chars, &chars_len, &need_free);
242 assert(chars_len > 0);
243
244 ID id = rb_op_tbl[i].token;
245 rb_sym_t *sym = sym_alloc(RSTR(str), id);
246 unsigned long name_hash = rb_str_hash_uchars(chars, chars_len);
247
248 //printf("pre-register %s hash %ld id %ld\n", RSTRING_PTR(str), name_hash, id);
249
250 CFDictionarySetValue(sym_id, (const void *)name_hash, (const void *)id);
251 CFDictionarySetValue(id_str, (const void *)id, (const void *)sym);
252
253 if (need_free && chars != NULL) {
254 free(chars);
255 }
256 }
257 }
258
259 /*
260 * call-seq:
261 * sym == obj => true or false
262 *
263 * Equality---If <i>sym</i> and <i>obj</i> are exactly the same
264 * symbol, returns <code>true</code>. Otherwise, compares them
265 * as strings.
266 */
267
268 static VALUE
269 rsym_equal(VALUE sym, SEL sel, VALUE other)
270 {
271 return sym == other ? Qtrue : Qfalse;
272 }
273
274 /*
275 * call-seq:
276 * sym.inspect => string
277 *
278 * Returns the representation of <i>sym</i> as a symbol literal.
279 *
280 * :fred.inspect #=> ":fred"
281 */
282
283 static VALUE
284 rsym_inspect(VALUE sym, SEL sel)
285 {
286 VALUE str = rb_str_new2(":");
287 rb_str_concat(str, str_inspect(RSYM(sym)->str, true));
288 return str;
289 }
290
291 /*
292 * call-seq:
293 * sym.to_proc
294 *
295 * Returns a _Proc_ object which respond to the given method by _sym_.
296 *
297 * (1..3).collect(&:to_s) #=> ["1", "2", "3"]
298 */
299
300 static VALUE
301 rsym_to_proc(VALUE sym, SEL sel)
302 {
303 SEL msel = sel_registerName(rb_id2name(SYM2ID(sym)));
304 rb_vm_block_t *b = rb_vm_create_block_calling_sel(msel);
305 return rb_proc_alloc_with_block(rb_cProc, b);
306 }
307
308 /*
309 * call-seq:
310 * sym.id2name => string
311 * sym.to_s => string
312 *
313 * Returns the name or string corresponding to <i>sym</i>.
314 *
315 * :fred.id2name #=> "fred"
316 */
317
318 static VALUE
319 rsym_to_s(VALUE sym, SEL sel)
320 {
321 return rb_sym_to_s(sym);
322 }
323
324 /*
325 * call-seq:
326 * sym.to_sym => sym
327 * sym.intern => sym
328 *
329 * In general, <code>to_sym</code> returns the <code>Symbol</code>
330 * corresponding to an object. As <i>sym</i> is already a symbol,
331 * <code>self</code> is returned in this case.
332 */
333
334 static VALUE
335 rsym_to_sym(VALUE sym, SEL sel)
336 {
337 return sym;
338 }
339
340 void
341 Init_Symbol(void)
342 {
343 // rb_cSymbol is defined earlier in Init_PreVM().
344 rb_set_class_path(rb_cSymbol, rb_cObject, "Symbol");
345 rb_const_set(rb_cObject, rb_intern("Symbol"), rb_cSymbol);
346
347 rb_undef_alloc_func(rb_cSymbol);
348 rb_undef_method(*(VALUE *)rb_cSymbol, "new");
349 rb_objc_define_method(*(VALUE *)rb_cSymbol, "all_symbols",
350 rsym_all_symbols, 0);
351
352 rb_objc_define_method(rb_cSymbol, "==", rsym_equal, 1);
353 rb_objc_define_method(rb_cSymbol, "eql?", rsym_equal, 1);
354 //rb_objc_define_method(rb_cSymbol, "<=>", rsym_cmp, 1);
355 rb_objc_define_method(rb_cSymbol, "inspect", rsym_inspect, 0);
356 rb_objc_define_method(rb_cSymbol, "to_proc", rsym_to_proc, 0);
357 rb_objc_define_method(rb_cSymbol, "to_s", rsym_to_s, 0);
358 rb_objc_define_method(rb_cSymbol, "id2name", rsym_to_s, 0);
359 rb_objc_define_method(rb_cSymbol, "description", rsym_to_s, 0);
360 rb_objc_define_method(rb_cSymbol, "intern", rsym_to_sym, 0);
361 rb_objc_define_method(rb_cSymbol, "to_sym", rsym_to_sym, 0);
362 }
Something went wrong with that request. Please try again.