Compare commits

...

29 Commits

Author SHA1 Message Date
Cloud Wu
f97c72d341 Release 1.7.0 2023-11-13 15:13:08 +08:00
shencyx
f7453973f8 避免 sharedata 误用大于32位数字做 key 时,可能导致程序崩溃 (#1820)
Co-authored-by: shencyx <yixin.cen@alibaba-inc.com>
2023-11-07 11:52:58 +08:00
ykxpb
0e6423484f 修复 openssl3 deprecated warning ERR_load_BIO_strings (#1816) 2023-10-30 18:15:27 +08:00
HYbutterfly
adf0cc5a6f check enablessl == "true" (#1815) 2023-10-27 17:16:48 +08:00
Cloud Wu
5ec02a4a2c Add a rare case 2023-09-28 20:15:29 +08:00
Cloud Wu
f3f7e725e8 check session == nil 2023-09-28 14:40:02 +08:00
云风
172278bc8f check conflict (#1798)
* rewind check conflict

* Use auxsend instead of c.send
2023-09-28 13:48:35 +08:00
Cloud Wu
c178c398b9 don't report error when session == 0 2023-07-31 21:31:21 +08:00
Cloud Wu
93ea565bcb Improve README, See #1785 2023-07-31 10:19:40 +08:00
华仔
bb90b64c3f websocket添加is_close方法 (#1781)
* websocket添加is_close方法

* 代码优化
2023-07-19 20:01:41 +08:00
Whislly
c6a8976bc3 findOne优化 (#1772)
1、直接调用runCommand方法
2、增加"limit": 1参数,限制只返回一条数据
2023-07-10 12:29:16 +08:00
Whislly
074426e273 pipline方法优化 (#1771)
1、增加sort_statge、skip_statge、limit_statge、count_statge替换临时table
2、改成for i = 1, 2 do self.__pipeline[len + i] = nil end清除老数据
2023-07-07 20:11:36 +08:00
Whislly
b402837628 聚合方法返回游标对象 (#1769)
* 聚合方法返回游标对象

* 使用table.move()替换for循环拷贝

* 改成使用类似继承的方法

1、返回aggregate_cursor不修改mongo_cursor,直接拷贝mongo_cursor的sort、skip、limit、next、close方法
2、修改了mongo_cursor:count,不再使用unpack
3、aggregate_cursor:count和aggregate_cursor:hasNext方法里options为nil时,不使用unpack
4、mongo_collection:aggregate方法里拷贝了pipeline和options
2023-07-07 15:14:37 +08:00
yzj
272af34736 修改delete使用send_command (#1770)
Co-authored-by: xingfan.yzj <xingfan.yzj@alibaba-inc.com>
2023-07-06 17:44:02 +08:00
Jin Xiao
69420bdfde upgrade lpeg to 1.1.0 (#1766)
* upgrade lpeg to 1.1.0

* fix lpeg compiler error
2023-06-27 16:28:21 +08:00
Cloud Wu
2f9d0c805a include stdint.h for uintptr_t 2023-06-14 23:05:40 +08:00
Cloud Wu
4893fa39ee remove unused code 2023-05-16 16:18:19 +08:00
Cloud Wu
a5af5f6ba7 Overload socket_read(), See #1750 2023-05-16 15:59:43 +08:00
Cloud Wu
8b55660274 Update lua 5.4.6 rc1 2023-05-08 19:08:11 +08:00
twinklinggu
d56cf80f95 Fix socket not closed when clusteragent exit (#1741)
Co-authored-by: twinklinggu <twinkling.gu@gmail.com>
2023-04-14 17:36:23 +08:00
Cloud Wu
aedfb9288d See #1529 2023-03-29 16:25:49 +08:00
子熏
223bd599ba 修复设置 callback导致的crash问题 (#1726)
* fix set callback in resolve _cb

* 调整注释

* 使用预处理callback 来解决引用问题

* add cb_pre and forward_pre

* delete precb_context

* format tab

---------

Co-authored-by: zixun <lb151450@alibaba-inc.com>
2023-03-29 10:42:31 +08:00
子熏
b9a5cec191 update sproto (#1720)
Co-authored-by: zixun <lb151450@alibaba-inc.com>
2023-03-21 21:37:03 +08:00
子熏
1e45d4a9be 添加get_protocol接口 (#1712)
* 添加get_protocol接口

* 添加_proto字段

---------

Co-authored-by: zixun <lb151450@alibaba-inc.com>
2023-03-15 12:44:08 +08:00
丁大龙
6fe520a7cb cluster.open添加maxclient参数 (#1705) 2023-02-18 00:57:39 +08:00
丁大龙
f344a49755 修复client_number计数错误 (#1704) 2023-02-17 14:36:32 +08:00
Cloud Wu
39f3ddbc10 update lua 2023-01-29 11:08:14 +08:00
华仔
cc562547a5 ignore .vscode (#1689)
Co-authored-by: Faker2 <faker@git.com>
2023-01-06 11:23:22 +08:00
Cloud Wu
58b1bda0d9 first key can't can be in hash part, see #1484 2022-11-21 19:26:16 +08:00
69 changed files with 2770 additions and 954 deletions

1
.gitignore vendored
View File

@@ -10,3 +10,4 @@
*.so
*.dSYM
.DS_Store
.vscode

View File

@@ -1,4 +1,13 @@
HISTORY for LPeg 1.0.2
HISTORY for LPeg 1.1.0
* Changes from version 1.0.2 to 1.1.0
---------------------------------
+ accumulator capture
+ UTF-8 ranges
+ Larger limit for number of rules in a grammar
+ Larger limit for number of captures in a match
+ bug fixes
+ other small improvements
* Changes from version 1.0.1 to 1.0.2
---------------------------------

4
3rd/lpeg/README.md Normal file
View File

@@ -0,0 +1,4 @@
# LPeg - Parsing Expression Grammars For Lua
For more information,
see [Lpeg](//www.inf.puc-rio.br/~roberto/lpeg/).

View File

@@ -1,29 +1,41 @@
/*
** $Id: lpcap.c $
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
*/
#include "lua.h"
#include "lauxlib.h"
#include "lpcap.h"
#include "lpprint.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)
#define skipclose(cs,head) \
if (isopencap(head)) { assert(isclosecap(cs->cap)); cs->cap++; }
/*
** Return the size of capture 'cap'. If it is an open capture, 'close'
** must be its corresponding close.
*/
static Index_t capsize (Capture *cap, Capture *close) {
if (isopencap(cap)) {
assert(isclosecap(close));
return close->index - cap->index;
}
else
return cap->siz - 1;
}
static Index_t closesize (CapState *cs, Capture *head) {
return capsize(head, cs->cap);
}
/*
** 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.
@@ -44,35 +56,40 @@ static int pushcapture (CapState *cs);
/*
** Goes back in a list of captures looking for an open capture
** corresponding to a close
** corresponding to a close one.
*/
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))
else if (isopencap(cap))
if (n-- == 0) return cap;
}
}
/*
** Go to the next capture
** Go to the next capture at the same level.
*/
static void nextcap (CapState *cs) {
Capture *cap = cs->cap;
if (!isfullcap(cap)) { /* not a single capture? */
if (isopencap(cap)) { /* must look for a close? */
int n = 0; /* number of opens waiting a close */
for (;;) { /* look for corresponding close */
cap++;
if (isclosecap(cap)) {
if (isopencap(cap)) n++;
else if (isclosecap(cap))
if (n-- == 0) break;
}
else if (!isfullcap(cap)) n++;
}
cs->cap = cap + 1; /* + 1 to skip last close */
}
else {
Capture *next;
for (next = cap + 1; capinside(cap, next); next++)
; /* skip captures inside current one */
cs->cap = next;
}
cs->cap = cap + 1; /* + 1 to skip last close (or entire single capture) */
}
@@ -84,22 +101,17 @@ static void nextcap (CapState *cs) {
** 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;
Capture *head = cs->cap++; /* original capture */
int n = 0; /* number of pushed subvalues */
/* repeat for all nested patterns */
while (capinside(head, cs->cap))
n += pushcapture(cs);
if (addextra || n == 0) { /* need extra? */
lua_pushlstring(cs->L, cs->s + head->index, closesize(cs, head));
n++;
}
skipclose(cs, head);
return n;
}
@@ -114,17 +126,43 @@ static void pushonenestedvalue (CapState *cs) {
/*
** Try to find a named group capture with the name given at the top of
** the stack; goes backward from 'cap'.
** Checks whether group 'grp' is visible to 'ref', that is, 'grp' is
** not nested inside a full capture that does not contain 'ref'. (We
** only need to care for full captures because the search at 'findback'
** skips open-end blocks; so, if 'grp' is nested in a non-full capture,
** 'ref' is also inside it.) To check this, we search backward for the
** inner full capture enclosing 'grp'. A full capture cannot contain
** non-full captures, so a close capture means we cannot be inside a
** full capture anymore.
*/
static Capture *findback (CapState *cs, Capture *cap) {
static int capvisible (CapState *cs, Capture *grp, Capture *ref) {
Capture *cap = grp;
int i = MAXLOP; /* maximum distance for an 'open' */
while (i-- > 0 && cap-- > cs->ocap) {
if (isclosecap(cap))
return 1; /* can stop the search */
else if (grp->index - cap->index >= UCHAR_MAX)
return 1; /* can stop the search */
else if (capinside(cap, grp)) /* is 'grp' inside cap? */
return capinside(cap, ref); /* ok iff cap also contains 'ref' */
}
return 1; /* 'grp' is not inside any capture */
}
/*
** Try to find a named group capture with the name given at the top of
** the stack; goes backward from 'ref'.
*/
static Capture *findback (CapState *cs, Capture *ref) {
lua_State *L = cs->L;
Capture *cap = ref;
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) {
else if (capinside(cap, ref))
continue; /* enclosing captures are not visible to 'ref' */
if (captype(cap) == Cgroup && capvisible(cs, cap, ref)) {
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 */
@@ -158,11 +196,10 @@ static int backrefcap (CapState *cs) {
*/
static int tablecap (CapState *cs) {
lua_State *L = cs->L;
Capture *head = cs->cap++;
int n = 0;
lua_newtable(L);
if (isfullcap(cs->cap++))
return 1; /* table is empty */
while (!isclosecap(cs->cap)) {
while (capinside(head, cs->cap)) {
if (captype(cs->cap) == Cgroup && cs->cap->idx != 0) { /* named group? */
pushluaval(cs); /* push group name */
pushonenestedvalue(cs);
@@ -176,7 +213,7 @@ static int tablecap (CapState *cs) {
n += k;
}
}
cs->cap++; /* skip close entry */
skipclose(cs, head);
return 1; /* number of values pushed (only the table) */
}
@@ -203,20 +240,20 @@ static int querycap (CapState *cs) {
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)? */
Capture *head = cs->cap++;
int idx = head->idx;
if (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)) {
while (capinside(head, 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 */
skipclose(cs, head);
return 1; /* only accumulator left on the stack */
}
@@ -234,6 +271,22 @@ static int functioncap (CapState *cs) {
}
/*
** Accumulator capture
*/
static int accumulatorcap (CapState *cs) {
lua_State *L = cs->L;
int n;
if (lua_gettop(L) < cs->firstcap)
luaL_error(L, "no previous value for accumulator capture");
pushluaval(cs); /* push function */
lua_insert(L, -2); /* previous value becomes first argument */
n = pushnestedvalues(cs, 0); /* push nested captures */
lua_call(L, n + 1, 1); /* call function */
return 0; /* did not add any extra value */
}
/*
** Select capture
*/
@@ -283,7 +336,7 @@ int runtimecap (CapState *cs, Capture *close, const char *s, int *rem) {
assert(captype(open) == Cgroup);
id = finddyncap(open, close); /* get first dynamic capture argument */
close->kind = Cclose; /* closes the group */
close->s = s;
close->index = s - cs->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 */
@@ -313,8 +366,8 @@ typedef struct StrAux {
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 */
Index_t idx; /* starts here */
Index_t siz; /* with this size */
} s;
} u;
} StrAux;
@@ -329,24 +382,23 @@ typedef struct StrAux {
*/
static int getstrcaps (CapState *cs, StrAux *cps, int n) {
int k = n++;
Capture *head = cs->cap++;
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++;
}
cps[k].u.s.idx = head->index; /* starts here */
while (capinside(head, cs->cap)) {
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 */
cps[k].u.s.siz = closesize(cs, head);
skipclose(cs, head);
return n;
}
@@ -368,7 +420,7 @@ static void stringcap (luaL_Buffer *b, CapState *cs) {
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 */
for (i = 0; i < len; i++) { /* traverse format string */
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? */
@@ -378,7 +430,7 @@ static void stringcap (luaL_Buffer *b, CapState *cs) {
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);
luaL_addlstring(b, cs->s + cps[l].u.s.idx, cps[l].u.s.siz);
else {
Capture *curr = cs->cap;
cs->cap = cps[l].u.cp; /* go back to evaluate that nested capture */
@@ -395,22 +447,20 @@ static void stringcap (luaL_Buffer *b, CapState *cs) {
** 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 */
const char *curr = cs->s + cs->cap->index;
Capture *head = cs->cap++;
while (capinside(head, cs->cap)) {
Capture *cap = cs->cap;
const char *caps = cs->s + cap->index;
luaL_addlstring(b, curr, caps - curr); /* add text up to capture */
if (addonestring(b, cs, "replacement"))
curr = caps + capsize(cap, cs->cap - 1); /* continue after match */
else /* no capture value */
curr = caps; /* keep original text in final result */
}
cs->cap++; /* go to next capture */
/* add last piece of text */
luaL_addlstring(b, curr, cs->s + head->index + closesize(cs, head) - curr);
skipclose(cs, head);
}
@@ -426,13 +476,16 @@ static int addonestring (luaL_Buffer *b, CapState *cs, const char *what) {
case Csubst:
substcap(b, cs); /* add capture directly to buffer */
return 1;
case Cacc: /* accumulator capture? */
return luaL_error(cs->L, "invalid context for an accumulator capture");
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));
return luaL_error(L, "invalid %s value (a %s)",
what, luaL_typename(L, -1));
luaL_addvalue(b);
}
return n;
@@ -458,7 +511,7 @@ static int pushcapture (CapState *cs) {
return luaL_error(L, "subcapture nesting too deep");
switch (captype(cs->cap)) {
case Cposition: {
lua_pushinteger(L, cs->cap->s - cs->s + 1);
lua_pushinteger(L, cs->cap->index + 1);
cs->cap++;
res = 1;
break;
@@ -516,6 +569,7 @@ static int pushcapture (CapState *cs) {
case Cbackref: res = backrefcap(cs); break;
case Ctable: res = tablecap(cs); break;
case Cfunction: res = functioncap(cs); break;
case Cacc: res = accumulatorcap(cs); break;
case Cnum: res = numcap(cs); break;
case Cquery: res = querycap(cs); break;
case Cfold: res = foldcap(cs); break;
@@ -529,7 +583,7 @@ static int pushcapture (CapState *cs) {
/*
** 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'
** 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.)
@@ -537,13 +591,16 @@ static int pushcapture (CapState *cs) {
int getcaptures (lua_State *L, const char *s, const char *r, int ptop) {
Capture *capture = (Capture *)lua_touserdata(L, caplistidx(ptop));
int n = 0;
/* printcaplist(capture); */
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;
cs.firstcap = lua_gettop(L) + 1; /* where first value (if any) will go */
do { /* collect their values */
n += pushcapture(&cs);
} while (!isclosecap(cs.cap));
assert(lua_gettop(L) - cs.firstcap == n - 1);
}
if (n == 0) { /* no capture values? */
lua_pushinteger(L, r - s + 1); /* return only end position */

View File

@@ -1,6 +1,3 @@
/*
** $Id: lpcap.h $
*/
#if !defined(lpcap_h)
#define lpcap_h
@@ -19,6 +16,7 @@ typedef enum CapKind {
Csimple, /* next node is pattern */
Ctable, /* next node is pattern */
Cfunction, /* ktable[key] is function; next node is pattern */
Cacc, /* 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 */
@@ -29,8 +27,20 @@ typedef enum CapKind {
} CapKind;
/*
** An unsigned integer large enough to index any subject entirely.
** It can be size_t, but that will double the size of the array
** of captures in a 64-bit machine.
*/
#if !defined(Index_t)
typedef uint Index_t;
#endif
#define MAXINDT (~(Index_t)0)
typedef struct Capture {
const char *s; /* subject position */
Index_t index; /* 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) */
@@ -41,13 +51,32 @@ typedef struct CapState {
Capture *cap; /* current capture */
Capture *ocap; /* (original) capture list */
lua_State *L;
int ptop; /* index of last argument to 'match' */
int ptop; /* stack index of last argument to 'match' */
int firstcap; /* stack index of first capture pushed in the stack */
const char *s; /* original string */
int valuecached; /* value stored in cache slot */
int reclevel; /* recursion level */
} CapState;
#define captype(cap) ((cap)->kind)
#define isclosecap(cap) (captype(cap) == Cclose)
#define isopencap(cap) ((cap)->siz == 0)
/* true if c2 is (any number of levels) inside c1 */
#define capinside(c1,c2) \
(isopencap(c1) ? !isclosecap(c2) \
: (c2)->index < (c1)->index + (c1)->siz - 1)
/**
** Maximum number of captures to visit when looking for an 'open'.
*/
#define MAXLOP 20
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);

View File

@@ -1,7 +1,3 @@
/*
** $Id: lpcode.c $
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
*/
#include <limits.h>
@@ -11,6 +7,7 @@
#include "lptypes.h"
#include "lpcode.h"
#include "lpcset.h"
/* signals a "no-instruction */
@@ -26,61 +23,13 @@ static const Charset fullset_ =
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
@@ -89,42 +38,12 @@ 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
@@ -196,7 +115,7 @@ int hascaptures (TTree *tree) {
int checkaux (TTree *tree, int pred) {
tailcall:
switch (tree->tag) {
case TChar: case TSet: case TAny:
case TChar: case TSet: case TAny: case TUTFR:
case TFalse: case TOpenCall:
return 0; /* not nullable */
case TRep: case TTrue:
@@ -220,7 +139,7 @@ int checkaux (TTree *tree, int pred) {
if (checkaux(sib2(tree), pred)) return 1;
/* else return checkaux(sib1(tree), pred); */
tree = sib1(tree); goto tailcall;
case TCapture: case TGrammar: case TRule:
case TCapture: case TGrammar: case TRule: case TXInfo:
/* return checkaux(sib1(tree), pred); */
tree = sib1(tree); goto tailcall;
case TCall: /* return checkaux(sib2(tree), pred); */
@@ -239,11 +158,13 @@ int fixedlen (TTree *tree) {
switch (tree->tag) {
case TChar: case TSet: case TAny:
return len + 1;
case TUTFR:
return (tree->cap == sib1(tree)->cap) ? len + tree->cap : -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:
case TCapture: case TRule: case TGrammar: case TXInfo:
/* return fixedlen(sib1(tree)); */
tree = sib1(tree); goto tailcall;
case TCall: {
@@ -294,18 +215,21 @@ int fixedlen (TTree *tree) {
static int getfirst (TTree *tree, const Charset *follow, Charset *firstset) {
tailcall:
switch (tree->tag) {
case TChar: case TSet: case TAny: {
case TChar: case TSet: case TAny: case TFalse: {
tocharset(tree, firstset);
return 0;
}
case TUTFR: {
int c;
clearset(firstset->cs); /* erase all chars */
for (c = tree->key; c <= sib1(tree)->key; c++)
setchar(firstset->cs, c);
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);
@@ -334,7 +258,7 @@ static int getfirst (TTree *tree, const Charset *follow, Charset *firstset) {
loopset(i, firstset->cs[i] |= follow->cs[i]);
return 1; /* accept the empty string */
}
case TCapture: case TGrammar: case TRule: {
case TCapture: case TGrammar: case TRule: case TXInfo: {
/* return getfirst(sib1(tree), follow, firstset); */
tree = sib1(tree); goto tailcall;
}
@@ -356,9 +280,8 @@ static int getfirst (TTree *tree, const Charset *follow, Charset *firstset) {
if (tocharset(sib1(tree), firstset)) {
cs_complement(firstset);
return 1;
}
/* else go through */
}
} /* else */
} /* FALLTHROUGH */
case TBehind: { /* instruction gives no new information */
/* call 'getfirst' only to check for math-time captures */
int e = getfirst(sib1(tree), follow, firstset);
@@ -380,9 +303,9 @@ static int headfail (TTree *tree) {
case TChar: case TSet: case TAny: case TFalse:
return 1;
case TTrue: case TRep: case TRunTime: case TNot:
case TBehind:
case TBehind: case TUTFR:
return 0;
case TCapture: case TGrammar: case TRule: case TAnd:
case TCapture: case TGrammar: case TRule: case TXInfo: case TAnd:
tree = sib1(tree); goto tailcall; /* return headfail(sib1(tree)); */
case TCall:
tree = sib2(tree); goto tailcall; /* return headfail(sib2(tree)); */
@@ -407,7 +330,7 @@ static int headfail (TTree *tree) {
static int needfollow (TTree *tree) {
tailcall:
switch (tree->tag) {
case TChar: case TSet: case TAny:
case TChar: case TSet: case TAny: case TUTFR:
case TFalse: case TTrue: case TAnd: case TNot:
case TRunTime: case TGrammar: case TCall: case TBehind:
return 0;
@@ -418,7 +341,7 @@ static int needfollow (TTree *tree) {
case TSeq:
tree = sib2(tree); goto tailcall;
default: assert(0); return 0;
}
}
}
/* }====================================================== */
@@ -437,10 +360,11 @@ static int needfollow (TTree *tree) {
*/
int sizei (const Instruction *i) {
switch((Opcode)i->i.code) {
case ISet: case ISpan: return CHARSETINSTSIZE;
case ITestSet: return CHARSETINSTSIZE + 1;
case ISet: case ISpan: return 1 + i->i.aux2.set.size;
case ITestSet: return 2 + i->i.aux2.set.size;
case ITestChar: case ITestAny: case IChoice: case IJmp: case ICall:
case IOpenCall: case ICommit: case IPartialCommit: case IBackCommit:
case IUTFR:
return 2;
default: return 1;
}
@@ -468,23 +392,67 @@ 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)
static void finishrelcode (lua_State *L, Pattern *p, Instruction *block,
int size) {
if (block == NULL)
luaL_error(L, "not enough memory");
p->code = (Instruction *)newblock;
p->codesize = nsize;
block->codesize = size;
p->code = (Instruction *)block + 1;
}
static int nextinstruction (CompileState *compst) {
int size = compst->p->codesize;
if (compst->ncode >= size)
realloccode(compst->L, compst->p, size * 2);
return compst->ncode++;
/*
** Initialize array 'p->code'
*/
static void newcode (lua_State *L, Pattern *p, int size) {
void *ud;
Instruction *block;
lua_Alloc f = lua_getallocf(L, &ud);
size++; /* slot for 'codesize' */
block = (Instruction*) f(ud, NULL, 0, size * sizeof(Instruction));
finishrelcode(L, p, block, size);
}
void freecode (lua_State *L, Pattern *p) {
if (p->code != NULL) {
void *ud;
lua_Alloc f = lua_getallocf(L, &ud);
uint osize = p->code[-1].codesize;
f(ud, p->code - 1, osize * sizeof(Instruction), 0); /* free block */
}
}
/*
** Assume that 'nsize' is not zero and that 'p->code' already exists.
*/
static void realloccode (lua_State *L, Pattern *p, int nsize) {
void *ud;
lua_Alloc f = lua_getallocf(L, &ud);
Instruction *block = p->code - 1;
uint osize = block->codesize;
nsize++; /* add the 'codesize' slot to size */
block = (Instruction*) f(ud, block, osize * sizeof(Instruction),
nsize * sizeof(Instruction));
finishrelcode(L, p, block, nsize);
}
/*
** Add space for an instruction with 'n' slots and return its index.
*/
static int nextinstruction (CompileState *compst, int n) {
int size = compst->p->code[-1].codesize - 1;
int ncode = compst->ncode;
if (ncode > size - n) {
uint nsize = size + (size >> 1) + n;
if (nsize >= INT_MAX)
luaL_error(compst->L, "pattern code too large");
realloccode(compst->L, compst->p, nsize);
}
compst->ncode = ncode + n;
return ncode;
}
@@ -492,9 +460,9 @@ static int nextinstruction (CompileState *compst) {
static int addinstruction (CompileState *compst, Opcode op, int aux) {
int i = nextinstruction(compst);
int i = nextinstruction(compst, 1);
getinstr(compst, i).i.code = op;
getinstr(compst, i).i.aux = aux;
getinstr(compst, i).i.aux1 = aux;
return i;
}
@@ -518,6 +486,16 @@ static void setoffset (CompileState *compst, int instruction, int offset) {
}
static void codeutfr (CompileState *compst, TTree *tree) {
int i = addoffsetinst(compst, IUTFR);
int to = sib1(tree)->u.n;
assert(sib1(tree)->tag == TXInfo);
getinstr(compst, i + 1).offset = tree->u.n;
getinstr(compst, i).i.aux1 = to & 0xff;
getinstr(compst, i).i.aux2.key = to >> 8;
}
/*
** Add a capture instruction:
** 'op' is the capture instruction; 'cap' the capture kind;
@@ -527,7 +505,7 @@ static void setoffset (CompileState *compst, int instruction, int 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;
getinstr(compst, i).i.aux2.key = key;
return i;
}
@@ -560,7 +538,7 @@ static void jumptohere (CompileState *compst, int instruction) {
*/
static void codechar (CompileState *compst, int c, int tt) {
if (tt >= 0 && getinstr(compst, tt).i.code == ITestChar &&
getinstr(compst, tt).i.aux == c)
getinstr(compst, tt).i.aux1 == c)
addinstruction(compst, IAny, 0);
else
addinstruction(compst, IChar, c);
@@ -568,45 +546,63 @@ static void codechar (CompileState *compst, int c, int tt) {
/*
** Add a charset posfix to an instruction
** Add a charset posfix to an instruction.
*/
static void addcharset (CompileState *compst, const byte *cs) {
int p = gethere(compst);
static void addcharset (CompileState *compst, int inst, charsetinfo *info) {
int p;
Instruction *I = &getinstr(compst, inst);
byte *charset;
int isize = instsize(info->size); /* size in instructions */
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]);
I->i.aux2.set.offset = info->offset * 8; /* offset in bits */
I->i.aux2.set.size = isize;
I->i.aux1 = info->deflt;
p = nextinstruction(compst, isize); /* space for charset */
charset = getinstr(compst, p).buff; /* charset buffer */
for (i = 0; i < isize * (int)sizeof(Instruction); i++)
charset[i] = getbytefromcharset(info, i); /* copy the buffer */
}
/*
** 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.
** Check whether charset 'info' is dominated by instruction 'p'
*/
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;
static int cs_equal (Instruction *p, charsetinfo *info) {
if (p->i.code != ITestSet)
return 0;
else if (p->i.aux2.set.offset != info->offset * 8 ||
p->i.aux2.set.size != instsize(info->size) ||
p->i.aux1 != info->deflt)
return 0;
else {
int i;
for (i = 0; i < instsize(info->size) * (int)sizeof(Instruction); i++) {
if ((p + 2)->buff[i] != getbytefromcharset(info, i))
return 0;
}
default: addinstruction(compst, op, c); break;
}
return 1;
}
/*
** Code a char set, using IAny when instruction is dominated by an
** equivalent test.
*/
static void codecharset (CompileState *compst, TTree *tree, int tt) {
charsetinfo info;
tree2cset(tree, &info);
if (tt >= 0 && cs_equal(&getinstr(compst, tt), &info))
addinstruction(compst, IAny, 0);
else {
int i = addinstruction(compst, ISet, 0);
addcharset(compst, i, &info);
}
}
/*
** code a test set, optimizing unit sets for ITestChar, "complete"
** 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.)
@@ -614,22 +610,22 @@ static void codecharset (CompileState *compst, const byte *cs, int tt) {
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);
charsetinfo info;
Opcode op = charsettype(cs->cs, &info);
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;
getinstr(compst, i).i.aux1 = info.offset;
return i;
}
case ISet: {
default: { /* regular set */
int i = addoffsetinst(compst, ITestSet);
addcharset(compst, cs->cs);
addcharset(compst, i, &info);
assert(op == ISet);
return i;
}
default: assert(0); return 0;
}
}
}
@@ -665,11 +661,11 @@ static void codebehind (CompileState *compst, TTree *tree) {
/*
** 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,
** - 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, either because p1 will accept
** (headfail) or because it is not in first(p2) (disjoint).
** (The second case 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.
@@ -686,7 +682,7 @@ static void codechoice (CompileState *compst, TTree *p1, TTree *p2, int opt,
int jmp = NOINST;
codegen(compst, p1, 0, test, fl);
if (!emptyp2)
jmp = addoffsetinst(compst, IJmp);
jmp = addoffsetinst(compst, IJmp);
jumptohere(compst, test);
codegen(compst, p2, opt, NOINST, fl);
jumptohere(compst, jmp);
@@ -697,7 +693,7 @@ static void codechoice (CompileState *compst, TTree *p1, TTree *p2, int opt,
codegen(compst, p1, 1, NOINST, fullset);
}
else {
/* <p1 / p2> ==
/* <p1 / p2> ==
test(first(p1)) -> L1; choice L1; <p1>; commit L2; L1: <p2>; L2: */
int pcommit;
int test = codetestset(compst, &cs1, e1);
@@ -764,9 +760,53 @@ static void coderuntime (CompileState *compst, TTree *tree, int tt) {
}
/*
** Create a jump to 'test' and fix 'test' to jump to next instruction
*/
static void closeloop (CompileState *compst, int test) {
int jmp = addoffsetinst(compst, IJmp);
jumptohere(compst, test);
jumptothere(compst, jmp, test);
}
/*
** Try repetition of charsets:
** For an empty set, repetition of fail is a no-op;
** For any or char, code a tight loop;
** For generic charset, use a span instruction.
*/
static int coderepcharset (CompileState *compst, TTree *tree) {
switch (tree->tag) {
case TFalse: return 1; /* 'fail*' is a no-op */
case TAny: { /* L1: testany -> L2; any; jmp L1; L2: */
int test = addoffsetinst(compst, ITestAny);
addinstruction(compst, IAny, 0);
closeloop(compst, test);
return 1;
}
case TChar: { /* L1: testchar c -> L2; any; jmp L1; L2: */
int test = addoffsetinst(compst, ITestChar);
getinstr(compst, test).i.aux1 = tree->u.n;
addinstruction(compst, IAny, 0);
closeloop(compst, test);
return 1;
}
case TSet: { /* regular set */
charsetinfo info;
int i = addinstruction(compst, ISpan, 0);
tree2cset(tree, &info);
addcharset(compst, i, &info);
return 1;
}
default: return 0; /* not a charset */
}
}
/*
** Repetion; optimizations:
** When pattern is a charset, can use special instruction ISpan.
** When pattern is a charset, use special code.
** 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
@@ -776,21 +816,14 @@ static void coderuntime (CompileState *compst, TTree *tree, int tt) {
*/
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 {
if (!coderepcharset(compst, tree)) {
Charset st;
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);
closeloop(compst, test);
}
else {
/* test(fail(p1)) -> L2; choice L2; L1: <p>; partialcommit L1; L2: */
@@ -847,7 +880,7 @@ static void correctcalls (CompileState *compst, int *positions,
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 n = code[i].i.aux2.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 ? */
@@ -874,8 +907,10 @@ static void codegrammar (CompileState *compst, TTree *grammar) {
int start = gethere(compst); /* here starts the initial rule */
jumptohere(compst, firstcall);
for (rule = sib1(grammar); rule->tag == TRule; rule = sib2(rule)) {
TTree *r = sib1(rule);
assert(r->tag == TXInfo);
positions[rulenumber++] = gethere(compst); /* save rule position */
codegen(compst, sib1(rule), 0, NOINST, fullset); /* code rule */
codegen(compst, sib1(r), 0, NOINST, fullset); /* code rule */
addinstruction(compst, IRet, 0);
}
assert(rule->tag == TTrue);
@@ -886,8 +921,8 @@ static void codegrammar (CompileState *compst, TTree *grammar) {
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);
assert(sib1(sib2(call))->tag == TXInfo);
getinstr(compst, c).i.aux2.key = sib1(sib2(call))->u.n; /* rule number */
}
@@ -922,9 +957,10 @@ static void codegen (CompileState *compst, TTree *tree, int opt, int tt,
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 TSet: codecharset(compst, tree, tt); break;
case TTrue: break;
case TFalse: addinstruction(compst, IFail, 0); break;
case TUTFR: codeutfr(compst, tree); 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;
@@ -971,7 +1007,7 @@ static void peephole (CompileState *compst) {
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 */
code[i + 1].i.code = IEmpty; /* 'no-op' for target position */
break;
}
case ICommit: case IPartialCommit:
@@ -996,12 +1032,13 @@ static void peephole (CompileState *compst) {
/*
** Compile a pattern
** Compile a pattern. 'size' is the size of the pattern's tree,
** which gives a hint for the size of the final code.
*/
Instruction *compile (lua_State *L, Pattern *p) {
Instruction *compile (lua_State *L, Pattern *p, uint size) {
CompileState compst;
compst.p = p; compst.ncode = 0; compst.L = L;
realloccode(L, p, 2); /* minimum initial size */
newcode(L, p, size/2u + 2); /* set initial size */
codegen(&compst, p->tree, 0, NOINST, fullset);
addinstruction(&compst, IEnd, 0);
realloccode(L, p, compst.ncode); /* set final size */

View File

@@ -1,6 +1,3 @@
/*
** $Id: lpcode.h $
*/
#if !defined(lpcode_h)
#define lpcode_h
@@ -11,13 +8,12 @@
#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);
Instruction *compile (lua_State *L, Pattern *p, uint size);
void freecode (lua_State *L, Pattern *p);
int sizei (const Instruction *i);

110
3rd/lpeg/lpcset.c Normal file
View File

@@ -0,0 +1,110 @@
#include "lptypes.h"
#include "lpcset.h"
/*
** Add to 'c' the index of the (only) bit set in byte 'b'
*/
static int onlybit (int c, int b) {
if ((b & 0xF0) != 0) { c += 4; b >>= 4; }
if ((b & 0x0C) != 0) { c += 2; b >>= 2; }
if ((b & 0x02) != 0) { c += 1; }
return c;
}
/*
** Check whether a charset is empty (returns IFail), singleton (IChar),
** full (IAny), or none of those (ISet). When singleton, 'info.offset'
** returns which character it is. When generic set, 'info' returns
** information about its range.
*/
Opcode charsettype (const byte *cs, charsetinfo *info) {
int low0, low1, high0, high1;
for (low1 = 0; low1 < CHARSETSIZE && cs[low1] == 0; low1++)
/* find lowest byte with a 1-bit */;
if (low1 == CHARSETSIZE)
return IFail; /* no characters in set */
for (high1 = CHARSETSIZE - 1; cs[high1] == 0; high1--)
/* find highest byte with a 1-bit; low1 is a sentinel */;
if (low1 == high1) { /* only one byte with 1-bits? */
int b = cs[low1];
if ((b & (b - 1)) == 0) { /* does byte has only one 1-bit? */
info->offset = onlybit(low1 * BITSPERCHAR, b); /* get that bit */
return IChar; /* single character */
}
}
for (low0 = 0; low0 < CHARSETSIZE && cs[low0] == 0xFF; low0++)
/* find lowest byte with a 0-bit */;
if (low0 == CHARSETSIZE)
return IAny; /* set has all bits set */
for (high0 = CHARSETSIZE - 1; cs[high0] == 0xFF; high0--)
/* find highest byte with a 0-bit; low0 is a sentinel */;
if (high1 - low1 <= high0 - low0) { /* range of 1s smaller than of 0s? */
info->offset = low1;
info->size = high1 - low1 + 1;
info->deflt = 0; /* all discharged bits were 0 */
}
else {
info->offset = low0;
info->size = high0 - low0 + 1;
info->deflt = 0xFF; /* all discharged bits were 1 */
}
info->cs = cs + info->offset;
return ISet;
}
/*
** Get a byte from a compact charset. If index is inside the charset
** range, get the byte from the supporting charset (correcting it
** by the offset). Otherwise, return the default for the set.
*/
byte getbytefromcharset (const charsetinfo *info, int index) {
if (index < info->size)
return info->cs[index];
else return info->deflt;
}
/*
** If 'tree' is a 'char' pattern (TSet, TChar, TAny, TFalse), convert it
** into a charset and return 1; else return 0.
*/
int tocharset (TTree *tree, Charset *cs) {
switch (tree->tag) {
case TChar: { /* only one char */
assert(0 <= tree->u.n && tree->u.n <= UCHAR_MAX);
clearset(cs->cs); /* erase all chars */
setchar(cs->cs, tree->u.n); /* add that one */
return 1;
}
case TAny: {
fillset(cs->cs, 0xFF); /* add all characters to the set */
return 1;
}
case TFalse: {
clearset(cs->cs); /* empty set */
return 1;
}
case TSet: { /* fill set */
int i;
fillset(cs->cs, tree->u.set.deflt);
for (i = 0; i < tree->u.set.size; i++)
cs->cs[tree->u.set.offset + i] = treebuffer(tree)[i];
return 1;
}
default: return 0;
}
}
void tree2cset (TTree *tree, charsetinfo *info) {
assert(tree->tag == TSet);
info->offset = tree->u.set.offset;
info->size = tree->u.set.size;
info->deflt = tree->u.set.deflt;
info->cs = treebuffer(tree);
}

30
3rd/lpeg/lpcset.h Normal file
View File

@@ -0,0 +1,30 @@
#if !defined(lpset_h)
#define lpset_h
#include "lpcset.h"
#include "lpcode.h"
#include "lptree.h"
/*
** Extra information for the result of 'charsettype'. When result is
** IChar, 'offset' is the character. When result is ISet, 'cs' is the
** supporting bit array (with offset included), 'offset' is the offset
** (in bytes), 'size' is the size (in bytes), and 'delt' is the default
** value for bytes outside the set.
*/
typedef struct {
const byte *cs;
int offset;
int size;
int deflt;
} charsetinfo;
int tocharset (TTree *tree, Charset *cs);
Opcode charsettype (const byte *cs, charsetinfo *info);
byte getbytefromcharset (const charsetinfo *info, int index);
void tree2cset (TTree *tree, charsetinfo *info);
#endif

View File

@@ -1,28 +1,27 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
"//www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="//www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>LPeg - Parsing Expression Grammars For Lua</title>
<link rel="stylesheet"
href="http://www.inf.puc-rio.br/~roberto/lpeg/doc.css"
href="//www.inf.puc-rio.br/~roberto/lpeg/doc.css"
type="text/css"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<!-- $Id: lpeg.html $ -->
<div id="container">
<div id="product">
<div id="product_logo">
<a href="http://www.inf.puc-rio.br/~roberto/lpeg/">
<a href="//www.inf.puc-rio.br/~roberto/lpeg/">
<img alt="LPeg logo" src="lpeg-128.gif"/></a>
</div>
<div id="product_name"><big><strong>LPeg</strong></big></div>
<div id="product_description">
Parsing Expression Grammars For Lua, version 1.0
Parsing Expression Grammars For Lua, version 1.1
</div>
</div> <!-- id="product" -->
@@ -56,16 +55,16 @@
<p>
<em>LPeg</em> is a new pattern-matching library for Lua,
based on
<a href="http://pdos.csail.mit.edu/%7Ebaford/packrat/">
<a href="//bford.info/packrat/">
Parsing Expression Grammars</a> (PEGs).
This text is a reference manual for the library.
For a more formal treatment of LPeg,
as well as some discussion about its implementation,
see
<a href="http://www.inf.puc-rio.br/~roberto/docs/peg.pdf">
<a href="//www.inf.puc-rio.br/~roberto/docs/peg.pdf">
A Text Pattern-Matching Tool based on Parsing Expression Grammars</a>.
(You may also be interested in my
<a href="http://vimeo.com/1485123">talk about LPeg</a>
<a href="//vimeo.com/1485123">talk about LPeg</a>
given at the III Lua Workshop.)
</p>
@@ -107,6 +106,9 @@ for creating patterns:
<td>Matches any character in <code>string</code> (Set)</td></tr>
<tr><td><a href="#op-r"><code>lpeg.R("<em>xy</em>")</code></a></td>
<td>Matches any character between <em>x</em> and <em>y</em> (Range)</td></tr>
<tr><td><a href="#op-utfR"><code>lpeg.utfR(cp1, cp2)</code></a></td>
<td>Matches an UTF-8 code point between <code>cp1</code> and
<code>cp2</code></td></tr>
<tr><td><a href="#op-pow"><code>patt^n</code></a></td>
<td>Matches at least <code>n</code> repetitions of <code>patt</code></td></tr>
<tr><td><a href="#op-pow"><code>patt^-n</code></a></td>
@@ -142,7 +144,7 @@ so, it succeeds only at the end of the subject.
LPeg also offers the <a href="re.html"><code>re</code> module</a>,
which implements patterns following a regular-expression style
(e.g., <code>[09]+</code>).
(This module is 260 lines of Lua code,
(This module is 270 lines of Lua code,
and of course it uses LPeg to parse regular expressions and
translate them to regular LPeg patterns.)
</p>
@@ -164,7 +166,7 @@ or the <a href="#captures">captured values</a>
<p>
An optional numeric argument <code>init</code> makes the match
start at that position in the subject string.
As usual in Lua libraries,
As in the Lua standard libraries,
a negative value counts from the end.
</p>
@@ -188,9 +190,9 @@ returns the string <code>"pattern"</code>.
Otherwise returns nil.
</p>
<h3><a name="f-version"></a><code>lpeg.version ()</code></h3>
<h3><a name="f-version"></a><code>lpeg.version</code></h3>
<p>
Returns a string with the running version of LPeg.
A string (not a function) with the running version of LPeg.
</p>
<h3><a name="f-setstack"></a><code>lpeg.setmaxstack (max)</code></h3>
@@ -329,6 +331,15 @@ are patterns that always fail.
</p>
<h3><a name="op-utfR"></a><code>lpeg.utfR (cp1, cp2)</code></h3>
<p>
Returns a pattern that matches a valid UTF-8 byte sequence
representing a code point in the range <code>[cp1, cp2]</code>.
The range is limited by the natural Unicode limit of 0x10FFFF,
but may include surrogates.
</p>
<h3><a name="op-v"></a><code>lpeg.V (v)</code></h3>
<p>
This operation creates a non-terminal (a <em>variable</em>)
@@ -433,7 +444,8 @@ letter = lower + upper
<h3><a name="op-sub"></a><code>patt1 - patt2</code></h3>
<p>
Returns a pattern equivalent to <em>!patt2 patt1</em>.
Returns a pattern equivalent to <em>!patt2 patt1</em>
in the origial PEG notation.
This pattern asserts that the input does not match
<code>patt2</code> and then matches <code>patt1</code>.
</p>
@@ -597,17 +609,17 @@ The following table summarizes the basic captures:
<tr><td><a href="#cap-arg"><code>lpeg.Carg(n)</code></a></td>
<td>the value of the n<sup>th</sup> extra argument to
<code>lpeg.match</code> (matches the empty string)</td></tr>
<tr><td><a href="#cap-b"><code>lpeg.Cb(name)</code></a></td>
<tr><td><a href="#cap-b"><code>lpeg.Cb(key)</code></a></td>
<td>the values produced by the previous
group capture named <code>name</code>
group capture named <code>key</code>
(matches the empty string)</td></tr>
<tr><td><a href="#cap-cc"><code>lpeg.Cc(values)</code></a></td>
<td>the given values (matches the empty string)</td></tr>
<tr><td><a href="#cap-f"><code>lpeg.Cf(patt, func)</code></a></td>
<td>a <em>folding</em> of the captures from <code>patt</code></td></tr>
<tr><td><a href="#cap-g"><code>lpeg.Cg(patt [, name])</code></a></td>
<td>folding capture (<em>deprecated</em>)</td></tr>
<tr><td><a href="#cap-g"><code>lpeg.Cg(patt [, key])</code></a></td>
<td>the values produced by <code>patt</code>,
optionally tagged with <code>name</code></td></tr>
optionally tagged with <code>key</code></td></tr>
<tr><td><a href="#cap-p"><code>lpeg.Cp()</code></a></td>
<td>the current position (matches the empty string)</td></tr>
<tr><td><a href="#cap-s"><code>lpeg.Cs(patt)</code></a></td>
@@ -627,6 +639,11 @@ or no value when <code>number</code> is zero.</td></tr>
<tr><td><a href="#cap-func"><code>patt / function</code></a></td>
<td>the returns of <code>function</code> applied to the captures
of <code>patt</code></td></tr>
<tr><td><a href="#cap-acc"><code>patt % function</code></a></td>
<td>produces no value;
it <em>accummulates</em> the captures from <code>patt</code>
into the previous capture through <code>function</code>
</td></tr>
<tr><td><a href="#matchtime"><code>lpeg.Cmt(patt, function)</code></a></td>
<td>the returns of <code>function</code> applied to the captures
of <code>patt</code>; the application is done at match time</td></tr>
@@ -652,10 +669,10 @@ LPeg does not specify when (and if) it evaluates its captures.
consider the pattern <code>lpeg.P"a" / func / 0</code>.
Because the "division" by 0 instructs LPeg to throw away the
results from the pattern,
LPeg may or may not call <code>func</code>.)
it is not specified whether LPeg will call <code>func</code>.)
Therefore, captures should avoid side effects.
Moreover,
most captures cannot affect the way a pattern matches a subject.
captures cannot affect the way a pattern matches a subject.
The only exception to this rule is the
so-called <a href="#matchtime"><em>match-time capture</em></a>.
When a match-time capture matches,
@@ -684,24 +701,25 @@ argument given in the call to <code>lpeg.match</code>.
</p>
<h3><a name="cap-b"></a><code>lpeg.Cb (name)</code></h3>
<h3><a name="cap-b"></a><code>lpeg.Cb (key)</code></h3>
<p>
Creates a <em>back capture</em>.
This pattern matches the empty string and
produces the values produced by the <em>most recent</em>
<a href="#cap-g">group capture</a> named <code>name</code>
(where <code>name</code> can be any Lua value).
<a href="#cap-g">group capture</a> named <code>key</code>
(where <code>key</code> can be any Lua value).
</p>
<p>
<em>Most recent</em> means the last
<em>complete</em>
<em>outermost</em>
group capture with the given name.
group capture with the given key.
A <em>Complete</em> capture means that the entire pattern
corresponding to the capture has matched.
corresponding to the capture has matched;
in other words, the back capture is not nested inside the group.
An <em>Outermost</em> capture means that the capture is not inside
another complete capture.
another complete capture that does not contain the back capture itself.
</p>
<p>
@@ -722,61 +740,21 @@ produces all given values as its captured values.
<h3><a name="cap-f"></a><code>lpeg.Cf (patt, func)</code></h3>
<p>
Creates a <em>fold capture</em>.
If <code>patt</code> produces a list of captures
<em>C<sub>1</sub> C<sub>2</sub> ... C<sub>n</sub></em>,
this capture will produce the value
<em>func(...func(func(C<sub>1</sub>, C<sub>2</sub>), C<sub>3</sub>)...,
C<sub>n</sub>)</em>,
that is, it will <em>fold</em>
(or <em>accumulate</em>, or <em>reduce</em>)
the captures from <code>patt</code> using function <code>func</code>.
</p>
<p>
This capture assumes that <code>patt</code> should produce
at least one capture with at least one value (of any type),
which becomes the initial value of an <em>accumulator</em>.
(If you need a specific initial value,
you may prefix a <a href="#cap-cc">constant capture</a> to <code>patt</code>.)
For each subsequent capture,
LPeg calls <code>func</code>
with this accumulator as the first argument and all values produced
by the capture as extra arguments;
the first result from this call
becomes the new value for the accumulator.
The final value of the accumulator becomes the captured value.
</p>
<p>
As an example,
the following pattern matches a list of numbers separated
by commas and returns their addition:
</p>
<pre class="example">
-- matches a numeral and captures its numerical value
number = lpeg.R"09"^1 / tonumber
-- matches a list of numbers, capturing their values
list = number * ("," * number)^0
-- auxiliary function to add two numbers
function add (acc, newvalue) return acc + newvalue end
-- folds the list of numbers adding them
sum = lpeg.Cf(list, add)
-- example of use
print(sum:match("10,30,43")) --&gt; 83
</pre>
This construction is deprecated;
use an <a href="#cap-acc">accumulator pattern</a> instead.
In general, a fold like
<code>lpeg.Cf(p1 * p2^0, func)</code>
can be translated to
<code>(p1 * (p2 % func)^0)</code>.
<h3><a name="cap-g"></a><code>lpeg.Cg (patt [, name])</code></h3>
<h3><a name="cap-g"></a><code>lpeg.Cg (patt [, key])</code></h3>
<p>
Creates a <em>group capture</em>.
It groups all values returned by <code>patt</code>
into a single capture.
The group may be anonymous (if no name is given)
or named with the given name
The group may be anonymous (if no key is given)
or named with the given key
(which can be any non-nil Lua value).
</p>
@@ -822,7 +800,7 @@ starting at 1.
Moreover,
for each named capture group created by <code>patt</code>,
the first value of the group is put into the table
with the group name as its key.
with the group key as its key.
The captured value is only the table.
</p>
@@ -878,6 +856,98 @@ there is no captured value.
</p>
<h3><a name="cap-acc"></a><code>patt % function</code></h3>
<p>
Creates an <em>accumulator capture</em>.
This pattern behaves similarly to a
<a href="#cap-func">function capture</a>,
with the following differences:
The last captured value before <code>patt</code>
is added as a first argument to the call;
the return of the function is adjusted to one single value;
that value replaces the last captured value.
Note that the capture itself produces no values;
it only changes the value of its previous capture.
</p>
<p>
As an example,
let us consider the problem of adding a list of numbers.
</p>
<pre class="example">
-- matches a numeral and captures its numerical value
number = lpeg.R"09"^1 / tonumber
-- auxiliary function to add two numbers
function add (acc, newvalue) return acc + newvalue end
-- matches a list of numbers, adding their values
sum = number * ("," * number % add)^0
-- example of use
print(sum:match("10,30,43")) --&gt; 83
</pre>
<p>
First, the initial <code>number</code> captures a number;
that first capture will play the role of an accumulator.
Then, each time the sequence <code>comma-number</code>
matches inside the loop there is an accumulator capture:
It calls <code>add</code> with the current value of the
accumulator&mdash;which is the last captured value, created by the
first <code>number</code>&mdash; and the value of the new number,
and the result of the call (the sum of the two numbers)
replaces the value of the accumulator.
At the end of the match,
the accumulator with all sums is the final value.
</p>
<p>
As another example,
consider the following code fragment:
</p>
<pre class="example">
local name = lpeg.C(lpeg.R("az")^1)
local p = name * (lpeg.P("^") % string.upper)^-1
print(p:match("count")) --&gt; count
print(p:match("count^")) --&gt; COUNT
</pre>
<p>
In the match against <code>"count"</code>,
as there is no <code>"^"</code>,
the optional accumulator capture does not match;
so, the match results in its sole capture, a name.
In the match against <code>"count^"</code>,
the accumulator capture matches,
so the function <code>string.upper</code>
is called with the previous captured value (created by <code>name</code>)
plus the string <code>"^"</code>;
the function ignores its second argument and returns the first argument
changed to upper case;
that value then becomes the first and only
capture value created by the match.
</p>
<p>
Due to the nature of this capture,
you should avoid using it in places where it is not clear
what is the "previous" capture,
such as directly nested in a <a href="#cap-string">string capture</a>
or a <a href="#cap-num">numbered capture</a>.
(Note that these captures may not need to evaluate
all their subcaptures to compute their results.)
Moreover, due to implementation details,
you should not use this capture directly nested in a
<a href="#cap-s">substitution capture</a>.
You should also avoid a direct nesting of this capture inside
a <a href="#cap-f">folding capture</a> (deprecated),
as the folding will try to fold each individual accumulator capture.
A simple and effective way to avoid all these issues is
to enclose the whole accumulation composition
(including the capture that generates the initial value)
into an anonymous <a href="#cap-g">group capture</a>.
</p>
<h3><a name="matchtime"></a><code>lpeg.Cmt(patt, function)</code></h3>
<p>
Creates a <em>match-time capture</em>.
@@ -930,9 +1000,9 @@ local lpeg = require "lpeg"
-- matches a word followed by end-of-string
p = lpeg.R"az"^1 * -1
print(p:match("hello")) --> 6
print(lpeg.match(p, "hello")) --> 6
print(p:match("1 hello")) --> nil
print(p:match("hello")) --&gt; 6
print(lpeg.match(p, "hello")) --&gt; 6
print(p:match("1 hello")) --&gt; nil
</pre>
<p>
The pattern is simply a sequence of one or more lower-case letters
@@ -957,19 +1027,18 @@ lpeg.locale(lpeg) -- adds locale entries into 'lpeg' table
local space = lpeg.space^0
local name = lpeg.C(lpeg.alpha^1) * space
local sep = lpeg.S(",;") * space
local pair = lpeg.Cg(name * "=" * space * name) * sep^-1
local list = lpeg.Cf(lpeg.Ct("") * pair^0, rawset)
t = list:match("a=b, c = hi; next = pi") --> { a = "b", c = "hi", next = "pi" }
local pair = name * "=" * space * name * sep^-1
local list = lpeg.Ct("") * (pair % rawset)^0
t = list:match("a=b, c = hi; next = pi")
--&gt; { a = "b", c = "hi", next = "pi" }
</pre>
<p>
Each pair has the format <code>name = name</code> followed by
an optional separator (a comma or a semicolon).
The <code>pair</code> pattern encloses the pair in a group pattern,
so that the names become the values of a single capture.
The <code>list</code> pattern then folds these captures.
The <code>list</code> pattern then <em>folds</em> these captures.
It starts with an empty table,
created by a table capture matching an empty string;
then for each capture (a pair of names) it applies <code>rawset</code>
then for each a pair of names it applies <code>rawset</code>
over the accumulator (the table) and the capture values (the pair of names).
<code>rawset</code> returns the table itself,
so the accumulator is always the table.
@@ -1003,7 +1072,7 @@ by <code>sep</code>.
If the split results in too many values,
it may overflow the maximum number of values
that can be returned by a Lua function.
In this case,
To avoid this problem,
we can collect these values in a table:
</p>
<pre class="example">
@@ -1039,7 +1108,7 @@ end
</pre>
<p>
This grammar has a straight reading:
it matches <code>p</code> or skips one character and tries again.
its sole rule matches <code>p</code> or skips one character and tries again.
</p>
<p>
@@ -1048,27 +1117,27 @@ If we want to know where the pattern is in the string
we can add position captures to the pattern:
</p>
<pre class="example">
local I = lpeg.Cp()
local Cp = lpeg.Cp()
function anywhere (p)
return lpeg.P{ I * p * I + 1 * lpeg.V(1) }
return lpeg.P{ Cp * p * Cp + 1 * lpeg.V(1) }
end
print(anywhere("world"):match("hello world!")) -> 7 12
print(anywhere("world"):match("hello world!")) --&gt; 7 12
</pre>
<p>
Another option for the search is like this:
</p>
<pre class="example">
local I = lpeg.Cp()
local Cp = lpeg.Cp()
function anywhere (p)
return (1 - lpeg.P(p))^0 * I * p * I
return (1 - lpeg.P(p))^0 * Cp * p * Cp
end
</pre>
<p>
Again the pattern has a straight reading:
it skips as many characters as possible while not matching <code>p</code>,
and then matches <code>p</code> (plus appropriate captures).
and then matches <code>p</code> plus appropriate captures.
</p>
<p>
@@ -1163,91 +1232,6 @@ local record = lpeg.Ct(field * (',' * field)^0) * (lpeg.P'\n' + -1)
</pre>
<h3>UTF-8 and Latin 1</h3>
<p>
It is not difficult to use LPeg to convert a string from
UTF-8 encoding to Latin 1 (ISO 8859-1):
</p>
<pre class="example">
-- convert a two-byte UTF-8 sequence to a Latin 1 character
local function f2 (s)
local c1, c2 = string.byte(s, 1, 2)
return string.char(c1 * 64 + c2 - 12416)
end
local utf8 = lpeg.R("\0\127")
+ lpeg.R("\194\195") * lpeg.R("\128\191") / f2
local decode_pattern = lpeg.Cs(utf8^0) * -1
</pre>
<p>
In this code,
the definition of UTF-8 is already restricted to the
Latin 1 range (from 0 to 255).
Any encoding outside this range (as well as any invalid encoding)
will not match that pattern.
</p>
<p>
As the definition of <code>decode_pattern</code> demands that
the pattern matches the whole input (because of the -1 at its end),
any invalid string will simply fail to match,
without any useful information about the problem.
We can improve this situation redefining <code>decode_pattern</code>
as follows:
</p>
<pre class="example">
local function er (_, i) error("invalid encoding at position " .. i) end
local decode_pattern = lpeg.Cs(utf8^0) * (-1 + lpeg.P(er))
</pre>
<p>
Now, if the pattern <code>utf8^0</code> stops
before the end of the string,
an appropriate error function is called.
</p>
<h3>UTF-8 and Unicode</h3>
<p>
We can extend the previous patterns to handle all Unicode code points.
Of course,
we cannot translate them to Latin 1 or any other one-byte encoding.
Instead, our translation results in a array with the code points
represented as numbers.
The full code is here:
</p>
<pre class="example">
-- decode a two-byte UTF-8 sequence
local function f2 (s)
local c1, c2 = string.byte(s, 1, 2)
return c1 * 64 + c2 - 12416
end
-- decode a three-byte UTF-8 sequence
local function f3 (s)
local c1, c2, c3 = string.byte(s, 1, 3)
return (c1 * 64 + c2) * 64 + c3 - 925824
end
-- decode a four-byte UTF-8 sequence
local function f4 (s)
local c1, c2, c3, c4 = string.byte(s, 1, 4)
return ((c1 * 64 + c2) * 64 + c3) * 64 + c4 - 63447168
end
local cont = lpeg.R("\128\191") -- continuation byte
local utf8 = lpeg.R("\0\127") / string.byte
+ lpeg.R("\194\223") * cont / f2
+ lpeg.R("\224\239") * cont * cont / f3
+ lpeg.R("\240\244") * cont * cont * cont / f4
local decode_pattern = lpeg.Ct(utf8^0) * -1
</pre>
<h3>Lua's long strings</h3>
<p>
A long string in Lua starts with the pattern <code>[=*[</code>
@@ -1347,7 +1331,7 @@ function evalExp (s)
end
-- small example
print(evalExp"3 + 5*9 / (1+1) - 12") --> 13.5
print(evalExp"3 + 5*9 / (1+1) - 12") --&gt; 13.5
</pre>
<p>
@@ -1369,16 +1353,16 @@ end
-- Grammar
local V = lpeg.V
G = lpeg.P{ "Exp",
Exp = lpeg.Cf(V"Term" * lpeg.Cg(TermOp * V"Term")^0, eval);
Term = lpeg.Cf(V"Factor" * lpeg.Cg(FactorOp * V"Factor")^0, eval);
Exp = V"Term" * (TermOp * V"Term" % eval)^0;
Term = V"Factor" * (FactorOp * V"Factor" % eval)^0;
Factor = Number / tonumber + Open * V"Exp" * Close;
}
-- small example
print(lpeg.match(G, "3 + 5*9 / (1+1) - 12")) --> 13.5
print(lpeg.match(G, "3 + 5*9 / (1+1) - 12")) --&gt; 13.5
</pre>
<p>
Note the use of the fold (accumulator) capture.
Note the use of the accumulator capture.
To compute the value of an expression,
the accumulator starts with the value of the first term,
and then applies <code>eval</code> over
@@ -1391,13 +1375,20 @@ and the new term for each repetition.
<h2><a name="download"></a>Download</h2>
<p>LPeg
<a href="http://www.inf.puc-rio.br/~roberto/lpeg/lpeg-1.0.2.tar.gz">source code</a>.</p>
<a href="//www.inf.puc-rio.br/~roberto/lpeg/lpeg-1.1.0.tar.gz">source code</a>.</p>
<p>
Probably, the easiest way to install LPeg is with
<a href="//luarocks.org/">LuaRocks</a>.
If you have LuaRocks installed,
the following command is all you need to install LPeg:
<pre>$ luarocks install lpeg</pre>
<h2><a name="license">License</a></h2>
<p>
Copyright &copy; 2007-2019 Lua.org, PUC-Rio.
Copyright &copy; 2007-2023 Lua.org, PUC-Rio.
</p>
<p>
Permission is hereby granted, free of charge,

View File

@@ -1,7 +1,3 @@
/*
** $Id: lpprint.c $
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
*/
#include <ctype.h>
#include <limits.h>
@@ -27,7 +23,7 @@ void printcharset (const byte *st) {
printf("[");
for (i = 0; i <= UCHAR_MAX; i++) {
int first = i;
while (testchar(st, i) && i <= UCHAR_MAX) i++;
while (i <= UCHAR_MAX && testchar(st, i)) i++;
if (i - 1 == first) /* unary range? */
printf("(%02x)", first);
else if (i - 1 > first) /* non-empty range? */
@@ -37,10 +33,34 @@ void printcharset (const byte *st) {
}
static void printIcharset (const Instruction *inst, const byte *buff) {
byte cs[CHARSETSIZE];
int i;
printf("(%02x-%d) ", inst->i.aux2.set.offset, inst->i.aux2.set.size);
clearset(cs);
for (i = 0; i < CHARSETSIZE * 8; i++) {
if (charinset(inst, buff, i))
setchar(cs, i);
}
printcharset(cs);
}
static void printTcharset (TTree *tree) {
byte cs[CHARSETSIZE];
int i;
printf("(%02x-%d) ", tree->u.set.offset, tree->u.set.size);
fillset(cs, tree->u.set.deflt);
for (i = 0; i < tree->u.set.size; i++)
cs[tree->u.set.offset + i] = treebuffer(tree)[i];
printcharset(cs);
}
static const char *capkind (int kind) {
const char *const modes[] = {
"close", "position", "constant", "backref",
"argument", "simple", "table", "function",
"argument", "simple", "table", "function", "accumulator",
"query", "string", "num", "substitution", "fold",
"runtime", "group"};
return modes[kind];
@@ -56,41 +76,46 @@ void printinst (const Instruction *op, const Instruction *p) {
const char *const names[] = {
"any", "char", "set",
"testany", "testchar", "testset",
"span", "behind",
"span", "utf-range", "behind",
"ret", "end",
"choice", "jmp", "call", "open_call",
"commit", "partial_commit", "back_commit", "failtwice", "fail", "giveup",
"fullcapture", "opencapture", "closecapture", "closeruntime"
"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);
printf("'%c' (%02x)", p->i.aux1, p->i.aux1);
break;
}
case ITestChar: {
printf("'%c'", p->i.aux); printjmp(op, p);
printf("'%c' (%02x)", p->i.aux1, p->i.aux1); printjmp(op, p);
break;
}
case IUTFR: {
printf("%d - %d", p[1].offset, utf_to(p));
break;
}
case IFullCapture: {
printf("%s (size = %d) (idx = %d)",
capkind(getkind(p)), getoff(p), p->i.key);
capkind(getkind(p)), getoff(p), p->i.aux2.key);
break;
}
case IOpenCapture: {
printf("%s (idx = %d)", capkind(getkind(p)), p->i.key);
printf("%s (idx = %d)", capkind(getkind(p)), p->i.aux2.key);
break;
}
case ISet: {
printcharset((p+1)->buff);
printIcharset(p, (p+1)->buff);
break;
}
case ITestSet: {
printcharset((p+2)->buff); printjmp(op, p);
printIcharset(p, (p+2)->buff); printjmp(op, p);
break;
}
case ISpan: {
printcharset((p+1)->buff);
printIcharset(p, (p+1)->buff);
break;
}
case IOpenCall: {
@@ -98,7 +123,7 @@ void printinst (const Instruction *op, const Instruction *p) {
break;
}
case IBehind: {
printf("%d", p->i.aux);
printf("%d", p->i.aux1);
break;
}
case IJmp: case ICall: case ICommit: case IChoice:
@@ -112,8 +137,9 @@ void printinst (const Instruction *op, const Instruction *p) {
}
void printpatt (Instruction *p, int n) {
void printpatt (Instruction *p) {
Instruction *op = p;
uint n = op[-1].codesize - 1;
while (p < op + n) {
printinst(op, p);
p += sizei(p);
@@ -121,20 +147,39 @@ void printpatt (Instruction *p, int n) {
}
#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);
static void printcap (Capture *cap, int ident) {
while (ident--) printf(" ");
printf("%s (idx: %d - size: %d) -> %lu (%p)\n",
capkind(cap->kind), cap->idx, cap->siz, (long)cap->index, (void*)cap);
}
void printcaplist (Capture *cap, Capture *limit) {
/*
** Print a capture and its nested captures
*/
static Capture *printcap2close (Capture *cap, int ident) {
Capture *head = cap++;
printcap(head, ident); /* print head capture */
while (capinside(head, cap))
cap = printcap2close(cap, ident + 2); /* print nested captures */
if (isopencap(head)) {
assert(isclosecap(cap));
printcap(cap++, ident); /* print and skip close capture */
}
return cap;
}
void printcaplist (Capture *cap) {
{ /* for debugging, print first a raw list of captures */
Capture *c = cap;
while (c->index != MAXINDT) { printcap(c, 0); c++; }
}
printf(">======\n");
for (; cap->s && (limit == NULL || cap < limit); cap++)
printcap(cap);
while (!isclosecap(cap))
cap = printcap2close(cap, 0);
printf("=======\n");
}
#endif
/* }====================================================== */
@@ -147,11 +192,11 @@ void printcaplist (Capture *cap, Capture *limit) {
static const char *tagnames[] = {
"char", "set", "any",
"true", "false",
"true", "false", "utf8.range",
"rep",
"seq", "choice",
"not", "and",
"call", "opencall", "rule", "grammar",
"call", "opencall", "rule", "xinfo", "grammar",
"behind",
"capture", "run-time"
};
@@ -159,6 +204,7 @@ static const char *tagnames[] = {
void printtree (TTree *tree, int ident) {
int i;
int sibs = numsiblings[tree->tag];
for (i = 0; i < ident; i++) printf(" ");
printf("%s", tagnames[tree->tag]);
switch (tree->tag) {
@@ -171,29 +217,38 @@ void printtree (TTree *tree, int ident) {
break;
}
case TSet: {
printcharset(treebuffer(tree));
printTcharset(tree);
printf("\n");
break;
}
case TUTFR: {
assert(sib1(tree)->tag == TXInfo);
printf(" %d (%02x %d) - %d (%02x %d) \n",
tree->u.n, tree->key, tree->cap,
sib1(tree)->u.n, sib1(tree)->key, sib1(tree)->cap);
break;
}
case TOpenCall: case TCall: {
assert(sib2(tree)->tag == TRule);
printf(" key: %d (rule: %d)\n", tree->key, sib2(tree)->cap);
assert(sib1(sib2(tree))->tag == TXInfo);
printf(" key: %d (rule: %d)\n", tree->key, sib1(sib2(tree))->u.n);
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 */
printf(" key: %d\n", tree->key);
sibs = 1; /* do not print 'sib2' (next rule) as a sibling */
break;
}
case TXInfo: {
printf(" n: %d\n", tree->u.n);
break;
}
case TGrammar: {
TTree *rule = sib1(tree);
@@ -203,18 +258,17 @@ void printtree (TTree *tree, int ident) {
rule = sib2(rule);
}
assert(rule->tag == TTrue); /* sentinel */
sibs = 0; /* siblings already handled */
break;
}
default: {
int sibs = numsiblings[tree->tag];
default:
printf("\n");
if (sibs >= 1) {
printtree(sib1(tree), ident + 2);
if (sibs >= 2)
printtree(sib2(tree), ident + 2);
}
break;
}
}
if (sibs >= 1) {
printtree(sib1(tree), ident + 2);
if (sibs >= 2)
printtree(sib2(tree), ident + 2);
}
}

View File

@@ -1,7 +1,3 @@
/*
** $Id: lpprint.h $
*/
#if !defined(lpprint_h)
#define lpprint_h
@@ -13,11 +9,11 @@
#if defined(LPEG_DEBUG)
void printpatt (Instruction *p, int n);
void printpatt (Instruction *p);
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 printcaplist (Capture *cap);
void printinst (const Instruction *op, const Instruction *p);
#else
@@ -26,7 +22,7 @@ void printinst (const Instruction *op, const Instruction *p);
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) \
#define printpatt(p) \
luaL_error(L, "function only implemented in debug mode")
#endif

View File

@@ -1,7 +1,3 @@
/*
** $Id: lptree.c $
** Copyright 2013, Lua.org & PUC-Rio (see 'lpeg.html' for license)
*/
#include <ctype.h>
#include <limits.h>
@@ -16,16 +12,17 @@
#include "lpcode.h"
#include "lpprint.h"
#include "lptree.h"
#include "lpcset.h"
/* number of siblings for each tree */
const byte numsiblings[] = {
0, 0, 0, /* char, set, any */
0, 0, /* true, false */
1, /* rep */
0, 0, 0, /* true, false, utf-range */
1, /* acc */
2, 2, /* seq, choice */
1, 1, /* not, and */
0, 0, 2, 1, /* call, opencall, rule, grammar */
0, 0, 2, 1, 1, /* call, opencall, rule, prerule, grammar */
1, /* behind */
1, 1 /* capture, runtime capture */
};
@@ -338,7 +335,7 @@ static Pattern *getpattern (lua_State *L, int idx) {
static int getsize (lua_State *L, int idx) {
return (lua_rawlen(L, idx) - sizeof(Pattern)) / sizeof(TTree) + 1;
return (lua_rawlen(L, idx) - offsetof(Pattern, tree)) / sizeof(TTree);
}
@@ -351,18 +348,18 @@ static TTree *gettree (lua_State *L, int idx, int *len) {
/*
** 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.)
** create a pattern followed by a tree with 'len' nodes. 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);
size_t size = offsetof(Pattern, tree) + len * sizeof(TTree);
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;
p->code = NULL;
return p->tree;
}
@@ -374,17 +371,44 @@ static TTree *newleaf (lua_State *L, int tag) {
}
static TTree *newcharset (lua_State *L) {
TTree *tree = newtree(L, bytes2slots(CHARSETSIZE) + 1);
tree->tag = TSet;
loopset(i, treebuffer(tree)[i] = 0);
return tree;
/*
** Create a tree for a charset, optimizing for special cases: empty set,
** full set, and singleton set.
*/
static TTree *newcharset (lua_State *L, byte *cs) {
charsetinfo info;
Opcode op = charsettype(cs, &info);
switch (op) {
case IFail: return newleaf(L, TFalse); /* empty set */
case IAny: return newleaf(L, TAny); /* full set */
case IChar: { /* singleton set */
TTree *tree =newleaf(L, TChar);
tree->u.n = info.offset;
return tree;
}
default: { /* regular set */
int i;
int bsize = /* tree size in bytes */
(int)offsetof(TTree, u.set.bitmap) + info.size;
TTree *tree = newtree(L, bytes2slots(bsize));
assert(op == ISet);
tree->tag = TSet;
tree->u.set.offset = info.offset;
tree->u.set.size = info.size;
tree->u.set.deflt = info.deflt;
for (i = 0; i < info.size; i++) {
assert(&treebuffer(tree)[i] < (byte*)tree + bsize);
treebuffer(tree)[i] = cs[info.offset + i];
}
return tree;
}
}
}
/*
** add to tree a sequence where first sibling is 'sib' (with size
** 'sibsize'); returns position for second sibling
** Add to tree a sequence where first sibling is 'sib' (with size
** 'sibsize'); return position for second sibling.
*/
static TTree *seqaux (TTree *tree, TTree *sib, int sibsize) {
tree->tag = TSeq; tree->u.ps = sibsize + 1;
@@ -553,8 +577,8 @@ static int lp_choice (lua_State *L) {
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]);
loopset(i, st1.cs[i] |= st2.cs[i]);
newcharset(L, st1.cs);
}
else if (nofail(t1) || t2->tag == TFalse)
lua_pushvalue(L, 1); /* true / x => true, x / false => x */
@@ -630,8 +654,8 @@ static int lp_sub (lua_State *L) {
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]);
loopset(i, st1.cs[i] &= ~st2.cs[i]);
newcharset(L, st1.cs);
}
else {
TTree *tree = newtree(L, 2 + s1 + s2);
@@ -649,11 +673,13 @@ static int lp_sub (lua_State *L) {
static int lp_set (lua_State *L) {
size_t l;
const char *s = luaL_checklstring(L, 1, &l);
TTree *tree = newcharset(L);
byte buff[CHARSETSIZE];
clearset(buff);
while (l--) {
setchar(treebuffer(tree), (byte)(*s));
setchar(buff, (byte)(*s));
s++;
}
newcharset(L, buff);
return 1;
}
@@ -661,14 +687,68 @@ static int lp_set (lua_State *L) {
static int lp_range (lua_State *L) {
int arg;
int top = lua_gettop(L);
TTree *tree = newcharset(L);
byte buff[CHARSETSIZE];
clearset(buff);
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);
setchar(buff, c);
}
newcharset(L, buff);
return 1;
}
/*
** Fills a tree node with basic information about the UTF-8 code point
** 'cpu': its value in 'n', its length in 'cap', and its first byte in
** 'key'
*/
static void codeutftree (lua_State *L, TTree *t, lua_Unsigned cpu, int arg) {
int len, fb, cp;
cp = (int)cpu;
if (cp <= 0x7f) { /* one byte? */
len = 1;
fb = cp;
} else if (cp <= 0x7ff) {
len = 2;
fb = 0xC0 | (cp >> 6);
} else if (cp <= 0xffff) {
len = 3;
fb = 0xE0 | (cp >> 12);
}
else {
luaL_argcheck(L, cpu <= 0x10ffffu, arg, "invalid code point");
len = 4;
fb = 0xF0 | (cp >> 18);
}
t->u.n = cp;
t->cap = len;
t->key = fb;
}
static int lp_utfr (lua_State *L) {
lua_Unsigned from = (lua_Unsigned)luaL_checkinteger(L, 1);
lua_Unsigned to = (lua_Unsigned)luaL_checkinteger(L, 2);
luaL_argcheck(L, from <= to, 2, "empty range");
if (to <= 0x7f) { /* ascii range? */
uint f;
byte buff[CHARSETSIZE]; /* code it as a regular charset */
clearset(buff);
for (f = (int)from; f <= to; f++)
setchar(buff, f);
newcharset(L, buff);
}
else { /* multi-byte utf-8 range */
TTree *tree = newtree(L, 2);
tree->tag = TUTFR;
codeutftree(L, tree, from, 1);
sib1(tree)->tag = TXInfo;
codeutftree(L, sib1(tree), to, 2);
}
return 1;
}
@@ -763,11 +843,18 @@ static int lp_divcapture (lua_State *L) {
tree->key = n;
return 1;
}
default: return luaL_argerror(L, 2, "invalid replacement value");
default:
return luaL_error(L, "unexpected %s as 2nd operand to LPeg '/'",
luaL_typename(L, 2));
}
}
static int lp_acccapture (lua_State *L) {
return capture_aux(L, Cacc, 2);
}
static int lp_substcapture (lua_State *L) {
return capture_aux(L, Csubst, 0);
}
@@ -906,7 +993,7 @@ static int collectrules (lua_State *L, int arg, int *totalsize) {
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 */
size = 3 + getsize(L, postab + 2); /* TGrammar + TRule + TXInfo + rule */
lua_pushnil(L); /* prepare to traverse grammar table */
while (lua_next(L, arg) != 0) {
if (lua_tonumber(L, -2) == 1 ||
@@ -920,11 +1007,11 @@ static int collectrules (lua_State *L, int arg, int *totalsize) {
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 */
size += 2 + getsize(L, -1); /* add 'TRule + TXInfo + rule' to size */
lua_pushvalue(L, -2); /* push key (for next lua_next) */
n++;
}
*totalsize = size + 1; /* TTrue to finish list of rules */
*totalsize = size + 1; /* space for 'TTrue' finishing list of rules */
return n;
}
@@ -936,11 +1023,13 @@ static void buildgrammar (lua_State *L, TTree *grammar, int frule, int n) {
int ridx = frule + 2*i + 1; /* index of i-th rule */
int rulesize;
TTree *rn = gettree(L, ridx, &rulesize);
TTree *pr = sib1(nd); /* points to rule's prerule */
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 */
pr->tag = TXInfo;
pr->u.n = i; /* rule number */
nd->u.ps = rulesize + 2; /* point to next rule */
memcpy(sib1(pr), rn, rulesize * sizeof(TTree)); /* copy rule */
mergektable(L, ridx, sib1(nd)); /* merge its ktable into new one */
nd = sib2(nd); /* move to next rule */
}
@@ -976,7 +1065,7 @@ static int checkloops (TTree *tree) {
** 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) {
static int verifyerror (lua_State *L, unsigned short *passed, int npassed) {
int i, j;
for (i = npassed - 1; i >= 0; i--) { /* search for a repetition */
for (j = i - 1; j >= 0; j--) {
@@ -1001,12 +1090,12 @@ static int verifyerror (lua_State *L, int *passed, int 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) {
static int verifyrule (lua_State *L, TTree *tree, unsigned short *passed,
int npassed, int nb) {
tailcall:
switch (tree->tag) {
case TChar: case TSet: case TAny:
case TFalse:
case TFalse: case TUTFR:
return nb; /* cannot pass from here */
case TTrue:
case TBehind: /* look-behind cannot have calls */
@@ -1014,7 +1103,7 @@ static int verifyrule (lua_State *L, TTree *tree, int *passed, int npassed,
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:
case TCapture: case TRunTime: case TXInfo:
/* return verifyrule(L, sib1(tree), passed, npassed, nb); */
tree = sib1(tree); goto tailcall;
case TCall:
@@ -1030,10 +1119,10 @@ static int verifyrule (lua_State *L, TTree *tree, int *passed, int npassed,
/* return verifyrule(L, sib2(tree), passed, npassed, nb); */
tree = sib2(tree); goto tailcall;
case TRule:
if (npassed >= MAXRULES)
return verifyerror(L, passed, npassed);
if (npassed >= MAXRULES) /* too many steps? */
return verifyerror(L, passed, npassed); /* error */
else {
passed[npassed++] = tree->key;
passed[npassed++] = tree->key; /* add rule to path */
/* return verifyrule(L, sib1(tree), passed, npassed); */
tree = sib1(tree); goto tailcall;
}
@@ -1045,7 +1134,7 @@ static int verifyrule (lua_State *L, TTree *tree, int *passed, int npassed,
static void verifygrammar (lua_State *L, TTree *grammar) {
int passed[MAXRULES];
unsigned short passed[MAXRULES];
TTree *rule;
/* check left-recursive rules */
for (rule = sib1(grammar); rule->tag == TRule; rule = sib2(rule)) {
@@ -1105,7 +1194,7 @@ 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);
return compile(L, p, getsize(L, idx));
}
@@ -1128,7 +1217,7 @@ static int lp_printcode (lua_State *L) {
printktable(L, 1);
if (p->code == NULL) /* not compiled yet? */
prepcompile(L, p, 1);
printpatt(p->code, p->codesize);
printpatt(p->code);
return 0;
}
@@ -1164,9 +1253,10 @@ static int lp_match (lua_State *L) {
const char *s = luaL_checklstring(L, SUBJIDX, &l);
size_t i = initposition(L, l);
int ptop = lua_gettop(L);
luaL_argcheck(L, l < MAXINDT, SUBJIDX, "subject too long");
lua_pushnil(L); /* initialize subscache */
lua_pushlightuserdata(L, capture); /* initialize caplistidx */
lua_getuservalue(L, 1); /* initialize penvidx */
lua_getuservalue(L, 1); /* initialize ktableidx */
r = match(L, s, s + i, s + l, code, capture, ptop);
if (r == NULL) {
lua_pushnil(L);
@@ -1195,12 +1285,6 @@ static int lp_setmax (lua_State *L) {
}
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");
@@ -1212,16 +1296,22 @@ static int lp_type (lua_State *L) {
int lp_gc (lua_State *L) {
Pattern *p = getpattern(L, 1);
realloccode(L, p, 0); /* delete code block */
freecode(L, p); /* delete code block */
return 0;
}
/*
** Create a charset representing a category of characters, given by
** the predicate 'catf'.
*/
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);
int c;
byte buff[CHARSETSIZE];
clearset(buff);
for (c = 0; c <= UCHAR_MAX; c++)
if (catf(c)) setchar(buff, c);
newcharset(L, buff);
lua_setfield(L, -2, catname);
}
@@ -1269,8 +1359,9 @@ static struct luaL_Reg pattreg[] = {
{"P", lp_P},
{"S", lp_set},
{"R", lp_range},
{"utfR", lp_utfr},
{"locale", lp_locale},
{"version", lp_version},
{"version", NULL},
{"setmaxstack", lp_setmax},
{"type", lp_type},
{NULL, NULL}
@@ -1284,6 +1375,7 @@ static struct luaL_Reg metareg[] = {
{"__gc", lp_gc},
{"__len", lp_and},
{"__div", lp_divcapture},
{"__mod", lp_acccapture},
{"__unm", lp_not},
{"__sub", lp_sub},
{NULL, NULL}
@@ -1299,6 +1391,8 @@ int luaopen_lpeg (lua_State *L) {
luaL_newlib(L, pattreg);
lua_pushvalue(L, -1);
lua_setfield(L, -3, "__index");
lua_pushliteral(L, "LPeg " VERSION);
lua_setfield(L, -2, "version");
return 1;
}

View File

@@ -1,12 +1,9 @@
/*
** $Id: lptree.h $
*/
#if !defined(lptree_h)
#define lptree_h
#include "lptypes.h"
#include "lptypes.h"
/*
@@ -14,10 +11,13 @@
*/
typedef enum TTag {
TChar = 0, /* 'n' = char */
TSet, /* the set is stored in next CHARSETSIZE bytes */
TSet, /* the set is encoded in 'u.set' and the next 'u.set.size' bytes */
TAny,
TTrue,
TFalse,
TUTFR, /* range of UTF-8 codepoints; 'n' has initial codepoint;
'cap' has length; 'key' has first byte;
extra info is similar for end codepoint */
TRep, /* 'sib1'* */
TSeq, /* 'sib1' 'sib2' */
TChoice, /* 'sib1' / 'sib2' */
@@ -26,8 +26,9 @@ typedef enum TTag {
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 */
'sib1' is rule's pattern pre-rule; 'sib2' is next rule;
extra info 'n' is rule's sequential number */
TXInfo, /* extra info */
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');
@@ -51,17 +52,26 @@ typedef struct TTree {
union {
int ps; /* occasional second child */
int n; /* occasional counter */
struct {
byte offset; /* compact set offset (in bytes) */
byte size; /* compact set size (in bytes) */
byte deflt; /* default value */
byte bitmap[1]; /* bitmap (open array) */
} set; /* for compact sets */
} u;
} TTree;
/* access to charset */
#define treebuffer(t) ((t)->u.set.bitmap)
/*
** 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;

View File

@@ -1,7 +1,6 @@
/*
** $Id: lptypes.h $
** LPeg - PEG pattern matching for Lua
** Copyright 2007-2019, Lua.org & PUC-Rio (see 'lpeg.html' for license)
** Copyright 2007-2023, Lua.org & PUC-Rio (see 'lpeg.html' for license)
** written by Roberto Ierusalimschy
*/
@@ -11,11 +10,12 @@
#include <assert.h>
#include <limits.h>
#include <string.h>
#include "lua.h"
#define VERSION "1.0.2"
#define VERSION "1.1.0"
#define PATTERN_T "lpeg-pattern"
@@ -37,6 +37,8 @@
#define luaL_setfuncs(L,f,n) luaL_register(L,NULL,f)
#define luaL_newlib(L,f) luaL_register(L,"lpeg",f)
typedef size_t lua_Unsigned;
#endif
@@ -51,9 +53,9 @@
#endif
/* maximum number of rules in a grammar (limited by 'unsigned char') */
/* maximum number of rules in a grammar (limited by 'unsigned short') */
#if !defined(MAXRULES)
#define MAXRULES 250
#define MAXRULES 1000
#endif
@@ -81,6 +83,8 @@
typedef unsigned char byte;
typedef unsigned int uint;
#define BITSPERCHAR 8
@@ -96,11 +100,11 @@ typedef struct Charset {
#define loopset(v,b) { int v; for (v = 0; v < CHARSETSIZE; v++) {b;} }
/* access to charset */
#define treebuffer(t) ((byte *)((t) + 1))
#define fillset(s,c) memset(s,c,CHARSETSIZE)
#define clearset(s) fillset(s,0)
/* number of slots needed for 'n' bytes */
#define bytes2slots(n) (((n) - 1) / sizeof(TTree) + 1)
#define bytes2slots(n) (((n) - 1u) / (uint)sizeof(TTree) + 1u)
/* set 'b' bit in charset 'cs' */
#define setchar(cs,b) ((cs)[(b) >> 3] |= (1 << ((b) & 7)))
@@ -110,8 +114,8 @@ typedef struct Charset {
** 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 getkind(op) ((op)->i.aux1 & 0xF)
#define getoff(op) (((op)->i.aux1 >> 4) & 0xF)
#define joinkindoff(k,o) ((k) | ((o) << 4))
#define MAXOFF 0xF
@@ -126,19 +130,20 @@ typedef struct Charset {
#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 instructions) for l bytes (l > 0) */
#define instsize(l) ((int)(((l) + (uint)sizeof(Instruction) - 1u) \
/ (uint)sizeof(Instruction)))
/* size (in elements) for a ISet instruction */
#define CHARSETINSTSIZE instsize(CHARSETSIZE)
#define CHARSETINSTSIZE (1 + 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))))
#define testchar(st,c) ((((uint)(st)[((c) >> 3)]) >> ((c) & 7)) & 1)
#endif

View File

@@ -1,7 +1,3 @@
/*
** $Id: lpvm.c $
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
*/
#include <limits.h>
#include <string.h>
@@ -24,7 +20,46 @@
#define getoffset(p) (((p) + 1)->offset)
static const Instruction giveup = {{IGiveup, 0, 0}};
static const Instruction giveup = {{IGiveup, 0, {0}}};
int charinset (const Instruction *i, const byte *buff, uint c) {
c -= i->i.aux2.set.offset;
if (c >= ((uint)i->i.aux2.set.size /* size in instructions... */
* (uint)sizeof(Instruction) /* in bytes... */
* 8u)) /* in bits */
return i->i.aux1; /* out of range; return default value */
return testchar(buff, c);
}
/*
** Decode one UTF-8 sequence, returning NULL if byte sequence is invalid.
*/
static const char *utf8_decode (const char *o, int *val) {
static const uint limits[] = {0xFF, 0x7F, 0x7FF, 0xFFFFu};
const unsigned char *s = (const unsigned char *)o;
uint c = s[0]; /* first byte */
uint res = 0; /* final result */
if (c < 0x80) /* ascii? */
res = c;
else {
int count = 0; /* to count number of continuation bytes */
while (c & 0x40) { /* still have continuation bytes? */
int cc = s[++count]; /* read next byte */
if ((cc & 0xC0) != 0x80) /* not a continuation byte? */
return NULL; /* invalid byte sequence */
res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
c <<= 1; /* to test next bit */
}
res |= (c & 0x7F) << (count * 5); /* add first byte */
if (count > 3 || res > 0x10FFFFu || res <= limits[count])
return NULL; /* invalid byte sequence */
s += count; /* skip continuation bytes read */
}
*val = res;
return (const char *)s + 1; /* +1 to include first byte */
}
/*
@@ -51,16 +86,25 @@ typedef struct Stack {
** array, it is simpler to ensure the array always has at least one free
** slot upfront and check its size later.)
*/
/* new size in number of elements cannot overflow integers, and new
size in bytes cannot overflow size_t. */
#define MAXNEWSIZE \
(((size_t)INT_MAX) <= (~(size_t)0 / sizeof(Capture)) ? \
((size_t)INT_MAX) : (~(size_t)0 / sizeof(Capture)))
static Capture *growcap (lua_State *L, Capture *capture, int *capsize,
int captop, int n, int ptop) {
if (*capsize - captop > n)
return capture; /* no need to grow array */
else { /* must grow */
Capture *newc;
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)))
uint newsize = captop + n + 1; /* minimum size needed */
if (newsize < (MAXNEWSIZE / 3) * 2)
newsize += newsize / 2; /* 1.5 that size, if not too big */
else if (newsize < (MAXNEWSIZE / 9) * 8)
newsize += newsize / 8; /* else, try 9/8 that size */
else
luaL_error(L, "too many captures");
newc = (Capture *)lua_newuserdata(L, newsize * sizeof(Capture));
memcpy(newc, capture, captop * sizeof(Capture));
@@ -125,7 +169,7 @@ static int resdyncaptures (lua_State *L, int fr, int curr, int limit) {
** 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) {
static void adddyncaptures (Index_t index, Capture *capture, int n, int fd) {
int i;
assert(capture[-1].kind == Cgroup && capture[-1].siz == 0);
capture[-1].idx = 0; /* make group capture an anonymous group */
@@ -133,11 +177,11 @@ static void adddyncaptures (const char *s, Capture *capture, int n, int fd) {
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[i].index = index;
}
capture[n].kind = Cclose; /* close group */
capture[n].siz = 1;
capture[n].s = s;
capture[n].index = index;
}
@@ -154,6 +198,32 @@ static int removedyncap (lua_State *L, Capture *capture,
}
/*
** Find the corresponding 'open' capture before 'cap', when that capture
** can become a full capture. If a full capture c1 is followed by an
** empty capture c2, there is no way to know whether c2 is inside
** c1. So, full captures can enclose only captures that start *before*
** its end.
*/
static Capture *findopen (Capture *cap, Index_t currindex) {
int i;
cap--; /* check last capture */
/* Must it be inside current one, but starts where current one ends? */
if (!isopencap(cap) && cap->index == currindex)
return NULL; /* current one cannot be a full capture */
/* else, look for an 'open' capture */
for (i = 0; i < MAXLOP; i++, cap--) {
if (currindex - cap->index >= UCHAR_MAX)
return NULL; /* capture too long for a full capture */
else if (isopencap(cap)) /* open capture? */
return cap; /* that's the one to be closed */
else if (cap->kind == Cclose)
return NULL; /* a full capture should not nest a non-full one */
}
return NULL; /* not found within allowed search limit */
}
/*
** Opcode interpreter
*/
@@ -181,7 +251,7 @@ const char *match (lua_State *L, const char *o, const char *s, const char *e,
case IEnd: {
assert(stack == getstackbase(L, ptop) + 1);
capture[captop].kind = Cclose;
capture[captop].s = NULL;
capture[captop].index = MAXINDT;
return s;
}
case IGiveup: {
@@ -198,47 +268,58 @@ const char *match (lua_State *L, const char *o, const char *s, const char *e,
else goto fail;
continue;
}
case IUTFR: {
int codepoint;
if (s >= e)
goto fail;
s = utf8_decode (s, &codepoint);
if (s && p[1].offset <= codepoint && codepoint <= utf_to(p))
p += 2;
else
goto fail;
continue;
}
case ITestAny: {
if (s < e) p += 2;
else p += getoffset(p);
continue;
}
case IChar: {
if ((byte)*s == p->i.aux && s < e) { p++; s++; }
if ((byte)*s == p->i.aux1 && s < e) { p++; s++; }
else goto fail;
continue;
}
case ITestChar: {
if ((byte)*s == p->i.aux && s < e) p += 2;
if ((byte)*s == p->i.aux1 && 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++; }
uint c = (byte)*s;
if (charinset(p, (p+1)->buff, c) && s < e)
{ p += 1 + p->i.aux2.set.size; s++; }
else goto fail;
continue;
}
case ITestSet: {
int c = (byte)*s;
if (testchar((p + 2)->buff, c) && s < e)
p += 1 + CHARSETINSTSIZE;
uint c = (byte)*s;
if (charinset(p, (p + 2)->buff, c) && s < e)
p += 2 + p->i.aux2.set.size;
else p += getoffset(p);
continue;
}
case IBehind: {
int n = p->i.aux;
int n = p->i.aux1;
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;
uint c = (byte)*s;
if (!charinset(p, (p+1)->buff, c)) break;
}
p += CHARSETINSTSIZE;
p += 1 + p->i.aux2.set.size;
continue;
}
case IJmp: {
@@ -280,6 +361,8 @@ const char *match (lua_State *L, const char *o, const char *s, const char *e,
case IBackCommit: {
assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
s = (--stack)->s;
if (ndyncap > 0) /* are there matchtime captures? */
ndyncap -= removedyncap(L, capture, stack->caplevel, captop);
captop = stack->caplevel;
p += getoffset(p);
continue;
@@ -287,7 +370,7 @@ const char *match (lua_State *L, const char *o, const char *s, const char *e,
case IFailTwice:
assert(stack > getstackbase(L, ptop));
stack--;
/* go through */
/* FALLTHROUGH */
case IFail:
fail: { /* pattern failed: try to backtrack */
do { /* remove pending calls */
@@ -326,38 +409,36 @@ const char *match (lua_State *L, const char *o, const char *s, const char *e,
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);
adddyncaptures(s - o, capture + captop, n, fr);
captop += n + 1; /* new captures + close group */
}
p++;
continue;
}
case ICloseCapture: {
const char *s1 = s;
Capture *open = findopen(capture + captop, s - o);
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;
if (open) { /* if possible, turn capture into a full capture */
open->siz = (s - o) - open->index + 1;
p++;
continue;
}
else {
else { /* must create a close capture */
capture[captop].siz = 1; /* mark entry as closed */
capture[captop].s = s;
capture[captop].index = s - o;
goto pushcapture;
}
}
case IOpenCapture:
capture[captop].siz = 0; /* mark entry as open */
capture[captop].s = s;
capture[captop].index = s - o;
goto pushcapture;
case IFullCapture:
capture[captop].siz = getoff(p) + 1; /* save capture size */
capture[captop].s = s - getoff(p);
capture[captop].index = s - o - getoff(p);
/* goto pushcapture; */
pushcapture: {
capture[captop].idx = p->i.key;
capture[captop].idx = p->i.aux2.key;
capture[captop].kind = getkind(p);
captop++;
capture = growcap(L, capture, &capsize, captop, 0, ptop);

View File

@@ -1,6 +1,3 @@
/*
** $Id: lpvm.h $
*/
#if !defined(lpvm_h)
#define lpvm_h
@@ -8,16 +5,24 @@
#include "lpcap.h"
/*
** About Character sets in instructions: a set is a bit map with an
** initial offset, in bits, and a size, in number of instructions.
** aux1 has the default value for the bits outsize that range.
*/
/* Virtual Machine's instructions */
typedef enum Opcode {
IAny, /* if no char, fail */
IChar, /* if char != aux, fail */
ISet, /* if char not in buff, fail */
IChar, /* if char != aux1, fail */
ISet, /* if char not in set, 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) */
ITestChar, /* if char != aux1, jump to 'offset' */
ITestSet, /* if char not in set, jump to 'offset' */
ISpan, /* read a span of chars in set */
IUTFR, /* if codepoint not in range [offset, utf_to], fail */
IBehind, /* walk back 'aux1' characters (fail if not possible) */
IRet, /* return from a rule */
IEnd, /* end of pattern */
IChoice, /* stack a choice; next fail will jump to 'offset' */
@@ -26,30 +31,46 @@ typedef enum Opcode {
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' */
IBackCommit, /* backtrack like "fail" 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
ICloseRunTime,
IEmpty /* to fill empty slots left by optimizations */
} Opcode;
/*
** All array of instructions has a 'codesize' as its first element
** and is referred by a pointer to its second element, which is the
** first actual opcode.
*/
typedef union Instruction {
struct Inst {
byte code;
byte aux;
short key;
byte aux1;
union {
short key;
struct {
byte offset;
byte size;
} set;
} aux2;
} i;
int offset;
uint codesize;
byte buff[1];
} Instruction;
void printpatt (Instruction *p, int n);
/* extract 24-bit value from an instruction */
#define utf_to(inst) (((inst)->i.aux2.key << 8) | (inst)->i.aux1)
int charinset (const Instruction *i, const byte *buff, uint c);
const char *match (lua_State *L, const char *o, const char *s, const char *e,
Instruction *op, Capture *capture, int ptop);

View File

@@ -2,7 +2,7 @@ LIBNAME = lpeg
LUADIR = ../lua/
COPT = -O2 -DNDEBUG
# COPT = -g
# COPT = -O0 -DLPEG_DEBUG -g
CWARNS = -Wall -Wextra -pedantic \
-Waggregate-return \
@@ -11,21 +11,24 @@ CWARNS = -Wall -Wextra -pedantic \
-Wdisabled-optimization \
-Wpointer-arith \
-Wshadow \
-Wredundant-decls \
-Wsign-compare \
-Wundef \
-Wwrite-strings \
-Wbad-function-cast \
-Wdeclaration-after-statement \
-Wmissing-prototypes \
-Wmissing-declarations \
-Wnested-externs \
-Wstrict-prototypes \
-Wc++-compat \
# -Wunreachable-code \
CFLAGS = $(CWARNS) $(COPT) -std=c99 -I$(LUADIR) -fPIC
CC = gcc
FILES = lpvm.o lpcap.o lptree.o lpcode.o lpprint.o
FILES = lpvm.o lpcap.o lptree.o lpcode.o lpprint.o lpcset.o
# For Linux
linux:
@@ -48,8 +51,9 @@ clean:
lpcap.o: lpcap.c lpcap.h lptypes.h
lpcode.o: lpcode.c lptypes.h lpcode.h lptree.h lpvm.h lpcap.h
lpprint.o: lpprint.c lptypes.h lpprint.h lptree.h lpvm.h lpcap.h
lptree.o: lptree.c lptypes.h lpcap.h lpcode.h lptree.h lpvm.h lpprint.h
lpcode.o: lpcode.c lptypes.h lpcode.h lptree.h lpvm.h lpcap.h lpcset.h
lpcset.o: lpcset.c lptypes.h lpcset.h lpcode.h lptree.h lpvm.h lpcap.h
lpprint.o: lpprint.c lptypes.h lpprint.h lptree.h lpvm.h lpcap.h lpcode.h
lptree.o: lptree.c lptypes.h lpcap.h lpcode.h lptree.h lpvm.h lpprint.h \
lpcset.h
lpvm.o: lpvm.c lpcap.h lptypes.h lpvm.h lpprint.h lptree.h

View File

@@ -1,22 +1,21 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
"//www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>LPeg.re - Regex syntax for LPEG</title>
<link rel="stylesheet"
href="http://www.inf.puc-rio.br/~roberto/lpeg/doc.css"
href="//www.inf.puc-rio.br/~roberto/lpeg/doc.css"
type="text/css"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<!-- $Id: re.html $ -->
<div id="container">
<div id="product">
<div id="product_logo">
<a href="http://www.inf.puc-rio.br/~roberto/lpeg/">
<a href="//www.inf.puc-rio.br/~roberto/lpeg/">
<img alt="LPeg logo" src="lpeg-128.gif"/>
</a>
</div>
@@ -62,6 +61,20 @@ Constructions are listed in order of decreasing precedence.
<table border="1">
<tbody><tr><td><b>Syntax</b></td><td><b>Description</b></td></tr>
<tr><td><code>( p )</code></td> <td>grouping</td></tr>
<tr><td><code>&amp; p</code></td> <td>and predicate</td></tr>
<tr><td><code>! p</code></td> <td>not predicate</td></tr>
<tr><td><code>p1 p2</code></td> <td>concatenation</td></tr>
<tr><td><code>p1 / p2</code></td> <td>ordered choice</td></tr>
<tr><td><code>p ?</code></td> <td>optional match</td></tr>
<tr><td><code>p *</code></td> <td>zero or more repetitions</td></tr>
<tr><td><code>p +</code></td> <td>one or more repetitions</td></tr>
<tr><td><code>p^num</code></td>
<td>exactly <code>num</code> repetitions</td></tr>
<tr><td><code>p^+num</code></td>
<td>at least <code>num</code> repetitions</td></tr>
<tr><td><code>p^-num</code></td>
<td>at most <code>num</code> repetitions</td></tr>
<tr><td>(<code>name &lt;- p</code>)<sup>+</sup></td> <td>grammar</td></tr>
<tr><td><code>'string'</code></td> <td>literal string</td></tr>
<tr><td><code>"string"</code></td> <td>literal string</td></tr>
<tr><td><code>[class]</code></td> <td>character class</td></tr>
@@ -70,22 +83,15 @@ Constructions are listed in order of decreasing precedence.
<td>pattern <code>defs[name]</code> or a pre-defined pattern</td></tr>
<tr><td><code>name</code></td><td>non terminal</td></tr>
<tr><td><code>&lt;name&gt;</code></td><td>non terminal</td></tr>
<tr><td><code>{}</code></td> <td>position capture</td></tr>
<tr><td><code>{ p }</code></td> <td>simple capture</td></tr>
<tr><td><code>{: p :}</code></td> <td>anonymous group capture</td></tr>
<tr><td><code>{:name: p :}</code></td> <td>named group capture</td></tr>
<tr><td><code>{~ p ~}</code></td> <td>substitution capture</td></tr>
<tr><td><code>{| p |}</code></td> <td>table capture</td></tr>
<tr><td><code>=name</code></td> <td>back reference
</td></tr>
<tr><td><code>p ?</code></td> <td>optional match</td></tr>
<tr><td><code>p *</code></td> <td>zero or more repetitions</td></tr>
<tr><td><code>p +</code></td> <td>one or more repetitions</td></tr>
<tr><td><code>p^num</code></td> <td>exactly <code>n</code> repetitions</td></tr>
<tr><td><code>p^+num</code></td>
<td>at least <code>n</code> repetitions</td></tr>
<tr><td><code>p^-num</code></td>
<td>at most <code>n</code> repetitions</td></tr>
<tr><td><code>=name</code></td> <td>back reference</td></tr>
<tr><td><code>p -&gt; 'string'</code></td> <td>string capture</td></tr>
<tr><td><code>p -&gt; "string"</code></td> <td>string capture</td></tr>
<tr><td><code>p -&gt; num</code></td> <td>numbered capture</td></tr>
@@ -94,16 +100,13 @@ equivalent to <code>p / defs[name]</code></td></tr>
<tr><td><code>p =&gt; name</code></td> <td>match-time capture
equivalent to <code>lpeg.Cmt(p, defs[name])</code></td></tr>
<tr><td><code>p ~&gt; name</code></td> <td>fold capture
equivalent to <code>lpeg.Cf(p, defs[name])</code></td></tr>
<tr><td><code>& p</code></td> <td>and predicate</td></tr>
<tr><td><code>! p</code></td> <td>not predicate</td></tr>
<tr><td><code>p1 p2</code></td> <td>concatenation</td></tr>
<tr><td><code>p1 / p2</code></td> <td>ordered choice</td></tr>
<tr><td>(<code>name &lt;- p</code>)<sup>+</sup></td> <td>grammar</td></tr>
(deprecated)</td></tr>
<tr><td><code>p &gt;&gt; name</code></td> <td>accumulator capture
equivalent to <code>(p % defs[name])</code></td></tr>
</tbody></table>
<p>
Any space appearing in a syntax description can be
replaced by zero or more space characters and Lua-style comments
replaced by zero or more space characters and Lua-style short comments
(<code>--</code> until end of line).
</p>
@@ -200,9 +203,10 @@ print(re.match("the number 423 is odd", "({%a+} / .)*"))
--&gt; the number is odd
-- returns the first numeral in a string
print(re.match("the number 423 is odd", "s <- {%d+} / . s"))
print(re.match("the number 423 is odd", "s &lt;- {%d+} / . s"))
--&gt; 423
-- substitutes a dot for each vowel in a string
print(re.gsub("hello World", "[aeiou]", "."))
--&gt; h.ll. W.rld
</pre>
@@ -329,7 +333,7 @@ respecting the indentation:
<pre class="example">
p = re.compile[[
block &lt;- {| {:ident:' '*:} line
((=ident !' ' line) / &(=ident ' ') block)* |}
((=ident !' ' line) / &amp;(=ident ' ') block)* |}
line &lt;- {[^%nl]*} %nl
]]
</pre>
@@ -416,6 +420,7 @@ prefix &lt;- '&amp;' S prefix / '!' S prefix / suffix
suffix &lt;- primary S (([+*?]
/ '^' [+-]? num
/ '-&gt;' S (string / '{}' / name)
/ '&gt&gt;' S name
/ '=&gt;' S name) S)*
primary &lt;- '(' exp ')' / string / class / defined
@@ -423,6 +428,7 @@ primary &lt;- '(' exp ')' / string / class / defined
/ '=' name
/ '{}'
/ '{~' exp '~}'
/ '{|' exp '|}'
/ '{' exp '}'
/ '.'
/ name S !arrow
@@ -436,7 +442,7 @@ item &lt;- defined / range / .
range &lt;- . '-' [^]]
S &lt;- (%s / '--' [^%nl]*)* -- spaces and comments
name &lt;- [A-Za-z][A-Za-z0-9_]*
name &lt;- [A-Za-z_][A-Za-z0-9_]*
arrow &lt;- '&lt;-'
num &lt;- [0-9]+
string &lt;- '"' [^"]* '"' / "'" [^']* "'"
@@ -452,37 +458,9 @@ print(re.match(p, p)) -- a self description must match itself
<h2><a name="license">License</a></h2>
<p>
Copyright &copy; 2008-2015 Lua.org, PUC-Rio.
</p>
<p>
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:
</p>
This module is part of the <a href="lpeg.html">LPeg</a> package and shares
its <a href="lpeg.html#license">license</a>.
<p>
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of the Software.
</p>
<p>
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.
</p>
</div> <!-- id="content" -->

View File

@@ -1,4 +1,7 @@
-- $Id: re.lua $
--
-- Copyright 2007-2023, Lua.org & PUC-Rio (see 'lpeg.html' for license)
-- written by Roberto Ierusalimschy
--
-- imported functions and modules
local tonumber, type, print, error = tonumber, type, print, error
@@ -10,14 +13,14 @@ local m = require"lpeg"
-- on 'mm'
local mm = m
-- pattern's metatable
-- patterns' metatable
local mt = getmetatable(mm.P(0))
local version = _VERSION
-- No more global accesses after this point
local version = _VERSION
if version == "Lua 5.2" then _ENV = nil end
_ENV = nil -- does no harm in Lua 5.1
local any = m.P(1)
@@ -142,7 +145,7 @@ local item = (defined + Range + m.C(any)) / m.P
local Class =
"["
* (m.C(m.P"^"^-1)) -- optional complement symbol
* m.Cf(item * (item - "]")^0, mt.__add) /
* (item * ((item % mt.__add) - "]")^0) /
function (c, p) return c == "^" and any - p or p end
* "]"
@@ -168,13 +171,13 @@ end
local exp = m.P{ "Exp",
Exp = S * ( m.V"Grammar"
+ m.Cf(m.V"Seq" * ("/" * S * m.V"Seq")^0, mt.__add) );
Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix"^0 , mt.__mul)
+ m.V"Seq" * ("/" * S * m.V"Seq" % mt.__add)^0 );
Seq = (m.Cc(m.P"") * (m.V"Prefix" % mt.__mul)^0)
* (#seq_follow + patt_error);
Prefix = "&" * S * m.V"Prefix" / mt.__len
+ "!" * S * m.V"Prefix" / mt.__unm
+ m.V"Suffix";
Suffix = m.Cf(m.V"Primary" * S *
Suffix = m.V"Primary" * S *
( ( m.P"+" * m.Cc(1, mt.__pow)
+ m.P"*" * m.Cc(0, mt.__pow)
+ m.P"?" * m.Cc(-1, mt.__pow)
@@ -185,10 +188,11 @@ local exp = m.P{ "Exp",
+ m.P"{}" * m.Cc(nil, m.Ct)
+ defwithfunc(mt.__div)
)
+ "=>" * S * defwithfunc(m.Cmt)
+ "~>" * S * defwithfunc(m.Cf)
) * S
)^0, function (a,b,f) return f(a,b) end );
+ "=>" * S * defwithfunc(mm.Cmt)
+ ">>" * S * defwithfunc(mt.__mod)
+ "~>" * S * defwithfunc(mm.Cf)
) % function (a,b,f) return f(a,b) end * S
)^0;
Primary = "(" * m.V"Exp" * ")"
+ String / mm.P
+ Class
@@ -204,8 +208,7 @@ local exp = m.P{ "Exp",
+ (name * -arrow + "<" * name * ">") * m.Cb("G") / NT;
Definition = name * arrow * m.V"Exp";
Grammar = m.Cg(m.Cc(true), "G") *
m.Cf(m.V"Definition" / firstdef * m.Cg(m.V"Definition")^0,
adddef) / mm.P
((m.V"Definition" / firstdef) * (m.V"Definition" % adddef)^0) / mm.P
}
local pattern = S * m.Cg(m.Cc(false), "G") * exp / mm.P * (-any + patt_error)

View File

@@ -1,7 +1,5 @@
#!/usr/bin/env lua
-- $Id: test.lua $
-- require"strict" -- just to be pedantic
local m = require"lpeg"
@@ -48,8 +46,8 @@ end
print"General tests for LPeg library"
assert(type(m.version()) == "string")
print("version " .. m.version())
assert(type(m.version) == "string")
print(m.version)
assert(m.type("alo") ~= "pattern")
assert(m.type(io.input) ~= "pattern")
assert(m.type(m.P"alo") == "pattern")
@@ -70,6 +68,8 @@ assert(m.match(#m.P(true) * "a", "a") == 2)
assert(m.match("a" * #m.P(false), "a") == nil)
assert(m.match("a" * #m.P(true), "a") == 2)
assert(m.match(m.P(1)^0, "abcd") == 5)
assert(m.match(m.S("")^0, "abcd") == 1)
-- tests for locale
do
@@ -119,6 +119,8 @@ eqcharset(m.S"\1\0\2", m.R"\0\2")
eqcharset(m.S"\1\0\2", m.R"\1\2" + "\0")
eqcharset(m.S"\1\0\2" - "\0", m.R"\1\2")
eqcharset(m.S("\0\255"), m.P"\0" + "\255") -- charset extremes
local word = alpha^1 * (1 - alpha)^0
assert((word^0 * -1):match"alo alo")
@@ -406,7 +408,7 @@ assert(p:match('abcx') == 5 and p:match('ayzx') == 5 and not p:match'abc')
do
-- large dynamic Cc
print "testing large dynamic Cc"
local lim = 2^16 - 1
local c = 0
local function seq (n)
@@ -491,8 +493,8 @@ local function f_term (v1, op, v2, d)
end
G = m.P{ "Exp",
Exp = m.Cf(V"Factor" * m.Cg(FactorOp * V"Factor")^0, f_factor);
Factor = m.Cf(V"Term" * m.Cg(TermOp * V"Term")^0, f_term);
Exp = V"Factor" * (FactorOp * V"Factor" % f_factor)^0;
Factor = V"Term" * (TermOp * V"Term" % f_term)^0;
Term = Number / tonumber + Open * V"Exp" * Close;
}
@@ -864,6 +866,7 @@ print"+"
-- accumulator capture
function f (x) return x + 1 end
assert(m.match(m.Cf(m.Cc(0) * m.C(1)^0, f), "alo alo") == 7)
assert(m.match(m.Cc(0) * (m.C(1) % f)^0, "alo alo") == 7)
t = {m.match(m.Cf(m.Cc(1,2,3), error), "")}
checkeq(t, {1})
@@ -873,7 +876,7 @@ t = p:match("a=b;c=du;xux=yuy;")
checkeq(t, {a="b", c="du", xux="yuy"})
-- errors in accumulator capture
-- errors in fold capture
-- no initial capture
checkerr("no initial value", m.match, m.Cf(m.P(5), print), 'aaaaaa')
@@ -881,8 +884,14 @@ checkerr("no initial value", m.match, m.Cf(m.P(5), print), 'aaaaaa')
checkerr("no initial value", m.match, m.Cf(m.P(500), print),
string.rep('a', 600))
-- nested capture produces no initial value
checkerr("no initial value", m.match, m.Cf(m.P(1) / {}, print), "alo")
-- errors in accumulator capture
-- no initial capture
checkerr("no previous value", m.match, m.P(5) % print, 'aaaaaa')
-- no initial capture (very long match forces fold to be a pair open-close)
checkerr("no previous value", m.match, m.P(500) % print,
string.rep('a', 600))
-- tests for loop checker
@@ -985,10 +994,10 @@ for i = 1, 10 do
assert(p:match("aaaaaaaaaaa") == 11 - i + 1)
end
print"+"
-- tests for back references
print "testing back references"
checkerr("back reference 'x' not found", m.match, m.Cb('x'), '')
checkerr("back reference 'b' not found", m.match, m.Cg(1, 'a') * m.Cb('b'), 'a')
@@ -996,6 +1005,35 @@ p = m.Cg(m.C(1) * m.C(1), "k") * m.Ct(m.Cb("k"))
t = p:match("ab")
checkeq(t, {"a", "b"})
do
-- some basic cases
assert(m.match(m.Cg(m.Cc(3), "a") * m.Cb("a"), "a") == 3)
assert(m.match(m.Cg(m.C(1), 133) * m.Cb(133), "X") == "X")
-- first reference to 'x' should not see the group enclosing it
local p = m.Cg(m.Cb('x'), 'x') * m.Cb('x')
checkerr("back reference 'x' not found", m.match, p, '')
local p = m.Cg(m.Cb('x') * m.C(1), 'x') * m.Cb('x')
checkerr("back reference 'x' not found", m.match, p, 'abc')
-- reference to 'x' should not see the group enclosed in another capture
local s = string.rep("a", 30)
local p = (m.C(1)^-4 * m.Cg(m.C(1), 'x')) / {} * m.Cb('x')
checkerr("back reference 'x' not found", m.match, p, s)
local p = (m.C(1)^-20 * m.Cg(m.C(1), 'x')) / {} * m.Cb('x')
checkerr("back reference 'x' not found", m.match, p, s)
-- second reference 'k' should refer to 10 and first ref. 'k'
p = m.Cg(m.Cc(20), 'k') * m.Cg(m.Cc(10) * m.Cb('k') * m.C(1), 'k')
* (m.Cb('k') / function (a,b,c) return a*10 + b + tonumber(c) end)
-- 10 * 10 (Cc) + 20 (Cb) + 7 (C) == 127
assert(p:match("756") == 127)
end
p = m.P(true)
for i = 1, 10 do p = p * m.Cg(1, i) end
for i = 1, 10 do
@@ -1032,6 +1070,17 @@ local function id (s, i, ...)
return true, ...
end
do -- run-time capture in an end predicate (should discard its value)
local x = 0
function foo (s, i)
x = x + 1
return true, x
end
local p = #(m.Cmt("", foo) * "xx") * m.Cmt("", foo)
assert(p:match("xx") == 2)
end
assert(m.Cmt(m.Cs((m.Cmt(m.S'abc' / { a = 'x', c = 'y' }, id) +
m.R'09'^1 / string.char +
m.P(1))^0), id):match"acb98+68c" == "xyb\98+\68y")
@@ -1156,7 +1205,7 @@ end
-- bug in 1.0: problems with math-times returning too many captures
do
if _VERSION >= "Lua 5.2" then
local lim = 2^11 - 10
local res = {m.match(manyCmt(lim), "a")}
assert(#res == lim and res[1] == lim - 1 and res[lim] == 0)
@@ -1171,9 +1220,91 @@ t = {p:match('abacc')}
checkeq(t, {'a', 'aa', 20, 'a', 'aaa', 'aaa'})
do print"testing large grammars"
local lim = 1000 -- number of rules
local t = {}
for i = 3, lim do
t[i] = m.V(i - 1) -- each rule calls previous one
end
t[1] = m.V(lim) -- start on last rule
t[2] = m.C("alo") -- final rule
local P = m.P(t) -- build grammar
assert(P:match("alo") == "alo")
t[#t + 1] = m.P("x") -- one more rule...
checkerr("too many rules", m.P, t)
end
print "testing UTF-8 ranges"
do -- a few typical UTF-8 ranges
local p = m.utfR(0x410, 0x44f)^1 / "cyr: %0"
+ m.utfR(0x4e00, 0x9fff)^1 / "cjk: %0"
+ m.utfR(0x1F600, 0x1F64F)^1 / "emot: %0"
+ m.utfR(0, 0x7f)^1 / "ascii: %0"
+ m.utfR(0, 0x10ffff) / "other: %0"
p = m.Ct(p^0) * -m.P(1)
local cyr = "ждюя"
local emot = "\240\159\152\128\240\159\153\128" -- 😀🙀
local cjk = "专举乸"
local ascii = "alo"
local last = "\244\143\191\191" -- U+10FFFF
local s = cyr .. "" .. emot .. "" .. cjk .. "" .. ascii .. last
t = (p:match(s))
assert(t[1] == "cyr: " .. cyr and t[2] == "other: —" and
t[3] == "emot: " .. emot and t[4] == "other: —" and
t[5] == "cjk: " .. cjk and t[6] == "other: —" and
t[7] == "ascii: " .. ascii and t[8] == "other: " .. last and
t[9] == nil)
-- failing UTF-8 matches and borders
assert(not m.match(m.utfR(10, 0x2000), "\9"))
assert(not m.match(m.utfR(10, 0x2000), "\226\128\129"))
assert(m.match(m.utfR(10, 0x2000), "\10") == 2)
assert(m.match(m.utfR(10, 0x2000), "\226\128\128") == 4)
end
do -- valid and invalid code points
local p = m.utfR(0, 0x10ffff)^0
assert(p:match("汉字\128") == #"汉字" + 1)
assert(p:match("\244\159\191") == 1)
assert(p:match("\244\159\191\191") == 1)
assert(p:match("\255") == 1)
-- basic errors
checkerr("empty range", m.utfR, 1, 0)
checkerr("invalid code point", m.utfR, 1, 0x10ffff + 1)
end
do -- back references (fixed width)
-- match a byte after a CJK point
local p = m.B(m.utfR(0x4e00, 0x9fff)) * m.C(1)
p = m.P{ p + m.P(1) * m.V(1) } -- search for 'p'
assert(p:match("ab д 专X x") == "X")
-- match a byte after a hebrew point
local p = m.B(m.utfR(0x5d0, 0x5ea)) * m.C(1)
p = m.P(#"ש") * p
assert(p:match("שX") == "X")
checkerr("fixed length", m.B, m.utfR(0, 0x10ffff))
end
-------------------------------------------------------------------
-- Tests for 're' module
-------------------------------------------------------------------
print"testing 're' module"
local re = require "re"
@@ -1307,6 +1438,12 @@ e = compile([[
e = compile("{[0-9]+'.'?[0-9]*} -> sin", math)
assert(e:match("2.34") == math.sin(2.34))
e = compile("'pi' -> math", _G)
assert(e:match("pi") == math.pi)
e = compile("[ ]* 'version' -> _VERSION", _G)
assert(e:match(" version") == _VERSION)
function eq (_, _, a, b) return a == b end
@@ -1380,6 +1517,13 @@ c = re.compile([[
]], {tonumber = tonumber, add = function (a,b) return a + b end})
assert(c:match("3 401 50") == 3 + 401 + 50)
-- test for accumulator captures
c = re.compile([[
S <- number (%s+ number >> add)*
number <- %d+ -> tonumber
]], {tonumber = tonumber, add = function (a,b) return a + b end})
assert(c:match("3 401 50") == 3 + 401 + 50)
-- tests for look-ahead captures
x = {re.match("alo", "&(&{.}) !{'b'} {&(...)} &{..} {...} {!.}")}
checkeq(x, {"", "alo", ""})

View File

@@ -1237,7 +1237,7 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
lua_State * eL;
int err;
const void * oldv;
if (level == CACHE_OFF) {
if (level == CACHE_OFF || filename == NULL) {
return luaL_loadfilex_(L, filename, mode);
}
proto = load_proto(filename);

View File

@@ -1351,6 +1351,35 @@ static int constfolding (FuncState *fs, int op, expdesc *e1,
}
/*
** Convert a BinOpr to an OpCode (ORDER OPR - ORDER OP)
*/
l_sinline OpCode binopr2op (BinOpr opr, BinOpr baser, OpCode base) {
lua_assert(baser <= opr &&
((baser == OPR_ADD && opr <= OPR_SHR) ||
(baser == OPR_LT && opr <= OPR_LE)));
return cast(OpCode, (cast_int(opr) - cast_int(baser)) + cast_int(base));
}
/*
** Convert a UnOpr to an OpCode (ORDER OPR - ORDER OP)
*/
l_sinline OpCode unopr2op (UnOpr opr) {
return cast(OpCode, (cast_int(opr) - cast_int(OPR_MINUS)) +
cast_int(OP_UNM));
}
/*
** Convert a BinOpr to a tag method (ORDER OPR - ORDER TM)
*/
l_sinline TMS binopr2TM (BinOpr opr) {
lua_assert(OPR_ADD <= opr && opr <= OPR_SHR);
return cast(TMS, (cast_int(opr) - cast_int(OPR_ADD)) + cast_int(TM_ADD));
}
/*
** Emit code for unary expressions that "produce values"
** (everything but 'not').
@@ -1389,15 +1418,15 @@ static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2,
** Emit code for binary expressions that "produce values" over
** two registers.
*/
static void codebinexpval (FuncState *fs, OpCode op,
static void codebinexpval (FuncState *fs, BinOpr opr,
expdesc *e1, expdesc *e2, int line) {
OpCode op = binopr2op(opr, OPR_ADD, OP_ADD);
int v2 = luaK_exp2anyreg(fs, e2); /* make sure 'e2' is in a register */
/* 'e1' must be already in a register or it is a constant */
lua_assert((VNIL <= e1->k && e1->k <= VKSTR) ||
e1->k == VNONRELOC || e1->k == VRELOC);
lua_assert(OP_ADD <= op && op <= OP_SHR);
finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN,
cast(TMS, (op - OP_ADD) + TM_ADD));
finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN, binopr2TM(opr));
}
@@ -1418,9 +1447,9 @@ static void codebini (FuncState *fs, OpCode op,
*/
static void codebinK (FuncState *fs, BinOpr opr,
expdesc *e1, expdesc *e2, int flip, int line) {
TMS event = cast(TMS, opr + TM_ADD);
TMS event = binopr2TM(opr);
int v2 = e2->u.info; /* K index */
OpCode op = cast(OpCode, opr + OP_ADDK);
OpCode op = binopr2op(opr, OPR_ADD, OP_ADDK);
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event);
}
@@ -1457,10 +1486,9 @@ static void swapexps (expdesc *e1, expdesc *e2) {
*/
static void codebinNoK (FuncState *fs, BinOpr opr,
expdesc *e1, expdesc *e2, int flip, int line) {
OpCode op = cast(OpCode, opr + OP_ADD);
if (flip)
swapexps(e1, e2); /* back to original order */
codebinexpval(fs, op, e1, e2, line); /* use standard operators */
codebinexpval(fs, opr, e1, e2, line); /* use standard operators */
}
@@ -1490,7 +1518,7 @@ static void codecommutative (FuncState *fs, BinOpr op,
flip = 1;
}
if (op == OPR_ADD && isSCint(e2)) /* immediate operand? */
codebini(fs, cast(OpCode, OP_ADDI), e1, e2, flip, line, TM_ADD);
codebini(fs, OP_ADDI, e1, e2, flip, line, TM_ADD);
else
codearith(fs, op, e1, e2, flip, line);
}
@@ -1518,25 +1546,27 @@ static void codebitwise (FuncState *fs, BinOpr opr,
** Emit code for order comparisons. When using an immediate operand,
** 'isfloat' tells whether the original value was a float.
*/
static void codeorder (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) {
static void codeorder (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
int r1, r2;
int im;
int isfloat = 0;
OpCode op;
if (isSCnumber(e2, &im, &isfloat)) {
/* use immediate operand */
r1 = luaK_exp2anyreg(fs, e1);
r2 = im;
op = cast(OpCode, (op - OP_LT) + OP_LTI);
op = binopr2op(opr, OPR_LT, OP_LTI);
}
else if (isSCnumber(e1, &im, &isfloat)) {
/* transform (A < B) to (B > A) and (A <= B) to (B >= A) */
r1 = luaK_exp2anyreg(fs, e2);
r2 = im;
op = (op == OP_LT) ? OP_GTI : OP_GEI;
op = binopr2op(opr, OPR_LT, OP_GTI);
}
else { /* regular case, compare two registers */
r1 = luaK_exp2anyreg(fs, e1);
r2 = luaK_exp2anyreg(fs, e2);
op = binopr2op(opr, OPR_LT, OP_LT);
}
freeexps(fs, e1, e2);
e1->u.info = condjump(fs, op, r1, r2, isfloat, 1);
@@ -1579,16 +1609,16 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
/*
** Apply prefix operation 'op' to expression 'e'.
*/
void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) {
void luaK_prefix (FuncState *fs, UnOpr opr, expdesc *e, int line) {
static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP};
luaK_dischargevars(fs, e);
switch (op) {
switch (opr) {
case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */
if (constfolding(fs, op + LUA_OPUNM, e, &ef))
if (constfolding(fs, opr + LUA_OPUNM, e, &ef))
break;
/* else */ /* FALLTHROUGH */
case OPR_LEN:
codeunexpval(fs, cast(OpCode, op + OP_UNM), e, line);
codeunexpval(fs, unopr2op(opr), e, line);
break;
case OPR_NOT: codenot(fs, e); break;
default: lua_assert(0);
@@ -1718,30 +1748,27 @@ void luaK_posfix (FuncState *fs, BinOpr opr,
/* coded as (r1 >> -I) */;
}
else /* regular case (two registers) */
codebinexpval(fs, OP_SHL, e1, e2, line);
codebinexpval(fs, opr, e1, e2, line);
break;
}
case OPR_SHR: {
if (isSCint(e2))
codebini(fs, OP_SHRI, e1, e2, 0, line, TM_SHR); /* r1 >> I */
else /* regular case (two registers) */
codebinexpval(fs, OP_SHR, e1, e2, line);
codebinexpval(fs, opr, e1, e2, line);
break;
}
case OPR_EQ: case OPR_NE: {
codeeq(fs, opr, e1, e2);
break;
}
case OPR_LT: case OPR_LE: {
OpCode op = cast(OpCode, (opr - OPR_EQ) + OP_EQ);
codeorder(fs, op, e1, e2);
break;
}
case OPR_GT: case OPR_GE: {
/* '(a > b)' <=> '(b < a)'; '(a >= b)' <=> '(b <= a)' */
OpCode op = cast(OpCode, (opr - OPR_NE) + OP_EQ);
swapexps(e1, e2);
codeorder(fs, op, e1, e2);
opr = cast(BinOpr, (opr - OPR_GT) + OPR_LT);
} /* FALLTHROUGH */
case OPR_LT: case OPR_LE: {
codeorder(fs, opr, e1, e2);
break;
}
default: lua_assert(0);

View File

@@ -76,7 +76,7 @@ static int luaB_auxwrap (lua_State *L) {
if (l_unlikely(r < 0)) { /* error? */
int stat = lua_status(co);
if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */
stat = lua_resetthread(co, L); /* close its tbc variables */
stat = lua_closethread(co, L); /* close its tbc variables */
lua_assert(stat != LUA_OK);
lua_xmove(co, L, 1); /* move error message to the caller */
}
@@ -172,7 +172,7 @@ static int luaB_close (lua_State *L) {
int status = auxstatus(L, co);
switch (status) {
case COS_DEAD: case COS_YIELD: {
status = lua_resetthread(co, L);
status = lua_closethread(co, L);
if (status == LUA_OK) {
lua_pushboolean(L, 1);
return 1;

View File

@@ -656,18 +656,19 @@ static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
/*
** Check whether pointer 'o' points to some value in the stack
** frame of the current function. Because 'o' may not point to a
** value in this stack, we cannot compare it with the region
** boundaries (undefined behaviour in ISO C).
** Check whether pointer 'o' points to some value in the stack frame of
** the current function and, if so, returns its index. Because 'o' may
** not point to a value in this stack, we cannot compare it with the
** region boundaries (undefined behavior in ISO C).
*/
static int isinstack (CallInfo *ci, const TValue *o) {
StkId pos;
for (pos = ci->func.p + 1; pos < ci->top.p; pos++) {
if (o == s2v(pos))
return 1;
static int instack (CallInfo *ci, const TValue *o) {
int pos;
StkId base = ci->func.p + 1;
for (pos = 0; base + pos < ci->top.p; pos++) {
if (o == s2v(base + pos))
return pos;
}
return 0; /* not found */
return -1; /* not found */
}
@@ -708,9 +709,11 @@ static const char *varinfo (lua_State *L, const TValue *o) {
const char *kind = NULL;
if (isLua(ci)) {
kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */
if (!kind && isinstack(ci, o)) /* no? try a register */
kind = getobjname(ci_func(ci)->p, currentpc(ci),
cast_int(cast(StkId, o) - (ci->func.p + 1)), &name);
if (!kind) { /* not an upvalue? */
int reg = instack(ci, o); /* try a register */
if (reg >= 0) /* is 'o' a register? */
kind = getobjname(ci_func(ci)->p, currentpc(ci), reg, &name);
}
}
return formatvarinfo(L, kind, name);
}
@@ -845,7 +848,7 @@ static int changedline (const Proto *p, int oldpc, int newpc) {
if (p->lineinfo == NULL) /* no debug information? */
return 0;
if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */
int delta = 0; /* line diference */
int delta = 0; /* line difference */
int pc = oldpc;
for (;;) {
int lineinfo = p->lineinfo[++pc];

View File

@@ -299,17 +299,13 @@ static int stackinuse (lua_State *L) {
*/
void luaD_shrinkstack (lua_State *L) {
int inuse = stackinuse(L);
int nsize = inuse * 2; /* proposed new size */
int max = inuse * 3; /* maximum "reasonable" size */
if (max > LUAI_MAXSTACK) {
max = LUAI_MAXSTACK; /* respect stack limit */
if (nsize > LUAI_MAXSTACK)
nsize = LUAI_MAXSTACK;
}
int max = (inuse > LUAI_MAXSTACK / 3) ? LUAI_MAXSTACK : inuse * 3;
/* if thread is currently not handling a stack overflow and its
size is larger than maximum "reasonable" size, shrink it */
if (inuse <= LUAI_MAXSTACK && stacksize(L) > max)
if (inuse <= LUAI_MAXSTACK && stacksize(L) > max) {
int nsize = (inuse > LUAI_MAXSTACK / 2) ? LUAI_MAXSTACK : inuse * 2;
luaD_reallocstack(L, nsize, 0); /* ok if that fails */
}
else /* don't change stack */
condmovestack(L,{},{}); /* (change only for debugging) */
luaE_shrinkCI(L); /* shrink CI list */
@@ -629,7 +625,7 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
** check the stack before doing anything else. 'luaD_precall' already
** does that.
*/
l_sinline void ccall (lua_State *L, StkId func, int nResults, int inc) {
l_sinline void ccall (lua_State *L, StkId func, int nResults, l_uint32 inc) {
CallInfo *ci;
L->nCcalls += inc;
if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) {

View File

@@ -10,6 +10,7 @@
#include "lprefix.h"
#include <limits.h>
#include <stddef.h>
#include "lua.h"
@@ -55,8 +56,11 @@ static void dumpByte (DumpState *D, int y) {
}
/* dumpInt Buff Size */
#define DIBS ((sizeof(size_t) * 8 / 7) + 1)
/*
** 'dumpSize' buffer size: each byte can store up to 7 bits. (The "+6"
** rounds up the division.)
*/
#define DIBS ((sizeof(size_t) * CHAR_BIT + 6) / 7)
static void dumpSize (DumpState *D, size_t x) {
lu_byte buff[DIBS];

View File

@@ -1687,12 +1687,15 @@ static void incstep (lua_State *L, global_State *g) {
}
/*
** performs a basic GC step if collector is running
** Performs a basic GC step if collector is running. (If collector is
** not running, set a reasonable debt to avoid it being called at
** every single check.)
*/
void luaC_step (lua_State *L) {
global_State *g = G(L);
lua_assert(!g->gcemergency);
if (gcrunning(g)) { /* running? */
if (!gcrunning(g)) /* not running? */
luaE_setdebt(g, -2000);
else {
if(isdecGCmodegen(g))
genstep(L, g);
else

View File

@@ -177,18 +177,19 @@
#define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0)
#define luaC_barrier(L,p,v) ( \
(iscollectable(v) && isblack(p) && ispurewhite(gcvalue(v))) ? \
luaC_barrier_(L,obj2gco(p),gcvalue(v)) : cast_void(0))
#define luaC_barrierback(L,p,v) ( \
(iscollectable(v) && isblack(p) && ispurewhite(gcvalue(v))) ? \
luaC_barrierback_(L,p) : cast_void(0))
#define luaC_objbarrier(L,p,o) ( \
(isblack(p) && ispurewhite(o)) ? \
luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0))
#define luaC_barrier(L,p,v) ( \
iscollectable(v) ? luaC_objbarrier(L,p,gcvalue(v)) : cast_void(0))
#define luaC_objbarrierback(L,p,o) ( \
(isblack(p) && ispurewhite(o)) ? luaC_barrierback_(L,p) : cast_void(0))
#define luaC_barrierback(L,p,v) ( \
iscollectable(v) ? luaC_objbarrierback(L, p, gcvalue(v)) : cast_void(0))
LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o);
LUAI_FUNC void luaC_freeallobjects (lua_State *L);
LUAI_FUNC void luaC_step (lua_State *L);

View File

@@ -128,7 +128,7 @@ l_noret luaX_syntaxerror (LexState *ls, const char *msg) {
** ensuring there is only one copy of each unique string. The table
** here is used as a set: the string enters as the key, while its value
** is irrelevant. We use the string itself as the value only because it
** is a TValue readly available. Later, the code generation can change
** is a TValue readily available. Later, the code generation can change
** this value.
*/
TString *luaX_newstring (LexState *ls, const char *str, size_t l) {

View File

@@ -71,11 +71,24 @@ typedef signed char ls_byte;
/*
** conversion of pointer to unsigned integer:
** this is for hashing only; there is no problem if the integer
** cannot hold the whole pointer value
** conversion of pointer to unsigned integer: this is for hashing only;
** there is no problem if the integer cannot hold the whole pointer
** value. (In strict ISO C this may cause undefined behavior, but no
** actual machine seems to bother.)
*/
#define point2uint(p) ((unsigned int)((size_t)(p) & UINT_MAX))
#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \
__STDC_VERSION__ >= 199901L
#include <stdint.h>
#if defined(UINTPTR_MAX) /* even in C99 this type is optional */
#define L_P2I uintptr_t
#else /* no 'intptr'? */
#define L_P2I uintmax_t /* use the largest available integer */
#endif
#else /* C89 option */
#define L_P2I size_t
#endif
#define point2uint(p) ((unsigned int)((L_P2I)(p) & UINT_MAX))

View File

@@ -267,7 +267,7 @@ static int math_type (lua_State *L) {
/* try to find an integer type with at least 64 bits */
#if (ULONG_MAX >> 31 >> 31) >= 3
#if ((ULONG_MAX >> 31) >> 31) >= 3
/* 'long' has at least 64 bits */
#define Rand64 unsigned long
@@ -277,9 +277,9 @@ static int math_type (lua_State *L) {
/* there is a 'long long' type (which must have at least 64 bits) */
#define Rand64 unsigned long long
#elif (LUA_MAXUNSIGNED >> 31 >> 31) >= 3
#elif ((LUA_MAXUNSIGNED >> 31) >> 31) >= 3
/* 'lua_Integer' has at least 64 bits */
/* 'lua_Unsigned' has at least 64 bits */
#define Rand64 lua_Unsigned
#endif
@@ -500,12 +500,12 @@ static lua_Number I2d (Rand64 x) {
/* convert a 'Rand64' to a 'lua_Unsigned' */
static lua_Unsigned I2UInt (Rand64 x) {
return ((lua_Unsigned)trim32(x.h) << 31 << 1) | (lua_Unsigned)trim32(x.l);
return (((lua_Unsigned)trim32(x.h) << 31) << 1) | (lua_Unsigned)trim32(x.l);
}
/* convert a 'lua_Unsigned' to a 'Rand64' */
static Rand64 Int2I (lua_Unsigned n) {
return packI((lu_int32)(n >> 31 >> 1), (lu_int32)n);
return packI((lu_int32)((n >> 31) >> 1), (lu_int32)n);
}
#endif /* } */

View File

@@ -22,25 +22,6 @@
#include "lstate.h"
#if defined(EMERGENCYGCTESTS)
/*
** First allocation will fail whenever not building initial state.
** (This fail will trigger 'tryagain' and a full GC cycle at every
** allocation.)
*/
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
if (completestate(g) && ns > 0) /* frees never fail */
return NULL; /* fail */
else /* normal allocation */
return (*g->frealloc)(g->ud, block, os, ns);
}
#else
#define firsttry(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns))
#endif
/*
** About the realloc function:
@@ -60,6 +41,43 @@ static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
*/
/*
** Macro to call the allocation function.
*/
#define callfrealloc(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns))
/*
** When an allocation fails, it will try again after an emergency
** collection, except when it cannot run a collection. The GC should
** not be called while the state is not fully built, as the collector
** is not yet fully initialized. Also, it should not be called when
** 'gcstopem' is true, because then the interpreter is in the middle of
** a collection step.
*/
#define cantryagain(g) (completestate(g) && !g->gcstopem)
#if defined(EMERGENCYGCTESTS)
/*
** First allocation will fail except when freeing a block (frees never
** fail) and when it cannot try again; this fail will trigger 'tryagain'
** and a full GC cycle at every allocation.
*/
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
if (ns > 0 && cantryagain(g))
return NULL; /* fail */
else /* normal allocation */
return callfrealloc(g, block, os, ns);
}
#else
#define firsttry(g,block,os,ns) callfrealloc(g, block, os, ns)
#endif
/*
@@ -132,7 +150,7 @@ l_noret luaM_toobig (lua_State *L) {
void luaM_free_ (lua_State *L, void *block, size_t osize) {
global_State *g = G(L);
lua_assert((osize == 0) == (block == NULL));
(*g->frealloc)(g->ud, block, osize, 0);
callfrealloc(g, block, osize, 0);
g->GCdebt -= osize;
}
@@ -140,19 +158,15 @@ void luaM_free_ (lua_State *L, void *block, size_t osize) {
/*
** In case of allocation fail, this function will do an emergency
** collection to free some memory and then try the allocation again.
** The GC should not be called while state is not fully built, as the
** collector is not yet fully initialized. Also, it should not be called
** when 'gcstopem' is true, because then the interpreter is in the
** middle of a collection step.
*/
static void *tryagain (lua_State *L, void *block,
size_t osize, size_t nsize) {
global_State *g = G(L);
if (completestate(g) && !g->gcstopem) {
if (cantryagain(g)) {
luaC_fullgc(L, 1); /* try to free some memory... */
return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */
return callfrealloc(g, block, osize, nsize); /* try again */
}
else return NULL; /* cannot free any memory without a full state */
else return NULL; /* cannot run an emergency collection */
}

View File

@@ -21,7 +21,7 @@ iABC C(8) | B(8) |k| A(8) | Op(7) |
iABx Bx(17) | A(8) | Op(7) |
iAsBx sBx (signed)(17) | A(8) | Op(7) |
iAx Ax(25) | Op(7) |
isJ sJ(25) | Op(7) |
isJ sJ (signed)(25) | Op(7) |
A signed argument is represented in excess K: the represented value is
the written unsigned value minus K, where K is half the maximum for the

View File

@@ -30,23 +30,14 @@
*/
#if !defined(LUA_STRFTIMEOPTIONS) /* { */
/* options for ANSI C 89 (only 1-char options) */
#define L_STRFTIMEC89 "aAbBcdHIjmMpSUwWxXyYZ%"
/* options for ISO C 99 and POSIX */
#define L_STRFTIMEC99 "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \
"||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */
/* options for Windows */
#define L_STRFTIMEWIN "aAbBcdHIjmMpSUwWxXyYzZ%" \
"||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */
#if defined(LUA_USE_WINDOWS)
#define LUA_STRFTIMEOPTIONS L_STRFTIMEWIN
#elif defined(LUA_USE_C89)
#define LUA_STRFTIMEOPTIONS L_STRFTIMEC89
#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \
"||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */
#elif defined(LUA_USE_C89) /* ANSI C 89 (only 1-char options) */
#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%"
#else /* C99 specification */
#define LUA_STRFTIMEOPTIONS L_STRFTIMEC99
#define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \
"||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */
#endif
#endif /* } */
@@ -138,12 +129,21 @@
/* }================================================================== */
#if !defined(l_system)
#if defined(LUA_USE_IOS)
/* Despite claiming to be ISO C, iOS does not implement 'system'. */
#define l_system(cmd) ((cmd) == NULL ? 0 : -1)
#else
#define l_system(cmd) system(cmd) /* default definition */
#endif
#endif
static int os_execute (lua_State *L) {
const char *cmd = luaL_optstring(L, 1, NULL);
int stat;
errno = 0;
stat = system(cmd);
stat = l_system(cmd);
if (cmd != NULL)
return luaL_execresult(L, stat);
else {

View File

@@ -521,12 +521,12 @@ static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) {
/*
** Solves the goto at index 'g' to given 'label' and removes it
** from the list of pending goto's.
** from the list of pending gotos.
** If it jumps into the scope of some variable, raises an error.
*/
static void solvegoto (LexState *ls, int g, Labeldesc *label) {
int i;
Labellist *gl = &ls->dyd->gt; /* list of goto's */
Labellist *gl = &ls->dyd->gt; /* list of gotos */
Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */
lua_assert(eqstr(gt->name, label->name));
if (l_unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */
@@ -580,7 +580,7 @@ static int newgotoentry (LexState *ls, TString *name, int line, int pc) {
/*
** Solves forward jumps. Check whether new label 'lb' matches any
** pending gotos in current block and solves them. Return true
** if any of the goto's need to close upvalues.
** if any of the gotos need to close upvalues.
*/
static int solvegotos (LexState *ls, Labeldesc *lb) {
Labellist *gl = &ls->dyd->gt;
@@ -601,7 +601,7 @@ static int solvegotos (LexState *ls, Labeldesc *lb) {
/*
** Create a new label with the given 'name' at the given 'line'.
** 'last' tells whether label is the last non-op statement in its
** block. Solves all pending goto's to this new label and adds
** block. Solves all pending gotos to this new label and adds
** a close instruction if necessary.
** Returns true iff it added a close instruction.
*/

View File

@@ -339,7 +339,7 @@ int luaE_resetthread (lua_State *L, int status) {
}
LUA_API int lua_resetthread (lua_State *L, lua_State *from) {
LUA_API int lua_closethread (lua_State *L, lua_State *from) {
int status;
lua_lock(L);
L->nCcalls = (from) ? getCcalls(from) : 0;
@@ -349,6 +349,14 @@ LUA_API int lua_resetthread (lua_State *L, lua_State *from) {
}
/*
** Deprecated! Use 'lua_closethread' instead.
*/
LUA_API int lua_resetthread (lua_State *L) {
return lua_closethread(L, NULL);
}
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
int i;
lua_State *L;

View File

@@ -9,6 +9,11 @@
#include "lua.h"
/* Some header files included here need this definition */
typedef struct CallInfo CallInfo;
#include "lobject.h"
#include "ltm.h"
#include "lzio.h"
@@ -169,7 +174,7 @@ typedef struct stringtable {
** - field 'transferinfo' is used only during call/returnhooks,
** before the function starts or after it ends.
*/
typedef struct CallInfo {
struct CallInfo {
StkIdRel func; /* function index in the stack */
StkIdRel top; /* top for this function */
struct CallInfo *previous, *next; /* dynamic call link */
@@ -196,7 +201,7 @@ typedef struct CallInfo {
} u2;
short nresults; /* expected number of results from this function */
unsigned short callstatus;
} CallInfo;
};
/*
@@ -290,7 +295,7 @@ typedef struct global_State {
struct lua_State *mainthread;
TString *memerrmsg; /* message for memory-allocation errors */
TString *tmname[TM_N]; /* array with tag-method names */
struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */
struct Table *mt[LUA_NUMTYPES]; /* metatables for basic types */
TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */
lua_WarnFunction warnf; /* warning function */
void *ud_warn; /* auxiliary data to 'warnf' */

View File

@@ -570,7 +570,7 @@ static const char *match_capture (MatchState *ms, const char *s, int l) {
static const char *match (MatchState *ms, const char *s, const char *p) {
if (l_unlikely(ms->matchdepth-- == 0))
luaL_error(ms->L, "pattern too complex");
init: /* using goto's to optimize tail recursion */
init: /* using goto to optimize tail recursion */
if (p != ms->p_end) { /* end of pattern? */
switch (*p) {
case '(': { /* start capture */

View File

@@ -259,9 +259,11 @@ LUAI_FUNC unsigned int luaH_realasize (const Table *t) {
size |= (size >> 2);
size |= (size >> 4);
size |= (size >> 8);
#if (UINT_MAX >> 14) > 3 /* unsigned int has more than 16 bits */
size |= (size >> 16);
#if (UINT_MAX >> 30) > 3
size |= (size >> 32); /* unsigned int has more than 32 bits */
#endif
#endif
size++;
lua_assert(ispow2(size) && size/2 < t->alimit && t->alimit < size);

View File

@@ -9,6 +9,7 @@
#include "lobject.h"
#include "lstate.h"
/*
@@ -95,8 +96,8 @@ LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
int inv, int isfloat, TMS event);
LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams,
struct CallInfo *ci, const Proto *p);
LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci,
CallInfo *ci, const Proto *p);
LUAI_FUNC void luaT_getvarargs (lua_State *L, CallInfo *ci,
StkId where, int wanted);

View File

@@ -633,7 +633,8 @@ static int pmain (lua_State *L) {
}
luaL_openlibs(L); /* open standard libraries */
createargtable(L, argv, argc, script); /* create table 'arg' */
lua_gc(L, LUA_GCGEN, 0, 0); /* GC in generational mode */
lua_gc(L, LUA_GCRESTART); /* start GC... */
lua_gc(L, LUA_GCGEN, 0, 0); /* ...in generational mode */
if (!(args & has_E)) { /* no option '-E'? */
if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */
return 0; /* error running LUA_INIT */
@@ -665,6 +666,7 @@ int main (int argc, char **argv) {
l_message(argv[0], "cannot create state: not enough memory");
return EXIT_FAILURE;
}
lua_gc(L, LUA_GCSTOP); /* stop GC while building state */
lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */
lua_pushinteger(L, argc); /* 1st argument */
lua_pushlightuserdata(L, argv); /* 2nd argument */

View File

@@ -18,14 +18,14 @@
#define LUA_VERSION_MAJOR "5"
#define LUA_VERSION_MINOR "4"
#define LUA_VERSION_RELEASE "5"
#define LUA_VERSION_RELEASE "6"
#define LUA_VERSION_NUM 504
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 5)
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 6)
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2022 Lua.org, PUC-Rio"
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2023 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
@@ -131,6 +131,16 @@ typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont);
/*
** Type used by the debug API to collect debug information
*/
typedef struct lua_Debug lua_Debug;
/*
** Functions to be called by the debugger in specific events
*/
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
/*
@@ -153,7 +163,8 @@ extern const char lua_ident[];
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
LUA_API void (lua_close) (lua_State *L);
LUA_API lua_State *(lua_newthread) (lua_State *L);
LUA_API int (lua_resetthread) (lua_State *L, lua_State *from);
LUA_API int (lua_closethread) (lua_State *L, lua_State *from);
LUA_API int (lua_resetthread) (lua_State *L); /* Deprecated! */
LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
@@ -446,12 +457,6 @@ LUA_API void (lua_closeslot) (lua_State *L, int idx);
#define LUA_MASKLINE (1 << LUA_HOOKLINE)
#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
typedef struct lua_Debug lua_Debug; /* activation record */
/* Functions to be called by the debugger in specific events */
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar);
LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar);
@@ -496,7 +501,7 @@ struct lua_Debug {
/******************************************************************************
* Copyright (C) 1994-2022 Lua.org, PUC-Rio.
* Copyright (C) 1994-2023 Lua.org, PUC-Rio.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the

723
3rd/lua/luac.c Normal file
View File

@@ -0,0 +1,723 @@
/*
** $Id: luac.c $
** Lua compiler (saves bytecodes to files; also lists bytecodes)
** See Copyright Notice in lua.h
*/
#define luac_c
#define LUA_CORE
#include "lprefix.h"
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "ldebug.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lopnames.h"
#include "lstate.h"
#include "lundump.h"
static void PrintFunction(const Proto* f, int full);
#define luaU_print PrintFunction
#define PROGNAME "luac" /* default program name */
#define OUTPUT PROGNAME ".out" /* default output file */
static int listing=0; /* list bytecodes? */
static int dumping=1; /* dump bytecodes? */
static int stripping=0; /* strip debug information? */
static char Output[]={ OUTPUT }; /* default output file name */
static const char* output=Output; /* actual output file name */
static const char* progname=PROGNAME; /* actual program name */
static TString **tmname;
static void fatal(const char* message)
{
fprintf(stderr,"%s: %s\n",progname,message);
exit(EXIT_FAILURE);
}
static void cannot(const char* what)
{
fprintf(stderr,"%s: cannot %s %s: %s\n",progname,what,output,strerror(errno));
exit(EXIT_FAILURE);
}
static void usage(const char* message)
{
if (*message=='-')
fprintf(stderr,"%s: unrecognized option '%s'\n",progname,message);
else
fprintf(stderr,"%s: %s\n",progname,message);
fprintf(stderr,
"usage: %s [options] [filenames]\n"
"Available options are:\n"
" -l list (use -l -l for full listing)\n"
" -o name output to file 'name' (default is \"%s\")\n"
" -p parse only\n"
" -s strip debug information\n"
" -v show version information\n"
" -- stop handling options\n"
" - stop handling options and process stdin\n"
,progname,Output);
exit(EXIT_FAILURE);
}
#define IS(s) (strcmp(argv[i],s)==0)
static int doargs(int argc, char* argv[])
{
int i;
int version=0;
if (argv[0]!=NULL && *argv[0]!=0) progname=argv[0];
for (i=1; i<argc; i++)
{
if (*argv[i]!='-') /* end of options; keep it */
break;
else if (IS("--")) /* end of options; skip it */
{
++i;
if (version) ++version;
break;
}
else if (IS("-")) /* end of options; use stdin */
break;
else if (IS("-l")) /* list */
++listing;
else if (IS("-o")) /* output file */
{
output=argv[++i];
if (output==NULL || *output==0 || (*output=='-' && output[1]!=0))
usage("'-o' needs argument");
if (IS("-")) output=NULL;
}
else if (IS("-p")) /* parse only */
dumping=0;
else if (IS("-s")) /* strip debug information */
stripping=1;
else if (IS("-v")) /* show version */
++version;
else /* unknown option */
usage(argv[i]);
}
if (i==argc && (listing || !dumping))
{
dumping=0;
argv[--i]=Output;
}
if (version)
{
printf("%s\n",LUA_COPYRIGHT);
if (version==argc-1) exit(EXIT_SUCCESS);
}
return i;
}
#define FUNCTION "(function()end)();\n"
static const char* reader(lua_State* L, void* ud, size_t* size)
{
UNUSED(L);
if ((*(int*)ud)--)
{
*size=sizeof(FUNCTION)-1;
return FUNCTION;
}
else
{
*size=0;
return NULL;
}
}
#define toproto(L,i) getproto(s2v(L->top.p+(i)))
static const Proto* combine(lua_State* L, int n)
{
if (n==1)
return toproto(L,-1);
else
{
Proto* f;
int i=n;
if (lua_load(L,reader,&i,"=(" PROGNAME ")",NULL)!=LUA_OK) fatal(lua_tostring(L,-1));
f=toproto(L,-1);
for (i=0; i<n; i++)
{
f->p[i]=toproto(L,i-n-1);
if (f->p[i]->sizeupvalues>0) f->p[i]->upvalues[0].instack=0;
}
return f;
}
}
static int writer(lua_State* L, const void* p, size_t size, void* u)
{
UNUSED(L);
return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0);
}
static int pmain(lua_State* L)
{
int argc=(int)lua_tointeger(L,1);
char** argv=(char**)lua_touserdata(L,2);
const Proto* f;
int i;
tmname=G(L)->tmname;
if (!lua_checkstack(L,argc)) fatal("too many input files");
for (i=0; i<argc; i++)
{
const char* filename=IS("-") ? NULL : argv[i];
if (luaL_loadfile(L,filename)!=LUA_OK) fatal(lua_tostring(L,-1));
}
f=combine(L,argc);
if (listing) luaU_print(f,listing>1);
if (dumping)
{
FILE* D= (output==NULL) ? stdout : fopen(output,"wb");
if (D==NULL) cannot("open");
lua_lock(L);
luaU_dump(L,f,writer,D,stripping);
lua_unlock(L);
if (ferror(D)) cannot("write");
if (fclose(D)) cannot("close");
}
return 0;
}
int main(int argc, char* argv[])
{
lua_State* L;
int i=doargs(argc,argv);
argc-=i; argv+=i;
if (argc<=0) usage("no input files given");
L=luaL_newstate();
if (L==NULL) fatal("cannot create state: not enough memory");
lua_pushcfunction(L,&pmain);
lua_pushinteger(L,argc);
lua_pushlightuserdata(L,argv);
if (lua_pcall(L,2,0,0)!=LUA_OK) fatal(lua_tostring(L,-1));
lua_close(L);
return EXIT_SUCCESS;
}
/*
** print bytecodes
*/
#define UPVALNAME(x) ((f->upvalues[x].name) ? getstr(f->upvalues[x].name) : "-")
#define VOID(p) ((const void*)(p))
#define eventname(i) (getstr(tmname[i]))
static void PrintString(const TString* ts)
{
const char* s=getstr(ts);
size_t i,n=tsslen(ts);
printf("\"");
for (i=0; i<n; i++)
{
int c=(int)(unsigned char)s[i];
switch (c)
{
case '"':
printf("\\\"");
break;
case '\\':
printf("\\\\");
break;
case '\a':
printf("\\a");
break;
case '\b':
printf("\\b");
break;
case '\f':
printf("\\f");
break;
case '\n':
printf("\\n");
break;
case '\r':
printf("\\r");
break;
case '\t':
printf("\\t");
break;
case '\v':
printf("\\v");
break;
default:
if (isprint(c)) printf("%c",c); else printf("\\%03d",c);
break;
}
}
printf("\"");
}
static void PrintType(const Proto* f, int i)
{
const TValue* o=&f->k[i];
switch (ttypetag(o))
{
case LUA_VNIL:
printf("N");
break;
case LUA_VFALSE:
case LUA_VTRUE:
printf("B");
break;
case LUA_VNUMFLT:
printf("F");
break;
case LUA_VNUMINT:
printf("I");
break;
case LUA_VSHRSTR:
case LUA_VLNGSTR:
printf("S");
break;
default: /* cannot happen */
printf("?%d",ttypetag(o));
break;
}
printf("\t");
}
static void PrintConstant(const Proto* f, int i)
{
const TValue* o=&f->k[i];
switch (ttypetag(o))
{
case LUA_VNIL:
printf("nil");
break;
case LUA_VFALSE:
printf("false");
break;
case LUA_VTRUE:
printf("true");
break;
case LUA_VNUMFLT:
{
char buff[100];
sprintf(buff,LUA_NUMBER_FMT,fltvalue(o));
printf("%s",buff);
if (buff[strspn(buff,"-0123456789")]=='\0') printf(".0");
break;
}
case LUA_VNUMINT:
printf(LUA_INTEGER_FMT,ivalue(o));
break;
case LUA_VSHRSTR:
case LUA_VLNGSTR:
PrintString(tsvalue(o));
break;
default: /* cannot happen */
printf("?%d",ttypetag(o));
break;
}
}
#define COMMENT "\t; "
#define EXTRAARG GETARG_Ax(code[pc+1])
#define EXTRAARGC (EXTRAARG*(MAXARG_C+1))
#define ISK (isk ? "k" : "")
static void PrintCode(const Proto* f)
{
const Instruction* code=f->code;
int pc,n=f->sizecode;
for (pc=0; pc<n; pc++)
{
Instruction i=code[pc];
OpCode o=GET_OPCODE(i);
int a=GETARG_A(i);
int b=GETARG_B(i);
int c=GETARG_C(i);
int ax=GETARG_Ax(i);
int bx=GETARG_Bx(i);
int sb=GETARG_sB(i);
int sc=GETARG_sC(i);
int sbx=GETARG_sBx(i);
int isk=GETARG_k(i);
int line=luaG_getfuncline(f,pc);
printf("\t%d\t",pc+1);
if (line>0) printf("[%d]\t",line); else printf("[-]\t");
printf("%-9s\t",opnames[o]);
switch (o)
{
case OP_MOVE:
printf("%d %d",a,b);
break;
case OP_LOADI:
printf("%d %d",a,sbx);
break;
case OP_LOADF:
printf("%d %d",a,sbx);
break;
case OP_LOADK:
printf("%d %d",a,bx);
printf(COMMENT); PrintConstant(f,bx);
break;
case OP_LOADKX:
printf("%d",a);
printf(COMMENT); PrintConstant(f,EXTRAARG);
break;
case OP_LOADFALSE:
printf("%d",a);
break;
case OP_LFALSESKIP:
printf("%d",a);
break;
case OP_LOADTRUE:
printf("%d",a);
break;
case OP_LOADNIL:
printf("%d %d",a,b);
printf(COMMENT "%d out",b+1);
break;
case OP_GETUPVAL:
printf("%d %d",a,b);
printf(COMMENT "%s",UPVALNAME(b));
break;
case OP_SETUPVAL:
printf("%d %d",a,b);
printf(COMMENT "%s",UPVALNAME(b));
break;
case OP_GETTABUP:
printf("%d %d %d",a,b,c);
printf(COMMENT "%s",UPVALNAME(b));
printf(" "); PrintConstant(f,c);
break;
case OP_GETTABLE:
printf("%d %d %d",a,b,c);
break;
case OP_GETI:
printf("%d %d %d",a,b,c);
break;
case OP_GETFIELD:
printf("%d %d %d",a,b,c);
printf(COMMENT); PrintConstant(f,c);
break;
case OP_SETTABUP:
printf("%d %d %d%s",a,b,c,ISK);
printf(COMMENT "%s",UPVALNAME(a));
printf(" "); PrintConstant(f,b);
if (isk) { printf(" "); PrintConstant(f,c); }
break;
case OP_SETTABLE:
printf("%d %d %d%s",a,b,c,ISK);
if (isk) { printf(COMMENT); PrintConstant(f,c); }
break;
case OP_SETI:
printf("%d %d %d%s",a,b,c,ISK);
if (isk) { printf(COMMENT); PrintConstant(f,c); }
break;
case OP_SETFIELD:
printf("%d %d %d%s",a,b,c,ISK);
printf(COMMENT); PrintConstant(f,b);
if (isk) { printf(" "); PrintConstant(f,c); }
break;
case OP_NEWTABLE:
printf("%d %d %d",a,b,c);
printf(COMMENT "%d",c+EXTRAARGC);
break;
case OP_SELF:
printf("%d %d %d%s",a,b,c,ISK);
if (isk) { printf(COMMENT); PrintConstant(f,c); }
break;
case OP_ADDI:
printf("%d %d %d",a,b,sc);
break;
case OP_ADDK:
printf("%d %d %d",a,b,c);
printf(COMMENT); PrintConstant(f,c);
break;
case OP_SUBK:
printf("%d %d %d",a,b,c);
printf(COMMENT); PrintConstant(f,c);
break;
case OP_MULK:
printf("%d %d %d",a,b,c);
printf(COMMENT); PrintConstant(f,c);
break;
case OP_MODK:
printf("%d %d %d",a,b,c);
printf(COMMENT); PrintConstant(f,c);
break;
case OP_POWK:
printf("%d %d %d",a,b,c);
printf(COMMENT); PrintConstant(f,c);
break;
case OP_DIVK:
printf("%d %d %d",a,b,c);
printf(COMMENT); PrintConstant(f,c);
break;
case OP_IDIVK:
printf("%d %d %d",a,b,c);
printf(COMMENT); PrintConstant(f,c);
break;
case OP_BANDK:
printf("%d %d %d",a,b,c);
printf(COMMENT); PrintConstant(f,c);
break;
case OP_BORK:
printf("%d %d %d",a,b,c);
printf(COMMENT); PrintConstant(f,c);
break;
case OP_BXORK:
printf("%d %d %d",a,b,c);
printf(COMMENT); PrintConstant(f,c);
break;
case OP_SHRI:
printf("%d %d %d",a,b,sc);
break;
case OP_SHLI:
printf("%d %d %d",a,b,sc);
break;
case OP_ADD:
printf("%d %d %d",a,b,c);
break;
case OP_SUB:
printf("%d %d %d",a,b,c);
break;
case OP_MUL:
printf("%d %d %d",a,b,c);
break;
case OP_MOD:
printf("%d %d %d",a,b,c);
break;
case OP_POW:
printf("%d %d %d",a,b,c);
break;
case OP_DIV:
printf("%d %d %d",a,b,c);
break;
case OP_IDIV:
printf("%d %d %d",a,b,c);
break;
case OP_BAND:
printf("%d %d %d",a,b,c);
break;
case OP_BOR:
printf("%d %d %d",a,b,c);
break;
case OP_BXOR:
printf("%d %d %d",a,b,c);
break;
case OP_SHL:
printf("%d %d %d",a,b,c);
break;
case OP_SHR:
printf("%d %d %d",a,b,c);
break;
case OP_MMBIN:
printf("%d %d %d",a,b,c);
printf(COMMENT "%s",eventname(c));
break;
case OP_MMBINI:
printf("%d %d %d %d",a,sb,c,isk);
printf(COMMENT "%s",eventname(c));
if (isk) printf(" flip");
break;
case OP_MMBINK:
printf("%d %d %d %d",a,b,c,isk);
printf(COMMENT "%s ",eventname(c)); PrintConstant(f,b);
if (isk) printf(" flip");
break;
case OP_UNM:
printf("%d %d",a,b);
break;
case OP_BNOT:
printf("%d %d",a,b);
break;
case OP_NOT:
printf("%d %d",a,b);
break;
case OP_LEN:
printf("%d %d",a,b);
break;
case OP_CONCAT:
printf("%d %d",a,b);
break;
case OP_CLOSE:
printf("%d",a);
break;
case OP_TBC:
printf("%d",a);
break;
case OP_JMP:
printf("%d",GETARG_sJ(i));
printf(COMMENT "to %d",GETARG_sJ(i)+pc+2);
break;
case OP_EQ:
printf("%d %d %d",a,b,isk);
break;
case OP_LT:
printf("%d %d %d",a,b,isk);
break;
case OP_LE:
printf("%d %d %d",a,b,isk);
break;
case OP_EQK:
printf("%d %d %d",a,b,isk);
printf(COMMENT); PrintConstant(f,b);
break;
case OP_EQI:
printf("%d %d %d",a,sb,isk);
break;
case OP_LTI:
printf("%d %d %d",a,sb,isk);
break;
case OP_LEI:
printf("%d %d %d",a,sb,isk);
break;
case OP_GTI:
printf("%d %d %d",a,sb,isk);
break;
case OP_GEI:
printf("%d %d %d",a,sb,isk);
break;
case OP_TEST:
printf("%d %d",a,isk);
break;
case OP_TESTSET:
printf("%d %d %d",a,b,isk);
break;
case OP_CALL:
printf("%d %d %d",a,b,c);
printf(COMMENT);
if (b==0) printf("all in "); else printf("%d in ",b-1);
if (c==0) printf("all out"); else printf("%d out",c-1);
break;
case OP_TAILCALL:
printf("%d %d %d%s",a,b,c,ISK);
printf(COMMENT "%d in",b-1);
break;
case OP_RETURN:
printf("%d %d %d%s",a,b,c,ISK);
printf(COMMENT);
if (b==0) printf("all out"); else printf("%d out",b-1);
break;
case OP_RETURN0:
break;
case OP_RETURN1:
printf("%d",a);
break;
case OP_FORLOOP:
printf("%d %d",a,bx);
printf(COMMENT "to %d",pc-bx+2);
break;
case OP_FORPREP:
printf("%d %d",a,bx);
printf(COMMENT "exit to %d",pc+bx+3);
break;
case OP_TFORPREP:
printf("%d %d",a,bx);
printf(COMMENT "to %d",pc+bx+2);
break;
case OP_TFORCALL:
printf("%d %d",a,c);
break;
case OP_TFORLOOP:
printf("%d %d",a,bx);
printf(COMMENT "to %d",pc-bx+2);
break;
case OP_SETLIST:
printf("%d %d %d",a,b,c);
if (isk) printf(COMMENT "%d",c+EXTRAARGC);
break;
case OP_CLOSURE:
printf("%d %d",a,bx);
printf(COMMENT "%p",VOID(f->p[bx]));
break;
case OP_VARARG:
printf("%d %d",a,c);
printf(COMMENT);
if (c==0) printf("all out"); else printf("%d out",c-1);
break;
case OP_VARARGPREP:
printf("%d",a);
break;
case OP_EXTRAARG:
printf("%d",ax);
break;
#if 0
default:
printf("%d %d %d",a,b,c);
printf(COMMENT "not handled");
break;
#endif
}
printf("\n");
}
}
#define SS(x) ((x==1)?"":"s")
#define S(x) (int)(x),SS(x)
static void PrintHeader(const Proto* f)
{
const char* s=f->source ? getstr(f->source) : "=?";
if (*s=='@' || *s=='=')
s++;
else if (*s==LUA_SIGNATURE[0])
s="(bstring)";
else
s="(string)";
printf("\n%s <%s:%d,%d> (%d instruction%s at %p)\n",
(f->linedefined==0)?"main":"function",s,
f->linedefined,f->lastlinedefined,
S(f->sizecode),VOID(f));
printf("%d%s param%s, %d slot%s, %d upvalue%s, ",
(int)(f->numparams),f->is_vararg?"+":"",SS(f->numparams),
S(f->maxstacksize),S(f->sizeupvalues));
printf("%d local%s, %d constant%s, %d function%s\n",
S(f->sizelocvars),S(f->sizek),S(f->sizep));
}
static void PrintDebug(const Proto* f)
{
int i,n;
n=f->sizek;
printf("constants (%d) for %p:\n",n,VOID(f));
for (i=0; i<n; i++)
{
printf("\t%d\t",i);
PrintType(f,i);
PrintConstant(f,i);
printf("\n");
}
n=f->sizelocvars;
printf("locals (%d) for %p:\n",n,VOID(f));
for (i=0; i<n; i++)
{
printf("\t%d\t%s\t%d\t%d\n",
i,getstr(f->locvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1);
}
n=f->sizeupvalues;
printf("upvalues (%d) for %p:\n",n,VOID(f));
for (i=0; i<n; i++)
{
printf("\t%d\t%s\t%d\t%d\n",
i,UPVALNAME(i),f->upvalues[i].instack,f->upvalues[i].idx);
}
}
static void PrintFunction(const Proto* f, int full)
{
int i,n=f->sizep;
PrintHeader(f);
PrintCode(f);
if (full) PrintDebug(f);
for (i=0; i<n; i++) PrintFunction(f->p[i],full);
}

View File

@@ -70,6 +70,12 @@
#endif
#if defined(LUA_USE_IOS)
#define LUA_USE_POSIX
#define LUA_USE_DLOPEN
#endif
/*
@@ LUAI_IS32INT is true iff 'int' has (at least) 32 bits.
*/

View File

@@ -248,6 +248,8 @@ static void loadDebug (LoadState *S, Proto *f) {
f->locvars[i].endpc = loadInt(S);
}
n = loadInt(S);
if (n != 0) /* does it have debug information? */
n = f->sizeupvalues; /* must be this many */
for (i = 0; i < n; i++)
f->upvalues[i].name = loadStringN(S, f);
}

View File

@@ -1412,6 +1412,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak;
}
vmcase(OP_MODK) {
savestate(L, ci); /* in case of division by 0 */
op_arithK(L, luaV_mod, luaV_modf);
vmbreak;
}
@@ -1424,6 +1425,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak;
}
vmcase(OP_IDIVK) {
savestate(L, ci); /* in case of division by 0 */
op_arithK(L, luaV_idiv, luai_numidiv);
vmbreak;
}
@@ -1472,6 +1474,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak;
}
vmcase(OP_MOD) {
savestate(L, ci); /* in case of division by 0 */
op_arith(L, luaV_mod, luaV_modf);
vmbreak;
}
@@ -1484,6 +1487,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak;
}
vmcase(OP_IDIV) { /* floor division */
savestate(L, ci); /* in case of division by 0 */
op_arith(L, luaV_idiv, luai_numidiv);
vmbreak;
}

View File

@@ -20,7 +20,7 @@ SYSCFLAGS=
SYSLDFLAGS=
SYSLIBS=
MYCFLAGS=-I../../skynet-src -g
MYCFLAGS= -I../../skynet-src -g
MYLDFLAGS=
MYLIBS=
MYOBJS=
@@ -30,7 +30,7 @@ CMCFLAGS=
# == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE =======
PLATS= guess aix bsd c89 freebsd generic linux linux-readline macosx mingw posix solaris
PLATS= guess aix bsd c89 freebsd generic ios linux linux-readline macosx mingw posix solaris
LUA_A= liblua.a
CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o
@@ -44,7 +44,7 @@ LUAC_T= luac
LUAC_O= luac.o
ALL_O= $(BASE_O) $(LUA_O) $(LUAC_O)
ALL_T= $(LUA_A) $(LUA_T)
ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T)
ALL_A= $(LUA_A)
# Targets start here.
@@ -117,6 +117,9 @@ FreeBSD NetBSD OpenBSD freebsd:
generic: $(ALL)
ios:
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_IOS"
Linux linux: linux-noreadline
linux-noreadline:

View File

@@ -1,3 +1,11 @@
v1.7.0 (2023-11-13)
-----------
* Update Lua to 5.4.6
* Update lpeg to 1.1.0
* Improve mongo driver
* Fix service session rewind issue
* Add websocket.is_closed
v1.6.0 (2022-11-16)
-----------
* Update Lua to 5.4.4 (github Nov 8, 2022)

View File

@@ -115,7 +115,7 @@ $(LUA_CLIB_PATH)/sproto.so : lualib-src/sproto/sproto.c lualib-src/sproto/lsprot
$(LUA_CLIB_PATH)/ltls.so : lualib-src/ltls.c | $(LUA_CLIB_PATH)
$(CC) $(CFLAGS) $(SHARED) -Iskynet-src -L$(TLS_LIB) -I$(TLS_INC) $^ -o $@ -lssl
$(LUA_CLIB_PATH)/lpeg.so : 3rd/lpeg/lpcap.c 3rd/lpeg/lpcode.c 3rd/lpeg/lpprint.c 3rd/lpeg/lptree.c 3rd/lpeg/lpvm.c | $(LUA_CLIB_PATH)
$(LUA_CLIB_PATH)/lpeg.so : 3rd/lpeg/lpcap.c 3rd/lpeg/lpcode.c 3rd/lpeg/lpprint.c 3rd/lpeg/lptree.c 3rd/lpeg/lpvm.c 3rd/lpeg/lpcset.c | $(LUA_CLIB_PATH)
$(CC) $(CFLAGS) $(SHARED) -I3rd/lpeg $^ -o $@
clean :

View File

@@ -1,6 +1,10 @@
## ![skynet logo](https://github.com/cloudwu/skynet/wiki/image/skynet_metro.jpg)
Skynet is a lightweight online game framework which can be used in many other fields.
Skynet is a multi-user Lua framework supporting the actor model, often used in games.
[It is heavily used in the Chinese game industry](https://github.com/cloudwu/skynet/wiki/Uses), but is also now spreading to other industries, and to English-centric developers. To visit related sites, visit the Chinese pages using something like Google or Deepl translate.
The community is friendly and almost contributors can speak English, so English speakers are welcome to ask questions in [Discussion](https://github.com/cloudwu/skynet/discussions), or sumbit issues in English.
## Build
@@ -38,5 +42,5 @@ Official Lua versions can also be used as long as the Makefile is edited.
## How To Use
* Read Wiki for documents https://github.com/cloudwu/skynet/wiki
* The FAQ in wiki https://github.com/cloudwu/skynet/wiki/FAQ
* Read Wiki for documents https://github.com/cloudwu/skynet/wiki (Written in both English and Chinese)
* The FAQ in wiki https://github.com/cloudwu/skynet/wiki/FAQ (In Chinese, but you can visit them using something like Google or Deepl translate.)

View File

@@ -411,7 +411,9 @@ ltls_init_constructor(lua_State* L) {
if(!TLS_IS_INIT) {
SSL_library_init();
SSL_load_error_strings();
#if OPENSSL_VERSION_NUMBER < 0x30000000L
ERR_load_BIO_strings();
#endif
OpenSSL_add_all_algorithms();
}
#endif

View File

@@ -519,7 +519,10 @@ is_rawarray(lua_State *L) {
}
lua_Integer firstkey = lua_isinteger(L, -2) ? lua_tointeger(L, -2) : 0;
lua_pop(L, 2);
return firstkey > 0;
if (firstkey <= 1) {
return firstkey > 0;
}
return firstkey <= lua_rawlen(L, -1);
}
static void

View File

@@ -306,6 +306,7 @@ convtable(lua_State *L) {
for (i=0;i<sizehash;i++) {
tbl->hash[i].valuetype = VALUETYPE_NIL;
tbl->hash[i].nocolliding = 0;
tbl->hash[i].next = -1;
}
tbl->sizehash = sizehash;

View File

@@ -94,25 +94,47 @@ forward_cb(struct skynet_context * context, void * ud, int type, int session, ui
return 1;
}
static void
clear_last_context(lua_State *L) {
if (lua_getfield(L, LUA_REGISTRYINDEX, "callback_context") == LUA_TUSERDATA) {
lua_pushnil(L);
lua_setiuservalue(L, -2, 2);
}
lua_pop(L, 1);
}
static int
_cb_pre(struct skynet_context * context, void * ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
struct callback_context *cb_ctx = (struct callback_context *)ud;
clear_last_context(cb_ctx->L);
skynet_callback(context, ud, _cb);
return _cb(context, cb_ctx, type, session, source, msg, sz);
}
static int
_forward_pre(struct skynet_context *context, void *ud, int type, int session, uint32_t source, const void *msg, size_t sz) {
struct callback_context *cb_ctx = (struct callback_context *)ud;
clear_last_context(cb_ctx->L);
skynet_callback(context, ud, forward_cb);
return forward_cb(context, cb_ctx, type, session, source, msg, sz);
}
static int
lcallback(lua_State *L) {
struct skynet_context * context = lua_touserdata(L, lua_upvalueindex(1));
int forward = lua_toboolean(L, 2);
luaL_checktype(L,1,LUA_TFUNCTION);
lua_settop(L,1);
struct callback_context *cb_ctx = (struct callback_context *)lua_newuserdata(L, sizeof(*cb_ctx));
struct callback_context * cb_ctx = (struct callback_context *)lua_newuserdatauv(L, sizeof(*cb_ctx), 2);
cb_ctx->L = lua_newthread(L);
lua_pushcfunction(cb_ctx->L, traceback);
lua_setuservalue(L, -2);
lua_setiuservalue(L, -2, 1);
lua_getfield(L, LUA_REGISTRYINDEX, "callback_context");
lua_setiuservalue(L, -2, 2);
lua_setfield(L, LUA_REGISTRYINDEX, "callback_context");
lua_xmove(L, cb_ctx->L, 1);
if (forward) {
skynet_callback(context, cb_ctx, forward_cb);
} else {
skynet_callback(context, cb_ctx, _cb);
}
skynet_callback(context, cb_ctx, (forward)?(_forward_pre):(_cb_pre));
return 0;
}

View File

@@ -104,7 +104,7 @@ local sprotocore = require "sproto.core" -- optional
* `sproto.new(spbin)` creates a sproto object by a schema binary string (generates by parser).
* `sprotocore.newproto(spbin)` creates a sproto c object by a schema binary string (generates by parser).
* `sproto.sharenew(spbin)` share a sproto object from a sproto c object (generates by sprotocore.newproto).
* `sproto.parse(schema)` creares a sproto object by a schema text string (by calling parser.parse)
* `sproto.parse(schema)` creates a sproto object by a schema text string (by calling parser.parse)
* `sproto:exist_type(typename)` detect whether a type exist in sproto object.
* `sproto:encode(typename, luatable)` encodes a lua table with typename into a binary string.
* `sproto:decode(typename, blob [,sz])` decodes a binary string generated by sproto.encode with typename. If blob is a lightuserdata (C ptr), sz (integer) is needed.
@@ -217,7 +217,7 @@ Types
* **string** : string
* **binary** : binary string (it's a sub type of string)
* **integer** : integer, the max length of an integer is signed 64bit. It can be a fixed-point number with specified precision.
* **double** : double, floating-point number.
* **double** : double precision floating-point number, satisfy [the IEEE 754 standard](https://en.wikipedia.org/wiki/Double-precision_floating-point_format).
* **boolean** : true or false
You can add * before the typename to declare an array.
@@ -230,7 +230,7 @@ User defined type can be any name in alphanumeric characters except the build-in
* Where are double or real types?
I have been using Google protocol buffers for many years in many projects, and I found the real types were seldom used. If you really need it, you can use string to serialize the double numbers. When you need decimal, you can specify the fixed-point presision.
I have been using Google protocol buffers for many years in many projects, and I found the real types were seldom used. If you really need it, you can use string to serialize the double numbers. When you need decimal, you can specify the fixed-point precision.
**NOTE** : `double` is supported now.
@@ -263,6 +263,7 @@ For integer array, an additional byte (4 or 8) to indicate the value is 32bit or
Read the examples below to see more details.
Notice: If the tag is not declared in schema, the decoder will simply ignore the field for protocol version compatibility.
Notice more: all examples are tested in `test_wire_protocol.lua`, update `test_wire_protocol.lua` when update examples.
```
.Person {
@@ -277,6 +278,9 @@ Notice: If the tag is not declared in schema, the decoder will simply ignore the
bools 1 : *boolean
number 2 : integer
bignumber 3 : integer
double 4 : double
doubles 5 : *double
fpn 6 : integer(2)
}
```
@@ -405,6 +409,39 @@ A0 86 01 00 (100000, 32bit integer)
00 1C F4 AB FD FF FF FF (-10000000000, 64bit integer)
```
Example 7:
```
data {
double = 0.01171875,
doubles = {0.01171875, 23, 4}
}
03 00 (fn = 3)
07 00 (skip id = 3)
00 00 (id = 4, value in data part)
00 00 (id = 5, value in data part)
08 00 00 00 (sizeof number, data part)
00 00 00 00 00 00 88 3f (0.01171875, 64bit double)
19 00 00 00 (sizeof doubles)
08 (sizeof double)
00 00 00 00 00 00 88 3f (0.01171875, 64bit double)
00 00 00 00 00 00 37 40 (23, 64bit double)
00 00 00 00 00 00 10 40 (4, 64bit double)
```
Example 8:
```
data {
fpn = 1.82,
}
02 00 (fn = 2)
0b 00 (skip id = 5)
6e 01 (id = 6, value = 0x16e/2 - 1 = 182)
```
0 Packing
=======

View File

@@ -84,14 +84,16 @@ lua_seti(lua_State *L, int index, lua_Integer n) {
#if defined(SPROTO_WEAK_TYPE)
static int64_t
tointegerx (lua_State *L, int idx, int *isnum) {
int64_t v;
if (lua_isnumber(L, idx)) {
v = (int64_t)(round(lua_tonumber(L, idx)));
if (isnum) *isnum = 1;
return v;
} else {
return lua_tointegerx(L, idx, isnum);
int _isnum = 0;
int64_t v = lua_tointegerx(L, idx, &_isnum);
if (!_isnum){
double num = lua_tonumberx(L, idx, &_isnum);
if(_isnum) {
v = (int64_t)llround(num);
}
}
if(isnum) *isnum = _isnum;
return v;
}
static int
@@ -613,7 +615,7 @@ getbuffer(lua_State *L, int index, size_t *sz) {
/*
lightuserdata sproto_type
string source / (lightuserdata , integer)
return table
return table, sz(decoded bytes)
*/
static int
ldecode(lua_State *L) {

View File

@@ -898,7 +898,7 @@ encode_array(sproto_callback cb, struct sproto_arg *args, uint8_t *data, int siz
buffer = encode_integer_array(cb,args,buffer,size, &noarray);
if (buffer == NULL)
return -1;
if (noarray) {
return 0;
}
@@ -1088,26 +1088,33 @@ expand64(uint32_t v) {
return value;
}
static int
decode_empty_array(sproto_callback cb, struct sproto_arg *args) {
// It's empty array, call cb with index == -1 to create the empty array.
args->index = -1;
args->value = NULL;
args->length = 0;
return cb(args);
}
static int
decode_array(sproto_callback cb, struct sproto_arg *args, uint8_t * stream) {
uint32_t sz = todword(stream);
int type = args->type;
int i;
if (sz == 0) {
// It's empty array, call cb with index == -1 to create the empty array.
args->index = -1;
args->value = NULL;
args->length = 0;
cb(args);
return 0;
}
return decode_empty_array(cb, args);
}
stream += SIZEOF_LENGTH;
switch (type) {
case SPROTO_TDOUBLE:
case SPROTO_TINTEGER: {
if (--sz == 0) {
// An empty array but with a len prefix
return decode_empty_array(cb, args);
}
int len = *stream;
++stream;
--sz;
if (len == SIZEOF_INT32) {
if (sz % SIZEOF_INT32 != 0)
return -1;

View File

@@ -9,6 +9,15 @@ local socket_error = sockethelper.socket_error
local GLOBAL_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
local MAX_FRAME_SIZE = 256 * 1024 -- max frame is 256K
local assert = assert
local pairs = pairs
local error = error
local string = string
local xpcall = xpcall
local debug = debug
local table = table
local tonumber = tonumber
local M = {}
@@ -526,5 +535,6 @@ function M.close(id, code ,reason)
end
end
M.is_close = _isws_closed
return M

View File

@@ -45,6 +45,7 @@ local skynet = {
-- code cache
skynet.cache = require "skynet.codecache"
skynet._proto = proto
function skynet.register_protocol(class)
local name = class.name
@@ -68,6 +69,109 @@ local watching_session = {}
local error_queue = {}
local fork_queue = { h = 1, t = 0 }
local auxsend, auxtimeout
do ---- avoid session rewind conflict
local csend = c.send
local cintcommand = c.intcommand
local dangerzone
local dangerzone_size = 0x1000
local dangerzone_low = 0x70000000
local dangerzone_up = dangerzone_low + dangerzone_size
local set_checkrewind -- set auxsend and auxtimeout for safezone
local set_checkconflict -- set auxsend and auxtimeout for dangerzone
local function reset_dangerzone(session)
dangerzone_up = session
dangerzone_low = session
dangerzone = { [session] = true }
for s in pairs(session_id_coroutine) do
if s < dangerzone_low then
dangerzone_low = s
elseif s > dangerzone_up then
dangerzone_up = s
end
dangerzone[s] = true
end
dangerzone_low = dangerzone_low - dangerzone_size
end
-- in dangerzone, we should check if the next session already exist.
local function checkconflict(session)
if session == nil then
return
end
local next_session = session + 1
if next_session > dangerzone_up then
-- leave dangerzone
reset_dangerzone(session)
assert(next_session > dangerzone_up)
set_checkrewind()
else
while true do
if not dangerzone[next_session] then
break
end
if not session_id_coroutine[next_session] then
reset_dangerzone(session)
break
end
-- skip the session already exist.
next_session = c.genid() + 1
end
end
-- session will rewind after 0x7fffffff
if next_session == 0x80000000 and dangerzone[1] then
assert(c.genid() == 1)
return checkconflict(1)
end
end
local function auxsend_checkconflict(addr, proto, msg, sz)
local session = csend(addr, proto, nil, msg, sz)
checkconflict(session)
return session
end
local function auxtimeout_checkconflict(timeout)
local session = cintcommand("TIMEOUT", timeout)
checkconflict(session)
return session
end
local function auxsend_checkrewind(addr, proto, msg, sz)
local session = csend(addr, proto, nil, msg, sz)
if session and session > dangerzone_low and session <= dangerzone_up then
-- enter dangerzone
set_checkconflict(session)
end
return session
end
local function auxtimeout_checkrewind(timeout)
local session = cintcommand("TIMEOUT", timeout)
if session and session > dangerzone_low and session <= dangerzone_up then
-- enter dangerzone
set_checkconflict(session)
end
return session
end
set_checkrewind = function()
auxsend = auxsend_checkrewind
auxtimeout = auxtimeout_checkrewind
end
set_checkconflict = function(session)
reset_dangerzone(session)
auxsend = auxsend_checkconflict
auxtimeout = auxtimeout_checkconflict
end
-- in safezone at the beginning
set_checkrewind()
end
do ---- request/select
local function send_requests(self)
local sessions = {}
@@ -84,7 +188,7 @@ do ---- request/select
c.trace(tag, "call", 4)
c.send(addr, skynet.PTYPE_TRACE, 0, tag)
end
local session = c.send(addr, p.id , nil , p.pack(tunpack(req, 3, req.n)))
local session = auxsend(addr, p.id , p.pack(tunpack(req, 3, req.n)))
if session == nil then
err = err or {}
err[#err+1] = req
@@ -187,7 +291,7 @@ do ---- request/select
self._error = send_requests(self)
self._resp = {}
if timeout then
self._timeout = c.intcommand("TIMEOUT",timeout)
self._timeout = auxtimeout(timeout)
session_id_coroutine[self._timeout] = self._thread
end
@@ -372,7 +476,7 @@ end
skynet.trace_timeout(false) -- turn off by default
function skynet.timeout(ti, func)
local session = c.intcommand("TIMEOUT",ti)
local session = auxtimeout(ti)
assert(session)
local co = co_create_for_timeout(func, ti)
assert(session_id_coroutine[session] == nil)
@@ -391,7 +495,7 @@ local function suspend_sleep(session, token)
end
function skynet.sleep(ti, token)
local session = c.intcommand("TIMEOUT",ti)
local session = auxtimeout(ti)
assert(session)
token = token or coroutine.running()
local succ, ret = suspend_sleep(session, token)
@@ -454,7 +558,10 @@ function skynet.killthread(thread)
if addr then
session_coroutine_address[co] = nil
session_coroutine_tracetag[co] = nil
c.send(addr, skynet.PTYPE_ERROR, session_coroutine_id[co], "")
local session = session_coroutine_id[co]
if session > 0 then
c.send(addr, skynet.PTYPE_ERROR, session, "")
end
session_coroutine_id[co] = nil
end
if watching_session[session] then
@@ -601,7 +708,7 @@ function skynet.call(addr, typename, ...)
end
local p = proto[typename]
local session = c.send(addr, p.id , nil , p.pack(...))
local session = auxsend(addr, p.id , p.pack(...))
if session == nil then
error("call to invalid address " .. skynet.address(addr))
end
@@ -615,7 +722,7 @@ function skynet.rawcall(addr, typename, msg, sz)
c.send(addr, skynet.PTYPE_TRACE, 0, tag)
end
local p = proto[typename]
local session = assert(c.send(addr, p.id , nil , msg, sz), "call to invalid address")
local session = assert(auxsend(addr, p.id , msg, sz), "call to invalid address")
return yield_call(addr, session)
end
@@ -623,7 +730,7 @@ function skynet.tracecall(tag, addr, typename, msg, sz)
c.trace(tag, "tracecall begin")
c.send(addr, skynet.PTYPE_TRACE, 0, tag)
local p = proto[typename]
local session = assert(c.send(addr, p.id , nil , msg, sz), "call to invalid address")
local session = assert(auxsend(addr, p.id , msg, sz), "call to invalid address")
local msg, sz = yield_call(addr, session)
c.trace(tag, "tracecall end")
return msg, sz

View File

@@ -75,11 +75,11 @@ function cluster.send(node, address, ...)
end
end
function cluster.open(port)
function cluster.open(port, maxclient)
if type(port) == "string" then
return skynet.call(clusterd, "lua", "listen", port)
return skynet.call(clusterd, "lua", "listen", port, nil, maxclient)
else
return skynet.call(clusterd, "lua", "listen", "0.0.0.0", port)
return skynet.call(clusterd, "lua", "listen", "0.0.0.0", port, maxclient)
end
end

View File

@@ -28,6 +28,11 @@ local cursor_meta = {
__index = mongo_cursor,
}
local aggregate_cursor = {}
local aggregate_cursor_meta = {
__index = aggregate_cursor,
}
local mongo_client = {}
local client_meta = {
@@ -457,7 +462,7 @@ function mongo_collection:raw_safe_update(update)
end
function mongo_collection:delete(query, single)
self.database:runCommand("delete", self.name, "deletes", {bson_encode({
self.database:send_command("delete", self.name, "deletes", {bson_encode({
q = query,
limit = single and 1 or 0,
})})
@@ -489,11 +494,12 @@ function mongo_collection:raw_safe_delete(delete)
end
function mongo_collection:findOne(query, projection)
local cursor = self:find(query, projection)
if cursor:hasNext() then
return cursor:next()
local r = self.database:runCommand("find", self.name, "filter", query and bson_encode(query) or empty_bson,
"limit", 1, "projection", projection and bson_encode(projection) or empty_bson)
if r.ok ~= 1 then
error(r.errmsg or "Reply from mongod error")
end
return nil
return r.cursor.firstBatch[1]
end
function mongo_collection:find(query, projection)
@@ -505,7 +511,6 @@ function mongo_collection:find(query, projection)
__data = nil,
__cursor = nil,
__document = {},
__flags = 0,
__skip = 0,
__limit = 0,
__sort = empty_bson,
@@ -544,18 +549,13 @@ function mongo_cursor:limit(amount)
end
function mongo_cursor:count(with_limit_and_skip)
local cmd = {
'count', self.__collection.name,
'query', self.__query,
}
local ret
if with_limit_and_skip then
local len = #cmd
cmd[len+1] = 'limit'
cmd[len+2] = self.__limit
cmd[len+3] = 'skip'
cmd[len+4] = self.__skip
ret = self.__collection.database:runCommand('count', self.__collection.name, 'query', self.__query,
'limit', self.__limit, 'skip', self.__skip)
else
ret = self.__collection.database:runCommand('count', self.__collection.name, 'query', self.__query)
end
local ret = self.__collection.database:runCommand(table.unpack(cmd))
assert(ret and ret.ok == 1)
return ret.n
end
@@ -667,12 +667,27 @@ end
-- @return
function mongo_collection:aggregate(pipeline, options)
assert(pipeline)
local cmd = {"aggregate", self.name, "pipeline", pipeline}
for k, v in pairs(options) do
table.insert(cmd, k)
table.insert(cmd, v)
local options_cmd
if options then
options_cmd = {}
for k, v in pairs(options) do
table.insert(options_cmd, k)
table.insert(options_cmd, v)
end
end
return self.database:runCommand(table.unpack(cmd))
local len = #pipeline
return setmetatable( {
__collection = self,
__pipeline = table.move(pipeline, 1, len, 1, {}),
__pipeline_len = len,
__options = options_cmd,
__ptr = nil,
__data = nil,
__cursor = nil,
__document = {},
__skip = 0,
__limit = 0,
} , aggregate_cursor_meta)
end
function mongo_cursor:hasNext()
@@ -703,7 +718,7 @@ function mongo_cursor:hasNext()
self.__document = nil
self.__data = nil
self.__cursor = nil
error(response["$err"] or "Reply from mongod error")
error(response["errmsg"] or "Reply from mongod error")
end
local cursor = response.cursor
@@ -754,4 +769,117 @@ function mongo_cursor:close()
end
end
local sort_stage = { ["$sort"] = true }
local skip_stage = { ["$skip"] = 0 }
local limit_stage = { ["$limit"] = 0 }
local count_stage = { ["$count"] = "__count" }
local function format_pipeline(self, with_limit_and_skip, is_count)
local len = self.__pipeline_len
if self.__sort and not is_count then
len = len + 1
sort_stage["$sort"] = self.__sort
self.__pipeline[len] = sort_stage
end
if with_limit_and_skip then
if self.__skip > 0 then
len = len + 1
skip_stage["$skip"] = self.__skip
self.__pipeline[len] = skip_stage
end
if self.__limit > 0 then
len = len + 1
limit_stage["$limit"] = self.__limit
self.__pipeline[len] = limit_stage
end
end
if is_count then
len = len + 1
self.__pipeline[len] = count_stage
end
for i = 1, 2 do self.__pipeline[len + i] = nil end
return self.__pipeline
end
function aggregate_cursor:count(with_limit_and_skip)
local ret
local name = self.__collection.name
local database = self.__collection.database
if self.__options then
ret = database:runCommand("aggregate", name, "pipeline", format_pipeline(self, with_limit_and_skip, true),
table.unpack(self.__options))
else
ret = database:runCommand("aggregate", name, "pipeline", format_pipeline(self, with_limit_and_skip, true),
"cursor", empty_bson)
end
if ret.ok ~= 1 then
error(ret["errmsg"] or "Reply from mongod error")
end
return ret.cursor.firstBatch[1].__count
end
function aggregate_cursor:hasNext()
if self.__ptr == nil then
if self.__document == nil then
return false
end
local ret
local name = self.__collection.name
local database = self.__collection.database
if self.__data == nil then
if self.__options then
ret = database:runCommand("aggregate", name, "pipeline", format_pipeline(self, true), table.unpack(self.__options))
else
ret = database:runCommand("aggregate", name, "pipeline", format_pipeline(self, true), "cursor", empty_bson)
end
else
if self.__cursor and self.__cursor > 0 then
ret = database:runCommand("getMore", bson_int64(self.__cursor), "collection", name)
else
-- no more
self.__document = nil
self.__data = nil
return false
end
end
if ret.ok ~= 1 then
self.__document = nil
self.__data = nil
self.__cursor = nil
error(ret["errmsg"] or "Reply from mongod error")
end
local cursor = ret.cursor
self.__document = cursor.firstBatch or cursor.nextBatch
self.__data = ret
self.__ptr = 1
self.__cursor = cursor.id
local limit = self.__limit
if cursor.id > 0 and limit > 0 then
limit = limit - #self.__document
if limit <= 0 then
-- reach limit
self:close()
end
self.__limit = limit
end
if cursor.id == 0 and #self.__document == 0 then -- nomore
return false
end
return true
end
return true
end
aggregate_cursor.sort = mongo_cursor.sort
aggregate_cursor.skip = mongo_cursor.skip
aggregate_cursor.limit = mongo_cursor.limit
aggregate_cursor.next = mongo_cursor.next
aggregate_cursor.close = mongo_cursor.close
return mongo

View File

@@ -41,7 +41,17 @@ function socket_channel.channel(desc)
__nodelay = desc.nodelay,
__overload_notify = desc.overload,
__overload = false,
__socket_meta = channel_socket_meta,
}
if desc.socket_read or desc.socket_readline then
c.__socket_meta = {
__index = {
read = desc.socket_read or channel_socket.read,
readline = desc.socket_readline or channel_socket.readline,
},
__gc = channel_socket_meta.__gc
}
end
return setmetatable(c, channel_meta)
end
@@ -319,7 +329,7 @@ local function connect_once(self)
skynet.yield()
end
self.__sock = setmetatable( {fd} , channel_socket_meta )
self.__sock = setmetatable( {fd} , self.__socket_meta )
self.__dispatch_thread = skynet.fork(function()
if self.__sock then
-- self.__sock can be false (socket closed) if error during connecting, See #1513

View File

@@ -90,6 +90,7 @@ function gateserver.start(handler)
MSG.more = dispatch_queue
function MSG.open(fd, msg)
client_number = client_number + 1
if client_number >= maxclient then
socketdriver.shutdown(fd)
return
@@ -98,7 +99,6 @@ function gateserver.start(handler)
socketdriver.nodelay(fd)
end
connection[fd] = true
client_number = client_number + 1
handler.connect(fd, msg)
end

View File

@@ -233,7 +233,7 @@ static int luaB_auxwrap (lua_State *L) {
if (r < 0) {
int stat = lua_status(co);
if (stat != LUA_OK && stat != LUA_YIELD)
lua_resetthread(co, L); /* close variables in case of errors */
lua_closethread(co, L); /* close variables in case of errors */
if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */
luaL_where(L, 1); /* add extra info, if available */
lua_insert(L, -2);

View File

@@ -42,7 +42,7 @@ skynet.start(function()
skynet.newservice "service_mgr"
local enablessl = skynet.getenv "enablessl"
if enablessl then
if enablessl == "true" then
service.new("ltls_holder", function ()
local c = require "ltls.init.c"
c.constructor()

View File

@@ -1,5 +1,4 @@
local skynet = require "skynet"
local sc = require "skynet.socketchannel"
local socket = require "skynet.socket"
local cluster = require "skynet.cluster.core"
local ignoreret = skynet.ignoreret
@@ -135,7 +134,7 @@ skynet.start(function()
skynet.dispatch("lua", function(_,source, cmd, ...)
if cmd == "exit" then
socket.close(fd)
socket.close_fd(fd)
skynet.exit()
elseif cmd == "namechange" then
register_name = new_register_name()

View File

@@ -142,17 +142,17 @@ function command.reload(source, config)
skynet.ret(skynet.pack(nil))
end
function command.listen(source, addr, port)
function command.listen(source, addr, port, maxclient)
local gate = skynet.newservice("gate")
if port == nil then
local address = assert(node_address[addr], addr .. " is down")
addr, port = string.match(address, "(.+):([^:]+)$")
port = tonumber(port)
assert(port ~= 0)
skynet.call(gate, "lua", "open", { address = addr, port = port })
skynet.call(gate, "lua", "open", { address = addr, port = port, maxclient = maxclient })
skynet.ret(skynet.pack(addr, port))
else
local realaddr, realport = skynet.call(gate, "lua", "open", { address = addr, port = port })
local realaddr, realport = skynet.call(gate, "lua", "open", { address = addr, port = port, maxclient = maxclient })
skynet.ret(skynet.pack(realaddr, realport))
end
end

View File

@@ -1,11 +1,11 @@
#ifndef SKYNET_ATOMIC_H
#define SKYNET_ATOMIC_H
#ifdef __STDC_NO_ATOMICS__
#include <stddef.h>
#include <stdint.h>
#ifdef __STDC_NO_ATOMICS__
#define ATOM_INT volatile int
#define ATOM_POINTER volatile uintptr_t
#define ATOM_SIZET volatile size_t