diff options
Diffstat (limited to 'libbuild2/test/script/parser.hxx')
-rw-r--r-- | libbuild2/test/script/parser.hxx | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/libbuild2/test/script/parser.hxx b/libbuild2/test/script/parser.hxx new file mode 100644 index 0000000..1beee49 --- /dev/null +++ b/libbuild2/test/script/parser.hxx @@ -0,0 +1,250 @@ +// file : libbuild2/test/script/parser.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBUILD2_TEST_SCRIPT_PARSER_HXX +#define LIBBUILD2_TEST_SCRIPT_PARSER_HXX + +#include <libbuild2/types.hxx> +#include <libbuild2/utility.hxx> + +#include <libbuild2/parser.hxx> +#include <libbuild2/diagnostics.hxx> + +#include <libbuild2/test/script/token.hxx> +#include <libbuild2/test/script/script.hxx> + +namespace build2 +{ + namespace test + { + namespace script + { + class lexer; + class runner; + + class parser: protected build2::parser + { + // Pre-parse. Issue diagnostics and throw failed in case of an error. + // + public: + void + pre_parse (script&); + + void + pre_parse (istream&, script&); + + // Helpers. + // + // Parse attribute string and perform attribute-guided assignment. + // Issue diagnostics and throw failed in case of an error. + // + void + apply_value_attributes (const variable*, // Optional. + value& lhs, + value&& rhs, + const string& attributes, + token_type assign_kind, + const path& name); // For diagnostics. + + // Recursive descent parser. + // + // Usually (but not always) parse functions receive the token/type + // from which it should start consuming and in return the token/type + // should contain the first token that has not been consumed. + // + // Functions that are called parse_*() rather than pre_parse_*() are + // used for both stages. + // + protected: + bool + pre_parse_demote_group_scope (unique_ptr<scope>&); + + token + pre_parse_scope_body (); + + unique_ptr<group> + pre_parse_scope_block (token&, token_type&, const string&); + + bool + pre_parse_line (token&, token_type&, + optional<description>&, + lines* = nullptr, + bool one = false); + + bool + pre_parse_if_else (token&, token_type&, + optional<description>&, + lines&); + + bool + pre_parse_if_else_scope (token&, token_type&, + optional<description>&, + lines&); + + bool + pre_parse_if_else_command (token&, token_type&, + optional<description>&, + lines&); + + void + pre_parse_directive (token&, token_type&); + + void + pre_parse_include_line (names, location); + + description + pre_parse_leading_description (token&, token_type&); + + description + parse_trailing_description (token&, token_type&); + + value + parse_variable_line (token&, token_type&); + + command_expr + parse_command_line (token&, token_type&); + + // Ordered sequence of here-document redirects that we can expect to + // see after the command line. + // + struct here_redirect + { + size_t expr; // Index in command_expr. + size_t pipe; // Index in command_pipe. + int fd; // Redirect fd (0 - in, 1 - out, 2 - err). + }; + + struct here_doc + { + // Redirects that share here_doc. Most of the time we will have no + // more than 2 (2 - for the roundtrip test cases). + // + small_vector<here_redirect, 2> redirects; + + string end; + bool literal; // Literal (single-quote). + string modifiers; + + // Regex introducer ('\0' if not a regex, so can be used as bool). + // + char regex; + + // Regex global flags. Meaningful if regex != '\0'. + // + string regex_flags; + }; + using here_docs = vector<here_doc>; + + pair<command_expr, here_docs> + parse_command_expr (token&, token_type&); + + command_exit + parse_command_exit (token&, token_type&); + + void + parse_here_documents (token&, token_type&, + pair<command_expr, here_docs>&); + + struct parsed_doc + { + union + { + string str; // Here-document literal. + regex_lines regex; // Here-document regex. + }; + + bool re; // True if regex. + uint64_t end_line; // Here-document end marker location. + uint64_t end_column; + + parsed_doc (string, uint64_t line, uint64_t column); + parsed_doc (regex_lines&&, uint64_t line, uint64_t column); + parsed_doc (parsed_doc&&); // Note: move constuctible-only type. + ~parsed_doc (); + }; + + parsed_doc + parse_here_document (token&, token_type&, + const string&, + const string& mode, + char re_intro); // '\0' if not a regex. + + // Execute. Issue diagnostics and throw failed in case of an error. + // + public: + void + execute (script& s, runner& r); + + void + execute (scope&, script&, runner&); + + protected: + void + exec_scope_body (); + + // Return false if the execution of the scope should be terminated + // with the success status (e.g., as a result of encountering the exit + // builtin). For unsuccessful termination the failed exception should + // be thrown. + // + bool + exec_lines (lines::iterator, lines::iterator, size_t&, command_type); + + // Customization hooks. + // + protected: + virtual lookup + lookup_variable (name&&, string&&, const location&) override; + + // Number of quoted tokens since last reset. Note that this includes + // the peeked token, if any. + // + protected: + size_t + quoted () const; + + void + reset_quoted (token& current); + + size_t replay_quoted_; + + // Insert id into the id map checking for duplicates. + // + protected: + const string& + insert_id (string, location); + + // Set lexer pointers for both the current and the base classes. + // + protected: + void + set_lexer (lexer* l); + + protected: + using base_parser = build2::parser; + + script* script_; + + // Pre-parse state. + // + using id_map = std::unordered_map<string, location>; + using include_set = std::set<path>; + + group* group_; + id_map* id_map_; + include_set* include_set_; // Testscripts already included in this + // scope. Must be absolute and normalized. + lexer* lexer_; + string id_prefix_; // Auto-derived id prefix. + + // Execute state. + // + runner* runner_; + scope* scope_; + }; + } + } +} + +#endif // LIBBUILD2_TEST_SCRIPT_PARSER_HXX |