From 6a68b1fd2161357a5905b875e9d59609a2b829b1 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 8 Dec 2021 22:46:50 +0300 Subject: Add support for package dependency and requirement alternatives representation new syntax --- libbpkg/buildfile-scanner.txx | 272 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 libbpkg/buildfile-scanner.txx (limited to 'libbpkg/buildfile-scanner.txx') diff --git a/libbpkg/buildfile-scanner.txx b/libbpkg/buildfile-scanner.txx new file mode 100644 index 0000000..acd9037 --- /dev/null +++ b/libbpkg/buildfile-scanner.txx @@ -0,0 +1,272 @@ +// file : libbpkg/buildfile-scanner.txx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#include + +#include + +namespace bpkg +{ + template + typename buildfile_scanner::xchar buildfile_scanner:: + peek () + { + xchar c (scan_.peek (ebuf_)); + + if (scanner::invalid (c)) + throw buildfile_scanning (name_, scan_.line, scan_.column, ebuf_); + + return c; + } + + template + char buildfile_scanner:: + scan_line (std::string& l, char stop) + { + using namespace std; + + auto fail = [this] (const string& d) + { + throw buildfile_scanning (name_, scan_.line, scan_.column, d); + }; + + xchar c (peek ()); + + auto next = [&l, &c, this] () + { + l += c; + scan_.get (c); + }; + + butl::optional r; + bool double_quoted (false); + + for (; + !scanner::eos (c) && (double_quoted || (c != '\n' && c != stop)); + c = peek ()) + { + switch (c) + { + case '\"': + { + // Start or finish scanning the double-quoted sequence. + // + double_quoted = !double_quoted; + + r = '\0'; + break; + } + case '\\': + { + next (); + + c = peek (); + + if (scanner::eos (c)) + fail (double_quoted + ? "unterminated double-quoted sequence" + : "unterminated escape sequence"); + + r = '\0'; + break; + } + case '(': + { + next (); + + scan_line (l, ')'); + + c = peek (); + + if (c != ')') + fail ("unterminated evaluation context"); + + next (); + + r = '\0'; + continue; + } + case '\'': + { + if (!double_quoted) + { + next (); + + for (;;) + { + c = peek (); + + if (scanner::eos (c)) + fail ("unterminated single-quoted sequence"); + + next (); + + if (c == '\'') + break; + } + + r = '\0'; + continue; + } + + break; + } + case '#': + { + if (!double_quoted) + { + next (); + + // See if this is a multi-line comment in the form: + // + /* + #\ + ... + #\ + */ + auto ml = [&c, &next, this] () -> bool + { + if ((c = peek ()) == '\\') + { + next (); + + if ((c = peek ()) == '\n' || scanner::eos (c)) + return true; + } + + return false; + }; + + if (ml ()) + { + // Scan until we see the closing one. + // + for (;;) + { + if (c == '#' && ml ()) + break; + + if (scanner::eos (c = peek ())) + fail ("unterminated multi-line comment"); + + next (); + } + } + else + { + // Read until newline or eos. + // + for (; !scanner::eos (c) && c != '\n'; c = peek ()) + next (); + } + + continue; + } + + break; + } + case '{': + case '}': + { + if (!double_quoted) + r = !r ? static_cast (c) : '\0'; + + break; + } + default: + { + if (!double_quoted && c != ' ' && c != '\t') + r = '\0'; + + break; + } + } + + next (); + } + + if (double_quoted) + fail ("unterminated double-quoted sequence"); + + return r ? *r : '\0'; + } + + template + std::string buildfile_scanner:: + scan_line (char stop) + { + std::string r; + scan_line (r, stop); + return r; + } + + template + std::string buildfile_scanner:: + scan_eval () + { + std::string r; + scan_line (r, ')'); + + if (peek () != ')') + throw buildfile_scanning (name_, + scan_.line, + scan_.column, + "unterminated evaluation context"); + + return r; + } + + template + std::string buildfile_scanner:: + scan_block () + { + using namespace std; + + auto fail = [this] (const string& d) + { + throw buildfile_scanning (name_, scan_.line, scan_.column, d); + }; + + string r; + for (size_t level (0);; ) + { + if (scanner::eos (peek ())) + fail ("unterminated buildfile block"); + + size_t n (r.size ()); + char bc (scan_line (r)); + + xchar c (peek ()); + + // Append the newline unless this is eos. + // + if (c == '\n') + { + r += c; + scan_.get (c); + } + else + assert (scanner::eos (c)); + + if (bc == '{') + { + ++level; + } + else if (bc == '}') + { + // If this is the fragment terminating line, then strip it from the + // fragment and bail out. + // + if (level == 0) + { + r.resize (n); + break; + } + else + --level; + } + } + + return r; + } +} -- cgit v1.1