aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/cc/parser.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/cc/parser.cxx')
-rw-r--r--libbuild2/cc/parser.cxx273
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;
}
}