From 9ab0f74aa643dc5d27508e9b2043f0c476f8f928 Mon Sep 17 00:00:00 2001 From: Fuwn Date: Sun, 3 Jul 2022 19:44:59 -0700 Subject: chore(ninja): rename src_dir --- build.ninja | 6 +- cait/cait.cc | 21 ++++ cait/cli.cc | 147 ++++++++++++++++++++++ cait/cli.hh | 47 +++++++ cait/context.cc | 84 +++++++++++++ cait/context.hh | 47 +++++++ cait/help.cc | 75 ++++++++++++ cait/help.hh | 44 +++++++ cait/lexer.cc | 69 +++++++++++ cait/lexer.hh | 44 +++++++ cait/node.cc | 165 +++++++++++++++++++++++++ cait/node.hh | 147 ++++++++++++++++++++++ cait/parser.cc | 371 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ cait/parser.hh | 90 ++++++++++++++ cait/token.cc | 102 ++++++++++++++++ cait/token.hh | 153 +++++++++++++++++++++++ cait/utility.hh | 38 ++++++ src/cait.cc | 21 ---- src/cli.cc | 147 ---------------------- src/cli.hh | 47 ------- src/context.cc | 84 ------------- src/context.hh | 47 ------- src/help.cc | 75 ------------ src/help.hh | 44 ------- src/lexer.cc | 69 ----------- src/lexer.hh | 44 ------- src/node.cc | 165 ------------------------- src/node.hh | 147 ---------------------- src/parser.cc | 371 -------------------------------------------------------- src/parser.hh | 90 -------------- src/token.cc | 102 ---------------- src/token.hh | 153 ----------------------- src/utility.hh | 38 ------ 33 files changed, 1647 insertions(+), 1647 deletions(-) create mode 100644 cait/cait.cc create mode 100644 cait/cli.cc create mode 100644 cait/cli.hh create mode 100644 cait/context.cc create mode 100644 cait/context.hh create mode 100644 cait/help.cc create mode 100644 cait/help.hh create mode 100644 cait/lexer.cc create mode 100644 cait/lexer.hh create mode 100644 cait/node.cc create mode 100644 cait/node.hh create mode 100644 cait/parser.cc create mode 100644 cait/parser.hh create mode 100644 cait/token.cc create mode 100644 cait/token.hh create mode 100644 cait/utility.hh delete mode 100644 src/cait.cc delete mode 100644 src/cli.cc delete mode 100644 src/cli.hh delete mode 100644 src/context.cc delete mode 100644 src/context.hh delete mode 100644 src/help.cc delete mode 100644 src/help.hh delete mode 100644 src/lexer.cc delete mode 100644 src/lexer.hh delete mode 100644 src/node.cc delete mode 100644 src/node.hh delete mode 100644 src/parser.cc delete mode 100644 src/parser.hh delete mode 100644 src/token.cc delete mode 100644 src/token.hh delete mode 100644 src/utility.hh diff --git a/build.ninja b/build.ninja index c97c215..c9efa7e 100644 --- a/build.ninja +++ b/build.ninja @@ -1,8 +1,8 @@ cc = clang++ -cxxflags = -Isrc -O3 -std=c++20 -Weverything -Wno-c++98-compat -out_dir = out name = cait -src_dir = src +src_dir = $name +cxxflags = -I$src_dir -O3 -std=c++20 -Weverything -Wno-c++98-compat +out_dir = out out_ext = .exe obj_ext = .o cc_ext = .cc diff --git a/cait/cait.cc b/cait/cait.cc new file mode 100644 index 0000000..623c781 --- /dev/null +++ b/cait/cait.cc @@ -0,0 +1,21 @@ +// This file is part of Cait . +// Copyright (C) 2022-2022 Fuwn +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Copyright (C) 2022-2022 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#include "cli.hh" + +auto main(int argc, char **argv) -> int { return cait::cli(argc, argv).look(); } diff --git a/cait/cli.cc b/cait/cli.cc new file mode 100644 index 0000000..573b9a9 --- /dev/null +++ b/cait/cli.cc @@ -0,0 +1,147 @@ +// This file is part of Cait . +// Copyright (C) 2022-2022 Fuwn +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Copyright (C) 2022-2022 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#include + +#include "cli.hh" +#include "context.hh" +#include "help.hh" +#include "lexer.hh" +#include "parser.hh" +#include "utility.hh" + +namespace cait { + +cli::cli(int argc, char **argv) : _argc(argc), _argv(argv) { + for (int i = 1; i < _argc; i++) { + if (_argv[i][0] == '-') { + std::string key = _argv[i]; + std::string value; + + if (i + 1 < _argc) { + value = _argv[i + 1]; + + i += 1; + } + + options[key] = value.empty() ? std::nullopt : std::optional(value); + } + } +} + +// auto cli::argc() const -> int { return this->_argc; } + +// auto cli::argv() const -> char ** { return this->_argv; } + +auto cli::arg(int i) -> std::optional { + if (i < this->_argc) { + return std::string(this->_argv[i]); + } + + return std::nullopt; +} + +auto cli::option(const std::string &key) + -> std::optional> { + if (this->options.contains(key)) { + return this->options[key]; + } + + return std::nullopt; +} + +auto cli::look() -> int { + switch (cait::utility::hash(this->arg(1).value_or("").c_str())) { + case cait::utility::hash("--help"): + case cait::utility::hash("-h"): { + std::cout << cait::help_message() << '\n'; + } break; + case cait::utility::hash("--version"): { + std::cout << version::whole() << '\n'; + } break; + default: { + std::string cait_file_name = this->option("-f")->value_or("build.cait"); + std::ifstream cait_file(cait_file_name, std::ios::in); + + if (cait_file.is_open()) { + lexer lexer(cait_file); + parser parser(lexer.tree()); + + try { + parser.generate_nodes(cait_file_name, lexer.lines()); + } catch (const parser_exception &e) { + std::cout << e.what() << '\n'; + + return 1; + } + + context context(parser); + + for (const auto &token_line : parser.tree()) { + for (const auto &token : token_line) { + std::cout << token.word << "(" << token.type.string() << ")"; + } + + std::cout << "\n\n"; + } + + for (const auto &token_line : parser.nodes()) { + std::visit( + [](const auto &node) -> void { + std::cout << node.string() << '\n'; + }, + token_line); + } + + // auto cc = context.rule("cc"); + + // if (cc.has_value()) { + // std::cout << "cc: '" << cc.value().string() << "'\n"; + // } else { + // std::cout << "cc has no value\n"; + // } + + return 0; + } + + std::cout << "cait: error: loading '" << cait_file_name + << "': The system cannot find " + "the file specified.\n\n"; + + return 1; + } // break; + } + + // auto option = this->option("-h"); + + // if (option.has_value()) { + // std::cout << "option exists "; + + // if (option->has_value()) { + // std::cout << "with value " << option->value() << std::endl; + // } else { + // std::cout << "with no value\n"; + // } + // } else { + // std::cout << "option does not exist\n"; + // } + + return 0; +} + +} // namespace cait diff --git a/cait/cli.hh b/cait/cli.hh new file mode 100644 index 0000000..155bbe5 --- /dev/null +++ b/cait/cli.hh @@ -0,0 +1,47 @@ +// This file is part of Cait . +// Copyright (C) 2022-2022 Fuwn +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Copyright (C) 2022-2022 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#ifndef CLI_HH +#define CLI_HH + +#include +#include +#include +#include + +namespace cait { + +class cli { +private: + int _argc; + [[maybe_unused]] int padding = 0; + char **_argv; + std::map> options; + +public: + cli(int, char **); + // auto argc() const -> int; + // auto argv() const -> char **; + auto arg(int) -> std::optional; + auto option(const std::string &) -> std::optional>; + auto look() -> int; +}; + +} // namespace cait + +#endif // CLI_HH diff --git a/cait/context.cc b/cait/context.cc new file mode 100644 index 0000000..f34d55c --- /dev/null +++ b/cait/context.cc @@ -0,0 +1,84 @@ +// This file is part of Cait . +// Copyright (C) 2022-2022 Fuwn +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Copyright (C) 2022-2022 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#include + +#include "context.hh" + +namespace cait { + +context::context(const parser &parser) { + this->jobs = std::thread::hardware_concurrency() + 2; + + for (const auto &parse_node : parser.nodes()) { + std::visit( + [this](auto &node) { + using T = std::decay_t; + + if constexpr (std::is_same_v) { + this->identifiers.insert( + {std::make_pair(node.name().word, + token_type::variable_declaration), + node}); + } else if constexpr (std::is_same_v) { + this->identifiers.insert( + {std::make_pair(node.name().word, token_type::rule_declaration), + node}); + } + }, + parse_node); + } +} + +[[maybe_unused]] auto context::variable(const std::string &variable) + -> std::optional { + auto *it = std::get_if(&this->identifiers.at( + std::make_pair(variable, token_type::variable_declaration))); + + if (it == nullptr) { + return std::nullopt; + } + + return *it; +} + +[[maybe_unused]] auto context::rule(const std::string &variable) + -> std::optional { + auto *it = std::get_if(&this->identifiers.at( + std::make_pair(variable, token_type::rule_declaration))); + + if (it == nullptr) { + return std::nullopt; + } + + return *it; +} + +[[maybe_unused]] auto context::build(const std::string &variable) + -> std::optional { + auto *it = std::get_if(&this->identifiers.at( + std::make_pair(variable, token_type::build_declaration))); + + if (it == nullptr) { + return std::nullopt; + } + + return *it; +} + +} // namespace cait diff --git a/cait/context.hh b/cait/context.hh new file mode 100644 index 0000000..f42555d --- /dev/null +++ b/cait/context.hh @@ -0,0 +1,47 @@ +// This file is part of Cait . +// Copyright (C) 2022-2022 Fuwn +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Copyright (C) 2022-2022 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#ifndef CONTEXT_HH +#define CONTEXT_HH + +#include +#include +#include + +#include "parser.hh" +#include "token.hh" + +namespace cait { + +class context { +private: + std::map, node::node_set> identifiers; + [[maybe_unused]] std::size_t jobs; + +public: + explicit context(const parser &); + [[maybe_unused]] auto variable(const std::string &) + -> std::optional; + [[maybe_unused]] auto rule(const std::string &) -> std::optional; + [[maybe_unused]] auto build(const std::string &) + -> std::optional; +}; + +} // namespace cait + +#endif // CONTEXT_HH diff --git a/cait/help.cc b/cait/help.cc new file mode 100644 index 0000000..2271e9f --- /dev/null +++ b/cait/help.cc @@ -0,0 +1,75 @@ +// This file is part of Cait . +// Copyright (C) 2022-2022 Fuwn +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Copyright (C) 2022-2022 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#include + +#include "help.hh" + +namespace cait { + +auto help_message() -> std::string { + std::ostringstream help; + + help + << "usage: cait [options] [targets...]\n" + "\n" + "if targets are unspecified, builds the 'default' target (see " + "manual).\n" + "\n" + "options:\n" + " --version print cait version (\"" + + version::whole() + + "\")\n" + " -v, --verbose " + "show all command " + "lines while " + "building\n" + "\n" + " -C DIR " + "change to DIR " + "before doing " + "anything else\n" + " -f FILE " + "specify input " + "build file " + "[default=build." + "cait]\n" + "\n" + " -j N run N " + "jobs in parallel " + "(0 means " + "infinity) " + "[default=" + << std::thread::hardware_concurrency() + 2 + << " on this system]\n" + " -k N keep going until N jobs fail (0 means infinity) " + "[default=1]\n" + " -l N do not start new jobs if the load average is greater " + "than N\n" + " -n dry run (don't run commands but act like they succeeded)\n" + "\n" + " -d MODE enable debugging (use '-d list' to list modes)\n" + " -t TOOL run a subtool (use '-t list' to list subtools)\n" + " terminates toplevel options; further flags are passed to the " + "tool\n" + " -w FLAG adjust warnings (use '-w list' to list warnings)"; + + return help.str(); +} + +} // namespace cait diff --git a/cait/help.hh b/cait/help.hh new file mode 100644 index 0000000..88517da --- /dev/null +++ b/cait/help.hh @@ -0,0 +1,44 @@ +// This file is part of Cait . +// Copyright (C) 2022-2022 Fuwn +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Copyright (C) 2022-2022 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#ifndef HELP_HH +#define HELP_HH + +#include + +#define CAIT_TO_STRING(x) #x +#define CAIT_STR(x) CAIT_TO_STRING(x) + +namespace cait { + +namespace version { + +constexpr unsigned int major = 0; +constexpr unsigned int minor = 1; +constexpr unsigned int patch = 0; +constexpr auto whole() -> std::string { + return CAIT_STR(major) "." CAIT_STR(minor) "." CAIT_STR(patch); +} + +} // namespace version + +auto help_message() -> std::string; + +} // namespace cait + +#endif // HELP_HH diff --git a/cait/lexer.cc b/cait/lexer.cc new file mode 100644 index 0000000..1100a2e --- /dev/null +++ b/cait/lexer.cc @@ -0,0 +1,69 @@ +// This file is part of Cait . +// Copyright (C) 2022-2022 Fuwn +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Copyright (C) 2022-2022 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#include "lexer.hh" + +namespace cait { + +lexer::lexer(std::ifstream &file) { + std::string line; + std::size_t line_number = 1; + + for (;;) { + while (std::getline(file, line)) { + std::vector token_line; + + this->_lines.push_back(line); + + start: + std::istringstream line_stream(line); + std::string word; + std::size_t character = 1; + + while (std::getline(line_stream, word, ' ')) { + if (word.empty()) { + continue; + } + + token_line.emplace_back(line_number, character, character + word.size(), + word); + + character += word.size() + 1; + } + + if (word == "$") { + token_line.pop_back(); + std::getline(file, line); + + goto start; + } + + this->_tree.push_back(token_line); + + line_number += 1; + } + + break; + } +} + +auto lexer::tree() -> token_tree & { return this->_tree; } + +auto lexer::lines() -> const std::vector & { return this->_lines; } + +} // namespace cait diff --git a/cait/lexer.hh b/cait/lexer.hh new file mode 100644 index 0000000..ffe3a33 --- /dev/null +++ b/cait/lexer.hh @@ -0,0 +1,44 @@ +// This file is part of Cait . +// Copyright (C) 2022-2022 Fuwn +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Copyright (C) 2022-2022 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#ifndef LEXER_HH +#define LEXER_HH + +#include +#include +#include +#include + +#include "token.hh" + +namespace cait { + +class lexer { +private: + token_tree _tree; + std::vector _lines; + +public: + explicit lexer(std::ifstream &); + auto tree() -> token_tree &; + auto lines() -> const std::vector &; +}; + +} // namespace cait + +#endif // LEXER_HH diff --git a/cait/node.cc b/cait/node.cc new file mode 100644 index 0000000..9b76c27 --- /dev/null +++ b/cait/node.cc @@ -0,0 +1,165 @@ +// This file is part of Cait . +// Copyright (C) 2022-2022 Fuwn +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Copyright (C) 2022-2022 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#include + +#include "node.hh" + +namespace cait::node { + +variable::variable(const token &variable_name, + std::vector variable_value_list) + : _name(variable_name), _value_list(std::move(variable_value_list)) {} + +[[nodiscard]] auto variable::string() const -> std::string { + std::ostringstream values; + + for (const auto &command : this->_value_list) { + values << command.word << " "; + } + + return this->_name.word + " = " + values.str(); +} + +[[nodiscard]] auto variable::name() const noexcept -> const token & { + return this->_name; +} + +[[maybe_unused]] [[nodiscard]] auto variable::value_list() const noexcept + -> const std::vector & { + return this->_value_list; +} + +rule::rule(const token &rule_name, item_map_type rule_item_map) + : _name(rule_name), _item_map(std::move(rule_item_map)) {} + +[[nodiscard]] auto rule::string() const -> std::string { + std::ostringstream values; + + for (const auto &item : this->_item_map) { + values << " " << item.first << " = "; + + for (const auto &command : item.second) { + values << command.word << " "; + } + + values << '\n'; + } + + // Clip off the last newline + return "rule " + this->_name.word + "\n" + + values.str().substr(0, values.str().size() - 1); +} + +[[nodiscard]] auto rule::name() const noexcept -> const token & { + return this->_name; +} + +[[maybe_unused]] [[nodiscard]] auto rule::item_map() const noexcept + -> const item_map_type & { + return this->_item_map; +} + +pool::pool(const token &pool_name, const token &pool_depth) + : _name(pool_name), _depth(pool_depth) {} + +[[nodiscard]] auto pool::string() const -> std::string { + return "pool " + this->_name.word + "\n depth = " + this->_depth.word; +} + +[[nodiscard]] auto pool::name() const noexcept -> const token & { + return this->_name; +} + +[[maybe_unused]] [[nodiscard]] auto pool::depth() const noexcept + -> const token & { + return this->_depth; +} + +build::build(const token &build_output, const token &build_rule, + std::vector build_inputs) + : _output(build_output), _rule(build_rule), + _inputs(std::move(build_inputs)) {} + +[[nodiscard]] auto build::string() const -> std::string { + std::ostringstream values; + + for (const auto &command : this->_inputs) { + values << command.word << " "; + } + + return "build " + this->_output.word + ": " + this->_rule.word + ' ' + + values.str(); +} + +[[maybe_unused]] [[nodiscard]] auto build::output() const noexcept + -> const token & { + return this->_output; +} + +[[maybe_unused]] [[nodiscard]] auto build::rule() const noexcept + -> const token & { + return this->_rule; +} + +[[maybe_unused]] [[nodiscard]] auto build::inputs() const noexcept + -> const std::vector & { + return this->_inputs; +} + +default_::default_(std::vector default_targets) + : _targets(std::move(default_targets)) {} + +[[nodiscard]] auto default_::string() const -> std::string { + std::ostringstream values; + + for (const auto &target : this->_targets) { + values << target.word << " "; + } + + return "default " + values.str(); +} + +[[maybe_unused]] [[nodiscard]] auto default_::targets() const noexcept + -> const std::vector & { + return this->_targets; +} + +include::include(const token &include_path) : _path(include_path) {} + +[[nodiscard]] auto include::string() const -> std::string { + return "include " + this->_path.word; +} + +[[maybe_unused]] [[nodiscard]] auto include::path() const noexcept + -> const token & { + return this->_path; +} + +subninja::subninja(const token &subninja_path) : _path(subninja_path) {} + +[[nodiscard]] auto subninja::string() const -> std::string { + return "subninja " + this->_path.word; +} + +[[maybe_unused]] [[nodiscard]] auto subninja::path() const noexcept + -> const token & { + return this->_path; +} + +} // namespace cait::node diff --git a/cait/node.hh b/cait/node.hh new file mode 100644 index 0000000..cc2362c --- /dev/null +++ b/cait/node.hh @@ -0,0 +1,147 @@ +// This file is part of Cait . +// Copyright (C) 2022-2022 Fuwn +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Copyright (C) 2022-2022 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#ifndef NODE_HH +#define NODE_HH + +#include +#include +#include +#include +#include +#include + +#include "token.hh" + +namespace cait { + +namespace { + +class stringable { +public: + [[maybe_unused]] stringable() = default; + stringable(const stringable &) = default; + virtual ~stringable() = default; + [[nodiscard]] virtual auto string() const -> std::string = 0; +}; + +} // namespace + +namespace node { + +class variable; +class rule; +class build; +class default_; +class pool; +class include; +class subninja; + +using item_map_type = std::map>; +using node_set = + std::variant; + +class variable : stringable { +private: + const token &_name; + std::vector _value_list; + +public: + variable(const token &, std::vector); + [[nodiscard]] auto string() const -> std::string override; + [[nodiscard]] auto name() const noexcept -> const token &; + [[maybe_unused]] [[nodiscard]] auto value_list() const noexcept + -> const std::vector &; +}; + +class rule : stringable { +private: + const token &_name; + item_map_type _item_map; + +public: + rule(const token &, item_map_type); + [[nodiscard]] auto string() const -> std::string override; + [[nodiscard]] auto name() const noexcept -> const token &; + [[maybe_unused]] [[nodiscard]] auto item_map() const noexcept + -> const item_map_type &; +}; + +class pool : stringable { +private: + const token &_name; + const token &_depth; + +public: + pool(const token &, const token &); + [[nodiscard]] auto string() const -> std::string override; + [[nodiscard]] auto name() const noexcept -> const token &; + [[maybe_unused]] [[nodiscard]] auto depth() const noexcept -> const token &; +}; + +class build : stringable { +private: + const token &_output; + const token &_rule; + std::vector _inputs; + +public: + build(const token &, const token &, std::vector); + [[nodiscard]] auto string() const -> std::string override; + [[maybe_unused]] [[nodiscard]] auto output() const noexcept -> const token &; + [[maybe_unused]] [[nodiscard]] auto rule() const noexcept -> const token &; + [[maybe_unused]] [[nodiscard]] auto inputs() const noexcept + -> const std::vector &; +}; + +class default_ : stringable { +private: + std::vector _targets; + +public: + explicit default_(std::vector); + [[nodiscard]] auto string() const -> std::string override; + [[maybe_unused]] [[nodiscard]] auto targets() const noexcept + -> const std::vector &; +}; + +class include : stringable { +private: + const token &_path; + +public: + explicit include(const token &); + [[nodiscard]] auto string() const -> std::string override; + [[maybe_unused]] [[nodiscard]] auto path() const noexcept -> const token &; +}; + +class subninja : stringable { +private: + const token &_path; + +public: + explicit subninja(const token &); + [[nodiscard]] auto string() const -> std::string override; + [[maybe_unused]] [[nodiscard]] auto path() const noexcept -> const token &; +}; + +} // namespace node + +} // namespace cait + +#endif // NODE_HH diff --git a/cait/parser.cc b/cait/parser.cc new file mode 100644 index 0000000..734be2f --- /dev/null +++ b/cait/parser.cc @@ -0,0 +1,371 @@ +// This file is part of Cait . +// Copyright (C) 2022-2022 Fuwn +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Copyright (C) 2022-2022 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#include + +#include "parser.hh" +#include "utility.hh" + +namespace cait { + +parser_exception::parser_exception(const std::string &message) + : std::runtime_error(message) {} + +auto parser_exception::x() -> void {} + +token_bundle::token_bundle(std::size_t &_j, token_t &bundle_token, + std::vector &_parsed_token_line, + std::vector &_token_line) + : j(_j), token(bundle_token), parsed_token_line(_parsed_token_line), + token_line(_token_line) {} + +template +range_t::range_t(T begin, T end) : _begin(begin), _end(end) {} + +template auto range_t::begin() -> T { return this->_begin; } + +template auto range_t::end() -> T { return this->_end; } + +template auto range(T b, T e) -> range_t { + return range_t(b, e); +} + +parser::parser(token_tree &token_tree) { + this->fix_tree(token_tree); + this->prune_empty_token_lines(); +} + +[[nodiscard]] auto parser::tree() const noexcept -> const token_tree & { + return this->_tree; +} + +[[nodiscard]] auto parser::nodes() const noexcept -> const node_tree & { + return this->_nodes; +} + +auto parser::token_parse_assignment_operator(token_bundle bundle) -> void { + auto &local = bundle.token_line[bundle.j - 1]; + + if (local.type != token_type::pool_depth) { + local = local.with_type(token_type::variable_declaration); + } + + bundle.parsed_token_line.push_back(local); + bundle.parsed_token_line.push_back(bundle.token); + + for (auto &token_line_item : + range(bundle.token_line.begin() + 2, bundle.token_line.end())) { + token_line_item.type = token_type::variable_value; + } + + bundle.parsed_token_line.insert(bundle.parsed_token_line.end(), + bundle.token_line.begin() + 2, + bundle.token_line.end()); + + bundle.j += bundle.token_line.size() - 1; +} + +auto parser::token_parse_rule_pool_declaration(token_bundle bundle) -> void { + bundle.parsed_token_line.push_back(bundle.token); + bundle.parsed_token_line.push_back( + bundle.token_line[bundle.j + 1].with_type(token_type::identifier)); + + bundle.j += bundle.token_line.size(); +} + +auto parser::token_parse_default_declaration(token_bundle bundle) -> void { + bundle.parsed_token_line.push_back(bundle.token); + + for (auto &token_line_item : + range(bundle.token_line.begin() + 1, bundle.token_line.end())) { + token_line_item.type = token_type::default_target; + } + + bundle.parsed_token_line.insert(bundle.parsed_token_line.end(), + bundle.token_line.begin() + 1, + bundle.token_line.end()); + + bundle.j += bundle.token_line.size(); +} + +auto parser::token_parse_include_subninja_declaration(token_bundle bundle) + -> void { + bundle.parsed_token_line.push_back(bundle.token); + bundle.parsed_token_line.push_back( + bundle.token_line[bundle.j + 1].with_type(token_type::string_literal)); + + bundle.j += bundle.token_line.size(); +} + +auto parser::token_parse_rule_variable(token_bundle bundle) -> void { + bundle.parsed_token_line.push_back(bundle.token); + + for (auto &token_line_item : + range(bundle.token_line.begin() + 2, bundle.token_line.end())) { + token_line_item.type = token_type::variable_value; + } + + bundle.parsed_token_line.insert(bundle.parsed_token_line.end(), + bundle.token_line.begin() + 1, + bundle.token_line.end()); + + bundle.j += bundle.token_line.size() - 1; +} + +auto parser::token_parse_build_declaration(token_bundle bundle) -> void { + bundle.parsed_token_line.push_back(bundle.token); + + { + auto &output_file_token = bundle.token_line[bundle.j + 1]; + + output_file_token.word = + output_file_token.word.substr(0, output_file_token.word.size() - 1); + + bundle.parsed_token_line.push_back( + output_file_token.with_type(token_type::build_output)); + + // We can actually just throw away the colon... + // parsed_token_line.push_back( + // cait::token(output_file_token.line, output_file_token.begin + + // 1, + // output_file_token.end + 1, ":") + // .with_type(token_type::colon)); + } + + bundle.token_line[2].type = token_type::build_rule; + + for (auto &token_line_item : + range(bundle.token_line.begin() + 3, bundle.token_line.end())) { + token_line_item.type = token_type::build_input; + } + + bundle.parsed_token_line.insert(bundle.parsed_token_line.end(), + bundle.token_line.begin() + 2, + bundle.token_line.end()); + + bundle.j += bundle.token_line.size(); +} + +auto parser::fix_tree(token_tree &token_tree) -> void { + for (auto &token_line : token_tree) { + std::vector parsed_token_line; + + for (std::size_t j = 0; j < token_line.size(); ++j) { + auto &token = token_line[j]; + + switch (token.type) { + case token_type::assignment_operator: { + this->token_parse_assignment_operator( + token_bundle(j, token, parsed_token_line, token_line)); + } break; + case token_type::rule_declaration: + case token_type::pool_declaration: { + this->token_parse_rule_pool_declaration( + token_bundle(j, token, parsed_token_line, token_line)); + } break; + case token_type::default_declaration: { + this->token_parse_default_declaration( + token_bundle(j, token, parsed_token_line, token_line)); + } break; + case token_type::include_declaration: + case token_type::subninja_declaration: { + this->token_parse_include_subninja_declaration( + token_bundle(j, token, parsed_token_line, token_line)); + } break; + case token_type::rule_command: + case token_type::rule_depfile: + case token_type::rule_deps: + case token_type::rule_msvc_deps_prefix: + case token_type::rule_description: + case token_type::rule_dyndep: + case token_type::rule_generator: + case token_type::rule_in: + case token_type::rule_in_newline: + case token_type::rule_out: + case token_type::rule_restat: + case token_type::rule_rspfile: + case token_type::rule_rspfile_content: { + this->token_parse_rule_variable( + token_bundle(j, token, parsed_token_line, token_line)); + } break; + case token_type::build_declaration: { + this->token_parse_build_declaration( + token_bundle(j, token, parsed_token_line, token_line)); + } break; + case token_type::string_literal: + case token_type::variable_declaration: + case token_type::identifier: + case token_type::build_output: + case token_type::build_input: + case token_type::build_rule: + case token_type::default_target: + case token_type::variable_value: + case token_type::pool_depth: + break; + } + + this->_tree.push_back(parsed_token_line); + } + } +} + +auto parser::generate_nodes(const std::string &file_name, + const std::vector &lines) -> void { + for (std::size_t i = 0; i < this->_tree.size(); ++i) { + auto &token_line = this->_tree[i]; + + for (std::size_t j = 0; j < token_line.size(); ++j) { + auto &token = token_line[j]; + + switch (token.type) { + case token_type::variable_declaration: { + this->_nodes.push_back( + node::variable(token_line[0], std::vector(token_line.begin() + 2, + token_line.end()))); + + j += token_line.size(); + } break; + case token_type::default_declaration: { + this->_nodes.push_back(node::default_( + std::vector(token_line.begin() + 1, token_line.end()))); + } break; + case token_type::include_declaration: { + this->_nodes.push_back(node::include(token_line[1])); + } break; + case token_type::subninja_declaration: { + this->_nodes.push_back(node::subninja(token_line[1])); + } break; + case token_type::build_declaration: { + this->_nodes.push_back( + node::build(token_line[1], token_line[2], + std::vector(token_line.begin() + 3, token_line.end()))); + } break; + case token_type::rule_declaration: { + auto command_line_index = i + 1; + auto &command_line = this->_tree[command_line_index]; + node::item_map_type item_map; + + while (command_line[0].type == token_type::rule_command || + command_line[0].type == token_type::rule_description || + command_line[0].type == token_type::rule_depfile || + command_line[0].type == token_type::rule_deps || + command_line[0].type == token_type::rule_msvc_deps_prefix || + command_line[0].type == token_type::rule_dyndep || + command_line[0].type == token_type::rule_generator || + command_line[0].type == token_type::rule_in || + command_line[0].type == token_type::rule_in_newline || + command_line[0].type == token_type::rule_out || + command_line[0].type == token_type::rule_restat || + command_line[0].type == token_type::rule_rspfile || + command_line[0].type == token_type::rule_rspfile_content) { + item_map.insert(std::make_pair( + command_line[0].word, + std::vector(command_line.begin() + 2, command_line.end()))); + + command_line_index += 1; + command_line = this->_tree[command_line_index]; + } + + // Handle unknown rule variable error + if (command_line[1].type == token_type::assignment_operator) { + std::ostringstream error; + + error << "cait: error: " << file_name << ":" << command_line[0].line + << ": unexpected variable '" << command_line[0].word << "'\n" + << lines[command_line[0].line - 1] << "\n"; + + for (auto &word : command_line) { + error << std::string(word.word.size(), ' '); + } + + auto leading_spaces = [](const std::string &string) -> std::size_t { + std::size_t count = 0; + + while (string[count] == ' ') { + count += 1; + } + + return count; + }; + + error << std::string(leading_spaces(lines[command_line[0].line - 1]), + ' ') + << " ^ near here\n"; + + throw parser_exception(error.str()); + } + + // Handle no command line error + if (item_map.count("command") == 0) { + std::ostringstream error; + + error << "cait: error: " << file_name << ":" << command_line[0].line + << ": expected 'command =' line\n"; + + throw parser_exception(error.str()); + } + + this->_nodes.push_back(node::rule(token_line[1], item_map)); + + i += 1; + } break; + case token_type::pool_declaration: { + this->_nodes.push_back( + node::pool(token_line[1], this->_tree[i + 1][2])); + + i += 1; + } break; + case token_type::string_literal: + std::cout << "string: " << token.word << "\n"; + break; + case token_type::rule_command: + case token_type::assignment_operator: + case token_type::identifier: + case token_type::build_output: + case token_type::rule_depfile: + case token_type::rule_deps: + case token_type::rule_msvc_deps_prefix: + case token_type::rule_description: + case token_type::rule_dyndep: + case token_type::rule_generator: + case token_type::rule_in: + case token_type::rule_in_newline: + case token_type::rule_out: + case token_type::rule_restat: + case token_type::rule_rspfile: + case token_type::rule_rspfile_content: + case token_type::build_input: + case token_type::build_rule: + case token_type::default_target: + case token_type::variable_value: + case token_type::pool_depth: + break; + } + } + } +} + +auto parser::prune_empty_token_lines() -> void { + for (int j = 0; static_cast(j) < this->_tree.size(); ++j) { + if (this->_tree[static_cast(j)].empty()) { + this->_tree.erase(this->_tree.begin() + j); + } + } +} + +} // namespace cait diff --git a/cait/parser.hh b/cait/parser.hh new file mode 100644 index 0000000..4c10a28 --- /dev/null +++ b/cait/parser.hh @@ -0,0 +1,90 @@ +// This file is part of Cait . +// Copyright (C) 2022-2022 Fuwn +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Copyright (C) 2022-2022 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#ifndef PARSER_HH +#define PARSER_HH + +#include +#include + +#include "lexer.hh" +#include "node.hh" + +namespace cait { + +using node_tree = std::vector; + +class parser_exception : public std::runtime_error { +public: + explicit parser_exception(const std::string &); + virtual auto x() -> void; +}; + +struct token_bundle { +private: + using token_t = token; + +public: + std::size_t &j; + token_t &token; + std::vector &parsed_token_line; + std::vector &token_line; + + token_bundle(std::size_t &, token_t &, std::vector &, + std::vector &); +}; + +// https://stackoverflow.com/a/25652548/14452787 +template struct range_t { +private: + T _begin, _end; + +public: + range_t(T, T); + T begin(); + T end(); +}; + +// https://stackoverflow.com/a/25652548/14452787 +template range_t range(T, T); + +class parser { +private: + token_tree _tree; + node_tree _nodes; + + auto fix_tree(token_tree &) -> void; + auto prune_empty_token_lines() -> void; + static auto token_parse_build_declaration(token_bundle) -> void; + static auto token_parse_rule_variable(token_bundle) -> void; + static auto token_parse_include_subninja_declaration(token_bundle) -> void; + static auto token_parse_default_declaration(token_bundle) -> void; + static auto token_parse_rule_pool_declaration(token_bundle) -> void; + static auto token_parse_assignment_operator(token_bundle) -> void; + +public: + explicit parser(token_tree &); + [[nodiscard]] auto tree() const noexcept -> const token_tree &; + [[nodiscard]] auto nodes() const noexcept -> const node_tree &; + auto generate_nodes(const std::string &, + const std::vector &) noexcept(false) -> void; +}; + +} // namespace cait + +#endif // PARSER_HH diff --git a/cait/token.cc b/cait/token.cc new file mode 100644 index 0000000..3ec3efd --- /dev/null +++ b/cait/token.cc @@ -0,0 +1,102 @@ +// This file is part of Cait . +// Copyright (C) 2022-2022 Fuwn +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Copyright (C) 2022-2022 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#include "token.hh" + +namespace cait { + +token::token(std::size_t _line, std::size_t _begin, std::size_t _end, + const std::string &_word) + : line(_line), begin(_begin), end(_end), word(_word) { + switch (utility::hash(_word.c_str())) { + case utility::hash("rule"): { + this->type = token_type::rule_declaration; + } break; + case utility::hash("command"): { + this->type = token_type::rule_command; + } break; + case utility::hash("depfile"): { + this->type = token_type::rule_depfile; + } break; + case utility::hash("deps"): { + this->type = token_type::rule_deps; + } break; + case utility::hash("msvc_deps_prefix"): { + this->type = token_type::rule_msvc_deps_prefix; + } break; + case utility::hash("description"): { + this->type = token_type::rule_description; + } break; + case utility::hash("dyndep"): { + this->type = token_type::rule_dyndep; + } break; + case utility::hash("generator"): { + this->type = token_type::rule_generator; + } break; + case utility::hash("in"): { + this->type = token_type::rule_in; + } break; + case utility::hash("in_newline"): { + this->type = token_type::rule_in_newline; + } break; + case utility::hash("out"): { + this->type = token_type::rule_out; + } break; + case utility::hash("restat"): { + this->type = token_type::rule_restat; + } break; + case utility::hash("rspfile"): { + this->type = token_type::rule_rspfile; + } break; + case utility::hash("rspfile_content"): { + this->type = token_type::rule_rspfile_content; + } break; + case utility::hash("depth"): { + this->type = token_type::pool_depth; + } break; + case utility::hash("build"): { + this->type = token_type::build_declaration; + } break; + case utility::hash("default"): { + this->type = token_type::default_declaration; + } break; + case utility::hash("pool"): { + this->type = token_type::pool_declaration; + } break; + case utility::hash("subninja"): { + this->type = token_type::subninja_declaration; + } break; + case utility::hash("include"): { + this->type = token_type::include_declaration; + } break; + case utility::hash("="): { + this->type = token_type::assignment_operator; + } break; + default: { + this->type = token_type::string_literal; + } break; + } +} + +auto token::with_type(token_type _type) -> token { + this->type = _type; + + return *this; +} + +} // namespace cait diff --git a/cait/token.hh b/cait/token.hh new file mode 100644 index 0000000..44f8f69 --- /dev/null +++ b/cait/token.hh @@ -0,0 +1,153 @@ +// This file is part of Cait . +// Copyright (C) 2022-2022 Fuwn +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Copyright (C) 2022-2022 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#ifndef TOKEN_HH +#define TOKEN_HH + +#include +#include + +#include "utility.hh" + +namespace cait { + +struct token; + +using token_tree = std::vector>; + +// https://stackoverflow.com/a/53284026/14452787 +class token_type { +public: + enum token_type_value { + rule_declaration, + rule_command, + rule_depfile, + rule_deps, + rule_msvc_deps_prefix, + rule_description, + rule_dyndep, + rule_generator, + rule_in, + rule_in_newline, + rule_out, + rule_restat, + rule_rspfile, + rule_rspfile_content, + build_declaration, + build_input, + build_rule, + build_output, + default_declaration, + default_target, + string_literal, + variable_declaration, + variable_value, + assignment_operator, + identifier, + pool_declaration, + pool_depth, + subninja_declaration, + include_declaration + }; + +private: + token_type_value value; + [[maybe_unused]] int padding; + +public: + token_type() = default; + constexpr token_type(token_type_value _value) : value(_value) {} + [[nodiscard]] constexpr std::string string() const { + switch (this->value) { + case rule_declaration: + return "rule_declaration"; + case rule_command: + return "rule_command"; + case rule_depfile: + return "rule_depfile"; + case rule_deps: + return "rule_deps"; + case rule_msvc_deps_prefix: + return "rule_msvc_deps_prefix"; + case rule_description: + return "rule_description"; + case rule_dyndep: + return "rule_dyndep"; + case rule_generator: + return "rule_generator"; + case rule_in: + return "rule_in"; + case rule_in_newline: + return "rule_in_newline"; + case rule_out: + return "rule_out"; + case rule_restat: + return "rule_restat"; + case rule_rspfile: + return "rule_rspfile"; + case rule_rspfile_content: + return "rule_rspfile_content"; + case build_declaration: + return "build_declaration"; + case build_input: + return "build_input"; + case build_rule: + return "build_rule"; + case default_declaration: + return "default_declaration"; + case default_target: + return "default_target"; + case string_literal: + return "string_literal"; + case variable_declaration: + return "variable_declaration"; + case variable_value: + return "variable_value"; + case assignment_operator: + return "assignment_operator"; + case identifier: + return "identifier"; + case build_output: + return "build_output"; + case pool_declaration: + return "pool_declaration"; + case pool_depth: + return "pool_depth"; + case subninja_declaration: + return "subninja_declaration"; + case include_declaration: + return "include_declaration"; + } + } + constexpr operator token_type_value() const { return this->value; } +}; + +struct token { + std::size_t line; + [[maybe_unused]] std::size_t begin; + [[maybe_unused]] std::size_t end; + std::string word; + token_type type{}; + + token(std::size_t, std::size_t, std::size_t, const std::string &); + auto with_type(token_type) -> token; +}; + +} // namespace cait + +#endif // TOKEN_HH diff --git a/cait/utility.hh b/cait/utility.hh new file mode 100644 index 0000000..26a324e --- /dev/null +++ b/cait/utility.hh @@ -0,0 +1,38 @@ +// This file is part of Cait . +// Copyright (C) 2022-2022 Fuwn +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// Copyright (C) 2022-2022 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#ifndef UTILITY_HH +#define UTILITY_HH + +#include + +namespace cait::utility { + +// "Hash" a string so that it can be used within a `switch` statement. +// +// https://stackoverflow.com/a/46711735/14452787 +constexpr auto hash(const char *data, std::size_t const size = 0) noexcept + -> std::uint32_t { + return !data[size] ? 5381 + : (hash(data, size + 1) * 33) ^ + static_cast(data[size]); +} + +} // namespace cait::utility + +#endif // UTILITY_HH diff --git a/src/cait.cc b/src/cait.cc deleted file mode 100644 index 623c781..0000000 --- a/src/cait.cc +++ /dev/null @@ -1,21 +0,0 @@ -// This file is part of Cait . -// Copyright (C) 2022-2022 Fuwn -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Copyright (C) 2022-2022 Fuwn -// SPDX-License-Identifier: GPL-3.0-only - -#include "cli.hh" - -auto main(int argc, char **argv) -> int { return cait::cli(argc, argv).look(); } diff --git a/src/cli.cc b/src/cli.cc deleted file mode 100644 index 573b9a9..0000000 --- a/src/cli.cc +++ /dev/null @@ -1,147 +0,0 @@ -// This file is part of Cait . -// Copyright (C) 2022-2022 Fuwn -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Copyright (C) 2022-2022 Fuwn -// SPDX-License-Identifier: GPL-3.0-only - -#include - -#include "cli.hh" -#include "context.hh" -#include "help.hh" -#include "lexer.hh" -#include "parser.hh" -#include "utility.hh" - -namespace cait { - -cli::cli(int argc, char **argv) : _argc(argc), _argv(argv) { - for (int i = 1; i < _argc; i++) { - if (_argv[i][0] == '-') { - std::string key = _argv[i]; - std::string value; - - if (i + 1 < _argc) { - value = _argv[i + 1]; - - i += 1; - } - - options[key] = value.empty() ? std::nullopt : std::optional(value); - } - } -} - -// auto cli::argc() const -> int { return this->_argc; } - -// auto cli::argv() const -> char ** { return this->_argv; } - -auto cli::arg(int i) -> std::optional { - if (i < this->_argc) { - return std::string(this->_argv[i]); - } - - return std::nullopt; -} - -auto cli::option(const std::string &key) - -> std::optional> { - if (this->options.contains(key)) { - return this->options[key]; - } - - return std::nullopt; -} - -auto cli::look() -> int { - switch (cait::utility::hash(this->arg(1).value_or("").c_str())) { - case cait::utility::hash("--help"): - case cait::utility::hash("-h"): { - std::cout << cait::help_message() << '\n'; - } break; - case cait::utility::hash("--version"): { - std::cout << version::whole() << '\n'; - } break; - default: { - std::string cait_file_name = this->option("-f")->value_or("build.cait"); - std::ifstream cait_file(cait_file_name, std::ios::in); - - if (cait_file.is_open()) { - lexer lexer(cait_file); - parser parser(lexer.tree()); - - try { - parser.generate_nodes(cait_file_name, lexer.lines()); - } catch (const parser_exception &e) { - std::cout << e.what() << '\n'; - - return 1; - } - - context context(parser); - - for (const auto &token_line : parser.tree()) { - for (const auto &token : token_line) { - std::cout << token.word << "(" << token.type.string() << ")"; - } - - std::cout << "\n\n"; - } - - for (const auto &token_line : parser.nodes()) { - std::visit( - [](const auto &node) -> void { - std::cout << node.string() << '\n'; - }, - token_line); - } - - // auto cc = context.rule("cc"); - - // if (cc.has_value()) { - // std::cout << "cc: '" << cc.value().string() << "'\n"; - // } else { - // std::cout << "cc has no value\n"; - // } - - return 0; - } - - std::cout << "cait: error: loading '" << cait_file_name - << "': The system cannot find " - "the file specified.\n\n"; - - return 1; - } // break; - } - - // auto option = this->option("-h"); - - // if (option.has_value()) { - // std::cout << "option exists "; - - // if (option->has_value()) { - // std::cout << "with value " << option->value() << std::endl; - // } else { - // std::cout << "with no value\n"; - // } - // } else { - // std::cout << "option does not exist\n"; - // } - - return 0; -} - -} // namespace cait diff --git a/src/cli.hh b/src/cli.hh deleted file mode 100644 index 155bbe5..0000000 --- a/src/cli.hh +++ /dev/null @@ -1,47 +0,0 @@ -// This file is part of Cait . -// Copyright (C) 2022-2022 Fuwn -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Copyright (C) 2022-2022 Fuwn -// SPDX-License-Identifier: GPL-3.0-only - -#ifndef CLI_HH -#define CLI_HH - -#include -#include -#include -#include - -namespace cait { - -class cli { -private: - int _argc; - [[maybe_unused]] int padding = 0; - char **_argv; - std::map> options; - -public: - cli(int, char **); - // auto argc() const -> int; - // auto argv() const -> char **; - auto arg(int) -> std::optional; - auto option(const std::string &) -> std::optional>; - auto look() -> int; -}; - -} // namespace cait - -#endif // CLI_HH diff --git a/src/context.cc b/src/context.cc deleted file mode 100644 index f34d55c..0000000 --- a/src/context.cc +++ /dev/null @@ -1,84 +0,0 @@ -// This file is part of Cait . -// Copyright (C) 2022-2022 Fuwn -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Copyright (C) 2022-2022 Fuwn -// SPDX-License-Identifier: GPL-3.0-only - -#include - -#include "context.hh" - -namespace cait { - -context::context(const parser &parser) { - this->jobs = std::thread::hardware_concurrency() + 2; - - for (const auto &parse_node : parser.nodes()) { - std::visit( - [this](auto &node) { - using T = std::decay_t; - - if constexpr (std::is_same_v) { - this->identifiers.insert( - {std::make_pair(node.name().word, - token_type::variable_declaration), - node}); - } else if constexpr (std::is_same_v) { - this->identifiers.insert( - {std::make_pair(node.name().word, token_type::rule_declaration), - node}); - } - }, - parse_node); - } -} - -[[maybe_unused]] auto context::variable(const std::string &variable) - -> std::optional { - auto *it = std::get_if(&this->identifiers.at( - std::make_pair(variable, token_type::variable_declaration))); - - if (it == nullptr) { - return std::nullopt; - } - - return *it; -} - -[[maybe_unused]] auto context::rule(const std::string &variable) - -> std::optional { - auto *it = std::get_if(&this->identifiers.at( - std::make_pair(variable, token_type::rule_declaration))); - - if (it == nullptr) { - return std::nullopt; - } - - return *it; -} - -[[maybe_unused]] auto context::build(const std::string &variable) - -> std::optional { - auto *it = std::get_if(&this->identifiers.at( - std::make_pair(variable, token_type::build_declaration))); - - if (it == nullptr) { - return std::nullopt; - } - - return *it; -} - -} // namespace cait diff --git a/src/context.hh b/src/context.hh deleted file mode 100644 index f42555d..0000000 --- a/src/context.hh +++ /dev/null @@ -1,47 +0,0 @@ -// This file is part of Cait . -// Copyright (C) 2022-2022 Fuwn -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Copyright (C) 2022-2022 Fuwn -// SPDX-License-Identifier: GPL-3.0-only - -#ifndef CONTEXT_HH -#define CONTEXT_HH - -#include -#include -#include - -#include "parser.hh" -#include "token.hh" - -namespace cait { - -class context { -private: - std::map, node::node_set> identifiers; - [[maybe_unused]] std::size_t jobs; - -public: - explicit context(const parser &); - [[maybe_unused]] auto variable(const std::string &) - -> std::optional; - [[maybe_unused]] auto rule(const std::string &) -> std::optional; - [[maybe_unused]] auto build(const std::string &) - -> std::optional; -}; - -} // namespace cait - -#endif // CONTEXT_HH diff --git a/src/help.cc b/src/help.cc deleted file mode 100644 index 2271e9f..0000000 --- a/src/help.cc +++ /dev/null @@ -1,75 +0,0 @@ -// This file is part of Cait . -// Copyright (C) 2022-2022 Fuwn -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Copyright (C) 2022-2022 Fuwn -// SPDX-License-Identifier: GPL-3.0-only - -#include - -#include "help.hh" - -namespace cait { - -auto help_message() -> std::string { - std::ostringstream help; - - help - << "usage: cait [options] [targets...]\n" - "\n" - "if targets are unspecified, builds the 'default' target (see " - "manual).\n" - "\n" - "options:\n" - " --version print cait version (\"" + - version::whole() + - "\")\n" - " -v, --verbose " - "show all command " - "lines while " - "building\n" - "\n" - " -C DIR " - "change to DIR " - "before doing " - "anything else\n" - " -f FILE " - "specify input " - "build file " - "[default=build." - "cait]\n" - "\n" - " -j N run N " - "jobs in parallel " - "(0 means " - "infinity) " - "[default=" - << std::thread::hardware_concurrency() + 2 - << " on this system]\n" - " -k N keep going until N jobs fail (0 means infinity) " - "[default=1]\n" - " -l N do not start new jobs if the load average is greater " - "than N\n" - " -n dry run (don't run commands but act like they succeeded)\n" - "\n" - " -d MODE enable debugging (use '-d list' to list modes)\n" - " -t TOOL run a subtool (use '-t list' to list subtools)\n" - " terminates toplevel options; further flags are passed to the " - "tool\n" - " -w FLAG adjust warnings (use '-w list' to list warnings)"; - - return help.str(); -} - -} // namespace cait diff --git a/src/help.hh b/src/help.hh deleted file mode 100644 index 88517da..0000000 --- a/src/help.hh +++ /dev/null @@ -1,44 +0,0 @@ -// This file is part of Cait . -// Copyright (C) 2022-2022 Fuwn -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Copyright (C) 2022-2022 Fuwn -// SPDX-License-Identifier: GPL-3.0-only - -#ifndef HELP_HH -#define HELP_HH - -#include - -#define CAIT_TO_STRING(x) #x -#define CAIT_STR(x) CAIT_TO_STRING(x) - -namespace cait { - -namespace version { - -constexpr unsigned int major = 0; -constexpr unsigned int minor = 1; -constexpr unsigned int patch = 0; -constexpr auto whole() -> std::string { - return CAIT_STR(major) "." CAIT_STR(minor) "." CAIT_STR(patch); -} - -} // namespace version - -auto help_message() -> std::string; - -} // namespace cait - -#endif // HELP_HH diff --git a/src/lexer.cc b/src/lexer.cc deleted file mode 100644 index 1100a2e..0000000 --- a/src/lexer.cc +++ /dev/null @@ -1,69 +0,0 @@ -// This file is part of Cait . -// Copyright (C) 2022-2022 Fuwn -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Copyright (C) 2022-2022 Fuwn -// SPDX-License-Identifier: GPL-3.0-only - -#include "lexer.hh" - -namespace cait { - -lexer::lexer(std::ifstream &file) { - std::string line; - std::size_t line_number = 1; - - for (;;) { - while (std::getline(file, line)) { - std::vector token_line; - - this->_lines.push_back(line); - - start: - std::istringstream line_stream(line); - std::string word; - std::size_t character = 1; - - while (std::getline(line_stream, word, ' ')) { - if (word.empty()) { - continue; - } - - token_line.emplace_back(line_number, character, character + word.size(), - word); - - character += word.size() + 1; - } - - if (word == "$") { - token_line.pop_back(); - std::getline(file, line); - - goto start; - } - - this->_tree.push_back(token_line); - - line_number += 1; - } - - break; - } -} - -auto lexer::tree() -> token_tree & { return this->_tree; } - -auto lexer::lines() -> const std::vector & { return this->_lines; } - -} // namespace cait diff --git a/src/lexer.hh b/src/lexer.hh deleted file mode 100644 index ffe3a33..0000000 --- a/src/lexer.hh +++ /dev/null @@ -1,44 +0,0 @@ -// This file is part of Cait . -// Copyright (C) 2022-2022 Fuwn -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Copyright (C) 2022-2022 Fuwn -// SPDX-License-Identifier: GPL-3.0-only - -#ifndef LEXER_HH -#define LEXER_HH - -#include -#include -#include -#include - -#include "token.hh" - -namespace cait { - -class lexer { -private: - token_tree _tree; - std::vector _lines; - -public: - explicit lexer(std::ifstream &); - auto tree() -> token_tree &; - auto lines() -> const std::vector &; -}; - -} // namespace cait - -#endif // LEXER_HH diff --git a/src/node.cc b/src/node.cc deleted file mode 100644 index 9b76c27..0000000 --- a/src/node.cc +++ /dev/null @@ -1,165 +0,0 @@ -// This file is part of Cait . -// Copyright (C) 2022-2022 Fuwn -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Copyright (C) 2022-2022 Fuwn -// SPDX-License-Identifier: GPL-3.0-only - -#include - -#include "node.hh" - -namespace cait::node { - -variable::variable(const token &variable_name, - std::vector variable_value_list) - : _name(variable_name), _value_list(std::move(variable_value_list)) {} - -[[nodiscard]] auto variable::string() const -> std::string { - std::ostringstream values; - - for (const auto &command : this->_value_list) { - values << command.word << " "; - } - - return this->_name.word + " = " + values.str(); -} - -[[nodiscard]] auto variable::name() const noexcept -> const token & { - return this->_name; -} - -[[maybe_unused]] [[nodiscard]] auto variable::value_list() const noexcept - -> const std::vector & { - return this->_value_list; -} - -rule::rule(const token &rule_name, item_map_type rule_item_map) - : _name(rule_name), _item_map(std::move(rule_item_map)) {} - -[[nodiscard]] auto rule::string() const -> std::string { - std::ostringstream values; - - for (const auto &item : this->_item_map) { - values << " " << item.first << " = "; - - for (const auto &command : item.second) { - values << command.word << " "; - } - - values << '\n'; - } - - // Clip off the last newline - return "rule " + this->_name.word + "\n" + - values.str().substr(0, values.str().size() - 1); -} - -[[nodiscard]] auto rule::name() const noexcept -> const token & { - return this->_name; -} - -[[maybe_unused]] [[nodiscard]] auto rule::item_map() const noexcept - -> const item_map_type & { - return this->_item_map; -} - -pool::pool(const token &pool_name, const token &pool_depth) - : _name(pool_name), _depth(pool_depth) {} - -[[nodiscard]] auto pool::string() const -> std::string { - return "pool " + this->_name.word + "\n depth = " + this->_depth.word; -} - -[[nodiscard]] auto pool::name() const noexcept -> const token & { - return this->_name; -} - -[[maybe_unused]] [[nodiscard]] auto pool::depth() const noexcept - -> const token & { - return this->_depth; -} - -build::build(const token &build_output, const token &build_rule, - std::vector build_inputs) - : _output(build_output), _rule(build_rule), - _inputs(std::move(build_inputs)) {} - -[[nodiscard]] auto build::string() const -> std::string { - std::ostringstream values; - - for (const auto &command : this->_inputs) { - values << command.word << " "; - } - - return "build " + this->_output.word + ": " + this->_rule.word + ' ' + - values.str(); -} - -[[maybe_unused]] [[nodiscard]] auto build::output() const noexcept - -> const token & { - return this->_output; -} - -[[maybe_unused]] [[nodiscard]] auto build::rule() const noexcept - -> const token & { - return this->_rule; -} - -[[maybe_unused]] [[nodiscard]] auto build::inputs() const noexcept - -> const std::vector & { - return this->_inputs; -} - -default_::default_(std::vector default_targets) - : _targets(std::move(default_targets)) {} - -[[nodiscard]] auto default_::string() const -> std::string { - std::ostringstream values; - - for (const auto &target : this->_targets) { - values << target.word << " "; - } - - return "default " + values.str(); -} - -[[maybe_unused]] [[nodiscard]] auto default_::targets() const noexcept - -> const std::vector & { - return this->_targets; -} - -include::include(const token &include_path) : _path(include_path) {} - -[[nodiscard]] auto include::string() const -> std::string { - return "include " + this->_path.word; -} - -[[maybe_unused]] [[nodiscard]] auto include::path() const noexcept - -> const token & { - return this->_path; -} - -subninja::subninja(const token &subninja_path) : _path(subninja_path) {} - -[[nodiscard]] auto subninja::string() const -> std::string { - return "subninja " + this->_path.word; -} - -[[maybe_unused]] [[nodiscard]] auto subninja::path() const noexcept - -> const token & { - return this->_path; -} - -} // namespace cait::node diff --git a/src/node.hh b/src/node.hh deleted file mode 100644 index cc2362c..0000000 --- a/src/node.hh +++ /dev/null @@ -1,147 +0,0 @@ -// This file is part of Cait . -// Copyright (C) 2022-2022 Fuwn -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Copyright (C) 2022-2022 Fuwn -// SPDX-License-Identifier: GPL-3.0-only - -#ifndef NODE_HH -#define NODE_HH - -#include -#include -#include -#include -#include -#include - -#include "token.hh" - -namespace cait { - -namespace { - -class stringable { -public: - [[maybe_unused]] stringable() = default; - stringable(const stringable &) = default; - virtual ~stringable() = default; - [[nodiscard]] virtual auto string() const -> std::string = 0; -}; - -} // namespace - -namespace node { - -class variable; -class rule; -class build; -class default_; -class pool; -class include; -class subninja; - -using item_map_type = std::map>; -using node_set = - std::variant; - -class variable : stringable { -private: - const token &_name; - std::vector _value_list; - -public: - variable(const token &, std::vector); - [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto name() const noexcept -> const token &; - [[maybe_unused]] [[nodiscard]] auto value_list() const noexcept - -> const std::vector &; -}; - -class rule : stringable { -private: - const token &_name; - item_map_type _item_map; - -public: - rule(const token &, item_map_type); - [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto name() const noexcept -> const token &; - [[maybe_unused]] [[nodiscard]] auto item_map() const noexcept - -> const item_map_type &; -}; - -class pool : stringable { -private: - const token &_name; - const token &_depth; - -public: - pool(const token &, const token &); - [[nodiscard]] auto string() const -> std::string override; - [[nodiscard]] auto name() const noexcept -> const token &; - [[maybe_unused]] [[nodiscard]] auto depth() const noexcept -> const token &; -}; - -class build : stringable { -private: - const token &_output; - const token &_rule; - std::vector _inputs; - -public: - build(const token &, const token &, std::vector); - [[nodiscard]] auto string() const -> std::string override; - [[maybe_unused]] [[nodiscard]] auto output() const noexcept -> const token &; - [[maybe_unused]] [[nodiscard]] auto rule() const noexcept -> const token &; - [[maybe_unused]] [[nodiscard]] auto inputs() const noexcept - -> const std::vector &; -}; - -class default_ : stringable { -private: - std::vector _targets; - -public: - explicit default_(std::vector); - [[nodiscard]] auto string() const -> std::string override; - [[maybe_unused]] [[nodiscard]] auto targets() const noexcept - -> const std::vector &; -}; - -class include : stringable { -private: - const token &_path; - -public: - explicit include(const token &); - [[nodiscard]] auto string() const -> std::string override; - [[maybe_unused]] [[nodiscard]] auto path() const noexcept -> const token &; -}; - -class subninja : stringable { -private: - const token &_path; - -public: - explicit subninja(const token &); - [[nodiscard]] auto string() const -> std::string override; - [[maybe_unused]] [[nodiscard]] auto path() const noexcept -> const token &; -}; - -} // namespace node - -} // namespace cait - -#endif // NODE_HH diff --git a/src/parser.cc b/src/parser.cc deleted file mode 100644 index 734be2f..0000000 --- a/src/parser.cc +++ /dev/null @@ -1,371 +0,0 @@ -// This file is part of Cait . -// Copyright (C) 2022-2022 Fuwn -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Copyright (C) 2022-2022 Fuwn -// SPDX-License-Identifier: GPL-3.0-only - -#include - -#include "parser.hh" -#include "utility.hh" - -namespace cait { - -parser_exception::parser_exception(const std::string &message) - : std::runtime_error(message) {} - -auto parser_exception::x() -> void {} - -token_bundle::token_bundle(std::size_t &_j, token_t &bundle_token, - std::vector &_parsed_token_line, - std::vector &_token_line) - : j(_j), token(bundle_token), parsed_token_line(_parsed_token_line), - token_line(_token_line) {} - -template -range_t::range_t(T begin, T end) : _begin(begin), _end(end) {} - -template auto range_t::begin() -> T { return this->_begin; } - -template auto range_t::end() -> T { return this->_end; } - -template auto range(T b, T e) -> range_t { - return range_t(b, e); -} - -parser::parser(token_tree &token_tree) { - this->fix_tree(token_tree); - this->prune_empty_token_lines(); -} - -[[nodiscard]] auto parser::tree() const noexcept -> const token_tree & { - return this->_tree; -} - -[[nodiscard]] auto parser::nodes() const noexcept -> const node_tree & { - return this->_nodes; -} - -auto parser::token_parse_assignment_operator(token_bundle bundle) -> void { - auto &local = bundle.token_line[bundle.j - 1]; - - if (local.type != token_type::pool_depth) { - local = local.with_type(token_type::variable_declaration); - } - - bundle.parsed_token_line.push_back(local); - bundle.parsed_token_line.push_back(bundle.token); - - for (auto &token_line_item : - range(bundle.token_line.begin() + 2, bundle.token_line.end())) { - token_line_item.type = token_type::variable_value; - } - - bundle.parsed_token_line.insert(bundle.parsed_token_line.end(), - bundle.token_line.begin() + 2, - bundle.token_line.end()); - - bundle.j += bundle.token_line.size() - 1; -} - -auto parser::token_parse_rule_pool_declaration(token_bundle bundle) -> void { - bundle.parsed_token_line.push_back(bundle.token); - bundle.parsed_token_line.push_back( - bundle.token_line[bundle.j + 1].with_type(token_type::identifier)); - - bundle.j += bundle.token_line.size(); -} - -auto parser::token_parse_default_declaration(token_bundle bundle) -> void { - bundle.parsed_token_line.push_back(bundle.token); - - for (auto &token_line_item : - range(bundle.token_line.begin() + 1, bundle.token_line.end())) { - token_line_item.type = token_type::default_target; - } - - bundle.parsed_token_line.insert(bundle.parsed_token_line.end(), - bundle.token_line.begin() + 1, - bundle.token_line.end()); - - bundle.j += bundle.token_line.size(); -} - -auto parser::token_parse_include_subninja_declaration(token_bundle bundle) - -> void { - bundle.parsed_token_line.push_back(bundle.token); - bundle.parsed_token_line.push_back( - bundle.token_line[bundle.j + 1].with_type(token_type::string_literal)); - - bundle.j += bundle.token_line.size(); -} - -auto parser::token_parse_rule_variable(token_bundle bundle) -> void { - bundle.parsed_token_line.push_back(bundle.token); - - for (auto &token_line_item : - range(bundle.token_line.begin() + 2, bundle.token_line.end())) { - token_line_item.type = token_type::variable_value; - } - - bundle.parsed_token_line.insert(bundle.parsed_token_line.end(), - bundle.token_line.begin() + 1, - bundle.token_line.end()); - - bundle.j += bundle.token_line.size() - 1; -} - -auto parser::token_parse_build_declaration(token_bundle bundle) -> void { - bundle.parsed_token_line.push_back(bundle.token); - - { - auto &output_file_token = bundle.token_line[bundle.j + 1]; - - output_file_token.word = - output_file_token.word.substr(0, output_file_token.word.size() - 1); - - bundle.parsed_token_line.push_back( - output_file_token.with_type(token_type::build_output)); - - // We can actually just throw away the colon... - // parsed_token_line.push_back( - // cait::token(output_file_token.line, output_file_token.begin + - // 1, - // output_file_token.end + 1, ":") - // .with_type(token_type::colon)); - } - - bundle.token_line[2].type = token_type::build_rule; - - for (auto &token_line_item : - range(bundle.token_line.begin() + 3, bundle.token_line.end())) { - token_line_item.type = token_type::build_input; - } - - bundle.parsed_token_line.insert(bundle.parsed_token_line.end(), - bundle.token_line.begin() + 2, - bundle.token_line.end()); - - bundle.j += bundle.token_line.size(); -} - -auto parser::fix_tree(token_tree &token_tree) -> void { - for (auto &token_line : token_tree) { - std::vector parsed_token_line; - - for (std::size_t j = 0; j < token_line.size(); ++j) { - auto &token = token_line[j]; - - switch (token.type) { - case token_type::assignment_operator: { - this->token_parse_assignment_operator( - token_bundle(j, token, parsed_token_line, token_line)); - } break; - case token_type::rule_declaration: - case token_type::pool_declaration: { - this->token_parse_rule_pool_declaration( - token_bundle(j, token, parsed_token_line, token_line)); - } break; - case token_type::default_declaration: { - this->token_parse_default_declaration( - token_bundle(j, token, parsed_token_line, token_line)); - } break; - case token_type::include_declaration: - case token_type::subninja_declaration: { - this->token_parse_include_subninja_declaration( - token_bundle(j, token, parsed_token_line, token_line)); - } break; - case token_type::rule_command: - case token_type::rule_depfile: - case token_type::rule_deps: - case token_type::rule_msvc_deps_prefix: - case token_type::rule_description: - case token_type::rule_dyndep: - case token_type::rule_generator: - case token_type::rule_in: - case token_type::rule_in_newline: - case token_type::rule_out: - case token_type::rule_restat: - case token_type::rule_rspfile: - case token_type::rule_rspfile_content: { - this->token_parse_rule_variable( - token_bundle(j, token, parsed_token_line, token_line)); - } break; - case token_type::build_declaration: { - this->token_parse_build_declaration( - token_bundle(j, token, parsed_token_line, token_line)); - } break; - case token_type::string_literal: - case token_type::variable_declaration: - case token_type::identifier: - case token_type::build_output: - case token_type::build_input: - case token_type::build_rule: - case token_type::default_target: - case token_type::variable_value: - case token_type::pool_depth: - break; - } - - this->_tree.push_back(parsed_token_line); - } - } -} - -auto parser::generate_nodes(const std::string &file_name, - const std::vector &lines) -> void { - for (std::size_t i = 0; i < this->_tree.size(); ++i) { - auto &token_line = this->_tree[i]; - - for (std::size_t j = 0; j < token_line.size(); ++j) { - auto &token = token_line[j]; - - switch (token.type) { - case token_type::variable_declaration: { - this->_nodes.push_back( - node::variable(token_line[0], std::vector(token_line.begin() + 2, - token_line.end()))); - - j += token_line.size(); - } break; - case token_type::default_declaration: { - this->_nodes.push_back(node::default_( - std::vector(token_line.begin() + 1, token_line.end()))); - } break; - case token_type::include_declaration: { - this->_nodes.push_back(node::include(token_line[1])); - } break; - case token_type::subninja_declaration: { - this->_nodes.push_back(node::subninja(token_line[1])); - } break; - case token_type::build_declaration: { - this->_nodes.push_back( - node::build(token_line[1], token_line[2], - std::vector(token_line.begin() + 3, token_line.end()))); - } break; - case token_type::rule_declaration: { - auto command_line_index = i + 1; - auto &command_line = this->_tree[command_line_index]; - node::item_map_type item_map; - - while (command_line[0].type == token_type::rule_command || - command_line[0].type == token_type::rule_description || - command_line[0].type == token_type::rule_depfile || - command_line[0].type == token_type::rule_deps || - command_line[0].type == token_type::rule_msvc_deps_prefix || - command_line[0].type == token_type::rule_dyndep || - command_line[0].type == token_type::rule_generator || - command_line[0].type == token_type::rule_in || - command_line[0].type == token_type::rule_in_newline || - command_line[0].type == token_type::rule_out || - command_line[0].type == token_type::rule_restat || - command_line[0].type == token_type::rule_rspfile || - command_line[0].type == token_type::rule_rspfile_content) { - item_map.insert(std::make_pair( - command_line[0].word, - std::vector(command_line.begin() + 2, command_line.end()))); - - command_line_index += 1; - command_line = this->_tree[command_line_index]; - } - - // Handle unknown rule variable error - if (command_line[1].type == token_type::assignment_operator) { - std::ostringstream error; - - error << "cait: error: " << file_name << ":" << command_line[0].line - << ": unexpected variable '" << command_line[0].word << "'\n" - << lines[command_line[0].line - 1] << "\n"; - - for (auto &word : command_line) { - error << std::string(word.word.size(), ' '); - } - - auto leading_spaces = [](const std::string &string) -> std::size_t { - std::size_t count = 0; - - while (string[count] == ' ') { - count += 1; - } - - return count; - }; - - error << std::string(leading_spaces(lines[command_line[0].line - 1]), - ' ') - << " ^ near here\n"; - - throw parser_exception(error.str()); - } - - // Handle no command line error - if (item_map.count("command") == 0) { - std::ostringstream error; - - error << "cait: error: " << file_name << ":" << command_line[0].line - << ": expected 'command =' line\n"; - - throw parser_exception(error.str()); - } - - this->_nodes.push_back(node::rule(token_line[1], item_map)); - - i += 1; - } break; - case token_type::pool_declaration: { - this->_nodes.push_back( - node::pool(token_line[1], this->_tree[i + 1][2])); - - i += 1; - } break; - case token_type::string_literal: - std::cout << "string: " << token.word << "\n"; - break; - case token_type::rule_command: - case token_type::assignment_operator: - case token_type::identifier: - case token_type::build_output: - case token_type::rule_depfile: - case token_type::rule_deps: - case token_type::rule_msvc_deps_prefix: - case token_type::rule_description: - case token_type::rule_dyndep: - case token_type::rule_generator: - case token_type::rule_in: - case token_type::rule_in_newline: - case token_type::rule_out: - case token_type::rule_restat: - case token_type::rule_rspfile: - case token_type::rule_rspfile_content: - case token_type::build_input: - case token_type::build_rule: - case token_type::default_target: - case token_type::variable_value: - case token_type::pool_depth: - break; - } - } - } -} - -auto parser::prune_empty_token_lines() -> void { - for (int j = 0; static_cast(j) < this->_tree.size(); ++j) { - if (this->_tree[static_cast(j)].empty()) { - this->_tree.erase(this->_tree.begin() + j); - } - } -} - -} // namespace cait diff --git a/src/parser.hh b/src/parser.hh deleted file mode 100644 index 4c10a28..0000000 --- a/src/parser.hh +++ /dev/null @@ -1,90 +0,0 @@ -// This file is part of Cait . -// Copyright (C) 2022-2022 Fuwn -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Copyright (C) 2022-2022 Fuwn -// SPDX-License-Identifier: GPL-3.0-only - -#ifndef PARSER_HH -#define PARSER_HH - -#include -#include - -#include "lexer.hh" -#include "node.hh" - -namespace cait { - -using node_tree = std::vector; - -class parser_exception : public std::runtime_error { -public: - explicit parser_exception(const std::string &); - virtual auto x() -> void; -}; - -struct token_bundle { -private: - using token_t = token; - -public: - std::size_t &j; - token_t &token; - std::vector &parsed_token_line; - std::vector &token_line; - - token_bundle(std::size_t &, token_t &, std::vector &, - std::vector &); -}; - -// https://stackoverflow.com/a/25652548/14452787 -template struct range_t { -private: - T _begin, _end; - -public: - range_t(T, T); - T begin(); - T end(); -}; - -// https://stackoverflow.com/a/25652548/14452787 -template range_t range(T, T); - -class parser { -private: - token_tree _tree; - node_tree _nodes; - - auto fix_tree(token_tree &) -> void; - auto prune_empty_token_lines() -> void; - static auto token_parse_build_declaration(token_bundle) -> void; - static auto token_parse_rule_variable(token_bundle) -> void; - static auto token_parse_include_subninja_declaration(token_bundle) -> void; - static auto token_parse_default_declaration(token_bundle) -> void; - static auto token_parse_rule_pool_declaration(token_bundle) -> void; - static auto token_parse_assignment_operator(token_bundle) -> void; - -public: - explicit parser(token_tree &); - [[nodiscard]] auto tree() const noexcept -> const token_tree &; - [[nodiscard]] auto nodes() const noexcept -> const node_tree &; - auto generate_nodes(const std::string &, - const std::vector &) noexcept(false) -> void; -}; - -} // namespace cait - -#endif // PARSER_HH diff --git a/src/token.cc b/src/token.cc deleted file mode 100644 index 3ec3efd..0000000 --- a/src/token.cc +++ /dev/null @@ -1,102 +0,0 @@ -// This file is part of Cait . -// Copyright (C) 2022-2022 Fuwn -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Copyright (C) 2022-2022 Fuwn -// SPDX-License-Identifier: GPL-3.0-only - -#include "token.hh" - -namespace cait { - -token::token(std::size_t _line, std::size_t _begin, std::size_t _end, - const std::string &_word) - : line(_line), begin(_begin), end(_end), word(_word) { - switch (utility::hash(_word.c_str())) { - case utility::hash("rule"): { - this->type = token_type::rule_declaration; - } break; - case utility::hash("command"): { - this->type = token_type::rule_command; - } break; - case utility::hash("depfile"): { - this->type = token_type::rule_depfile; - } break; - case utility::hash("deps"): { - this->type = token_type::rule_deps; - } break; - case utility::hash("msvc_deps_prefix"): { - this->type = token_type::rule_msvc_deps_prefix; - } break; - case utility::hash("description"): { - this->type = token_type::rule_description; - } break; - case utility::hash("dyndep"): { - this->type = token_type::rule_dyndep; - } break; - case utility::hash("generator"): { - this->type = token_type::rule_generator; - } break; - case utility::hash("in"): { - this->type = token_type::rule_in; - } break; - case utility::hash("in_newline"): { - this->type = token_type::rule_in_newline; - } break; - case utility::hash("out"): { - this->type = token_type::rule_out; - } break; - case utility::hash("restat"): { - this->type = token_type::rule_restat; - } break; - case utility::hash("rspfile"): { - this->type = token_type::rule_rspfile; - } break; - case utility::hash("rspfile_content"): { - this->type = token_type::rule_rspfile_content; - } break; - case utility::hash("depth"): { - this->type = token_type::pool_depth; - } break; - case utility::hash("build"): { - this->type = token_type::build_declaration; - } break; - case utility::hash("default"): { - this->type = token_type::default_declaration; - } break; - case utility::hash("pool"): { - this->type = token_type::pool_declaration; - } break; - case utility::hash("subninja"): { - this->type = token_type::subninja_declaration; - } break; - case utility::hash("include"): { - this->type = token_type::include_declaration; - } break; - case utility::hash("="): { - this->type = token_type::assignment_operator; - } break; - default: { - this->type = token_type::string_literal; - } break; - } -} - -auto token::with_type(token_type _type) -> token { - this->type = _type; - - return *this; -} - -} // namespace cait diff --git a/src/token.hh b/src/token.hh deleted file mode 100644 index 44f8f69..0000000 --- a/src/token.hh +++ /dev/null @@ -1,153 +0,0 @@ -// This file is part of Cait . -// Copyright (C) 2022-2022 Fuwn -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Copyright (C) 2022-2022 Fuwn -// SPDX-License-Identifier: GPL-3.0-only - -#ifndef TOKEN_HH -#define TOKEN_HH - -#include -#include - -#include "utility.hh" - -namespace cait { - -struct token; - -using token_tree = std::vector>; - -// https://stackoverflow.com/a/53284026/14452787 -class token_type { -public: - enum token_type_value { - rule_declaration, - rule_command, - rule_depfile, - rule_deps, - rule_msvc_deps_prefix, - rule_description, - rule_dyndep, - rule_generator, - rule_in, - rule_in_newline, - rule_out, - rule_restat, - rule_rspfile, - rule_rspfile_content, - build_declaration, - build_input, - build_rule, - build_output, - default_declaration, - default_target, - string_literal, - variable_declaration, - variable_value, - assignment_operator, - identifier, - pool_declaration, - pool_depth, - subninja_declaration, - include_declaration - }; - -private: - token_type_value value; - [[maybe_unused]] int padding; - -public: - token_type() = default; - constexpr token_type(token_type_value _value) : value(_value) {} - [[nodiscard]] constexpr std::string string() const { - switch (this->value) { - case rule_declaration: - return "rule_declaration"; - case rule_command: - return "rule_command"; - case rule_depfile: - return "rule_depfile"; - case rule_deps: - return "rule_deps"; - case rule_msvc_deps_prefix: - return "rule_msvc_deps_prefix"; - case rule_description: - return "rule_description"; - case rule_dyndep: - return "rule_dyndep"; - case rule_generator: - return "rule_generator"; - case rule_in: - return "rule_in"; - case rule_in_newline: - return "rule_in_newline"; - case rule_out: - return "rule_out"; - case rule_restat: - return "rule_restat"; - case rule_rspfile: - return "rule_rspfile"; - case rule_rspfile_content: - return "rule_rspfile_content"; - case build_declaration: - return "build_declaration"; - case build_input: - return "build_input"; - case build_rule: - return "build_rule"; - case default_declaration: - return "default_declaration"; - case default_target: - return "default_target"; - case string_literal: - return "string_literal"; - case variable_declaration: - return "variable_declaration"; - case variable_value: - return "variable_value"; - case assignment_operator: - return "assignment_operator"; - case identifier: - return "identifier"; - case build_output: - return "build_output"; - case pool_declaration: - return "pool_declaration"; - case pool_depth: - return "pool_depth"; - case subninja_declaration: - return "subninja_declaration"; - case include_declaration: - return "include_declaration"; - } - } - constexpr operator token_type_value() const { return this->value; } -}; - -struct token { - std::size_t line; - [[maybe_unused]] std::size_t begin; - [[maybe_unused]] std::size_t end; - std::string word; - token_type type{}; - - token(std::size_t, std::size_t, std::size_t, const std::string &); - auto with_type(token_type) -> token; -}; - -} // namespace cait - -#endif // TOKEN_HH diff --git a/src/utility.hh b/src/utility.hh deleted file mode 100644 index 26a324e..0000000 --- a/src/utility.hh +++ /dev/null @@ -1,38 +0,0 @@ -// This file is part of Cait . -// Copyright (C) 2022-2022 Fuwn -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 3. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -// Copyright (C) 2022-2022 Fuwn -// SPDX-License-Identifier: GPL-3.0-only - -#ifndef UTILITY_HH -#define UTILITY_HH - -#include - -namespace cait::utility { - -// "Hash" a string so that it can be used within a `switch` statement. -// -// https://stackoverflow.com/a/46711735/14452787 -constexpr auto hash(const char *data, std::size_t const size = 0) noexcept - -> std::uint32_t { - return !data[size] ? 5381 - : (hash(data, size + 1) * 33) ^ - static_cast(data[size]); -} - -} // namespace cait::utility - -#endif // UTILITY_HH -- cgit v1.2.3