diff options
| author | Fuwn <[email protected]> | 2022-06-24 04:34:06 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2022-06-24 04:34:06 -0700 |
| commit | 06a2da38c0bffa43c9e8a26b6d98dc95595dc081 (patch) | |
| tree | 09d1c29f9202b1cb8670fa1f0d6bcf256f8f738c /src/parser.cc | |
| parent | docs(license): add gpl-3.0 (diff) | |
| download | cait-06a2da38c0bffa43c9e8a26b6d98dc95595dc081.tar.xz cait-06a2da38c0bffa43c9e8a26b6d98dc95595dc081.zip | |
ci: push source to remote
Diffstat (limited to 'src/parser.cc')
| -rw-r--r-- | src/parser.cc | 364 |
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 |