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 ) );
}
}
|