#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::parse;
using namespace goose::builtins;
namespace
{
// "ref" is actually an infix operator, which can take either
// a constant value of type "AccessLevel" or a TVar on the lhs,
// and a type or a TExpr on the rhs.
optional< uint32_t > RefOpPrecedence( const Parser& p )
{
const auto& leftVal = p.peekLastValue();
if( !leftVal )
return nullopt;
if( leftVal->type() != GetValueType< AccessLevel >() && !IsTVar( *leftVal ) )
return nullopt;
return precedence::RefType;
}
bool ParseRefOp( Parser& p, LocationId locationId, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
auto accessLevel = p.popValue();
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence::RefType ) )
{
dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(),
"expected a type.", 0 );
p.pushValue( PoisonValue() );
return true;
}
auto type = np.popType();
if( !type || ( !IsTExpr( *type ) && !IsType( p.context(), *type ) ) )
{
dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(),
"expected a type.", 0 );
p.pushValue( PoisonValue() );
return true;
}
if( !type->isType() && !IsTExpr( *type ) )
type = ToType( p.context(), *type );
// If the accessLevel is a TVar, turn it into a TDecl, so we can constrain it to
// the AccessLevel type. Otherwise, it woud be possible to do all kind of
// funky stuff to create refs with pretty much any old value of any type as
// the access level.
if( IsTVar( *accessLevel ) )
{
auto tv = *FromValue< TVar >( *accessLevel );
accessLevel = ToValue( TDecl( GetValueType< AccessLevel >(), tv.name() ) )
.setLocationId( accessLevel->locationId() );
}
auto loc = Location::CreateSpanningLocation( accessLevel->locationId(), type->locationId() );
ReferenceType rt( ValueToEIR( *type ), ValueToEIR( *accessLevel ) );
p.pushValue( ToDeclValue( rt ).setLocationId( loc ) );
return true;
}
}
namespace goose::builtins
{
void SetupRefTypeParsingRule( Env& e )
{
DefineConstant( e, "mut"_sid, MutAccessLevel() );
DefineConstant( e, "const"_sid, ConstAccessLevel() );
DefineConstant( e, "temp"_sid, TempAccessLevel() );
RegisterRule( e, "ref"_sid, Rule( RefOpPrecedence, ParseRefOp ) );
}
}