lisp-in-rs-macros

Rust के declarative macro में पूरी तरह काम करने वाला एक सरल lexical scope Lisp interpreter। lisp! macro कोड द्वारा गणना किए गए Lisp मानों को expand करता है और उन्हें string में बदल देता है। उदाहरण के लिए, lisp!(CAR (CONS (QUOTE A) (QUOTE (B)))) string "A" में expand होता है, और सारी गणना compile time पर होती है जब rustc macro को expand करता है.

यह क्यों महत्वपूर्ण है

Rust macro में लिखा गया Lisp interpreter काफ़ी दिलचस्प है। साथ ही, यह 250 लाइनों से कम में लिखा गया है, इसलिए काफ़ी संक्षिप्त भी है.

उदाहरण

let output = lisp!(CAR (LIST (QUOTE A) (QUOTE B) (QUOTE C)));
assert_eq!(output, "A");

lisp!(PROGN
  (DEFINE message (LAMBDA () (QUOTE "hello there")))
  (DISPLAY (message))
  (DEFINE NOT (LAMBDA (X) (COND (X NIL) (TRUE TRUE))))
  (DISPLAY (NOT NIL))
); // "hello there" और "TRUE" प्रिंट करता है

एक और मज़ेदार उदाहरण quine है:

lisp!((LAMBDA (s) (LIST s (LIST (QUOTE QUOTE) s))) (QUOTE (LAMBDA (s) (LIST s (LIST (QUOTE QUOTE) s)))));

यह कोड खुद का ही मूल्यांकन करता है.

Recursion

यह Lisp फिलहाल recursion के explicit रूप को support नहीं करता। सौभाग्य से, explicit recursion की ज़रूरत नहीं है; सिर्फ lambda काफ़ी है। दो lists को जोड़ने वाला एक सरल function लिखा जा सकता है:

lisp!(PROGN
  (DEFINE append
    (LAMBDA (self X Y)
      (COND
        ((EQ X NIL) Y)
        (TRUE (CONS (CAR X) (self self (CDR X) Y)))
      )))
  (append append (QUOTE (A B)) (QUOTE (C D)))
);

इसका परिणाम "(A B C D)" है। append function body में append का उल्लेख नहीं करता, फिर भी इसे recursively call किया जा सकता है.

उपयोग के समय ध्यान देने वाली बातें

  • lisp! macro केवल एक single expression evaluate करता है। कई expressions evaluate करने के लिए (PROGN expr1 expr2 expr3) का उपयोग करना होगा.
  • DISPLAY form एक single expression evaluate करने के बाद println!("{}", stringify!(...)) statement generate करता है और tokens का string version प्रिंट करता है.
  • खाली list self-evaluating नहीं है; खाली list value पाने के लिए NIL या (QUOTE ()) का उपयोग करना होगा.
  • dotted list support नहीं है, और cons यह मानकर चलता है कि अंतिम argument एक list है.
  • define form कहीं भी इस्तेमाल किया जा सकता है और empty list के रूप में evaluate होता है, लेकिन recursion support नहीं करता.
  • TRUE एकमात्र self-evaluating atom है, function नहीं.

समर्थित forms

  • DEFINE
  • QUOTE
  • LAMBDA
  • LET
  • PROGN
  • CAR
  • CDR
  • CONS
  • LIST
  • EQ
  • ATOM
  • APPLY

Meta-circular interpreter

नीचे Lisp में लिखा गया एक Lisp interpreter है:

