MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

RegExp

+311 -32
+170 -32
src/ant.c
··· 4 4 5 5 #include <assert.h> 6 6 #include <math.h> 7 + #include <regex.h> 7 8 #include <stdarg.h> 8 9 #include <stdio.h> 9 10 #include <stdlib.h> ··· 169 170 static jsval_t builtin_string_template(struct js *js, jsval_t *args, int nargs); 170 171 static jsval_t builtin_string_charCodeAt(struct js *js, jsval_t *args, int nargs); 171 172 static jsval_t builtin_Object(struct js *js, jsval_t *args, int nargs); 173 + static jsval_t builtin_RegExp(struct js *js, jsval_t *args, int nargs); 172 174 static jsval_t builtin_Promise(struct js *js, jsval_t *args, int nargs); 173 175 static jsval_t builtin_Promise_resolve(struct js *js, jsval_t *args, int nargs); 174 176 static jsval_t builtin_Promise_reject(struct js *js, jsval_t *args, int nargs); ··· 3625 3627 return err_obj; 3626 3628 } 3627 3629 3630 + static jsval_t builtin_RegExp(struct js *js, jsval_t *args, int nargs) { 3631 + jsval_t regexp_obj = js->this_val; 3632 + bool use_this = (vtype(regexp_obj) == T_OBJ); 3633 + 3634 + if (!use_this) { 3635 + regexp_obj = mkobj(js, 0); 3636 + } 3637 + 3638 + jsval_t pattern = js_mkstr(js, "", 0); 3639 + if (nargs > 0) { 3640 + if (vtype(args[0]) == T_STR) { 3641 + pattern = args[0]; 3642 + } else { 3643 + const char *str = js_str(js, args[0]); 3644 + pattern = js_mkstr(js, str, strlen(str)); 3645 + } 3646 + } 3647 + 3648 + jsval_t flags = js_mkstr(js, "", 0); 3649 + if (nargs > 1 && vtype(args[1]) == T_STR) { 3650 + flags = args[1]; 3651 + } 3652 + 3653 + jsval_t source_key = js_mkstr(js, "source", 6); 3654 + setprop(js, regexp_obj, source_key, pattern); 3655 + 3656 + jsval_t flags_key = js_mkstr(js, "flags", 5); 3657 + setprop(js, regexp_obj, flags_key, flags); 3658 + 3659 + jsoff_t flags_len, flags_off = vstr(js, flags, &flags_len); 3660 + const char *flags_str = (char *) &js->mem[flags_off]; 3661 + 3662 + bool global = false, ignoreCase = false, multiline = false; 3663 + for (jsoff_t i = 0; i < flags_len; i++) { 3664 + if (flags_str[i] == 'g') global = true; 3665 + if (flags_str[i] == 'i') ignoreCase = true; 3666 + if (flags_str[i] == 'm') multiline = true; 3667 + } 3668 + 3669 + setprop(js, regexp_obj, js_mkstr(js, "global", 6), mkval(T_BOOL, global ? 1 : 0)); 3670 + setprop(js, regexp_obj, js_mkstr(js, "ignoreCase", 10), mkval(T_BOOL, ignoreCase ? 1 : 0)); 3671 + setprop(js, regexp_obj, js_mkstr(js, "multiline", 9), mkval(T_BOOL, multiline ? 1 : 0)); 3672 + 3673 + return regexp_obj; 3674 + } 3675 + 3628 3676 static jsval_t builtin_object_keys(struct js *js, jsval_t *args, int nargs) { 3629 3677 if (nargs == 0) return mkarr(js); 3630 3678 jsval_t obj = args[0]; ··· 4086 4134 jsval_t search = args[0]; 4087 4135 jsval_t replacement = args[1]; 4088 4136 4089 - if (vtype(search) != T_STR || vtype(replacement) != T_STR) return str; 4090 4137 jsoff_t str_len, str_off = vstr(js, str, &str_len); 4091 - jsoff_t search_len, search_off = vstr(js, search, &search_len); 4092 - jsoff_t repl_len, repl_off = vstr(js, replacement, &repl_len); 4093 - 4094 4138 const char *str_ptr = (char *) &js->mem[str_off]; 4095 - const char *search_ptr = (char *) &js->mem[search_off]; 4096 - const char *repl_ptr = (char *) &js->mem[repl_off]; 4097 4139 4098 - if (search_len > str_len) return str; 4099 - jsoff_t found_at = -1; 4140 + bool is_regex = false; 4141 + bool global_flag = false; 4142 + char pattern_buf[256]; 4143 + jsoff_t pattern_len = 0; 4100 4144 4101 - for (jsoff_t i = 0; i <= str_len - search_len; i++) { 4102 - if (memcmp(str_ptr + i, search_ptr, search_len) == 0) { 4103 - found_at = i; 4104 - break; 4145 + if (vtype(search) == T_OBJ) { 4146 + jsoff_t pattern_off = lkp(js, search, "source", 6); 4147 + jsoff_t flags_off = lkp(js, search, "flags", 5); 4148 + 4149 + if (pattern_off != 0) { 4150 + jsval_t pattern_val = resolveprop(js, mkval(T_PROP, pattern_off)); 4151 + if (vtype(pattern_val) == T_STR) { 4152 + is_regex = true; 4153 + jsoff_t plen, poff = vstr(js, pattern_val, &plen); 4154 + pattern_len = plen < sizeof(pattern_buf) - 1 ? plen : sizeof(pattern_buf) - 1; 4155 + memcpy(pattern_buf, &js->mem[poff], pattern_len); 4156 + pattern_buf[pattern_len] = '\0'; 4157 + 4158 + if (flags_off != 0) { 4159 + jsval_t flags_val = resolveprop(js, mkval(T_PROP, flags_off)); 4160 + if (vtype(flags_val) == T_STR) { 4161 + jsoff_t flen, foff = vstr(js, flags_val, &flen); 4162 + const char *flags_str = (char *) &js->mem[foff]; 4163 + for (jsoff_t i = 0; i < flen; i++) { 4164 + if (flags_str[i] == 'g') global_flag = true; 4165 + } 4166 + } 4167 + } 4168 + } 4105 4169 } 4106 4170 } 4107 4171 4108 - if (found_at == -1) return str; 4109 - char result[2048]; 4110 - jsoff_t result_len = 0; 4111 - 4112 - if (found_at > 0 && result_len + found_at < sizeof(result)) { 4113 - memcpy(result + result_len, str_ptr, found_at); 4114 - result_len += found_at; 4115 - } 4116 - 4117 - if (result_len + repl_len < sizeof(result)) { 4118 - memcpy(result + result_len, repl_ptr, repl_len); 4119 - result_len += repl_len; 4120 - } 4172 + if (vtype(replacement) != T_STR) return str; 4173 + jsoff_t repl_len, repl_off = vstr(js, replacement, &repl_len); 4174 + const char *repl_ptr = (char *) &js->mem[repl_off]; 4121 4175 4122 - jsoff_t after_start = found_at + search_len; 4123 - jsoff_t after_len = str_len - after_start; 4176 + char result[4096]; 4177 + jsoff_t result_len = 0; 4124 4178 4125 - if (after_len > 0 && result_len + after_len < sizeof(result)) { 4126 - memcpy(result + result_len, str_ptr + after_start, after_len); 4127 - result_len += after_len; 4179 + if (is_regex) { 4180 + regex_t regex; 4181 + int cflags = REG_EXTENDED; 4182 + 4183 + if (regcomp(&regex, pattern_buf, cflags) != 0) { 4184 + return js_mkerr(js, "invalid regex pattern"); 4185 + } 4186 + 4187 + char str_buf[4096]; 4188 + if (str_len >= sizeof(str_buf)) { 4189 + regfree(&regex); 4190 + return js_mkerr(js, "string too long"); 4191 + } 4192 + memcpy(str_buf, str_ptr, str_len); 4193 + str_buf[str_len] = '\0'; 4194 + 4195 + jsoff_t pos = 0; 4196 + bool replaced = false; 4197 + 4198 + while (pos < str_len) { 4199 + regmatch_t match; 4200 + int ret = regexec(&regex, str_buf + pos, 1, &match, 0); 4201 + 4202 + if (ret == 0 && match.rm_so >= 0) { 4203 + jsoff_t before_len = (jsoff_t)match.rm_so; 4204 + if (result_len + before_len < sizeof(result)) { 4205 + memcpy(result + result_len, str_buf + pos, before_len); 4206 + result_len += before_len; 4207 + } 4208 + 4209 + if (result_len + repl_len < sizeof(result)) { 4210 + memcpy(result + result_len, repl_ptr, repl_len); 4211 + result_len += repl_len; 4212 + } 4213 + 4214 + jsoff_t match_len = (jsoff_t)match.rm_eo; 4215 + if (match_len == 0) { 4216 + if (pos < str_len && result_len < sizeof(result)) { 4217 + result[result_len++] = str_buf[pos]; 4218 + } 4219 + pos++; 4220 + } else { 4221 + pos += match_len; 4222 + } 4223 + 4224 + replaced = true; 4225 + 4226 + if (!global_flag) break; 4227 + } else { 4228 + break; 4229 + } 4230 + } 4231 + 4232 + if (pos < str_len && result_len + (str_len - pos) < sizeof(result)) { 4233 + memcpy(result + result_len, str_buf + pos, str_len - pos); 4234 + result_len += str_len - pos; 4235 + } 4236 + 4237 + regfree(&regex); 4238 + return replaced ? js_mkstr(js, result, result_len) : str; 4239 + 4240 + } else { 4241 + if (vtype(search) != T_STR) return str; 4242 + jsoff_t search_len, search_off = vstr(js, search, &search_len); 4243 + const char *search_ptr = (char *) &js->mem[search_off]; 4244 + 4245 + if (search_len > str_len) return str; 4246 + 4247 + for (jsoff_t i = 0; i <= str_len - search_len; i++) { 4248 + if (memcmp(str_ptr + i, search_ptr, search_len) == 0) { 4249 + if (result_len + i < sizeof(result)) { 4250 + memcpy(result + result_len, str_ptr, i); 4251 + result_len += i; 4252 + } 4253 + if (result_len + repl_len < sizeof(result)) { 4254 + memcpy(result + result_len, repl_ptr, repl_len); 4255 + result_len += repl_len; 4256 + } 4257 + jsoff_t after_start = i + search_len; 4258 + jsoff_t after_len = str_len - after_start; 4259 + if (after_len > 0 && result_len + after_len < sizeof(result)) { 4260 + memcpy(result + result_len, str_ptr + after_start, after_len); 4261 + result_len += after_len; 4262 + } 4263 + return js_mkstr(js, result, result_len); 4264 + } 4265 + } 4266 + return str; 4128 4267 } 4129 - 4130 - return js_mkstr(js, result, result_len); 4131 4268 } 4132 4269 4133 4270 static jsval_t builtin_string_template(struct js *js, jsval_t *args, int nargs) { ··· 4577 4714 setprop(js, glob, js_mkstr(js, "Boolean", 7), js_mkfun(builtin_Boolean)); 4578 4715 setprop(js, glob, js_mkstr(js, "Array", 5), js_mkfun(builtin_Array)); 4579 4716 setprop(js, glob, js_mkstr(js, "Error", 5), js_mkfun(builtin_Error)); 4717 + setprop(js, glob, js_mkstr(js, "RegExp", 6), js_mkfun(builtin_RegExp)); 4580 4718 4581 4719 jsval_t p_proto = js_mkobj(js); 4582 4720 setprop(js, p_proto, js_mkstr(js, "then", 4), js_mkfun(builtin_promise_then));
+18
tests/test_regexp_basic.cjs
··· 1 + // Test basic RegExp constructor 2 + const re1 = new RegExp('hello'); 3 + Ant.println('RegExp created:', re1.source); 4 + Ant.println('Flags:', re1.flags); 5 + Ant.println('Global:', re1.global); 6 + 7 + // Test with flags 8 + const re2 = new RegExp('test', 'g'); 9 + Ant.println('Global flag set:', re2.global); 10 + Ant.println('Flags:', re2.flags); 11 + 12 + // Test multiple flags 13 + const re3 = new RegExp('pattern', 'gi'); 14 + Ant.println('Multiple flags:', re3.flags); 15 + Ant.println('Global:', re3.global); 16 + Ant.println('IgnoreCase:', re3.ignoreCase); 17 + 18 + Ant.println('All basic RegExp tests passed!');
+41
tests/test_regexp_edge_cases.cjs
··· 1 + // Test edge cases for RegExp 2 + 3 + // Empty pattern 4 + const re1 = new RegExp(''); 5 + Ant.println('Empty pattern source:', re1.source); 6 + 7 + // Empty string replacement 8 + const str1 = 'hello'; 9 + const result1 = str1.replace(new RegExp('l', 'g'), ''); 10 + Ant.println('Remove chars:', result1); 11 + 12 + // No match 13 + const str2 = 'hello world'; 14 + const result2 = str2.replace(new RegExp('xyz'), 'foo'); 15 + Ant.println('No match:', result2); 16 + 17 + // Special characters that need escaping 18 + const str3 = 'Price: $10.99'; 19 + const re3 = new RegExp('[0-9.]+'); 20 + const result3 = str3.replace(re3, '20.00'); 21 + Ant.println('Price replace:', result3); 22 + 23 + // Whitespace replacement 24 + const str4 = 'hello world'; 25 + const re4 = new RegExp(' +', 'g'); 26 + const result4 = str4.replace(re4, ' '); 27 + Ant.println('Normalize spaces:', result4); 28 + 29 + // Replace at start 30 + const str5 = 'hello world'; 31 + const re5 = new RegExp('^hello'); 32 + const result5 = str5.replace(re5, 'hi'); 33 + Ant.println('Replace at start:', result5); 34 + 35 + // Replace at end 36 + const str6 = 'hello world'; 37 + const re6 = new RegExp('world$'); 38 + const result6 = str6.replace(re6, 'there'); 39 + Ant.println('Replace at end:', result6); 40 + 41 + Ant.println('All edge case tests passed!');
+44
tests/test_regexp_patterns.cjs
··· 1 + // Test various regex patterns 2 + 3 + // Test word boundaries 4 + const str1 = 'The quick brown fox'; 5 + const re1 = new RegExp('quick', 'g'); 6 + const result1 = str1.replace(re1, 'slow'); 7 + Ant.println('Word replace:', result1); 8 + 9 + // Test character classes 10 + const str2 = 'a1b2c3'; 11 + const re2 = new RegExp('[0-9]', 'g'); 12 + const result2 = str2.replace(re2, 'X'); 13 + Ant.println('Digit replace:', result2); 14 + 15 + // Test alternation 16 + const str3 = 'I like cats and dogs'; 17 + const re3 = new RegExp('cats', 'g'); 18 + const result3 = str3.replace(re3, 'birds'); 19 + Ant.println('Alternation:', result3); 20 + 21 + // Test start of line 22 + const str4 = 'hello world\nhello there'; 23 + const re4 = new RegExp('hello', 'g'); 24 + const result4 = str4.replace(re4, 'hi'); 25 + Ant.println('Multiple hello:', result4); 26 + 27 + // Test any character 28 + const str5 = 'abc def ghi'; 29 + const re5 = new RegExp('d.f'); 30 + const result5 = str5.replace(re5, 'XXX'); 31 + Ant.println('Any char:', result5); 32 + 33 + // Test quantifiers 34 + const str6 = 'aaaa bb c'; 35 + const re6 = new RegExp('a+', 'g'); 36 + const result6 = str6.replace(re6, 'X'); 37 + Ant.println('Quantifier +:', result6); 38 + 39 + const str7 = 'aaaa bb c'; 40 + const re7 = new RegExp('a*', 'g'); 41 + const result7 = str7.replace(re7, 'X'); 42 + Ant.println('Quantifier *:', result7); 43 + 44 + Ant.println('All pattern tests passed!');
+38
tests/test_string_replace_regex.cjs
··· 1 + // Test string replace with regex 2 + 3 + // Basic regex replacement 4 + const str1 = 'hello world'; 5 + const re1 = new RegExp('world'); 6 + const result1 = str1.replace(re1, 'universe'); 7 + Ant.println('Basic replace:', result1); 8 + 9 + // Test with global flag 10 + const str2 = 'foo bar foo baz'; 11 + const re2 = new RegExp('foo', 'g'); 12 + const result2 = str2.replace(re2, 'qux'); 13 + Ant.println('Global replace:', result2); 14 + 15 + // Test without global flag (should replace only first) 16 + const str3 = 'foo bar foo baz'; 17 + const re3 = new RegExp('foo'); 18 + const result3 = str3.replace(re3, 'qux'); 19 + Ant.println('Non-global replace:', result3); 20 + 21 + // Test with regex pattern (digits) 22 + const str4 = 'I have 2 apples and 3 oranges'; 23 + const re4 = new RegExp('[0-9]+', 'g'); 24 + const result4 = str4.replace(re4, 'X'); 25 + Ant.println('Replace digits:', result4); 26 + 27 + // Test with dot pattern 28 + const str5 = 'abc123def'; 29 + const re5 = new RegExp('...', 'g'); 30 + const result5 = str5.replace(re5, 'X'); 31 + Ant.println('Replace with dot:', result5); 32 + 33 + // Test string replace (without regex) - should still work 34 + const str6 = 'hello world'; 35 + const result6 = str6.replace('world', 'there'); 36 + Ant.println('String replace:', result6); 37 + 38 + Ant.println('All replace tests passed!');