#include "parse.h"
using namespace goose;
using namespace goose::parse;
bool Resolver::eos() const
{
clearCacheIfNeeded();
return m_tokProvider->eos() && m_lookAheadCache.empty();
}
optional< TermLoc > Resolver::consume()
{
clearCacheIfNeeded();
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< TermLoc > Resolver::consumeUnresolved()
{
clearCacheIfNeeded();
consumeNewLines();
if( !m_lookAheadCache.empty() )
m_lookAheadCache.pop_front();
return m_tokProvider->consume();
}
optional< TermLoc > Resolver::consumeRaw()
{
clearCacheIfNeeded();
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->first );
if( ( !delim || *delim != Delimiter::Newline )
&& !m_lookAheadCache.empty() )
{
m_lookAheadCache.pop_front();
}
return tok;
}
optional< TermLoc > Resolver::lookAhead( size_t distance )
{
clearCacheIfNeeded();
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->first );
if( delim && *delim == Delimiter::Newline )
continue;
m_lookAheadCache.emplace_back( resolve( *tok ) );
}
return m_lookAheadCache[distance];
}
optional< TermLoc > Resolver::lookAheadUnresolved( size_t distance )
{
size_t i = distance;
while( auto tok = m_tokProvider->lookAhead( i++ ) )
{
const auto* delim = get_if< Delimiter >( &tok->first );
if( !delim || *delim != Delimiter::Newline )
return tok;
}
return nullopt;
}
optional< TermLoc > Resolver::lookAheadRaw( size_t distance )
{
return m_tokProvider->lookAhead( distance );
}
void Resolver::clearLookAheadCache() const
{
m_lookAheadCache.clear();
m_valueStoreVersion = m_context.env()->valueStoreVersion();
}
TermLoc Resolver::resolve( const TermLoc& t ) const
{
auto identifier = Decompose( t.first, Val< StringId >() );
if( !identifier )
return t;
Term result;
DiagnosticsContext dc( t.second, "Referenced here." );
auto valId = AppendToVectorTerm( m_context.identity(), t.first );
switch( m_context.env()->retrieveValue( valId, m_context.identity(), result ) )
{
case sema::Env::Status::NoMatch:
return t;
case sema::Env::Status::AmbiguousMatch:
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
t.second, "ambiguous match for symbol." );
return t;
default:
break;
}
return TermLoc( result, t.second );
}
void Resolver::consumeNewLines()
{
while( auto tok = lookAheadRaw() )
{
const auto* delim = get_if< Delimiter >( &tok->first );
if( !delim )
return;
if( *delim != Delimiter::Newline )
return;
m_tokProvider->consume();
}
}
Generator< TermLoc > Resolver::consumeUnit()
{
auto tok = consumeRaw();
if( !tok )
co_return;
const auto* delim = get_if< Delimiter >( &tok->first );
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:
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
tok->second, "mismatched ')'.", 0 );
break;
case Delimiter::CloseBrace:
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
tok->second, "mismatched '}'.", 0 );
break;
case Delimiter::CloseBracket:
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
tok->second, "mismatched ']'.", 0 );
break;
default:
co_yield move( *tok );
break;
}
}
Generator< TermLoc > Resolver::consumeBlock( Delimiter end )
{
while( const auto& tok = lookAheadRaw() )
{
const auto* delim = get_if< Delimiter >( &tok->first );
if( delim )
{
if( *delim == end )
{
co_yield *consumeRaw();
co_return;
}
else
{
switch( *delim )
{
case Delimiter::CloseParen:
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
tok->second, "mismatched ')'.", 0 );
co_return;
case Delimiter::CloseBrace:
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
tok->second, "mismatched '}'.", 0 );
co_return;
case Delimiter::CloseBracket:
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
tok->second, "mismatched ']'.", 0 );
co_return;
default:
break;
}
}
}
co_yield consumeUnit();
}
switch( end )
{
case Delimiter::CloseParen:
DiagnosticsManager::GetInstance().emitLexerErrorMessage( currentLocation(), "missing ')'." );
break;
case Delimiter::CloseBrace:
DiagnosticsManager::GetInstance().emitLexerErrorMessage( currentLocation(), "missing '}'." );
break;
case Delimiter::CloseBracket:
DiagnosticsManager::GetInstance().emitLexerErrorMessage( currentLocation(), "missing ']'." );
break;
default:
break;
}
}