lpeg (empty) → 1.0.0
raw patch · 19 files changed
+4295/−0 lines, 19 filesdep +basedep +lpegdep +lua
Dependencies added: base, lpeg, lua, tasty, tasty-hunit
Files
- CHANGELOG.md +11/−0
- LICENSE +21/−0
- README.md +19/−0
- cbits/lpeg-1.0.2/lpcap.c +555/−0
- cbits/lpeg-1.0.2/lpcap.h +57/−0
- cbits/lpeg-1.0.2/lpcode.c +1014/−0
- cbits/lpeg-1.0.2/lpcode.h +40/−0
- cbits/lpeg-1.0.2/lpprint.c +244/−0
- cbits/lpeg-1.0.2/lpprint.h +36/−0
- cbits/lpeg-1.0.2/lptree.c +1305/−0
- cbits/lpeg-1.0.2/lptree.h +82/−0
- cbits/lpeg-1.0.2/lptypes.h +145/−0
- cbits/lpeg-1.0.2/lpvm.c +374/−0
- cbits/lpeg-1.0.2/lpvm.h +58/−0
- cbits/placeholder.c +12/−0
- lpeg.cabal +89/−0
- src/Lua/LPeg.hs +42/−0
- substitute/Lua/LPeg.hs +46/−0
- test/test-lpeg.hs +145/−0
+ CHANGELOG.md view
@@ -0,0 +1,11 @@+# Changelog++`lua` uses [PVP Versioning][1].++## lpeg 1.0.0++Release pending.++- Initially created.++[1]: https://pvp.haskell.org
+ LICENSE view
@@ -0,0 +1,21 @@+Copyright © 2007-2019 Lua.org, PUC-Rio.+Copyright © 2021 Albert Krewinkel++Permission is hereby granted, free of charge, to any person obtaining a+copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ README.md view
@@ -0,0 +1,19 @@+# lpeg – Parsing Expression Grammars For Lua++[![GitHub CI][CI badge]](https://github.com/hslua/hslua/actions)+[![Hackage][Hackage badge]](https://hackage.haskell.org/package/lpeg)+[![Stackage Lts][Stackage Lts badge]](http://stackage.org/lts/package/lpeg)+[![Stackage Nightly][Stackage Nightly badge]](http://stackage.org/nightly/package/lpeg)+[![MIT license][License badge]](LICENSE)++[CI badge]: https://github.com/hslua/hslua/workflows/CI/badge.svg+[Hackage badge]: https://img.shields.io/hackage/v/lpeg.svg?logo=haskell+[Stackage Lts badge]: http://stackage.org/package/lpeg/badge/lts+[Stackage Nightly badge]: http://stackage.org/package/lpeg/badge/nightly+[License badge]: https://img.shields.io/badge/license-MIT-blue.svg++This package contains the C sources of LPeg, as well as some tiny+Haskell helper to load the package.++See also the official [LPeg+homepage](http://www.inf.puc-rio.br/~roberto/lpeg/)
+ cbits/lpeg-1.0.2/lpcap.c view
@@ -0,0 +1,555 @@+/*+** $Id: lpcap.c $+** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)+*/++#include "lua.h"+#include "lauxlib.h"++#include "lpcap.h"+#include "lptypes.h"+++#define captype(cap) ((cap)->kind)++#define isclosecap(cap) (captype(cap) == Cclose)++#define closeaddr(c) ((c)->s + (c)->siz - 1)++#define isfullcap(cap) ((cap)->siz != 0)++#define getfromktable(cs,v) lua_rawgeti((cs)->L, ktableidx((cs)->ptop), v)++#define pushluaval(cs) getfromktable(cs, (cs)->cap->idx)++++/*+** Put at the cache for Lua values the value indexed by 'v' in ktable+** of the running pattern (if it is not there yet); returns its index.+*/+static int updatecache (CapState *cs, int v) {+ int idx = cs->ptop + 1; /* stack index of cache for Lua values */+ if (v != cs->valuecached) { /* not there? */+ getfromktable(cs, v); /* get value from 'ktable' */+ lua_replace(cs->L, idx); /* put it at reserved stack position */+ cs->valuecached = v; /* keep track of what is there */+ }+ return idx;+}+++static int pushcapture (CapState *cs);+++/*+** Goes back in a list of captures looking for an open capture+** corresponding to a close+*/+static Capture *findopen (Capture *cap) {+ int n = 0; /* number of closes waiting an open */+ for (;;) {+ cap--;+ if (isclosecap(cap)) n++; /* one more open to skip */+ else if (!isfullcap(cap))+ if (n-- == 0) return cap;+ }+}+++/*+** Go to the next capture+*/+static void nextcap (CapState *cs) {+ Capture *cap = cs->cap;+ if (!isfullcap(cap)) { /* not a single capture? */+ int n = 0; /* number of opens waiting a close */+ for (;;) { /* look for corresponding close */+ cap++;+ if (isclosecap(cap)) {+ if (n-- == 0) break;+ }+ else if (!isfullcap(cap)) n++;+ }+ }+ cs->cap = cap + 1; /* + 1 to skip last close (or entire single capture) */+}+++/*+** Push on the Lua stack all values generated by nested captures inside+** the current capture. Returns number of values pushed. 'addextra'+** makes it push the entire match after all captured values. The+** entire match is pushed also if there are no other nested values,+** so the function never returns zero.+*/+static int pushnestedvalues (CapState *cs, int addextra) {+ Capture *co = cs->cap;+ if (isfullcap(cs->cap++)) { /* no nested captures? */+ lua_pushlstring(cs->L, co->s, co->siz - 1); /* push whole match */+ return 1; /* that is it */+ }+ else {+ int n = 0;+ while (!isclosecap(cs->cap)) /* repeat for all nested patterns */+ n += pushcapture(cs);+ if (addextra || n == 0) { /* need extra? */+ lua_pushlstring(cs->L, co->s, cs->cap->s - co->s); /* push whole match */+ n++;+ }+ cs->cap++; /* skip close entry */+ return n;+ }+}+++/*+** Push only the first value generated by nested captures+*/+static void pushonenestedvalue (CapState *cs) {+ int n = pushnestedvalues(cs, 0);+ if (n > 1)+ lua_pop(cs->L, n - 1); /* pop extra values */+}+++/*+** Try to find a named group capture with the name given at the top of+** the stack; goes backward from 'cap'.+*/+static Capture *findback (CapState *cs, Capture *cap) {+ lua_State *L = cs->L;+ while (cap-- > cs->ocap) { /* repeat until end of list */+ if (isclosecap(cap))+ cap = findopen(cap); /* skip nested captures */+ else if (!isfullcap(cap))+ continue; /* opening an enclosing capture: skip and get previous */+ if (captype(cap) == Cgroup) {+ getfromktable(cs, cap->idx); /* get group name */+ if (lp_equal(L, -2, -1)) { /* right group? */+ lua_pop(L, 2); /* remove reference name and group name */+ return cap;+ }+ else lua_pop(L, 1); /* remove group name */+ }+ }+ luaL_error(L, "back reference '%s' not found", lua_tostring(L, -1));+ return NULL; /* to avoid warnings */+}+++/*+** Back-reference capture. Return number of values pushed.+*/+static int backrefcap (CapState *cs) {+ int n;+ Capture *curr = cs->cap;+ pushluaval(cs); /* reference name */+ cs->cap = findback(cs, curr); /* find corresponding group */+ n = pushnestedvalues(cs, 0); /* push group's values */+ cs->cap = curr + 1;+ return n;+}+++/*+** Table capture: creates a new table and populates it with nested+** captures.+*/+static int tablecap (CapState *cs) {+ lua_State *L = cs->L;+ int n = 0;+ lua_newtable(L);+ if (isfullcap(cs->cap++))+ return 1; /* table is empty */+ while (!isclosecap(cs->cap)) {+ if (captype(cs->cap) == Cgroup && cs->cap->idx != 0) { /* named group? */+ pushluaval(cs); /* push group name */+ pushonenestedvalue(cs);+ lua_settable(L, -3);+ }+ else { /* not a named group */+ int i;+ int k = pushcapture(cs);+ for (i = k; i > 0; i--) /* store all values into table */+ lua_rawseti(L, -(i + 1), n + i);+ n += k;+ }+ }+ cs->cap++; /* skip close entry */+ return 1; /* number of values pushed (only the table) */+}+++/*+** Table-query capture+*/+static int querycap (CapState *cs) {+ int idx = cs->cap->idx;+ pushonenestedvalue(cs); /* get nested capture */+ lua_gettable(cs->L, updatecache(cs, idx)); /* query cap. value at table */+ if (!lua_isnil(cs->L, -1))+ return 1;+ else { /* no value */+ lua_pop(cs->L, 1); /* remove nil */+ return 0;+ }+}+++/*+** Fold capture+*/+static int foldcap (CapState *cs) {+ int n;+ lua_State *L = cs->L;+ int idx = cs->cap->idx;+ if (isfullcap(cs->cap++) || /* no nested captures? */+ isclosecap(cs->cap) || /* no nested captures (large subject)? */+ (n = pushcapture(cs)) == 0) /* nested captures with no values? */+ return luaL_error(L, "no initial value for fold capture");+ if (n > 1)+ lua_pop(L, n - 1); /* leave only one result for accumulator */+ while (!isclosecap(cs->cap)) {+ lua_pushvalue(L, updatecache(cs, idx)); /* get folding function */+ lua_insert(L, -2); /* put it before accumulator */+ n = pushcapture(cs); /* get next capture's values */+ lua_call(L, n + 1, 1); /* call folding function */+ }+ cs->cap++; /* skip close entry */+ return 1; /* only accumulator left on the stack */+}+++/*+** Function capture+*/+static int functioncap (CapState *cs) {+ int n;+ int top = lua_gettop(cs->L);+ pushluaval(cs); /* push function */+ n = pushnestedvalues(cs, 0); /* push nested captures */+ lua_call(cs->L, n, LUA_MULTRET); /* call function */+ return lua_gettop(cs->L) - top; /* return function's results */+}+++/*+** Select capture+*/+static int numcap (CapState *cs) {+ int idx = cs->cap->idx; /* value to select */+ if (idx == 0) { /* no values? */+ nextcap(cs); /* skip entire capture */+ return 0; /* no value produced */+ }+ else {+ int n = pushnestedvalues(cs, 0);+ if (n < idx) /* invalid index? */+ return luaL_error(cs->L, "no capture '%d'", idx);+ else {+ lua_pushvalue(cs->L, -(n - idx + 1)); /* get selected capture */+ lua_replace(cs->L, -(n + 1)); /* put it in place of 1st capture */+ lua_pop(cs->L, n - 1); /* remove other captures */+ return 1;+ }+ }+}+++/*+** Return the stack index of the first runtime capture in the given+** list of captures (or zero if no runtime captures)+*/+int finddyncap (Capture *cap, Capture *last) {+ for (; cap < last; cap++) {+ if (cap->kind == Cruntime)+ return cap->idx; /* stack position of first capture */+ }+ return 0; /* no dynamic captures in this segment */+}+++/*+** Calls a runtime capture. Returns number of captures "removed" by the+** call, that is, those inside the group capture. Captures to be added+** are on the Lua stack.+*/+int runtimecap (CapState *cs, Capture *close, const char *s, int *rem) {+ int n, id;+ lua_State *L = cs->L;+ int otop = lua_gettop(L);+ Capture *open = findopen(close); /* get open group capture */+ assert(captype(open) == Cgroup);+ id = finddyncap(open, close); /* get first dynamic capture argument */+ close->kind = Cclose; /* closes the group */+ close->s = s;+ cs->cap = open; cs->valuecached = 0; /* prepare capture state */+ luaL_checkstack(L, 4, "too many runtime captures");+ pushluaval(cs); /* push function to be called */+ lua_pushvalue(L, SUBJIDX); /* push original subject */+ lua_pushinteger(L, s - cs->s + 1); /* push current position */+ n = pushnestedvalues(cs, 0); /* push nested captures */+ lua_call(L, n + 2, LUA_MULTRET); /* call dynamic function */+ if (id > 0) { /* are there old dynamic captures to be removed? */+ int i;+ for (i = id; i <= otop; i++)+ lua_remove(L, id); /* remove old dynamic captures */+ *rem = otop - id + 1; /* total number of dynamic captures removed */+ }+ else+ *rem = 0; /* no dynamic captures removed */+ return close - open - 1; /* number of captures to be removed */+}+++/*+** Auxiliary structure for substitution and string captures: keep+** information about nested captures for future use, avoiding to push+** string results into Lua+*/+typedef struct StrAux {+ int isstring; /* whether capture is a string */+ union {+ Capture *cp; /* if not a string, respective capture */+ struct { /* if it is a string... */+ const char *s; /* ... starts here */+ const char *e; /* ... ends here */+ } s;+ } u;+} StrAux;++#define MAXSTRCAPS 10++/*+** Collect values from current capture into array 'cps'. Current+** capture must be Cstring (first call) or Csimple (recursive calls).+** (In first call, fills %0 with whole match for Cstring.)+** Returns number of elements in the array that were filled.+*/+static int getstrcaps (CapState *cs, StrAux *cps, int n) {+ int k = n++;+ cps[k].isstring = 1; /* get string value */+ cps[k].u.s.s = cs->cap->s; /* starts here */+ if (!isfullcap(cs->cap++)) { /* nested captures? */+ while (!isclosecap(cs->cap)) { /* traverse them */+ if (n >= MAXSTRCAPS) /* too many captures? */+ nextcap(cs); /* skip extra captures (will not need them) */+ else if (captype(cs->cap) == Csimple) /* string? */+ n = getstrcaps(cs, cps, n); /* put info. into array */+ else {+ cps[n].isstring = 0; /* not a string */+ cps[n].u.cp = cs->cap; /* keep original capture */+ nextcap(cs);+ n++;+ }+ }+ cs->cap++; /* skip close */+ }+ cps[k].u.s.e = closeaddr(cs->cap - 1); /* ends here */+ return n;+}+++/*+** add next capture value (which should be a string) to buffer 'b'+*/+static int addonestring (luaL_Buffer *b, CapState *cs, const char *what);+++/*+** String capture: add result to buffer 'b' (instead of pushing+** it into the stack)+*/+static void stringcap (luaL_Buffer *b, CapState *cs) {+ StrAux cps[MAXSTRCAPS];+ int n;+ size_t len, i;+ const char *fmt; /* format string */+ fmt = lua_tolstring(cs->L, updatecache(cs, cs->cap->idx), &len);+ n = getstrcaps(cs, cps, 0) - 1; /* collect nested captures */+ for (i = 0; i < len; i++) { /* traverse them */+ if (fmt[i] != '%') /* not an escape? */+ luaL_addchar(b, fmt[i]); /* add it to buffer */+ else if (fmt[++i] < '0' || fmt[i] > '9') /* not followed by a digit? */+ luaL_addchar(b, fmt[i]); /* add to buffer */+ else {+ int l = fmt[i] - '0'; /* capture index */+ if (l > n)+ luaL_error(cs->L, "invalid capture index (%d)", l);+ else if (cps[l].isstring)+ luaL_addlstring(b, cps[l].u.s.s, cps[l].u.s.e - cps[l].u.s.s);+ else {+ Capture *curr = cs->cap;+ cs->cap = cps[l].u.cp; /* go back to evaluate that nested capture */+ if (!addonestring(b, cs, "capture"))+ luaL_error(cs->L, "no values in capture index %d", l);+ cs->cap = curr; /* continue from where it stopped */+ }+ }+ }+}+++/*+** Substitution capture: add result to buffer 'b'+*/+static void substcap (luaL_Buffer *b, CapState *cs) {+ const char *curr = cs->cap->s;+ if (isfullcap(cs->cap)) /* no nested captures? */+ luaL_addlstring(b, curr, cs->cap->siz - 1); /* keep original text */+ else {+ cs->cap++; /* skip open entry */+ while (!isclosecap(cs->cap)) { /* traverse nested captures */+ const char *next = cs->cap->s;+ luaL_addlstring(b, curr, next - curr); /* add text up to capture */+ if (addonestring(b, cs, "replacement"))+ curr = closeaddr(cs->cap - 1); /* continue after match */+ else /* no capture value */+ curr = next; /* keep original text in final result */+ }+ luaL_addlstring(b, curr, cs->cap->s - curr); /* add last piece of text */+ }+ cs->cap++; /* go to next capture */+}+++/*+** Evaluates a capture and adds its first value to buffer 'b'; returns+** whether there was a value+*/+static int addonestring (luaL_Buffer *b, CapState *cs, const char *what) {+ switch (captype(cs->cap)) {+ case Cstring:+ stringcap(b, cs); /* add capture directly to buffer */+ return 1;+ case Csubst:+ substcap(b, cs); /* add capture directly to buffer */+ return 1;+ default: {+ lua_State *L = cs->L;+ int n = pushcapture(cs);+ if (n > 0) {+ if (n > 1) lua_pop(L, n - 1); /* only one result */+ if (!lua_isstring(L, -1))+ luaL_error(L, "invalid %s value (a %s)", what, luaL_typename(L, -1));+ luaL_addvalue(b);+ }+ return n;+ }+ }+}+++#if !defined(MAXRECLEVEL)+#define MAXRECLEVEL 200+#endif+++/*+** Push all values of the current capture into the stack; returns+** number of values pushed+*/+static int pushcapture (CapState *cs) {+ lua_State *L = cs->L;+ int res;+ luaL_checkstack(L, 4, "too many captures");+ if (cs->reclevel++ > MAXRECLEVEL)+ return luaL_error(L, "subcapture nesting too deep");+ switch (captype(cs->cap)) {+ case Cposition: {+ lua_pushinteger(L, cs->cap->s - cs->s + 1);+ cs->cap++;+ res = 1;+ break;+ }+ case Cconst: {+ pushluaval(cs);+ cs->cap++;+ res = 1;+ break;+ }+ case Carg: {+ int arg = (cs->cap++)->idx;+ if (arg + FIXEDARGS > cs->ptop)+ return luaL_error(L, "reference to absent extra argument #%d", arg);+ lua_pushvalue(L, arg + FIXEDARGS);+ res = 1;+ break;+ }+ case Csimple: {+ int k = pushnestedvalues(cs, 1);+ lua_insert(L, -k); /* make whole match be first result */+ res = k;+ break;+ }+ case Cruntime: {+ lua_pushvalue(L, (cs->cap++)->idx); /* value is in the stack */+ res = 1;+ break;+ }+ case Cstring: {+ luaL_Buffer b;+ luaL_buffinit(L, &b);+ stringcap(&b, cs);+ luaL_pushresult(&b);+ res = 1;+ break;+ }+ case Csubst: {+ luaL_Buffer b;+ luaL_buffinit(L, &b);+ substcap(&b, cs);+ luaL_pushresult(&b);+ res = 1;+ break;+ }+ case Cgroup: {+ if (cs->cap->idx == 0) /* anonymous group? */+ res = pushnestedvalues(cs, 0); /* add all nested values */+ else { /* named group: add no values */+ nextcap(cs); /* skip capture */+ res = 0;+ }+ break;+ }+ case Cbackref: res = backrefcap(cs); break;+ case Ctable: res = tablecap(cs); break;+ case Cfunction: res = functioncap(cs); break;+ case Cnum: res = numcap(cs); break;+ case Cquery: res = querycap(cs); break;+ case Cfold: res = foldcap(cs); break;+ default: assert(0); res = 0;+ }+ cs->reclevel--;+ return res;+}+++/*+** Prepare a CapState structure and traverse the entire list of+** captures in the stack pushing its results. 's' is the subject+** string, 'r' is the final position of the match, and 'ptop' +** the index in the stack where some useful values were pushed.+** Returns the number of results pushed. (If the list produces no+** results, push the final position of the match.)+*/+int getcaptures (lua_State *L, const char *s, const char *r, int ptop) {+ Capture *capture = (Capture *)lua_touserdata(L, caplistidx(ptop));+ int n = 0;+ if (!isclosecap(capture)) { /* is there any capture? */+ CapState cs;+ cs.ocap = cs.cap = capture; cs.L = L; cs.reclevel = 0;+ cs.s = s; cs.valuecached = 0; cs.ptop = ptop;+ do { /* collect their values */+ n += pushcapture(&cs);+ } while (!isclosecap(cs.cap));+ }+ if (n == 0) { /* no capture values? */+ lua_pushinteger(L, r - s + 1); /* return only end position */+ n = 1;+ }+ return n;+}++
+ cbits/lpeg-1.0.2/lpcap.h view
@@ -0,0 +1,57 @@+/*+** $Id: lpcap.h $+*/++#if !defined(lpcap_h)+#define lpcap_h+++#include "lptypes.h"+++/* kinds of captures */+typedef enum CapKind {+ Cclose, /* not used in trees */+ Cposition,+ Cconst, /* ktable[key] is Lua constant */+ Cbackref, /* ktable[key] is "name" of group to get capture */+ Carg, /* 'key' is arg's number */+ Csimple, /* next node is pattern */+ Ctable, /* next node is pattern */+ Cfunction, /* ktable[key] is function; next node is pattern */+ Cquery, /* ktable[key] is table; next node is pattern */+ Cstring, /* ktable[key] is string; next node is pattern */+ Cnum, /* numbered capture; 'key' is number of value to return */+ Csubst, /* substitution capture; next node is pattern */+ Cfold, /* ktable[key] is function; next node is pattern */+ Cruntime, /* not used in trees (is uses another type for tree) */+ Cgroup /* ktable[key] is group's "name" */+} CapKind;+++typedef struct Capture {+ const char *s; /* subject position */+ unsigned short idx; /* extra info (group name, arg index, etc.) */+ byte kind; /* kind of capture */+ byte siz; /* size of full capture + 1 (0 = not a full capture) */+} Capture;+++typedef struct CapState {+ Capture *cap; /* current capture */+ Capture *ocap; /* (original) capture list */+ lua_State *L;+ int ptop; /* index of last argument to 'match' */+ const char *s; /* original string */+ int valuecached; /* value stored in cache slot */+ int reclevel; /* recursion level */+} CapState;+++int runtimecap (CapState *cs, Capture *close, const char *s, int *rem);+int getcaptures (lua_State *L, const char *s, const char *r, int ptop);+int finddyncap (Capture *cap, Capture *last);++#endif++
+ cbits/lpeg-1.0.2/lpcode.c view
@@ -0,0 +1,1014 @@+/*+** $Id: lpcode.c $+** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)+*/++#include <limits.h>+++#include "lua.h"+#include "lauxlib.h"++#include "lptypes.h"+#include "lpcode.h"+++/* signals a "no-instruction */+#define NOINST -1++++static const Charset fullset_ =+ {{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};++static const Charset *fullset = &fullset_;++/*+** {======================================================+** Analysis and some optimizations+** =======================================================+*/++/*+** Check whether a charset is empty (returns IFail), singleton (IChar),+** full (IAny), or none of those (ISet). When singleton, '*c' returns+** which character it is. (When generic set, the set was the input,+** so there is no need to return it.)+*/+static Opcode charsettype (const byte *cs, int *c) {+ int count = 0; /* number of characters in the set */+ int i;+ int candidate = -1; /* candidate position for the singleton char */+ for (i = 0; i < CHARSETSIZE; i++) { /* for each byte */+ int b = cs[i];+ if (b == 0) { /* is byte empty? */+ if (count > 1) /* was set neither empty nor singleton? */+ return ISet; /* neither full nor empty nor singleton */+ /* else set is still empty or singleton */+ }+ else if (b == 0xFF) { /* is byte full? */+ if (count < (i * BITSPERCHAR)) /* was set not full? */+ return ISet; /* neither full nor empty nor singleton */+ else count += BITSPERCHAR; /* set is still full */+ }+ else if ((b & (b - 1)) == 0) { /* has byte only one bit? */+ if (count > 0) /* was set not empty? */+ return ISet; /* neither full nor empty nor singleton */+ else { /* set has only one char till now; track it */+ count++;+ candidate = i;+ }+ }+ else return ISet; /* byte is neither empty, full, nor singleton */+ }+ switch (count) {+ case 0: return IFail; /* empty set */+ case 1: { /* singleton; find character bit inside byte */+ int b = cs[candidate];+ *c = candidate * BITSPERCHAR;+ if ((b & 0xF0) != 0) { *c += 4; b >>= 4; }+ if ((b & 0x0C) != 0) { *c += 2; b >>= 2; }+ if ((b & 0x02) != 0) { *c += 1; }+ return IChar;+ }+ default: {+ assert(count == CHARSETSIZE * BITSPERCHAR); /* full set */+ return IAny;+ }+ }+}+++/*+** A few basic operations on Charsets+*/+static void cs_complement (Charset *cs) {+ loopset(i, cs->cs[i] = ~cs->cs[i]);+}++static int cs_equal (const byte *cs1, const byte *cs2) {+ loopset(i, if (cs1[i] != cs2[i]) return 0);+ return 1;+}++static int cs_disjoint (const Charset *cs1, const Charset *cs2) {+ loopset(i, if ((cs1->cs[i] & cs2->cs[i]) != 0) return 0;)+ return 1;+}+++/*+** If 'tree' is a 'char' pattern (TSet, TChar, TAny), convert it into a+** charset and return 1; else return 0.+*/+int tocharset (TTree *tree, Charset *cs) {+ switch (tree->tag) {+ case TSet: { /* copy set */+ loopset(i, cs->cs[i] = treebuffer(tree)[i]);+ return 1;+ }+ case TChar: { /* only one char */+ assert(0 <= tree->u.n && tree->u.n <= UCHAR_MAX);+ loopset(i, cs->cs[i] = 0); /* erase all chars */+ setchar(cs->cs, tree->u.n); /* add that one */+ return 1;+ }+ case TAny: {+ loopset(i, cs->cs[i] = 0xFF); /* add all characters to the set */+ return 1;+ }+ default: return 0;+ }+}+++/*+** Visit a TCall node taking care to stop recursion. If node not yet+** visited, return 'f(sib2(tree))', otherwise return 'def' (default+** value)+*/+static int callrecursive (TTree *tree, int f (TTree *t), int def) {+ int key = tree->key;+ assert(tree->tag == TCall);+ assert(sib2(tree)->tag == TRule);+ if (key == 0) /* node already visited? */+ return def; /* return default value */+ else { /* first visit */+ int result;+ tree->key = 0; /* mark call as already visited */+ result = f(sib2(tree)); /* go to called rule */+ tree->key = key; /* restore tree */+ return result;+ }+}+++/*+** Check whether a pattern tree has captures+*/+int hascaptures (TTree *tree) {+ tailcall:+ switch (tree->tag) {+ case TCapture: case TRunTime:+ return 1;+ case TCall:+ return callrecursive(tree, hascaptures, 0);+ case TRule: /* do not follow siblings */+ tree = sib1(tree); goto tailcall;+ case TOpenCall: assert(0);+ default: {+ switch (numsiblings[tree->tag]) {+ case 1: /* return hascaptures(sib1(tree)); */+ tree = sib1(tree); goto tailcall;+ case 2:+ if (hascaptures(sib1(tree)))+ return 1;+ /* else return hascaptures(sib2(tree)); */+ tree = sib2(tree); goto tailcall;+ default: assert(numsiblings[tree->tag] == 0); return 0;+ }+ }+ }+}+++/*+** Checks how a pattern behaves regarding the empty string,+** in one of two different ways:+** A pattern is *nullable* if it can match without consuming any character;+** A pattern is *nofail* if it never fails for any string+** (including the empty string).+** The difference is only for predicates and run-time captures;+** for other patterns, the two properties are equivalent.+** (With predicates, &'a' is nullable but not nofail. Of course,+** nofail => nullable.)+** These functions are all convervative in the following way:+** p is nullable => nullable(p)+** nofail(p) => p cannot fail+** The function assumes that TOpenCall is not nullable;+** this will be checked again when the grammar is fixed.+** Run-time captures can do whatever they want, so the result+** is conservative.+*/+int checkaux (TTree *tree, int pred) {+ tailcall:+ switch (tree->tag) {+ case TChar: case TSet: case TAny:+ case TFalse: case TOpenCall:+ return 0; /* not nullable */+ case TRep: case TTrue:+ return 1; /* no fail */+ case TNot: case TBehind: /* can match empty, but can fail */+ if (pred == PEnofail) return 0;+ else return 1; /* PEnullable */+ case TAnd: /* can match empty; fail iff body does */+ if (pred == PEnullable) return 1;+ /* else return checkaux(sib1(tree), pred); */+ tree = sib1(tree); goto tailcall;+ case TRunTime: /* can fail; match empty iff body does */+ if (pred == PEnofail) return 0;+ /* else return checkaux(sib1(tree), pred); */+ tree = sib1(tree); goto tailcall;+ case TSeq:+ if (!checkaux(sib1(tree), pred)) return 0;+ /* else return checkaux(sib2(tree), pred); */+ tree = sib2(tree); goto tailcall;+ case TChoice:+ if (checkaux(sib2(tree), pred)) return 1;+ /* else return checkaux(sib1(tree), pred); */+ tree = sib1(tree); goto tailcall;+ case TCapture: case TGrammar: case TRule:+ /* return checkaux(sib1(tree), pred); */+ tree = sib1(tree); goto tailcall;+ case TCall: /* return checkaux(sib2(tree), pred); */+ tree = sib2(tree); goto tailcall;+ default: assert(0); return 0;+ }+}+++/*+** number of characters to match a pattern (or -1 if variable)+*/+int fixedlen (TTree *tree) {+ int len = 0; /* to accumulate in tail calls */+ tailcall:+ switch (tree->tag) {+ case TChar: case TSet: case TAny:+ return len + 1;+ case TFalse: case TTrue: case TNot: case TAnd: case TBehind:+ return len;+ case TRep: case TRunTime: case TOpenCall:+ return -1;+ case TCapture: case TRule: case TGrammar:+ /* return fixedlen(sib1(tree)); */+ tree = sib1(tree); goto tailcall;+ case TCall: {+ int n1 = callrecursive(tree, fixedlen, -1);+ if (n1 < 0)+ return -1;+ else+ return len + n1;+ }+ case TSeq: {+ int n1 = fixedlen(sib1(tree));+ if (n1 < 0)+ return -1;+ /* else return fixedlen(sib2(tree)) + len; */+ len += n1; tree = sib2(tree); goto tailcall;+ }+ case TChoice: {+ int n1 = fixedlen(sib1(tree));+ int n2 = fixedlen(sib2(tree));+ if (n1 != n2 || n1 < 0)+ return -1;+ else+ return len + n1;+ }+ default: assert(0); return 0;+ };+}+++/*+** Computes the 'first set' of a pattern.+** The result is a conservative aproximation:+** match p ax -> x (for some x) ==> a belongs to first(p)+** or+** a not in first(p) ==> match p ax -> fail (for all x)+**+** The set 'follow' is the first set of what follows the+** pattern (full set if nothing follows it).+**+** The function returns 0 when this resulting set can be used for+** test instructions that avoid the pattern altogether.+** A non-zero return can happen for two reasons:+** 1) match p '' -> '' ==> return has bit 1 set+** (tests cannot be used because they would always fail for an empty input);+** 2) there is a match-time capture ==> return has bit 2 set+** (optimizations should not bypass match-time captures).+*/+static int getfirst (TTree *tree, const Charset *follow, Charset *firstset) {+ tailcall:+ switch (tree->tag) {+ case TChar: case TSet: case TAny: {+ tocharset(tree, firstset);+ return 0;+ }+ case TTrue: {+ loopset(i, firstset->cs[i] = follow->cs[i]);+ return 1; /* accepts the empty string */+ }+ case TFalse: {+ loopset(i, firstset->cs[i] = 0);+ return 0;+ }+ case TChoice: {+ Charset csaux;+ int e1 = getfirst(sib1(tree), follow, firstset);+ int e2 = getfirst(sib2(tree), follow, &csaux);+ loopset(i, firstset->cs[i] |= csaux.cs[i]);+ return e1 | e2;+ }+ case TSeq: {+ if (!nullable(sib1(tree))) {+ /* when p1 is not nullable, p2 has nothing to contribute;+ return getfirst(sib1(tree), fullset, firstset); */+ tree = sib1(tree); follow = fullset; goto tailcall;+ }+ else { /* FIRST(p1 p2, fl) = FIRST(p1, FIRST(p2, fl)) */+ Charset csaux;+ int e2 = getfirst(sib2(tree), follow, &csaux);+ int e1 = getfirst(sib1(tree), &csaux, firstset);+ if (e1 == 0) return 0; /* 'e1' ensures that first can be used */+ else if ((e1 | e2) & 2) /* one of the children has a matchtime? */+ return 2; /* pattern has a matchtime capture */+ else return e2; /* else depends on 'e2' */+ }+ }+ case TRep: {+ getfirst(sib1(tree), follow, firstset);+ loopset(i, firstset->cs[i] |= follow->cs[i]);+ return 1; /* accept the empty string */+ }+ case TCapture: case TGrammar: case TRule: {+ /* return getfirst(sib1(tree), follow, firstset); */+ tree = sib1(tree); goto tailcall;+ }+ case TRunTime: { /* function invalidates any follow info. */+ int e = getfirst(sib1(tree), fullset, firstset);+ if (e) return 2; /* function is not "protected"? */+ else return 0; /* pattern inside capture ensures first can be used */+ }+ case TCall: {+ /* return getfirst(sib2(tree), follow, firstset); */+ tree = sib2(tree); goto tailcall;+ }+ case TAnd: {+ int e = getfirst(sib1(tree), follow, firstset);+ loopset(i, firstset->cs[i] &= follow->cs[i]);+ return e;+ }+ case TNot: {+ if (tocharset(sib1(tree), firstset)) {+ cs_complement(firstset);+ return 1;+ }+ /* else go through */+ }+ case TBehind: { /* instruction gives no new information */+ /* call 'getfirst' only to check for math-time captures */+ int e = getfirst(sib1(tree), follow, firstset);+ loopset(i, firstset->cs[i] = follow->cs[i]); /* uses follow */+ return e | 1; /* always can accept the empty string */+ }+ default: assert(0); return 0;+ }+}+++/*+** If 'headfail(tree)' true, then 'tree' can fail only depending on the+** next character of the subject.+*/+static int headfail (TTree *tree) {+ tailcall:+ switch (tree->tag) {+ case TChar: case TSet: case TAny: case TFalse:+ return 1;+ case TTrue: case TRep: case TRunTime: case TNot:+ case TBehind:+ return 0;+ case TCapture: case TGrammar: case TRule: case TAnd:+ tree = sib1(tree); goto tailcall; /* return headfail(sib1(tree)); */+ case TCall:+ tree = sib2(tree); goto tailcall; /* return headfail(sib2(tree)); */+ case TSeq:+ if (!nofail(sib2(tree))) return 0;+ /* else return headfail(sib1(tree)); */+ tree = sib1(tree); goto tailcall;+ case TChoice:+ if (!headfail(sib1(tree))) return 0;+ /* else return headfail(sib2(tree)); */+ tree = sib2(tree); goto tailcall;+ default: assert(0); return 0;+ }+}+++/*+** Check whether the code generation for the given tree can benefit+** from a follow set (to avoid computing the follow set when it is+** not needed)+*/+static int needfollow (TTree *tree) {+ tailcall:+ switch (tree->tag) {+ case TChar: case TSet: case TAny:+ case TFalse: case TTrue: case TAnd: case TNot:+ case TRunTime: case TGrammar: case TCall: case TBehind:+ return 0;+ case TChoice: case TRep:+ return 1;+ case TCapture:+ tree = sib1(tree); goto tailcall;+ case TSeq:+ tree = sib2(tree); goto tailcall;+ default: assert(0); return 0;+ } +}++/* }====================================================== */++++/*+** {======================================================+** Code generation+** =======================================================+*/+++/*+** size of an instruction+*/+int sizei (const Instruction *i) {+ switch((Opcode)i->i.code) {+ case ISet: case ISpan: return CHARSETINSTSIZE;+ case ITestSet: return CHARSETINSTSIZE + 1;+ case ITestChar: case ITestAny: case IChoice: case IJmp: case ICall:+ case IOpenCall: case ICommit: case IPartialCommit: case IBackCommit:+ return 2;+ default: return 1;+ }+}+++/*+** state for the compiler+*/+typedef struct CompileState {+ Pattern *p; /* pattern being compiled */+ int ncode; /* next position in p->code to be filled */+ lua_State *L;+} CompileState;+++/*+** code generation is recursive; 'opt' indicates that the code is being+** generated as the last thing inside an optional pattern (so, if that+** code is optional too, it can reuse the 'IChoice' already in place for+** the outer pattern). 'tt' points to a previous test protecting this+** code (or NOINST). 'fl' is the follow set of the pattern.+*/+static void codegen (CompileState *compst, TTree *tree, int opt, int tt,+ const Charset *fl);+++void realloccode (lua_State *L, Pattern *p, int nsize) {+ void *ud;+ lua_Alloc f = lua_getallocf(L, &ud);+ void *newblock = f(ud, p->code, p->codesize * sizeof(Instruction),+ nsize * sizeof(Instruction));+ if (newblock == NULL && nsize > 0)+ luaL_error(L, "not enough memory");+ p->code = (Instruction *)newblock;+ p->codesize = nsize;+}+++static int nextinstruction (CompileState *compst) {+ int size = compst->p->codesize;+ if (compst->ncode >= size)+ realloccode(compst->L, compst->p, size * 2);+ return compst->ncode++;+}+++#define getinstr(cs,i) ((cs)->p->code[i])+++static int addinstruction (CompileState *compst, Opcode op, int aux) {+ int i = nextinstruction(compst);+ getinstr(compst, i).i.code = op;+ getinstr(compst, i).i.aux = aux;+ return i;+}+++/*+** Add an instruction followed by space for an offset (to be set later)+*/+static int addoffsetinst (CompileState *compst, Opcode op) {+ int i = addinstruction(compst, op, 0); /* instruction */+ addinstruction(compst, (Opcode)0, 0); /* open space for offset */+ assert(op == ITestSet || sizei(&getinstr(compst, i)) == 2);+ return i;+}+++/*+** Set the offset of an instruction+*/+static void setoffset (CompileState *compst, int instruction, int offset) {+ getinstr(compst, instruction + 1).offset = offset;+}+++/*+** Add a capture instruction:+** 'op' is the capture instruction; 'cap' the capture kind;+** 'key' the key into ktable; 'aux' is the optional capture offset+**+*/+static int addinstcap (CompileState *compst, Opcode op, int cap, int key,+ int aux) {+ int i = addinstruction(compst, op, joinkindoff(cap, aux));+ getinstr(compst, i).i.key = key;+ return i;+}+++#define gethere(compst) ((compst)->ncode)++#define target(code,i) ((i) + code[i + 1].offset)+++/*+** Patch 'instruction' to jump to 'target'+*/+static void jumptothere (CompileState *compst, int instruction, int target) {+ if (instruction >= 0)+ setoffset(compst, instruction, target - instruction);+}+++/*+** Patch 'instruction' to jump to current position+*/+static void jumptohere (CompileState *compst, int instruction) {+ jumptothere(compst, instruction, gethere(compst));+}+++/*+** Code an IChar instruction, or IAny if there is an equivalent+** test dominating it+*/+static void codechar (CompileState *compst, int c, int tt) {+ if (tt >= 0 && getinstr(compst, tt).i.code == ITestChar &&+ getinstr(compst, tt).i.aux == c)+ addinstruction(compst, IAny, 0);+ else+ addinstruction(compst, IChar, c);+}+++/*+** Add a charset posfix to an instruction+*/+static void addcharset (CompileState *compst, const byte *cs) {+ int p = gethere(compst);+ int i;+ for (i = 0; i < (int)CHARSETINSTSIZE - 1; i++)+ nextinstruction(compst); /* space for buffer */+ /* fill buffer with charset */+ loopset(j, getinstr(compst, p).buff[j] = cs[j]);+}+++/*+** code a char set, optimizing unit sets for IChar, "complete"+** sets for IAny, and empty sets for IFail; also use an IAny+** when instruction is dominated by an equivalent test.+*/+static void codecharset (CompileState *compst, const byte *cs, int tt) {+ int c = 0; /* (=) to avoid warnings */+ Opcode op = charsettype(cs, &c);+ switch (op) {+ case IChar: codechar(compst, c, tt); break;+ case ISet: { /* non-trivial set? */+ if (tt >= 0 && getinstr(compst, tt).i.code == ITestSet &&+ cs_equal(cs, getinstr(compst, tt + 2).buff))+ addinstruction(compst, IAny, 0);+ else {+ addinstruction(compst, ISet, 0);+ addcharset(compst, cs);+ }+ break;+ }+ default: addinstruction(compst, op, c); break;+ }+}+++/*+** code a test set, optimizing unit sets for ITestChar, "complete"+** sets for ITestAny, and empty sets for IJmp (always fails).+** 'e' is true iff test should accept the empty string. (Test+** instructions in the current VM never accept the empty string.)+*/+static int codetestset (CompileState *compst, Charset *cs, int e) {+ if (e) return NOINST; /* no test */+ else {+ int c = 0;+ Opcode op = charsettype(cs->cs, &c);+ switch (op) {+ case IFail: return addoffsetinst(compst, IJmp); /* always jump */+ case IAny: return addoffsetinst(compst, ITestAny);+ case IChar: {+ int i = addoffsetinst(compst, ITestChar);+ getinstr(compst, i).i.aux = c;+ return i;+ }+ case ISet: {+ int i = addoffsetinst(compst, ITestSet);+ addcharset(compst, cs->cs);+ return i;+ }+ default: assert(0); return 0;+ }+ }+}+++/*+** Find the final destination of a sequence of jumps+*/+static int finaltarget (Instruction *code, int i) {+ while (code[i].i.code == IJmp)+ i = target(code, i);+ return i;+}+++/*+** final label (after traversing any jumps)+*/+static int finallabel (Instruction *code, int i) {+ return finaltarget(code, target(code, i));+}+++/*+** <behind(p)> == behind n; <p> (where n = fixedlen(p))+*/+static void codebehind (CompileState *compst, TTree *tree) {+ if (tree->u.n > 0)+ addinstruction(compst, IBehind, tree->u.n);+ codegen(compst, sib1(tree), 0, NOINST, fullset);+}+++/*+** Choice; optimizations:+** - when p1 is headfail or+** when first(p1) and first(p2) are disjoint, than+** a character not in first(p1) cannot go to p1, and a character+** in first(p1) cannot go to p2 (at it is not in first(p2)).+** (The optimization is not valid if p1 accepts the empty string,+** as then there is no character at all...)+** - when p2 is empty and opt is true; a IPartialCommit can reuse+** the Choice already active in the stack.+*/+static void codechoice (CompileState *compst, TTree *p1, TTree *p2, int opt,+ const Charset *fl) {+ int emptyp2 = (p2->tag == TTrue);+ Charset cs1, cs2;+ int e1 = getfirst(p1, fullset, &cs1);+ if (headfail(p1) ||+ (!e1 && (getfirst(p2, fl, &cs2), cs_disjoint(&cs1, &cs2)))) {+ /* <p1 / p2> == test (fail(p1)) -> L1 ; p1 ; jmp L2; L1: p2; L2: */+ int test = codetestset(compst, &cs1, 0);+ int jmp = NOINST;+ codegen(compst, p1, 0, test, fl);+ if (!emptyp2)+ jmp = addoffsetinst(compst, IJmp); + jumptohere(compst, test);+ codegen(compst, p2, opt, NOINST, fl);+ jumptohere(compst, jmp);+ }+ else if (opt && emptyp2) {+ /* p1? == IPartialCommit; p1 */+ jumptohere(compst, addoffsetinst(compst, IPartialCommit));+ codegen(compst, p1, 1, NOINST, fullset);+ }+ else {+ /* <p1 / p2> == + test(first(p1)) -> L1; choice L1; <p1>; commit L2; L1: <p2>; L2: */+ int pcommit;+ int test = codetestset(compst, &cs1, e1);+ int pchoice = addoffsetinst(compst, IChoice);+ codegen(compst, p1, emptyp2, test, fullset);+ pcommit = addoffsetinst(compst, ICommit);+ jumptohere(compst, pchoice);+ jumptohere(compst, test);+ codegen(compst, p2, opt, NOINST, fl);+ jumptohere(compst, pcommit);+ }+}+++/*+** And predicate+** optimization: fixedlen(p) = n ==> <&p> == <p>; behind n+** (valid only when 'p' has no captures)+*/+static void codeand (CompileState *compst, TTree *tree, int tt) {+ int n = fixedlen(tree);+ if (n >= 0 && n <= MAXBEHIND && !hascaptures(tree)) {+ codegen(compst, tree, 0, tt, fullset);+ if (n > 0)+ addinstruction(compst, IBehind, n);+ }+ else { /* default: Choice L1; p1; BackCommit L2; L1: Fail; L2: */+ int pcommit;+ int pchoice = addoffsetinst(compst, IChoice);+ codegen(compst, tree, 0, tt, fullset);+ pcommit = addoffsetinst(compst, IBackCommit);+ jumptohere(compst, pchoice);+ addinstruction(compst, IFail, 0);+ jumptohere(compst, pcommit);+ }+}+++/*+** Captures: if pattern has fixed (and not too big) length, and it+** has no nested captures, use a single IFullCapture instruction+** after the match; otherwise, enclose the pattern with OpenCapture -+** CloseCapture.+*/+static void codecapture (CompileState *compst, TTree *tree, int tt,+ const Charset *fl) {+ int len = fixedlen(sib1(tree));+ if (len >= 0 && len <= MAXOFF && !hascaptures(sib1(tree))) {+ codegen(compst, sib1(tree), 0, tt, fl);+ addinstcap(compst, IFullCapture, tree->cap, tree->key, len);+ }+ else {+ addinstcap(compst, IOpenCapture, tree->cap, tree->key, 0);+ codegen(compst, sib1(tree), 0, tt, fl);+ addinstcap(compst, ICloseCapture, Cclose, 0, 0);+ }+}+++static void coderuntime (CompileState *compst, TTree *tree, int tt) {+ addinstcap(compst, IOpenCapture, Cgroup, tree->key, 0);+ codegen(compst, sib1(tree), 0, tt, fullset);+ addinstcap(compst, ICloseRunTime, Cclose, 0, 0);+}+++/*+** Repetion; optimizations:+** When pattern is a charset, can use special instruction ISpan.+** When pattern is head fail, or if it starts with characters that+** are disjoint from what follows the repetions, a simple test+** is enough (a fail inside the repetition would backtrack to fail+** again in the following pattern, so there is no need for a choice).+** When 'opt' is true, the repetion can reuse the Choice already+** active in the stack.+*/+static void coderep (CompileState *compst, TTree *tree, int opt,+ const Charset *fl) {+ Charset st;+ if (tocharset(tree, &st)) {+ addinstruction(compst, ISpan, 0);+ addcharset(compst, st.cs);+ }+ else {+ int e1 = getfirst(tree, fullset, &st);+ if (headfail(tree) || (!e1 && cs_disjoint(&st, fl))) {+ /* L1: test (fail(p1)) -> L2; <p>; jmp L1; L2: */+ int jmp;+ int test = codetestset(compst, &st, 0);+ codegen(compst, tree, 0, test, fullset);+ jmp = addoffsetinst(compst, IJmp);+ jumptohere(compst, test);+ jumptothere(compst, jmp, test);+ }+ else {+ /* test(fail(p1)) -> L2; choice L2; L1: <p>; partialcommit L1; L2: */+ /* or (if 'opt'): partialcommit L1; L1: <p>; partialcommit L1; */+ int commit, l2;+ int test = codetestset(compst, &st, e1);+ int pchoice = NOINST;+ if (opt)+ jumptohere(compst, addoffsetinst(compst, IPartialCommit));+ else+ pchoice = addoffsetinst(compst, IChoice);+ l2 = gethere(compst);+ codegen(compst, tree, 0, NOINST, fullset);+ commit = addoffsetinst(compst, IPartialCommit);+ jumptothere(compst, commit, l2);+ jumptohere(compst, pchoice);+ jumptohere(compst, test);+ }+ }+}+++/*+** Not predicate; optimizations:+** In any case, if first test fails, 'not' succeeds, so it can jump to+** the end. If pattern is headfail, that is all (it cannot fail+** in other parts); this case includes 'not' of simple sets. Otherwise,+** use the default code (a choice plus a failtwice).+*/+static void codenot (CompileState *compst, TTree *tree) {+ Charset st;+ int e = getfirst(tree, fullset, &st);+ int test = codetestset(compst, &st, e);+ if (headfail(tree)) /* test (fail(p1)) -> L1; fail; L1: */+ addinstruction(compst, IFail, 0);+ else {+ /* test(fail(p))-> L1; choice L1; <p>; failtwice; L1: */+ int pchoice = addoffsetinst(compst, IChoice);+ codegen(compst, tree, 0, NOINST, fullset);+ addinstruction(compst, IFailTwice, 0);+ jumptohere(compst, pchoice);+ }+ jumptohere(compst, test);+}+++/*+** change open calls to calls, using list 'positions' to find+** correct offsets; also optimize tail calls+*/+static void correctcalls (CompileState *compst, int *positions,+ int from, int to) {+ int i;+ Instruction *code = compst->p->code;+ for (i = from; i < to; i += sizei(&code[i])) {+ if (code[i].i.code == IOpenCall) {+ int n = code[i].i.key; /* rule number */+ int rule = positions[n]; /* rule position */+ assert(rule == from || code[rule - 1].i.code == IRet);+ if (code[finaltarget(code, i + 2)].i.code == IRet) /* call; ret ? */+ code[i].i.code = IJmp; /* tail call */+ else+ code[i].i.code = ICall;+ jumptothere(compst, i, rule); /* call jumps to respective rule */+ }+ }+ assert(i == to);+}+++/*+** Code for a grammar:+** call L1; jmp L2; L1: rule 1; ret; rule 2; ret; ...; L2:+*/+static void codegrammar (CompileState *compst, TTree *grammar) {+ int positions[MAXRULES];+ int rulenumber = 0;+ TTree *rule;+ int firstcall = addoffsetinst(compst, ICall); /* call initial rule */+ int jumptoend = addoffsetinst(compst, IJmp); /* jump to the end */+ int start = gethere(compst); /* here starts the initial rule */+ jumptohere(compst, firstcall);+ for (rule = sib1(grammar); rule->tag == TRule; rule = sib2(rule)) {+ positions[rulenumber++] = gethere(compst); /* save rule position */+ codegen(compst, sib1(rule), 0, NOINST, fullset); /* code rule */+ addinstruction(compst, IRet, 0);+ }+ assert(rule->tag == TTrue);+ jumptohere(compst, jumptoend);+ correctcalls(compst, positions, start, gethere(compst));+}+++static void codecall (CompileState *compst, TTree *call) {+ int c = addoffsetinst(compst, IOpenCall); /* to be corrected later */+ getinstr(compst, c).i.key = sib2(call)->cap; /* rule number */+ assert(sib2(call)->tag == TRule);+}+++/*+** Code first child of a sequence+** (second child is called in-place to allow tail call)+** Return 'tt' for second child+*/+static int codeseq1 (CompileState *compst, TTree *p1, TTree *p2,+ int tt, const Charset *fl) {+ if (needfollow(p1)) {+ Charset fl1;+ getfirst(p2, fl, &fl1); /* p1 follow is p2 first */+ codegen(compst, p1, 0, tt, &fl1);+ }+ else /* use 'fullset' as follow */+ codegen(compst, p1, 0, tt, fullset);+ if (fixedlen(p1) != 0) /* can 'p1' consume anything? */+ return NOINST; /* invalidate test */+ else return tt; /* else 'tt' still protects sib2 */+}+++/*+** Main code-generation function: dispatch to auxiliar functions+** according to kind of tree. ('needfollow' should return true+** only for consructions that use 'fl'.)+*/+static void codegen (CompileState *compst, TTree *tree, int opt, int tt,+ const Charset *fl) {+ tailcall:+ switch (tree->tag) {+ case TChar: codechar(compst, tree->u.n, tt); break;+ case TAny: addinstruction(compst, IAny, 0); break;+ case TSet: codecharset(compst, treebuffer(tree), tt); break;+ case TTrue: break;+ case TFalse: addinstruction(compst, IFail, 0); break;+ case TChoice: codechoice(compst, sib1(tree), sib2(tree), opt, fl); break;+ case TRep: coderep(compst, sib1(tree), opt, fl); break;+ case TBehind: codebehind(compst, tree); break;+ case TNot: codenot(compst, sib1(tree)); break;+ case TAnd: codeand(compst, sib1(tree), tt); break;+ case TCapture: codecapture(compst, tree, tt, fl); break;+ case TRunTime: coderuntime(compst, tree, tt); break;+ case TGrammar: codegrammar(compst, tree); break;+ case TCall: codecall(compst, tree); break;+ case TSeq: {+ tt = codeseq1(compst, sib1(tree), sib2(tree), tt, fl); /* code 'p1' */+ /* codegen(compst, p2, opt, tt, fl); */+ tree = sib2(tree); goto tailcall;+ }+ default: assert(0);+ }+}+++/*+** Optimize jumps and other jump-like instructions.+** * Update labels of instructions with labels to their final+** destinations (e.g., choice L1; ... L1: jmp L2: becomes+** choice L2)+** * Jumps to other instructions that do jumps become those+** instructions (e.g., jump to return becomes a return; jump+** to commit becomes a commit)+*/+static void peephole (CompileState *compst) {+ Instruction *code = compst->p->code;+ int i;+ for (i = 0; i < compst->ncode; i += sizei(&code[i])) {+ redo:+ switch (code[i].i.code) {+ case IChoice: case ICall: case ICommit: case IPartialCommit:+ case IBackCommit: case ITestChar: case ITestSet:+ case ITestAny: { /* instructions with labels */+ jumptothere(compst, i, finallabel(code, i)); /* optimize label */+ break;+ }+ case IJmp: {+ int ft = finaltarget(code, i);+ switch (code[ft].i.code) { /* jumping to what? */+ case IRet: case IFail: case IFailTwice:+ case IEnd: { /* instructions with unconditional implicit jumps */+ code[i] = code[ft]; /* jump becomes that instruction */+ code[i + 1].i.code = IAny; /* 'no-op' for target position */+ break;+ }+ case ICommit: case IPartialCommit:+ case IBackCommit: { /* inst. with unconditional explicit jumps */+ int fft = finallabel(code, ft);+ code[i] = code[ft]; /* jump becomes that instruction... */+ jumptothere(compst, i, fft); /* but must correct its offset */+ goto redo; /* reoptimize its label */+ }+ default: {+ jumptothere(compst, i, ft); /* optimize label */+ break;+ }+ }+ break;+ }+ default: break;+ }+ }+ assert(code[i - 1].i.code == IEnd);+}+++/*+** Compile a pattern+*/+Instruction *compile (lua_State *L, Pattern *p) {+ CompileState compst;+ compst.p = p; compst.ncode = 0; compst.L = L;+ realloccode(L, p, 2); /* minimum initial size */+ codegen(&compst, p->tree, 0, NOINST, fullset);+ addinstruction(&compst, IEnd, 0);+ realloccode(L, p, compst.ncode); /* set final size */+ peephole(&compst);+ return p->code;+}+++/* }====================================================== */+
+ cbits/lpeg-1.0.2/lpcode.h view
@@ -0,0 +1,40 @@+/*+** $Id: lpcode.h $+*/++#if !defined(lpcode_h)+#define lpcode_h++#include "lua.h"++#include "lptypes.h"+#include "lptree.h"+#include "lpvm.h"++int tocharset (TTree *tree, Charset *cs);+int checkaux (TTree *tree, int pred);+int fixedlen (TTree *tree);+int hascaptures (TTree *tree);+int lp_gc (lua_State *L);+Instruction *compile (lua_State *L, Pattern *p);+void realloccode (lua_State *L, Pattern *p, int nsize);+int sizei (const Instruction *i);+++#define PEnullable 0+#define PEnofail 1++/*+** nofail(t) implies that 't' cannot fail with any input+*/+#define nofail(t) checkaux(t, PEnofail)++/*+** (not nullable(t)) implies 't' cannot match without consuming+** something+*/+#define nullable(t) checkaux(t, PEnullable)++++#endif
+ cbits/lpeg-1.0.2/lpprint.c view
@@ -0,0 +1,244 @@+/*+** $Id: lpprint.c $+** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)+*/++#include <ctype.h>+#include <limits.h>+#include <stdio.h>+++#include "lptypes.h"+#include "lpprint.h"+#include "lpcode.h"+++#if defined(LPEG_DEBUG)++/*+** {======================================================+** Printing patterns (for debugging)+** =======================================================+*/+++void printcharset (const byte *st) {+ int i;+ printf("[");+ for (i = 0; i <= UCHAR_MAX; i++) {+ int first = i;+ while (testchar(st, i) && i <= UCHAR_MAX) i++;+ if (i - 1 == first) /* unary range? */+ printf("(%02x)", first);+ else if (i - 1 > first) /* non-empty range? */+ printf("(%02x-%02x)", first, i - 1);+ }+ printf("]");+}+++static const char *capkind (int kind) {+ const char *const modes[] = {+ "close", "position", "constant", "backref",+ "argument", "simple", "table", "function",+ "query", "string", "num", "substitution", "fold",+ "runtime", "group"};+ return modes[kind];+}+++static void printjmp (const Instruction *op, const Instruction *p) {+ printf("-> %d", (int)(p + (p + 1)->offset - op));+}+++void printinst (const Instruction *op, const Instruction *p) {+ const char *const names[] = {+ "any", "char", "set",+ "testany", "testchar", "testset",+ "span", "behind",+ "ret", "end",+ "choice", "jmp", "call", "open_call",+ "commit", "partial_commit", "back_commit", "failtwice", "fail", "giveup",+ "fullcapture", "opencapture", "closecapture", "closeruntime"+ };+ printf("%02ld: %s ", (long)(p - op), names[p->i.code]);+ switch ((Opcode)p->i.code) {+ case IChar: {+ printf("'%c'", p->i.aux);+ break;+ }+ case ITestChar: {+ printf("'%c'", p->i.aux); printjmp(op, p);+ break;+ }+ case IFullCapture: {+ printf("%s (size = %d) (idx = %d)",+ capkind(getkind(p)), getoff(p), p->i.key);+ break;+ }+ case IOpenCapture: {+ printf("%s (idx = %d)", capkind(getkind(p)), p->i.key);+ break;+ }+ case ISet: {+ printcharset((p+1)->buff);+ break;+ }+ case ITestSet: {+ printcharset((p+2)->buff); printjmp(op, p);+ break;+ }+ case ISpan: {+ printcharset((p+1)->buff);+ break;+ }+ case IOpenCall: {+ printf("-> %d", (p + 1)->offset);+ break;+ }+ case IBehind: {+ printf("%d", p->i.aux);+ break;+ }+ case IJmp: case ICall: case ICommit: case IChoice:+ case IPartialCommit: case IBackCommit: case ITestAny: {+ printjmp(op, p);+ break;+ }+ default: break;+ }+ printf("\n");+}+++void printpatt (Instruction *p, int n) {+ Instruction *op = p;+ while (p < op + n) {+ printinst(op, p);+ p += sizei(p);+ }+}+++#if defined(LPEG_DEBUG)+static void printcap (Capture *cap) {+ printf("%s (idx: %d - size: %d) -> %p\n",+ capkind(cap->kind), cap->idx, cap->siz, cap->s);+}+++void printcaplist (Capture *cap, Capture *limit) {+ printf(">======\n");+ for (; cap->s && (limit == NULL || cap < limit); cap++)+ printcap(cap);+ printf("=======\n");+}+#endif++/* }====================================================== */+++/*+** {======================================================+** Printing trees (for debugging)+** =======================================================+*/++static const char *tagnames[] = {+ "char", "set", "any",+ "true", "false",+ "rep",+ "seq", "choice",+ "not", "and",+ "call", "opencall", "rule", "grammar",+ "behind",+ "capture", "run-time"+};+++void printtree (TTree *tree, int ident) {+ int i;+ for (i = 0; i < ident; i++) printf(" ");+ printf("%s", tagnames[tree->tag]);+ switch (tree->tag) {+ case TChar: {+ int c = tree->u.n;+ if (isprint(c))+ printf(" '%c'\n", c);+ else+ printf(" (%02X)\n", c);+ break;+ }+ case TSet: {+ printcharset(treebuffer(tree));+ printf("\n");+ break;+ }+ case TOpenCall: case TCall: {+ assert(sib2(tree)->tag == TRule);+ printf(" key: %d (rule: %d)\n", tree->key, sib2(tree)->cap);+ break;+ }+ case TBehind: {+ printf(" %d\n", tree->u.n);+ printtree(sib1(tree), ident + 2);+ break;+ }+ case TCapture: {+ printf(" kind: '%s' key: %d\n", capkind(tree->cap), tree->key);+ printtree(sib1(tree), ident + 2);+ break;+ }+ case TRule: {+ printf(" n: %d key: %d\n", tree->cap, tree->key);+ printtree(sib1(tree), ident + 2);+ break; /* do not print next rule as a sibling */+ }+ case TGrammar: {+ TTree *rule = sib1(tree);+ printf(" %d\n", tree->u.n); /* number of rules */+ for (i = 0; i < tree->u.n; i++) {+ printtree(rule, ident + 2);+ rule = sib2(rule);+ }+ assert(rule->tag == TTrue); /* sentinel */+ break;+ }+ default: {+ int sibs = numsiblings[tree->tag];+ printf("\n");+ if (sibs >= 1) {+ printtree(sib1(tree), ident + 2);+ if (sibs >= 2)+ printtree(sib2(tree), ident + 2);+ }+ break;+ }+ }+}+++void printktable (lua_State *L, int idx) {+ int n, i;+ lua_getuservalue(L, idx);+ if (lua_isnil(L, -1)) /* no ktable? */+ return;+ n = lua_rawlen(L, -1);+ printf("[");+ for (i = 1; i <= n; i++) {+ printf("%d = ", i);+ lua_rawgeti(L, -1, i);+ if (lua_isstring(L, -1))+ printf("%s ", lua_tostring(L, -1));+ else+ printf("%s ", lua_typename(L, lua_type(L, -1)));+ lua_pop(L, 1);+ }+ printf("]\n");+ /* leave ktable at the stack */+}++/* }====================================================== */++#endif
+ cbits/lpeg-1.0.2/lpprint.h view
@@ -0,0 +1,36 @@+/*+** $Id: lpprint.h $+*/+++#if !defined(lpprint_h)+#define lpprint_h+++#include "lptree.h"+#include "lpvm.h"+++#if defined(LPEG_DEBUG)++void printpatt (Instruction *p, int n);+void printtree (TTree *tree, int ident);+void printktable (lua_State *L, int idx);+void printcharset (const byte *st);+void printcaplist (Capture *cap, Capture *limit);+void printinst (const Instruction *op, const Instruction *p);++#else++#define printktable(L,idx) \+ luaL_error(L, "function only implemented in debug mode")+#define printtree(tree,i) \+ luaL_error(L, "function only implemented in debug mode")+#define printpatt(p,n) \+ luaL_error(L, "function only implemented in debug mode")++#endif+++#endif+
+ cbits/lpeg-1.0.2/lptree.c view
@@ -0,0 +1,1305 @@+/*+** $Id: lptree.c $+** Copyright 2013, Lua.org & PUC-Rio (see 'lpeg.html' for license)+*/++#include <ctype.h>+#include <limits.h>+#include <string.h>+++#include "lua.h"+#include "lauxlib.h"++#include "lptypes.h"+#include "lpcap.h"+#include "lpcode.h"+#include "lpprint.h"+#include "lptree.h"+++/* number of siblings for each tree */+const byte numsiblings[] = {+ 0, 0, 0, /* char, set, any */+ 0, 0, /* true, false */ + 1, /* rep */+ 2, 2, /* seq, choice */+ 1, 1, /* not, and */+ 0, 0, 2, 1, /* call, opencall, rule, grammar */+ 1, /* behind */+ 1, 1 /* capture, runtime capture */+};+++static TTree *newgrammar (lua_State *L, int arg);+++/*+** returns a reasonable name for value at index 'idx' on the stack+*/+static const char *val2str (lua_State *L, int idx) {+ const char *k = lua_tostring(L, idx);+ if (k != NULL)+ return lua_pushfstring(L, "%s", k);+ else+ return lua_pushfstring(L, "(a %s)", luaL_typename(L, idx));+}+++/*+** Fix a TOpenCall into a TCall node, using table 'postable' to+** translate a key to its rule address in the tree. Raises an+** error if key does not exist.+*/+static void fixonecall (lua_State *L, int postable, TTree *g, TTree *t) {+ int n;+ lua_rawgeti(L, -1, t->key); /* get rule's name */+ lua_gettable(L, postable); /* query name in position table */+ n = lua_tonumber(L, -1); /* get (absolute) position */+ lua_pop(L, 1); /* remove position */+ if (n == 0) { /* no position? */+ lua_rawgeti(L, -1, t->key); /* get rule's name again */+ luaL_error(L, "rule '%s' undefined in given grammar", val2str(L, -1));+ }+ t->tag = TCall;+ t->u.ps = n - (t - g); /* position relative to node */+ assert(sib2(t)->tag == TRule);+ sib2(t)->key = t->key; /* fix rule's key */+}+++/*+** Transform left associative constructions into right+** associative ones, for sequence and choice; that is:+** (t11 + t12) + t2 => t11 + (t12 + t2)+** (t11 * t12) * t2 => t11 * (t12 * t2)+** (that is, Op (Op t11 t12) t2 => Op t11 (Op t12 t2))+*/+static void correctassociativity (TTree *tree) {+ TTree *t1 = sib1(tree);+ assert(tree->tag == TChoice || tree->tag == TSeq);+ while (t1->tag == tree->tag) {+ int n1size = tree->u.ps - 1; /* t1 == Op t11 t12 */+ int n11size = t1->u.ps - 1;+ int n12size = n1size - n11size - 1;+ memmove(sib1(tree), sib1(t1), n11size * sizeof(TTree)); /* move t11 */+ tree->u.ps = n11size + 1;+ sib2(tree)->tag = tree->tag;+ sib2(tree)->u.ps = n12size + 1;+ }+}+++/*+** Make final adjustments in a tree. Fix open calls in tree 't',+** making them refer to their respective rules or raising appropriate+** errors (if not inside a grammar). Correct associativity of associative+** constructions (making them right associative). Assume that tree's+** ktable is at the top of the stack (for error messages).+*/+static void finalfix (lua_State *L, int postable, TTree *g, TTree *t) {+ tailcall:+ switch (t->tag) {+ case TGrammar: /* subgrammars were already fixed */+ return;+ case TOpenCall: {+ if (g != NULL) /* inside a grammar? */+ fixonecall(L, postable, g, t);+ else { /* open call outside grammar */+ lua_rawgeti(L, -1, t->key);+ luaL_error(L, "rule '%s' used outside a grammar", val2str(L, -1));+ }+ break;+ }+ case TSeq: case TChoice:+ correctassociativity(t);+ break;+ }+ switch (numsiblings[t->tag]) {+ case 1: /* finalfix(L, postable, g, sib1(t)); */+ t = sib1(t); goto tailcall;+ case 2:+ finalfix(L, postable, g, sib1(t));+ t = sib2(t); goto tailcall; /* finalfix(L, postable, g, sib2(t)); */+ default: assert(numsiblings[t->tag] == 0); break;+ }+}++++/*+** {===================================================================+** KTable manipulation+**+** - The ktable of a pattern 'p' can be shared by other patterns that+** contain 'p' and no other constants. Because of this sharing, we+** should not add elements to a 'ktable' unless it was freshly created+** for the new pattern.+**+** - The maximum index in a ktable is USHRT_MAX, because trees and+** patterns use unsigned shorts to store those indices.+** ====================================================================+*/++/*+** Create a new 'ktable' to the pattern at the top of the stack.+*/+static void newktable (lua_State *L, int n) {+ lua_createtable(L, n, 0); /* create a fresh table */+ lua_setuservalue(L, -2); /* set it as 'ktable' for pattern */+}+++/*+** Add element 'idx' to 'ktable' of pattern at the top of the stack;+** Return index of new element.+** If new element is nil, does not add it to table (as it would be+** useless) and returns 0, as ktable[0] is always nil.+*/+static int addtoktable (lua_State *L, int idx) {+ if (lua_isnil(L, idx)) /* nil value? */+ return 0;+ else {+ int n;+ lua_getuservalue(L, -1); /* get ktable from pattern */+ n = lua_rawlen(L, -1);+ if (n >= USHRT_MAX)+ luaL_error(L, "too many Lua values in pattern");+ lua_pushvalue(L, idx); /* element to be added */+ lua_rawseti(L, -2, ++n);+ lua_pop(L, 1); /* remove 'ktable' */+ return n;+ }+}+++/*+** Return the number of elements in the ktable at 'idx'.+** In Lua 5.2/5.3, default "environment" for patterns is nil, not+** a table. Treat it as an empty table. In Lua 5.1, assumes that+** the environment has no numeric indices (len == 0)+*/+static int ktablelen (lua_State *L, int idx) {+ if (!lua_istable(L, idx)) return 0;+ else return lua_rawlen(L, idx);+}+++/*+** Concatentate the contents of table 'idx1' into table 'idx2'.+** (Assume that both indices are negative.)+** Return the original length of table 'idx2' (or 0, if no+** element was added, as there is no need to correct any index).+*/+static int concattable (lua_State *L, int idx1, int idx2) {+ int i;+ int n1 = ktablelen(L, idx1);+ int n2 = ktablelen(L, idx2);+ if (n1 + n2 > USHRT_MAX)+ luaL_error(L, "too many Lua values in pattern");+ if (n1 == 0) return 0; /* nothing to correct */+ for (i = 1; i <= n1; i++) {+ lua_rawgeti(L, idx1, i);+ lua_rawseti(L, idx2 - 1, n2 + i); /* correct 'idx2' */+ }+ return n2;+}+++/*+** When joining 'ktables', constants from one of the subpatterns must+** be renumbered; 'correctkeys' corrects their indices (adding 'n'+** to each of them)+*/+static void correctkeys (TTree *tree, int n) {+ if (n == 0) return; /* no correction? */+ tailcall:+ switch (tree->tag) {+ case TOpenCall: case TCall: case TRunTime: case TRule: {+ if (tree->key > 0)+ tree->key += n;+ break;+ }+ case TCapture: {+ if (tree->key > 0 && tree->cap != Carg && tree->cap != Cnum)+ tree->key += n;+ break;+ }+ default: break;+ }+ switch (numsiblings[tree->tag]) {+ case 1: /* correctkeys(sib1(tree), n); */+ tree = sib1(tree); goto tailcall;+ case 2:+ correctkeys(sib1(tree), n);+ tree = sib2(tree); goto tailcall; /* correctkeys(sib2(tree), n); */+ default: assert(numsiblings[tree->tag] == 0); break;+ }+}+++/*+** Join the ktables from p1 and p2 the ktable for the new pattern at the+** top of the stack, reusing them when possible.+*/+static void joinktables (lua_State *L, int p1, TTree *t2, int p2) {+ int n1, n2;+ lua_getuservalue(L, p1); /* get ktables */+ lua_getuservalue(L, p2);+ n1 = ktablelen(L, -2);+ n2 = ktablelen(L, -1);+ if (n1 == 0 && n2 == 0) /* are both tables empty? */+ lua_pop(L, 2); /* nothing to be done; pop tables */+ else if (n2 == 0 || lp_equal(L, -2, -1)) { /* 2nd table empty or equal? */+ lua_pop(L, 1); /* pop 2nd table */+ lua_setuservalue(L, -2); /* set 1st ktable into new pattern */+ }+ else if (n1 == 0) { /* first table is empty? */+ lua_setuservalue(L, -3); /* set 2nd table into new pattern */+ lua_pop(L, 1); /* pop 1st table */+ }+ else {+ lua_createtable(L, n1 + n2, 0); /* create ktable for new pattern */+ /* stack: new p; ktable p1; ktable p2; new ktable */+ concattable(L, -3, -1); /* from p1 into new ktable */+ concattable(L, -2, -1); /* from p2 into new ktable */+ lua_setuservalue(L, -4); /* new ktable becomes 'p' environment */+ lua_pop(L, 2); /* pop other ktables */+ correctkeys(t2, n1); /* correction for indices from p2 */+ }+}+++/*+** copy 'ktable' of element 'idx' to new tree (on top of stack)+*/+static void copyktable (lua_State *L, int idx) {+ lua_getuservalue(L, idx);+ lua_setuservalue(L, -2);+}+++/*+** merge 'ktable' from 'stree' at stack index 'idx' into 'ktable'+** from tree at the top of the stack, and correct corresponding+** tree.+*/+static void mergektable (lua_State *L, int idx, TTree *stree) {+ int n;+ lua_getuservalue(L, -1); /* get ktables */+ lua_getuservalue(L, idx);+ n = concattable(L, -1, -2);+ lua_pop(L, 2); /* remove both ktables */+ correctkeys(stree, n);+}+++/*+** Create a new 'ktable' to the pattern at the top of the stack, adding+** all elements from pattern 'p' (if not 0) plus element 'idx' to it.+** Return index of new element.+*/+static int addtonewktable (lua_State *L, int p, int idx) {+ newktable(L, 1);+ if (p)+ mergektable(L, p, NULL);+ return addtoktable(L, idx);+}++/* }====================================================== */+++/*+** {======================================================+** Tree generation+** =======================================================+*/++/*+** In 5.2, could use 'luaL_testudata'...+*/+static int testpattern (lua_State *L, int idx) {+ if (lua_touserdata(L, idx)) { /* value is a userdata? */+ if (lua_getmetatable(L, idx)) { /* does it have a metatable? */+ luaL_getmetatable(L, PATTERN_T);+ if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */+ lua_pop(L, 2); /* remove both metatables */+ return 1;+ }+ }+ }+ return 0;+}+++static Pattern *getpattern (lua_State *L, int idx) {+ return (Pattern *)luaL_checkudata(L, idx, PATTERN_T);+}+++static int getsize (lua_State *L, int idx) {+ return (lua_rawlen(L, idx) - sizeof(Pattern)) / sizeof(TTree) + 1;+}+++static TTree *gettree (lua_State *L, int idx, int *len) {+ Pattern *p = getpattern(L, idx);+ if (len)+ *len = getsize(L, idx);+ return p->tree;+}+++/*+** create a pattern. Set its uservalue (the 'ktable') equal to its+** metatable. (It could be any empty sequence; the metatable is at+** hand here, so we use it.)+*/+static TTree *newtree (lua_State *L, int len) {+ size_t size = (len - 1) * sizeof(TTree) + sizeof(Pattern);+ Pattern *p = (Pattern *)lua_newuserdata(L, size);+ luaL_getmetatable(L, PATTERN_T);+ lua_pushvalue(L, -1);+ lua_setuservalue(L, -3);+ lua_setmetatable(L, -2);+ p->code = NULL; p->codesize = 0;+ return p->tree;+}+++static TTree *newleaf (lua_State *L, int tag) {+ TTree *tree = newtree(L, 1);+ tree->tag = tag;+ return tree;+}+++static TTree *newcharset (lua_State *L) {+ TTree *tree = newtree(L, bytes2slots(CHARSETSIZE) + 1);+ tree->tag = TSet;+ loopset(i, treebuffer(tree)[i] = 0);+ return tree;+}+++/*+** add to tree a sequence where first sibling is 'sib' (with size+** 'sibsize'); returns position for second sibling+*/+static TTree *seqaux (TTree *tree, TTree *sib, int sibsize) {+ tree->tag = TSeq; tree->u.ps = sibsize + 1;+ memcpy(sib1(tree), sib, sibsize * sizeof(TTree));+ return sib2(tree);+}+++/*+** Build a sequence of 'n' nodes, each with tag 'tag' and 'u.n' got+** from the array 's' (or 0 if array is NULL). (TSeq is binary, so it+** must build a sequence of sequence of sequence...)+*/+static void fillseq (TTree *tree, int tag, int n, const char *s) {+ int i;+ for (i = 0; i < n - 1; i++) { /* initial n-1 copies of Seq tag; Seq ... */+ tree->tag = TSeq; tree->u.ps = 2;+ sib1(tree)->tag = tag;+ sib1(tree)->u.n = s ? (byte)s[i] : 0;+ tree = sib2(tree);+ }+ tree->tag = tag; /* last one does not need TSeq */+ tree->u.n = s ? (byte)s[i] : 0;+}+++/*+** Numbers as patterns:+** 0 == true (always match); n == TAny repeated 'n' times;+** -n == not (TAny repeated 'n' times)+*/+static TTree *numtree (lua_State *L, int n) {+ if (n == 0)+ return newleaf(L, TTrue);+ else {+ TTree *tree, *nd;+ if (n > 0)+ tree = nd = newtree(L, 2 * n - 1);+ else { /* negative: code it as !(-n) */+ n = -n;+ tree = newtree(L, 2 * n);+ tree->tag = TNot;+ nd = sib1(tree);+ }+ fillseq(nd, TAny, n, NULL); /* sequence of 'n' any's */+ return tree;+ }+}+++/*+** Convert value at index 'idx' to a pattern+*/+static TTree *getpatt (lua_State *L, int idx, int *len) {+ TTree *tree;+ switch (lua_type(L, idx)) {+ case LUA_TSTRING: {+ size_t slen;+ const char *s = lua_tolstring(L, idx, &slen); /* get string */+ if (slen == 0) /* empty? */+ tree = newleaf(L, TTrue); /* always match */+ else {+ tree = newtree(L, 2 * (slen - 1) + 1);+ fillseq(tree, TChar, slen, s); /* sequence of 'slen' chars */+ }+ break;+ }+ case LUA_TNUMBER: {+ int n = lua_tointeger(L, idx);+ tree = numtree(L, n);+ break;+ }+ case LUA_TBOOLEAN: {+ tree = (lua_toboolean(L, idx) ? newleaf(L, TTrue) : newleaf(L, TFalse));+ break;+ }+ case LUA_TTABLE: {+ tree = newgrammar(L, idx);+ break;+ }+ case LUA_TFUNCTION: {+ tree = newtree(L, 2);+ tree->tag = TRunTime;+ tree->key = addtonewktable(L, 0, idx);+ sib1(tree)->tag = TTrue;+ break;+ }+ default: {+ return gettree(L, idx, len);+ }+ }+ lua_replace(L, idx); /* put new tree into 'idx' slot */+ if (len)+ *len = getsize(L, idx);+ return tree;+}+++/*+** create a new tree, whith a new root and one sibling.+** Sibling must be on the Lua stack, at index 1.+*/+static TTree *newroot1sib (lua_State *L, int tag) {+ int s1;+ TTree *tree1 = getpatt(L, 1, &s1);+ TTree *tree = newtree(L, 1 + s1); /* create new tree */+ tree->tag = tag;+ memcpy(sib1(tree), tree1, s1 * sizeof(TTree));+ copyktable(L, 1);+ return tree;+}+++/*+** create a new tree, whith a new root and 2 siblings.+** Siblings must be on the Lua stack, first one at index 1.+*/+static TTree *newroot2sib (lua_State *L, int tag) {+ int s1, s2;+ TTree *tree1 = getpatt(L, 1, &s1);+ TTree *tree2 = getpatt(L, 2, &s2);+ TTree *tree = newtree(L, 1 + s1 + s2); /* create new tree */+ tree->tag = tag;+ tree->u.ps = 1 + s1;+ memcpy(sib1(tree), tree1, s1 * sizeof(TTree));+ memcpy(sib2(tree), tree2, s2 * sizeof(TTree));+ joinktables(L, 1, sib2(tree), 2);+ return tree;+}+++static int lp_P (lua_State *L) {+ luaL_checkany(L, 1);+ getpatt(L, 1, NULL);+ lua_settop(L, 1);+ return 1;+}+++/*+** sequence operator; optimizations:+** false x => false, x true => x, true x => x+** (cannot do x . false => false because x may have runtime captures)+*/+static int lp_seq (lua_State *L) {+ TTree *tree1 = getpatt(L, 1, NULL);+ TTree *tree2 = getpatt(L, 2, NULL);+ if (tree1->tag == TFalse || tree2->tag == TTrue)+ lua_pushvalue(L, 1); /* false . x == false, x . true = x */+ else if (tree1->tag == TTrue)+ lua_pushvalue(L, 2); /* true . x = x */+ else+ newroot2sib(L, TSeq);+ return 1;+}+++/*+** choice operator; optimizations:+** charset / charset => charset+** true / x => true, x / false => x, false / x => x+** (x / true is not equivalent to true)+*/+static int lp_choice (lua_State *L) {+ Charset st1, st2;+ TTree *t1 = getpatt(L, 1, NULL);+ TTree *t2 = getpatt(L, 2, NULL);+ if (tocharset(t1, &st1) && tocharset(t2, &st2)) {+ TTree *t = newcharset(L);+ loopset(i, treebuffer(t)[i] = st1.cs[i] | st2.cs[i]);+ }+ else if (nofail(t1) || t2->tag == TFalse)+ lua_pushvalue(L, 1); /* true / x => true, x / false => x */+ else if (t1->tag == TFalse)+ lua_pushvalue(L, 2); /* false / x => x */+ else+ newroot2sib(L, TChoice);+ return 1;+}+++/*+** p^n+*/+static int lp_star (lua_State *L) {+ int size1;+ int n = (int)luaL_checkinteger(L, 2);+ TTree *tree1 = getpatt(L, 1, &size1);+ if (n >= 0) { /* seq tree1 (seq tree1 ... (seq tree1 (rep tree1))) */+ TTree *tree = newtree(L, (n + 1) * (size1 + 1));+ if (nullable(tree1))+ luaL_error(L, "loop body may accept empty string");+ while (n--) /* repeat 'n' times */+ tree = seqaux(tree, tree1, size1);+ tree->tag = TRep;+ memcpy(sib1(tree), tree1, size1 * sizeof(TTree));+ }+ else { /* choice (seq tree1 ... choice tree1 true ...) true */+ TTree *tree;+ n = -n;+ /* size = (choice + seq + tree1 + true) * n, but the last has no seq */+ tree = newtree(L, n * (size1 + 3) - 1);+ for (; n > 1; n--) { /* repeat (n - 1) times */+ tree->tag = TChoice; tree->u.ps = n * (size1 + 3) - 2;+ sib2(tree)->tag = TTrue;+ tree = sib1(tree);+ tree = seqaux(tree, tree1, size1);+ }+ tree->tag = TChoice; tree->u.ps = size1 + 1;+ sib2(tree)->tag = TTrue;+ memcpy(sib1(tree), tree1, size1 * sizeof(TTree));+ }+ copyktable(L, 1);+ return 1;+}+++/*+** #p == &p+*/+static int lp_and (lua_State *L) {+ newroot1sib(L, TAnd);+ return 1;+}+++/*+** -p == !p+*/+static int lp_not (lua_State *L) {+ newroot1sib(L, TNot);+ return 1;+}+++/*+** [t1 - t2] == Seq (Not t2) t1+** If t1 and t2 are charsets, make their difference.+*/+static int lp_sub (lua_State *L) {+ Charset st1, st2;+ int s1, s2;+ TTree *t1 = getpatt(L, 1, &s1);+ TTree *t2 = getpatt(L, 2, &s2);+ if (tocharset(t1, &st1) && tocharset(t2, &st2)) {+ TTree *t = newcharset(L);+ loopset(i, treebuffer(t)[i] = st1.cs[i] & ~st2.cs[i]);+ }+ else {+ TTree *tree = newtree(L, 2 + s1 + s2);+ tree->tag = TSeq; /* sequence of... */+ tree->u.ps = 2 + s2;+ sib1(tree)->tag = TNot; /* ...not... */+ memcpy(sib1(sib1(tree)), t2, s2 * sizeof(TTree)); /* ...t2 */+ memcpy(sib2(tree), t1, s1 * sizeof(TTree)); /* ... and t1 */+ joinktables(L, 1, sib1(tree), 2);+ }+ return 1;+}+++static int lp_set (lua_State *L) {+ size_t l;+ const char *s = luaL_checklstring(L, 1, &l);+ TTree *tree = newcharset(L);+ while (l--) {+ setchar(treebuffer(tree), (byte)(*s));+ s++;+ }+ return 1;+}+++static int lp_range (lua_State *L) {+ int arg;+ int top = lua_gettop(L);+ TTree *tree = newcharset(L);+ for (arg = 1; arg <= top; arg++) {+ int c;+ size_t l;+ const char *r = luaL_checklstring(L, arg, &l);+ luaL_argcheck(L, l == 2, arg, "range must have two characters");+ for (c = (byte)r[0]; c <= (byte)r[1]; c++)+ setchar(treebuffer(tree), c);+ }+ return 1;+}+++/*+** Look-behind predicate+*/+static int lp_behind (lua_State *L) {+ TTree *tree;+ TTree *tree1 = getpatt(L, 1, NULL);+ int n = fixedlen(tree1);+ luaL_argcheck(L, n >= 0, 1, "pattern may not have fixed length");+ luaL_argcheck(L, !hascaptures(tree1), 1, "pattern have captures");+ luaL_argcheck(L, n <= MAXBEHIND, 1, "pattern too long to look behind");+ tree = newroot1sib(L, TBehind);+ tree->u.n = n;+ return 1;+}+++/*+** Create a non-terminal+*/+static int lp_V (lua_State *L) {+ TTree *tree = newleaf(L, TOpenCall);+ luaL_argcheck(L, !lua_isnoneornil(L, 1), 1, "non-nil value expected");+ tree->key = addtonewktable(L, 0, 1);+ return 1;+}+++/*+** Create a tree for a non-empty capture, with a body and+** optionally with an associated Lua value (at index 'labelidx' in the+** stack)+*/+static int capture_aux (lua_State *L, int cap, int labelidx) {+ TTree *tree = newroot1sib(L, TCapture);+ tree->cap = cap;+ tree->key = (labelidx == 0) ? 0 : addtonewktable(L, 1, labelidx);+ return 1;+}+++/*+** Fill a tree with an empty capture, using an empty (TTrue) sibling.+** (The 'key' field must be filled by the caller to finish the tree.)+*/+static TTree *auxemptycap (TTree *tree, int cap) {+ tree->tag = TCapture;+ tree->cap = cap;+ sib1(tree)->tag = TTrue;+ return tree;+}+++/*+** Create a tree for an empty capture.+*/+static TTree *newemptycap (lua_State *L, int cap, int key) {+ TTree *tree = auxemptycap(newtree(L, 2), cap);+ tree->key = key;+ return tree;+}+++/*+** Create a tree for an empty capture with an associated Lua value.+*/+static TTree *newemptycapkey (lua_State *L, int cap, int idx) {+ TTree *tree = auxemptycap(newtree(L, 2), cap);+ tree->key = addtonewktable(L, 0, idx);+ return tree;+}+++/*+** Captures with syntax p / v+** (function capture, query capture, string capture, or number capture)+*/+static int lp_divcapture (lua_State *L) {+ switch (lua_type(L, 2)) {+ case LUA_TFUNCTION: return capture_aux(L, Cfunction, 2);+ case LUA_TTABLE: return capture_aux(L, Cquery, 2);+ case LUA_TSTRING: return capture_aux(L, Cstring, 2);+ case LUA_TNUMBER: {+ int n = lua_tointeger(L, 2);+ TTree *tree = newroot1sib(L, TCapture);+ luaL_argcheck(L, 0 <= n && n <= SHRT_MAX, 1, "invalid number");+ tree->cap = Cnum;+ tree->key = n;+ return 1;+ }+ default: return luaL_argerror(L, 2, "invalid replacement value");+ }+}+++static int lp_substcapture (lua_State *L) {+ return capture_aux(L, Csubst, 0);+}+++static int lp_tablecapture (lua_State *L) {+ return capture_aux(L, Ctable, 0);+}+++static int lp_groupcapture (lua_State *L) {+ if (lua_isnoneornil(L, 2))+ return capture_aux(L, Cgroup, 0);+ else+ return capture_aux(L, Cgroup, 2);+}+++static int lp_foldcapture (lua_State *L) {+ luaL_checktype(L, 2, LUA_TFUNCTION);+ return capture_aux(L, Cfold, 2);+}+++static int lp_simplecapture (lua_State *L) {+ return capture_aux(L, Csimple, 0);+}+++static int lp_poscapture (lua_State *L) {+ newemptycap(L, Cposition, 0);+ return 1;+}+++static int lp_argcapture (lua_State *L) {+ int n = (int)luaL_checkinteger(L, 1);+ luaL_argcheck(L, 0 < n && n <= SHRT_MAX, 1, "invalid argument index");+ newemptycap(L, Carg, n);+ return 1;+}+++static int lp_backref (lua_State *L) {+ luaL_checkany(L, 1);+ newemptycapkey(L, Cbackref, 1);+ return 1;+}+++/*+** Constant capture+*/+static int lp_constcapture (lua_State *L) {+ int i;+ int n = lua_gettop(L); /* number of values */+ if (n == 0) /* no values? */+ newleaf(L, TTrue); /* no capture */+ else if (n == 1)+ newemptycapkey(L, Cconst, 1); /* single constant capture */+ else { /* create a group capture with all values */+ TTree *tree = newtree(L, 1 + 3 * (n - 1) + 2);+ newktable(L, n); /* create a 'ktable' for new tree */+ tree->tag = TCapture;+ tree->cap = Cgroup;+ tree->key = 0;+ tree = sib1(tree);+ for (i = 1; i <= n - 1; i++) {+ tree->tag = TSeq;+ tree->u.ps = 3; /* skip TCapture and its sibling */+ auxemptycap(sib1(tree), Cconst);+ sib1(tree)->key = addtoktable(L, i);+ tree = sib2(tree);+ }+ auxemptycap(tree, Cconst);+ tree->key = addtoktable(L, i);+ }+ return 1;+}+++static int lp_matchtime (lua_State *L) {+ TTree *tree;+ luaL_checktype(L, 2, LUA_TFUNCTION);+ tree = newroot1sib(L, TRunTime);+ tree->key = addtonewktable(L, 1, 2);+ return 1;+}++/* }====================================================== */+++/*+** {======================================================+** Grammar - Tree generation+** =======================================================+*/++/*+** push on the stack the index and the pattern for the+** initial rule of grammar at index 'arg' in the stack;+** also add that index into position table.+*/+static void getfirstrule (lua_State *L, int arg, int postab) {+ lua_rawgeti(L, arg, 1); /* access first element */+ if (lua_isstring(L, -1)) { /* is it the name of initial rule? */+ lua_pushvalue(L, -1); /* duplicate it to use as key */+ lua_gettable(L, arg); /* get associated rule */+ }+ else {+ lua_pushinteger(L, 1); /* key for initial rule */+ lua_insert(L, -2); /* put it before rule */+ }+ if (!testpattern(L, -1)) { /* initial rule not a pattern? */+ if (lua_isnil(L, -1))+ luaL_error(L, "grammar has no initial rule");+ else+ luaL_error(L, "initial rule '%s' is not a pattern", lua_tostring(L, -2));+ }+ lua_pushvalue(L, -2); /* push key */+ lua_pushinteger(L, 1); /* push rule position (after TGrammar) */+ lua_settable(L, postab); /* insert pair at position table */+}++/*+** traverse grammar at index 'arg', pushing all its keys and patterns+** into the stack. Create a new table (before all pairs key-pattern) to+** collect all keys and their associated positions in the final tree+** (the "position table").+** Return the number of rules and (in 'totalsize') the total size+** for the new tree.+*/+static int collectrules (lua_State *L, int arg, int *totalsize) {+ int n = 1; /* to count number of rules */+ int postab = lua_gettop(L) + 1; /* index of position table */+ int size; /* accumulator for total size */+ lua_newtable(L); /* create position table */+ getfirstrule(L, arg, postab);+ size = 2 + getsize(L, postab + 2); /* TGrammar + TRule + rule */+ lua_pushnil(L); /* prepare to traverse grammar table */+ while (lua_next(L, arg) != 0) {+ if (lua_tonumber(L, -2) == 1 ||+ lp_equal(L, -2, postab + 1)) { /* initial rule? */+ lua_pop(L, 1); /* remove value (keep key for lua_next) */+ continue;+ }+ if (!testpattern(L, -1)) /* value is not a pattern? */+ luaL_error(L, "rule '%s' is not a pattern", val2str(L, -2));+ luaL_checkstack(L, LUA_MINSTACK, "grammar has too many rules");+ lua_pushvalue(L, -2); /* push key (to insert into position table) */+ lua_pushinteger(L, size);+ lua_settable(L, postab);+ size += 1 + getsize(L, -1); /* update size */+ lua_pushvalue(L, -2); /* push key (for next lua_next) */+ n++;+ }+ *totalsize = size + 1; /* TTrue to finish list of rules */+ return n;+}+++static void buildgrammar (lua_State *L, TTree *grammar, int frule, int n) {+ int i;+ TTree *nd = sib1(grammar); /* auxiliary pointer to traverse the tree */+ for (i = 0; i < n; i++) { /* add each rule into new tree */+ int ridx = frule + 2*i + 1; /* index of i-th rule */+ int rulesize;+ TTree *rn = gettree(L, ridx, &rulesize);+ nd->tag = TRule;+ nd->key = 0; /* will be fixed when rule is used */+ nd->cap = i; /* rule number */+ nd->u.ps = rulesize + 1; /* point to next rule */+ memcpy(sib1(nd), rn, rulesize * sizeof(TTree)); /* copy rule */+ mergektable(L, ridx, sib1(nd)); /* merge its ktable into new one */+ nd = sib2(nd); /* move to next rule */+ }+ nd->tag = TTrue; /* finish list of rules */+}+++/*+** Check whether a tree has potential infinite loops+*/+static int checkloops (TTree *tree) {+ tailcall:+ if (tree->tag == TRep && nullable(sib1(tree)))+ return 1;+ else if (tree->tag == TGrammar)+ return 0; /* sub-grammars already checked */+ else {+ switch (numsiblings[tree->tag]) {+ case 1: /* return checkloops(sib1(tree)); */+ tree = sib1(tree); goto tailcall;+ case 2:+ if (checkloops(sib1(tree))) return 1;+ /* else return checkloops(sib2(tree)); */+ tree = sib2(tree); goto tailcall;+ default: assert(numsiblings[tree->tag] == 0); return 0;+ }+ }+}+++/*+** Give appropriate error message for 'verifyrule'. If a rule appears+** twice in 'passed', there is path from it back to itself without+** advancing the subject.+*/+static int verifyerror (lua_State *L, int *passed, int npassed) {+ int i, j;+ for (i = npassed - 1; i >= 0; i--) { /* search for a repetition */+ for (j = i - 1; j >= 0; j--) {+ if (passed[i] == passed[j]) {+ lua_rawgeti(L, -1, passed[i]); /* get rule's key */+ return luaL_error(L, "rule '%s' may be left recursive", val2str(L, -1));+ }+ }+ }+ return luaL_error(L, "too many left calls in grammar");+}+++/*+** Check whether a rule can be left recursive; raise an error in that+** case; otherwise return 1 iff pattern is nullable.+** The return value is used to check sequences, where the second pattern+** is only relevant if the first is nullable.+** Parameter 'nb' works as an accumulator, to allow tail calls in+** choices. ('nb' true makes function returns true.)+** Parameter 'passed' is a list of already visited rules, 'npassed'+** counts the elements in 'passed'.+** Assume ktable at the top of the stack.+*/+static int verifyrule (lua_State *L, TTree *tree, int *passed, int npassed,+ int nb) {+ tailcall:+ switch (tree->tag) {+ case TChar: case TSet: case TAny:+ case TFalse:+ return nb; /* cannot pass from here */+ case TTrue:+ case TBehind: /* look-behind cannot have calls */+ return 1;+ case TNot: case TAnd: case TRep:+ /* return verifyrule(L, sib1(tree), passed, npassed, 1); */+ tree = sib1(tree); nb = 1; goto tailcall;+ case TCapture: case TRunTime:+ /* return verifyrule(L, sib1(tree), passed, npassed, nb); */+ tree = sib1(tree); goto tailcall;+ case TCall:+ /* return verifyrule(L, sib2(tree), passed, npassed, nb); */+ tree = sib2(tree); goto tailcall;+ case TSeq: /* only check 2nd child if first is nb */+ if (!verifyrule(L, sib1(tree), passed, npassed, 0))+ return nb;+ /* else return verifyrule(L, sib2(tree), passed, npassed, nb); */+ tree = sib2(tree); goto tailcall;+ case TChoice: /* must check both children */+ nb = verifyrule(L, sib1(tree), passed, npassed, nb);+ /* return verifyrule(L, sib2(tree), passed, npassed, nb); */+ tree = sib2(tree); goto tailcall;+ case TRule:+ if (npassed >= MAXRULES)+ return verifyerror(L, passed, npassed);+ else {+ passed[npassed++] = tree->key;+ /* return verifyrule(L, sib1(tree), passed, npassed); */+ tree = sib1(tree); goto tailcall;+ }+ case TGrammar:+ return nullable(tree); /* sub-grammar cannot be left recursive */+ default: assert(0); return 0;+ }+}+++static void verifygrammar (lua_State *L, TTree *grammar) {+ int passed[MAXRULES];+ TTree *rule;+ /* check left-recursive rules */+ for (rule = sib1(grammar); rule->tag == TRule; rule = sib2(rule)) {+ if (rule->key == 0) continue; /* unused rule */+ verifyrule(L, sib1(rule), passed, 0, 0);+ }+ assert(rule->tag == TTrue);+ /* check infinite loops inside rules */+ for (rule = sib1(grammar); rule->tag == TRule; rule = sib2(rule)) {+ if (rule->key == 0) continue; /* unused rule */+ if (checkloops(sib1(rule))) {+ lua_rawgeti(L, -1, rule->key); /* get rule's key */+ luaL_error(L, "empty loop in rule '%s'", val2str(L, -1));+ }+ }+ assert(rule->tag == TTrue);+}+++/*+** Give a name for the initial rule if it is not referenced+*/+static void initialrulename (lua_State *L, TTree *grammar, int frule) {+ if (sib1(grammar)->key == 0) { /* initial rule is not referenced? */+ int n = lua_rawlen(L, -1) + 1; /* index for name */+ lua_pushvalue(L, frule); /* rule's name */+ lua_rawseti(L, -2, n); /* ktable was on the top of the stack */+ sib1(grammar)->key = n;+ }+}+++static TTree *newgrammar (lua_State *L, int arg) {+ int treesize;+ int frule = lua_gettop(L) + 2; /* position of first rule's key */+ int n = collectrules(L, arg, &treesize);+ TTree *g = newtree(L, treesize);+ luaL_argcheck(L, n <= MAXRULES, arg, "grammar has too many rules");+ g->tag = TGrammar; g->u.n = n;+ lua_newtable(L); /* create 'ktable' */+ lua_setuservalue(L, -2);+ buildgrammar(L, g, frule, n);+ lua_getuservalue(L, -1); /* get 'ktable' for new tree */+ finalfix(L, frule - 1, g, sib1(g));+ initialrulename(L, g, frule);+ verifygrammar(L, g);+ lua_pop(L, 1); /* remove 'ktable' */+ lua_insert(L, -(n * 2 + 2)); /* move new table to proper position */+ lua_pop(L, n * 2 + 1); /* remove position table + rule pairs */+ return g; /* new table at the top of the stack */+}++/* }====================================================== */+++static Instruction *prepcompile (lua_State *L, Pattern *p, int idx) {+ lua_getuservalue(L, idx); /* push 'ktable' (may be used by 'finalfix') */+ finalfix(L, 0, NULL, p->tree);+ lua_pop(L, 1); /* remove 'ktable' */+ return compile(L, p);+}+++static int lp_printtree (lua_State *L) {+ TTree *tree = getpatt(L, 1, NULL);+ int c = lua_toboolean(L, 2);+ if (c) {+ lua_getuservalue(L, 1); /* push 'ktable' (may be used by 'finalfix') */+ finalfix(L, 0, NULL, tree);+ lua_pop(L, 1); /* remove 'ktable' */+ }+ printktable(L, 1);+ printtree(tree, 0);+ return 0;+}+++static int lp_printcode (lua_State *L) {+ Pattern *p = getpattern(L, 1);+ printktable(L, 1);+ if (p->code == NULL) /* not compiled yet? */+ prepcompile(L, p, 1);+ printpatt(p->code, p->codesize);+ return 0;+}+++/*+** Get the initial position for the match, interpreting negative+** values from the end of the subject+*/+static size_t initposition (lua_State *L, size_t len) {+ lua_Integer ii = luaL_optinteger(L, 3, 1);+ if (ii > 0) { /* positive index? */+ if ((size_t)ii <= len) /* inside the string? */+ return (size_t)ii - 1; /* return it (corrected to 0-base) */+ else return len; /* crop at the end */+ }+ else { /* negative index */+ if ((size_t)(-ii) <= len) /* inside the string? */+ return len - ((size_t)(-ii)); /* return position from the end */+ else return 0; /* crop at the beginning */+ }+}+++/*+** Main match function+*/+static int lp_match (lua_State *L) {+ Capture capture[INITCAPSIZE];+ const char *r;+ size_t l;+ Pattern *p = (getpatt(L, 1, NULL), getpattern(L, 1));+ Instruction *code = (p->code != NULL) ? p->code : prepcompile(L, p, 1);+ const char *s = luaL_checklstring(L, SUBJIDX, &l);+ size_t i = initposition(L, l);+ int ptop = lua_gettop(L);+ lua_pushnil(L); /* initialize subscache */+ lua_pushlightuserdata(L, capture); /* initialize caplistidx */+ lua_getuservalue(L, 1); /* initialize penvidx */+ r = match(L, s, s + i, s + l, code, capture, ptop);+ if (r == NULL) {+ lua_pushnil(L);+ return 1;+ }+ return getcaptures(L, s, r, ptop);+}++++/*+** {======================================================+** Library creation and functions not related to matching+** =======================================================+*/++/* maximum limit for stack size */+#define MAXLIM (INT_MAX / 100)++static int lp_setmax (lua_State *L) {+ lua_Integer lim = luaL_checkinteger(L, 1);+ luaL_argcheck(L, 0 < lim && lim <= MAXLIM, 1, "out of range");+ lua_settop(L, 1);+ lua_setfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX);+ return 0;+}+++static int lp_version (lua_State *L) {+ lua_pushstring(L, VERSION);+ return 1;+}+++static int lp_type (lua_State *L) {+ if (testpattern(L, 1))+ lua_pushliteral(L, "pattern");+ else+ lua_pushnil(L);+ return 1;+}+++int lp_gc (lua_State *L) {+ Pattern *p = getpattern(L, 1);+ realloccode(L, p, 0); /* delete code block */+ return 0;+}+++static void createcat (lua_State *L, const char *catname, int (catf) (int)) {+ TTree *t = newcharset(L);+ int i;+ for (i = 0; i <= UCHAR_MAX; i++)+ if (catf(i)) setchar(treebuffer(t), i);+ lua_setfield(L, -2, catname);+}+++static int lp_locale (lua_State *L) {+ if (lua_isnoneornil(L, 1)) {+ lua_settop(L, 0);+ lua_createtable(L, 0, 12);+ }+ else {+ luaL_checktype(L, 1, LUA_TTABLE);+ lua_settop(L, 1);+ }+ createcat(L, "alnum", isalnum);+ createcat(L, "alpha", isalpha);+ createcat(L, "cntrl", iscntrl);+ createcat(L, "digit", isdigit);+ createcat(L, "graph", isgraph);+ createcat(L, "lower", islower);+ createcat(L, "print", isprint);+ createcat(L, "punct", ispunct);+ createcat(L, "space", isspace);+ createcat(L, "upper", isupper);+ createcat(L, "xdigit", isxdigit);+ return 1;+}+++static struct luaL_Reg pattreg[] = {+ {"ptree", lp_printtree},+ {"pcode", lp_printcode},+ {"match", lp_match},+ {"B", lp_behind},+ {"V", lp_V},+ {"C", lp_simplecapture},+ {"Cc", lp_constcapture},+ {"Cmt", lp_matchtime},+ {"Cb", lp_backref},+ {"Carg", lp_argcapture},+ {"Cp", lp_poscapture},+ {"Cs", lp_substcapture},+ {"Ct", lp_tablecapture},+ {"Cf", lp_foldcapture},+ {"Cg", lp_groupcapture},+ {"P", lp_P},+ {"S", lp_set},+ {"R", lp_range},+ {"locale", lp_locale},+ {"version", lp_version},+ {"setmaxstack", lp_setmax},+ {"type", lp_type},+ {NULL, NULL}+};+++static struct luaL_Reg metareg[] = {+ {"__mul", lp_seq},+ {"__add", lp_choice},+ {"__pow", lp_star},+ {"__gc", lp_gc},+ {"__len", lp_and},+ {"__div", lp_divcapture},+ {"__unm", lp_not},+ {"__sub", lp_sub},+ {NULL, NULL}+};+++int luaopen_lpeg (lua_State *L);+int luaopen_lpeg (lua_State *L) {+ luaL_newmetatable(L, PATTERN_T);+ lua_pushnumber(L, MAXBACK); /* initialize maximum backtracking */+ lua_setfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX);+ luaL_setfuncs(L, metareg, 0);+ luaL_newlib(L, pattreg);+ lua_pushvalue(L, -1);+ lua_setfield(L, -3, "__index");+ return 1;+}++/* }====================================================== */
+ cbits/lpeg-1.0.2/lptree.h view
@@ -0,0 +1,82 @@+/* +** $Id: lptree.h $+*/++#if !defined(lptree_h)+#define lptree_h+++#include "lptypes.h" +++/*+** types of trees+*/+typedef enum TTag {+ TChar = 0, /* 'n' = char */+ TSet, /* the set is stored in next CHARSETSIZE bytes */+ TAny,+ TTrue,+ TFalse,+ TRep, /* 'sib1'* */+ TSeq, /* 'sib1' 'sib2' */+ TChoice, /* 'sib1' / 'sib2' */+ TNot, /* !'sib1' */+ TAnd, /* &'sib1' */+ TCall, /* ktable[key] is rule's key; 'sib2' is rule being called */+ TOpenCall, /* ktable[key] is rule's key */+ TRule, /* ktable[key] is rule's key (but key == 0 for unused rules);+ 'sib1' is rule's pattern;+ 'sib2' is next rule; 'cap' is rule's sequential number */+ TGrammar, /* 'sib1' is initial (and first) rule */+ TBehind, /* 'sib1' is pattern, 'n' is how much to go back */+ TCapture, /* captures: 'cap' is kind of capture (enum 'CapKind');+ ktable[key] is Lua value associated with capture;+ 'sib1' is capture body */+ TRunTime /* run-time capture: 'key' is Lua function;+ 'sib1' is capture body */+} TTag;+++/*+** Tree trees+** The first child of a tree (if there is one) is immediately after+** the tree. A reference to a second child (ps) is its position+** relative to the position of the tree itself.+*/+typedef struct TTree {+ byte tag;+ byte cap; /* kind of capture (if it is a capture) */+ unsigned short key; /* key in ktable for Lua data (0 if no key) */+ union {+ int ps; /* occasional second child */+ int n; /* occasional counter */+ } u;+} TTree;+++/*+** A complete pattern has its tree plus, if already compiled,+** its corresponding code+*/+typedef struct Pattern {+ union Instruction *code;+ int codesize;+ TTree tree[1];+} Pattern;+++/* number of children for each tree */+extern const byte numsiblings[];++/* access to children */+#define sib1(t) ((t) + 1)+#define sib2(t) ((t) + (t)->u.ps)+++++++#endif+
+ cbits/lpeg-1.0.2/lptypes.h view
@@ -0,0 +1,145 @@+/*+** $Id: lptypes.h $+** LPeg - PEG pattern matching for Lua+** Copyright 2007-2019, Lua.org & PUC-Rio (see 'lpeg.html' for license)+** written by Roberto Ierusalimschy+*/++#if !defined(lptypes_h)+#define lptypes_h+++#include <assert.h>+#include <limits.h>++#include "lua.h"+++#define VERSION "1.0.2"+++#define PATTERN_T "lpeg-pattern"+#define MAXSTACKIDX "lpeg-maxstack"+++/*+** compatibility with Lua 5.1+*/+#if (LUA_VERSION_NUM == 501)++#define lp_equal lua_equal++#define lua_getuservalue lua_getfenv+#define lua_setuservalue lua_setfenv++#define lua_rawlen lua_objlen++#define luaL_setfuncs(L,f,n) luaL_register(L,NULL,f)+#define luaL_newlib(L,f) luaL_register(L,"lpeg",f)++#endif+++#if !defined(lp_equal)+#define lp_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ)+#endif+++/* default maximum size for call/backtrack stack */+#if !defined(MAXBACK)+#define MAXBACK 400+#endif+++/* maximum number of rules in a grammar (limited by 'unsigned char') */+#if !defined(MAXRULES)+#define MAXRULES 250+#endif++++/* initial size for capture's list */+#define INITCAPSIZE 32+++/* index, on Lua stack, for subject */+#define SUBJIDX 2++/* number of fixed arguments to 'match' (before capture arguments) */+#define FIXEDARGS 3++/* index, on Lua stack, for capture list */+#define caplistidx(ptop) ((ptop) + 2)++/* index, on Lua stack, for pattern's ktable */+#define ktableidx(ptop) ((ptop) + 3)++/* index, on Lua stack, for backtracking stack */+#define stackidx(ptop) ((ptop) + 4)++++typedef unsigned char byte;+++#define BITSPERCHAR 8++#define CHARSETSIZE ((UCHAR_MAX/BITSPERCHAR) + 1)++++typedef struct Charset {+ byte cs[CHARSETSIZE];+} Charset;++++#define loopset(v,b) { int v; for (v = 0; v < CHARSETSIZE; v++) {b;} }++/* access to charset */+#define treebuffer(t) ((byte *)((t) + 1))++/* number of slots needed for 'n' bytes */+#define bytes2slots(n) (((n) - 1) / sizeof(TTree) + 1)++/* set 'b' bit in charset 'cs' */+#define setchar(cs,b) ((cs)[(b) >> 3] |= (1 << ((b) & 7)))+++/*+** in capture instructions, 'kind' of capture and its offset are+** packed in field 'aux', 4 bits for each+*/+#define getkind(op) ((op)->i.aux & 0xF)+#define getoff(op) (((op)->i.aux >> 4) & 0xF)+#define joinkindoff(k,o) ((k) | ((o) << 4))++#define MAXOFF 0xF+#define MAXAUX 0xFF+++/* maximum number of bytes to look behind */+#define MAXBEHIND MAXAUX+++/* maximum size (in elements) for a pattern */+#define MAXPATTSIZE (SHRT_MAX - 10)+++/* size (in elements) for an instruction plus extra l bytes */+#define instsize(l) (((l) + sizeof(Instruction) - 1)/sizeof(Instruction) + 1)+++/* size (in elements) for a ISet instruction */+#define CHARSETINSTSIZE instsize(CHARSETSIZE)++/* size (in elements) for a IFunc instruction */+#define funcinstsize(p) ((p)->i.aux + 2)++++#define testchar(st,c) (((int)(st)[((c) >> 3)] & (1 << ((c) & 7))))+++#endif+
+ cbits/lpeg-1.0.2/lpvm.c view
@@ -0,0 +1,374 @@+/*+** $Id: lpvm.c $+** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)+*/++#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}};+++/*+** {======================================================+** 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.)+*/+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;+ int newsize = captop + n + 1; /* minimum size needed */+ if (newsize < INT_MAX/((int)sizeof(Capture) * 2))+ newsize *= 2; /* twice that size, if not too big */+ else if (newsize >= INT_MAX/((int)sizeof(Capture)))+ 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 (const char *s, 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].s = s;+ }+ capture[n].kind = Cclose; /* close group */+ capture[n].siz = 1;+ capture[n].s = s;+}+++/*+** 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 */+}+++/*+** 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].s = NULL;+ 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 ITestAny: {+ if (s < e) p += 2;+ else p += getoffset(p);+ continue;+ }+ case IChar: {+ if ((byte)*s == p->i.aux && s < e) { p++; s++; }+ else goto fail;+ continue;+ }+ case ITestChar: {+ if ((byte)*s == p->i.aux && s < e) p += 2;+ else p += getoffset(p);+ continue;+ }+ case ISet: {+ int c = (byte)*s;+ if (testchar((p+1)->buff, c) && s < e)+ { p += CHARSETINSTSIZE; s++; }+ else goto fail;+ continue;+ }+ case ITestSet: {+ int c = (byte)*s;+ if (testchar((p + 2)->buff, c) && s < e)+ p += 1 + CHARSETINSTSIZE;+ else p += getoffset(p);+ continue;+ }+ case IBehind: {+ int n = p->i.aux;+ if (n > s - o) goto fail;+ s -= n; p++;+ continue;+ }+ case ISpan: {+ for (; s < e; s++) {+ int c = (byte)*s;+ if (!testchar((p+1)->buff, c)) break;+ }+ p += CHARSETINSTSIZE;+ 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;+ captop = stack->caplevel;+ p += getoffset(p);+ continue;+ }+ case IFailTwice:+ assert(stack > getstackbase(L, ptop));+ stack--;+ /* go through */+ 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, capture + captop, n, fr);+ captop += n + 1; /* new captures + close group */+ }+ p++;+ continue;+ }+ case ICloseCapture: {+ const char *s1 = s;+ assert(captop > 0);+ /* if possible, turn capture into a full capture */+ if (capture[captop - 1].siz == 0 &&+ s1 - capture[captop - 1].s < UCHAR_MAX) {+ capture[captop - 1].siz = s1 - capture[captop - 1].s + 1;+ p++;+ continue;+ }+ else {+ capture[captop].siz = 1; /* mark entry as closed */+ capture[captop].s = s;+ goto pushcapture;+ }+ }+ case IOpenCapture:+ capture[captop].siz = 0; /* mark entry as open */+ capture[captop].s = s;+ goto pushcapture;+ case IFullCapture:+ capture[captop].siz = getoff(p) + 1; /* save capture size */+ capture[captop].s = s - getoff(p);+ /* goto pushcapture; */+ pushcapture: {+ capture[captop].idx = p->i.key;+ capture[captop].kind = getkind(p);+ captop++;+ capture = growcap(L, capture, &capsize, captop, 0, ptop);+ p++;+ continue;+ }+ default: assert(0); return NULL;+ }+ }+}++/* }====================================================== */++
+ cbits/lpeg-1.0.2/lpvm.h view
@@ -0,0 +1,58 @@+/*+** $Id: lpvm.h $+*/++#if !defined(lpvm_h)+#define lpvm_h++#include "lpcap.h"+++/* Virtual Machine's instructions */+typedef enum Opcode {+ IAny, /* if no char, fail */+ IChar, /* if char != aux, fail */+ ISet, /* if char not in buff, fail */+ ITestAny, /* in no char, jump to 'offset' */+ ITestChar, /* if char != aux, jump to 'offset' */+ ITestSet, /* if char not in buff, jump to 'offset' */+ ISpan, /* read a span of chars in buff */+ IBehind, /* walk back 'aux' characters (fail if not possible) */+ IRet, /* return from a rule */+ IEnd, /* end of pattern */+ IChoice, /* stack a choice; next fail will jump to 'offset' */+ IJmp, /* jump to 'offset' */+ ICall, /* call rule at 'offset' */+ IOpenCall, /* call rule number 'key' (must be closed to a ICall) */+ ICommit, /* pop choice and jump to 'offset' */+ IPartialCommit, /* update top choice to current position and jump */+ IBackCommit, /* "fails" but jump to its own 'offset' */+ IFailTwice, /* pop one choice and then fail */+ IFail, /* go back to saved state on choice and jump to saved offset */+ IGiveup, /* internal use */+ IFullCapture, /* complete capture of last 'off' chars */+ IOpenCapture, /* start a capture */+ ICloseCapture,+ ICloseRunTime+} Opcode;++++typedef union Instruction {+ struct Inst {+ byte code;+ byte aux;+ short key;+ } i;+ int offset;+ byte buff[1];+} Instruction;+++void printpatt (Instruction *p, int n);+const char *match (lua_State *L, const char *o, const char *s, const char *e,+ Instruction *op, Capture *capture, int ptop);+++#endif+
+ cbits/placeholder.c view
@@ -0,0 +1,12 @@+#include <string.h>++#include "lua.h"++int luaopen_lpeg (lua_State *L);+int luaopen_lpeg (lua_State *L)+{+ const char *msg =+ lua_pushliteral(L, "Non-functional placeholder. Configuration prevented "+ "the inclusion of the real LPeg package.");+ lua_error(L);+}
+ lpeg.cabal view
@@ -0,0 +1,89 @@+cabal-version: 2.2+name: lpeg+version: 1.0.0+synopsis: LPeg – Parsing Expression Grammars For Lua+description: This package contains the C sources of LPeg, as well+ as some tiny Haskell helper to load the package.+ .+ <http://www.inf.puc-rio.br/~roberto/lpeg/>+homepage: https://hslua.org/+bug-reports: https://github.com/hslua/hslua/issues+license: MIT+license-file: LICENSE+author: Albert Krewinkel+maintainer: Albert Krewinkel <albert+hslua@zeitkraut.de>+copyright: © 2007-2019 Lua.org, PUC-Rio;+ © 2021 Albert Krewinkel+category: Foreign+build-type: Simple+extra-source-files: cbits/lpeg-1.0.2/*.h+ , README.md+ , CHANGELOG.md+tested-with: GHC == 8.0.2+ , GHC == 8.2.2+ , GHC == 8.4.4+ , GHC == 8.6.5+ , GHC == 8.8.4+ , GHC == 8.10.7+ , GHC == 9.0.1++source-repository head+ type: git+ location: https://github.com/hslua/hslua.git+ subdir: lpeg++flag rely-on-shared-lpeg-library+ default: False+ manual: True+ description: Do not build lpeg from source, but trust that lpeg is+ made available in other ways, e.g., through a+ system-wide installation. The loader will be replaced+ with a placeholder function that only emits a message.++common common-options+ default-language: Haskell2010+ build-depends: base >= 4.8 && < 5+ , lua >= 2.0 && < 2.1+ ghc-options: -Wall+ -Wincomplete-record-updates+ -Wnoncanonical-monad-instances+ -Wredundant-constraints+ if impl(ghc >= 8.2)+ ghc-options: -Wcpp-undef+ -Werror=missing-home-modules+ if impl(ghc >= 8.4)+ ghc-options: -Widentities+ -Wincomplete-uni-patterns+ -Wpartial-fields+ -fhide-source-paths++library+ import: common-options+ exposed-modules: Lua.LPeg+ default-extensions: ForeignFunctionInterface++ if flag(rely-on-shared-lpeg-library)+ hs-source-dirs: substitute+ c-sources: cbits/placeholder.c+ cpp-options: -DRELY_ON_SYSTEM_INSTALL+ else+ hs-source-dirs: src+ include-dirs: cbits/lpeg-1.0.2+ c-sources: cbits/lpeg-1.0.2/lpvm.c+ , cbits/lpeg-1.0.2/lptree.c+ , cbits/lpeg-1.0.2/lpprint.c+ , cbits/lpeg-1.0.2/lpcode.c+ , cbits/lpeg-1.0.2/lpcap.c+ cc-options: -std=c99 -fPIC++test-suite test-lpeg+ import: common-options+ type: exitcode-stdio-1.0+ main-is: test-lpeg.hs+ hs-source-dirs: test+ build-depends: lpeg+ , tasty >= 0.11+ , tasty-hunit >= 0.9+ other-extensions: CPP+ if flag(rely-on-shared-lpeg-library)+ cpp-options: -DRELY_ON_SYSTEM_INSTALL
+ src/Lua/LPeg.hs view
@@ -0,0 +1,42 @@+{-|+Module : Lua.LPeg+Copyright : © 2021 Albert Krewinkel+License : MIT+Maintainer : Albert Krewinkel <tarleb+hslua@zeitkraut.de>++Haskell bindings to the LPeg Lua package.+-}+module Lua.LPeg+ ( luaopen_lpeg_ptr+ , lpeg_searcher+ ) where++import Foreign.C (peekCStringLen)+import Foreign.Ptr (nullPtr)+import Foreign.Storable (peek)+import Foreign.Marshal (alloca)+import Lua++-- | Pointer to the function which loads the lpeg library.+foreign import ccall unsafe "lptree.c &luaopen_lpeg"+ luaopen_lpeg_ptr :: CFunction++-- | Name under which the module is loaded.+moduleName :: String+moduleName = "lpeg"++-- | A package searcher to be used with @package.searchers@), just for+-- the "lpeg" module. Returns @nil@ on most inputs, but pushes a+-- function that loads the LPeg module when called with key @"lpeg"@.+lpeg_searcher :: PreCFunction+lpeg_searcher l = 1 <$ do+ alloca $ \lenPtr -> do+ cstr <- lua_tolstring l 1 lenPtr+ if cstr == nullPtr+ then lua_pushnil l+ else do+ cstrLen <- peek lenPtr+ pkg <- peekCStringLen (cstr, fromIntegral cstrLen)+ if pkg /= moduleName+ then lua_pushnil l+ else lua_pushcclosure l luaopen_lpeg_ptr 0
+ substitute/Lua/LPeg.hs view
@@ -0,0 +1,46 @@+{-|+Module : Lua.LPeg+Copyright : © 2021 Albert Krewinkel+License : MIT+Maintainer : Albert Krewinkel <tarleb+hslua@zeitkraut.de>++Haskell bindings to the LPeg Lua package.+-}+module Lua.LPeg+ ( luaopen_lpeg_ptr+ , lpeg_searcher+ ) where++import Foreign.C+import Foreign.Ptr (nullPtr)+import Foreign.Storable (peek)+import Foreign.Marshal (alloca)+import Lua++-- | Pointer to the placeholder library loader. Trying to load the+-- library will result in an error.+foreign import ccall unsafe "placeholder.c &luaopen_lpeg"+ luaopen_lpeg_ptr :: CFunction++-- | Placeholder package searcher to be used with @package.searchers@),+-- just for the "lpeg" module. Returns @nil@ on most inputs, but pushes+-- adds a message about the module being unavailable when called on the+-- search key "lpeg".+lpeg_searcher :: PreCFunction+lpeg_searcher l = do+ alloca $ \lenPtr -> do+ cstr <- lua_tolstring l 1 lenPtr+ if cstr == nullPtr+ then 1 <$ lua_pushnil l+ else do+ cstrLen <- peek lenPtr+ pkg <- peekCStringLen (cstr, fromIntegral cstrLen)+ if pkg /= moduleName+ then 1 <$ lua_pushnil l+ else 1 <$ withCString msg (lua_pushstring l)+ where+ msg = "\n\tlpeg was configured to be excluded from the binary."++-- | Name under which the module is loaded.+moduleName :: String+moduleName = "lpeg"
+ test/test-lpeg.hs view
@@ -0,0 +1,145 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -Wno-warnings-deprecations #-}+{-|+Module : Main+Copyright : © 2021 Albert Krewinkel+License : MIT+Maintainer : Albert Krewinkel <albert+hslua@zeitkraut.de>++Tests for the @lpeg@ Lua package.+-}+module Main where++import Control.Monad (void, when)+import Foreign.C+import Foreign.Ptr (nullPtr)+import Lua+import Lua.LPeg+import Test.Tasty (TestTree, defaultMain, testGroup)+import Test.Tasty.HUnit (assertEqual, testCase)++main :: IO ()+main = defaultMain tests++tests :: TestTree+tests = testGroup "LPeg" $+ -- tests to check the correct types to make sure placeholder and the real+ -- thing don't diverge.+ [ testCase "can push loader" $ do+ l <- hsluaL_newstate+ lua_pushcclosure l luaopen_lpeg_ptr 0+ assertEqual "loader should be a function"+ LUA_TFUNCTION =<< lua_type l (-1)+ lua_close l+ , testCase "can push searcher" $ do+ l <- hsluaL_newstate+ hslua_pushhsfunction l lpeg_searcher+ assertEqual "searcher should be a function"+ LUA_TFUNCTION =<< lua_type l (-1)+ lua_close l+ ] +++#ifndef RELY_ON_SYSTEM_INSTALL+ [ testCase "load library via CFunction" $ do+ l <- hsluaL_newstate+ lua_pushcclosure l luaopen_lpeg_ptr 0+ stts <- lua_pcall l 0 1 0+ when (stts /= LUA_OK) $+ fail =<< peekCString =<< lua_tolstring l (-1) nullPtr++ _ <- withCString "version" $ lua_pushstring l+ lua_rawget l (-2)+ assertEqual "module should have `version` field of type function"+ LUA_TFUNCTION =<< lua_type l (-1)+ lua_close l++ , testCase "load library manually" $ do+ l <- hsluaL_newstate+ luaL_openlibs l+ -- get table "_LOADED" from registry+ _ <- withCString loadedTableRegistryField $ lua_pushstring l+ lua_rawget l LUA_REGISTRYINDEX++ _ <- withCString "lpeg" $ lua_pushstring l -- key+ lua_pushcclosure l luaopen_lpeg_ptr 0+ stts <- lua_pcall l 0 1 0+ when (stts /= LUA_OK) $+ fail =<< peekCString =<< lua_tolstring l (-1) nullPtr++ runTestScript l++ lua_close l++ , testCase "use lpeg_searcher as searcher" $ do+ l <- hsluaL_newstate+ luaL_openlibs l+ _ <- withCString "package" $ lua_getglobal l+ pushstring l "searchers"+ _ <- lua_gettable l (-2)+ hslua_pushhsfunction l lpeg_searcher+ lua_rawseti l (-2) 2+ -- remove other searchers+ lua_pushnil l *> lua_rawseti l (-2) 3+ lua_pushnil l *> lua_rawseti l (-2) 4+ lua_pop l 2++ runTestScript l++ lua_close l+ ]+ where+ runTestScript l = do+ stts <- withCStringLen testScript $ \(s, slen) ->+ withCString "test script" $ \n ->+ luaL_loadbuffer l s (fromIntegral slen) n+ when (stts /= LUA_OK) $ do+ fail =<< peekCString =<< lua_tolstring l (-1) nullPtr++testScript :: String+testScript = unlines+ [ "local m = require'lpeg'"+ , "assert(type(m.version()) == 'string')"+ , "assert(m.type(m.P'alo') == 'pattern')"+ , "assert(m.match('a' * m.P(true), 'a') == 2)"+ , "print 'HI MOM'"+ ]++#else+ [ testCase "loading the placeholder library causes an error" $ do+ l <- hsluaL_newstate+ lua_pushcclosure l luaopen_lpeg_ptr 0+ stts <- lua_pcall l 0 1 0+ stts `seq` lua_close l+ when (stts /= LUA_ERRRUN) $+ fail "library loading should have failed with LUA_ERRRUN"++ , testCase "searcher returns message" $ do+ l <- hsluaL_newstate+ luaL_openlibs l+ _ <- withCString "package" $ lua_getglobal l+ pushstring l "searchers"+ _ <- lua_gettable l (-2)+ hslua_pushhsfunction l lpeg_searcher+ lua_rawseti l (-2) 1+ -- remove other searchers+ lua_pushnil l *> lua_rawseti l (-2) 2+ lua_pushnil l *> lua_rawseti l (-2) 3+ lua_pushnil l *> lua_rawseti l (-2) 4+ lua_pop l 2 -- tables 'package' and 'searchers'++ stts <- withCStringLen "require 'lpeg'" $ \(s, slen) ->+ withCString "test script" $ \n ->+ luaL_loadbuffer l s (fromIntegral slen) n+ when (stts /= LUA_OK) $ do+ fail =<< peekCString =<< lua_tolstring l (-1) nullPtr+ callStatus <- lua_pcall l 0 0 0+ case callStatus of+ LUA_OK -> fail "require should have failed"+ LUA_ERRRUN -> return ()+ _ -> fail =<< peekCString =<< lua_tolstring l (-1) nullPtr++ lua_close l+ ]+#endif++pushstring :: State -> String -> IO ()+pushstring l s = void $ withCString s $ lua_pushstring l