Memprogram bahasa dengan mekanisme ekstensi sintaks seperti Lisp [ditutup]

20

Saya hanya memiliki pengetahuan terbatas tentang Lisp (mencoba belajar sedikit di waktu luang saya) tetapi sejauh yang saya mengerti Lisp macro memungkinkan untuk memperkenalkan konstruksi bahasa baru dan sintaksis dengan menggambarkannya dalam Lisp itu sendiri. Ini berarti bahwa konstruk baru dapat ditambahkan sebagai pustaka, tanpa mengubah kompiler / interpreter Lisp.

Pendekatan ini sangat berbeda dengan bahasa pemrograman lainnya. Misalnya, jika saya ingin memperluas Pascal dengan jenis loop baru atau idiom tertentu saya harus memperluas sintaks dan semantik bahasa dan kemudian mengimplementasikan fitur baru itu di kompiler.

Apakah ada bahasa pemrograman lain di luar keluarga Lisp (yaitu selain Common Lisp, Scheme, Clojure (?), Racket (?), Dll) yang menawarkan kemungkinan serupa untuk memperluas bahasa dalam bahasa itu sendiri?

EDIT

Harap, hindari diskusi yang panjang dan spesifik dalam jawaban Anda. Alih-alih daftar panjang bahasa pemrograman yang dapat diperluas dengan cara tertentu, saya ingin memahami dari sudut pandang konseptual apa yang spesifik untuk makro Lisp sebagai mekanisme ekstensi, dan bahasa pemrograman non-Lisp yang menawarkan beberapa konsep itu dekat dengan mereka.

Giorgio
sumber
6
Lisp memiliki trik lain selain makro biasa. "Reader Macros" memungkinkan sintaks parser untuk ditangkap dan diperpanjang saat runtime, sehingga bahkan struktur token dasar bahasa di bawah kendali.
ddyer
@ddyer: Terima kasih, saya tidak tahu tentang itu: topik lain akan ditambahkan ke daftar bacaan saya.
Giorgio
Saya berani bertaruh bahwa pemrograman Ruby dapat memenuhi hal ini.
Rig
1
pertanyaan Anda kontradiktif, pertama Anda meminta daftar, lalu Anda meminta informasi konseptual, yang mana itu ???
Saya meminta daftar, tetapi bukan yang umum (bukan sembarang bahasa pemrograman yang dapat diperluas dalam beberapa cara) karena ini akan terlalu umum. Saya ingin tahu bahasa yang dapat diperluas dengan cara yang mirip dengan Lisp (ekstensi didefinisikan menggunakan bahasa yang sama yang diperluas, bukan semacam bahasa meta). Jawaban Péter Török, Thomas Eding, SK-logic, dan Siput Mekanik tampaknya paling dekat dengan apa yang ada dalam pikiran saya. Saya masih harus membaca semua jawaban dengan hati-hati.
Giorgio

Jawaban:

19

Scala memungkinkan ini juga (sebenarnya secara sadar dirancang untuk mendukung definisi konstruksi bahasa baru dan bahkan menyelesaikan DSL).

Terlepas dari fungsi tingkat tinggi, lambdas dan kari, yang umum dalam bahasa fungsional, ada beberapa fitur bahasa khusus di sini untuk memungkinkan ini *:

  • tidak ada operator - semuanya adalah fungsi, tetapi nama fungsi dapat menyertakan karakter khusus seperti '+', '-' atau ':'
  • titik dan kawat gigi dapat dihilangkan untuk panggilan metode parameter tunggal, yaitu a.and(b)setara dengan a and bdalam bentuk infiks
  • untuk panggilan fungsi parameter tunggal, Anda dapat menggunakan kurung keriting alih-alih kawat gigi normal - ini (bersama dengan kari) memungkinkan Anda untuk menulis hal-hal seperti

    val file = new File("example.txt")
    
    withPrintWriter(file) {
      writer => writer.println("this line is a function call parameter")
    }
    

    dimana withPrintWriteradalah metode biasa dengan dua daftar parameter, keduanya berisi satu parameter

  • parameter dengan nama memungkinkan Anda untuk menghilangkan daftar parameter kosong di lambdas, memungkinkan Anda untuk menulis panggilan seperti myAssert(() => x > 3)dalam bentuk yang lebih pendekmyAssert(x > 3)

Penciptaan contoh DSL dibahas secara rinci dalam Bab 11. Bahasa Domain-Khusus di Scala dari buku gratis Pemrograman Scala .

