diff --git a/opast.hh b/opast.hh
index bdf5016..4844cc3 100644
--- a/opast.hh
+++ b/opast.hh
@@ -99,6 +99,11 @@ class T_InstrListNode : public A_Node
 		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<
@@ -189,6 +194,11 @@ class T_RootNode : public A_Node
 		{ 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 ]; }
 };
 
 /*----------------------------------------------------------------------------*/
diff --git a/parsercheck.cc b/parsercheck.cc
index 6da818a..d085b16 100644
--- a/parsercheck.cc
+++ b/parsercheck.cc
@@ -8,6 +8,232 @@ using namespace opast;
 
 namespace {
 
+/*============================================================================*/
+// FIXME TESTING, MOVE THIS LATER
+
+template< typename NodeType >
+class T_Visitor
+{
+    public:
+	using T_Node = NodeType;
+
+	// Node browser. Returns the Nth child of the specified node, or null
+	// if there are no children left.
+	using F_NodeBrowser = std::function< T_Node*( T_Node& , uint32_t ) >;
+
+	// Node action. Second parameter indicates whether the action is
+	// being called before (false) or after (true) visiting the children.
+	using F_NodeAction = std::function< bool( T_Node& , bool ) >;
+
+    private:
+	enum E_State_ {
+		BEFORE , CHILDREN , AFTER
+	};
+	struct T_NodeRef_ {
+		T_Node* node;
+		uint32_t child;
+		E_State_ state{ BEFORE };
+
+		explicit T_NodeRef_( T_Node* node )
+			: node( node ) , child( 0 ) {}
+		explicit T_NodeRef_( T_Node* node , uint32_t child )
+			: node( node ) , child( child ) {}
+	};
+
+	F_NodeBrowser nodeBrowser_;
+	T_Array< T_NodeRef_ > stack_;
+
+    public:
+	T_Visitor( ) = delete;
+	T_Visitor( T_Visitor const& ) noexcept = default;
+	T_Visitor( T_Visitor&& ) noexcept = default;
+
+	explicit T_Visitor( F_NodeBrowser browser ) noexcept;
+
+	void visit( T_Node& root , F_NodeAction action );
+};
+
+template< typename T >
+inline T_Visitor< T >::T_Visitor(
+		F_NodeBrowser browser ) noexcept
+	: nodeBrowser_( std::move( browser ) )
+{ }
+
+template< typename T >
+inline void T_Visitor< T >::visit(
+		T_Node& root ,
+		F_NodeAction action )
+{
+	stack_.addNew( &root , 0 );
+
+	while ( !stack_.empty( ) ) {
+		auto& n( stack_.last( ) );
+		switch ( n.state ) {
+		    case BEFORE:
+			n.state = action( *n.node , false ) ? CHILDREN : AFTER;
+			break;
+		    case CHILDREN: {
+			T_Node* child( nodeBrowser_( *n.node , n.child ++ ) );
+			if ( child ) {
+				stack_.addNew( child , 0 );
+			} else {
+				n.state = AFTER;
+			}
+			break;
+		    }
+		    case AFTER:
+			action( *n.node , true );
+			stack_.removeLast( );
+			break;
+		}
+	}
+}
+
+A_Node* OpASTBrowser(
+		A_Node& node ,
+		const uint32_t child )
+{
+	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::OP_PROGRAM: case A_Node::OP_PIPELINE:
+	    case A_Node::OP_INPUT:
+		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 );
+		if ( child < n.arguments( ) ) {
+			return &n.argument( child );
+		}
+		break;
+	    }
+
+	    // Conditional instruction
+	    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;
+	    }
+
+	    // 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.hasWidth( ) ) {
+			if ( c == 0 ) {
+				return &n.width( );
+			}
+			c --;
+		}
+		if ( n.hasHeight( ) && c == 0 ) {
+			return &n.height( );
+		}
+		break;
+	    }
+
+	}
+	return nullptr;
+}
+
+
+/*============================================================================*/
+
+
 void PrintStreamError(
 		char const* const prefix ,
 		T_String const& name ,
@@ -69,6 +295,15 @@ int main( int argc , char** argv )
 	T_Parser parser;
 	if ( parser.parse( srdOut.list( ) ) ) {
 		printf( "Success!\n" );
+
+		auto result( parser.result( ) );
+		T_Visitor< A_Node > visitor( OpASTBrowser );
+		visitor.visit( *result , []( A_Node& node , bool enter ) {
+			if ( enter ) {
+				printf( "Enter node %p\n" , &node );
+			}
+			return true;
+		} );
 		return 0;
 	} else {
 		T_StringBuilder sb;