immunio 0.15.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +234 -0
  3. data/README.md +147 -0
  4. data/bin/immunio +5 -0
  5. data/lib/immunio.rb +29 -0
  6. data/lib/immunio/agent.rb +260 -0
  7. data/lib/immunio/authentication.rb +96 -0
  8. data/lib/immunio/blocked_app.rb +38 -0
  9. data/lib/immunio/channel.rb +432 -0
  10. data/lib/immunio/cli.rb +39 -0
  11. data/lib/immunio/context.rb +114 -0
  12. data/lib/immunio/errors.rb +43 -0
  13. data/lib/immunio/immunio_ca.crt +45 -0
  14. data/lib/immunio/logger.rb +87 -0
  15. data/lib/immunio/plugins/action_dispatch.rb +45 -0
  16. data/lib/immunio/plugins/action_view.rb +431 -0
  17. data/lib/immunio/plugins/active_record.rb +707 -0
  18. data/lib/immunio/plugins/active_record_relation.rb +370 -0
  19. data/lib/immunio/plugins/authlogic.rb +80 -0
  20. data/lib/immunio/plugins/csrf.rb +24 -0
  21. data/lib/immunio/plugins/devise.rb +40 -0
  22. data/lib/immunio/plugins/environment_reporter.rb +69 -0
  23. data/lib/immunio/plugins/eval.rb +51 -0
  24. data/lib/immunio/plugins/exception_handler.rb +55 -0
  25. data/lib/immunio/plugins/gems_tracker.rb +5 -0
  26. data/lib/immunio/plugins/haml.rb +36 -0
  27. data/lib/immunio/plugins/http_finisher.rb +50 -0
  28. data/lib/immunio/plugins/http_tracker.rb +203 -0
  29. data/lib/immunio/plugins/io.rb +96 -0
  30. data/lib/immunio/plugins/redirect.rb +42 -0
  31. data/lib/immunio/plugins/warden.rb +66 -0
  32. data/lib/immunio/processor.rb +234 -0
  33. data/lib/immunio/rails.rb +26 -0
  34. data/lib/immunio/request.rb +139 -0
  35. data/lib/immunio/rufus_lua_ext/ref.rb +27 -0
  36. data/lib/immunio/rufus_lua_ext/state.rb +157 -0
  37. data/lib/immunio/rufus_lua_ext/table.rb +137 -0
  38. data/lib/immunio/rufus_lua_ext/utils.rb +13 -0
  39. data/lib/immunio/version.rb +5 -0
  40. data/lib/immunio/vm.rb +291 -0
  41. data/lua-hooks/ext/all.c +78 -0
  42. data/lua-hooks/ext/bitop/README +22 -0
  43. data/lua-hooks/ext/bitop/bit.c +189 -0
  44. data/lua-hooks/ext/extconf.rb +38 -0
  45. data/lua-hooks/ext/libinjection/COPYING +37 -0
  46. data/lua-hooks/ext/libinjection/libinjection.h +65 -0
  47. data/lua-hooks/ext/libinjection/libinjection_html5.c +847 -0
  48. data/lua-hooks/ext/libinjection/libinjection_html5.h +54 -0
  49. data/lua-hooks/ext/libinjection/libinjection_sqli.c +2301 -0
  50. data/lua-hooks/ext/libinjection/libinjection_sqli.h +295 -0
  51. data/lua-hooks/ext/libinjection/libinjection_sqli_data.h +9349 -0
  52. data/lua-hooks/ext/libinjection/libinjection_xss.c +531 -0
  53. data/lua-hooks/ext/libinjection/libinjection_xss.h +21 -0
  54. data/lua-hooks/ext/libinjection/lualib.c +109 -0
  55. data/lua-hooks/ext/lpeg/HISTORY +90 -0
  56. data/lua-hooks/ext/lpeg/lpcap.c +537 -0
  57. data/lua-hooks/ext/lpeg/lpcap.h +43 -0
  58. data/lua-hooks/ext/lpeg/lpcode.c +986 -0
  59. data/lua-hooks/ext/lpeg/lpcode.h +34 -0
  60. data/lua-hooks/ext/lpeg/lpeg-128.gif +0 -0
  61. data/lua-hooks/ext/lpeg/lpeg.html +1429 -0
  62. data/lua-hooks/ext/lpeg/lpprint.c +244 -0
  63. data/lua-hooks/ext/lpeg/lpprint.h +35 -0
  64. data/lua-hooks/ext/lpeg/lptree.c +1238 -0
  65. data/lua-hooks/ext/lpeg/lptree.h +77 -0
  66. data/lua-hooks/ext/lpeg/lptypes.h +149 -0
  67. data/lua-hooks/ext/lpeg/lpvm.c +355 -0
  68. data/lua-hooks/ext/lpeg/lpvm.h +58 -0
  69. data/lua-hooks/ext/lpeg/makefile +55 -0
  70. data/lua-hooks/ext/lpeg/re.html +498 -0
  71. data/lua-hooks/ext/lpeg/test.lua +1409 -0
  72. data/lua-hooks/ext/lua-cmsgpack/CMakeLists.txt +45 -0
  73. data/lua-hooks/ext/lua-cmsgpack/README.md +115 -0
  74. data/lua-hooks/ext/lua-cmsgpack/lua_cmsgpack.c +957 -0
  75. data/lua-hooks/ext/lua-cmsgpack/test.lua +570 -0
  76. data/lua-hooks/ext/lua-snapshot/LICENSE +7 -0
  77. data/lua-hooks/ext/lua-snapshot/Makefile +12 -0
  78. data/lua-hooks/ext/lua-snapshot/README.md +18 -0
  79. data/lua-hooks/ext/lua-snapshot/dump.lua +15 -0
  80. data/lua-hooks/ext/lua-snapshot/snapshot.c +455 -0
  81. data/lua-hooks/ext/lua/COPYRIGHT +34 -0
  82. data/lua-hooks/ext/lua/lapi.c +1087 -0
  83. data/lua-hooks/ext/lua/lapi.h +16 -0
  84. data/lua-hooks/ext/lua/lauxlib.c +652 -0
  85. data/lua-hooks/ext/lua/lauxlib.h +174 -0
  86. data/lua-hooks/ext/lua/lbaselib.c +659 -0
  87. data/lua-hooks/ext/lua/lcode.c +831 -0
  88. data/lua-hooks/ext/lua/lcode.h +76 -0
  89. data/lua-hooks/ext/lua/ldblib.c +398 -0
  90. data/lua-hooks/ext/lua/ldebug.c +638 -0
  91. data/lua-hooks/ext/lua/ldebug.h +33 -0
  92. data/lua-hooks/ext/lua/ldo.c +519 -0
  93. data/lua-hooks/ext/lua/ldo.h +57 -0
  94. data/lua-hooks/ext/lua/ldump.c +164 -0
  95. data/lua-hooks/ext/lua/lfunc.c +174 -0
  96. data/lua-hooks/ext/lua/lfunc.h +34 -0
  97. data/lua-hooks/ext/lua/lgc.c +710 -0
  98. data/lua-hooks/ext/lua/lgc.h +110 -0
  99. data/lua-hooks/ext/lua/linit.c +38 -0
  100. data/lua-hooks/ext/lua/liolib.c +556 -0
  101. data/lua-hooks/ext/lua/llex.c +463 -0
  102. data/lua-hooks/ext/lua/llex.h +81 -0
  103. data/lua-hooks/ext/lua/llimits.h +128 -0
  104. data/lua-hooks/ext/lua/lmathlib.c +263 -0
  105. data/lua-hooks/ext/lua/lmem.c +86 -0
  106. data/lua-hooks/ext/lua/lmem.h +49 -0
  107. data/lua-hooks/ext/lua/loadlib.c +705 -0
  108. data/lua-hooks/ext/lua/loadlib_rel.c +760 -0
  109. data/lua-hooks/ext/lua/lobject.c +214 -0
  110. data/lua-hooks/ext/lua/lobject.h +381 -0
  111. data/lua-hooks/ext/lua/lopcodes.c +102 -0
  112. data/lua-hooks/ext/lua/lopcodes.h +268 -0
  113. data/lua-hooks/ext/lua/loslib.c +243 -0
  114. data/lua-hooks/ext/lua/lparser.c +1339 -0
  115. data/lua-hooks/ext/lua/lparser.h +82 -0
  116. data/lua-hooks/ext/lua/lstate.c +214 -0
  117. data/lua-hooks/ext/lua/lstate.h +169 -0
  118. data/lua-hooks/ext/lua/lstring.c +111 -0
  119. data/lua-hooks/ext/lua/lstring.h +31 -0
  120. data/lua-hooks/ext/lua/lstrlib.c +871 -0
  121. data/lua-hooks/ext/lua/ltable.c +588 -0
  122. data/lua-hooks/ext/lua/ltable.h +40 -0
  123. data/lua-hooks/ext/lua/ltablib.c +287 -0
  124. data/lua-hooks/ext/lua/ltm.c +75 -0
  125. data/lua-hooks/ext/lua/ltm.h +54 -0
  126. data/lua-hooks/ext/lua/lua.c +392 -0
  127. data/lua-hooks/ext/lua/lua.def +131 -0
  128. data/lua-hooks/ext/lua/lua.h +388 -0
  129. data/lua-hooks/ext/lua/lua.rc +28 -0
  130. data/lua-hooks/ext/lua/lua_dll.rc +26 -0
  131. data/lua-hooks/ext/lua/luac.c +200 -0
  132. data/lua-hooks/ext/lua/luac.rc +1 -0
  133. data/lua-hooks/ext/lua/luaconf.h +763 -0
  134. data/lua-hooks/ext/lua/luaconf.h.in +724 -0
  135. data/lua-hooks/ext/lua/luaconf.h.orig +763 -0
  136. data/lua-hooks/ext/lua/lualib.h +53 -0
  137. data/lua-hooks/ext/lua/lundump.c +227 -0
  138. data/lua-hooks/ext/lua/lundump.h +36 -0
  139. data/lua-hooks/ext/lua/lvm.c +767 -0
  140. data/lua-hooks/ext/lua/lvm.h +36 -0
  141. data/lua-hooks/ext/lua/lzio.c +82 -0
  142. data/lua-hooks/ext/lua/lzio.h +67 -0
  143. data/lua-hooks/ext/lua/print.c +227 -0
  144. data/lua-hooks/ext/luautf8/README.md +152 -0
  145. data/lua-hooks/ext/luautf8/lutf8lib.c +1274 -0
  146. data/lua-hooks/ext/luautf8/unidata.h +3064 -0
  147. data/lua-hooks/lib/boot.lua +254 -0
  148. data/lua-hooks/lib/encode.lua +4 -0
  149. data/lua-hooks/lib/lexers/LICENSE +21 -0
  150. data/lua-hooks/lib/lexers/bash.lua +134 -0
  151. data/lua-hooks/lib/lexers/bash_dqstr.lua +62 -0
  152. data/lua-hooks/lib/lexers/css.lua +216 -0
  153. data/lua-hooks/lib/lexers/html.lua +106 -0
  154. data/lua-hooks/lib/lexers/javascript.lua +68 -0
  155. data/lua-hooks/lib/lexers/lexer.lua +1575 -0
  156. data/lua-hooks/lib/lexers/markers.lua +33 -0
  157. metadata +308 -0
