Quale sarebbe il modo migliore per rilevare quale linguaggio di programmazione viene utilizzato in uno snippet di codice?
Quale sarebbe il modo migliore per rilevare quale linguaggio di programmazione viene utilizzato in uno snippet di codice?
Risposte:
Penso che il metodo utilizzato nei filtri antispam funzionerebbe molto bene. Hai diviso lo snippet in parole. Quindi confronti le occorrenze di queste parole con frammenti noti e calcoli la probabilità che questo frammento sia scritto nella lingua X per ogni lingua che ti interessa.
http://en.wikipedia.org/wiki/Bayesian_spam_filtering
Se hai il meccanismo di base, è molto facile aggiungere nuove lingue: basta addestrare il rilevatore con alcuni frammenti nella nuova lingua (potresti alimentarlo con un progetto open source). In questo modo apprende che è probabile che "System" appaia negli snippet di C # e "put" negli snippet di Ruby.
In realtà ho usato questo metodo per aggiungere il rilevamento della lingua agli snippet di codice per il software del forum. Ha funzionato il 100% delle volte, tranne in casi ambigui:
print "Hello"
Fammi trovare il codice.
Non sono riuscito a trovare il codice, quindi ne ho creato uno nuovo. È un po 'semplicistico ma funziona per i miei test. Attualmente, se gli dai molto più codice Python rispetto al codice Ruby, è probabile che questo codice:
def foo
puts "hi"
end
è codice Python (sebbene in realtà sia Ruby). Questo perché anche Python ha una def
parola chiave. Quindi, se ha visto 1000x def
in Python e 100x def
in Ruby, allora può ancora dire Python, anche se puts
e end
è Ruby-specifici. Puoi risolvere questo problema tenendo traccia delle parole visualizzate per lingua e dividendole da qualche parte (o inserendole uguali quantità di codice in ciascuna lingua).
Spero che ti aiuti:
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)
$
, quindi forse non dovresti dividere i limiti di parola, perché $
dovrebbero restare con la variabile. Agli operatori piace =>
e :=
dovrebbero essere uniti come un singolo token, ma OTH probabilmente dovresti dividere intorno a {
s perché stanno sempre da soli.
Rilevamento della lingua risolto da altri:
L'approccio di Ohloh: https://github.com/blackducksw/ohcount/
L'approccio di Github: https://github.com/github/linguist
Potresti trovare del materiale utile qui: http://alexgorbatchev.com/wiki/SyntaxHighlighter . Alex ha passato molto tempo a capire come analizzare un gran numero di lingue diverse e quali sono gli elementi chiave della sintassi.
Guesslang è una possibile soluzione:
http://guesslang.readthedocs.io/en/latest/index.html
C'è anche SourceClassifier:
https://github.com/chrislo/sourceclassifier/tree/master
Mi sono interessato a questo problema dopo aver trovato del codice in un articolo del blog che non sono riuscito a identificare. L'aggiunta di questa risposta poiché questa domanda è stata il primo risultato della ricerca per "identificare il linguaggio di programmazione".
È molto difficile ea volte impossibile. Da quale lingua proviene questo breve snippet?
int i = 5;
int k = 0;
for (int j = 100 ; j > i ; i++) {
j = j + 1000 / i;
k = k + i * j;
}
(Suggerimento: potrebbe essere uno qualsiasi dei tanti.)
Puoi provare ad analizzare varie lingue e provare a decidere utilizzando l'analisi della frequenza delle parole chiave. Se alcuni gruppi di parole chiave si verificano con determinate frequenze in un testo è probabile che la lingua sia Java ecc. Ma non penso che otterrai nulla che sia completamente a prova di errore, poiché potresti nominare ad esempio una variabile in C con lo stesso nome come parola chiave in Java e l'analisi della frequenza verrà ingannata.
Se aumenti la complessità potresti cercare strutture, se una certa parola chiave viene sempre dopo un'altra, questo ti darà più indizi. Ma sarà anche molto più difficile da progettare e implementare.
Un'alternativa è usare highlight.js , che esegue l'evidenziazione della sintassi ma utilizza la percentuale di successo del processo di evidenziazione per identificare la lingua. In linea di principio, qualsiasi codebase per l'evidenziatore di sintassi potrebbe essere utilizzato allo stesso modo, ma la cosa bella di highlight.js è che il rilevamento della lingua è considerato una funzionalità e viene utilizzato a scopo di test .
AGGIORNAMENTO: l' ho provato e non ha funzionato molto bene. JavaScript compresso lo ha completamente confuso, cioè il tokenizer è sensibile agli spazi. In generale, il solo conteggio dei risultati in evidenza non sembra molto affidabile. Un parser più forte, o forse un conteggio delle sezioni senza eguali, potrebbe funzionare meglio.
In primo luogo, proverei a trovare i keywork specifici di una lingua, ad es
"package, class, implements "=> JAVA
"<?php " => PHP
"include main fopen strcmp stdout "=>C
"cout"=> C++
etc...
Dipenderà dal tipo di frammento che hai, ma lo eseguirò attraverso una serie di tokenizzatori e vedrei quale BNF della lingua è risultato valido.
Bel puzzle.
Penso che sia impossibile rilevare tutte le lingue. Ma potresti attivare i token chiave. (alcune parole riservate e combinazioni di caratteri usate spesso).
Ben ci sono molte lingue con sintassi simile. Quindi dipende dalla dimensione dello snippet.
Prettify è un pacchetto Javascript che fa un buon lavoro nel rilevare i linguaggi di programmazione:
http://code.google.com/p/google-code-prettify/
È principalmente un evidenziatore della sintassi, ma probabilmente esiste un modo per estrarre la parte di rilevamento allo scopo di rilevare la lingua da uno snippet.
Avevo bisogno di questo così ho creato il mio. https://github.com/bertyhell/CodeClassifier
È molto facilmente estendibile aggiungendo un file di allenamento nella cartella corretta. Scritto in c #. Ma immagino che il codice possa essere facilmente convertito in qualsiasi altra lingua.
Non penserei che ci sarebbe un modo semplice per ottenere questo risultato. Probabilmente genererei elenchi di simboli / parole chiave comuni univoche per determinati linguaggi / classi di linguaggi (es. Parentesi graffe per il linguaggio in stile C, le parole chiave Dim e Sub per i linguaggi BASIC, la parola chiave def per Python, la parola chiave let per i linguaggi funzionali) . Potresti quindi essere in grado di utilizzare le funzionalità di sintassi di base per restringerlo ulteriormente.
Penso che la più grande distinzione tra le lingue sia la sua struttura. Quindi la mia idea sarebbe quella di guardare alcuni elementi comuni in tutte le lingue e vedere come differiscono. Ad esempio, potresti usare le espressioni regolari per selezionare cose come:
E forse alcune altre cose che la maggior parte delle lingue dovrebbe avere. Quindi usa un sistema a punti. Assegna al massimo 1 punto per ogni elemento se viene trovata la regex. Ovviamente, alcune lingue useranno la stessa identica sintassi (i cicli for sono spesso scritti in for(int i=0; i<x; ++i)
modo che più lingue possano ottenere un punto per la stessa cosa, ma almeno stai riducendo la probabilità che sia una lingua completamente diversa). Alcuni di loro potrebbero segnare 0 su tutta la linea (lo snippet non contiene affatto una funzione, per esempio) ma va benissimo.
Combina questo con la soluzione di Jules e dovrebbe funzionare abbastanza bene. Forse cerca anche le frequenze delle parole chiave per un punto in più.
Interessante. Ho un compito simile per riconoscere il testo in diversi formati. Proprietà YAML, JSON, XML o Java? Anche con errori di sintassi, ad esempio, dovrei distinguere JSON da XML con sicurezza.
Immagino che il modo in cui modelliamo il problema sia critico. Come ha detto Mark, la tokenizzazione di una sola parola è necessaria ma probabilmente non sufficiente. Avremo bisogno di bigrammi, o anche di trigrammi. Ma penso che possiamo andare oltre sapendo che stiamo guardando ai linguaggi di programmazione. Ho notato che quasi tutti i linguaggi di programmazione hanno due tipi unici di token: simboli e parole chiave . I simboli sono relativamente facili (alcuni simboli potrebbero essere letterali non parte della lingua) da riconoscere. Quindi bigrammi o trigrammi di simboli raccoglieranno strutture di sintassi uniche attorno ai simboli. Le parole chiave sono un altro obiettivo facile se il set di formazione è abbastanza ampio e diversificato. Una caratteristica utile potrebbe essere bigram intorno a possibili parole chiave. Un altro tipo interessante di token è lo spazio bianco. In realtà, se tokenizziamo nel solito modo con spazi bianchi, perderemo questa informazione. Direi che, per analizzare i linguaggi di programmazione, teniamo i token di spazi vuoti in quanto ciò potrebbe contenere informazioni utili sulla struttura della sintassi.
Infine, se scelgo un classificatore come la foresta casuale, eseguirò la scansione di github e raccoglierò tutto il codice sorgente pubblico. La maggior parte del file del codice sorgente può essere etichettato in base al suffisso del file. Per ogni file, lo dividerò casualmente su righe vuote in frammenti di varie dimensioni. Quindi estrarò le funzionalità e addestrerò il classificatore utilizzando gli snippet etichettati. Al termine dell'addestramento, è possibile testare la precisione e il richiamo del classificatore.
La migliore soluzione che ho trovato è usare la gemma del linguista in un'app Ruby on Rails. È un modo specifico per farlo, ma funziona. Questo è stato menzionato sopra da @nisc ma ti dirò i miei passi esatti per usarlo. (Alcuni dei seguenti comandi della riga di comando sono specifici di Ubuntu ma dovrebbero essere facilmente tradotti in altri sistemi operativi)
Se hai un'app rails con cui non ti dispiace temporaneamente scherzare, crea un nuovo file al suo interno per inserire lo snippet di codice in questione. (Se non si dispone di rotaie installato c'è una buona guida qui anche se per Ubuntu vi consiglio questo . Quindi eseguire rails new <name-your-app-dir>
e cd in quella directory. Tutto il necessario per eseguire un'applicazione Rails è già lì).
Dopo che hai un'app rails con cui usarlo, aggiungilo gem 'github-linguist'
al tuo Gemfile (letteralmente chiamato solo Gemfile
nella directory dell'app, senza ext).
Quindi installa ruby-dev ( sudo apt-get install ruby-dev
)
Quindi installa cmake ( sudo apt-get install cmake
)
Ora puoi eseguire gem install github-linguist
(se ricevi un errore che dice che è necessaria la terapia intensiva, fallo sudo apt-get install libicu-dev
e riprova)
(Potrebbe essere necessario eseguire un sudo apt-get update
o sudo apt-get install make
o sudo apt-get install build-essential
se quanto sopra non ha funzionato)
Adesso è tutto pronto. Ora puoi utilizzarlo ogni volta che desideri controllare gli snippet di codice. In un editor di testo, apri il file che hai creato per inserire lo snippet di codice (diciamo solo che lo è, app/test.tpl
ma se conosci l'estensione del tuo snippet, usalo invece di .tpl
. Se non conosci l'estensione, non usarne uno ). Ora incolla lo snippet di codice in questo file. Vai alla riga di comando ed esegui bundle install
(deve essere nella directory dell'applicazione). Quindi esegui linguist app/test.tpl
(più in generale linguist <path-to-code-snippet-file>
). Ti dirà il tipo, il tipo MIME e la lingua. Per più file (o per uso generale con un'app ruby / rails) puoi eseguire bundle exec linguist --breakdown
nella directory dell'applicazione.
Sembra che ci sia un sacco di lavoro extra, soprattutto se non hai già i binari, ma in realtà non hai bisogno di sapere NULLA sui binari se segui questi passaggi e non ho davvero trovato un modo migliore per rilevare il lingua di un file / frammento di codice.
Credo che non ci sia un'unica soluzione che possa identificare in quale lingua sia uno snippet, basandosi solo su quel singolo snippet. Prendi la parola chiave print
. Potrebbe apparire in un numero qualsiasi di lingue, ognuna delle quali ha scopi diversi e ha una sintassi diversa.
Ho qualche consiglio. Attualmente sto scrivendo un piccolo pezzo di codice per il mio sito web che può essere utilizzato per identificare i linguaggi di programmazione. Come la maggior parte degli altri post, potrebbe esserci una vasta gamma di linguaggi di programmazione che semplicemente non hai sentito, non puoi spiegarli tutti.
Quello che ho fatto è che ogni lingua può essere identificata da una selezione di parole chiave. Ad esempio, Python potrebbe essere identificato in diversi modi. Probabilmente è più facile se scegli "tratti" che sono anche certamente unici per la lingua. Per Python, scelgo la caratteristica di usare i due punti per iniziare una serie di istruzioni, che credo sia una caratteristica abbastanza unica (correggetemi se sbaglio).
Se, nel mio esempio, non riesci a trovare i due punti per avviare un set di istruzioni, passa a un altro tratto possibile, diciamo utilizzando la def
parola chiave per definire una funzione. Ora questo può causare alcuni problemi, perché Ruby usa anche la parola chiave def
per definire una funzione. La chiave per distinguere i due (Python e Ruby) è usare vari livelli di filtraggio per ottenere la migliore corrispondenza. Ruby usa la parola chiave end
per finire una funzione, mentre Python non ha nulla per finire una funzione, solo un de-indent ma tu non vuoi andare lì. Ma ancora una volta, end
potrebbe anche essere Lua, un altro linguaggio di programmazione da aggiungere al mix.
Puoi vedere che i linguaggi di programmazione si sovrappongono semplicemente troppo. Una parola chiave che potrebbe essere una parola chiave in una lingua potrebbe essere una parola chiave in un'altra lingua. L'uso di una combinazione di parole chiave che spesso vanno insieme, come quelle di Java, public static void main(String[] args)
aiuta a eliminare questi problemi.
Come ho già detto, la tua migliore possibilità è cercare parole chiave o set di parole chiave relativamente uniche per separare l'una dall'altra. E, se sbagli, almeno ci hai provato.
Questo sito sembra essere abbastanza bravo nell'identificare le lingue, se desideri un modo rapido per incollare uno snippet in un modulo web, piuttosto che farlo in modo programmatico: http://dpaste.com/