Detectando linguagem de programação a partir de um snippet


114

Qual seria a melhor maneira de detectar qual linguagem de programação é usada em um trecho de código?


1
Há praticamente um número infinito de idiomas por aí ... você quer detectar ALGUM deles? Ou estamos falando apenas dos populares?
Spencer Ruport

Apenas os mais populares (C / C ++, C #, Java, Pascal, Python, VB.NET. PHP, JavaScript e talvez Haskell).
João Matos

12
Bem, Haskell não pode ser popular porque nunca ouvi falar dele. ;-)
Stephanie Page

22
Você provavelmente não sabe muito sobre linguagens de programação se nunca ouviu falar de Haskell.
Akhorus

4
Existe este serviço online que faz isso: algorithmia.com/algorithms/PetiteProgrammer/…
Benny Neugebauer

Respostas:


99

Acho que o método usado em filtros de spam funcionaria muito bem. Você divide o fragmento em palavras. Em seguida, você compara as ocorrências dessas palavras com trechos conhecidos e calcula a probabilidade de que esse trecho seja escrito na linguagem X para cada idioma no qual você está interessado.

http://en.wikipedia.org/wiki/Bayesian_spam_filtering

Se você tem o mecanismo básico, é muito fácil adicionar novos idiomas: apenas treine o detector com alguns trechos no novo idioma (você pode alimentá-lo com um projeto de código aberto). Dessa forma, ele aprende que "Sistema" provavelmente aparecerá em fragmentos C # e "puts" em fragmentos Ruby.

Na verdade, usei esse método para adicionar detecção de idioma a trechos de código para software de fórum. Funcionou 100% do tempo, exceto em casos ambíguos:

print "Hello"

Deixe-me encontrar o código.

Não consegui encontrar o código, então fiz um novo. É um pouco simplista, mas funciona para meus testes. Atualmente, se você o alimentar com muito mais código Python do que código Ruby, é provável que diga que este código:

def foo
   puts "hi"
end

é o código Python (embora seja realmente Ruby). Isso ocorre porque Python também tem uma defpalavra - chave. Então, se ele viu 1000x defem Python e 100x defem Ruby, ele ainda pode dizer Python, embora putseend é específico de Ruby. Você poderia corrigir isso mantendo o controle das palavras vistas por idioma e dividindo por isso em algum lugar (ou alimentando-as com quantidades iguais de código em cada idioma).

Espero que ajude você:

class Classifier
  def initialize
    @data = {}
    @totals = Hash.new(1)
  end

  def words(code)
    code.split(/[^a-z]/).reject{|w| w.empty?}
  end

  def train(code,lang)
    @totals[lang] += 1
    @data[lang] ||= Hash.new(1)
    words(code).each {|w| @data[lang][w] += 1 }
  end

  def classify(code)
    ws = words(code)
    @data.keys.max_by do |lang|
      # We really want to multiply here but I use logs 
      # to avoid floating point underflow
      # (adding logs is equivalent to multiplication)
      Math.log(@totals[lang]) +
      ws.map{|w| Math.log(@data[lang][w])}.reduce(:+)
    end
  end
end

# Example usage

c = Classifier.new

# Train from files
c.train(open("code.rb").read, :ruby)
c.train(open("code.py").read, :python)
c.train(open("code.cs").read, :csharp)

# Test it on another file
c.classify(open("code2.py").read) # => :python (hopefully)

1
Também preciso usá-lo no software do fórum. Obrigado pela dica sobre a filtragem Bayesiana.
João Matos

12
Fiz algo assim na minha aula de PNL, mas demos um passo adiante. Você não gosta de olhar para as frequências de uma única palavra, mas pares e triplos de palavras. Por exemplo, "public" pode ser uma palavra-chave em muitas linguagens, mas "public static void" é mais comum em C #. Se o triplo não puder ser encontrado, você volta para 2 e, em seguida, 1.
início de

