| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include <fstream> |
| #include <iostream> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <fidl/c_generator.h> |
| #include <fidl/flat_ast.h> |
| #include <fidl/identifier_table.h> |
| #include <fidl/json_generator.h> |
| #include <fidl/lexer.h> |
| #include <fidl/parser.h> |
| #include <fidl/source_manager.h> |
| |
| namespace { |
| |
| [[noreturn]] void Usage() { |
| std::cout << "fidl usage:\n"; |
| std::cout << " fidl c-structs HEADER_PATH [FIDL_FILE...]\n"; |
| std::cout << " Parses the FIDL_FILEs and generates C structures\n"; |
| std::cout << " into HEADER_PATH.\n"; |
| std::cout << "\n"; |
| std::cout << " fidl json JSON_PATH [FIDL_FILE...]\n"; |
| std::cout << " Parses the FIDL_FILEs and generates JSON intermediate data\n"; |
| std::cout << " into JSON_PATH.\n"; |
| std::cout << "\n"; |
| std::cout << "The [FIDL_FILE...] arguments can also be provided via a response\n"; |
| std::cout << " file, denoted as `@filepath'. The contents of the file at\n"; |
| std::cout << " `filepath' will be interpreted as a whitespace-delimited list\n"; |
| std::cout << " of files to parse.\n"; |
| std::cout.flush(); |
| exit(1); |
| } |
| |
| std::fstream Open(std::string filename, std::ios::openmode mode) { |
| std::fstream stream; |
| stream.open(filename, mode); |
| if (!stream.is_open()) { |
| Usage(); |
| } |
| return stream; |
| } |
| |
| class Arguments { |
| public: |
| virtual ~Arguments() {} |
| |
| virtual std::string Claim() = 0; |
| virtual bool Remaining() const = 0; |
| }; |
| |
| class ArgvArguments : public Arguments { |
| public: |
| ArgvArguments(int count, char** arguments) |
| : count_(count), arguments_(const_cast<const char**>(arguments)) {} |
| |
| std::string Claim() override { |
| if (count_ < 1) { |
| Usage(); |
| } |
| std::string argument = arguments_[0]; |
| --count_; |
| ++arguments_; |
| return argument; |
| } |
| |
| bool Remaining() const override { return count_ > 0; } |
| |
| bool HeadIsResponseFile() { |
| if (count_ != 1) { |
| return false; |
| } |
| return arguments_[0][0] == '@'; |
| } |
| |
| private: |
| int count_; |
| const char** arguments_; |
| }; |
| |
| class ResponseFileArguments : public Arguments { |
| public: |
| ResponseFileArguments(fidl::StringView filename) : file_(Open(filename, std::ios::in)) { |
| ConsumeWhitespace(); |
| } |
| |
| std::string Claim() override { |
| std::string argument; |
| while (Remaining() && !IsWhitespace()) { |
| argument.push_back(file_.get()); |
| } |
| ConsumeWhitespace(); |
| return argument; |
| } |
| |
| bool Remaining() const override { |
| return !file_.eof(); |
| } |
| |
| private: |
| bool IsWhitespace() { |
| switch (file_.peek()) { |
| case ' ': |
| case '\n': |
| case '\r': |
| case '\t': |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| void ConsumeWhitespace() { |
| while (Remaining() && IsWhitespace()) { |
| file_.get(); |
| } |
| } |
| |
| std::fstream file_; |
| }; |
| |
| enum struct Behavior { |
| CStructs, |
| JSON, |
| }; |
| |
| bool Parse(Arguments* args, fidl::SourceManager* source_manager, |
| fidl::IdentifierTable* identifier_table, fidl::ErrorReporter* error_reporter, |
| fidl::flat::Library* library) { |
| while (args->Remaining()) { |
| std::string filename = args->Claim(); |
| const fidl::SourceFile* source = source_manager->CreateSource(filename.data()); |
| if (source == nullptr) { |
| fprintf(stderr, "Couldn't read in source data from %s\n", filename.data()); |
| return false; |
| } |
| |
| fidl::Lexer lexer(*source, identifier_table); |
| fidl::Parser parser(&lexer, error_reporter); |
| auto ast = parser.Parse(); |
| if (!parser.Ok()) { |
| error_reporter->PrintReports(); |
| return false; |
| } |
| |
| if (!library->ConsumeFile(std::move(ast))) { |
| fprintf(stderr, "Parse failed!\n"); |
| return false; |
| } |
| } |
| |
| if (!library->Resolve()) { |
| fprintf(stderr, "flat::Library resolution failed!\n"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool GenerateC(fidl::flat::Library* library, std::fstream header_output) { |
| std::ostringstream header_file; |
| fidl::CGenerator c_generator(library); |
| |
| c_generator.ProduceCStructs(&header_file); |
| |
| header_output << "// header file\n"; |
| header_output << header_file.str(); |
| header_output.flush(); |
| |
| return true; |
| } |
| |
| bool GenerateJSON(fidl::flat::Library* library, std::fstream json_output) { |
| std::ostringstream json_file; |
| fidl::JSONGenerator json_generator(library); |
| |
| json_generator.ProduceJSON(&json_file); |
| |
| json_output << json_file.str(); |
| json_output.flush(); |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| int main(int argc, char* argv[]) { |
| auto argv_args = std::make_unique<ArgvArguments>(argc, argv); |
| |
| // Parse the program name. |
| argv_args->Claim(); |
| |
| std::string behavior_argument = argv_args->Claim(); |
| Behavior behavior; |
| std::fstream output_file; |
| if (behavior_argument == "c-structs") { |
| behavior = Behavior::CStructs; |
| // Parse a file name to write output to. |
| if (argc < 2) { |
| return 1; |
| } |
| output_file = Open(argv_args->Claim(), std::ios::out); |
| } else if (behavior_argument == "json") { |
| behavior = Behavior::JSON; |
| // Parse a file name to write output to. |
| if (argc < 2) { |
| return 1; |
| } |
| output_file = Open(argv_args->Claim(), std::ios::out); |
| } else { |
| Usage(); |
| } |
| |
| // Either continue with the list of fidl files to compile, or else |
| // with a response file. |
| std::unique_ptr<ResponseFileArguments> response_file_args; |
| Arguments* args = argv_args.get(); |
| if (argv_args->HeadIsResponseFile()) { |
| std::string response = argv_args->Claim(); |
| // Drop the leading '@'. |
| fidl::StringView response_file = response.data() + 1; |
| response_file_args = std::make_unique<ResponseFileArguments>(response_file); |
| args = response_file_args.get(); |
| } |
| |
| fidl::SourceManager source_manager; |
| fidl::IdentifierTable identifier_table; |
| fidl::ErrorReporter error_reporter; |
| fidl::flat::Library library; |
| if (!Parse(args, &source_manager, &identifier_table, &error_reporter, &library)) { |
| return 1; |
| } |
| |
| switch (behavior) { |
| case Behavior::CStructs: { |
| if (!GenerateC(&library, std::move(output_file))) { |
| return 1; |
| } |
| break; |
| } |
| case Behavior::JSON: { |
| if (!GenerateJSON(&library, std::move(output_file))) { |
| return 1; |
| } |
| break; |
| } |
| } |
| } |