![]() |
1
202
我可以为您描述我们如何在“真实”的C ide中有效地做到这一点。 我们首先要做的是运行一个pass,它只分析源代码中的“顶级”内容。我们跳过所有的方法体。这允许我们快速建立一个数据库,其中包含关于程序源代码中的名称空间、类型和方法(以及构造函数等)的信息。如果您试图在击键之间进行分析,那么分析每个方法体中的每一行代码将花费太长的时间。 当IDE需要计算出一个方法体中特定表达式的类型时——比如说你已经输入了“foo”。我们需要弄清楚foo的成员是什么——我们做同样的事情;我们尽可能地跳过工作。 我们从一个仅分析 局部变量 该方法中的声明。当我们运行该传递时,我们将从一对“scope”和“name”映射到一个“type determiner”。“类型限定符”是一个表示“如果需要,我可以计算出这个本地类型”的概念的对象。计算出一个地方的类型可能很昂贵,所以如果需要的话,我们想推迟这项工作。 我们现在有了一个懒散地构建的数据库,可以告诉我们每个本地的类型。所以,回到那个“foo”。——我们找出 陈述 相关表达式在中,然后针对该语句运行语义分析器。例如,假设您有方法体:
现在我们需要知道foo是char类型。我们构建了一个数据库,其中包含所有元数据、扩展方法、源代码类型等。我们为x、y和z建立了一个类型为determiners的数据库。我们分析了包含有趣表达式的语句。我们从语法上把它转换为
为了确定foo的类型,我们首先必须知道y的类型,所以在这一点上,我们问类型决定者“y的类型是什么?”然后它启动一个表达式计算器,解析x.tochararray()并询问“x的类型是什么?”我们有一个类型限定符,上面写着“我需要在当前上下文中查找”string“。当前类型中没有类型字符串,因此我们查看命名空间。它也不存在,所以我们查看using指令,发现有一个“using system”,该系统有一个类型字符串。好的,这就是x的类型。 然后,我们查询system.string的元数据以获取tochararray的类型,它说它是system.char[]。超级的。所以我们有一个Y型。 现在我们问“System.char[]有方法在哪里?”不,所以我们查看using指令;我们已经预计算了一个数据库,其中包含了可能使用的扩展方法的所有元数据。 现在我们说“好的,有十八个扩展方法在作用域的何处命名,它们中有没有第一个形式参数的类型与system.char[]兼容?”所以我们开始了一轮可兑换性测试。但是,扩展方法的位置 通用的 这意味着我们必须进行类型推断。
我已经编写了一个特殊的类型推理引擎,它可以处理从第一个参数到扩展方法的不完全推理。我们运行类型推断器,发现有一个Where方法
此方法的签名是
现在我们有了分析lambda主体所需的所有信息,即“foo”。我们查找foo的类型,发现根据lambda绑定器,它是system.char,我们已经完成了;我们显示system.char的类型信息。 除了“顶层”分析,我们什么都做 在击键之间 . 这才是真正棘手的问题。事实上,写下所有的分析并不难,它正在使它 足够快 你可以以打字的速度来完成,这才是真正棘手的一点。 祝你好运! |
![]() |
2
15
我可以大致告诉您DelphiIDE如何与Delphi编译器一起工作来实现IntelliSense(代码洞察是Delphi所称的代码洞察)。这并非100%适用于C,但这是一种值得考虑的有趣方法。 Delphi中的大多数语义分析都是在解析器本身中完成的。表达式在解析时被类型化,除了不容易实现的情况——在这种情况下,使用先行分析来计算出预期的结果,然后在解析中使用该决策。 除了使用运算符优先级解析的表达式外,解析主要是ll(2)递归下降。Delphi的一个独特之处是它是一种单通道语言,因此在使用之前需要声明结构,因此不需要顶级的通道来显示信息。 这些特性的组合意味着,对于任何需要的地方,解析器都大致拥有代码洞察所需的所有信息。它的工作方式是这样的:IDE通知编译器的lexer光标的位置(需要代码洞察的地方),lexer将其转换为一个特殊的标记(称为kibitz标记)。每当解析器遇到这个令牌(可能在任何地方)时,它就知道这是一个将它拥有的所有信息发送回编辑器的信号。它使用longjmp来实现这一点,因为它是用C语言编写的;它所做的是通知最终调用方kibitz点所在的句法结构(即语法上下文)以及该点所需的所有符号表。例如,如果上下文在一个表达式中,该表达式是一个方法的参数,那么我们可以检查方法重载,查看参数类型,并将有效符号筛选为只能解析为该参数类型的符号(这会减少下拉列表中许多不相关的问题)。如果它在嵌套的作用域上下文中(例如“.”之后),解析器将返回对该作用域的引用,并且IDE可以枚举在该作用域中找到的所有符号。 其他事情也可以做;例如,如果kibitz令牌不在其范围内,方法体将被跳过-这是乐观地完成的,如果跳过令牌,则回滚。相当于扩展方法(Delphi中的类助手)有一种版本化的缓存,因此它们的查找速度相当快。但是Delphi的一般类型推理比C弱得多。
现在,具体问题是:推断用
|
![]() |
3
7
如果您不想编写自己的解析器来构建抽象语法树,那么您可以从以下两个方面来看使用解析器: SharpDevelop 或 MonoDevelop ,两者都是开源的。 |
![]() |
4
4
IntelliSense系统通常使用抽象语法树表示代码,这允许它们以与编译器相同的方式或多或少地解析分配给“var”变量的函数的返回类型。如果使用vs intellisense,您可能会注意到,在输入有效(可解析)赋值表达式之前,它不会给您var类型。如果表达式仍然不明确(例如,它无法完全推断表达式的泛型参数),则var类型将无法解析。这可能是一个相当复杂的过程,因为您可能需要深入到树中才能解析类型。例如:
返回类型为
|
![]() |
5
4
既然您的目标是Emacs,最好从cedet套件开始。Eric Lippert在CEDET/语义工具中的代码分析器中已经覆盖了所有的细节。还有一个C解析器(可能需要一点TLC),因此唯一缺少的部分与为C调优必要的部分有关。 基本行为在核心算法中定义,这些核心算法依赖于基于每种语言定义的可重载函数。完成引擎的成功取决于完成了多少优化。以C++为指导,获得类似C++的支持不应该太糟糕。 丹尼尔的回答建议使用MonoDevelop进行解析和分析。这可以是一种替代机制,而不是现有的C解析器,也可以用来扩充现有的解析器。 |
![]() |
6
2
做得好是个难题。基本上,您需要通过大多数的词法/解析/类型检查来为语言规范/编译器建模,并构建一个源代码的内部模型,然后您可以查询它。埃里克为C详细描述了这一点。您可以下载f编译器源代码(f ctp的一部分)并查看
另一种方法是像描述的那样重新使用编译器,然后使用反射或查看生成的代码。从您需要“完整程序”从编译器获取编译输出的角度来看,这是有问题的,而在编辑器中编辑源代码时,您通常只有“部分程序”尚未解析,尚未实现所有方法等。 总之,我认为“低预算”版本很难做得好,“真正”版本非常, 非常 很难做好。(这里“困难”衡量的是“努力”和“技术难度”。) |
![]() |
7
2
NRefactory 会帮你的。 |
![]() |
8
0
对于解决方案“1”,在.NET 4中有一个新的工具可以快速、轻松地执行此操作。 因此,如果您可以将程序转换为.NET 4,这将是您的最佳选择。 |
|
Robert King · Unity C#语法问题-转换位置 1 年前 |
![]() |
JBryanB · 如何从基本抽象类访问类属性 1 年前 |
|
law · 检查答案按钮的输入字符串格式不正确 2 年前 |
![]() |
i_sniff_ket · 在unity之外使用unity类 2 年前 |