lisp!(PROGN
  (DEFINE Y2
    (LAMBDA (h)
      ((LAMBDA (x) (h (LAMBDA (a b) ((x x) a b))))
        (LAMBDA (x) (h (LAMBDA (a b) ((x x) a b)))))))
  (DEFINE CADR (LAMBDA (X) (CAR (CDR X))))
  (DEFINE CAAR (LAMBDA (X) (CAR (CAR X))))
  (DEFINE CADAR (LAMBDA (X) (CAR (CDR (CAR X)))))
  (DEFINE CADDR (LAMBDA (X) (CAR (CDR (CDR X)))))
  (DEFINE CADDAR (LAMBDA (X) (CAR (CDR (CDR (CAR X))))))
  (DEFINE CAADAR (LAMBDA (X) (CAR (CAR (CDR (CAR X))))))
  (DEFINE ASSOC (Y2 (LAMBDA (ASSOC) (LAMBDA (X ENV)
    (IF (EQ (CAAR ENV) X) (CADAR ENV) (ASSOC X (CDR ENV))))))
  )
  (DEFINE eval (Y2 (LAMBDA (EVAL) (LAMBDA (E A)
    (COND
      ((ATOM E) (ASSOC E A))
      ((ATOM (CAR E))
        (COND
          ((EQ (CAR E) (QUOTE quote)) (CADR E))
          ((EQ (CAR E) (QUOTE atom)) (ATOM (EVAL (CADR E) A)))
          ((EQ (CAR E) (QUOTE car)) (CAR (EVAL (CADR E) A)))
          ((EQ (CAR E) (QUOTE cdr)) (CDR (EVAL (CADR E) A)))
          ((EQ (CAR E) (QUOTE equal)) (EQ (EVAL (CADR E) A) (EVAL (CADDR E) A)))
          ((EQ (CAR E) (QUOTE cons)) (CONS (EVAL (CADR E) A) (EVAL (CADDR E) A)))
          (TRUE (EVAL (CONS (ASSOC (CAR E) A) (CDR E)) A))
        ))
      ((EQ (CAAR E) (QUOTE lambda)) (EVAL (CADDAR E) (CONS (LIST (CAADAR E) (EVAL (CADR E) A)) A)))
    ))))
  )
  (eval (QUOTE (quote (A))) NIL)
  (eval (QUOTE ((lambda (X) X) (quote a))) NIL)
);

यह कोड काम करता है, लेकिन interpreter में ((lambda (X) X) (quote a)) को evaluate करने की कोशिश करने पर 30 सेकंड से ज़्यादा समय लगता है और दस लाख से अधिक tokens generate होते हैं। explicit y combinator के साथ recursion यहाँ efficient नहीं है। इसे ठीक करने के लिए explicit recursion primitive जोड़ना होगा। meta-circular evaluator लिखने के तरीक़े की शानदार व्याख्या के लिए Paul Graham की "Roots of Lisp" देखें.

तकनीकी विवरण

EXPLANATION.md देखें। यह macro SECD machine को simulate करता है, जो lambda calculus expressions को evaluate करने के लिए एक सरल stack-based abstract machine है.

बेहतरीन संसाधन

  • Peter Henderson की "Functional Programming: Application and Implementation"
  • Mads Sig Ager आदि की "A functional correspondence between evaluators and abstract machines." (2003)
  • Simon Peyton Jones की "The Implementation of Functional Programming Languages"
  • Matt Might के ब्लॉग (https://matt.might.net) पर Lisp से जुड़ी सभी पोस्ट

TODO

  • letrec जोड़ना
  • recursive definitions जोड़ना

GN⁺ का सारांश

  • Rust macro में लिखा गया Lisp interpreter काफ़ी दिलचस्प है और संक्षिप्त कोड में शक्तिशाली capabilities देता है.
  • यह recursion को explicit रूप में support नहीं करता, लेकिन lambda के माध्यम से recursion इम्प्लीमेंट की जा सकती है.
  • Meta-circular interpreter efficient नहीं है, इसलिए explicit recursion primitive जोड़ने की ज़रूरत है.
  • Paul Graham की "Roots of Lisp" meta-circular evaluator को समझने के लिए शानदार संसाधन है.
  • समान capabilities देने वाले दूसरे projects के रूप में Scheme interpreter या Lisp के अन्य variants की सिफारिश की जाती है.

अभी कोई टिप्पणी नहीं है.

अभी कोई टिप्पणी नहीं है.