diff --git a/include/ebcl/SyntaxTree.hh b/include/ebcl/SyntaxTree.hh new file mode 100644 index 0000000..409e486 --- /dev/null +++ b/include/ebcl/SyntaxTree.hh @@ -0,0 +1,119 @@ +/******************************************************************************/ +/* ABSTRACT SYNTAX TREE *******************************************************/ +/******************************************************************************/ + +#ifndef _H_EBCL_SYNTAXTREE +#define _H_EBCL_SYNTAXTREE +#include +#include +namespace ebcl { + + +/*= SYNTAX TREE ==============================================================*/ + +/* + * A syntax tree has typed nodes; each nodes may have 0, 1 or more children + * of various types. In addition, a node may carry additional data. The type + * of the data associated to a given node is not known. + * + * Nodes are accessed using integer indices. The tree's root node is always at + * index 0. + */ +template< + // Node type (should be an enum) + typename NodeType , + // Data storage size + size_t InPlaceSize = sizeof( void* ) +> class T_SyntaxTree +{ + public: + using E_Node = NodeType; + using T_Self = T_SyntaxTree< NodeType , InPlaceSize >; + static constexpr size_t StorageSize + = T_VariantExt< InPlaceSize >::StorageSize; + + private: + + struct T_Node_ + { + // Node type + E_Node type; + // Index of the parent node + uint32_t parent; + // Indices of the node's children + T_AutoArray< uint32_t , 8 > children; + // Extra data storage + T_VariantExt< StorageSize > data; + + explicit T_Node_( E_Node type , + uint32_t parent ) noexcept; + + T_Node_( T_Node_ const& ) = delete; + T_Node_& operator =( T_Node_ const& ) = delete; + + T_Node_( T_Node_&& other ) noexcept; + T_Node_& operator =( T_Node_&& other ) noexcept; + }; + + T_Array< T_Node_ > nodes_; + + public: + M_TEMPLATE_POINTERS( T_SyntaxTree ); + + // Create the syntax tree, specifying the type of the root node + explicit T_SyntaxTree( E_Node rootType ) noexcept; + + // Copy is disabled + T_SyntaxTree( T_Self const& ) = delete; + T_Self& operator =( T_Self const& other ) = delete; + + // Move construction / assignment + T_SyntaxTree( T_Self&& other ) noexcept; + T_Self& operator =( T_Self&& other ) noexcept; + + // Swapping + T_Self& swap( T_Self& other ) noexcept; + + //---------------------------------------------------------------------- + + // Get the type of a node + E_Node typeOf( uint32_t node ) const noexcept; + + // Returns the index of a node's parent. The root node will always + // return its own index. + uint32_t parentOf( uint32_t node ) const noexcept; + + // Access the list of children for a node + T_AutoArray< uint32_t , 8 > const& childrenOf( + uint32_t node ) const noexcept; + + // Check whether some node contains data + bool hasData( uint32_t node ) const noexcept; + + // Access the data stored inside a node + template< typename D > + D const& dataOf( uint32_t node ) const; + + //---------------------------------------------------------------------- + + // Add a new child, return its index + uint32_t addChild( + uint32_t parent , + E_Node type ) noexcept; + + // Add data to a node + template< typename D , typename ... Args > + D& newData( uint32_t node , Args&& ... args ); + + // Access the data stored inside a node + template< typename D > + D& dataOf( uint32_t node ); +}; + +template< typename T , size_t S > +void swap( T_SyntaxTree< T , S >& lhs , T_SyntaxTree< T , S >& rhs ) noexcept; + + +} +#endif // _H_EBCL_SYNTAXTREE +#include diff --git a/include/ebcl/inline/SyntaxTree.hh b/include/ebcl/inline/SyntaxTree.hh new file mode 100644 index 0000000..8a7122b --- /dev/null +++ b/include/ebcl/inline/SyntaxTree.hh @@ -0,0 +1,162 @@ +/******************************************************************************/ +/* ABSTRACT SYNTAX TREE - INLINE CODE******************************************/ +/******************************************************************************/ + +#ifndef _H_EBCL_INLINE_SYNTAXTREE +#define _H_EBCL_INLINE_SYNTAXTREE +#include +namespace ebcl { + + +/*= T_SyntaxTree =============================================================*/ + +template< typename T , size_t S > +T_SyntaxTree< T , S >::T_Node_::T_Node_( + const T nodeType , + const uint32_t parent ) noexcept + : type{ nodeType } , parent{ parent } +{ + // EMPTY +} + +template< typename T , size_t S > +T_SyntaxTree< T , S >::T_Node_::T_Node_( + T_Node_&& other ) noexcept + : type{ other.type } , parent{ other.parent } , + children{ std::move( other.children ) } , + data{ std::move( other.data ) } +{ + // EMPTY +} + +template< typename T , size_t S > +typename T_SyntaxTree< T , S >::T_Node_& T_SyntaxTree< T , S >::T_Node_::operator =( + T_Node_&& other ) noexcept +{ + type = other.type; + parent = other.parent; + children = std::move( other.children ); + data = std::move( other.data ); + return *this; +} + +/*----------------------------------------------------------------------------*/ + +template< typename T , size_t S > +T_SyntaxTree< T , S >::T_SyntaxTree( + const T rootType ) noexcept + : nodes_( 64 ) +{ + nodes_.addNew( rootType , 0 ); +} + +/*----------------------------------------------------------------------------*/ + +template< typename T , size_t S > +T_SyntaxTree< T , S >::T_SyntaxTree( + T_SyntaxTree< T , S >&& other ) noexcept + : nodes_{ std::move( other.nodes_ ) } +{ + // EMPTY +} + +template< typename T , size_t S > +T_SyntaxTree< T , S >& T_SyntaxTree< T , S >::operator =( + T_SyntaxTree< T , S >&& other ) noexcept +{ + nodes_ = std::move( other.nodes_ ); + return *this; +} + +/*----------------------------------------------------------------------------*/ + +template< typename T , size_t S > +T_SyntaxTree< T , S >& T_SyntaxTree< T , S >::swap( + T_SyntaxTree< T , S >& other ) noexcept +{ + using std::swap; + swap( nodes_ , other.nodes_ ); + return *this; +} + +template< typename T , size_t S > +void swap( T_SyntaxTree< T , S >& lhs , + T_SyntaxTree< T , S >& rhs ) noexcept +{ + lhs.swap( rhs ); +} + +/*----------------------------------------------------------------------------*/ + +template< typename T , size_t S > +T T_SyntaxTree< T , S >::typeOf( + const uint32_t node ) const noexcept +{ + return nodes_[ node ].type; +} + +template< typename T , size_t S > +uint32_t T_SyntaxTree< T , S >::parentOf( + const uint32_t node ) const noexcept +{ + return nodes_[ node ].parent; +} + +template< typename T , size_t S > +T_AutoArray< uint32_t , 8 > const& T_SyntaxTree< T , S >::childrenOf( + const uint32_t node ) const noexcept +{ + return nodes_[ node ].children; +} + +template< typename T , size_t S > +bool T_SyntaxTree< T , S >::hasData( + const uint32_t node ) const noexcept +{ + return bool( nodes_[ node ].data ); +} + +template< typename T , size_t S > +template< typename D > +D const& T_SyntaxTree< T , S >::dataOf( + const uint32_t node ) const +{ + return nodes_[ node ].data.template value< D >( ); +} + +/*----------------------------------------------------------------------------*/ + +template< typename T , size_t S > +uint32_t T_SyntaxTree< T , S >::addChild( + const uint32_t parent , + const E_Node type ) noexcept +{ + T_Node_ node{ type , parent }; + const auto idx{ nodes_.add( std::move( node ) ) }; + assert( idx != parent ); + nodes_[ parent ].children.add( idx ); + return idx; +} + +template< typename T , size_t S > +template< typename D , typename ... ArgTypes > +D& T_SyntaxTree< T , S >::newData( + const uint32_t node , + ArgTypes&& ... args ) +{ + auto& data{ nodes_[ node ].data }; + data.template setNew< D >( std::forward< ArgTypes >( args ) ... ); + return data.template value< D >( ); +} + +template< typename T , size_t S > +template< typename D > +D& T_SyntaxTree< T , S >::dataOf( + const uint32_t node ) +{ + return nodes_[ node ].data.template value< D >( ); +} + + +} // namespace +#endif // _H_EBCL_INLINE_SYNTAXTREE diff --git a/tests/list.mk b/tests/list.mk index 53fc34b..03190b7 100644 --- a/tests/list.mk +++ b/tests/list.mk @@ -33,4 +33,5 @@ TESTS = \ srd-parser-defs \ srd-parser-cfg \ srd-parser \ + syntax-tree \ # END diff --git a/tests/syntax-tree.cc b/tests/syntax-tree.cc new file mode 100644 index 0000000..e6d9c6e --- /dev/null +++ b/tests/syntax-tree.cc @@ -0,0 +1,77 @@ +#include +#include +using namespace ebcl; + +class SyntaxTreeTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE( SyntaxTreeTest ); + CPPUNIT_TEST( testEmpty ); + CPPUNIT_TEST( testSetData ); + CPPUNIT_TEST( testUpdateData ); + CPPUNIT_TEST( testAddChildren ); + CPPUNIT_TEST_SUITE_END( ); + +public: + void testEmpty( ); + void testSetData( ); + void testUpdateData( ); + void testAddChildren( ); +}; +CPPUNIT_TEST_SUITE_REGISTRATION( SyntaxTreeTest ); + +/*----------------------------------------------------------------------------*/ + +enum class E_TestNode_ +{ + ROOT , + ADD , + VALUE +}; + +using T_Test_ = T_SyntaxTree< E_TestNode_ >; + +/*----------------------------------------------------------------------------*/ + +void SyntaxTreeTest::testEmpty( ) +{ + T_Test_ st{ E_TestNode_::ROOT }; + CPPUNIT_ASSERT( E_TestNode_::ROOT == st.typeOf( 0 ) ); + CPPUNIT_ASSERT( st.childrenOf( 0 ).empty( ) ); + CPPUNIT_ASSERT_EQUAL( 0u , st.parentOf( 0 ) ); + CPPUNIT_ASSERT( !st.hasData( 0 ) ); +} + +void SyntaxTreeTest::testSetData( ) +{ + T_Test_ st{ E_TestNode_::ROOT }; + CPPUNIT_ASSERT( E_TestNode_::ROOT == st.typeOf( 0 ) ); + CPPUNIT_ASSERT( !st.hasData( 0 ) ); + CPPUNIT_ASSERT_EQUAL( 12u , st.newData< uint32_t >( 0 , 12 ) ); + CPPUNIT_ASSERT( st.hasData( 0 ) ); + CPPUNIT_ASSERT_EQUAL( 12u , st.dataOf< uint32_t>( 0 ) ); +} + +void SyntaxTreeTest::testUpdateData( ) +{ + T_Test_ st{ E_TestNode_::ROOT }; + st.newData< uint32_t >( 0 , 12 ); + st.dataOf< uint32_t>( 0 ) = 42u; + CPPUNIT_ASSERT_EQUAL( 42u , st.dataOf< uint32_t>( 0 ) ); +} + +void SyntaxTreeTest::testAddChildren( ) +{ + T_Test_ st{ E_TestNode_::ROOT }; + const auto rc{ st.addChild( 0 , E_TestNode_::ADD ) }; + CPPUNIT_ASSERT_EQUAL( 1u , rc ); + CPPUNIT_ASSERT_EQUAL( 1u , st.childrenOf( 0 ).size( ) ); + CPPUNIT_ASSERT_EQUAL( rc , st.childrenOf( 0 )[ 0 ] ); + CPPUNIT_ASSERT_EQUAL( 0u , st.parentOf( rc ) ); + CPPUNIT_ASSERT( E_TestNode_::ADD == st.typeOf( rc ) ); + + const auto cc{ st.addChild( rc , E_TestNode_::VALUE ) }; + CPPUNIT_ASSERT_EQUAL( 2u , cc ); + CPPUNIT_ASSERT_EQUAL( 1u , st.childrenOf( rc ).size( ) ); + CPPUNIT_ASSERT_EQUAL( cc , st.childrenOf( rc )[ 0 ] ); + CPPUNIT_ASSERT( E_TestNode_::VALUE == st.typeOf( cc ) ); +}