Mendeteksi bahasa pemrograman dari cuplikan

115

Apa cara terbaik untuk mendeteksi bahasa pemrograman apa yang digunakan dalam cuplikan kode?

João Matos
sumber
1
Praktis ada jumlah bahasa yang tak terbatas di luar sana ... apakah Anda ingin mendeteksi SALAH SATU dari mereka? Atau apakah kita hanya membicarakan yang populer?
Spencer Ruport
Hanya yang populer (C / C ++, C #, Java, Pascal, Python, VB.NET. PHP, JavaScript dan mungkin Haskell).
João Matos
12
Haskell tidak mungkin populer karena saya belum pernah mendengarnya. ;-)
Stephanie Halaman
22
Anda mungkin tidak tahu banyak tentang bahasa pemrograman jika Anda belum pernah mendengar tentang Haskell.
Akhorus
4
Ada layanan online ini yang melakukannya: algoritmeia.com/algorithms/PetiteProgrammer/…
Benny Neugebauer

Jawaban:

99

Saya pikir metode yang digunakan dalam filter spam akan bekerja dengan sangat baik. Anda membagi potongan menjadi kata-kata. Kemudian Anda membandingkan kemunculan kata-kata ini dengan cuplikan yang diketahui, dan menghitung kemungkinan bahwa cuplikan ini ditulis dalam bahasa X untuk setiap bahasa yang Anda minati.

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

Jika Anda memiliki mekanisme dasar, maka sangat mudah untuk menambahkan bahasa baru: cukup latih detektor dengan beberapa cuplikan dalam bahasa baru (Anda dapat memberinya proyek sumber terbuka). Dengan cara ini ia mempelajari bahwa "Sistem" kemungkinan besar muncul di cuplikan C # dan "menempatkan" di cuplikan Ruby.

Saya sebenarnya menggunakan metode ini untuk menambahkan deteksi bahasa ke cuplikan kode untuk perangkat lunak forum. Ini berhasil 100% dari waktu, kecuali dalam kasus yang ambigu:

print "Hello"

Biarkan saya menemukan kodenya.

Saya tidak dapat menemukan kodenya, jadi saya membuat yang baru. Ini agak sederhana tetapi berfungsi untuk pengujian saya. Saat ini jika Anda memberinya lebih banyak kode Python daripada kode Ruby, kemungkinan akan mengatakan bahwa kode ini:

def foo
   puts "hi"
end

adalah kode Python (meskipun sebenarnya itu Ruby). Ini karena Python juga memiliki defkata kunci. Jadi jika telah melihat 1000x defdi Python dan 100x defdi Ruby maka itu mungkin masih mengatakan Python putsdan endspesifik untuk Ruby. Anda dapat memperbaikinya dengan melacak kata-kata yang terlihat per bahasa dan membaginya di suatu tempat (atau dengan memberinya jumlah kode yang sama dalam setiap bahasa).

