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