private void substituteProductions(Dictionary <string, List <ProductionInfo> > productionsDict, ProductionInfo[] substitutes, // same LHS bool mixWithSource) { // this function is part of optimization of given production rules // we could have case, that sub production is marked and the one where there is replacement as well // in such case which marking to choose? so we don't allow substitutes to have markings if (substitutes.Any(it => it.IsMarked)) { throw new ArgumentException(); } string sub_lhs = substitutes.Select(it => it.LhsSymbol).Distinct().Single(); // making sure LHS symbol is the same Console.WriteLine("Substituting " + sub_lhs); foreach (string lhs in productionsDict.Keys.ToArray()) { var replacements = new List <ProductionInfo>(); foreach (ProductionInfo prod in productionsDict[lhs]) { // nothing to replace if (!prod.RhsSymbols.Any(it => it.SymbolName.Equals(sub_lhs))) { replacements.Add(prod); } else { // -1 -- use original symbol, >=0 -- substitute (the value is the index of substitution) IEnumerable <CycleCounter> counters = prod.RhsSymbols.ZipWithIndex() .Select(it => { bool hit = it.Item1.SymbolName.Equals(sub_lhs); return(new CycleCounter(((!hit || mixWithSource) ? -1 : 0), (hit ? substitutes.Length : 0), it.Item2)); }).ToArray(); // we have initial run only in case if we mix substitutions with original production, otherwise it pure substitution bool pass_first_as_source = mixWithSource; do { if (pass_first_as_source) { pass_first_as_source = false; if (!counters.All(it => it.Value == -1)) { throw new Exception("Oops, something wrong."); } // it is simply better to add original production instead of re-creating it from symbols // after all, for every rhs symbol we would have -1 value, meaning "use original" replacements.Add(prod); continue; } var p = new ProductionInfo(prod.Position, prod.LhsSymbol, prod.Recursive, counters.SyncZip(prod.RhsSymbols) .Select(it => it.Item1.Value == -1 ? new[] { it.Item2 } : substitutes[it.Item1.Value].RhsSymbols).Flatten(), prod.PassedMarkedWith); // if there was no action code, no point of building proxy for it if (prod.ActionCode != null) { FuncCallCode func_call = (FuncCallCode)(prod.ActionCode.Body); // do not rename those parameters which have counter == -1 IEnumerable <Tuple <FuncParameter, int>[]> parameters = null; parameters = counters.SyncZip(prod.ActionCode.Parameters) .Select(cit => cit.Item1.Value == -1 ? new[] { Tuple.Create(cit.Item2, cit.Item1.Index) } : substitutes[cit.Item1.Value].ActionCode.Parameters.Select(x => Tuple.Create(x, cit.Item1.Index)).ToArray()) .ToArray(); // only subsituted parameters are renamed Dictionary <Tuple <FuncParameter, int>, FuncParameter> param_map = FuncParameter.BuildParamMapping(parameters.Flatten()); p.ActionCode = CodeLambda.CreateProxy(lhs + "_sub__", // parameters parameters.Flatten().Select(it => param_map[it]), prod.ActionCode.ResultTypeName, functionsRegistry.Add(prod.ActionCode), // arguments counters.SyncZip(parameters) .Select(cit => cit.Item1.Value == -1 ? param_map[cit.Item2.Single()].NameAsCode() : new FuncCallCode(functionsRegistry.Add(substitutes[cit.Item1.Value].ActionCode), cit.Item2.Select(x => param_map[x].NameAsCode()))) ); } replacements.Add(p); }while (counters.Iterate()); } } productionsDict[lhs] = replacements; } }