ภาษาการเขียนโปรแกรมที่มีกลไกการขยายไวยากรณ์คล้าย Lisp [ปิด]


20

ฉันมีความรู้ จำกัด ใน Lisp (พยายามที่จะเรียนรู้เล็กน้อยในเวลาว่างของฉัน) แต่เท่าที่ฉันเข้าใจมาโคร Lisp อนุญาตให้แนะนำการสร้างภาษาใหม่และไวยากรณ์โดยการอธิบายพวกเขาใน Lisp เอง ซึ่งหมายความว่าสามารถเพิ่มการสร้างใหม่เป็นไลบรารีโดยไม่ต้องเปลี่ยน Lisp คอมไพเลอร์ / ล่าม

วิธีนี้แตกต่างจากภาษาโปรแกรมอื่นมาก เช่นถ้าฉันต้องการขยาย Pascal ด้วยลูปชนิดใหม่หรือสำนวนเฉพาะบางอย่างฉันจะต้องขยายไวยากรณ์และความหมายของภาษาแล้วนำคุณสมบัติใหม่นั้นมาใช้ในคอมไพเลอร์

มีภาษาการเขียนโปรแกรมอื่น ๆ นอกตระกูล Lisp (เช่นนอกเหนือจาก Common Lisp, Scheme, Clojure (?), Racket (?), ฯลฯ ) ที่มีความเป็นไปได้ที่คล้ายคลึงกันในการขยายภาษาภายในภาษานั้นหรือไม่

แก้ไข

โปรดหลีกเลี่ยงการอภิปรายเพิ่มเติมและเฉพาะเจาะจงในคำตอบของคุณ แทนที่จะเป็นรายการที่มีความยาวของภาษาโปรแกรมที่สามารถขยายออกไปไม่ทางใดก็ทางหนึ่งฉันต้องการที่จะเข้าใจจากมุมมองเชิงความคิดเกี่ยวกับมาโคร Lisp เฉพาะที่เป็นกลไกการขยายและภาษาที่ไม่ใช่ Lisp เสนอแนวคิดบางอย่าง ที่ใกล้กับพวกเขา


6
Lisp มีเคล็ดลับอื่นนอกเหนือจากมาโครปกติ "Reader Macros" อนุญาตให้จับภาพไวยากรณ์ของโปรแกรมแยกวิเคราะห์และขยายเวลารันไทม์ดังนั้นแม้โครงสร้างโทเค็นพื้นฐานของภาษาจะอยู่ภายใต้การควบคุม
ddyer

@ddyer: ขอบคุณฉันไม่รู้เกี่ยวกับเรื่องนั้น: อีกหัวข้อที่จะเพิ่มในรายการเรื่องรออ่านของฉัน
Giorgio

ฉันเดิมพัน Ruby metaprogramming สามารถพบสิ่งนี้ได้
Rig

1
คำถามของคุณขัดแย้งกันก่อนอื่นคุณขอรายการจากนั้นคุณขอข้อมูลเกี่ยวกับแนวคิดซึ่งมันคืออะไร ???

ฉันกำลังขอรายการ แต่ไม่ใช่ภาษาทั่วไป (ไม่ใช่แค่ภาษาโปรแกรมที่สามารถขยายออกไปได้) เพราะนี่จะกว้างเกินไป ฉันต้องการทราบภาษาที่สามารถขยายได้ในลักษณะที่คล้ายกับ Lisp (ส่วนขยายถูกกำหนดโดยใช้ภาษาเดียวกันกับที่ขยายออกไม่ใช่เมตาดาต้าบางภาษา) คำตอบของPéterTörök, Thomas Eding, SK-logic และหอยทากเครื่องกลดูเหมือนจะใกล้เคียงกับที่ฉันคิดไว้มากที่สุด ฉันยังคงต้องอ่านคำตอบทั้งหมดด้วยความระมัดระวัง
Giorgio

คำตอบ:


19

สกาล่าทำให้สิ่งนี้เป็นไปได้เช่นกัน (ในความเป็นจริงมันถูกออกแบบอย่างมีสติเพื่อรองรับคำจำกัดความของภาษาใหม่และแม้แต่ DSL ที่สมบูรณ์)

นอกเหนือจากฟังก์ชั่นการสั่งซื้อขั้นสูงแลมบ์ดาและเคอร์ริ่งซึ่งเป็นเรื่องปกติในภาษาที่ใช้งานได้แล้วยังมีฟีเจอร์ภาษาพิเศษบางอย่างที่นี่เพื่อเปิดใช้งาน *:

  • ไม่มีตัวดำเนินการ - ทุกอย่างเป็นฟังก์ชั่น แต่ชื่อฟังก์ชั่นสามารถมีอักขระพิเศษเช่น '+', '-' หรือ ':'
  • จุดและวงเล็บปีกกาสามารถละเว้นสำหรับการเรียกใช้วิธีการพารามิเตอร์เดียวa.and(b)คือเทียบเท่ากับa and bในรูปแบบมัด
  • สำหรับการเรียกใช้ฟังก์ชั่นแบบพารามิเตอร์เดียวคุณสามารถใช้วงเล็บปีกกาแทนการจัดฟันแบบปกติ - สิ่งนี้ (พร้อมกับการแก้เผ็ด) ช่วยให้คุณสามารถเขียนสิ่งต่าง ๆ เช่น

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

    โดยที่withPrintWriterเป็นวิธีธรรมดาที่มีรายการพารามิเตอร์สองรายการทั้งคู่มีพารามิเตอร์เดียว

  • พารามิเตอร์แบบชื่อช่วยให้คุณละเว้นรายการพารามิเตอร์ที่ว่างเปล่าใน lambdas ช่วยให้คุณสามารถเขียนการโทรเช่นmyAssert(() => x > 3)ในรูปแบบที่สั้นกว่าเช่นmyAssert(x > 3)

