Qual seria a melhor maneira de detectar qual linguagem de programação é usada em um trecho de código?
Qual seria a melhor maneira de detectar qual linguagem de programação é usada em um trecho de código?
Respostas:
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 def
palavra - chave. Então, se ele viu 1000x def
em Python e 100x def
em Ruby, ele ainda pode dizer Python, embora puts
eend
é 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)
$
, 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.
Detecção de idioma resolvida por outros:
Abordagem de Ohloh: https://github.com/blackducksw/ohcount/
Abordagem do Github: https://github.com/github/linguist
Você pode encontrar algum material útil aqui: http://alexgorbatchev.com/wiki/SyntaxHighlighter . Alex passou muito tempo descobrindo como analisar um grande número de linguagens diferentes e quais são os principais elementos da sintaxe.
Guesslang é uma solução possível:
http://guesslang.readthedocs.io/en/latest/index.html
Há também SourceClassifier:
https://github.com/chrislo/sourceclassifier/tree/master
Fiquei interessado neste problema depois de encontrar algum código em um artigo de blog que não consegui identificar. Adicionar esta resposta, uma vez que esta pergunta foi a primeira ocorrência de pesquisa por "identificar linguagem de programação".
É 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.
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.
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...
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.
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.
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.
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.
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.
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:
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.
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.
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 Gemfile
no 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-dev
e tente novamente)
(Você pode precisar fazer um sudo apt-get update
ou sudo apt-get install make
ou sudo apt-get install build-essential
se 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.tpl
mas 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 --breakdown
no 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.
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 def
palavra-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, end
també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.
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/