diff options
Diffstat (limited to 'libbackend-elements/backend-elements/indentation')
9 files changed, 1864 insertions, 0 deletions
diff --git a/libbackend-elements/backend-elements/indentation/buffer.hxx b/libbackend-elements/backend-elements/indentation/buffer.hxx new file mode 100644 index 0000000..7058814 --- /dev/null +++ b/libbackend-elements/backend-elements/indentation/buffer.hxx @@ -0,0 +1,61 @@ +// file : backend-elements/indentation/buffer.hxx +// author : Boris Kolpackov <boris@kolpackov.net> +// copyright : Copyright (c) 2005-2010 Boris Kolpackov +// license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +#ifndef BACKEND_ELEMENTS_INDENTATION_BUFFER_HXX +#define BACKEND_ELEMENTS_INDENTATION_BUFFER_HXX + +#include <backend-elements/types.hxx> + +#include <string> + +namespace BackendElements +{ + namespace Indentation + { + template <typename C> + class Buffer: public NonCopyable + { + public: + struct Write {}; + + public: + virtual + ~Buffer () + { + } + + public: + typedef + std::char_traits<C> + Traits; + + typedef + typename Traits::char_type + AsChar; + + typedef + typename Traits::int_type + AsInt; + + public: + virtual AsInt + put (AsChar c) = 0; + + // Unbuffer flushes internal formatting buffers (if any). + // Note that unbuffer is not exactly flushing since it can + // result in formatting errors and in general can not be + // called at arbitrary points. Natural use case would be + // to call unbuffer at the end of the stream when no more + // data is expected. + // + virtual Void + unbuffer () = 0; + }; + } +} + +#include <backend-elements/indentation/buffer.txx> + +#endif // BACKEND_ELEMENTS_INDENTATION_BUFFER_HXX diff --git a/libbackend-elements/backend-elements/indentation/buffer.txx b/libbackend-elements/backend-elements/indentation/buffer.txx new file mode 100644 index 0000000..57ba7ab --- /dev/null +++ b/libbackend-elements/backend-elements/indentation/buffer.txx @@ -0,0 +1,12 @@ +// file : backend-elements/indentation/buffer.txx +// author : Boris Kolpackov <boris@kolpackov.net> +// copyright : Copyright (c) 2005-2010 Boris Kolpackov +// license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +namespace BackendElements +{ + namespace Indentation + { + } +} + diff --git a/libbackend-elements/backend-elements/indentation/clip.hxx b/libbackend-elements/backend-elements/indentation/clip.hxx new file mode 100644 index 0000000..068ed0d --- /dev/null +++ b/libbackend-elements/backend-elements/indentation/clip.hxx @@ -0,0 +1,173 @@ +// file : backend-elements/indentation/clip.hxx +// author : Boris Kolpackov <boris@kolpackov.net> +// copyright : Copyright (c) 2005-2010 Boris Kolpackov +// license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +#ifndef BACKEND_ELEMENTS_INDENTATION_CLIP_HXX +#define BACKEND_ELEMENTS_INDENTATION_CLIP_HXX + +#include <backend-elements/types.hxx> + +#include <backend-elements/indentation/buffer.hxx> + +#include <ostream> + +namespace BackendElements +{ + namespace Indentation + { + template <typename C> + class ToStreambufAdapter: public std::basic_streambuf<C>, + public NonCopyable + { + public: + typedef + typename std::basic_streambuf<C>::traits_type + Traits; + + typedef + typename std::basic_streambuf<C>::char_type + AsChar; + + typedef + typename std::basic_streambuf<C>::int_type + AsInt; + + public: + ToStreambufAdapter (Buffer<C>& buffer) + : buffer_ (buffer) + { + } + + virtual AsInt + overflow (AsInt ch) + { + return buffer_.put (Traits::to_char_type (ch)); + } + + virtual Int + sync () + { + return 0; + } + + private: + Buffer<C>& buffer_; + }; + + + template <typename C> + class FromStreambufAdapter: public Buffer<C> + { + public: + typedef + typename Buffer<C>::Traits + Traits; + + typedef + typename Buffer<C>::AsChar + AsChar; + + typedef + typename Buffer<C>::AsInt + AsInt; + + typedef + typename Buffer<C>::Write + Write; + + public: + FromStreambufAdapter (std::basic_streambuf<C>& b) + : buffer_ (b) + { + } + + virtual AsInt + put (AsChar ch) + { + return buffer_.sputc (ch); + } + + virtual Void + unbuffer () + { + try + { + if (buffer_.pubsync () == 0) return; + } + catch (std::ios_base::failure const&) + { + } + + throw Write (); + } + + private: + std::basic_streambuf<C>& buffer_; + }; + + + template <template <typename> class Buffer, typename C = Char> + class Clip: public NonCopyable + { + public: + Clip (std::basic_ostream<C>& os) + : os_ (os), + prev_ (os_.rdbuf ()), + from_adapter_ (*prev_), + buffer_ (from_adapter_), + to_adapter_ (buffer_) + { + os_.rdbuf (&to_adapter_); + } + + /* + template <typename Arg0> + Clip (std::basic_ostream<C>& os, Arg0 a0) + : os_ (os), + prev_ (os_.rdbuf ()), + from_adapter_ (*prev_), + buffer_ (from_adapter_, a0), + to_adapter_ (buffer_) + { + os_.rdbuf (&to_adapter_); + } + */ + + ~Clip () + { + try + { + buffer_.unbuffer (); + } + catch (...) + { + os_.rdbuf (prev_); + throw; + } + + os_.rdbuf (prev_); + } + + Buffer<C>& + buffer () + { + return buffer_; + } + + private: + std::basic_ostream<C>& os_; + std::basic_streambuf<C>* prev_; + + FromStreambufAdapter<C> from_adapter_; + + Buffer<C> buffer_; + + ToStreambufAdapter<C> to_adapter_; + }; + } +} + +#include <backend-elements/indentation/clip.txx> + +#endif // BACKEND_ELEMENTS_INDENTATION_CLIP_HXX diff --git a/libbackend-elements/backend-elements/indentation/clip.txx b/libbackend-elements/backend-elements/indentation/clip.txx new file mode 100644 index 0000000..58112f1 --- /dev/null +++ b/libbackend-elements/backend-elements/indentation/clip.txx @@ -0,0 +1,12 @@ +// file : backend-elements/indentation/clip.txx +// author : Boris Kolpackov <boris@kolpackov.net> +// copyright : Copyright (c) 2005-2010 Boris Kolpackov +// license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +namespace BackendElements +{ + namespace Indentation + { + } +} + diff --git a/libbackend-elements/backend-elements/indentation/cxx.hxx b/libbackend-elements/backend-elements/indentation/cxx.hxx new file mode 100644 index 0000000..f10341a --- /dev/null +++ b/libbackend-elements/backend-elements/indentation/cxx.hxx @@ -0,0 +1,1016 @@ +// file : backend-elements/indentation/cxx.hxx +// author : Boris Kolpackov <boris@kolpackov.net> +// copyright : Copyright (c) 2005-2010 Boris Kolpackov +// license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +#ifndef BACKEND_ELEMENTS_INDENTATION_CXX_HXX +#define BACKEND_ELEMENTS_INDENTATION_CXX_HXX + +#include <backend-elements/types.hxx> +#include <backend-elements/indentation/buffer.hxx> + +#include <cult/containers/set.hxx> +#include <cult/containers/stack.hxx> +#include <cult/containers/deque.hxx> + +#include <locale> + +// #include <iostream> // @@ tmp + +namespace BackendElements +{ + namespace Indentation + { + template <typename C> + class CXX: public Buffer<C>/*, public NonCopyable*/ + { + public: + typedef + typename Buffer<C>::Traits + Traits; + + typedef + typename Buffer<C>::AsChar + AsChar; + + typedef + typename Buffer<C>::AsInt + AsInt; + + typedef + typename Buffer<C>::Write + Write; + + public: + CXX (Buffer<C>& out) + : out_ (out), + buffering_ (false), + position_ (0), + paren_balance_ (0), + spaces_ (2), + construct_ (Construct::other), + lbrace_ ("{"), + rbrace_ ("}") + // locale_ ("C") + { + indentation_.push (0); + + single_line_blocks_.insert ("if"); + single_line_blocks_.insert ("do"); + single_line_blocks_.insert ("for"); + single_line_blocks_.insert ("else"); + single_line_blocks_.insert ("case"); + single_line_blocks_.insert ("while"); + + follow_blocks_.insert ("else"); + follow_blocks_.insert ("case"); + follow_blocks_.insert ("catch"); + follow_blocks_.insert ("default"); + } + + public: + virtual AsInt + put (AsChar c) + { + AsInt result (Traits::to_int_type (c)); + + try + { + // First determine what kind of construct we are in. + // + typename Construct::Value new_construct (construct_); + typename Construct::Value old_construct (construct_); + + switch (c) + { + case '\n': + { + if (construct_ == Construct::pp_directive || + construct_ == Construct::cxx_comment) + construct_ = new_construct = Construct::other; + + break; + } + case '#': + { + if (construct_ == Construct::other) + construct_ = new_construct = Construct::pp_directive; + + break; + } + case '\"': + { + if (construct_ != Construct::pp_directive && + construct_ != Construct::c_comment && + construct_ != Construct::cxx_comment && + construct_ != Construct::char_literal) + { + // We might be in an escape sequence. + // + Boolean 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::ReverseIterator i (hold_.rbegin () + 1), + e (hold_.rend ()); i != e && *i == '\\'; ++i) + es = !es; + } + + if (!es) + { + // Not an escape sequence. + // + if (construct_ == Construct::string_literal) + new_construct = Construct::other; + else + construct_ = new_construct = Construct::string_literal; + } + } + + break; + } + case '\'': + { + if (construct_ != Construct::pp_directive && + construct_ != Construct::c_comment && + construct_ != Construct::cxx_comment && + construct_ != Construct::string_literal) + { + // We might be in an escape sequence. + // + Boolean 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::ReverseIterator i (hold_.rbegin () + 1), + e (hold_.rend ()); i != e && *i == '\\'; ++i) + es = !es; + } + + if (!es) + { + if (construct_ == Construct::char_literal) + new_construct = Construct::other; + else + construct_ = new_construct = Construct::char_literal; + } + } + + break; + } + case '/': + { + if (construct_ == Construct::other) + { + if (!hold_.empty () && hold_.back () == '/') + construct_ = new_construct = Construct::cxx_comment; + } + + if (construct_ == Construct::c_comment) + { + if (!hold_.empty () && hold_.back () == '*') + construct_ = new_construct = Construct::other; + } + + break; + } + case '*': + { + if (construct_ == Construct::other) + { + if (!hold_.empty () && hold_.back () == '/') + construct_ = new_construct = Construct::c_comment; + } + + break; + } + default: + { + break; + } + } + + // Special handling of CPP directives. + // + if (construct_ == Construct::pp_directive) + { + result = write (c); + position_++; + return result; + } + + // + // + tokenize (c, old_construct); + + + // 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_); + } + + + // + // + Boolean defaulting (false); + + switch (c) + { + case '\n': + { + if (!indent_block_.empty () && construct_ == Construct::other) + indent_block_.top ().newline_ = true; + + hold_.push_back (c); + position_ = 0; // Starting a new line. + + break; + } + case '{': + { + if (construct_ == Construct::other) + { + if (!indent_block_.empty ()) + { + // Pop all the blocks until the one that was indented. + // + while (!indent_block_.top ().indented_) + indent_block_.pop (); + + if (indentation_.size () > 1) + indentation_.pop (); + + indent_block_.pop (); + } + + ensure_new_line (); + output_indentation (); + result = write (c); + ensure_new_line (); + + indentation_.push (indentation_.top () + spaces_); + } + else + defaulting = true; + + break; + } + case '}': + { + if (construct_ == Construct::other) + { + if (indentation_.size () > 1) + indentation_.pop (); + + // Reduce multiple newlines to one. + // + while (hold_.size () > 1) + { + typename Hold::ReverseIterator 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; + + buffering_ = true; + } + else + defaulting = true; + + break; + } + case ';': + { + if (construct_ == Construct::other) + { + // for (;;) + // + if (!indent_block_.empty () && paren_balance_ == 0) + { + // Pop all the blocks until the one that was indented. + // + while (!indent_block_.top ().indented_) + indent_block_.pop (); + + if (indentation_.size () > 1) + indentation_.pop (); + + indent_block_.pop (); + } + + if (paren_balance_ != 0) + { + // We are inside for (;;) statement. Nothing to do here. + // + defaulting = true; + } + else + { + // Handling '};' case. + // + + Boolean brace (false); + + if (hold_.size () > 1 && hold_.back () == '\n') + { + Boolean pop_nl (false); + + for (typename Hold::ReverseIterator + 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 (); + result = write (c); + position_++; + + if (brace) + { + hold_.push_back ('\n'); + hold_.push_back ('\n'); + } + + ensure_new_line (); + } + } + else + defaulting = true; + + break; + } + case ' ': + { + if (construct_ == Construct::other) + { + // Handling '} foo_;' case. + // + if (hold_.size () > 1 && hold_.back () == '\n') + { + Boolean pop_nl (false); + + for (typename Hold::ReverseIterator + 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_ != Construct::pp_directive && + construct_ != Construct::c_comment && + construct_ != Construct::cxx_comment) + { + output_indentation (); + hold_.push_back (c); + position_++; + } + else + defaulting = true; + + break; + + } + case '(': + { + if (construct_ == Construct::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_ == Construct::other) + { + if (indentation_.size () > 1) + indentation_.pop (); + + if (paren_balance_ > 0) + paren_balance_--; + } + + defaulting = true; + break; + } + case '/': + { + if (construct_ == Construct::other) + { + output_indentation (); + hold_.push_back (c); + position_++; + } + else + defaulting = true; + + break; + } + case '*': + { + if (construct_ == Construct::c_comment) + { + output_indentation (); + hold_.push_back (c); + position_++; + } + else + defaulting = true; + + break; + } + default: + { + defaulting = true; + break; + } + } + + + if (defaulting) + { + output_indentation (); + result = write (c); + position_++; + } + + construct_ = new_construct; + } + catch (Write const&) + { + result = Traits::eof (); + } + + return result; + } + + private: + typedef Cult::StringTemplate<C> String; + + Void + next_token (String const& old, AsChar c) + { + //std::cerr << "next token: " << token_ + // << "; old token: " << old << std::endl; + + // 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. + // + Boolean indent (indent_block_.empty () || + indent_block_.top ().newline_); + + indent_block_.push (IndentBlockInfo (c == '\n', indent)); + + if (indent) + indentation_.push (indentation_.top () + spaces_); + } + + // Keep track of the do ... while construct in order to suppress + // the newline after } and before while. + // + if (old == String ("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_) + { + Boolean 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; + } + } + } + } + } + else if (token_ != rbrace_) + { + buffering_ = false; + } + } + } + + public: + virtual Void + unbuffer () + { + AsInt result; + + while (!hold_.empty ()) + { + result = out_.put (hold_.front ()); + + //@@ failed + if (result == Traits::eof ()) + throw Write (); + + hold_.pop_front (); + } + } + + private: + Void + ensure_new_line () + { + if (hold_.empty () || hold_.back () != '\n') + { + hold_.push_back ('\n'); + position_ = 0; // Starting a new line. + } + } + + + Void + output_indentation () + { + if (!hold_.empty () && hold_.back () == '\n') + { + for (UnsignedLong i (0); i < indentation_.top (); ++i) + write (' '); + + position_ += indentation_.top (); + } + } + + AsInt + write (AsChar c) + { + hold_.push_back (c); + + if (!buffering_) + { + AsInt result (Traits::eof ()); + + while (!hold_.empty ()) + { + result = out_.put (hold_.front ()); + + if (result == Traits::eof ()) + throw Write (); + + hold_.pop_front (); + } + + return result; + } + else + return c; + } + + private: + struct Construct + { + enum Value + { + other, + pp_directive, + c_comment, + cxx_comment, + string_literal, + char_literal + }; + }; + + Void + tokenize (AsChar c, typename Construct::Value old) + { + // + // + switch (construct_) + { + case Construct::pp_directive: + { + if (old == Construct::other) // Start PP directive + retire (c); + + return; + } + case Construct::c_comment: + { + if (old == Construct::other) // Start C comment. + lexeme_.clear (); + + return; + } + case Construct::cxx_comment: + { + if (old == Construct::other) // Start C++ comment. + lexeme_.clear (); + + return; + } + case Construct::string_literal: + { + if (old == Construct::other) // Start string literal + retire (c); + + lexeme_ += c; + return; + } + case Construct::char_literal: + { + if (old == Construct::other) // Start char literal + retire (c); + + lexeme_ += c; + return; + } + default: + break; + } + + // construct_ == other + // + switch (old) + { + case Construct::pp_directive: + { + // End PP directive (newline). + // + return; + } + case Construct::c_comment: + { + // End C comment. + // + return; + } + case Construct::cxx_comment: + { + // End C++ comment (newline). + // + return; + } + case Construct::string_literal: + { + // End string literal ("). + // + lexeme_ += c; + return; + } + case Construct::char_literal: + { + // End char literal ('). + // + lexeme_ += c; + return; + } + default: + break; + } + + + // construct_ == old == other + // + + /* + + The code below is equivalent to this (simpler) code which is + unfortunately not fast enough. + + using std::isalpha; + using std::isalnum; + using std::isdigit; + using std::isspace; + + if (c == '_' || isalpha (c, locale_)) + { + if (lexeme_.empty () || + lexeme_[0] == '_' || isalpha (lexeme_[0], locale_)) + lexeme_ += c; + else + { + retire (c); + lexeme_ += c; + } + } + else if (isdigit (c, locale_)) + { + if (lexeme_.empty () || + lexeme_[0] == '_' || isalnum (lexeme_[0], locale_)) + lexeme_ += c; + else + { + retire (c); + lexeme_ += c; + } + } + else // Delimiters + { + retire (c); + + if (!isspace (c, locale_)) + lexeme_ += c; + } + */ + + switch (char_class (c)) + { + case CharClass::alpha: + { + if (lexeme_.empty () || + char_class (lexeme_[0]) == CharClass::alpha) + lexeme_ += c; + else + { + retire (c); + lexeme_ += c; + } + break; + } + case CharClass::digit: + { + if (lexeme_.empty ()) + lexeme_ += c; + else + { + typename CharClass::Value cc (char_class (lexeme_[0])); + + if (cc == CharClass::alpha || cc == CharClass::digit) + lexeme_ += c; + else + { + retire (c); + lexeme_ += c; + } + } + break; + } + case CharClass::op_punc: + { + retire (c); + lexeme_ += c; + break; + } + case CharClass::space: + { + retire (c); + break; + } + } + } + + struct CharClass + { + enum Value + { + alpha, // Alpha + '_'. + digit, + op_punc, // Operator or punctuation. + space + }; + + }; + + typename CharClass::Value + char_class (C c) + { + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return CharClass::digit; + + case '!': + case '%': + case '^': + case '&': + case '*': + case '(': + case ')': + case '-': + case '+': + case '=': + case '{': + case '}': + case '|': + case '~': + case '[': + case ']': + case '\\': + case ';': + case '\'': + case ':': + case '"': + case '<': + case '>': + case '?': + case ',': + case '.': + case '/': + return CharClass::op_punc; + + case ' ': + case '\n': + case '\t': + case '\f': + case '\r': + case '\v': + return CharClass::space; + + default: + return CharClass::alpha; + } + } + + + Void + retire (AsChar c) + { + if (!lexeme_.empty ()) + { + token_.swap (lexeme_); + next_token (lexeme_, c); + lexeme_.clear (); + } + } + + private: + Buffer<C>& out_; + Boolean buffering_; // True if write() should buffer the char. + UnsignedLong position_; // Current position on the line. + UnsignedLong paren_balance_; // ( ) balance. + Cult::Containers::Stack<UnsignedLong> indentation_; + UnsignedLong spaces_; + + Boolean suppress_nl_; + + //@@ gcc bug# 18304 + // + typename Construct::Value construct_; + + // Special state stach for the do-while construct. The presence + // of an element in the stack indicates that we are in a braced + // do-while construct. The value of the element is the brace + // balance. + Cult::Containers::Stack<UnsignedLong> do_while_state_; + + typedef + Cult::Containers::Deque<AsInt> + Hold; + + Hold hold_; + + private: + String token_; // previously fully recognized token + String lexeme_; // current lexeme (accumulator) + + // std::locale locale_; + + // Keywords that may be folowed by a single-line block, e.g., if, + // else, etc. + // + Cult::Containers::Set<String> single_line_blocks_; + + // Keywords that may follow (and be related) to a previous block, + // e.g., else, case, catch. + // + Cult::Containers::Set<String> follow_blocks_; + + String lbrace_; + String rbrace_; + + private: + // Single-line indented blocks such as if, else, while, etc. The + // newline flag indicates whether a new line has been seen after + // the keyword. This is needed to properly distinguish cases such + // as: + // + // else if (...) + // foo (); + // + // else + // if (...) + // foo (); + // + struct IndentBlockInfo + { + IndentBlockInfo (Boolean newline, Boolean indented) + : newline_ (newline), indented_ (indented) + { + } + + Boolean newline_; + Boolean indented_; + }; + + Cult::Containers::Stack<IndentBlockInfo> indent_block_; + }; + } +} + +#include <backend-elements/indentation/cxx.txx> + +#endif // BACKEND_ELEMENTS_INDENTATION_CXX_HXX diff --git a/libbackend-elements/backend-elements/indentation/cxx.txx b/libbackend-elements/backend-elements/indentation/cxx.txx new file mode 100644 index 0000000..9acff37 --- /dev/null +++ b/libbackend-elements/backend-elements/indentation/cxx.txx @@ -0,0 +1,12 @@ +// file : backend-elements/indentation/cxx.txx +// author : Boris Kolpackov <boris@kolpackov.net> +// copyright : Copyright (c) 2005-2010 Boris Kolpackov +// license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +namespace BackendElements +{ + namespace Indentation + { + } +} + diff --git a/libbackend-elements/backend-elements/indentation/idl.hxx b/libbackend-elements/backend-elements/indentation/idl.hxx new file mode 100644 index 0000000..e5a234f --- /dev/null +++ b/libbackend-elements/backend-elements/indentation/idl.hxx @@ -0,0 +1,290 @@ +// file : backend-elements/indentation/idl.hxx +// author : Boris Kolpackov <boris@kolpackov.net> +// copyright : Copyright (c) 2005-2010 Boris Kolpackov +// license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +#ifndef BACKEND_ELEMENTS_INDENTATION_IDL_HXX +#define BACKEND_ELEMENTS_INDENTATION_IDL_HXX + +#include <backend-elements/types.hxx> +#include <backend-elements/indentation/buffer.hxx> + +#include <cult/containers/deque.hxx> + +namespace BackendElements +{ + namespace Indentation + { + template <typename C> + class IDL: public Buffer<C>, public NonCopyable + { + public: + typedef + typename Buffer<C>::Traits + Traits; + + typedef + typename Buffer<C>::AsChar + AsChar; + + typedef + typename Buffer<C>::AsInt + AsInt; + + typedef + typename Buffer<C>::Write + Write; + + public: + IDL (Buffer<C>& out) + : out_ (out), + indentation_ (0), + spaces_ (2), + construct_ (Construct::other) + { + } + + public: + virtual AsInt + put (AsChar c) + { + AsInt result (Traits::to_int_type (c)); + + try + { + Boolean defaulting (false); + + switch (c) + { + case '\n': + { + hold_.push_back (c); + break; + } + case '{': + { + ensure_new_line (); + output_indentation (); + result = write (c); + ensure_new_line (); + indentation_++; + break; + } + case '}': + { + if (indentation_ > 0) indentation_--; + + // Reduce multiple newlines to one. + // + while (hold_.size () > 1) + { + typename Hold::ReverseIterator 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'); + + + break; + } + case ';': + { + // Handling '};' case. + // + + Boolean brace (false); + + if (hold_.size () > 1 && hold_.back () == '\n') + { + Boolean pop_nl (false); + + for (typename Hold::ReverseIterator + 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 (); + result = write (c); + + if (brace) + { + hold_.push_back ('\n'); + hold_.push_back ('\n'); + } + + if (construct_ != Construct::string_literal && + construct_ != Construct::char_literal) + { + ensure_new_line (); + } + break; + } + case '\\': + { + hold_.push_back (c); + break; + } + case '\"': + { + if (hold_.empty () || hold_.back () != '\\') + { + // not escape sequence + if (construct_ == Construct::string_literal) + { + construct_ = Construct::other; + } + else construct_ = Construct::string_literal; + } + + defaulting = true; + break; + } + case '\'': + { + if (hold_.empty () || hold_.back () != '\\') + { + // not escape sequence + if (construct_ == Construct::char_literal) + { + construct_ = Construct::other; + } + else construct_ = Construct::char_literal; + } + + defaulting = true; + break; + } + default: + { + defaulting = true; + break; + } + } + + if (defaulting) + { + output_indentation (); + result = write (c); + } + } + catch (Write const&) + { + result = Traits::eof (); + } + + return result; + } + + virtual Void + unbuffer () + { + AsInt result; + + while (!hold_.empty ()) + { + result = out_.put (hold_.front ()); + + //@@ failed + if (result == Traits::eof ()) + throw Write (); + + hold_.pop_front (); + } + } + + private: + Void + ensure_new_line () + { + if (hold_.empty () || hold_.back () != '\n') + hold_.push_back ('\n'); + } + + + Void + output_indentation () + { + if (!hold_.empty () && hold_.back () == '\n') + for (UnsignedLong i (0); i < indentation_ * spaces_; ++i) + write (' '); + } + + AsInt + write (AsChar c) + { + hold_.push_back (c); + + AsInt result (Traits::eof ()); + + while (!hold_.empty ()) + { + result = out_.put (hold_.front ()); + + if (result == Traits::eof ()) + throw Write (); + + hold_.pop_front (); + } + + return result; + } + + + private: + + Buffer<C>& out_; + UnsignedLong indentation_; + UnsignedLong spaces_; + + Boolean suppress_nl_; + + struct Construct + { + enum Value + { + other, + string_literal, + char_literal + }; + }; + + //@@ gcc bug# 18304 + // + typename Construct::Value construct_; + + typedef + Cult::Containers::Deque<AsInt> + Hold; + + Hold hold_; + }; + } +} + +#include <backend-elements/indentation/idl.txx> + +#endif // BACKEND_ELEMENTS_INDENTATION_IDL_HXX diff --git a/libbackend-elements/backend-elements/indentation/idl.txx b/libbackend-elements/backend-elements/indentation/idl.txx new file mode 100644 index 0000000..6ddc63e --- /dev/null +++ b/libbackend-elements/backend-elements/indentation/idl.txx @@ -0,0 +1,11 @@ +// file : backend-elements/indentation/idl.txx +// author : Boris Kolpackov <boris@kolpackov.net> +// copyright : Copyright (c) 2005-2010 Boris Kolpackov +// license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +namespace BackendElements +{ + namespace Indentation + { + } +} diff --git a/libbackend-elements/backend-elements/indentation/sloc.hxx b/libbackend-elements/backend-elements/indentation/sloc.hxx new file mode 100644 index 0000000..57e6989 --- /dev/null +++ b/libbackend-elements/backend-elements/indentation/sloc.hxx @@ -0,0 +1,277 @@ +// file : backend-elements/indentation/sloc.hxx +// author : Boris Kolpackov <boris@kolpackov.net> +// copyright : Copyright (c) 2005-2010 Boris Kolpackov +// license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +#ifndef BACKEND_ELEMENTS_INDENTATION_SLOC_HXX +#define BACKEND_ELEMENTS_INDENTATION_SLOC_HXX + +#include <backend-elements/types.hxx> +#include <backend-elements/indentation/buffer.hxx> + +#include <cctype> +#include <iostream> //@@ tmp + +namespace BackendElements +{ + namespace Indentation + { + template <typename C> + class SLOC: public Buffer<C>/*, public NonCopyable*/ + { + public: + typedef + typename Buffer<C>::Traits + Traits; + + typedef + typename Buffer<C>::AsChar + AsChar; + + typedef + typename Buffer<C>::AsInt + AsInt; + + typedef + typename Buffer<C>::Write + Write; + + public: + SLOC (Buffer<C>& out) + : out_ (out), + count_ (0), + prev_ ('\0'), + code_counted_ (false), + construct_ (Construct::code) + { + } + + UnsignedLong + count () const + { + return count_; + } + + public: + virtual AsInt + put (AsChar c) + { + typename Construct::Value old (construct_); + + switch (construct_) + { + case Construct::code: + { + code (c); + break; + } + case Construct::c_comment: + { + c_comment (c); + break; + } + case Construct::cxx_comment: + { + cxx_comment (c); + break; + } + case Construct::char_literal: + { + char_literal (c); + break; + } + case Construct::string_literal: + { + string_literal (c); + break; + } + } + + // There are cases when a previous character has been already + // 'used' and therefore can not be used again. Good example + // would be '/* *//'. Here, the second slash doesn't start + // C++ comment since it was already used by C comment. + // + // To account for this we are going to set prev_ to '\0' when + // the mode changes. + // + + prev_ = (old == construct_) ? c : '\0'; + + return out_.put (c); + } + + virtual Void + unbuffer () + { + } + + private: + Void + code (AsChar c) + { + bool count (true); + + switch (c) + { + case '/': + { + if (prev_ == '/') + { + construct_ = Construct::cxx_comment; + count = false; + } + else + { + // This slash can be a beginning of a comment but we + // yet have no way to know. Will have to examine it later + // (see below). + // + count = false; + } + + break; + } + case '*': + { + if (prev_ == '/') + { + construct_ = Construct::c_comment; + count = false; + } + break; + } + case '\'': + { + construct_ = Construct::char_literal; + break; + } + case '"': + { + construct_ = Construct::string_literal; + break; + } + case '\n': + { + code_counted_ = false; // reset for a new line + count = false; + break; + } + default: + { + if (std::isspace (c)) + count = false; + break; + } + } + + // The second condition here accounts for the fact that we cannot + // count '/' right away since it can be a beginning of a comment. + // + if (!code_counted_ && + (count || (prev_ == '/' && construct_ == Construct::code))) + { + //std::wcerr << "detected code @ " << c << std::endl; + count_++; + code_counted_ = true; + } + } + + Void + c_comment (AsChar c) + { + switch (c) + { + case '/': + { + if (prev_ == '*') + construct_ = Construct::code; + break; + } + case '\n': + { + code_counted_ = false; // reset for a new line + break; + } + } + } + + Void + cxx_comment (AsChar c) + { + switch (c) + { + case '\n': + { + construct_ = Construct::code; + code_counted_ = false; // reset for a new line + break; + } + } + } + + Void + char_literal (AsChar c) + { + switch (c) + { + case '\'': + { + if (prev_ != '\\') + construct_ = Construct::code; + break; + } + } + } + + Void + string_literal (AsChar c) + { + switch (c) + { + case '"': + { + if (prev_ != '\\') + construct_ = Construct::code; + break; + } + case '\n': + { + /*@@ Should I count multi-line string literal as multiple SLOC? */ + break; + } + } + } + + private: + Buffer<C>& out_; + UnsignedLong count_; + + AsChar prev_; // previous character or '\0' + + struct Construct + { + enum Value + { + code, + c_comment, + cxx_comment, + char_literal, + string_literal + }; + }; + + // code + // + bool code_counted_; + + //@@ gcc bug# 18304 + // + typename Construct::Value construct_; + }; + } +} + +//#include <backend-elements/indentation/sloc.txx> + +#endif // BACKEND_ELEMENTS_INDENTATION_SLOC_HXX |