File Coverage

Lua.xs
Criterion Covered Total %
statement 220 259 84.9
branch 106 138 76.8
condition n/a
subroutine n/a
pod n/a
total 326 397 82.1


line stmt bran cond sub pod time code
1             #include
2             #include
3             #include
4              
5             typedef int lua_State;
6             static int luaL_error(lua_State * st, const char * msg);
7              
8              
9             /* code fragment from lstrlib.c */
10              
11             /*
12             ** maximum number of captures that a pattern can do during
13             ** pattern-matching. This limit is arbitrary, but must fit in
14             ** an unsigned char.
15             */
16             #if !defined(LUA_MAXCAPTURES)
17             #define LUA_MAXCAPTURES 32
18             #endif
19              
20              
21             /* macro to 'unsign' a character */
22             #define uchar(c) ((unsigned char)(c))
23              
24              
25             /*
26             ** Some sizes are better limited to fit in 'int', but must also fit in
27             ** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.)
28             */
29             #define MAX_SIZET ((size_t)(~(size_t)0))
30              
31             /*
32             ** {======================================================
33             ** PATTERN MATCHING
34             ** =======================================================
35             */
36              
37              
38             #define CAP_UNFINISHED (-1)
39             #define CAP_POSITION (-2)
40              
41              
42             typedef struct MatchState {
43             const char *src_init; /* init of source string */
44             const char *src_end; /* end ('\0') of source string */
45             const char *p_end; /* end ('\0') of pattern */
46             lua_State *L;
47             int matchdepth; /* control for recursive depth (to avoid C stack overflow) */
48             unsigned char level; /* total number of captures (finished or unfinished) */
49             struct {
50             const char *init;
51             ptrdiff_t len;
52             } capture[LUA_MAXCAPTURES];
53             } MatchState;
54              
55              
56             /* recursive function */
57             static const char *match (MatchState *ms, const char *s, const char *p);
58              
59              
60             /* maximum recursion depth for 'match' */
61             #if !defined(MAXCCALLS)
62             #define MAXCCALLS 200
63             #endif
64              
65              
66             #define L_ESC '%'
67             #define SPECIALS "^$*+?.([%-"
68              
69              
70 2           static int check_capture (MatchState *ms, int l) {
71 2           l -= '1';
72 2 50         if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)
    50          
    50          
