views:

719

answers:

14

Hello,

I'm building an application that receives source code as input and analyzes several aspects of the code. It can accept code from many common languages, e.g. C/C++, C#, Java, Python, PHP, Pascal, SQL, and more (however many languages are unsupported, e.g. Ada, Cobol, Fortran). Once the language is known, my application knows what to do (I have different handlers for different languages).

Currently I'm asking the user to input the programming language the code is written in, and this is error-prone: although users know the programming languages, a small percentage of them (on rare occasions) click the wrong option just due to recklessness, and that breaks the system (i.e. my analysis fails).

It seems to me like there should be a way to figure out (in most cases) what the language is, from the input text itself. Several notes:

  • I'm receiving pure text and not file names, so I can't use the extension as a hint.
  • The user is not required to input complete source codes, and can also input code snippets (i.e. the include/import part may not be included).
  • it's clear to me that any algorithm I choose will not be 100% proof, certainly for very short input codes (e.g. that could be accepted by both Python and Ruby), in which cases I will still need the user's assistance, however I would like to minimize user involvement in the process to minimize mistakes.

Examples:

  • If the text contains "x->y()", I may know for sure it's C++ (?)
  • If the text contains "public static void main", I may know for sure it's Java (?)
  • If the text contains "for x := y to z do begin", I may know for sure it's Pascal (?)

My question:

  1. Are you familiar with any standard library/method for figuring out automatically what the language of an input source code is?
  2. What are the unique code "tokens" with which I could certainly differentiate one language from another?

I'm writing my code in Python but I believe the question to be language agnostic.

Thanks

+2  A: 

Some thoughts:

$x->y() would be valid in PHP, so ensure that there's no $ symbol if you think C++ (though I think you can store function pointers in a C struct, so this could also be C).

public static void main is Java if it is cased properly - write Main and it's C#. This gets complicated if you take case-insensitive languages like many scripting languages or Pascal into account. The [] attribute syntax in C# on the other hand seems to be rather unique.

You can also try to use the keywords of a language - for example, Option Strict or End Sub are typical for VB and the like, while yield is likely C# and initialization/implementation are Object Pascal / Delphi.

If your application is analyzing the source code anyway, you code try to throw your analysis code at it for every language and if it fails really bad, it was the wrong language :)

OregonGhost
#1 and #2 were the first things I was thinking about
devio
+6  A: 

Vim has a autodetect filetype feature. If you download vim sourcecode you will find a /vim/runtime/filetype.vim file.

For each language it checks the extension of the file and also, for some of them (most common), it has a function that can get the filetype from the source code. You can check that out. The code is pretty easy to understand and there are some very useful comments there.

