解析源代码 - 不同语言的唯一标识符?

时间:2009-05-29 08:14:14

标签: parsing language-agnostic programming-languages

我正在构建一个接收源代码作为输入的应用程序,并分析代码的几个方面。它可以接受来自许多常用语言的代码,例如C / C ++,C#,Java,Python,PHP,Pascal,SQL等(但不支持多种语言,例如Ada,Cobol,Fortran)。知道语言之后,我的应用程序知道该怎么做(我有不同语言的处理程序)。

目前我要求用户输入编写代码的编程语言,这很容易出错:虽然用户知道编程语言,但只有一小部分(在极少数情况下)单击错误的选项由于鲁莽,这打破了系统(即我的分析失败)。

在我看来,应该有一种方法可以从输入文本本身中找出(在大多数情况下)语言是什么。几点说明:

  • 我收到纯文本而不是文件名,所以我不能将扩展名作为提示使用。
  • 用户无需输入完整的源代码,也可以输入代码段(即可能不包含include / import部分)。
  • 我很清楚,我选择的任何算法都不是100%证明,当然对于非常短的输入代码(例如,Python和Ruby都可以接受),在这种情况下我仍然需要用户的帮助,但是我希望尽量减少用户参与流程以最大限度地减少错误。

示例:

  • 如果文字包含“x-> y()”,我可能确定它是C ++(?)
  • 如果文本包含“public static void main”,我可能确定它是Java(?)
  • 如果文字包含“for x:= y to z do begin”,我可能确定它是Pascal(?)

我的问题:

  1. 您是否熟悉任何标准库/方法,以自动确定输入源代码的语言是什么?
  2. 什么是独特的代码“令牌”,我当然可以将一种语言与另一种语言区分开来?
  3. 我正在用Python编写代码,但我认为这个问题与语言无关。

    由于

14 个答案:

答案 0 :(得分:7)

Vim具有自动检测文件类型功能。如果您下载vim源代码,您将找到/vim/runtime/filetype.vim文件。

对于每种语言,它检查文件的扩展名,并且对于其中一些(最常见),它有一个可以从源代码获取文件类型的函数。你可以检查一下。代码很容易理解,并且有一些非常有用的注释。

答案 1 :(得分:7)

构建通用标记生成器,然后对它们使用贝叶斯过滤器。使用现有的“用户检查框”系统来训练它。

答案 2 :(得分:4)

这是一种简单的方法。只需在每种语言上运行解析器。无论遇到任何错误(或错误最少),任何语言都能获得最大的胜利。

该技术具有以下优点:

  • 您已经拥有完成此操作所需的大部分代码。
  • 分析可以在多核机器上并行完成。
  • 大多数语言都可以很快消除。
  • 这种技术非常强大。使用模糊分析(例如baysian)时可能看起来非常相似的语言在运行实际解析器时可能会出现很多错误。
  • 如果用两种不同的语言正确解析一个程序,那么首先就没有希望区分它们。

答案 3 :(得分:3)

我知道的一个程序甚至可以区分同一文件中的几种不同语言ohcount。你可能会在那里得到一些想法,虽然我真的不知道他们是怎么做的。

一般来说,你可以寻找独特的模式:

  • 运算符可能是一个指标,例如Pascal / Modula / Oberon的:==>或C#中的整个LINQ
  • 关键字将是另一个,因为可能没有两种语言具有相同的关键字集
  • 标识符的套管规则,假设代码片段符合最佳实践。可能是一个非常弱的规则
  • 标准库函数或类型。特别是对于通常严重依赖它们的语言,例如PHP,您可能只使用一长串标准库函数。

您可以创建一组规则,每个规则指示一组可能匹配的语言。相交结果列表有望只为您提供一种语言。

然而,这种方法的问题在于你需要进行标记化和比较标记(否则你无法真正知道运算符是什么,或者你发现的是否在注释或字符串中)。但是,每种语言的标记规则也不同;只是在空格和标点符号上分割所有内容可能不会产生非常有用的标记序列。您可以尝试几种不同的标记化规则(每种规则也指示一组特定的语言)并使规则与指定的标记化匹配。例如,尝试在带有一条注释的VB片段中查找单引号字符串(用于尝试Pascal)可能会失败,但另一个标记器可能会有更多运气。