73 0           return luaL_error(ms->L, "invalid capture index");
74 2           return l;
75             }
76              
77              
78 55           static int capture_to_close (MatchState *ms) {
79 55           int level = ms->level;
80 56 50         for (level--; level>=0; level--)
81 56 100         if (ms->capture[level].len == CAP_UNFINISHED) return level;
82 0           return luaL_error(ms->L, "invalid pattern capture");
83             }
84              
85              
86 229           static const char *classend (MatchState *ms, const char *p) {
87 229           switch (*p++) {
88 14           case L_ESC: {
89 14 50         if (p == ms->p_end)
90 0           luaL_error(ms->L, "malformed pattern (ends with '%%')");
91 14           return p+1;
92             }
93 31           case '[': {
94 31 100         if (*p == '^') p++;
95             do { /* look for a ']' */
96 81 100         if (p == ms->p_end)
97 1           luaL_error(ms->L, "malformed pattern (missing ']')");
98 80 100         if (*(p++) == L_ESC && p < ms->p_end)
    50          
99 3           p++; /* skip escapes (e.g. '%]') */
100 80 100         } while (*p != ']');
101 30           return p+1;
102             }
103 184           default: {
104 184           return p;
105             }
106             }
107             }
108              
109              
110 18           static int match_class (int c, int cl) {
111             int res;
112 18           switch (tolower(cl)) {
113 0           case 'a' : res = isalpha(c); break;
114 0           case 'c' : res = iscntrl(c); break;
115 3           case 'd' : res = isdigit(c); break;
116 1           case 'g' : res = isgraph(c); break;
117 0           case 'l' : res = islower(c); break;
118 0           case 'p' : res = ispunct(c); break;
119 0           case 's' : res = isspace(c); break;
120 4           case 'u' : res = isupper(c); break;
121 6           case 'w' : res = isalnum(c); break;
122 0           case 'x' : res = isxdigit(c); break;
123 1           case 'z' : res = (c == 0); break; /* deprecated option */
124 3           default: return (cl == c);
125             }
126 15 100         return (islower(cl) ? res : !res);
127             }
128              
129              
130 42           static int matchbracketclass (int c, const char *p, const char *ec) {
131 42           int sig = 1;
132 42 100         if (*(p+1) == '^') {
133 19           sig = 0;
134 19           p++; /* skip the '^' */
135             }
136 79 100         while (++p < ec) {
137 57 100         if (*p == L_ESC) {
138 3           p++;
139 3 100         if (match_class(c, uchar(*p)))
140 1           return sig;
141             }
142 54 100         else if ((*(p+1) == '-') && (p+2 < ec)) {
    50          
143 21           p+=2;
144 21 100         if (uchar(*(p-2)) <= c && c <= uchar(*p))
    100          
145 8           return sig;
146             }
147 33 100         else if (uchar(*p) == c) return sig;
148             }
149 22           return !sig;
150             }
151              
152              
153 232           static int singlematch (MatchState *ms, const char *s, const char *p,
154             const char *ep) {
155 232 100         if (s >= ms->src_end)
156 19           return 0;
157             else {
158 213           int c = uchar(*s);
159 213           switch (*p) {
160 71           case '.': return 1; /* matches any char */
161 15           case L_ESC: return match_class(c, uchar(*(p+1)));
162 24           case '[': return matchbracketclass(c, p, ep-1);
163 103           default: return (uchar(*p) == c);
164             }
165             }
166             }
167              
168              
169 2           static const char *matchbalance (MatchState *ms, const char *s,
170             const char *p) {
171 2 100         if (p >= ms->p_end - 1)
172 1           luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')");
173 1 50         if (*s != *p) return NULL;
174             else {
175 1           int b = *p;
176 1           int e = *(p+1);
177 1           int cont = 1;
178 5 50         while (++s < ms->src_end) {
179 5 100         if (*s == e) {
180 1 50         if (--cont == 0) return s+1;
181             }
182 4 50         else if (*s == b) cont++;
183             }
184             }
185 0           return NULL; /* string ends out of balance */
186             }
187              
188              
189 5           static const char *max_expand (MatchState *ms, const char *s,
190             const char *p, const char *ep) {
191 5           ptrdiff_t i = 0; /* counts maximum expand for item */
192 16 100         while (singlematch(ms, s + i, p, ep))
193 11           i++;
194             /* keeps trying to match with the maximum repetitions */
195 5 50         while (i>=0) {
196 5           const char *res = match(ms, (s+i), ep+1);
197 5 50         if (res) return res;
198 0           i--; /* else didn't match; reduce 1 repetition to try again */
199             }
200 0           return NULL;
201             }
202              
203              
204 0           static const char *min_expand (MatchState *ms, const char *s,
205             const char *p, const char *ep) {
206 0           for (;;) {
207 0           const char *res = match(ms, s, ep+1);
208 0 0         if (res != NULL)
209 0           return res;
210 0 0         else if (singlematch(ms, s, p, ep))
211 0           s++; /* try with one more repetition */
212 0           else return NULL;
213             }
214             }
215              
216              
217 69           static const char *start_capture (MatchState *ms, const char *s,
218             const char *p, int what) {
219             const char *res;
220 69           int level = ms->level;
221 69 50         if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures");
222 69           ms->capture[level].init = s;
223 69           ms->capture[level].len = what;
224 69           ms->level = level+1;
225 69 100         if ((res=match(ms, s, p)) == NULL) /* match failed? */
226 29           ms->level--; /* undo capture */
227 69           return res;
228             }
229              
230              
231 55           static const char *end_capture (MatchState *ms, const char *s,
232             const char *p) {
233 55           int l = capture_to_close(ms);
234             const char *res;
235 55           ms->capture[l].len = s - ms->capture[l].init; /* close capture */
236 55 100         if ((res = match(ms, s, p)) == NULL) /* match failed? */
237 17           ms->capture[l].len = CAP_UNFINISHED; /* undo capture */
238 55           return res;
239             }
240              
241              
242 2           static const char *match_capture (MatchState *ms, const char *s, int l) {
243             size_t len;
244 2           l = check_capture(ms, l);
245 2           len = ms->capture[l].len;
246 2 50         if ((size_t)(ms->src_end-s) >= len &&
247 2 100         memcmp(ms->capture[l].init, s, len) == 0)
248 1           return s+len;
249 1           else return NULL;
250             }
251              
252              
253 281           static const char *match (MatchState *ms, const char *s, const char *p) {
254 281 50         if (ms->matchdepth-- == 0)
255 0           luaL_error(ms->L, "pattern too complex");
256 422           init: /* using goto's to optimize tail recursion */
257 422 100         if (p != ms->p_end) { /* end of pattern? */
258 357           switch (*p) {
259 69           case '(': { /* start capture */
260 69 100         if (*(p + 1) == ')') /* position capture? */
261 1           s = start_capture(ms, s, p + 2, CAP_POSITION);
262             else
263 68           s = start_capture(ms, s, p + 1, CAP_UNFINISHED);
264 69           break;
265             }
266 55           case ')': { /* end capture */
267 55           s = end_capture(ms, s, p + 1);
268 55           break;
269             }
270 0           case '$': {
271 0 0         if ((p + 1) != ms->p_end) /* is the '$' the last char in pattern? */
272 0           goto dflt; /* no; go to default */
273 0 0         s = (s == ms->src_end) ? s : NULL; /* check end of string */
274 0           break;
275             }
276 30           case L_ESC: { /* escaped sequences not in the format class[*+?-]? */
277 30           switch (*(p + 1)) {
278 2           case 'b': { /* balanced string? */
279 2           s = matchbalance(ms, s, p + 2);
280 1 50         if (s != NULL) {
281 1           p += 4; goto init; /* return match(ms, s, p + 4); */
282             } /* else fail (s == NULL) */
283 0           break;
284             }
285 12           case 'f': { /* frontier? */
286             const char *ep; char previous;
287 12           p += 2;
288 12 50         if (*p != '[')
289 0           luaL_error(ms->L, "missing '[' after '%%f' in pattern");
290 12           ep = classend(ms, p); /* points to what is next */
291 12 100         previous = (s == ms->src_init) ? '\0' : *(s - 1);
292 18           if (!matchbracketclass(uchar(previous), p, ep - 1) &&
293 6           matchbracketclass(uchar(*s), p, ep - 1)) {
294 2           p = ep; goto init; /* return match(ms, s, ep); */
295             }
296 10           s = NULL; /* match failed */
297 10           break;
298             }
299 2           case '0': case '1': case '2': case '3':
300             case '4': case '5': case '6': case '7':
301             case '8': case '9': { /* capture results (%0-%9)? */
302 2           s = match_capture(ms, s, uchar(*(p + 1)));
303 2 100         if (s != NULL) {
304 1           p += 2; goto init; /* return match(ms, s, p + 2) */
305             }
306 1           break;
307             }
308 14           default: goto dflt;
309             }
310 11           break;
311             }
312 217           default: dflt: { /* pattern class plus optional suffix */
313 217           const char *ep = classend(ms, p); /* points to optional suffix */
314             /* does not match at least once? */
315 216 100         if (!singlematch(ms, s, p, ep)) {
316 76 100         if (*ep == '*' || *ep == '?' || *ep == '-') { /* accept empty? */
    100          
    50          
317 3           p = ep + 1; goto init; /* return match(ms, s, ep + 1); */
318             }
319             else /* '+' or no suffix */
320 73           s = NULL; /* fail */
321             }
322             else { /* matched once */
323 140           switch (*ep) { /* handle optional suffix */
324 1           case '?': { /* optional */
325             const char *res;
326 1 50         if ((res = match(ms, s + 1, ep + 1)) != NULL)
327 1           s = res;
328             else {
329 0           p = ep + 1; goto init; /* else return match(ms, s, ep + 1); */
330             }
331 1           break;
332             }
333 4           case '+': /* 1 or more repetitions */
334 4           s++; /* 1 match already done */
335             /* FALLTHROUGH */
336 5           case '*': /* 0 or more repetitions */
337 5           s = max_expand(ms, s, p, ep);
338 5           break;
339 0           case '-': /* 0 or more repetitions (minimum) */
340 0           s = min_expand(ms, s, p, ep);
341 0           break;
342 134           default: /* no suffix */
343 134           s++; p = ep; goto init; /* return match(ms, s + 1, ep); */
344             }
345             }
346 79           break;
347             }
348             }
349             }
350 279           ms->matchdepth++;
351 279           return s;
352             }
353              
354             /* end of code fragment from lstrlib.c */
355              
356             #include "EXTERN.h"
357             #include "perl.h"
358             #include "XSUB.h"
359              
360             #include "Lua.h" /* re::engine glue */
361              
362             #ifndef RX_WRAPPED
363             #define RX_WRAPPED(rx) (rx)->wrapped
364             #define RX_WRAPLEN(rx) (rx)->wraplen
365             #endif
366              
367             #if PERL_VERSION == 10
368             #define RegSV(p) (p)
369             #else
370             #define RegSV(p) SvANY(p)
371             #endif
372              
373 3           static int luaL_error(lua_State * st, const char * msg)
374             {
375 3           croak("%s", msg);
376             return 0;
377             }
378              
379             /* lua_engine methods */
380              
381             REGEXP *
382 61           Lua_comp(pTHX_ SV * const pattern, U32 flags)
383             {
384             REGEXP *rx;
385             regexp *re;
386              
387             STRLEN plen;
388 61           char *exp = SvPV((SV*)pattern, plen);
389 61           U32 extflags = flags;
390              
391 61           SV * wrapped = newSVpvn("/", 1);
392 61           sv_2mortal(wrapped);
393              
394 61 100         if (flags & ~(RXf_SPLIT)) {
395 1           warn("flags not supported by re::engine::Lua\n");
396             #ifdef DEBUG
397             warn("\t0x%08x\n", flags);
398             #endif
399             }
400              
401             #ifdef DEBUG
402             warn("Lua_comp |%s|\n", exp);
403             #endif
404              
405             /* C, bypass the engine alltogether and act as perl does */
406 61 100         if (flags & RXf_SPLIT && plen == 1 && exp[0] == ' ')
    50          
    50          
407 1           extflags |= (RXf_SKIPWHITE|RXf_WHITE);
408              
409             /* RXf_NULL - Have C split by characters */
410 61 100         if (plen == 0)
411 1           extflags |= RXf_NULL;
412              
413             /* RXf_START_ONLY - Have C split on newlines */
414 60 100         else if (plen == 1 && exp[0] == '^')
    50          
415 0           extflags |= RXf_START_ONLY;
416              
417             /* RXf_WHITE - Have C split on whitespace */
418 60 100         else if (plen == 3 && strnEQ("%s+", exp, 3))
    100          
419 1           extflags |= RXf_WHITE;
420              
421             #if PERL_VERSION == 10
422             Newxz(rx, 1, REGEXP);
423             re = RegSV(rx);
424             re->refcnt = 1;
425              
426             /* Preserve a copy of the original pattern */
427             re->prelen = (I32)plen;
428             re->precomp = SAVEPVN(exp, plen);
429             #else
430 61           rx = (REGEXP*) newSV_type(SVt_REGEXP);
431 61           re = RegSV(rx);
432              
433 61           re->pre_prefix = SvCUR(wrapped);
434              
435             #if PERL_VERSION < 18
436             /* workaround for segfault in Perl_reg_temp_copy */
437             re->nparens = re->lastparen = re->lastcloseparen = 0;
438             Newxz(re->offs, 1, regexp_paren_pair);
439             /* see https://rt.perl.org/rt3//Public/Bug/Display.html?id=112962 */
440             #endif
441             #endif
442              
443 61           re->extflags = extflags;
444 61           re->engine = &lua_engine;
445              
446             /* qr// stringification */
447 61           sv_catpvn(wrapped, exp, plen);
448 61           sv_catpvn(wrapped, "/", 1);
449 61           RX_WRAPPED(rx) = savepvn(SvPVX(wrapped), SvCUR(wrapped));
450 61           RX_WRAPLEN(rx) = SvCUR(wrapped);
451              
452 61           re->pprivate = pattern;
453 61           SvREFCNT_inc(pattern);
454              
455             /* return the regexp */
456 61           return rx;
457             }
458              
459             #if PERL_VERSION < 20
460             I32
461             Lua_exec(pTHX_ REGEXP * const rx, char *stringarg, char *strend,
462             char *strbeg, I32 minend, SV * sv,
463             void *data, U32 flags)
464             #else
465             I32
466 84           Lua_exec(pTHX_ REGEXP * const rx, char *stringarg, char *strend,
467             char *strbeg, SSize_t minend, SV * sv,
468             void *data, U32 flags)
469             #endif
470             {
471 84           regexp * re = RegSV(rx);
472             STRLEN plen;
473 84           const char *pat = SvPV((SV*)re->pprivate, plen);
474             MatchState ms;
475 84           const char *s1 = stringarg;
476              
477 84           int anchor = (*pat == '^');
478 84 100         if (anchor) {
479 1           pat++;
480 1           plen--;
481             }
482              
483             #ifdef DEBUG
484             warn("Lua_exec |%s|%s|\n", stringarg, pat);
485             #endif
486              
487 84           ms.matchdepth = MAXCCALLS;
488 84           ms.src_init = strbeg;
489 84           ms.src_end = strend;
490 84           ms.p_end = pat + plen;
491              
492             do {
493             const char *res;
494 151           ms.level = 0;
495 151           res = match(&ms, s1, pat);
496 149 100         if (res != NULL) {
497             unsigned i;
498              
499 65           re->subbeg = strbeg;
500 65           re->sublen = strend - strbeg;
501              
502 65           re->nparens = re->lastparen = re->lastcloseparen = ms.level;
503 65           Newxz(re->offs, ms.level + 1, regexp_paren_pair);
504              
505 65           re->offs[0].start = s1 - ms.src_init;
506 65           re->offs[0].end = res - ms.src_init;
507              
508             #ifdef DEBUG
509             warn("match (%d) [%d-%d]\n", ms.level, re->offs[0].start, re->offs[0].end);
510             #endif
511              
512 104 100         for (i = 0; i < ms.level; i++) {
513 40           ptrdiff_t l = ms.capture[i].len;
514 40 100         if (l == CAP_UNFINISHED)
515 1           luaL_error(ms.L, "unfinished capture");
516 39 100         if (l == CAP_POSITION)
517 1           re->offs[i+1].start = re->offs[i+1].end = ms.capture[i].init - ms.src_init;
518             else {
519 38           re->offs[i+1].start = ms.capture[i].init - ms.src_init;
520 38           re->offs[i+1].end = re->offs[i+1].start + l;
521             }
522             #ifdef DEBUG
523             warn("capt %d [%d-%d]\n", i+1, re->offs[i+1].start, re->offs[i+1].end);
524             #endif
525             }
526              
527 64           return 1;
528             }
529 84 100         } while (s1++ < ms.src_end && !anchor);
    100          
530              
531             /* Matching failed */
532             #ifdef DEBUG
533             warn("not match\n");
534             #endif
535 17           return 0;
536             }
537              
538             #if PERL_VERSION < 20
539             char *
540             Lua_intuit(pTHX_ REGEXP * const rx, SV * sv, char *strpos,
541             char *strend, U32 flags, re_scream_pos_data *data)
542             {
543             PERL_UNUSED_ARG(rx);
544             PERL_UNUSED_ARG(sv);
545             PERL_UNUSED_ARG(strpos);
546             PERL_UNUSED_ARG(strend);
547             PERL_UNUSED_ARG(flags);
548             PERL_UNUSED_ARG(data);
549             return NULL;
550             }
551             #else
552             char *
553 0           Lua_intuit(pTHX_ REGEXP * const rx, SV * sv, const char * const strbeg, char *strpos,
554             char *strend, const U32 flags, re_scream_pos_data *data)
555             {
556             PERL_UNUSED_ARG(rx);
557             PERL_UNUSED_ARG(sv);
558             PERL_UNUSED_ARG(strbeg);
559             PERL_UNUSED_ARG(strpos);
560             PERL_UNUSED_ARG(strend);
561             PERL_UNUSED_ARG(flags);
562             PERL_UNUSED_ARG(data);
563 0           return NULL;
564             }
565             #endif
566              
567             SV *
568 0           Lua_checkstr(pTHX_ REGEXP * const rx)
569             {
570             PERL_UNUSED_ARG(rx);
571 0           return NULL;
572             }
573              
574             void
575 57           Lua_free(pTHX_ REGEXP * const rx)
576             {
577 57           SvREFCNT_dec(RegSV(rx)->pprivate);
578 57           }
579              
580             void *
581 0           Lua_dupe(pTHX_ REGEXP * const rx, CLONE_PARAMS *param)
582             {
583             PERL_UNUSED_ARG(param);
584 0           return RegSV(rx)->pprivate;
585             }
586              
587             SV *
588 1           Lua_package(pTHX_ REGEXP * const rx)
589             {
590             PERL_UNUSED_ARG(rx);
591 1           return newSVpvs("re::engine::Lua");
592             }
593              
594             /* end of lua_engine methods */
595              
596             /* XS glue */
597              
598             MODULE = re::engine::Lua PACKAGE = re::engine::Lua
599             PROTOTYPES: ENABLE
600              
601             void
602             ENGINE(...)
603             PROTOTYPE:
604             PPCODE:
605 19 50         XPUSHs(sv_2mortal(newSViv(PTR2IV(&lua_engine))));
606