programming languages - tiobe - Detectando el lenguaje de programación de un fragmento
tiobe software bv (17)
¿Cuál sería la mejor manera de detectar qué lenguaje de programación se usa en un fragmento de código?
Buen rompecabezas.
Creo que es imposible detectar todos los idiomas. Pero puedes activar los tokens de clave. (ciertas palabras reservadas y combinaciones de caracteres a menudo usadas).
Ben hay muchos idiomas con sintaxis similar. Por lo tanto, depende del tamaño del fragmento.
Configure el aleatorizador aleatorio 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;
Creo que el método utilizado en los filtros de spam funcionaría muy bien. Usted divide el fragmento en palabras. Luego, compara las ocurrencias de estas palabras con fragmentos conocidos y calcula la probabilidad de que este fragmento esté escrito en el lenguaje X para cada idioma que le interese.
http://en.wikipedia.org/wiki/Bayesian_spam_filtering
Si tiene el mecanismo básico, entonces es muy fácil agregar nuevos idiomas: simplemente entrene el detector con unos pocos fragmentos en el nuevo idioma (podría alimentarlo con un proyecto de código abierto). De esta forma, se entera de que es probable que el "Sistema" aparezca en los fragmentos de C # y "puts" en los fragmentos de Ruby.
De hecho, he usado este método para agregar detección de idioma a los fragmentos de código para el software del foro. Funcionó el 100% del tiempo, excepto en casos ambiguos:
print "Hello"
Déjame encontrar el código.
No pude encontrar el código, así que hice uno nuevo. Es un poco simplista pero funciona para mis pruebas. Actualmente, si le das mucho más código Python que código Ruby, es probable que diga que este código:
def foo
puts "hi"
end
es el código Python (aunque realmente es Ruby). Esto se debe a que Python también tiene una palabra clave def
. Entonces, si ha visto 1000x def
en Python y 100x def
en Ruby, aún puede decir Python aunque puts
and end
sea específico de Ruby. Podrías arreglar esto haciendo un seguimiento de las palabras vistas por idioma y dividiéndolo en algún lugar (o alimentándolo con cantidades iguales de código en cada idioma).
Espero que te ayude:
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)
Creo que la mayor distinción entre idiomas es su estructura. Entonces mi idea sería mirar ciertos elementos comunes en todos los idiomas y ver cómo difieren. Por ejemplo, puede usar expresiones regulares para seleccionar cosas como:
- definiciones de funciones
- declaraciones variables
- declaraciones de clase
- comentarios
- para bucles
- mientras que loops
- imprimir declaraciones
Y tal vez algunas otras cosas que deberían tener la mayoría de los idiomas. Luego usa un sistema de puntos. Otorga como máximo 1 punto por cada elemento si se encuentra la expresión regular. Obviamente, algunos lenguajes utilizarán exactamente la misma sintaxis (para los loops a menudo se escriben como for(int i=0; i<x; ++i)
por lo que múltiples lenguajes podrían sumar un punto para la misma cosa, pero al menos tú " re reduciendo la probabilidad de que sea un lenguaje completamente diferente). Algunos de ellos pueden puntuar 0 en general (el fragmento no contiene ninguna función, por ejemplo), pero eso está perfectamente bien.
Combina esto con la solución de Jules, y debería funcionar bastante bien. Tal vez también busque frecuencias de palabras clave para un punto extra.
Creo que no existe una solución única que pueda identificar el idioma en el que se encuentra un fragmento, solo en función de ese único fragmento. Tome la palabra clave print
. Podría aparecer en cualquier número de idiomas, cada uno de los cuales tiene diferentes propósitos y tiene una sintaxis diferente.
Tengo algunos consejos. Actualmente estoy escribiendo un pequeño fragmento de código para mi sitio web que se puede utilizar para identificar los lenguajes de programación. Al igual que la mayoría de las otras publicaciones, puede haber una gran variedad de lenguajes de programación que simplemente no has escuchado, no puedes contabilizarlos a todos.
Lo que he hecho es que cada idioma puede identificarse mediante una selección de palabras clave. Por ejemplo, Python podría identificarse de varias maneras. Probablemente sea más fácil si eliges "rasgos" que también son únicos en el lenguaje. Para Python, elijo el rasgo de usar dos puntos para comenzar un conjunto de declaraciones, que creo que es un rasgo bastante único (corríjanme si me equivoco).
Si, en mi ejemplo, no puede encontrar dos puntos para iniciar un conjunto de enunciados, pase a otro rasgo posible, digamos usando la palabra clave def
para definir una función. Ahora bien, esto puede causar algunos problemas, porque Ruby también usa la palabra clave def
para definir una función. La clave para distinguir a los dos (Python y Ruby) es usar varios niveles de filtrado para obtener la mejor combinación. Ruby usa la palabra clave end
para finalizar una función, mientras que Python no tiene nada para terminar una función, solo un de-indent pero no quieres ir allí. Pero, de nuevo, el end
también podría ser Lua, otro lenguaje de programación para agregar a la mezcla.
Puede ver que los lenguajes de programación simplemente se superponen demasiado. Una palabra clave que podría ser una palabra clave en un idioma podría ser una palabra clave en otro idioma. El uso de una combinación de palabras clave que a menudo van juntas, como el public static void main(String[] args)
Java public static void main(String[] args)
ayuda a eliminar esos problemas.
Como ya he dicho, su mejor oportunidad es buscar palabras clave relativamente únicas o conjuntos de palabras clave para separar una de la otra. Y, si lo haces mal, al menos tienes una oportunidad.
Depende del tipo de fragmento que tengas, pero lo ejecutaría a través de una serie de tokenizadores y vería en qué lenguaje del BNF surgió como válido.
Detección de idioma resuelto por otros:
El enfoque de Ohloh: https://github.com/blackducksw/ohcount/
El enfoque de Github: https://github.com/github/linguist
Es muy difícil y a veces imposible. ¿De qué idioma es este breve fragmento?
int i = 5;
int k = 0;
for (int j = 100 ; j > i ; i++) {
j = j + 1000 / i;
k = k + i * j;
}
(Sugerencia: podría ser cualquiera de varios).
Puede tratar de analizar varios idiomas e intentar decidir mediante el uso de análisis de frecuencia de palabras clave. Si se producen ciertos conjuntos de palabras clave con ciertas frecuencias en un texto, es probable que el lenguaje sea Java, etc. Pero no creo que obtenga nada que sea completamente infalible, como podría nombrar, por ejemplo, una variable en C del mismo nombre. como una palabra clave en Java, y el análisis de frecuencia será engañado.
Si tomas una muesca en complejidad puedes buscar estructuras, si una palabra clave determinada siempre viene después de otra, eso te dará más pistas. Pero también será mucho más difícil de diseñar e implementar.
Guesslang es una posible solución:
http://guesslang.readthedocs.io/en/latest/index.html
También hay SourceClassifier:
https://github.com/chrislo/sourceclassifier/tree/master
Me interesé en este problema después de encontrar un código en un artículo de blog que no pude identificar. Agregar esta respuesta ya que esta pregunta fue la primera búsqueda realizada para "identificar el lenguaje de programación".
Interesante. Tengo una tarea similar para reconocer el texto en diferentes formatos. ¿YAML, JSON, XML o propiedades de Java? Incluso con errores de sintaxis, por ejemplo, debo diferenciar JSON de XML con confianza.
Me imagino cómo modelamos el problema es fundamental. Como dijo Mark, la tokenización de una sola palabra es necesaria, pero probablemente no suficiente. Necesitaremos bigramas, o incluso trigramas. Pero creo que podemos ir más allá sabiendo que estamos viendo lenguajes de programación. Noté que casi cualquier lenguaje de programación tiene dos tipos únicos de tokens: símbolos y palabras clave . Los símbolos son relativamente fáciles de reconocer (algunos símbolos pueden ser literales que no forman parte del idioma). Entonces los bigrams o trigramas de símbolos recogerán estructuras de sintaxis únicas alrededor de los símbolos. Las palabras clave son otro objetivo fácil si el conjunto de capacitación es lo suficientemente grande y diverso. Una característica útil podría ser bigrams alrededor de posibles palabras clave. Otro tipo interesante de token es el espacio en blanco . En realidad, si tokenizamos de la manera habitual con espacios en blanco, perderemos esta información. Yo diría que, para analizar los lenguajes de programación, conservamos los tokens de espacios en blanco, ya que esto puede llevar información útil sobre la estructura de la sintaxis.
Finalmente, si elijo un clasificador como random forest, rastrearé github y reuniré todo el código fuente público. La mayor parte del archivo de código fuente puede etiquetarse por sufijo de archivo. Para cada archivo, lo dividiré aleatoriamente en líneas vacías en fragmentos de varios tamaños. Luego extraeré las características y entrenaré al clasificador usando los fragmentos etiquetados. Después de completar el entrenamiento, el clasificador se puede probar para precisión y recuperación.
La mejor solución que he encontrado es utilizar la https://github.com/github/linguist en una aplicación de Ruby on Rails. Es una forma específica de hacerlo, pero funciona. Esto fue mencionado anteriormente por @nisc pero le diré mis pasos exactos para usarlo. (Algunos de los siguientes comandos de línea de comando son específicos de ubuntu pero deben traducirse fácilmente a otros sistemas operativos)
Si tiene alguna aplicación de rieles a la que no le importe desordenar temporalmente, cree un nuevo archivo para insertar su fragmento de código en cuestión. (Si no tiene los rieles instalados hay una buena guía here aunque para ubuntu lo recomiendo. Luego ejecute rails new <name-your-app-dir>
y cd en ese directorio. Todo lo que necesita para ejecutar una aplicación de rieles ya está ahí).
Después de que tengas una aplicación de rieles para usar esto, agrega gem ''github-linguist''
a tu Gemfile (literalmente llamado Gemfile
en el directorio de tu aplicación, no ext).
Luego instale ruby-dev ( sudo apt-get install ruby-dev
)
Luego instala cmake ( sudo apt-get install cmake
)
Ahora puede ejecutar gem install github-linguist
(si obtiene un error que dice icu requerido, haga sudo apt-get install libicu-dev
y vuelva a intentarlo)
(Es posible que deba hacer un sudo apt-get update
o sudo apt-get install make
o sudo apt-get install build-essential
si lo anterior no funcionó)
Ahora todo está configurado. Ahora puede usar esto cada vez que quiera verificar los fragmentos de código. En un editor de texto, abra el archivo que ha creado para insertar su fragmento de código (digamos que es app/test.tpl
pero si conoce la extensión de su fragmento, utilícelo en lugar de .tpl
. Si no conoce el extensión, no use uno). Ahora pegue su fragmento de código en este archivo. Vaya a la línea de comandos y ejecute bundle install
(debe estar en el directorio de su aplicación). A continuación, ejecute linguist app/test.tpl
(más en general, linguist <path-to-code-snippet-file>
). Le dirá el tipo, el tipo de mimo y el idioma. Para múltiples archivos (o para uso general con una aplicación de ruby / rails) puede ejecutar bundle exec linguist --breakdown
en el directorio de su aplicación.
Parece mucho trabajo extra, especialmente si todavía no tienes rieles, pero en realidad no necesitas saber NADA sobre raíles si sigues estos pasos y realmente no he encontrado una manera mejor de detectar el idioma de un archivo / fragmento de código.
Necesitaba esto, así que creé el mío. https://github.com/bertyhell/CodeClassifier
Se puede ampliar fácilmente agregando un archivo de entrenamiento en la carpeta correcta. Escrito en c #. Pero me imagino que el código se convierte fácilmente en cualquier otro idioma.
No creo que haya una forma fácil de lograr esto. Probablemente generaría listas de símbolos / palabras clave comunes exclusivas para ciertos idiomas / clases de idiomas (por ejemplo, llaves para lenguaje C, las palabras clave Dim y Sub para lenguajes BASIC, la palabra clave def para Python, la palabra clave let para idiomas funcionales) . A continuación, puede utilizar funciones de sintaxis básicas para reducir aún más.
Prettify es un paquete de Javascript que hace un buen trabajo de detección de lenguajes de programación:
http://code.google.com/p/google-code-prettify/
Principalmente es un resaltador de sintaxis, pero probablemente exista una forma de extraer la parte de detección con el fin de detectar el idioma de un fragmento.
Primero, trataría de encontrar los keyworks específicos de un idioma, por ejemplo
"package, class, implements "=> JAVA
"<?php " => PHP
"include main fopen strcmp stdout "=>C
"cout"=> C++
etc...
Puede encontrar material útil aquí: http://alexgorbatchev.com/wiki/SyntaxHighlighter . Alex ha dedicado mucho tiempo a descifrar cómo analizar un gran número de idiomas diferentes y cuáles son los elementos de sintaxis clave.
Una alternativa es usar highlight.js , que realiza resaltado de sintaxis pero utiliza la tasa de éxito del proceso de resaltado para identificar el idioma. En principio, cualquier base de código de resaltado de sintaxis se podría usar de la misma manera, pero lo bueno de highlight.js es que la detección de idioma se considera una función y se usa con fines de prueba .
ACTUALIZACIÓN: Intenté esto y no funcionó tan bien. El JavaScript comprimido lo confundió por completo, es decir, el tokenizer es sensible al espacio en blanco. En general, solo contar los hits destacados no parece muy confiable. Un analizador más potente, o tal vez un conteo de secciones sin igual, podría funcionar mejor.