26
\$\begingroup\$

Your challenge is to take some source code as input, and output which programming language it is written in.

For example, you could have the input

class A{public static void main(String[]a){System.out.println("Hello, World!");}}

And output

Java

Your two main goals are diversity (how many programming languages you can detect) and accuracy (how good you are at detecting these languages).

For polyglots (programs valid in more than one language), you can decide what to do. You could just output the language that your program thinks is more likely, or you could output an error, or you could output an array of possible choices (which would probably get more upvotes than just an error!).

This is a , because it would be very difficult to specify a different objective winning criterion. Voters, please vote on how many languages it can detect and how accurate it is.

\$\endgroup\$
15
  • \$\begingroup\$ That is impossible, cause print("") can be used in many languages. \$\endgroup\$ Commented Feb 18, 2014 at 22:18
  • 1
    \$\begingroup\$ With your edit, now it seems more possible. \$\endgroup\$ Commented Feb 18, 2014 at 22:27
  • 4
    \$\begingroup\$ What about languages that are valid for EVERY input? Like whitespace. This sentence is a valid whitespace program. This whole page is a valid whitespace program. \$\endgroup\$ Commented Feb 18, 2014 at 23:40
  • 1
    \$\begingroup\$ Is the input guaranteed to be a valid program? Like some input could be class A{public static void main(String[]a){System.println.out("Hello, World!");}} which is invalid. \$\endgroup\$ Commented Feb 19, 2014 at 5:29
  • 1
    \$\begingroup\$ Or likewise will HTML input always start with <!DOCTYPE html> followed by the <html>,<body> and other tags (like meta) in their correct order? \$\endgroup\$ Commented Feb 19, 2014 at 11:23

7 Answers 7

21
\$\begingroup\$

234 text formats - Unix Shell

(not all of them languages - I need to count them carefully)

file $1

I hesitate to post this somewhat smart-a$$ answer, but I don't see anything in the rules banning it and the file shell utility really does do a good job of this. e.g:

$ file golfscript.rb 
golfscript.rb: Ruby module source, ASCII text
$ file template.c 
template.c: ASCII C program text
$ file adams.sh
adams.sh: Bourne-Again shell script, ASCII text executable
$ 

Furthermore you can use the -k option to "keep going" when testing a polyglot:

 -k, --keep-going
         Don't stop at the first match, keep going.  Subsequent matches
         will be have the string ‘\012- ’ prepended.  (If you want a new‐
         line, see the -r option.)

Also, the -l option will give you an idea of how good the algorithm is for differing languages:

