// file : libbuild2/make-parser.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #include #include namespace build2 { auto make_parser:: next (const string& l, size_t& p, const location& ll, bool strict) -> pair { assert (state != end); pair r ( next (l, p, !strict ? state == prereqs : optional ())); type t (state == prereqs ? type::prereq : type::target); // Deal with the end. // if (r.second) { if (state == begin && r.first.empty ()) ; // Skip leading blank line. else { if (state != prereqs) fail (ll) << "end of make dependency declaration before ':'"; state = end; } } // Deal with the first target. // else if (state == begin && !r.first.empty ()) state = targets; // Deal with `:`. // if (p != l.size () && l[p] == ':') { switch (state) { case begin: fail (ll) << "':' before make target"; break; case targets: state = prereqs; break; case prereqs: fail (ll) << "':' after make prerequisite"; break; case end: break; } if (++p == l.size ()) state = end; // Not a mere optimization: the caller will get next line. } return pair (t, move (r.first)); } pair make_parser:: next (const string& l, size_t& p, optional prereq) { size_t n (l.size ()); // Skip leading spaces. // for (; p != n && l[p] == ' '; p++) ; // Lines containing multiple targets/prerequisites are customarily 80 // characters max. // string r; r.reserve (n - p); // Scan the next target/prerequisite while watching out for escape // sequences. // // @@ Can't we do better for the (common) case where nothing is escaped? // for (char c, q (prereq && *prereq ? '\0' : ':'); p != n && (c = l[p]) != ' ' && c != q; ) { // If we have another character, then handle the escapes. // if (++p != n) { if (c == '\\') { // This may or may not be an escape sequence depending on whether // what follows is "escapable". // switch (c = l[p]) { case '\\': case ' ': case ':': ++p; break; default: c = '\\'; // Restore. } } else if (c == '$') { // Got to be another (escaped) '$'. // if (l[p] == '$') ++p; } } // Note that the newline escape is not necessarily separated with space. // else if (c == '\\') { --p; break; } r += c; } // Skip trailing spaces. // for (; p != n && l[p] == ' '; p++) ; // Skip final '\' and determine if this is the end. // bool e (false); if (p == n - 1) { if (l[p] == '\\') p++; } else if (p == n) e = true; return pair (move (r), e); } }