#pragma once #ifndef REAL_BUILD # include "externals.hh" #endif #include "texture.hh" #include namespace opast { using namespace ebcl; class T_RootNode; class A_Node { public: enum E_Type { ROOT , // Root node ILIST , // Instruction list // DECL_INIT , // Initialisation block DECL_FRAME , // Frame block DECL_FN , // Function // OP_CALL , // Function call OP_COND , // Conditional instruction OP_INPUT , // Input declaration OP_PIPELINE , // Shader pipeline declaration OP_PROFILE , // Profiling block OP_PROGRAM , // Shader program declaration OP_SET , // Set instruction OP_TEXTURE , // Define texture // Unary operators EXPR_NEG , EXPR_INV , EXPR_NOT , EXPR_SIN , EXPR_COS , EXPR_TAN , EXPR_SQRT , EXPR_EXP , EXPR_LN , // Binary operators EXPR_ADD , EXPR_SUB , EXPR_MUL , EXPR_DIV , EXPR_POW , // Binary operators - comparisons EXPR_CMP_EQ , EXPR_CMP_NE , EXPR_CMP_GT , EXPR_CMP_GE , EXPR_CMP_LT , EXPR_CMP_LE , // EXPR_ID , // Variable access EXPR_CONST , // Numeric constant }; 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; }; /*----------------------------------------------------------------------------*/ // Base class for instruction nodes class A_InstructionNode : public A_Node { protected: A_InstructionNode( E_Type type , A_Node& parent ) noexcept : A_Node( type , &parent ) {} }; // Nodes that store lists of instructions class T_InstrListNode : public A_Node { private: T_Array< T_OwnPtr< A_InstructionNode > > instructions_; public: T_InstrListNode( A_Node& parent ) noexcept; template< typename IType , typename... ArgTypes > IType& add( ArgTypes&&... args ); uint32_t size( ) const noexcept { return instructions_.size( ); } A_InstructionNode& node( const uint32_t index ) const noexcept { return *instructions_[ index ]; } }; template< typename IType , typename... ArgTypes > inline IType& T_InstrListNode::add( ArgTypes&&... args ) { instructions_.add( NewOwned< IType >( *this , std::forward< ArgTypes >( args ) ... ) ); return (IType&) *instructions_.last( ); } /*----------------------------------------------------------------------------*/ // Function-like nodes class A_FuncNode : public A_Node { private: T_String name_; T_InstrListNode instructions_; 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_; } T_InstrListNode& instructions( ) noexcept { return instructions_; } T_InstrListNode const& instructions( ) const noexcept { return instructions_; } }; using P_InstrListNode = T_OwnPtr< T_InstrListNode >; // 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; // --------------------------------------------------------------------- bool hasFunction( T_String const& name ) noexcept { return functions_.contains( name ); } bool hasInit( ) noexcept { return hasFunction( "*init*" ); } bool hasFrame( ) noexcept { return hasFunction( "*frame*" ); } uint32_t nFunctions( ) const noexcept { return functions_.size( ); } A_FuncNode& function( const uint32_t index ) const noexcept { return *functions_.values( )[ index ]; } }; /*----------------------------------------------------------------------------*/ // 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; }; /*----------------------------------------------------------------------------*/ // Base class for expressions class A_ExpressionNode : public A_Node { protected: A_ExpressionNode( E_Type type , A_Node& parent ) noexcept : A_Node( type , &parent ) { } }; using P_ExpressionNode = T_OwnPtr< A_ExpressionNode >; /*============================================================================*/ // Function call class T_CallInstrNode : public A_InstructionNode { private: T_String id_; T_SRDLocation idLocation_; T_AutoArray< P_ExpressionNode , 8 > arguments_; public: T_CallInstrNode( T_InstrListNode& parent , T_SRDToken const& idToken ) noexcept : A_InstructionNode( OP_CALL , parent ) , id_( idToken.stringValue( ) ) , idLocation_( idToken.location( ) ) { } void addArgument( P_ExpressionNode expr ) noexcept { if ( expr ) { arguments_.add( std::move( expr ) ); } } T_String const& id( ) const noexcept { return id_; } T_SRDLocation const& idLocation( ) const noexcept { return idLocation_; } uint32_t arguments( ) const noexcept { return arguments_.size( ); } A_ExpressionNode& argument( const uint32_t index ) const noexcept { return *arguments_[ index ]; } }; // Conditional instruction class T_CondInstrNode : public A_InstructionNode { private: P_ExpressionNode expression_; T_KeyValueTable< int64_t , P_InstrListNode > cases_; P_InstrListNode defaultCase_; public: explicit T_CondInstrNode( T_InstrListNode& parent ) noexcept : A_InstructionNode( OP_COND , parent ) { } void setExpression( P_ExpressionNode expression ) noexcept { expression_ = std::move( expression ); } bool hasExpression( ) const noexcept { return bool( expression_ ); } A_ExpressionNode& expression( ) const noexcept { return *expression_; } void setCase( const int64_t value , P_InstrListNode instrList ) noexcept { if ( instrList ) { cases_.set( value , std::move( instrList ) ); } } void rmCase( const int64_t value ) noexcept { cases_.remove( value ); } T_Array< int64_t > const& cases( ) const noexcept { return cases_.keys( ); } bool hasCase( const int64_t value ) const noexcept { return cases_.contains( value ); } T_InstrListNode& getCase( const int64_t value ) const noexcept { return **cases_.get( value ); } void setDefaultCase( P_InstrListNode defaultCase ) noexcept { defaultCase_ = std::move( defaultCase ); } bool hasDefaultCase( ) const noexcept { return bool( defaultCase_ ); } T_InstrListNode& defaultCase( ) const noexcept { return *defaultCase_; } }; // Input declaration class T_InputInstrNode : public A_InstructionNode { private: T_String name_; T_SRDLocation nameLocation_; float defValue_; T_SRDLocation dvLocation_; public: T_InputInstrNode( T_InstrListNode& parent , T_SRDToken const& tName ) noexcept : A_InstructionNode( OP_INPUT , parent ) , name_( tName.stringValue( ) ) , nameLocation_( tName.location( ) ) {} T_InputInstrNode( T_InstrListNode& parent , T_SRDToken const& tName , T_SRDToken const& tDefault ) noexcept : A_InstructionNode( OP_INPUT , parent ) , name_( tName.stringValue( ) ) , nameLocation_( tName.location( ) ) , defValue_( tDefault.floatValue( ) ) , dvLocation_( tDefault.location( ) ) {} T_String const& name( ) const noexcept { return name_; } T_SRDLocation const& nameLocation( ) const noexcept { return nameLocation_; } float defValue( ) const noexcept { return defValue_; } T_SRDLocation const& defValueLocation( ) const noexcept { return dvLocation_; } }; // Pipeline declaration instruction class T_PipelineInstrNode : public A_InstructionNode { private: T_String id_; T_SRDLocation idLocation_; T_StaticArray< T_String , 6 > pids_; T_StaticArray< T_SRDLocation , 6 > pidLocations_; public: T_PipelineInstrNode( T_InstrListNode& parent , T_SRDToken const& idToken ) noexcept; explicit T_PipelineInstrNode( T_InstrListNode& parent ) noexcept; // Add a program identifier. The token is assumed to be a valid word, // and the list of programs is assumed not to be full. If the identifer // is already in the pipeline's list, the location of the first // occurrence will be returned. T_Optional< T_SRDLocation > addProgram( T_SRDToken const& pidToken ) noexcept; uint32_t size( ) const noexcept { return pids_.size( ); } T_String const& program( const uint32_t index ) const noexcept { return pids_[ index ]; } T_SRDLocation const& pLocation( const uint32_t index ) const noexcept { return pidLocations_[ index ]; } }; // Profiling instruction class T_ProfileInstrNode : public A_InstructionNode { private: T_String text_; T_InstrListNode instructions_; public: T_ProfileInstrNode( T_InstrListNode& parent , T_String const& text ) noexcept; T_String const& text( ) const { return text_; } T_InstrListNode& instructions( ) noexcept { return instructions_; } T_InstrListNode const& instructions( ) const noexcept { return instructions_; } }; // Program loader instruction class T_ProgramInstrNode : public A_InstructionNode { private: T_String id_; T_SRDLocation idLocation_; T_String path_; T_SRDLocation pathLocation_; public: T_ProgramInstrNode( T_InstrListNode& parent , T_SRDToken const& idToken , T_SRDToken const& pathToken ) noexcept; }; // Setting a global variable class T_SetInstrNode : public A_InstructionNode { private: T_String id_; T_SRDLocation idLocation_; P_ExpressionNode expression_; public: T_SetInstrNode( T_InstrListNode& parent , T_SRDToken const& idToken ) noexcept; T_String const& identifier( ) const noexcept { return id_; } T_SRDLocation const& idLocation( ) const noexcept { return idLocation_; } void setExpression( P_ExpressionNode expression ) noexcept { expression_ = std::move( expression ); } bool hasExpression( ) const noexcept { return bool( expression_ ); } A_ExpressionNode& expression( ) const noexcept { return *expression_; } }; // Texture definition class T_TextureInstrNode : public A_InstructionNode { private: T_String id_; T_SRDLocation idLocation_; E_TexType type_; P_ExpressionNode width_; P_ExpressionNode height_; public: T_TextureInstrNode( T_InstrListNode& parent , T_SRDToken const& id , E_TexType type ) noexcept; void setWidth( P_ExpressionNode width ) noexcept { width_ = std::move( width ); } bool hasWidth( ) const noexcept { return bool( width_ ); } A_ExpressionNode& width( ) const noexcept { return *width_; } void setHeight( P_ExpressionNode height ) noexcept { height_ = std::move( height ); } bool hasHeight( ) const noexcept { return bool( height_ ); } A_ExpressionNode& height( ) const noexcept { return *height_; } }; /*============================================================================*/ // A constant value class T_ConstantExprNode : public A_ExpressionNode { private: bool wasFloat_; double vFloat_; int64_t vInt_; public: T_ConstantExprNode( A_Node& parent , T_SRDToken const& token ) noexcept; T_ConstantExprNode( A_Node& parent , int64_t value ) noexcept; T_ConstantExprNode( A_Node& parent , double value ) noexcept; bool wasFloat( ) const noexcept { return wasFloat_; } double floatValue( ) const noexcept { return vFloat_; } int64_t intValue( ) const noexcept { return vInt_; } }; // An identifier used in an expression class T_IdentifierExprNode : public A_ExpressionNode { private: T_String id_; public: T_IdentifierExprNode( A_Node& parent , T_SRDToken const& token ) noexcept; T_IdentifierExprNode( A_Node& parent , T_String const& id ) noexcept; T_String const& id( ) const noexcept { return id_; } }; // A unary operator class T_UnaryOperatorNode : public A_ExpressionNode { public: enum E_Operator { NEG , INV , NOT , SIN , COS , TAN , SQRT , EXP , LN }; private: E_Operator op_; P_ExpressionNode argument_; public: T_UnaryOperatorNode( A_Node& parent , const E_Operator op ) noexcept; E_Operator op( ) const noexcept { return op_; } void setArgument( P_ExpressionNode argument ) noexcept { argument_ = std::move( argument ); } bool hasArgument( ) const noexcept { return bool( argument_ ); } A_ExpressionNode const& argument( ) const noexcept { return *argument_; } A_ExpressionNode& argument( ) noexcept { return *argument_; } }; // A binary operator class T_BinaryOperatorNode : public A_ExpressionNode { public: enum E_Operator { ADD , SUB , MUL , DIV , POW , CMP_EQ , CMP_NE , CMP_GT , CMP_GE , CMP_LT , CMP_LE , }; private: E_Operator op_; P_ExpressionNode left_; P_ExpressionNode right_; public: T_BinaryOperatorNode( A_Node& parent , const E_Operator op ) noexcept; E_Operator op( ) const noexcept { return op_; } void setLeft( P_ExpressionNode left ) noexcept { left_ = std::move( left ); } void setRight( P_ExpressionNode right ) noexcept { right_ = std::move( right ); } bool hasLeft( ) const noexcept { return bool( left_ ); } bool hasRight( ) const noexcept { return bool( right_ ); } A_ExpressionNode const& left( ) const noexcept { return *left_; } A_ExpressionNode& left( ) noexcept { return *left_; } A_ExpressionNode const& right( ) const noexcept { return *right_; } A_ExpressionNode& right( ) noexcept { return *right_; } }; /*============================================================================*/ 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