Saya harap ini membantu Anda:

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)
Jules
sumber
1
Saya juga perlu menggunakannya di perangkat lunak forum. Terima kasih atas tip tentang penyaringan Bayesian.
João Matos
12
Saya melakukan sesuatu seperti ini di kelas NLP saya, tetapi kami melangkah lebih jauh. Anda tidak suka melihat frekuensi dari satu kata, tetapi pasangan dan tiga kali lipat kata. Misalnya, "public" mungkin merupakan kata kunci dalam banyak bahasa, tetapi "public static void" lebih umum untuk C #. Jika triple tidak dapat ditemukan, Anda kembali ke 2, dan kemudian 1.
mpen
1
Mungkin juga ingin memikirkan di mana Anda memisahkan kata-kata. Di PHP, variabel dimulai dengan $, jadi mungkin Anda tidak boleh memisahkan batas kata, karena $harus tetap dengan variabel. Operator suka =>dan :=harus terikat bersama sebagai satu token, tetapi OTH Anda mungkin harus berpisah {karena mereka selalu berdiri sendiri.
mpen
2
Ya. Cara untuk menghindari pemisahan sama sekali adalah dengan menggunakan ngrams: Anda mengambil setiap n panjang substring. Misalnya, 5 gram "put foo" adalah "put" "uts f", "ts fo" dan "s foo". Strategi ini mungkin tampak aneh tetapi berhasil lebih baik dari yang Anda kira, hanya saja bukan bagaimana cara manusia menyelesaikan masalah. Untuk memutuskan metode mana yang bekerja lebih baik Anda harus menguji keduanya ...
Jules
2
Beberapa bahasa memiliki sintaks yang sangat sedikit. Saya juga berspekulasi bahwa nama variabel umum akan mendominasi kata kunci bahasa tersebut. Pada dasarnya, jika Anda memiliki potongan kode C yang ditulis oleh seorang Hongaria, dengan nama variabel dan komentar dalam bahasa Hongaria, di data pelatihan Anda, maka sumber lain dengan bahasa Hongaria di dalamnya kemungkinan besar akan ditentukan sebagai "serupa".
tripleee
26

Deteksi bahasa diselesaikan oleh orang lain:

Pendekatan Ohloh: https://github.com/blackducksw/ohcount/

Pendekatan Github: https://github.com/github/linguist

nisc
sumber
4
Saya memeriksa kedua solusi ini dan tidak akan melakukan apa yang diminta. Mereka terutama melihat ekstensi file untuk menentukan bahasanya, sehingga mereka tidak dapat serta merta memeriksa cuplikan tanpa petunjuk dari ekstensi tersebut.
Hawkee
5
Pendekatan Github sekarang menyertakan pengklasifikasi Bayesian juga. Ini terutama mendeteksi kandidat bahasa berdasarkan ekstensi file, tetapi ketika ekstensi file cocok dengan beberapa kandidat (mis. ".H" -> C, C ++, ObjC), itu akan membuat token sampel kode input dan mengklasifikasikannya berdasarkan set terlatih data. Versi Github dapat dipaksa untuk memindai kode selalu tanpa melihat ekstensinya juga.
Benzi
5

Ini sangat sulit dan terkadang tidak mungkin. Dari bahasa manakah cuplikan singkat ini berasal?

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

(Petunjuk: Bisa salah satu dari beberapa.)

Anda dapat mencoba menganalisis berbagai bahasa dan mencoba memutuskan menggunakan analisis frekuensi kata kunci. Jika kumpulan kata kunci tertentu muncul dengan frekuensi tertentu dalam teks, kemungkinan besar bahasanya adalah Java, dll. Tapi saya rasa Anda tidak akan mendapatkan apa pun yang sepenuhnya merupakan bukti bodoh, karena Anda dapat memberi nama misalnya variabel dalam C dengan nama yang sama sebagai kata kunci di Jawa, dan analisis frekuensi akan tertipu.

Jika Anda meningkatkan kerumitannya, Anda dapat mencari struktur, jika kata kunci tertentu selalu muncul setelah kata kunci lainnya, itu akan memberi Anda lebih banyak petunjuk. Tapi itu juga akan jauh lebih sulit untuk dirancang dan diterapkan.


sumber
26
Nah, jika beberapa bahasa memungkinkan, detektor dapat memberikan semua kandidat yang memungkinkan.
Steven Haryanto
Atau, dapat memberikan yang pertama yang cocok. Jika kasus penggunaan dunia nyata adalah sesuatu seperti penyorotan sintaks, maka itu benar-benar tidak akan membuat perbedaan. Artinya, salah satu bahasa yang cocok akan menghasilkan penyorotan kode dengan benar.
jonschlinkert
5

Alternatifnya adalah dengan menggunakan highlight.js , yang melakukan penyorotan sintaks tetapi menggunakan tingkat keberhasilan proses penyorotan untuk mengidentifikasi bahasa. Pada prinsipnya, basis kode penyorot sintaks apa pun dapat digunakan dengan cara yang sama, tetapi hal yang menyenangkan tentang highlight.js adalah bahwa deteksi bahasa dianggap sebagai fitur dan digunakan untuk tujuan pengujian .

UPDATE: Saya mencoba ini dan tidak berhasil dengan baik. JavaScript yang dikompresi benar-benar membingungkannya, yaitu tokenizer peka spasi putih. Umumnya, hanya menghitung hit sorotan tampaknya tidak terlalu dapat diandalkan. Pengurai yang lebih kuat, atau mungkin jumlah bagian yang tidak cocok, mungkin bekerja lebih baik.

Andy Jackson
sumber
Data bahasa yang disertakan dalam highlight.js terbatas pada nilai-nilai yang diperlukan untuk penyorotan, yang ternyata tidak cukup untuk mendeteksi bahasa (terutama untuk sejumlah kecil kode).
Adam Kennedy
Saya pikir tidak apa-apa, periksa dengan biola ini jsfiddle.net/3tgjnz10
sebilasse
4

Pertama, saya akan mencoba menemukan kunci spesifik dari suatu bahasa misalnya

"package, class, implements "=> JAVA
"<?php " => PHP
"include main fopen strcmp stdout "=>C
"cout"=> C++
etc...
Pierre
sumber
3
Masalahnya, kata kunci tersebut masih dapat muncul dalam bahasa apa pun, baik sebagai nama variabel atau dalam string. Itu, dan ada banyak kata kunci yang tumpang tindih yang digunakan. Anda harus melakukan lebih dari sekadar melihat kata kunci.
mpen
2

Itu akan tergantung pada jenis cuplikan yang Anda miliki, tetapi saya akan menjalankannya melalui serangkaian tokenizers dan melihat BNF bahasa mana yang valid.

Ya - Jake itu.
sumber
Semua bahasa bahkan tidak bisa dijelaskan oleh BNF. Jika Anda diizinkan untuk menentukan ulang kata kunci dan membuat makro, itu menjadi jauh lebih sulit. Juga saat kita berbicara tentang cuplikan, Anda harus melakukan pertandingan parsial melawan BNF, yang lebih sulit dan lebih rentan kesalahan.
2

Teka-teki yang bagus.

Saya pikir tidak mungkin untuk mendeteksi semua bahasa. Tapi Anda bisa memicu token kunci. (kata-kata tertentu yang dipesan dan kombinasi karakter yang sering digunakan).

Ben ada banyak bahasa dengan sintaks yang mirip. Jadi itu tergantung pada ukuran potongannya.

Toon Krijthe
sumber
1

Prettify adalah paket Javascript yang berfungsi baik dalam mendeteksi bahasa pemrograman:

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

Ini terutama penyorot sintaks, tetapi mungkin ada cara untuk mengekstrak bagian deteksi untuk tujuan mendeteksi bahasa dari cuplikan.

Hawkee
sumber
1
Setelah pemeriksaan lebih lanjut, tampaknya prettify tidak benar-benar mendeteksi bahasa, tetapi menyorot sesuai dengan sintaks setiap elemen.
Hawkee
1

Saya membutuhkan ini jadi saya membuatnya sendiri. https://github.com/bertyhell/CodeClassifier

Ini sangat mudah diperpanjang dengan menambahkan file pelatihan di folder yang benar. Ditulis dalam c #. Tapi saya membayangkan kodenya mudah diubah ke bahasa lain.

Berty
sumber
0

Saya tidak berpikir akan ada cara mudah untuk mencapai ini. Saya mungkin akan membuat daftar simbol / kata kunci umum yang unik untuk bahasa / kelas bahasa tertentu (misalnya tanda kurung kurawal untuk bahasa C-style, kata kunci Dim dan Sub untuk bahasa BASIC, kata kunci def untuk Python, kata kunci let untuk bahasa fungsional) . Anda kemudian mungkin dapat menggunakan fitur sintaks dasar untuk mempersempitnya lebih jauh.

Noldorin
sumber
0

Saya pikir perbedaan terbesar antara bahasa adalah strukturnya. Jadi ide saya adalah melihat elemen umum tertentu di semua bahasa dan melihat perbedaannya. Misalnya, Anda dapat menggunakan ekspresi reguler untuk memilih hal-hal seperti:

  • definisi fungsi
  • deklarasi variabel
  • deklarasi kelas
  • komentar
  • untuk loop
  • while loop
  • pernyataan cetak

Dan mungkin beberapa hal lain yang seharusnya dimiliki sebagian besar bahasa. Kemudian gunakan sistem poin. Berikan maksimal 1 poin untuk setiap elemen jika regex ditemukan. Jelas, beberapa bahasa akan menggunakan sintaks yang sama persis (untuk loop sering ditulis sepertifor(int i=0; i<x; ++i) sedemikian rupa sehingga beberapa bahasa masing-masing dapat memberi skor satu poin untuk hal yang sama, tetapi setidaknya Anda mengurangi kemungkinannya menjadi bahasa yang sama sekali berbeda). Beberapa dari mereka mungkin mendapat skor 0s di seluruh papan (cuplikan tidak berisi fungsi sama sekali, misalnya) tapi itu tidak masalah.

Gabungkan ini dengan solusi Jules, dan ini akan bekerja dengan baik. Mungkin juga mencari frekuensi kata kunci untuk poin tambahan.

mpen
sumber
0

Menarik. Saya memiliki tugas serupa untuk mengenali teks dalam format berbeda. Properti YAML, JSON, XML, atau Java? Bahkan dengan kesalahan sintaks, misalnya, saya harus membedakan JSON dari XML dengan yakin.

Saya pikir bagaimana kita memodelkan masalah itu penting. Seperti yang dikatakan Mark, tokenisasi satu kata diperlukan tetapi kemungkinan tidak cukup. Kami membutuhkan bigram, atau bahkan trigram. Tapi saya pikir kita bisa melangkah lebih jauh dari sana dengan mengetahui bahwa kita sedang mempelajari bahasa pemrograman. Saya perhatikan bahwa hampir semua bahasa pemrograman memiliki dua jenis token unik - simbol dan kata kunci . Simbol relatif mudah (beberapa simbol mungkin literal bukan bagian dari bahasa) untuk dikenali. Kemudian bigram atau trigram simbol akan mengambil struktur sintaks unik di sekitar simbol. Kata kunci adalah sasaran empuk lain jika set pelatihan besar dan cukup beragam. Fitur yang berguna bisa berupa bigram seputar kemungkinan kata kunci. Jenis token lain yang menarik adalah spasi. Sebenarnya jika kita melakukan tokenisasi dengan cara biasa dengan spasi, kita akan kehilangan informasi ini. Saya akan mengatakan, untuk menganalisis bahasa pemrograman, kami menyimpan token spasi karena ini dapat membawa informasi berguna tentang struktur sintaks.

Akhirnya jika saya memilih pengklasifikasi seperti hutan acak, saya akan merayapi github dan mengumpulkan semua kode sumber publik. Sebagian besar file kode sumber dapat diberi label dengan akhiran file. Untuk setiap file, saya akan membaginya secara acak di baris kosong menjadi potongan dengan berbagai ukuran. Saya kemudian akan mengekstrak fitur dan melatih pengklasifikasi menggunakan cuplikan berlabel. Setelah pelatihan selesai, pengklasifikasi dapat diuji presisi dan perolehannya.

neurit
sumber
0

Solusi terbaik yang saya temukan adalah menggunakan permata ahli bahasa di aplikasi Ruby on Rails. Ini semacam cara khusus untuk melakukannya, tetapi berhasil. Ini disebutkan di atas oleh @nisc tetapi saya akan memberi tahu Anda langkah-langkah tepat saya untuk menggunakannya. (Beberapa dari perintah baris perintah berikut khusus untuk ubuntu tetapi harus dengan mudah diterjemahkan ke OS lain)

Jika Anda memiliki aplikasi rails yang tidak keberatan Anda mainkan untuk sementara, buat file baru di dalamnya untuk memasukkan cuplikan kode yang dipermasalahkan. (Jika Anda belum memasang rel, ada panduan yang bagus di sini meskipun untuk ubuntu saya merekomendasikan ini . Kemudian jalankan rails new <name-your-app-dir>dan cd ke direktori itu. Semua yang Anda butuhkan untuk menjalankan aplikasi rel sudah ada di sana).

Setelah Anda memiliki aplikasi rel untuk menggunakan ini, tambahkan gem 'github-linguist'ke Gemfile Anda (secara harfiah baru saja dipanggil Gemfiledi direktori aplikasi Anda, tidak ada ext).

Kemudian instal ruby-dev ( sudo apt-get install ruby-dev)

Kemudian instal cmake ( sudo apt-get install cmake)

Sekarang Anda dapat menjalankan gem install github-linguist(jika Anda mendapatkan kesalahan yang mengatakan icu diperlukan, lakukan sudo apt-get install libicu-devdan coba lagi)

(Anda mungkin perlu melakukan sudo apt-get updateatau sudo apt-get install makeatau sudo apt-get install build-essentialjika cara di atas tidak berhasil)

Sekarang semuanya sudah diatur. Anda sekarang dapat menggunakan ini kapan saja Anda ingin memeriksa cuplikan kode. Di editor teks, buka file yang telah Anda buat untuk menyisipkan cuplikan kode Anda (anggap saja, app/test.tpltetapi jika mengetahui ekstensi dari cuplikan Anda, gunakan itu sebagai gantinya .tpl. Jika Anda tidak tahu ekstensinya, jangan gunakan salah satu ). Sekarang tempel cuplikan kode Anda di file ini. Buka baris perintah dan jalankan bundle install(harus ada di direktori aplikasi Anda). Kemudian jalankan linguist app/test.tpl(lebih umum linguist <path-to-code-snippet-file>). Ini akan memberi tahu Anda jenis, jenis pantomim, dan bahasa. Untuk banyak file (atau untuk penggunaan umum dengan aplikasi ruby ​​/ rails) Anda dapat menjalankannya bundle exec linguist --breakdowndi direktori aplikasi Anda.

Sepertinya banyak pekerjaan ekstra, terutama jika Anda belum memiliki rel, tetapi Anda sebenarnya tidak perlu tahu APA PUN tentang rel jika Anda mengikuti langkah-langkah ini dan saya benar-benar belum menemukan cara yang lebih baik untuk mendeteksi rel bahasa dari sebuah file / potongan kode.

StephanieS
sumber
0

Saya percaya bahwa tidak ada solusi tunggal yang mungkin dapat mengidentifikasi bahasa apa yang digunakan cuplikan, hanya berdasarkan cuplikan tunggal tersebut. Ambil kata kuncinya print. Itu bisa muncul dalam sejumlah bahasa, yang masing-masing untuk tujuan yang berbeda, dan memiliki sintaks yang berbeda.

Saya punya beberapa nasihat. Saat ini saya sedang menulis sepotong kecil kode untuk situs web saya yang dapat digunakan untuk mengidentifikasi bahasa pemrograman. Seperti kebanyakan posting lainnya, mungkin ada banyak sekali bahasa pemrograman yang belum pernah Anda dengar, Anda tidak dapat menjelaskan semuanya.

Apa yang telah saya lakukan adalah setiap bahasa dapat diidentifikasi dengan pemilihan kata kunci. Misalnya, Python dapat diidentifikasi dengan berbagai cara. Mungkin lebih mudah jika Anda memilih 'ciri-ciri' yang juga pasti unik untuk bahasa tersebut. Untuk Python, saya memilih sifat menggunakan titik dua untuk memulai serangkaian pernyataan, yang menurut saya merupakan sifat yang cukup unik (perbaiki saya jika saya salah).

Jika, dalam contoh saya, Anda tidak dapat menemukan titik dua untuk memulai kumpulan pernyataan, kemudian pindah ke sifat lain yang mungkin, katakanlah menggunakan defkata kunci untuk mendefinisikan fungsi. Sekarang ini bisa menyebabkan beberapa masalah, karena Ruby juga menggunakan kata kunci defuntuk mendefinisikan suatu fungsi. Kunci untuk membedakan keduanya (Python dan Ruby) adalah dengan menggunakan berbagai level pemfilteran untuk mendapatkan kecocokan terbaik. Ruby menggunakan kata kunci enduntuk menyelesaikan suatu fungsi, sedangkan Python tidak memiliki apa pun untuk menyelesaikan suatu fungsi, hanya sebuah de-indentasi tetapi Anda tidak ingin pergi ke sana. Tapi lagi,end bisa juga Lua, bahasa pemrograman lain untuk ditambahkan ke dalam campuran.

Anda dapat melihat bahwa bahasa pemrograman terlalu banyak menutupi. Satu kata kunci yang bisa menjadi kata kunci dalam satu bahasa bisa menjadi kata kunci dalam bahasa lain. Menggunakan kombinasi kata kunci yang sering kali digabungkan, seperti Javapublic static void main(String[] args) membantu menghilangkan masalah tersebut.

Seperti yang telah saya katakan sebelumnya, peluang terbaik Anda adalah mencari kata kunci yang relatif unik atau kumpulan kata kunci untuk memisahkan satu dari yang lain. Dan, jika Anda salah, setidaknya Anda mencobanya.

William Lee
sumber
0

Siapkan pengacak acak seperti

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;
Rakesh
sumber
0

Situs ini tampaknya cukup bagus dalam mengidentifikasi bahasa, jika Anda menginginkan cara cepat untuk menempelkan potongan ke dalam formulir web, daripada melakukannya secara terprogram: http://dpaste.com/

drkvogel.dll
sumber