up: Table of contents | prev: Printer Visitor | next: Parser Debugging

3.8 Program

We will now write a function that will parse a given .minilang file, construct an abstract syntax tree while parsing and then print the code to standard output. The name of the function is PrintMinilangCode.

        // Main.cpp:
        // ...
        #include <minilang/PrinterVisitor.hpp>
        // ...

        void PrintMinilangCode(const std::string& minilangFilePath)
        {
            // ...
        }

        // ...
    

Using the Printer Visitor

We start by printing the path name of the given .minilang file:

        // Main.cpp:
        // ...
        #include <minilang/PrinterVisitor.hpp>
        // ...

        void PrintMinilangCode(const std::string& minilangFilePath)
        {
            std::cout << "> " << minilangFilePath << std::endl;
            // ...
        }
    

Then read the file into a string:

        // ....
        std::string s = soulng::util::ReadFile(minilangFilePath);
    

Then convert the content to UTF-32:

        // ...
        std::u32string content = soulng::unicode::ToUtf32(s);
    

Construct a lexer...

        // ...
        MinilangLexer lexer(content, minilangFilePath, 0);
    

And parse the content. The Parse() function of the SourceFileParser will now return the abstract syntax tree representation of the parsed program:

        // ...
        std::unique_ptr<minilang::SourceFileNode> sourceFile = SourceFileParser::Parse(lexer);
    

Then we construct a code formatter that will print the code to the standard output as indented text:

        // ...
        soulng::util::CodeFormatter formatter(std::cout);
    

Now we construct a printer visitor with the formatter:

        minilang::PrinterVisitor visitor(formatter);
    

... and walk the syntax tree with the printer visitor:

        sourceFile->Accept(visitor);
    

This is the whole function:

        void PrintMinilangCode(const std::string& minilangFilePath)
        {
            std::cout << "> " << minilangFilePath << std::endl;std::cout << "> " << minilangFilePath << std::endl;
            std::string s = soulng::util::ReadFile(minilangFilePath);
            std::u32string content = soulng::unicode::ToUtf32(s);
            MinilangLexer lexer(content, minilangFilePath, 0);
            std::unique_ptr<minilang::SourceFileNode> sourceFile = SourceFileParser::Parse(lexer);
            soulng::util::CodeFormatter formatter(std::cout);
            minilang::PrinterVisitor visitor(formatter);
            sourceFile->Accept(visitor);
        }
    

Changes to the Main Program

The main program is changed to print the given .minilang code by default. This is the changed main program:

        // Main.cpp:
        // ...

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

        // ...

        int main(int argc, const char** argv)
        {
            try
            {
                InitApplication();
                std::vector files;
                Command command = Command::print;
                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
                        {
                            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
                            {
                                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);
                    }
                    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

We now run the minilang program with the test files. This is the test output:

        C:\soulng-1.0.0\examples\minilang\test>minilang empty.minilang
        > C:/soulng-1.0.0/examples/minilang/test/empty.minilang

        C:\soulng-1.0.0\examples\minilang\test>minilang error_comma.minilang
        > C:/soulng-1.0.0/examples/minilang/test/error_comma.minilang
        parsing error in 'C:/soulng-1.0.0/examples/minilang/test/error_comma.minilang:1': '(' expected:
        void foo,()
                ^

        C:\soulng-1.0.0\examples\minilang\test>minilang error_semicolon.minilang
        > C:/soulng-1.0.0/examples/minilang/test/error_semicolon.minilang
        parsing error in 'C:/soulng-1.0.0/examples/minilang/test/error_semicolon.minilang:7': ';' expected:
                a = b;
                ^

        C:\soulng-1.0.0\examples\minilang\test>minilang fibonacci.minilang
        > C:/soulng-1.0.0/examples/minilang/test/fibonacci.minilang
        int fibonacci(int n)
        {
            if (n == 0) return 0;
            if (n == 1) return 1;
            return fibonacci(n - 1) + fibonacci(n - 2);
        }

        C:\soulng-1.0.0\examples\minilang\test>minilang gcd.minilang
        > C:/soulng-1.0.0/examples/minilang/test/gcd.minilang
        int gcd(int a, int b)
        {
            while (b != 0)
            {
                a = a % b;
                int t = a;
                a = b;
                b = t;
            }
            return a;
        }

        C:\soulng-1.0.0\examples\minilang\test>minilang invalid.minilang
        > C:/soulng-1.0.0/examples/minilang/test/invalid.minilang
        soulng::lexer::Lexer::NextToken(): error: invalid character '@' in file 'C:/soulng-1.0.0/examples/minilang/test/invalid.minilang' at line 1

        C:\soulng-1.0.0\examples\minilang\test>minilang minimal.minilang
        > C:/soulng-1.0.0/examples/minilang/test/minimal.minilang
        void minimal()
        {
        }

        C:\soulng-1.0.0\examples\minilang\test>minilang unicodeid.minilang
        > C:/soulng-1.0.0/examples/minilang/test/unicodeid.minilang
        int örkki()
        {
            int öljyä = 1;
            return öljyä;
        }        

        C:\soulng-1.0.0\examples\minilang\test>
    
up: Table of contents | prev: Printer Visitor | next: Parser Debugging