src/final.lisp: Improve `test-parser' for testing error reporting.
[sod] / src / final.lisp
index 071ebc9..e7a3eb4 100644 (file)
                   :if-does-not-exist :create)
     (output-module (read-module path) reason out)))
 
+(export 'test-parse-c-type)
+(defun test-parse-c-type (string)
+  "Parse STRING as a C type, with optional kernel, and show the results."
+  (with-input-from-string (in string)
+    (let* ((*module-type-map* (make-hash-table))
+          (charscan (make-instance 'charbuf-scanner
+                                   :stream in
+                                   :filename "<string>"))
+          (tokscan (make-instance 'sod-token-scanner
+                                  :char-scanner charscan
+                                  :filename "<string>")))
+      (with-parser-context (token-scanner-context :scanner tokscan)
+       (multiple-value-bind (value winp consumedp)
+           (parse (seq ((decls (parse-c-type tokscan))
+                        (type (parse-declarator tokscan decls :abstractp t))
+                        :eof)
+                    type))
+         (declare (ignore consumedp))
+         (if winp
+             (values t (car value) (cdr value)
+                     (princ-to-string (car value)))
+             (values nil value)))))))
+
 (export 'test-parser)
-(defmacro test-parser ((scanner &key) parser input)
+(defmacro test-parser ((scanner &key backtrace) parser input)
   "Convenient macro for testing parsers at the REPL.
 
    This is a macro so that the parser can use the fancy syntax.  The name
    SCANNER is bound to a `sod-token-scanner' reading tokens from the INPUT
-   string.  Then the PARSER is invoked and three values are returned: a
-   `successp' flag indicating whether the parser succeeded; the result,
-   output or error indicator, of the parser; and a list consisting of the
-   lookahead token type and value, and a string containing the untokenized
-   remaining input."
+   string.  Then the PARSER is invoked and three values are returned: the
+   result of the parse, or `nil' if the main parse failed; a list containing
+   the number of errors and warnings (respectively) reported during the
+   parse; and a list consisting of the lookahead token type and value, and a
+   string containing the untokenized remaining input.
+
+   If BACKTRACE is nil (the default) then leave errors to the calling
+   environment to sort out (e.g., by entering the Lisp debugger); otherwise,
+   catch and report them as they happen so that you can test error recovery
+   strategies."
   (once-only (input)
-    (with-gensyms (char-scanner value winp consumedp where)
-      `(let* ((,char-scanner (make-string-scanner ,input))
-             (,scanner (make-instance 'sod-token-scanner
-                                      :char-scanner ,char-scanner
-                                      :filename "<test-input>")))
+    (with-gensyms (char-scanner value winp body consumedp where nerror nwarn)
+      `(let ((,char-scanner nil) (,scanner nil))
         (with-parser-context (token-scanner-context :scanner ,scanner)
-          (multiple-value-bind (,value ,winp ,consumedp) (parse ,parser)
-            (declare (ignore ,consumedp))
+          (multiple-value-bind (,value ,nerror ,nwarn)
+              (flet ((,body ()
+                       (setf ,char-scanner (make-string-scanner ,input)
+                             ,scanner (make-instance
+                                       'sod-token-scanner
+                                       :char-scanner ,char-scanner))
+                       (multiple-value-bind (,value ,winp ,consumedp)
+                           (parse ,parser)
+                         (declare (ignore ,consumedp))
+                         (cond (,winp ,value)
+                               (t (syntax-error ,scanner ,value)
+                                  nil)))))
+                (if ,backtrace (,body)
+                    (count-and-report-errors ()
+                      (with-default-error-location (,scanner)
+                        (,body)))))
             (let ((,where (scanner-capture-place ,char-scanner)))
-              (values ,winp ,value
-                      (list (token-type ,scanner) (token-value ,scanner)
-                            (subseq ,input ,where))))))))))
+              (values ,value
+                      (list ,nerror ,nwarn)
+                      (and ,scanner (list (token-type ,scanner)
+                                          (token-value ,scanner)
+                                          (subseq ,input ,where)))))))))))
 
 ;;;--------------------------------------------------------------------------
 ;;; Calisthenics.