@@ -0,0 +1,43 @@
1
+ /*
2
+ ** $Id: lpcap.h,v 1.1 2013/03/21 20:25:12 roberto Exp $
3
+ */
4
+
5
+ #if !defined(lpcap_h)
6
+ #define lpcap_h
7
+
8
+
9
+ #include "lptypes.h"
10
+
11
+
12
+ /* kinds of captures */
13
+ typedef enum CapKind {
14
+ Cclose, Cposition, Cconst, Cbackref, Carg, Csimple, Ctable, Cfunction,
15
+ Cquery, Cstring, Cnum, Csubst, Cfold, Cruntime, Cgroup
16
+ } CapKind;
17
+
18
+
19
+ typedef struct Capture {
20
+ const char *s; /* subject position */
21
+ short idx; /* extra info about capture (group name, arg index, etc.) */
22
+ byte kind; /* kind of capture */
23
+ byte siz; /* size of full capture + 1 (0 = not a full capture) */
24
+ } Capture;
25
+
26
+
27
+ typedef struct CapState {
28
+ Capture *cap; /* current capture */
29
+ Capture *ocap; /* (original) capture list */
30
+ lua_State *L;
31
+ int ptop; /* index of last argument to 'match' */
32
+ const char *s; /* original string */
33
+ int valuecached; /* value stored in cache slot */
34
+ } CapState;
35
+
36
+
37
+ int runtimecap (CapState *cs, Capture *close, const char *s, int *rem);
38
+ int getcaptures (lua_State *L, const char *s, const char *r, int ptop);
39
+ int finddyncap (Capture *cap, Capture *last);
40
+
41
+ #endif
42
+
43
+
@@ -0,0 +1,986 @@
1
+ /*
2
+ ** $Id: lpcode.c,v 1.21 2014/12/12 17:01:29 roberto Exp $
3
+ ** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
4
+ */
5
+
6
+ #include <limits.h>
7
+
8
+
9
+ #include "../lua/lua.h"
10
+ #include "../lua/lauxlib.h"
11
+
12
+ #include "lptypes.h"
13
+ #include "lpcode.h"
14
+
15
+
16
+ /* signals a "no-LpegInstruction */
17
+ #define NOINST -1
18
+
19
+
20
+
21
+ static const Charset fullset_ =
22
+ {{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
23
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
24
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
25
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};
26
+
27
+ static const Charset *fullset = &fullset_;
28
+
29
+ /*
30
+ ** {======================================================
31
+ ** Analysis and some optimizations
32
+ ** =======================================================
33
+ */
34
+
35
+ /*
36
+ ** Check whether a charset is empty (returns IFail), singleton (IChar),
37
+ ** full (IAny), or none of those (ISet). When singleton, '*c' returns
38
+ ** which character it is. (When generic set, the set was the input,
39
+ ** so there is no need to return it.)
40
+ */
41
+ static Opcode charsettype (const byte *cs, int *c) {
42
+ int count = 0; /* number of characters in the set */
43
+ int i;
44
+ int candidate = -1; /* candidate position for the singleton char */
45
+ for (i = 0; i < CHARSETSIZE; i++) { /* for each byte */
46
+ int b = cs[i];
47
+ if (b == 0) { /* is byte empty? */
48
+ if (count > 1) /* was set neither empty nor singleton? */
49
+ return ISet; /* neither full nor empty nor singleton */
50
+ /* else set is still empty or singleton */
51
+ }
52
+ else if (b == 0xFF) { /* is byte full? */
53
+ if (count < (i * BITSPERCHAR)) /* was set not full? */
54
+ return ISet; /* neither full nor empty nor singleton */
55
+ else count += BITSPERCHAR; /* set is still full */
56
+ }
57
+ else if ((b & (b - 1)) == 0) { /* has byte only one bit? */
58
+ if (count > 0) /* was set not empty? */
59
+ return ISet; /* neither full nor empty nor singleton */
60
+ else { /* set has only one char till now; track it */
61
+ count++;
62
+ candidate = i;
63
+ }
64
+ }
65
+ else return ISet; /* byte is neither empty, full, nor singleton */
66
+ }
67
+ switch (count) {
68
+ case 0: return IFail; /* empty set */
69
+ case 1: { /* singleton; find character bit inside byte */
70
+ int b = cs[candidate];
71
+ *c = candidate * BITSPERCHAR;
72
+ if ((b & 0xF0) != 0) { *c += 4; b >>= 4; }
73
+ if ((b & 0x0C) != 0) { *c += 2; b >>= 2; }
74
+ if ((b & 0x02) != 0) { *c += 1; }
75
+ return IChar;
76
+ }
77
+ default: {
78
+ assert(count == CHARSETSIZE * BITSPERCHAR); /* full set */
79
+ return IAny;
80
+ }
81
+ }
82
+ }
83
+
84
+
85
+ /*
86
+ ** A few basic operations on Charsets
87
+ */
88
+ static void cs_complement (Charset *cs) {
89
+ loopset(i, cs->cs[i] = ~cs->cs[i]);
90
+ }
91
+
92
+ static int cs_equal (const byte *cs1, const byte *cs2) {
93
+ loopset(i, if (cs1[i] != cs2[i]) return 0);
94
+ return 1;
95
+ }
96
+
97
+ static int cs_disjoint (const Charset *cs1, const Charset *cs2) {
98
+ loopset(i, if ((cs1->cs[i] & cs2->cs[i]) != 0) return 0;)
99
+ return 1;
100
+ }
101
+
102
+
103
+ /*
104
+ ** If 'tree' is a 'char' pattern (TSet, TChar, TAny), convert it into a
105
+ ** charset and return 1; else return 0.
106
+ */
107
+ int tocharset (TTree *tree, Charset *cs) {
108
+ switch (tree->tag) {
109
+ case TSet: { /* copy set */
110
+ loopset(i, cs->cs[i] = treebuffer(tree)[i]);
111
+ return 1;
112
+ }
113
+ case TChar: { /* only one char */
114
+ assert(0 <= tree->u.n && tree->u.n <= UCHAR_MAX);
115
+ loopset(i, cs->cs[i] = 0); /* erase all chars */
116
+ setchar(cs->cs, tree->u.n); /* add that one */
117
+ return 1;
118
+ }
119
+ case TAny: {
120
+ loopset(i, cs->cs[i] = 0xFF); /* add all characters to the set */
121
+ return 1;
122
+ }
123
+ default: return 0;
124
+ }
125
+ }
126
+
127
+
128
+ /*
129
+ ** Check whether a pattern tree has captures
130
+ */
131
+ int hascaptures (TTree *tree) {
132
+ tailcall:
133
+ switch (tree->tag) {
134
+ case TCapture: case TRunTime:
135
+ return 1;
136
+ case TCall:
137
+ tree = sib2(tree); goto tailcall; /* return hascaptures(sib2(tree)); */
138
+ case TOpenCall: assert(0);
139
+ default: {
140
+ switch (numsiblings[tree->tag]) {
141
+ case 1: /* return hascaptures(sib1(tree)); */
142
+ tree = sib1(tree); goto tailcall;
143
+ case 2:
144
+ if (hascaptures(sib1(tree))) return 1;
145
+ /* else return hascaptures(sib2(tree)); */
146
+ tree = sib2(tree); goto tailcall;
147
+ default: assert(numsiblings[tree->tag] == 0); return 0;
148
+ }
149
+ }
150
+ }
151
+ }
152
+
153
+
154
+ /*
155
+ ** Checks how a pattern behaves regarding the empty string,
156
+ ** in one of two different ways:
157
+ ** A pattern is *nullable* if it can match without consuming any character;
158
+ ** A pattern is *nofail* if it never fails for any string
159
+ ** (including the empty string).
160
+ ** The difference is only for predicates and run-time captures;
161
+ ** for other patterns, the two properties are equivalent.
162
+ ** (With predicates, &'a' is nullable but not nofail. Of course,
163
+ ** nofail => nullable.)
164
+ ** These functions are all convervative in the following way:
165
+ ** p is nullable => nullable(p)
166
+ ** nofail(p) => p cannot fail
167
+ ** The function assumes that TOpenCall is not nullable;
168
+ ** this will be checked again when the grammar is fixed.
169
+ ** Run-time captures can do whatever they want, so the result
170
+ ** is conservative.
171
+ */
172
+ int checkaux (TTree *tree, int pred) {
173
+ tailcall:
174
+ switch (tree->tag) {
175
+ case TChar: case TSet: case TAny:
176
+ case TFalse: case TOpenCall:
177
+ return 0; /* not nullable */
178
+ case TRep: case TTrue:
179
+ return 1; /* no fail */
180
+ case TNot: case TBehind: /* can match empty, but can fail */
181
+ if (pred == PEnofail) return 0;
182
+ else return 1; /* PEnullable */
183
+ case TAnd: /* can match empty; fail iff body does */
184
+ if (pred == PEnullable) return 1;
185
+ /* else return checkaux(sib1(tree), pred); */
186
+ tree = sib1(tree); goto tailcall;
187
+ case TRunTime: /* can fail; match empty iff body does */
188
+ if (pred == PEnofail) return 0;
189
+ /* else return checkaux(sib1(tree), pred); */
190
+ tree = sib1(tree); goto tailcall;
191
+ case TSeq:
192
+ if (!checkaux(sib1(tree), pred)) return 0;
193
+ /* else return checkaux(sib2(tree), pred); */
194
+ tree = sib2(tree); goto tailcall;
195
+ case TChoice:
196
+ if (checkaux(sib2(tree), pred)) return 1;
197
+ /* else return checkaux(sib1(tree), pred); */
198
+ tree = sib1(tree); goto tailcall;
199
+ case TCapture: case TGrammar: case TRule:
200
+ /* return checkaux(sib1(tree), pred); */
201
+ tree = sib1(tree); goto tailcall;
202
+ case TCall: /* return checkaux(sib2(tree), pred); */
203
+ tree = sib2(tree); goto tailcall;
204
+ default: assert(0); return 0;
205
+ }
206
+ }
207
+
208
+
209
+ /*
210
+ ** number of characters to match a pattern (or -1 if variable)
211
+ ** ('count' avoids infinite loops for grammars)
212
+ */
213
+ int fixedlenx (TTree *tree, int count, int len) {
214
+ tailcall:
215
+ switch (tree->tag) {
216
+ case TChar: case TSet: case TAny:
217
+ return len + 1;
218
+ case TFalse: case TTrue: case TNot: case TAnd: case TBehind:
219
+ return len;
220
+ case TRep: case TRunTime: case TOpenCall:
221
+ return -1;
222
+ case TCapture: case TRule: case TGrammar:
223
+ /* return fixedlenx(sib1(tree), count); */
224
+ tree = sib1(tree); goto tailcall;
225
+ case TCall:
226
+ if (count++ >= MAXRULES)
227
+ return -1; /* may be a loop */
228
+ /* else return fixedlenx(sib2(tree), count); */
229
+ tree = sib2(tree); goto tailcall;
230
+ case TSeq: {
231
+ len = fixedlenx(sib1(tree), count, len);
232
+ if (len < 0) return -1;
233
+ /* else return fixedlenx(sib2(tree), count, len); */
234
+ tree = sib2(tree); goto tailcall;
235
+ }
236
+ case TChoice: {
237
+ int n1, n2;
238
+ n1 = fixedlenx(sib1(tree), count, len);
239
+ if (n1 < 0) return -1;
240
+ n2 = fixedlenx(sib2(tree), count, len);
241
+ if (n1 == n2) return n1;
242
+ else return -1;
243
+ }
244
+ default: assert(0); return 0;
245
+ };
246
+ }
247
+
248
+
249
+ /*
250
+ ** Computes the 'first set' of a pattern.
251
+ ** The result is a conservative aproximation:
252
+ ** match p ax -> x (for some x) ==> a belongs to first(p)
253
+ ** or
254
+ ** a not in first(p) ==> match p ax -> fail (for all x)
255
+ **
256
+ ** The set 'follow' is the first set of what follows the
257
+ ** pattern (full set if nothing follows it).
258
+ **
259
+ ** The function returns 0 when this resulting set can be used for
260
+ ** test LpegInstructions that avoid the pattern altogether.
261
+ ** A non-zero return can happen for two reasons:
262
+ ** 1) match p '' -> '' ==> return has bit 1 set
263
+ ** (tests cannot be used because they would always fail for an empty input);
264
+ ** 2) there is a match-time capture ==> return has bit 2 set
265
+ ** (optimizations should not bypass match-time captures).
266
+ */
267
+ static int getfirst (TTree *tree, const Charset *follow, Charset *firstset) {
268
+ tailcall:
269
+ switch (tree->tag) {
270
+ case TChar: case TSet: case TAny: {
271
+ tocharset(tree, firstset);
272
+ return 0;
273
+ }
274
+ case TTrue: {
275
+ loopset(i, firstset->cs[i] = follow->cs[i]);
276
+ return 1; /* accepts the empty string */
277
+ }
278
+ case TFalse: {
279
+ loopset(i, firstset->cs[i] = 0);
280
+ return 0;
281
+ }
282
+ case TChoice: {
283
+ Charset csaux;
284
+ int e1 = getfirst(sib1(tree), follow, firstset);
285
+ int e2 = getfirst(sib2(tree), follow, &csaux);
286
+ loopset(i, firstset->cs[i] |= csaux.cs[i]);
287
+ return e1 | e2;
288
+ }
289
+ case TSeq: {
290
+ if (!nullable(sib1(tree))) {
291
+ /* when p1 is not nullable, p2 has nothing to contribute;
292
+ return getfirst(sib1(tree), fullset, firstset); */
293
+ tree = sib1(tree); follow = fullset; goto tailcall;
294
+ }
295
+ else { /* FIRST(p1 p2, fl) = FIRST(p1, FIRST(p2, fl)) */
296
+ Charset csaux;
297
+ int e2 = getfirst(sib2(tree), follow, &csaux);
298
+ int e1 = getfirst(sib1(tree), &csaux, firstset);
299
+ if (e1 == 0) return 0; /* 'e1' ensures that first can be used */
300
+ else if ((e1 | e2) & 2) /* one of the children has a matchtime? */
301
+ return 2; /* pattern has a matchtime capture */
302
+ else return e2; /* else depends on 'e2' */
303
+ }
304
+ }
305
+ case TRep: {
306
+ getfirst(sib1(tree), follow, firstset);
307
+ loopset(i, firstset->cs[i] |= follow->cs[i]);
308
+ return 1; /* accept the empty string */
309
+ }
310
+ case TCapture: case TGrammar: case TRule: {
311
+ /* return getfirst(sib1(tree), follow, firstset); */
312
+ tree = sib1(tree); goto tailcall;
313
+ }
314
+ case TRunTime: { /* function invalidates any follow info. */
315
+ int e = getfirst(sib1(tree), fullset, firstset);
316
+ if (e) return 2; /* function is not "protected"? */
317
+ else return 0; /* pattern inside capture ensures first can be used */
318
+ }
319
+ case TCall: {
320
+ /* return getfirst(sib2(tree), follow, firstset); */
321
+ tree = sib2(tree); goto tailcall;
322
+ }
323
+ case TAnd: {
324
+ int e = getfirst(sib1(tree), follow, firstset);
325
+ loopset(i, firstset->cs[i] &= follow->cs[i]);
326
+ return e;
327
+ }
328
+ case TNot: {
329
+ if (tocharset(sib1(tree), firstset)) {
330
+ cs_complement(firstset);
331
+ return 1;
332
+ }
333
+ /* else go through */
334
+ }
335
+ case TBehind: { /* LpegInstruction gives no new information */
336
+ /* call 'getfirst' only to check for math-time captures */
337
+ int e = getfirst(sib1(tree), follow, firstset);
338
+ loopset(i, firstset->cs[i] = follow->cs[i]); /* uses follow */
339
+ return e | 1; /* always can accept the empty string */
340
+ }
341
+ default: assert(0); return 0;
342
+ }
343
+ }
344
+
345
+
346
+ /*
347
+ ** If 'headfail(tree)' true, then 'tree' can fail only depending on the
348
+ ** next character of the subject.
349
+ */
350
+ static int headfail (TTree *tree) {
351
+ tailcall:
352
+ switch (tree->tag) {
353
+ case TChar: case TSet: case TAny: case TFalse:
354
+ return 1;
355
+ case TTrue: case TRep: case TRunTime: case TNot:
356
+ case TBehind:
357
+ return 0;
358
+ case TCapture: case TGrammar: case TRule: case TAnd:
359
+ tree = sib1(tree); goto tailcall; /* return headfail(sib1(tree)); */
360
+ case TCall:
361
+ tree = sib2(tree); goto tailcall; /* return headfail(sib2(tree)); */
362
+ case TSeq:
363
+ if (!nofail(sib2(tree))) return 0;
364
+ /* else return headfail(sib1(tree)); */
365
+ tree = sib1(tree); goto tailcall;
366
+ case TChoice:
367
+ if (!headfail(sib1(tree))) return 0;
368
+ /* else return headfail(sib2(tree)); */
369
+ tree = sib2(tree); goto tailcall;
370
+ default: assert(0); return 0;
371
+ }
372
+ }
373
+
374
+
375
+ /*
376
+ ** Check whether the code generation for the given tree can benefit
377
+ ** from a follow set (to avoid computing the follow set when it is
378
+ ** not needed)
379
+ */
380
+ static int needfollow (TTree *tree) {
381
+ tailcall:
382
+ switch (tree->tag) {
383
+ case TChar: case TSet: case TAny:
384
+ case TFalse: case TTrue: case TAnd: case TNot:
385
+ case TRunTime: case TGrammar: case TCall: case TBehind:
386
+ return 0;
387
+ case TChoice: case TRep:
388
+ return 1;
389
+ case TCapture:
390
+ tree = sib1(tree); goto tailcall;
391
+ case TSeq:
392
+ tree = sib2(tree); goto tailcall;
393
+ default: assert(0); return 0;
394
+ }
395
+ }
396
+
397
+ /* }====================================================== */
398
+
399
+
400
+
401
+ /*
402
+ ** {======================================================
403
+ ** Code generation
404
+ ** =======================================================
405
+ */
406
+
407
+
408
+ /*
409
+ ** size of an LpegInstruction
410
+ */
411
+ int sizei (const LpegInstruction *i) {
412
+ switch((Opcode)i->i.code) {
413
+ case ISet: case ISpan: return CHARSETINSTSIZE;
414
+ case ITestSet: return CHARSETINSTSIZE + 1;
415
+ case ITestChar: case ITestAny: case IChoice: case IJmp: case ICall:
416
+ case IOpenCall: case ICommit: case IPartialCommit: case IBackCommit:
417
+ return 2;
418
+ default: return 1;
419
+ }
420
+ }
421
+
422
+
423
+ /*
424
+ ** state for the compiler
425
+ */
426
+ typedef struct CompileState {
427
+ Pattern *p; /* pattern being compiled */
428
+ int ncode; /* next position in p->code to be filled */
429
+ lua_State *L;
430
+ } CompileState;
431
+
432
+
433
+ /*
434
+ ** code generation is recursive; 'opt' indicates that the code is
435
+ ** being generated under a 'IChoice' operator jumping to its end
436
+ ** (that is, the match is "optional").
437
+ ** 'tt' points to a previous test protecting this code. 'fl' is
438
+ ** the follow set of the pattern.
439
+ */
440
+ static void codegen (CompileState *compst, TTree *tree, int opt, int tt,
441
+ const Charset *fl);
442
+
443
+
444
+ void realloccode (lua_State *L, Pattern *p, int nsize) {
445
+ void *ud;
446
+ lua_Alloc f = lua_getallocf(L, &ud);
447
+ void *newblock = f(ud, p->code, p->codesize * sizeof(LpegInstruction),
448
+ nsize * sizeof(LpegInstruction));
449
+ if (newblock == NULL && nsize > 0)
450
+ luaL_error(L, "not enough memory");
451
+ p->code = (LpegInstruction *)newblock;
452
+ p->codesize = nsize;
453
+ }
454
+
455
+
456
+ static int nextLpegInstruction (CompileState *compst) {
457
+ int size = compst->p->codesize;
458
+ if (compst->ncode >= size)
459
+ realloccode(compst->L, compst->p, size * 2);
460
+ return compst->ncode++;
461
+ }
462
+
463
+
464
+ #define getinstr(cs,i) ((cs)->p->code[i])
465
+
466
+
467
+ static int addLpegInstruction (CompileState *compst, Opcode op, int aux) {
468
+ int i = nextLpegInstruction(compst);
469
+ getinstr(compst, i).i.code = op;
470
+ getinstr(compst, i).i.aux = aux;
471
+ return i;
472
+ }
473
+
474
+
475
+ /*
476
+ ** Add an LpegInstruction followed by space for an offset (to be set later)
477
+ */
478
+ static int addoffsetinst (CompileState *compst, Opcode op) {
479
+ int i = addLpegInstruction(compst, op, 0); /* LpegInstruction */
480
+ addLpegInstruction(compst, (Opcode)0, 0); /* open space for offset */
481
+ assert(op == ITestSet || sizei(&getinstr(compst, i)) == 2);
482
+ return i;
483
+ }
484
+
485
+
486
+ /*
487
+ ** Set the offset of an LpegInstruction
488
+ */
489
+ static void setoffset (CompileState *compst, int LpegInstruction, int offset) {
490
+ getinstr(compst, LpegInstruction + 1).offset = offset;
491
+ }
492
+
493
+
494
+ /*
495
+ ** Add a capture LpegInstruction:
496
+ ** 'op' is the capture LpegInstruction; 'cap' the capture kind;
497
+ ** 'key' the key into ktable; 'aux' is the optional capture offset
498
+ **
499
+ */
500
+ static int addinstcap (CompileState *compst, Opcode op, int cap, int key,
501
+ int aux) {
502
+ int i = addLpegInstruction(compst, op, joinkindoff(cap, aux));
503
+ getinstr(compst, i).i.key = key;
504
+ return i;
505
+ }
506
+
507
+
508
+ #define gethere(compst) ((compst)->ncode)
509
+
510
+ #define target(code,i) ((i) + code[i + 1].offset)
511
+
512
+
513
+ /*
514
+ ** Patch 'LpegInstruction' to jump to 'target'
515
+ */
516
+ static void jumptothere (CompileState *compst, int LpegInstruction, int target) {
517
+ if (LpegInstruction >= 0)
518
+ setoffset(compst, LpegInstruction, target - LpegInstruction);
519
+ }
520
+
521
+
522
+ /*
523
+ ** Patch 'LpegInstruction' to jump to current position
524
+ */
525
+ static void jumptohere (CompileState *compst, int LpegInstruction) {
526
+ jumptothere(compst, LpegInstruction, gethere(compst));
527
+ }
528
+
529
+
530
+ /*
531
+ ** Code an IChar LpegInstruction, or IAny if there is an equivalent
532
+ ** test dominating it
533
+ */
534
+ static void codechar (CompileState *compst, int c, int tt) {
535
+ if (tt >= 0 && getinstr(compst, tt).i.code == ITestChar &&
536
+ getinstr(compst, tt).i.aux == c)
537
+ addLpegInstruction(compst, IAny, 0);
538
+ else
539
+ addLpegInstruction(compst, IChar, c);
540
+ }
541
+
542
+
543
+ /*
544
+ ** Add a charset posfix to an LpegInstruction
545
+ */
546
+ static void addcharset (CompileState *compst, const byte *cs) {
547
+ int p = gethere(compst);
548
+ int i;
549
+ for (i = 0; i < (int)CHARSETINSTSIZE - 1; i++)
550
+ nextLpegInstruction(compst); /* space for buffer */
551
+ /* fill buffer with charset */
552
+ loopset(j, getinstr(compst, p).buff[j] = cs[j]);
553
+ }
554
+
555
+
556
+ /*
557
+ ** code a char set, optimizing unit sets for IChar, "complete"
558
+ ** sets for IAny, and empty sets for IFail; also use an IAny
559
+ ** when LpegInstruction is dominated by an equivalent test.
560
+ */
561
+ static void codecharset (CompileState *compst, const byte *cs, int tt) {
562
+ int c = 0; /* (=) to avoid warnings */
563
+ Opcode op = charsettype(cs, &c);
564
+ switch (op) {
565
+ case IChar: codechar(compst, c, tt); break;
566
+ case ISet: { /* non-trivial set? */
567
+ if (tt >= 0 && getinstr(compst, tt).i.code == ITestSet &&
568
+ cs_equal(cs, getinstr(compst, tt + 2).buff))
569
+ addLpegInstruction(compst, IAny, 0);
570
+ else {
571
+ addLpegInstruction(compst, ISet, 0);
572
+ addcharset(compst, cs);
573
+ }
574
+ break;
575
+ }
576
+ default: addLpegInstruction(compst, op, c); break;
577
+ }
578
+ }
579
+
580
+
581
+ /*
582
+ ** code a test set, optimizing unit sets for ITestChar, "complete"
583
+ ** sets for ITestAny, and empty sets for IJmp (always fails).
584
+ ** 'e' is true iff test should accept the empty string. (Test
585
+ ** LpegInstructions in the current VM never accept the empty string.)
586
+ */
587
+ static int codetestset (CompileState *compst, Charset *cs, int e) {
588
+ if (e) return NOINST; /* no test */
589
+ else {
590
+ int c = 0;
591
+ Opcode op = charsettype(cs->cs, &c);
592
+ switch (op) {
593
+ case IFail: return addoffsetinst(compst, IJmp); /* always jump */
594
+ case IAny: return addoffsetinst(compst, ITestAny);
595
+ case IChar: {
596
+ int i = addoffsetinst(compst, ITestChar);
597
+ getinstr(compst, i).i.aux = c;
598
+ return i;
599
+ }
600
+ case ISet: {
601
+ int i = addoffsetinst(compst, ITestSet);
602
+ addcharset(compst, cs->cs);
603
+ return i;
604
+ }
605
+ default: assert(0); return 0;
606
+ }
607
+ }
608
+ }
609
+
610
+
611
+ /*
612
+ ** Find the final destination of a sequence of jumps
613
+ */
614
+ static int finaltarget (LpegInstruction *code, int i) {
615
+ while (code[i].i.code == IJmp)
616
+ i = target(code, i);
617
+ return i;
618
+ }
619
+
620
+
621
+ /*
622
+ ** final label (after traversing any jumps)
623
+ */
624
+ static int finallabel (LpegInstruction *code, int i) {
625
+ return finaltarget(code, target(code, i));
626
+ }
627
+
628
+
629
+ /*
630
+ ** <behind(p)> == behind n; <p> (where n = fixedlen(p))
631
+ */
632
+ static void codebehind (CompileState *compst, TTree *tree) {
633
+ if (tree->u.n > 0)
634
+ addLpegInstruction(compst, IBehind, tree->u.n);
635
+ codegen(compst, sib1(tree), 0, NOINST, fullset);
636
+ }
637
+
638
+
639
+ /*
640
+ ** Choice; optimizations:
641
+ ** - when p1 is headfail
642
+ ** - when first(p1) and first(p2) are disjoint; than
643
+ ** a character not in first(p1) cannot go to p1, and a character
644
+ ** in first(p1) cannot go to p2 (at it is not in first(p2)).
645
+ ** (The optimization is not valid if p1 accepts the empty string,
646
+ ** as then there is no character at all...)
647
+ ** - when p2 is empty and opt is true; a IPartialCommit can resuse
648
+ ** the Choice already active in the stack.
649
+ */
650
+ static void codechoice (CompileState *compst, TTree *p1, TTree *p2, int opt,
651
+ const Charset *fl) {
652
+ int emptyp2 = (p2->tag == TTrue);
653
+ Charset cs1, cs2;
654
+ int e1 = getfirst(p1, fullset, &cs1);
655
+ if (headfail(p1) ||
656
+ (!e1 && (getfirst(p2, fl, &cs2), cs_disjoint(&cs1, &cs2)))) {
657
+ /* <p1 / p2> == test (fail(p1)) -> L1 ; p1 ; jmp L2; L1: p2; L2: */
658
+ int test = codetestset(compst, &cs1, 0);
659
+ int jmp = NOINST;
660
+ codegen(compst, p1, 0, test, fl);
661
+ if (!emptyp2)
662
+ jmp = addoffsetinst(compst, IJmp);
663
+ jumptohere(compst, test);
664
+ codegen(compst, p2, opt, NOINST, fl);
665
+ jumptohere(compst, jmp);
666
+ }
667
+ else if (opt && emptyp2) {
668
+ /* p1? == IPartialCommit; p1 */
669
+ jumptohere(compst, addoffsetinst(compst, IPartialCommit));
670
+ codegen(compst, p1, 1, NOINST, fullset);
671
+ }
672
+ else {
673
+ /* <p1 / p2> ==
674
+ test(fail(p1)) -> L1; choice L1; <p1>; commit L2; L1: <p2>; L2: */
675
+ int pcommit;
676
+ int test = codetestset(compst, &cs1, e1);
677
+ int pchoice = addoffsetinst(compst, IChoice);
678
+ codegen(compst, p1, emptyp2, test, fullset);
679
+ pcommit = addoffsetinst(compst, ICommit);
680
+ jumptohere(compst, pchoice);
681
+ jumptohere(compst, test);
682
+ codegen(compst, p2, opt, NOINST, fl);
683
+ jumptohere(compst, pcommit);
684
+ }
685
+ }
686
+
687
+
688
+ /*
689
+ ** And predicate
690
+ ** optimization: fixedlen(p) = n ==> <&p> == <p>; behind n
691
+ ** (valid only when 'p' has no captures)
692
+ */
693
+ static void codeand (CompileState *compst, TTree *tree, int tt) {
694
+ int n = fixedlen(tree);
695
+ if (n >= 0 && n <= MAXBEHIND && !hascaptures(tree)) {
696
+ codegen(compst, tree, 0, tt, fullset);
697
+ if (n > 0)
698
+ addLpegInstruction(compst, IBehind, n);
699
+ }
700
+ else { /* default: Choice L1; p1; BackCommit L2; L1: Fail; L2: */
701
+ int pcommit;
702
+ int pchoice = addoffsetinst(compst, IChoice);
703
+ codegen(compst, tree, 0, tt, fullset);
704
+ pcommit = addoffsetinst(compst, IBackCommit);
705
+ jumptohere(compst, pchoice);
706
+ addLpegInstruction(compst, IFail, 0);
707
+ jumptohere(compst, pcommit);
708
+ }
709
+ }
710
+
711
+
712
+ /*
713
+ ** Captures: if pattern has fixed (and not too big) length, use
714
+ ** a single IFullCapture LpegInstruction after the match; otherwise,
715
+ ** enclose the pattern with OpenCapture - CloseCapture.
716
+ */
717
+ static void codecapture (CompileState *compst, TTree *tree, int tt,
718
+ const Charset *fl) {
719
+ int len = fixedlen(sib1(tree));
720
+ if (len >= 0 && len <= MAXOFF && !hascaptures(sib1(tree))) {
721
+ codegen(compst, sib1(tree), 0, tt, fl);
722
+ addinstcap(compst, IFullCapture, tree->cap, tree->key, len);
723
+ }
724
+ else {
725
+ addinstcap(compst, IOpenCapture, tree->cap, tree->key, 0);
726
+ codegen(compst, sib1(tree), 0, tt, fl);
727
+ addinstcap(compst, ICloseCapture, Cclose, 0, 0);
728
+ }
729
+ }
730
+
731
+
732
+ static void coderuntime (CompileState *compst, TTree *tree, int tt) {
733
+ addinstcap(compst, IOpenCapture, Cgroup, tree->key, 0);
734
+ codegen(compst, sib1(tree), 0, tt, fullset);
735
+ addinstcap(compst, ICloseRunTime, Cclose, 0, 0);
736
+ }
737
+
738
+
739
+ /*
740
+ ** Repetion; optimizations:
741
+ ** When pattern is a charset, can use special LpegInstruction ISpan.
742
+ ** When pattern is head fail, or if it starts with characters that
743
+ ** are disjoint from what follows the repetions, a simple test
744
+ ** is enough (a fail inside the repetition would backtrack to fail
745
+ ** again in the following pattern, so there is no need for a choice).
746
+ ** When 'opt' is true, the repetion can reuse the Choice already
747
+ ** active in the stack.
748
+ */
749
+ static void coderep (CompileState *compst, TTree *tree, int opt,
750
+ const Charset *fl) {
751
+ Charset st;
752
+ if (tocharset(tree, &st)) {
753
+ addLpegInstruction(compst, ISpan, 0);
754
+ addcharset(compst, st.cs);
755
+ }
756
+ else {
757
+ int e1 = getfirst(tree, fullset, &st);
758
+ if (headfail(tree) || (!e1 && cs_disjoint(&st, fl))) {
759
+ /* L1: test (fail(p1)) -> L2; <p>; jmp L1; L2: */
760
+ int jmp;
761
+ int test = codetestset(compst, &st, 0);
762
+ codegen(compst, tree, opt, test, fullset);
763
+ jmp = addoffsetinst(compst, IJmp);
764
+ jumptohere(compst, test);
765
+ jumptothere(compst, jmp, test);
766
+ }
767
+ else {
768
+ /* test(fail(p1)) -> L2; choice L2; L1: <p>; partialcommit L1; L2: */
769
+ /* or (if 'opt'): partialcommit L1; L1: <p>; partialcommit L1; */
770
+ int commit, l2;
771
+ int test = codetestset(compst, &st, e1);
772
+ int pchoice = NOINST;
773
+ if (opt)
774
+ jumptohere(compst, addoffsetinst(compst, IPartialCommit));
775
+ else
776
+ pchoice = addoffsetinst(compst, IChoice);
777
+ l2 = gethere(compst);
778
+ codegen(compst, tree, 0, NOINST, fullset);
779
+ commit = addoffsetinst(compst, IPartialCommit);
780
+ jumptothere(compst, commit, l2);
781
+ jumptohere(compst, pchoice);
782
+ jumptohere(compst, test);
783
+ }
784
+ }
785
+ }
786
+
787
+
788
+ /*
789
+ ** Not predicate; optimizations:
790
+ ** In any case, if first test fails, 'not' succeeds, so it can jump to
791
+ ** the end. If pattern is headfail, that is all (it cannot fail
792
+ ** in other parts); this case includes 'not' of simple sets. Otherwise,
793
+ ** use the default code (a choice plus a failtwice).
794
+ */
795
+ static void codenot (CompileState *compst, TTree *tree) {
796
+ Charset st;
797
+ int e = getfirst(tree, fullset, &st);
798
+ int test = codetestset(compst, &st, e);
799
+ if (headfail(tree)) /* test (fail(p1)) -> L1; fail; L1: */
800
+ addLpegInstruction(compst, IFail, 0);
801
+ else {
802
+ /* test(fail(p))-> L1; choice L1; <p>; failtwice; L1: */
803
+ int pchoice = addoffsetinst(compst, IChoice);
804
+ codegen(compst, tree, 0, NOINST, fullset);
805
+ addLpegInstruction(compst, IFailTwice, 0);
806
+ jumptohere(compst, pchoice);
807
+ }
808
+ jumptohere(compst, test);
809
+ }
810
+
811
+
812
+ /*
813
+ ** change open calls to calls, using list 'positions' to find
814
+ ** correct offsets; also optimize tail calls
815
+ */
816
+ static void correctcalls (CompileState *compst, int *positions,
817
+ int from, int to) {
818
+ int i;
819
+ LpegInstruction *code = compst->p->code;
820
+ for (i = from; i < to; i += sizei(&code[i])) {
821
+ if (code[i].i.code == IOpenCall) {
822
+ int n = code[i].i.key; /* rule number */
823
+ int rule = positions[n]; /* rule position */
824
+ assert(rule == from || code[rule - 1].i.code == IRet);
825
+ if (code[finaltarget(code, i + 2)].i.code == IRet) /* call; ret ? */
826
+ code[i].i.code = IJmp; /* tail call */
827
+ else
828
+ code[i].i.code = ICall;
829
+ jumptothere(compst, i, rule); /* call jumps to respective rule */
830
+ }
831
+ }
832
+ assert(i == to);
833
+ }
834
+
835
+
836
+ /*
837
+ ** Code for a grammar:
838
+ ** call L1; jmp L2; L1: rule 1; ret; rule 2; ret; ...; L2:
839
+ */
840
+ static void codegrammar (CompileState *compst, TTree *grammar) {
841
+ int positions[MAXRULES];
842
+ int rulenumber = 0;
843
+ TTree *rule;
844
+ int firstcall = addoffsetinst(compst, ICall); /* call initial rule */
845
+ int jumptoend = addoffsetinst(compst, IJmp); /* jump to the end */
846
+ int start = gethere(compst); /* here starts the initial rule */
847
+ jumptohere(compst, firstcall);
848
+ for (rule = sib1(grammar); rule->tag == TRule; rule = sib2(rule)) {
849
+ positions[rulenumber++] = gethere(compst); /* save rule position */
850
+ codegen(compst, sib1(rule), 0, NOINST, fullset); /* code rule */
851
+ addLpegInstruction(compst, IRet, 0);
852
+ }
853
+ assert(rule->tag == TTrue);
854
+ jumptohere(compst, jumptoend);
855
+ correctcalls(compst, positions, start, gethere(compst));
856
+ }
857
+
858
+
859
+ static void codecall (CompileState *compst, TTree *call) {
860
+ int c = addoffsetinst(compst, IOpenCall); /* to be corrected later */
861
+ getinstr(compst, c).i.key = sib2(call)->cap; /* rule number */
862
+ assert(sib2(call)->tag == TRule);
863
+ }
864
+
865
+
866
+ /*
867
+ ** Code first child of a sequence
868
+ ** (second child is called in-place to allow tail call)
869
+ ** Return 'tt' for second child
870
+ */
871
+ static int codeseq1 (CompileState *compst, TTree *p1, TTree *p2,
872
+ int tt, const Charset *fl) {
873
+ if (needfollow(p1)) {
874
+ Charset fl1;
875
+ getfirst(p2, fl, &fl1); /* p1 follow is p2 first */
876
+ codegen(compst, p1, 0, tt, &fl1);
877
+ }
878
+ else /* use 'fullset' as follow */
879
+ codegen(compst, p1, 0, tt, fullset);
880
+ if (fixedlen(p1) != 0) /* can 'p1' consume anything? */
881
+ return NOINST; /* invalidate test */
882
+ else return tt; /* else 'tt' still protects sib2 */
883
+ }
884
+
885
+
886
+ /*
887
+ ** Main code-generation function: dispatch to auxiliar functions
888
+ ** according to kind of tree. ('needfollow' should return true
889
+ ** only for consructions that use 'fl'.)
890
+ */
891
+ static void codegen (CompileState *compst, TTree *tree, int opt, int tt,
892
+ const Charset *fl) {
893
+ tailcall:
894
+ switch (tree->tag) {
895
+ case TChar: codechar(compst, tree->u.n, tt); break;
896
+ case TAny: addLpegInstruction(compst, IAny, 0); break;
897
+ case TSet: codecharset(compst, treebuffer(tree), tt); break;
898
+ case TTrue: break;
899
+ case TFalse: addLpegInstruction(compst, IFail, 0); break;
900
+ case TChoice: codechoice(compst, sib1(tree), sib2(tree), opt, fl); break;
901
+ case TRep: coderep(compst, sib1(tree), opt, fl); break;
902
+ case TBehind: codebehind(compst, tree); break;
903
+ case TNot: codenot(compst, sib1(tree)); break;
904
+ case TAnd: codeand(compst, sib1(tree), tt); break;
905
+ case TCapture: codecapture(compst, tree, tt, fl); break;
906
+ case TRunTime: coderuntime(compst, tree, tt); break;
907
+ case TGrammar: codegrammar(compst, tree); break;
908
+ case TCall: codecall(compst, tree); break;
909
+ case TSeq: {
910
+ tt = codeseq1(compst, sib1(tree), sib2(tree), tt, fl); /* code 'p1' */
911
+ /* codegen(compst, p2, opt, tt, fl); */
912
+ tree = sib2(tree); goto tailcall;
913
+ }
914
+ default: assert(0);
915
+ }
916
+ }
917
+
918
+
919
+ /*
920
+ ** Optimize jumps and other jump-like LpegInstructions.
921
+ ** * Update labels of LpegInstructions with labels to their final
922
+ ** destinations (e.g., choice L1; ... L1: jmp L2: becomes
923
+ ** choice L2)
924
+ ** * Jumps to other LpegInstructions that do jumps become those
925
+ ** LpegInstructions (e.g., jump to return becomes a return; jump
926
+ ** to commit becomes a commit)
927
+ */
928
+ static void peephole (CompileState *compst) {
929
+ LpegInstruction *code = compst->p->code;
930
+ int i;
931
+ for (i = 0; i < compst->ncode; i += sizei(&code[i])) {
932
+ redo:
933
+ switch (code[i].i.code) {
934
+ case IChoice: case ICall: case ICommit: case IPartialCommit:
935
+ case IBackCommit: case ITestChar: case ITestSet:
936
+ case ITestAny: { /* LpegInstructions with labels */
937
+ jumptothere(compst, i, finallabel(code, i)); /* optimize label */
938
+ break;
939
+ }
940
+ case IJmp: {
941
+ int ft = finaltarget(code, i);
942
+ switch (code[ft].i.code) { /* jumping to what? */
943
+ case IRet: case IFail: case IFailTwice:
944
+ case IEnd: { /* LpegInstructions with unconditional implicit jumps */
945
+ code[i] = code[ft]; /* jump becomes that LpegInstruction */
946
+ code[i + 1].i.code = IAny; /* 'no-op' for target position */
947
+ break;
948
+ }
949
+ case ICommit: case IPartialCommit:
950
+ case IBackCommit: { /* inst. with unconditional explicit jumps */
951
+ int fft = finallabel(code, ft);
952
+ code[i] = code[ft]; /* jump becomes that LpegInstruction... */
953
+ jumptothere(compst, i, fft); /* but must correct its offset */
954
+ goto redo; /* reoptimize its label */
955
+ }
956
+ default: {
957
+ jumptothere(compst, i, ft); /* optimize label */
958
+ break;
959
+ }
960
+ }
961
+ break;
962
+ }
963
+ default: break;
964
+ }
965
+ }
966
+ assert(code[i - 1].i.code == IEnd);
967
+ }
968
+
969
+
970
+ /*
971
+ ** Compile a pattern
972
+ */
973
+ LpegInstruction *compile (lua_State *L, Pattern *p) {
974
+ CompileState compst;
975
+ compst.p = p; compst.ncode = 0; compst.L = L;
976
+ realloccode(L, p, 2); /* minimum initial size */
977
+ codegen(&compst, p->tree, 0, NOINST, fullset);
978
+ addLpegInstruction(&compst, IEnd, 0);
979
+ realloccode(L, p, compst.ncode); /* set final size */
980
+ peephole(&compst);
981
+ return p->code;
982
+ }
983
+
984
+
985
+ /* }====================================================== */
986
+