Goose  Diff

Differences From Artifact [6ca4ddaa93]:

  • File bs/builtins/operators/dollar.cpp — part of check-in [23e0cd5dc7] at 2019-02-18 21:54:23 on branch trunk — Parser:
    • Make the resolver skip newlines by default, and provide an additional "raw" api to retrieve unresolved tokens without skipping newlines.
    • Parsing rules now only return a bool to indicate whether they were successful, and can push any number of values themselves as needed.
    • Stop trying to shoehorn implicit separators in the pratt parser. Instead, use the pratt parser only for expressions, and use a different parsing loop for sequences of expressions (ie for brace blocks and top level).
    (user: achavasse size: 2211)

To Artifact [944f75d56c]:

  • File bs/builtins/operators/dollar.cpp — part of check-in [1b651c004f] at 2019-02-20 21:22:08 on branch trunk — Templates: implemented the infix variant of the dollar operator as a shortcut to construct TDecls where the rhs is a TVar. (user: achavasse size: 4423)

1
2
3
4
5
6
7



















































































































8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127











128



































129
130
131







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-



#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"

using namespace empathy;
using namespace empathy::ir;
using namespace empathy::parse;
using namespace empathy::builtins;

// Dollar is a low level operator: it doesn't expect a value rhs operand,
// but just an identifier ir element.
// So we can't use the convenient operator wrapper rule.
//
// Also, it works both as a prefix operator to construct a TVar,
// and as an infix operator (with a decl or TEXpr as the left value) to
// construct a Decl of the form "<type> $FOO" or a TDecl of the form
// "<TExpr> $FOO" (for instance $T $FOO)
namespace
{
    optional< Value > BuildOrRetrieveTVar( const Context& c, const StringId& name )
    {
        // Template name bindings are stored with a "$$" prefix so they don't
        // collide with regular names.
        auto captureIdentity = AppendToVectorTerm( c.identity(),
            TERM( "$$"_sid ), TERM( name ) );

        // Look up the TVar name. If it exists, then we're inside of a template function
        // body, refering to a bound TVar, so we just return the stored value.
        // Otherwise, we're in a template declaration and have to construct a new TVar.
        Term result;

        switch( c.env()->retrieveValue( captureIdentity, c, result ) )
        {
            case sema::Env::Status::Success:
                return ValueFromIRExpr( result );

            case sema::Env::Status::AmbiguousMatch:
                cout << "unexpected ambiguous match when resolving '$" << name << "'.\n";
                return nullopt;
        }

        // No bound value was found: construct a TVar.
        return ToValue( TVar( name ) );
    }

    bool ParsePrefixDollarOperator( Parser& p, const Term& t, uint32_t prec )
    {
        auto nameTerm = p.resolver()->consumeUnresolved();
        if( !nameTerm )
        {
            cout << t.location() << ": expected an identifier after '$'.\n";
            return false;
        }

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

        const auto& c = p.resolver()->context();
        if( auto val = BuildOrRetrieveTVar( c, *name ) )
        {
            p.push( move( *val ) );
            return true;
        }

        return false;
    }

    optional< uint32_t > InfixDollarPrecedence( const Parser& p, const Term& t )
    {
        const auto& leftVal = p.lastValue();
        if( !leftVal )
            return nullopt;

        auto nameTerm = p.resolver()->lookAheadUnresolved();
        if( !nameTerm )
            return nullopt;

        if( !holds_alternative< StringId >( nameTerm->content() ) )
            return nullopt;

        if( leftVal->type() != TSID( type ) && !builtins::IsTExpr( *leftVal ) )
            return nullopt;

        // The infix dollar operator is a syntactic shortcut meant to behave like we are applying
        // a TExpr to a type or another TExpr (just like a regular decl), so it have the application precedence.
        return precedence::Application;
    }

    bool ParseInfixDollarOperator( Parser& p, const Term& t, uint32_t prec )
    {
        const auto& leftVal = p.lastValue();
        if( !leftVal )
            return false;

        auto nameTerm = p.resolver()->consumeUnresolved();
        const auto* name = get_if< StringId >( &nameTerm->content() );

        const auto& c = p.resolver()->context();
        auto val = BuildOrRetrieveTVar( c, *name );
        if( !val )
            return false;

        // If the TVar was already bound, we may end up with something in val
        // that isn't a TVar, which is an error.
        if( !IsTVar( *val ) )
        {
            cout << nameTerm->location() << ": expected an unbound identifier after '$'.\n";
            return false;
        }

        if( leftVal->type() != TSID( type ) && !IsTExpr( *leftVal )  )
            return false;

        auto typeOrTExpr = ValueToIRExpr( *p.pop() );
        p.push( ToValue( TDecl( move( typeOrTExpr ), ValueToIRExpr( *val ) ) ) );
        return true;
    }
}

namespace empathy::builtins
{
    void SetupDollarOp( Env& e )
    {
        // Dollar is a low level operator: it doesn't expect a value rhs operand, but just an identifier ir element.
        // So we can't use the convenient operator wrapper rule.
        Rule r( []( Parser& p, const Term& t, uint32_t prec )
        {
            auto nameTerm = p.resolver()->consumeUnresolved();
            if( !nameTerm )
            {
                cout << t.location() << ": expected an identifier after '$'.\n";
                return false;
            }

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

            const auto& c = p.resolver()->context();

            // Template name bindings are stored with a "$$" prefix so they don't
            // collide with regular names.
            auto captureIdentity = AppendToVectorTerm( c.identity(),
                TERM( "$$"_sid ), TERM( *name ) );

            // Look up the TVar name. If it exists, then we're inside of a template function
            // body, refering to a bound TVar, so we just return the stored value.
            // Otherwise, we're in a template declaration and have to construct a new TVar.
            Term result;

            switch( c.env()->retrieveValue( captureIdentity, c, result ) )
            {
                case sema::Env::Status::Success:
                    p.push( ValueFromIRExpr( result ) );
                    return true;

                case sema::Env::Status::AmbiguousMatch:
                    cout << t.location() << ": unexpected ambiguous match when resolving '$" << *name << "'.\n";
                    return false;
            }

            // No bound value was found: construct a TVar.
            p.push( ToValue( TVar( *name ) ) );
            return true;
        } );

        RegisterRule( e, "$"_sid, move( r ) );
    }
}