emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/javaimp 9c834db: javaimp-parse.el: Add parsing of metho


From: Filipp Gunbin
Subject: [elpa] externals/javaimp 9c834db: javaimp-parse.el: Add parsing of methods and statements
Date: Thu, 3 Jun 2021 12:57:58 -0400 (EDT)

branch: externals/javaimp
commit 9c834db1a69168f25498c0f2c3fe49ecc71f29d9
Author: Filipp Gunbin <fgunbin@fastmail.fm>
Commit: Filipp Gunbin <fgunbin@fastmail.fm>

    javaimp-parse.el: Add parsing of methods and statements
---
 javaimp-parse.el | 268 ++++++++++++++++++++++++++++++++++++++++++++++++-------
 javaimp-tests.el |  46 ++++++++++
 2 files changed, 283 insertions(+), 31 deletions(-)

diff --git a/javaimp-parse.el b/javaimp-parse.el
index 7c29794..e603b4b 100644
--- a/javaimp-parse.el
+++ b/javaimp-parse.el
@@ -20,39 +20,161 @@
 
 (require 'cl-lib)
 (require 'seq)
+(require 'cc-mode)                      ;for java-mode-syntax-table
+
+(defcustom javaimp-parse-format-method-name
+  #'javaimp--parse-format-method-name-full
+  "Function to format method name, invoked with 3 arguments:
+NAME, ARGS and THROWS-ARGS.  The last two are lists with elements
+of the form (TYPE . NAME).  For THROWS-ARGS, only TYPE is
+present."
+  :group 'javaimp
+  :type 'function)
 
 (cl-defstruct javaimp-scope
-  type ; one of anonymous-class, class, interface, enum, local-class, unknown
+  type ; one of anonymous-class, class, interface, enum, local-class,
+       ; method, statement, unknown
   name
   start
   open-brace)
 
-(defsubst javaimp--parse-is-class (scope)
-  (memq (javaimp-scope-type scope) '(class interface enum)))
+(defconst javaimp--parse-class-keywords
+  '("class" "interface" "enum"))
+(defconst javaimp--parse-stmt-keywords
+  '("if" "for" "while" "switch" "try" "catch"))
 
-(defconst javaimp--parse-class-re
+(defun javaimp--parse-block-re (keywords)
   (concat
-   (regexp-opt '("class" "interface" "enum") 'words)
+   (regexp-opt keywords 'words)
    (rx (and (+ (syntax whitespace))
             (group (+ (any alnum ?_)))))))
 
+(defconst javaimp--parse-class-re
+  (javaimp--parse-block-re javaimp--parse-class-keywords))
+(defconst javaimp--parse-stmt-re
+  (javaimp--parse-block-re javaimp--parse-stmt-keywords))
 
-(defun javaimp--parse-get-package ()
+(defsubst javaimp--parse-is-class (scope)
+  (member (symbol-name (javaimp-scope-type scope)) 
javaimp--parse-class-keywords))
+
+(defvar javaimp--arglist-syntax-table
+  (let ((st (make-syntax-table java-mode-syntax-table)))
+    (modify-syntax-entry ?< "(>" st)
+    (modify-syntax-entry ?> ")<" st)
+    (modify-syntax-entry ?. "_" st) ; separates parts of fully-qualified type
+    st)
+  "Enables parsing angle brackets as lists")
+
+
+
+;;; Arglist
+
+(defun javaimp--parse-arglist (beg end &optional only-type)
   (save-excursion
     (save-restriction
-      (widen)
-      (goto-char (point-min))
-      (catch 'found
-        (while (re-search-forward "^\\s *package\\s +\\([^;]+\\)\\s *;" nil t)
-          (let ((state (syntax-ppss)))
-            (unless (syntax-ppss-context state)
-              (throw 'found (match-string 1)))))))))
+      (narrow-to-region beg end)
+      (with-syntax-table javaimp--arglist-syntax-table ;skip generics like 
lists
+        (goto-char (point-max))
+        (ignore-errors
+          (let (res)
+            (while (not (bobp))
+              (push (javaimp--parse-arglist-one-arg only-type) res)
+              ;; move back to the previous argument, if any
+              (when (javaimp--parse-arglist-until (lambda ()
+                                                    (and (not (bobp))
+                                                         (= (char-before) 
?,))))
+                (backward-char)))
+            res))))))
+
+(defun javaimp--parse-arglist-one-arg (only-type)
+  ;; Parse one argument as type and name backwards starting from point
+  ;; and return it in the form (TYPE . NAME).  Name is skipped if
+  ;; ONLY-TYPE is non-nil.  Leave point at where the job is done:
+  ;; skipping further backwards is done by the caller.
+  (let ((limit (progn
+                 (skip-syntax-backward "-")
+                 (point)))
+        name)
+    ;; Parse name
+    (unless only-type
+      (if (= 0 (skip-syntax-backward "w_"))
+          (error "Cannot parse argument name"))
+      (setq name (buffer-substring-no-properties (point) limit))
+      (skip-syntax-backward "-")
+      (setq limit (point)))
+    ;; Parse type: allow anything, but stop at the word boundary which
+    ;; is not inside list (this is presumably the type start..)
+    (if (javaimp--parse-arglist-until (lambda ()
+                                        (save-excursion
+                                          (skip-syntax-forward "-" limit)
+                                          (looking-at "\\_<"))))
+        (progn
+          (skip-syntax-forward "-")     ;undo skipping by ..-until
+          (let ((type (replace-regexp-in-string
+                       "[[:space:]\n]+" " "
+                       (buffer-substring-no-properties (point) limit))))
+            (cons type name)))
+      (error "Cannot parse argument type"))))
+
+(defun javaimp--parse-arglist-until (stop-p)
+  ;; Goes backwards until position at which STOP-P returns non-nil.
+  ;; Returns non-nil if stopped on this condition, nil if reached bob.
+  ;; STOP-P is invoked (possibly at the bob) without arguments and
+  ;; should not move point.  Backward movement also skips any
+  ;; whitespace, so STOP-P looking forward should be prepared to
+  ;; see leading whitespace.
+  (catch 'done
+    (while t
+      (skip-syntax-backward "-")
+      (let ((state (syntax-ppss)))
+        (cond ((syntax-ppss-context state)
+               ;; move out of comment/string if in one
+               (goto-char (nth 8 state)))
+              ((funcall stop-p)
+               (throw 'done t))
+              ((bobp)
+               (throw 'done nil))
+              ((= (char-syntax (char-before)) ?\))
+               (backward-list))
+              (t
+               (backward-char)))))))
+
+
+
+;;; Formatting
 
+(defsubst javaimp--parse-format-method-name-full (name args throws-args)
+  "Outputs NAME, ARGS (name and type) and THROWS-ARGS (only type)."
+  (concat name
+          "("
+          (mapconcat (lambda (arg)
+                       (concat (car arg) " " (cdr arg)))
+                     args
+                     ", ")
+          ")"
+          (if throws-args
+              (concat "throws "
+                      (mapconcat #'car throws-args ", ")))
+          ))
+
+(defsubst javaimp--parse-format-method-name-types (name args throws-args)
+  "Outputs NAME and ARGS (only type)."
+  (concat name
+          "("
+          (mapconcat #'car args ", ")
+          ")"
+          ))
+
+
+;;; Scopes
 
 (defvar javaimp--parse-scope-hook
-  '(javaimp--parse-scope-class
+  '(;; should be before method/stmt because looks similar, but with
+    ;; "new " in front
     javaimp--parse-scope-anonymous-class
-    javaimp--parse-scope-unknown      ;fallback
+    javaimp--parse-scope-method-or-stmt
+    javaimp--parse-scope-class
+    javaimp--parse-scope-unknown        ; catch-all
     ))
 
 (defun javaimp--parse-scope-class (state)
@@ -69,24 +191,93 @@
 
 (defun javaimp--parse-scope-anonymous-class (state)
   (save-excursion
-    (let (end)
-      (if (and (re-search-backward "\\<new\\s +" nil t)
-               ;; skip arg list and ws
-               (setq end (save-excursion
-                           (ignore-errors
-                             (goto-char
-                              (scan-lists (nth 1 state) -1 0))
-                             (skip-syntax-backward "-")
-                             (point))))
-               (not (save-match-data
-                      (search-forward "(" end t))))
+    ;; skip arg-list and ws
+    (when (and (progn
+                 (skip-syntax-backward "-")
+                 (= (char-before) ?\)))
+               (ignore-errors
+                 (goto-char
+                  (scan-lists (point) -1 0))))
+      (skip-syntax-backward "-")
+      (let ((end (point)))
+        (when (and (re-search-backward "\\<new\\s-+" nil t)
+                   (not (save-match-data
+                          (search-forward "(" end t))))
           (make-javaimp-scope :type 'anonymous-class
-                              :name (concat
-                                     "Anon_"
-                                     (buffer-substring-no-properties
-                                      (match-end 0) end))
+                              :name (concat "Anon_"
+                                            (buffer-substring-no-properties
+                                             (match-end 0) end))
                               :start (match-beginning 0)
-                              :open-brace (nth 1 state))))))
+                              :open-brace (nth 1 state)))))))
+
+(defun javaimp--parse-scope-method-or-stmt (state)
+  (save-excursion
+    (let ((throws-args (javaimp--parse-scope-method-throws state)))
+      (when (and (not (eq throws-args t))
+                 (progn
+                   (skip-syntax-backward "-")
+                   (= (char-before) ?\)))
+                 (ignore-errors
+                   (goto-char
+                    (scan-lists (point) -1 0))))
+        (let* (;; leave open/close parens out
+               (arglist-region (cons (1+ (point))
+                                     (1- (scan-lists (point) 1 0))))
+               (count (progn
+                        (skip-syntax-backward "-")
+                        (skip-syntax-backward "w_")))
+               (name (and (< count 0)
+                          (buffer-substring-no-properties
+                           (point) (+ (point) (abs count)))))
+               (type (when name
+                       (if (and (member name javaimp--parse-stmt-keywords)
+                                (not throws-args))
+                           'statement 'method))))
+          (when type
+            (make-javaimp-scope
+             :type type
+             :name (if (eq type 'statement)
+                       name
+                     (funcall javaimp-parse-format-method-name
+                              name
+                              (javaimp--parse-arglist (car arglist-region)
+                                                      (cdr arglist-region))
+                              throws-args))
+             :start (point)
+             :open-brace (nth 1 state))))))))
+
+(defun javaimp--parse-scope-method-throws (state)
+  "Subroutine of `javaimp--parse-scope-method-or-stmt'.  Attempts
+to parse throws clause backwards, returning THROWS-ARGS in the
+same format as `javaimp--parse-arglist' (but here, only TYPE
+element component will be present).  Point is left at the
+position from where method signature parsing may be continued.
+Returns t if parsing failed and should not be continued."
+  (let ((pos (point))
+        res)
+    (when (re-search-backward "\\<throws\\s-+" nil t)
+      (setq res (save-match-data
+                  (javaimp--parse-arglist
+                   (match-end 0)
+                   (save-excursion
+                     (goto-char pos)
+                     (skip-syntax-backward "-")
+                     (point))
+                   t)))
+      (if res
+          (goto-char (match-beginning 0))
+        ;; If throws-args did not parse, then it could either 1) be
+        ;; malformed, 2) just belong to some other method.  In case 2
+        ;; we want to return to where we started and continue without
+        ;; throws.
+        (if (ignore-errors
+              ;; Is the next list construct the same which we're
+              ;; analyzing?  If yes, then assume throws is malformed.
+              (/= (scan-lists (match-end 0) 1 -1)
+                  (1+ (nth 1 state))))
+            (goto-char pos)
+          (setq res t))))
+    res))
 
 (defun javaimp--parse-scope-unknown (state)
   (make-javaimp-scope :type 'unknown
@@ -123,6 +314,21 @@
         (setq tmp (cdr tmp))))
     res))
 
+
+
+;; Main
+
+(defun javaimp--parse-get-package ()
+  (save-excursion
+    (save-restriction
+      (widen)
+      (goto-char (point-min))
+      (catch 'found
+        (while (re-search-forward "^\\s *package\\s +\\([^;]+\\)\\s *;" nil t)
+          (let ((state (syntax-ppss)))
+            (unless (syntax-ppss-context state)
+              (throw 'found (match-string 1)))))))))
+
 (defun javaimp--parse-get-file-classes (file)
   (with-temp-buffer
     (insert-file-contents file)
diff --git a/javaimp-tests.el b/javaimp-tests.el
index 9951a81..497514c 100644
--- a/javaimp-tests.el
+++ b/javaimp-tests.el
@@ -23,6 +23,52 @@
       (should (eql (length projects) 2)))))
 
 
+(ert-deftest javaimp-test--parse-arglist ()
+  (dolist (data '(("")
+                  ("  ")
+                  ("int i"
+                   ("int" . "i"))
+                  (" List<? extends Comparable<? super T>> list, T... elements"
+                   ("List<? extends Comparable<? super T>>" . "list")
+                   ("T..." . "elements"))
+                  ("org.foo.Map <? extends K,   \n? extends V> m, String [] 
array, int i"
+                   ;; TODO remove extra ws within
+                   ("org.foo.Map <? extends K, ? extends V>" . "m")
+                   ("String []" . "array")
+                   ("int" . "i"))
+                  (" Bi_Function<? super K, ? super V, ? extends V> function "
+                   ("Bi_Function<? super K, ? super V, ? extends V>" . 
"function"))
+                  ("@Annotation1 final int i,
+@org.foo.Annotation_2(value_1 = \"value1 , (){}\", value2 = -2.3) String[][] 
arr"
+                   ("int" . "i")
+                   ("String[][]" . "arr"))
+                  ))
+    (with-temp-buffer
+      (java-mode)
+      (insert (car data))
+      (should (equal (javaimp--parse-arglist (point-min) (point-max))
+                     (cdr data))))))
+
+(ert-deftest javaimp-test--parse-arglist-throws ()
+  (dolist (data '(("")
+                  ("  ")
+                  ("Exception1"
+                   ("Exception1"))
+                  (" Exception1 , org.foo_bar_3.Exception_2 "
+                   ("Exception1")
+                   ("org.foo_bar_3.Exception_2"))
+                  ("Exception_1<? extends org.foo.Exception_2<? super 
Exception3>,
+  Exception4<? super Exception5>>, \nException6,Exception7<Exception8>"
+                   ("Exception_1<? extends org.foo.Exception_2<? super 
Exception3>, \
+Exception4<? super Exception5>>")
+                   ("Exception6")
+                   ("Exception7<Exception8>"))))
+    (with-temp-buffer
+      (java-mode)
+      (insert (car data))
+      (should (equal (javaimp--parse-arglist (point-min) (point-max) t)
+                     (cdr data))))))
+
 (ert-deftest javaimp-test--parse-get-package ()
   (with-temp-buffer
     (insert "//package org.commented1;



reply via email to

[Prev in Thread] Current Thread [Next in Thread]