การสร้างตัวอย่าง DSL จะกล่าวถึงในรายละเอียดในบทที่ 11 ภาษาเฉพาะของโดเมนใน ScalaของหนังสือฟรีProgramming สกาล่า

* ฉันไม่ได้หมายความว่าสิ่งเหล่านี้มีความพิเศษสำหรับ Scala แต่อย่างน้อยพวกเขาก็ไม่ได้เป็นคนธรรมดา ฉันไม่ใช่ผู้เชี่ยวชาญด้านภาษาที่ใช้งานได้


1
+1: น่าสนใจ นี่หมายความว่าเพื่อขยายไวยากรณ์ฉันสามารถกำหนดคลาสใหม่และลายเซ็นของวิธีนั้นจะให้ไวยากรณ์ใหม่เป็นผลพลอยได้หรือไม่
Giorgio

@ จอร์โจโดยทั่วไปใช่
PéterTörök

ลิงก์ไม่ทำงานอีกต่อไป
Nate Glenn

13

Perl ช่วยให้การประมวลผลภาษาล่วงหน้า แม้ว่าสิ่งนี้ไม่ได้ใช้บ่อยเท่าที่ขอบเขตการเปลี่ยนแปลงไวยากรณ์ในภาษาอย่างรุนแรง แต่ก็สามารถเห็นได้ในบางส่วนของโมดูลคี่ ...

  • Acme :: Bleachสำหรับรหัสที่สะอาดจริงๆ
  • จุดสุดยอด :: มอร์สสำหรับการเขียนในรหัสมอร์ส
  • Lingua :: Romana :: Perligataสำหรับการเขียนเป็นภาษาละติน (ตัวอย่างเช่นคำนามคือตัวแปรและจำนวนตัวพิมพ์เล็กและตัวพิมพ์ใหญ่) เปลี่ยนประเภทของคำนาม nextum ==> $ next, nexta ==> @next)

นอกจากนี้ยังมีโมดูลที่ทำให้ Perl สามารถรันโค้ดที่ดูเหมือนว่ามันถูกเขียนใน Python

วิธีการที่ทันสมัยกว่านี้ภายใน Perl จะใช้Filter :: Simple (หนึ่งในโมดูลหลักใน perl5)

โปรดทราบว่าตัวอย่างเหล่านั้นทั้งหมดเกี่ยวข้องกับ Damian Conway ที่ได้รับการขนานนามว่าเป็น "Mad Doctor of Perl" มันยังคงมีความสามารถที่มีพลังอย่างน่าอัศจรรย์ภายใน Perl เพื่อบิดภาษาที่เราต้องการ

เอกสารเพิ่มเติมนี้และทางเลือกอื่น ๆ ที่มีอยู่ในperlfilter


13

Haskell

Haskell มี "Template Haskell" และ "Quasiquotation":

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

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

คุณสมบัติเหล่านี้ช่วยให้ผู้ใช้เพิ่มไปยังไวยากรณ์ของภาษานอกหมายถึงปกติอย่างมาก สิ่งเหล่านี้ได้รับการแก้ไขในเวลารวบรวมเช่นกันซึ่งฉันคิดว่าเป็นเรื่องใหญ่ (สำหรับภาษาที่รวบรวมอย่างน้อย) [1]

ฉันเคยใช้ quasiquotation ใน Haskell หนึ่งครั้งก่อนที่จะสร้าง matcher รูปแบบขั้นสูงผ่านภาษา 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] มีคุณสมบัติดังต่อไปนี้เป็นส่วนขยายไวยากรณ์: runFeature "some complicated grammar enclosed in a string to be evaluated at runtime"ซึ่งแน่นอนว่าเป็นภาระของอึ


3
นอกจากนี้ยังมีคุณสมบัติอื่น ๆ ของ Haskell ที่อนุญาตให้ใช้ไวยากรณ์แบบกำหนดเองเช่นสร้างตัวดำเนินการของคุณเองหรือการ currying อัตโนมัติควบคู่ไปกับฟังก์ชั่นแลมบ์ดา (พิจารณาเช่นการใช้งานทั่วไปforM)
Xion

ฉันคิดอย่างซื่อสัตย์ว่าผู้ดูแลระบบที่กำหนดเองไม่ได้เข้าร่วม พวกเขาอนุญาตให้ใช้ภาษาอย่างเรียบร้อย แต่พวกเขาไม่อนุญาตให้คุณเพิ่ม / ใหม่ / ฟังก์ชันการทำงานกับภาษา TH และ QQ ทำ ในความหมายที่เข้มงวดไม่ทำ TH และ QQ ในแง่ที่ว่าพวกเขาทำสิ่งที่พวกเขาถูกออกแบบมาเพื่อทำ แต่พวกเขาช่วยให้คุณ "ออกไปจากภาษา" ในเวลารวบรวม
Thomas Eding

1
"อื่นต่อไปนี้ถือว่าเป็นส่วนขยายไวยากรณ์ ... ": จุดดีมาก
จอร์โจ

12

Tclมีประวัติอันยาวนานในการสนับสนุนไวยากรณ์ที่สามารถขยายได้ ตัวอย่างเช่นต่อไปนี้คือการใช้ลูปที่วนซ้ำตัวแปรสามตัว (จนกว่าจะหยุด) บน cardinals, squares และ cubes:

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
    }
}