* Saya tidak bermaksud ini unik untuk Scala, tetapi setidaknya mereka sepertinya tidak terlalu umum. Saya bukan ahli dalam bahasa fungsional.

Péter Török
sumber
1
+1: Menarik. Apakah ini berarti bahwa untuk memperluas sintaksis saya dapat mendefinisikan kelas baru dan bahwa tanda tangan metode akan menyediakan sintaksis baru sebagai produk sampingan?
Giorgio
@Iorgio, pada dasarnya ya.
Péter Török
Tautan tidak berfungsi lagi.
Nate Glenn
13

Perl memungkinkan untuk pra-pemrosesan bahasanya. Meskipun ini tidak sering digunakan untuk mengubah sintaksis secara radikal dalam bahasa, hal ini dapat dilihat pada beberapa ... modul ganjil:

  • Acme :: Bleach untuk kode yang benar-benar bersih
  • Acme :: Morse untuk menulis dalam kode morse
  • Lingua :: Romana :: Perligata untuk menulis dalam bahasa Latin (misalnya, kata benda adalah variabel dan angka, huruf, dan kemiringan mengubah jenis kata benda nextum ==> $ next, nexta ==> @next)

Ada juga modul yang memungkinkan perl untuk menjalankan kode yang sepertinya ditulis dengan Python.

Pendekatan yang lebih modern untuk perl ini adalah dengan menggunakan Filter :: Simple (salah satu modul inti di perl5).

Perhatikan bahwa semua contoh itu melibatkan Damian Conway yang telah disebut sebagai "Dokter Gila Perl". Masih kemampuan yang luar biasa kuat dalam perl untuk memutar bahasa seperti yang diinginkan.

Lebih banyak dokumentasi untuk ini dan alternatif lain ada di perlfilter .

pengguna40980
sumber
13

Haskell

Haskell memiliki "Templat Haskell" serta "Kuasiquotation":

http://www.haskell.org/haskellwiki/Template_Haskell

http://www.haskell.org/haskellwiki/Quasiquotation

Fitur-fitur ini memungkinkan pengguna untuk secara dramatis menambah sintaks bahasa di luar cara normal. Ini diselesaikan pada waktu kompilasi juga, yang saya pikir adalah suatu keharusan besar (untuk bahasa yang dikompilasi setidaknya) [1].

Saya telah menggunakan quasiquotation di Haskell sekali sebelumnya untuk membuat pencocokan pola lanjutan di atas bahasa C-like:

moveSegment :: [Token] -> Maybe (SegPath, SegPath, [Token])
moveSegment [hc| HC_Move_Segment(@s, @s); | s1 s2 ts |] = Just (mkPath s1, mkPath s2, ts)
moveSegment _ = Nothing

[1] Lain-lain yang memenuhi syarat sebagai ekstensi sintaks:, runFeature "some complicated grammar enclosed in a string to be evaluated at runtime"yang tentu saja merupakan omong kosong.

Thomas Eding
sumber
3
Ada juga fitur-fitur lain dari Haskell yang pada dasarnya memungkinkan sintaks kustom, seperti membuat operator Anda sendiri atau currying otomatis ditambah dengan fungsi lambda (pertimbangkan misalnya penggunaan khas dari forM).
Xion
Jujur saya pikir kari dan operator kustom tidak memenuhi syarat. Mereka memungkinkan seseorang untuk menggunakan bahasa dengan rapi, tetapi mereka tidak memungkinkan Anda untuk menambahkan / baru / fungsionalitas ke bahasa. TH dan QQ lakukan. Dalam arti yang ketat, TH dan QQ juga tidak dalam arti bahwa mereka melakukan persis apa yang dirancang untuk mereka lakukan, tetapi mereka memungkinkan Anda untuk benar-benar "keluar dari bahasa" pada waktu kompilasi.
Thomas Eding
1
"Lepaskan yang berikut ini sebagai ekstensi sintaksis ...": poin yang sangat bagus.
Giorgio
12

Tcl memiliki sejarah panjang dalam mendukung sintaks yang dapat dikembangkan. Sebagai contoh, inilah implementasi dari loop yang mengulangi tiga variabel (sampai berhenti) di atas kardinal, kotak dan kubus mereka:

