An actual running implementation, which makes code that compiles.
[sod] / src / parser / floc-proto.lisp
index 9e246ab..ca5aaee 100644 (file)
          file-location-filename file-location-line file-location-column))
 (defstruct (file-location
             (:constructor make-file-location
-                          (%filename line column
+                          (%filename &optional line column
                            &aux (filename
                                  (etypecase %filename
                                    ((or string null) %filename)
                                    (pathname (namestring %filename)))))))
   "A simple structure containing file location information.
 
-   Construct using MAKE-FILE-LOCATION; the main useful function is
-   ERROR-FILE-LOCATION."
+   Construct using `make-file-location'; the main useful function is
+   `error-file-location'."
   (filename nil :type (or string null) :read-only t)
   (line nil :type (or fixnum null) :read-only t)
   (column nil :type (or fixnum null) :read-only t))
 
 (defgeneric file-location (thing)
   (:documentation
-   "Convert THING into a FILE-LOCATION, if possible.
+   "Convert THING into a `file-location', if possible.
 
-   A THING which can be converted into a FILE-LOCATION is termed a
+   A THING which can be converted into a `file-location' is termed a
    `file-location designator'.")
   (:method ((thing file-location)) thing))
 
@@ -65,7 +65,7 @@
 
    This is useful if one wants to attach additional information to an
    existing condition.  The enclosed condition can be obtained using the
-   ENCLOSED-CONDITION function.")
+   `enclosed-condition' function.")
   (:report (lambda (condition stream)
             (princ (enclosed-condition condition) stream))))
 
 
 (export 'make-condition-with-location)
 (defun make-condition-with-location (default-type floc datum &rest arguments)
-  "Construct a CONDITION-WITH-LOCATION given a condition designator.
+  "Construct a `condition-with-location' given a condition designator.
 
-   The returned condition will always be a CONDITION-WITH-LOCATION.  The
+   The returned condition will always be a `condition-with-location'.  The
    process consists of two stages.  In the first stage, a condition is
    constructed from the condition designator DATUM and ARGUMENTS with default
    type DEFAULT-TYPE (a symbol).  The precise behaviour depends on DATUM:
      * If DATUM is a symbol, then it must name a condition type.  An instance
        of this class is constructed using ARGUMENTS as initargs, i.e., as
        if (apply #'make-condition ARGUMENTS); if the type is a subtype of
-       CONDITION-WITH-LOCATION then FLOC is attached as the location.
+       `condition-with-location' then FLOC is attached as the location.
 
      * If DATUM is a format control (i.e., a string or function), then the
        condition is constructed as if, instead, DEFAULT-TYPE had been
        :format-arguments ARGUMENTS) supplied as ARGUMENTS.
 
    In the second stage, the condition constructed by the first stage is
-   converted into a CONDITION-WITH-LOCATION.  If the condition already has
-   type CONDITION-WITH-LOCATION then it is returned as is.  Otherwise it is
-   wrapped in an appropriate subtype of ENCLOSING-CONDITION-WITH-LOCATION:
+   converted into a `condition-with-location'.  If the condition already has
+   type `condition-with-location' then it is returned as is.  Otherwise it is
+   wrapped in an appropriate subtype of `enclosing-condition-with-location':
    if the condition was a subtype of ERROR or WARNING then the resulting
    condition will also be subtype of ERROR or WARNING as appropriate."
 
               'simple-warning-with-location
               floc datum arguments)))
 
+(defun my-cerror (continue-string datum &rest arguments)
+  "Like standard `cerror', but robust against sneaky changes of conditions.
+
+   It seems that `cerror' (well, at least the version in SBCL) is careful
+   to limit its restart to the specific condition it signalled.  But that's
+   annoying, because `with-default-error-location' substitutes different
+   conditions carrying the error-location information."
+  (restart-case (apply #'error datum arguments)
+    (continue ()
+      :report (lambda (stream)
+               (apply #'format stream continue-string datum arguments))
+      nil)))
+
 (export 'cerror-with-location)
 (defun cerror-with-location (floc continue-string datum &rest arguments)
   "Report a continuable error with attached location information."
-  (cerror continue-string
-         (apply #'make-condition-with-location
-                'simple-error-with-location
-                floc datum arguments)))
+  (my-cerror continue-string
+            (apply #'make-condition-with-location
+                   'simple-error-with-location
+                   floc datum arguments)))
 
 (export 'cerror*)
 (defun cerror* (datum &rest arguments)
-  (apply #'cerror "Continue" datum arguments))
+  (apply #'my-cerror "Continue" datum arguments))
 
 (export 'cerror*-with-location)
 (defun cerror*-with-location (floc datum &rest arguments)
    other conditions) which do not have file location information attached to
    them already.
 
-   See the WITH-DEFAULT-ERROR-LOCATION macro for more details."
+   See the `with-default-error-location' macro for more details."
 
   (if floc
       (handler-bind
    attaches FLOC to errors (and other conditions) which do not have file
    location information attached to them already.
 
-   If a condition other than a CONDITION-WITH-LOCATION is signalled during
+   If a condition other than a `condition-with-location' is signalled during
    the evaluation of the BODY, then an instance of an appropriate subcalass
-   of ENCLOSING-CONDITION-WITH-LOCATION is constructed, enclosing the
+   of `enclosing-condition-with-location' is constructed, enclosing the
    original condition, and signalled.  In particular, if the original
    condition was a subtype of ERROR or WARNING, then the new condition will
    also be a subtype of ERROR or WARNING as appropriate.
 
-   The FLOC argument is coerced to a FILE-LOCATION object each time a
+   The FLOC argument is coerced to a `file-location' object each time a
    condition is signalled.  For example, if FLOC is a lexical analyser object
-   which reports its current position in response to FILE-LOCATION, then each
-   condition will be reported as arising at the lexer's current position at
-   that time, rather than all being reported at the same position.
+   which reports its current position in response to `file-location', then
+   each condition will be reported as arising at the lexer's current position
+   at that time, rather than all being reported at the same position.
 
    If the new enclosing condition is not handled, the handler established by
    this macro will decline to handle the original condition.  Typically,
-   however, the new condition will be handled by COUNT-AND-REPORT-ERRORS.
+   however, the new condition will be handled by `count-and-report-errors'.
 
    As a special case, if FLOC is nil, then no special action is taken, and
    BODY is simply evaluated, as an implicit progn."
 (defun count-and-report-errors* (thunk)
   "Invoke THUNK in a dynamic environment which traps and reports errors.
 
-   See the COUNT-AND-REPORT-ERRORS macro for more detais."
+   See the `count-and-report-errors' macro for more details."
 
   (let ((errors 0)
        (warnings 0))
-    (handler-bind
-       ((error (lambda (error)
-                 (let ((fatal (not (find-restart 'continue error))))
-                   (format *error-output* "~&~A: ~:[~;Fatal error: ~]~A~%"
-                           (file-location error)
-                           fatal
-                           error)
-                   (incf errors)
-                   (if fatal
-                       (return-from count-and-report-errors*
-                         (values nil errors warnings))
-                       (invoke-restart 'continue)))))
-        (warning (lambda (warning)
-                   (format *error-output* "~&~A: Warning: ~A~%"
-                         (file-location warning)
-                         warning)
-                   (incf warnings)
-                   (invoke-restart 'muffle-warning))))
-      (values (funcall thunk)
-             errors
-             warnings))))
+    (restart-case
+       (let ((our-continue-restart (find-restart 'continue)))
+         (handler-bind
+             ((error (lambda (error)
+                       (let ((fatal (eq (find-restart 'continue error)
+                                        our-continue-restart)))
+                         (format *error-output*
+                                 "~&~A: ~:[~;Fatal error: ~]~A~%"
+                                 (file-location error)
+                                 fatal
+                                 error)
+                         (incf errors)
+                         (if fatal
+                             (return-from count-and-report-errors*
+                               (values nil errors warnings))
+                             (invoke-restart 'continue)))))
+              (warning (lambda (warning)
+                         (format *error-output* "~&~A: Warning: ~A~%"
+                                 (file-location warning)
+                                 warning)
+                         (incf warnings)
+                         (invoke-restart 'muffle-warning))))
+           (values (funcall thunk)
+                   errors
+                   warnings)))
+      (continue ()
+       :report (lambda (stream) (write-string "Exit to top-level" stream))
+       (values nil errors warnings)))))
 
 (export 'count-and-report-errors)
 (defmacro count-and-report-errors (() &body body)
 
    The BODY is evaluated.  If an error or warning is signalled, it is
    reported (using its report function), and counted.  Warnings are otherwise
-   muffled; continuable errors (i.e., when a CONTINUE restart is defined) are
-   continued; non-continuable errors cause an immediate exit from the BODY.
+   muffled; continuable errors (i.e., when a `continue' restart is defined)
+   are continued; non-continuable errors cause an immediate exit from the
+   BODY.
 
    The final value consists of three values: the primary value of the BODY
-   (or NIL if a non-continuable error occurred), the number of errors
+   (or nil if a non-continuable error occurred), the number of errors
    reported, and the number of warnings reported."
   `(count-and-report-errors* (lambda () ,@body)))