ที่จะถูกใช้เช่นนี้:

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

เทคนิคการเรียงลำดับนี้ถูกใช้อย่างกว้างขวางในการเขียนโปรแกรม Tcl และกุญแจสำคัญในการดำเนินการอย่างมีสติคือupvarและuplevelคำสั่ง ( upvarผูกตัวแปรที่ระบุชื่อไว้ในขอบเขตอื่นกับตัวแปรโลคัลและuplevelรันสคริปต์ในขอบเขตอื่น: ในทั้งสองกรณี1บ่งชี้ว่า ขอบเขตในคำถามคือผู้โทร) นอกจากนี้ยังใช้รหัสจำนวนมากที่มาพร้อมกับฐานข้อมูล (ใช้งานโค้ดบางชุดสำหรับแต่ละแถวในชุดผลลัพธ์) ใน Tk สำหรับ GUI (สำหรับการเชื่อมโยงการเรียกกลับสู่เหตุการณ์) เป็นต้น

อย่างไรก็ตามนั่นเป็นเพียงเศษเสี้ยวของสิ่งที่ทำ ภาษาที่ฝังไม่จำเป็นต้องเป็น Tcl แม้แต่ตัวเดียว มันสามารถเป็นจริงได้ทุกอย่าง (ตราบใดที่มันจัดฟันที่สมดุล - สิ่งต่าง ๆ น่ากลัวมากถ้ามันไม่เป็นความจริง - ซึ่งเป็นโปรแกรมส่วนใหญ่) และ Tcl ก็สามารถส่งไปยังภาษาต่างประเทศที่ฝังอยู่ได้ตามความจำเป็น ตัวอย่างของการทำเช่นนี้รวมถึงการฝัง C เพื่อใช้คำสั่ง Tclและเทียบเท่ากับ Fortran (เนื้อหาคำสั่งในตัวของ Tcl ทั้งหมดทำในลักษณะนี้ในแง่ที่ว่าพวกเขาเป็นเพียงห้องสมุดมาตรฐานจริงๆไม่ใช่ภาษาเอง)


10

นี่เป็นคำถามส่วนหนึ่งของความหมาย แนวคิดพื้นฐานของ Lisp คือโปรแกรมเป็นข้อมูลที่สามารถจัดการได้ ภาษาที่ใช้กันทั่วไปในตระกูล Lisp เช่น Scheme ไม่อนุญาตให้คุณเพิ่มไวยากรณ์ใหม่ในแบบแยกวิเคราะห์ มันเป็นเพียงรายการที่คั่นด้วยช่องว่าง เป็นเพียงแค่ว่าเนื่องจากไวยากรณ์หลักทำน้อยมากคุณสามารถสร้างเกือบทุกความหมายจากมัน Scala (อธิบายไว้ด้านล่าง) คล้ายกัน: กฎชื่อตัวแปรมีอิสระมากมายที่คุณสามารถสร้าง DSL ที่ดีได้อย่างง่ายดาย (ในขณะที่อยู่ในกฎไวยากรณ์หลักเดียวกัน)

ภาษาเหล่านี้ในขณะที่พวกเขาไม่ได้ให้คุณกำหนดไวยากรณ์ใหม่ในแง่ของตัวกรอง Perl มีแกนกลางที่ยืดหยุ่นเพียงพอที่คุณสามารถใช้เพื่อสร้าง DSL และเพิ่มโครงสร้างภาษา

คุณสมบัติทั่วไปที่สำคัญคือพวกเขาให้คุณกำหนดโครงสร้างภาษาที่ทำงานได้ดีและมีอยู่แล้วภายในโดยใช้คุณสมบัติที่เปิดเผยโดยภาษา ระดับการรองรับคุณสมบัตินี้แตกต่างกันไป:

  • ภาษาเก่า ๆ ในตัวฟังก์ชั่นเช่นsin(), round()ฯลฯ โดยไม่ต้องใช้วิธีการใด ๆ ของคุณเอง
  • C ++ ให้การสนับสนุนที่ จำกัด ยกตัวอย่างเช่นบางตัวในคำหลักเช่นปลดเปลื้อง ( static_cast<target_type>(input), dynamic_cast<>(), const_cast<>(), reinterpret_cast<>()) สามารถเทิดทูนโดยใช้ฟังก์ชั่นแม่แบบซึ่งใช้ Boost ให้lexical_cast<>(), polymorphic_cast<>(), any_cast<>(), ....
  • Java มีในตัวโครงสร้างการควบคุม ( for(;;){}, while(){}, if(){}else{}, do{}while(), synchronized(){}, strictfp{}) และไม่ปล่อยให้คุณกำหนดด้วยตัวคุณเอง Scala จะกำหนดไวยากรณ์ที่เป็นนามธรรมซึ่งให้คุณเรียกใช้ฟังก์ชันโดยใช้ไวยากรณ์โครงสร้างคล้ายการควบคุมที่สะดวกและไลบรารีใช้สิ่งนี้เพื่อกำหนดโครงสร้างการควบคุมใหม่ได้อย่างมีประสิทธิภาพ (เช่นreact{}ในไลบรารีนักแสดง)

นอกจากนี้คุณอาจจะมองไปที่การทำงานไวยากรณ์ที่กำหนดเอง Mathematica ในแพคเกจโน้ต (โดยทางเทคนิคแล้วมันอยู่ในตระกูล Lisp แต่มีคุณสมบัติการขยายบางอย่างที่ทำแตกต่างกันเช่นเดียวกับ Lisp Extensibility ปกติ)