proc loopCard23 {cardinalVar squareVar cubeVar body} {
    upvar 1 $cardinalVar cardinal $squareVar square $cubeVar cube

    # We borrow a 'for' loop for the implementation...
    for {set cardinal 0} true {incr cardinal} {
        set square [expr {$cardinal ** 2}]
        set cube [expr {$cardinal ** 3}]

        uplevel 1 $body
    }
}

Itu kemudian akan digunakan seperti ini:

loopCard23 a b c {
    puts "got triplet: $a, $b, $c"
    if {$c > 400} {
        break
    }
}

Jenis teknik ini digunakan secara luas dalam pemrograman Tcl, dan kunci untuk melakukannya dengan benar adalah upvardan uplevelperintah ( upvarmengikat variabel bernama dalam lingkup lain ke variabel lokal, dan uplevelmenjalankan skrip di lingkup lain: dalam kedua kasus, 1menunjukkan bahwa ruang lingkup yang dimaksud adalah penelepon). Ini juga banyak digunakan dalam kode yang dipasangkan dengan basis data (menjalankan beberapa kode untuk setiap baris dalam set hasil), dalam Tk untuk GUI (untuk melakukan pengikatan callback ke acara), dll.

Namun, itu hanya sebagian kecil dari apa yang dilakukan. Bahasa yang disematkan bahkan tidak perlu Tcl; itu bisa berupa apa saja (selama itu menyeimbangkan kawat gigi - hal-hal menjadi mengerikan secara sintaksis jika itu tidak benar - yang merupakan sebagian besar program) dan Tcl hanya dapat mengirim ke bahasa asing yang tertanam seperlunya. Contoh untuk melakukan ini termasuk menanamkan C untuk mengimplementasikan perintah Tcl dan yang setara dengan Fortran. (Dapat diperdebatkan, semua perintah bawaan Tcl dilakukan dengan cara ini dalam arti tertentu, dalam arti mereka hanya pustaka standar dan bukan bahasa itu sendiri.)

Donal Fellows
sumber
10

Ini sebagian pertanyaan semantik. Ide dasar Lisp adalah bahwa program ini adalah data yang dapat dimanipulasi sendiri. Bahasa yang umum digunakan dalam keluarga Lisp, seperti Skema, jangan biarkan Anda menambahkan sintaks baru dalam pengertian parser; itu semua hanya tanda kurung yang dibatasi ruang. Hanya saja karena sintaksis inti tidak begitu banyak, Anda dapat membuat hampir semua konstruksi semantik darinya. Scala (dibahas di bawah) serupa: aturan nama-variabel sangat liberal sehingga Anda dapat dengan mudah membuat DSL yang bagus darinya (sambil tetap berada dalam aturan sintaksis inti yang sama).

Bahasa-bahasa ini, sementara mereka tidak benar-benar membiarkan Anda mendefinisikan sintaks baru dalam arti filter Perl, memiliki inti yang cukup fleksibel yang dapat Anda gunakan untuk membangun DSL dan menambahkan konstruksi bahasa.

Fitur umum yang penting adalah bahwa mereka memungkinkan Anda menentukan konstruksi bahasa yang berfungsi serta yang built-in, menggunakan fitur-fitur yang diekspos oleh bahasa. Tingkat dukungan untuk fitur ini bervariasi:

  • Banyak bahasa yang lebih tua tersedia built-in fungsi seperti sin(), round(), dll, tanpa cara untuk mengimplementasikan Anda sendiri.
  • C ++ memberikan dukungan terbatas. Sebagai contoh beberapa built-in kata kunci seperti gips ( static_cast<target_type>(input), dynamic_cast<>(), const_cast<>(), reinterpret_cast<>()) dapat ditiru menggunakan fungsi template, yang menggunakan Meningkatkan untuk lexical_cast<>(), polymorphic_cast<>(), any_cast<>(), ....
  • Java telah built-in struktur kontrol ( for(;;){}, while(){}, if(){}else{}, do{}while(), synchronized(){}, strictfp{}) dan tidak membiarkan Anda menentukan sendiri. Scala sebagai gantinya mendefinisikan sintaksis abstrak yang memungkinkan Anda memanggil fungsi menggunakan sintaksis seperti struktur kontrol yang nyaman, dan pustaka menggunakan ini untuk secara efektif mendefinisikan struktur kontrol baru (misalnya react{}di perpustakaan aktor).

Juga, Anda mungkin melihat fungsionalitas sintaksis khusus Mathematica dalam paket Notation . (Secara teknis itu ada di keluarga Lisp, tetapi memiliki beberapa fitur ekstensibilitas yang dilakukan secara berbeda, serta ekstensibilitas Lisp yang biasa.)

