diff --git a/Makefile b/Makefile index d45d2ed..d41e530 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,7 @@ DEMO = \ odbg.cc \ sync.cc \ control.cc \ + opast.cc \ \ demo.cc \ \ diff --git a/demo.srd b/demo.srd index 81e75f5..ba0aa66 100644 --- a/demo.srd +++ b/demo.srd @@ -1,4 +1,4 @@ -(fn init () +(init # Compute viewport size (if (gt width height) ( @@ -18,7 +18,7 @@ (call dof-init) ) -(fn frame () +(frame (profiling "Frame render" { FIXME: other stages } (call dof-main { FIXME: args here }) diff --git a/opast.cc b/opast.cc new file mode 100644 index 0000000..5ac8adf --- /dev/null +++ b/opast.cc @@ -0,0 +1,217 @@ +#include "opast.hh" + +using namespace ebcl; +using namespace opast; + + +/*= A_Node ===================================================================*/ + +A_Node::A_Node( const E_Type type , + A_Node* const parent ) noexcept + : type_( type ) , parent_( parent ) +{ + assert( ( type == ROOT && !parent ) || ( type != ROOT && parent ) ); +} + +A_Node::~A_Node( ) { } + +T_RootNode& A_Node::root( ) const noexcept +{ + A_Node const* node( this ); + while ( node->parent_ ) { + node = node->parent_; + } + assert( node ); + assert( node->type_ == ROOT ); + return *dynamic_cast< T_RootNode* >( + const_cast< A_Node* >( node ) ); +} + +/*= A_FuncNode ===============================================================*/ + +A_FuncNode::A_FuncNode( + const bool isInit , + T_RootNode* const root ) noexcept + : A_Node( isInit ? DECL_INIT : DECL_FRAME , root ) , + name_( isInit ? "*init*" : "*frame*" ) +{ } + +A_FuncNode::A_FuncNode( + T_String const& name , + T_RootNode* const root ) noexcept + : A_Node( DECL_FN , root ) , name_( name ) +{ } + + +/*= T_RootNode ===============================================================*/ + +T_RootNode::T_RootNode( ) noexcept + : A_Node( ROOT , nullptr ) , functions_( + []( T_OwnPtr< A_FuncNode > const& f ) { + return f->name( ); + } ) +{ } + + +T_RootNode::T_AddFunctionResult T_RootNode::addFunction( + T_OwnPtr< A_FuncNode >& function ) noexcept +{ + T_String const& fn( function->name( ) ); + auto const* const pf( functions_.get( fn ) ); + if ( !pf ) { + auto* const rv( function.get( ) ); + functions_.add( std::move( function ) ); + return *rv; + } + + T_String dfn; + uint32_t dupCtr( 0 ); + do { + T_StringBuilder fnsb; + fnsb << fn << " dup " << dupCtr ++; + dfn = std::move( fnsb ); + } while ( functions_.contains( dfn ) ); + + T_OwnPtr< A_FuncNode > df( NewOwned< T_FuncNode >( dfn , *this ) ); + auto* const rv( df.get( ) ); + functions_.add( std::move( df ) ); + return T_AddFunctionResult{ *rv , (*pf)->location( ) }; +} + + +/*= T_SpecialFuncNode ========================================================*/ + +T_SpecialFuncNode::T_SpecialFuncNode( + bool isInit , + T_RootNode& parent ) noexcept + : A_FuncNode( isInit , &parent ) +{ } + + +/*= T_FuncNode ===============================================================*/ + +T_FuncNode::T_FuncNode( + T_String const& name , + T_RootNode& parent ) noexcept + : A_FuncNode( name , &parent ) +{ } + +T_Optional< T_SRDLocation > T_FuncNode::addArgument( + T_SRDToken const& token ) noexcept +{ + assert( token.type( ) == E_SRDTokenType::WORD ); + assert( token.hasLocation( ) ); + + const auto pnp( argNames_.indexOf( token.stringValue( ) ) ); + if ( pnp != -1 ) { + return argLocations_[ pnp ]; + } + argNames_.add( token.stringValue( ) ); + argLocations_.add( token.location( ) ); + return {}; +} + + +/*= T_Parser =================================================================*/ + +namespace { + +struct T_ParserImpl_ +{ + T_OwnPtr< T_RootNode >& root; + T_Array< T_SRDError >& errors; + + T_ParserImpl_( T_Array< T_SRDError >* errors , + T_OwnPtr< T_RootNode >* root ) noexcept; + + void parseFunction( T_SRDList const& funcList ) noexcept; +}; + +T_ParserImpl_::T_ParserImpl_( + T_Array< T_SRDError >* const errors , + T_OwnPtr< T_RootNode >* const root ) noexcept + : root( *root ) , errors( *errors ) +{ } + +void T_ParserImpl_::parseFunction( + T_SRDList const& funcList ) noexcept +{ + assert( funcList.size( ) != 0 ); + + auto const& fw( funcList[ 0 ] ); + if ( fw.type( ) != E_SRDTokenType::WORD + || ( fw.stringValue( ) != "init" && fw.stringValue( ) != "frame" + && fw.stringValue( ) != "fn" ) ) { + errors.addNew( "init, frame or fn expected" , fw.location( ) ); + return; + } + + T_String const& ftw( fw.stringValue( ) ); + T_OwnPtr< A_FuncNode > fn; + if ( ftw == "fn" ) { + if ( funcList.size( ) < 3 ) { + errors.addNew( "function name and arguments expected" , + fw.location( ) ); + return; + } + if ( funcList[ 1 ].type( ) != E_SRDTokenType::WORD ) { + errors.addNew( "function name expected" , funcList[ 1 ].location( ) ); + return; + } + + fn = NewOwned< T_FuncNode >( funcList[ 1 ].stringValue( ) , *root ); + // TODO parseFunctionArguments( funcList[ 2 ] ); + } else { + fn = NewOwned< T_SpecialFuncNode >( ftw == "init" , *root ); + } + fn->location( ) = fw.location( ); + + const auto af( root->addFunction( fn ) ); + if ( af.dupLocation.present( ) ) { + T_StringBuilder esb( "duplicate " ); + switch ( fn->type( ) ) { + case A_Node::DECL_FN: + esb << "function '" << fn->name( ) << "'"; + break; + case A_Node::DECL_INIT: + esb << "initialisation function"; + break; + case A_Node::DECL_FRAME: + esb << "frame function"; + break; + default: std::abort( ); + } + esb << "; previous declaration: " << *af.dupLocation.target( ); + errors.addNew( std::move( esb ) , fw.location( ) ); + } + + // TODO parseInstructions( fn->instructions , funcList , + // ftw == "fn" ? 3 : 1 ); +} + +} // namespace + +/*----------------------------------------------------------------------------*/ + +T_Parser::T_Parser( ) noexcept + : A_PrivateImplementation( new T_ParserImpl_( &errors_ , &rootNode_ ) ) , + errors_( 64 ) , rootNode_{} +{} + +bool T_Parser::parse( + T_SRDList const& input ) noexcept +{ + errors_.clear( ); + rootNode_ = NewOwned< T_RootNode >( ); + + for ( auto const& t : input ) { + if ( t.type( ) == E_SRDTokenType::LIST && t.list( ).size( ) > 0 ) { + p< T_ParserImpl_ >( ).parseFunction( t.list( ) ); + } else { + errors_.addNew( "Function or special block expected" , + t.location( ) ); + } + } + + return errors_.empty( ); +} diff --git a/opast.hh b/opast.hh new file mode 100644 index 0000000..f0ac73f --- /dev/null +++ b/opast.hh @@ -0,0 +1,170 @@ +#pragma once +//#ifndef REAL_BUILD +# include "externals.hh" +//#endif + +#include + + +namespace opast { + +using namespace ebcl; + +class T_RootNode; + +class A_Node +{ + public: + enum E_Type { + ROOT , + // + DECL_INIT , + DECL_FRAME , + DECL_FN , + // + OP_SET , + // + EXPR_ADD , + EXPR_MUL , + EXPR_SUB , + EXPR_DIV , + EXPR_VAR , + EXPR_CONST , + }; + + private: + const E_Type type_; + A_Node* const parent_; + T_SRDLocation location_; + + protected: + explicit A_Node( const E_Type type , + A_Node* const parent ) noexcept; + + public: + virtual ~A_Node( ) = 0; + + E_Type type( ) const noexcept + { return type_; } + + T_SRDLocation& location( ) noexcept + { return location_; } + T_SRDLocation const& location( ) const noexcept + { return location_; } + + A_Node& parent( ) const noexcept + { assert( parent_ ); return *parent_; } + + T_RootNode& root( ) const noexcept; +}; + +/*----------------------------------------------------------------------------*/ + +// Function-like nodes +class A_FuncNode : public A_Node +{ + private: + T_String name_; + + protected: + // For init or frame entry points. + // isInit = true => init entry point + // isInit = false => frame entry point + explicit A_FuncNode( bool isInit , + T_RootNode* const parent ) noexcept; + + // For normal functions + explicit A_FuncNode( T_String const& name , + T_RootNode* const parent ) noexcept; + + public: + T_String const& name( ) const noexcept + { return name_; } +}; + +// Root node, keeps track of the whole tree and related data (function table, +// assets...) +class T_RootNode : public A_Node +{ + private: + T_ObjectTable< T_String , T_OwnPtr< A_FuncNode > > functions_; + + public: + T_RootNode( ) noexcept; + + // Return type for addFunction. We'll always return a reference to a + // function node (which may or may not be the same as the initial one, + // if there were duplicates), and we'll return the location of the + // initial function in the case of a duplicate. + struct T_AddFunctionResult + { + A_FuncNode& function; + T_Optional< T_SRDLocation > dupLocation; + + T_AddFunctionResult( A_FuncNode& function ) noexcept + : function{ function } , dupLocation{} + {} + + T_AddFunctionResult( A_FuncNode& function , + T_SRDLocation const& dupLocation ) noexcept + : function{ function } , dupLocation{ dupLocation } + {} + }; + + // Attempts to add a function. If the function is already present in + // the table, the result will be set up with the previous declaration's + // location, and the specified function will not be modified (a duplicate + // entry will be added to the table instead). + T_AddFunctionResult addFunction( + T_OwnPtr< A_FuncNode >& function ) noexcept; +}; + +/*----------------------------------------------------------------------------*/ + +// Init & frame functions +class T_SpecialFuncNode : public A_FuncNode +{ + public: + T_SpecialFuncNode( + bool isInit , + T_RootNode& parent ) noexcept; +}; + +// Normal functions +class T_FuncNode : public A_FuncNode +{ + private: + T_AutoArray< T_String , 8 > argNames_; + T_AutoArray< T_SRDLocation , 8 > argLocations_; + + public: + T_FuncNode( T_String const& name , + T_RootNode& parent ) noexcept; + + // Add an argument. If the argument is a duplicate, return the location + // of the initial argument. + T_Optional< T_SRDLocation > addArgument( + T_SRDToken const& token ) noexcept; +}; + +/*----------------------------------------------------------------------------*/ + +class T_Parser : public A_PrivateImplementation +{ + private: + T_Array< T_SRDError > errors_; + T_OwnPtr< T_RootNode > rootNode_; + + public: + T_Parser( ) noexcept; + + bool parse( T_SRDList const& input ) noexcept; + + T_Array< T_SRDError > const& errors( ) const noexcept + { return errors_; } + T_OwnPtr< T_RootNode > result( ) noexcept + { return std::move( rootNode_ ); } +}; + + +} // namespace opast