2
ไม่ถูกต้อง. เสียงกระเพื่อมไม่จริงช่วยให้คุณสามารถเพิ่มอย่างชนิดใด ๆ ของไวยากรณ์ใหม่ เช่นเดียวกับในตัวอย่างนี้: meta-alternative.net/pfront.pdf - มันเป็นอะไรนอกจากแมโคร Lisp
SK-logic

ที่ดูเหมือนว่าจะนำมาใช้ในภาษาที่ออกแบบมาโดยเฉพาะสำหรับการสร้าง DSLs แน่นอนคุณสามารถสร้างภาษาในตระกูล Lisp ที่มีคุณสมบัติดังกล่าว ฉันหมายความว่าฟีเจอร์นั้นไม่ใช่แนวคิดหลักของ Lisp ที่สนับสนุน Lisps ที่ใช้แบบทั่วไป (เช่น Scheme) แก้ไขเพื่อชี้แจง
หอยทากเครื่องกล

"ภาษาสำหรับการสร้าง DSL" นี้เป็นคอลเลกชันของมาโครที่สร้างขึ้นจาก Lisp แบบเรียบง่ายทั่วไป มันสามารถ ported ได้อย่างง่ายดายเพื่อเสียงกระเพื่อมอื่น ๆ (defmacro ...)ที่มี ที่จริงฉันกำลังนำภาษานี้ไปใช้กับ Racket เพื่อความสนุกสนาน แต่ฉันเห็นด้วยว่ามันเป็นสิ่งที่ไม่มีประโยชน์มากเนื่องจากไวยากรณ์ของ S-expressions นั้นมีมากเกินพอสำหรับความหมายที่เป็นประโยชน์ส่วนใหญ่ที่เป็นไปได้
SK-logic

และโครงการไม่แตกต่างจาก Common LISP เริ่มต้นจาก R6RS อย่างเป็นทางการและไม่เป็นทางการสำหรับอายุเนื่องจากมันให้(define-macro ...)เทียบเท่าซึ่งในทางกลับกันสามารถใช้การแยกประเภทใด ๆ ภายใน
SK-logic

8

Rebolฟังดูคล้ายกับสิ่งที่คุณกำลังอธิบาย แต่อยู่ด้านข้างเล็กน้อย

แทนที่จะกำหนดไวยากรณ์เฉพาะทุกอย่างใน Rebol เป็นการเรียกใช้ฟังก์ชั่น - ไม่มีคำหลัก (ใช่คุณสามารถกำหนดใหม่ifและwhileหากคุณต้องการอย่างแท้จริง) ตัวอย่างเช่นนี่คือifคำสั่ง:

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

ifเป็นฟังก์ชั่นที่รับอาร์กิวเมนต์ 2 ตัวคือเงื่อนไขและบล็อก หากเงื่อนไขเป็นจริงบล็อกจะถูกประเมิน เสียงเหมือนภาษาส่วนใหญ่ใช่ไหม ดีบล็อกเป็นโครงสร้างข้อมูลไม่ จำกัด รหัส - นี่คือบล็อกของบล็อกตัวอย่างและตัวอย่างรวดเร็วของความยืดหยุ่นของ "รหัสคือข้อมูล":

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"]

ตราบใดที่คุณสามารถยึดติดกับกฎไวยากรณ์การขยายภาษานี้ส่วนใหญ่จะไม่มีอะไรมากไปกว่าการนิยามฟังก์ชั่นใหม่ ผู้ใช้บางคนได้รับคุณสมบัติย้อนกลับของ Rebol 3 เป็น Rebol 2 เป็นต้น


7

Ruby มีไวยากรณ์ที่ค่อนข้างยืดหยุ่นฉันคิดว่ามันเป็นวิธีการ "ขยายภาษาภายในภาษานั้น"

ตัวอย่างคือการเสาะหา มันเขียนในรูบีมันเป็นทับทิม แต่ดูเหมือนว่าทำให้

ในการตรวจสอบความเป็นไปได้บางอย่างที่คุณสามารถมองหาคำหลักทับทิมและmetaprogramming


13
"Flexible syntax" นั้นแตกต่างจาก "extensible syntax" มาก เป็นเวลานานแล้วที่ฉันได้ตั้งโปรแกรมใน Ruby แต่ดูเหมือนว่า Rake ดูเหมือนการใช้ไวยากรณ์ในตัวที่ออกแบบมาอย่างดี กล่าวอีกนัยหนึ่งนี่ไม่ใช่ตัวอย่าง
Thomas Eding

2
นั่นเป็นเรื่องของการศึกษาระดับปริญญาใช่หรือไม่? คุณจะแยกความแตกต่างระหว่างภาษา "ไวยากรณ์ที่ขยายได้" ที่สามารถขยายบางแง่มุมของไวยากรณ์ แต่ไม่ใช่ภาษาอื่นและภาษา "ไวยากรณ์ที่ยืดหยุ่น" ได้อย่างไร
Comingstorm

1
หากบรรทัดนั้นพร่ามัวให้ลองย้อนกลับบรรทัดนั้นเพื่อให้ C พิจารณาว่าสนับสนุนไวยากรณ์ที่ขยายได้
Thomas Eding

