如何告诉lisp读取器函数在解析过程中忽略错误

如何告诉lisp读取器函数在解析过程中忽略错误,lisp,common-lisp,token,Lisp,Common Lisp,Token,我需要一种方法来抑制使用read from string解析代码时产生的任何错误消息,以便我可以使用如下方式从Clojure代码中读取: (let* ((string-with-code " '(func UpperCase \"string\")") (brace-pos (position #\( string-with-code)) (end-of-token (+ brace-pos (position #\

我需要一种方法来抑制使用
read from string
解析代码时产生的任何错误消息,以便我可以使用如下方式从
Clojure
代码中读取:

(let* ((string-with-code "  '(func UpperCase \"string\")")
       (brace-pos (position #\( string-with-code))
       (end-of-token (+ brace-pos 
                        (position #\Space (subseq string-with-code brace-pos))))
       (first-token (subseq string-with-code (incf brace-pos) end-of-token)))
  (format t "Found function: '~a'" 
          (read-from-string first-token)))
  ;; ==> Found function: 'func'
它基本上是从字符串中的lisp代码打印函数名。在您尝试将点运算符(
)用作列表中的第一项之前,它工作正常
Clojure
使用
既可以
cons
又可以访问Java包中的类,因此可以使用以下有效代码:

(defmacro chain
  ([x form] `(. ~x ~form))
  ([x form & more] `(chain (. ~x ~form) ~@more)))
将导致错误:

*** - READ from #<INPUT STRING-INPUT-STREAM>: token "." not allowed here
输出:

Found function: 'defmacro'
Found function: '[x'
Found function: 'd'
Found function: '[x'
Found function: 'chain'
;; You'll get the error below if ignore-errors wraps around the function call
;; *** - READ from #<INPUT STRING-INPUT-STREAM>: token "." not allowed here
找到函数:“defmacro”
找到的函数:'[x'
找到函数:“d”
找到的函数:'[x'
已找到函数:“链”
;如果忽略错误围绕函数调用,则会出现以下错误
***-此处不允许从#:标记“”读取

不清楚您在问什么,但忽略错误只是:

CL-USER 37 > (ignore-errors (read-from-string "(. foo bar)"))
NIL
#<CONDITIONS:SIMPLE-READER-ERROR 402000243B>
CL-USER 37>(忽略错误(从字符串“(.foo-bar)”读取)
无
#
如果出现错误,
IGNORE-ERRORS
返回
NIL
,并将条件作为第二个返回值


如果您想要更多的控制,您需要编写一个错误处理程序。

不清楚您在问什么,但忽略错误只是:

CL-USER 37 > (ignore-errors (read-from-string "(. foo bar)"))
NIL
#<CONDITIONS:SIMPLE-READER-ERROR 402000243B>
CL-USER 37>(忽略错误(从字符串“(.foo-bar)”读取)
无
#
如果出现错误,
IGNORE-ERRORS
返回
NIL
,并将条件作为第二个返回值


如果您想要更多的控制,您需要编写一个错误处理程序。

这里是Clojure yacc解析器的开始。这需要您更多的注意来处理特殊的Clojure reader宏,并可能确保一些其他语法方面,但这已经是一个正常的开始:

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun print-args (&rest args) (format nil "~{~a~^ ~}" args) ))

(defun clojure-lexer (stream)
  (let ((digits '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9))
        (translations (make-hash-table)))
    (loop :for (key . value) :in
       '((#\( . oparen)
         (#\) . cparen)
         (#\[ . obracket)
         (#\] . cbracket)
         (#\' . squote)
         (#\` . accent)
         (#\: . colon)
         (#\, . comma)
         (#\@ . at)) :do
       (setf (gethash key translations) value))
    (labels ((%reverse-coerce (collected)
               (coerce (nreverse collected) 'string))
             (%read-token ()
               (loop
                  :with collected := nil
                  :and stringp := nil
                  :and commentp := nil
                  :for token := (read-char stream nil nil) :do
                  (cond
                    ((null token)
                     (return (and collected (%reverse-coerce collected))))
                    ((char= token #\;)
                     (push token collected)
                     (setf commentp t))
                    ((char= token #\")
                     (if commentp
                         (push token collected)
                         (if stringp
                             (progn
                               (push token collected)
                               (return (%reverse-coerce collected)))
                             (if collected
                                 (progn
                                   (unread-char token)
                                   (return (%reverse-coerce collected)))
                                 (progn
                                   (push token collected)
                                   (setf stringp t))))))
                    ((gethash token translations)
                     (if (or stringp commentp)
                         (push token collected)
                         (if collected
                             (progn
                               (unread-char token stream)
                               (return (%reverse-coerce collected)))
                             (return (gethash token translations)))))
                    ((member token '(#\Newline #\Rubout))
                     (if commentp
                         (return (and collected (%reverse-coerce collected)))
                         (if stringp
                             (push token collected)
                             (and collected (return (%reverse-coerce collected))))))
                    ((member token '(#\Space #\Tab))
                     (if (or stringp commentp)
                         (push token collected)
                         (and collected (return (%reverse-coerce collected)))))
                    (t (push token collected))))))
      (lambda ()
        (let* ((key (%read-token))
               (value (or (gethash key translations) key)))
          (if (null key)
              (values nil nil)
              (let ((terminal
                     (cond
                       ((member key '(oparen cparen squote colon accent
                                      comma at obracket cbracket))
                        key)
                       ((or (member (char key 0) digits)
                            (and (char= (char key 0) #\-)
                                 (> (length key) 1)
                                 (member (char key 1) digits)))
                        'number)
                       ((char= (char key 0) #\") 'string)
                       ((char= (char key 0) #\;) 'comment)
                       (t 'id))))
                (values terminal value))))))))

(yacc:define-parser *clojure-parser*
  (:start-symbol exp)
  (:terminals (id oparen cparen squote colon accent
                  comma at obracket cbracket string number))

  (exp
   (oparen id exp-list cparen #'print-args)
   (oparen id cparen #'print-args)
   (obracket exp-list cbracket #'print-args)
   (obracket cbracket #'print-args)
   (comment #'print-args)
   (accented-exp #'print-args)
   (quoted-exp #'print-args)
   (term #'print-args))

  (term id string number)
  (quoted-exp (quote exp))
  (accented-exp (accent exp) (accent at exp))
  (exp-list (exp exp-list) exp))

(defun parse-clojure (string)
  (yacc:parse-with-lexer
   (clojure-lexer (make-string-input-stream string)) *clojure-parser*))

(parse-clojure
 "(defn str-invoke [instance method-str & args]
            (clojure.lang.Reflector/invokeInstanceMethod 
                \"instance\" 123 
                method-str 
                (to-array args)))")
结果:

;; "OPAREN defn (str-invoke
;;              (OBRACKET (instance (method-str (& args))) CBRACKET
;;               OPAREN clojure.lang.Reflector/invokeInstanceMethod (\"instance\"
;;                                                     (123
;;                                                      (method-str
;;                                                       OPAREN to-array args CPAREN))) CPAREN)) CPAREN"

下面是上述语法的BNF(并不是说它是Clojure语法,它只反映了上述Lisp代码):

exp::='('id exp list')'
|“('id')”
|“['经验列表']”
| '[' ']'
|“;”/[^\n]*/
|重音exp
|报价exp
|术语
术语::=id |'“'/[^”]*/'“'|/-?[0-9][^\s]+/
引用的exp::='\''exp
重音exp::='`exp|'''''@'exp
exp list::=exp exp list | exp
id::=/[^()[\]:,`@']+/

为简单起见,某些部分以正则表达式的形式给出,这些部分由
/

分隔。这是Clojure yacc解析器的一个开始。这需要您更加注意处理特殊的Clojure reader宏,并可能确保某些其他语法方面,但这已经是一个正常的开始:

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun print-args (&rest args) (format nil "~{~a~^ ~}" args) ))

(defun clojure-lexer (stream)
  (let ((digits '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9))
        (translations (make-hash-table)))
    (loop :for (key . value) :in
       '((#\( . oparen)
         (#\) . cparen)
         (#\[ . obracket)
         (#\] . cbracket)
         (#\' . squote)
         (#\` . accent)
         (#\: . colon)
         (#\, . comma)
         (#\@ . at)) :do
       (setf (gethash key translations) value))
    (labels ((%reverse-coerce (collected)
               (coerce (nreverse collected) 'string))
             (%read-token ()
               (loop
                  :with collected := nil
                  :and stringp := nil
                  :and commentp := nil
                  :for token := (read-char stream nil nil) :do
                  (cond
                    ((null token)
                     (return (and collected (%reverse-coerce collected))))
                    ((char= token #\;)
                     (push token collected)
                     (setf commentp t))
                    ((char= token #\")
                     (if commentp
                         (push token collected)
                         (if stringp
                             (progn
                               (push token collected)
                               (return (%reverse-coerce collected)))
                             (if collected
                                 (progn
                                   (unread-char token)
                                   (return (%reverse-coerce collected)))
                                 (progn
                                   (push token collected)
                                   (setf stringp t))))))
                    ((gethash token translations)
                     (if (or stringp commentp)
                         (push token collected)
                         (if collected
                             (progn
                               (unread-char token stream)
                               (return (%reverse-coerce collected)))
                             (return (gethash token translations)))))
                    ((member token '(#\Newline #\Rubout))
                     (if commentp
                         (return (and collected (%reverse-coerce collected)))
                         (if stringp
                             (push token collected)
                             (and collected (return (%reverse-coerce collected))))))
                    ((member token '(#\Space #\Tab))
                     (if (or stringp commentp)
                         (push token collected)
                         (and collected (return (%reverse-coerce collected)))))
                    (t (push token collected))))))
      (lambda ()
        (let* ((key (%read-token))
               (value (or (gethash key translations) key)))
          (if (null key)
              (values nil nil)
              (let ((terminal
                     (cond
                       ((member key '(oparen cparen squote colon accent
                                      comma at obracket cbracket))
                        key)
                       ((or (member (char key 0) digits)
                            (and (char= (char key 0) #\-)
                                 (> (length key) 1)
                                 (member (char key 1) digits)))
                        'number)
                       ((char= (char key 0) #\") 'string)
                       ((char= (char key 0) #\;) 'comment)
                       (t 'id))))
                (values terminal value))))))))

(yacc:define-parser *clojure-parser*
  (:start-symbol exp)
  (:terminals (id oparen cparen squote colon accent
                  comma at obracket cbracket string number))

  (exp
   (oparen id exp-list cparen #'print-args)
   (oparen id cparen #'print-args)
   (obracket exp-list cbracket #'print-args)
   (obracket cbracket #'print-args)
   (comment #'print-args)
   (accented-exp #'print-args)
   (quoted-exp #'print-args)
   (term #'print-args))

  (term id string number)
  (quoted-exp (quote exp))
  (accented-exp (accent exp) (accent at exp))
  (exp-list (exp exp-list) exp))

(defun parse-clojure (string)
  (yacc:parse-with-lexer
   (clojure-lexer (make-string-input-stream string)) *clojure-parser*))

(parse-clojure
 "(defn str-invoke [instance method-str & args]
            (clojure.lang.Reflector/invokeInstanceMethod 
                \"instance\" 123 
                method-str 
                (to-array args)))")
结果:

;; "OPAREN defn (str-invoke
;;              (OBRACKET (instance (method-str (& args))) CBRACKET
;;               OPAREN clojure.lang.Reflector/invokeInstanceMethod (\"instance\"
;;                                                     (123
;;                                                      (method-str
;;                                                       OPAREN to-array args CPAREN))) CPAREN)) CPAREN"

下面是上述语法的BNF(并不是说它是Clojure语法,它只反映了上述Lisp代码):

exp::='('id exp list')'
|“('id')”
|“['经验列表']”
| '[' ']'
|“;”/[^\n]*/
|重音exp
|报价exp
|术语
术语::=id |'“'/[^”]*/'“'|/-?[0-9][^\s]+/
引用的exp::='\''exp
重音exp::='`exp|'''''@'exp
exp list::=exp exp list | exp
id::=/[^()[\]:,`@']+/

为了简单起见,有些部分是以正则表达式的形式给出的,它们由
/

分隔,它是什么意思“忽略”?它应该做什么有用的事情吗?只返回而不返回结果吗?Lisp的语法非常简单,但以您的方式解析它…嗯,这不是一个好主意。我建议您研究
cl yacc
这将使您的生活更加轻松,而且,作为一个副作用,还将解决点字符的这一特殊问题。使用通用Lisp读取器解析Clojure代码并不是一个好主意。一种方法是重新编程读取器,使其接受点等符号和类似的东西。您不需要编写Clojure解析器,但需要编写一个它能读懂Clojure的s表达式。@wvxvw,一想到要冒险进入
yacc
,我就不寒而栗。为了解决这个问题,学习一门新语言可能不值得……这不是一个完整的程序。编译它时,我会收到
标记结束
第一个标记
的未定义变量警告,以及样式警告ng表示从未使用过
start pos
。它是什么意思“忽略”?它应该做什么有用的事情吗?只返回而不返回结果吗?Lisp的语法非常简单,但用这种方式解析它……嗯,这不是一个好主意。我建议您研究
cl yacc
。这将使您的生活更加轻松,并且,作为一个副作用,会导致also解决点字符的一个特殊问题。用普通的Lisp读取器解析Clojure代码不是一个好主意。一种方法是重新编程读取器,使其接受点作为符号或类似的东西。您不需要编写Clojure解析器,但它可以读Clojure的s表达式。@wvxvw,我对t感到不寒而栗想尝试一下
yacc
。为了解决这个问题,学习一门新语言可能是不值得的……这不是一个完整的程序。当我编译它时,我得到了
标记结束
第一个标记
的未定义变量警告,以及从未使用过
开始位置
的样式警告。对此感到抱歉。我的意思是在不告诉我错误的情况下运行程序时忽略错误。我已尝试使用程序忽略错误(请参见编辑)它会抑制所有输出。我希望它打印找到的函数,当它遇到
时,它应该跳过它。有点像一个try-catch块。我粗略地看了一下hyperspec中的
处理程序案例
,它需要错误名称。这个错误似乎没有。是的,重新编程读取器是我的边界。我不是一个很好的lisp程序员。我通过将
忽略错误
块带到读取字符串的位置,使它按照我想要的方式工作。我现在只需要使用它。对此感到抱歉。我的意思是在运行程序时忽略,而不告诉我错误。我尝试了