Ejemplo n.º 1
0
        private OpResult TryParseStruct(ParserState state, LexerData data, ParsingIndex index)
        {
            if (!TryReadToken(data, TokenType.Identifier, out var namePos, out var structName))
            {
                return(OpResult.Fail($"Struct name is not defined at {namePos}"));
            }

            if (_mapper != null)
            {
                structName = _mapper.RemapTypeName(TypeKind.Struct, structName, state.RemappedTypes);
            }

            var validationResult = ValidateTypeName(structName, namePos, index);

            if (validationResult.HasError)
            {
                return(validationResult);
            }

            if (!TryReadToken(data, TokenType.CurlyBraceLeft, out var bracePos, out _))
            {
                return(OpResult.Fail($"Missing `{{` after struct `{structName}` declaration at {bracePos}"));
            }

            state.StartBlock(ParsingBlockType.Struct, structName);
            index.BeginStruct(structName);

            return(OpResult.Ok());
        }
Ejemplo n.º 2
0
        public LexerResult Read(Stream input)
        {
            var state = new LexerState();
            var data  = new LexerData();

            var offset = 0;
            int bytesRead;

            while ((bytesRead = input.Read(_buffer, offset, _buffer.Length - offset)) > 0)
            {
                var length = offset + bytesRead;
                var span   = _buffer.AsSpan(0, length);

                var opResult = ReadTokens(state, span, offset, data);
                if (opResult.HasError)
                {
                    return(LexerResult.Fail(opResult.Error));
                }

                offset = state.CurrBlockDataLen;
                if (offset == _buffer.Length)
                {
                    Array.Resize(ref _buffer, _buffer.Length * 2);
                }
            }

            return(LexerResult.Ok(data));
        }
Ejemplo n.º 3
0
        private OpResult TryParseEnum(ParserState state, LexerData data, ParsingIndex index)
        {
            if (!TryReadToken(data, TokenType.Identifier, out var namePos, out var enumName))
            {
                return(OpResult.Fail($"Enum name is not defined at {namePos}"));
            }

            if (_mapper != null)
            {
                enumName = _mapper.RemapTypeName(TypeKind.Enum, enumName, state.RemappedTypes);
            }

            var validationResult = ValidateTypeName(enumName, namePos, index);

            if (validationResult.HasError)
            {
                return(validationResult);
            }

            if (!TryReadToken(data, TokenType.Colon, out var colonPos, out _))
            {
                return(OpResult.Fail($"Missing : at {colonPos}"));
            }

            if (!TryReadToken(data, TokenType.Identifier, out var undTypePos, out var underlyingType))
            {
                return(OpResult.Fail($"Missing enum `{enumName}` type definition at {undTypePos}"));
            }

            if (!ParsingHelper.IsInteger(underlyingType))
            {
                return(OpResult.Fail($"Enum `{enumName}` has invalid underlying type `{underlyingType}`"));
            }

            var isFlags = false;

            if (TryReadToken(data, TokenType.Identifier, out var flagsPos, out var keyword))
            {
                if (keyword != FlagsId)
                {
                    return(OpResult.Fail($"Unknown enum `{enumName}` optional keyword `{keyword}` at {flagsPos}." +
                                         " Only `flags` keyword is supported."));
                }

                isFlags = true;
            }

            if (!TryReadToken(data, TokenType.CurlyBraceLeft, out var bracePos, out _))
            {
                return(OpResult.Fail($"Missing `{{` after enum `{enumName}` declaration at {bracePos}"));
            }

            state.StartBlock(ParsingBlockType.Enum, enumName);
            index.BeginEnum(enumName, underlyingType, isFlags);

            return(OpResult.Ok());
        }
