2017-05-26 52 views
3

我在導出宏時遇到問題,它在同一包中聲明時有效,但在導入時不起作用。我在Windows上使用Emacs,SLIME,Clozure。導出常見lisp包中的指代宏

包文件

(defpackage :tokenizer 
    (:use :common-lisp) 
    (:export :tokenize-with-symbols 
     :current-token 
     :advanze-token 
     :peek-token 
     :with-token-and-peek 
     :with-token)) 

(defpackage :csharp-parser 
    (:use :common-lisp :tokenizer) 
    (:import-from :tokenizer :with-token-and-peek :with-token)) 

標記生成文件

(in-package :tokenizer) 

(defmacro with-token-and-peek (&body body) 
    `(let ((token (current-token tokenizer)) 
    (peek (peek-token tokenizer))) 
    ,@body)) 

分析器文件

(in-package :csharp-parser) 

(defun expression (tokenizer node-stack) 
    (with-token-and-peek 
    (cond ((is-number? token) (make-value-node "number" token)) 
     ((is-bool? token) (make-value-node "bool" token)) 
     ((is-identifier? token peek) (make-identifier-node tokenizer node-stack)) 
     (t (make-ast-node :identifier "bla"))))) 

給出的編譯錯誤:

csharpParser.lisp:265:3: 
    warning: Undeclared free variable TOKENIZER::TOKENIZER (2 references) 
      style-warning: Unused lexical variable TOKENIZER::PEEK 
      style-warning: Unused lexical variable TOKENIZER::TOKEN 
csharpParser.lisp:266:14: 
    warning: Undeclared free variable TOKEN 
etc etc etc 

如果我嘗試在包宏擴展:csharp的解析器

(macroexpand-1 '(with-token-and-peek tok)) 

(LET ((TOKENIZER::TOKEN (CURRENT-TOKEN TOKENIZER::TOKENIZER)) 
     (TOKENIZER::PEEK (PEEK-TOKEN TOKENIZER::TOKENIZER))) 
    TOK) 
T 

現在想,如果我移動宏解析器文件我說,它編譯和完美的作品。但是當我嘗試將其重構到tokenizer文件並通過包系統導出時,它會給出這些錯誤,因爲它似乎將符號內部化爲調用包。我已經通過冒號嘗試了多種方式,但無法使其運行。

如果有人能幫助我,我會非常感激。

回答

6

符號TOKENPEEK在宏被扣留在TOKENIZER封裝,而COND內的代碼使用在CSHARP-PARSER包實習符號。有兩種方法可以解決這個問題。

  1. 擴展是否在代碼所在的包中使用了一個符號。這可以通過在擴展宏時在當前包中手動實習符號來完成。例如:

    (defpackage #:foo 
        (:use #:cl) 
        (:export #:aif)) 
    
    (in-package #:foo) 
    
    (defmacro aif (test then &optional else) 
        (let ((it (intern (symbol-name 'it)))) 
        `(let ((,it ,test)) 
         (if ,it ,then ,else)))) 
    
    (in-package :cl-user) 
    (use-package :foo) 
    (aif (+ 3 3) it) ;=> 6 
    

    使用(intern (symbol-name 'it)),而不是僅僅(intern "IT")是避免問題的情況下,口齒不清的符號不能轉換爲大寫的方式。

  2. 讓代碼使用標記器包中實現的符號。這可以通過導出符號來完成。

    (defpackage #:foo 
        (:use #:cl) 
        (:export #:aif 
          #:it)) 
    
    (in-package #:foo) 
    
    (defmacro aif (test then &optional else) 
        `(let ((it ,test)) 
        (if it ,then ,else))) 
    
    (in-package :cl-user) 
    (use-package :foo) 
    (aif (+ 3 3) it) ;=> 6 
    

    的缺點是宏的用戶必須導入符號,所以他們不能爲宏使用包限定名稱。

    (defpackage #:foo 
        (:use #:cl) 
        (:export #:aif 
          #:it)) 
    
    (in-package #:foo) 
    
    (defmacro aif (test then &optional else) 
        `(let ((it ,test)) 
        (if it ,then ,else))) 
    
    (in-package :cl-user) 
    (foo:aif (+ 3 3) it) ; Fails 
    
+0

謝謝!第一個解決方案運行良好,編譯! – HeinzGuderian