From 0737afc0028e0abc9d451c34fc1e4e176fc66503 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Thu, 8 Aug 2024 17:12:33 -0400 Subject: [PATCH] Add line-comment filter for streambuf. --- IOStreams/CMakeLists.txt | 1 + IOStreams/LineComments.h | 170 +++++++++++++++++++++ IOStreams/LineComments.test/0.cc | 39 +++++ IOStreams/LineComments.test/CMakeLists.txt | 1 + 4 files changed, 211 insertions(+) create mode 100644 IOStreams/LineComments.h create mode 100644 IOStreams/LineComments.test/0.cc create mode 100644 IOStreams/LineComments.test/CMakeLists.txt diff --git a/IOStreams/CMakeLists.txt b/IOStreams/CMakeLists.txt index f5f707a..fadae32 100644 --- a/IOStreams/CMakeLists.txt +++ b/IOStreams/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory( IStreamable.test ) add_subdirectory( OStreamable.test ) add_subdirectory( Streamable.test ) add_subdirectory( LineTrackingStreambuf.test ) +add_subdirectory( LineComments.test ) add_subdirectory( OutUnixFileBuf.test ) add_subdirectory( StackableStreambuf.test ) add_subdirectory( Filter.test ) diff --git a/IOStreams/LineComments.h b/IOStreams/LineComments.h new file mode 100644 index 0000000..f41b478 --- /dev/null +++ b/IOStreams/LineComments.h @@ -0,0 +1,170 @@ +static_assert( __cplusplus > 2023'00 ); + +#pragma once + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include "StackableStreambuf.h" +#include "StreamState.h" + +namespace Alepha::Hydrogen::IOStreams ::detail:: LineComments_m +{ + namespace C + { + const bool debug= false; + const bool debugUnderflow= false or C::debug; + } + + inline namespace exports {} + + struct LineComments_params {}; + + namespace exports + { + struct LineCommentsStreambuf; + + using DropComments= IOStreams::PushStack< LineComments_params >; + } + + struct exports::LineCommentsStreambuf + : public virtual StackableStreambuf, public virtual std::streambuf + { + private: + enum { Active, Commented } state= Active; + + public: + explicit + LineCommentsStreambuf( std::ios &is ) + : StackableStreambuf( is ) + {} + + void writeChar( char ch ) override { throw "Unimpl"; } + void drain() override { throw "Unimpl"; } + + int + underflow() override + { + std::string_view view{ gptr(), egptr() }; + while( view.empty() ) + { + if( gptr() == nullptr or gptr() >= underlying_egptr() ) + { + if( C::debugUnderflow ) + { + Alepha::error() << "Current view of buffer is empty." << std::endl; + } + const auto rv= forwardUnderflow(); + if( C::debugUnderflow ) + { + Alepha::error() << "Underlying underflow returned `" << rv << "`" << std::endl; + } + if( rv == EOF ) + { + Alepha::error() << "Returning EOF from initial-kind read." << std::endl; + setg( nullptr, nullptr, nullptr ); + return rv; + } + view= { underlying_gptr(), underlying_egptr() }; + } + + while( state == Commented ) + { + if( C::debugUnderflow ) + { + Alepha::error() << "Current state is `Commented`, underlying view peek is `" << view << "`" << std::endl; + } + while( not view.empty() ) + { + if( C::debugUnderflow ) + { + Alepha::error() << "Examining: `" << view.front() << "`" << std::endl; + } + if( view.front() == '\n' ) + { + if( C::debugUnderflow ) + { + Alepha::error() << "Found end of line." << std::endl; + } + state= Active; + break; + } + underlying_sbumpc(); + view.remove_prefix( 1 ); + } + if( state == Active ) break; + if( C::debugUnderflow ) + { + Alepha::error() << "Underlying view exhausted, getting more..." << std::endl; + } + const auto rv= forwardUnderflow(); + if( C::debugUnderflow ) + { + Alepha::error() << "Underlying view underflow returned `" << rv << "`" << std::endl; + } + if( rv == EOF ) + { + Alepha::error() << "Returning EOF from mid-comment read." << std::endl; + setg( nullptr, nullptr, nullptr ); + return rv; + } + view= { underlying_gptr(), underlying_egptr() }; + } + assert( state == Active ); + assert( not view.empty() ); + + if( C::debugUnderflow ) + { + Alepha::error() << "Current state is `Active`, underlying view peek is `" << view << "`" << std::endl; + } + + for( std::size_t i= 0; i < view.size(); ++i ) + { + if( C::debugUnderflow ) + { + Alepha::error() << "Looking at `" << view.at( i ) << "`'" << std::endl; + } + underlying_sbumpc(); + if( view.at( i ) == '#' ) + { + view= view.substr( 0, i ); + state= Commented; + break; + } + } + + if( C::debugUnderflow ) + { + Alepha::error() << "Current state is `Active`, setting underlying view to `" << view << "`" << std::endl; + } + setg( (char *) view.data(), (char *) view.data(), (char *) view.data() + view.size() ); + } + + if( C::debugUnderflow ) + { + Alepha::error() << "Normal return with first char being `" << view.front() << "`" << std::endl; + } + return view.front(); + } + }; + + inline void + build_streambuf( std::istream &is, DropComments && ) + { + new LineCommentsStreambuf{ is }; + } +} + +namespace Alepha::Hydrogen::IOStreams::inline exports::inline LineComments_m +{ + using namespace detail::LineComments_m::exports; +} diff --git a/IOStreams/LineComments.test/0.cc b/IOStreams/LineComments.test/0.cc new file mode 100644 index 0000000..f3535df --- /dev/null +++ b/IOStreams/LineComments.test/0.cc @@ -0,0 +1,39 @@ +#include "../LineComments.h" + +#include + +#include +#include + +#include + + + +static auto +stream( const std::string &text ) +{ + std::istringstream iss{ text }; + + iss >> Alepha::IOStreams::DropComments{}; + + std::string s; + getline( iss, s, {} ); + + return s; +} + +static auto init= Alepha::Utility::enroll <=[] +{ + using namespace Alepha::Testing::exports; + + "Do we strip comments correctly"_test <=TableTest< stream > + ::Cases + { + { "Simple (no comment)", { "Hello World!" }, "Hello World!" }, + { "Simple (One comment)", { "Hello World!# Comment" }, "Hello World!" }, + { "Two lines (One comment at end)", { "Hello World!\nGoodbye World! # Comment" }, "Hello World!\nGoodbye World! " }, + { "Two lines (One comment after first)", { "Hello World! #Comment\nGoodbye World!" }, "Hello World! \nGoodbye World!" }, + { "Two lines (Two comments)", { "Hello World! #Comment\nGoodbye World! #Comment" }, "Hello World! \nGoodbye World! " }, + { "Two lines (Two comments, extra newline)", { "Hello World! #Comment\nGoodbye World! #Comment\n" }, "Hello World! \nGoodbye World! \n" }, + }; +}; diff --git a/IOStreams/LineComments.test/CMakeLists.txt b/IOStreams/LineComments.test/CMakeLists.txt new file mode 100644 index 0000000..b099603 --- /dev/null +++ b/IOStreams/LineComments.test/CMakeLists.txt @@ -0,0 +1 @@ +unit_test( 0 )