Ejemplo n.º 4
0
        private static OpResult ParseEnumContent(ParserState state, LexerData data, ParsingIndex index)
        {
            var enumName = state.CurrentBlock.Name;

            if (TryReadToken(data, TokenType.CurlyBraceRight, out _, out _))
            {
                state.EndBlock();
                index.EndEnum(enumName);

                return(index.IsEnumNonEmpty(enumName) ?
                       OpResult.Ok() :
                       OpResult.Fail($"Enum `{enumName}` is empty. No default value is available"));
            }

            if (!TryReadToken(data, TokenType.Identifier, out var namePos, out var name))
            {
                return(OpResult.Fail($"Failed to read enum name at {namePos}"));
            }

            if (!ParsingHelper.IsNameValid(name))
            {
                return(OpResult.Fail($"Enum item `{enumName}.{name}` has invalid name at {namePos}"));
            }

            if (index.IsEnumContainsItem(enumName, name))
            {
                return(OpResult.Fail($"Enum item `{enumName}.{name}` is defined second time at {namePos}"));
            }

            if (!TryReadToken(data, TokenType.Assignment, out var assignPos, out _))
            {
                return(OpResult.Fail($"Missing `=` at {assignPos}"));
            }

            if (!TryReadToken(data, TokenType.Identifier, out var valPos, out var value))
            {
                return(OpResult.Fail($"Missing enum item `{enumName}.{name}` default value at {valPos}"));
            }

            if (!IsEnumValueValid(enumName, value, index))
            {
                return(OpResult.Fail($"Enum item `{enumName}.{name}` has invalid default value at {valPos}"));
            }

            if (!TryReadToken(data, TokenType.Semicolon, out var semicolonPos, out _))
            {
                return(OpResult.Fail($"Missing ; at {semicolonPos}"));
            }

            index.PutEnumItem(enumName, name, value);

            return(OpResult.Ok());
        }
Ejemplo n.º 5
0
        public ParsingResult Parse(LexerData data)
        {
            var state = new ParserState();
            var index = new ParsingIndex();

            while (data.Tokens.Count > 0)
            {
                var opResult = ParseData(state, data, index);
                if (opResult.HasError)
                {
                    return(ParsingResult.Fail(opResult.Error));
                }
            }

            return(ParsingResult.Ok(index.BuildParsedData()));
        }
Ejemplo n.º 6
0
        private OpResult TryParseType(string id, Position p, ParserState state, LexerData data, ParsingIndex index)
        {
            switch (id)
            {
            case EnumId:
                return(TryParseEnum(state, data, index));

            case ArrayId:
                return(TryParseArray(state, data, index));

            case StructId:
                return(TryParseStruct(state, data, index));
            }

            return(OpResult.Fail($"Unknown datatype `{id}` declaration at {p}"));
        }
Ejemplo n.º 7
0
 /// <summary>
 /// Constructs a Lexer using the necessary data stored in the LexerData instance.
 /// </summary>
 /// <param name="lexerData">The LexerData containing information necessary to construct the Lexer.</param>
 public Lexer(LexerData lexerData)
 {
     this.groupSymbolCodes = lexerData.GroupSymbolCodes;
     this.regex = lexerData.Regex;
 }
Ejemplo n.º 8
0
        private OpResult ParseStructContent(ParserState state, LexerData data, ParsingIndex index)
        {
            var structName = state.CurrentBlock.Name;

            if (TryReadToken(data, TokenType.CurlyBraceRight, out _, out _))
            {
                state.EndBlock();
                index.EndStruct(structName);

                return(index.IsStructNonEmpty(structName) ?
                       OpResult.Ok() :
                       OpResult.Fail($"Struct `{structName}` is zero-sized"));
            }

            if (!TryReadToken(data, TokenType.Identifier, out var typePos, out var fieldType))
            {
                return(OpResult.Fail($"Missing field type definition at {typePos}"));
            }

            if (_mapper != null)
            {
                fieldType = _mapper.RemapMemberType(fieldType, state.RemappedTypes);
            }

            if (!TryReadToken(data, TokenType.Identifier, out var namePos, out var fieldName))
            {
                return(OpResult.Fail($"Missing field name definition at {namePos}"));
            }

            if (!ParsingHelper.IsNameValid(fieldName))
            {
                return(OpResult.Fail($"Field `{structName}.{fieldName}` has invalid name at {namePos}"));
            }

            if (index.IsStructContainsField(structName, fieldName))
            {
                return(OpResult.Fail($"Field `{structName}.{fieldName}` is defined second time at {namePos}"));
            }

            if (!IsTypeKnown(fieldType, index))
            {
                return(OpResult.Fail($"Field `{structName}.{fieldName}` has unknown type `{fieldType}` at {typePos}"));
            }

            string fieldDefaultValue = null;

            if (TryReadToken(data, TokenType.Assignment, out _, out _))
            {
                if (!TryReadToken(data, TokenType.Identifier, out var valuePos, out fieldDefaultValue))
                {
                    return(OpResult.Fail($"Missing default value of field `{structName}.{fieldName}` at {valuePos}"));
                }

                if (_mapper != null)
                {
                    fieldDefaultValue = _mapper.RemapMemberDefaultValue(fieldType, fieldDefaultValue);
                }

                if (!IsDefaultValueValid(fieldType, fieldDefaultValue, index))
                {
                    return(OpResult.Fail($"Invalid default value `{fieldDefaultValue}` is defined for " +
                                         $"field `{structName}.{fieldName}` at {valuePos}"));
                }
            }

            if (!TryReadToken(data, TokenType.Semicolon, out var semicolonPos, out _))
            {
                return(OpResult.Fail($"Missing ; after field `{structName}.{fieldName}` declaration at {semicolonPos}"));
            }

            index.PutStructField(structName, fieldType, fieldName, fieldDefaultValue);
            return(OpResult.Ok());
        }
