2012-05-28 15 views
7

Giả sử tôi muốn kích hoạt macro lược đồ trên một thứ khác ngoài mục đầu tiên trong biểu thức s. Ví dụ, giả sử rằng tôi muốn thay thế define với một trung tố kiểu :=, do đó:Macro lược đồ được kích hoạt bởi từ khóa không phải là đầu của danh sách

(a := 5) -> (define a 5) 
((square x) := (* x x)) -> (define (square x) (* x x)) 

Việc chuyển đổi thực tế có vẻ là khá đơn giản. Bí quyết sẽ nhận được Đề án để tìm các biểu thức := và mở rộng macro. Tôi đã suy nghĩ về xung quanh phần lớn mã sử dụng cú pháp infix với macro chuẩn, có thể: (with-infix-define expr1 expr2 ...) và có macro tiêu chuẩn đi qua các biểu thức trong phần nội dung của nó và thực hiện bất kỳ biến đổi cần thiết nào. Tôi biết rằng nếu tôi thực hiện phương pháp này, tôi sẽ phải cẩn thận để tránh việc chuyển đổi danh sách thực sự được coi là dữ liệu, chẳng hạn như danh sách được trích dẫn và một số phần nhất định của danh sách được phân phối. Một ví dụ về những gì tôi hình dung:

