#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"
using namespace empathy;
using namespace empathy::ir;
using namespace empathy::parse;
namespace empathy::builtins
{
void SetupUsingStmt( Env& e )
{
auto handleUsing = []( Parser& p, const Term& t, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
auto nameTerm = p.resolver()->consume();
if( !nameTerm )
{
// TODO: use the current parsing position before consume() as the location.
dm.emitErrorMessage( 0, "expected an identifier." );
return false;
}
const auto* name = get_if< StringId >( &nameTerm->first );
if( !name )
{
dm.emitErrorMessage( nameTerm->second, "expected an identifier." );
return false;
}
auto eqTerm = p.resolver()->consumeUnresolved();
if( !eqTerm )
{
dm.emitErrorMessage( 0, "expected '='." );
return false;
}
const auto* eq = get_if< StringId >( &eqTerm->first );
if( !eq || *eq != "="_sid )
{
dm.emitErrorMessage( eqTerm->second, "expected '='." );
return false;
}
p.resolver()->consumeNewLines();
// Store all the units following the equal sign until the next semicolon or newline.
vector< TermLoc > toks;
while( !p.resolver()->eos() )
{
const auto& tok = p.resolver()->lookAheadRaw();
if( !tok )
break;
const auto* delim = get_if< Delimiter >( &tok->first );
if( delim && *delim == Delimiter::Newline )
{
p.resolver()->consumeRaw();
break;
}
const auto* id = get_if< StringId >( &tok->first );
if( id && *id == ";"_sid )
break;
auto g = p.resolver()->consumeUnit();
move( g.begin(), g.end(), back_inserter( toks ) );
}
if( toks.empty() )
{
// TODO maybe use the current location after consuming the equal sign as the location here
dm.emitErrorMessage( eqTerm->second, "expected an expression." );
return false;
}
const auto& context = p.resolver()->context();
// Create a local identity for the constant, from which the current identity will be visible.
// This is to avoid anything declared from within the using expression to leak outside,
// and also to make sure that whenever it will be parsed, it will have access to the current
// context in which it has been declared.
auto localIdentity = AppendToVectorTerm( context.identity(), nameTerm->first );
context.env()->addVisibilityRule( context.identity(), localIdentity );
variant< vector< TermLoc >, Term > content = move( toks );
auto nameSid = *name;
bool bInUse = false;
auto UsingValProvider = [content, localIdentity, bInUse, nameSid]( Env& e, const Term& identity, const Term& contextId, Term& result ) mutable
{
auto& dm = DiagnosticsManager::GetInstance();
if( holds_alternative< vector< TermLoc > >( content ) )
{
if( bInUse )
{
// TODO: decide what location to use here. Perhaps the name's location?
// We just want to indicate which using expression is at fault here, the most
// useful part of that diagnosis is going to be the context.
dm.emitErrorMessage( 0, "recursive using expression." );
return Env::Status::NoMatch;
}
bInUse = true;
Context localContext( e.shared_from_this(), localIdentity );
auto tokProvider = lex::MakeVectorAdapter( get< vector< TermLoc > >( content ) );
auto r = make_shared< parse::Resolver >( tokProvider, localContext );
Parser p( r );
if( !p.parseExpression() )
{
// TODO: use the name's location. This is a fallback message in case
// the oparsing somehow didn't emit a more useful one that caused that
// one to be silenced.
// TODO: Also perhaps return a poison value instead of nothing?
dm.emitErrorMessage( 0, "invalid expression." );
return Env::Status::NoMatch;
}
auto result = p.peekLastValue();
if( !result )
{
// TODO same remarks as above.
dm.emitErrorMessage( 0, "invalid expression." );
return Env::Status::NoMatch;
}
if( !result->isConstant() )
{
// TODO same remarks as above re: location ans poisoning,
// that message is actually important though.
dm.emitErrorMessage( 0, "using expression doesn't evaluate to a constant." );
return Env::Status::NoMatch;
}
content = ValueToIRExpr( *result );
bInUse = false;
}
result = get< Term >( content );
return Env::Status::Success;
};
context.env()->storeValue( localIdentity, ANYTERM( _ ),
make_shared< Env::ValueProvider >( move( UsingValProvider ) ) );
return true;
};
Rule r( handleUsing );
auto ruleVal = ToValue( move( r ) );
auto ruleTerm = ValueToIRExpr( ruleVal );
e.storeValue( AppendToVectorTerm( RootIdentity(), TSID( using ) ), ANYTERM( _ ), ruleTerm );
}
}