diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2009-09-13 11:52:46 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2009-09-13 11:52:46 +0200 |
commit | 0143e511f7a5f3595907787a832bee4e3cd03daf (patch) | |
tree | 141d76fb8c18a8258eaa7d20035e22875f1bf0a3 | |
parent | cb9ea47e7825b5073d4d645afb94f6326cb7cf4d (diff) |
Add code stream interface and SLOC counter
-rw-r--r-- | cutl/compiler/code-stream.hxx | 153 | ||||
-rw-r--r-- | cutl/compiler/code-stream.txx | 96 | ||||
-rw-r--r-- | cutl/compiler/sloc-counter.hxx | 80 | ||||
-rw-r--r-- | cutl/compiler/sloc-counter.txx | 225 | ||||
-rw-r--r-- | tests/compiler/makefile | 2 | ||||
-rw-r--r-- | tests/compiler/sloc-counter/driver.cxx | 37 | ||||
-rw-r--r-- | tests/compiler/sloc-counter/makefile | 71 | ||||
-rw-r--r-- | tests/compiler/sloc-counter/test.cxx | 34 | ||||
-rw-r--r-- | tests/compiler/sloc-counter/test.std | 36 |
9 files changed, 733 insertions, 1 deletions
diff --git a/cutl/compiler/code-stream.hxx b/cutl/compiler/code-stream.hxx new file mode 100644 index 0000000..bb8305a --- /dev/null +++ b/cutl/compiler/code-stream.hxx @@ -0,0 +1,153 @@ +// file : cutl/compiler/code-stream.hxx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : Copyright (c) 2009 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#ifndef CUTL_COMPILER_CODE_STREAM_HXX +#define CUTL_COMPILER_CODE_STREAM_HXX + +#include <memory> // std::auto_ptr +#include <ostream> + +namespace cutl +{ + namespace compiler + { + // + // + template <typename C> + class code_stream + { + public: + code_stream () {} + + virtual + ~code_stream (); + + public: + virtual void + put (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; + + private: + code_stream (code_stream const&); + + code_stream& + operator= (code_stream const&); + }; + + // + // + template <typename C> + class from_streambuf_adapter: public code_stream<C> + { + public: + typedef typename std::basic_streambuf<C>::traits_type traits_type; + typedef typename std::basic_streambuf<C>::int_type int_type; + + struct eof {}; + struct sync {}; + + public: + from_streambuf_adapter (std::basic_streambuf<C>& stream) + : stream_ (stream) + { + } + + private: + from_streambuf_adapter (from_streambuf_adapter const&); + + from_streambuf_adapter& + operator= (from_streambuf_adapter const&); + + public: + virtual void + put (C c); + + virtual void + unbuffer (); + + private: + std::basic_streambuf<C>& stream_; + }; + + // + // + template <typename C> + class to_streambuf_adapter: public std::basic_streambuf<C> + { + public: + typedef typename std::basic_streambuf<C>::traits_type traits_type; + typedef typename std::basic_streambuf<C>::int_type int_type; + + public: + to_streambuf_adapter (code_stream<C>& stream) + : stream_ (stream) + { + } + + private: + to_streambuf_adapter (to_streambuf_adapter const&); + + to_streambuf_adapter& + operator= (to_streambuf_adapter const&); + + public: + virtual int_type + overflow (int_type i); + + // Does nothing since calling unbuffer here would be dangerous. + // See the note in code_stream. + // + virtual int + sync (); + + private: + code_stream<C>& stream_; + }; + + // + // + template <template <typename> class S, typename C> + class ostream_filter + { + public: + typedef S<C> stream_type; + + ostream_filter (std::basic_ostream<C>& os); + ~ostream_filter (); + + stream_type& + stream () + { + return stream_; + } + + private: + ostream_filter (ostream_filter const&); + + ostream_filter& + operator= (ostream_filter const&); + + private: + std::basic_ostream<C>& os_; + std::basic_streambuf<C>* prev_; + from_streambuf_adapter<C> from_adapter_; + stream_type stream_; + to_streambuf_adapter<C> to_adapter_; + }; + } +} + +#include <cutl/compiler/code-stream.txx> + +#endif // CUTL_COMPILER_CODE_STREAM_HXX diff --git a/cutl/compiler/code-stream.txx b/cutl/compiler/code-stream.txx new file mode 100644 index 0000000..e834da7 --- /dev/null +++ b/cutl/compiler/code-stream.txx @@ -0,0 +1,96 @@ +// file : cutl/compiler/code-stream.txx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : Copyright (c) 2009 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +namespace cutl +{ + namespace compiler + { + // code_stream + // + + template <typename C> + code_stream<C>::~code_stream () + { + } + + // from_streambuf_adapter + // + + template <typename C> + void from_streambuf_adapter<C>:: + put (C c) + { + int_type i (stream_.sputc (c)); + + if (i == traits_type::eof ()) + throw eof (); + } + + template <typename C> + void from_streambuf_adapter<C>:: + unbuffer () + { + if (stream_.pubsync () != 0) + throw sync (); + } + + // to_streambuf_adapter + // + + template <typename C> + typename to_streambuf_adapter<C>::int_type to_streambuf_adapter<C>:: + overflow (int_type i) + { + try + { + stream_.put (traits_type::to_char_type (i)); + return i; + } + catch (typename from_streambuf_adapter<C>::eof const&) + { + return traits_type::eof (); + } + } + + template <typename C> + int to_streambuf_adapter<C>:: + sync () + { + return 0; + } + + // ostream_filter + // + + template <template <typename> class S, typename C> + ostream_filter<S, C>:: + ostream_filter (std::basic_ostream<C>& os) + : os_ (os), + prev_ (os_.rdbuf ()), + from_adapter_ (*prev_), + stream_ (from_adapter_), + to_adapter_ (stream_) + { + os_.rdbuf (&to_adapter_); + } + + template <template <typename> class S, typename C> + ostream_filter<S, C>:: + ~ostream_filter () + { + try + { + stream_.unbuffer (); + } + catch (...) + { + os_.rdbuf (prev_); + throw; + } + + os_.rdbuf (prev_); + } + } +} diff --git a/cutl/compiler/sloc-counter.hxx b/cutl/compiler/sloc-counter.hxx new file mode 100644 index 0000000..eb9e259 --- /dev/null +++ b/cutl/compiler/sloc-counter.hxx @@ -0,0 +1,80 @@ +// file : cutl/compiler/sloc-counter.hxx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : Copyright (c) 2009 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#ifndef CUTL_COMPILER_SLOC_COUNTER_HXX +#define CUTL_COMPILER_SLOC_COUNTER_HXX + +#include <cctype> // std::isspace +#include <cstddef> // std::size_t + +#include <cutl/compiler/code-stream.hxx> + +namespace cutl +{ + namespace compiler + { + template <typename C> + class sloc_counter: public code_stream<C> + { + public: + sloc_counter (code_stream<C>& out); + + std::size_t + count () const + { + return count_; + } + + private: + sloc_counter (sloc_counter const&); + + sloc_counter& + operator= (sloc_counter const&); + + public: + virtual void + put (C c); + + virtual void + unbuffer (); + + private: + void + code (C c); + + void + c_comment (C c); + + void + cxx_comment (C c); + + void + char_literal (C c); + + void + string_literal (C c); + + private: + code_stream<C>& out_; + std::size_t count_; + + C prev_; // Previous character or '\0'. + bool code_counted_; // This code line has already been counted. + + enum construct + { + con_code, + con_c_com, + con_cxx_com, + con_char_lit, + con_string_lit + } construct_; + }; + } +} + +#include <cutl/compiler/sloc-counter.txx> + +#endif // CUTL_COMPILER_SLOC_COUNTER_HXX diff --git a/cutl/compiler/sloc-counter.txx b/cutl/compiler/sloc-counter.txx new file mode 100644 index 0000000..9befac7 --- /dev/null +++ b/cutl/compiler/sloc-counter.txx @@ -0,0 +1,225 @@ +// file : cutl/compiler/sloc-counter.txx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : Copyright (c) 2009 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#include <cctype> // std::isspace + +namespace cutl +{ + namespace compiler + { + template <typename C> + sloc_counter<C>:: + sloc_counter (code_stream<C>& out) + : out_ (out), + count_ (0), + prev_ ('\0'), + code_counted_ (false), + construct_ (con_code) + { + } + + template <typename C> + void sloc_counter<C>:: + put (C c) + { + construct old (construct_); + + switch (construct_) + { + case con_code: + { + code (c); + break; + } + case con_c_com: + { + c_comment (c); + break; + } + case con_cxx_com: + { + cxx_comment (c); + break; + } + case con_char_lit: + { + char_literal (c); + break; + } + case con_string_lit: + { + 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'; + + out_.put (c); + } + + template <typename C> + void sloc_counter<C>:: + unbuffer () + { + } + + template <typename C> + void sloc_counter<C>:: + code (C c) + { + bool count (true); + + switch (c) + { + case '/': + { + if (prev_ == '/') + { + construct_ = con_cxx_com; + 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_ = con_c_com; + count = false; + } + break; + } + case '\'': + { + construct_ = con_char_lit; + break; + } + case '"': + { + construct_ = con_string_lit; + break; + } + case '\n': + { + code_counted_ = false; // Reset for a new line. + count = false; + break; + } + default: + { + if (std::isspace (c)) + count = false; + break; + } + } + + if (!code_counted_) + { + if (count) + { + count_++; + code_counted_ = true; + } + else if (prev_ == '/' && construct_ == con_code) + { + // This condition accounts for the fact that we cannot count + // '/' right away since it can be a beginning of a comment. + // + count_++; + code_counted_ = (c != '\n'); + } + } + } + + template <typename C> + void sloc_counter<C>:: + c_comment (C c) + { + switch (c) + { + case '/': + { + if (prev_ == '*') + construct_ = con_code; + break; + } + case '\n': + { + code_counted_ = false; // Reset for a new line. + break; + } + } + } + + template <typename C> + void sloc_counter<C>:: + cxx_comment (C c) + { + switch (c) + { + case '\n': + { + construct_ = con_code; + code_counted_ = false; // Reset for a new line. + break; + } + } + } + + template <typename C> + void sloc_counter<C>:: + char_literal (C c) + { + switch (c) + { + case '\'': + { + if (prev_ != '\\') + construct_ = con_code; + break; + } + } + } + + template <typename C> + void sloc_counter<C>:: + string_literal (C c) + { + switch (c) + { + case '"': + { + if (prev_ != '\\') + construct_ = con_code; + break; + } + case '\n': + { + count_++; + break; + } + } + } + } +} diff --git a/tests/compiler/makefile b/tests/compiler/makefile index 5a63fc8..42fd2bf 100644 --- a/tests/compiler/makefile +++ b/tests/compiler/makefile @@ -5,7 +5,7 @@ include $(dir $(lastword $(MAKEFILE_LIST)))../../build/bootstrap.make -tests := traversal +tests := sloc-counter traversal default := $(out_base)/ test := $(out_base)/.test diff --git a/tests/compiler/sloc-counter/driver.cxx b/tests/compiler/sloc-counter/driver.cxx new file mode 100644 index 0000000..b5d6462 --- /dev/null +++ b/tests/compiler/sloc-counter/driver.cxx @@ -0,0 +1,37 @@ +// file : tests/compiler/sloc-counter/driver.cxx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : Copyright (c) 2009 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#include <fstream> +#include <iostream> + +#include <cutl/compiler/code-stream.hxx> +#include <cutl/compiler/sloc-counter.hxx> + +using namespace std; +using namespace cutl::compiler; + +int +main (int argc, char* argv[]) +{ + if (argc != 2) + { + cerr << "usage: " << argv[0] << " <file>" << endl; + return 1; + } + + ostream_filter<sloc_counter, char> filt (cout); + + ifstream ifs(argv[1]); + + for (istream::int_type c (ifs.get ()); + c != istream::traits_type::eof (); + c = ifs.get ()) + { + cout.put (istream::traits_type::to_char_type (c)); + } + + cout << endl + << filt.stream ().count () << endl; +} diff --git a/tests/compiler/sloc-counter/makefile b/tests/compiler/sloc-counter/makefile new file mode 100644 index 0000000..498845f --- /dev/null +++ b/tests/compiler/sloc-counter/makefile @@ -0,0 +1,71 @@ +# file : tests/compiler/sloc-counter/makefile +# author : Boris Kolpackov <boris@codesynthesis.com> +# copyright : Copyright (c) 2009 Code Synthesis Tools CC +# license : MIT; see accompanying LICENSE file + +include $(dir $(lastword $(MAKEFILE_LIST)))../../../build/bootstrap.make + +cxx_tun := driver.cxx + +# +# +cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o)) +cxx_od := $(cxx_obj:.o=.o.d) + +cutl.l := $(out_root)/cutl/cutl.l +cutl.l.cpp-options := $(out_root)/cutl/cutl.l.cpp-options + +driver := $(out_base)/driver +test := $(out_base)/.test +clean := $(out_base)/.clean + +# Build. +# +$(driver): $(cxx_obj) $(cutl.l) +$(cxx_obj) $(cxx_od): $(cutl.l.cpp-options) + + +$(call include-dep,$(cxx_od)) + + +# Alias for default target. +# +$(out_base)/: $(driver) + + +# Test. +# +$(test): driver := $(driver) +$(test): $(driver) $(src_base)/test.cxx $(src_base)/test.std + $(call message,test $$1,$$1 $(src_base)/test.cxx | diff -u $(src_base)/test.std -,$(driver)) + + +# Clean. +# +$(clean): \ + $(driver).o.clean \ + $(addsuffix .cxx.clean,$(cxx_obj)) \ + $(addsuffix .cxx.clean,$(cxx_od)) + + +# Generated .gitignore. +# +ifeq ($(out_base),$(src_base)) +$(driver): | $(out_base)/.gitignore + +$(out_base)/.gitignore: files := driver +$(clean): $(out_base)/.gitignore.clean + +$(call include,$(bld_root)/git/gitignore.make) +endif + + +# How to. +# +$(call include,$(bld_root)/cxx/o-e.make) +$(call include,$(bld_root)/cxx/cxx-o.make) +$(call include,$(bld_root)/cxx/cxx-d.make) + +# Dependencies. +# +$(call import,$(src_root)/cutl/makefile) diff --git a/tests/compiler/sloc-counter/test.cxx b/tests/compiler/sloc-counter/test.cxx new file mode 100644 index 0000000..ff0f5b2 --- /dev/null +++ b/tests/compiler/sloc-counter/test.cxx @@ -0,0 +1,34 @@ +// C++ comment + // C++ comment + +/* C comment */ + +/* Multiline + C + Comment + + +*/ + +#include <iostream> + +char str[] = "multi\ +line\ +string\ +literal"; + +using namespace std; + + +int main( + int argc /*count*/, + char* argv[] /*array*/) +{ + /* comment start */ int x = 0; + char* s = + /* comment start */"foo"; + int y = 2 + /* tricky stuff *// + 2; + cerr << "Hello, \"world!" << '\'' << endl; +} diff --git a/tests/compiler/sloc-counter/test.std b/tests/compiler/sloc-counter/test.std new file mode 100644 index 0000000..00b9c31 --- /dev/null +++ b/tests/compiler/sloc-counter/test.std @@ -0,0 +1,36 @@ +// C++ comment + // C++ comment + +/* C comment */ + +/* Multiline + C + Comment + + +*/ + +#include <iostream> + +char str[] = "multi\ +line\ +string\ +literal"; + +using namespace std; + + +int main( + int argc /*count*/, + char* argv[] /*array*/) +{ + /* comment start */ int x = 0; + char* s = + /* comment start */"foo"; + int y = 2 + /* tricky stuff *// + 2; + cerr << "Hello, \"world!" << '\'' << endl; +} + +18 |