aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cutl/compiler/code-stream.hxx153
-rw-r--r--cutl/compiler/code-stream.txx96
-rw-r--r--cutl/compiler/sloc-counter.hxx80
-rw-r--r--cutl/compiler/sloc-counter.txx225
-rw-r--r--tests/compiler/makefile2
-rw-r--r--tests/compiler/sloc-counter/driver.cxx37
-rw-r--r--tests/compiler/sloc-counter/makefile71
-rw-r--r--tests/compiler/sloc-counter/test.cxx34
-rw-r--r--tests/compiler/sloc-counter/test.std36
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