diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2015-11-11 15:52:04 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2015-11-11 15:52:04 +0200 |
commit | fe8cfbfb1aecb279e294fbaf3c8e3d26639942d1 (patch) | |
tree | b91c0df35bdaac3f09b63ebea832303a91a81079 /cli/context.cxx | |
parent | 5d6ca66e3cdd800e9c6ee3274b74e478588f6983 (diff) |
Implement list support
Diffstat (limited to 'cli/context.cxx')
-rw-r--r-- | cli/context.cxx | 270 |
1 files changed, 207 insertions, 63 deletions
diff --git a/cli/context.cxx b/cli/context.cxx index f950f76..463ce69 100644 --- a/cli/context.cxx +++ b/cli/context.cxx @@ -636,12 +636,12 @@ format_line (output_type ot, string& r, const char* s, size_t n) struct block { - enum value {none, h/*, ul, li*/} v_; - block (value v = h): v_ (v) {} + enum value {h, ul, ol, dl, li, text} v_; + block (value v = text): v_ (v) {} operator value () const {return v_;} }; -const char* block_str[] = {"", "\\h", "\\ul", "\\li"}; +const char* block_str[] = {"\\h", "\\ul", "\\ol", "\\dl", "\\li", "text"}; inline ostream& operator<< (ostream& os, block b) @@ -650,18 +650,22 @@ operator<< (ostream& os, block b) } string context:: -format (output_type ot, string const& s, bool para) +format (output_type ot, string const& s, bool first_para) { string r; - stack<block> blocks; + // Flag that indicates whether the next fragment of text should start + // in its own paragraph. + // + bool para (first_para); + // Iterate over lines (paragraphs) or pre-formatted sections. // for (size_t b (0), e; ; b = e + 1) { bool pre (s[b] == 0x02); - bool first (b == 0), last; + bool last; if (pre) { @@ -689,42 +693,53 @@ format (output_type ot, string const& s, bool para) if (ot == ot_html) r += "</pre>"; + + para = true; } else { 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). + // First determine what kind of paragraph block this is (i.e., + // handle the "prefix"). // + block b (block::text); + if (n >= 3 && strncmp (l, "\\h|", 3) == 0) { - if (!blocks.empty ()) - { - cerr << "error: '\\h' inside '" << blocks.top () << "' " - << "in documentation string '" << s << "'" << endl; - throw generation_failed (); - } - - if (!para) + b = block::h; + l += 3; + n -= 3; + } + else if (n >= 4 && + (strncmp (l, "\\ul|", 4) == 0 || + strncmp (l, "\\ol|", 4) == 0 || + strncmp (l, "\\dl|", 4) == 0)) + { + switch (l[1]) { - cerr << "error: paragraph '" << string (ol, 0, on) << "' " - << "not allowed in '" << s << "'" << endl; - throw generation_failed (); + case 'u': b = block::ul; break; + case 'o': b = block::ol; break; + case 'd': b = block::dl; break; } - if (ot == ot_html) - r += "<h1>"; - - l += 3; - n -= 3; - blocks.push (b = block::h); + l += 4; + n -= 4; + } + else if (n >= 4 && strncmp (l, "\\li|", 4) == 0) + { + b = block::li; + l += 4; + n -= 4; } - // Figure out how many blocks we need to pop. Things get complicated + // Skip leading spaces after opening '|'. + // + while (n != 0 && (*l == 0x20 || *l == 0x0D || *l == 0x09)) {l++; n--;} + + // Next figure out how many blocks we need to pop at the end of this + // paragraph (i.e., handle the "suffix"). Things get a bit complicated // since '|' could be escaped. // size_t pc (0); // Pop count. @@ -743,75 +758,203 @@ format (output_type ot, string const& s, bool para) // if (ec % 2 != 0) --pc; + + n -= pc; // Number of special '|' at the end. + + // Skip trailing spaces before closing '|'. + // + while (n != 0 && (l[n - 1] == 0x20 || + l[n - 1] == 0x0D || + l[n - 1] == 0x09)) + n--; } - if (pc > blocks.size ()) + if (pc > blocks.size () + (b != block::text ? 1 : 0)) { cerr << "error: extraneous '|' at the end of paragraph '" << string (ol, 0, on) << "'" << endl; throw generation_failed (); } - n -= pc; // Number of special '|' at the end. + // Verify that this block type is valid in this context. Skip + // empty text blocks (can happen if we just have '|'). + // + if (b != block::text || n != 0) + { + bool good (true); + block ob (blocks.empty () ? block (block::text) : blocks.top ()); + + switch (ob) + { + case block::h: good = false; break; + case block::ul: + case block::ol: + case block::dl: good = (b == block::li); break; + case block::li: good = (b == block::text); break; + case block::text: good = (b != block::li); break; + } + + if (!good) + { + cerr << "error: " << b << " inside " << ob << " " + << "in documentation string '" << s << "'" << endl; + throw generation_failed (); + } + } - // Some blocks, like \h, are single-paragraph and cannot be empty. + // Verify the block itself. // - if (b == block::h && n == 0) + switch (b) { - cerr << "error: empty paragraph '" << string (ol, 0, on) << "' " - << "in documentation string '" << s << "'" << endl; - throw generation_failed (); + case block::h: + + // \h blocks are only valid if we are required to start a new + // paragraph (first_para is true). + // + if (!first_para) + { + cerr << "error: paragraph '" << string (ol, 0, on) << "' " + << "not allowed in '" << s << "'" << endl; + throw generation_failed (); + } + + // \h must be single-paragraph. + // + if (pc == 0) + { + cerr << "error: '|' expected at the end of paragraph '" + << string (ol, 0, on) << "'" << endl; + throw generation_failed (); + } + + // \h must not be empty. + // + if (n == 0) + { + cerr << "error: empty paragraph '" << string (ol, 0, on) << "' " + << "in documentation string '" << s << "'" << endl; + throw generation_failed (); + } + + break; + case block::ul: + case block::ol: + case block::dl: + + if (pc != 0) + { + cerr << "error: empty list '" << string (ol, 0, on) << "' " + << "in documentation string '" << s << "'" << endl; + throw generation_failed (); + } + + if (n != 0) + { + cerr << "error: unexpected text after " << b << "| " + << "in paragraph '" << string (ol, 0, on) << "'" << endl; + throw generation_failed (); + } + + break; + case block::li: + + if (blocks.top () == block::dl) + { + if (n == 0) + { + cerr << "error: term text missing in paragraph '" + << string (ol, 0, on) << "'" << endl; + throw generation_failed (); + } + } + + break; + case block::text: + break; } - if (b == block::none) // Normal text. + // Output opening markup. + // + if (ot == ot_html) { - if (para || !first) // Start a paragraph? + switch (b) { - if (ot == ot_html) + case block::h: r += "<h1>"; break; + case block::ul: r += "<ul>"; break; + case block::ol: r += "<ol>"; break; + case block::dl: r += "<dl>"; break; + case block::li: + r += (blocks.top () == block::dl ? "<dt>" : "<li>"); + break; + case block::text: + if (n != 0 && para) r += "<p>"; + break; } } - format_line (ot, r, l, n); + // Output paragraph text. + // + if (n != 0) + format_line (ot, r, l, n); - if (b == block::none) // Normal text. + // Output intermediate markup, if any. + // + if (ot == ot_html) { - if (para || !first) // End a paragraph? + switch (b) { - if (ot == ot_html) + case block::li: + if (blocks.top () == block::dl) + r += "</dt>\n<dd>"; + break; + case block::text: + if (n != 0 && para) r += "</p>"; + break; + default: break; } } + // Set the para flag. + // + switch (b) + { + case block::li: para = (blocks.top () != block::dl); break; + case block::text: para = para || (n != 0); break; + default: para = true; break; + } + + // Push the paragraph block. + // + if (b != block::text) + blocks.push (b); + // Pop paragraph blocks. // for (; pc != 0; --pc) { - switch (blocks.top ()) + b = blocks.top (); + blocks.pop (); + + if (ot == ot_html) { - case block::h: + switch (b) { - if (ot == ot_html) - r += "</h1>"; - + case block::h: r += "</h1>"; break; + case block::ul: r += "</ul>"; break; + case block::ol: r += "</ol>"; break; + case block::dl: r += "</dl>"; break; + case block::li: r += blocks.top () == block::dl ? "</dd>" : "</li>"; break; + case block::text: break; } - case block::none: - break; - } - blocks.pop (); - } - - // Some blocks, like \h, must be single-paragraph. - // - b = blocks.empty () ? block (block::none) : blocks.top (); + if (pc != 1) // Add empty line unless this is the last separator. + r += "\n\n"; + } - if (b == block::h) - { - cerr << "error: '|' expected at the end of paragraph '" - << string (ol, 0, on) << "'" << endl; - throw generation_failed (); + para = true; // End of a block always means new paragraph. } } @@ -820,12 +963,13 @@ format (output_type ot, string const& s, bool para) // Separate paragraphs with newline. // - r += "\n\n"; + if (para) + r += "\n\n"; } if (!blocks.empty ()) { - cerr << "error: unterminated paragraph '\\" << blocks.top () << "' " + cerr << "error: unterminated paragraph " << blocks.top () << " " << "in documentation string '" << s << "'" << endl; throw generation_failed (); } |