;; parse-recover.sx — Parser with skipped-token restart
;;
;; Classic CL pattern: a simple token parser that signals a condition
;; when it encounters an unexpected token. The :skip-token restart
;; allows the parser to continue past the offending token.
;;
;; Depends on: lib/common-lisp/runtime.sx already loaded.

;; ── condition type ─────────────────────────────────────────────────────────

(cl-define-condition "parse-error" (list "error") (list "token" "position"))

;; ── simple token parser ────────────────────────────────────────────────────
;;
;; parse-numbers: given a list of tokens (strings), parse integers.
;; Non-integer tokens signal parse-error with two restarts:
;;   skip-token   — skip the bad token and continue
;;   use-zero     — use 0 in place of the bad token

(define
  parse-numbers
  (fn
    (tokens)
    (define result (list))
    (define
      process
      (fn
        (toks)
        (if
          (empty? toks)
          result
          (let
            ((tok (first toks)) (rest-toks (rest toks)))
            (let
              ((n (string->number tok 10)))
              (if
                n
                (begin
                  (set! result (append result (list n)))
                  (process rest-toks))
                (cl-restart-case
                  (fn
                    ()
                    (cl-signal
                      (cl-make-condition
                        "parse-error"
                        "token"
                        tok
                        "position"
                        (len result)))
                    (set! result (append result (list 0)))
                    (process rest-toks))
                  (list "skip-token" (list) (fn () (process rest-toks)))
                  (list
                    "use-zero"
                    (list)
                    (fn
                      ()
                      (begin
                        (set! result (append result (list 0)))
                        (process rest-toks)))))))))))
    (process tokens)
    result))

;; ── tests ─────────────────────────────────────────────────────────────────

(define passed 0)
(define failed 0)
(define failures (list))

(define
  check
  (fn
    (label got expected)
    (if
      (= got expected)
      (set! passed (+ passed 1))
      (begin
        (set! failed (+ failed 1))
        (set!
          failures
          (append
            failures
            (list
              (str
                "FAIL ["
                label
                "]: got="
                (inspect got)
                " expected="
                (inspect expected)))))))))

(define
  reset-stacks!
  (fn () (set! cl-handler-stack (list)) (set! cl-restart-stack (list))))

;; All valid tokens
(reset-stacks!)
(check
  "all valid: 1 2 3"
  (cl-handler-bind
    (list (list "parse-error" (fn (c) (cl-invoke-restart "skip-token"))))
    (fn () (parse-numbers (list "1" "2" "3"))))
  (list 1 2 3))

;; Skip bad token
(reset-stacks!)
(check
  "skip bad token: 1 x 3 -> (1 3)"
  (cl-handler-bind
    (list (list "parse-error" (fn (c) (cl-invoke-restart "skip-token"))))
    (fn () (parse-numbers (list "1" "x" "3"))))
  (list 1 3))

;; Use zero for bad token
(reset-stacks!)
(check
  "use-zero for bad: 1 x 3 -> (1 0 3)"
  (cl-handler-bind
    (list (list "parse-error" (fn (c) (cl-invoke-restart "use-zero"))))
    (fn () (parse-numbers (list "1" "x" "3"))))
  (list 1 0 3))

;; Multiple bad tokens, all skipped
(reset-stacks!)
(check
  "skip multiple bad: a 2 b 4 -> (2 4)"
  (cl-handler-bind
    (list (list "parse-error" (fn (c) (cl-invoke-restart "skip-token"))))
    (fn () (parse-numbers (list "a" "2" "b" "4"))))
  (list 2 4))

;; handler-case: abort on first bad token
(reset-stacks!)
(check
  "handler-case: abort on first bad"
  (cl-handler-case
    (fn () (parse-numbers (list "1" "bad" "3")))
    (list
      "parse-error"
      (fn
        (c)
        (str
          "parse error at position "
          (cl-condition-slot c "position")
          ": "
          (cl-condition-slot c "token")))))
  "parse error at position 1: bad")

;; Verify condition type hierarchy
(reset-stacks!)
(check
  "parse-error isa error"
  (cl-condition-of-type?
    (cl-make-condition "parse-error" "token" "x" "position" 0)
    "error")
  true)

;; ── summary ────────────────────────────────────────────────────────────────

(define parse-passed passed)
(define parse-failed failed)
(define parse-failures failures)