branch: externals/phpinspect
commit 58ad65932b3e2369f327a16f968802798ac3e0a6
Author: Hugo Thunnissen <de...@hugot.nl>
Commit: Hugo Thunnissen <de...@hugot.nl>

    Add `phpinspect-parser' type and `phpinspect-defparser' macro
---
 phpinspect-parser.el | 255 +++++++++++++++++++++++++++++++--------------------
 1 file changed, 157 insertions(+), 98 deletions(-)

diff --git a/phpinspect-parser.el b/phpinspect-parser.el
index 514721ca72..e41bc8503c 100644
--- a/phpinspect-parser.el
+++ b/phpinspect-parser.el
@@ -331,7 +331,7 @@ You can purge the parser cache with 
\\[phpinspect-purge-parser-cache]."
                  (intern ,name phpinspect-handler-obarray)))
          (byte-compile (intern ,name phpinspect-handler-obarray))))))
 
-(defun phpinspect-get-parser-create (tree-type &rest parser-parameters)
+(defun phpinspect-get-parser-func (name)
   "Retrieve a parser for TREE-TYPE from `phpinspect-parser-obarray'.
 
 TREE-TYPE must be a symbol or keyword representing the type of
@@ -341,12 +341,14 @@ If a parser by TREE-TYPE doesn't exist, it is created by 
callng
 `phpinspect-make-parser` with TREE-TYPE as first argument and
 PARSER-PARAMETERS as the rest of the arguments.  The resulting
 parser function is then returned in byte-compiled form."
-  (let* ((parser-name (symbol-name tree-type))
+  (let* ((parser-name (symbol-name name))
          (parser-symbol (intern-soft parser-name phpinspect-parser-obarray)))
-    (or (and parser-symbol (symbol-function parser-symbol))
-        (defalias (intern parser-name phpinspect-parser-obarray)
-          (byte-compile (apply #'phpinspect-make-parser
-                               `(,tree-type ,@parser-parameters)))))))
+    (unless parser-symbol
+      (error "Phpinspect: No parser found by name %s" name))
+
+    (or (symbol-function parser-symbol)
+        (defalias parser-symbol
+          (phpinspect-parser-compile (symbol-value parser-symbol))))))
 
 (defun phpinspect-purge-parser-cache ()
   "Empty `phpinspect-parser-obarray`.
@@ -358,7 +360,7 @@ have any effect."
   (interactive)
   (setq phpinspect-parser-obarray (obarray-make)))
 
-(defun phpinspect-make-parser (tree-type handler-list &optional 
delimiter-predicate)
+(defun phpinspect-make-parser-function (tree-type handler-list &optional 
delimiter-predicate)
   "Create a parser function using the handlers by names defined in 
HANDLER-LIST.
 
 See also `phpinspect-defhandler`.
@@ -424,6 +426,47 @@ token is \";\", which marks the end of a statement in PHP."
                    (t (forward-char))))
            (push ,tree-type tokens))))))
 
+(cl-defstruct (phpinspect-parser (:constructor phpinspect-make-parser))
+  (tree-keyword "root"
+                :type string
+                :read-only t
+                :documentation "Name of the keyword that is used as car of the
+root token, in string form without \":\" prefix.")
+  (handlers '(array tag equals list comma
+                         attribute-reference variable
+                         assignment-operator whitespace scope-keyword
+                         static-keyword const-keyword use-keyword
+                         class-keyword function-keyword word terminator
+                         here-doc string comment block)
+            :type list
+            :read-only t
+            :documentation "A list of symbols referring to the
+handlers that this parser uses.")
+  (delimiter-predicate nil
+                       :type function
+                       :read-only t
+                       :documentation "A predicate function that is passed each
+parsed token. When the predicate returns a non-nil value, the parser stops
+executing.")
+  (func nil
+        :type function
+        :documentation "The parser function."))
+
+(cl-defmethod phpinspect-parser-compile ((parser phpinspect-parser))
+  "Create/return parser function."
+  (or (phpinspect-parser-func parser)
+      (setf (phpinspect-parser-func parser)
+            (byte-compile
+             (phpinspect-make-parser-function
+              (intern (concat ":" (phpinspect-parser-tree-keyword parser)))
+              (phpinspect-parser-handlers parser)
+              (phpinspect-parser-delimiter-predicate parser))))))
+
+(defmacro phpinspect-defparser (name &rest parameters)
+  (declare (indent 1))
+  `(set (intern ,(symbol-name name) phpinspect-parser-obarray)
+        (phpinspect-make-parser ,@parameters)))
+
 (phpinspect-defhandler comma (comma &rest _ignored)
   "Handler for comma tokens"
   (regexp ",")
@@ -502,6 +545,15 @@ token is \";\", which marks the end of a statement in PHP."
       (goto-char max-point))
   (list :html))
 
+(phpinspect-defparser doc-block
+  :tree-keyword "doc-block"
+  :handlers '(annotation whitespace))
+
+(phpinspect-defparser comment
+  :tree-keyword "comment"
+  :handlers '(tag)
+  :delimiter-predicate #'phpinspect-html-p)
+
 (phpinspect-defhandler comment (start-token max-point)
   "Handler for comments and doc blocks"
   (regexp "#\\|//\\|/\\*")
@@ -516,9 +568,7 @@ token is \";\", which marks the end of a statement in PHP."
                      (forward-char))
                    (point)))
                 (comment-contents (buffer-substring region-start region-end))
-                (parser (phpinspect-get-parser-create
-                         :doc-block
-                         '(annotation whitespace)))
+                (parser (phpinspect-get-parser-func 'doc-block))
                 (doc-block (with-temp-buffer
                              (insert comment-contents)
                              (goto-char (point-min))
@@ -526,7 +576,7 @@ token is \";\", which marks the end of a statement in PHP."
            (forward-char 2)
            doc-block))
         (t
-         (let ((parser (phpinspect-get-parser-create :comment '(tag) 
#'phpinspect-html-p))
+         (let ((parser (phpinspect-get-parser-func 'comment))
                (end-position (line-end-position)))
            (funcall parser (current-buffer) end-position)))))
 
@@ -558,16 +608,18 @@ token is \";\", which marks the end of a statement in 
PHP."
   (regexp ";")
   (phpinspect-munch-token-without-attribs terminator :terminator))
 
+(phpinspect-defparser use
+  :tree-keyword "use"
+  :handlers '(word tag block-without-scopes terminator)
+  :delimiter-predicate #'phpinspect-end-of-use-p)
+
 (phpinspect-defhandler use-keyword (start-token max-point)
   "Handler for the use keyword and tokens that might follow to give it meaning"
   (regexp (concat "use" (phpinspect--word-end-regex)))
   (setq start-token (phpinspect--strip-last-char start-token))
   (forward-char (length start-token))
 
-  (let ((parser (phpinspect-get-parser-create
-                 :use
-                 '(word tag block-without-scopes terminator)
-                 #'phpinspect-end-of-use-p)))
+  (let ((parser (phpinspect-get-parser-func 'use)))
     (funcall parser (current-buffer) max-point)))
 
 (phpinspect-defhandler attribute-reference (start-token &rest _ignored)
@@ -584,6 +636,10 @@ token is \";\", which marks the end of a statement in PHP."
      ((string= start-token "->")
       (list :object-attrib name)))))
 
+(phpinspect-defparser namespace
+  :tree-keyword "namespace"
+  :delimiter-predicate #'phpinspect-block-p)
+
 (phpinspect-defhandler namespace (start-token max-point)
   "Handler for the namespace keyword. This is a special one
  because it is not always delimited by a block like classes or
@@ -594,23 +650,22 @@ token is \";\", which marks the end of a statement in 
PHP."
   (regexp (concat "namespace" (phpinspect--word-end-regex)))
   (setq start-token (phpinspect--strip-last-char start-token))
   (forward-char (length start-token))
-  (phpinspect-parse-with-handler-list
-   (current-buffer)
-   :namespace
-   max-point
-   (lambda () (not (looking-at (phpinspect-handler-regexp 'namespace))))
-   #'phpinspect-block-p))
+  (funcall (phpinspect-get-parser-func 'namespace)
+           (current-buffer)
+           max-point
+           (lambda () (not (looking-at (phpinspect-handler-regexp 
'namespace))))))
+
+(phpinspect-defparser const
+  :tree-keyword "const"
+  :handlers '(word comment assignment-operator string array terminator)
+  :delimiter-predicate #'phpinspect-end-of-token-p)
 
 (phpinspect-defhandler const-keyword (start-token max-point)
   "Handler for the const keyword."
   (regexp (concat "const" (phpinspect--word-end-regex)))
   (setq start-token (phpinspect--strip-last-char start-token))
   (forward-char (length start-token))
-  (let* ((parser (phpinspect-get-parser-create
-                  :const
-                  '(word comment assignment-operator string array
-                         terminator)
-                  #'phpinspect-end-of-token-p))
+  (let* ((parser (phpinspect-get-parser-func 'const))
          (token (funcall parser (current-buffer) max-point)))
     (when (phpinspect-incomplete-token-p (car (last token)))
       (setcar token :incomplete-const))
@@ -621,19 +676,19 @@ token is \";\", which marks the end of a statement in 
PHP."
   (regexp "\"\\|'")
   (list :string (phpinspect--munch-string start-token)))
 
+(phpinspect-defparser block-without-scopes
+  :tree-keyword "block"
+  :handlers '(array tag equals list comma attribute-reference variable
+                    assignment-operator whitespace function-keyword word
+                    terminator here-doc string comment block-without-scopes))
+
 (phpinspect-defhandler block-without-scopes (start-token max-point)
   "Handler for code blocks that cannot contain scope, const or
 static keywords with the same meaning as in a class block."
   (regexp "{")
   (forward-char (length start-token))
   (let* ((complete-block nil)
-         (parser (phpinspect-get-parser-create
-                  :block
-                  '(array tag equals list comma
-                          attribute-reference variable
-                          assignment-operator whitespace
-                          function-keyword word terminator here-doc
-                          string comment block-without-scopes)))
+         (parser (phpinspect-get-parser-func 'block-without-scopes))
          (continue-condition (lambda ()
                                (not (and (char-equal (char-after) ?})
                                          (setq complete-block t)))))
@@ -643,20 +698,19 @@ static keywords with the same meaning as in a class 
block."
       (setcar parsed :incomplete-block))
     parsed))
 
+(phpinspect-defparser class-block
+  :tree-keyword "block"
+  :handlers '(array tag equals list comma attribute-reference variable
+                    assignment-operator whitespace scope-keyword static-keyword
+                    const-keyword use-keyword function-keyword word terminator
+                    here-doc string comment block))
 
 (phpinspect-defhandler class-block (start-token max-point)
   "Handler for code blocks that cannot contain classes"
   (regexp "{")
   (forward-char (length start-token))
   (let* ((complete-block nil)
-         (parser (phpinspect-get-parser-create
-                  :block
-                  '(array tag equals list comma
-                          attribute-reference variable
-                          assignment-operator whitespace scope-keyword
-                          static-keyword const-keyword use-keyword
-                          function-keyword word terminator here-doc
-                          string comment block)))
+         (parser (phpinspect-get-parser-func 'class-block))
          (continue-condition (lambda ()
                                (not (and (char-equal (char-after) ?})
                                          (setq complete-block t)))))
@@ -666,6 +720,9 @@ static keywords with the same meaning as in a class block."
       (setcar parsed :incomplete-block))
     parsed))
 
+(phpinspect-defparser block
+  :tree-keyword "block")
+
 (phpinspect-defhandler block (start-token max-point)
   "Handler for code blocks"
   (regexp "{")
@@ -676,8 +733,8 @@ static keywords with the same meaning as in a class block."
                                ;; block, we can mark the block as complete.
                                (not (and (char-equal (char-after) ?})
                                          (setq complete-block t)))))
-         (parsed (phpinspect-parse-with-handler-list
-                  (current-buffer) :block max-point continue-condition)))
+         (parsed (funcall (phpinspect-get-parser-func 'block)
+                          (current-buffer) max-point continue-condition)))
     (if complete-block
         ;; After meeting the char-after requirement above, we need to move
         ;; one char forward to prevent parent-blocks from exiting because
@@ -716,6 +773,13 @@ Returns the consumed text string without face properties."
                               nil t)
            (buffer-substring-no-properties start-point (- (point) 1))))))
 
+(phpinspect-defparser list
+  :tree-keyword "list"
+  :handlers '(array tag equals list comma
+                    attribute-reference variable assignment-operator
+                    whitespace function-keyword word terminator here-doc
+                    string comment block-without-scopes))
+
 (phpinspect-defhandler list (start-token max-point)
   "Handler for php syntactic lists (Note: this does not include
 datatypes like arrays, merely lists that are of a syntactic
@@ -724,13 +788,7 @@ nature like argument lists"
   (forward-char (length start-token))
   (let* ((complete-list nil)
          (php-list (funcall
-                    (phpinspect-get-parser-create
-                     :list
-                     '(array tag equals list comma
-                             attribute-reference variable
-                             assignment-operator whitespace
-                             function-keyword word terminator here-doc
-                             string comment block-without-scopes))
+                    (phpinspect-get-parser-func 'list)
                     (current-buffer)
                     max-point
                     (lambda () (not (and (char-equal (char-after) ?\)) (setq 
complete-list t)))))))
@@ -742,13 +800,15 @@ nature like argument lists"
       (setcar php-list :incomplete-list))
     php-list))
 
+(phpinspect-defparser declaration
+  :tree-keyword "declaration"
+  :handlers '(comment word list terminator tag)
+  :delimiter-predicate #'phpinspect-end-of-token-p)
+
 ;; TODO: Look into using different names for function and class :declaration 
tokens. They
 ;; don't necessarily require the same handlers to parse.
 (defsubst phpinspect-get-or-create-declaration-parser ()
-  (let ((parser (phpinspect-get-parser-create
-                 :declaration
-                 '(comment word list terminator tag)
-                 #'phpinspect-end-of-token-p)))
+  (let ((parser (phpinspect-get-parser-func 'declaration)))
     (lambda (&rest arguments)
       (let ((result (apply parser arguments)))
         (if (phpinspect-terminator-p (car (last result)))
@@ -770,6 +830,24 @@ nature like argument lists"
             (funcall (phpinspect-handler 'block-without-scopes)
                      (char-to-string (char-after)) max-point)))))
 
+(phpinspect-defparser scope-public
+  :tree-keyword "public"
+  :handlers '(function-keyword static-keyword const-keyword variable here-doc
+                               string terminator tag comment)
+  :delimiter-predicate #'phpinspect--scope-terminator-p)
+
+(phpinspect-defparser scope-private
+  :tree-keyword "private"
+  :handlers '(function-keyword static-keyword const-keyword variable here-doc
+                               string terminator tag comment)
+  :delimiter-predicate #'phpinspect--scope-terminator-p)
+
+(phpinspect-defparser scope-protected
+  :tree-keyword "protected"
+  :handlers '(function-keyword static-keyword const-keyword variable here-doc
+                               string terminator tag comment)
+  :delimiter-predicate #'phpinspect--scope-terminator-p)
+
 (phpinspect-defhandler scope-keyword (start-token max-point)
   "Handler for scope keywords"
   (regexp (mapconcat (lambda (word)
@@ -778,26 +856,24 @@ nature like argument lists"
                      "\\|"))
   (setq start-token (phpinspect--strip-last-char start-token))
   (forward-char (length start-token))
-  (funcall (phpinspect-get-parser-create
-            (cond ((string= start-token "public") :public)
-                  ((string= start-token "private") :private)
-                  ((string= start-token "protected") :protected))
-            '(function-keyword static-keyword const-keyword
-                               variable here-doc string terminator tag comment)
-            #'phpinspect--scope-terminator-p)
+  (funcall (phpinspect-get-parser-func
+            (cond ((string= start-token "public") 'scope-public)
+                  ((string= start-token "private") 'scope-private)
+                  ((string= start-token "protected") 'scope-protected)))
            (current-buffer)
            max-point))
 
+(phpinspect-defparser static
+  :tree-keyword "static"
+  :handlers '(comment function-keyword variable array word terminator tag)
+  :delimiter-predicate #'phpinspect--static-terminator-p)
+
 (phpinspect-defhandler static-keyword (start-token max-point)
   "Handler for the static keyword"
   (regexp (concat "static" (phpinspect--word-end-regex)))
   (setq start-token (phpinspect--strip-last-char start-token))
   (forward-char (length start-token))
-  (funcall (phpinspect-get-parser-create
-            :static
-            '(comment function-keyword variable array word
-                      terminator tag)
-            #'phpinspect--static-terminator-p)
+  (funcall (phpinspect-get-parser-func 'static)
            (current-buffer)
            max-point))
 
@@ -806,6 +882,11 @@ nature like argument lists"
   (regexp "=>")
   (phpinspect-munch-token-without-attribs arrow :fat-arrow))
 
+(phpinspect-defparser array
+  :tree-keyword "array"
+  :handlers '(comment comma list here-doc string array variable
+                      attribute-reference word fat-arrow))
+
 (phpinspect-defhandler array (start-token max-point)
   "Handler for arrays, in the bracketet as well as the list notation"
   (regexp "\\[\\|array(")
@@ -813,11 +894,7 @@ nature like argument lists"
   (let* ((end-char (cond ((string= start-token "[") ?\])
                          ((string= start-token "array(") ?\))))
          (end-char-reached nil)
-         (token (funcall (phpinspect-get-parser-create
-                          :array
-                          '(comment comma list here-doc string
-                                    array variable attribute-reference
-                                    word fat-arrow))
+         (token (funcall (phpinspect-get-parser-func 'array)
                          (current-buffer)
                          max-point
                          (lambda () (not (and (char-equal (char-after) 
end-char)
@@ -844,41 +921,23 @@ the properties of the class"
         (funcall (phpinspect-handler 'class-block)
                  (char-to-string (char-after)) max-point)))
 
-(defun phpinspect-parse-with-handler-list
-    (buffer tree-type max-point &optional continue-condition 
delimiter-predicate)
-  "Parse BUFFER for TREE-TYPE tokens until MAX-POINT.
-
-Stop at CONTINUE-CONDITION or DELIMITER-PREDICATE.
-
-This just calls `phpinspect-get-parser-create` to make a parser
-that contains all handlers necessary to parse code."
-  (let ((parser (phpinspect-get-parser-create
-                 tree-type
-                 '(array tag equals list comma
-                         attribute-reference variable
-                         assignment-operator whitespace scope-keyword
-                         static-keyword const-keyword use-keyword
-                         class-keyword function-keyword word terminator
-                         here-doc string comment block)
-                 delimiter-predicate)))
-    (funcall parser buffer max-point continue-condition)))
-
+(phpinspect-defparser root
+  :tree-keyword "root"
+  :handlers '(namespace array equals list comma attribute-reference variable
+                        assignment-operator whitespace scope-keyword
+                        static-keyword const-keyword use-keyword class-keyword
+                        function-keyword word terminator here-doc string 
comment
+                        tag block))
 
 (defun phpinspect-parse-buffer-until-point (buffer point)
   (with-current-buffer buffer
     (save-excursion
       (goto-char (point-min))
       (re-search-forward "<\\?php\\|<\\?" nil t)
-      (funcall (phpinspect-get-parser-create
-                :root
-                '(namespace array equals list comma
-                            attribute-reference variable assignment-operator
-                            whitespace scope-keyword static-keyword
-                            const-keyword use-keyword class-keyword
-                            function-keyword word terminator here-doc string
-                            comment tag block))
+      (funcall (phpinspect-get-parser-func 'root)
                (current-buffer)
                point))))
 
+
 (provide 'phpinspect-parser)
 ;;; phpinspect-parser.el ends here

Reply via email to