(defgeneric push-char (accumulator value)
            (:documentation "Process a character from the input."))

(defgeneric peek (accumulator)
            (:documentation "Determine whether or not a \"digit\" has been found."))

(defclass digit-accumulator ()
  ((current-digit :initform nil)))

(defmethod push-char ((accumulator digit-accumulator) value)
  (setf (slot-value accumulator 'current-digit) (digit-char-p value)))

(defmethod peek ((accumulator digit-accumulator))
  (slot-value accumulator 'current-digit))

(defclass english-accumulator ()
  ((matching :initform nil)))

(defconstant english-numbers
  (append (let ((ix 0))
    (mapcar #'(lambda (name) (incf ix) (append (coerce name 'list) ix))
                   '("one" "two" "three" "four" "five"
                      "six" "seven" "eight" "nine" "ten")))
    (loop
      for n from 1 to 9
      collect (append (coerce (format nil "~a" n) 'list) n))))

(defmethod push-char ((accumulator english-accumulator) value)
  (setf (slot-value accumulator 'matching)
          (mapcan
            #'(lambda (entry)
                (if (and (consp entry) (equal value (car entry)))
                    (list (cdr entry))))
            (append (slot-value accumulator 'matching) english-numbers))))

(defmethod peek ((accumulator english-accumulator))
  (dolist (entry (slot-value accumulator 'matching))
    (if (numberp entry)
        (return entry))))

(defun calibration (line)
  (let ((accs
          (mapcar
           #'(lambda (which)
                (list 'accumulator (make-instance which)'low () 'high ()))
             '(digit-accumulator english-accumulator))))
    (loop for c across line
          do (dolist (ac accs)
               (push-char (getf ac 'accumulator) c)
               (let ((digit (peek (getf ac 'accumulator))))
                 (when digit
                   (setf (getf ac 'high) digit)
                   (unless (getf ac 'low) (setf (getf ac 'low) digit))))))
    (mapcar
     #'(lambda (acc)
         (let ((low (getf acc 'low)) (high (getf acc 'high)))
           (when (and low high) (+ (* 10 low) high))))
     accs)))

(defun process (instream)
  (let ((p1 0) (p2 0))
    (loop for line = (read-line instream nil)
          while line
          do (destructuring-bind
                 (p1-1 p2-1)
                 (calibration line)
               (setf p1 (+ p1 p1-1))
               (setf p2 (+ p2 p2-1))))
    (list p1 p2)))

(loop for arg in (cdr *posix-argv*)
      do (with-open-file (s arg :direction :input)
           (loop for result in (process s)
                  for part from 1
                  do (format T "Part ~a: ~a~%" part result))))