Преглед изворни кода

[toolchain] Add dump-parse-tree command to driver. (#584)

Also stop producing a redundant additional error message if lexing fails in dump-tokens. The lexer will have produced a diagnostic already.
Richard Smith пре 4 година
родитељ
комит
d6b47ba10f

+ 1 - 0
toolchain/driver/BUILD

@@ -15,6 +15,7 @@ cc_library(
     deps = [
         "//toolchain/diagnostics:diagnostic_emitter",
         "//toolchain/lexer:tokenized_buffer",
+        "//toolchain/parser:parse_tree",
         "//toolchain/source:source_buffer",
         "@llvm-project//llvm:Support",
     ],

+ 33 - 5
toolchain/driver/driver.cpp

@@ -13,6 +13,7 @@
 #include "llvm/Support/Format.h"
 #include "toolchain/diagnostics/diagnostic_emitter.h"
 #include "toolchain/lexer/tokenized_buffer.h"
+#include "toolchain/parser/parse_tree.h"
 #include "toolchain/source/source_buffer.h"
 
 namespace Carbon {
@@ -118,13 +119,40 @@ auto Driver::RunDumpTokensSubcommand(llvm::ArrayRef<llvm::StringRef> args)
   }
   auto tokenized_source =
       TokenizedBuffer::Lex(*source, ConsoleDiagnosticConsumer());
-  if (tokenized_source.HasErrors()) {
-    error_stream << "ERROR: Unable to tokenize source file '" << input_file_name
-                 << "'!\n";
+  tokenized_source.Print(output_stream);
+  return !tokenized_source.HasErrors();
+}
+
+auto Driver::RunDumpParseTreeSubcommand(llvm::ArrayRef<llvm::StringRef> args)
+    -> bool {
+  if (args.empty()) {
+    error_stream << "ERROR: No input file specified.\n";
     return false;
   }
-  tokenized_source.Print(output_stream);
-  return true;
+
+  llvm::StringRef input_file_name = args.front();
+  args = args.drop_front();
+  if (!args.empty()) {
+    ReportExtraArgs("dump-parse-tree", args);
+    return false;
+  }
+
+  auto source = SourceBuffer::CreateFromFile(input_file_name);
+  if (!source) {
+    error_stream << "ERROR: Unable to open input source file: ";
+    llvm::handleAllErrors(source.takeError(),
+                          [&](const llvm::ErrorInfoBase& ei) {
+                            ei.log(error_stream);
+                            error_stream << "\n";
+                          });
+    return false;
+  }
+  auto tokenized_source =
+      TokenizedBuffer::Lex(*source, ConsoleDiagnosticConsumer());
+  auto parse_tree =
+      ParseTree::Parse(tokenized_source, ConsoleDiagnosticConsumer());
+  parse_tree.Print(output_stream);
+  return !tokenized_source.HasErrors() && !parse_tree.HasErrors();
 }
 
 auto Driver::ReportExtraArgs(llvm::StringRef subcommand_text,

+ 10 - 0
toolchain/driver/driver.h

@@ -57,6 +57,16 @@ class Driver {
   // registered error stream (stderr by default).
   auto RunDumpTokensSubcommand(llvm::ArrayRef<llvm::StringRef> args) -> bool;
 
+  // Subcommand that dumps the parse tree for the provided source file.
+  //
+  // Requires exactly one positional parameter to designate the source file to
+  // read. May be `-` to read from stdin.
+  //
+  // Returns true if the operation succeeds. If the operation fails, this
+  // returns false and any information about the failure is printed to the
+  // registered error stream (stderr by default).
+  auto RunDumpParseTreeSubcommand(llvm::ArrayRef<llvm::StringRef> args) -> bool;
+
  private:
   auto ReportExtraArgs(llvm::StringRef subcommand_text,
                        llvm::ArrayRef<llvm::StringRef> args) -> void;

+ 55 - 0
toolchain/driver/driver_test.cpp

@@ -179,5 +179,60 @@ TEST(DriverTest, DumpTokenErrors) {
   EXPECT_THAT(test_error_stream.TakeStr(), HasSubstr("ERROR"));
 }
 
+TEST(DriverTest, DumpParseTree) {
+  RawTestOstream test_output_stream;
+  RawTestOstream test_error_stream;
+  Driver driver = Driver(test_output_stream, test_error_stream);
+
+  auto test_file_path = CreateTestFile("var v: Int = 42;");
+  EXPECT_TRUE(driver.RunDumpParseTreeSubcommand({test_file_path}));
+  EXPECT_THAT(test_error_stream.TakeStr(), StrEq(""));
+  auto tokenized_text = test_output_stream.TakeStr();
+
+  EXPECT_THAT(
+      Yaml::Value::FromText(tokenized_text),
+      ElementsAre(Yaml::SequenceValue{
+          Yaml::MappingValue{
+              {"node_index", "6"},
+              {"kind", "VariableDeclaration"},
+              {"text", "var"},
+              {"subtree_size", "7"},
+              {"children",
+               Yaml::SequenceValue{
+                   Yaml::MappingValue{
+                       {"node_index", "2"},
+                       {"kind", "PatternBinding"},
+                       {"text", ":"},
+                       {"subtree_size", "3"},
+                       {"children",
+                        Yaml::SequenceValue{
+                            Yaml::MappingValue{{"node_index", "0"},
+                                               {"kind", "DeclaredName"},
+                                               {"text", "v"}},
+                            Yaml::MappingValue{{"node_index", "1"},
+                                               {"kind", "NameReference"},
+                                               {"text", "Int"}}}}},
+                   Yaml::MappingValue{{"node_index", "4"},
+                                      {"kind", "VariableInitializer"},
+                                      {"text", "="},
+                                      {"subtree_size", "2"},
+                                      {"children",  //
+                                       Yaml::SequenceValue{Yaml::MappingValue{
+                                           {"node_index", "3"},
+                                           {"kind", "Literal"},
+                                           {"text", "42"}}}}},
+                   Yaml::MappingValue{{"node_index", "5"},
+                                      {"kind", "DeclarationEnd"},
+                                      {"text", ";"}}}}},
+          Yaml::MappingValue{{"node_index", "7"},  //
+                             {"kind", "FileEnd"},
+                             {"text", ""}}}));
+
+  // Check that the subcommand dispatch works.
+  EXPECT_TRUE(driver.RunFullCommand({"dump-parse-tree", test_file_path}));
+  EXPECT_THAT(test_error_stream.TakeStr(), StrEq(""));
+  EXPECT_THAT(test_output_stream.TakeStr(), StrEq(tokenized_text));
+}
+
 }  // namespace
 }  // namespace Carbon

+ 3 - 0
toolchain/driver/flags.def

@@ -15,5 +15,8 @@ CARBON_SUBCOMMAND(Help, "help",
 CARBON_SUBCOMMAND(
     DumpTokens, "dump-tokens",
     "Dumps the sequence of tokens lexed out of the input source file.")
+CARBON_SUBCOMMAND(
+    DumpParseTree, "dump-parse-tree",
+    "Dumps the parse tree for the input source file.")
 
 #undef CARBON_SUBCOMMAND