Siput mekanik
sumber
2
Salah. Cadel tidak benar memungkinkan Anda untuk menambahkan benar-benar jenis sintaks baru. Seperti dalam contoh ini: meta-alternative.net/pfront.pdf - itu tidak lain adalah makro Lisp.
SK-logic
Itu tampaknya diimplementasikan dalam bahasa yang dirancang khusus untuk membangun DSL . Tentu saja Anda dapat membuat bahasa dalam keluarga Lisp yang juga menyediakan fitur-fitur tersebut; Maksud saya, fitur itu bukan ide Lisp inti yang didukung Lisps yang digunakan secara umum (misalnya Skema). Diedit untuk memperjelas.
Siput mekanik
"Bahasa untuk membangun DSL" ini adalah kumpulan makro yang dibangun di atas Lisp minimalis yang cukup tipikal. Itu dapat dengan mudah porting ke Lisp lain dengan (defmacro ...). Sebenarnya saya saat ini memindahkan bahasa ini ke Racket, hanya untuk bersenang-senang. Tapi, saya setuju bahwa itu adalah sesuatu yang tidak terlalu berguna, karena sintaks ekspresi-S lebih dari cukup untuk sebagian besar semantik yang mungkin berguna.
SK-logic
Dan, Skema tidak ada bedanya dengan Common Lisp, mulai dari R6RS secara resmi, dan secara tidak resmi selama berabad-abad, karena memang menyediakan yang (define-macro ...)setara, yang, pada gilirannya, dapat menggunakan segala jenis penguraian secara internal.
SK-logic
8

Rebol terdengar hampir seperti apa yang Anda gambarkan, tetapi sedikit menyamping.

Daripada mendefinisikan sintaksis spesifik, semua yang ada di Rebol adalah panggilan fungsi - tidak ada kata kunci. (Ya, Anda dapat mendefinisikan ulang ifdan whilejika Anda benar-benar ingin). Misalnya, ini adalah ifpernyataan:

if now/time < 12:00 [print "Morning"]

ifadalah fungsi yang mengambil 2 argumen: kondisi dan blok. Jika kondisinya benar, blok dievaluasi. Kedengarannya seperti kebanyakan bahasa, bukan? Yah, blok adalah struktur data, tidak terbatas pada kode - ini adalah blok blok, misalnya, dan contoh cepat dari fleksibilitas "kode adalah data":

