-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathXsTokenExpressionProvider.cs
More file actions
142 lines (116 loc) · 5.02 KB
/
Copy pathXsTokenExpressionProvider.cs
File metadata and controls
142 lines (116 loc) · 5.02 KB
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
132
133
134
135
136
137
138
139
140
141
142
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using Hyperbee.Templating.Compiler;
using Hyperbee.Templating.Text;
using Hyperbee.XS;
using Hyperbee.XS.Core;
using static System.Linq.Expressions.Expression;
namespace Hyperbee.Templating.Provider.XS.Compiler;
public delegate TokenExpression CompileLambda( Expression<TokenExpression> lambda );
public sealed class XsTokenExpressionProvider : ITokenExpressionProvider
{
private readonly ConcurrentDictionary<string, TokenExpression> TokenExpressions = new();
private readonly CompileLambda _compile;
private readonly XsParser _xsParser;
public XsTokenExpressionProvider(
CompileLambda compile = null,
TypeResolver typeResolver = null,
List<IParseExtension> extensions = null )
{
_compile = compile ?? (lambda => lambda.Compile());
typeResolver ??= new MemberTypeResolver( ReferenceManager.Create() );
_xsParser = new XsParser(
new XsConfig( typeResolver ) { Extensions = extensions ?? [] }
);
}
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public TokenExpression GetTokenExpression( string codeExpression, MemberDictionary members )
{
return TokenExpressions.GetOrAdd( codeExpression, _ => Compile( codeExpression ) );
}
[MethodImpl( MethodImplOptions.AggressiveInlining )]
public void Reset()
{
TokenExpressions.Clear();
}
private TokenExpression Compile( ReadOnlySpan<char> codeExpression )
{
var start = codeExpression.IndexOf( "=>" );
var argument = codeExpression[..start].Trim().ToString();
var body = codeExpression[(start + 2)..].Trim().ToString();
var scope = new ParseScope();
try
{
scope.EnterScope( FrameType.Method );
var codeParameter = Parameter( typeof( IReadOnlyMemberDictionary ), argument );
scope.Variables.Add( argument, codeParameter );
var expression = _xsParser.Parse( body, scope: scope );
var expressionBody = expression as BlockExpression;
var lambdaParameter = Parameter( typeof( IReadOnlyMemberDictionary ) );
// create a new block expression assigning the parameter to the argument
var expressions = new List<Expression> { Assign( codeParameter, lambdaParameter ) };
if ( expressionBody == null )
expressions.Add( expression );
else
expressions.AddRange( expressionBody.Expressions );
var lambda = Lambda<TokenExpression>(
Convert(
Block(
expressionBody?.Variables,
expressions
),
typeof( object )
),
lambdaParameter );
return _compile( lambda );
}
finally
{
scope.ExitScope();
}
}
public class MemberTypeResolver : TypeResolver
{
private static readonly MethodInfo MemberInvoke = typeof( IReadOnlyMemberDictionary )
.GetMethod( nameof( IReadOnlyMemberDictionary.Invoke ), [typeof( string ), typeof( object[] )] )!;
private static readonly MethodInfo MemberGetValueAs = typeof( IReadOnlyMemberDictionary )
.GetMethod( nameof( IReadOnlyMemberDictionary.GetValueAs ), [typeof( string )] )!;
private static readonly PropertyInfo MemberIndexer = typeof( MemberDictionary )
.GetProperties()
.First( x => x.GetIndexParameters().Length > 0 );
public MemberTypeResolver( ReferenceManager referenceManager ) : base( referenceManager ) { }
// Resolves a member expression for the given target expression.
//
// 1. x => x.someProp to x["someProp"]
// 2. x => x.someProp<T> to x.GetValueAs<T>("someProp")
// 3. x => x.someMethod(..) to x.Invoke("someMethod", ..)
public override Expression RewriteMemberExpression( Expression targetExpression, string name, IReadOnlyList<Type> typeArgs, IReadOnlyList<Expression> args )
{
if ( targetExpression.Type != typeof( IReadOnlyMemberDictionary ) )
return base.RewriteMemberExpression( targetExpression, name, typeArgs, args );
if ( args != null )
{
return Call(
targetExpression,
MemberInvoke,
Constant( name ),
NewArrayInit( typeof( object ), args )
);
}
if ( typeArgs != null )
{
return Call(
targetExpression,
MemberGetValueAs
.MakeGenericMethod( typeArgs[0] ),
Constant( name ) );
}
return Property(
Convert( targetExpression, typeof( MemberDictionary ) ),
MemberIndexer,
Constant( name ) );
}
}
}