Exporting anaphoric macros to common lisp packages

I have a problem exporting a macro, it works when it is declared in the same package, but not when it is imported. I am using Emacs, SLIME, Clozure on Windows.

Package file

(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))

      

Tokenizer file

(in-package :tokenizer)

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

      

Parser file

(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")))))

      

Gives compilation errors:

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

      

If I try macro expansion in package: csharp-parser

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

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

      

Now, as I said, if you move the macros to the parser file, it compiles and works fine. But when I try to refactor it into a tokenizer file and export it through the package system it gives these errors because it seems like it is assimilating the character to the calling package. I've tried multiple paths through colons, but can't get it to work.

If anyone could help me, I would be very grateful.

+3


source to share


1 answer


The symbols TOKEN

and PEEK

in the macro are internalized in the package TOKENIZER

, and the code inside COND

uses the symbols interned in the package CSHARP-PARSER

. There are two ways.



  • The extension uses the character interned in the packet where the code resides. This can be done by manually interpolating the symbol in the current package when expanding the macro. For example:

    (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
    
          

    Using (intern (symbol-name 'it))

    instead of simple (intern "IT")

    is a way to avoid problems if lisp doesn't convert characters to uppercase.

  • Whether to use code interned in the tokenizer package. This can be done by exporting the symbol.

    (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
    
          

    The disadvantage is that the user of the macro has to import the symbol, so they cannot use the qualified package name for the macro.

    (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
    
          

+6


source







All Articles