1
Também pode querer pensar sobre onde você está dividindo as palavras. No PHP, as variáveis ​​começam com $, então talvez você não deva estar dividindo em limites de palavras, porque $deve ficar com a variável. Os operadores gostam =>e :=devem ficar juntos como um único token, mas OTH você provavelmente deve dividir em torno de {s porque eles sempre estão por conta própria.
2010

2
Sim. Uma maneira de evitar a divisão é usar ngrams: você pega cada substring de comprimento n. Por exemplo, os 5 gramas de "puts foo" são "puts" "uts f", "ts fo" e "s foo". Esta estratégia pode parecer estranha, mas funciona melhor do que você pensa, apenas não é como um humano resolveria o problema. Para decidir qual método funciona melhor, você terá que testar os dois ...
Jules

2
Algumas linguagens têm muito pouca sintaxe, entretanto. Também estou especulando que nomes de variáveis ​​comuns dominariam as palavras-chave da linguagem. Basicamente, se você tiver um trecho de código C escrito por um húngaro, com nomes de variáveis ​​e comentários em húngaro, em seus dados de treinamento, é provável que qualquer outra fonte com húngaro seja considerada "semelhante".
tripleee

26

Detecção de idioma resolvida por outros:

Abordagem de Ohloh: https://github.com/blackducksw/ohcount/

Abordagem do Github: https://github.com/github/linguist


4
Eu examinei ambas as soluções e nenhuma fará exatamente o que foi pedido. Eles examinam principalmente as extensões de arquivo para determinar o idioma, portanto, não podem necessariamente examinar um fragmento sem uma pista da extensão.
Hawkee,

5
A abordagem do Github agora inclui um classificador Bayesiano também. Ele detecta principalmente um candidato a idioma com base na extensão de arquivo, mas quando uma extensão de arquivo corresponde a vários candidatos (por exemplo, ".h" -> C, C ++, ObjC), ele tokenizará a amostra de código de entrada e classificará em relação a um conjunto pré-treinado De dados. A versão Github pode ser forçada a escanear o código sempre sem olhar para a extensão também.
Benzi



5

É muito difícil e às vezes impossível. De qual idioma é este pequeno snippet?

int i = 5;
int k = 0;
for (int j = 100 ; j > i ; i++) {
    j = j + 1000 / i;
    k = k + i * j;
}

(Dica: pode ser qualquer um entre vários.)

Você pode tentar analisar vários idiomas e tentar decidir usando a análise de frequência de palavras-chave. Se certos conjuntos de palavras-chave ocorrem com certas frequências em um texto, é provável que a linguagem seja Java etc. Mas eu não acho que você obterá algo totalmente à prova de idiotas, já que você poderia nomear, por exemplo, uma variável em C com o mesmo nome como uma palavra-chave em Java, e a análise de frequência será enganada.

Se você aumentar um pouco a complexidade, poderá procurar estruturas; se uma determinada palavra-chave sempre vier depois de outra, isso lhe dará mais pistas. Mas também será muito mais difícil de projetar e implementar.


26
Bem, se vários idiomas são possíveis, o detector pode simplesmente fornecer todos os candidatos possíveis.
Steven Haryanto

Ou pode fornecer o primeiro que corresponda. Se o caso de uso do mundo real for algo como destaque de sintaxe, então realmente não faria diferença. O que significa que qualquer um dos idiomas correspondentes resultaria em realçar o código corretamente.
jonschlinkert

5

Uma alternativa é usar o highlight.js , que realiza o realce da sintaxe, mas usa a taxa de sucesso do processo de realce para identificar o idioma. Em princípio, qualquer base de código de realce de sintaxe pode ser usada da mesma maneira, mas o bom do destaque.js é que a detecção de idioma é considerada um recurso e é usada para fins de teste .

ATUALIZAÇÃO: eu tentei isso e não funcionou muito bem. O JavaScript compactado confundiu completamente, ou seja, o tokenizer é sensível a espaços em branco. Geralmente, apenas contar as ocorrências de destaque não parece muito confiável. Um analisador mais forte, ou talvez contagens de seção sem correspondência, pode funcionar melhor.


Os dados de idioma incluídos no destaque.js são limitados aos valores necessários para realce, o que acaba sendo insuficiente para a detecção de idioma (especialmente para pequenas quantidades de código).
Adam Kennedy

Acho que está tudo bem, verifique com este violino jsfiddle.net/3tgjnz10
sebilasse

4

Primeiro, eu tentaria encontrar os keyworks específicos de uma linguagem, por exemplo

"package, class, implements "=> JAVA
"<?php " => PHP
"include main fopen strcmp stdout "=>C
"cout"=> C++
etc...

3
O problema é que essas palavras-chave ainda podem aparecer em qualquer idioma, seja como nomes de variáveis ​​ou em strings. Isso, e há muita sobreposição nas palavras-chave usadas. Você teria que fazer mais do que apenas procurar palavras-chave.
2010

2

Dependeria do tipo de snippet que você tem, mas eu o executaria em uma série de tokenizers e veria em qual BNF da linguagem ele seria válido.


Todas as linguagens nem podem ser descritas por um BNF. Se você puder redefinir palavras-chave e criar macros, será muito mais difícil. Além disso, como estamos falando sobre um snippet, você teria que fazer uma correspondência parcial contra um BNF, que é mais difícil e mais sujeito a erros.

2

Belo quebra-cabeça.

Acho que é impossível detectar todas as línguas. Mas você pode acionar tokens de chave. (certas palavras reservadas e combinações de caracteres frequentemente usadas).

Bem, existem muitas linguagens com sintaxe semelhante. Portanto, depende do tamanho do snippet.


1

Prettify é um pacote Javascript que faz um bom trabalho na detecção de linguagens de programação:

http://code.google.com/p/google-code-prettify/

É principalmente um realce de sintaxe, mas provavelmente há uma maneira de extrair a parte de detecção com o propósito de detectar o idioma de um snippet.


1
Após uma inspeção mais detalhada, parece que o embelezamento não detecta realmente a linguagem, mas destaca de acordo com a sintaxe de cada elemento.
Hawkee,


1

Eu precisava disso, então criei o meu próprio. https://github.com/bertyhell/CodeClassifier

É facilmente extensível adicionando um arquivo de treinamento na pasta correta. Escrito em c #. Mas imagino que o código seja facilmente convertido para qualquer outra linguagem.


0

Eu não acho que haveria uma maneira fácil de fazer isso. Eu provavelmente geraria listas de símbolos / palavras-chave comuns exclusivas para determinados idiomas / classes de idiomas (por exemplo, colchetes para linguagem C-style, as palavras-chave Dim e Sub para linguagens BASIC, a palavra-chave def para Python, a palavra-chave let para linguagens funcionais) . Então, você poderá usar os recursos básicos de sintaxe para restringi-la ainda mais.


0

Acho que a maior distinção entre as línguas é sua estrutura. Portanto, minha ideia seria examinar certos elementos comuns em todas as línguas e ver como eles diferem. Por exemplo, você pode usar regexes para escolher coisas como:

  • definições de função
  • declarações de variáveis
  • declarações de classe
  • comentários
  • para loops
  • while loops
  • imprimir declarações

E talvez algumas outras coisas que a maioria dos idiomas deveria ter. Em seguida, use um sistema de pontos. Conceda no máximo 1 ponto para cada elemento se a regex for encontrada. Obviamente, algumas linguagens usarão exatamente a mesma sintaxe (os loops for são frequentemente escritos comofor(int i=0; i<x; ++i) forma que várias linguagens podem cada uma marcar um ponto para a mesma coisa, mas pelo menos você está reduzindo a probabilidade de ser uma linguagem totalmente diferente). Alguns deles podem pontuar 0s no quadro (o snippet não contém nenhuma função, por exemplo), mas isso é perfeitamente normal.

Combine isso com a solução de Jules e funcionará muito bem. Talvez também procure frequências de palavras-chave para um ponto extra.


0

Interessante. Tenho uma tarefa semelhante de reconhecer texto em diferentes formatos. Propriedades YAML, JSON, XML ou Java? Mesmo com erros de sintaxe, por exemplo, devo diferenciar JSON de XML com confiança.

Eu acho que a forma como modelamos o problema é crítica. Como disse Mark, a tokenização de uma palavra é necessária, mas provavelmente não o suficiente. Precisaremos de bigramas ou mesmo trigramas. Mas acho que podemos ir mais longe sabendo que estamos olhando para linguagens de programação. Percebo que quase todas as linguagens de programação têm dois tipos exclusivos de tokens - símbolos e palavras - chave . Os símbolos são relativamente fáceis (alguns símbolos podem ser literais que não fazem parte da linguagem) de reconhecer. Então, os bigramas ou trigramas de símbolos coletarão estruturas de sintaxe exclusivas em torno dos símbolos. Palavras-chave são outro alvo fácil se o conjunto de treinamento for grande e diversificado o suficiente. Um recurso útil pode ser bigrams em torno de possíveis palavras-chave. Outro tipo interessante de token é espaço em branco. Na verdade, se tokenizarmos da maneira usual por espaço em branco, perderemos essa informação. Eu diria que, para analisar linguagens de programação, mantemos os tokens de espaço em branco, pois isso pode conter informações úteis sobre a estrutura da sintaxe.

Finalmente, se eu escolher um classificador como floresta aleatória, rastrearei o github e reunirei todo o código-fonte público. A maior parte do arquivo de código-fonte pode ser rotulada pelo sufixo do arquivo. Para cada arquivo, vou dividi-lo aleatoriamente em linhas vazias em fragmentos de vários tamanhos. Em seguida, extrairei os recursos e treinarei o classificador usando os fragmentos rotulados. Após a conclusão do treinamento, o classificador pode ser testado quanto à precisão e recuperação.


0

A melhor solução que encontrei é usar a gema do linguista em um aplicativo Ruby on Rails. É uma maneira específica de fazer isso, mas funciona. Isso foi mencionado acima por @nisc, mas direi a você meus passos exatos para usá-lo. (Alguns dos seguintes comandos de linha de comando são específicos para o Ubuntu, mas devem ser facilmente traduzidos para outros sistemas operacionais)

Se você tiver qualquer aplicativo rails com o qual não se importe de mexer temporariamente, crie um novo arquivo nele para inserir o trecho de código em questão. (Se você não tem rails instalado, há um bom guia aqui embora para ubuntu eu recomendo isso . Então execute rails new <name-your-app-dir>e faça cd nesse diretório. Tudo que você precisa para executar um aplicativo Rails já está lá).

Depois de ter um aplicativo rails para usar, adicione gem 'github-linguist'ao seu Gemfile (literalmente chamado apenas Gemfileno diretório do aplicativo, sem ext).

Em seguida, instale ruby-dev ( sudo apt-get install ruby-dev)

Em seguida, instale cmake ( sudo apt-get install cmake)

Agora você pode executar gem install github-linguist(se você receber um erro que diz icu obrigatório, execute sudo apt-get install libicu-deve tente novamente)

(Você pode precisar fazer um sudo apt-get updateou sudo apt-get install makeou sudo apt-get install build-essentialse o acima não funcionar)

Agora tudo está configurado. Agora você pode usar isso a qualquer momento que quiser verificar trechos de código. Em um editor de texto, abra o arquivo que você fez para inserir o seu snippet de código (digamos que seja, app/test.tplmas se souber a extensão do seu snippet, use-a em vez de .tpl. Se você não conhece a extensão, não use uma ) Agora cole seu snippet de código neste arquivo. Vá para a linha de comando e execute bundle install(deve estar no diretório do seu aplicativo). Em seguida, execute linguist app/test.tpl(de forma mais geral linguist <path-to-code-snippet-file>). Ele informará o tipo, o tipo MIME e o idioma. Para vários arquivos (ou para uso geral com um aplicativo ruby ​​/ rails), você pode executar bundle exec linguist --breakdownno diretório do seu aplicativo.

Parece muito trabalho extra, especialmente se você ainda não tem trilhos, mas você realmente não precisa saber NADA sobre trilhos se seguir estes passos e eu realmente não encontrei uma maneira melhor de detectar o idioma de um arquivo / trecho de código.


0

Acredito que não haja uma solução única que possa identificar em qual idioma um snippet está, apenas com base nesse único snippet. Pegue a palavra-chave print. Ele pode aparecer em qualquer número de idiomas, cada um deles para finalidades diferentes e com sintaxe diferente.

Eu tenho alguns conselhos. No momento, estou escrevendo um pequeno código para meu site que pode ser usado para identificar linguagens de programação. Como a maioria dos outros posts, pode haver uma grande variedade de linguagens de programação que você simplesmente não ouviu, você não pode contabilizar todas elas.

O que fiz é que cada idioma pode ser identificado por uma seleção de palavras-chave. Por exemplo, Python pode ser identificado de várias maneiras. Provavelmente será mais fácil se você escolher "características" que certamente são exclusivas do idioma. Para Python, eu escolho a característica de usar dois pontos para iniciar um conjunto de declarações, o que acredito ser uma característica bastante única (corrija-me se estiver errado).

Se, em meu exemplo, você não conseguir encontrar dois pontos para iniciar um conjunto de instruções, vá para outra característica possível, digamos, usar a defpalavra-chave para definir uma função. Agora, isso pode causar alguns problemas, porque Ruby também usa a palavra def- chave para definir uma função. A chave para diferenciar os dois (Python e Ruby) é usar vários níveis de filtragem para obter a melhor correspondência. Ruby usa a palavra end- chave para terminar uma função, enquanto Python não tem nada para terminar uma função, apenas um desindentação, mas você não quer ir para lá. Mas, novamente, endtambém poderia ser Lua, mais uma linguagem de programação para adicionar à mistura.

Você pode ver que as linguagens de programação simplesmente se sobrepõem. Uma palavra-chave que pode ser uma palavra-chave em um idioma pode ser uma palavra-chave em outro idioma. Usar uma combinação de palavras-chave que geralmente andam juntas, como Java, public static void main(String[] args)ajuda a eliminar esses problemas.

Como eu já disse, sua melhor chance é procurar palavras-chave ou conjuntos de palavras-chave relativamente exclusivos para separar um do outro. E, se você errar, pelo menos você tentou.


0

Configure o misturador aleatório como

matrix S = matrix(GF(2),k,[random()<0.5for _ in range(k^2)]); while (rank(S) < k) : S[floor(k*random()),floor(k*random())] +=1;

0

Este site parece ser muito bom na identificação de idiomas, se você deseja uma maneira rápida de colar um snippet em um formulário da web, em vez de fazê-lo programaticamente: http://dpaste.com/

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.