From 5d6ca66e3cdd800e9c6ee3274b74e478588f6983 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 10 Nov 2015 17:20:41 +0200 Subject: Rework paragraph block handling in formatter --- cli/context.cxx | 169 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 122 insertions(+), 47 deletions(-) (limited to 'cli/context.cxx') diff --git a/cli/context.cxx b/cli/context.cxx index 165ecc2..f950f76 100644 --- a/cli/context.cxx +++ b/cli/context.cxx @@ -634,27 +634,19 @@ format_line (output_type ot, string& r, const char* s, size_t n) } } -// Return true if this line ends the paragraph, that is, ends with an -// unescaped '|'. -// -static bool -end_para (const char* l, size_t n) +struct block { - if (l[n - 1] != '|') - return false; - - if (n == 1 || l[n - 2] != '\\') - return true; + enum value {none, h/*, ul, li*/} v_; + block (value v = h): v_ (v) {} + operator value () const {return v_;} +}; - // To determine whether this is an escape sequence we have to find - // first non-backslash character and then figure out who escapes who. - // - size_t i (n - 2); - for (; i != 0 && l[i - 1] == '\\'; --i) ; +const char* block_str[] = {"", "\\h", "\\ul", "\\li"}; - // If we have an even number of backslashes, the it is unescaped. - // - return (n - i - 1) % 2 == 0; +inline ostream& +operator<< (ostream& os, block b) +{ + return os << block_str[b]; } string context:: @@ -662,6 +654,8 @@ format (output_type ot, string const& s, bool para) { string r; + stack blocks; + // Iterate over lines (paragraphs) or pre-formatted sections. // for (size_t b (0), e; ; b = e + 1) @@ -696,54 +690,128 @@ format (output_type ot, string const& s, bool para) if (ot == ot_html) r += ""; } - // - // Some paragraph blocks are only valid if we are required to start - // a new paragraph (para is true). - // - else if (n >= 3 && strncmp (l, "\\h|", 3) == 0) + else { - if (!para) + const char* ol (l); // Original, full line, for diagnostics. + size_t on (n); + + block b (block::none); + + // Some paragraph blocks are only valid if we are required to start + // a new paragraph (para is true). + // + if (n >= 3 && strncmp (l, "\\h|", 3) == 0) { - cerr << "error: invalid context '" << s << "' for paragraph '" - << string (l, 0, n) << "'" << endl; - throw generation_failed (); + if (!blocks.empty ()) + { + cerr << "error: '\\h' inside '" << blocks.top () << "' " + << "in documentation string '" << s << "'" << endl; + throw generation_failed (); + } + + if (!para) + { + cerr << "error: paragraph '" << string (ol, 0, on) << "' " + << "not allowed in '" << s << "'" << endl; + throw generation_failed (); + } + + if (ot == ot_html) + r += "

"; + + l += 3; + n -= 3; + blocks.push (b = block::h); } - if (!end_para (l, n)) + // Figure out how many blocks we need to pop. Things get complicated + // since '|' could be escaped. + // + size_t pc (0); // Pop count. + for (; n - pc > 0 && l[n - pc - 1] == '|'; ++pc) ; + if (pc != 0) { - cerr << "error: '|' expected at the end of paragraph '" - << string (l, 0, n) << "'" << endl; - throw generation_failed (); + // To determine whether the first '|' is part of an escape sequence + // we have to find the first non-backslash character and then figure + // out who escapes whom. + // + size_t ec (0); // Escape count. + for (; n - pc - ec > 0 && l[n - pc - ec - 1] == '\\'; ++ec) ; + + // If we have an odd number of backslashes, then the last '|' is + // escaped. + // + if (ec % 2 != 0) + --pc; } - if (n == 4) + if (pc > blocks.size ()) { - cerr << "error: empty paragraph '" << string (l, 0, n) << "'" << endl; + cerr << "error: extraneous '|' at the end of paragraph '" + << string (ol, 0, on) << "'" << endl; throw generation_failed (); } - if (ot == ot_html) - r += "

"; + n -= pc; // Number of special '|' at the end. - format_line (ot, r, l + 3, n - 4); + // Some blocks, like \h, are single-paragraph and cannot be empty. + // + if (b == block::h && n == 0) + { + cerr << "error: empty paragraph '" << string (ol, 0, on) << "' " + << "in documentation string '" << s << "'" << endl; + throw generation_failed (); + } - if (ot == ot_html) - r += "

"; - } - else // Normal text. - { - if (para || !first) // Start a paragraph? + if (b == block::none) // Normal text. { - if (ot == ot_html) - r += "

"; + if (para || !first) // Start a paragraph? + { + if (ot == ot_html) + r += "

"; + } } format_line (ot, r, l, n); - if (para || !first) // End a paragraph? + if (b == block::none) // Normal text. { - if (ot == ot_html) - r += "

"; + if (para || !first) // End a paragraph? + { + if (ot == ot_html) + r += "

"; + } + } + + // Pop paragraph blocks. + // + for (; pc != 0; --pc) + { + switch (blocks.top ()) + { + case block::h: + { + if (ot == ot_html) + r += ""; + + break; + } + case block::none: + break; + } + + blocks.pop (); + } + + // Some blocks, like \h, must be single-paragraph. + // + b = blocks.empty () ? block (block::none) : blocks.top (); + + if (b == block::h) + { + cerr << "error: '|' expected at the end of paragraph '" + << string (ol, 0, on) << "'" << endl; + throw generation_failed (); } } @@ -755,6 +823,13 @@ format (output_type ot, string const& s, bool para) r += "\n\n"; } + if (!blocks.empty ()) + { + cerr << "error: unterminated paragraph '\\" << blocks.top () << "' " + << "in documentation string '" << s << "'" << endl; + throw generation_failed (); + } + return r; } -- cgit v1.1