但是既然你想要执行分析,你可能已经拥有了你支持的语言的解析器,所以你可以尝试通过每个解析器运行代码片段并将其作为指示器,将其作为指示器(如同OregonGhost所建议的那样)

答案 4 :(得分:3)

我认为这个问题是不可能的。你能做的最好的事情是想出一个程序使用特定语言的概率,即便如此,我猜想产生一个可靠的概率是非常困难的。立即浮现在脑海中的问题:

  • 使用像C预处理器这样的功能可以有效地掩盖完整的语言
  • 寻找关键字是不够的,因为关键字可以在其他语言中用作标识符
  • 寻找实际的语言结构需要你解析代码,但要做到这一点,你需要知道语言
  • 你怎么处理格式错误的代码?

这些似乎有足够的问题可以解决。

答案 5 :(得分:2)

一些想法:

$ x-> y()在PHP中有效,所以如果你认为C ++,请确保没有$符号(虽然我认为你可以在C结构中存储函数指针,所以这也可以是C)。

public static void main是Java,如果它正确套装 - 写Main,它是C#。如果你考虑使用像许多脚本语言或Pascal这样的不区分大小写的语言,这会变得复杂。另一方面,C#中的[]属性语法似乎相当独特。

您还可以尝试使用某种语言的关键字 - 例如,Option StrictEnd Sub是典型的VB等,而yield可能是C#和{{1 } / initialization是Object Pascal / Delphi。

如果你的应用程序正在分析源代码,那么代码会尝试为每种语言抛出你的分析代码,如果它真的很糟糕,那就是错误的语言:)

答案 6 :(得分:2)

我的方法是:

创建一个字符串或正则表达式列表(有和没有区分大小写),其中每个元素都分配了一个语言列表,该元素是一个指示符:

  • class => C ++,C#,Java
  • interface => C#,Java
  • implements =>爪哇
  • [attribute] => C#
  • procedure => Pascal,Modula
  • create table / insert / ... => SQL

等。然后逐行解析文件,匹配列表中的每个元素,并计算命中数。

获得最多点击次数的语言;)

答案 7 :(得分:2)

词频分析怎么样(有一个扭曲)?解析源代码并将其分类,就像垃圾邮件过滤器一样。这样,当您的应用程序中输入的代码片段无法100%识别时,您可以让它显示用户可以选择的最接近的匹配项 - 然后可以将其输入您的数据库。

答案 8 :(得分:2)

这是给你的一个想法。对于你的每种语言,找一些语言文件,每种语言10-20就足够了,每一种都不会太短。用一种语言连接所有文件。叫这个lang1.txt。将它gZip为lang1.txt.gz。你将拥有一套N langX.txt和langX.txt.gz文件。

现在,取出有问题的文件并附加到每个langX.txt文件,生成langXapp.txt和相应的gzipped langXapp.txt.gz。对于每个X,找到langXapp.gz和langX.gz的大小之间的差异。最小的差异将对应于文件的语言。

免责声明:只有较长的文件,这才能合理地运作。而且,效率不高。但从好的方面来说,你不需要了解语言,它是完全自动的。它还可以检测自然语言,并在法语或中文之间进行分析。万一你需要它:)但主要原因,我只是认为这是有趣的尝试:)

答案 9 :(得分:1)

