Masalah: Jalankan perintah dalam bentuk string.
contoh perintah:
/user/files/ list all;
setara dengan:/user/files/ ls -la;
yang lainnya:
post tw fb "HOW DO YOU STOP THE TICKLE MONSTER?;"
setara dengan:
post -tf "HOW DO YOU STOP THE TICKLE MONSTER?;"
Solusi saat ini:
tokenize string(string, array);
switch(first item in array) {
case "command":
if ( argument1 > stuff) {
// do the actual work;
}
}
Masalah yang saya lihat dalam solusi ini adalah:
- Tidak ada kesalahan memeriksa selain jika bersarang-lain di dalam setiap kasus. Naskah menjadi sangat besar dan sulit untuk dipertahankan.
- Perintah dan tanggapan dikodekan dengan keras.
- Tidak ada cara untuk mengetahui apakah flag adalah parameter yang benar atau tidak ada.
- Kurangnya kecerdasan untuk menyarankan "Anda mungkin ingin menjalankan $ command".
Dan hal terakhir yang tidak bisa saya sampaikan adalah sinonim dalam pengkodean yang berbeda, contoh:
case command:
case command_in_hebrew:
do stuff;
break;
Yang terakhir mungkin sepele, tapi yah, yang ingin saya lihat adalah dana yang kuat dari program semacam ini.
Saya sedang memprogram ini dalam PHP tetapi mungkin melakukannya dalam PERL.
php
algorithms
perl
parsing
command-line
alfa64
sumber
sumber
Jawaban:
Biar saya akui terus terang, membangun parser adalah pekerjaan yang membosankan dan mendekati teknologi kompiler, tetapi membangunnya akan menjadi petualangan yang baik. Dan pengurai dilengkapi dengan juru bahasa. Jadi, Anda harus membangun keduanya.
Pengantar cepat untuk parser dan penerjemah
Ini tidak terlalu teknis. Jadi para ahli tidak khawatir pada saya.
Saat Anda memasukkan input ke terminal, terminal membagi input menjadi beberapa unit. Input disebut ekspresi dan beberapa unit disebut token. Token ini dapat berupa operator atau simbol. Jadi, jika Anda memasukkan 4 + 5 dalam kalkulator, ungkapan ini akan dibagi menjadi tiga token 4, +, 5. Nilai tambah dianggap sebagai operator sementara 4 dan 5 simbol. Ini diteruskan ke program (menganggap ini sebagai juru bahasa) yang berisi definisi untuk operator. Berdasarkan definisi (dalam kasus kami, tambahkan), ia menambahkan dua simbol dan mengembalikan hasilnya ke terminal. Semua kompiler didasarkan pada teknologi ini. Program yang membagi ekspresi menjadi beberapa token disebut lexer dan program yang mengubah token ini menjadi tag untuk diproses dan dieksekusi lebih lanjut disebut parser.
Lex dan Yacc adalah bentuk kanonik untuk membangun lexer dan parser berdasarkan tata bahasa BNF di bawah C dan itu adalah opsi yang disarankan. Sebagian besar parser adalah tiruan dari Lex dan Yacc.
Langkah-langkah dalam membangun parser / intrepreter
Jadi dalam kasus di atas token tambahan Anda akan berupa digit dan tanda tambah apa pun yang harus dilakukan dengan tanda tambah di lexer
Catatan dan Tip
Pendekatan sederhana
Jika Anda hanya perlu mekanisme parsing sederhana dengan fungsi terbatas, ubah persyaratan Anda menjadi Ekspresi Reguler dan buat saja sejumlah fungsi. Untuk menggambarkan, asumsikan pengurai sederhana untuk empat fungsi aritmatika. Jadi Anda akan menjadi operator panggilan pertama dan kemudian daftar fungsi (mirip dengan lisp) dalam gaya
(+ 4 5)
atau(add [4,5])
kemudian Anda bisa menggunakan RegExp sederhana untuk mendapatkan daftar operator dan simbol untuk dioperasikan.Sebagian besar kasus umum dapat dengan mudah diselesaikan dengan pendekatan ini. Kelemahannya adalah Anda tidak dapat memiliki banyak ekspresi bersarang dengan sintaks yang jelas dan Anda tidak dapat memiliki fungsi urutan lebih tinggi yang mudah.
sumber
Pertama, ketika berbicara tentang tata bahasa, atau bagaimana menentukan argumen, jangan buat argumen Anda sendiri. Standar gaya GNU sudah sangat populer dan terkenal.
Kedua, karena Anda menggunakan standar yang diterima, jangan menemukan kembali kemudi. Gunakan perpustakaan yang ada untuk melakukannya untuk Anda. Jika Anda menggunakan argumen gaya GNU, sudah hampir pasti ada perpustakaan yang sudah matang dalam bahasa pilihan Anda. Misalnya: c # , php , c .
Pilihan parsing library yang baik bahkan akan mencetak bantuan yang diformat pada opsi yang tersedia untuk Anda.
EDIT 12/27
Sepertinya Anda membuat ini menjadi lebih rumit dari itu.
Ketika Anda melihat baris perintah, itu sangat sederhana. Itu hanya opsi dan argumen untuk opsi itu. Ada beberapa masalah rumit. Opsi dapat memiliki alias. Argumen dapat berupa daftar argumen.
Satu masalah dengan pertanyaan Anda adalah bahwa Anda belum benar-benar menentukan aturan apa pun untuk jenis baris perintah yang ingin Anda tangani. Saya telah menyarankan standar GNU, dan contoh Anda mendekati itu (meskipun saya tidak benar-benar memahami contoh pertama Anda dengan path sebagai item pertama?).
Jika kita berbicara GNU, setiap opsi tunggal hanya dapat memiliki bentuk panjang dan pendek (karakter tunggal) sebagai alias. Setiap argumen yang mengandung spasi harus dikelilingi dalam tanda kutip. Beberapa opsi formulir pendek dapat dirantai. Opsi formulir pendek harus diproses dengan satu tanda hubung, bentuk panjang dengan dua tanda hubung. Hanya opsi form pendek berantai terakhir yang dapat memiliki argumen.
Semua sangat mudah. Semuanya sangat umum. Juga telah diterapkan dalam setiap bahasa yang dapat Anda temukan, mungkin lima kali lipat.
Jangan tulis itu. Gunakan apa yang sudah ditulis.
Kecuali Anda memiliki sesuatu dalam pikiran selain argumen baris perintah standar, cukup gunakan salah satu dari BANYAK perpustakaan yang sudah ada dan teruji yang melakukan ini.
Apa komplikasinya?
sumber
Sudahkah Anda mencoba sesuatu seperti http://qntm.org/loco ? Pendekatan ini jauh lebih bersih daripada ad hoc tulisan tangan apa pun, tetapi tidak memerlukan alat pembuat kode mandiri seperti Lemon.
EDIT: Dan trik umum untuk menangani baris perintah dengan sintaks yang kompleks adalah menggabungkan argumen kembali menjadi string yang dipisahkan dengan spasi putih dan kemudian menguraikannya dengan baik seolah-olah itu adalah ekspresi dari beberapa bahasa khusus domain.
sumber
Anda belum memberikan banyak spesifik tentang tata bahasa Anda, hanya beberapa contoh. Apa yang dapat saya lihat adalah bahwa ada beberapa string, spasi putih dan (mungkin, contoh Anda tidak acuh dalam pertanyaan Anda) string yang dikutip ganda dan kemudian satu ";" pada akhirnya.
Sepertinya ini bisa mirip dengan sintaks PHP. Jika demikian, PHP dilengkapi dengan parser, Anda dapat menggunakan kembali dan kemudian memvalidasi lebih konkret. Akhirnya Anda harus berurusan dengan token, tetapi sepertinya ini hanya dari kiri ke kanan sehingga sebenarnya hanya iterasi dari semua token.
Beberapa contoh untuk menggunakan kembali parser token PHP (
token_get_all
) diberikan dalam jawaban atas pertanyaan berikut:Kedua contoh mengandung parser sederhana juga, mungkin sesuatu seperti itu cocok untuk skenario Anda.
sumber
Jika kebutuhan Anda sederhana, dan Anda berdua punya waktu dan tertarik, saya akan menentangnya di sini dan mengatakan jangan malu untuk menulis parser Anda sendiri. Ini pengalaman belajar yang baik, jika tidak ada yang lain. Jika Anda memiliki persyaratan yang lebih kompleks - panggilan fungsi bersarang, array, dll - hanya perlu diketahui bahwa melakukan hal itu bisa memakan banyak waktu. Salah satu hal positif dari menggulirkan milik Anda sendiri adalah tidak akan ada masalah untuk mengintegrasikan dengan sistem Anda. Kelemahannya, tentu saja, semua gangguan adalah kesalahan Anda.
Namun, bekerja melawan token, jangan gunakan perintah kode keras. Kemudian masalah dengan perintah terdengar serupa hilang.
Semua orang selalu merekomendasikan buku naga, tetapi saya selalu menemukan "Menulis Kompiler dan Penerjemah" oleh Ronald Mak menjadi pengantar yang lebih baik.
sumber
Saya punya program tertulis yang berfungsi seperti itu. Salah satunya adalah bot IRC yang memiliki sintaks perintah serupa. Ada file besar yang merupakan pernyataan saklar besar. Ini bekerja - bekerja cepat - tetapi agak sulit untuk mempertahankannya.
Pilihan lain, yang memiliki putaran OOP lebih banyak, adalah menggunakan event handler. Anda membuat array nilai kunci dengan perintah dan fungsi khusus mereka. Ketika perintah diberikan, Anda memeriksa apakah array memiliki kunci yang diberikan. Jika ya, panggil fungsi tersebut. Ini akan menjadi rekomendasi saya untuk kode baru.
sumber
I think my implementation is very crude and faulty
kebut as i stated, if you want other people to use, you need to add error checking and stuff
... Beritahu kami apa yang kasar tentang itu dan apa yang salah, itu akan membantu Anda mendapatkan jawaban yang lebih baik.Saya sarankan menggunakan alat, alih-alih menerapkan kompiler atau penerjemah sendiri. Irony menggunakan C # untuk mengekspresikan tata bahasa bahasa target (tata bahasa dari baris perintah Anda). Deskripsi tentang CodePlex mengatakan: "Irony adalah kit pengembangan untuk mengimplementasikan bahasa pada platform .NET."
Lihat beranda resmi Irony di CodePlex: Irony - .NET Language Implementation Kit .
sumber
Saran saya adalah google untuk perpustakaan yang memecahkan masalah Anda.
Saya telah menggunakan NodeJS banyak akhir-akhir ini, dan Optimist adalah apa yang saya gunakan untuk pemrosesan baris perintah. Saya mendorong Anda untuk mencari yang dapat Anda gunakan untuk bahasa pilihan Anda sendiri. Jika tidak .. tulis satu dan buka sumbernya: D Anda bahkan dapat membaca kode sumber Optimist dan mengirimkannya ke bahasa pilihan Anda.
sumber
Mengapa Anda tidak menyederhanakan sedikit, persyaratan Anda?
Jangan gunakan parser penuh, terlalu rumit, dan bahkan tidak perlu untuk kasus Anda.
Buat perulangan, tulis pesan yang mewakili "prompt" Anda, bisa menjadi jalur Anda saat ini.
Tunggu string, "parsing" string, dan lakukan sesuatu tergantung pada isi string.
String dapat "parse" seperti mengharapkan garis, di mana spasi adalah pemisah ("tokenizer"), dan karakter lainnya dikelompokkan.
Contoh.
Output program (dan tetap di baris yang sama): / user / files / Pengguna menulis (di baris yang sama) daftar semua;
Program Anda akan menghasilkan daftar, koleksi, atau susunan seperti
atau jika ";" dianggap sebagai pemisah seperti spasi
Program Anda bisa mulai dengan mengharapkan satu instruksi tunggal, tanpa "pipa" unix-style, tidak ada redirection gaya windowze.
Program Anda dapat membuat kamus instruksi, setiap instruksi, mungkin memiliki daftar parameter.
Pola desain perintah berlaku untuk kasus Anda:
http://en.wikipedia.org/wiki/Command_pattern
Ini pseudocode "c polos", tidak diuji atau selesai, hanya sebuah gagasan tentang bagaimana bisa dilakukan.
Anda juga bisa membuatnya lebih berorientasi objek, dan dalam bahasa pemrograman, Anda suka.
Contoh:
Anda tidak menyebutkan bahasa pemrograman Anda. Anda juga dapat menyebutkan bahasa pemrograman apa pun, tetapi lebih disukai "XYZ".
sumber
Anda memiliki beberapa tugas di depan Anda.
melihat kebutuhan Anda ...
Bahasa perintah extensible menunjukkan bahwa DSL diperlukan. Saya sarankan tidak menggulung sendiri tetapi menggunakan JSON jika ekstensi Anda sederhana. Jika mereka kompleks, sintaks s-ekspresi bagus.
Pemeriksaan kesalahan menyiratkan bahwa sistem Anda juga tahu tentang perintah yang mungkin. Itu akan menjadi bagian dari sistem pasca-perintah.
Jika saya menerapkan sistem seperti itu dari awal, saya akan menggunakan Common Lisp dengan pembaca yang tidak benar. Setiap token perintah akan memetakan ke simbol, yang akan ditentukan dalam file RC s-ekspresi. Setelah tokenization, itu akan dievaluasi / diperluas dalam konteks terbatas, menjebak kesalahan, dan pola kesalahan yang dikenali akan mengembalikan saran. Setelah itu, perintah yang sebenarnya akan dikirim ke OS.
sumber
Ada fitur bagus dalam pemrograman fungsional yang mungkin Anda tertarik untuk melihatnya.
Ini disebut pencocokan pola .
Berikut adalah dua tautan untuk beberapa contoh pencocokan pola di Scala dan di F # .
Saya setuju dengan Anda bahwa menggunakan
switch
struktur agak membosankan, dan saya sangat menikmati menggunakan pencocokan pola selama implementasi kompiler di Scala.Secara khusus, saya akan merekomendasikan Anda untuk melihat contoh kalkulus lambda dari situs web Scala.
Itu, menurut saya, cara paling cerdas untuk melanjutkan, tetapi jika Anda harus tetap menggunakan PHP, maka Anda terjebak dengan "old-school"
switch
.sumber
Lihat Apache CLI , seluruh tujuan tampaknya melakukan persis apa yang ingin Anda lakukan, jadi bahkan jika Anda tidak dapat menggunakannya, Anda dapat memeriksa arsitekturnya dan menyalinnya.
sumber