หากต้องการเชื่อมโยงกับตัวอย่างของคุณฉันจะลากเส้นที่นี่: ภาษาที่มีไวยากรณ์ที่สามารถขยายได้สามารถทำการเรคซึ่งดูเหมือนว่าจะทำให้ ภาษาที่มีไวยากรณ์ที่ยืดหยุ่นสามารถขยายได้ (ฮา, การผสมผสานทางภาษาที่ดี) เพื่อรวบรวมและเรียกใช้ makefiles จุดของคุณในเรื่องของการศึกษาระดับปริญญาเป็นสิ่งที่ดีแม้ว่า อาจมีบางภาษาที่อนุญาตให้คอมไพล์ Make แต่ไม่ใช่ Python และคนอื่นก็ยอมให้ทั้งคู่
Clayton Stanley

ภาษาที่สมบูรณ์แบบทัวริงควรจะสามารถทำการประมวลผลไฟล์ได้ ความยืดหยุ่นของไวยากรณ์ไม่ได้เป็นเพียงแค่ความท้าทาย
Rig

7

การขยายไวยากรณ์ตามที่คุณกำลังพูดถึงช่วยให้คุณสามารถสร้างภาษาเฉพาะโดเมนได้ ดังนั้นวิธีที่มีประโยชน์ที่สุดในการเรียบเรียงคำถามของคุณใหม่คือภาษาใดที่มีการสนับสนุนที่ดีสำหรับภาษาเฉพาะของโดเมน

Ruby มีไวยากรณ์ที่ยืดหยุ่นมากและ DSL จำนวนมากมีความเจริญรุ่งเรืองที่นั่นเช่นคราด Groovy ประกอบไปด้วยคุณงามความดีมากมาย นอกจากนี้ยังรวมถึงการแปลง AST ซึ่งคล้ายกับแมโคร Lisp โดยตรง

R ภาษาสำหรับการคำนวณทางสถิติอนุญาตให้ฟังก์ชันรับอาร์กิวเมนต์ที่ไม่มีการประเมิน มันใช้สิ่งนี้เพื่อสร้าง DSL สำหรับการระบุสูตรการถดถอย ตัวอย่างเช่น:

y ~ a + b

หมายถึง "ให้พอดีกับบรรทัดของแบบฟอร์ม k0 + k1 * a + k2 * b กับค่าใน y"

y ~ a * b

หมายถึง "ให้พอดีกับบรรทัดของแบบฟอร์ม k0 + k1 * a + k2 * b + k3 * a * b กับค่าใน y"

และอื่น ๆ