(with-infix-define 
    ((make-adder n) := (lambda (m) (+ n m))) 

    ((foo) := 
     (add-3 := (make-adder 3)) 
     (add-6 := (make-adder 6)) 
     (let ((a 5) (b 6)) 
      (+ (add-3 a) (add-6 b)))) 

    (display (foo)) 
    (display '(This := should not be transformed)) 

Vì vậy, câu hỏi của tôi là hai lần:

  1. Nếu tôi đi theo con đường with-infix-define, tôi phải xem ra cho bất kỳ trở ngại khác ngoài báo giá và quasiquote?
  2. Tôi cảm thấy hơi giống như tôi đang sáng tạo lại bánh xe. Loại mã đi bộ này có vẻ như chính xác hệ thống mở rộng macro chuẩn nào phải làm - sự khác biệt duy nhất là chúng chỉ xem mục đầu tiên trong danh sách khi quyết định có thực hiện bất kỳ chuyển đổi mã nào hay không. Có cách nào tôi có thể chỉ piggyback trên hệ thống hiện tại?
+0

Có gói chương trình mã dành cho người đi bộ không? Dường như tất cả những gì bạn cần làm là quấn mã trong một macro có, đi bộ mã đó và hoán đổi: = với s-exp trước đó, sau đó xác định macro biểu tượng mà làm cho: = tương đương với xác định. Tuy nhiên, không chắc chắn về mặc định s-exps. –

Trả lời

12
  1. Trước khi tiếp tục với điều này, tốt nhất là để suy nghĩ những điều trên hoàn toàn - IME bạn muốn thường thấy rằng những gì bạn thực sự muốn có một xử lý reader cấp := như một cú pháp ghi vào. Tất nhiên điều đó có nghĩa là nó cũng được nhúng trong các trích dẫn vv, do đó, nó sẽ có vẻ xấu cho bây giờ, nhưng một lần nữa, kinh nghiệm của tôi là bạn cuối cùng nhận ra rằng nó tốt hơn để làm mọi thứ một cách nhất quán.

  2. Để hoàn chỉnh, tôi sẽ đề cập rằng trong vợt có một đọc cú pháp hack cho biểu thức trung tố như: (x . define . 1)đọc như (define x 1). (Và như trên, nó hoạt động ở mọi nơi.)

  3. Nếu không, ý tưởng của bạn về macro bao quanh là điều duy nhất bạn có thể làm. Điều này không làm cho nó hoàn toàn vô vọng mặc dù, bạn có thể có một móc vào expander thực hiện của bạn mà có thể cho phép bạn làm những việc như vậy - ví dụ, Racket có một macro đặc biệt gọi là #%module-begin kết thúc tốt đẹp một mô-đun hoàn chỉnh và #%top-interaction kết thúc tốt đẹp toplevel biểu thức trên REPL. (Cả hai được thêm ngầm trong hai bối cảnh.) Dưới đây là một ví dụ (Tôi đang sử dụng vợt của define-syntax-rule vì đơn giản):

    #lang racket/base 
    
    (provide (except-out (all-from-out racket/base) 
            #%module-begin #%top-interaction) 
         (rename-out [my-module-begin #%module-begin] 
            [my-top-interaction #%top-interaction])) 
    
    (define-syntax infix-def 
        (syntax-rules (:= begin) 
        [(_ (begin E ...)) (begin (infix-def E) ...)] 
        [(_ (x := E ...)) (define x (infix-def E) ...)] 
        [(_ E)    E])) 
    
    (define-syntax-rule (my-module-begin E ...) 
        (#%module-begin (infix-def E) ...)) 
    (define-syntax-rule (my-top-interaction . E) 
        (#%top-interaction . (infix-def E))) 
    

    Nếu tôi đặt điều này trong một tập tin gọi my-lang.rkt, tôi bây giờ có thể sử dụng nó như sau:

    #lang s-exp "my-lang.rkt" 
    (x := 10) 
    ((fib n) := 
    (done? := (<= n 1)) 
    (if done? n (+ (fib (- n 1)) (fib (- n 2))))) 
    (fib x) 
    
  4. Có, bạn cần phải xử lý nhiều thứ. Hai ví dụ ở trên là xử lý các biểu thức begin và các hàm chức năng xử lý. Đây rõ ràng là một danh sách rất một phần - bạn cũng sẽ muốn các cơ quan của lambda, let, v.v.Nhưng điều này vẫn còn tốt hơn so với một số mát xa mù, vì đó là chỉ là không thực tế như bạn không thể thực sự nói trước cách một số đoạn ngẫu nhiên của mã sẽ kết thúc. Như một ví dụ đơn giản, hãy xem xét macro này đơn giản:

    (define-syntax-rule (track E) 
        (begin (eprintf "Evaluating: ~s\n" 'E) 
         E)) 
    (x := 1) 
    
  5. Kết quả cuối cùng của việc này là một giải pháp thích hợp, bạn cần một số cách để trước mở rộng mã, do đó bạn có thể quét và đối phó với vài hình thức cốt lõi được biết đến trong implmenetation của bạn.

  6. Có, tất cả điều này là công việc lặp lại mà các trình tăng kích thước macro làm, nhưng vì bạn đang thay đổi cách mở rộng hoạt động, không có cách nào xung quanh việc này. (Để xem lý do tại sao đó là thay đổi cơ bản, hãy xem xét điều gì đó như (if := 1) - đây có phải là biểu thức có điều kiện hay định nghĩa không? Làm cách nào để bạn quyết định cái nào được ưu tiên?) Vì lý do này, đối với các ngôn ngữ có cú pháp dễ thương như vậy cách tiếp cận là đọc và phân tích cú pháp mã thành các biểu thức S đơn giản, và sau đó để thực hiện ngôn ngữ thực tế sử dụng các hàm và macro đơn giản.

+0

Hmmm ... Tôi có thể thấy giải pháp đề xuất của tôi có thể rất xấu xí rất nhanh ... Nếu tôi quyết định đi theo lộ trình đọc vĩ mô, bạn có biết bất kỳ tài nguyên nào tốt mà tôi có thể tìm hiểu về Đề án (đặc biệt là Racket) -macros? Tôi đã có rất ít kinh nghiệm với họ, nhưng tôi dường như nhớ rằng các chương trình đọc-macro dường như với tôi ít mạnh hơn nhiều so với các trình đọc macro phổ biến Lisp. Cụ thể: tôi có thể viết macro thông thường mở rộng sang định nghĩa macro đọc không? Hay nó sẽ quá trễ vào thời điểm đó để sửa đổi việc đọc? – Ord

+0

Vợt có khả năng phân tích cú pháp đầy đủ (quá nhiều), và các macro đọc dựa trên kiểu đọc CL (cũng không phù hợp ở đây). Thay vào đó, tốt nhất là chỉ cần đọc tệp như thường lệ, sau đó tìm S-exprs trông giống như những gì bạn muốn và chuyển đổi chúng bằng cách di chuyển ': =' sang bên trái. Nhưng có thể thực hiện điều đó với điều '#% module-begin' bằng cách chỉ tạo một vòng lặp đơn giản, thực hiện tương tự và sau đó gửi kết quả đến bình thường - nhưng vòng lặp này phải được thực hiện bằng mã thực, chứ không phải với một 'syntax-rules'. –

+0

Được rồi, vì vậy nếu tôi hiểu giải pháp của bạn một cách chính xác - tôi sử dụng macro 'define-syntax' thông thường giống như ví dụ' my-module-begin' của bạn, và sau đó tôi chỉ xem qua các số s-expr được chuyển vào macro của tôi và biến đổi chúng khi cần thiết thành các biểu thức tiền tố? – Ord

3

Xác định lại define hơi phức tạp. Xem lời giải thích tuyệt vời của @ Eli.

Nếu mặt khác, bạn có nội dung với := để sử dụng set! mọi thứ đơn giản hơn một chút.

Dưới đây là một ví dụ nhỏ:

#lang racket 

(module assignment racket 
    (provide (rename-out [app #%app])) 

    (define-syntax (app stx) 
    (syntax-case stx (:=) 
     [(_ id := expr) 
     (identifier? #'id) 
     (syntax/loc stx (set! id expr))]  
     [(_ . more) 
     (syntax/loc stx (#%app . more))]))) 

(require 'assignment) 

(define x 41) 
(x := (+ x 1)) 
(displayln x) 

Để giữ cho ví dụ vào một tập tin duy nhất, tôi sử dụng các môđun con (có sẵn trong phiên bản phát hành trước của vợt).

+0

Cảm ơn soegaard; nhưng tại sao tôi không thể áp dụng giải pháp của bạn để 'định nghĩa'? Nếu mọi biểu thức được bao bọc hoàn toàn trong ứng dụng #%, điều đó có hiệu quả không? – Ord

+0

Tóm lại: Định nghĩa không phải là biểu thức. Các định nghĩa và biểu thức có thể xảy ra ở những nơi khác nhau. Hãy xem xét phía bên phải của một let ràng buộc ví dụ. – soegaard

+0

Vì vậy, không phải mọi mẫu 'định dạng' nhất thiết phải được bao bọc bằng ứng dụng #%? Hoặc là nó là một số hình thức mà một định nghĩa sẽ không được thích hợp được bọc bằng #% ứng dụng (như trong, tôi giả định, ví dụ của bạn cho phép)? – Ord