(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))))