summaryrefslogtreecommitdiff
path: root/src/parser.cc
diff options
context:
space:
mode:
authorFuwn <[email protected]>2022-06-24 04:34:06 -0700
committerFuwn <[email protected]>2022-06-24 04:34:06 -0700
commit06a2da38c0bffa43c9e8a26b6d98dc95595dc081 (patch)
tree09d1c29f9202b1cb8670fa1f0d6bcf256f8f738c /src/parser.cc
parentdocs(license): add gpl-3.0 (diff)
downloadcait-06a2da38c0bffa43c9e8a26b6d98dc95595dc081.tar.xz
cait-06a2da38c0bffa43c9e8a26b6d98dc95595dc081.zip
ci: push source to remote
Diffstat (limited to 'src/parser.cc')
-rw-r--r--src/parser.cc364
1 files changed, 364 insertions, 0 deletions
diff --git a/src/parser.cc b/src/parser.cc
new file mode 100644
index 0000000..e884287
--- /dev/null
+++ b/src/parser.cc
@@ -0,0 +1,364 @@
+// This file is part of Cait <https://github.com/Fuwn/cait>.
+// Copyright (C) 2022-2022 Fuwn <[email protected]>
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+// Copyright (C) 2022-2022 Fuwn <[email protected]>
+// SPDX-License-Identifier: GPL-3.0-only
+
+#include <iostream>
+
+#include "parser.hh"
+#include "utility.hh"
+
+namespace cait {
+
+token_bundle::token_bundle(std::size_t &_j, token_t &bundle_token,
+ std::vector<token_t> &_parsed_token_line,
+ std::vector<token_t> &_token_line)
+ : j(_j), token(bundle_token), parsed_token_line(_parsed_token_line),
+ token_line(_token_line) {}
+
+template <typename T>
+range_t<T>::range_t(T begin, T end) : _begin(begin), _end(end) {}
+
+template <typename T> T range_t<T>::begin() { return this->_begin; }
+
+template <typename T> T range_t<T>::end() { return this->_end; }
+
+template <typename T> range_t<T> range(T b, T e) { return range_t<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<token> 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<std::string> &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<size_t>(j) < this->_tree.size(); ++j) {
+ if (this->_tree[static_cast<size_t>(j)].empty()) {
+ this->_tree.erase(this->_tree.begin() + j);
+ }
+ }
+}
+
+} // namespace cait