diff options
Diffstat (limited to 'libbuild2/cc/parser.cxx')
-rw-r--r-- | libbuild2/cc/parser.cxx | 273 |
1 files changed, 193 insertions, 80 deletions
diff --git a/libbuild2/cc/parser.cxx b/libbuild2/cc/parser.cxx index 55be8b7..f62847e 100644 --- a/libbuild2/cc/parser.cxx +++ b/libbuild2/cc/parser.cxx @@ -14,13 +14,13 @@ namespace build2 { using type = token_type; - unit parser:: - parse (ifdstream& is, const path_name& in) + void parser:: + parse (ifdstream& is, const path_name& in, unit& u, const compiler_id& cid) { - lexer l (is, in); - l_ = &l; + cid_ = &cid; - unit u; + lexer l (is, in, true /* preprocessed */); + l_ = &l; u_ = &u; // If the source has errors then we want the compiler to issues the @@ -43,8 +43,8 @@ namespace build2 token t; for (bool n (true); (n ? l_->next (t) : t.type) != type::eos; ) { - // Break to stop, continue to continue, set n to false if the - // next token already extracted. + // Break to stop, continue to continue, and set n to false if the + // next token is already extracted. // n = true; @@ -66,42 +66,82 @@ namespace build2 { // Constructs we need to recognize: // - // module ; - // [export] module <module-name> [<attributes>] ; - // [export] import <module-name> [<attributes>] ; - // [export] import <header-name> [<attributes>] ; + // module ; + // [export] module <module-name> [<module-part>] [<attributes>] ; + // [export] import <module-name> [<attributes>] ; + // [export] import <module-part> [<attributes>] ; + // [export] import <header-name> [<attributes>] ; + // + // The leading module/export/import keyword should be the first + // token of a logical line and only if certain characters appear + // after module/import and all the tokens are on the same line, + // then the line is recognized as a pseudo-directive; see p1857 + // for details. // // Additionally, when include is translated to an import, it's - // normally replaced with the special __import keyword since it - // may appear in C context. + // normally replaced with special import (special since it may + // appear in C context); it could be a special keyword (GCC used + // to call it __import) or it can have a special attribute (GCC + // currently marks it with [[__translated]]). // - const string& id (t.value); - - if (bb == 0) + // Similarly, MSVC drops the `module;` marker and replaces all + // other `module` keywords with `__preprocessed_module`. + // + // Clang doesn't appear to rewrite anything, at least as of + // version 18. + // + if (bb == 0 && t.first) { - if (id == "import" || id == "__import") + const string& id (t.value); // Note: tracks t. + + // Handle the export prefix which can appear for both module + // and import. + // + bool ex (false); + if (id == "export") { - parse_import (t, false); + if (l_->next (t) != type::identifier || t.first) + { + n = false; // Could be module/import on next line. + continue; + } + + ex = true; + // Fall through. } - else if (id == "module") + + if (id == "module" || + (cid_->type == compiler_type::msvc && + id == "__preprocessed_module")) { - parse_module (t, false); + location_value l (get_location (t)); + l_->next (t); + + if ((t.type == type::semi || + t.type == type::identifier) && !t.first) + parse_module (t, ex, move (l)); + else + n = false; } - else if (id == "export") + else if (id == "import" /* || + (cid_->type == compiler_type::gcc && + id == "__import")*/) { - if (l_->next (t) == type::identifier) - { - if (id == "module") parse_module (t, true); - else if (id == "import") parse_import (t, true); - else n = false; // Something else (e.g., export namespace). - } + l_->next (t); + + if ((t.type == type::less || + t.type == type::colon || + t.type == type::string || + t.type == type::identifier) && !t.first) + parse_import (t, ex); else n = false; } } continue; } - default: continue; + default: + continue; } break; @@ -120,56 +160,136 @@ namespace build2 // if anything in between fails (probably by having it sitting in a // diag_frame). So let's keep it simple for now. // + // @@ We now do that for missing include, so could do here as well. + // if (bb != 0) warn (t) << (bb > 0 ? "missing '}'" : "extraneous '}'"); if (module_marker_ && u.module_info.name.empty ()) fail (*module_marker_) << "module declaration expected after " - << "leading module marker"; + << "global module fragment"; checksum = l.checksum (); - return u; + } + + void parser:: + parse_module (token& t, bool ex, location_value l) + { + // enter: token after module keyword (l is the module keyword location) + // leave: semi + + // Handle the leading 'module;' marker (p0713). + // + // Note that we don't bother diagnosing invalid/duplicate markers + // leaving that to the compiler. + // + if (!ex && t.type == type::semi && !t.first) + { + module_marker_ = move (l); + return; + } + + // Otherwise it should be the start of the module name. + // + pair<string, bool> np (parse_module_name (t, true /* partition */)); + + // Skip attributes (should be {}-balanced). + // + for (; + t.type != type::eos && t.type != type::semi && !t.first; + l_->next (t)) ; + + if (t.type != type::semi) + fail (t) << "';' expected instead of " << t; + else if (t.first) + fail (t) << "';' must be on the same line"; + + if (!u_->module_info.name.empty ()) + fail (l) << "multiple module declarations"; + + u_->type = np.second + ? (ex ? unit_type::module_intf_part : unit_type::module_impl_part) + : (ex ? unit_type::module_intf : unit_type::module_impl); + u_->module_info.name = move (np.first); } void parser:: parse_import (token& t, bool ex) { - // enter: import keyword + // enter: token after import keyword // leave: semi + // Note that in import a partition can only be specified without a + // module name. In other words, the following is invalid: + // + // module m; + // import m:p; + // string un; - unit_type ut; - switch (l_->next (t)) // Start of module/header name. + import_type ut; + switch (t.type) // Start of module/header name. { case type::less: case type::string: { un = parse_header_name (t); - ut = unit_type::module_header; + ut = import_type::module_header; + break; + } + case type::colon: + { + // Add the module name to the partition so that code that doesn't + // need to distinguish between different kinds of imports doesn't + // have to. + // + // Note that if this itself is a partition, then we need to strip + // the partition part from the module name. + // + switch (u_->type) + { + case unit_type::module_intf: + case unit_type::module_impl: + un = u_->module_info.name; + break; + case unit_type::module_intf_part: + case unit_type::module_impl_part: + un.assign (u_->module_info.name, 0, u_->module_info.name.find (':')); + break; + default: + fail (t) << "partition importation out of module purview"; + } + + parse_module_part (t, un); + ut = import_type::module_part; break; } case type::identifier: { - un = parse_module_name (t); - ut = unit_type::module_iface; + un = parse_module_name (t, false /* partition */).first; + ut = import_type::module_intf; break; } default: - fail (t) << "module or header name expected instead of " << t << endf; + assert (false); + return; } - // Should be {}-balanced. + // Skip attributes (should be {}-balanced). // - for (; t.type != type::eos && t.type != type::semi; l_->next (t)) ; + for (; + t.type != type::eos && t.type != type::semi && !t.first; + l_->next (t)) ; if (t.type != type::semi) fail (t) << "';' expected instead of " << t; + else if (t.first) + fail (t) << "';' must be on the same line"; // For now we skip header units (see a comment on module type/info // string serialization in compile rule for details). Note that // currently parse_header_name() always returns empty name. // - if (ut == unit_type::module_header) + if (ut == import_type::module_header) return; // Ignore duplicates. We don't expect a large numbers of (direct) @@ -190,69 +310,62 @@ namespace build2 i->exported = i->exported || ex; } - void parser:: - parse_module (token& t, bool ex) + pair<string, bool /* partition */> parser:: + parse_module_name (token& t, bool part) { - // enter: module keyword - // leave: semi - - location_value l (get_location (t)); + // enter: first token of module name + // leave: token after module name - l_->next (t); + string n; - // Handle the leading 'module;' marker (p0713). + // <identifier>[ . <identifier>]* [<module-part>] // - // Note that we don't bother diagnosing invalid/duplicate markers - // leaving that to the compiler. - // - if (!ex && t.type == type::semi) + for (;; l_->next (t)) { - module_marker_ = move (l); - return; - } + if (t.type != type::identifier) + fail (t) << "module name expected instead of " << t; + else if (t.first) + fail (t) << "module name must be on the same line"; - // Otherwise it should be the start of the module name. - // - string n (parse_module_name (t)); + n += t.value; - // Should be {}-balanced. - // - for (; t.type != type::eos && t.type != type::semi; l_->next (t)) ; + if (l_->next (t) != type::dot || t.first) + break; - if (t.type != type::semi) - fail (t) << "';' expected instead of " << t; + n += '.'; + } - if (!u_->module_info.name.empty ()) - fail (l) << "multiple module declarations"; + bool p (part && t.type == type::colon && !t.first); + if (p) + parse_module_part (t, n); - u_->type = ex ? unit_type::module_iface : unit_type::module_impl; - u_->module_info.name = move (n); + return make_pair (move (n), p); } - string parser:: - parse_module_name (token& t) + void parser:: + parse_module_part (token& t, string& n) { - // enter: first token of module name - // leave: token after module name + // enter: colon + // leave: token after module partition - string n; + n += ':'; - // <identifier>[ . <identifier>]* + // : <identifier>[ . <identifier>]* // - for (;; l_->next (t)) + for (;;) { - if (t.type != type::identifier) - fail (t) << "module name expected instead of " << t; + if (l_->next (t) != type::identifier) + fail (t) << "partition name expected instead of " << t; + else if (t.first) + fail (t) << "partition name must be on the same line"; n += t.value; - if (l_->next (t) != type::dot) + if (l_->next (t) != type::dot || t.first) break; n += '.'; } - - return n; } string parser:: @@ -271,7 +384,7 @@ namespace build2 { while (l_->next (t) != type::greater) { - if (t.type == type::eos) + if (t.type == type::eos || t.first) fail (t) << "closing '>' expected after header name" << endf; } } |