質問

Here's a macro definition which constructs a loop with the specified variable name bound to a new line in the specified file each time through the loop:

(defmacro with-string-from-file ((in-string filename
                                   &key (if-does-not-exist :error))
                                   &rest body)
  (let ((working-stream (gensym)))
    `(with-open-file (,working-stream
                       ,filename 
                       :direction :input
                       :external-format charset:iso-8859-1
                       :if-does-not-exist ,if-does-not-exist)
       (let* ((,in-string))
         (when ,working-stream
           (loop while (setf ,in-string (read-line
                                          ,working-stream
                                          nil))
             do ,@body))))))

(princ "test 1:") (terpri)

(with-string-from-file (the-string "/etc/resolv.conf"
                         :if-does-not-exist nil)
  (princ "one line is \"")
  (princ the-string)
  (princ "\"")
  (terpri))

; Test 2 has not been introduced yet.

(princ "test 3:") (terpri)

(with-string-from-file (the-string "/etc/resolv.conf.nonexistent"
                         :if-does-not-exist nil)
  (princ "one line is \"")
  (princ the-string)
  (princ "\"")
  (terpri))

(princ "test 4:") (terpri)

(with-string-from-file (the-string "/etc/resolv.conf.nonexistent")
  (princ "one line is \"")
  (princ the-string)
  (princ "\"")
  (terpri))

The output:

test 1:
one line is "# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)"
one line is "#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN"
one line is "nameserver 192.168.6.1"
one line is "search x441afea5.org"
test 3:
test 4:
*** - OPEN: File #P"/etc/resolv.conf.nonexistent" does not exist

This works whether running the file directly, or compiling it first and then running the compiled file.

Now I attempt to add a feature to the macro so I can specify the input character set:

(defmacro with-string-from-file ((in-string filename
                                   &key (if-does-not-exist :error)
                                        (external-format charset:iso-8859-1))
                                   &rest body)
  (let ((working-stream (gensym)))
    `(with-open-file (,working-stream
                       ,filename 
                       :direction :input
                       :external-format ,external-format
                       :if-does-not-exist ,if-does-not-exist)
       (let* ((,in-string))
         (when ,working-stream
           (loop while (setf ,in-string (read-line
                                          ,working-stream
                                          nil))
             do ,@body))))))

(princ "test 1:") (terpri)

(with-string-from-file (the-string "/etc/resolv.conf"
                         :if-does-not-exist nil)
  (princ "one line is \"")
  (princ the-string)
  (princ "\"")
  (terpri))

(princ "test 2:") (terpri)

(with-string-from-file (the-string "/etc/resolv.conf"
                         :external-format charset:utf-8
                         :if-does-not-exist nil)
  (princ "one line is \"")
  (princ the-string)
  (princ "\"")
  (terpri))

(princ "test 3:") (terpri)

(with-string-from-file (the-string "/etc/resolv.conf.nonexistent"
                         :if-does-not-exist nil)
  (princ "one line is \"")
  (princ the-string)
  (princ "\"")
  (terpri))

(princ "test 4:") (terpri)

(with-string-from-file (the-string "/etc/resolv.conf.nonexistent")
  (princ "one line is \"")
  (princ the-string)
  (princ "\"")
  (terpri))

When I run the file directly it works:

test 1:
one line is "# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)"
one line is "#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN"
one line is "nameserver 192.168.6.1"
one line is "search x441afea5.org"
test 2:
one line is "# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)"
one line is "#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN"
one line is "nameserver 192.168.6.1"
one line is "search x441afea5.org"
test 3:
test 4:
*** - OPEN: File #P"/etc/resolv.conf.nonexistent" does not exist

But when I try to compile it, I get this error:

;; Compiling file /u/home/clisp/experiments/y1.lisp ...
*** - PRINT: Despite *PRINT-READABLY*, #<ENCODING CHARSET:ISO-8859-1 :UNIX>
      cannot be printed readably.

How can I get this to compile? I'd like to specify a character set explicitly with charset:, rather than potentially having those character set names conflict with symbols in my main namespace.

役に立ちましたか?

解決

Problem

The problem is that the default argument to the macro should be a symbol, not its value.

Explanation

Using objects as macro default args is okay for self-evaluating objects, like strings, numbers, keywords, but no good with more complex objects.

When you use the macro, it expands into code

... :external-format #<ENCODING CHARSET:ISO-8859-1 :UNIX> ...

and CLISP cannot write it into the fas file (which is just a text file).

Solution

Replace (external-format charset:iso-8859-1) with (external-format 'charset:iso-8859-1).

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top