An easy, fast, and robust library to parse C/C++ source code.
- No preprocessing; preprocessor constructs are part of the AST.
- Preserves most comments.
- Implemented from scratch using backtracking Yacc (BtYacc), so no dependency on libclang.
- Parses into a structured AST where file elements form a tree.
- Minimal dependencies: Flex on Unix-like platforms (Linux, macOS, etc.). On Windows, Flex for Windows is bundled for out-of-the-box usage.
- Supports parsing multi-file programs.
CppParser can be used to build tools that need parsing of C/C++ files. It is also used to develop cib, which implements ABI-stable SDK architecture for C++ libraries.
To begin with we will see an example of parsing a hello-world program and see 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 the generated AST to look like the following:
So, how do we access these elements of AST using CppParser?
Below is the program written as a unit test for validating the correctness of the 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 real and is part of the actual unit tests for CppParser.
For AST traversal, see CppWriter, which uses the generated AST to create files.
git clone https://github.com/satya-das/CppParser.git
cd cppparser mkdir builds cd builds cmake .. make
Alternatively, if you prefer Ninja instead of make:
cd cppparser mkdir builds cd builds cmake -G Ninja .. ninja
Make sure you also clone the dependencies. Run the following command in the parent directory of the root of cppparser.
git clone https://github.com/satya-das/common.git
After this command the common and cppparser folders should be side by side.
cd cppparser mkdir builds cd builds cmake -G Ninja .. -DCPPPARSER_BUILD_TESTS=ON ninja && ninja test
Modify the command by removing -G Ninja if you prefer make.