git.fiddlerwoaroof.com
labels-parser.lisp
7c37909c
 (defpackage :labels-parser
   (:use :cl )
   (:export ))
 (in-package :labels-parser)
 
 (defmacro cond-switch* ((&rest bindings) &body clauses)
   `(let* (,@bindings)
     (cond ,@clauses)))
 
 (defun read-group (s)
   (labels ((is-terminal-p (char)
              (or (null char)
                  (member char '(#\space #\) #\,))))
            (peek ()
              (peek-char nil *standard-input* nil))
            (discard ()
              (read-char *standard-input*))
            (getch ()
              (read-char *standard-input*))
 
            (consume-whitespace (next)
              (if (eql #\space (peek))
                  (progn (read-char)
                         (consume-whitespace next))
                  (funcall next)))
            (consume-matching (pred)
              (if (funcall pred (peek))
                  (getch)
                  (error 'parse-fail)))
            (consume-until (term-pred pred &optional accum)
              (cond-switch* ((nc (peek)))
                ((funcall term-pred nc)
                 (consume-whitespace
                  (lambda () (reverse accum))))
                (t
                 (consume-until term-pred pred
                                (cons (consume-matching pred)
                                      accum)))))
            (consume-nonterminal (pred)
              (consume-until #'is-terminal-p pred))
 
            (read-symbol ()
              (coerce (consume-nonterminal #'alphanumericp)
                      'string))
            (read-number ()
              (parse-integer
               (coerce (consume-nonterminal #'digit-char-p)
                       'string)))
            (read-primitive ()
              (cond-switch* ((c (peek)))
                ((null c) nil)
                ((digit-char-p c) (read-number))
                ((alpha-char-p c) (read-symbol))))
            (read-list (&optional accum)
              (cond-switch* ((next (read-primitive))
                             (nc (peek)))
                ((null next)
                 (if (eql #\) nc)
                     (reverse accum)
                     (error 'parse-fail)))
                ((eql #\) nc)
                 (discard)
                 (reverse (cons next accum)))
                (t
                 (read-list (cons next accum)))))
 
            (read-main ()
              (cond-switch* ((nc (peek)))
                ((null nc) nil)
                ((eql #\space nc) (read-char) (read-main))
                ((eql nc #\()
                 (read-char)
                 (consume-whitespace
                  (lambda () (read-list))))
                (t (prog1 (read-primitive)
                     (when (listen *standard-input*)
                       (error 'parse-fail)))))))
     (let ((*standard-input* s))
       (read-main))))