File Coverage

Lua.xs
Criterion Covered Total %
statement 199 242 82.2
branch 105 144 72.9
condition n/a
subroutine n/a
pod n/a
total 304 386 78.7


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 54           static int capture_to_close (MatchState *ms) {
79 54           int level = ms->level;
80 55 50         for (level--; level>=0; level--)
81 55 100         if (ms->capture[level].len == CAP_UNFINISHED) return level;
82 0           return luaL_error(ms->L, "invalid pattern capture");
83             }
84              
85              
86 224           static const char *classend (MatchState *ms, const char *p) {
87 224           switch (*p++) {
88             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             case '[': {
94 30 100         if (*p == '^') p++;
95             do { /* look for a ']' */
96 78 50         if (p == ms->p_end)
97 0           luaL_error(ms->L, "malformed pattern (missing ']')");
98 78 100         if (*(p++) == L_ESC && p < ms->p_end)
    50          
99 3           p++; /* skip escapes (e.g. '%]') */
100 78 100         } while (*p != ']');
101 30           return p+1;
102             }
103             default: {
104 180           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 228           static int singlematch (MatchState *ms, const char *s, const char *p,
154             const char *ep) {
155 228 100         if (s >= ms->src_end)
156 19           return 0;
157             else {
158 209           int c = uchar(*s);
159 209           switch (*p) {
160 69           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 101           default: return (uchar(*p) == c);
164             }
165             }
166             }
167              
168              
169 1           static const char *matchbalance (MatchState *ms, const char *s,
170             const char *p) {
171 1 50         if (p >= ms->p_end - 1)
172 0           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             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 0           }
214             }
215              
216              
217 67           static const char *start_capture (MatchState *ms, const char *s,
218             const char *p, int what) {
219             const char *res;
220 67           int level = ms->level;
221 67 50         if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures");
222 67           ms->capture[level].init = s;
223 67           ms->capture[level].len = what;
224 67           ms->level = level+1;
225 67 100         if ((res=match(ms, s, p)) == NULL) /* match failed? */
226 29           ms->level--; /* undo capture */
227 67           return res;
228             }
229              
230              
231 54           static const char *end_capture (MatchState *ms, const char *s,
232             const char *p) {
233 54           int l = capture_to_close(ms);
234             const char *res;
235 54           ms->capture[l].len = s - ms->capture[l].init; /* close capture */
236 54 100         if ((res = match(ms, s, p)) == NULL) /* match failed? */
237 17           ms->capture[l].len = CAP_UNFINISHED; /* undo capture */
238 54           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 &&
    100          
247 2           memcmp(ms->capture[l].init, s, len) == 0)
248 1           return s+len;
249 1           else return NULL;
250             }
251              
252              
253 274           static const char *match (MatchState *ms, const char *s, const char *p) {
254 274 50         if (ms->matchdepth-- == 0)
255 0           luaL_error(ms->L, "pattern too complex");
256             init: /* using goto's to optimize tail recursion */
257 411 100         if (p != ms->p_end) { /* end of pattern? */
258 348           switch (*p) {
259             case '(': { /* start capture */
260 67 100         if (*(p + 1) == ')') /* position capture? */
261 1           s = start_capture(ms, s, p + 2, CAP_POSITION);
262             else
263 66           s = start_capture(ms, s, p + 1, CAP_UNFINISHED);
264 67           break;
265             }
266             case ')': { /* end capture */
267 54           s = end_capture(ms, s, p + 1);
268 54           break;
269             }
270             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             case L_ESC: { /* escaped sequences not in the format class[*+?-]? */
277 29           switch (*(p + 1)) {
278             case 'b': { /* balanced string? */
279 1           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             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             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             default: dflt: { /* pattern class plus optional suffix */
313 212           const char *ep = classend(ms, p); /* points to optional suffix */
314             /* does not match at least once? */
315 212 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 136           switch (*ep) { /* handle optional suffix */
324             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             case '+': /* 1 or more repetitions */
334 4           s++; /* 1 match already done */
335             /* FALLTHROUGH */
336             case '*': /* 0 or more repetitions */
337 5           s = max_expand(ms, s, p, ep);
338 5           break;
339             case '-': /* 0 or more repetitions (minimum) */
340 0           s = min_expand(ms, s, p, ep);
341 0           break;
342             default: /* no suffix */
343 130           s++; p = ep; goto init; /* return match(ms, s + 1, ep); */
344             }
345             }
346 79           break;
347             }
348             }
349             }
350 274           ms->matchdepth++;
351 274           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 0           static int luaL_error(lua_State * st, const char * msg)
374             {
375 0           croak(msg);
376             return 0;
377             }
378              
379             /* lua_engine methods */
380              
381             REGEXP *
382 57           Lua_comp(pTHX_ SV * const pattern, U32 flags)
383             {
384             REGEXP *rx;
385             regexp *re;
386              
387             STRLEN plen;
388 57 50         char *exp = SvPV((SV*)pattern, plen);
389 57           U32 extflags = flags;
390              
391 57           SV * wrapped = newSVpvn("/", 1);
392 57           sv_2mortal(wrapped);
393              
394 57 50         if (flags & ~(RXf_SPLIT)) {
395 0           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 57 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 57 100         if (plen == 0)
411 1           extflags |= RXf_NULL;
412              
413             /* RXf_START_ONLY - Have C split on newlines */
414 56 100         else if (plen == 1 && exp[0] == '^')
    50          
415 0           extflags |= RXf_START_ONLY;
416              
417             /* RXf_WHITE - Have C split on whitespace */
418 56 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 57           rx = (REGEXP*) newSV_type(SVt_REGEXP);
431 57           re = RegSV(rx);
432              
433 57           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 57           re->extflags = extflags;
444 57           re->engine = &lua_engine;
445              
446             /* qr// stringification */
447 57           sv_catpvn(wrapped, exp, plen);
448 57           sv_catpvn(wrapped, "/", 1);
449 57           RX_WRAPPED(rx) = savepvn(SvPVX(wrapped), SvCUR(wrapped));
450 57           RX_WRAPLEN(rx) = SvCUR(wrapped);
451              
452 57           re->pprivate = pattern;
453 57           SvREFCNT_inc(pattern);
454              
455             /* return the regexp */
456 57           return rx;
457             }
458              
459             I32
460 80           Lua_exec(pTHX_ REGEXP * const rx, char *stringarg, char *strend,
461             char *strbeg, I32 minend, SV * sv,
462             void *data, U32 flags)
463             {
464 80           regexp * re = RegSV(rx);
465             STRLEN plen;
466 80 50         const char *pat = SvPV((SV*)re->pprivate, plen);
467             MatchState ms;
468 80           const char *s1 = stringarg;
469              
470 80           int anchor = (*pat == '^');
471 80 100         if (anchor) {
472 1           pat++;
473 1           plen--;
474             }
475              
476             #ifdef DEBUG
477             warn("Lua_exec |%s|%s|\n", stringarg, pat);
478             #endif
479              
480 80           ms.matchdepth = MAXCCALLS;
481 80           ms.src_init = strbeg;
482 80           ms.src_end = strend;
483 80           ms.p_end = pat + plen;
484              
485             do {
486             const char *res;
487 147           ms.level = 0;
488 147           res = match(&ms, s1, pat);
489 147 100         if (res != NULL) {
490             unsigned i;
491              
492 63           re->subbeg = strbeg;
493 63           re->sublen = strend - strbeg;
494              
495 63           re->nparens = re->lastparen = re->lastcloseparen = ms.level;
496 63 50         Newxz(re->offs, ms.level + 1, regexp_paren_pair);
497              
498 63           re->offs[0].start = s1 - ms.src_init;
499 63           re->offs[0].end = res - ms.src_init;
500              
501             #ifdef DEBUG
502             warn("match (%d) [%d-%d]\n", ms.level, re->offs[0].start, re->offs[0].end);
503             #endif
504              
505 101 100         for (i = 0; i < ms.level; i++) {
506 38           ptrdiff_t l = ms.capture[i].len;
507 38 50         if (l == CAP_UNFINISHED)
508 0           luaL_error(ms.L, "unfinished capture");
509 38 100         if (l == CAP_POSITION)
510 1           re->offs[i+1].start = re->offs[i+1].end = ms.capture[i].init - ms.src_init;
511             else {
512 37           re->offs[i+1].start = ms.capture[i].init - ms.src_init;
513 37           re->offs[i+1].end = re->offs[i+1].start + l;
514             }
515             #ifdef DEBUG
516             warn("capt %d [%d-%d]\n", i+1, re->offs[i+1].start, re->offs[i+1].end);
517             #endif
518             }
519              
520 63           return 1;
521             }
522 84 100         } while (s1++ < ms.src_end && !anchor);
    100          
523              
524             /* Matching failed */
525             #ifdef DEBUG
526             warn("not match\n");
527             #endif
528 80           return 0;
529             }
530              
531             char *
532 0           Lua_intuit(pTHX_ REGEXP * const rx, SV * sv, char *strpos,
533             char *strend, U32 flags, re_scream_pos_data *data)
534             {
535             PERL_UNUSED_ARG(rx);
536             PERL_UNUSED_ARG(sv);
537             PERL_UNUSED_ARG(strpos);
538             PERL_UNUSED_ARG(strend);
539             PERL_UNUSED_ARG(flags);
540             PERL_UNUSED_ARG(data);
541 0           return NULL;
542             }
543              
544             SV *
545 0           Lua_checkstr(pTHX_ REGEXP * const rx)
546             {
547             PERL_UNUSED_ARG(rx);
548 0           return NULL;
549             }
550              
551             void
552 57           Lua_free(pTHX_ REGEXP * const rx)
553             {
554 57           SvREFCNT_dec(RegSV(rx)->pprivate);
555 57           }
556              
557             void *
558 0           Lua_dupe(pTHX_ REGEXP * const rx, CLONE_PARAMS *param)
559             {
560             PERL_UNUSED_ARG(param);
561 0           return RegSV(rx)->pprivate;
562             }
563              
564             SV *
565 1           Lua_package(pTHX_ REGEXP * const rx)
566             {
567             PERL_UNUSED_ARG(rx);
568 1           return newSVpvs("re::engine::Lua");
569             }
570              
571             /* end of lua_engine methods */
572              
573             /* XS glue */
574              
575             MODULE = re::engine::Lua PACKAGE = re::engine::Lua
576             PROTOTYPES: ENABLE
577              
578             void
579             ENGINE(...)
580             PROTOTYPE:
581             PPCODE:
582 17 50         XPUSHs(sv_2mortal(newSViv(PTR2IV(&lua_engine))));
583