Example #1
0
 private bool Equals(RankedType other)
 {
     return(Type == other.Type && Rank == other.Rank);
 }
Example #2
0
        /// <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
                if (!maxRank.TryGetValue(m.ReturnType, out var 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);

                if (!rankedTypeId.TryGetValue(rt, out var 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;

                var context = m.GetCustomAttribute <ContextAttribute>();

                // 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, context?.GetTag(m.ReturnType)));
                    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();

            void PropagateStart(int stok, int 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

            if (!MaxRank.TryGetValue(result, out var resultMaxRank))
            {
                throw new Exception($"Unknown result type {result}.");
            }

            InitialRules = Enumerable.Range(0, resultMaxRank + 1)
                           .SelectMany(i => RankedTypeId.TryGetValue(new RankedType(result, i), out var id) ? RuleId[id] : new int[0])
                           .ToArray();
        }