-1 He specifically mentioned that he doesn't have the file name.
Aaron Digulla
Aaron: "also for some of them (most common) has a function that can get the filetype from the source code". Even commented code which does exactly what the OP wanted qualifies for a −1?
Joey
@Aaron: dude at least read the whole answer between voting or posting ... I know that my english is not that good, but it seems Johannes got it :)
+1 to correct the downvote :)
Roee Adler
+1 to overcorrect the downvote
eyelidlessness
(and also because it's a good answer)
eyelidlessness
-1 to correct excessive corrections of downwotes. Language guessing capabilities, although exist there, can only determine whether text with certain file name indeed is of certain language. Without file extensions it's of no use.
Pavel Shved
+3  A: 

One program I know which even can distinguish several different languages within the same file is ohcount. You might get some ideas there, although I don't really know how they do it.

In general you can look for distinctive patterns:

  • Operators might be an indicator, such as := for Pascal/Modula/Oberon, => or the whole of LINQ in C#
  • Keywords would be another one as probably no two languages have the same set of keywords
  • Casing rules for identifiers, assuming the piece of code was writting conforming to best practices. Probably a very weak rule
  • Standard library functions or types. Especially for languages that usually rely heavily on them, such as PHP you might just use a long list of standard library functions.

You may create a set of rules, each of which indicates a possible set of languages if it matches. Intersecting the resulting lists will hopefully get you only one language.

The problem with this approach however, is that you need to do tokenizing and compare tokens (otherwise you can't really know what operators are or whether something you found was inside a comment or string). Tokenizing rules are different for each language as well, though; just splitting everything at whitespace and punctuation will probably not yield a very useful sequence of tokens. You can try several different tokenizing rules (each of which would indicate a certain set of languages as well) and have your rules match to a specified tokenization. For example, trying to find a single-quoted string (for trying out Pascal) in a VB snippet with one comment will probably fail, but another tokenizer might have more luck.

But since you want to perform analysis anyway you probably have parsers for the languages you support, so you can just try running the snippet through each parser and take that as indicator which language it would be (as suggested by OregonGhost as well).

Joey
Other languages in the Wirthian family also use :=. It is not unique enough
Marco van de Voort
Marco: Yes, I agree, hence the idea that each rule might yield a set of possible languages. Intersecting those will probably yield a most likely correct one. I'll elaborate that again, I think.
Joey
+1  A: 

Very interesting question, I don't know if it is possible to be able to distinguish languages by code snippets, but here are some ideas:

  • One simple way is to watch out for single-quotes: In some languages, it is used as character wrapper, whereas in the others it can contain a whole string
  • A unary asterisk or a unary ampersand operator is a certain indication that it's either of C/C++/C#.
  • Pascal is the only language (of the ones given) to use two characters for assignments :=. Pascal has many unique keywords, too (begin, sub, end, ...)
  • The class initialization with a function could be a nice hint for Java.
  • Functions that do not belong to a class eliminates java (there is no max(), for example)
  • Naming of basic types (bool vs boolean)
  • Which reminds me: C++ can look very differently across projects (#define boolean int) So you can never guarantee, that you found the correct language.
  • If you run the source code through a hashing algorithm and it looks the same, you're most likely analyzing Perl
  • Indentation is a good hint for Python
  • You could use functions provided by the languages themselves - like token_get_all() for PHP - or third-party tools - like pychecker for python - to check the syntax

Summing it up: This project would make an interesting research paper (IMHO) and if you want it to work well, be prepared to put a lot of effort into it.

soulmerge
C uses unary ampersands too. Nor do I see how you're going to find all those things without parsing the source code, which you really can't do without knowing what it's written in.
David Thornley
Thx, changed the answer. Yes, you must parse the source code, you would start by the first character and check if the next character reduces the pool of possible languages in a loop. I don't think one has any other option.
soulmerge
+2  A: 

My approach would be:

Create a list of strings or regexes (with and without case sensitivity), where each element has assigned a list of languages that the element is an indicator for:

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

etc. Then parse the file line-by-line, match each element of the list, and count the hits.

The language with the most hits wins ;)

devio
I think this is a perfect case for TDD: Implement some rules, run the code and when it fails, add a test and a new rule.
Aaron Digulla
class and interface are also Object Pascal, so these are not really suitable.
OregonGhost
This is error prone since java/C# code can sometimes not use interfaces for example.
the_drow
you are free to vote up or down, but it is quite obvious you did not understand my approach
devio
+3  A: 

I think the problem is impossible. The best you can do is to come up with some probability that a program is in a particular language, and even then I would guess producing a solid probability is very hard. Problems that come to mind at once:

  • use of features like the C pre-processor can effectively mask the underlyuing language altogether
  • looking for keywords is not sufficient as the keywords can be used in other languages as identifiers
  • looking for actual language constructs requires you to parse the code, but to do that you need to know the language
  • what do you do about malformed code?

Those seem enough problems to solve to be going on with.

anon
I wouldn't say *impossible*, just *really, really difficult*. +1 regardless.
Matthew Iselin
+2  A: 

How about word frequency analysis (with a twist)? Parse the source code and categorise it much like a spam filter does. This way when a code snippet is entered into your app which cannot be 100% identified you can have it show the closest matches which the user can pick from - this can then be fed into your database.

gridzbi
+1  A: 

The most bulletproof but also most work intensive way is to write a parser for each language and just run them in sequence to see which one would accept the code. This won't work well if code has syntax errors though and you most probably would have to deal with code like that, people do make mistakes. One of the fast ways to implement this is to get common compilers for every language you support and just run them and check how many errors they produce.

Heuristics works up to a certain point and the more languages you will support the less help you would get from them. But for first few versions it's a good start, mostly because it's fast to implement and works good enough in most cases. You could check for specific keywords, function/class names in API that is used often, some language constructions etc. Best way is to check how many of these specific stuff a file have for each possible language, this will help with some syntax errors, user defined functions with names like this() in languages that doesn't have such keywords, stuff written in comments and string literals.

Anyhow you most likely would fail sometimes so some mechanism for user to override language choice is still necessary.

vava
+1  A: 

I think you never should rely on one single feature, since the absence in a fragment (e.g. somebody systematically using WHILE instead of for) might confuse you.

Also try to stay away from global identifiers like "IMPORT" or "MODULE" or "UNIT" or INITIALIZATION/FINALIZATION, since they might not always exist, be optional in complete sources, and totally absent in fragments.

Dialects and similar languages (e.g. Modula2 and Pascal) are dangerous too.

I would create simple lexers for a bunch of languages that keep track of key tokens, and then simply calculate a key tokens to "other" identifiers ratio. Give each token a weight, since some might be a key indicator to disambiguate between dialects or versions.

Note that this is also a convenient way to allow users to plugin "known" keywords to increase the detection ratio, by e.g. providing identifiers of runtime library routines or types.

Marco van de Voort
For source code analysis mistakenly identifying one dialect as another might not be such big a problem, though. If a piece of Pascal is syntactically correct Modula 2 they will have the same (or nearly the same) semantics with a pretty high probability.
Joey
If you really do a full syntax check yes. I was talking more about too simplistic heuristics.
Marco van de Voort
Well, the OP wanted to perform code analysis and I'd think when the semantics of two languages as well as their syntax are sufficiently close to each other, then their analysis would show similar results as well (whatever it is he is analyzing). So having wrong guesses with languages that are very close to each other may not hurt in practise.
Joey
+1  A: 

Here's an idea for you. For each of your N languages, find some files in the language, something like 10-20 per language would be enough, each one not too short. Concatenate all files in one language together. Call this lang1.txt. GZip it to lang1.txt.gz. You will have a set of N langX.txt and langX.txt.gz files.

Now, take the file in question and append to each of he langX.txt files, producing langXapp.txt, and corresponding gzipped langXapp.txt.gz. For each X, find the difference between the size of langXapp.gz and langX.gz. The smallest difference will correspond to the language of your file.

Disclaimer: this will work reasonably well only for longer files. Also, it's not very efficient. But on the plus side you don't need to know anything about the language, it's completely automatic. And it can detect natural languages and tell between French or Chinese as well. Just in case you need it :) But the main reason, I just think it's interesting thing to try :)

Igor Krivokon
+6  A: 

build a generic tokenizer and then use a Bayesian filter on them. Use the existing "user checks a box" system to train it.

BCS
I was wondering why nobody mentioned Bayesian filtering before. This, combined with some training (from Google code for instance) should prove to be fairly accurate. After all spam differs from normal email message much less than programming languages differ from each other. I think it might even be possible to distinguish different versions of a language.
gooli
I would recommend using this technique to suggest the language to the user. Let them override its guess if they're sure it is wrong.
Steve S
+1  A: 

There is no way of making this foolproof, but I would personally start with operators, since they are in most cases "set in stone" (I can't say this holds true to every language since I know only a limited set). This would narrow it down quite considerably, but not nearly enough. For instance "->" is used in many languages (at least C, C++ and Perl).

I would go for something like this:

Create a list of features for each language, these could be operators, commenting style (since most use some sort of easily detectable character or character combination).

For instance: Some languages have lines that start with the character "#", these include C, C++ and Perl. Do others than the first two use #include and #define in their vocabulary? If you detect this character at the beginning of line, the language is probably one of those. If the character is in the middle of the line, the language is most likely Perl.

Also, if you find the pattern := this would narrow it down to some likely languages.

Etc.

I would have a two-dimensional table with languages and patterns found and after analysis I would simply count which language had most "hits". If I wanted it to be really clever I would give each feature a weight which would signify how likely or unlikely it is that this feature is included in a snippet of this language. For instance if you can find a snippet that starts with /* and ends with */ it is more than likely that this is either C or C++.

The problem with keywords is someone might use it as a normal variable or even inside comments. They can be used as a decider (e.g. the word "class" is much more likely in C++ than C if everything else is equal), but you can't rely on them.

After the analysis I would offer the most likely language as the choice for the user with the rest ordered which would also be selectable. So the user would accept your guess by simply clicking a button, or he can switch it easily.

Makis
+1  A: 

In answer to 2: if there's a "#!" and the name of an interpreter at the very beginning, then you definitely know which language it is. (Can't believe this wasn't mentioned by anyone else.)

philomathohollic
Note, #! is called shebang.
eyelidlessness
+2  A: 

Here is a simple way to do it. Just run the parser on every language. Whatever language gets the farthest without encountering any errors (or has the fewest errors) wins.

This technique has the following advantages:

  • You already have most of the code necessary to do this.
  • The analysis can be done in parallel on multi-core machines.
  • Most languages can be eliminated very quickly.
  • This technique is very robust. Languages that might appear very similar when using a fuzzy analysis (baysian for example), would likely have many errors when the actual parser is run.
  • If a program is parsed correctly in two different languages, then there was never any hope of distinguishing them in the first place.
Jeffrey L Whitledge
+1 This is a good idea. However: 1) I may need to parse code fragments, in which case a parser may fail immediately, e.g. if it encounters a method definition without encountering a class definition. 2) Your suggestion seems very difficult to implement in practice. Where can I find parsers for all major languages? (this is a question, not a complaint, I would appreciate if you can share on that point)
Roee Adler
The most correct answer out there.
Pavel Shved