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)
{
// ...
}
// ...
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);
}
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;
}
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