Ejemplo n.º 9
0
        private OpResult TryParseArray(ParserState state, LexerData data, ParsingIndex index)
        {
            if (!TryReadToken(data, TokenType.Identifier, out var namePos, out var arrayName))
            {
                return(OpResult.Fail($"Array name is not defined at {namePos}"));
            }

            if (_mapper != null)
            {
                arrayName = _mapper.RemapTypeName(TypeKind.Array, arrayName, state.RemappedTypes);
            }

            var validationResult = ValidateTypeName(arrayName, namePos, index);

            if (validationResult.HasError)
            {
                return(validationResult);
            }

            if (!TryReadToken(data, TokenType.Identifier, out var typePos, out var itemType))
            {
                return(OpResult.Fail($"Array `{arrayName}` items type is not defined at {typePos}"));
            }

            if (_mapper != null)
            {
                itemType = _mapper.RemapMemberType(itemType, state.RemappedTypes);
            }

            if (!IsTypeKnown(itemType, index))
            {
                return(OpResult.Fail($"Array `{arrayName}` has unknown items type `{itemType}` at {typePos}"));
            }

            if (!TryReadToken(data, TokenType.SquareBraceLeft, out var lBracePos, out _))
            {
                return(OpResult.Fail($"Missing [ at {lBracePos}"));
            }

            if (!TryReadToken(data, TokenType.Identifier, out var lengthPos, out var lengthString))
            {
                return(OpResult.Fail($"Array `{arrayName}` length is not defined at {lengthPos}"));
            }

            if (!int.TryParse(lengthString, out var length))
            {
                return(OpResult.Fail($"Failed to parse `{arrayName}` length at {lengthPos}"));
            }

            if (length <= 0)
            {
                return(OpResult.Fail($"Array `{arrayName}` has invalid length {length}"));
            }

            if (!TryReadToken(data, TokenType.SquareBraceRight, out var rBracePos, out _))
            {
                return(OpResult.Fail($"Missing ] at {rBracePos}"));
            }

            string defValue = null;

            if (TryReadToken(data, TokenType.Assignment, out _, out _))
            {
                if (!TryReadToken(data, TokenType.Identifier, out var valuePos, out defValue))
                {
                    return(OpResult.Fail($"Missing default item value of array `{arrayName}` at {valuePos}"));
                }

                if (_mapper != null)
                {
                    defValue = _mapper.RemapMemberDefaultValue(itemType, defValue);
                }

                if (!IsDefaultValueValid(itemType, defValue, index))
                {
                    return(OpResult.Fail($"Invalid default value `{defValue}` is defined for array `{arrayName}` at {valuePos}"));
                }
            }

            if (!TryReadToken(data, TokenType.Semicolon, out var semicolonPos, out _))
            {
                return(OpResult.Fail($"Missing ; at {semicolonPos}"));
            }

            index.PutArray(arrayName, itemType, length, defValue);

            return(OpResult.Ok());
        }