非常有趣的问题,我不知道是否有可能通过代码片段区分语言,但这里有一些想法:

  • 一种简单的方法是注意单引号:在某些语言中,它用作字符包装器,而在其他语言中,它可以包含整个字符串
  • 一元星号或一元号&符号运算符表明它是C / C ++ / C#。
  • Pascal是使用两个字符进行作业:=的唯一语言(给定的语言)。 Pascal也有许多独特的关键词(开头,子,结尾......)
  • class initialization with a function可能是Java的一个很好的提示。
  • 不属于某个类的函数会删除java(例如,没有max()
  • 基本类型的命名(bool vs boolean)
  • 这提醒我:C ++可以在项目中以不同的方式显示非常#define boolean int)所以你永远无法保证,你找到了正确的语言。
  • 如果您通过散列算法运行源代码并且看起来相同,那么您最有可能分析Perl
  • 缩进是Python的一个很好的提示
  • 你可以使用语言本身提供的函数 - 比如用于PHP的token_get_all() - 或第三方工具 - 比如用于python的pychecker - 来检查语法

总结一下:这个项目将成为一篇有趣的研究论文(恕我直言),如果你想让它运作良好,那就准备好投入很多的努力。

答案 10 :(得分:1)

最安全但也最耗费大量工作的方法是为每种语言编写一个解析器,然后按顺序运行它们以查看哪一个会接受代码。如果代码有语法错误,这将无法正常工作,你很可能必须处理这样的代码,人们确实会犯错误。实现这一目标的一种快速方法是为您支持的每种语言提供通用编译器,然后运行它们并检查它们产生的错误数量。

启发式算法可以达到某一点,您支持的语言越多,从中获得的帮助就越少。但是对于前几个版本来说这是一个好的开始,主要是因为它实施起来很快并且在大多数情况下工作得很好。您可以检查API中经常使用的特定关键字,函数/类名,一些语言结构等。最好的方法是检查文件对于每种可能的语言有多少这些特定的东西,这将有助于解决一些语法错误,用户定义的函数,其名称为this(),在没有这些关键字的语言中,用注释和字符串文字写成。

无论如何,你很可能有时会失败,所以仍然需要一些用户覆盖语言选择的机制。

答案 11 :(得分:1)

我认为你永远不应该依赖一个单一的功能,因为片段中的缺席(例如有人系统地使用WHILE而不是for)可能会让你感到困惑。

还要尽量远离像“IMPORT”或“MODULE”或“UNIT”或INITIALIZATION / FINALIZATION这样的全局标识符,因为它们可能并不总是存在,在完整的源代码中是可选的,并且在片段中完全不存在。

方言和类似语言(例如Modula2和Pascal)也很危险。

我会为一堆跟踪关键令牌的语言创建简单的词法分析器,然后简单地将关键令牌计算为“其他”标识符比率。为每个标记赋予权重,因为某些标记可​​能是消除方言或版本之间歧义的关键指标。

注意,这也是允许用户插入“已知”关键字以提高检测率的便捷方式,例如通过例如提供运行时库例程或类型的标识符。

答案 12 :(得分:1)

没有办法让这个万无一失,但我个人会从操作员开始,因为他们在大多数情况下都是“一成不变”(我不能说这适用于所有语言,因为我只知道一个有限的集合)。这会大大缩小范围,但还不够。例如“ - >”用于多种语言(至少C,C ++和Perl)。

我会选择这样的事情:

为每种语言创建一个功能列表,这些功能可以是运算符,注释样式(因为大多数使用某种易于检测的字符或字符组合)。

例如: 有些语言的行以“#”开头,​​包括C,C ++和Perl。除了前两个以外的其他人在他们的词汇表中使用#include和#define吗?如果您在行的开头检测到此字符,则该语言可能就是其中之一。如果角色位于该行的中间,则该语言很可能是Perl。

另外,如果您找到模式:=这会将其缩小到某些可能的语言。

我会找到一个包含语言和模式的二维表,经过分析后我会简单地计算哪种语言最“点击”。如果我希望它真的很聪明,我会给每个功能一个权重,这意味着这个功能被包含在这种语言的片段中的可能性或可​​能性。例如,如果您可以找到以/ *开头并以* /结尾的片段,则很可能是C或C ++。

关键字的问题是有人可能会将其用作普通变量甚至是评论内部。它们可以用作决策者(例如,如果其他条件相同的话,“C ++”这个词在C ++中的可能性比C大得多),但你不能依赖它们。

在分析之后,我将提供最可能的语言作为用户的选择,其余订购也可以选择。因此,用户只需单击按钮即可接受您的猜测,或者他可以轻松切换。

答案 13 :(得分:1)

回答2:如果有“#!”和一开始的翻译名称,那么你肯定知道它是哪种语言。 (不能相信其他人没有提到过。)