Copyright update to 2015
[supertux.git] / external / squirrel / sqstdlib / sqstdrex.cpp
1 /* see copyright notice in squirrel.h */\r
2 #include <squirrel.h>\r
3 #include <string.h>\r
4 #include <ctype.h>\r
5 #include <setjmp.h>\r
6 #include <sqstdstring.h>\r
7 \r
8 #ifdef _UINCODE\r
9 #define scisprint iswprint\r
10 #else\r
11 #define scisprint isprint\r
12 #endif\r
13 \r
14 #ifdef _DEBUG\r
15 #include <stdio.h>\r
16 \r
17 static const SQChar *g_nnames[] =\r
18 {\r
19         _SC("NONE"),_SC("OP_GREEDY"),   _SC("OP_OR"),\r
20         _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),       _SC("OP_CLASS"),\r
21         _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),\r
22         _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")\r
23 };\r
24 \r
25 #endif\r
26 \r
27 #define OP_GREEDY               (MAX_CHAR+1) // * + ? {n}\r
28 #define OP_OR                   (MAX_CHAR+2)\r
29 #define OP_EXPR                 (MAX_CHAR+3) //parentesis ()\r
30 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)\r
31 #define OP_DOT                  (MAX_CHAR+5)\r
32 #define OP_CLASS                (MAX_CHAR+6)\r
33 #define OP_CCLASS               (MAX_CHAR+7)\r
34 #define OP_NCLASS               (MAX_CHAR+8) //negates class the [^\r
35 #define OP_RANGE                (MAX_CHAR+9)\r
36 #define OP_CHAR                 (MAX_CHAR+10)\r
37 #define OP_EOL                  (MAX_CHAR+11)\r
38 #define OP_BOL                  (MAX_CHAR+12)\r
39 #define OP_WB                   (MAX_CHAR+13)\r
40 \r
41 #define SQREX_SYMBOL_ANY_CHAR ('.')\r
42 #define SQREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')\r
43 #define SQREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')\r
44 #define SQREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')\r
45 #define SQREX_SYMBOL_BRANCH ('|')\r
46 #define SQREX_SYMBOL_END_OF_STRING ('$')\r
47 #define SQREX_SYMBOL_BEGINNING_OF_STRING ('^')\r
48 #define SQREX_SYMBOL_ESCAPE_CHAR ('\\')\r
49 \r
50 \r
51 typedef int SQRexNodeType;\r
52 \r
53 typedef struct tagSQRexNode{\r
54         SQRexNodeType type;\r
55         SQInteger left;\r
56         SQInteger right;\r
57         SQInteger next;\r
58 }SQRexNode;\r
59 \r
60 struct SQRex{\r
61         const SQChar *_eol;\r
62         const SQChar *_bol;\r
63         const SQChar *_p;\r
64         SQInteger _first;\r
65         SQInteger _op;\r
66         SQRexNode *_nodes;\r
67         SQInteger _nallocated;\r
68         SQInteger _nsize;\r
69         SQInteger _nsubexpr;\r
70         SQRexMatch *_matches;\r
71         SQInteger _currsubexp;\r
72         void *_jmpbuf;\r
73         const SQChar **_error;\r
74 };\r
75 \r
76 static SQInteger sqstd_rex_list(SQRex *exp);\r
77 \r
78 static SQInteger sqstd_rex_newnode(SQRex *exp, SQRexNodeType type)\r
79 {\r
80         SQRexNode n;\r
81         n.type = type;\r
82         n.next = n.right = n.left = -1;\r
83         if(type == OP_EXPR)\r
84                 n.right = exp->_nsubexpr++;\r
85         if(exp->_nallocated < (exp->_nsize + 1)) {\r
86                 SQInteger oldsize = exp->_nallocated;\r
87                 exp->_nallocated *= 2;\r
88                 exp->_nodes = (SQRexNode *)sq_realloc(exp->_nodes, oldsize * sizeof(SQRexNode) ,exp->_nallocated * sizeof(SQRexNode));\r
89         }\r
90         exp->_nodes[exp->_nsize++] = n;\r
91         SQInteger newid = exp->_nsize - 1;\r
92         return (SQInteger)newid;\r
93 }\r
94 \r
95 static void sqstd_rex_error(SQRex *exp,const SQChar *error)\r
96 {\r
97         if(exp->_error) *exp->_error = error;\r
98         longjmp(*((jmp_buf*)exp->_jmpbuf),-1);\r
99 }\r
100 \r
101 static void sqstd_rex_expect(SQRex *exp, SQInteger n){\r
102         if((*exp->_p) != n) \r
103                 sqstd_rex_error(exp, _SC("expected paren"));\r
104         exp->_p++;\r
105 }\r
106 \r
107 static SQChar sqstd_rex_escapechar(SQRex *exp)\r
108 {\r
109         if(*exp->_p == SQREX_SYMBOL_ESCAPE_CHAR){\r
110                 exp->_p++;\r
111                 switch(*exp->_p) {\r
112                 case 'v': exp->_p++; return '\v';\r
113                 case 'n': exp->_p++; return '\n';\r
114                 case 't': exp->_p++; return '\t';\r
115                 case 'r': exp->_p++; return '\r';\r
116                 case 'f': exp->_p++; return '\f';\r
117                 default: return (*exp->_p++);\r
118                 }\r
119         } else if(!scisprint(*exp->_p)) sqstd_rex_error(exp,_SC("letter expected"));\r
120         return (*exp->_p++);\r
121 }\r
122 \r
123 static SQInteger sqstd_rex_charclass(SQRex *exp,SQInteger classid)\r
124 {\r
125         SQInteger n = sqstd_rex_newnode(exp,OP_CCLASS);\r
126         exp->_nodes[n].left = classid;\r
127         return n;\r
128 }\r
129 \r
130 static SQInteger sqstd_rex_charnode(SQRex *exp,SQBool isclass)\r
131 {\r
132         SQChar t;\r
133         if(*exp->_p == SQREX_SYMBOL_ESCAPE_CHAR) {\r
134                 exp->_p++;\r
135                 switch(*exp->_p) {\r
136                         case 'n': exp->_p++; return sqstd_rex_newnode(exp,'\n');\r
137                         case 't': exp->_p++; return sqstd_rex_newnode(exp,'\t');\r
138                         case 'r': exp->_p++; return sqstd_rex_newnode(exp,'\r');\r
139                         case 'f': exp->_p++; return sqstd_rex_newnode(exp,'\f');\r
140                         case 'v': exp->_p++; return sqstd_rex_newnode(exp,'\v');\r
141                         case 'a': case 'A': case 'w': case 'W': case 's': case 'S': \r
142                         case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': \r
143                         case 'p': case 'P': case 'l': case 'u': \r
144                                 {\r
145                                 t = *exp->_p; exp->_p++; \r
146                                 return sqstd_rex_charclass(exp,t);\r
147                                 }\r
148                         case 'b': \r
149                         case 'B':\r
150                                 if(!isclass) {\r
151                                         SQInteger node = sqstd_rex_newnode(exp,OP_WB);\r
152                                         exp->_nodes[node].left = *exp->_p;\r
153                                         exp->_p++; \r
154                                         return node;\r
155                                 } //else default\r
156                         default: \r
157                                 t = *exp->_p; exp->_p++; \r
158                                 return sqstd_rex_newnode(exp,t);\r
159                 }\r
160         }\r
161         else if(!scisprint(*exp->_p)) {\r
162                 \r
163                 sqstd_rex_error(exp,_SC("letter expected"));\r
164         }\r
165         t = *exp->_p; exp->_p++; \r
166         return sqstd_rex_newnode(exp,t);\r
167 }\r
168 static SQInteger sqstd_rex_class(SQRex *exp)\r
169 {\r
170         SQInteger ret = -1;\r
171         SQInteger first = -1,chain;\r
172         if(*exp->_p == SQREX_SYMBOL_BEGINNING_OF_STRING){\r
173                 ret = sqstd_rex_newnode(exp,OP_NCLASS);\r
174                 exp->_p++;\r
175         }else ret = sqstd_rex_newnode(exp,OP_CLASS);\r
176         \r
177         if(*exp->_p == ']') sqstd_rex_error(exp,_SC("empty class"));\r
178         chain = ret;\r
179         while(*exp->_p != ']' && exp->_p != exp->_eol) {\r
180                 if(*exp->_p == '-' && first != -1){ \r
181                         SQInteger r;\r
182                         if(*exp->_p++ == ']') sqstd_rex_error(exp,_SC("unfinished range"));\r
183                         r = sqstd_rex_newnode(exp,OP_RANGE);\r
184                         if(exp->_nodes[first].type>*exp->_p) sqstd_rex_error(exp,_SC("invalid range"));\r
185                         if(exp->_nodes[first].type == OP_CCLASS) sqstd_rex_error(exp,_SC("cannot use character classes in ranges"));\r
186                         exp->_nodes[r].left = exp->_nodes[first].type;\r
187                         SQInteger t = sqstd_rex_escapechar(exp);\r
188                         exp->_nodes[r].right = t;\r
189             exp->_nodes[chain].next = r;\r
190                         chain = r;\r
191                         first = -1;\r
192                 }\r
193                 else{\r
194                         if(first!=-1){\r
195                                 SQInteger c = first;\r
196                                 exp->_nodes[chain].next = c;\r
197                                 chain = c;\r
198                                 first = sqstd_rex_charnode(exp,SQTrue);\r
199                         }\r
200                         else{\r
201                                 first = sqstd_rex_charnode(exp,SQTrue);\r
202                         }\r
203                 }\r
204         }\r
205         if(first!=-1){\r
206                 SQInteger c = first;\r
207                 exp->_nodes[chain].next = c;\r
208                 chain = c;\r
209                 first = -1;\r
210         }\r
211         /* hack? */\r
212         exp->_nodes[ret].left = exp->_nodes[ret].next;\r
213         exp->_nodes[ret].next = -1;\r
214         return ret;\r
215 }\r
216 \r
217 static SQInteger sqstd_rex_parsenumber(SQRex *exp)\r
218 {\r
219         SQInteger ret = *exp->_p-'0';\r
220         SQInteger positions = 10;\r
221         exp->_p++;\r
222         while(isdigit(*exp->_p)) {\r
223                 ret = ret*10+(*exp->_p++-'0');\r
224                 if(positions==1000000000) sqstd_rex_error(exp,_SC("overflow in numeric constant"));\r
225                 positions *= 10;\r
226         };\r
227         return ret;\r
228 }\r
229 \r
230 static SQInteger sqstd_rex_element(SQRex *exp)\r
231 {\r
232         SQInteger ret = -1;\r
233         switch(*exp->_p)\r
234         {\r
235         case '(': {\r
236                 SQInteger expr;\r
237                 exp->_p++;\r
238 \r
239 \r
240                 if(*exp->_p =='?') {\r
241                         exp->_p++;\r
242                         sqstd_rex_expect(exp,':');\r
243                         expr = sqstd_rex_newnode(exp,OP_NOCAPEXPR);\r
244                 }\r
245                 else\r
246                         expr = sqstd_rex_newnode(exp,OP_EXPR);\r
247                 SQInteger newn = sqstd_rex_list(exp);\r
248                 exp->_nodes[expr].left = newn;\r
249                 ret = expr;\r
250                 sqstd_rex_expect(exp,')');\r
251                           }\r
252                           break;\r
253         case '[':\r
254                 exp->_p++;\r
255                 ret = sqstd_rex_class(exp);\r
256                 sqstd_rex_expect(exp,']');\r
257                 break;\r
258         case SQREX_SYMBOL_END_OF_STRING: exp->_p++; ret = sqstd_rex_newnode(exp,OP_EOL);break;\r
259         case SQREX_SYMBOL_ANY_CHAR: exp->_p++; ret = sqstd_rex_newnode(exp,OP_DOT);break;\r
260         default:\r
261                 ret = sqstd_rex_charnode(exp,SQFalse);\r
262                 break;\r
263         }\r
264 \r
265 \r
266         SQBool isgreedy = SQFalse;\r
267         unsigned short p0 = 0, p1 = 0;\r
268         switch(*exp->_p){\r
269                 case SQREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = SQTrue; break;\r
270                 case SQREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = SQTrue; break;\r
271                 case SQREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = SQTrue; break;\r
272                 case '{':\r
273                         exp->_p++;\r
274                         if(!isdigit(*exp->_p)) sqstd_rex_error(exp,_SC("number expected"));\r
275                         p0 = (unsigned short)sqstd_rex_parsenumber(exp);\r
276                         /*******************************/\r
277                         switch(*exp->_p) {\r
278                 case '}':\r
279                         p1 = p0; exp->_p++;\r
280                         break;\r
281                 case ',':\r
282                         exp->_p++;\r
283                         p1 = 0xFFFF;\r
284                         if(isdigit(*exp->_p)){\r
285                                 p1 = (unsigned short)sqstd_rex_parsenumber(exp);\r
286                         }\r
287                         sqstd_rex_expect(exp,'}');\r
288                         break;\r
289                 default:\r
290                         sqstd_rex_error(exp,_SC(", or } expected"));\r
291                         }\r
292                         /*******************************/\r
293                         isgreedy = SQTrue; \r
294                         break;\r
295 \r
296         }\r
297         if(isgreedy) {\r
298                 SQInteger nnode = sqstd_rex_newnode(exp,OP_GREEDY);\r
299                 exp->_nodes[nnode].left = ret;\r
300                 exp->_nodes[nnode].right = ((p0)<<16)|p1;\r
301                 ret = nnode;\r
302         }\r
303 \r
304         if((*exp->_p != SQREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != SQREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != SQREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {\r
305                 SQInteger nnode = sqstd_rex_element(exp);\r
306                 exp->_nodes[ret].next = nnode;\r
307         }\r
308 \r
309         return ret;\r
310 }\r
311 \r
312 static SQInteger sqstd_rex_list(SQRex *exp)\r
313 {\r
314         SQInteger ret=-1,e;\r
315         if(*exp->_p == SQREX_SYMBOL_BEGINNING_OF_STRING) {\r
316                 exp->_p++;\r
317                 ret = sqstd_rex_newnode(exp,OP_BOL);\r
318         }\r
319         e = sqstd_rex_element(exp);\r
320         if(ret != -1) {\r
321                 exp->_nodes[ret].next = e;\r
322         }\r
323         else ret = e;\r
324 \r
325         if(*exp->_p == SQREX_SYMBOL_BRANCH) {\r
326                 SQInteger temp,tright;\r
327                 exp->_p++;\r
328                 temp = sqstd_rex_newnode(exp,OP_OR);\r
329                 exp->_nodes[temp].left = ret;\r
330                 tright = sqstd_rex_list(exp);\r
331                 exp->_nodes[temp].right = tright;\r
332                 ret = temp;\r
333         }\r
334         return ret;\r
335 }\r
336 \r
337 static SQBool sqstd_rex_matchcclass(SQInteger cclass,SQChar c)\r
338 {\r
339         switch(cclass) {\r
340         case 'a': return isalpha(c)?SQTrue:SQFalse;\r
341         case 'A': return !isalpha(c)?SQTrue:SQFalse;\r
342         case 'w': return (isalnum(c) || c == '_')?SQTrue:SQFalse;\r
343         case 'W': return (!isalnum(c) && c != '_')?SQTrue:SQFalse;\r
344         case 's': return isspace(c)?SQTrue:SQFalse;\r
345         case 'S': return !isspace(c)?SQTrue:SQFalse;\r
346         case 'd': return isdigit(c)?SQTrue:SQFalse;\r
347         case 'D': return !isdigit(c)?SQTrue:SQFalse;\r
348         case 'x': return isxdigit(c)?SQTrue:SQFalse;\r
349         case 'X': return !isxdigit(c)?SQTrue:SQFalse;\r
350         case 'c': return iscntrl(c)?SQTrue:SQFalse;\r
351         case 'C': return !iscntrl(c)?SQTrue:SQFalse;\r
352         case 'p': return ispunct(c)?SQTrue:SQFalse;\r
353         case 'P': return !ispunct(c)?SQTrue:SQFalse;\r
354         case 'l': return islower(c)?SQTrue:SQFalse;\r
355         case 'u': return isupper(c)?SQTrue:SQFalse;\r
356         }\r
357         return SQFalse; /*cannot happen*/\r
358 }\r
359 \r
360 static SQBool sqstd_rex_matchclass(SQRex* exp,SQRexNode *node,SQChar c)\r
361 {\r
362         do {\r
363                 switch(node->type) {\r
364                         case OP_RANGE:\r
365                                 if(c >= node->left && c <= node->right) return SQTrue;\r
366                                 break;\r
367                         case OP_CCLASS:\r
368                                 if(sqstd_rex_matchcclass(node->left,c)) return SQTrue;\r
369                                 break;\r
370                         default:\r
371                                 if(c == node->type)return SQTrue;\r
372                 }\r
373         } while((node->next != -1) && (node = &exp->_nodes[node->next]));\r
374         return SQFalse;\r
375 }\r
376 \r
377 static const SQChar *sqstd_rex_matchnode(SQRex* exp,SQRexNode *node,const SQChar *str,SQRexNode *next)\r
378 {\r
379         \r
380         SQRexNodeType type = node->type;\r
381         switch(type) {\r
382         case OP_GREEDY: {\r
383                 //SQRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;\r
384                 SQRexNode *greedystop = NULL;\r
385                 SQInteger p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;\r
386                 const SQChar *s=str, *good = str;\r
387 \r
388                 if(node->next != -1) {\r
389                         greedystop = &exp->_nodes[node->next];\r
390                 }\r
391                 else {\r
392                         greedystop = next;\r
393                 }\r
394 \r
395                 while((nmaches == 0xFFFF || nmaches < p1)) {\r
396 \r
397                         const SQChar *stop;\r
398                         if(!(s = sqstd_rex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))\r
399                                 break;\r
400                         nmaches++;\r
401                         good=s;\r
402                         if(greedystop) {\r
403                                 //checks that 0 matches satisfy the expression(if so skips)\r
404                                 //if not would always stop(for instance if is a '?')\r
405                                 if(greedystop->type != OP_GREEDY ||\r
406                                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))\r
407                                 {\r
408                                         SQRexNode *gnext = NULL;\r
409                                         if(greedystop->next != -1) {\r
410                                                 gnext = &exp->_nodes[greedystop->next];\r
411                                         }else if(next && next->next != -1){\r
412                                                 gnext = &exp->_nodes[next->next];\r
413                                         }\r
414                                         stop = sqstd_rex_matchnode(exp,greedystop,s,gnext);\r
415                                         if(stop) {\r
416                                                 //if satisfied stop it\r
417                                                 if(p0 == p1 && p0 == nmaches) break;\r
418                                                 else if(nmaches >= p0 && p1 == 0xFFFF) break;\r
419                                                 else if(nmaches >= p0 && nmaches <= p1) break;\r
420                                         }\r
421                                 }\r
422                         }\r
423                         \r
424                         if(s >= exp->_eol)\r
425                                 break;\r
426                 }\r
427                 if(p0 == p1 && p0 == nmaches) return good;\r
428                 else if(nmaches >= p0 && p1 == 0xFFFF) return good;\r
429                 else if(nmaches >= p0 && nmaches <= p1) return good;\r
430                 return NULL;\r
431         }\r
432         case OP_OR: {\r
433                         const SQChar *asd = str;\r
434                         SQRexNode *temp=&exp->_nodes[node->left];\r
435                         while( (asd = sqstd_rex_matchnode(exp,temp,asd,NULL)) ) {\r
436                                 if(temp->next != -1)\r
437                                         temp = &exp->_nodes[temp->next];\r
438                                 else\r
439                                         return asd;\r
440                         }\r
441                         asd = str;\r
442                         temp = &exp->_nodes[node->right];\r
443                         while( (asd = sqstd_rex_matchnode(exp,temp,asd,NULL)) ) {\r
444                                 if(temp->next != -1)\r
445                                         temp = &exp->_nodes[temp->next];\r
446                                 else\r
447                                         return asd;\r
448                         }\r
449                         return NULL;\r
450                         break;\r
451         }\r
452         case OP_EXPR:\r
453         case OP_NOCAPEXPR:{\r
454                         SQRexNode *n = &exp->_nodes[node->left];\r
455                         const SQChar *cur = str;\r
456                         SQInteger capture = -1;\r
457                         if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {\r
458                                 capture = exp->_currsubexp;\r
459                                 exp->_matches[capture].begin = cur;\r
460                                 exp->_currsubexp++;\r
461                         }\r
462                         int tempcap = exp->_currsubexp;\r
463                         do {\r
464                                 SQRexNode *subnext = NULL;\r
465                                 if(n->next != -1) {\r
466                                         subnext = &exp->_nodes[n->next];\r
467                                 }else {\r
468                                         subnext = next;\r
469                                 }\r
470                                 if(!(cur = sqstd_rex_matchnode(exp,n,cur,subnext))) {\r
471                                         if(capture != -1){\r
472                                                 exp->_matches[capture].begin = 0;\r
473                                                 exp->_matches[capture].len = 0;\r
474                                         }\r
475                                         return NULL;\r
476                                 }\r
477                         } while((n->next != -1) && (n = &exp->_nodes[n->next]));\r
478 \r
479                         exp->_currsubexp = tempcap;\r
480                         if(capture != -1) \r
481                                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;\r
482                         return cur;\r
483         }                                \r
484         case OP_WB:\r
485                 if((str == exp->_bol && !isspace(*str))\r
486                  || (str == exp->_eol && !isspace(*(str-1)))\r
487                  || (!isspace(*str) && isspace(*(str+1)))\r
488                  || (isspace(*str) && !isspace(*(str+1))) ) {\r
489                         return (node->left == 'b')?str:NULL;\r
490                 }\r
491                 return (node->left == 'b')?NULL:str;\r
492         case OP_BOL:\r
493                 if(str == exp->_bol) return str;\r
494                 return NULL;\r
495         case OP_EOL:\r
496                 if(str == exp->_eol) return str;\r
497                 return NULL;\r
498         case OP_DOT:{\r
499                 str++;\r
500                                 }\r
501                 return str;\r
502         case OP_NCLASS:\r
503         case OP_CLASS:\r
504                 if(sqstd_rex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?SQTrue:SQFalse):(type == OP_NCLASS?SQTrue:SQFalse)) {\r
505                         str++;\r
506                         return str;\r
507                 }\r
508                 return NULL;\r
509         case OP_CCLASS:\r
510                 if(sqstd_rex_matchcclass(node->left,*str)) {\r
511                         str++;\r
512                         return str;\r
513                 }\r
514                 return NULL;\r
515         default: /* char */\r
516                 if(*str != node->type) return NULL;\r
517                 str++;\r
518                 return str;\r
519         }\r
520         return NULL;\r
521 }\r
522 \r
523 /* public api */\r
524 SQRex *sqstd_rex_compile(const SQChar *pattern,const SQChar **error)\r
525 {\r
526         SQRex *exp = (SQRex *)sq_malloc(sizeof(SQRex));\r
527         exp->_eol = exp->_bol = NULL;\r
528         exp->_p = pattern;\r
529         exp->_nallocated = (SQInteger)scstrlen(pattern) * sizeof(SQChar);\r
530         exp->_nodes = (SQRexNode *)sq_malloc(exp->_nallocated * sizeof(SQRexNode));\r
531         exp->_nsize = 0;\r
532         exp->_matches = 0;\r
533         exp->_nsubexpr = 0;\r
534         exp->_first = sqstd_rex_newnode(exp,OP_EXPR);\r
535         exp->_error = error;\r
536         exp->_jmpbuf = sq_malloc(sizeof(jmp_buf));\r
537         if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {\r
538                 SQInteger res = sqstd_rex_list(exp);\r
539                 exp->_nodes[exp->_first].left = res;\r
540                 if(*exp->_p!='\0')\r
541                         sqstd_rex_error(exp,_SC("unexpected character"));\r
542 #ifdef _DEBUG\r
543                 {\r
544                         SQInteger nsize,i;\r
545                         SQRexNode *t;\r
546                         nsize = exp->_nsize;\r
547                         t = &exp->_nodes[0];\r
548                         scprintf(_SC("\n"));\r
549                         for(i = 0;i < nsize; i++) {\r
550                                 if(exp->_nodes[i].type>MAX_CHAR)\r
551                                         scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);\r
552                                 else\r
553                                         scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);\r
554                                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);\r
555                         }\r
556                         scprintf(_SC("\n"));\r
557                 }\r
558 #endif\r
559                 exp->_matches = (SQRexMatch *) sq_malloc(exp->_nsubexpr * sizeof(SQRexMatch));\r
560                 memset(exp->_matches,0,exp->_nsubexpr * sizeof(SQRexMatch));\r
561         }\r
562         else{\r
563                 sqstd_rex_free(exp);\r
564                 return NULL;\r
565         }\r
566         return exp;\r
567 }\r
568 \r
569 void sqstd_rex_free(SQRex *exp)\r
570 {\r
571         if(exp) {\r
572                 if(exp->_nodes) sq_free(exp->_nodes,exp->_nallocated * sizeof(SQRexNode));\r
573                 if(exp->_jmpbuf) sq_free(exp->_jmpbuf,sizeof(jmp_buf));\r
574                 if(exp->_matches) sq_free(exp->_matches,exp->_nsubexpr * sizeof(SQRexMatch));\r
575                 sq_free(exp,sizeof(SQRex));\r
576         }\r
577 }\r
578 \r
579 SQBool sqstd_rex_match(SQRex* exp,const SQChar* text)\r
580 {\r
581         const SQChar* res = NULL;\r
582         exp->_bol = text;\r
583         exp->_eol = text + scstrlen(text);\r
584         exp->_currsubexp = 0;\r
585         res = sqstd_rex_matchnode(exp,exp->_nodes,text,NULL);\r
586         if(res == NULL || res != exp->_eol)\r
587                 return SQFalse;\r
588         return SQTrue;\r
589 }\r
590 \r
591 SQBool sqstd_rex_searchrange(SQRex* exp,const SQChar* text_begin,const SQChar* text_end,const SQChar** out_begin, const SQChar** out_end)\r
592 {\r
593         const SQChar *cur = NULL;\r
594         SQInteger node = exp->_first;\r
595         if(text_begin >= text_end) return SQFalse;\r
596         exp->_bol = text_begin;\r
597         exp->_eol = text_end;\r
598         do {\r
599                 cur = text_begin;\r
600                 while(node != -1) {\r
601                         exp->_currsubexp = 0;\r
602                         cur = sqstd_rex_matchnode(exp,&exp->_nodes[node],cur,NULL);\r
603                         if(!cur)\r
604                                 break;\r
605                         node = exp->_nodes[node].next;\r
606                 }\r
607                 text_begin++;\r
608         } while(cur == NULL && text_begin != text_end);\r
609 \r
610         if(cur == NULL)\r
611                 return SQFalse;\r
612 \r
613         --text_begin;\r
614 \r
615         if(out_begin) *out_begin = text_begin;\r
616         if(out_end) *out_end = cur;\r
617         return SQTrue;\r
618 }\r
619 \r
620 SQBool sqstd_rex_search(SQRex* exp,const SQChar* text, const SQChar** out_begin, const SQChar** out_end)\r
621 {\r
622         return sqstd_rex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);\r
623 }\r
624 \r
625 SQInteger sqstd_rex_getsubexpcount(SQRex* exp)\r
626 {\r
627         return exp->_nsubexpr;\r
628 }\r
629 \r
630 SQBool sqstd_rex_getsubexp(SQRex* exp, SQInteger n, SQRexMatch *subexp)\r
631 {\r
632         if( n<0 || n >= exp->_nsubexpr) return SQFalse;\r
633         *subexp = exp->_matches[n];\r
634         return SQTrue;\r
635 }\r
636 \r