PageRenderTime 164ms CodeModel.GetById 36ms app.highlight 116ms RepoModel.GetById 1ms app.codeStats 0ms

/core/regex.d

http://github.com/wilkie/djehuty
D | 1702 lines | 1425 code | 153 blank | 124 comment | 256 complexity | 72824d970fe155d6a80b7f8454cf27e5 MD5 | raw file
   1/*
   2 * regex.d
   3 *
   4 * This file contains the logic behind a regular expression parser.
   5 *
   6 * Author: Dave Wilkinson
   7 * Originated: May 9th, 2009
   8 * Inspiration: "Albino 2" by Mark Knight
   9 *
  10 */
  11
  12module core.regex;
  13
  14import core.string;
  15import core.definitions;
  16
  17import synch.thread;
  18
  19import io.console;
  20
  21import data.stack;
  22import data.list;
  23
  24// This provides thread-local access to regex variables set via
  25// Regex groups.
  26
  27uint _position() {
  28	if (Thread.current() in Regex.regexPos) {
  29		return Regex.regexPos[Thread.current()];
  30	}
  31
  32	return uint.max;
  33}
  34
  35string _1() {
  36	if (Thread.current() in Regex.regexRefs) {
  37		return Regex.regexRefs[Thread.current()][0];
  38	}
  39
  40	return ("");
  41}
  42
  43string _2() {
  44	if (Thread.current() in Regex.regexRefs) {
  45		return Regex.regexRefs[Thread.current()][1];
  46	}
  47
  48	return ("");
  49}
  50
  51string _3() {
  52	if (Thread.current() in Regex.regexRefs) {
  53		return Regex.regexRefs[Thread.current()][2];
  54	}
  55
  56	return ("");
  57}
  58
  59string _4() {
  60	if (Thread.current() in Regex.regexRefs) {
  61		return Regex.regexRefs[Thread.current()][3];
  62	}
  63
  64	return ("");
  65}
  66
  67string _5() {
  68	if (Thread.current() in Regex.regexRefs) {
  69		return Regex.regexRefs[Thread.current()][4];
  70	}
  71
  72	return ("");
  73}
  74
  75string _6() {
  76	if (Thread.current() in Regex.regexRefs) {
  77		return Regex.regexRefs[Thread.current()][5];
  78	}
  79
  80	return ("");
  81}
  82
  83string _7() {
  84	if (Thread.current() in Regex.regexRefs) {
  85		return Regex.regexRefs[Thread.current()][6];
  86	}
  87
  88	return ("");
  89}
  90
  91string _8() {
  92	if (Thread.current() in Regex.regexRefs) {
  93		return Regex.regexRefs[Thread.current()][7];
  94	}
  95
  96	return ("");
  97}
  98
  99string _9() {
 100	if (Thread.current() in Regex.regexRefs) {
 101		return Regex.regexRefs[Thread.current()][8];
 102	}
 103
 104	return ("");
 105}
 106
 107class Regex {
 108
 109	// Description: This constructor will create an instance of a Regex that will efficiently compute the regular expression given.
 110	// regex: The regular expression to utilize.
 111	this(string regex) {
 112		regularExpression = (regex);
 113
 114		buildDFA(false);
 115	}
 116
 117	// Description: This function will return a matched regular expression on the given string. Single use regular expression functions, such as this one, use a backtracking algorithm.
 118	// str: The string to run the regular expression upon.
 119	// regex: The regular expression to use.
 120	// Returns: The matched substring or null when no match could be found.
 121	static string eval(string str, string regex, string options = "") {
 122		RegexInfo regexInfo;
 123		
 124		/*
 125		static RegexInfo[string] oldRuns;
 126
 127		string oldRunIndex = (regex.tostring() ~ "_" ~ options);
 128		if (oldRunIndex in oldRuns) {
 129			regexInfo = oldRuns[oldRunIndex];
 130		}*/
 131
 132		regexInfo.memoizer = new int[][](str.length, regex.length);
 133
 134		int strPos;
 135		int regexPos;
 136
 137		int currentGroupIdx = -1;
 138
 139		int strPosStart;
 140		int regexPosStart;
 141		int regexGroupStart = int.max;
 142		int regexFlagPotential;
 143
 144		int nextUnionPos = -1;
 145		int currentUnionPos = -1;
 146
 147		int currentClassStart;
 148
 149		int groupCount;
 150
 151		int flags;
 152
 153		const int PLUS_OK = 1;
 154		const int LAZY_KLEENE = 2;
 155		const int KLEENE_MATCHED = 4;
 156
 157		bool multiline;
 158
 159		foreach(chr; options) {
 160			switch(chr) {
 161				case 'm':
 162					multiline = true;
 163					break;
 164				default:
 165					break;
 166			}
 167		}
 168
 169		// This is a stack of the groupings currently in context.
 170		Stack!(int) groupStart = new Stack!(int)();
 171		Stack!(int) stack = new Stack!(int)();
 172
 173		// Running flags
 174		bool running = true;
 175		bool matchMade = true;
 176		bool backtrack = false;
 177		bool noMatch = false;
 178		bool matchClass = false;
 179		bool matchInverse = false;
 180		bool matchRange = false;
 181
 182		bool noMatchClass = false;
 183
 184		bool backtrackedOnCaret = false;
 185
 186		regexRefs[Thread.current()] = new string [](9);
 187
 188		// Suppresses group matching until a position is reached.
 189		int noMatchUntilClosedAtPos = -1;
 190		int noMatchUntilUnionForPos = -1;
 191
 192		// This function will set a backtracking point in the regex.
 193		void setBacktrack(int newRegexPos, int newStrPos) {
 194			stack.push(newRegexPos);
 195			stack.push(newStrPos);
 196			stack.push(regexGroupStart);
 197			stack.push(currentGroupIdx);
 198			stack.push(regexFlagPotential);
 199		}
 200
 201		// This function finds the regex position that will undo the last move.
 202		int findBackupRegexPosition() {
 203			int ret = regexPos - 1;
 204			if (ret in regexInfo.groupInfo) {
 205				ret = regexInfo.groupInfo[ret].startPos;
 206			}
 207			else if (ret < regex.length && regex[ret] == ']' && ret in regexInfo.operatorFlag) {
 208				ret = regexInfo.operatorFlag[ret];
 209			}
 210			else {
 211				if (ret > 0 && regex[ret-1] == '\\') {
 212					ret--;
 213				}
 214			}
 215			return ret;
 216		}
 217
 218		// Like above, but for the working position.
 219		int findBackupPosition() {
 220			if (regexPos-1 in regexInfo.groupInfo) {
 221				return regexInfo.groupInfo[regexInfo.groupInfo[regexPos-1].startPos].strStartPos;
 222			}
 223			else {
 224				return strPos-1;
 225			}
 226		}
 227
 228		// Set a backtrack that will return to the front of both strings.
 229		setBacktrack(0,0);
 230
 231		// Alright, main loop! This won't be broken until either a match is
 232		// found or nothing can be found.
 233		while(running) {
 234
 235			// This is the mechanics for the memoizer. If a valid match has
 236			// been made and the regex positions are valid, set this position
 237			// pair in the memoizer indicating we've done this work before.
 238			if (strPos < str.length && regexPos < regex.length && matchMade && !noMatch) {
 239				if (regexInfo.memoizer[strPos][regexPos] == 1) {
 240					// we have been here before
 241					backtrack = true;
 242				}
 243				else {
 244					regexInfo.memoizer[strPos][regexPos] = 1;
 245				}
 246			}
 247
 248			// If we are meant to backtrack this turn, this code path is taken.
 249			if (backtrack) {
 250				// steps are saved after successful matches
 251				// therefore the matchMade flag is always set
 252				matchMade = true;
 253
 254				int oldRegexPos = regexPos;
 255
 256				regexFlagPotential = stack.pop();
 257				currentGroupIdx = stack.pop();
 258				regexGroupStart = stack.pop();
 259				strPos = stack.pop();
 260				regexPos = stack.pop();
 261
 262				if (regexPos == 0) {
 263					// We have gone back to the beginning...
 264
 265					// we could attempt to find a union
 266					noMatch = true;
 267					noMatchUntilClosedAtPos = -1;
 268					noMatchUntilUnionForPos = -1;
 269
 270					regexPos = oldRegexPos;
 271				}
 272
 273				// OMG; Do not want to backtrack twice!
 274				backtrack = false;
 275			}
 276
 277			if (regexPos >= regex.length) {
 278				// The regex has been consumed.
 279
 280				if (noMatch) {
 281					if (noMatchUntilClosedAtPos == -1) {
 282
 283						// No union, so just start the regex at the next character in the string.
 284
 285						// UNLESS the backtrack happened at a 'caret' character in the regex
 286						if (backtrackedOnCaret) {
 287							matchMade = false;
 288							running = false;
 289							break;
 290						}
 291
 292						strPosStart++;
 293						strPos = strPosStart;
 294
 295						if (strPosStart >= str.length) {
 296							// bad
 297							matchMade = false;
 298							running = false;
 299							continue;
 300						}
 301
 302						// start from a good state
 303						matchMade = true;
 304
 305						// turn off find mode
 306						noMatch = false;
 307
 308						regexPos = 0;
 309
 310						// Set the backtrack to point to the start of the regex
 311						// with the new working position.
 312						setBacktrack(0, strPos);
 313					}
 314					else {
 315						// bad
 316						matchMade = false;
 317						running = false;
 318					}
 319				}
 320				else if (matchMade) {
 321					// good
 322					running = false;
 323					break;
 324				}
 325				else {
 326					// backtrack
 327					//regexPos = findBackupRegexPosition();
 328					//strPos = findBackupPosition();
 329					backtrack = true;
 330				}
 331				continue;
 332			}
 333			else if (noMatch && regex[regexPos] == '\\') {
 334				regexPos+=2;
 335				continue;
 336			}
 337			else if (noMatch && noMatchClass) {
 338				if (regex[regexPos] == ']') {
 339					noMatchClass = false;
 340				}
 341				regexPos++;
 342			}
 343			else if (noMatch && regex[regexPos] == '[') {
 344				// ignore!
 345				noMatchClass = true;
 346				continue;
 347			}
 348			else if (regex[regexPos] == '|') {
 349
 350				// A union operator.
 351
 352				if (currentGroupIdx >= 0) {
 353					if (regexInfo.groupInfo[currentGroupIdx].unionPos >= 0) {
 354						// the current group already has at least one union
 355						// use the current unionPos to append to the list
 356						if (!(currentUnionPos in regexInfo.operatorFlag) && regexPos > currentUnionPos) {
 357							regexInfo.operatorFlag[currentUnionPos] = regexPos;
 358						}
 359					}
 360					else {
 361						// this is the first union of the current group
 362						regexInfo.groupInfo[currentGroupIdx].unionPos = regexPos;
 363					}
 364
 365					if (noMatch && noMatchUntilUnionForPos != -1 && currentGroupIdx == noMatchUntilClosedAtPos) {
 366						// turn off find mode
 367						noMatch = false;
 368
 369						// start from a good state
 370						matchMade = true;
 371					}
 372					else if (matchMade && !noMatch) {
 373						// do not take this union
 374						// declare this group as good
 375
 376						// but set a backtrack just in case
 377						// this will start the regular expression search from the next regex
 378						// point, but undoing the actions of the group thus far
 379						setBacktrack(regexPos+1, regexInfo.groupInfo[currentGroupIdx].strStartPos);
 380
 381						if (regexInfo.groupInfo[currentGroupIdx].endPos >= 0) {
 382							regexPos = regexInfo.groupInfo[currentGroupIdx].endPos-1;
 383						}
 384						else {
 385							noMatch = true;
 386							noMatchUntilClosedAtPos = currentGroupIdx;
 387							noMatchUntilUnionForPos = -1;
 388						}
 389					}
 390					else if (!noMatch) {
 391						// undo actions
 392						strPos = regexInfo.groupInfo[currentGroupIdx].strStartPos;
 393
 394						noMatch = false;
 395
 396						matchMade = true;
 397					}
 398				}
 399				else {
 400					// union operator is in the main regex (top level)
 401
 402					// If we are searching for a union to continue a failed search
 403					// We will enter the next code path. We have found a top level
 404					// union operator.
 405					if (noMatch && noMatchUntilClosedAtPos == -1 && noMatchUntilUnionForPos == -1) {
 406						// Set the backtrack to point to the start of the regex
 407						// with the new working position.
 408						setBacktrack(0, strPos);
 409
 410						// turn off find mode
 411						noMatch = false;
 412
 413						// start from a good state
 414						matchMade = true;
 415					}
 416					else if (noMatch && noMatchUntilUnionForPos != -1) {
 417						// turn off find mode
 418						noMatch = false;
 419
 420						// start from a good state
 421						matchMade = true;
 422					}
 423					else if (matchMade) {
 424						// accept the regular expression
 425						running = false;
 426						break;
 427					}
 428					else {
 429						// we start anew, but at this regular expression
 430						strPos = strPosStart;
 431					}
 432				}
 433
 434				currentUnionPos = regexPos;
 435				regexPos++;
 436			}
 437			else if (regex[regexPos] == '(' && (matchMade || noMatch) ) {
 438
 439				// The start of a grouping.
 440
 441				bool isNew;
 442
 443				if (!(regexPos in regexInfo.groupInfo)) {
 444					GroupInfo newGroup;
 445					newGroup.startPos = regexPos;
 446					newGroup.endPos = -1;
 447					newGroup.strPos = strPos;
 448					newGroup.strStartPos = strPos;
 449					newGroup.parent = currentGroupIdx;
 450					newGroup.unionPos = -1;
 451
 452					// This assumes that all groups will be visited
 453					// in order from left to right.
 454					newGroup.groupId = groupCount;
 455					groupCount++;
 456
 457					regexInfo.groupInfo[regexPos] = newGroup;
 458
 459					isNew = true;
 460				}
 461
 462				regexInfo.groupInfo[regexPos].strStartPos = strPos;
 463				regexInfo.groupInfo[regexPos].strPos = strPos;
 464
 465				currentGroupIdx = regexPos;
 466				regexPos++;
 467
 468				if (regexPos < regex.length - 1 && regex[regexPos] == '?') {
 469					switch(regex[regexPos+1]) {
 470						case '#':
 471							// comments
 472							if (regexInfo.groupInfo[currentGroupIdx].endPos > 0) {
 473								regexPos = regexInfo.groupInfo[currentGroupIdx].endPos;
 474							}
 475							else {
 476								// find the end of the group, ignoring everything
 477								while(regexPos < regex.length && regex[regexPos] != ')') {
 478									regexPos++;
 479								}
 480
 481								// save the result
 482								regexInfo.groupInfo[currentGroupIdx].endPos = regexPos;
 483							}
 484							break;
 485
 486						case '>':
 487							// atomic grouping
 488							break;
 489
 490						case ':':
 491							// non-capturing
 492							if (isNew) {
 493								regexInfo.groupInfo[currentGroupIdx].groupId = int.max;
 494								groupCount--;
 495							}
 496							regexPos+=2;
 497							break;
 498
 499						case '=':
 500							// zero-width positive lookahead
 501							break;
 502
 503						case '!':
 504							// zero-width negative lookahead
 505							break;
 506
 507						case '<':
 508							// zero-width lookbehind
 509							if (regexPos < regex.length - 3) {
 510								if (regex[regexPos+3] == '=') {
 511									// positive
 512								}
 513								else if (regex[regexPos+3] == '!') {
 514									// negative
 515								}
 516							}
 517							regexPos+=2;
 518							break;
 519
 520						default:
 521							break;
 522					}
 523				}
 524			}
 525			else if (regex[regexPos] == ')') {
 526
 527				// A group is ending.
 528
 529				if (!(regexPos in regexInfo.groupInfo)) {
 530					regexInfo.groupInfo[currentGroupIdx].endPos = regexPos;
 531					regexInfo.groupInfo[regexPos] = regexInfo.groupInfo[currentGroupIdx];
 532
 533					if (currentGroupIdx == noMatchUntilClosedAtPos) {
 534						noMatch = false;
 535					}
 536				}
 537
 538				if (noMatch && noMatchUntilClosedAtPos == currentGroupIdx) {
 539					noMatch = false;
 540				}
 541
 542				if (matchMade || noMatch) {
 543					regexInfo.groupInfo[regexInfo.groupInfo[regexPos].startPos].strPos = strPos;
 544
 545					regexGroupStart = regexInfo.groupInfo[regexInfo.groupInfo[regexPos].startPos].groupId;
 546
 547					// set consumption string
 548
 549					if (!noMatch) {
 550						if (regexGroupStart < 9) {
 551							string consumed = (str[regexInfo.groupInfo[regexInfo.groupInfo[regexPos].startPos].strStartPos..strPos]);
 552							regexRefs[Thread.current()][regexGroupStart] = consumed;
 553							regexGroupStart++;
 554						}
 555					}
 556				}
 557				else {
 558					// if we can backtrack to make another decision in this group, do so
 559					// that would effectively undo moves that this group had made
 560					strPos = regexInfo.groupInfo[regexInfo.groupInfo[regexPos].startPos].strPos;
 561					//backtrack = true;
 562				}
 563
 564				currentGroupIdx = regexInfo.groupInfo[regexPos].parent;
 565				regexPos++;
 566			}
 567			else if (noMatch) {
 568				regexPos++;
 569			}
 570			else if (regex[regexPos] == '*') {
 571
 572				// Kleene star operator.
 573
 574				if (regexPos < regex.length - 1 && regex[regexPos+1] == '?') {
 575					// this is a lazy kleene
 576
 577					// it may have matched something, but it should ignore the work
 578					// for now that it had done and save it as part of the lazy operator
 579
 580					if (matchMade) {
 581						// set backtrack to do another computation
 582						setBacktrack(findBackupRegexPosition(), strPos);
 583
 584						//if (!(regexPos in regexInfo.operatorFlag)) {
 585						if (regexFlagPotential < regexPos) {
 586							// we have made a match, but have not attempted
 587							// to try not matching anything first
 588
 589							// set the flag so that this operator knows that it has
 590							// already found a match
 591							regexInfo.operatorFlag[regexPos] = strPos;
 592							regexFlagPotential = regexPos;
 593
 594							// set backtrack to start where this one would have
 595							// continued to
 596							setBacktrack(regexPos+2, strPos);
 597
 598							// and then start all over by assuming nothing is taken
 599							strPos = findBackupPosition();
 600							regexPos+=2;
 601						}
 602						else {
 603							// we have already found a match
 604							// just continue on our way
 605							regexPos+=2;
 606						}
 607					}
 608					else {
 609						// the group fails, it is ok
 610						matchMade = true;
 611						regexPos+=2;
 612					}
 613				}
 614				else if (matchMade) {
 615					// this is a greedy kleene
 616
 617					// the backtrack will suggest to just go to the next regex
 618					// character at this same string. this computation path,
 619					// however, will be attempting to match the previous group
 620					// as much as possible
 621
 622					// we need to set a backtrack for having not matched anything even though
 623					// something was just matched. It could be that what we matched belongs to
 624					// another section of the regex.
 625
 626					if (!(regexPos in regexInfo.operatorFlag) || regexFlagPotential < regexPos) {
 627						// set a backtrack for having nothing found
 628						setBacktrack(regexPos+1,findBackupPosition());
 629					}
 630
 631					regexInfo.operatorFlag[regexPos] = 1;
 632
 633					setBacktrack(regexPos+1, strPos);
 634					regexPos--;
 635
 636					if (regexPos in regexInfo.groupInfo) {
 637						regexPos = regexInfo.groupInfo[regexPos].startPos;
 638						currentGroupIdx = regexPos;
 639					}
 640					else if (regexPos < regex.length && regex[regexPos] == ']' && regexPos in regexInfo.operatorFlag) {
 641						regexPos = regexInfo.operatorFlag[regexPos];
 642					}
 643					else {
 644						if (regexPos > 0 && regex[regexPos-1] == '\\') {
 645							regexPos--;
 646						}
 647					}
 648				}
 649				else {
 650					// it is ok
 651					matchMade = true;
 652					regexPos++;
 653				}
 654			}
 655			else if (regex[regexPos] == '+') {
 656
 657				// Kleene plus operator.
 658
 659				if (regexPos < regex.length - 1 && regex[regexPos+1] == '?') {
 660					// this is a lazy kleene
 661
 662					if (matchMade) {
 663						// good, continue and set a backtrack to attempt another
 664						// match on this kleene
 665
 666						// set the flag so that this operator knows that it has
 667						// already found a match
 668						regexInfo.operatorFlag[regexPos] = 1;
 669						regexFlagPotential = regexPos;
 670
 671						// set the backtrace
 672						int newRegexPos = regexPos+2;
 673
 674						regexPos--;
 675						if (regexPos in regexInfo.groupInfo) {
 676							regexPos = regexInfo.groupInfo[regexPos].startPos;
 677							currentGroupIdx = regexPos;
 678						}
 679						else if (regexPos < regex.length && regex[regexPos] == ']' && regexPos in regexInfo.operatorFlag) {
 680							regexPos = regexInfo.operatorFlag[regexPos];
 681						}
 682						else {
 683							if (regexPos > 0 && regex[regexPos-1] == '\\') {
 684								regexPos--;
 685							}
 686						}
 687
 688						setBacktrack(regexPos, strPos);
 689
 690						regexPos = newRegexPos;
 691					}
 692					else {
 693						if (regexFlagPotential < regexPos) {
 694							// we have not found any matches at all
 695							// fail the op
 696
 697							//regexPos = findBackupRegexPosition();
 698							//strPos = findBackupPosition();
 699							backtrack = true;
 700							continue;
 701						}
 702						else {
 703							// it is ok, we found at least one
 704							matchMade = true;
 705							regexPos+=2;
 706						}
 707					}
 708				}
 709				else if (matchMade) {
 710					// this is a greedy kleene
 711
 712					// the backtrack will suggest to just go to the next regex
 713					// character at this same string. this computation path,
 714					// however, will be attempting to match the previous group
 715					// as much as possible
 716
 717					setBacktrack(regexPos+1, strPos);
 718
 719					// set the flag so that this operator knows that it has
 720					// already found a match
 721					regexInfo.operatorFlag[regexPos] = 1;
 722					regexFlagPotential = regexPos;
 723
 724					regexPos--;
 725					if (regexPos in regexInfo.groupInfo) {
 726						regexPos = regexInfo.groupInfo[regexPos].startPos;
 727						currentGroupIdx = regexPos;
 728					}
 729					else if (regexPos < regex.length && regex[regexPos] == ']' && regexPos in regexInfo.operatorFlag) {
 730						regexPos = regexInfo.operatorFlag[regexPos];
 731					}
 732					else {
 733						if (regexPos > 0 && regex[regexPos-1] == '\\') {
 734							regexPos--;
 735						}
 736					}
 737				}
 738				else {
 739					// it is ok
 740					if (regexPos in regexInfo.operatorFlag && regexFlagPotential >= regexPos) {
 741						// good
 742						matchMade = true;
 743						regexPos++;
 744					}
 745					else {
 746						// fail the op
 747						//regexPos = findBackupRegexPosition();
 748						//strPos = findBackupPosition();
 749						backtrack = true;
 750						continue;
 751					}
 752				}
 753			}
 754			else if (regex[regexPos] == '?') {
 755				// option
 756				regexPos++;
 757
 758				if (regexPos < regex.length && regex[regexPos] == '?') {
 759					// lazy option
 760					regexPos++;
 761					if (matchMade) {
 762						// unfortunately, this work that has been done
 763						// has been done in vain. We want to attempt to
 764						// not consume this option.
 765
 766						// set the backtrack to backtrack to the current
 767						// situation (taking the option)
 768						setBacktrack(regexPos, strPos);
 769
 770						// now, attempt to carry on to the next part of
 771						// the regex while undoing the last group
 772						strPos = findBackupPosition();
 773					}
 774					else {
 775						// very good, only one possible outcome: no match
 776						matchMade = true;
 777					}
 778				}
 779				else if (matchMade) {
 780					// greedy option
 781
 782					// backtrack to not taking the option
 783					setBacktrack(regexPos, findBackupPosition());
 784				}
 785				else {
 786					// greedy option
 787					matchMade = true;
 788				}
 789			}
 790			else if (!matchMade) {
 791				// the group fails if a concatenation fails
 792				if (currentGroupIdx >= 0) {
 793					int curUnionPos = -1;
 794
 795					if (regexInfo.groupInfo[currentGroupIdx].unionPos >= 0) {
 796						curUnionPos = regexInfo.groupInfo[currentGroupIdx].unionPos;
 797
 798						while(curUnionPos < regexPos && curUnionPos in regexInfo.operatorFlag) {
 799							curUnionPos = regexInfo.operatorFlag[curUnionPos];
 800						}
 801
 802						if (curUnionPos < regexPos) {
 803							curUnionPos = -1;
 804						}
 805					}
 806					
 807					strPos = regexInfo.groupInfo[currentGroupIdx].strStartPos;
 808
 809					if (curUnionPos >= 0) {
 810						regexPos = curUnionPos;
 811					}
 812					else if (regexInfo.groupInfo[currentGroupIdx].endPos >= 0) {
 813						regexPos = regexInfo.groupInfo[currentGroupIdx].endPos;
 814					}
 815					else {
 816						// need to find either a union for this group
 817						// or the group end
 818						noMatch = true;
 819						noMatchUntilClosedAtPos = currentGroupIdx;
 820						noMatchUntilUnionForPos = currentGroupIdx;
 821					}
 822				}
 823				else {
 824					backtrack = true;
 825					continue;
 826				}
 827			}
 828			else if (regex[regexPos] == '$') {
 829
 830				// dollar anchor
 831
 832				if (strPos == str.length || str[strPos] == '\n' || str[strPos] == '\r') {
 833					matchMade = true;
 834				}
 835				else {
 836					//regexPos = findBackupRegexPosition();
 837					//strPos = findBackupPosition();
 838					backtrack = true;
 839					continue;
 840				}
 841				regexPos++;
 842			}
 843			else if (regex[regexPos] == '^') {
 844
 845				// caret anchor
 846
 847				if (multiline) {
 848					if (strPos == 0 || str[strPos-1] == '\n' || str[strPos-1] == '\r') {
 849						matchMade = true;
 850					}
 851					else {
 852						// Multiline option:
 853						backtrack = true;
 854						continue;
 855					}
 856				}
 857				else {
 858					if (strPos == 0) {
 859						matchMade = true;
 860					}
 861					else {
 862						backtrackedOnCaret = true;
 863						// Nonmultiline option:
 864						backtrack = true;
 865						continue;
 866					}
 867				}
 868				regexPos++;
 869			}
 870			else if (((regexPos + 1) < regex.length) && (regex[regexPos] == '\\') && (regex[regexPos+1] == 'b')) {
 871
 872				// word boundary anchor
 873
 874				// Check for boundary
 875				if (strPos == 0) {
 876					// Anchored to the beginning of the string
 877
 878					// The first character should be a word character
 879					if ( (str[strPos] >= 'a' && str[strPos] <= 'z') ||
 880						 (str[strPos] >= 'A' && str[strPos] <= 'Z') ||
 881						 (str[strPos] >= '0' && str[strPos] <= '9') ||
 882						 (str[strPos] == '_')) {
 883						matchMade = true;
 884					}
 885					else {
 886						backtrack = true;
 887						continue;
 888					}
 889				}
 890				else if ((strPos == str.length) && (str.length > 0)) {
 891					// Anchored at end of string
 892					matchMade = true;
 893
 894					// The last character should be a word character
 895					if ( (str[strPos-1] >= 'a' && str[strPos-1] <= 'z') ||
 896						 (str[strPos-1] >= 'A' && str[strPos-1] <= 'Z') ||
 897						 (str[strPos-1] >= '0' && str[strPos-1] <= '9') ||
 898						 (str[strPos-1] == '_')) {
 899						matchMade = true;
 900					}
 901					else {
 902						backtrack = true;
 903						continue;
 904					}
 905				}
 906				else {
 907					// It is between two characters
 908					// One or the other (exclusive) should be a word character
 909
 910					bool firstWordCharacter = false;
 911
 912					if ( (str[strPos-1] >= 'a' && str[strPos-1] <= 'z') ||
 913						 (str[strPos-1] >= 'A' && str[strPos-1] <= 'Z') ||
 914						 (str[strPos-1] >= '0' && str[strPos-1] <= '9') ||
 915						 (str[strPos-1] == '_')) {
 916						firstWordCharacter = true;
 917					}
 918
 919					if ( (str[strPos] >= 'a' && str[strPos] <= 'z') ||
 920						 (str[strPos] >= 'A' && str[strPos] <= 'Z') ||
 921						 (str[strPos] >= '0' && str[strPos] <= '9') ||
 922						 (str[strPos] == '_')) {
 923						if (!firstWordCharacter) {
 924							matchMade = true;
 925						}
 926						else {
 927							backtrack = true;
 928							continue;
 929						}
 930					}
 931					else if (firstWordCharacter) {
 932						matchMade = true;
 933					}
 934					else {
 935						backtrack = true;
 936						continue;
 937					}
 938				}
 939				regexPos+=2;
 940			}
 941			else {
 942				// concatentation
 943
 944				if (regex[regexPos] == '[') {
 945					currentClassStart = regexPos;
 946
 947					matchClass = true;
 948
 949					regexPos++;
 950					if (regexPos < regex.length && regex[regexPos] == '^') {
 951						matchInverse = true;
 952						regexPos++;
 953					}
 954					else {
 955						matchInverse = false;
 956					}
 957
 958					// cancel when we run out of space
 959					if (regexPos == regex.length) {
 960						continue;
 961					}
 962				}
 963
 964				do {
 965					if (matchClass && regex[regexPos] == ']') {
 966						regexInfo.operatorFlag[currentClassStart] = regexPos;
 967						regexInfo.operatorFlag[regexPos] = currentClassStart;
 968						if (matchInverse && !matchMade) {
 969							matchMade = true;
 970							matchInverse = false;
 971						}
 972						matchClass = false;
 973					}
 974					else if (matchClass && regexPos < regex.length - 1 && regex[regexPos+1] == '-') {
 975						// character class range, use the last character
 976						// and build a range of possible values
 977
 978						matchRange = true;
 979						regexPos+=2;
 980						continue;
 981					}
 982					else if (matchRange) {
 983						matchMade = strPos < str.length && str[strPos] >= regex[regexPos-2] && str[strPos] <= regex[regexPos];
 984
 985						// no more ranges!
 986						matchRange = false;
 987					}
 988					else if (regex[regexPos] == '\\' && regexPos < regex.length-1) {
 989						regexPos++;
 990						if (strPos >= str.length) {
 991							matchMade = false;
 992						}
 993						else {
 994							switch(regex[regexPos]) {
 995								case '1':
 996								case '2':
 997								case '3':
 998								case '4':
 999								case '5':
1000								case '6':
1001								case '7':
1002								case '8':
1003								case '9':
1004									int refIndex = cast(uint)regex[regexPos] - cast(uint)'1';
1005									// forward and backward references
1006
1007									if (Thread.current() in regexRefs) {
1008										if (regexRefs[Thread.current()][refIndex] !is null) {
1009											matchMade = true;
1010
1011											foreach(int i, chr; regexRefs[Thread.current()][refIndex]) {
1012
1013												if (strPos >= str.length) {
1014													matchMade = false;
1015													break;
1016												}
1017
1018												if (str[strPos] != chr) {
1019													matchMade = false;
1020													break;
1021												}
1022
1023												strPos++;
1024											}
1025
1026											if (matchMade) {
1027												strPos--;
1028											}
1029										}
1030										else {
1031											matchMade = false;
1032										}
1033									}
1034									else {
1035										matchMade = false;
1036									}
1037									break;
1038								case 'd':
1039									matchMade = (str[strPos] >= '0' && str[strPos] <= '9');
1040									break;
1041
1042								case 'D':
1043									matchMade = !(str[strPos] >= '0' && str[strPos] <= '9');
1044									break;
1045
1046								case 's':
1047									matchMade = (str[strPos] == ' '
1048												|| str[strPos] == '\t'
1049												|| str[strPos] == '\r'
1050												|| str[strPos] == '\n'
1051												|| str[strPos] == '\v'
1052												|| str[strPos] == '\f');
1053									break;
1054
1055								case 'S':
1056									matchMade = (str[strPos] != ' '
1057												&& str[strPos] != '\t'
1058												&& str[strPos] != '\r'
1059												&& str[strPos] != '\n'
1060												&& str[strPos] != '\v'
1061												&& str[strPos] != '\f');
1062									break;
1063
1064								case 'w':
1065									matchMade = (str[strPos] == '_'
1066												|| (str[strPos] >= 'a' && str[strPos] <= 'z')
1067												|| (str[strPos] >= 'A' && str[strPos] <= 'Z'));
1068									break;
1069
1070								case 'W':
1071									matchMade = (str[strPos] != '_'
1072												&& (str[strPos] < 'a' || str[strPos] > 'z')
1073												&& (str[strPos] < 'A' || str[strPos] > 'Z'));
1074									break;
1075
1076								case 'b':
1077									// backspace
1078									matchMade = str[strPos] == '\b';
1079									break;
1080
1081								case 'n':
1082									// newline
1083									matchMade = str[strPos] == '\n';
1084									break;
1085
1086								case 'e':
1087									// escape
1088									matchMade = str[strPos] == '\x1b';
1089									break;
1090
1091								case 'v':
1092									matchMade = str[strPos] == '\v';
1093									break;
1094
1095								case 't':
1096									matchMade = str[strPos] == '\t';
1097									break;
1098
1099								case 'r':
1100									matchMade = str[strPos] == '\r';
1101									break;
1102
1103								case 'a':
1104									matchMade = str[strPos] == '\a';
1105									break;
1106
1107								case '0':
1108									matchMade = str[strPos] == '\0';
1109									break;
1110
1111								case '\0':
1112									matchMade = str[strPos] == '\0';
1113									break;
1114
1115								default:
1116									matchMade = str[strPos] == regex[regexPos];
1117									break;
1118							}
1119						}
1120					}
1121					else if (regexPos < regex.length && strPos < str.length
1122							&& ((str[strPos] == regex[regexPos])
1123							|| (!matchClass && regex[regexPos] == '.'
1124							&& str[strPos] != '\n' && str[strPos] != '\r'))) {
1125						// match made
1126						matchMade = true;
1127					}
1128					else {
1129						// no match made
1130						matchMade = false;
1131					}
1132
1133					if ((matchMade && matchInverse) || (matchInverse && strPos >= str.length)) {
1134						matchMade = false;
1135						break;
1136					}
1137
1138					if (matchClass && !matchMade && regexPos < regex.length) {
1139						regexPos++;
1140						continue;
1141					}
1142
1143					break;
1144
1145				} while (true);
1146
1147				matchRange = false;
1148				matchInverse = false;
1149
1150				if (matchClass) {
1151					matchClass = false;
1152
1153					if (currentClassStart in regexInfo.operatorFlag) {
1154						regexPos = regexInfo.operatorFlag[currentClassStart];
1155					}
1156					else {
1157						// dang, need to search for it
1158						regexPos++;
1159						for(;regexPos < regex.length && regex[regexPos] != ']'; regexPos++) {
1160							if (regex[regexPos] == '\\') { regexPos++; }
1161						}
1162
1163						if (regexPos >= regex.length) { continue; }
1164
1165						regexInfo.operatorFlag[currentClassStart] = regexPos;
1166						regexInfo.operatorFlag[regexPos] = currentClassStart;
1167					}
1168				}
1169
1170				if (matchMade) {
1171
1172					// consume input string
1173					strPos++;
1174				}
1175
1176				// consume
1177				regexPos++;
1178			}
1179		}
1180
1181		// Null out any outstanding groups
1182		if (Thread.current() in regexRefs) {
1183			for( ; regexGroupStart < 9 ; regexGroupStart++ ) {
1184			//	regexRefs[Thread.current()][regexGroupStart]
1185				//	= null;
1186			}
1187		}
1188
1189		/*
1190		if (!(oldRunIndex in oldRuns)) {
1191			oldRuns[oldRunIndex] = regexInfo;
1192		}*/
1193
1194		// Return the result
1195		if (matchMade && strPosStart <= str.length) {
1196			if (strPos-strPosStart == 0) {
1197				return ("");
1198			}
1199
1200			// Save the position where the string was consumed
1201			this.regexPos[Thread.current()] = strPosStart;
1202
1203			// Slice and return the consumed string
1204			return str.substring(strPosStart, strPos-strPosStart);
1205		}
1206
1207		return null;
1208	}
1209
1210	// Description: This function will return a matched regular expression on the given string. Instances of a Regex will use a DFA based approach.
1211	// str: The string to run the regular expression upon.
1212	// Returns: The matched substring or null when no match could be found.
1213	string eval(string str) {
1214		State currentState = startingState;
1215
1216		uint strPos;
1217		uint startingStrPos;
1218
1219		State acceptState;
1220		uint acceptStrEnd;
1221
1222		dchar chr;
1223		for (strPos = startingStrPos; strPos < str.length; strPos++) {
1224		//	Console.putln("starting ... ", startingStrPos);
1225			chr = str[strPos];
1226		//	Console.putln("chr ... ", str[strPos]);
1227			if (chr in currentState.transitions) {
1228				// Take transition
1229				//Console.putln("taking transition ", chr, " from ", currentState.id, " to ", currentState.transitions[chr].id);
1230				currentState = currentState.transitions[chr];
1231				if (currentState.accept) {
1232					//Console.putln("found accept at ", strPos, " from ", startingStrPos);
1233					acceptStrEnd = strPos + 1;
1234					acceptState = currentState;
1235				}
1236			}
1237			else {
1238				// No transition
1239				
1240				if (acceptStrEnd > startingStrPos) {
1241					Console.putln("Leaving Early");
1242					strPos = acceptStrEnd;
1243					currentState = acceptState;
1244				}
1245
1246				// Is this an accept state?
1247				if (currentState.accept) {
1248					break;
1249				}
1250
1251				// Start over
1252
1253				if (startingStrPos >= str.length) {
1254					// No more to search
1255					return null;
1256				}
1257
1258				// Next turn, strPos will be startingStrPos + 1
1259				// (because of loop iteration)
1260				strPos = startingStrPos;
1261
1262				// We are sliding down the string by one character
1263				startingStrPos++;
1264
1265				// We go back to the beginning
1266				currentState = startingState;
1267			}
1268		}
1269				
1270		if (acceptStrEnd > startingStrPos) {
1271			Console.putln("Leaving Early");
1272			strPos = acceptStrEnd;
1273			currentState = acceptState;
1274		}
1275
1276		// Return consumed string
1277		if (currentState.accept) {
1278			return str.substring(startingStrPos, strPos - startingStrPos);
1279		}
1280
1281		// No match
1282		return null;
1283	}
1284
1285protected:
1286
1287	// These instance variables contain the data structures
1288	// that will build and maintain the DFA for the regular expression
1289
1290	// Holds the regular expression for the instance
1291	string regularExpression;
1292
1293	// For DFA regex operations
1294	
1295	class Link {
1296		State from;
1297		dchar transition;
1298	}
1299
1300	static class State {
1301		State[dchar] transitions;
1302		// List!(Group) groupStarts;
1303		// List!(Group) groupEnds;
1304		List!(dchar) backwardList;
1305		List!(Link) incomingList;
1306
1307		bool accept;
1308
1309		int id;
1310
1311		this() {
1312			this(count);
1313		}
1314
1315		this(int id) {
1316			this.id = id;
1317			backwardList = new List!(dchar);
1318			incomingList = new List!(Link);
1319			debugThis();
1320		}
1321
1322		// Debugging block
1323		static int count = 0;
1324		static List!(State) all;
1325
1326		static this() {
1327			all = new List!(State);
1328		}
1329
1330		void debugThis() {
1331			count++;
1332			all.add(this);
1333		}
1334	
1335		string tostring() {
1336			string ret = "State " ~ toStr(id) ~ ": [";
1337
1338			if (accept) {
1339				ret ~= "A] ";
1340			}
1341			else {
1342				ret ~= " ] ";
1343			}
1344
1345			foreach(key; transitions.keys) {
1346				if (transitions[key].id <= id) {
1347					ret ~= toStr(key) ~ "<>" ~ toStr(transitions[key].id) ~ " ";
1348				}
1349				else {
1350					ret ~= toStr(key) ~ "->" ~ toStr(transitions[key].id) ~ " ";
1351				}
1352			}
1353			return ret;
1354		}
1355
1356		static void printall() {
1357			foreach(state; all) {
1358				Console.putln(state);
1359			}
1360		}
1361	}
1362
1363	State startingState;
1364
1365	void buildDFA(bool useDFARules = false) {
1366		_DFARules = useDFARules;
1367
1368		// Go through the regular expression and build the DFAs
1369		startingState = buildDFA(regularExpression);
1370	}
1371
1372private:
1373
1374	bool _DFARules;
1375
1376	struct DFAGroupInfo {
1377		bool hasKleene;
1378		int endPos;
1379	}
1380
1381	DFAGroupInfo[int] _groupInfo;
1382
1383	void fillGroupInfo() {
1384		_groupInfo = null;
1385
1386		dchar ch;
1387
1388		List!(int) groupStack = new List!(int);
1389
1390		for (uint i; i < regularExpression.length; i++) {
1391			Console.putln("foo ", i);
1392			ch = regularExpression[i];
1393			switch (ch) {
1394				case '\0':
1395					return;
1396				case '\\':
1397					i++;
1398					continue;
1399				case '(':
1400					groupStack.add(cast(int)i);
1401					DFAGroupInfo dgi;
1402					_groupInfo[i] = dgi;
1403					break;
1404				case ')':
1405					int startPos = groupStack.remove();
1406					if (startPos in _groupInfo) {
1407						_groupInfo[startPos].endPos = i;
1408						if ((i + 1 < regularExpression.length) && regularExpression[i+1] == '*') {
1409							Console.putln("HAS KLEENE");
1410							_groupInfo[startPos].hasKleene = true;
1411							i++;
1412						}
1413					}
1414					break;
1415				default:
1416					if (groupStack.empty()) {
1417						Console.putln("NULLED");
1418						_groupInfo = null;
1419					}
1420					break;
1421			}
1422		}
1423	}
1424
1425	State buildDFA(string regex) {
1426		fillGroupInfo();
1427		uint regexPos = 0;
1428		List!(State) current = new List!(State);
1429		return buildDFA(regex, regexPos, current);
1430	}
1431
1432	State buildDFA(string regex, ref uint regexPos, ref List!(State) current, bool isKleene = false) {
1433		State startState = new State();
1434		Console.putln("Start State: ", startState.id);
1435
1436		uint groupPos = regexPos - 1;
1437
1438		dchar lastChar = '\0';
1439		dchar thisChar;
1440		dchar lastConcatChar = '\0';
1441
1442		enum Operation {
1443			None,
1444			Kleene,
1445			Concat
1446		}
1447
1448		Operation lastOp = Operation.None;
1449
1450		List!(State) old = current.dup();
1451		current.add(startState);
1452
1453		if (regexPos < regex.length) {
1454			lastChar = regex[regexPos];
1455			if (lastChar == '*') {
1456				// error
1457			}
1458			else if (lastChar == '(') {
1459				// group
1460				regexPos++;
1461				buildDFA(regex, regexPos, current);
1462				if (regex[regexPos] == '*') {
1463					Console.putln("Inner Group Kleened");
1464					lastOp = Operation.Kleene;
1465				}
1466			}
1467			else {
1468				lastConcatChar = lastChar;
1469			}
1470			regexPos++;
1471		}
1472
1473		while (regexPos <= regex.length) {
1474			if (regexPos == regex.length) {
1475				thisChar = '\0';
1476			}
1477			else {
1478				thisChar = regex[regexPos];
1479			}
1480
1481			if (thisChar == '*') {
1482				// Kleene Star
1483				//Console.putln("Kleene (", lastChar, ")");
1484				if (lastChar == ')') {
1485					Console.putln("Kleene Group End, connecting ", lastConcatChar, " to ", startState.id);
1486					foreach(state; current) {
1487						State ret = concat(state, lastConcatChar, startState);
1488						if (ret is startState && startState.id <= state.id) {
1489							state.backwardList.add(lastConcatChar);
1490							Link link = new Link();
1491							link.from = state;
1492							link.transition = lastConcatChar;
1493							startState.incomingList.add(link);
1494						}
1495					}
1496					old.add(startState);
1497					current = old;
1498					State.printall();
1499					return startState;
1500				}
1501				else {
1502					// Single Character Kleene
1503					// ex. "a*" => [p] -> 'a' -> [p]
1504					Console.putln("Single Character Kleene (", lastConcatChar, ")");
1505
1506					List!(State) newStateList = current.dup;
1507					State loopState;
1508					foreach(state; current) {
1509						if (state.backwardList.empty) {
1510							while (lastConcatChar in state.transitions) {
1511								state = concat(state, lastConcatChar, state);
1512							}
1513
1514							state.transitions[lastConcatChar] = state;
1515							state.backwardList.add(lastConcatChar);
1516						}
1517						else {
1518							if (loopState is null) {
1519								loopState = new State();
1520								loopState.transitions[lastConcatChar] = loopState;
1521								loopState.backwardList.add(lastConcatChar);
1522							}
1523							State ret = concat(state, lastConcatChar, loopState);
1524						}
1525					}
1526
1527					current = newStateList;
1528
1529					if (loopState !is null) {
1530						current.add(loopState);
1531					}
1532
1533					//Console.putln("Done Single Character Kleene (", lastConcatChar, ")");
1534				}
1535				lastOp = Operation.Kleene;
1536				lastConcatChar = '\0';
1537			}
1538			else {
1539				// concatenation
1540				if (lastConcatChar != '\0' && thisChar != ')') {
1541					Console.putln("-=-=-=-=-");
1542					Console.putln("boo: ", lastOp == Operation.Kleene);
1543					State concatState;
1544					List!(State) newStateList = new List!(State);
1545					foreach(state; current) {
1546						State ret = concat(state, lastConcatChar, concatState, lastOp == Operation.Kleene);
1547						if (ret !is concatState && ret !is null) {
1548							newStateList.add(ret);
1549						}
1550					}
1551					if (concatState !is null) {
1552						newStateList.add(concatState);
1553					}
1554					current = newStateList;
1555					Console.putln("Concat Character (", lastConcatChar, ")");
1556					State.printall();
1557					Console.putln("-=-=-=-=-");
1558					foreach(state; current) {
1559						Console.put(state.id, " ... ");
1560					}
1561					Console.putln;
1562					lastOp = Operation.Concat;
1563				}
1564
1565				if (thisChar == '(') {
1566					// group start
1567					Console.putln("Inner Group Found");
1568					regexPos+=1;
1569					buildDFA(regex, regexPos, current, false);
1570					if (regex[regexPos] == '*') {
1571						Console.putln("Inner Group Kleened");
1572						lastOp = Operation.Kleene;
1573					}
1574					lastConcatChar = '\0';
1575				}
1576				else if (thisChar != ')') {
1577					lastConcatChar = thisChar;
1578				}
1579			}
1580
1581			//Console.putln("lastChar = ", thisChar);
1582			lastChar = thisChar;
1583
1584			regexPos++;
1585		}
1586
1587		foreach(state; current) {
1588			isolate(state);
1589			state.accept = true;
1590		}
1591
1592		Console.putln("Done");
1593		State.printall();
1594
1595		return startState;
1596	}
1597
1598	State concat(State start, dchar transition, ref State to, bool doNotUnroll = false) {
1599		if (to !is null) {
1600			Console.putln(start.id, " to ", to.id);
1601		}
1602		else {
1603			Console.putln(start.id, " to null");
1604		}
1605
1606		if ((to is null) || (to.id > start.id)) {
1607			if (!doNotUnroll) {
1608				isolate(start);
1609				unroll(start);
1610				isolate(start);
1611			}
1612		}
1613
1614		if (transition in start.transitions) {
1615			return start.transitions[transition];
1616		}
1617		else {
1618			if (to is null) {
1619				to = new State();
1620			}
1621			start.transitions[transition] = to;
1622		}
1623
1624		return to;
1625	}
1626
1627	void unroll(State state) {
1628		Console.putln("unrolling ", state.id);
1629		foreach(backwardTrans; state.backwardList) {
1630			State newState = new State();
1631			State destState = state.transitions[backwardTrans];
1632
1633			state.transitions[backwardTrans] = newState;
1634			foreach(transition; destState.transitions.keys) {
1635				State toState = destState.transitions[transition];
1636				newState.transitions[transition] = toState;
1637				newState.backwardList.add(transition);
1638				Link link = new Link();
1639				link.from = newState;
1640				link.transition = transition;
1641				toState.incomingList.add(link);
1642/*
1643				if (toState is state) {
1644					Link link = new Link();
1645					link.from = newState;
1646					link.transition = transition;
1647					state.incomingList.add(link);
1648				}
1649*/
1650			}
1651		}
1652		state.backwardList = new List!(dchar);
1653	}
1654
1655	void isolate(State state) {
1656		Console.putln("isolating ", state.id);
1657		foreach(link; state.incomingList) {
1658			unroll(link.from);
1659		}
1660		state.incomingList = new List!(Link);
1661	}
1662
1663	public static void test() {
1664	}
1665
1666	static void my_unroll(State state, dchar chr) {
1667	}
1668
1669	// Common
1670
1671	static string[][Thread] regexRefs;
1672	static uint[Thread] regexPos;
1673
1674	// For backtracking regex operations
1675
1676	struct GroupInfo {
1677		int startPos;
1678		int endPos;
1679		int strStartPos;
1680		int strPos;
1681		int parent;
1682		int unionPos;
1683		int groupId;
1684	}
1685
1686	struct RegexInfo {
1687
1688		// This hash table contains information about a grouping
1689		// for a specific position in the regex.
1690		GroupInfo[int] groupInfo;
1691
1692		// This hash table contains information that aids operators
1693		// for a specific position in the regex.
1694		int[int] operatorFlag;
1695
1696		// This structure hopes to minimize work already done by merely setting
1697		// a flag whenever a position in each string is reached. Since this
1698		// denotes that the regex will be parsing from the same state, and the
1699		// regex is pure, it will not have to repeat the work.
1700		int[][] memoizer;
1701	}
1702}