up: Table of contents | prev: Program | next

3.9 Parser Debugging

A SoulNG parser may be debugged by calling the SetLog member function of the lexer before parsing starts. The SetLog member function takes a pointer to an object of a class derived from the soulng::lexer::ParsingLog class. We now extend the TestMinilangParser function with parser debugging support:

        void TestMinilangParser(const std::string& minilangFilePath, bool debug)
        {
            std::cout << "> " << minilangFilePath << std::endl;
            std::string s = soulng::util::ReadFile(minilangFilePath);
            std::u32string content = soulng::unicode::ToUtf32(s);
            soulng::lexer::XmlParsingLog debugLog(std::cerr);
            MinilangLexer lexer(content, minilangFilePath, 0);
            if (debug)
            {
                lexer.SetLog(&debugLog);
            }
            SourceFileParser::Parse(lexer);
            std::cout << "end of file '" << minilangFilePath << "' reached" << std::endl;
        }
    

We have constructed a variable of type soulng::lexer::XmlParsingLog that is derived from the soulng::lexer::ParsingLog class. It prints a parsing log to the given stream in XML.

By default the parser debugging support is enabled only for debug builds. If the generated parser code is compiled with the preprocessor symbol SOULNG_PARSER_DEBUG_SUPPORT defined, the parser debugging support is enabled for that compilation configuration too.

Changes to the Main Program

Here's the changed main program:

        void PrintUsage()
        {
            std::cout << "Usage: minilang [options] { file.minilang }" << std::endl;
            std::cout << "Options:" << std::endl;
            std::cout << "--help | -h:" << std::endl;
            std::cout << "  Print help and exit." << std::endl;
            std::cout << "--lexer-test | -l" << std::endl;
            std::cout << "  Test lexical analyzer with <file.minilang>." << std::endl;
            std::cout << "--parser-test | -p" << std::endl;
            std::cout << "  Test parser with <file.minilang>." << std::endl;
            std::cout << "--debug | -d" << std::endl;
            std::cout << "  Debug parsing and print debug log to stderr." << std::endl;
        }

        enum class Command
        {
            none, lexerTest, parserTest, print
        };

        int main(int argc, const char** argv)
        {
            try
            {
                InitApplication();
                std::vector files;
                Command command = Command::print;
                bool debug = false;
                for (int i = 1; i < argc; ++i)
                {
                    std::string arg = argv[i];
                    if (soulng::util::StartsWith(arg, "--"))
                    {
                        if (arg == "--help")
                        {
                            PrintUsage();
                            return 1;
                        }
                        else if (arg == "--lexer-test")
                        {
                            command = Command::lexerTest;
                        }
                        else if (arg == "--parser-test")
                        {
                            command = Command::parserTest;
                        }
                        else if (arg == "--debug")
                        {
                            debug = true;
                        }
                        else
                        {
                            throw std::runtime_error("unknown argument '" + arg + "'");
                        }
                    }
                    else if (soulng::util::StartsWith(arg, "-"))
                    {
                        std::string options = arg.substr(1);
                        if (options.empty())
                        {
                            throw std::runtime_error("unknown argument '" + arg + "'");
                        }
                        for (char o : options)
                        {
                            if (o == 'h')
                            {
                                PrintUsage();
                                return 1;
                            }
                            else if (o == 'l')
                            {
                                command = Command::lexerTest;
                            }
                            else if (o == 'p')
                            {
                                command = Command::parserTest;
                            }
                            else if (o == 'd')
                            {
                                debug = true;
                            }
                            else
                            {
                                throw std::runtime_error("unknown argument '-" + std::string(1, o) + "'");
                            }
                        }
                    }
                    else
                    {
                        files.push_back(soulng::util::GetFullPath(arg));
                    }
                }
                if (files.empty() || command == Command::none)
                {
                    PrintUsage();
                    return 1;
                }
                for (const std::string& filePath : files)
                {
                    if (command == Command::lexerTest)
                    {
                        TestMinilangLexer(filePath);
                    }
                    else if (command == Command::parserTest)
                    {
                        TestMinilangParser(filePath, debug);
                    }
                    else if (command == Command::print)
                    {
                        PrintMinilangCode(filePath);
                    }
                    else
                    {
                        PrintUsage();
                        throw std::runtime_error("minilang: unknown command");
                    }
                }
            }
            catch (const std::exception& ex)
            {
                std::cerr << soulng::unicode::ToUtf32(ex.what()) << std::endl;
                return 1;
            }
            DoneApplication();
            return 0;
        }
    

Testing

Running the following command:

        C:\soulng-1.2.0\examples\minilang\test>minilangd -pd minimal.minilang
    

produces the following debug log:

        > C:/soulng-1.2.0/examples/minilang/test/minimal.minilang
        <parse>
         <SourceFile>
          <try>void minimal(){}</try>
           <Function>
            <try>void minimal(){}</try>
             <Type>
              <try>void minimal(){}</try>
              <success>void</success>
             </Type>
             <Identifier>
              <try>minimal(){}</try>
              <success>minimal</success>
             </Identifier>
             <ParameterList>
              <try>){}</try>
               <Parameter>
                <try>){}</try>
                 <Type>
                  <try>){}</try>
                  <fail/>
                 </Type>
                <fail/>
               </Parameter>
              <fail/>
             </ParameterList>
             <CompoundStatement>
              <try>{}</try>
               <Statement>
                <try>}</try>
                 <IfStatement>
                  <try>}</try>
                  <fail/>
                 </IfStatement>
                 <WhileStatement>
                  <try>}</try>
                  <fail/>
                 </WhileStatement>
                 <ReturnStatement>
                  <try>}</try>
                  <fail/>
                 </ReturnStatement>
                 <ConstructionStatement>
                  <try>}</try>
                   <Type>
                    <try>}</try>
                    <fail/>
                   </Type>
                  <fail/>
                 </ConstructionStatement>
                 <AssignmentStatement>
                  <try>}</try>
                   <Identifier>
                    <try>}</try>
                    <fail/>
                   </Identifier>
                  <fail/>
                 </AssignmentStatement>
                 <CompoundStatement>
                  <try>}</try>
                  <fail/>
                 </CompoundStatement>
                <fail/>
               </Statement>
              <success>{</success>
             </CompoundStatement>
            <success>void</success>
           </Function>
           <Function>
            <try></try>
             <Type>
              <try></try>
              <fail/>
             </Type>
            <fail/>
           </Function>
          <success>void</success>
         </SourceFile>
        </parse>
        end of file 'C:/soulng-1.2.0/examples/minilang/test/minimal.minilang' reached
    
up: Table of contents | prev: Program | next