[DPL] AbstractInputReader
authorTomasz Iwanek <t.iwanek@samsung.com>
Mon, 15 Jul 2013 06:35:15 +0000 (08:35 +0200)
committerSoo-Hyun Choi <sh9.choi@samsung.com>
Tue, 22 Oct 2013 07:34:15 +0000 (16:34 +0900)
[Issue#]   LINUXWRT-639
[Problem]  Base classes for parser reading for output of program
[Cause]    N/A
[Solution] Base class for parser input.

[Remarks]
    This generic skeleton for parser which assumes being composed from abstract
    two logical components:
      - parser,
      - tokenizer/lexer,
    which implements token flow logic.
    Logic of components may be arbitrary. See depending change for uses.

    Components are created at start time of reader (constructor which moves arguments).
    Virtuality (abstract base classes) are for enforcing same token type.
    I assumed it's more clear than writen static asserts in code enforcing this.

[Verification]
    - Build with tests and WITH_CHILD ON.
    - $> wrt-commons-tests-test --output=text --regexp='AbstractInputReader_'

Change-Id: Id5262a69945670ddf072e393c43d48ee6fc64e06

modules/test/config.cmake
modules/test/include/dpl/test/abstract_input_parser.h [new file with mode: 0644]
modules/test/include/dpl/test/abstract_input_reader.h [new file with mode: 0644]
modules/test/include/dpl/test/abstract_input_tokenizer.h [new file with mode: 0644]
tests/test/CMakeLists.txt
tests/test/test_abstract_input_reader.cpp [new file with mode: 0644]

index 7938eaf..ec4298f 100644 (file)
@@ -35,6 +35,9 @@ SET(DPL_TEST_ENGINE_HEADERS
     ${PROJECT_SOURCE_DIR}/modules/test/include/dpl/test/test_runner_child.h
     ${PROJECT_SOURCE_DIR}/modules/test/include/dpl/test/test_runner_multiprocess.h
     ${PROJECT_SOURCE_DIR}/modules/test/include/dpl/test/process_pipe.h
+    ${PROJECT_SOURCE_DIR}/modules/test/include/dpl/test/abstract_input_parser.h
+    ${PROJECT_SOURCE_DIR}/modules/test/include/dpl/test/abstract_input_reader.h
+    ${PROJECT_SOURCE_DIR}/modules/test/include/dpl/test/abstract_input_tokenizer.h
     PARENT_SCOPE
 )
 
diff --git a/modules/test/include/dpl/test/abstract_input_parser.h b/modules/test/include/dpl/test/abstract_input_parser.h
new file mode 100644 (file)
index 0000000..dcb2243
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+/*
+ * @file        abstract_input_parser.h
+ * @author      Tomasz Iwanek (t.iwanek@samsung.com)
+ * @brief       Simple parser abstraction to be included into reader
+ */
+
+#ifndef ABSTRACT_INPUT_PARSER_H
+#define ABSTRACT_INPUT_PARSER_H
+
+#include <dpl/exception.h>
+
+#include <memory>
+
+namespace DPL {
+
+/**
+ * Abstract class of parser that produces some higher level abstraction
+ * basing on incoming tokens
+ */
+template<class Result, class Token> class AbstractInputParser
+{
+public:
+    class Exception
+    {
+    public:
+        DECLARE_EXCEPTION_TYPE(DPL::Exception, Base)
+        DECLARE_EXCEPTION_TYPE(Base, ParserError)
+    };
+
+    typedef Result ResultType;
+    typedef Token TokenType;
+
+    virtual ~AbstractInputParser() {}
+
+    virtual void ConsumeToken(std::unique_ptr<Token> && token) = 0;
+    virtual bool IsStateValid() = 0;
+    virtual Result GetResult() const = 0;
+};
+
+}
+
+#endif
diff --git a/modules/test/include/dpl/test/abstract_input_reader.h b/modules/test/include/dpl/test/abstract_input_reader.h
new file mode 100644 (file)
index 0000000..6b23dd6
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+/*
+ * @file        abstract_input_reader.h
+ * @author      Tomasz Iwanek (t.iwanek@samsung.com)
+ * @brief       Simple output reader template
+ *
+ * This generic skeleton for parser which assume being composed from abstract two logical components:
+ *
+ *  -  parser,
+ *  -  tokenizer/lexer,
+ *     which implements token flow logic. Logic of components may be arbitrary. See depending change for uses.
+ *
+ * Components are created at start time of reader (constructor which moves arguments).
+ * Virtuality (abstract base classes) are for enforcing same token type.
+ * I assumed it's more clear than writen static asserts in code enforcing this.
+ */
+
+#ifndef ABSTRACT_INPUT_READER_H
+#define ABSTRACT_INPUT_READER_H
+
+#include <memory>
+
+#include <dpl/optional.h>
+#include <dpl/test/abstract_input_tokenizer.h>
+#include <dpl/test/abstract_input_parser.h>
+#include <dpl/abstract_input.h>
+
+namespace DPL {
+
+/**
+ * Base reader class that can be used with any AbstractInput instance
+ *
+ * This class is encapsulation class for tokenizer and reader subelements
+ * and contains basic calculation pattern
+ *
+ * There a waste in form of virtuality for parser and tokenizer
+ * -> this for forcing same tokenT type in both components
+ */
+template<class ResultT, class TokenT> class AbstractInputReader
+{
+public:
+    typedef ResultT TokenType;
+    typedef TokenT ResultType;
+    typedef AbstractInputParser<ResultT, TokenT> ParserBase;
+    typedef AbstractInputTokenizer<TokenT> TokenizerBase;
+
+    class Exception
+    {
+    public:
+        typedef typename TokenizerBase::Exception::TokenizerError TokenizerError;
+        typedef typename ParserBase::Exception::ParserError ParserError;
+    };
+
+    AbstractInputReader(std::shared_ptr<AbstractInput> ia,
+                        std::unique_ptr<ParserBase> && parser,
+                        std::unique_ptr<TokenizerBase> && tokenizer)
+        : m_parser(std::move(parser)), m_tokenizer(std::move(tokenizer))
+    {
+        m_tokenizer->Reset(ia);
+    }
+
+    virtual ~AbstractInputReader() {}
+
+    ResultT ReadInput()
+    {
+        typedef typename Exception::TokenizerError TokenizerError;
+        typedef typename Exception::ParserError ParserError;
+
+        while(true)
+        {
+            std::unique_ptr<TokenT> token = m_tokenizer->GetNextToken();
+            if(!token)
+            {
+                if(!m_tokenizer->IsStateValid())
+                {
+                    ThrowMsg(TokenizerError, "Tokenizer error");
+                }
+                if(!m_parser->IsStateValid())
+                {
+                    ThrowMsg(ParserError, "Parser error");
+                }
+
+                return m_parser->GetResult();
+            }
+            m_parser->ConsumeToken(std::move(token));
+        }
+    }
+
+protected:
+    std::unique_ptr<ParserBase> m_parser;
+    std::unique_ptr<TokenizerBase> m_tokenizer;
+};
+
+}
+
+#endif
diff --git a/modules/test/include/dpl/test/abstract_input_tokenizer.h b/modules/test/include/dpl/test/abstract_input_tokenizer.h
new file mode 100644 (file)
index 0000000..a376341
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+/*
+ * @file        abstract_input_tokenizer.h
+ * @author      Tomasz Iwanek (t.iwanek@samsung.com)
+ * @brief       Simple tokenizer abstraction
+ */
+
+#ifndef ABSTRACT_INPUT_TOKENIZER_H
+#define ABSTRACT_INPUT_TOKENIZER_H
+
+#include <memory>
+#include <string>
+
+#include <dpl/abstract_input.h>
+#include <dpl/optional.h>
+#include <dpl/exception.h>
+
+namespace DPL {
+
+/**
+ * Tokenizer abstract base class
+ *
+ * This class is supposed to accept AbstractInput in constructor
+ * and produce tokens until end of source. If parsing ends in invalid state
+ * then IsStateValid() should return false
+ */
+template<class Token> class AbstractInputTokenizer
+{
+public:
+    class Exception
+    {
+    public:
+        DECLARE_EXCEPTION_TYPE(DPL::Exception, Base)
+        DECLARE_EXCEPTION_TYPE(Base, TokenizerError)
+    };
+
+    typedef Token TokenType;
+
+    AbstractInputTokenizer() {}
+    virtual ~AbstractInputTokenizer() {}
+
+    /**
+     * @brief Reset resets data source
+     * @param wia AbstractWaitableInputAdapter instance
+     */
+    virtual void Reset(std::shared_ptr<AbstractInput> wia)
+    {
+        m_input = wia;
+    }
+
+    /**
+     * @brief GetNextToken
+     *
+     * Parses next token.
+     * Returns pointer to token
+     * @throw TokenizerError in condition of input source error
+     * If returned empty pointer IsStateValid() == true -> end of input
+     *                           IsStateValid() == false -> error
+     *
+     * @param token token to be set
+     * @return
+     */
+    virtual std::unique_ptr<Token> GetNextToken() = 0;
+    virtual bool IsStateValid() = 0;
+
+protected:
+    std::shared_ptr<AbstractInput> m_input;
+};
+
+}
+
+#endif
index 500e52b..639ebbd 100644 (file)
@@ -26,6 +26,7 @@ SET(DPL_TESTS_UTIL_SOURCES
     ${TESTS_DIR}/test/runner_multiprocess.cpp
     ${TESTS_DIR}/test/runner_child.cpp
     ${TESTS_DIR}/test/test_process_pipe.cpp
+    ${TESTS_DIR}/test/test_abstract_input_reader.cpp
 )
 
 #WRT_TEST_ADD_INTERNAL_DEPENDENCIES(${TARGET_NAME} ${TARGET_DPL_UTILS_EFL})
diff --git a/tests/test/test_abstract_input_reader.cpp b/tests/test/test_abstract_input_reader.cpp
new file mode 100644 (file)
index 0000000..8e74faa
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+/*
+ * @file        test_abstract_input_reader.h
+ * @author      Tomasz Iwanek (t.iwanek@samsung.com)
+ * @brief       tests for AbstractInputReader
+ */
+
+#include <cstring>
+#include <string>
+
+#include <dpl/test/abstract_input_reader.h>
+#include <dpl/abstract_input.h>
+#include <dpl/test/test_runner.h>
+#include <dpl/binary_queue.h>
+#include <dpl/optional.h>
+
+using namespace DPL;
+
+namespace {
+
+// TOKENIZER
+
+class DigitTokenizer : public AbstractInputTokenizer<int>
+{
+public:
+    DigitTokenizer() : m_valid(true) {}
+
+    std::unique_ptr<int> GetNextToken()
+    {
+        typedef AbstractInputTokenizer<int>::Exception::TokenizerError TokenizerError;
+
+        std::unique_ptr<int> token;
+
+        char buffer;
+        BinaryQueueAutoPtr baptr = m_input->Read(1); //not effective but it's test...
+        if(baptr.get() == NULL)
+        {
+            ThrowMsg(TokenizerError, "Input reading failed");
+        }
+        if(baptr->Empty()) //end of source
+        {
+            return token;
+        }
+        baptr->FlattenConsume(&buffer,1);
+        if(!isdigit(buffer))
+        {
+            ThrowMsg(TokenizerError, "Input source contains no digit characters/bytes");
+        }
+        token.reset(new int(static_cast<int>(buffer)));
+        return token;
+    }
+
+    void Reset(std::shared_ptr<AbstractInput> ia)
+    {
+        AbstractInputTokenizer<int>::Reset(ia);
+        m_valid = true;
+    }
+
+    bool IsStateValid()
+    {
+        return true;
+    }
+
+private:
+    bool m_valid;
+};
+
+// PARSER
+
+class SumatorParser : public AbstractInputParser<int, int>
+{
+public:
+    SumatorParser() : m_sum(0) {}
+
+    void ConsumeToken(std::unique_ptr<int> && token)
+    {
+        m_sum += (*token - '0');
+    }
+
+    bool IsStateValid()
+    {
+        return true;
+    }
+
+    int GetResult() const
+    {
+        return m_sum;
+    }
+
+private:
+    int m_sum;
+};
+
+// READER
+
+class Sumator : public AbstractInputReader<int, int>
+{
+public:
+    Sumator(std::shared_ptr<AbstractInput> ia)
+        : AbstractInputReader<int, int>(ia,
+            std::unique_ptr<ParserBase>(new SumatorParser()),
+            std::unique_ptr<TokenizerBase>(new DigitTokenizer()))
+    {}
+};
+
+}
+
+RUNNER_TEST_GROUP_INIT(AbstractInputReader)
+
+RUNNER_TEST(AbstractInputReader_ByteSumatorInstance_Sum)
+{
+    const std::string data("1234567890");
+    std::shared_ptr<AbstractInput> mem(new BinaryQueue());
+    dynamic_cast<BinaryQueue*>(mem.get())->AppendCopy(data.c_str(), data.size());
+    Sumator sum(mem);
+    int result = sum.ReadInput();
+    RUNNER_ASSERT_MSG(result == 45, "Sum is invalid");
+}
+
+RUNNER_TEST(AbstractInputReader_ByteSumatorInstance_Exception)
+{
+    const std::string data("12345string90");
+    std::shared_ptr<AbstractInput> mem(new BinaryQueue());
+    dynamic_cast<BinaryQueue*>(mem.get())->AppendCopy(data.c_str(), data.size());
+    Sumator sum(mem);
+    Try
+    {
+        sum.ReadInput();
+    }
+    Catch(Sumator::Exception::TokenizerError)
+    {
+        return;
+    }
+    RUNNER_ASSERT_MSG(false, "Tokenizer exception should be thrown");
+}