private void SaveGroups(ref RxContext cxt, int start, int end, bool clear, out RxSavedGroups groups) { groups.Start = start; groups.LastOpenCapture = cxt.LastOpenCapture; groups.LastClosedCapture = cxt.LastClosedCapture; groups.Data = new RxCapture[end - start]; for (int i = start; i < end; ++i) { if (clear || cxt.Captures.Length <= i) groups.Data[i - start].Start = groups.Data[i - start].End = -1; else { groups.Data[i - start] = cxt.Captures[i]; } } }
private bool MatchAt(Runtime runtime, string str, int pos, out RxResult res) { int len = str.Length; res.Matched = false; res.Start = res.End = -1; res.Captures = null; res.StringCaptures = null; RxContext cxt; cxt.Pos = pos; cxt.Groups = new List<RxGroup>(); cxt.States = new List<RxState>(); cxt.StateBacktrack = new List<int>(); if (Captures > 0) cxt.Captures = new RxCapture[Captures]; else cxt.Captures = null; if (Saved > 0) cxt.Saved = new int[Saved]; else cxt.Saved = null; cxt.LastOpenCapture = cxt.LastClosedCapture = -1; for (int index = 0; index >= 0;) { switch (Ops[index].Number) { case Opcode.OpNumber.OP_RX_START_MATCH: // do nothing for now ++index; break; case Opcode.OpNumber.OP_RX_EXACT: case Opcode.OpNumber.OP_RX_EXACT_I: { var s = Exact[Ops[index].Index]; bool case_insensitive = Ops[index].Number == Opcode.OpNumber.OP_RX_EXACT_I; if ( cxt.Pos + s.Length > len || string.Compare(str, cxt.Pos, s, 0, s.Length, case_insensitive) != 0) index = Backtrack(ref cxt); else { cxt.Pos += s.Length; ++index; } break; } case Opcode.OpNumber.OP_RX_SAVE_POS: { cxt.Saved[Ops[index].Index] = cxt.Pos; ++index; break; } case Opcode.OpNumber.OP_RX_RESTORE_POS: { cxt.Pos = cxt.Saved[Ops[index].Index]; ++index; break; } case Opcode.OpNumber.OP_RX_ANY_NONEWLINE: { if (cxt.Pos == len || str[cxt.Pos] == '\n') index = Backtrack(ref cxt); else { ++cxt.Pos; ++index; } break; } case Opcode.OpNumber.OP_RX_ANY: { if (cxt.Pos == len) index = Backtrack(ref cxt); else { ++cxt.Pos; ++index; } break; } case Opcode.OpNumber.OP_RX_CLASS: { var c = Classes[Ops[index].Index]; if (cxt.Pos < len) { char ch = str[cxt.Pos]; ++cxt.Pos; ++index; if (c.Exact.IndexOf(ch) >= 0) break; if ((c.Flags & Opcode.RX_CLASS_WORDS) != 0 && IsWord(ch)) break; if ((c.Flags & Opcode.RX_CLASS_NOT_WORDS) != 0 && !IsWord(ch)) break; if ((c.Flags & Opcode.RX_CLASS_DIGITS) != 0 && char.IsDigit(ch)) break; if ((c.Flags & Opcode.RX_CLASS_NOT_DIGITS) != 0 && !char.IsDigit(ch)) break; if ((c.Flags & Opcode.RX_CLASS_SPACES) != 0 && char.IsWhiteSpace(ch)) break; if ((c.Flags & Opcode.RX_CLASS_NOT_SPACES) != 0 && !char.IsWhiteSpace(ch)) break; } index = Backtrack(ref cxt); break; } case Opcode.OpNumber.OP_RX_BEGINNING: { if (cxt.Pos != 0) index = Backtrack(ref cxt); else ++index; break; } case Opcode.OpNumber.OP_RX_START_GROUP: { var grp = new RxGroup(-1); cxt.Groups.Add(grp); cxt.StateBacktrack.Add(cxt.States.Count); index = Targets[Ops[index].Index]; break; } case Opcode.OpNumber.OP_RX_BACKTRACK: case Opcode.OpNumber.OP_RX_TRY: { var st = new RxState(cxt.Pos, Targets[Ops[index].Index], cxt.Groups.Count); cxt.States.Add(st); ++index; break; } case Opcode.OpNumber.OP_RX_POP_STATE: { cxt.States.RemoveAt(cxt.States.Count - 1); ++index; break; } case Opcode.OpNumber.OP_RX_FAIL: { index = Backtrack(ref cxt); break; } case Opcode.OpNumber.OP_RX_QUANTIFIER: { var group = cxt.Groups[cxt.Groups.Count - 1]; var quant = Quantifiers[Ops[index].Index]; int lastMatch = group.LastMatch; ++index; group.Count += 1; group.LastMatch = cxt.Pos; if (group.Count > 0 && quant.Group >= 0) EndCapture(ref cxt, quant.Group); // max repeat count if (group.Count == quant.MaxCount) { cxt.Groups.RemoveAt(cxt.Groups.Count - 1); break; } // zero-length match if (cxt.Pos == lastMatch) { break; } RxSavedGroups gr; if (group.Count == 0 || group.Count >= quant.MinCount) SaveGroups(ref cxt, quant.SubgroupStart, quant.SubgroupEnd, group.Count == 0, out gr); else gr = new RxSavedGroups(); if (group.Count == 0 && quant.MinCount > 0) { // force failure on backtrack var st = new RxState(cxt.Pos, -1, -1); st.Groups = gr; cxt.States.Add(st); } else if (quant.IsGreedy && group.Count >= quant.MinCount) { var st = new RxState(cxt.Pos, index, cxt.Groups.Count - 1); st.Groups = gr; cxt.States.Add(st); } cxt.Groups[cxt.Groups.Count - 1] = group; // if nongreedy, match at least min if (!quant.IsGreedy && group.Count >= quant.MinCount) { var st = new RxState(cxt.Pos, Targets[quant.To], cxt.Groups.Count); st.Groups = gr; cxt.States.Add(st); break; } if (quant.Group >= 0) StartCapture(ref cxt, quant.Group); index = Targets[quant.To]; break; } case Opcode.OpNumber.OP_RX_CAPTURE_START: { StartCapture(ref cxt, Ops[index].Index); ++index; break; } case Opcode.OpNumber.OP_RX_CAPTURE_END: { EndCapture(ref cxt, Ops[index].Index); ++index; break; } case Opcode.OpNumber.OP_RX_ACCEPT: { for (int i = cxt.LastOpenCapture + 1; i < Ops[index].Index; ++i) cxt.Captures[i].Start = cxt.Captures[i].End = -1; if (cxt.Captures != null) { res.StringCaptures = new string[cxt.Captures.Length]; for (int i = 0; i < cxt.Captures.Length; ++i) if (cxt.Captures[i].End != -1) res.StringCaptures[i] = str.Substring(cxt.Captures[i].Start, cxt.Captures[i].End - cxt.Captures[i].Start); } res.Start = pos; res.End = cxt.Pos; res.Captures = cxt.Captures; res.Matched = true; index = -1; break; } case Opcode.OpNumber.OP_JUMP: index = Targets[Ops[index].Index]; break; default: throw new System.Exception("PANIC: unhandled opcode " + Ops[index].Number); } } return res.Matched; }
private void RestoreGroups(ref RxContext cxt, ref RxSavedGroups groups) { if (groups.Data == null) return; for (int i = 0; i < groups.Data.Length; ++i) cxt.Captures[i + groups.Start] = groups.Data[i]; cxt.LastOpenCapture = groups.LastOpenCapture; cxt.LastClosedCapture = groups.LastClosedCapture; }
public RxState(int pos, int target, int group) { Pos = pos; Target = target; Group = group; Groups = new RxSavedGroups(); }