2
การแปลง AST ของ Groovy นั้นมีความละเอียดมากเมื่อเทียบกับมาโคร Lisp หรือ Clojure ตัวอย่างเช่นตัวอย่าง Groovy 20+ บรรทัดที่groovy.codehaus.org/Global+AST+Transformationsสามารถเขียนใหม่เป็นหนึ่งบรรทัดสั้น ๆ ใน Clojure เช่น `(นี่ (ข้อความ (ข้อความนี้ println ~)) ไม่เพียงแค่นั้น แต่คุณไม่จำเป็นต้องรวบรวม jar เขียนข้อมูลเมตาหรือสิ่งอื่นใดในหน้า Groovy
Vorg van Geir

7

การบรรจบกันเป็นอีกภาษาหนึ่งที่ไม่ใช่ภาษาอ่อนไหว และในระดับหนึ่ง C ++ ก็มีคุณสมบัติเช่นกัน

เนื้อหาMetaOCamlค่อนข้างห่างไกลจากเสียงกระเพื่อม สำหรับรูปแบบที่แตกต่างกันโดยสิ้นเชิงของไวยากรณ์การขยาย แต่ยังมีประสิทธิภาพค่อนข้างจะดูที่CamlP4

Nemerleเป็นอีกภาษาที่สามารถขยายได้ด้วย metaprogramming แบบ Lisp-style แม้ว่ามันจะใกล้เคียงกับภาษาเช่น Scala

และสกาล่าเองก็จะกลายเป็นภาษาดังกล่าวเช่นกัน

แก้ไข: ฉันลืมเกี่ยวกับตัวอย่างที่น่าสนใจที่สุด - JetBrains MPS มันไม่เพียง แต่ห่างไกลจากสิ่งใดก็ตามที่ Lispish เท่านั้น แต่ยังเป็นระบบการเขียนโปรแกรมที่ไม่ใช่ข้อความด้วยโปรแกรมแก้ไขที่ทำงานโดยตรงในระดับ AST

แก้ไข 2: เพื่อตอบคำถามที่อัปเดต - ไม่มีอะไรพิเศษและยอดเยี่ยมในมาโคร Lisp ในทางทฤษฎีภาษาใด ๆ สามารถให้กลไกดังกล่าวได้ (ฉันทำได้ด้วย C ธรรมดา) สิ่งที่คุณต้องมีคือการเข้าถึง AST ของคุณและความสามารถในการรันโค้ดในเวลารวบรวม การสะท้อนบางอย่างอาจช่วยได้ (สอบถามเกี่ยวกับประเภทคำจำกัดความที่มีอยู่ ฯลฯ )


ลิงก์สกาล่าของคุณบอกอย่างชัดเจนว่ามาโครที่เสนอ "ไม่สามารถเปลี่ยนไวยากรณ์ของสกาล่า" ได้ (ที่น่าสนใจก็จะแสดงรายการนี้เป็นความแตกต่างระหว่างข้อเสนอที่และ C / C ++ preprocessor แมโคร!)
ruakh

@ruakh ใช่มันเป็นวิธีการเดียวกับ Converge และ Template Haskell - แอปพลิเคชันมาโครมีการทำเครื่องหมายอย่างชัดเจนและไม่สามารถผสมกับไวยากรณ์ "ปกติ" แต่ภายในคุณสามารถมีไวยากรณ์ใด ๆ ที่คุณต้องการดังนั้นจึงเป็นไวยากรณ์ที่ขยายได้ ข้อกำหนด "ไม่ใช่เสียงกระเพื่อม" แต่น่าเสียดายที่ จำกัด ตัวเลือกของคุณเป็นภาษาเช่นนี้
SK-logic

"ฉันทำได้ด้วย C ธรรมดา": เป็นไปได้อย่างไร?
Giorgio

@Giorgio แน่นอนฉันได้แก้ไขคอมไพเลอร์ (เพิ่มมาโครและการคอมไพล์โมดูลเพิ่มเติมซึ่งจริงๆแล้วเป็นเรื่องธรรมดาสำหรับ C)
SK-logic

ทำไมการเข้าถึง AST จึงมีความจำเป็น
Elliot Gorokhovsky

6

อารัมภบทช่วยให้การกำหนดผู้ประกอบการใหม่ซึ่งแปลเป็นคำประสมในชื่อเดียวกัน ตัวอย่างเช่นสิ่งนี้นิยามhas_catโอเปอเรเตอร์และกำหนดเป็นเพรดิเคตสำหรับตรวจสอบว่ารายการมีอะตอมหรือไม่cat:

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

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

xfหมายความว่าhas_catเป็นผู้ประกอบการ postfix; ใช้fxจะทำให้มันเป็นตัวดำเนินการคำนำหน้าและxfxจะทำให้มันเป็นตัวดำเนินการมัดใช้สองข้อโต้แย้ง ตรวจสอบลิงค์นี้สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับการกำหนดโอเปอเรเตอร์ใน Prolog


5

TeXหายไปจากรายการโดยสิ้นเชิง คุณทุกคนรู้ใช่มั้ย ดูเหมือนว่า:

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

…ยกเว้นว่าคุณสามารถกำหนดไวยากรณ์ใหม่ได้โดยไม่มีข้อ จำกัด ทุกโทเค็น (!) ในภาษาสามารถกำหนดความหมายใหม่ ConTeXtเป็นแพคเกจมาโครซึ่งแทนที่วงเล็บปีกกาด้วยวงเล็บปีกกา:

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

LaTeX ที่เป็นแพคเกจแมโครที่ใช้กันทั่วไปจะนิยามภาษาอีกครั้งเพื่อจุดประสงค์ของมันเช่นการเพิ่ม\begin{environment}…\end{environment}ไวยากรณ์

แต่มันไม่หยุดเพียงแค่นั้น ในทางเทคนิคคุณสามารถกำหนดโทเค็นใหม่เพื่อแยกวิเคราะห์ดังต่อไปนี้:

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

ใช่เป็นไปได้อย่างแน่นอน บางแพ็คเกจใช้สิ่งนี้เพื่อกำหนดภาษาเฉพาะโดเมนขนาดเล็ก ตัวอย่างเช่นแพคเกจTikZกำหนดไวยากรณ์รัดกุมสำหรับภาพวาดทางเทคนิคซึ่งช่วยให้ต่อไปนี้:

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

นอกจากนี้เท็กซ์เป็นทัวริงสมบูรณ์เพื่อให้คุณอย่างแท้จริงสามารถทำทุกอย่างกับมัน ฉันไม่เคยเห็นสิ่งนี้เคยชินกับศักยภาพเต็มที่เพราะมันจะไร้ประโยชน์และมีความซับซ้อนมาก แต่มันเป็นไปได้ทั้งหมดที่จะทำให้รหัสต่อไปนี้สามารถแยกวิเคราะห์ได้โดยการกำหนดโทเค็นใหม่ (แต่นี่อาจจะไปถึงขีด จำกัด ทางกายภาพ วิธีที่มันสร้างขึ้น):

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

5

บูช่วยให้คุณปรับแต่งภาษาอย่างหนักในเวลารวบรวมผ่านแมโครวากยสัมพันธ์

Boo มี "ไพพ์ไลน์คอมไพเลอร์ที่ขยายได้" นั่นหมายความว่าคอมไพเลอร์สามารถเรียกรหัสของคุณเพื่อทำการแปลง AST ณ จุดใด ๆ ระหว่างไพพ์ไลน์คอมไพเลอร์ อย่างที่คุณทราบสิ่งต่าง ๆ เช่น Generics ของ Java หรือ Linq ของ C # เป็นเพียงการแปลงไวยากรณ์ในเวลารวบรวมดังนั้นนี่จึงค่อนข้างทรงพลัง

เมื่อเทียบกับ LISP ประโยชน์หลักคือมันใช้ได้กับไวยากรณ์ทุกชนิด Boo ใช้ไวยากรณ์ที่ได้รับแรงบันดาลใจจาก Python แต่คุณอาจจะเขียนคอมไพเลอร์แบบขยายได้ด้วย C หรือ Pascal และเนื่องจากแมโครได้รับการประเมิน ณ เวลารวบรวมจึงไม่มีการลงโทษประสิทธิภาพ

ข้อเสียเมื่อเทียบกับเสียงกระเพื่อมคือ:

  • การทำงานกับ AST นั้นไม่สวยงามเท่าการทำงานกับ s-expressions
  • เนื่องจากแมโครถูกเรียกใช้ในเวลาคอมไพล์จึงไม่สามารถเข้าถึงข้อมูลรันไทม์ได้

ตัวอย่างเช่นนี่คือวิธีที่คุณอาจใช้โครงสร้างการควบคุมใหม่:

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

การใช้งาน:

repeatLines 2, "foo", "bar"

ซึ่งได้รับการแปลในเวลารวบรวมเพื่อสิ่งที่ชอบ:

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

(น่าเสียดายที่เอกสารออนไลน์ของ Boo ล้าสมัยอย่างสิ้นหวังเสมอและไม่ได้ครอบคลุมสิ่งขั้นสูงเช่นนี้เอกสารที่ดีที่สุดสำหรับภาษาที่ฉันรู้จักคือหนังสือเล่มนี้: http://www.manning.com/rahien/ )


1
ขณะนี้เอกสารประกอบทางเว็บสำหรับคุณลักษณะนี้ใช้งานไม่ได้และฉันไม่ได้เขียน Boo ด้วยตัวเอง แต่ฉันคิดว่าน่าเสียดายหากมองข้ามไป ฉันขอขอบคุณข้อเสนอแนะ mod และจะพิจารณาว่าฉันไปเกี่ยวกับการให้ข้อมูลฟรีในเวลาว่าง
ด่าน

4

Mathematicaการประเมินผลขึ้นอยู่กับการจับคู่รูปแบบและการแทนที่ ที่ช่วยให้คุณสร้างโครงสร้างการควบคุมของคุณเองเปลี่ยนโครงสร้างการควบคุมที่มีอยู่หรือเปลี่ยนวิธีการประเมินนิพจน์ ตัวอย่างเช่นคุณสามารถใช้ "fuzzy logic" เช่นนี้ (ลดความซับซ้อนลงเล็กน้อย):

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]

สิ่งนี้จะแทนที่การประเมินผลสำหรับตัวดำเนินการเชิงตรรกะที่กำหนดไว้ล่วงหน้า &&, ||,! และIfประโยคในตัว

คุณสามารถอ่านคำจำกัดความเหล่านี้เช่นนิยามฟังก์ชัน แต่ความหมายที่แท้จริงคือ: ถ้านิพจน์ตรงกับรูปแบบที่อธิบายไว้ทางด้านซ้ายมันจะถูกแทนที่ด้วยนิพจน์ทางด้านขวา คุณสามารถกำหนด If-clause ของคุณเองเช่นนี้:

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

SetAttributes[..., HoldRest] บอกผู้ประเมินว่าควรประเมินค่าอาร์กิวเมนต์แรกก่อนการจับคู่รูปแบบ แต่เก็บการประเมินสำหรับส่วนที่เหลือจนกระทั่งหลังจากจับคู่รูปแบบและแทนที่

สิ่งนี้ถูกใช้อย่างกว้างขวางภายในไลบรารีมาตรฐาน Mathematica เพื่อกำหนดฟังก์ชันDที่รับนิพจน์และประเมินผลเป็นสัญลักษณ์เชิงอนุพันธ์


3

Metaluaเป็นภาษาและคอมไพเลอร์ที่เข้ากันได้กับ Lua ที่ให้สิ่งนี้

  • เข้ากันได้เต็มรูปแบบกับแหล่ง Lua 5.1 และ bytecode: ความหมายที่สะอาดสง่างามและไวยากรณ์พลังการถ่ายทอดที่น่าทึ่งการแสดงที่ดีการพกพาใกล้เคียงกับสากล - ระบบมาโครที่สมบูรณ์คล้ายกับพลังของภาษา Lisp หรือ Template Haskell โปรแกรมจัดการสามารถมองเห็น
    เป็นรหัสที่มาเหมือนต้นไม้ไวยากรณ์นามธรรมหรือเป็นส่วนผสมโดยพลการ
    นั้นแล้วแต่จำนวนใดเหมาะสมกับงานของคุณดีขึ้น
  • ตัวแยกวิเคราะห์ที่ขยายได้แบบไดนามิกซึ่งช่วยให้คุณสนับสนุนมาโครของคุณด้วยไวยากรณ์ที่ผสมผสานอย่างลงตัวกับส่วนที่เหลือของภาษา

  • ชุดส่วนขยายภาษาทั้งหมดนำมาใช้เป็นมาโคร metalua ปกติ

ความแตกต่างกับ Lisp:

  • อย่ารำคาญนักพัฒนาที่มีมาโครเมื่อพวกเขาไม่ได้เขียนหนึ่ง: ไวยากรณ์และความหมายของภาษาควรจะเหมาะสมที่สุดสำหรับ 95% ของเวลาที่เราไม่ได้เขียนมาโคร
  • กระตุ้นให้ผู้พัฒนาทำตามแบบแผนของภาษา: ไม่เพียง แต่กับ "วิธีปฏิบัติที่ดีที่สุด" ไม่มีใครฟัง แต่ด้วยการเสนอ API ที่ทำให้การเขียนสิ่งต่าง ๆ ในแบบของ Metalua ง่ายขึ้น ความสามารถในการอ่านของนักพัฒนาซอฟต์แวร์เพื่อนนั้นมีความสำคัญและยากต่อการบรรลุผลมากกว่าการอ่านโดยคอมไพเลอร์และด้วยเหตุนี้การมีการประชุมที่เคารพนับถือร่วมกันก็ช่วยได้มาก
  • แต่ให้พลังทั้งหมดที่เต็มใจจะจัดการ ทั้ง Lua และ Metalua ไม่ได้เป็นทาสและวินัยที่บังคับดังนั้นถ้าคุณรู้ว่าคุณกำลังทำอะไรอยู่ภาษาก็จะไม่เข้าทาง
  • ทำให้ชัดเจนเมื่อมีสิ่งที่น่าสนใจเกิดขึ้น: การดำเนินการเมตาทั้งหมดเกิดขึ้นระหว่าง + + + + + + ...

ตัวอย่างของแอปพลิเคชั่นคือการใช้การจับคู่รูปแบบที่คล้าย ML

ดูเพิ่มเติมที่: http://lua-users.org/wiki/MetaLua


แม้ว่า Lua ไม่ใช่ Lisp แน่นอนรายการของคุณ "แตกต่าง" ค่อนข้างน่าสงสัย (เฉพาะรายการที่เกี่ยวข้องคือรายการสุดท้าย) และแน่นอนชุมชนเสียงกระเพื่อมจะไม่เห็นด้วยกับการพูดจาโผงผางว่าไม่ควรเขียน / ใช้แมโคร 95% ของเวลา - วิธีเสียงกระเพื่อมโน้มเอียงไปทางบางสิ่งบางอย่างเช่น 95% ของเวลาที่ใช้และการเขียน DSL ด้านบนของมาโคร หลักสูตร
SK-logic

2

หากคุณกำลังมองหาภาษาที่ขยายได้คุณควรดูที่สมอลล์ทอล์ค

ใน Smalltalk วิธีเดียวที่จะตั้งโปรแกรมคือการขยายภาษาจริง ไม่มีความแตกต่างระหว่าง IDE, ไลบรารีหรือภาษาเอง พวกเขาต่างก็ยึดติดอยู่กับที่ Smalltalk มักถูกอ้างถึงว่าเป็นสภาพแวดล้อมมากกว่าภาษา

คุณไม่ได้เขียนแอปพลิเคชันแบบสแตนด์อะโลนใน Smalltalk คุณขยายสภาพแวดล้อมภาษาแทน

ตรวจสอบhttp://www.world.st/เพื่อรับทรัพยากรและข้อมูลจำนวนมาก

ฉันอยากจะแนะนำ Pharo ให้เป็นภาษาถิ่นสู่โลกของ Smalltalk: http://pharo-project.org

หวังว่ามันจะช่วย!


1
ฟังดูน่าสนใจ.
Thomas Eding

1

มีเครื่องมือที่อนุญาตให้สร้างภาษาที่กำหนดเองโดยไม่ต้องเขียนคอมไพเลอร์ทั้งหมดตั้งแต่ต้น ตัวอย่างเช่นมีSpoofaxซึ่งเป็นเครื่องมือในการแปลงรหัส: คุณใส่ไวยากรณ์การป้อนข้อมูลและกฎการแปลง (เขียนด้วยวิธีการประกาศระดับสูงมาก) จากนั้นคุณสามารถสร้างซอร์สโค้ด Java (หรือภาษาอื่น ๆ ถ้าคุณใส่ใจเพียงพอ) จากภาษาที่กำหนดเองที่ออกแบบโดยคุณ

ดังนั้นจึงเป็นไปได้ที่จะใช้ไวยากรณ์ของภาษา X, กำหนดไวยากรณ์ของภาษา X '(X ด้วยส่วนขยายที่กำหนดเองของคุณ) และการแปลง X' → X และ Spoofax จะสร้างคอมไพเลอร์ X '→ X

ปัจจุบันถ้าฉันเข้าใจถูกต้องการสนับสนุนที่ดีที่สุดสำหรับ Java ด้วยการสนับสนุน C # ที่กำลังพัฒนา (หรือดังนั้นฉันได้ยิน) เทคนิคนี้สามารถนำไปใช้กับภาษาใด ๆ ที่มีไวยากรณ์แบบคงที่ (เช่นอาจจะไม่ใช่ Perl )


1

Forthเป็นอีกภาษาที่สามารถขยายได้ การใช้งาน Forth จำนวนมากประกอบด้วยเคอร์เนลขนาดเล็กที่เขียนในแอสเซมเบลอร์หรือ C จากนั้นภาษาที่เหลือจะถูกเขียนใน Forth เอง

นอกจากนี้ยังมีหลายภาษาสแต็คตามที่มีแรงบันดาลใจมาและแบ่งปันคุณลักษณะนี้เช่นปัจจัย


0

Funge-98

อนุญาตให้ใช้คุณลักษณะลายนิ้วมือของ Funge-98 เพื่อให้สามารถปรับโครงสร้างไวยากรณ์และความหมายทั้งหมดของภาษาได้อย่างสมบูรณ์ แต่ถ้าผู้ดำเนินการจัดหากลไกลายนิ้วมือที่อนุญาตให้ผู้ใช้ปรับเปลี่ยนโปรแกรมโดยทางโปรแกรม ถ้าเป็นเช่นนั้นเราสามารถทำให้ไฟล์ที่เหลือ (หรือส่วนใด ๆ ของไฟล์) ทำหน้าที่เป็น C ++ หรือ Lisp (หรืออะไรก็ตามที่เขาต้องการ)

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


ว่าทำไมคุณไม่โพสต์นี้แยกกันแทนการเพิ่มลงในคำตอบก่อนหน้าของคุณ ?
ริ้น

1
เพราะ Funge ไม่มีส่วนเกี่ยวข้องกับ Haskell
Thomas Eding

-1

เพื่อให้ได้สิ่งที่คุณต้องการคุณต้องใช้วงเล็บและการขาดไวยากรณ์ ภาษาที่ใช้งานไวยากรณ์บางภาษาอาจเข้ามาใกล้ แต่มันก็ไม่เหมือนกับแมโครจริง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.