public static FA Repeat(FA expr, int minOccurs = -1, int maxOccurs = -1, int accept = -1) { expr = expr.Clone(); if (minOccurs > 0 && maxOccurs > 0 && minOccurs > maxOccurs) { throw new ArgumentOutOfRangeException(nameof(maxOccurs)); } FA result; switch (minOccurs) { case -1: case 0: switch (maxOccurs) { case -1: case 0: return(Repeat(Optional(expr, accept), 1, 0, accept)); /*result = new FA(); * var final = new FA(true, accept); * final.EpsilonTransitions.Add(result); * foreach (var afa in expr.FillAcceptingStates()) * { * afa.IsAccepting = false; * afa.EpsilonTransitions.Add(final); * } * result.EpsilonTransitions.Add(expr); * result.EpsilonTransitions.Add(final); * //Debug.Assert(null != result.FirstAcceptingState); * return result;*/ case 1: result = Optional(expr, accept); //Debug.Assert(null != result.FirstAcceptingState); return(result); default: var l = new List <FA>(); expr = Optional(expr); l.Add(expr); for (int i = 1; i < maxOccurs; ++i) { l.Add(expr.Clone()); } result = Concat(l, accept); //Debug.Assert(null != result.FirstAcceptingState); return(result); } case 1: switch (maxOccurs) { case -1: case 0: result = new FA(); var final = new FA(true, accept); final.EpsilonTransitions.Add(result); foreach (var afa in expr.FillAcceptingStates()) { afa.IsAccepting = false; afa.EpsilonTransitions.Add(final); } result.EpsilonTransitions.Add(expr); //Debug.Assert(null != result.FirstAcceptingState); return(result); case 1: //Debug.Assert(null != expr.FirstAcceptingState); return(expr); default: result = Concat(new FA[] { expr, Repeat(expr.Clone(), 0, maxOccurs - 1) }, accept); //Debug.Assert(null != result.FirstAcceptingState); return(result); } default: switch (maxOccurs) { case -1: case 0: result = Concat(new FA[] { Repeat(expr, minOccurs, minOccurs, accept), Repeat(expr, 0, 0, accept) }, accept); //Debug.Assert(null != result.FirstAcceptingState); return(result); case 1: throw new ArgumentOutOfRangeException(nameof(maxOccurs)); default: if (minOccurs == maxOccurs) { var l = new List <FA>(); l.Add(expr); //Debug.Assert(null != expr.FirstAcceptingState); for (int i = 1; i < minOccurs; ++i) { var e = expr.Clone(); //Debug.Assert(null != e.FirstAcceptingState); l.Add(e); } result = Concat(l, accept); //Debug.Assert(null != result.FirstAcceptingState); return(result); } result = Concat(new FA[] { Repeat(expr.Clone(), minOccurs, minOccurs, accept), Repeat(Optional(expr.Clone()), maxOccurs - minOccurs, maxOccurs - minOccurs, accept) }, accept); //Debug.Assert(null != result.FirstAcceptingState); return(result); } } // should never get here throw new NotImplementedException(); }
internal static void EmitFAPart(FA fa, IList <int[]> prog) { //fa = fa.ToDfa(); //fa.TrimDuplicates(); //fa = fa.ToGnfa(); if (fa.IsNeutral) { foreach (var efa in fa.EpsilonTransitions) { fa = efa; } } var acc = fa.FillAcceptingStates(); foreach (var afa in acc) { if (!afa.IsFinal) { var ffa = new FA(true, afa.AcceptSymbol); afa.EpsilonTransitions.Add(ffa); afa.IsAccepting = false; } } var rendered = new Dictionary <FA, int>(); var swFixups = new Dictionary <FA, int>(); var jmpFixups = new Dictionary <FA, int>(); var l = new List <FA>(); fa.FillClosure(l); for (int ic = l.Count, i = 0; i < ic; ++i) { var reused = false; var cfa = l[i]; if (!cfa.IsFinal) { rendered.Add(cfa, prog.Count); } else { foreach (var r in rendered) { if (r.Key.IsFinal) { if (r.Key.IsAccepting && cfa.AcceptSymbol == r.Key.AcceptSymbol) { // we can reuse this rendered.Add(cfa, r.Value); reused = true; break; } } } if (!reused) { rendered.Add(cfa, prog.Count); } } if (!cfa.IsFinal) { int swfixup = prog.Count; prog.Add(null); // switch swFixups.Add(cfa, swfixup); } else { #if DEBUG System.Diagnostics.Debug.Assert(cfa.IsAccepting); #endif if (!reused) { prog.Add(new int[] { Save, 1 }); // save prog.Add(new int[] { Match, cfa.AcceptSymbol }); } } } for (int ic = l.Count, i = 0; i < ic; ++i) { var cfa = l[i]; if (!cfa.IsFinal) { var sw = new List <int>(); sw.Add(Switch); var rngGrps = cfa.FillInputTransitionRangesGroupedByState(); foreach (var grp in rngGrps) { var dst = rendered[grp.Key]; sw.AddRange(grp.Value); sw.Add(-1); sw.Add(dst); } if (1 < sw.Count) { if (0 < cfa.EpsilonTransitions.Count) { sw.Add(-2); foreach (var efa in cfa.EpsilonTransitions) { var dst = rendered[efa]; sw.Add(dst); } } } else { // basically a NOP. Will get removed sw[0] = Jmp; sw.Add(swFixups[cfa] + 1); } prog[swFixups[cfa]] = sw.ToArray(); } var jfi = -1; if (jmpFixups.TryGetValue(cfa, out jfi)) { var jmp = new int[2]; jmp[0] = Jmp; jmp[1] = prog.Count; prog[jfi] = jmp; } } }