update lpeg to 1.0.0

This commit is contained in:
Cloud Wu
2015-10-05 09:56:12 +08:00
parent 3a3159d429
commit 97fd5053c9
13 changed files with 376 additions and 259 deletions

View File

@@ -1,4 +1,10 @@
HISTORY for LPeg 0.12 HISTORY for LPeg 1.0
* Changes from version 0.12 to 1.0
---------------------------------
+ group "names" can be any Lua value
+ some bugs fixed
+ other small improvements
* Changes from version 0.11 to 0.12 * Changes from version 0.11 to 0.12
--------------------------------- ---------------------------------

View File

@@ -1,5 +1,5 @@
/* /*
** $Id: lpcap.c,v 1.5 2014/12/12 16:58:47 roberto Exp $ ** $Id: lpcap.c,v 1.6 2015/06/15 16:09:57 roberto Exp $
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license) ** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
*/ */
@@ -126,7 +126,7 @@ static Capture *findback (CapState *cs, Capture *cap) {
continue; /* opening an enclosing capture: skip and get previous */ continue; /* opening an enclosing capture: skip and get previous */
if (captype(cap) == Cgroup) { if (captype(cap) == Cgroup) {
getfromktable(cs, cap->idx); /* get group name */ getfromktable(cs, cap->idx); /* get group name */
if (lua_equal(L, -2, -1)) { /* right group? */ if (lp_equal(L, -2, -1)) { /* right group? */
lua_pop(L, 2); /* remove reference name and group name */ lua_pop(L, 2); /* remove reference name and group name */
return cap; return cap;
} }

View File

@@ -1,5 +1,5 @@
/* /*
** $Id: lpcap.h,v 1.1 2013/03/21 20:25:12 roberto Exp $ ** $Id: lpcap.h,v 1.2 2015/02/27 17:13:17 roberto Exp $
*/ */
#if !defined(lpcap_h) #if !defined(lpcap_h)
@@ -18,7 +18,7 @@ typedef enum CapKind {
typedef struct Capture { typedef struct Capture {
const char *s; /* subject position */ const char *s; /* subject position */
short idx; /* extra info about capture (group name, arg index, etc.) */ unsigned short idx; /* extra info (group name, arg index, etc.) */
byte kind; /* kind of capture */ byte kind; /* kind of capture */
byte siz; /* size of full capture + 1 (0 = not a full capture) */ byte siz; /* size of full capture + 1 (0 = not a full capture) */
} Capture; } Capture;

View File

@@ -1,5 +1,5 @@
/* /*
** $Id: lpcode.c,v 1.21 2014/12/12 17:01:29 roberto Exp $ ** $Id: lpcode.c,v 1.23 2015/06/12 18:36:47 roberto Exp $
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license) ** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
*/ */
@@ -431,11 +431,11 @@ typedef struct CompileState {
/* /*
** code generation is recursive; 'opt' indicates that the code is ** code generation is recursive; 'opt' indicates that the code is being
** being generated under a 'IChoice' operator jumping to its end ** generated as the last thing inside an optional pattern (so, if that
** (that is, the match is "optional"). ** code is optional too, it can reuse the 'IChoice' already in place for
** 'tt' points to a previous test protecting this code. 'fl' is ** the outer pattern). 'tt' points to a previous test protecting this
** the follow set of the pattern. ** code (or NOINST). 'fl' is the follow set of the pattern.
*/ */
static void codegen (CompileState *compst, TTree *tree, int opt, int tt, static void codegen (CompileState *compst, TTree *tree, int opt, int tt,
const Charset *fl); const Charset *fl);
@@ -638,13 +638,13 @@ static void codebehind (CompileState *compst, TTree *tree) {
/* /*
** Choice; optimizations: ** Choice; optimizations:
** - when p1 is headfail ** - when p1 is headfail or
** - when first(p1) and first(p2) are disjoint; than ** when first(p1) and first(p2) are disjoint, than
** a character not in first(p1) cannot go to p1, and a character ** 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)). ** 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, ** (The optimization is not valid if p1 accepts the empty string,
** as then there is no character at all...) ** as then there is no character at all...)
** - when p2 is empty and opt is true; a IPartialCommit can resuse ** - when p2 is empty and opt is true; a IPartialCommit can reuse
** the Choice already active in the stack. ** the Choice already active in the stack.
*/ */
static void codechoice (CompileState *compst, TTree *p1, TTree *p2, int opt, static void codechoice (CompileState *compst, TTree *p1, TTree *p2, int opt,
@@ -671,7 +671,7 @@ static void codechoice (CompileState *compst, TTree *p1, TTree *p2, int opt,
} }
else { else {
/* <p1 / p2> == /* <p1 / p2> ==
test(fail(p1)) -> L1; choice L1; <p1>; commit L2; L1: <p2>; L2: */ test(first(p1)) -> L1; choice L1; <p1>; commit L2; L1: <p2>; L2: */
int pcommit; int pcommit;
int test = codetestset(compst, &cs1, e1); int test = codetestset(compst, &cs1, e1);
int pchoice = addoffsetinst(compst, IChoice); int pchoice = addoffsetinst(compst, IChoice);
@@ -759,7 +759,7 @@ static void coderep (CompileState *compst, TTree *tree, int opt,
/* L1: test (fail(p1)) -> L2; <p>; jmp L1; L2: */ /* L1: test (fail(p1)) -> L2; <p>; jmp L1; L2: */
int jmp; int jmp;
int test = codetestset(compst, &st, 0); int test = codetestset(compst, &st, 0);
codegen(compst, tree, opt, test, fullset); codegen(compst, tree, 0, test, fullset);
jmp = addoffsetinst(compst, IJmp); jmp = addoffsetinst(compst, IJmp);
jumptohere(compst, test); jumptohere(compst, test);
jumptothere(compst, jmp, test); jumptothere(compst, jmp, test);

View File

@@ -1,5 +1,5 @@
/* /*
** $Id: lpcode.h,v 1.6 2013/11/28 14:56:02 roberto Exp $ ** $Id: lpcode.h,v 1.7 2015/06/12 18:24:45 roberto Exp $
*/ */
#if !defined(lpcode_h) #if !defined(lpcode_h)
@@ -24,7 +24,15 @@ int sizei (const Instruction *i);
#define PEnullable 0 #define PEnullable 0
#define PEnofail 1 #define PEnofail 1
/*
** nofail(t) implies that 't' cannot fail with any input
*/
#define nofail(t) checkaux(t, PEnofail) #define nofail(t) checkaux(t, PEnofail)
/*
** (not nullable(t)) implies 't' cannot match without consuming
** something
*/
#define nullable(t) checkaux(t, PEnullable) #define nullable(t) checkaux(t, PEnullable)
#define fixedlen(t) fixedlenx(t, 0, 0) #define fixedlen(t) fixedlenx(t, 0, 0)

View File

@@ -10,7 +10,7 @@
</head> </head>
<body> <body>
<!-- $Id: lpeg.html,v 1.72 2014/12/12 17:11:35 roberto Exp $ --> <!-- $Id: lpeg.html,v 1.75 2015/09/28 17:17:41 roberto Exp $ -->
<div id="container"> <div id="container">
@@ -22,7 +22,7 @@
</div> </div>
<div id="product_name"><big><strong>LPeg</strong></big></div> <div id="product_name"><big><strong>LPeg</strong></big></div>
<div id="product_description"> <div id="product_description">
Parsing Expression Grammars For Lua, version 0.12 Parsing Expression Grammars For Lua, version 1.0
</div> </div>
</div> <!-- id="product" --> </div> <!-- id="product" -->
@@ -195,13 +195,16 @@ Returns a string with the running version of LPeg.
<h3><a name="f-setstack"></a><code>lpeg.setmaxstack (max)</code></h3> <h3><a name="f-setstack"></a><code>lpeg.setmaxstack (max)</code></h3>
<p> <p>
Sets the maximum size for the backtrack stack used by LPeg to Sets a limit for the size of the backtrack stack used by LPeg to
track calls and choices. track calls and choices.
(The default limit is 400.)
Most well-written patterns need little backtrack levels and Most well-written patterns need little backtrack levels and
therefore you seldom need to change this maximum; therefore you seldom need to change this limit;
but a few useful patterns may need more space. before changing it you should try to rewrite your
Before changing this maximum you should try to rewrite your
pattern to avoid the need for extra space. pattern to avoid the need for extra space.
Nevertheless, a few useful patterns may overflow.
Also, with recursive grammars,
subjects with deep recursion may also need larger limits.
</p> </p>
@@ -682,7 +685,8 @@ argument given in the call to <code>lpeg.match</code>.
Creates a <em>back capture</em>. Creates a <em>back capture</em>.
This pattern matches the empty string and This pattern matches the empty string and
produces the values produced by the <em>most recent</em> produces the values produced by the <em>most recent</em>
<a href="#cap-g">group capture</a> named <code>name</code>. <a href="#cap-g">group capture</a> named <code>name</code>
(where <code>name</code> can be any Lua value).
</p> </p>
<p> <p>
@@ -762,7 +766,8 @@ Creates a <em>group capture</em>.
It groups all values returned by <code>patt</code> It groups all values returned by <code>patt</code>
into a single capture. into a single capture.
The group may be anonymous (if no name is given) The group may be anonymous (if no name is given)
or named with the given name. or named with the given name
(which can be any non-nil Lua value).
</p> </p>
<p> <p>
@@ -1375,13 +1380,13 @@ and the new term for each repetition.
<h2><a name="download"></a>Download</h2> <h2><a name="download"></a>Download</h2>
<p>LPeg <p>LPeg
<a href="http://www.inf.puc-rio.br/~roberto/lpeg/lpeg-0.12.1.tar.gz">source code</a>.</p> <a href="http://www.inf.puc-rio.br/~roberto/lpeg/lpeg-1.0.0.tar.gz">source code</a>.</p>
<h2><a name="license">License</a></h2> <h2><a name="license">License</a></h2>
<p> <p>
Copyright &copy; 2014 Lua.org, PUC-Rio. Copyright &copy; 2007-2015 Lua.org, PUC-Rio.
</p> </p>
<p> <p>
Permission is hereby granted, free of charge, Permission is hereby granted, free of charge,
@@ -1419,7 +1424,7 @@ THE SOFTWARE.
<div id="about"> <div id="about">
<p><small> <p><small>
$Id: lpeg.html,v 1.72 2014/12/12 17:11:35 roberto Exp $ $Id: lpeg.html,v 1.75 2015/09/28 17:17:41 roberto Exp $
</small></p> </small></p>
</div> <!-- id="about" --> </div> <!-- id="about" -->

View File

@@ -1,5 +1,5 @@
/* /*
** $Id: lpprint.c,v 1.7 2013/04/12 16:29:49 roberto Exp $ ** $Id: lpprint.c,v 1.9 2015/06/15 16:09:57 roberto Exp $
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license) ** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
*/ */
@@ -52,7 +52,7 @@ static void printjmp (const Instruction *op, const Instruction *p) {
} }
static void printinst (const Instruction *op, const Instruction *p) { void printinst (const Instruction *op, const Instruction *p) {
const char *const names[] = { const char *const names[] = {
"any", "char", "set", "any", "char", "set",
"testany", "testchar", "testset", "testany", "testchar", "testset",
@@ -221,10 +221,10 @@ void printtree (TTree *tree, int ident) {
void printktable (lua_State *L, int idx) { void printktable (lua_State *L, int idx) {
int n, i; int n, i;
lua_getfenv(L, idx); lua_getuservalue(L, idx);
if (lua_isnil(L, -1)) /* no ktable? */ if (lua_isnil(L, -1)) /* no ktable? */
return; return;
n = lua_objlen(L, -1); n = lua_rawlen(L, -1);
printf("["); printf("[");
for (i = 1; i <= n; i++) { for (i = 1; i <= n; i++) {
printf("%d = ", i); printf("%d = ", i);

View File

@@ -1,5 +1,5 @@
/* /*
** $Id: lpprint.h,v 1.1 2013/03/21 20:25:12 roberto Exp $ ** $Id: lpprint.h,v 1.2 2015/06/12 18:18:08 roberto Exp $
*/ */
@@ -18,6 +18,7 @@ void printtree (TTree *tree, int ident);
void printktable (lua_State *L, int idx); void printktable (lua_State *L, int idx);
void printcharset (const byte *st); void printcharset (const byte *st);
void printcaplist (Capture *cap, Capture *limit); void printcaplist (Capture *cap, Capture *limit);
void printinst (const Instruction *op, const Instruction *p);
#else #else

View File

@@ -1,5 +1,5 @@
/* /*
** $Id: lptree.c,v 1.13 2014/12/12 16:59:10 roberto Exp $ ** $Id: lptree.c,v 1.21 2015/09/28 17:01:25 roberto Exp $
** Copyright 2013, Lua.org & PUC-Rio (see 'lpeg.html' for license) ** Copyright 2013, Lua.org & PUC-Rio (see 'lpeg.html' for license)
*/ */
@@ -126,6 +126,189 @@ static void finalfix (lua_State *L, int postable, TTree *g, TTree *t) {
} }
/*
** {===================================================================
** 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 ** Tree generation
@@ -155,7 +338,7 @@ static Pattern *getpattern (lua_State *L, int idx) {
static int getsize (lua_State *L, int idx) { static int getsize (lua_State *L, int idx) {
return (lua_objlen(L, idx) - sizeof(Pattern)) / sizeof(TTree) + 1; return (lua_rawlen(L, idx) - sizeof(Pattern)) / sizeof(TTree) + 1;
} }
@@ -168,12 +351,16 @@ static TTree *gettree (lua_State *L, int idx, int *len) {
/* /*
** create a pattern ** 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) { static TTree *newtree (lua_State *L, int len) {
size_t size = (len - 1) * sizeof(TTree) + sizeof(Pattern); size_t size = (len - 1) * sizeof(TTree) + sizeof(Pattern);
Pattern *p = (Pattern *)lua_newuserdata(L, size); Pattern *p = (Pattern *)lua_newuserdata(L, size);
luaL_getmetatable(L, PATTERN_T); luaL_getmetatable(L, PATTERN_T);
lua_pushvalue(L, -1);
lua_setuservalue(L, -3);
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
p->code = NULL; p->codesize = 0; p->code = NULL; p->codesize = 0;
return p->tree; return p->tree;
@@ -206,35 +393,6 @@ static TTree *seqaux (TTree *tree, TTree *sib, int sibsize) {
} }
/*
** Add element 'idx' to 'ktable' of pattern at the top of the stack;
** create new 'ktable' if necessary. 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 (idx == 0) /* no actual value to insert? */
return 0;
else {
int n;
lua_getfenv(L, -1); /* get ktable from pattern */
n = lua_objlen(L, -1);
if (n == 0) { /* is it empty/non-existent? */
lua_pop(L, 1); /* remove it */
lua_createtable(L, 1, 0); /* create a fresh table */
lua_pushvalue(L, -1); /* make a copy */
lua_setfenv(L, -3); /* set it as 'ktable' for pattern */
}
if (!lua_isnil(L, idx)) { /* non-nil value? */
lua_pushvalue(L, idx); /* element to be added */
lua_rawseti(L, -2, ++n);
}
lua_pop(L, 1); /* remove 'ktable' */
return n;
}
}
/* /*
** Build a sequence of 'n' nodes, each with tag 'tag' and 'u.n' got ** 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 ** from the array 's' (or 0 if array is NULL). (TSeq is binary, so it
@@ -310,7 +468,7 @@ static TTree *getpatt (lua_State *L, int idx, int *len) {
case LUA_TFUNCTION: { case LUA_TFUNCTION: {
tree = newtree(L, 2); tree = newtree(L, 2);
tree->tag = TRunTime; tree->tag = TRunTime;
tree->key = addtoktable(L, idx); tree->key = addtonewktable(L, 0, idx);
sib1(tree)->tag = TTrue; sib1(tree)->tag = TTrue;
break; break;
} }
@@ -325,123 +483,6 @@ static TTree *getpatt (lua_State *L, int idx, int *len) {
} }
/*
** Return the number of elements in the ktable of pattern at 'idx'.
** In Lua 5.2, 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_objlen(L, idx);
}
/*
** Concatentate the contents of table 'idx1' into table 'idx2'.
** (Assume that both indices are negative.)
** Return the original length of table 'idx2'
*/
static int concattable (lua_State *L, int idx1, int idx2) {
int i;
int n1 = ktablelen(L, idx1);
int n2 = ktablelen(L, idx2);
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;
}
/*
** Make a merge of ktables from p1 and p2 the ktable for the new
** pattern at the top of the stack.
*/
static int joinktables (lua_State *L, int p1, int p2) {
int n1, n2;
lua_getfenv(L, p1); /* get ktables */
lua_getfenv(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 */
return 0; /* nothing to correct */
}
if (n2 == 0 || lua_equal(L, -2, -1)) { /* second table is empty or equal? */
lua_pop(L, 1); /* pop 2nd table */
lua_setfenv(L, -2); /* set 1st ktable into new pattern */
return 0; /* nothing to correct */
}
if (n1 == 0) { /* first table is empty? */
lua_setfenv(L, -3); /* set 2nd table into new pattern */
lua_pop(L, 1); /* pop 1st table */
return 0; /* nothing to correct */
}
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_setfenv(L, -4); /* new ktable becomes p env */
lua_pop(L, 2); /* pop other ktables */
return n1; /* correction for indices from p2 */
}
}
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;
}
}
/*
** copy 'ktable' of element 'idx' to new tree (on top of stack)
*/
static void copyktable (lua_State *L, int idx) {
lua_getfenv(L, idx);
lua_setfenv(L, -2);
}
/*
** merge 'ktable' from rule 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 *rule) {
int n;
lua_getfenv(L, -1); /* get ktables */
lua_getfenv(L, idx);
n = concattable(L, -1, -2);
lua_pop(L, 2); /* remove both ktables */
correctkeys(rule, n);
}
/* /*
** create a new tree, whith a new root and one sibling. ** create a new tree, whith a new root and one sibling.
** Sibling must be on the Lua stack, at index 1. ** Sibling must be on the Lua stack, at index 1.
@@ -470,7 +511,7 @@ static TTree *newroot2sib (lua_State *L, int tag) {
tree->u.ps = 1 + s1; tree->u.ps = 1 + s1;
memcpy(sib1(tree), tree1, s1 * sizeof(TTree)); memcpy(sib1(tree), tree1, s1 * sizeof(TTree));
memcpy(sib2(tree), tree2, s2 * sizeof(TTree)); memcpy(sib2(tree), tree2, s2 * sizeof(TTree));
correctkeys(sib2(tree), joinktables(L, 1, 2)); joinktables(L, 1, sib2(tree), 2);
return tree; return tree;
} }
@@ -599,7 +640,7 @@ static int lp_sub (lua_State *L) {
sib1(tree)->tag = TNot; /* ...not... */ sib1(tree)->tag = TNot; /* ...not... */
memcpy(sib1(sib1(tree)), t2, s2 * sizeof(TTree)); /* ...t2 */ memcpy(sib1(sib1(tree)), t2, s2 * sizeof(TTree)); /* ...t2 */
memcpy(sib2(tree), t1, s1 * sizeof(TTree)); /* ... and t1 */ memcpy(sib2(tree), t1, s1 * sizeof(TTree)); /* ... and t1 */
correctkeys(sib1(tree), joinktables(L, 1, 2)); joinktables(L, 1, sib1(tree), 2);
} }
return 1; return 1;
} }
@@ -640,7 +681,7 @@ static int lp_behind (lua_State *L) {
TTree *tree; TTree *tree;
TTree *tree1 = getpatt(L, 1, NULL); TTree *tree1 = getpatt(L, 1, NULL);
int n = fixedlen(tree1); int n = fixedlen(tree1);
luaL_argcheck(L, n > 0, 1, "pattern may not have fixed length"); luaL_argcheck(L, n >= 0, 1, "pattern may not have fixed length");
luaL_argcheck(L, !hascaptures(tree1), 1, "pattern have captures"); luaL_argcheck(L, !hascaptures(tree1), 1, "pattern have captures");
luaL_argcheck(L, n <= MAXBEHIND, 1, "pattern too long to look behind"); luaL_argcheck(L, n <= MAXBEHIND, 1, "pattern too long to look behind");
tree = newroot1sib(L, TBehind); tree = newroot1sib(L, TBehind);
@@ -655,7 +696,7 @@ static int lp_behind (lua_State *L) {
static int lp_V (lua_State *L) { static int lp_V (lua_State *L) {
TTree *tree = newleaf(L, TOpenCall); TTree *tree = newleaf(L, TOpenCall);
luaL_argcheck(L, !lua_isnoneornil(L, 1), 1, "non-nil value expected"); luaL_argcheck(L, !lua_isnoneornil(L, 1), 1, "non-nil value expected");
tree->key = addtoktable(L, 1); tree->key = addtonewktable(L, 0, 1);
return 1; return 1;
} }
@@ -668,7 +709,7 @@ static int lp_V (lua_State *L) {
static int capture_aux (lua_State *L, int cap, int labelidx) { static int capture_aux (lua_State *L, int cap, int labelidx) {
TTree *tree = newroot1sib(L, TCapture); TTree *tree = newroot1sib(L, TCapture);
tree->cap = cap; tree->cap = cap;
tree->key = addtoktable(L, labelidx); tree->key = (labelidx == 0) ? 0 : addtonewktable(L, 1, labelidx);
return 1; return 1;
} }
@@ -676,10 +717,9 @@ static int capture_aux (lua_State *L, int cap, int labelidx) {
/* /*
** Fill a tree with an empty capture, using an empty (TTrue) sibling. ** Fill a tree with an empty capture, using an empty (TTrue) sibling.
*/ */
static TTree *auxemptycap (lua_State *L, TTree *tree, int cap, int idx) { static TTree *auxemptycap (TTree *tree, int cap) {
tree->tag = TCapture; tree->tag = TCapture;
tree->cap = cap; tree->cap = cap;
tree->key = addtoktable(L, idx);
sib1(tree)->tag = TTrue; sib1(tree)->tag = TTrue;
return tree; return tree;
} }
@@ -688,8 +728,18 @@ static TTree *auxemptycap (lua_State *L, TTree *tree, int cap, int idx) {
/* /*
** Create a tree for an empty capture ** Create a tree for an empty capture
*/ */
static TTree *newemptycap (lua_State *L, int cap, int idx) { static TTree *newemptycap (lua_State *L, int cap) {
return auxemptycap(L, newtree(L, 2), cap, idx); return auxemptycap(newtree(L, 2), cap);
}
/*
** 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;
} }
@@ -728,10 +778,8 @@ static int lp_tablecapture (lua_State *L) {
static int lp_groupcapture (lua_State *L) { static int lp_groupcapture (lua_State *L) {
if (lua_isnoneornil(L, 2)) if (lua_isnoneornil(L, 2))
return capture_aux(L, Cgroup, 0); return capture_aux(L, Cgroup, 0);
else { else
luaL_checkstring(L, 2);
return capture_aux(L, Cgroup, 2); return capture_aux(L, Cgroup, 2);
}
} }
@@ -747,14 +795,14 @@ static int lp_simplecapture (lua_State *L) {
static int lp_poscapture (lua_State *L) { static int lp_poscapture (lua_State *L) {
newemptycap(L, Cposition, 0); newemptycap(L, Cposition);
return 1; return 1;
} }
static int lp_argcapture (lua_State *L) { static int lp_argcapture (lua_State *L) {
int n = (int)luaL_checkinteger(L, 1); int n = (int)luaL_checkinteger(L, 1);
TTree *tree = newemptycap(L, Carg, 0); TTree *tree = newemptycap(L, Carg);
tree->key = n; tree->key = n;
luaL_argcheck(L, 0 < n && n <= SHRT_MAX, 1, "invalid argument index"); luaL_argcheck(L, 0 < n && n <= SHRT_MAX, 1, "invalid argument index");
return 1; return 1;
@@ -762,8 +810,8 @@ static int lp_argcapture (lua_State *L) {
static int lp_backref (lua_State *L) { static int lp_backref (lua_State *L) {
luaL_checkstring(L, 1); luaL_checkany(L, 1);
newemptycap(L, Cbackref, 1); newemptycapkey(L, Cbackref, 1);
return 1; return 1;
} }
@@ -777,9 +825,10 @@ static int lp_constcapture (lua_State *L) {
if (n == 0) /* no values? */ if (n == 0) /* no values? */
newleaf(L, TTrue); /* no capture */ newleaf(L, TTrue); /* no capture */
else if (n == 1) else if (n == 1)
newemptycap(L, Cconst, 1); /* single constant capture */ newemptycapkey(L, Cconst, 1); /* single constant capture */
else { /* create a group capture with all values */ else { /* create a group capture with all values */
TTree *tree = newtree(L, 1 + 3 * (n - 1) + 2); TTree *tree = newtree(L, 1 + 3 * (n - 1) + 2);
newktable(L, n); /* create a 'ktable' for new tree */
tree->tag = TCapture; tree->tag = TCapture;
tree->cap = Cgroup; tree->cap = Cgroup;
tree->key = 0; tree->key = 0;
@@ -787,10 +836,12 @@ static int lp_constcapture (lua_State *L) {
for (i = 1; i <= n - 1; i++) { for (i = 1; i <= n - 1; i++) {
tree->tag = TSeq; tree->tag = TSeq;
tree->u.ps = 3; /* skip TCapture and its sibling */ tree->u.ps = 3; /* skip TCapture and its sibling */
auxemptycap(L, sib1(tree), Cconst, i); auxemptycap(sib1(tree), Cconst);
sib1(tree)->key = addtoktable(L, i);
tree = sib2(tree); tree = sib2(tree);
} }
auxemptycap(L, tree, Cconst, i); auxemptycap(tree, Cconst);
tree->key = addtoktable(L, i);
} }
return 1; return 1;
} }
@@ -800,7 +851,7 @@ static int lp_matchtime (lua_State *L) {
TTree *tree; TTree *tree;
luaL_checktype(L, 2, LUA_TFUNCTION); luaL_checktype(L, 2, LUA_TFUNCTION);
tree = newroot1sib(L, TRunTime); tree = newroot1sib(L, TRunTime);
tree->key = addtoktable(L, 2); tree->key = addtonewktable(L, 1, 2);
return 1; return 1;
} }
@@ -857,7 +908,7 @@ static int collectrules (lua_State *L, int arg, int *totalsize) {
lua_pushnil(L); /* prepare to traverse grammar table */ lua_pushnil(L); /* prepare to traverse grammar table */
while (lua_next(L, arg) != 0) { while (lua_next(L, arg) != 0) {
if (lua_tonumber(L, -2) == 1 || if (lua_tonumber(L, -2) == 1 ||
lua_equal(L, -2, postab + 1)) { /* initial rule? */ lp_equal(L, -2, postab + 1)) { /* initial rule? */
lua_pop(L, 1); /* remove value (keep key for lua_next) */ lua_pop(L, 1); /* remove value (keep key for lua_next) */
continue; continue;
} }
@@ -934,36 +985,40 @@ static int verifyerror (lua_State *L, int *passed, int npassed) {
/* /*
** Check whether a rule can be left recursive; raise an error in that ** Check whether a rule can be left recursive; raise an error in that
** case; otherwise return 1 iff pattern is nullable. Assume ktable at ** case; otherwise return 1 iff pattern is nullable.
** the top of the stack. ** 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.)
** Assume ktable at the top of the stack.
*/ */
static int verifyrule (lua_State *L, TTree *tree, int *passed, int npassed, static int verifyrule (lua_State *L, TTree *tree, int *passed, int npassed,
int nullable) { int nb) {
tailcall: tailcall:
switch (tree->tag) { switch (tree->tag) {
case TChar: case TSet: case TAny: case TChar: case TSet: case TAny:
case TFalse: case TFalse:
return nullable; /* cannot pass from here */ return nb; /* cannot pass from here */
case TTrue: case TTrue:
case TBehind: /* look-behind cannot have calls */ case TBehind: /* look-behind cannot have calls */
return 1; return 1;
case TNot: case TAnd: case TRep: case TNot: case TAnd: case TRep:
/* return verifyrule(L, sib1(tree), passed, npassed, 1); */ /* return verifyrule(L, sib1(tree), passed, npassed, 1); */
tree = sib1(tree); nullable = 1; goto tailcall; tree = sib1(tree); nb = 1; goto tailcall;
case TCapture: case TRunTime: case TCapture: case TRunTime:
/* return verifyrule(L, sib1(tree), passed, npassed); */ /* return verifyrule(L, sib1(tree), passed, npassed, nb); */
tree = sib1(tree); goto tailcall; tree = sib1(tree); goto tailcall;
case TCall: case TCall:
/* return verifyrule(L, sib2(tree), passed, npassed); */ /* return verifyrule(L, sib2(tree), passed, npassed, nb); */
tree = sib2(tree); goto tailcall; tree = sib2(tree); goto tailcall;
case TSeq: /* only check 2nd child if first is nullable */ case TSeq: /* only check 2nd child if first is nb */
if (!verifyrule(L, sib1(tree), passed, npassed, 0)) if (!verifyrule(L, sib1(tree), passed, npassed, 0))
return nullable; return nb;
/* else return verifyrule(L, sib2(tree), passed, npassed); */ /* else return verifyrule(L, sib2(tree), passed, npassed, nb); */
tree = sib2(tree); goto tailcall; tree = sib2(tree); goto tailcall;
case TChoice: /* must check both children */ case TChoice: /* must check both children */
nullable = verifyrule(L, sib1(tree), passed, npassed, nullable); nb = verifyrule(L, sib1(tree), passed, npassed, nb);
/* return verifyrule(L, sib2(tree), passed, npassed, nullable); */ /* return verifyrule(L, sib2(tree), passed, npassed, nb); */
tree = sib2(tree); goto tailcall; tree = sib2(tree); goto tailcall;
case TRule: case TRule:
if (npassed >= MAXRULES) if (npassed >= MAXRULES)
@@ -1006,7 +1061,7 @@ static void verifygrammar (lua_State *L, TTree *grammar) {
*/ */
static void initialrulename (lua_State *L, TTree *grammar, int frule) { static void initialrulename (lua_State *L, TTree *grammar, int frule) {
if (sib1(grammar)->key == 0) { /* initial rule is not referenced? */ if (sib1(grammar)->key == 0) { /* initial rule is not referenced? */
int n = lua_objlen(L, -1) + 1; /* index for name */ int n = lua_rawlen(L, -1) + 1; /* index for name */
lua_pushvalue(L, frule); /* rule's name */ lua_pushvalue(L, frule); /* rule's name */
lua_rawseti(L, -2, n); /* ktable was on the top of the stack */ lua_rawseti(L, -2, n); /* ktable was on the top of the stack */
sib1(grammar)->key = n; sib1(grammar)->key = n;
@@ -1022,9 +1077,9 @@ static TTree *newgrammar (lua_State *L, int arg) {
luaL_argcheck(L, n <= MAXRULES, arg, "grammar has too many rules"); luaL_argcheck(L, n <= MAXRULES, arg, "grammar has too many rules");
g->tag = TGrammar; g->u.n = n; g->tag = TGrammar; g->u.n = n;
lua_newtable(L); /* create 'ktable' */ lua_newtable(L); /* create 'ktable' */
lua_setfenv(L, -2); lua_setuservalue(L, -2);
buildgrammar(L, g, frule, n); buildgrammar(L, g, frule, n);
lua_getfenv(L, -1); /* get 'ktable' for new tree */ lua_getuservalue(L, -1); /* get 'ktable' for new tree */
finalfix(L, frule - 1, g, sib1(g)); finalfix(L, frule - 1, g, sib1(g));
initialrulename(L, g, frule); initialrulename(L, g, frule);
verifygrammar(L, g); verifygrammar(L, g);
@@ -1038,7 +1093,7 @@ static TTree *newgrammar (lua_State *L, int arg) {
static Instruction *prepcompile (lua_State *L, Pattern *p, int idx) { static Instruction *prepcompile (lua_State *L, Pattern *p, int idx) {
lua_getfenv(L, idx); /* push 'ktable' (may be used by 'finalfix') */ lua_getuservalue(L, idx); /* push 'ktable' (may be used by 'finalfix') */
finalfix(L, 0, NULL, p->tree); finalfix(L, 0, NULL, p->tree);
lua_pop(L, 1); /* remove 'ktable' */ lua_pop(L, 1); /* remove 'ktable' */
return compile(L, p); return compile(L, p);
@@ -1049,7 +1104,7 @@ static int lp_printtree (lua_State *L) {
TTree *tree = getpatt(L, 1, NULL); TTree *tree = getpatt(L, 1, NULL);
int c = lua_toboolean(L, 2); int c = lua_toboolean(L, 2);
if (c) { if (c) {
lua_getfenv(L, 1); /* push 'ktable' (may be used by 'finalfix') */ lua_getuservalue(L, 1); /* push 'ktable' (may be used by 'finalfix') */
finalfix(L, 0, NULL, tree); finalfix(L, 0, NULL, tree);
lua_pop(L, 1); /* remove 'ktable' */ lua_pop(L, 1); /* remove 'ktable' */
} }
@@ -1102,7 +1157,7 @@ static int lp_match (lua_State *L) {
int ptop = lua_gettop(L); int ptop = lua_gettop(L);
lua_pushnil(L); /* initialize subscache */ lua_pushnil(L); /* initialize subscache */
lua_pushlightuserdata(L, capture); /* initialize caplistidx */ lua_pushlightuserdata(L, capture); /* initialize caplistidx */
lua_getfenv(L, 1); /* initialize penvidx */ lua_getuservalue(L, 1); /* initialize penvidx */
r = match(L, s, s + i, s + l, code, capture, ptop); r = match(L, s, s + i, s + l, code, capture, ptop);
if (r == NULL) { if (r == NULL) {
lua_pushnil(L); lua_pushnil(L);
@@ -1119,8 +1174,12 @@ static int lp_match (lua_State *L) {
** ======================================================= ** =======================================================
*/ */
/* maximum limit for stack size */
#define MAXLIM (INT_MAX / 100)
static int lp_setmax (lua_State *L) { static int lp_setmax (lua_State *L) {
luaL_optinteger(L, 1, -1); lua_Integer lim = luaL_checkinteger(L, 1);
luaL_argcheck(L, 0 < lim && lim <= MAXLIM, 1, "out of range");
lua_settop(L, 1); lua_settop(L, 1);
lua_setfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX); lua_setfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX);
return 0; return 0;
@@ -1144,8 +1203,7 @@ static int lp_type (lua_State *L) {
int lp_gc (lua_State *L) { int lp_gc (lua_State *L) {
Pattern *p = getpattern(L, 1); Pattern *p = getpattern(L, 1);
if (p->codesize > 0) realloccode(L, p, 0); /* delete code block */
realloccode(L, p, 0);
return 0; return 0;
} }
@@ -1228,8 +1286,8 @@ int luaopen_lpeg (lua_State *L) {
luaL_newmetatable(L, PATTERN_T); luaL_newmetatable(L, PATTERN_T);
lua_pushnumber(L, MAXBACK); /* initialize maximum backtracking */ lua_pushnumber(L, MAXBACK); /* initialize maximum backtracking */
lua_setfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX); lua_setfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX);
luaL_register(L, NULL, metareg); luaL_setfuncs(L, metareg, 0);
luaL_register(L, "lpeg", pattreg); luaL_newlib(L, pattreg);
lua_pushvalue(L, -1); lua_pushvalue(L, -1);
lua_setfield(L, -3, "__index"); lua_setfield(L, -3, "__index");
return 1; return 1;

View File

@@ -1,7 +1,7 @@
/* /*
** $Id: lptypes.h,v 1.10 2014/12/12 17:11:35 roberto Exp $ ** $Id: lptypes.h,v 1.14 2015/09/28 17:17:41 roberto Exp $
** LPeg - PEG pattern matching for Lua ** LPeg - PEG pattern matching for Lua
** Copyright 2007-2014, Lua.org & PUC-Rio (see 'lpeg.html' for license) ** Copyright 2007-2015, Lua.org & PUC-Rio (see 'lpeg.html' for license)
** written by Roberto Ierusalimschy ** written by Roberto Ierusalimschy
*/ */
@@ -19,7 +19,7 @@
#include "lua.h" #include "lua.h"
#define VERSION "0.12.1" #define VERSION "1.0.0"
#define PATTERN_T "lpeg-pattern" #define PATTERN_T "lpeg-pattern"
@@ -27,31 +27,31 @@
/* /*
** compatibility with Lua 5.2 ** compatibility with Lua 5.1
*/ */
#if (LUA_VERSION_NUM >= 502) #if (LUA_VERSION_NUM == 501)
#undef lua_equal #define lp_equal lua_equal
#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ)
#undef lua_getfenv #define lua_getuservalue lua_getfenv
#define lua_getfenv lua_getuservalue #define lua_setuservalue lua_setfenv
#undef lua_setfenv
#define lua_setfenv lua_setuservalue
#undef lua_objlen #define lua_rawlen lua_objlen
#define lua_objlen lua_rawlen
#undef luaL_register #define luaL_setfuncs(L,f,n) luaL_register(L,NULL,f)
#define luaL_register(L,n,f) \ #define luaL_newlib(L,f) luaL_register(L,"lpeg",f)
{ if ((n) == NULL) luaL_setfuncs(L,f,0); else luaL_newlib(L,f); }
#endif #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 */ /* default maximum size for call/backtrack stack */
#if !defined(MAXBACK) #if !defined(MAXBACK)
#define MAXBACK 100 #define MAXBACK 400
#endif #endif

View File

@@ -1,5 +1,5 @@
/* /*
** $Id: lpvm.c,v 1.5 2013/04/12 16:29:49 roberto Exp $ ** $Id: lpvm.c,v 1.6 2015/09/28 17:01:25 roberto Exp $
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license) ** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
*/ */
@@ -18,7 +18,7 @@
/* initial size for call/backtrack stack */ /* initial size for call/backtrack stack */
#if !defined(INITBACK) #if !defined(INITBACK)
#define INITBACK 100 #define INITBACK MAXBACK
#endif #endif
@@ -70,7 +70,7 @@ static Stack *doublestack (lua_State *L, Stack **stacklimit, int ptop) {
max = lua_tointeger(L, -1); /* maximum allowed size */ max = lua_tointeger(L, -1); /* maximum allowed size */
lua_pop(L, 1); lua_pop(L, 1);
if (n >= max) /* already at maximum size? */ if (n >= max) /* already at maximum size? */
luaL_error(L, "too many pending calls/choices"); luaL_error(L, "backtrack stack overflow (current limit is %d)", max);
newn = 2 * n; /* new size */ newn = 2 * n; /* new size */
if (newn > max) newn = max; if (newn > max) newn = max;
newstack = (Stack *)lua_newuserdata(L, newn * sizeof(Stack)); newstack = (Stack *)lua_newuserdata(L, newn * sizeof(Stack));

View File

@@ -10,7 +10,7 @@
</head> </head>
<body> <body>
<!-- $Id: re.html,v 1.21 2013/03/28 20:43:30 roberto Exp $ --> <!-- $Id: re.html,v 1.23 2015/09/28 17:17:41 roberto Exp $ -->
<div id="container"> <div id="container">
@@ -296,7 +296,7 @@ it would be useful if each table had
a <code>tag</code> field telling what non terminal a <code>tag</code> field telling what non terminal
that table represents. that table represents.
We can add such a tag using We can add such a tag using
<a href="lpeg.html/#cap-g">named group captures</a>: <a href="lpeg.html#cap-g">named group captures</a>:
</p> </p>
<pre class="example"> <pre class="example">
x = re.compile[[ x = re.compile[[
@@ -450,7 +450,7 @@ print(re.match(p, p)) -- a self description must match itself
<h2><a name="license">License</a></h2> <h2><a name="license">License</a></h2>
<p> <p>
Copyright &copy; 2008-2010 Lua.org, PUC-Rio. Copyright &copy; 2008-2015 Lua.org, PUC-Rio.
</p> </p>
<p> <p>
Permission is hereby granted, free of charge, Permission is hereby granted, free of charge,
@@ -488,7 +488,7 @@ THE SOFTWARE.
<div id="about"> <div id="about">
<p><small> <p><small>
$Id: re.html,v 1.21 2013/03/28 20:43:30 roberto Exp $ $Id: re.html,v 1.23 2015/09/28 17:17:41 roberto Exp $
</small></p> </small></p>
</div> <!-- id="about" --> </div> <!-- id="about" -->

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env lua5.1 #!/usr/bin/env lua
-- $Id: test.lua,v 1.105 2014/12/12 17:00:39 roberto Exp $ -- $Id: test.lua,v 1.109 2015/09/28 17:01:25 roberto Exp $
-- require"strict" -- just to be pedantic -- require"strict" -- just to be pedantic
@@ -16,9 +16,6 @@ local unpack = rawget(table, "unpack") or unpack
local loadstring = rawget(_G, "loadstring") or load local loadstring = rawget(_G, "loadstring") or load
-- most tests here do not need much stack space
m.setmaxstack(5)
local any = m.P(1) local any = m.P(1)
local space = m.S" \t\n"^0 local space = m.S" \t\n"^0
@@ -291,6 +288,13 @@ assert(m.match(m.P"ab"^-1 - "c", "abcd") == 3)
p = ('Aa' * ('Bb' * ('Cc' * m.P'Dd'^0)^0)^0)^-1 p = ('Aa' * ('Bb' * ('Cc' * m.P'Dd'^0)^0)^0)^-1
assert(p:match("AaBbCcDdBbCcDdDdDdBb") == 21) assert(p:match("AaBbCcDdBbCcDdDdDdBb") == 21)
-- bug in 0.12.2
-- p = { ('ab' ('c' 'ef'?)*)? }
p = m.C(('ab' * ('c' * m.P'ef'^-1)^0)^-1)
s = "abcefccefc"
assert(s == p:match(s))
pi = "3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510" pi = "3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510"
@@ -352,6 +356,11 @@ checkeq(t, {hi = 10, ho = 20})
t = p:match'abc' t = p:match'abc'
checkeq(t, {hi = 10, ho = 20, 'a', 'b', 'c'}) checkeq(t, {hi = 10, ho = 20, 'a', 'b', 'c'})
-- non-string group names
p = m.Ct(m.Cg(1, print) * m.Cg(1, 23.5) * m.Cg(1, io))
t = p:match('abcdefghij')
assert(t[print] == 'a' and t[23.5] == 'b' and t[io] == 'c')
-- test for error messages -- test for error messages
local function checkerr (msg, f, ...) local function checkerr (msg, f, ...)
@@ -388,6 +397,25 @@ p = m.P { (m.P {m.P'abc'} + 'ayz') * m.V'y'; y = m.P'x' }
assert(p:match('abcx') == 5 and p:match('ayzx') == 5 and not p:match'abc') assert(p:match('abcx') == 5 and p:match('ayzx') == 5 and not p:match'abc')
do
-- large dynamic Cc
local lim = 2^16 - 1
local c = 0
local function seq (n)
if n == 1 then c = c + 1; return m.Cc(c)
else
local m = math.floor(n / 2)
return seq(m) * seq(n - m)
end
end
p = m.Ct(seq(lim))
t = p:match('')
assert(t[lim] == lim)
checkerr("too many", function () p = p / print end)
checkerr("too many", seq, lim + 1)
end
-- tests for non-pattern as arguments to pattern functions -- tests for non-pattern as arguments to pattern functions
p = { ('a' * m.V(1))^-1 } * m.P'b' * { 'a' * m.V(2); m.V(1)^-1 } p = { ('a' * m.V(1))^-1 } * m.P'b' * { 'a' * m.V(2); m.V(1)^-1 }
@@ -575,9 +603,9 @@ assert(not p:match(string.rep("011", 10001)))
-- this grammar does need backtracking info. -- this grammar does need backtracking info.
local lim = 10000 local lim = 10000
p = m.P{ '0' * m.V(1) + '0' } p = m.P{ '0' * m.V(1) + '0' }
checkerr("too many pending", m.match, p, string.rep("0", lim)) checkerr("stack overflow", m.match, p, string.rep("0", lim))
m.setmaxstack(2*lim) m.setmaxstack(2*lim)
checkerr("too many pending", m.match, p, string.rep("0", lim)) checkerr("stack overflow", m.match, p, string.rep("0", lim))
m.setmaxstack(2*lim + 4) m.setmaxstack(2*lim + 4)
assert(m.match(p, string.rep("0", lim)) == lim + 1) assert(m.match(p, string.rep("0", lim)) == lim + 1)
@@ -586,7 +614,7 @@ p = m.P{ ('a' * m.V(1))^0 * 'b' + 'c' }
m.setmaxstack(200) m.setmaxstack(200)
assert(p:match(string.rep('a', 180) .. 'c' .. string.rep('b', 180)) == 362) assert(p:match(string.rep('a', 180) .. 'c' .. string.rep('b', 180)) == 362)
m.setmaxstack(5) -- restore original limit m.setmaxstack(100) -- restore low limit
-- tests for optional start position -- tests for optional start position
assert(m.match("a", "abc", 1)) assert(m.match("a", "abc", 1))
@@ -718,6 +746,10 @@ t = {m.match(m.Cc(nil,nil,4) * m.Cc(nil,3) * m.Cc(nil, nil) / g / g, "")}
t1 = {1,1,nil,nil,4,nil,3,nil,nil} t1 = {1,1,nil,nil,4,nil,3,nil,nil}
for i=1,10 do assert(t[i] == t1[i]) end for i=1,10 do assert(t[i] == t1[i]) end
-- bug in 0.12.2: ktable with only nil could be eliminated when joining
-- with a pattern without ktable
assert((m.P"aaa" * m.Cc(nil)):match"aaa" == nil)
t = {m.match((m.C(1) / function (x) return x, x.."x" end)^0, "abc")} t = {m.match((m.C(1) / function (x) return x, x.."x" end)^0, "abc")}
checkeq(t, {"a", "ax", "b", "bx", "c", "cx"}) checkeq(t, {"a", "ax", "b", "bx", "c", "cx"})
@@ -925,6 +957,13 @@ p = m.Cg(m.C(1) * m.C(1), "k") * m.Ct(m.Cb("k"))
t = p:match("ab") t = p:match("ab")
checkeq(t, {"a", "b"}) checkeq(t, {"a", "b"})
p = m.P(true)
for i = 1, 10 do p = p * m.Cg(1, i) end
for i = 1, 10 do
local p = p * m.Cb(i)
assert(p:match('abcdefghij') == string.sub('abcdefghij', i, i))
end
t = {} t = {}
function foo (p) t[#t + 1] = p; return p .. "x" end function foo (p) t[#t + 1] = p; return p .. "x" end