123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456 |
- #include <limits.h>
- #include <string.h>
- #include "lua.h"
- #include "lauxlib.h"
- #include "lpcap.h"
- #include "lptypes.h"
- #include "lpvm.h"
- #include "lpprint.h"
- /* initial size for call/backtrack stack */
- #if !defined(INITBACK)
- #define INITBACK MAXBACK
- #endif
- #define getoffset(p) (((p) + 1)->offset)
- static const Instruction giveup = {{IGiveup, 0, {0}}};
- int charinset (const Instruction *i, const byte *buff, uint c) {
- c -= i->i.aux2.set.offset;
- if (c >= ((uint)i->i.aux2.set.size /* size in instructions... */
- * (uint)sizeof(Instruction) /* in bytes... */
- * 8u)) /* in bits */
- return i->i.aux1; /* out of range; return default value */
- return testchar(buff, c);
- }
- /*
- ** Decode one UTF-8 sequence, returning NULL if byte sequence is invalid.
- */
- static const char *utf8_decode (const char *o, int *val) {
- static const uint limits[] = {0xFF, 0x7F, 0x7FF, 0xFFFFu};
- const unsigned char *s = (const unsigned char *)o;
- uint c = s[0]; /* first byte */
- uint res = 0; /* final result */
- if (c < 0x80) /* ascii? */
- res = c;
- else {
- int count = 0; /* to count number of continuation bytes */
- while (c & 0x40) { /* still have continuation bytes? */
- int cc = s[++count]; /* read next byte */
- if ((cc & 0xC0) != 0x80) /* not a continuation byte? */
- return NULL; /* invalid byte sequence */
- res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
- c <<= 1; /* to test next bit */
- }
- res |= (c & 0x7F) << (count * 5); /* add first byte */
- if (count > 3 || res > 0x10FFFFu || res <= limits[count])
- return NULL; /* invalid byte sequence */
- s += count; /* skip continuation bytes read */
- }
- *val = res;
- return (const char *)s + 1; /* +1 to include first byte */
- }
- /*
- ** {======================================================
- ** Virtual Machine
- ** =======================================================
- */
- typedef struct Stack {
- const char *s; /* saved position (or NULL for calls) */
- const Instruction *p; /* next instruction */
- int caplevel;
- } Stack;
- #define getstackbase(L, ptop) ((Stack *)lua_touserdata(L, stackidx(ptop)))
- /*
- ** Ensures the size of array 'capture' (with size '*capsize' and
- ** 'captop' elements being used) is enough to accomodate 'n' extra
- ** elements plus one. (Because several opcodes add stuff to the capture
- ** array, it is simpler to ensure the array always has at least one free
- ** slot upfront and check its size later.)
- */
- /* new size in number of elements cannot overflow integers, and new
- size in bytes cannot overflow size_t. */
- #define MAXNEWSIZE \
- (((size_t)INT_MAX) <= (~(size_t)0 / sizeof(Capture)) ? \
- ((size_t)INT_MAX) : (~(size_t)0 / sizeof(Capture)))
- static Capture *growcap (lua_State *L, Capture *capture, int *capsize,
- int captop, int n, int ptop) {
- if (*capsize - captop > n)
- return capture; /* no need to grow array */
- else { /* must grow */
- Capture *newc;
- uint newsize = captop + n + 1; /* minimum size needed */
- if (newsize < (MAXNEWSIZE / 3) * 2)
- newsize += newsize / 2; /* 1.5 that size, if not too big */
- else if (newsize < (MAXNEWSIZE / 9) * 8)
- newsize += newsize / 8; /* else, try 9/8 that size */
- else
- luaL_error(L, "too many captures");
- newc = (Capture *)lua_newuserdata(L, newsize * sizeof(Capture));
- memcpy(newc, capture, captop * sizeof(Capture));
- *capsize = newsize;
- lua_replace(L, caplistidx(ptop));
- return newc;
- }
- }
- /*
- ** Double the size of the stack
- */
- static Stack *doublestack (lua_State *L, Stack **stacklimit, int ptop) {
- Stack *stack = getstackbase(L, ptop);
- Stack *newstack;
- int n = *stacklimit - stack; /* current stack size */
- int max, newn;
- lua_getfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX);
- max = lua_tointeger(L, -1); /* maximum allowed size */
- lua_pop(L, 1);
- if (n >= max) /* already at maximum size? */
- luaL_error(L, "backtrack stack overflow (current limit is %d)", max);
- newn = 2 * n; /* new size */
- if (newn > max) newn = max;
- newstack = (Stack *)lua_newuserdata(L, newn * sizeof(Stack));
- memcpy(newstack, stack, n * sizeof(Stack));
- lua_replace(L, stackidx(ptop));
- *stacklimit = newstack + newn;
- return newstack + n; /* return next position */
- }
- /*
- ** Interpret the result of a dynamic capture: false -> fail;
- ** true -> keep current position; number -> next position.
- ** Return new subject position. 'fr' is stack index where
- ** is the result; 'curr' is current subject position; 'limit'
- ** is subject's size.
- */
- static int resdyncaptures (lua_State *L, int fr, int curr, int limit) {
- lua_Integer res;
- if (!lua_toboolean(L, fr)) { /* false value? */
- lua_settop(L, fr - 1); /* remove results */
- return -1; /* and fail */
- }
- else if (lua_isboolean(L, fr)) /* true? */
- res = curr; /* keep current position */
- else {
- res = lua_tointeger(L, fr) - 1; /* new position */
- if (res < curr || res > limit)
- luaL_error(L, "invalid position returned by match-time capture");
- }
- lua_remove(L, fr); /* remove first result (offset) */
- return res;
- }
- /*
- ** Add capture values returned by a dynamic capture to the list
- ** 'capture', nested inside a group. 'fd' indexes the first capture
- ** value, 'n' is the number of values (at least 1). The open group
- ** capture is already in 'capture', before the place for the new entries.
- */
- static void adddyncaptures (Index_t index, Capture *capture, int n, int fd) {
- int i;
- assert(capture[-1].kind == Cgroup && capture[-1].siz == 0);
- capture[-1].idx = 0; /* make group capture an anonymous group */
- for (i = 0; i < n; i++) { /* add runtime captures */
- capture[i].kind = Cruntime;
- capture[i].siz = 1; /* mark it as closed */
- capture[i].idx = fd + i; /* stack index of capture value */
- capture[i].index = index;
- }
- capture[n].kind = Cclose; /* close group */
- capture[n].siz = 1;
- capture[n].index = index;
- }
- /*
- ** Remove dynamic captures from the Lua stack (called in case of failure)
- */
- static int removedyncap (lua_State *L, Capture *capture,
- int level, int last) {
- int id = finddyncap(capture + level, capture + last); /* index of 1st cap. */
- int top = lua_gettop(L);
- if (id == 0) return 0; /* no dynamic captures? */
- lua_settop(L, id - 1); /* remove captures */
- return top - id + 1; /* number of values removed */
- }
- /*
- ** Find the corresponding 'open' capture before 'cap', when that capture
- ** can become a full capture. If a full capture c1 is followed by an
- ** empty capture c2, there is no way to know whether c2 is inside
- ** c1. So, full captures can enclose only captures that start *before*
- ** its end.
- */
- static Capture *findopen (Capture *cap, Index_t currindex) {
- int i;
- cap--; /* check last capture */
- /* Must it be inside current one, but starts where current one ends? */
- if (!isopencap(cap) && cap->index == currindex)
- return NULL; /* current one cannot be a full capture */
- /* else, look for an 'open' capture */
- for (i = 0; i < MAXLOP; i++, cap--) {
- if (currindex - cap->index >= UCHAR_MAX)
- return NULL; /* capture too long for a full capture */
- else if (isopencap(cap)) /* open capture? */
- return cap; /* that's the one to be closed */
- else if (cap->kind == Cclose)
- return NULL; /* a full capture should not nest a non-full one */
- }
- return NULL; /* not found within allowed search limit */
- }
- /*
- ** Opcode interpreter
- */
- const char *match (lua_State *L, const char *o, const char *s, const char *e,
- Instruction *op, Capture *capture, int ptop) {
- Stack stackbase[INITBACK];
- Stack *stacklimit = stackbase + INITBACK;
- Stack *stack = stackbase; /* point to first empty slot in stack */
- int capsize = INITCAPSIZE;
- int captop = 0; /* point to first empty slot in captures */
- int ndyncap = 0; /* number of dynamic captures (in Lua stack) */
- const Instruction *p = op; /* current instruction */
- stack->p = &giveup; stack->s = s; stack->caplevel = 0; stack++;
- lua_pushlightuserdata(L, stackbase);
- for (;;) {
- #if defined(DEBUG)
- printf("-------------------------------------\n");
- printcaplist(capture, capture + captop);
- printf("s: |%s| stck:%d, dyncaps:%d, caps:%d ",
- s, (int)(stack - getstackbase(L, ptop)), ndyncap, captop);
- printinst(op, p);
- #endif
- assert(stackidx(ptop) + ndyncap == lua_gettop(L) && ndyncap <= captop);
- switch ((Opcode)p->i.code) {
- case IEnd: {
- assert(stack == getstackbase(L, ptop) + 1);
- capture[captop].kind = Cclose;
- capture[captop].index = MAXINDT;
- return s;
- }
- case IGiveup: {
- assert(stack == getstackbase(L, ptop));
- return NULL;
- }
- case IRet: {
- assert(stack > getstackbase(L, ptop) && (stack - 1)->s == NULL);
- p = (--stack)->p;
- continue;
- }
- case IAny: {
- if (s < e) { p++; s++; }
- else goto fail;
- continue;
- }
- case IUTFR: {
- int codepoint;
- if (s >= e)
- goto fail;
- s = utf8_decode (s, &codepoint);
- if (s && p[1].offset <= codepoint && codepoint <= utf_to(p))
- p += 2;
- else
- goto fail;
- continue;
- }
- case ITestAny: {
- if (s < e) p += 2;
- else p += getoffset(p);
- continue;
- }
- case IChar: {
- if ((byte)*s == p->i.aux1 && s < e) { p++; s++; }
- else goto fail;
- continue;
- }
- case ITestChar: {
- if ((byte)*s == p->i.aux1 && s < e) p += 2;
- else p += getoffset(p);
- continue;
- }
- case ISet: {
- uint c = (byte)*s;
- if (charinset(p, (p+1)->buff, c) && s < e)
- { p += 1 + p->i.aux2.set.size; s++; }
- else goto fail;
- continue;
- }
- case ITestSet: {
- uint c = (byte)*s;
- if (charinset(p, (p + 2)->buff, c) && s < e)
- p += 2 + p->i.aux2.set.size;
- else p += getoffset(p);
- continue;
- }
- case IBehind: {
- int n = p->i.aux1;
- if (n > s - o) goto fail;
- s -= n; p++;
- continue;
- }
- case ISpan: {
- for (; s < e; s++) {
- uint c = (byte)*s;
- if (!charinset(p, (p+1)->buff, c)) break;
- }
- p += 1 + p->i.aux2.set.size;
- continue;
- }
- case IJmp: {
- p += getoffset(p);
- continue;
- }
- case IChoice: {
- if (stack == stacklimit)
- stack = doublestack(L, &stacklimit, ptop);
- stack->p = p + getoffset(p);
- stack->s = s;
- stack->caplevel = captop;
- stack++;
- p += 2;
- continue;
- }
- case ICall: {
- if (stack == stacklimit)
- stack = doublestack(L, &stacklimit, ptop);
- stack->s = NULL;
- stack->p = p + 2; /* save return address */
- stack++;
- p += getoffset(p);
- continue;
- }
- case ICommit: {
- assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
- stack--;
- p += getoffset(p);
- continue;
- }
- case IPartialCommit: {
- assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
- (stack - 1)->s = s;
- (stack - 1)->caplevel = captop;
- p += getoffset(p);
- continue;
- }
- case IBackCommit: {
- assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
- s = (--stack)->s;
- if (ndyncap > 0) /* are there matchtime captures? */
- ndyncap -= removedyncap(L, capture, stack->caplevel, captop);
- captop = stack->caplevel;
- p += getoffset(p);
- continue;
- }
- case IFailTwice:
- assert(stack > getstackbase(L, ptop));
- stack--;
- /* FALLTHROUGH */
- case IFail:
- fail: { /* pattern failed: try to backtrack */
- do { /* remove pending calls */
- assert(stack > getstackbase(L, ptop));
- s = (--stack)->s;
- } while (s == NULL);
- if (ndyncap > 0) /* is there matchtime captures? */
- ndyncap -= removedyncap(L, capture, stack->caplevel, captop);
- captop = stack->caplevel;
- p = stack->p;
- #if defined(DEBUG)
- printf("**FAIL**\n");
- #endif
- continue;
- }
- case ICloseRunTime: {
- CapState cs;
- int rem, res, n;
- int fr = lua_gettop(L) + 1; /* stack index of first result */
- cs.reclevel = 0; cs.L = L;
- cs.s = o; cs.ocap = capture; cs.ptop = ptop;
- n = runtimecap(&cs, capture + captop, s, &rem); /* call function */
- captop -= n; /* remove nested captures */
- ndyncap -= rem; /* update number of dynamic captures */
- fr -= rem; /* 'rem' items were popped from Lua stack */
- res = resdyncaptures(L, fr, s - o, e - o); /* get result */
- if (res == -1) /* fail? */
- goto fail;
- s = o + res; /* else update current position */
- n = lua_gettop(L) - fr + 1; /* number of new captures */
- ndyncap += n; /* update number of dynamic captures */
- if (n == 0) /* no new captures? */
- captop--; /* remove open group */
- else { /* new captures; keep original open group */
- if (fr + n >= SHRT_MAX)
- luaL_error(L, "too many results in match-time capture");
- /* add new captures + close group to 'capture' list */
- capture = growcap(L, capture, &capsize, captop, n + 1, ptop);
- adddyncaptures(s - o, capture + captop, n, fr);
- captop += n + 1; /* new captures + close group */
- }
- p++;
- continue;
- }
- case ICloseCapture: {
- Capture *open = findopen(capture + captop, s - o);
- assert(captop > 0);
- if (open) { /* if possible, turn capture into a full capture */
- open->siz = (s - o) - open->index + 1;
- p++;
- continue;
- }
- else { /* must create a close capture */
- capture[captop].siz = 1; /* mark entry as closed */
- capture[captop].index = s - o;
- goto pushcapture;
- }
- }
- case IOpenCapture:
- capture[captop].siz = 0; /* mark entry as open */
- capture[captop].index = s - o;
- goto pushcapture;
- case IFullCapture:
- capture[captop].siz = getoff(p) + 1; /* save capture size */
- capture[captop].index = s - o - getoff(p);
- /* goto pushcapture; */
- pushcapture: {
- capture[captop].idx = p->i.aux2.key;
- capture[captop].kind = getkind(p);
- captop++;
- capture = growcap(L, capture, &capsize, captop, 0, ptop);
- p++;
- continue;
- }
- default: assert(0); return NULL;
- }
- }
- }
- /* }====================================================== */
|