Commit Diff


commit - c8859bce66424fc093ae4f999552d80c4bfc0989
commit + 35dfaacf7838e916e7bebf9a02c17488493785b6
blob - 7245c45bf227508578f37e4600f0896bb3e2f579
blob + 273bba8109dbb3855ead32a0dffa7645355d1684
--- src/day-3.lisp
+++ src/day-3.lisp
@@ -1,25 +1,35 @@
 (defpackage #:aoc/day-3
   (:use #:cl #:aoc/utils)
-  (:import-from :cl-ppcre)
   (:export #:day-3))
 (in-package #:aoc/day-3)
 
-(defparameter *regex* "(mul\\((\\d+),(\\d+)\\))|((do|don't)\\(\\))")
+(defun try-read-number (stream)
+  (loop while (digit-char-p (peek-char nil stream))
+        collect (read-char stream) into bag
+        finally (return (if bag
+                            (parse-integer (coerce bag 'string))
+                            nil))))
 
+(define-parser day-3-parser (stream)
+    ((mul-enabled t)
+     (num-1 0)
+     (num-2 0)
+     (task-1 0)
+     (task-2 0))
+  (#\d #\o #\( #\) (setf mul-enabled t))
+  (#\d #\o #\n #\' #\t #\( #\) (setf mul-enabled nil))
+  (#\m #\u #\l #\(
+       (setf num-1 (try-read-number stream))
+       #\,
+       (setf num-2 (try-read-number stream))
+       #\)
+       (let ((result (* num-1 num-2)))
+         (incf task-1 result)
+         (when mul-enabled
+           (incf task-2 result)))))
+
 (defun day-3 (input)
-  (let ((input-string (read-stream-content-into-string input))
-        (mul-enabled t)
-        (task-1 0)
-        (task-2 0))
-    (ppcre:do-scans (start end reg-starts reg-ends *regex* input-string)
-      (case (aref input-string start)
-        (#\d
-         (setf mul-enabled (char/= (aref input-string (+ start 2)) #\n)))
-        (#\m
-         (let* ((num-1 (parse-integer input-string :start (aref reg-starts 1) :end (aref reg-ends 1)))
-                (num-2 (parse-integer input-string :start (aref reg-starts 2) :end (aref reg-ends 2)))
-                (result (* num-1 num-2)))
-           (incf task-1 result)
-           (when mul-enabled
-             (incf task-2 result))))))
+  (multiple-value-bind (mul-enabled num-1 num-2 task-1 task-2)
+      (day-3-parser input)
+    (declare (ignore mul-enabled num-1 num-2))
     (values task-1 task-2)))
blob - a8abc0920f0022c1badca89d153f023eb570ddc4
blob + 82aebcea64d17249b4688cb7f55e92eea86dd0b5
--- src/utils.lisp
+++ src/utils.lisp
@@ -24,7 +24,8 @@
    #:manhattan-distance
    #:do-map-neighbours
    #:read-number-list
-   #:find-pattern))
+   #:find-pattern
+   #:define-parser))
 (in-package #:aoc/utils)
 
 (defun normalize-type (type)
@@ -208,3 +209,55 @@
                    for c-2 = (elt list (+ i length))
                    always (= c-1 c-2))
           do (return-from find-pattern length)))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (defun transform-state-table (definitions)
+    (loop with table = (list nil nil)
+          with callbacks = nil
+          with current = nil
+          for definition in definitions
+          do (setf current table)
+             (loop for element in definition
+                   do (etypecase element
+                        (character
+                         (setf current (or (assoc element (cddr current))
+                                           (car (setf (cdr (last current))
+                                                      (list (list element nil)))))))
+                        (list
+                         (let ((sym (GENSYM "CALLBACK")))
+                           (setf (cadr current) sym)
+                           (push (cons sym element) callbacks)))))
+          finally (return (values table
+                                  callbacks)))))
+
+(defun do-parse (stream table)
+  (loop with current = table
+        with callback = nil
+        with global = t
+        for char = (read-char stream nil)
+        until (null char)
+        do (setf current (assoc char (cddr current))
+                 callback (cadr current))
+           (if (or (null current)
+                   (and callback
+                        (null (funcall (symbol-value callback)))))
+               (progn
+                 (setf current table)
+                 (unless global
+                   (unread-char char stream))
+                 (setf global t))
+               (setf global nil))))
+
+(defmacro define-parser (name (stream) (&rest variable-bindings) &body parse-tables)
+  (multiple-value-bind (table callbacks)
+      (transform-state-table parse-tables)
+    (let ((table-var (gensym "TABLE"))
+          (callback-syms (mapcar #'car callbacks)))
+      `(defun ,name (,stream)
+         (let ((,table-var ',table)
+               ,@variable-bindings)
+           (let ,(loop for (sym . code) in callbacks
+                       collect `(,sym (lambda () ,code)))
+             (declare (special ,@callback-syms))
+             (do-parse stream ,table-var))
+           (values ,@(mapcar #'car variable-bindings)))))))