$ file -l | grep shell
unknown, 0: Warning: using regular magic file `/etc/magic'
Strength = 280 : shell archive text [application/octet-stream]
Strength = 250 : Tenex C shell script text executable [text/x-shellscript]
Strength = 250 : Bourne-Again shell script text executable [text/x-shellscript]
Strength = 240 : Paul Falstad's zsh script text executable [text/x-shellscript]
Strength = 240 : Neil Brown's ash script text executable [text/x-shellscript]
Strength = 230 : Neil Brown's ae script text executable [text/x-shellscript]
Strength = 210 : Tenex C shell script text executable [text/x-shellscript]
Strength = 210 : Bourne-Again shell script text executable [text/x-shellscript]
Strength = 190 : Tenex C shell script text executable [text/x-shellscript]
Strength = 190 : Bourne-Again shell script text executable [text/x-shellscript]
Strength = 180 : Paul Falstad's zsh script text executable [text/x-shellscript]
Strength = 150 : Tenex C shell script text executable [text/x-shellscript]
Strength = 150 : Bourne-Again shell script text executable [text/x-shellscript]
Strength = 140 : C shell script text executable [text/x-shellscript]
Strength = 140 : Korn shell script text executable [text/x-shellscript]
Strength = 140 : Paul Falstad's zsh script text executable [text/x-shellscript]
Strength = 130 : POSIX shell script text executable [text/x-shellscript]
Strength = 130 : Plan 9 rc shell script text executable []
$ 

This is file-5.09 (on Ubuntu 12.04)

\$\endgroup\$
3
  • \$\begingroup\$ This actually does pretty well on a 16-language polyglot - gist.github.com/riking/9088817 \$\endgroup\$
    – Riking
    Commented Feb 19, 2014 at 9:34
  • \$\begingroup\$ You might as well cut out the middle man and avoid the shell entirely: ln -s /usr/bin/file /usr/local/bin/myspecialtool. If your answer counts, then doesn't this count just as well? (Don't worry, I'm not serious.) \$\endgroup\$
    – hvd
    Commented Feb 19, 2014 at 10:01
  • 2
    \$\begingroup\$ Looks like a standard loophole, i.e. delegating solution to existing program. \$\endgroup\$
    – Vi.
    Commented Feb 13, 2015 at 15:57
10
\$\begingroup\$

18 programming languages, 1002 bytes, accuracy: test for yourself :)

(yep I know this is not code golf, but for the fun of it)

The program searches for iconic code snippets, the checks are ordered in a way that the most clear checks are at the top and programming languages embedded in other programming languages are below (e.g. HTML in PHP).

This obviously fails for programs like System.out.println('<?php');

t = (p) ->
    h = (x) -> -1 != p.indexOf x
    s = (x) -> 0 == p.indexOf x

    if h "⍵" then "APL"
    else if h "<?php" then "PHP"
    else if h("<?xml") and h "<html" then "XHTML"
    else if h "<html" then "HTML"
    else if h "<?xml" then "XML"
    else if h("jQuery") or h "document.get" then "JavaScript"
    else if h "def __init__(self" then "Python"
    else if h "\\documentclass" then "TeX"
    else if h("java.") or h "public class" then "Java"
    else if s("SELE") or s("UPDATE") or s "DELE" then "SQL"
    else if /[-\+\.,\[\]\>\<]{9}/.test p then "Brainfuck"
    else if h "NSString" then "Objective-C"
    else if h "do |" then "Ruby"
    else if h("prototype") or h "$(" then "JavaScript"
    else if h "(defun" then "Common Lisp"
    else if /::\s*[a-z]+\s*->/i.test p then "Haskell"
    else if h "using System" then "C#"
    else if h "#include"
        if h("iostream") or h "using namespace" then "C++"
        else "C"
    else "???"

program = ""
process.stdin.on 'data', (chunk) -> program += chunk
process.stdin.on 'end', -> console.log t program

Usage on node: coffee timwolla.coffee < Example.java

Demo (Online-Demo on JSFiddle):

[timwolla@~/workspace/js]coffee puzzle.coffee < ../c/nginx/src/core/nginx.c 
C
[timwolla@~/workspace/js]coffee puzzle.coffee < ../ruby/github-services/lib/service.rb
Ruby
[timwolla@~/workspace/js]coffee puzzle.coffee < ../python/seafile/python/seaserv/api.py
Python
\$\endgroup\$
9
  • \$\begingroup\$ On my computer this outputs nothing, not even on input that should obviously work. Granted, I might be doing something wrong as I've never used Coffeescript before. \$\endgroup\$
    – marinus
    Commented Feb 19, 2014 at 2:39
  • 1
    \$\begingroup\$ @marinus Note that when manually inputting code you need to send an EOF (STRG+D) to trigger execution. Generally: The detector should at least spit out three question marks. \$\endgroup\$
    – TimWolla
    Commented Feb 19, 2014 at 2:41
  • \$\begingroup\$ Nope, nothing. Do I need to pass coffee any arguments? I had just tried redirecting files into it, but just running it and going ^D doesn't do anything either. \$\endgroup\$
    – marinus
    Commented Feb 19, 2014 at 2:49
  • \$\begingroup\$ @marinus Try: npm install coffee-script && node_modules/.bin/coffee timwolla.coffee < timwolla.coffee in a temporary folder, this should spit out APL. (assuming you have a recent version of node and npm installed) \$\endgroup\$
    – TimWolla
    Commented Feb 19, 2014 at 2:51
  • 5
    \$\begingroup\$ I'll start using lowercase omega more in my non-APL programs. \$\endgroup\$ Commented Feb 19, 2014 at 8:44
10
\$\begingroup\$

Bash — about 50 35 bytes per compilable language

Trick is to just compile, then you don't have to worry about linking errors from missing libraries, and it's more forgiving if you just have code snippets.

Thanks to Shahbaz for shorter forms!

gcc -c $1 && (echo C; exit 0)
g++ -c $1 && (echo C++; exit 0)
gpc -c $1 && (echo Pascal; exit 0)
gfortran -c $1 && (echo Fortran; exit 0)

etc...

\$\endgroup\$
4
  • \$\begingroup\$ Since you mention number of bytes per compilable language, you might be interested in lines like: gcc -c $1 && (echo C; exit 0) \$\endgroup\$
    – Shahbaz
    Commented Feb 20, 2014 at 12:51
  • \$\begingroup\$ Thank you, I'm not very good at really squeezing code! \$\endgroup\$
    – user15259
    Commented Feb 20, 2014 at 14:16
  • \$\begingroup\$ Sure. The && and || in bash are really useful and help cleanup the code a lot. They are by no means used for obfuscation, so you'd do well to learn them. \$\endgroup\$
    – Shahbaz
    Commented Feb 20, 2014 at 14:20
  • 2
    \$\begingroup\$ You can also pass -fsyntax-only to only check the syntax and skip actual compilation. \$\endgroup\$
    – peppe
    Commented Feb 21, 2014 at 21:59
6
\$\begingroup\$

This answer is a proof of concept, that will not likely receive any more work from myself.

It falls short in several ways:

  • The output is not exactly as the question requests, but close enough and could easily be modified to produce the exact output required.
  • There are several ways to make the code perform better and/or better ways to represent the data structures.
  • and more

The idea is to set a list of keywords/characters/phrases that can identify a specific language and assign a score to that keyword for each language. Then check the source file(s) for these keywords, and tally up the scores for each language that you find keywords for. In the end the language with the highest score is the likely winner. This also caters for polyglot programs as both (or all) the relevant languages will score high.

The only thing to add more languages is to identify their "signatures" and add them to the mapping.

You can also assign different scores to different keywords per language. For example, if you feel volatile is used more in Java than in C, set the score for volatile keyword to 2 for Java and 1 for C.

public class SourceTest {

  public static void main(String[] args) {
    if (args.length < 1) {
      System.out.println("No file provided.");
      System.exit(0);
    }
    SourceTest sourceTest = new SourceTest();
    for (String fileName : args) {
      try {
        sourceTest.checkFile(fileName);
      } catch (FileNotFoundException e) {
        System.out.println(fileName + " : not found.");
      } catch (IOException e) {
        System.out.println(fileName + " : could not read");
      }
    }
    System.exit(0);
  }

  private Map<String, LanguagePoints> keyWordPoints;
  private Map<LANGUAGES, Integer> scores;

  private enum LANGUAGES {
    C, HTML, JAVA;
  }

  public SourceTest() {
    init();
  }

  public void checkFile(String fileName) throws FileNotFoundException, IOException {
    String fileContent = getFileContent(fileName);
    testFile(fileContent);
    printResults(fileName);
  }

  private void printResults(String fileName) {
    System.out.println(fileName);
    for (LANGUAGES lang : scores.keySet()) {
      System.out.println("\t" + lang + "\t" + scores.get(lang));
    }
  }

  private void testFile(String fileContent) {
    for (String key : keyWordPoints.keySet()) {
      if (fileContent.indexOf(key) != -1) {
        for (LANGUAGES lang : keyWordPoints.get(key).keySet()) {
          scores.put(lang, scores.get(lang) == null ? new Integer(1) : scores.get(lang) + 1);
        }
      }
    }
  }

  private String getFileContent(String fileName) throws FileNotFoundException, IOException {
    File file = new File(fileName);
    FileReader fr = new FileReader(file);// Using 1.6 so no Files
    BufferedReader br = new BufferedReader(fr);
    StringBuilder fileContent = new StringBuilder();
    String line = br.readLine();
    while (line != null) {
      fileContent.append(line);
      line = br.readLine();
    }
    return fileContent.toString();
  }

  private void init() {
    scores = new HashMap<LANGUAGES, Integer>();

    keyWordPoints = new HashMap<String, LanguagePoints>();
    keyWordPoints.put("public class", new LanguagePoints().add(LANGUAGES.JAVA, 1));
    keyWordPoints.put("public static void main", new LanguagePoints().add(LANGUAGES.JAVA, 1));
    keyWordPoints.put("<html", new LanguagePoints().add(LANGUAGES.HTML, 1));
    keyWordPoints.put("<body", new LanguagePoints().add(LANGUAGES.HTML, 1));
    keyWordPoints.put("cout", new LanguagePoints().add(LANGUAGES.C, 1));
    keyWordPoints.put("#include", new LanguagePoints().add(LANGUAGES.C, 1));
    keyWordPoints.put("volatile", new LanguagePoints().add(LANGUAGES.JAVA, 1).add(LANGUAGES.C, 1));
  }

  private class LanguagePoints extends HashMap<LANGUAGES, Integer> {
    public LanguagePoints add(LANGUAGES l, Integer i) {
      this.put(l, i);
      return this;
    }
  }
}
\$\endgroup\$
6
\$\begingroup\$

Just a few broad generalizations.

I think it's fairly accurate.

This is Ruby btw. Takes (multiline) input from stdin.

puts case $<.read
when /\)\)\)\)\)/
  "Lisp"
when /}\s+}\s+}\s+}/
  "Java"
when /<>/
  "Perl"
when /|\w+|/
  "Ruby"
when /\w+ :- \w+ \./
  "Prolog"
when /^[+-<>\[\],.]+$/
  "brainfuck"
when /\[\[.*\]\]/
  "Bash"
when /~]\.{,/
  "golfscript"
end
\$\endgroup\$
8
  • \$\begingroup\$ I would think #include is a better predictor for c. What about #!/bin/(ba)?sh for bash/shell scripts? \$\endgroup\$ Commented Feb 19, 2014 at 4:01
  • \$\begingroup\$ @DigitalTrauma Yea, I think you're right about #include. For artistic reasons I'm not going to just catch the hash-bang where the name of the language is explicitly spelled out tho. \$\endgroup\$
    – daniero
    Commented Feb 19, 2014 at 4:18
  • \$\begingroup\$ #include is a comment in ini files and php \$\endgroup\$ Commented Feb 19, 2014 at 13:51
  • 1
    \$\begingroup\$ +1 for having prolog, but no C :) \$\endgroup\$
    – SztupY
    Commented Feb 19, 2014 at 23:48
  • 1
    \$\begingroup\$ I would add \$\w+ after the perl one to detect PHP. Also (\w+)::~\1 is usually a C++ destructor \$\endgroup\$
    – SztupY
    Commented Feb 19, 2014 at 23:53
3
\$\begingroup\$

Javascript - 6 languages - high accuracy

Current Languages: Java, C, HTML, PHP, CSS, Javascript

I work on the principle that whenever an input satisfies a criteria, it is given a score, and based on that score results are given.

Features:

  • No built-in functions that determine language type used.
  • Does not straightaway declare the input text is x language on seeing a keyword.
  • Suggests other probable languages also.

Should you feel that any of your inputs of the programs (that I have done till now) are not caught or get invalid results, then please report and I'd be happy to fix them.

Sample Input 1:

class A{public static void main(String[]a){System.out.println("<?php");}}

Sample Output 1:

My program thinks you have :
Java with a chance of 100%
Php with a chance of 25%
----------------

Explanation:

This should have failed the program and I would have printed PHP, but since my program works on the basis of scores, nothing fails and it easily identifies Java in the first place, followed by other possible results.

Sample Input 2:

class A{public static void main(String[]a){System.out.println("HelloWorld!");}}

Sample Output 2:

Java
----------------

Sample Input 3:

ABCDEFGHIJKLMNOPQRSTUVWXYZ

Sample Output 3:

Language not catched! Sorry.
----------------

The code:

// Helper functions

String.prototype.m = function(condition){
  return this.match(condition);
};

String.prototype.capitalize = function(){
  return this[0].toUpperCase() + this.substr(1);
};

function getFuncName(func){
  var temp =  func.toString();
  temp = temp.substr( "function ".length);
  temp = temp.substr( 0, temp.indexOf("("));
  return temp.capitalize();
}

// Get input
var lang_input = prompt("Enter programming language");

// Max score of 4 per lang

function java(input){
  var score = 0;
  score += input.m(/class[\s\n]+[\w$]+[\s\n]*\{/) ? 1 : 0;
  score += input.m(/public[\s\n]+static[\s\n]+void[\s\n]+main[\s\n]*/) ? 1 : 0;
  score += input.m(/\}[\s\n]*\}[\s\n]*$/) ? 1 : 0;
  score += input.m(/System[\s\n]*[.][\s\n]*out/) ? 1 : 0;
  return score;
}

function c(input){
  var score = 0;
  // if java has passsed
  if(checks[0][1] >= 3)return 0;

  score += input.m(/^#include\s+<[\w.]+>\s*\n/) ? 1 : 0;
  score += input.m(/main[\s\n]*\([\s\n]*(void)?[\s\n]*\)[\s\n]*\{/) ? 1 : 0;
  score += input.m(/printf[\s\n]+\(/) || input.m(/%d/) ? 1 : 0;
  score += input.m(/#include\s+<[\w.]+>\s*\n/) || input.m(/(%c|%f|%s)/) ? 1 : 0;
  return score;
}

function PHP(input){
  var score = 0;
  score += input.m(/<\?php/) ? 1 : 0;
  score += input.m(/\?>/) ? 1 : 0;
  score += input.m(/echo/) ? 1 : 0;
  score += input.m(/$[\w]+\s*=\s*/) ? 1 : 0;
  return score;
}

function HTML(input){
  var score = 0;
  // if php has passed
  if(checks[2][1] >= 2) return 0;

  score += input.m(/<!DOCTYPE ["' \w:\/\/]*>/) ? 1 : 0;
  score += input.m(/<html>/) && input.m(/<\/html>/) ? 1 : 0;
  score += input.m(/<body>/) && input.m(/<\/body/) ? 1 :  0;
  score += input.m(/<head>/) && input.m(/<\/head>/) ? 1 : 0;
  return score;
}

function javascript(input){
  var score = 0;
  score += input.m(/console[\s\n]*[.][\s\n]*log[\s\n*]\(/) ? 1 : 0;
  score += input.m(/[\s\n]*var[\s\n]+/) ? 1 : 0;
  score += input.m(/[\s\n]*function[\s\n]+[\w]+[\s\n]+\(/) ? 1 : 0;
  score += input.m(/document[\s\n]*[.]/) || 
           ( input.m(/\/\*/) && input.m(/\*\//) ) ||
           ( input.m(/\/\/.*\n/) )? 1 : 0;
  return score;
}

function CSS(input){
  var score = 0;
  score += input.m(/[a-zA-Z]+[\s\n]*\{[\w\n]*[a-zA-Z\-]+[\s\n]*:/) ? 1 : 0;
  // since color is more common, I give it a separate place
  score += input.m(/color/) ? 1 : 0;          
  score += input.m(/height/) || input.m(/width/) ? 1 : 0;
  score += input.m(/#[a-zA-Z]+[\s\n]*\{[\w\n]*[a-zA-Z\-]+[\s\n]*:/) ||
           input.m(/[.][a-zA-Z]+[\s\n]*\{[\w\n]*[a-zA-Z\-]+[\s\n]*:/) ||
           ( input.m(/\/\*/) && input.m(/\*\//) ) ? 1 : 0;
  return score;
}

// [Langs to check, scores]
var checks = [[java, 0], [c, 0], [PHP, 0], [HTML, 0], [javascript, 0], [CSS, 0]];
//Their scores

// Assign scores
for(var i = 0; i < checks.length; i++){
  var func = checks[i][0];
  checks[i][1] = func(lang_input);
}

// Sort the scores
checks.sort(function(a,b){ return b[1] - a[1]; });

var all_zero = true;

function check_all_zero(index){
  if(checks[index][1] > 0){ all_zero = false; return 0; } // someone is above zero

  // check next index only if it defined, else return zero
  if(checks[index + 1])
    check_all_zero(index + 1);
}

check_all_zero(0);

if(all_zero){
  console.log("Language not catched! Sorry.");
}else {
  var new_arr = [];                   // temp

  checks.map(function(value, index){
    if(value[1] > 0){
      var temp = [getFuncName(value[0]), value[1]];
      new_arr.push(temp);
    }
  });

  checks = new_arr.slice(0);          // array copy, because of mutation

  if(checks.length === 1){
    console.log(checks[0][0]);
  }else{
    console.log("My program thinks you have :");
    checks.map(function(value){
      var prob = (value[1]/4 * 100);
      console.log(value[0] + " with a chance of " + prob + "%");
    });
  }

} // Main else block finish

console.log("----------------");
\$\endgroup\$
1
\$\begingroup\$

Python, 4 languages, can you test how accurate this is?

Sorry, but you need to put the source code in a file.

import sys

file = sys.argv[1]

def detect(code: str):

    python_keys = {"#": 1, "print": 2, "def": 5, "with": 5, "in": 2, "tuple": 5, ":": 1.5, "except":5, "str": 1.5, "complex": 4, "_": 2, "match": 3}
    javascript_keys = {"{": 3, "function": 4, "=>": 5, "catch": 2.5, "switch": 2, "var": 5, "let": 5, "const": 5, "//": 2}
    c_keys = {"#include": 5, "#pragma once": 5, "#ifdef": 4.5, "#ifndef": 4.5, "*": 2, "size_t": 3, "{": 4, "struct": 5, "switch": 2, "//": 2}
    html_keys = {"<!--": 5, "<!DOCTYPE":5, "html": 5, "href": 5, ".css": 4, "</": 3, "http": 3}

    langs = ["Python", "JavaScript", "C", "HTML"]
    scores = {lang: 0 for lang in langs}

    for lang in langs:
        score = 0

        for key, value in eval(f"{lang.lower()}_keys").items():
            score += value * code.count(key)

        scores[lang] = score
    
    return scores

with open(file) as file:
    results = detect(file.read())

if "-l" in sys.argv:
    summed = sum(results.values())
    for lang, score in results.items():
        print(f"{lang}: {100 * score / summed}% likely")
else:
    max_score = max(results.values())
    print(*filter(lambda lang: results[lang] == max_score, results.keys()), sep=", ")

This can also receive the -l flag to list how likely each language is.

Here is it checking itself with the -l flag enabled (on powershell, current directory hidden for obvious reasons):

>py langdetect.py langdetect.py -l
Python: 49.022346368715084% likely
JavaScript: 18.01675977653631% likely
C: 23.18435754189944% likely
HTML: 9.776536312849162% likely
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.