Ejemplo n.º 10
0
        public static Grammar ParseGrammar(string specificationPath, out IList<string> warningMessages)
        {
            #if BOOTSTRAP
            GrammarLexer lexer = new GrammarLexer();
            lexer.SourceString = File.ReadAllText(specificationPath);
            Token token = lexer.GetNextToken();

            //sem si budeme ukládat chyby a warningy; pokud se vyskytne nějaká chyba, čteme dál a
            //až na konci vyhodíme výjimku se všemi chybami a warningami; pokud vše proběhne bez
            //závažnějších chyb, tak seznam warningů pošlem zpátky volajícímu
            List<string> errorMessages = new List<string>();
            warningMessages = new List<string>();

            //the code to be inserted at the start of the generated code
            string headerCode = null;

            //seznam jmen všech symbolů; slouží potom jako převodní tabulka z kódu symbolu na jeho jméno
            List<string> symbolNames = new List<string>();
            //"inverzní tabulka" k symbolNames, která nám pro jméno symbolu řekne jeho kód
            Dictionary<string, int> symbolCodes = new Dictionary<string, int>();

            //seznam regulárních výrazů definujících terminální symboly; netvoříme z nich rovnou výsledný
            //lexerův regex, ale ukládáme si je zvlášť, abychom v případě chyby při kompilaci celkového regexu
            //mohli jednodušše otestovat, které výrazy jsou na vině
            List<string> regexes = new List<string>();
            //pro každý výraz v regexes si pamatujeme pozici, kde jsme ho našli, abychom mohli vydat podrobnější
            //zprávu
            List<int> regexLines = new List<int>();
            List<int> regexColumns = new List<int>();
            //pro každý výraz v regexes si také pamatujeme kód symbolu, který je popisován oním výrazem,
            //v případě, že výraz má matchovat řetězce, které chceme ignorovat, je v tomto poli hodnota -1;
            //jedná se o runtime data, která pak přímo používá náš lexer
            List<int> groupSymbolCodes = new List<int>();
            //výsledný regulární výraz, pomocí kterého lexer scanuje tokeny; druhá část runtime dat pro náš lexer
            Regex regex = null;

            //jméno pseudoterminálu, jehož tokeny se nemají posílat parseru, ale zahazovat
            string nullTerminal = "";
            //globální optiony .NETímu regex stroji (case insensitive, multiline...)
            string regexOpts = null;

            if (token.SymbolCode == CODE_HEADER) {
                token = lexer.GetNextToken();
                headerCode = token.Value;
                token = lexer.GetNextToken();
            }

            // skipping LEXER
            token = lexer.GetNextToken();

            if (token.SymbolCode == CODE_NULL) {
                token = lexer.GetNextToken();
                nullTerminal = token.Value;
                token = lexer.GetNextToken();
            }

            if (token.SymbolCode == CODE_REGEXOPTS) {
                regexOpts = token.Value;
                token = lexer.GetNextToken();
            }

            symbolNames.Add("$end");
            symbolCodes["$end"] = 0;

            while (token.SymbolCode == CODE_IDENTIFIER)
            {
                string symbol = token.Value;
                token = lexer.GetNextToken();
                // skipping EQUALS
                token = lexer.GetNextToken();
                string capturingRegex = token.Value;
                token = lexer.GetNextToken();

                if (symbol == nullTerminal)
                {
                    groupSymbolCodes.Add(-1);
                }
                else
                {
                    if (!symbolCodes.ContainsKey(symbol))
                    {
                        symbolNames.Add(symbol);
                        symbolCodes[symbol] = symbolNames.Count - 1;
                    }
                    groupSymbolCodes.Add(symbolCodes[symbol]);
                }

                regexes.Add(capturingRegex);
                regexLines.Add(token.LineNumber);
                regexColumns.Add(token.ColumnNumber);
            }

            StringBuilder pattern = new StringBuilder();

            if (regexOpts != null)
                pattern.Append(regexOpts);

            for (int i = 0; i < regexes.Count; i++)
            {
                //všechny uživatelovi regulární výrazy oddělíme ořítky a zapíšeme je v pořadí, v jakém nám
                //je zadal (v .NETím Regex enginu mají výrazy v ořítku víc nalevo přednost) a každý výraz
                //strčíme do capture groupy pojmenované __i, kde i je pořadové číslo výrazu, počítáno od 0
                if (i != 0)
                    pattern.Append('|');
                pattern.AppendFormat("(?<{0}>{1})", "__" + i.ToString(), regexes[i]);
            }

            try
            {
                regex = new Regex(pattern.ToString(), RegexOptions.Compiled);
            }
            catch (ArgumentException)
            {
                try
                {
                    new Regex(regexOpts);
                }
                catch (ArgumentException)
                {
                    // FIXME: We no longer have the line and column data on regexOpts.
                    errorMessages.Add(string.Format("{0},{1}: The RegEx options are invalid.", -1, -1));
                }

                for (int i = 0; i < regexes.Count; i++)
                {
                    try
                    {
                        new Regex(regexes[i]);
                    }
                    catch (ArgumentException)
                    {
                        errorMessages.Add(string.Format("{0},{1}: This regular expression is invalid.",
                            regexLines[i], regexColumns[i]));
                    }
                }
            }

            int numTerminals = symbolNames.Count;

            // skipping PARSER
            token = lexer.GetNextToken();

            //neterminály, které se objevily na levé straně nějakého pravidla
            HashSet<int> reducibleNonterminals = new HashSet<int>();
            //neterminály, které se objevily na pravé straně nějakého pravidla
            HashSet<int> usedNonterminals = new HashSet<int>();

            bool[] terminalUsed = new bool[numTerminals];

            List<ProductionWithAction> productions = new List<ProductionWithAction>();

            symbolNames.Add("$start");
            symbolCodes["$start"] = numTerminals;
            //semhle dáme <$start> výjimečně, protože ani nechceme,
            //aby ho někdo dával na pravou stranu nějakého pravidla
            usedNonterminals.Add(symbolCodes["$start"]);

            // skipping START
            token = lexer.GetNextToken();
            // skipping LANGLE
            token = lexer.GetNextToken();

            string startSymbol = token.Value;

            if (symbolCodes.ContainsKey(startSymbol) && symbolCodes[startSymbol] < numTerminals)
                errorMessages.Add(string.Format("{0},{1}: The nonterminal <{2}> shares it's name with a terminal symbol.",
                    token.LineNumber, token.ColumnNumber, startSymbol));

            token = lexer.GetNextToken();

            // skipping RANGLE
            token = lexer.GetNextToken();

            symbolNames.Add(startSymbol);
            symbolCodes[startSymbol] = symbolNames.Count - 1;

            string userObjectType = null;

            if (token.SymbolCode == CODE_USEROBJECT) {

                token = lexer.GetNextToken();

                if (token.SymbolCode == CODE_QUOTED) {
                    userObjectType = token.Value.Substring(1, token.Value.Length - 2);
                    token = lexer.GetNextToken();
                } else {
                    StringBuilder typeBuilder = new StringBuilder(token.Value);
                    token = lexer.GetNextToken();
                    while (token.SymbolCode == CODE_DOT) {
                        typeBuilder.Append(".");
                        token = lexer.GetNextToken();
                        typeBuilder.Append(token.Value);
                        token = lexer.GetNextToken();
                    }

                    userObjectType = typeBuilder.ToString();
                }
            }

            //naše 0. pravidlo, které výstižně popisuje způsob, jakým si gramatiku upravujeme
            productions.Add(new ProductionWithAction(new Production(
                symbolCodes["$start"], new int[] { symbolCodes[startSymbol], symbolCodes["$end"] }), "{ return _1; }"));

            reducibleNonterminals.Add(symbolCodes["$start"]);
            usedNonterminals.Add(symbolCodes[startSymbol]);
            terminalUsed[symbolCodes["$end"]] = true;

            var typeMappings = new Dictionary<string, string>();

            //zpracování pravidel
            while (token.SymbolCode != CODE_END)
            {
                if (token.SymbolCode == CODE_TYPE) {

                    token = lexer.GetNextToken();

                    // skipping LANGLE
                    token = lexer.GetNextToken();
                    string nonterminal = token.Value;

                    if (symbolCodes.ContainsKey(nonterminal) && symbolCodes[nonterminal] < numTerminals)
                        errorMessages.Add(string.Format("{0},{1}: The nonterminal <{2}> shares it's name with a terminal symbol.",
                            token.LineNumber, token.ColumnNumber, nonterminal));

                    if (!symbolCodes.ContainsKey(nonterminal))
                    {
                        symbolNames.Add(nonterminal);
                        symbolCodes[nonterminal] = symbolNames.Count - 1;
                    }

                    token = lexer.GetNextToken();
                    // skipping RANGLE
                    token = lexer.GetNextToken();

                    if (token.SymbolCode == CODE_QUOTED) {
                        // QUOTED
                        typeMappings.Add(nonterminal, token.Value.Substring(1, token.Value.Length - 2));
                        token = lexer.GetNextToken();
                    }
                    else {
                        StringBuilder typeBuilder = new StringBuilder(token.Value);
                        token = lexer.GetNextToken();
                        while (token.SymbolCode == CODE_DOT) {
                            typeBuilder.Append(".");
                            token = lexer.GetNextToken();
                            typeBuilder.Append(token.Value);
                            token = lexer.GetNextToken();
                        }

                        typeMappings.Add(nonterminal, typeBuilder.ToString());
                    }

                } else {

                    //extrahujeme symbol na levé straně a zpracujeme ho
                    // skipping LANGLE
                    token = lexer.GetNextToken();

                    string lhsSymbol = token.Value;

                    if (symbolCodes.ContainsKey(lhsSymbol) && symbolCodes[lhsSymbol] < numTerminals)
                        errorMessages.Add(string.Format("{0},{1}: The nonterminal <{2}> shares it's name with a terminal symbol.",
                            token.LineNumber, token.ColumnNumber, lhsSymbol));

                    if (!symbolCodes.ContainsKey(lhsSymbol))
                    {
                        symbolNames.Add(lhsSymbol);
                        symbolCodes[lhsSymbol] = symbolNames.Count - 1;
                    }

                    if (!reducibleNonterminals.Contains(symbolCodes[lhsSymbol]))
                        reducibleNonterminals.Add(symbolCodes[lhsSymbol]);

                    token = lexer.GetNextToken();

                    //skipping RANGLE
                    token = lexer.GetNextToken();

                    int lhsSymbolCode = symbolCodes[lhsSymbol];

                    //Zpracujeme výraz na pravé straně, který může sestávat z několika seznamů symbolů oddělenými
                    //ořítky. Každý z těchto seznamů pak tvoří jedno pravidlo bez ořítek.
                    while ((token.SymbolCode == CODE_DERIVES) || (token.SymbolCode == CODE_OR))
                    {
                        token = lexer.GetNextToken();
                        List<int> rhsSymbols = new List<int>();

                        while (token.SymbolCode != CODE_CODE)
                        {
                            int rhsSymbolCode = -1;
                            if (token.SymbolCode == CODE_LANGLE)
                            {
                                //skipping LANGLE
                                token = lexer.GetNextToken();

                                string rhsSymbol = token.Value;

                                if (symbolCodes.ContainsKey(rhsSymbol) && symbolCodes[rhsSymbol] < numTerminals)
                                    errorMessages.Add(string.Format("{0},{1}: The nonterminal <{2}> shares it's name with a terminal symbol.",
                                        token.LineNumber, token.ColumnNumber, rhsSymbol));

                                if (!symbolCodes.ContainsKey(rhsSymbol))
                                {
                                    symbolNames.Add(rhsSymbol);
                                    symbolCodes[rhsSymbol] = symbolNames.Count - 1;
                                }

                                if (!usedNonterminals.Contains(symbolCodes[rhsSymbol]))
                                    usedNonterminals.Add(symbolCodes[rhsSymbol]);

                                token = lexer.GetNextToken();

                                //skipping RANGLE
                                token = lexer.GetNextToken();

                                rhsSymbolCode = symbolCodes[rhsSymbol];
                            }
                            else
                            {
                                string rhsSymbol = token.Value;

                                if (!symbolCodes.ContainsKey(rhsSymbol))
                                    errorMessages.Add(string.Format("{0},{1}: The terminal '{2}' is used but not defined.",
                                        token.LineNumber, token.ColumnNumber, rhsSymbol));
                                else
                                {
                                    rhsSymbolCode = symbolCodes[rhsSymbol];
                                    terminalUsed[rhsSymbolCode] = true;
                                }

                                token = lexer.GetNextToken();
                            }

                            rhsSymbols.Add(rhsSymbolCode);
                        }

                        string code = token.Value;
                        token = lexer.GetNextToken();

                        productions.Add(new ProductionWithAction(new Production(lhsSymbolCode, rhsSymbols), code));
                    }
                }
            }

            //ToArray voláme proto, aby se líná metoda Intersect vyhodnotila a nedošlo by pak při vykonávání
            //dalšího příkazu k chybě
            int[] theGoodOnes = usedNonterminals.Intersect(reducibleNonterminals).ToArray();
            usedNonterminals.ExceptWith(theGoodOnes);
            reducibleNonterminals.ExceptWith(theGoodOnes);

            foreach (int nonterminal in usedNonterminals)
                warningMessages.Add(string.Format("Warning: The nonterminal <{0}> isn't reducible.",
                                                symbolNames[nonterminal]));
            foreach (int nonterminal in reducibleNonterminals)
                warningMessages.Add(string.Format("Warning: The nonterminal <{0}> is defined but never used.", symbolNames[nonterminal]));

            for (int terminal = 0; terminal < numTerminals; terminal++)
                if (!terminalUsed[terminal])
                    warningMessages.Add(string.Format("Warning: The terminal '{0}' is defined but never used.", symbolNames[terminal]));

            if (errorMessages.Count > 0)
                throw new InvalidSpecificationException(errorMessages.Concat(warningMessages));

            //už máme vše načte a zkontrolováno, teď už jen setřídíme pravidla podle levé strany,
            //přečíslujeme je a pro každý neterminál dopočítáme indexy, na kterých začínají pravidla
            //s daným neterminálem
            Production[] productionsArray = new Production[productions.Count];
            productionsArray[0] = productions[0].Production;
            string[] actions = new string[productions.Count];
            actions[0] = productions[0].Action;
            IEnumerable<ProductionWithAction> sortedProductions =
                productions.GetRange(1, productions.Count - 1).OrderBy((prod => prod.Production.LHSSymbol));

            int k = 1;
            foreach (ProductionWithAction productionWithAction in sortedProductions)
            {
                productionsArray[k] = productionWithAction.Production;
                productionsArray[k].ProductionCode = k;
                actions[k] = productionWithAction.Action;
                k++;
            }

            int numNonterminals = symbolCodes.Count - numTerminals;

            int[] nonterminalProductionOffset = new int[numNonterminals + 1];

            int offset = 0;
            for (int nonterminal = 0; nonterminal < numNonterminals; nonterminal++)
            {
                nonterminalProductionOffset[nonterminal] = offset;
                while ((offset < productionsArray.Length) &&
                    (productionsArray[offset].LHSSymbol == numTerminals + nonterminal))
                    offset++;
            }
            nonterminalProductionOffset[nonterminalProductionOffset.Length - 1] = offset;

            string[] nonterminalTypes = new string[numNonterminals];
            foreach (var typeMapping in typeMappings) {
                nonterminalTypes[symbolCodes[typeMapping.Key] - numTerminals] = typeMapping.Value;
            }

            //a teď už to jen zabalíme a pošleme
            GrammarDefinition grammarDefinition = new GrammarDefinition(symbolNames.ToArray(), productionsArray, nonterminalProductionOffset, numTerminals);
            LexerData lexerData = new LexerData(regex, groupSymbolCodes);
            GrammarCode grammarCode = new GrammarCode(headerCode, actions, nonterminalTypes, userObjectType);
            Grammar grammar = new Grammar(grammarDefinition, lexerData, grammarCode);

            return grammar;
            #else
            LexerData lexerData;
            ParserData parserData;
            Grammar.ReadRuntimeDataFromStream(
                new MemoryStream(YetAnotherParserGenerator.Properties.Resources.SpecificationGrammar),
                out lexerData, out parserData);

            GrammarLexer lexer = new GrammarLexer();
            Parser parser = new Parser(parserData);

            GrammarParserLocals locals = new GrammarParserLocals(specificationPath, out warningMessages);
            lexer.SourceString = File.ReadAllText(specificationPath);
            return (Grammar)parser.Parse(lexer, locals);
            #endif
        }