mbed I/F binding for mruby

Dependents:   mruby_mbed_web mirb_mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers symbol.c Source File

symbol.c

00001 /*
00002 ** symbol.c - Symbol class
00003 **
00004 ** See Copyright Notice in mruby.h
00005 */
00006 
00007 #include <ctype.h>
00008 #include <limits.h>
00009 #include <string.h>
00010 #include "mruby.h"
00011 #include "mruby/khash.h"
00012 #include "mruby/string.h"
00013 #include "mruby/dump.h"
00014 
00015 /* ------------------------------------------------------ */
00016 typedef struct symbol_name {
00017   mrb_bool lit : 1;
00018   uint16_t len;
00019   const char *name;
00020 } symbol_name;
00021 
00022 static inline khint_t
00023 sym_hash_func(mrb_state *mrb, mrb_sym s)
00024 {
00025   khint_t h = 0;
00026   size_t i, len = mrb->symtbl[s].len;
00027   const char *p = mrb->symtbl[s].name;
00028 
00029   for (i=0; i<len; i++) {
00030     h = (h << 5) - h + *p++;
00031   }
00032   return h;
00033 }
00034 #define sym_hash_equal(mrb,a, b) (mrb->symtbl[a].len == mrb->symtbl[b].len && memcmp(mrb->symtbl[a].name, mrb->symtbl[b].name, mrb->symtbl[a].len) == 0)
00035 
00036 KHASH_DECLARE(n2s, mrb_sym, mrb_sym, FALSE)
00037 KHASH_DEFINE (n2s, mrb_sym, mrb_sym, FALSE, sym_hash_func, sym_hash_equal)
00038 /* ------------------------------------------------------ */
00039 
00040 static void
00041 sym_validate_len(mrb_state *mrb, size_t len)
00042 {
00043   if (len >= RITE_LV_NULL_MARK) {
00044     mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long");
00045   }
00046 }
00047 
00048 static mrb_sym
00049 sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit)
00050 {
00051   khash_t(n2s) *h = mrb->name2sym;
00052   symbol_name *sname = mrb->symtbl; /* symtbl[0] for working memory */
00053   khiter_t k;
00054   mrb_sym sym;
00055   char *p;
00056 
00057   sym_validate_len(mrb, len);
00058   if (sname) {
00059     sname->lit = lit;
00060     sname->len = (uint16_t)len;
00061     sname->name = name;
00062     k = kh_get(n2s, mrb, h, 0);
00063     if (k != kh_end(h))
00064       return kh_key(h, k);
00065   }
00066 
00067   /* registering a new symbol */
00068   sym = ++mrb->symidx;
00069   if (mrb->symcapa < sym) {
00070     if (mrb->symcapa == 0) mrb->symcapa = 100;
00071     else mrb->symcapa = (size_t)(mrb->symcapa * 1.2);
00072     mrb->symtbl = (symbol_name*)mrb_realloc(mrb, mrb->symtbl, sizeof(symbol_name)*(mrb->symcapa+1));
00073   }
00074   sname = &mrb->symtbl[sym];
00075   sname->len = (uint16_t)len;
00076   if (lit || mrb_ro_data_p(name)) {
00077     sname->name = name;
00078     sname->lit = TRUE;
00079   }
00080   else {
00081     p = (char *)mrb_malloc(mrb, len+1);
00082     memcpy(p, name, len);
00083     p[len] = 0;
00084     sname->name = (const char*)p;
00085     sname->lit = FALSE;
00086   }
00087   kh_put(n2s, mrb, h, sym);
00088 
00089   return sym;
00090 }
00091 
00092 MRB_API mrb_sym
00093 mrb_intern(mrb_state *mrb, const char *name, size_t len)
00094 {
00095   return sym_intern(mrb, name, len, FALSE);
00096 }
00097 
00098 MRB_API mrb_sym
00099 mrb_intern_static(mrb_state *mrb, const char *name, size_t len)
00100 {
00101   return sym_intern(mrb, name, len, TRUE);
00102 }
00103 
00104 MRB_API mrb_sym
00105 mrb_intern_cstr(mrb_state *mrb, const char *name)
00106 {
00107   return mrb_intern(mrb, name, strlen(name));
00108 }
00109 
00110 MRB_API mrb_sym
00111 mrb_intern_str(mrb_state *mrb, mrb_value str)
00112 {
00113   return mrb_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str));
00114 }
00115 
00116 MRB_API mrb_value
00117 mrb_check_intern(mrb_state *mrb, const char *name, size_t len)
00118 {
00119   khash_t(n2s) *h = mrb->name2sym;
00120   symbol_name *sname = mrb->symtbl;
00121   khiter_t k;
00122 
00123   sym_validate_len(mrb, len);
00124   sname->len = (uint16_t)len;
00125   sname->name = name;
00126 
00127   k = kh_get(n2s, mrb, h, 0);
00128   if (k != kh_end(h)) {
00129     return mrb_symbol_value(kh_key(h, k));
00130   }
00131   return mrb_nil_value();
00132 }
00133 
00134 MRB_API mrb_value
00135 mrb_check_intern_cstr(mrb_state *mrb, const char *name)
00136 {
00137   return mrb_check_intern(mrb, name, (mrb_int)strlen(name));
00138 }
00139 
00140 MRB_API mrb_value
00141 mrb_check_intern_str(mrb_state *mrb, mrb_value str)
00142 {
00143   return mrb_check_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str));
00144 }
00145 
00146 /* lenp must be a pointer to a size_t variable */
00147 MRB_API const char*
00148 mrb_sym2name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp)
00149 {
00150   if (sym == 0 || mrb->symidx < sym) {
00151     if (lenp) *lenp = 0;
00152     return NULL;
00153   }
00154 
00155   if (lenp) *lenp = mrb->symtbl[sym].len;
00156   return mrb->symtbl[sym].name;
00157 }
00158 
00159 void
00160 mrb_free_symtbl(mrb_state *mrb)
00161 {
00162   mrb_sym i, lim;
00163 
00164   for (i=1, lim=mrb->symidx+1; i<lim; i++) {
00165     if (!mrb->symtbl[i].lit) {
00166       mrb_free(mrb, (char*)mrb->symtbl[i].name);
00167     }
00168   }
00169   mrb_free(mrb, mrb->symtbl);
00170   kh_destroy(n2s, mrb, mrb->name2sym);
00171 }
00172 
00173 void
00174 mrb_init_symtbl(mrb_state *mrb)
00175 {
00176   mrb->name2sym = kh_init(n2s, mrb);
00177 }
00178 
00179 /**********************************************************************
00180  * Document-class: Symbol
00181  *
00182  *  <code>Symbol</code> objects represent names and some strings
00183  *  inside the Ruby
00184  *  interpreter. They are generated using the <code>:name</code> and
00185  *  <code>:"string"</code> literals
00186  *  syntax, and by the various <code>to_sym</code> methods. The same
00187  *  <code>Symbol</code> object will be created for a given name or string
00188  *  for the duration of a program's execution, regardless of the context
00189  *  or meaning of that name. Thus if <code>Fred</code> is a constant in
00190  *  one context, a method in another, and a class in a third, the
00191  *  <code>Symbol</code> <code>:Fred</code> will be the same object in
00192  *  all three contexts.
00193  *
00194  *     module One
00195  *       class Fred
00196  *       end
00197  *       $f1 = :Fred
00198  *     end
00199  *     module Two
00200  *       Fred = 1
00201  *       $f2 = :Fred
00202  *     end
00203  *     def Fred()
00204  *     end
00205  *     $f3 = :Fred
00206  *     $f1.object_id   #=> 2514190
00207  *     $f2.object_id   #=> 2514190
00208  *     $f3.object_id   #=> 2514190
00209  *
00210  */
00211 
00212 
00213 /* 15.2.11.3.1  */
00214 /*
00215  *  call-seq:
00216  *     sym == obj   -> true or false
00217  *
00218  *  Equality---If <i>sym</i> and <i>obj</i> are exactly the same
00219  *  symbol, returns <code>true</code>.
00220  */
00221 
00222 static mrb_value
00223 sym_equal(mrb_state *mrb, mrb_value sym1)
00224 {
00225   mrb_value sym2;
00226 
00227   mrb_get_args(mrb, "o", &sym2);
00228 
00229   return mrb_bool_value(mrb_obj_equal(mrb, sym1, sym2));
00230 }
00231 
00232 /* 15.2.11.3.2  */
00233 /* 15.2.11.3.3  */
00234 /*
00235  *  call-seq:
00236  *     sym.id2name   -> string
00237  *     sym.to_s      -> string
00238  *
00239  *  Returns the name or string corresponding to <i>sym</i>.
00240  *
00241  *     :fred.id2name   #=> "fred"
00242  */
00243 static mrb_value
00244 mrb_sym_to_s(mrb_state *mrb, mrb_value sym)
00245 {
00246   mrb_sym id = mrb_symbol(sym);
00247   const char *p;
00248   mrb_int len;
00249 
00250   p = mrb_sym2name_len(mrb, id, &len);
00251   return mrb_str_new_static(mrb, p, len);
00252 }
00253 
00254 /* 15.2.11.3.4  */
00255 /*
00256  * call-seq:
00257  *   sym.to_sym   -> sym
00258  *   sym.intern   -> sym
00259  *
00260  * In general, <code>to_sym</code> returns the <code>Symbol</code> corresponding
00261  * to an object. As <i>sym</i> is already a symbol, <code>self</code> is returned
00262  * in this case.
00263  */
00264 
00265 static mrb_value
00266 sym_to_sym(mrb_state *mrb, mrb_value sym)
00267 {
00268   return sym;
00269 }
00270 
00271 /* 15.2.11.3.5(x)  */
00272 /*
00273  *  call-seq:
00274  *     sym.inspect    -> string
00275  *
00276  *  Returns the representation of <i>sym</i> as a symbol literal.
00277  *
00278  *     :fred.inspect   #=> ":fred"
00279  */
00280 
00281 #if __STDC__
00282 # define SIGN_EXTEND_CHAR(c) ((signed char)(c))
00283 #else  /* not __STDC__ */
00284 /* As in Harbison and Steele.  */
00285 # define SIGN_EXTEND_CHAR(c) ((((unsigned char)(c)) ^ 128) - 128)
00286 #endif
00287 #define is_identchar(c) (SIGN_EXTEND_CHAR(c)!=-1&&(ISALNUM(c) || (c) == '_'))
00288 
00289 static mrb_bool
00290 is_special_global_name(const char* m)
00291 {
00292   switch (*m) {
00293     case '~': case '*': case '$': case '?': case '!': case '@':
00294     case '/': case '\\': case ';': case ',': case '.': case '=':
00295     case ':': case '<': case '>': case '\"':
00296     case '&': case '`': case '\'': case '+':
00297     case '0':
00298       ++m;
00299       break;
00300     case '-':
00301       ++m;
00302       if (is_identchar(*m)) m += 1;
00303       break;
00304     default:
00305       if (!ISDIGIT(*m)) return FALSE;
00306       do ++m; while (ISDIGIT(*m));
00307       break;
00308   }
00309   return !*m;
00310 }
00311 
00312 static mrb_bool
00313 symname_p(const char *name)
00314 {
00315   const char *m = name;
00316   mrb_bool localid = FALSE;
00317 
00318   if (!m) return FALSE;
00319   switch (*m) {
00320     case '\0':
00321       return FALSE;
00322 
00323     case '$':
00324       if (is_special_global_name(++m)) return TRUE;
00325       goto id;
00326 
00327     case '@':
00328       if (*++m == '@') ++m;
00329       goto id;
00330 
00331     case '<':
00332       switch (*++m) {
00333         case '<': ++m; break;
00334         case '=': if (*++m == '>') ++m; break;
00335         default: break;
00336       }
00337       break;
00338 
00339     case '>':
00340       switch (*++m) {
00341         case '>': case '=': ++m; break;
00342         default: break;
00343       }
00344       break;
00345 
00346     case '=':
00347       switch (*++m) {
00348         case '~': ++m; break;
00349         case '=': if (*++m == '=') ++m; break;
00350         default: return FALSE;
00351       }
00352       break;
00353 
00354     case '*':
00355       if (*++m == '*') ++m;
00356       break;
00357     case '!':
00358       if (*++m == '=') ++m;
00359       break;
00360     case '+': case '-':
00361       if (*++m == '@') ++m;
00362       break;
00363     case '|':
00364       if (*++m == '|') ++m;
00365       break;
00366     case '&':
00367       if (*++m == '&') ++m;
00368       break;
00369 
00370     case '^': case '/': case '%': case '~': case '`':
00371       ++m;
00372       break;
00373 
00374     case '[':
00375       if (*++m != ']') return FALSE;
00376       if (*++m == '=') ++m;
00377       break;
00378 
00379     default:
00380       localid = !ISUPPER(*m);
00381 id:
00382       if (*m != '_' && !ISALPHA(*m)) return FALSE;
00383       while (is_identchar(*m)) m += 1;
00384       if (localid) {
00385         switch (*m) {
00386           case '!': case '?': case '=': ++m;
00387           default: break;
00388             }
00389         }
00390       break;
00391   }
00392   return *m ? FALSE : TRUE;
00393 }
00394 
00395 static mrb_value
00396 sym_inspect(mrb_state *mrb, mrb_value sym)
00397 {
00398   mrb_value str;
00399   const char *name;
00400   mrb_int len;
00401   mrb_sym id = mrb_symbol(sym);
00402   char *sp;
00403 
00404   name = mrb_sym2name_len(mrb, id, &len);
00405   str = mrb_str_new(mrb, 0, len+1);
00406   sp = RSTRING_PTR(str);
00407   RSTRING_PTR(str)[0] = ':';
00408   memcpy(sp+1, name, len);
00409   mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
00410   if (!symname_p(name) || strlen(name) != (size_t)len) {
00411     str = mrb_str_dump(mrb, str);
00412     sp = RSTRING_PTR(str);
00413     sp[0] = ':';
00414     sp[1] = '"';
00415   }
00416   return str;
00417 }
00418 
00419 MRB_API mrb_value
00420 mrb_sym2str(mrb_state *mrb, mrb_sym sym)
00421 {
00422   mrb_int len;
00423   const char *name = mrb_sym2name_len(mrb, sym, &len);
00424 
00425   if (!name) return mrb_undef_value(); /* can't happen */
00426   return mrb_str_new_static(mrb, name, len);
00427 }
00428 
00429 MRB_API const char*
00430 mrb_sym2name(mrb_state *mrb, mrb_sym sym)
00431 {
00432   mrb_int len;
00433   const char *name = mrb_sym2name_len(mrb, sym, &len);
00434 
00435   if (!name) return NULL;
00436   if (symname_p(name) && strlen(name) == (size_t)len) {
00437     return name;
00438   }
00439   else {
00440     mrb_value str = mrb_str_dump(mrb, mrb_str_new_static(mrb, name, len));
00441     return RSTRING_PTR(str);
00442   }
00443 }
00444 
00445 #define lesser(a,b) (((a)>(b))?(b):(a))
00446 
00447 static mrb_value
00448 sym_cmp(mrb_state *mrb, mrb_value s1)
00449 {
00450   mrb_value s2;
00451   mrb_sym sym1, sym2;
00452 
00453   mrb_get_args(mrb, "o", &s2);
00454   if (mrb_type(s2) != MRB_TT_SYMBOL) return mrb_nil_value();
00455   sym1 = mrb_symbol(s1);
00456   sym2 = mrb_symbol(s2);
00457   if (sym1 == sym2) return mrb_fixnum_value(0);
00458   else {
00459     const char *p1, *p2;
00460     int retval;
00461     mrb_int len, len1, len2;
00462 
00463     p1 = mrb_sym2name_len(mrb, sym1, &len1);
00464     p2 = mrb_sym2name_len(mrb, sym2, &len2);
00465     len = lesser(len1, len2);
00466     retval = memcmp(p1, p2, len);
00467     if (retval == 0) {
00468       if (len1 == len2) return mrb_fixnum_value(0);
00469       if (len1 > len2)  return mrb_fixnum_value(1);
00470       return mrb_fixnum_value(-1);
00471     }
00472     if (retval > 0) return mrb_fixnum_value(1);
00473     return mrb_fixnum_value(-1);
00474   }
00475 }
00476 
00477 void
00478 mrb_init_symbol(mrb_state *mrb)
00479 {
00480   struct RClass *sym;
00481 
00482   sym = mrb->symbol_class = mrb_define_class(mrb, "Symbol", mrb->object_class);                 /* 15.2.11 */
00483 
00484   mrb_define_method(mrb, sym, "===",             sym_equal,      MRB_ARGS_REQ(1));              /* 15.2.11.3.1  */
00485   mrb_define_method(mrb, sym, "id2name",         mrb_sym_to_s,   MRB_ARGS_NONE());              /* 15.2.11.3.2  */
00486   mrb_define_method(mrb, sym, "to_s",            mrb_sym_to_s,   MRB_ARGS_NONE());              /* 15.2.11.3.3  */
00487   mrb_define_method(mrb, sym, "to_sym",          sym_to_sym,     MRB_ARGS_NONE());              /* 15.2.11.3.4  */
00488   mrb_define_method(mrb, sym, "inspect",         sym_inspect,    MRB_ARGS_NONE());              /* 15.2.11.3.5(x)  */
00489   mrb_define_method(mrb, sym, "<=>",             sym_cmp,        MRB_ARGS_REQ(1));
00490 }
00491