606 lines
18 KiB
C++
606 lines
18 KiB
C++
#include <lw/lib/SRDParser.hh>
|
|
#include <lw/lib/SRDText.hh>
|
|
#include <cppunit/extensions/HelperMacros.h>
|
|
using namespace lw;
|
|
|
|
|
|
class SRDParserTest : public CppUnit::TestFixture
|
|
{
|
|
CPPUNIT_TEST_SUITE( SRDParserTest );
|
|
CPPUNIT_TEST( testTokenTypes );
|
|
CPPUNIT_TEST( testParse );
|
|
|
|
CPPUNIT_TEST( testExecStartRuleFinish );
|
|
CPPUNIT_TEST( testExecContext );
|
|
CPPUNIT_TEST( testExecContextAndRule );
|
|
CPPUNIT_TEST( testExecInterruptTop );
|
|
CPPUNIT_TEST( testExecInterruptContext );
|
|
CPPUNIT_TEST( testExecLoneEnter );
|
|
CPPUNIT_TEST( testExecLoneExit );
|
|
CPPUNIT_TEST( testExecNoContextHandler );
|
|
|
|
CPPUNIT_TEST( testParseOLBCList );
|
|
CPPUNIT_TEST( testParseOLBCContext );
|
|
CPPUNIT_TEST( testParseOLBCListThenContext );
|
|
CPPUNIT_TEST( testParseOLBCListOverrides );
|
|
CPPUNIT_TEST( testParseOLBCContextOverrides );
|
|
CPPUNIT_TEST( testParseOLBCFailure );
|
|
|
|
CPPUNIT_TEST( testErrorTopLevelJunk );
|
|
CPPUNIT_TEST( testErrorMismatchInRule );
|
|
CPPUNIT_TEST( testErrorContextJunk );
|
|
CPPUNIT_TEST( testErrorUnterminatedLists );
|
|
|
|
CPPUNIT_TEST( testErrorNoExec );
|
|
CPPUNIT_TEST( testErrorInHandlerTop );
|
|
CPPUNIT_TEST( testErrorInHandlerContext );
|
|
CPPUNIT_TEST_SUITE_END( );
|
|
|
|
static T_SRDParserDefs makeParseOnlyDefs( );
|
|
static T_SRDParserDefs makeExecDefs( );
|
|
static T_SRDParserDefs makeOLBCDefs( );
|
|
|
|
public:
|
|
void testTokenTypes( );
|
|
void testParse( );
|
|
|
|
void testExecStartRuleFinish( );
|
|
void testExecContext( );
|
|
void testExecContextAndRule( );
|
|
void testExecInterruptTop( );
|
|
void testExecInterruptContext( );
|
|
void testExecLoneEnter( );
|
|
void testExecLoneExit( );
|
|
void testExecNoContextHandler( );
|
|
|
|
// Parse with Optional List Before Context
|
|
// * Clear choice towards the list
|
|
void testParseOLBCList( );
|
|
// * Clear choice towards the context
|
|
void testParseOLBCContext( );
|
|
// * List then context, both unambiguous
|
|
void testParseOLBCListThenContext( );
|
|
// * Ambiguity between list and context resolved by using the list
|
|
void testParseOLBCListOverrides( );
|
|
// * Initial ambiguity between list and context resolved by using the context
|
|
void testParseOLBCContextOverrides( );
|
|
// * Neither the list nor the context match the input
|
|
void testParseOLBCFailure( );
|
|
|
|
void testErrorTopLevelJunk( );
|
|
void testErrorMismatchInRule( );
|
|
void testErrorContextJunk( );
|
|
void testErrorUnterminatedLists( );
|
|
|
|
void testErrorNoExec( );
|
|
void testErrorInHandlerTop( );
|
|
void testErrorInHandlerContext( );
|
|
};
|
|
CPPUNIT_TEST_SUITE_REGISTRATION( SRDParserTest );
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
namespace {
|
|
|
|
// parse( input , IN/OUT parser , OUT errors )
|
|
void parse( char const* input , T_SRDParser & parser , T_SRDErrors & errors )
|
|
{
|
|
T_SRDLexer lexer( T_String( "test" ) , errors , parser );
|
|
parser.start( errors );
|
|
char const* ptr = input;
|
|
while ( *ptr != 0 ) {
|
|
lexer.processCharacter( *ptr ++ );
|
|
}
|
|
lexer.processEnd( );
|
|
}
|
|
|
|
// parse( input , defs , OUT errors ) - Helper function for the tests
|
|
void parse( char const* input , T_SRDParserDefs const& defs , T_SRDErrors & errors )
|
|
{
|
|
T_SRDParserConfig cfg( defs );
|
|
T_SRDParser parser( cfg );
|
|
parse( input , parser , errors );
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// M_PRINTERR_( index ) - Print an error message
|
|
#define M_PRINTERR_(IDX) \
|
|
do { \
|
|
auto const& _e( errors[ IDX ] ); \
|
|
char err[ _e.error( ).size( ) + 1 ]; \
|
|
err[ sizeof( err ) - 1 ] = 0; \
|
|
memcpy( err , _e.error( ).data( ) , \
|
|
sizeof( err ) - 1 ); \
|
|
printf( "ERR %s l. %u c. %lu\n" , err , \
|
|
_e.location( ).line( ) , \
|
|
_e.location( ).character( ) ); \
|
|
} while ( 0 )
|
|
|
|
// M_CKERR_( index , string , line , character ) - Check an error
|
|
#define M_CKERR_(IDX,STR,L,C) \
|
|
do { \
|
|
auto const& _e( errors[ IDX ] ); \
|
|
CPPUNIT_ASSERT( T_String( STR ) == _e.error( ) ); \
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( L ) , \
|
|
_e.location( ).line( ) ); \
|
|
CPPUNIT_ASSERT_EQUAL( size_t( C ) , \
|
|
_e.location( ).character( ) ); \
|
|
} while ( 0 )
|
|
|
|
// M_PARSE_( defs , input ) - Run the parser using definitions provided by
|
|
// SRDParserTest::make{defs}Defs() on the specified input.
|
|
#define M_PARSE_( DEFS , INPUT ) \
|
|
T_SRDErrors errors; \
|
|
T_SRDParserConfig cfg( make ##DEFS## Defs( ) ); \
|
|
T_SRDParser parser( cfg ); \
|
|
parse( INPUT , parser , errors )
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
T_SRDParserDefs SRDParserTest::makeParseOnlyDefs( )
|
|
{
|
|
using namespace SRD;
|
|
const T_String name( T_String::Pooled( "base" ) );
|
|
const T_String sub( T_String::Pooled( "sub" ) );
|
|
|
|
T_SRDParserDefs d( name );
|
|
d.enumeration( T_String( "enum" ) )
|
|
<< "this" << "that";
|
|
d.context( name )
|
|
<< ( Rule( ) << "simple" << "test" )
|
|
<< ( Rule( ) << Enum( "enum" ) << "works" << ( Opt( ) << "too" )
|
|
<< EnterContext( sub ) )
|
|
<< ( Rule( ) << Int32( ) << "little" << "piggies" )
|
|
<< ( Rule( ) << "too" << "sexy" << "for" << "the"
|
|
<< ( AtLeast( 0 ) << "wabbit" << "too" << "sexy" << "for" << "the" )
|
|
<< "wabbiiiiit" )
|
|
;
|
|
d.context( sub )
|
|
<< ( Rule( ) << "sometimes" );
|
|
return d;
|
|
}
|
|
|
|
T_SRDParserDefs SRDParserTest::makeExecDefs( )
|
|
{
|
|
using namespace SRD;
|
|
const T_String name( T_String::Pooled( "base" ) );
|
|
const T_String sub( T_String::Pooled( "sub" ) );
|
|
static const F_SRDHandler start(
|
|
[]( T_SRDParserData const& d ) -> bool {
|
|
*( d.currentData ) = T_StringBuilder( );
|
|
d.currentData->value< T_StringBuilder >( ) << "START";
|
|
return true;
|
|
} );
|
|
static const F_SRDHandler finish(
|
|
[]( T_SRDParserData const& d ) -> bool {
|
|
d.currentData->value< T_StringBuilder >( ) << " FINISH";
|
|
return true;
|
|
} );
|
|
static const F_SRDHandler op(
|
|
[]( T_SRDParserData const& d ) -> bool {
|
|
d.currentData->value< T_StringBuilder >( )
|
|
<< " OP("
|
|
<< (* d.input)[ 1 ].longValue( )
|
|
<< ')';
|
|
return true;
|
|
} );
|
|
static const F_SRDHandler enter(
|
|
[]( T_SRDParserData const& d ) -> bool {
|
|
*( d.targetData ) = T_StringBuilder( );
|
|
d.targetData->value< T_StringBuilder >( )
|
|
<< " ENTER(" << d.targetContext << ')';
|
|
return true;
|
|
} );
|
|
static const F_SRDHandler exit(
|
|
[]( T_SRDParserData const& d ) -> bool {
|
|
d.targetData->value< T_StringBuilder >( )
|
|
<< d.currentData->value< T_StringBuilder >( )
|
|
<< " EXIT(" << d.currentContext << ')';
|
|
return true;
|
|
} );
|
|
static const F_SRDHandler ctxExec(
|
|
[]( T_SRDParserData const& d ) -> bool {
|
|
d.currentData->value< T_StringBuilder >( )
|
|
<< " CTXEXEC("
|
|
<< (* d.input)[ 2 ].longValue( )
|
|
<< ')';
|
|
return true;
|
|
} );
|
|
static const F_SRDHandler failError(
|
|
[]( T_SRDParserData const& d ) -> bool {
|
|
d.errors.add( "Failure on demand!" , (* d.input)[ 0 ] );
|
|
return true;
|
|
} );
|
|
static const F_SRDHandler failHandler(
|
|
[]( T_SRDParserData const& d ) -> bool {
|
|
d.currentData->value< T_StringBuilder >( )
|
|
<< " FAIL";
|
|
return false;
|
|
} );
|
|
static const F_SRDHandler exitAlone(
|
|
[]( T_SRDParserData const& d ) -> bool {
|
|
d.currentData->value< T_StringBuilder >( )
|
|
<< " LONE-EXIT(" << d.currentContext << ')';
|
|
return true;
|
|
} );
|
|
static const F_SRDHandler enterAlone(
|
|
[]( T_SRDParserData const& d ) -> bool {
|
|
d.currentData->value< T_StringBuilder >( )
|
|
<< " LONE-ENTER(" << d.targetContext << ')';
|
|
return true;
|
|
} );
|
|
|
|
T_SRDParserDefs d( sub );
|
|
d << OnStart( start ) << OnFinish( finish );
|
|
|
|
d.context( sub )
|
|
<< ( Rule( ) << "do-op" << Int32( ) << op )
|
|
<< ( Rule( ) << "fail" << "error" << failError )
|
|
<< ( Rule( ) << "fail" << "handler" << failHandler );
|
|
|
|
d.context( name , sub )
|
|
<< ( Rule( ) << "enter" << EnterContext( sub )
|
|
<< OnEnter( enter ) << OnExit( exit ) )
|
|
<< ( Rule( ) << "enter" << "exec" << Int32( )
|
|
<< ctxExec << EnterContext( sub )
|
|
<< OnEnter( enter ) << OnExit( exit ) )
|
|
<< ( Rule( ) << "handle" << "nothing" << EnterContext( sub ) )
|
|
<< ( Rule( ) << "handle" << "only" << "exit" << EnterContext( sub )
|
|
<< OnExit( exitAlone ) )
|
|
<< ( Rule( ) << "handle" << "only" << "entrance" << EnterContext( sub )
|
|
<< OnEnter( enterAlone ) );
|
|
|
|
d.defaultContext( name );
|
|
return d;
|
|
}
|
|
|
|
T_SRDParserDefs SRDParserTest::makeOLBCDefs( )
|
|
{
|
|
using namespace SRD;
|
|
T_SRDParserDefs d( "base" );
|
|
d << OnStart(
|
|
[]( T_SRDParserData const& d ) -> bool {
|
|
*( d.currentData ) = T_StringBuilder( );
|
|
d.currentData->value< T_StringBuilder >( ) << "START";
|
|
return true;
|
|
} )
|
|
<< OnFinish(
|
|
[]( T_SRDParserData const& d ) -> bool {
|
|
d.currentData->value< T_StringBuilder >( ) << " FINISH";
|
|
return true;
|
|
} );
|
|
|
|
d.context( "base" )
|
|
<< ( Rule( ) << "olbc"
|
|
<< ( AtLeast( 0 ) << ( List( ) << ( Alt( )
|
|
<< "non-context"
|
|
<< "context" ) ) )
|
|
<< EnterContext( "sub" )
|
|
<< OnEnter( []( T_SRDParserData const& d ) -> bool {
|
|
d.currentData->value< T_StringBuilder >( ) << " SUB";
|
|
return true;
|
|
} )
|
|
<< []( T_SRDParserData const& d ) -> bool {
|
|
d.currentData->value< T_StringBuilder >( )
|
|
<< " EXEC("
|
|
<< d.input->size( )
|
|
<< ')';
|
|
return true;
|
|
} )
|
|
;
|
|
d.context( "sub" )
|
|
<< ( Rule( ) << "context" << Opt( Word( "too" ) ) )
|
|
<< ( Rule( ) << "in" << "context" )
|
|
;
|
|
return d;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
void SRDParserTest::testTokenTypes( )
|
|
{
|
|
using F_InitRule = std::function< void( T_SRDContext& ) >;
|
|
using namespace lw::SRD;
|
|
const F_InitRule rules[] = {
|
|
[]( T_SRDContext& c ) { c << ( Rule() << Word( ) ); } ,
|
|
[]( T_SRDContext& c ) { c << ( Rule() << String( ) ); } ,
|
|
[]( T_SRDContext& c ) { c << ( Rule() << Integer( ) ); } ,
|
|
[]( T_SRDContext& c ) { c << ( Rule() << Float( ) ); } ,
|
|
[]( T_SRDContext& c ) { c << ( Rule() << Binary( ) ); } ,
|
|
};
|
|
char const* const inputs[] = {
|
|
"(word)" , "(\"string\")" , "(123)" , "(123.0)" , "([])"
|
|
};
|
|
const uint32_t nTests( sizeof( inputs ) / sizeof( inputs[ 0 ] ) );
|
|
static_assert( nTests == sizeof( rules ) / sizeof( rules[ 0 ] ) ,
|
|
"bad tests" );
|
|
|
|
for ( uint32_t i = 0 ; i < nTests ; i ++ ) {
|
|
T_SRDParserDefs defs( "default" );
|
|
rules[ i ]( defs.context( "default" ) );
|
|
for ( uint32_t j = 0 ; j < nTests ; j ++ ) {
|
|
T_SRDErrors errors;
|
|
parse( inputs[ j ] , defs , errors );
|
|
if ( i == j ) {
|
|
CPPUNIT_ASSERT_EQUAL( 0u , errors.size( ) );
|
|
} else {
|
|
CPPUNIT_ASSERT_EQUAL( 1u , errors.size( ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SRDParserTest::testParse( )
|
|
{
|
|
static const char input[] =
|
|
"( simple test )\n"
|
|
"( this works )\n"
|
|
"( that works too )\n"
|
|
"( this works ( sometimes ) )\n"
|
|
"( 3 little piggies )\n"
|
|
"( too sexy for the wabbit too sexy for the wabbit too sexy for the wabbiiiiit )"
|
|
;
|
|
|
|
T_SRDErrors errors;
|
|
parse( input , makeParseOnlyDefs( ) , errors );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , errors.size( ) );
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
void SRDParserTest::testExecStartRuleFinish( )
|
|
{
|
|
M_PARSE_( Exec , "( do-op 2 )" );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , errors.size( ) );
|
|
T_StringBuilder sb( parser.getData< T_StringBuilder >( ) );
|
|
CPPUNIT_ASSERT( sb == "START OP(2) FINISH" );
|
|
}
|
|
|
|
void SRDParserTest::testExecContext( )
|
|
{
|
|
M_PARSE_( Exec , "( enter ( do-op 2 ) ( do-op 3 ) )" );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , errors.size( ) );
|
|
T_StringBuilder sb( parser.getData< T_StringBuilder >( ) );
|
|
CPPUNIT_ASSERT( T_String( sb ) == T_String(
|
|
"START "
|
|
"ENTER(sub) "
|
|
"OP(2) "
|
|
"OP(3) "
|
|
"EXIT(sub) "
|
|
"FINISH" ) );
|
|
}
|
|
|
|
void SRDParserTest::testExecContextAndRule( )
|
|
{
|
|
M_PARSE_( Exec , "( enter exec 4 ( do-op 2 ) ( do-op 3 ) )" );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , errors.size( ) );
|
|
T_StringBuilder sb( parser.getData< T_StringBuilder >( ) );
|
|
CPPUNIT_ASSERT( T_String( sb ) == T_String(
|
|
"START "
|
|
"ENTER(sub) "
|
|
"OP(2) "
|
|
"OP(3) "
|
|
"EXIT(sub) "
|
|
"CTXEXEC(4) "
|
|
"FINISH" ) );
|
|
}
|
|
|
|
void SRDParserTest::testExecInterruptTop( )
|
|
{
|
|
M_PARSE_( Exec , "( do-op 2 ) ( fail handler )" );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , errors.size( ) );
|
|
T_StringBuilder sb( parser.getData< T_StringBuilder >( ) );
|
|
CPPUNIT_ASSERT( T_String( sb ) == T_String(
|
|
"START "
|
|
"OP(2) "
|
|
"FAIL" ) );
|
|
}
|
|
|
|
void SRDParserTest::testExecInterruptContext( )
|
|
{
|
|
M_PARSE_( Exec , "( enter ( do-op 2 ) ( fail handler ) )" );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , errors.size( ) );
|
|
T_StringBuilder sb( parser.getData< T_StringBuilder >( ) );
|
|
CPPUNIT_ASSERT( T_String( sb ) == T_String( "START" ) );
|
|
}
|
|
|
|
void SRDParserTest::testExecLoneEnter( )
|
|
{
|
|
M_PARSE_( Exec , "( handle only entrance ( do-op 2 ) )" );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , errors.size( ) );
|
|
T_StringBuilder sb( parser.getData< T_StringBuilder >( ) );
|
|
CPPUNIT_ASSERT( T_String( sb ) == T_String(
|
|
"START "
|
|
"LONE-ENTER(sub) "
|
|
"OP(2) "
|
|
"FINISH" ) );
|
|
}
|
|
|
|
void SRDParserTest::testExecLoneExit( )
|
|
{
|
|
M_PARSE_( Exec , "( handle only exit ( do-op 2 ) )" );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , errors.size( ) );
|
|
T_StringBuilder sb( parser.getData< T_StringBuilder >( ) );
|
|
CPPUNIT_ASSERT( T_String( sb ) == T_String(
|
|
"START "
|
|
"OP(2) "
|
|
"LONE-EXIT(sub) "
|
|
"FINISH" ) );
|
|
}
|
|
|
|
void SRDParserTest::testExecNoContextHandler( )
|
|
{
|
|
M_PARSE_( Exec , "( handle nothing ( do-op 2 ) )" );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , errors.size( ) );
|
|
T_StringBuilder sb( parser.getData< T_StringBuilder >( ) );
|
|
CPPUNIT_ASSERT( sb == "START OP(2) FINISH" );
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
void SRDParserTest::testParseOLBCList( )
|
|
{
|
|
M_PARSE_( OLBC , "( olbc ( non-context ) ( non-context ) )" );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , errors.size( ) );
|
|
T_StringBuilder sb( parser.getData< T_StringBuilder >( ) );
|
|
CPPUNIT_ASSERT( sb == "START EXEC(3) FINISH" );
|
|
}
|
|
|
|
void SRDParserTest::testParseOLBCContext( )
|
|
{
|
|
M_PARSE_( OLBC , "( olbc ( in context ) ( in context ) )" );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , errors.size( ) );
|
|
T_StringBuilder sb( parser.getData< T_StringBuilder >( ) );
|
|
CPPUNIT_ASSERT( sb == "START SUB EXEC(1) FINISH" );
|
|
}
|
|
|
|
void SRDParserTest::testParseOLBCListThenContext( )
|
|
{
|
|
M_PARSE_( OLBC , "( olbc ( non-context ) ( in context ) )" );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , errors.size( ) );
|
|
T_StringBuilder sb( parser.getData< T_StringBuilder >( ) );
|
|
CPPUNIT_ASSERT( sb == "START SUB EXEC(2) FINISH" );
|
|
}
|
|
|
|
void SRDParserTest::testParseOLBCListOverrides( )
|
|
{
|
|
M_PARSE_( OLBC , "( olbc ( context ) )" );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , errors.size( ) );
|
|
T_StringBuilder sb( parser.getData< T_StringBuilder >( ) );
|
|
CPPUNIT_ASSERT( sb == "START EXEC(2) FINISH" );
|
|
}
|
|
|
|
void SRDParserTest::testParseOLBCContextOverrides( )
|
|
{
|
|
M_PARSE_( OLBC , "( olbc ( context too ) )" );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 0 ) , errors.size( ) );
|
|
T_StringBuilder sb( parser.getData< T_StringBuilder >( ) );
|
|
CPPUNIT_ASSERT( sb == "START SUB EXEC(1) FINISH" );
|
|
}
|
|
|
|
void SRDParserTest::testParseOLBCFailure( )
|
|
{
|
|
M_PARSE_( OLBC , "( olbc ( incorrect ) )" );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 1 ) , errors.size( ) );
|
|
M_CKERR_( 0 , "'context' or 'in' expected, 'incorrect' found instead." , 1 , 10 );
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
void SRDParserTest::testErrorTopLevelJunk( )
|
|
{
|
|
static const char input[] =
|
|
")\n"
|
|
"( simple test )\n"
|
|
"junk\n"
|
|
"( this works )";
|
|
;
|
|
|
|
T_SRDErrors errors;
|
|
parse( input , makeParseOnlyDefs( ) , errors );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 2 ) , errors.size( ) );
|
|
M_CKERR_( 0 , "'(' expected, ')' found instead." , 1 , 1 );
|
|
M_CKERR_( 1 , "'(' expected, 'junk' found instead." , 3 , 1 );
|
|
}
|
|
|
|
void SRDParserTest::testErrorMismatchInRule( )
|
|
{
|
|
static const char input[] =
|
|
"( simple piggies )\n"
|
|
"( tests suck )\n"
|
|
"( 3 little piggies ate a farmer )\n"
|
|
"( this works when you punch it in the balls )\n"
|
|
"( this works ( sort of ) )\n"
|
|
;
|
|
|
|
T_SRDErrors errors;
|
|
parse( input , makeParseOnlyDefs( ) , errors );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 5 ) , errors.size( ) );
|
|
M_CKERR_( 0 , "'test' expected, 'piggies' found instead." , 1 , 10 );
|
|
M_CKERR_( 1 , "'simple', 'too', 'enum' enum value or INT token expected, 'tests' found instead." , 2 , 3 );
|
|
M_CKERR_( 2 , "')' expected, 'ate' found instead." , 3 , 20 );
|
|
M_CKERR_( 3 , "'(', ')' or 'too' expected, 'when' found instead." , 4 , 14 );
|
|
M_CKERR_( 4 , "'sometimes' expected, 'sort' found instead." , 5 , 16 );
|
|
}
|
|
|
|
void SRDParserTest::testErrorContextJunk( )
|
|
{
|
|
static const char input[] =
|
|
"( this works (sometimes) when you punch it (sometimes) forcefully )";
|
|
;
|
|
|
|
T_SRDErrors errors;
|
|
parse( input , makeParseOnlyDefs( ) , errors );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 2 ) , errors.size( ) );
|
|
M_CKERR_( 0 , "'(' or ')' expected, 'when' found instead." , 1 , 26 );
|
|
// Second error because finding a valid rule resets error recovery
|
|
M_CKERR_( 1 , "'(' or ')' expected, 'forcefully' found instead." , 1 , 56 );
|
|
}
|
|
|
|
void SRDParserTest::testErrorUnterminatedLists( )
|
|
{
|
|
static const char input[] =
|
|
"( this works\n"
|
|
"( sometimes"
|
|
;
|
|
|
|
T_SRDErrors errors;
|
|
parse( input , makeParseOnlyDefs( ) , errors );
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 2 ) , errors.size( ) );
|
|
M_CKERR_( 0 , "Unterminated list." , 1 , 1 );
|
|
M_CKERR_( 1 , "Unterminated list." , 2 , 1 );
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
void SRDParserTest::testErrorNoExec( )
|
|
{
|
|
static const char input[] =
|
|
"( enter ( do-op 2 ) ( invalid ) )"
|
|
;
|
|
|
|
T_SRDErrors errors;
|
|
T_SRDParserConfig cfg( makeExecDefs( ) );
|
|
T_SRDParser parser( cfg );
|
|
parse( input , parser , errors );
|
|
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 1 ) , errors.size( ) );
|
|
CPPUNIT_ASSERT_THROW( parser.getData< T_StringBuilder >( ) ,
|
|
std::bad_cast );
|
|
}
|
|
|
|
void SRDParserTest::testErrorInHandlerTop( )
|
|
{
|
|
static const char input[] =
|
|
"( fail error )"
|
|
;
|
|
|
|
T_SRDErrors errors;
|
|
T_SRDParserConfig cfg( makeExecDefs( ) );
|
|
T_SRDParser parser( cfg );
|
|
parse( input , parser , errors );
|
|
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 1 ) , errors.size( ) );
|
|
M_CKERR_( 0 , "Failure on demand!" , 1 , 3 );
|
|
T_StringBuilder sb( parser.getData< T_StringBuilder >( ) );
|
|
CPPUNIT_ASSERT( T_String( sb ) == T_String( "START" ) );
|
|
}
|
|
|
|
void SRDParserTest::testErrorInHandlerContext( )
|
|
{
|
|
static const char input[] =
|
|
"( enter\n( fail error ) )"
|
|
;
|
|
|
|
T_SRDErrors errors;
|
|
T_SRDParserConfig cfg( makeExecDefs( ) );
|
|
T_SRDParser parser( cfg );
|
|
parse( input , parser , errors );
|
|
|
|
CPPUNIT_ASSERT_EQUAL( uint32_t( 1 ) , errors.size( ) );
|
|
M_CKERR_( 0 , "Failure on demand!" , 2 , 3 );
|
|
T_StringBuilder sb( parser.getData< T_StringBuilder >( ) );
|
|
CPPUNIT_ASSERT( T_String( sb ) == T_String( "START" ) );
|
|
}
|