#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"
#include "builtins/helpers.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::parse;
namespace goose::builtins
{
void SetupHIfStmt( Env& e )
{
auto handleHIf = []( Parser& p, LocationId locationId, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence::IfStmt ) )
{
dm.emitSyntaxErrorMessage( locationId, "expected an expression following the #if statement1.", 0 );
return false;
}
auto condVal = np.popValue();
if( !condVal )
{
dm.emitSyntaxErrorMessage( locationId, "expected an expression following the #if statement2.", 0 );
return false;
}
const auto& context = p.context();
auto converted = ConvertValueToType( context, *condVal, GetValueType< bool >() );
if( holds_alternative< ValUnifyError >( converted ) )
{
switch( get< ValUnifyError >( converted ) )
{
case ValUnifyError::NoSolution:
dm.emitErrorMessage( condVal->locationId(), "the #if condition doesn't evaluate to a bool constant." );
break;
case ValUnifyError::Ambiguous:
dm.emitErrorMessage( condVal->locationId(), "ambiguous #if condition bool conversion." );
break;
}
return false;
}
auto condBool = get< Value >( converted );
if( !condBool.isConstant() )
{
dm.emitErrorMessage( condVal->locationId(), "the #if condition doesn't evaluate to a bool constant." );
return false;
}
auto cond = FromValue< bool >( condBool );
// If the cond is invalid, both blocks will be excluded, but parsing will continue,
// to give the best chance of a graceful recovery of the parsing and hopefully
// be able to detect more legit errors before we bail out.
// We do mark the current diagnostics context as bust, however, since what we're going to
// miss in the #if block that should have been parsed is likey to cause cascading errors
// in the current scope.
if( !cond )
dm.emitSyntaxErrorMessage( condVal->locationId(), "the #if condition doesn't evaluate to a bool constant." );
auto next = p.resolver()->lookAheadUnresolved();
if( !next )
{
dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "brace block expected.", 0 );
return false;
}
auto decomp = Decompose( next->first, Val< Delimiter >() );
if( !decomp || *decomp != Delimiter::OpenBrace )
{
dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "brace block expected.", 0 );
return false;
}
// Deal with the then block.
if( cond && *cond )
{
// Condition is true: parse the 'then' block.
if( !p.parseBraceBlock() )
return false;
}
else
{
// Condition is false: skip the 'then' block.
auto gen = p.resolver()->consumeUnit();
for_each( gen.begin(), gen.end(), []( auto&& ){} );
}
next = p.resolver()->lookAheadUnresolved();
if( next )
{
const auto* nextSid = get_if< StringId >( &next->first );
if( nextSid && *nextSid == "#else"_sid )
{
p.resolver()->consumeUnresolved();
auto next = p.resolver()->lookAheadUnresolved();
auto decomp = Decompose( next->first, Val< Delimiter >() );
if( !decomp || *decomp != Delimiter::OpenBrace )
{
dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "brace block expected.", 0 );
return false;
}
// Deal with the else block.
if( cond && !*cond )
{
// Condition is false: parse the 'else' block.
if( !p.parseBraceBlock() )
return false;
}
else
{
// Condition is true: skip the 'else' block.
auto gen = p.resolver()->consumeUnit();
for_each( gen.begin(), gen.end(), []( auto&& ){} );
}
}
}
return true;
};
RegisterRule( e, "#if"_sid, Rule( handleHIf ) );
}
}