[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;
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [elpa] externals/javaimp 9c834db: javaimp-parse.el: Add parsing of methods and statements,
Filipp Gunbin <=