An easy, fast, and robust library to parse C/C++ source.
- No pre-processing, and preprocessors are part of the ast.
- Most comments are preserved too.
- Developed from scratch and uses back-tracking yacc (BtYacc) to write C++ grammer, that means no dependency on libclang.
- The result of parsing is an AST where elements of a file are arranged in a tree.
- Minimum dependency. Only external dependency is a lexer which is by default available on unix like platforms and can be easily get on Windows.
- Parsing of multi-file program is supported too.
CppParser can be used to build tools that need parsing of C/C++ files. I am using it to develop cib which implements ABI stable SDK architecture for C++ library.
To begin with we will see an example of parsing a hello-world program and see what is the AST that CppParser
creates:
#include <iostream>
int main()
{
std::cout << "Hello, World!\n";
return 0;
}
For the above hello-world program we can expect that when it is parsed the generated AST should look like following:
So, how we are going to access these elements of AST using CppParser
?
Below is the program written as unit-test for validating the correctness of generated AST:
#include <catch/catch.hpp>
#include "cppparser/cppparser.h"
#include <filesystem>
namespace fs = std::filesystem;
TEST_CASE("Parsing hello world program")
{
cppparser::CppParser parser;
const auto testFilePath = fs::path(__FILE__).parent_path() / "test-files/hello-world.cpp";
const auto ast = parser.parseFile(testFilePath.string());
REQUIRE(ast);
const auto members = GetAllOwnedEntities(*ast);
REQUIRE(members.size() == 2);
const cppast::CppConstPreprocessorIncludeEPtr hashInclude = members[0];
REQUIRE(hashInclude);
CHECK(hashInclude->name() == "<iostream>");
cppast::CppConstFunctionEPtr func = members[1];
REQUIRE(func);
CHECK(func->name() == "main");
REQUIRE(func->defn());
const auto mainBodyMembers = GetAllOwnedEntities(*func->defn());
REQUIRE(mainBodyMembers.size() == 2);
cppast::CppConstBinomialExprEPtr coutHelloWorld = mainBodyMembers[0];
REQUIRE(coutHelloWorld);
CHECK(coutHelloWorld->oper() == cppast::CppBinaryOperator::INSERTION);
cppast::CppConstNameExprEPtr coutOperand1 = &(coutHelloWorld->term1());
REQUIRE(coutOperand1);
CHECK(coutOperand1->value() == "std::cout");
cppast::CppConstStringLiteralExprEPtr coutOperand2 = &(coutHelloWorld->term2());
REQUIRE(coutOperand2);
CHECK(coutOperand2->value() == "\"Hello, World!\\n\"");
}
This example is a real one and is part of actual unit test of CppParser.
For AST traversing, see the CppWriter, that uses the generated AST to create files.
git clone https://github.com/satya-das/common.git
git clone https://github.com/satya-das/CppParser.git
cd cppparser
mkdir builds
cd builds
cmake ..
make && make test
Alternatively, if you prefer Ninja
instead of make
:
cd cppparser
mkdir builds
cd builds
cmake -G Ninja ..
ninja && ninja test