private bool Equals(RankedType other) { return(Type == other.Type && Rank == other.Rank); }
/// <summary> Extract the reflection rules from type <paramref name="t"/>. </summary> public RuleSet(Type t, Type token, Type result, int end, IReadOnlyDictionary <int, IReadOnlyList <int> > publicChildren) { PublicChildren = publicChildren; // Extract token names TokenNames = token.GetEnumNames(); TokenType = token; EndOfStream = end; var maxRank = new Dictionary <Type, int>(); var rankedTypeId = new Dictionary <RankedType, int>(); var rankedTypes = new List <RankedType>(); var ruleId = new List <List <int> >(); var uncompiledRules = new List <UncompiledRule>(); // Explore methods to find those to be compiled foreach (var m in t.GetMethods()) { var ruleAttribute = m.GetCustomAttribute <RuleAttribute>(); if (ruleAttribute == null) { continue; } // Update the max-rank for this type int tMaxRank; if (!maxRank.TryGetValue(m.ReturnType, out tMaxRank) || tMaxRank < ruleAttribute.Rank) { maxRank[m.ReturnType] = ruleAttribute.Rank; } // Get or create the RankedType and its identifier var rt = new RankedType(m.ReturnType, ruleAttribute.Rank); int rtid; if (!rankedTypeId.TryGetValue(rt, out rtid)) { rankedTypes.Add(rt); ruleId.Add(new List <int>()); rankedTypeId.Add(rt, rtid = rankedTypeId.Count); } // Determine the number of optional parameters, to generate one // rule for each parameter combination. This includes both optional // terminals AND lists which allow zero elements. var options = m.GetParameters() .Count(p => (p.GetCustomAttribute <TerminalAttribute>()?.Optional ?? false) || (p.GetCustomAttribute <NonTerminalAttribute>()?.Optional ?? false) || (p.GetCustomAttribute <ListAttribute>()?.Min ?? 1) == 0); var combinations = 1 << options; // Allocate news rule (not compiled yet) and bind to RankedType for (var i = 0; i < combinations; ++i) { var rid = uncompiledRules.Count + TokenNames.Count; uncompiledRules.Add(new UncompiledRule(m, i)); ruleId[rtid].Add(rid); } } MaxRank = maxRank; RankedTypeId = rankedTypeId; RankedTypes = rankedTypes; RuleId = ruleId; // All RankedTypes have been identified. Now explore method parameters // to deduce actual rules, and generate any 'list' (star, actually) rules // on-the-fly. var listRules = new List <Rule>(); var nextListRule = uncompiledRules.Count + TokenNames.Count; var rules = uncompiledRules.Select(r => Compile(r, ref nextListRule, listRules)).ToArray(); Rules = rules.Concat(listRules).ToArray(); // The starting tokens for each rule var isStartOf = Enumerable.Range(TokenNames.Count, Rules.Count).Select(i => Rules.Select((r, j) => r.StartsWithRule(i) ? j : -1) .Where(j => j >= 0).ToArray()).ToArray(); Action <int, int> propagateStart = null; propagateStart = (stok, rul) => { foreach (var rul2 in isStartOf[rul]) { if (!Rules[rul2].StartingTokens.Contains(stok)) { Rules[rul2].StartingTokens.Add(stok); propagateStart(stok, rul2); } } }; for (var r = 0; r < Rules.Count; ++r) { foreach (var tok in Rules[r].StartingTokens) { propagateStart(tok, r); } } // The reducing tokens for each rule foreach (var r in Rules) { r.AddReducingToken(this, EndOfStream); r.ExtractEndingTokens(this); } // Initial rules int resultMaxRank; if (!MaxRank.TryGetValue(result, out resultMaxRank)) { throw new Exception($"Unknown result type {result}."); } InitialRules = Enumerable.Range(0, resultMaxRank + 1).SelectMany(i => { int id; return(RankedTypeId.TryGetValue(new RankedType(result, i), out id) ? RuleId[id] : new int[0]); }).ToArray(); }