我需要解析用户编写的C#代码片段,并用方法调用替换本地未定义的所有变量。即。
public class Foo
{
public dynamic Bar()
{
return Math.Min(x + width, maxWidth);
}
}
必须成为:
public class Foo
{
public dynamic Bar()
{
return Math.Min(Resolve("x") + Resolve("width"), Resolve("maxWidth"));
}
}
我正在使用Microsoft.CodeAnalysis.CSharp和CSharpSyntaxTree来检查字符串,但它没有提供足够的信息来执行替换。或者如果确实如此,我不知道在哪里寻找它。我已粘贴下面的SyntaxTree布局。所有变量都以IdentifierName节点的形式出现,但我不知道如何区分不同的IdentifierNames。从这里去哪里?
CompilationUnit[0..99) {
code: public class Foo\n{\n public dynamic Bar()\n {\n return Math.Min(x + width, maxWidth);\n }\n}
tokens: EndOfFileToken[]
nodes{
ClassDeclaration[0..99) {
code: public class Foo\n{\n public dynamic Bar()\n {\n return Math.Min(x + width, maxWidth);\n }\n}
tokens: PublicKeyword[public ] ClassKeyword[class ] IdentifierToken[Foo\n] OpenBraceToken[{\n] CloseBraceToken[}]
nodes{
MethodDeclaration[21..98) {
code: public dynamic Bar()\n {\n return Math.Min(x + width, maxWidth);\n }\n
tokens: PublicKeyword[ public ] IdentifierToken[Bar]
nodes{
IdentifierName[30..38) {
code: dynamic
tokens: IdentifierToken[dynamic ]
}
ParameterList[41..45) {
code: ()\n
tokens: OpenParenToken[(] CloseParenToken[)\n]
}
Block[45..98) {
code: {\n return Math.Min(x + width, maxWidth);\n }\n
tokens: OpenBraceToken[ {\n] CloseBraceToken[ }\n]
nodes{
ReturnStatement[50..93) {
code: return Math.Min(x + width, maxWidth);\n
tokens: ReturnKeyword[ return ] SemicolonToken[;\n]
nodes{
InvocationExpression[61..90) {
code: Math.Min(x + width, maxWidth)
nodes{
SimpleMemberAccessExpression[61..69) {
code: Math.Min
tokens: DotToken[.]
nodes{
IdentifierName[61..65) {
code: Math
tokens: IdentifierToken[Math]
}
IdentifierName[66..69) {
code: Min
tokens: IdentifierToken[Min]
}
}
}
ArgumentList[69..90) {
code: (x + width, maxWidth)
tokens: OpenParenToken[(] CommaToken[, ] CloseParenToken[)]
nodes{
Argument[70..79) {
code: x + width
nodes{
AddExpression[70..79) {
code: x + width
tokens: PlusToken[+ ]
nodes{
IdentifierName[70..72) {
code: x
tokens: IdentifierToken[x ]
}
IdentifierName[74..79) {
code: width
tokens: IdentifierToken[width]
}
}
}
}
}
Argument[81..89) {
code: maxWidth
nodes{
IdentifierName[81..89) {
code: maxWidth
tokens: IdentifierToken[maxWidth]
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
答案 0 :(得分:7)
我认为你需要使用语义模型。这是一个(非常基本的)示例,展示了如何查找未解析的符号:
var tree = CSharpSyntaxTree.ParseFile(fileName);
var root = tree.GetRoot();
var refs = new MetadataReference[]
{
new MetadataFileReference(@"C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.dll", new MetadataReferenceProperties(MetadataImageKind.Assembly))
};
var compilation = CSharpCompilation.Create("testRoslyn", new[] { tree }, refs);
var model = compilation.GetSemanticModel(tree);
var unknownSymbols =
from node in root.DescendantNodes()
where node.IsKind(SyntaxKind.IdentifierName)
let symbolInfo = model.GetSymbolInfo(node)
where symbolInfo.Symbol == null && !symbolInfo.CandidateSymbols.Any()
select node;
从那里你可以用Resolve(name)
替换节点。
答案 1 :(得分:2)
这篇文章并没有真正为您提供问题的解决方案,而是另一种可能更容易实现的方式,但为用户引入了更改。我这里只是作为一个想法发布。
目标不是允许用户写x
而是Var.x
或Var.maxWidth
然后,在解析C#代码时,您只需要为Var
类型的属性CustomDynamicObject
插入代码(或者您要提供的任何名称)
public (static?) CustomDynamicObject Var { get { /* create the object once and return */ }}
然后你可以定义CustomDynamicObject
继承DynamicObject,这样你就可以拦截对未定义方法/属性的所有调用
DynamicObject和使用.NET 4的动态功能只是一种拦截呼叫的方法,但你可以谷歌搜索其他技术。
答案 2 :(得分:1)
也许您不小心粘贴了CSharpSyntaxTree代码而没有声明" double width = 10.0;"? 如果是这样,您将在CSharpSyntaxTree中获得此附加声明。
您需要做的只是扫描未在用户代码中声明的IdentifierToken的树,所有这些令牌都有必须用于替换变量访问代码到方法调用代码的位置。