示例#1
0
 protected SemanticParser(SemanticParserGrammar <TAstNode, TInput, TPosition> grammar, ParserContextBase <TAstNode, TInput, TPosition> context) : base(true, SymbolId.Eof, default(Id <DfaState <LetterId> >))
 {
     this.Grammar = grammar;
     this.Context = context;
 }
        public SemanticParserGrammarBuilder(IUnicodeMapper <TInput> mapper, TInput?eof)
        {
            string GetGrammarKeyForDisplay()
            {
                return($"typeof({typeof(TAstNode).FullName})");
            }

            string MemberInfoForDisplay(MethodBase member)
            {
                return(member == null ? "(assembly)" : $"{member.DeclaringType.FullName}.{member.Name}");
            }

            var errors = new List <Exception>();

            try {
                var parts = SemanticParserGrammar <TAstNode, TInput, TPosition> .FindGrammarParts()
                            .OrderByDescending(p => p.Key.GetType().Name)
                            .ThenBy(p => (p.Key as GrammarSymbolAttribute)?.SymbolName ?? (p.Key as CharsetAttribute)?.CharsetName ?? "")
                            .ToList();

                // Compute charsets
                var charsetQueue = new Queue <KeyValuePair <string, CharsetNode> >(parts
                                                                                   .Select(p => p.Key)
                                                                                   .OfType <CharsetAttribute>()
                                                                                   .Select(a => new KeyValuePair <string, CharsetNode>(a.CharsetName, CharsetParser.Parse(a.CharsetExpression))));
                var charsets = charsetQueue
                               .SelectMany(p => p.Value.GetCharsetNames())
                               .Except(charsetQueue.Select(p => p.Key), StringComparer.OrdinalIgnoreCase)
                               .ToDictionary(n => n, UnicodeRanges.FromUnicodeName, StringComparer.OrdinalIgnoreCase);
                var provider  = new UnicodeCharSetProvider(charsets);
                var skipCount = 0;
                while (charsetQueue.Count > 0)
                {
                    var current = charsetQueue.Dequeue();
                    if (current.Value.GetCharsetNames().All(charsets.ContainsKey))
                    {
                        charsets.Add(current.Key, current.Value.Compute(provider));
                        skipCount = 0;
                    }
                    else
                    {
                        charsetQueue.Enqueue(current);
                        if (skipCount++ > charsetQueue.Count)
                        {
                            errors.Add(new InvalidOperationException($"The charsets cannot be computed because {String.Join(", ", charsetQueue.Select(p => p.Key))} contain circular references"));
                            break;
                        }
                    }
                }

                // Gather symbol information
                var startsymbol = parts.Select(p => p.Key).OfType <StartSymbolAttribute>().SingleOrDefault();
                if (startsymbol == null)
                {
                    errors.Add(new InvalidOperationException($"Start symbol has not been defined: [assembly: StartSymbol({GetGrammarKeyForDisplay()}, ...)]"));
                }
                foreach (var symbol in parts
                         .Select(p => p.Key)
                         .OfType <GrammarSymbolAttribute>()
                         .GroupBy(a => a.SymbolName, a => a.SymbolKind, StringComparer.OrdinalIgnoreCase))
                {
                    if (symbol.Distinct().Skip(1).Any())
                    {
                        errors.Add(new InvalidOperationException($"The symbol {symbol.Key} must not be defined as both terminal and nonterminal"));
                    }
                    else if (StringComparer.OrdinalIgnoreCase.Equals(symbol.Key, startsymbol?.SymbolName) && (symbol.First() != SymbolKind.Nonterminal))
                    {
                        errors.Add(new InvalidOperationException($"The start symbol {symbol.Key} must be a nonterminal"));
                    }
                    this.symbolsByName.Add(symbol.Key, this.symbolsByName.Count + 1);
                }

                SymbolId GetSymbol(string symbolName)
                {
                    if (this.symbolsByName.TryGetValue(symbolName, out var id))
                    {
                        return(id);
                    }
                    errors.Add(new InvalidOperationException($"The symbol {symbolName} has not been defined. If the symbol name is correct, define it as virtual: [assembly: VirtualSymbol({GetGrammarKeyForDisplay()}, ...)]"));
                    return(SymbolId.Eof);
                }

                MethodBase PopulateGenericArguments(MethodBase methodBase, GrammarSymbolAttribute attribute)
                {
                    var genericTypeParameters = attribute.GenericTypeParameters;

                    if (methodBase?.DeclaringType.IsGenericTypeDefinition == true)
                    {
                        var typeGenericArguments = methodBase.DeclaringType.GetGenericArguments();
                        if (genericTypeParameters.Length < typeGenericArguments.Length)
                        {
                            errors.Add(new InvalidOperationException($"Missing type generic arguments for {attribute} on {MemberInfoForDisplay(methodBase)}"));
                            return(methodBase);
                        }
                        var genericType = methodBase.DeclaringType.MakeGenericType(genericTypeParameters.Take(typeGenericArguments.Length).ToArray());
                        genericTypeParameters = genericTypeParameters.Skip(typeGenericArguments.Length).ToArray();
                        IReadOnlyDictionary <Type, Type> genericArgumentMap = genericType.GetGenericArguments().Select((t, ix) => new KeyValuePair <Type, Type>(typeGenericArguments[ix], t)).ToDictionary(p => p.Key, p => p.Value);
                        var mappedParameters = methodBase.GetParameters().Select(p => genericArgumentMap.GetValueOrDefault(p.ParameterType, p.ParameterType)).ToArray();
                        if (methodBase is ConstructorInfo)
                        {
                            methodBase = genericType.GetConstructor(mappedParameters);
                        }
                        else
                        {
                            methodBase = genericType.GetMethod(methodBase.Name, BindingFlags.Static | BindingFlags.Public, null, mappedParameters, null);
                        }
                    }
                    if (methodBase is MethodInfo method && method.IsGenericMethodDefinition)
                    {
                        if (method.GetGenericArguments().Length != genericTypeParameters.Length)
                        {
                            errors.Add(new InvalidOperationException($"Invalid number of method generic arguments for {attribute} on {MemberInfoForDisplay(methodBase)}"));
                        }
                        methodBase = method.MakeGenericMethod(genericTypeParameters);
                    }
                    else if (genericTypeParameters.Length > 0)
                    {
                        errors.Add(new InvalidOperationException($"Excess generic arguments for {attribute} on {MemberInfoForDisplay(methodBase)}"));
                    }
                    return(methodBase);
                }