diff --git a/clang-tools-extra/pseudo/test/html-forest.c b/clang-tools-extra/pseudo/test/html-forest.c
new file mode 100644
--- /dev/null
+++ b/clang-tools-extra/pseudo/test/html-forest.c
@@ -0,0 +1,8 @@
+// RUN: clang-pseudo -source %s -html-forest=%t.html
+// RUN: FileCheck %s < %t.html
+int main() {
+}
+// Sanity check for some obvious strings.
+// CHECK-DAG:
+// CHECK-DAG: "compound-statement"
+// CHECK-DAG: main
diff --git a/clang-tools-extra/pseudo/tool/CMakeLists.txt b/clang-tools-extra/pseudo/tool/CMakeLists.txt
--- a/clang-tools-extra/pseudo/tool/CMakeLists.txt
+++ b/clang-tools-extra/pseudo/tool/CMakeLists.txt
@@ -2,6 +2,7 @@
add_clang_tool(clang-pseudo
ClangPseudo.cpp
+ HTMLForest.cpp
)
clang_target_link_libraries(clang-pseudo
@@ -16,3 +17,13 @@
clangPseudoCLI
)
+add_custom_command(OUTPUT HTMLForestResources.inc
+ COMMAND "${Python3_EXECUTABLE}" bundle_resources.py
+ ${CMAKE_CURRENT_BINARY_DIR}/HTMLForestResources.inc
+ HTMLForest.css HTMLForest.js HTMLForest.html
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMENT "Bundling HTMLForest resources"
+ DEPENDS bundle_resources.py HTMLForest.css HTMLForest.js HTMLForest.html
+ VERBATIM)
+add_custom_target(clang-pseudo-resources DEPENDS HTMLForestResources.inc)
+add_dependencies(clang-pseudo clang-pseudo-resources)
diff --git a/clang-tools-extra/pseudo/tool/ClangPseudo.cpp b/clang-tools-extra/pseudo/tool/ClangPseudo.cpp
--- a/clang-tools-extra/pseudo/tool/ClangPseudo.cpp
+++ b/clang-tools-extra/pseudo/tool/ClangPseudo.cpp
@@ -8,6 +8,7 @@
#include "clang-pseudo/Bracket.h"
#include "clang-pseudo/DirectiveTree.h"
+#include "clang-pseudo/Forest.h"
#include "clang-pseudo/GLR.h"
#include "clang-pseudo/Language.h"
#include "clang-pseudo/Token.h"
@@ -18,7 +19,6 @@
#include "clang/Basic/LangOptions.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/STLFunctionalExtras.h"
-#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/MemoryBuffer.h"
@@ -45,10 +45,12 @@
desc("Strip directives and select conditional sections"));
static opt PrintStatistics("print-statistics", desc("Print GLR parser statistics"));
static opt PrintForest("print-forest", desc("Print parse forest"));
+static opt HTMLForest("html-forest", desc("output file for HTML forest"));
static opt StartSymbol("start-symbol",
desc("specify the start symbol to parse"),
init("translation-unit"));
+
static std::string readOrDie(llvm::StringRef Path) {
llvm::ErrorOr> Text =
llvm::MemoryBuffer::getFile(Path);
@@ -62,6 +64,9 @@
namespace clang {
namespace pseudo {
+// Defined in HTMLForest.cpp
+void writeHTMLForest(llvm::raw_ostream &OS, const Grammar &,
+ const ForestNode &Root, const TokenStream &);
namespace {
struct NodeStats {
@@ -150,6 +155,17 @@
if (PrintForest)
llvm::outs() << Root.dumpRecursive(Lang.G, /*Abbreviated=*/true);
+ if (HTMLForest.getNumOccurrences()) {
+ std::error_code EC;
+ llvm::raw_fd_ostream HTMLOut(HTMLForest, EC);
+ if (EC) {
+ llvm::errs() << "Couldn't write " << HTMLForest << ": " << EC.message()
+ << "\n";
+ return 2;
+ }
+ clang::pseudo::writeHTMLForest(HTMLOut, Lang.G, Root, *ParseableStream);
+ }
+
if (PrintStatistics) {
llvm::outs() << "Forest bytes: " << Arena.bytes()
<< " nodes: " << Arena.nodeCount() << "\n";
diff --git a/clang-tools-extra/pseudo/tool/HTMLForest.html b/clang-tools-extra/pseudo/tool/HTMLForest.html
new file mode 100644
--- /dev/null
+++ b/clang-tools-extra/pseudo/tool/HTMLForest.html
@@ -0,0 +1,15 @@
+
+
+
diff --git a/clang-tools-extra/pseudo/tool/HTMLForest.cpp b/clang-tools-extra/pseudo/tool/HTMLForest.cpp
new file mode 100644
--- /dev/null
+++ b/clang-tools-extra/pseudo/tool/HTMLForest.cpp
@@ -0,0 +1,178 @@
+//===-- HTMLForest.cpp - browser-based parse forest explorer ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// The plain text forest node dump (clang-pseudo -print-forest) is useful but
+// hard to reconcile with the code being examined, especially when it is large.
+//
+// HTMLForest produces a self-contained HTML file containing both the code and
+// the forest representation, linking them interactively with javascript.
+// At any given time, a single parse tree is shown (ambiguities resolved).
+// The user can switch between ambiguous alternatives.
+//
+// +-------+---------------+
+// | | +-----+|
+// | #tree | #code |#info||
+// | | +-----+|
+// | | |
+// +-------+---------------+
+//
+// #tree is a hierarchical view of the nodes (nested