openlang

with-lenient-parse logs the error, then SBCL still drops into the debugger

I wrote a macro so a bad line in a hosts file gets logged and the load carries on. This is the whole thing:

(defmacro with-lenient-parse ((&key (log t)) &body body)
  `(handler-bind ((missing-field
                    (lambda (c)
                      (when ,log
                        (format *error-output* "skipping: ~A~%" c))
                      nil))
                  (bad-port
                    (lambda (c)
                      (when ,log
                        (format *error-output* "warn: ~A~%" c))
                      nil)))
     ,@body))

parse-line has a skip-line restart, but I'm not sure anything ever invokes it. The input is name addr [port], one host per line, and I run it with sh run.sh.

With web-02 (no addr) in the file, I get skipping: line 2: missing addr on stderr. Then SBCL drops into the debugger with that same missing-field error and the script dies. I figured returning from the handler would make parse-line return nil for that line, right? Apparently not.

I deleted the web-02 line to get past it. Now I get warn: line 4: bad port abc, and then a type error in privileged-p saying NIL is not of type NUMBER. That function has nothing to do with parsing. The port condition is a plain signal of a warning subclass, and nothing I wrote treats it as fatal.

Is this two bugs or one misunderstanding of how the handler hooks in? What is the macro actually promising the caller? I'm going from memory of the transcript, so I can't paste the full backtrace.

The conversation continues 2

Follow the reasoning
  1. Permalink

    it's one misunderstanding, i think. a handler-bind handler that returns just declines, so the condition goes on to the next handler, and with none left, to the debugger. nothing returns nil from parse-line. you have to invoke the restart inside the handler: (invoke-restart 'skip-line).

    the bad-port case is the same. signal returns nil, so the port stays nil in privileged-p.

    https://www.lispworks.com/documentation/HyperSpec/Body/m_handle.htm

  2. Permalink

    Small caveat on the restart fix (handy, since the handler runs before unwinding): guard it with find-restart, because a bare invoke-restart errors out when skip-line isn't active.

    (lambda (c)
      (let ((r (find-restart 'skip-line c)))
        (when r (invoke-restart r))))

Other sides of the same question

  1. Empty reductions return the identity

    Reduce over an empty vector. APL answers. Each primitive carries its identity. Zero for plus. One for times and for and. ⌈/⍬…

    Replies 3Latest activity apl
  2. Most of my macros turned into const and static

    Nim makes macros easy to write. That's the problem. Before reaching for macro, check the cheaper tools. A const is evaluated…

    Replies 3Latest activity nim
  3. it, _1 and the block names I still write

    Ruby 3.4 adds it for the block parameter, so users.map { it.name } reads almost like a sentence. I like it better than _1, wh…

    Replies 0Latest activity ruby