Example #1
0
        private Rule Compile(UncompiledRule uncompiledRule, ref int nextListRule, List <Rule> listRules)
        {
            var option = uncompiledRule.Choices;
            var m      = uncompiledRule.Method;

            var steps = new List <Step>();
            var args  = new List <bool>();

            foreach (var p in m.GetParameters())
            {
                var tag = p.GetCustomAttribute <ContextAttribute>()?.GetTag(p.ParameterType);

                var nonTerminal = p.GetCustomAttribute <NonTerminalAttribute>();
                if (nonTerminal != null)
                {
                    if (nonTerminal.Optional)
                    {
                        var keep = option % 2 == 1;
                        option = option / 2;

                        if (!keep)
                        {
                            args.Add(false);
                            continue;
                        }
                    }

                    // Non-terminal step

                    var type = p.ParameterType;

                    if (type.IsGenericType &&
                        (type.GetGenericTypeDefinition() == typeof(Pos <>) ||
                         type.GetGenericTypeDefinition() == typeof(Nullable <>)))
                    {
                        type = type.GetGenericArguments()[0];
                    }

                    if (type.IsArray)
                    {
                        throw new Exception($"Parameter {p.Name} in {m.Name}, array type {type} only allowed for lists.");
                    }

                    if (!MaxRank.TryGetValue(type, out var typeMaxRank))
                    {
                        throw new Exception($"Parameter {p.Name} in {m.Name}, unknown type {type}.");
                    }

                    if (nonTerminal.MaxRank != null)
                    {
                        typeMaxRank = (int)nonTerminal.MaxRank;
                    }

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

                    steps.Add(new Step(source, tag, false));
                    args.Add(true);
                    continue;
                }

                var terminal = p.GetCustomAttribute <TerminalAttribute>();
                if (terminal != null)
                {
                    if (terminal.Optional)
                    {
                        var keep = option % 2 == 1;
                        option = option / 2;

                        args.Add(keep);

                        if (!keep)
                        {
                            continue;
                        }
                    }
                    else
                    {
                        args.Add(true);
                    }

                    var type = p.ParameterType;
                    if (type != TokenType &&
                        type != typeof(Nullable <>).MakeGenericType(TokenType) &&
                        type != typeof(string) &&
                        type != typeof(Pos <string>))
                    {
                        throw new Exception($"Parameter {p.Name} in {m.Name}, unknown terminal type {type}.");
                    }

                    // Terminal step

                    steps.Add(new Step(Expand(terminal.Tokens, PublicChildren), tag, true));
                    continue;
                }

                var list = p.GetCustomAttribute <ListAttribute>();
                if (list != null)
                {
                    if (list.Min == 0)
                    {
                        // The zero-element list is handled in a separate rule, so the code
                        // below can assume that it has at least one element.
                        var keep = option % 2 == 1;
                        option = option / 2;

                        args.Add(keep);

                        if (!keep)
                        {
                            continue;
                        }
                    }
                    else
                    {
                        args.Add(true);
                    }

                    // List step

                    var type = p.ParameterType;
                    if (!type.IsArray || type.GetArrayRank() != 1)
                    {
                        throw new Exception($"Parameter {p.Name} in {m.Name}, unsupported list type {type}.");
                    }

                    type = type.GetElementType();

                    var separator =
                        list.Separator != null
                        ? Expand(new[] { list.Separator.Value }, PublicChildren)
                        : list.Terminator != null
                        ? Expand(new[] { list.Terminator.Value }, PublicChildren)
                        : null;

                    if (!MaxRank.TryGetValue(type, out var typeMaxRank))
                    {
                        throw new Exception($"Parameter {p.Name} in {m.Name}, unknown type {type}.");
                    }

                    if (list.MaxRank != null)
                    {
                        typeMaxRank = (int)list.MaxRank;
                    }

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

                    // The list is split as (LOOP* END) with:
                    //
                    //   LIST = LOOP LIST
                    //        | END
                    //
                    // So there are always two rules to consider.

                    var loopKey = new TypeLoopKey(list, type);
                    if (!_typeLoops.TryGetValue(loopKey, out var typeLoop))
                    {
                        var end  = nextListRule++;
                        var loop = nextListRule++;
                        typeLoop = new[] { end, loop };

                        listRules.Add(new Rule(
                                          isListEnd: true,
                                          type: type,
                                          steps: list.Terminator == null
                                ? new[] { new Step(itemSource, tag, false) }
                                : new[] { new Step(itemSource, tag, false), new Step(separator, tag, true) }
                                          ));

                        listRules.Add(new Rule(
                                          isListEnd: false,
                                          type: type,
                                          steps: separator == null
                                ? new[] { new Step(itemSource, tag, false), new Step(typeLoop, tag, false) }
                                : new[] { new Step(itemSource, tag, false), new Step(separator, tag, true), new Step(typeLoop, tag, false) }));

                        _typeLoops.Add(loopKey, typeLoop);
                    }

                    // If the list has a minimum size, we need an initial segment before starting the loop,
                    // e.g. FULL = LOOP LOOP LOOP LIST <-- a list of min size 4 (!)

                    if (list.Min > 2)
                    {
                        var init      = nextListRule++;
                        var initSteps = new List <Step>();

                        for (var i = 1; i < list.Min; ++i)
                        {
                            initSteps.Add(new Step(itemSource, tag, false));
                            if (separator != null)
                            {
                                initSteps.Add(new Step(separator, tag, true));
                            }
                        }

                        initSteps.Add(new Step(typeLoop, tag, false));
                        listRules.Add(new Rule(isListEnd: false, type: type, steps: initSteps));

                        steps.Add(new Step(new[] { init }, tag, false));
                    }
                    else if (list.Min == 2)
                    {
                        // typeLoop[1] is the loop rule "LOOP LIST" which always
                        // contains at least two elements. So this is an optimization
                        // to avoid creating an 'init' rule.
                        steps.Add(new Step(new [] { typeLoop[1] }, tag, false));
                    }
                    else
                    {
                        steps.Add(new Step(typeLoop, tag, false));
                    }

                    continue;
                }

                throw new Exception($"Parameter {p.Name} in {m.Name}: nothing to do.");
            }

            return(new Rule(m, steps, args, uncompiledRule.Context ?? -1));
        }
Example #2
0
 private bool Equals(TypeLoopKey other) =>
 _separator == other._separator && _terminator == other._terminator && _type == other._type;
Example #3
0
 private bool Equals(TypeLoopKey other)
 {
     return(_separator == other._separator && _terminator == other._terminator && _type == other._type);
 }