Goose  Artifact [a076c3d952]

Artifact a076c3d95286221908e929f8291a23421417ec66dfea100b63d5aa0405b304a3:

  • File bs/builtins/statements/using.cpp — part of check-in [50da1401ee] at 2019-07-13 23:30:57 on branch trunk —
    • Added missing quoting around function type's domain.
    • Detect and error out when an using expression is invoked recursively.
    • Fixed passing of generic values to builtin funcs (the pattern used was incorrect and didn't work when passing some types).
    • Added an unification rule to prevent single element tuples from being peeled off when unified against generic values.
    • Implemented an api function to compile a file into a function of a specified type and signature.
    (user: achavasse size: 5009)

#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 nameTerm = p.resolver()->consume();
            if( !nameTerm )
            {
                cout << t.location() << ": expected an identifier after 'using'.\n";
                return false;
            }

            const auto* name = get_if< StringId >( &nameTerm->content() );
            if( !name )
            {
                cout << nameTerm->location() << ": expected an identifier after 'using'.\n";
                return false;
            }

            auto eqTerm = p.resolver()->consumeUnresolved();
            if( !eqTerm )
            {
                cout << eqTerm->location() << ": expected '=' after 'using " << name->c_str() << "'.\n";
                return false;
            }

            const auto* eq = get_if< StringId >( &eqTerm->content() );
            if( !eq || *eq != "="_sid )
            {
                cout << eqTerm->location() << ": expected '=' after 'using " << name->c_str() << "'.\n";
                return false;
            }

            p.resolver()->consumeNewLines();

            // Store all the units following the equal sign until the next semicolon or newline.
            vector< Term > toks;
            while( !p.resolver()->eos() )
            {
                const auto& tok = p.resolver()->lookAheadRaw();
                if( !tok )
                    break;

                const auto* delim = get_if< Delimiter >( &tok->content() );
                if( delim && *delim == Delimiter::Newline )
                {
                    p.resolver()->consumeRaw();
                    break;
                }

                const auto* id = get_if< StringId >( &tok->content() );
                if( id && *id == ";"_sid )
                    break;

                auto g = p.resolver()->consumeUnit();
                move( g.begin(), g.end(), back_inserter( toks ) );
            }

            if( toks.empty() )
            {
                cout << eqTerm->location() << ": expected an expression after 'using " << name->c_str() << " ='.\n";
                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 );
            context.env()->addTransitiveImport( context.identity(), localIdentity );

            variant< vector< Term >, Term > content = move( toks );

            bool bInUse = false;

            auto UsingValProvider = [content, localIdentity, bInUse]( const Env& e, const Term& identity, const Term& contextId, Term& result ) mutable
            {
                if( holds_alternative< vector< Term > >( content ) )
                {
                    if( bInUse )
                    {
                        cout << "error: recursive using expression.\n";
                        return Env::Status::NoMatch;
                    }

                    bInUse = true;

                    Context localContext( make_shared< Env >( e ), localIdentity );
                    auto tokProvider = lex::MakeVectorAdapter( get< vector< Term > >( content ) );
                    auto r = make_shared< parse::Resolver >( tokProvider, localContext );
                    Parser p( r );

                    if( !p.parseExpression() )
                    {
                        cout << "invalid using expression.\n";
                        return Env::Status::NoMatch;
                    }

                    auto result = p.peekLastValue();
                    if( !result )
                    {
                        cout << "invalid using expression.\n";
                        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 );
    }
}