demotool/opast.cc

662 lines
15 KiB
C++

#include "externals.hh"
#include "opast.hh"
#include <ebcl/Algorithms.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_Node* opast::ASTVisitorBrowser(
A_Node& node ,
const uint32_t child ) noexcept
{
switch ( node.type( ) ) {
// Root node
case A_Node::ROOT: {
auto& n( (T_RootNode&) node );
if ( child < n.nFunctions( ) ) {
return &n.function( child );
}
break;
}
// Functions / special blocks
case A_Node::DECL_FN: case A_Node::DECL_INIT: case A_Node::DECL_FRAME:
if ( child == 0 ) {
auto& n( (A_FuncNode&) node );
return &n.instructions( );
}
break;
// Instruction list
case A_Node::ILIST: {
auto& n( (T_InstrListNode&) node );
if ( child < n.size( ) ) {
return &n.node( child );
}
break;
}
// Unary operators
case A_Node::EXPR_NEG: case A_Node::EXPR_INV:
case A_Node::EXPR_NOT: case A_Node::EXPR_SIN:
case A_Node::EXPR_COS: case A_Node::EXPR_TAN:
case A_Node::EXPR_SQRT: case A_Node::EXPR_EXP:
case A_Node::EXPR_LN:
{
auto& n( (T_UnaryOperatorNode&) node );
if ( child == 0 && n.hasArgument( ) ) {
return &n.argument( );
}
break;
}
// Binary operators
case A_Node::EXPR_ADD: case A_Node::EXPR_SUB:
case A_Node::EXPR_MUL: case A_Node::EXPR_DIV:
case A_Node::EXPR_POW:
case A_Node::EXPR_CMP_EQ: case A_Node::EXPR_CMP_NE:
case A_Node::EXPR_CMP_GT: case A_Node::EXPR_CMP_GE:
case A_Node::EXPR_CMP_LT: case A_Node::EXPR_CMP_LE:
{
auto& n( (T_BinaryOperatorNode&) node );
if ( child == 0 && n.hasLeft( ) ) {
return &n.left( );
}
if ( child < 2 && n.hasRight( ) ) {
return &n.right( );
}
break;
}
// Nodes that do not have children
case A_Node::EXPR_ID: case A_Node::EXPR_CONST:
case A_Node::EXPR_INPUT:
//
case A_Node::OP_PROGRAM: case A_Node::OP_PIPELINE:
case A_Node::OP_INPUT: case A_Node::OP_FULLSCREEN:
case A_Node::OP_ODBG: case A_Node::OP_LOCALS:
case A_Node::OP_MAINOUT:
//
case A_Node::OP_USE_FRAMEBUFFER: case A_Node::OP_USE_PIPELINE:
case A_Node::OP_USE_PROGRAM: case A_Node::OP_USE_TEXTURE:
break;
// Profile instruction
case A_Node::OP_PROFILE:
if ( child == 0 ) {
return &( ((T_ProfileInstrNode&) node).instructions( ) );
}
break;
// Call instruction
case A_Node::OP_CALL:
{
auto& n( (T_CallInstrNode&) node );
const auto nArgs( n.arguments( ) );
if ( child < nArgs ) {
return &n.argument( nArgs - child - 1 );
}
break;
}
// Argument node
case A_Node::TN_ARG:
{
if ( child == 0 ) {
return &( (T_ArgumentNode&) node ).expression( );
}
break;
}
// Clear instruction
case A_Node::OP_CLEAR:
{
auto& n( (T_ClearInstrNode&) node );
const auto nArgs( n.components( ) );
if ( child < nArgs ) {
return &n.component( nArgs - child - 1 );
}
break;
}
// Conditional instruction & associated technical nodes
case A_Node::OP_COND:
{
auto& n( (T_CondInstrNode&) node );
auto c = child;
if ( n.hasExpression( ) ) {
if ( c == 0 ) {
return &n.expression( );
}
c --;
}
if ( !n.cases( ).empty( ) ) {
if ( c < n.cases( ).size( ) ) {
return &n.getCase( n.cases( )[ c ] );
}
c -= n.cases( ).size( );
}
if ( n.hasDefaultCase( ) && c == 0 ) {
return &n.defaultCase( );
}
break;
}
case A_Node::TN_CONDITION:
if ( child == 0 ) {
return &( (T_CondInstrNode::T_Expression&) node ).expression( );
}
break;
case A_Node::TN_CASE:
if ( child == 0 ) {
return &( (T_CondInstrNode::T_ValuedCase&) node ).instructions( );
}
break;
case A_Node::TN_DEFAULT:
if ( child == 0 ) {
return &( (T_CondInstrNode::T_DefaultCase&) node ).instructions( );
}
break;
// Set instruction
case A_Node::OP_SET:
if ( child == 0 ) {
auto& n( (T_SetInstrNode&) node );
if ( n.hasExpression( ) ) {
return &n.expression( );
}
}
break;
// Texture instruction
case A_Node::OP_TEXTURE:
{
auto& n( (T_TextureInstrNode&) node );
auto c = child;
if ( n.lods( ) ) {
if ( c == 0 ) {
return n.lods( );
}
c --;
}
if ( n.hasHeight( ) ) {
if ( c == 0 ) {
return &n.height( );
}
c --;
}
if ( n.hasWidth( ) && c == 0 ) {
return &n.width( );
}
return nullptr;
}
// Framebuffer definition; may have LOD expressions
case A_Node::OP_FRAMEBUFFER:
{
auto& n( (T_FramebufferInstrNode&) node );
if ( child < n.colorAttachments( ) ) {
return &n.colorAttachment( child );
}
if ( child == n.colorAttachments( ) && n.depthAttachment( ) ) {
return n.depthAttachment( );
}
break;
}
case A_Node::TN_FBATT:
{
auto& n( (T_FramebufferInstrNode::T_Attachment&) node );
if ( n.lod( ) && child == 0 ) {
return n.lod( );
}
break;
}
// Sampler definition may have LOD expressions
case A_Node::OP_SAMPLER:
{
auto& n( (T_SamplerInstrNode&) node );
auto c = child;
if ( n.minLod( ) ) {
if ( c == 0 ) {
return n.minLod( );
}
c--;
}
if ( n.maxLod( ) && c == 0 ) {
return n.maxLod( );
}
break;
}
// Uniforms instruction
case A_Node::OP_UNIFORMS:
{
auto& n( (T_UniformsInstrNode&) node );
const auto nv( n.values( ) );
if ( child < nv ) {
return &n.value( nv - child - 1 );
}
break;
}
// Viewport instruction
case A_Node::OP_VIEWPORT:
{
auto& n( (T_ViewportInstrNode&) node );
auto c = child;
for ( int i = 0 ; i < 4 ; i ++ ) {
T_ViewportInstrNode::E_Parameter p{
T_ViewportInstrNode::E_Parameter( 3 - i ) };
if ( n.hasParameter( p ) ) {
if ( c == 0 ) {
return &n.parameter( p );
}
c --;
}
}
break;
}
}
return nullptr;
}
/*= E_DataType ===============================================================*/
T_StringBuilder& opast::operator<<(
T_StringBuilder& sb ,
const E_DataType dt )
{
switch ( dt ) {
case E_DataType::UNKNOWN:
sb << "unknown";
return sb;
case E_DataType::BUILTIN:
sb << "built-in variable";
return sb;
case E_DataType::VARIABLE:
sb << "variable";
return sb;
case E_DataType::FRAMEBUFFER:
sb << "framebuffer";
return sb;
case E_DataType::INPUT:
sb << "input";
return sb;
case E_DataType::PIPELINE:
sb << "pipeline";
return sb;
case E_DataType::PROGRAM:
sb << "program";
return sb;
case E_DataType::SAMPLER:
sb << "sampler";
return sb;
case E_DataType::TEXTURE:
sb << "texture";
return sb;
}
sb << "*unknown data type*";
return sb;
}
/*= 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*" ) ,
instructions_( *this )
{ }
A_FuncNode::A_FuncNode(
T_String const& name ,
T_RootNode* const root ) noexcept
: A_Node( DECL_FN , root ) , name_( name ) ,
instructions_( *this )
{ }
T_Optional< T_SRDLocation > A_FuncNode::addLocalVariable(
T_String const& name ,
T_SRDLocation const& location ) noexcept
{
auto const* const pnp( locals_.get( name ) );
if ( pnp ) {
return pnp->location;
}
T_Local_ lv{ name , location , false };
lv.type = E_DataType::VARIABLE;
locals_.add( std::move( lv ) );
return {};
}
/*= 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_FuncNode ===============================================================*/
T_Optional< T_SRDLocation > T_FuncNode::addArgument(
T_SRDToken const& token ) noexcept
{
assert( token.type( ) == E_SRDTokenType::WORD );
assert( token.hasLocation( ) );
const auto pnp( locals_.indexOf( token.stringValue( ) ) );
if ( pnp != T_HashIndex::INVALID_INDEX ) {
return locals_[ pnp ].location;
}
locals_.add( T_Local_{ token.stringValue( ) , token.location( ) , true } );
return {};
}
uint32_t T_FuncNode::arguments( ) const noexcept
{
const auto n( locals_.size( ) );
uint32_t na{ 0u };
for ( auto i = 0u ; i < n ; i ++ ) {
if ( locals_[ i ].argument ) {
na ++;
}
}
return na;
}
/*= T_PipelineInstrNode ======================================================*/
T_Optional< T_SRDLocation > T_PipelineInstrNode::addProgram(
T_SRDToken const& pidToken ) noexcept
{
T_String const& name( pidToken.stringValue( ) );
const auto pIndex( pids_.indexOf( name ) );
if ( pIndex != -1 ) {
return pidLocations_[ pIndex ];
}
pids_.add( name );
pidLocations_.add( pidToken.location( ) );
return {};
}
/*= T_UnaryOperatorNode ======================================================*/
T_UnaryOperatorNode::T_UnaryOperatorNode(
A_Node& parent ,
const E_Operator op ) noexcept
: A_ExpressionNode( ([op]() {
switch ( op ) {
case NEG: return EXPR_NEG;
case INV: return EXPR_INV;
case NOT: return EXPR_NOT;
case SIN: return EXPR_SIN;
case COS: return EXPR_COS;
case TAN: return EXPR_TAN;
case SQRT: return EXPR_SQRT;
case EXP: return EXPR_EXP;
case LN: return EXPR_LN;
}
std::abort( );
})( ) , parent ) , op_( op )
{ }
/*= T_BinaryOperatorNode =====================================================*/
T_BinaryOperatorNode::T_BinaryOperatorNode(
A_Node& parent ,
const E_Operator op ) noexcept
: A_ExpressionNode( ([op]() {
switch ( op ) {
case ADD: return EXPR_ADD;
case SUB: return EXPR_SUB;
case MUL: return EXPR_MUL;
case DIV: return EXPR_DIV;
case POW: return EXPR_POW;
case CMP_EQ: return EXPR_CMP_EQ;
case CMP_NE: return EXPR_CMP_NE;
case CMP_GT: return EXPR_CMP_GT;
case CMP_GE: return EXPR_CMP_GE;
case CMP_LT: return EXPR_CMP_LT;
case CMP_LE: return EXPR_CMP_LE;
}
std::abort( );
})( ) , parent ) , op_( op )
{ }
/*= T_FramebufferInstrNode ===================================================*/
bool T_FramebufferInstrNode::hasAttachment(
T_String const& name ) const noexcept
{
if ( depthAttachment_ && depthAttachment_->id( ) == name ) {
return true;
}
const auto nca( colorAttachments_.size( ) );
for ( auto i = 0u ; i < nca ; i ++ ) {
if ( colorAttachments_[ i ]->id( ) == name ) {
return true;
}
}
return false;
}
bool T_FramebufferInstrNode::addColorAttachment(
T_SRDToken const& id ,
P_ExpressionNode lod ) noexcept
{
if ( hasAttachment( id.stringValue( ) ) ) {
return false;
}
colorAttachments_.add( NewOwned< T_Attachment >( *this ,
false , id , std::move( lod ) ) );
return true;
}
bool T_FramebufferInstrNode::setDepthAttachment(
T_SRDToken const& id ,
P_ExpressionNode lod ) noexcept
{
if ( depthAttachment_ || hasAttachment( id.stringValue( ) ) ) {
return false;
}
depthAttachment_ = NewOwned< T_Attachment >( *this ,
true , id , std::move( lod ) );
return true;
}
/*= T_OutputDebugInstrNode ===================================================*/
T_OutputDebugInstrNode::T_OutputDebugInstrNode(
T_InstrListNode& parent ,
T_SRDToken const& texture ,
const E_ODbgMode mode ,
T_SRDLocation const& modeLocation ,
T_SRDToken const& description ) noexcept
: A_InstructionNode( OP_ODBG , parent ) ,
idTexture_( texture.stringValue( ) ) ,
locTexture_( texture.location( ) ) ,
mode_( mode ) , locMode_( modeLocation ) ,
description_( description.stringValue( ) ) ,
locDescription_( description.location( ) )
{ }
/*= T_SamplerInstrNode =========================================================*/
T_Optional< T_SRDLocation > T_SamplerInstrNode::setSampling(
E_TexSampling mode ,
T_SRDLocation const& location ) noexcept
{
if ( sampling_ ) {
return sampling_->location;
}
sampling_ = T_Sampling_{ location , mode };
return {};
}
T_Optional< T_SRDLocation > T_SamplerInstrNode::setMipmapSampling(
E_TexSampling mode ,
T_SRDLocation const& location ) noexcept
{
if ( mipmaps_ ) {
return mipmaps_->location;
}
mipmaps_ = T_Mipmaps_{ location , mode };
return {};
}
T_Optional< T_SRDLocation > T_SamplerInstrNode::setNoMipmap(
T_SRDLocation const& location ) noexcept
{
if ( mipmaps_ ) {
return mipmaps_->location;
}
mipmaps_ = T_Mipmaps_{ location , {} };
return {};
}
T_Optional< T_SRDLocation > T_SamplerInstrNode::setWrapping(
E_TexWrap mode ,
T_SRDLocation const& location ) noexcept
{
if ( wrapping_ ) {
return wrapping_->location;
}
wrapping_ = T_Wrapping_{ location , mode };
return {};
}
T_Optional< T_SRDLocation > T_SamplerInstrNode::setLOD(
T_SRDLocation const& location ,
P_ExpressionNode min ,
P_ExpressionNode max ) noexcept
{
if ( lod_ ) {
return lod_->location;
}
lod_ = T_LOD_{ location ,
min ? NewOwned< T_ArgumentNode >( *this , std::move( min ) ) : P_ArgumentNode{} ,
max ? NewOwned< T_ArgumentNode >( *this , std::move( max ) ) : P_ArgumentNode{} };
return {};
}
/*= T_LocalsInstrNode ===========================================================*/
T_Optional< T_SRDLocation > T_LocalsInstrNode::addVariable(
T_SRDToken const& token ) noexcept
{
const auto pnp( vars_.indexOf( token.stringValue( ) ) );
if ( pnp != -1 ) {
return varLocs_[ pnp ];
}
vars_.add( token.stringValue( ) );
varLocs_.add( token.location( ) );
return {};
}
/*= T_CondInstrNode ============================================================*/
void T_CondInstrNode::setExpression(
P_ExpressionNode expression ) noexcept
{
if ( !expression ) {
expression_.clear( );
return;
}
expression_ = NewOwned< T_Expression >(
*this , std::move( expression ) );
}
void T_CondInstrNode::setCase(
const int64_t value ,
P_InstrListNode instrList ) noexcept
{
cases_.remove( value );
if ( instrList ) {
cases_.add( NewOwned< T_ValuedCase >( *this , value ,
std::move( instrList ) ) );
}
}
void T_CondInstrNode::setDefaultCase(
P_InstrListNode defaultCase ) noexcept
{
if ( !defaultCase ) {
defaultCase_.clear( );
return;
}
defaultCase_ = NewOwned< T_DefaultCase >( *this ,
std::move( defaultCase ) );
}