From 6a2d1e3062964fc16cfbc43bc69284f854c35dca Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 18 Nov 2020 08:00:16 +0200 Subject: Handle C++20 module partitions in scanner --- libbuild2/cc/parser.cxx | 139 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 94 insertions(+), 45 deletions(-) (limited to 'libbuild2/cc/parser.cxx') diff --git a/libbuild2/cc/parser.cxx b/libbuild2/cc/parser.cxx index 8f97fcc..976fd89 100644 --- a/libbuild2/cc/parser.cxx +++ b/libbuild2/cc/parser.cxx @@ -66,10 +66,11 @@ namespace build2 { // Constructs we need to recognize: // - // module ; - // [export] module [] ; - // [export] import [] ; - // [export] import [] ; + // module ; + // [export] module [] [] ; + // [export] import [] ; + // [export] import [] ; + // [export] import [] ; // // The leading module/export/import keyword should be the first // token of a logical line and only if certain characters appear @@ -117,6 +118,7 @@ namespace build2 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); @@ -153,13 +155,52 @@ namespace build2 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. + // + string n (parse_module_name (t, true /* partition */)); + + // 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 = ex ? unit_type::module_iface : unit_type::module_impl; + u_->module_info.name = move (n); + } + + void parser:: parse_import (token& t, bool ex) { // enter: token after import keyword @@ -176,9 +217,25 @@ namespace build2 ut = unit_type::module_header; break; } + case type::colon: + { + if (u_->type != unit_type::module_iface && + u_->type != unit_type::module_impl) + fail (t) << "partition importation out of module purview"; + + un = parse_module_part (t); + ut = unit_type::module_iface; // @@ _part? + break; + } case type::identifier: { - un = parse_module_name (t); + // 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; + // + un = parse_module_name (t, false /* partition */); ut = unit_type::module_iface; break; } @@ -223,59 +280,53 @@ namespace build2 i->exported = i->exported || ex; } - void parser:: - parse_module (token& t, bool ex, location_value l) + string parser:: + parse_module_name (token& t, bool part) { - // enter: token after module keyword (l is the module keyword location) - // leave: semi + // enter: first token of module name + // leave: token after module name - // Handle the leading 'module;' marker (p0713). - // - // Note that we don't bother diagnosing invalid/duplicate markers - // leaving that to the compiler. + string n; + + // [ . ]* [] // - if (!ex && t.type == type::semi && !t.first) + 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 && !t.first; - l_->next (t)) ; + if (l_->next (t) != type::dot || t.first) + break; - if (t.type != type::semi) - fail (t) << "';' expected instead of " << t; - else if (t.first) - fail (t) << "';' must be on the same line"; + n += '.'; + } - if (!u_->module_info.name.empty ()) - fail (l) << "multiple module declarations"; + if (part && t.type == type::colon && !t.first) + parse_module_part (t, n); - u_->type = ex ? unit_type::module_iface : unit_type::module_impl; - u_->module_info.name = move (n); + return n; } - 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 += ':'; - // [ . ]* + // : [ . ]* // - for (;; l_->next (t)) + for (;;) { - if (t.type != type::identifier || t.first) - 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; @@ -284,8 +335,6 @@ namespace build2 n += '.'; } - - return n; } string parser:: -- cgit v1.1