4

I'm trying to write a Flycheck checker definition based on Hy, using the API here. As part of that, I have to define a pattern for error matching. Now, Hy gives quite a large error spew, for example:

Traceback (most recent call last):
  File "/usr/bin/hy", line 9, in <module>
    load_entry_point('hy==0.10.1', 'console_scripts', 'hy')()
  File "/usr/lib/python3.4/site-packages/hy/cmdline.py", line 305, in hy_main
    sys.exit(cmdline_handler("hy", sys.argv))
  File "/usr/lib/python3.4/site-packages/hy/cmdline.py", line 293, in cmdline_handler
    return run_file(options.args[0])
  File "/usr/lib/python3.4/site-packages/hy/cmdline.py", line 194, in run_file
    import_file_to_module("__main__", filename)
  File "/usr/lib/python3.4/site-packages/hy/importer.py", line 74, in import_file_to_module
    eval(ast_compile(_ast, fpath, "exec"), mod.__dict__)
  File "helloworld.hy", line 1, in <module>
    (prin "Hello world")
NameError: name 'prin' is not defined

The only part of interest to the checker is the very last bit:

  File "helloworld.hy", line 1, in <module>
    (prin "Hello world")
NameError: name 'prin' is not defined

So I need to write a :error-patters form that will match the following exactly:

  1. A tab character
  2. The string File "
  3. The name of my file (Flycheck does this using (file-name))
  4. The string ", line (with a terminating space)
  5. The line number (Flycheck does this using line)
  6. The string , in <module>\n\t\t (where \n\t\t is a newline followed by two tabs)
  7. Whatever follows until the next newline (including that newline)
  8. Whatever remains is the error message (which Flycheck matches with (message))

So far, I have this:

:error-patterns ((error line-start "\tFile \"" (file-name) "\", line " line ", in <module>\n\t\t")) ;something more needs to go here

This gets me as far as step 6, but I'm not sure what to do for step 7, as I don't know rx at all. Any and all help would be very much appreciated, both by me and the fledgling Hy community.

6
  • 2
    Will ".*\n" work?
    – mbork
    Nov 20, 2014 at 21:40
  • why do you call it :error-patters instead of :error-patterns?
    – wdkrnls
    Nov 21, 2014 at 1:26
  • @wdkrnls: Because I can't spell. Fixing now.
    – Koz Ross
    Nov 21, 2014 at 3:07
  • Consider using re-builder (comes with Emacs, see C-h f re-builder RET) or helm-regexp to build the regular expression. Nov 21, 2014 at 3:15
  • 2
    Please write a dedicated program that statically analyzes the Hy code for (stylistic) issues and let it write parseable output to STDOUT instead of running the program and waiting for errors.
    – wasamasa
    Nov 21, 2014 at 15:33

1 Answer 1

3

I'm not sure whether you can really parse that with regular expressions only. I think you are better off with a custom :error-parser, i.e. a function that parses this error.

This function would insert the output into a temporary buffer, search forwards to search for occurrences of Traceback …, skip over all indented lines to extract the error message, and then search backwards to the first occurrence of File "...":

(defun flycheck-parse-hy-traceback (output _checker _buffer)
  (with-temp-buffer
    ;; Insert output of Hy
    (insert output)
    (goto-char (point-min))

    (let (errors)
      ;; Search forward for the next traceback
      (while (re-search-forward "^Traceback" nil 'noerror)
        ;; Goto to the first line of the backtrace,…
        (forward-line 1)
        ;; …, and skip over indented lines, that is all lines of the backtrace up
        ;; to the error message
        (while (looking-at (rx line-start blank))
          (forward-line 1))
        ;; Extract the message
        (let ((message (buffer-substring-no-properties
                        (line-beginning-position) (line-end-position)))
              (end (point)))
          ;; Search backwards for the last error location
          (when (re-search-backward (rx line-start
                                        (one-or-more blank)
                                        "File \""
                                        (group-n 1 (one-or-more (not (any "\""))))
                                        "\", line "
                                        (group-n 2 (one-or-more digit)))
                                    nil 'noerror)
            ;; Extract the file name and the line, and create a new error object
            (push (flycheck-error-new :filename (match-string 1)
                                      :line (flycheck-string-to-number-safe
                                             (match-string 2))
                                      :level 'error
                                      :message message)
                  errors))
          ;; Move out of this error to continue searching for other errors
          (goto-char end)))
      errors)))

Then use :error-parser flycheck-parse-hy-traceback instead of :error-patterns in the definition of your syntax checker.

Besides, a word of warning: It looks like your syntax checker runs Hy files to check them. That's a bad idea, for many reasons, and you should really not do that, if there's any other way.

Disclaimer: I'm the Flycheck maintainer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.