SomeArray: [ [foo "One"] [bar "Two"] [baz "Three"] ]
foreach action SomeArray [action/1: 'print] ; Change the data
if now/time < 12:00 SomeArray/2 ; Use the data as code - right now, if now/time < 12:00 [print "Two"]

Selama Anda dapat tetap berpegang pada aturan sintaksis, memperluas bahasa ini, sebagian besar, tidak lebih dari mendefinisikan fungsi-fungsi baru. Beberapa pengguna telah mendukung fitur Rebol 3 ke dalam Rebol 2, misalnya.

Izkata
sumber
7

Ruby memiliki sintaks yang cukup fleksibel, saya pikir itu adalah cara "memperluas bahasa di dalam bahasa itu sendiri".

Contohnya adalah rake . Itu ditulis dalam Ruby, itu adalah Ruby, tetapi sepertinya make .

Untuk memeriksa beberapa kemungkinan, Anda dapat mencari kata kunci Ruby dan metaprogramming .

knut
sumber
13
"sintaks fleksibel" SANGAT berbeda dengan "sintaksis yang dapat diperpanjang". Sudah lama sejak saya diprogram di Ruby, tetapi Rake hanya terlihat seperti penggunaan sintaks bawaan yang dirancang dengan baik. Dengan kata lain, ini bukan contoh.
Thomas Eding
2
Bukankah itu masalah derajat, bukan jenis? Bagaimana Anda membedakan antara bahasa "sintaksis yang dapat diperpanjang" yang dapat memperluas beberapa aspek sintaksinya tetapi tidak pada yang lain, dan bahasa "sintaks yang fleksibel"?
badai
1
Jika garisnya buram, maka mari dorong kembali garis itu sehingga C dianggap mendukung sintaks yang bisa diperluas.
Thomas Eding
Untuk menautkan dengan contoh Anda, saya akan menarik garis di sini: Bahasa dengan sintaks yang dapat diperluas dapat membuat rake, yang terlihat seperti make. Bahasa dengan sintaks yang fleksibel dapat diperluas (ha, perpaduan bahasa yang bagus di sana) untuk mengkompilasi dan menjalankan makefile. Tapi poin Anda tentang masalah derajat adalah bagus. Mungkin beberapa bahasa mengizinkan kompilasi Make, tetapi tidak untuk Python. Dan yang lain akan mengizinkan keduanya.
Clayton Stanley
Semua bahasa lengkap turing harus dapat dibuat untuk memproses Membuat file. Fleksibilitas sintaksis bukanlah faktor di luar seberapa besar tantangannya.
Rig
7

Memperluas sintaks seperti yang Anda bicarakan memungkinkan Anda membuat bahasa khusus domain. Jadi mungkin cara yang paling berguna untuk mengulangi pertanyaan Anda adalah, bahasa apa yang memiliki dukungan yang baik untuk bahasa khusus domain?

Ruby memiliki sintaks yang sangat fleksibel, dan banyak DSL telah berkembang di sana, seperti rake. Groovy termasuk banyak kebaikan itu. Ini juga mencakup transformasi AST, yang lebih langsung analog dengan makro Lisp.

R, bahasa untuk komputasi statistik, memungkinkan fungsi untuk membuat argumen mereka tidak dievaluasi. Ini menggunakan ini untuk membuat DSL untuk menentukan rumus regresi. Sebagai contoh:

y ~ a + b

berarti "paskan garis bentuk k0 + k1 * a + k2 * b dengan nilai dalam y."

y ~ a * b

berarti "paskan garis bentuk k0 + k1 * a + k2 * b + k3 * a * b dengan nilai dalam y."

Dan seterusnya.

Martin C. Martin
sumber
2
Transformasi AST Groovy sangat verbose dibandingkan dengan macro Lisp atau Clojure. Misalnya 20+ baris Groovy contohnya di groovy.codehaus.org/Global+AST+Transformasi dapat ditulis ulang sebagai satu baris pendek di Clojure, misalnya `(this (println ~ message)). Tidak hanya itu tetapi Anda juga tidak perlu mengkompilasi toples, menulis metadata, atau hal-hal lain di halaman Groovy itu.
Vorg van Geir
7

Converge adalah satu lagi bahasa pemograman non-lispy. Dan, sampai batas tertentu, C ++ memenuhi syarat juga.

Boleh dibilang, MetaOCaml cukup jauh dari Lisp. Untuk gaya ekstensi sintaks yang sama sekali berbeda, namun cukup kuat, lihat CamlP4 .

Nemerle adalah bahasa lain yang bisa dikembangkan dengan pemrograman ala Lisp, meskipun lebih dekat ke bahasa seperti Scala.

Dan, Scala sendiri akan segera menjadi bahasa seperti itu juga.

Sunting: Saya lupa tentang contoh paling menarik - JetBrains MPS . Ini tidak hanya sangat jauh dari apa pun Lispish, itu bahkan merupakan sistem pemrograman non-tekstual, dengan editor yang beroperasi langsung pada tingkat AST.

Sunting2: Untuk menjawab pertanyaan yang diperbarui - tidak ada yang unik dan luar biasa di makro Lisp. Secara teori, bahasa apa pun dapat menyediakan mekanisme seperti itu (saya bahkan melakukannya dengan C sederhana). Yang Anda butuhkan hanyalah akses ke AST Anda dan kemampuan untuk mengeksekusi kode dalam waktu kompilasi. Beberapa refleksi mungkin membantu (menanyakan tentang jenis, definisi yang ada, dll.).

SK-logic
sumber
Tautan Scala Anda secara eksplisit mengatakan bahwa makro yang diusulkan "tidak dapat mengubah sintaksis Scala". (Menarik, ini mencantumkan ini sebagai perbedaan antara proposal itu dan makro preprocessor C / C ++!)
ruakh
@ruakh, ya, itu adalah pendekatan yang sama dengan Converge dan Template Haskell - aplikasi makro ditandai secara eksplisit dan tidak dapat dicampur dengan sintaks "normal". Tapi, di dalam Anda dapat memiliki sintaksis yang Anda suka, jadi bisa dibilang itu adalah sintaks yang bisa dikembangkan. Sayangnya, persyaratan "non-pelat" membatasi opsi Anda untuk bahasa seperti ini.
SK-logic
"Aku bahkan melakukannya dengan C sederhana": Bagaimana ini mungkin?
Giorgio
@ Giorgio, tentu saja saya telah memodifikasi kompiler (makro ditambahkan dan kompilasi modul tambahan, yang sebenarnya cukup alami untuk C).
SK-logic
Mengapa akses ke AST diperlukan?
Elliot Gorokhovsky
6

Prolog memungkinkan mendefinisikan operator baru yang diterjemahkan ke istilah majemuk dengan nama yang sama. Misalnya, ini mendefinisikan has_catoperator dan mendefinisikannya sebagai predikat untuk memeriksa apakah daftar berisi atom cat:

:- op(500, xf, has_cat).
X has_cat :- member(cat, X).

?- [apple, cat, orange] has_cat.
true ;
false.

The xfberarti bahwa has_catadalah operator postfix; menggunakan fxakan menjadikannya operator awalan, dan xfxmembuatnya menjadi operator infiks, mengambil dua argumen. Periksa tautan ini untuk detail lebih lanjut tentang mendefinisikan operator di Prolog.

Ambroz Bizjak
sumber
5

TeX benar-benar hilang dari daftar. Anda semua tahu itu, kan? Itu terlihat seperti ini:

Some {\it ``interesting''} example.

... kecuali Anda dapat mendefinisikan ulang sintaksis tanpa batasan. Setiap token (!) Dalam bahasa dapat diberi arti baru. ConTeXt adalah paket makro yang telah menggantikan kurung kurawal dengan kurung kurawal:

Some \it[``interesting''] example.

Paket makro yang lebih umum, LaTeX juga mendefinisikan kembali bahasa untuk tujuannya, misalnya menambahkan \begin{environment}…\end{environment}sintaks.

Tapi itu tidak berhenti di situ. Secara teknis, Anda bisa mendefinisikan ulang token untuk menguraikan berikut:

Some <it>“interesting”</it> example.

Ya, sangat mungkin. Beberapa paket menggunakan ini untuk mendefinisikan bahasa spesifik domain kecil. Misalnya, paket TikZ mendefinisikan sintaksis ringkas untuk gambar teknis, yang memungkinkan berikut ini:

\foreach \angle in {0, 30, ..., 330} 
  \draw[line width=1pt] (\angle:0.82cm) -- (\angle:1cm);

Selanjutnya, TeX adalah Turing lengkap sehingga Anda benar-benar dapat melakukan semuanya dengan itu. Saya belum pernah melihat ini digunakan untuk potensi penuh karena itu akan sangat tidak berguna dan sangat berbelit-belit tetapi sangat mungkin untuk membuat kode berikut dapat diuraikan hanya dengan mendefinisikan kembali token (tetapi ini mungkin akan pergi ke batas fisik parser, karena cara itu dibangun):

for word in [Some interesting example.]:
    if word == interesting:
        it(word)
    else:
        word
Konrad Rudolph
sumber
5

Boo memungkinkan Anda menyesuaikan bahasa dengan berat pada waktu kompilasi melalui makro sintaksis.

Boo memiliki "pipa penyusun extensible". Itu berarti kompiler dapat memanggil kode Anda untuk melakukan transformasi AST di titik mana pun selama pipa kompiler. Seperti yang Anda ketahui, hal-hal seperti Java's Generics atau C #'s Linq hanyalah transformasi sintaksis pada waktu kompilasi, jadi ini cukup kuat.

Dibandingkan dengan Lisp, keuntungan utamanya adalah ini bekerja dengan semua jenis sintaksis. Boo menggunakan sintaks yang diilhami oleh Python, tetapi Anda mungkin bisa menulis kompilator yang dapat diperluas dengan sintaks C atau Pascal. Dan karena makro dievaluasi pada waktu kompilasi, tidak ada penalti kinerja.

Kerugian, dibandingkan dengan Lisp, adalah:

  • Bekerja dengan AST tidak seanggun bekerja dengan ekspresi-s
  • Karena makro dipanggil pada waktu kompilasi, makro tidak memiliki akses ke data runtime.

Misalnya, ini adalah bagaimana Anda dapat menerapkan struktur kontrol baru:

macro repeatLines(repeatCount as int, lines as string*):
    for line in lines:
        yield [| print $line * $repeatCount |]

pemakaian:

repeatLines 2, "foo", "bar"

yang kemudian diterjemahkan, pada waktu kompilasi, ke sesuatu seperti:

print "foo" * 2
print "bar" * 2

(Sayangnya, dokumentasi online Boo selalu ketinggalan zaman dan bahkan tidak mencakup hal-hal canggih seperti ini. Dokumentasi terbaik untuk bahasa yang saya tahu adalah buku ini: http://www.manning.com/rahien/ )

nikie
sumber
1
Dokumentasi web untuk fitur ini saat ini rusak, dan saya tidak menulis Boo sendiri, tetapi saya pikir akan sangat disayangkan jika diabaikan. Saya menghargai umpan balik mod dan akan mempertimbangkan kembali bagaimana saya berkontribusi informasi gratis di waktu luang saya.
Dan
4

Evaluasi Mathematica didasarkan pada pencocokan pola dan penggantian. Itu memungkinkan Anda untuk membuat struktur kontrol Anda sendiri, mengubah struktur kontrol yang ada atau mengubah cara ekspresi dievaluasi. Misalnya, Anda dapat menerapkan "logika fuzzy" seperti ini (sedikit disederhanakan):

fuzzy[a_ && b_]      := Min[fuzzy[a], fuzzy[b]]
fuzzy[a_ || b_]      := Max[fuzzy[a], fuzzy[b]]
fuzzy[!a_]           := 1-fuzzy[a]
If[fuzzy[a_], b_,c_] := fuzzy[a] * fuzzy[b] + fuzzy[!a] * fuzzy[c]

Ini mengesampingkan evaluasi untuk operator logis yang sudah ditentukan sebelumnya &&, || ,! dan Ifklausa bawaan.

Anda dapat membaca definisi ini seperti definisi fungsi, tetapi arti sebenarnya adalah: Jika sebuah ekspresi cocok dengan pola yang dijelaskan di sisi kiri, itu diganti dengan ekspresi di sisi kanan. Anda bisa menentukan sendiri jika-klausa seperti ini:

myIf[True, then_, else_] := then
myIf[False, then_, else_] := else
SetAttributes[myIf, HoldRest]

SetAttributes[..., HoldRest] memberi tahu evaluator bahwa ia harus mengevaluasi argumen pertama sebelum pencocokan pola, tetapi tahan evaluasi untuk sisanya sampai setelah pola dicocokkan dan diganti.

Ini digunakan secara luas di dalam perpustakaan standar Mathematica untuk misalnya mendefinisikan fungsi Dyang mengambil ekspresi dan mengevaluasi turunan simbolisnya.

nikie
sumber
3

Metalua adalah bahasa dan kompiler yang kompatibel dengan Lua yang menyediakan ini.

  • Kompatibilitas penuh dengan sumber Lua 5.1 dan bytecode: bersih, semantik dan sintaksis yang elegan, kekuatan ekspresif yang luar biasa, kinerja yang baik, portabilitas yang hampir universal. - Sistem makro lengkap, mirip dengan apa yang ditawarkan oleh dialek Lisp atau Template Haskell; program yang dimanipulasi dapat dilihat
    sebagai kode sumber, sebagai pohon sintaksis abstrak, atau sebagai campuran sewenang-wenang
    , mana yang lebih sesuai dengan tugas Anda.
  • Pengurai yang dapat dikembangkan secara dinamis, yang memungkinkan Anda mendukung makro Anda dengan sintaksis yang berpadu apik dengan bahasa lainnya.

  • Satu set ekstensi bahasa, semua diimplementasikan sebagai macro metalua biasa.

Perbedaan dengan Lisp:

  • Jangan ganggu para pengembang dengan makro ketika mereka tidak menulisnya: sintaks dan semantik bahasa harus paling cocok untuk 95% dari waktu ketika kita tidak menulis makro.
  • Dorong pengembang untuk mengikuti konvensi bahasa: tidak hanya dengan "kata-kata kasar praktik terbaik" tidak ada yang mendengarkan, tetapi dengan menawarkan API yang membuatnya lebih mudah untuk menulis hal-hal dengan Cara Metalua. Keterbacaan oleh sesama pengembang lebih penting dan lebih sulit untuk dicapai daripada keterbacaan oleh kompiler, dan untuk ini, memiliki seperangkat konvensi yang dihormati sangat membantu.
  • Namun berikan semua kekuatan yang bersedia ditangani seseorang. Baik Lua maupun Metalua tidak memiliki ikatan dan disiplin wajib, jadi jika Anda tahu apa yang Anda lakukan, bahasa tersebut tidak akan menghalangi Anda.
  • Jelaskan ketika sesuatu yang menarik terjadi: semua operasi meta terjadi antara + {...} dan - {...}, dan secara visual keluar dari kode biasa.

Contoh aplikasi adalah penerapan pencocokan pola seperti-ML.

Lihat juga: http://lua-users.org/wiki/MetaLua

Clement J.
sumber
Meskipun Lua bukan Lisp, daftar "perbedaan" Anda cukup mencurigakan (satu-satunya item yang relevan adalah yang terakhir). Dan, tentu saja, komunitas Lisp tidak akan setuju dengan kata-kata kasar bahwa seseorang tidak boleh menulis / menggunakan macro 95% dari waktu - cara Lisp condong ke arah sesuatu seperti 95% dari waktu menggunakan dan menulis DSL, di atas macro, dari tentu saja
SK-logic
2

Jika Anda mencari bahasa yang dapat diperpanjang, Anda harus melihat Smalltalk.

Di Smalltalk, satu - satunya cara memprogram adalah benar-benar memperluas bahasa. Tidak ada perbedaan antara IDE, perpustakaan atau bahasa itu sendiri. Mereka semua begitu terjalin sehingga Smalltalk sering disebut sebagai lingkungan daripada sebagai bahasa.

Anda tidak menulis aplikasi yang berdiri sendiri di Smalltalk, Anda malah memperluas lingkungan bahasa.

Lihatlah http://www.world.st/ untuk mengetahui beberapa sumber daya dan informasi.

Saya ingin merekomendasikan Pharo sebagai dialek masuk ke dunia Smalltalk: http://pharo-project.org

Semoga ini bisa membantu!

Bernat Romagosa
sumber
1
Kedengarannya menarik.
Thomas Eding
1

Ada alat yang memungkinkan membuat bahasa khusus tanpa menulis seluruh kompiler dari awal. Misalnya ada Spoofax , yang merupakan alat transformasi kode: Anda memasukkan tata bahasa input dan aturan transformasi (ditulis dengan cara deklaratif tingkat yang sangat tinggi), dan kemudian Anda dapat menghasilkan kode sumber Java (atau bahasa lain, jika Anda cukup peduli) dari bahasa khusus yang dirancang oleh Anda.

Jadi, dimungkinkan untuk mengambil tata bahasa bahasa X, mendefinisikan tata bahasa bahasa X '(X dengan ekstensi khusus Anda) dan transformasi X' → X, dan Spoofax akan menghasilkan kompiler X '→ X.

Saat ini, jika saya mengerti dengan benar, dukungan terbaik adalah untuk Java, dengan dukungan C # sedang dikembangkan (atau begitulah yang saya dengar). Teknik ini dapat diterapkan pada bahasa apa pun dengan tata bahasa statis (jadi, misalnya mungkin bukan Perl ).

liori
sumber
1

Keempat adalah bahasa lain yang sangat mudah dikembangkan. Banyak implementasi Forth terdiri dari kernel kecil yang ditulis dalam assembler atau C, kemudian bahasa lainnya ditulis dalam Forth sendiri.

Ada juga beberapa bahasa berbasis tumpukan yang terinspirasi oleh Forth dan berbagi fitur ini, seperti Factor .

Dave Kirby
sumber
0

Funge-98

Fitur sidik jari Funge-98 memungkinkan dapat dilakukan untuk memungkinkan restrukturisasi lengkap seluruh sintaks dan semantik bahasa. Tetapi hanya jika implementor menyediakan mekanisme sidik jari yang memungkinkan pengguna untuk mengubah bahasa pemrograman secara terprogram (ini secara teori dimungkinkan untuk diterapkan dalam sintaks dan semantik Funge-98 normal). Jika demikian, seseorang dapat benar-benar membuat sisa file (atau bagian file apa pun) bertindak sebagai C ++ atau Lisp (atau apa pun yang diinginkannya).

http://quadium.net/funge/spec98.html#Fingerprints

Thomas Eding
sumber
mengapa Anda memposting ini secara terpisah alih-alih menambahkan ke jawaban Anda sebelumnya ?
nyamuk
1
Karena Funge tidak ada hubungannya dengan Haskell.
Thomas Eding
-1

Untuk mendapatkan apa yang Anda cari, Anda benar-benar membutuhkan tanda kurung dan kurangnya sintaksis. Beberapa bahasa berbasis sintaksis mungkin saja mendekati, tetapi itu tidak persis sama dengan makro yang sebenarnya.

mike30
sumber