mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 21:21:19 +01:00
[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.
This commit is contained in:
@@ -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",
|
||||
],
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user