jsregexp.c 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. #include "jsi.h"
  2. #include "regexp.h"
  3. static char *escaperegexp(js_State *J, const char *pattern) {
  4. char *copy, *p;
  5. const char *s;
  6. int n = 0;
  7. for (s = pattern; *s; ++s) {
  8. if (*s == '/')
  9. ++n;
  10. ++n;
  11. }
  12. copy = p = js_malloc(J, n+1);
  13. for (s = pattern; *s; ++s) {
  14. if (*s == '/')
  15. *p++ = '\\';
  16. *p++ = *s;
  17. }
  18. *p = 0;
  19. return copy;
  20. }
  21. static void js_newregexpx(js_State *J, const char *pattern, int flags, int is_clone)
  22. {
  23. const char *error;
  24. js_Object *obj;
  25. Reprog *prog;
  26. int opts;
  27. obj = jsV_newobject(J, JS_CREGEXP, J->RegExp_prototype);
  28. opts = 0;
  29. if (flags & JS_REGEXP_I) opts |= REG_ICASE;
  30. if (flags & JS_REGEXP_M) opts |= REG_NEWLINE;
  31. prog = js_regcompx(J->alloc, J->actx, pattern, opts, &error);
  32. if (!prog)
  33. js_syntaxerror(J, "regular expression: %s", error);
  34. obj->u.r.prog = prog;
  35. obj->u.r.source = is_clone ? js_strdup(J, pattern) : escaperegexp(J, pattern);
  36. obj->u.r.flags = flags;
  37. obj->u.r.last = 0;
  38. js_pushobject(J, obj);
  39. }
  40. void js_newregexp(js_State *J, const char *pattern, int flags)
  41. {
  42. js_newregexpx(J, pattern, flags, 0);
  43. }
  44. void js_RegExp_prototype_exec(js_State *J, js_Regexp *re, const char *text)
  45. {
  46. const char *haystack;
  47. int result;
  48. int i;
  49. int opts;
  50. Resub m;
  51. haystack = text;
  52. opts = 0;
  53. if (re->flags & JS_REGEXP_G) {
  54. if (re->last > strlen(haystack)) {
  55. re->last = 0;
  56. js_pushnull(J);
  57. return;
  58. }
  59. if (re->last > 0) {
  60. haystack = text + re->last;
  61. opts |= REG_NOTBOL;
  62. }
  63. }
  64. result = js_regexec(re->prog, haystack, &m, opts);
  65. if (result < 0)
  66. js_error(J, "regexec failed");
  67. if (result == 0) {
  68. js_newarray(J);
  69. js_pushstring(J, text);
  70. js_setproperty(J, -2, "input");
  71. js_pushnumber(J, js_utfptrtoidx(text, m.sub[0].sp));
  72. js_setproperty(J, -2, "index");
  73. for (i = 0; i < m.nsub; ++i) {
  74. js_pushlstring(J, m.sub[i].sp, m.sub[i].ep - m.sub[i].sp);
  75. js_setindex(J, -2, i);
  76. }
  77. if (re->flags & JS_REGEXP_G)
  78. re->last = m.sub[0].ep - text;
  79. return;
  80. }
  81. if (re->flags & JS_REGEXP_G)
  82. re->last = 0;
  83. js_pushnull(J);
  84. }
  85. static void Rp_test(js_State *J)
  86. {
  87. js_Regexp *re;
  88. const char *text;
  89. int result;
  90. int opts;
  91. Resub m;
  92. re = js_toregexp(J, 0);
  93. text = js_tostring(J, 1);
  94. opts = 0;
  95. if (re->flags & JS_REGEXP_G) {
  96. if (re->last > strlen(text)) {
  97. re->last = 0;
  98. js_pushboolean(J, 0);
  99. return;
  100. }
  101. if (re->last > 0) {
  102. text += re->last;
  103. opts |= REG_NOTBOL;
  104. }
  105. }
  106. result = js_regexec(re->prog, text, &m, opts);
  107. if (result < 0)
  108. js_error(J, "regexec failed");
  109. if (result == 0) {
  110. if (re->flags & JS_REGEXP_G)
  111. re->last = re->last + (m.sub[0].ep - text);
  112. js_pushboolean(J, 1);
  113. return;
  114. }
  115. if (re->flags & JS_REGEXP_G)
  116. re->last = 0;
  117. js_pushboolean(J, 0);
  118. }
  119. static void jsB_new_RegExp(js_State *J)
  120. {
  121. js_Regexp *old;
  122. const char *pattern;
  123. int flags;
  124. int is_clone = 0;
  125. if (js_isregexp(J, 1)) {
  126. if (js_isdefined(J, 2))
  127. js_typeerror(J, "cannot supply flags when creating one RegExp from another");
  128. old = js_toregexp(J, 1);
  129. pattern = old->source;
  130. flags = old->flags;
  131. is_clone = 1;
  132. } else if (js_isundefined(J, 1)) {
  133. pattern = "(?:)";
  134. flags = 0;
  135. } else {
  136. pattern = js_tostring(J, 1);
  137. flags = 0;
  138. }
  139. if (strlen(pattern) == 0)
  140. pattern = "(?:)";
  141. if (js_isdefined(J, 2)) {
  142. const char *s = js_tostring(J, 2);
  143. int g = 0, i = 0, m = 0;
  144. while (*s) {
  145. if (*s == 'g') ++g;
  146. else if (*s == 'i') ++i;
  147. else if (*s == 'm') ++m;
  148. else js_syntaxerror(J, "invalid regular expression flag: '%c'", *s);
  149. ++s;
  150. }
  151. if (g > 1) js_syntaxerror(J, "invalid regular expression flag: 'g'");
  152. if (i > 1) js_syntaxerror(J, "invalid regular expression flag: 'i'");
  153. if (m > 1) js_syntaxerror(J, "invalid regular expression flag: 'm'");
  154. if (g) flags |= JS_REGEXP_G;
  155. if (i) flags |= JS_REGEXP_I;
  156. if (m) flags |= JS_REGEXP_M;
  157. }
  158. js_newregexpx(J, pattern, flags, is_clone);
  159. }
  160. static void jsB_RegExp(js_State *J)
  161. {
  162. if (js_isregexp(J, 1))
  163. return;
  164. jsB_new_RegExp(J);
  165. }
  166. static void Rp_toString(js_State *J)
  167. {
  168. js_Regexp *re;
  169. char * volatile out = NULL;
  170. re = js_toregexp(J, 0);
  171. if (js_try(J)) {
  172. js_free(J, out);
  173. js_throw(J);
  174. }
  175. out = js_malloc(J, strlen(re->source) + 6); /* extra space for //gim */
  176. strcpy(out, "/");
  177. strcat(out, re->source);
  178. strcat(out, "/");
  179. if (re->flags & JS_REGEXP_G) strcat(out, "g");
  180. if (re->flags & JS_REGEXP_I) strcat(out, "i");
  181. if (re->flags & JS_REGEXP_M) strcat(out, "m");
  182. js_pop(J, 0);
  183. js_pushstring(J, out);
  184. js_endtry(J);
  185. js_free(J, out);
  186. }
  187. static void Rp_exec(js_State *J)
  188. {
  189. js_RegExp_prototype_exec(J, js_toregexp(J, 0), js_tostring(J, 1));
  190. }
  191. void jsB_initregexp(js_State *J)
  192. {
  193. js_pushobject(J, J->RegExp_prototype);
  194. {
  195. jsB_propf(J, "RegExp.prototype.toString", Rp_toString, 0);
  196. jsB_propf(J, "RegExp.prototype.test", Rp_test, 0);
  197. jsB_propf(J, "RegExp.prototype.exec", Rp_exec, 0);
  198. }
  199. js_newcconstructor(J, jsB_RegExp, jsB_new_RegExp, "RegExp", 1);
  200. js_defglobal(J, "RegExp", JS_DONTENUM);
  201. }