2323#include < ostream>
2424#include < iterator>
2525#include < cctype>
26+ #include < cstddef>
27+ #include < span>
2628
2729
2830namespace cpp2 {
@@ -107,29 +109,35 @@ auto is_preprocessor(
107109
108110
109111// ---------------------------------------------------------------------------
110- // starts_with_import : returns whether the line starts with "import"
112+ // starts_with_tokens : returns whether the line starts with the tokens
111113//
112114// line current line being processed
113115//
114- auto starts_with_import (std::string const & line)
116+ auto starts_with_tokens (std::string const & line, std::initializer_list<std::string_view> const tokens )
115117 -> bool
116118{
117119 auto i = 0 ;
118120
119- // find first non-whitespace character
120- if (!move_next (line, i, isspace)) {
121- return false ;
122- }
121+ for (auto token: tokens) {
122+ // find first non-whitespace character
123+ if (!move_next (line, i, isspace)) {
124+ return false ;
125+ }
123126
124- static constexpr auto import_keyword = std::string_view{" import" };
127+ // now must begin with the token
128+ if (!std::string_view (line).substr (i).starts_with (token)) {
129+ return false ;
130+ }
125131
126- // the first token must begin with 'import'
127- if (!std::string_view (line).substr (i).starts_with (import_keyword)) {
128- return false ;
132+ // and not be immediately followed by an _identifier-continue_
133+ if (is_identifier_continue (line[i + token.size ()])) {
134+ return false ;
135+ }
136+
137+ i += token.size ();
129138 }
130139
131- // and not be immediately followed by an _identifier-continue_
132- return !is_identifier_continue (line[i + import_keyword.size ()]);
140+ return true ;
133141}
134142
135143
@@ -301,6 +309,9 @@ auto starts_with_identifier_colon(std::string const& line)
301309 else if (s.starts_with (" private" )) {
302310 j += 7 ;
303311 }
312+ else if (s.starts_with (" export" )) {
313+ j += 6 ;
314+ }
304315 while (
305316 j < std::ssize (s)
306317 && isspace (s[j])
@@ -623,7 +634,7 @@ auto process_cpp_line(
623634 }
624635 }
625636 }
626-
637+
627638 break ;case ' \" ' :
628639 // If this isn't an escaped quote, toggle string literal state
629640 if (
@@ -772,8 +783,10 @@ class source
772783{
773784 std::vector<error_entry>& errors;
774785 std::vector<source_line> lines;
775- bool cpp1_found = false ;
776- bool cpp2_found = false ;
786+ std::ptrdiff_t module_lines = 0 ;
787+ bool module_directive_found = false ;
788+ bool cpp1_found = false ;
789+ bool cpp2_found = false ;
777790
778791 static const int max_line_len = 90'000 ;
779792 // do not reduce this - I encountered an 80,556-char
@@ -796,9 +809,27 @@ class source
796809 }
797810
798811
812+ // -----------------------------------------------------------------------
813+ // is_module: Returns true if this file is a module unit
814+ // (note: module, export, and import lines don't count toward Cpp1 or Cpp2)
815+ //
816+ auto is_module () const -> bool {
817+ return module_lines != 0 ;
818+ }
819+
820+
821+ // -----------------------------------------------------------------------
822+ // has_module_directive: Returns true if this file has a module directive
823+ // (note: module, export, and import lines don't count toward Cpp1 or Cpp2)
824+ //
825+ auto has_module_directive () const -> bool {
826+ return module_directive_found;
827+ }
828+
829+
799830 // -----------------------------------------------------------------------
800831 // has_cpp1: Returns true if this file has some Cpp1/preprocessor lines
801- // (note: import lines don't count toward Cpp1 or Cpp2)
832+ // (note: module, export, and import lines don't count toward Cpp1 or Cpp2)
802833 //
803834 auto has_cpp1 () const -> bool {
804835 return cpp1_found;
@@ -807,7 +838,7 @@ class source
807838
808839 // -----------------------------------------------------------------------
809840 // has_cpp2: Returns true if this file has some Cpp2 lines
810- // (note: import lines don't count toward Cpp1 or Cpp2)
841+ // (note: module, export, and import lines don't count toward Cpp1 or Cpp2)
811842 //
812843 auto has_cpp2 () const -> bool {
813844 return cpp2_found;
@@ -923,11 +954,23 @@ class source
923954 }
924955 }
925956
926- // Else still in Cpp1 code, but could be a comment, empty, or import
957+ // Else still in Cpp1 code, but could be a comment, empty, module, export, or import
927958 //
928959 else
929960 {
930- if (starts_with_import (lines.back ().text )) {
961+ if (!is_module () && starts_with_tokens (lines.back ().text , {" module" , " ;" })) {
962+ lines.back ().cat = source_line::category::module_directive;
963+ module_directive_found = true ;
964+ }
965+ else if (!is_module () && starts_with_tokens (lines.back ().text , {" module" })) {
966+ lines.back ().cat = source_line::category::module_declaration;
967+ module_lines = lines.size ();
968+ }
969+ else if (!is_module () && starts_with_tokens (lines.back ().text , {" export" , " module" })) {
970+ lines.back ().cat = source_line::category::module_declaration;
971+ module_lines = lines.size ();
972+ }
973+ else if (starts_with_tokens (lines.back ().text , {" import" })) {
931974 lines.back ().cat = source_line::category::import ;
932975 }
933976 else {
@@ -1002,6 +1045,16 @@ class source
10021045 return lines;
10031046 }
10041047
1048+ auto get_non_module_lines () const -> std::span<const source_line>
1049+ {
1050+ return std::span<const source_line>{lines.begin () + module_lines, lines.end ()};
1051+ }
1052+
1053+ auto get_module_lines () const -> std::span<const source_line>
1054+ {
1055+ return std::span<const source_line>{lines.begin (), lines.begin () + module_lines};
1056+ }
1057+
10051058 // -----------------------------------------------------------------------
10061059 // debug_print
10071060 //
0 commit comments