aboutsummaryrefslogtreecommitdiff
path: root/libcutl/compiler/cxx-indenter.txx
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2020-12-16 20:29:05 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2021-02-24 16:40:04 +0300
commit8e761289a2446367267c6c0d9a26e734f0f78306 (patch)
treefb495d8c18801f271d124ee48731f10df396ca89 /libcutl/compiler/cxx-indenter.txx
parent4c8104756b92b9fa16b3a725e8a6aa620dfd606e (diff)
Get rid of legacy build systems and rename cutl/ to libcutl/
Diffstat (limited to 'libcutl/compiler/cxx-indenter.txx')
-rw-r--r--libcutl/compiler/cxx-indenter.txx816
1 files changed, 816 insertions, 0 deletions
diff --git a/libcutl/compiler/cxx-indenter.txx b/libcutl/compiler/cxx-indenter.txx
new file mode 100644
index 0000000..d1af9b2
--- /dev/null
+++ b/libcutl/compiler/cxx-indenter.txx
@@ -0,0 +1,816 @@
+// file : libcutl/compiler/cxx-indenter.txx
+// license : MIT; see accompanying LICENSE file
+
+namespace cutl
+{
+ namespace compiler
+ {
+ template <typename C>
+ cxx_indenter<C>::
+ cxx_indenter (code_stream<C>& out)
+ : out_ (out),
+ buffering_ (false),
+ position_ (0),
+ paren_balance_ (0),
+ spaces_ (2),
+ construct_ (con_other),
+ do_ (keyword(kw_do)),
+ lbrace_ (1, '{'),
+ rbrace_ (1, '}')
+ {
+ indentation_.push (0);
+
+ single_line_blocks_.insert (keyword(kw_if));
+ single_line_blocks_.insert (keyword(kw_do));
+ single_line_blocks_.insert (keyword(kw_for));
+ single_line_blocks_.insert (keyword(kw_else));
+ single_line_blocks_.insert (keyword(kw_case));
+ single_line_blocks_.insert (keyword(kw_while));
+
+ follow_blocks_.insert (keyword(kw_else));
+ follow_blocks_.insert (keyword(kw_case));
+ follow_blocks_.insert (keyword(kw_catch));
+ follow_blocks_.insert (keyword(kw_default));
+ }
+
+ template <typename C>
+ void cxx_indenter<C>::
+ put (C c)
+ {
+ // First determine what kind of construct we are in.
+ //
+ construct new_con (construct_);
+ construct old_con (construct_);
+
+ switch (c)
+ {
+ case '\n':
+ {
+ if (construct_ == con_pp_dir ||
+ construct_ == con_cxx_com)
+ construct_ = new_con = con_other;
+
+ break;
+ }
+ case '#':
+ {
+ if (construct_ == con_other)
+ construct_ = new_con = con_pp_dir;
+
+ break;
+ }
+ case '\"':
+ {
+ if (construct_ != con_pp_dir &&
+ construct_ != con_c_com &&
+ construct_ != con_cxx_com &&
+ construct_ != con_char_lit)
+ {
+ // We might be in an escape sequence.
+ //
+ bool es (!hold_.empty () && hold_.back () == '\\');
+
+ if (es)
+ {
+ // Scan the hold sequence backwards to figure out if this
+ // backslash is part of this escape sequence or a preceding
+ // one.
+ //
+ for (typename hold::reverse_iterator i (hold_.rbegin () + 1),
+ e (hold_.rend ()); i != e && *i == '\\'; ++i)
+ es = !es;
+ }
+
+ if (!es)
+ {
+ if (construct_ == con_string_lit)
+ new_con = con_other;
+ else
+ construct_ = new_con = con_string_lit;
+ }
+ }
+
+ break;
+ }
+ case '\'':
+ {
+ if (construct_ != con_pp_dir &&
+ construct_ != con_c_com &&
+ construct_ != con_cxx_com &&
+ construct_ != con_string_lit)
+ {
+ // We might be in an escape sequence.
+ //
+ bool es (!hold_.empty () && hold_.back () == '\\');
+
+ if (es)
+ {
+ // Scan the hold sequence backwards to figure out if this
+ // backslash is part of this escape sequence or a preceding
+ // one.
+ //
+ for (typename hold::reverse_iterator i (hold_.rbegin () + 1),
+ e (hold_.rend ()); i != e && *i == '\\'; ++i)
+ es = !es;
+ }
+
+ if (!es)
+ {
+ if (construct_ == con_char_lit)
+ new_con = con_other;
+ else
+ construct_ = new_con = con_char_lit;
+ }
+ }
+
+ break;
+ }
+ case '/':
+ {
+ if (construct_ == con_other)
+ {
+ if (!hold_.empty () && hold_.back () == '/')
+ construct_ = new_con = con_cxx_com;
+ }
+
+ if (construct_ == con_c_com)
+ {
+ if (!hold_.empty () && hold_.back () == '*')
+ construct_ = new_con = con_other;
+ }
+
+ break;
+ }
+ case '*':
+ {
+ if (construct_ == con_other)
+ {
+ if (!hold_.empty () && hold_.back () == '/')
+ construct_ = new_con = con_c_com;
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ // Special handling of CPP directives.
+ //
+ if (construct_ == con_pp_dir)
+ {
+ write (c);
+ position_++;
+ return;
+ }
+
+ //
+ //
+ tokenize (c, old_con);
+
+
+ // Indentation in parenthesis. We don't need to make sure
+ // we are not in a comments, etc., because we make sure we
+ // don't hold anything in those states.
+ //
+ if (!hold_.empty () && hold_.back () == '(')
+ {
+ unbuffer (); // We don't need to hold it anymore.
+
+ if (c == '\n')
+ indentation_.push (indentation_.top () + spaces_);
+ else
+ indentation_.push (position_);
+ }
+
+
+ //
+ //
+ bool defaulting (false);
+
+ switch (c)
+ {
+ case '\n':
+ {
+ if (!indent_stack_.empty () && construct_ == con_other)
+ indent_stack_.top ().newline_ = true;
+
+ hold_.push_back (c);
+ position_ = 0; // Starting a new line.
+
+ break;
+ }
+ case '{':
+ {
+ if (construct_ == con_other)
+ {
+ if (!indent_stack_.empty ())
+ {
+ // Pop all the blocks until the one that was indented.
+ //
+ while (indent_stack_.top ().indentation_ == 0)
+ indent_stack_.pop ();
+
+ // Pop the indented block and one level of indentation.
+ //
+ if (indentation_.size () > 1)
+ indentation_.pop ();
+
+ indent_stack_.pop ();
+ }
+
+ ensure_new_line ();
+ output_indentation ();
+ write (c);
+ ensure_new_line ();
+
+ indentation_.push (indentation_.top () + spaces_);
+ }
+ else
+ defaulting = true;
+
+ break;
+ }
+ case '}':
+ {
+ if (construct_ == con_other)
+ {
+ if (indentation_.size () > 1)
+ indentation_.pop ();
+
+ // Reduce multiple newlines to one.
+ //
+ while (hold_.size () > 1)
+ {
+ typename hold::reverse_iterator i (hold_.rbegin ());
+
+ if (*i == '\n' && *(i + 1) == '\n')
+ hold_.pop_back ();
+ else
+ break;
+ }
+
+ ensure_new_line ();
+ output_indentation ();
+
+ hold_.push_back (c);
+
+ // Add double newline after '}'.
+ //
+ hold_.push_back ('\n');
+ hold_.push_back ('\n');
+ position_ = 0;
+
+ if (!indent_stack_.empty ())
+ {
+ // Pop all the blocks until the one that was indented.
+ //
+ while (indent_stack_.top ().indentation_ == 0)
+ indent_stack_.pop ();
+
+ // Now pop all the indented blocks while also popping the
+ // indentation stack. Do it only if the indentation match.
+ // If it doesn't then that means this inden_stack entry is
+ // for some other, outer block.
+ //
+ while (!indent_stack_.empty () &&
+ indent_stack_.top ().indentation_ ==
+ indentation_.size ())
+ {
+ if (indentation_.size () > 1)
+ indentation_.pop ();
+
+ indent_stack_.pop ();
+ }
+ }
+
+ buffering_ = true;
+ }
+ else
+ defaulting = true;
+
+ break;
+ }
+ case ';':
+ {
+ if (construct_ == con_other)
+ {
+ // for (;;)
+ //
+ if (!indent_stack_.empty () && paren_balance_ == 0)
+ {
+ // Pop all the blocks until the one that was indented.
+ //
+ while (indent_stack_.top ().indentation_ == 0)
+ indent_stack_.pop ();
+
+ // Now pop all the indented blocks while also popping the
+ // indentation stack. Do it only if the indentation match.
+ // If they don't then it means we are inside a block and
+ // the stack should be popped after seeing '}', not ';'.
+ //
+ while (!indent_stack_.empty () &&
+ indent_stack_.top ().indentation_ ==
+ indentation_.size ())
+ {
+ if (indentation_.size () > 1)
+ indentation_.pop ();
+
+ indent_stack_.pop ();
+ }
+ }
+
+ if (paren_balance_ != 0)
+ {
+ // We are inside for (;;) statement. Nothing to do here.
+ //
+ defaulting = true;
+ }
+ else
+ {
+ // Handling '};' case.
+ //
+
+ bool brace (false);
+
+ if (hold_.size () > 1 && hold_.back () == '\n')
+ {
+ bool pop_nl (false);
+
+ for (typename hold::reverse_iterator
+ i (hold_.rbegin ()), e (hold_.rend ());
+ i != e; ++i)
+ {
+ if (*i != '\n')
+ {
+ if (*i == '}')
+ brace = pop_nl = true;
+
+ break;
+ }
+ }
+
+ if (pop_nl)
+ while (hold_.back () == '\n')
+ hold_.pop_back ();
+ }
+
+ output_indentation ();
+ write (c);
+ position_++;
+
+ if (brace)
+ {
+ hold_.push_back ('\n');
+ hold_.push_back ('\n');
+ }
+
+ ensure_new_line ();
+ }
+ }
+ else
+ defaulting = true;
+
+ break;
+ }
+ case ',':
+ {
+ if (construct_ == con_other)
+ {
+ // Handling '},' case.
+ //
+
+ bool brace (false);
+
+ if (hold_.size () > 1 && hold_.back () == '\n')
+ {
+ bool pop_nl (false);
+
+ for (typename hold::reverse_iterator
+ i (hold_.rbegin ()), e (hold_.rend ());
+ i != e; ++i)
+ {
+ if (*i != '\n')
+ {
+ if (*i == '}')
+ brace = pop_nl = true;
+
+ break;
+ }
+ }
+
+ if (pop_nl)
+ while (hold_.back () == '\n')
+ hold_.pop_back ();
+ }
+
+ output_indentation ();
+ write (c);
+ position_++;
+
+ if (brace)
+ hold_.push_back ('\n');
+ }
+ else
+ defaulting = true;
+
+ break;
+ }
+ case ' ':
+ {
+ if (construct_ == con_other)
+ {
+ // Handling '} foo_;' case.
+ //
+ if (hold_.size () > 1 && hold_.back () == '\n')
+ {
+ bool pop_nl (false);
+
+ for (typename hold::reverse_iterator
+ i (hold_.rbegin ()), e (hold_.rend ());
+ i != e; ++i)
+ {
+ if (*i != '\n')
+ {
+ if (*i == '}')
+ pop_nl = true;
+
+ break;
+ }
+ }
+
+ if (pop_nl)
+ while (hold_.back () == '\n')
+ hold_.pop_back ();
+ }
+ }
+
+ defaulting = true;
+ break;
+ }
+ case '\\':
+ {
+ if (construct_ != con_pp_dir &&
+ construct_ != con_c_com &&
+ construct_ != con_cxx_com)
+ {
+ output_indentation ();
+ hold_.push_back (c);
+ position_++;
+ }
+ else
+ defaulting = true;
+
+ break;
+
+ }
+ case '(':
+ {
+ if (construct_ == con_other)
+ {
+ // Hold it so that we can see what's coming next.
+ //
+ output_indentation ();
+ hold_.push_back (c);
+ position_++;
+ paren_balance_++;
+ }
+ else
+ defaulting = true;
+ break;
+ }
+ case ')':
+ {
+ if (construct_ == con_other)
+ {
+ if (indentation_.size () > 1)
+ indentation_.pop ();
+
+ if (paren_balance_ > 0)
+ paren_balance_--;
+ }
+
+ defaulting = true;
+ break;
+ }
+ case '/':
+ {
+ if (construct_ == con_other)
+ {
+ output_indentation ();
+ hold_.push_back (c);
+ position_++;
+ }
+ else
+ defaulting = true;
+
+ break;
+ }
+ case '*':
+ {
+ if (construct_ == con_c_com)
+ {
+ output_indentation ();
+ hold_.push_back (c);
+ position_++;
+ }
+ else
+ defaulting = true;
+
+ break;
+ }
+ default:
+ {
+ defaulting = true;
+ break;
+ }
+ }
+
+ if (defaulting)
+ {
+ output_indentation ();
+ write (c);
+ position_++;
+ }
+
+ construct_ = new_con;
+ }
+
+ template <typename C>
+ void cxx_indenter<C>::
+ unbuffer ()
+ {
+ for (; !hold_.empty (); hold_.pop_front ())
+ out_.put (hold_.front ());
+ }
+
+ template <typename C>
+ void cxx_indenter<C>::
+ next_token (string const& old, C c)
+ {
+ // Handle one line indentation blocks (if, else, etc).
+ //
+ if (single_line_blocks_.find (token_) != single_line_blocks_.end ())
+ {
+ // Only indent sub-blocks if we are on a new line.
+ //
+ bool indent (indent_stack_.empty () ||
+ indent_stack_.top ().newline_);
+
+ if (indent)
+ indentation_.push (indentation_.top () + spaces_);
+
+ indent_stack_.push (
+ indent_block (c == '\n', (indent ? indentation_.size () : 0)));
+ }
+
+ // Keep track of the do ... while construct in order to suppress
+ // the newline after } and before while.
+ //
+ if (old == do_ && token_ == lbrace_)
+ do_while_state_.push (0);
+
+ if (!do_while_state_.empty ())
+ {
+ if (token_ == lbrace_)
+ do_while_state_.top ()++;
+
+ if (token_ == rbrace_)
+ do_while_state_.top ()--;
+ }
+
+ // Suppress double newline in the "}else", etc., cases.
+ //
+ if (old == rbrace_)
+ {
+ bool dw (!do_while_state_.empty () && do_while_state_.top () == 0);
+
+ if (follow_blocks_.find (token_) != follow_blocks_.end () || dw)
+ {
+ if (dw)
+ do_while_state_.pop ();
+
+ // Reduce double newline after "}" into a single one.
+ //
+ typename hold::iterator i (hold_.end ()), b (hold_.begin ());
+
+ for (--i; i != b; --i)
+ {
+ // See if this is the end of the "}\n\n" sequence.
+ //
+ if (*i == '\n')
+ {
+ --i;
+ if (i != b && *i == '\n')
+ {
+ --i;
+ if (*i == '}')
+ {
+ ++i;
+ hold_.erase (i);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Stop buffering unless we have another closing brace.
+ //
+ if (token_ != rbrace_)
+ buffering_ = false;
+ }
+ }
+
+ template <typename C>
+ void cxx_indenter<C>::
+ ensure_new_line ()
+ {
+ if (hold_.empty () || hold_.back () != '\n')
+ {
+ hold_.push_back ('\n');
+ position_ = 0; // Starting a new line.
+ }
+ }
+
+
+ template <typename C>
+ void cxx_indenter<C>::
+ output_indentation ()
+ {
+ if (!hold_.empty () && hold_.back () == '\n')
+ {
+ for (std::size_t i (0); i < indentation_.top (); ++i)
+ write (' ');
+
+ position_ += indentation_.top ();
+ }
+ }
+
+ template <typename C>
+ void cxx_indenter<C>::
+ write (C c)
+ {
+ hold_.push_back (c);
+
+ if (!buffering_)
+ {
+ for (; !hold_.empty (); hold_.pop_front ())
+ out_.put (hold_.front ());
+ }
+ }
+
+ template <typename C>
+ void cxx_indenter<C>::
+ tokenize (C c, construct old)
+ {
+ //
+ //
+ switch (construct_)
+ {
+ case con_pp_dir:
+ {
+ if (old == con_other) // Start PP directive
+ retire (c);
+
+ return;
+ }
+ case con_c_com:
+ {
+ if (old == con_other) // Start C comment.
+ lexeme_.clear ();
+
+ return;
+ }
+ case con_cxx_com:
+ {
+ if (old == con_other) // Start C++ comment.
+ lexeme_.clear ();
+
+ return;
+ }
+ case con_string_lit:
+ {
+ if (old == con_other) // Start string literal
+ retire (c);
+
+ lexeme_ += c;
+ return;
+ }
+ case con_char_lit:
+ {
+ if (old == con_other) // Start char literal
+ retire (c);
+
+ lexeme_ += c;
+ return;
+ }
+ default:
+ break;
+ }
+
+ // construct_ == other
+ //
+ switch (old)
+ {
+ case con_pp_dir:
+ {
+ // End PP directive (newline).
+ //
+ return;
+ }
+ case con_c_com:
+ {
+ // End C comment.
+ //
+ return;
+ }
+ case con_cxx_com:
+ {
+ // End C++ comment (newline).
+ //
+ return;
+ }
+ case con_string_lit:
+ {
+ // End string literal (").
+ //
+ lexeme_ += c;
+ return;
+ }
+ case con_char_lit:
+ {
+ // End char literal (').
+ //
+ lexeme_ += c;
+ return;
+ }
+ default:
+ break;
+ }
+
+
+ // construct_ == old == other
+ //
+
+ switch (char_class (c))
+ {
+ case cc_alpha:
+ {
+ if (lexeme_.empty () ||
+ char_class (lexeme_[0]) == cc_alpha)
+ lexeme_ += c;
+ else
+ {
+ retire (c);
+ lexeme_ += c;
+ }
+ break;
+ }
+ case cc_digit:
+ {
+ if (lexeme_.empty ())
+ lexeme_ += c;
+ else
+ {
+ char_class_type cc (char_class (lexeme_[0]));
+
+ if (cc == cc_alpha || cc == cc_digit)
+ lexeme_ += c;
+ else
+ {
+ retire (c);
+ lexeme_ += c;
+ }
+ }
+ break;
+ }
+ case cc_op_punc:
+ {
+ retire (c);
+ lexeme_ += c;
+ break;
+ }
+ case cc_space:
+ {
+ retire (c);
+ break;
+ }
+ }
+ }
+
+ template <typename C>
+ void cxx_indenter<C>::
+ retire (C c)
+ {
+ if (!lexeme_.empty ())
+ {
+ token_.swap (lexeme_);
+ next_token (lexeme_, c);
+ lexeme_.clear ();
+ }
+ }
+ }
+}