在Roslyn合成lambda';s C#编译器
我一直在Roslyn试用最近开源的C#编译器,看看是否可以添加语言功能 我现在尝试添加一些语法sugar,一个新的前缀操作符,基本上是某种模式的简写。目前,我在在Roslyn合成lambda';s C#编译器,c#,roslyn,C#,Roslyn,我一直在Roslyn试用最近开源的C#编译器,看看是否可以添加语言功能 我现在尝试添加一些语法sugar,一个新的前缀操作符,基本上是某种模式的简写。目前,我在不安全的上下文之外,依靠预先存在的&地址 我要展开的模式如下:&n相当于: Property.Bind(v => n = v, () => n) 假定方法Property.Bind在库中可用,其签名为: public static IProperty<T> Bind<T>(Action<T>
不安全的
上下文之外,依靠预先存在的&
地址
我要展开的模式如下:&n
相当于:
Property.Bind(v => n = v, () => n)
假定方法Property.Bind
在库中可用,其签名为:
public static IProperty<T> Bind<T>(Action<T> set, Func<T> get)
所以(大概吧!)现在我有了第一个lambda的赋值块,但是获得一个完整的BoundLambda
看起来是一个全新的挑战
我想知道:有没有一种方法可以“欺骗”这种语法糖,让解析器/绑定器处理一个C#字符串,就好像它出现在实际代码的位置一样?这样,就不需要手动构建所有零件并将它们缝合在一起。毕竟,现有的编译器非常适合这种情况 更新:我已经确定了一个名为
SyntaxTemplate
的新类,它是不可变的,因此可以静态创建并重用。e、 g
private static readonly SyntaxTemplate _pointerIndirectionTemplate
= new SyntaxTemplate("p.Value");
private static readonly SyntaxTemplate _propertyReferenceTemplate
= new SyntaxTemplate("System.Property.Bind(__v_pr__ => o = __v_pr__, () => o)");
private static readonly SyntaxTemplate _propertyReferenceTypeTemplate
= new SyntaxTemplate("System.IProperty<T>");
private static readonly SyntaxTemplate _enumerableTypeTemplate
= new SyntaxTemplate("System.Collections.Generic.IEnumerable<T>");
或对于类型:
if (this.IsIndirectlyInIterator || !this.InUnsafeRegion)
return BindNamespaceOrTypeOrAliasSymbol(
_enumerableTypeTemplate.Replace("T", node.ElementType).Syntax,
diagnostics, basesBeingResolved, suppressUseSiteDiagnostics);
SyntaxTemplate
如下所示:
internal class SyntaxTemplate
{
public ExpressionSyntax Syntax { get; private set; }
private readonly ImmutableDictionary<string, ImmutableList<IdentifierNameSyntax>> _identifiers;
public SyntaxTemplate(string source)
{
Syntax = SyntaxFactory.ParseExpression(source);
var identifiers = ImmutableDictionary<string, ImmutableList<IdentifierNameSyntax>.Builder>.Empty.ToBuilder();
foreach (var node in Syntax.DescendantNodes().OfType<IdentifierNameSyntax>())
{
ImmutableList<IdentifierNameSyntax>.Builder list;
if (!identifiers.TryGetValue(node.Identifier.Text, out list))
list = identifiers[node.Identifier.Text] =
ImmutableList<IdentifierNameSyntax>.Empty.ToBuilder();
list.Add(node);
}
_identifiers = identifiers.ToImmutableDictionary(
p => p.Key, p => p.Value.ToImmutableList());
}
private SyntaxTemplate(ExpressionSyntax syntax,
ImmutableDictionary<string, ImmutableList<IdentifierNameSyntax>> identifiers)
{
Syntax = syntax;
_identifiers = identifiers;
}
public SyntaxTemplate Replace(string identifier, SyntaxNode value)
{
return new SyntaxTemplate(
Syntax.ReplaceNodes(_identifiers[identifier], (o1, o2) => value),
_identifiers.Remove(identifier));
}
}
示例用法,仅包装上面的第一个示例:
if (!operand.Type.IsPointerType())
return RedirectDiagnostics(diagnostics, node, redirected =>
BindExpression(_pointerIndirectionTemplate.Replace("p", node.Operand).Syntax, redirected));
现在红色的曲线可以正常工作了(在真正的编译中,错误消息上的行号是正确的)。我建议您看看查询表达式如何使用编译器生成的lambda“扩展”成方法调用。让它只在安全区域工作似乎违反了最小意外原则<如果
p
是一个属性访问表达式,那么代码>&p就没有意义了,那么为什么不从中触发呢?另外,您可以选择property.Bind(delegate(tv){n=v;},delegate(){return n;})
并跳过lambdas。我只是借用&
进行实验。如果我能让它工作,我计划定义一个新的操作符%
来模拟C++/CLI和C++/CX。@BenVoigt-使用委托会使构建边界表达式更容易吗?我真的不知道。但是旧的功能通常更简单。我至少会在替换项周围添加一些括号,并可能在生成的AST中放置一个可以替换的占位符。@BenVoigt是的,我正在考虑在语言中为占位符定义一种特殊语法。我可以用它来代替{0}
,这样可以避免对操作数进行双重解析。甚至可以将其作为元编程特性公开,这样类似的扩展就可以在用户源代码中声明。在什么情况下需要括号?如果v=>{0}=v
被视为文本,则lambda运算符=>
和赋值运算符=
的任何优先级都将与出现在{0}
中的任何运算符进行比较。例如,x |=y=v
被解析为x |=(y=v)
Ah。我有一个理论,&
的用户需要自己为任何复杂的东西添加括号,但如果这不是真的,那么你是对的。但是,如果我能让占位符替代方案正常工作,那就可以完全避免这个问题。好吧,我不想打赌括号在解析过程中会被保留,然后Operand.GetText()
+1不确定你为什么会对此投反对票,所以我已经平衡了它。这是一个非常好的建议,在我真正懒惰之前,我确实开始了调查。
internal class SyntaxTemplate
{
public ExpressionSyntax Syntax { get; private set; }
private readonly ImmutableDictionary<string, ImmutableList<IdentifierNameSyntax>> _identifiers;
public SyntaxTemplate(string source)
{
Syntax = SyntaxFactory.ParseExpression(source);
var identifiers = ImmutableDictionary<string, ImmutableList<IdentifierNameSyntax>.Builder>.Empty.ToBuilder();
foreach (var node in Syntax.DescendantNodes().OfType<IdentifierNameSyntax>())
{
ImmutableList<IdentifierNameSyntax>.Builder list;
if (!identifiers.TryGetValue(node.Identifier.Text, out list))
list = identifiers[node.Identifier.Text] =
ImmutableList<IdentifierNameSyntax>.Empty.ToBuilder();
list.Add(node);
}
_identifiers = identifiers.ToImmutableDictionary(
p => p.Key, p => p.Value.ToImmutableList());
}
private SyntaxTemplate(ExpressionSyntax syntax,
ImmutableDictionary<string, ImmutableList<IdentifierNameSyntax>> identifiers)
{
Syntax = syntax;
_identifiers = identifiers;
}
public SyntaxTemplate Replace(string identifier, SyntaxNode value)
{
return new SyntaxTemplate(
Syntax.ReplaceNodes(_identifiers[identifier], (o1, o2) => value),
_identifiers.Remove(identifier));
}
}
private T RedirectDiagnostics<T>(DiagnosticBag diagnostics, CSharpSyntaxNode nodeWithLocation, Func<DiagnosticBag, T> generate)
{
var captured = new DiagnosticBag();
var result = generate(captured);
foreach (var diag in captured.AsEnumerable().OfType<DiagnosticWithInfo>())
diagnostics.Add(new CSDiagnostic(diag.Info, nodeWithLocation.Location));
return result;
}
if (!operand.Type.IsPointerType())
return RedirectDiagnostics(diagnostics, node, redirected =>
BindExpression(_pointerIndirectionTemplate.Replace("p", node.Operand).Syntax, redirected));