Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7639 views
1
#include "jsi.h"
2
#include "jsvalue.h"
3
#include "jsbuiltin.h"
4
#include "utf.h"
5
#include "regex.h"
6
7
int js_runeat(js_State *J, const char *s, int i)
8
{
9
Rune rune = 0;
10
while (i-- >= 0) {
11
rune = *(unsigned char*)s;
12
if (rune < Runeself) {
13
if (rune == 0)
14
return 0;
15
++s;
16
} else
17
s += chartorune(&rune, s);
18
}
19
return rune;
20
}
21
22
const char *js_utfidxtoptr(const char *s, int i)
23
{
24
Rune rune;
25
while (i-- > 0) {
26
rune = *(unsigned char*)s;
27
if (rune < Runeself) {
28
if (rune == 0)
29
return NULL;
30
++s;
31
} else
32
s += chartorune(&rune, s);
33
}
34
return s;
35
}
36
37
int js_utfptrtoidx(const char *s, const char *p)
38
{
39
Rune rune;
40
int i = 0;
41
while (s < p) {
42
if (*(unsigned char *)s < Runeself)
43
++s;
44
else
45
s += chartorune(&rune, s);
46
++i;
47
}
48
return i;
49
}
50
51
static void jsB_new_String(js_State *J)
52
{
53
js_newstring(J, js_gettop(J) > 1 ? js_tostring(J, 1) : "");
54
}
55
56
static void jsB_String(js_State *J)
57
{
58
js_pushstring(J, js_gettop(J) > 1 ? js_tostring(J, 1) : "");
59
}
60
61
static void Sp_toString(js_State *J)
62
{
63
js_Object *self = js_toobject(J, 0);
64
if (self->type != JS_CSTRING) js_typeerror(J, "not a string");
65
js_pushliteral(J, self->u.s.string);
66
}
67
68
static void Sp_valueOf(js_State *J)
69
{
70
js_Object *self = js_toobject(J, 0);
71
if (self->type != JS_CSTRING) js_typeerror(J, "not a string");
72
js_pushliteral(J, self->u.s.string);
73
}
74
75
static void Sp_charAt(js_State *J)
76
{
77
char buf[UTFmax + 1];
78
const char *s = js_tostring(J, 0);
79
int pos = js_tointeger(J, 1);
80
Rune rune = js_runeat(J, s, pos);
81
if (rune > 0) {
82
buf[runetochar(buf, &rune)] = 0;
83
js_pushstring(J, buf);
84
} else {
85
js_pushliteral(J, "");
86
}
87
}
88
89
static void Sp_charCodeAt(js_State *J)
90
{
91
const char *s = js_tostring(J, 0);
92
int pos = js_tointeger(J, 1);
93
Rune rune = js_runeat(J, s, pos);
94
if (rune > 0)
95
js_pushnumber(J, rune);
96
else
97
js_pushnumber(J, NAN);
98
}
99
100
static void Sp_concat(js_State *J)
101
{
102
unsigned int i, top = js_gettop(J);
103
unsigned int n;
104
char * volatile out;
105
const char *s;
106
107
if (top == 1)
108
return;
109
110
s = js_tostring(J, 0);
111
n = strlen(s);
112
out = js_malloc(J, n + 1);
113
strcpy(out, s);
114
115
if (js_try(J)) {
116
js_free(J, out);
117
js_throw(J);
118
}
119
120
for (i = 1; i < top; ++i) {
121
s = js_tostring(J, i);
122
n += strlen(s);
123
out = realloc(out, n + 1);
124
strcat(out, s);
125
}
126
127
js_pushstring(J, out);
128
js_endtry(J);
129
js_free(J, out);
130
}
131
132
static void Sp_indexOf(js_State *J)
133
{
134
const char *haystack = js_tostring(J, 0);
135
const char *needle = js_tostring(J, 1);
136
int pos = js_tointeger(J, 2);
137
int len = strlen(needle);
138
int k = 0;
139
Rune rune;
140
while (*haystack) {
141
if (k >= pos && !strncmp(haystack, needle, len)) {
142
js_pushnumber(J, k);
143
return;
144
}
145
haystack += chartorune(&rune, haystack);
146
++k;
147
}
148
js_pushnumber(J, -1);
149
}
150
151
static void Sp_lastIndexOf(js_State *J)
152
{
153
const char *haystack = js_tostring(J, 0);
154
const char *needle = js_tostring(J, 1);
155
int pos = js_isdefined(J, 2) ? js_tointeger(J, 2) : strlen(haystack);
156
int len = strlen(needle);
157
int k = 0, last = -1;
158
Rune rune;
159
while (*haystack && k <= pos) {
160
if (!strncmp(haystack, needle, len))
161
last = k;
162
haystack += chartorune(&rune, haystack);
163
++k;
164
}
165
js_pushnumber(J, last);
166
}
167
168
static void Sp_localeCompare(js_State *J)
169
{
170
const char *a = js_tostring(J, 0);
171
const char *b = js_tostring(J, 1);
172
js_pushnumber(J, strcmp(a, b));
173
}
174
175
static void Sp_slice(js_State *J)
176
{
177
const char *str = js_tostring(J, 0);
178
const char *ss, *ee;
179
int len = utflen(str);
180
int s = js_tointeger(J, 1);
181
int e = js_isdefined(J, 2) ? js_tointeger(J, 2) : len;
182
183
s = s < 0 ? s + len : s;
184
e = e < 0 ? e + len : e;
185
186
s = s < 0 ? 0 : s > len ? len : s;
187
e = e < 0 ? 0 : e > len ? len : e;
188
189
if (s < e) {
190
ss = js_utfidxtoptr(str, s);
191
ee = js_utfidxtoptr(ss, e - s);
192
} else {
193
ss = js_utfidxtoptr(str, e);
194
ee = js_utfidxtoptr(ss, s - e);
195
}
196
197
js_pushlstring(J, ss, ee - ss);
198
}
199
200
static void Sp_substring(js_State *J)
201
{
202
const char *str = js_tostring(J, 0);
203
const char *ss, *ee;
204
int len = utflen(str);
205
int s = js_tointeger(J, 1);
206
int e = js_isdefined(J, 2) ? js_tointeger(J, 2) : len;
207
208
s = s < 0 ? 0 : s > len ? len : s;
209
e = e < 0 ? 0 : e > len ? len : e;
210
211
if (s < e) {
212
ss = js_utfidxtoptr(str, s);
213
ee = js_utfidxtoptr(ss, e - s);
214
} else {
215
ss = js_utfidxtoptr(str, e);
216
ee = js_utfidxtoptr(ss, s - e);
217
}
218
219
js_pushlstring(J, ss, ee - ss);
220
}
221
222
static void Sp_toLowerCase(js_State *J)
223
{
224
const char *src = js_tostring(J, 0);
225
char *dst = js_malloc(J, UTFmax * strlen(src) + 1);
226
const char *s = src;
227
char *d = dst;
228
Rune rune;
229
while (*s) {
230
s += chartorune(&rune, s);
231
rune = tolowerrune(rune);
232
d += runetochar(d, &rune);
233
}
234
*d = 0;
235
if (js_try(J)) {
236
js_free(J, dst);
237
js_throw(J);
238
}
239
js_pushstring(J, dst);
240
js_endtry(J);
241
js_free(J, dst);
242
}
243
244
static void Sp_toUpperCase(js_State *J)
245
{
246
const char *src = js_tostring(J, 0);
247
char *dst = js_malloc(J, UTFmax * strlen(src) + 1);
248
const char *s = src;
249
char *d = dst;
250
Rune rune;
251
while (*s) {
252
s += chartorune(&rune, s);
253
rune = toupperrune(rune);
254
d += runetochar(d, &rune);
255
}
256
*d = 0;
257
if (js_try(J)) {
258
js_free(J, dst);
259
js_throw(J);
260
}
261
js_pushstring(J, dst);
262
js_endtry(J);
263
js_free(J, dst);
264
}
265
266
static int istrim(int c)
267
{
268
return c == 0x9 || c == 0xB || c == 0xC || c == 0x20 || c == 0xA0 || c == 0xFEFF ||
269
c == 0xA || c == 0xD || c == 0x2028 || c == 0x2029;
270
}
271
272
static void Sp_trim(js_State *J)
273
{
274
const char *s, *e;
275
s = js_tostring(J, 0);
276
while (istrim(*s))
277
++s;
278
e = s + strlen(s);
279
while (e > s && istrim(e[-1]))
280
--e;
281
js_pushlstring(J, s, e - s);
282
}
283
284
static void S_fromCharCode(js_State *J)
285
{
286
unsigned int i, top = js_gettop(J);
287
Rune c;
288
char *s, *p;
289
290
s = p = js_malloc(J, (top-1) * UTFmax + 1);
291
292
if (js_try(J)) {
293
js_free(J, s);
294
js_throw(J);
295
}
296
297
for (i = 1; i < top; ++i) {
298
c = js_touint16(J, i);
299
p += runetochar(p, &c);
300
}
301
*p = 0;
302
js_pushstring(J, s);
303
304
js_endtry(J);
305
js_free(J, s);
306
}
307
308
static void Sp_match(js_State *J)
309
{
310
js_Regexp *re;
311
const char *text;
312
unsigned int len;
313
const char *a, *b, *c, *e;
314
Resub m;
315
316
text = js_tostring(J, 0);
317
318
if (js_isregexp(J, 1))
319
js_copy(J, 1);
320
else if (js_isundefined(J, 1))
321
js_newregexp(J, "", 0);
322
else
323
js_newregexp(J, js_tostring(J, 1), 0);
324
325
re = js_toregexp(J, -1);
326
if (!(re->flags & JS_REGEXP_G)) {
327
js_RegExp_prototype_exec(J, re, text);
328
return;
329
}
330
331
re->last = 0;
332
333
js_newarray(J);
334
335
len = 0;
336
a = text;
337
e = text + strlen(text);
338
while (a <= e) {
339
if (js_regexec(re->prog, a, &m, a > text ? REG_NOTBOL : 0))
340
break;
341
342
b = m.sub[0].sp;
343
c = m.sub[0].ep;
344
345
js_pushlstring(J, b, c - b);
346
js_setindex(J, -2, len++);
347
348
a = c;
349
if (c - b == 0)
350
++a;
351
}
352
}
353
354
static void Sp_search(js_State *J)
355
{
356
js_Regexp *re;
357
const char *text;
358
Resub m;
359
360
text = js_tostring(J, 0);
361
362
if (js_isregexp(J, 1))
363
js_copy(J, 1);
364
else if (js_isundefined(J, 1))
365
js_newregexp(J, "", 0);
366
else
367
js_newregexp(J, js_tostring(J, 1), 0);
368
369
re = js_toregexp(J, -1);
370
371
if (!js_regexec(re->prog, text, &m, 0))
372
js_pushnumber(J, js_utfptrtoidx(text, m.sub[0].sp));
373
else
374
js_pushnumber(J, -1);
375
}
376
377
static void Sp_replace_regexp(js_State *J)
378
{
379
js_Regexp *re;
380
const char *source, *s, *r;
381
js_Buffer *sb = NULL;
382
unsigned int n, x;
383
Resub m;
384
385
source = js_tostring(J, 0);
386
re = js_toregexp(J, 1);
387
388
if (js_regexec(re->prog, source, &m, 0)) {
389
js_copy(J, 0);
390
return;
391
}
392
393
re->last = 0;
394
395
loop:
396
s = m.sub[0].sp;
397
n = m.sub[0].ep - m.sub[0].sp;
398
399
if (js_iscallable(J, 2)) {
400
js_copy(J, 2);
401
js_pushundefinedthis(J);
402
for (x = 0; m.sub[x].sp; ++x) /* arg 0..x: substring and subexps that matched */
403
js_pushlstring(J, m.sub[x].sp, m.sub[x].ep - m.sub[x].sp);
404
js_pushnumber(J, s - source); /* arg x+2: offset within search string */
405
js_copy(J, 0); /* arg x+3: search string */
406
js_call(J, 2 + x);
407
r = js_tostring(J, -1);
408
js_putm(J, &sb, source, s);
409
js_puts(J, &sb, r);
410
js_pop(J, 1);
411
} else {
412
r = js_tostring(J, 2);
413
js_putm(J, &sb, source, s);
414
while (*r) {
415
if (*r == '$') {
416
switch (*(++r)) {
417
case '$': js_putc(J, &sb, '$'); break;
418
case '`': js_putm(J, &sb, source, s); break;
419
case '\'': js_puts(J, &sb, s + n); break;
420
case '&':
421
js_putm(J, &sb, s, s + n);
422
break;
423
case '0': case '1': case '2': case '3': case '4':
424
case '5': case '6': case '7': case '8': case '9':
425
x = *r - '0';
426
if (r[1] >= '0' && r[1] <= '9')
427
x = x * 10 + *(++r) - '0';
428
if (x > 0 && x < m.nsub) {
429
js_putm(J, &sb, m.sub[x].sp, m.sub[x].ep);
430
} else {
431
js_putc(J, &sb, '$');
432
if (x > 10) {
433
js_putc(J, &sb, '0' + x / 10);
434
js_putc(J, &sb, '0' + x % 10);
435
} else {
436
js_putc(J, &sb, '0' + x);
437
}
438
}
439
break;
440
default:
441
js_putc(J, &sb, '$');
442
js_putc(J, &sb, *r);
443
break;
444
}
445
++r;
446
} else {
447
js_putc(J, &sb, *r++);
448
}
449
}
450
}
451
452
if (re->flags & JS_REGEXP_G) {
453
source = m.sub[0].ep;
454
if (n == 0) {
455
if (*source)
456
js_putc(J, &sb, *source++);
457
else
458
goto end;
459
}
460
if (!js_regexec(re->prog, source, &m, REG_NOTBOL))
461
goto loop;
462
}
463
464
end:
465
js_puts(J, &sb, s + n);
466
js_putc(J, &sb, 0);
467
468
if (js_try(J)) {
469
js_free(J, sb);
470
js_throw(J);
471
}
472
js_pushstring(J, sb ? sb->s : "");
473
js_endtry(J);
474
js_free(J, sb);
475
}
476
477
static void Sp_replace_string(js_State *J)
478
{
479
const char *source, *needle, *s, *r;
480
js_Buffer *sb = NULL;
481
int n;
482
483
source = js_tostring(J, 0);
484
needle = js_tostring(J, 1);
485
486
s = strstr(source, needle);
487
if (!s) {
488
js_copy(J, 0);
489
return;
490
}
491
n = strlen(needle);
492
493
if (js_iscallable(J, 2)) {
494
js_copy(J, 2);
495
js_pushundefinedthis(J);
496
js_pushlstring(J, s, n); /* arg 1: substring that matched */
497
js_pushnumber(J, s - source); /* arg 2: offset within search string */
498
js_copy(J, 0); /* arg 3: search string */
499
js_call(J, 3);
500
r = js_tostring(J, -1);
501
js_putm(J, &sb, source, s);
502
js_puts(J, &sb, r);
503
js_puts(J, &sb, s + n);
504
js_putc(J, &sb, 0);
505
js_pop(J, 1);
506
} else {
507
r = js_tostring(J, 2);
508
js_putm(J, &sb, source, s);
509
while (*r) {
510
if (*r == '$') {
511
switch (*(++r)) {
512
case '$': js_putc(J, &sb, '$'); break;
513
case '&': js_putm(J, &sb, s, s + n); break;
514
case '`': js_putm(J, &sb, source, s); break;
515
case '\'': js_puts(J, &sb, s + n); break;
516
default: js_putc(J, &sb, '$'); js_putc(J, &sb, *r); break;
517
}
518
++r;
519
} else {
520
js_putc(J, &sb, *r++);
521
}
522
}
523
js_puts(J, &sb, s + n);
524
js_putc(J, &sb, 0);
525
}
526
527
if (js_try(J)) {
528
js_free(J, sb);
529
js_throw(J);
530
}
531
js_pushstring(J, sb ? sb->s : "");
532
js_endtry(J);
533
js_free(J, sb);
534
}
535
536
static void Sp_replace(js_State *J)
537
{
538
if (js_isregexp(J, 1))
539
Sp_replace_regexp(J);
540
else
541
Sp_replace_string(J);
542
}
543
544
static void Sp_split_regexp(js_State *J)
545
{
546
js_Regexp *re;
547
const char *text;
548
unsigned int limit, len, k;
549
const char *p, *a, *b, *c, *e;
550
Resub m;
551
552
text = js_tostring(J, 0);
553
re = js_toregexp(J, 1);
554
limit = js_isdefined(J, 2) ? js_touint32(J, 2) : 1 << 30;
555
556
js_newarray(J);
557
len = 0;
558
559
e = text + strlen(text);
560
561
/* splitting the empty string */
562
if (e == text) {
563
if (js_regexec(re->prog, text, &m, 0)) {
564
if (len == limit) return;
565
js_pushliteral(J, "");
566
js_setindex(J, -2, 0);
567
}
568
return;
569
}
570
571
p = a = text;
572
while (a < e) {
573
if (js_regexec(re->prog, a, &m, a > text ? REG_NOTBOL : 0))
574
break; /* no match */
575
576
b = m.sub[0].sp;
577
c = m.sub[0].ep;
578
579
/* empty string at end of last match */
580
if (b == p) {
581
++a;
582
continue;
583
}
584
585
if (len == limit) return;
586
js_pushlstring(J, p, b - p);
587
js_setindex(J, -2, len++);
588
589
for (k = 1; k < m.nsub; ++k) {
590
if (len == limit) return;
591
js_pushlstring(J, m.sub[k].sp, m.sub[k].ep - m.sub[k].sp);
592
js_setindex(J, -2, len++);
593
}
594
595
a = p = c;
596
}
597
598
if (len == limit) return;
599
js_pushstring(J, p);
600
js_setindex(J, -2, len);
601
}
602
603
static void Sp_split_string(js_State *J)
604
{
605
const char *str = js_tostring(J, 0);
606
const char *sep = js_tostring(J, 1);
607
unsigned int limit = js_isdefined(J, 2) ? js_touint32(J, 2) : 1 << 30;
608
unsigned int i, n;
609
610
js_newarray(J);
611
612
n = strlen(sep);
613
614
/* empty string */
615
if (n == 0) {
616
Rune rune;
617
for (i = 0; *str && i < limit; ++i) {
618
n = chartorune(&rune, str);
619
js_pushlstring(J, str, n);
620
js_setindex(J, -2, i);
621
str += n;
622
}
623
return;
624
}
625
626
for (i = 0; str && i < limit; ++i) {
627
const char *s = strstr(str, sep);
628
if (s) {
629
js_pushlstring(J, str, s-str);
630
js_setindex(J, -2, i);
631
str = s + n;
632
} else {
633
js_pushstring(J, str);
634
js_setindex(J, -2, i);
635
str = NULL;
636
}
637
}
638
}
639
640
static void Sp_split(js_State *J)
641
{
642
if (js_isundefined(J, 1)) {
643
js_newarray(J);
644
js_copy(J, 0);
645
js_setindex(J, -2, 0);
646
} else if (js_isregexp(J, 1)) {
647
Sp_split_regexp(J);
648
} else {
649
Sp_split_string(J);
650
}
651
}
652
653
void jsB_initstring(js_State *J)
654
{
655
J->String_prototype->u.s.string = "";
656
J->String_prototype->u.s.length = 0;
657
658
js_pushobject(J, J->String_prototype);
659
{
660
jsB_propf(J, "toString", Sp_toString, 0);
661
jsB_propf(J, "valueOf", Sp_valueOf, 0);
662
jsB_propf(J, "charAt", Sp_charAt, 1);
663
jsB_propf(J, "charCodeAt", Sp_charCodeAt, 1);
664
jsB_propf(J, "concat", Sp_concat, 1);
665
jsB_propf(J, "indexOf", Sp_indexOf, 1);
666
jsB_propf(J, "lastIndexOf", Sp_lastIndexOf, 1);
667
jsB_propf(J, "localeCompare", Sp_localeCompare, 1);
668
jsB_propf(J, "match", Sp_match, 1);
669
jsB_propf(J, "replace", Sp_replace, 2);
670
jsB_propf(J, "search", Sp_search, 1);
671
jsB_propf(J, "slice", Sp_slice, 2);
672
jsB_propf(J, "split", Sp_split, 2);
673
jsB_propf(J, "substring", Sp_substring, 2);
674
jsB_propf(J, "toLowerCase", Sp_toLowerCase, 0);
675
jsB_propf(J, "toLocaleLowerCase", Sp_toLowerCase, 0);
676
jsB_propf(J, "toUpperCase", Sp_toUpperCase, 0);
677
jsB_propf(J, "toLocaleUpperCase", Sp_toUpperCase, 0);
678
679
/* ES5 */
680
jsB_propf(J, "trim", Sp_trim, 0);
681
}
682
js_newcconstructor(J, jsB_String, jsB_new_String, "String", 1);
683
{
684
jsB_propf(J, "fromCharCode", S_fromCharCode, 1);
685
}
686
js_defglobal(J, "String", JS_DONTENUM);
687
}
688
689