1 | /* |
2 | ** $Id: lpvm.c,v 1.9 2016/06/03 20:11:18 roberto Exp $ |
3 | ** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license) |
4 | */ |
5 | |
6 | #include <limits.h> |
7 | #include <string.h> |
8 | |
9 | |
10 | #include "lua.h" |
11 | #include "lauxlib.h" |
12 | |
13 | #include "lpcap.h" |
14 | #include "lptypes.h" |
15 | #include "lpvm.h" |
16 | #include "lpprint.h" |
17 | |
18 | |
19 | /* initial size for call/backtrack stack */ |
20 | #if !defined(INITBACK) |
21 | #define INITBACK MAXBACK |
22 | #endif |
23 | |
24 | |
25 | #define getoffset(p) (((p) + 1)->offset) |
26 | |
27 | static const Instruction giveup = {{IGiveup, 0, 0}}; |
28 | |
29 | |
30 | /* |
31 | ** {====================================================== |
32 | ** Virtual Machine |
33 | ** ======================================================= |
34 | */ |
35 | |
36 | |
37 | typedef struct Stack { |
38 | const char *s; /* saved position (or NULL for calls) */ |
39 | const Instruction *p; /* next instruction */ |
40 | int caplevel; |
41 | } Stack; |
42 | |
43 | |
44 | #define getstackbase(L, ptop) ((Stack *)lua_touserdata(L, stackidx(ptop))) |
45 | |
46 | |
47 | /* |
48 | ** Make the size of the array of captures 'cap' twice as large as needed |
49 | ** (which is 'captop'). ('n' is the number of new elements.) |
50 | */ |
51 | static Capture *doublecap (lua_State *L, Capture *cap, int captop, |
52 | int n, int ptop) { |
53 | Capture *newc; |
54 | if (captop >= INT_MAX/((int)sizeof(Capture) * 2)) |
55 | luaL_error(L, "too many captures" ); |
56 | newc = (Capture *)lua_newuserdata(L, captop * 2 * sizeof(Capture)); |
57 | memcpy(newc, cap, (captop - n) * sizeof(Capture)); |
58 | lua_replace(L, caplistidx(ptop)); |
59 | return newc; |
60 | } |
61 | |
62 | |
63 | /* |
64 | ** Double the size of the stack |
65 | */ |
66 | static Stack *doublestack (lua_State *L, Stack **stacklimit, int ptop) { |
67 | Stack *stack = getstackbase(L, ptop); |
68 | Stack *newstack; |
69 | int n = *stacklimit - stack; /* current stack size */ |
70 | int max, newn; |
71 | lua_getfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX); |
72 | max = lua_tointeger(L, -1); /* maximum allowed size */ |
73 | lua_pop(L, 1); |
74 | if (n >= max) /* already at maximum size? */ |
75 | luaL_error(L, "backtrack stack overflow (current limit is %d)" , max); |
76 | newn = 2 * n; /* new size */ |
77 | if (newn > max) newn = max; |
78 | newstack = (Stack *)lua_newuserdata(L, newn * sizeof(Stack)); |
79 | memcpy(newstack, stack, n * sizeof(Stack)); |
80 | lua_replace(L, stackidx(ptop)); |
81 | *stacklimit = newstack + newn; |
82 | return newstack + n; /* return next position */ |
83 | } |
84 | |
85 | |
86 | /* |
87 | ** Interpret the result of a dynamic capture: false -> fail; |
88 | ** true -> keep current position; number -> next position. |
89 | ** Return new subject position. 'fr' is stack index where |
90 | ** is the result; 'curr' is current subject position; 'limit' |
91 | ** is subject's size. |
92 | */ |
93 | static int resdyncaptures (lua_State *L, int fr, int curr, int limit) { |
94 | lua_Integer res; |
95 | if (!lua_toboolean(L, fr)) { /* false value? */ |
96 | lua_settop(L, fr - 1); /* remove results */ |
97 | return -1; /* and fail */ |
98 | } |
99 | else if (lua_isboolean(L, fr)) /* true? */ |
100 | res = curr; /* keep current position */ |
101 | else { |
102 | res = lua_tointeger(L, fr) - 1; /* new position */ |
103 | if (res < curr || res > limit) |
104 | luaL_error(L, "invalid position returned by match-time capture" ); |
105 | } |
106 | lua_remove(L, fr); /* remove first result (offset) */ |
107 | return res; |
108 | } |
109 | |
110 | |
111 | /* |
112 | ** Add capture values returned by a dynamic capture to the capture list |
113 | ** 'base', nested inside a group capture. 'fd' indexes the first capture |
114 | ** value, 'n' is the number of values (at least 1). |
115 | */ |
116 | static void adddyncaptures (const char *s, Capture *base, int n, int fd) { |
117 | int i; |
118 | base[0].kind = Cgroup; /* create group capture */ |
119 | base[0].siz = 0; |
120 | base[0].idx = 0; /* make it an anonymous group */ |
121 | for (i = 1; i <= n; i++) { /* add runtime captures */ |
122 | base[i].kind = Cruntime; |
123 | base[i].siz = 1; /* mark it as closed */ |
124 | base[i].idx = fd + i - 1; /* stack index of capture value */ |
125 | base[i].s = s; |
126 | } |
127 | base[i].kind = Cclose; /* close group */ |
128 | base[i].siz = 1; |
129 | base[i].s = s; |
130 | } |
131 | |
132 | |
133 | /* |
134 | ** Remove dynamic captures from the Lua stack (called in case of failure) |
135 | */ |
136 | static int removedyncap (lua_State *L, Capture *capture, |
137 | int level, int last) { |
138 | int id = finddyncap(capture + level, capture + last); /* index of 1st cap. */ |
139 | int top = lua_gettop(L); |
140 | if (id == 0) return 0; /* no dynamic captures? */ |
141 | lua_settop(L, id - 1); /* remove captures */ |
142 | return top - id + 1; /* number of values removed */ |
143 | } |
144 | |
145 | |
146 | /* |
147 | ** Opcode interpreter |
148 | */ |
149 | const char *match (lua_State *L, const char *o, const char *s, const char *e, |
150 | Instruction *op, Capture *capture, int ptop) { |
151 | Stack stackbase[INITBACK]; |
152 | Stack *stacklimit = stackbase + INITBACK; |
153 | Stack *stack = stackbase; /* point to first empty slot in stack */ |
154 | int capsize = INITCAPSIZE; |
155 | int captop = 0; /* point to first empty slot in captures */ |
156 | int ndyncap = 0; /* number of dynamic captures (in Lua stack) */ |
157 | const Instruction *p = op; /* current instruction */ |
158 | stack->p = &giveup; stack->s = s; stack->caplevel = 0; stack++; |
159 | lua_pushlightuserdata(L, stackbase); |
160 | for (;;) { |
161 | #if defined(DEBUG) |
162 | printf("-------------------------------------\n" ); |
163 | printcaplist(capture, capture + captop); |
164 | printf("s: |%s| stck:%d, dyncaps:%d, caps:%d " , |
165 | s, (int)(stack - getstackbase(L, ptop)), ndyncap, captop); |
166 | printinst(op, p); |
167 | #endif |
168 | assert(stackidx(ptop) + ndyncap == lua_gettop(L) && ndyncap <= captop); |
169 | switch ((Opcode)p->i.code) { |
170 | case IEnd: { |
171 | assert(stack == getstackbase(L, ptop) + 1); |
172 | capture[captop].kind = Cclose; |
173 | capture[captop].s = NULL; |
174 | return s; |
175 | } |
176 | case IGiveup: { |
177 | assert(stack == getstackbase(L, ptop)); |
178 | return NULL; |
179 | } |
180 | case IRet: { |
181 | assert(stack > getstackbase(L, ptop) && (stack - 1)->s == NULL); |
182 | p = (--stack)->p; |
183 | continue; |
184 | } |
185 | case IAny: { |
186 | if (s < e) { p++; s++; } |
187 | else goto fail; |
188 | continue; |
189 | } |
190 | case ITestAny: { |
191 | if (s < e) p += 2; |
192 | else p += getoffset(p); |
193 | continue; |
194 | } |
195 | case IChar: { |
196 | if ((byte)*s == p->i.aux && s < e) { p++; s++; } |
197 | else goto fail; |
198 | continue; |
199 | } |
200 | case ITestChar: { |
201 | if ((byte)*s == p->i.aux && s < e) p += 2; |
202 | else p += getoffset(p); |
203 | continue; |
204 | } |
205 | case ISet: { |
206 | int c = (byte)*s; |
207 | if (testchar((p+1)->buff, c) && s < e) |
208 | { p += CHARSETINSTSIZE; s++; } |
209 | else goto fail; |
210 | continue; |
211 | } |
212 | case ITestSet: { |
213 | int c = (byte)*s; |
214 | if (testchar((p + 2)->buff, c) && s < e) |
215 | p += 1 + CHARSETINSTSIZE; |
216 | else p += getoffset(p); |
217 | continue; |
218 | } |
219 | case IBehind: { |
220 | int n = p->i.aux; |
221 | if (n > s - o) goto fail; |
222 | s -= n; p++; |
223 | continue; |
224 | } |
225 | case ISpan: { |
226 | for (; s < e; s++) { |
227 | int c = (byte)*s; |
228 | if (!testchar((p+1)->buff, c)) break; |
229 | } |
230 | p += CHARSETINSTSIZE; |
231 | continue; |
232 | } |
233 | case IJmp: { |
234 | p += getoffset(p); |
235 | continue; |
236 | } |
237 | case IChoice: { |
238 | if (stack == stacklimit) |
239 | stack = doublestack(L, &stacklimit, ptop); |
240 | stack->p = p + getoffset(p); |
241 | stack->s = s; |
242 | stack->caplevel = captop; |
243 | stack++; |
244 | p += 2; |
245 | continue; |
246 | } |
247 | case ICall: { |
248 | if (stack == stacklimit) |
249 | stack = doublestack(L, &stacklimit, ptop); |
250 | stack->s = NULL; |
251 | stack->p = p + 2; /* save return address */ |
252 | stack++; |
253 | p += getoffset(p); |
254 | continue; |
255 | } |
256 | case ICommit: { |
257 | assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL); |
258 | stack--; |
259 | p += getoffset(p); |
260 | continue; |
261 | } |
262 | case IPartialCommit: { |
263 | assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL); |
264 | (stack - 1)->s = s; |
265 | (stack - 1)->caplevel = captop; |
266 | p += getoffset(p); |
267 | continue; |
268 | } |
269 | case IBackCommit: { |
270 | assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL); |
271 | s = (--stack)->s; |
272 | captop = stack->caplevel; |
273 | p += getoffset(p); |
274 | continue; |
275 | } |
276 | case IFailTwice: |
277 | assert(stack > getstackbase(L, ptop)); |
278 | stack--; |
279 | /* go through */ |
280 | case IFail: |
281 | fail: { /* pattern failed: try to backtrack */ |
282 | do { /* remove pending calls */ |
283 | assert(stack > getstackbase(L, ptop)); |
284 | s = (--stack)->s; |
285 | } while (s == NULL); |
286 | if (ndyncap > 0) /* is there matchtime captures? */ |
287 | ndyncap -= removedyncap(L, capture, stack->caplevel, captop); |
288 | captop = stack->caplevel; |
289 | p = stack->p; |
290 | #if defined(DEBUG) |
291 | printf("**FAIL**\n" ); |
292 | #endif |
293 | continue; |
294 | } |
295 | case ICloseRunTime: { |
296 | CapState cs; |
297 | int rem, res, n; |
298 | int fr = lua_gettop(L) + 1; /* stack index of first result */ |
299 | cs.s = o; cs.L = L; cs.ocap = capture; cs.ptop = ptop; |
300 | n = runtimecap(&cs, capture + captop, s, &rem); /* call function */ |
301 | captop -= n; /* remove nested captures */ |
302 | ndyncap -= rem; /* update number of dynamic captures */ |
303 | fr -= rem; /* 'rem' items were popped from Lua stack */ |
304 | res = resdyncaptures(L, fr, s - o, e - o); /* get result */ |
305 | if (res == -1) /* fail? */ |
306 | goto fail; |
307 | s = o + res; /* else update current position */ |
308 | n = lua_gettop(L) - fr + 1; /* number of new captures */ |
309 | ndyncap += n; /* update number of dynamic captures */ |
310 | if (n > 0) { /* any new capture? */ |
311 | if (fr + n >= SHRT_MAX) |
312 | luaL_error(L, "too many results in match-time capture" ); |
313 | if ((captop += n + 2) >= capsize) { |
314 | capture = doublecap(L, capture, captop, n + 2, ptop); |
315 | capsize = 2 * captop; |
316 | } |
317 | /* add new captures to 'capture' list */ |
318 | adddyncaptures(s, capture + captop - n - 2, n, fr); |
319 | } |
320 | p++; |
321 | continue; |
322 | } |
323 | case ICloseCapture: { |
324 | const char *s1 = s; |
325 | assert(captop > 0); |
326 | /* if possible, turn capture into a full capture */ |
327 | if (capture[captop - 1].siz == 0 && |
328 | s1 - capture[captop - 1].s < UCHAR_MAX) { |
329 | capture[captop - 1].siz = s1 - capture[captop - 1].s + 1; |
330 | p++; |
331 | continue; |
332 | } |
333 | else { |
334 | capture[captop].siz = 1; /* mark entry as closed */ |
335 | capture[captop].s = s; |
336 | goto pushcapture; |
337 | } |
338 | } |
339 | case IOpenCapture: |
340 | capture[captop].siz = 0; /* mark entry as open */ |
341 | capture[captop].s = s; |
342 | goto pushcapture; |
343 | case IFullCapture: |
344 | capture[captop].siz = getoff(p) + 1; /* save capture size */ |
345 | capture[captop].s = s - getoff(p); |
346 | /* goto pushcapture; */ |
347 | pushcapture: { |
348 | capture[captop].idx = p->i.key; |
349 | capture[captop].kind = getkind(p); |
350 | if (++captop >= capsize) { |
351 | capture = doublecap(L, capture, captop, 0, ptop); |
352 | capsize = 2 * captop; |
353 | } |
354 | p++; |
355 | continue; |
356 | } |
357 | default: assert(0); return NULL; |
358 | } |
359 | } |
360 | } |
361 | |
362 | /* }====================================================== */ |
363 | |
364 | |
365 | |