Reorganized the code and added documentation.
authorD Herring <dherring@at.tentpost.dot.com>
Sat, 10 Mar 2007 22:14:26 +0000 (17:14 -0500)
committerD Herring <dherring@at.tentpost.dot.com>
Sat, 10 Mar 2007 22:14:26 +0000 (17:14 -0500)
parser.lisp

index cab7df0..7ad28f6 100644 (file)
@@ -1,41 +1,38 @@
 ;; A simple top-down, backtracking parser
 ;; Modelled after EBNF notation
 
-(defun starts-with (string prefix &key (start 0))
-  "Does 'string' begin with 'prefix'?"
-  (let ((end (+ start (length prefix)))
-        (l (length string)))
-    (if (> end l)
-        nil
-        (string= prefix string :start2 start :end2 end))))
 
-;; return a list of the children
-;; return the end of the last child
-(defun kleene* (f string &key (start 0))
-  (multiple-value-bind (end value) (funcall f string :start start)
-    (if end
-        (multiple-value-bind (e v) (kleene* f string :start end)
-          (values e (cons value v)))
-        start)))
+;; Internal utilities
 
-;;
-;; Construction macros
-;;
 
-;; Provide a clean interface to rules expressed as either function names or macros
 (defmacro grammar-call (x)
+  "Call function or macro x"
   (cond ((null x) (error "Cannot execute nil."))
         ((symbolp x) `(,x string :start start))
         ((listp x) x)
         (t (error "Cannot call ~S" x))))
 
 (defmacro grammar-wrap (x)
+  "Wrap function or macro x as a callback"
   (cond ((null x) (error "Cannot execute nil."))
         ((symbolp x) (list 'quote x))
         ((listp x) `(lambda (string &key (start 0)) ,x))
         (t (error "Cannot call ~S" x))))
 
+
+;; Parser construction
+
+
+(defun starts-with (string prefix &key (start 0))
+  "Does 'string' begin with 'prefix'?"
+  (let ((end (+ start (length prefix)))
+        (l (length string)))
+    (if (> end l)
+        nil
+        (string= prefix string :start2 start :end2 end))))
+
 (defmacro grammar-string (str)
+  "match = 'str'"
   (let ((l (length str)))
     (cond ((= l 0) '(values start ""))
           ((= l 1) `(when (and
              (values (+ start ,(length str)) ,str))))))
 
 (defmacro grammar-optional (x)
+  "match = [x]"
   `(multiple-value-bind (end value) (grammar-call ,x)
     (if end
         (values end value)
         start)))
 
 (defmacro grammar-and (first &rest rest)
+  "match = first, (grammar-and rest)"
   (if (null rest)
       `(grammar-call ,first)
       `(multiple-value-bind (end value) (grammar-call ,first)
@@ -64,8 +63,8 @@
                   (values e (cons value v))
                   (values e (list value v))))))))))
 
-
 (defmacro grammar-or (first &rest rest)
+  "match = first | (grammar-or rest)"
   (if (null rest)
       `(grammar-call ,first)
       `(multiple-value-bind (end value) (grammar-call ,first)
@@ -74,6 +73,7 @@
             (grammar-or ,@rest)))))
 
 (defmacro grammar-n (n x)
+  "match = n * x"
   (if (> n 0)
       (let ((n1 (1- n)))
       `(multiple-value-bind (end value) (grammar-call ,x)
                     (values e (list value)))))))))
       'start))
 
+(defun kleene* (f string &key (start 0))
+  "match f 0 or more times"
+  (multiple-value-bind (end value) (funcall f string :start start)
+    (if end
+        (multiple-value-bind (e v) (kleene* f string :start end)
+          (values e (cons value v)))
+        start)))
+
 (defmacro grammar-* (x)
+  "match = {x}"
   `(kleene* (grammar-wrap ,x) string :start start))
 
 (defmacro grammar-exception (x y)
-  "a syntactic-exception; x but not also y"
+  "match = x - y"
   `(multiple-value-bind (end value) (grammar-call ,x)
     (when end
       (multiple-value-bind (e v) (grammar-call ,y)
         (when (not e)
           (values end value))))))
 
-;;
-;; Value-changing mechanism
-;;
+
+;; Output control
+
+
 (defmacro grammar-func (x f)
   "Apply f to the value of x"
   `(multiple-value-bind (end value) (grammar-call ,x)
       (values end (,f value)))))
 
 
+;; Helper macros
+
+
 (defmacro grammar-rule (name &rest body)
+  "defun wrapper to simplify rule production"
   `(defun ,name (string &key (start 0))
     ,@body))
-
-
-;;;; Special conditions
-
-;; Flag whether to ignore whitespace between tokens
-;(defparameter *grammar-no-white* nil)
-
-;; List of restricted keywords
-;(defparameter *grammar-keywords* (make-hash-table :test #'equal))
\ No newline at end of file