#include "parse.h"
using namespace empathy;
using namespace empathy::parse;
bool Resolver::eos() const
{
return m_tokProvider->eos() && m_lookAheadCache.empty();
}
optional< Term > Resolver::consume()
{
consumeNewLines();
auto tok = m_tokProvider->consume();
if( m_lookAheadCache.empty() )
{
if( !tok )
return nullopt;
return resolve( *tok );
}
auto t = move( m_lookAheadCache.front() );
m_lookAheadCache.pop_front();
return t;
}
optional< Term > Resolver::consumeUnresolved()
{
consumeNewLines();
if( !m_lookAheadCache.empty() )
m_lookAheadCache.pop_front();
return m_tokProvider->consume();
}
optional< Term > Resolver::consumeRaw()
{
auto tok = m_tokProvider->consume();
// If we already cached a resolved value, discard it
// unless the value we just consumed is a newline,
// in which case there is no corresponding entry in the cache.
const auto* delim = get_if< Delimiter >( &tok->content() );
if( ( !delim || *delim != Delimiter::Newline )
&& !m_lookAheadCache.empty() )
{
m_lookAheadCache.pop_front();
}
return tok;
}
optional< Term > Resolver::lookAhead( size_t distance )
{
size_t i = 0;
while( m_lookAheadCache.size() < ( distance + 1 ) )
{
auto tok = m_tokProvider->lookAhead( i++ );
if( !tok )
return nullopt;
// Skip newlines
const auto* delim = get_if< Delimiter >( &tok->content() );
if( delim && *delim == Delimiter::Newline )
continue;
m_lookAheadCache.emplace_back( resolve( *tok ) );
}
return m_lookAheadCache[distance];
}
optional< Term > Resolver::lookAheadUnresolved( size_t distance )
{
size_t i = distance;
while( auto tok = m_tokProvider->lookAhead( i++ ) )
{
const auto* delim = get_if< Delimiter >( &tok->content() );
if( !delim || *delim != Delimiter::Newline )
return tok;
}
return nullopt;
}
optional< Term > Resolver::lookAheadRaw( size_t distance )
{
return m_tokProvider->lookAhead( distance );
}
Term Resolver::resolve( const Term& t ) const
{
auto identifier = Decompose( t, Val< StringId >() );
if( !identifier )
return t;
Term result;
auto valId = AppendToVectorTerm( m_context.identity(), t );
switch( m_context.env()->retrieveValue( valId, m_context, result ) )
{
case sema::Env::Status::NoMatch:
return t;
case sema::Env::Status::AmbiguousMatch:
cout << t.location() << ": ambiguous match for symbol '" << t << "'.\n";
result = t;
result.setFaulty();
return t;
}
return result;
}
void Resolver::consumeNewLines()
{
while( auto tok = lookAheadRaw() )
{
const auto* delim = get_if< Delimiter >( &tok->content() );
if( !delim )
return;
if( *delim != Delimiter::Newline )
return;
m_tokProvider->consume();
}
}
Generator< Term > Resolver::consumeUnit()
{
auto tok = consumeRaw();
if( !tok )
co_return;
const auto* delim = get_if< Delimiter >( &tok->content() );
if( !delim )
{
co_yield move( *tok );
co_return;
}
switch( *delim )
{
case Delimiter::OpenParen:
co_yield move( *tok );
co_yield consumeBlock( Delimiter::CloseParen );
break;
case Delimiter::OpenBrace:
co_yield move( *tok );
co_yield consumeBlock( Delimiter::CloseBrace );
break;
case Delimiter::OpenBracket:
co_yield move( *tok );
co_yield consumeBlock( Delimiter::CloseBracket );
break;
case Delimiter::CloseParen:
cout << tok->location() << ": mismatched ')'\n";
break;
case Delimiter::CloseBrace:
cout << tok->location() << ": mismatched '}'\n";
break;
case Delimiter::CloseBracket:
cout << tok->location() << ": mismatched ']'\n";
break;
default:
co_yield move( *tok );
break;
}
}
Generator< Term > Resolver::consumeBlock( Delimiter end )
{
while( const auto& tok = lookAheadRaw() )
{
const auto* delim = get_if< Delimiter >( &tok->content() );
if( delim )
{
if( *delim == end )
{
co_yield *consumeRaw();
co_return;
}
else
{
switch( *delim )
{
case Delimiter::CloseParen:
cout << tok->location() << ": mismatched ')'\n";
co_return;
case Delimiter::CloseBrace:
cout << tok->location() << ": mismatched '}'\n";
co_return;
case Delimiter::CloseBracket:
cout << tok->location() << ": mismatched ']'\n";
co_return;
}
}
}
co_yield consumeUnit();
}
}