From 3c2a4d04775c8040b05dd0e1ec6e65abb16fd932 Mon Sep 17 00:00:00 2001 From: Eshel Yaron Date: Mon, 21 Nov 2022 18:39:37 +0200 Subject: [PATCH] Support defining DCG non-terminals in sweeprolog-insert-term-dwim * sweep.pl (sweep_color_normalized_/4): expose DCG term "kind" to Elisp. * sweeprolog.el (sweeprolog-new-predicate-location-function): change expected function signature. (sweeprolog-default-new-predicate-location) (sweeprolog-new-predicate-location-above-current): update arguments. (sweeprolog-maybe-define-predicate): support defining DCGs. --- .gitignore | 2 + NEWS.org | 34 +++++++++++++---- sweep.pl | 3 ++ sweeprolog-tests.el | 92 ++++++++++++++++++++++++++++++++++++++++++++- sweeprolog.el | 53 ++++++++++++++++++++------ 5 files changed, 162 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index ee9db44..dd499e6 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,5 @@ /#sweep.pl# /sweeprolog-tests.elc /sweeprolog.elc +/.dir-locals.el +/NEWS.md diff --git a/NEWS.org b/NEWS.org index 8bc9bff..cae8769 100644 --- a/NEWS.org +++ b/NEWS.org @@ -11,17 +11,35 @@ SWI-Prolog in Emacs. For further details, please consult the manual: . +* Version 0.8.11 on 2022-11-21 + +** ~sweeprolog-new-predicate-location-function~ signature changed + +The function specified by ~sweeprolog-new-predicate-location-function~ +should now take three arguments, namely the functor, arity and neck of +the new predicate, instead of taking only the predicate indicator as a +sole argument. + +** ~sweeprolog-insert-term-dwim~ now supports defining undefined DCG non-terminals + +Defining a previously undefined predicate with +~sweeprolog-insert-term-dwim~ now analyzes the context of the undefined +predicate invocation to determine if it is expected to be a DCG +non-terminal, in which case an appropriate non-terminal definition is +inserted instead of a regular predicate. + * Version 0.8.10 on 2022-11-21 ** ~sweeprolog-top-level-signal-current~ now calls ~trace/0~ by default -Calling ~sweeprolog-top-level-signal-current~ (e.g. with ~C-c C-c~) now -signals the top-level thread with the goal specified by the user -option ~sweeprolog-top-level-signal-default-goal~, instead of prompting -for a goal. By default this user option is set to ~"trace"~, causing -the top-level thread to enter trace mode. To have -~sweeprolog-top-level-signal-current~ prompt for a different goal on -invocation, call it with a prefix argument, i.e. ~C-u C-c C-c~. +Calling ~sweeprolog-top-level-signal-current~ (~C-c C-c~ in +~sweeprolog-top-level~ buffers) now signals the top-level thread with +the goal specified by the user option +~sweeprolog-top-level-signal-default-goal~, instead of prompting for a +goal. By default this user option is set to ~"trace"~, causing the +top-level thread to enter trace mode. To have +~sweeprolog-top-level-signal-current~ prompt for a different goal +instead, call it with a prefix argument, i.e. ~C-u C-c C-c~. ** Fixes @@ -32,7 +50,7 @@ invocation, call it with a prefix argument, i.e. ~C-u C-c C-c~. * Version 0.8.9 on 2022-11-19 -** Predicate completions now use holes for arguments +** Predicate completions now uses holes for arguments When completing a predicate with ~completion-at-point~ (~C-M-i~) and choosing a predicate that takes arguments, holes are inserted is place diff --git a/sweep.pl b/sweep.pl index 84f6dbc..1342485 100644 --- a/sweep.pl +++ b/sweep.pl @@ -481,6 +481,9 @@ sweep_color_normalized_(Offset, syntax_error, [Message0,Start0-End0|_], ["syntax sweep_color_normalized_(_, comment, [Kind0|_], ["comment"|Kind]) :- !, atom_string(Kind0, Kind). +sweep_color_normalized_(_, dcg, [Kind0|_], ["dcg"|Kind]) :- + !, + atom_string(Kind0, Kind). sweep_color_normalized_(_, qq_content, [Type0|_], ["qq_content"|Type]) :- !, atom_string(Type0, Type). diff --git a/sweeprolog-tests.el b/sweeprolog-tests.el index 4fa1a4f..0a0f723 100644 --- a/sweeprolog-tests.el +++ b/sweeprolog-tests.el @@ -434,6 +434,92 @@ foo :- bar. foo :- Body. ")))) +(ert-deftest dwim-define-nested-phrase- () + "Tests complex undefined predicate scenario" + (let ((temp (make-temp-file "sweeprolog-test" + nil + "pl" + " +foo --> {baz, phrase(bar, Baz)}. +" + ))) + (find-file-literally temp) + (sweeprolog-mode) + (goto-char (point-max)) + (backward-word 2) + (sweeprolog-insert-term-dwim) + (call-interactively #'kill-region) + (insert "foo") + (should (string= (buffer-string) + " +foo --> {baz, phrase(bar, Baz)}. + +bar --> foo. +" + )))) + +(ert-deftest dwim-define-phrase-non-terminal () + "Tests defining an undefined DCG non-terminal from a clause." + (let ((temp (make-temp-file "sweeprolog-test" + nil + "pl" + " +foo :- phrase(bar, Baz). +" + ))) + (find-file-literally temp) + (sweeprolog-mode) + (goto-char (point-max)) + (backward-word 2) + (sweeprolog-insert-term-dwim) + (call-interactively #'kill-region) + (insert "foo") + (should (string= (buffer-string) + " +foo :- phrase(bar, Baz). + +bar --> foo. +" + )))) + +(ert-deftest dwim-define-braces-predicate () + "Tests defining an undefined predicate from a DCG non-terminal." + (with-temp-buffer + (sweeprolog-mode) + (insert " +foo --> {bar}. +") + (backward-word) + (sweeprolog-insert-term-dwim) + (call-interactively #'kill-region) + (insert "foo") + (should (string= (buffer-string) + " +foo --> {bar}. + +bar :- foo. +" + )))) + +(ert-deftest dwim-define-non-terminal () + "Tests defining an undefined DCG non-terminal." + (with-temp-buffer + (sweeprolog-mode) + (insert " +foo --> bar. +") + (backward-word) + (sweeprolog-insert-term-dwim) + (call-interactively #'kill-region) + (insert "foo") + (should (string= (buffer-string) + " +foo --> bar. + +bar --> foo. +" + )))) + (ert-deftest dwim-define-predicate () "Tests defining a new predicate with `sweeprolog-insert-term-dwim'." (with-temp-buffer @@ -450,7 +536,8 @@ foo :- bar. foo :- bar. bar :- foo. -")))) +" + )))) (ert-deftest dwim-define-predicate-above () @@ -475,7 +562,8 @@ bar :- foo. %! foo is det. foo :- bar. -")))) +" + )))) (ert-deftest end-of-top-term-with-univ () "Tests detecting the fullstop in presence of `=..'." diff --git a/sweeprolog.el b/sweeprolog.el index dc48979..0dce785 100644 --- a/sweeprolog.el +++ b/sweeprolog.el @@ -314,10 +314,17 @@ clause." (defcustom sweeprolog-new-predicate-location-function #'sweeprolog-default-new-predicate-location "Function used to choose a location for a new predicate definition. -It should take one argument, the name of the new predicate given -as a string, and move point to a suitable position in the current -buffer where the new predicate defintion should be inserted." - :package-version '((sweeprolog "0.8.6")) + +It should take three arguments describing the new predicate, +FUNCTOR, ARITY and NECK, and move point to a suitable position in +the current buffer where the new predicate defintion should be +inserted. + +FUNCTOR is the predicate name given as a string, ARITY is its +arity given as an integer, and NECK is the neck operator of the +predicate (e.g. \":-\" for regular clauses and \"-->\" for DCG +non-terminals)." + :package-version '((sweeprolog "0.8.11")) :type '(choice (const :tag "Below Current Predicate" sweeprolog-default-new-predicate-location) (const :tag "Above Current Predicate" @@ -2626,22 +2633,44 @@ instead." neck) t)) -(defun sweeprolog-default-new-predicate-location (_pred) +(defun sweeprolog-default-new-predicate-location (&rest _) (sweeprolog-end-of-predicate-at-point)) -(defun sweeprolog-new-predicate-location-above-current (_pred) +(defun sweeprolog-new-predicate-location-above-current (&rest _) (sweeprolog-beginning-of-predicate-at-point) (let ((last (or (caddr (sweeprolog-last-token-boundaries)) (point-min)))) (while (re-search-backward (rx bol "%" (or "%" "!")) last t)))) (defun sweeprolog-maybe-define-predicate (point _kind _beg _end) - (when-let ((pred (sweeprolog-identifier-at-point point))) - (unless (sweeprolog-predicate-properties pred) - (funcall sweeprolog-new-predicate-location-function pred) - (let ((functor-arity (sweeprolog--mfn-to-functor-arity pred))) - (sweeprolog-insert-clause (car functor-arity) - (cdr functor-arity))) + (let ((functor nil) + (arity nil) + (neck ":-")) + (sweeprolog-analyze-term-at-point + (lambda (beg end arg) + (pcase arg + (`("goal_term" "undefined" ,f ,a) + (when (<= beg point end) + (setq functor f + arity a))) + ("neck" + (setq neck + (buffer-substring-no-properties beg end))) + (`("goal_term" "built_in" "phrase" ,a) + (when (and (<= beg point end) + (member a '(2 3)) + (string= neck ":-")) + (setq neck "-->"))) + (`("dcg" . "plain") + (when (and (<= beg point end) + (string= neck "-->")) + (setq neck ":-")))))) + (when functor + (funcall sweeprolog-new-predicate-location-function + functor arity neck) + (sweeprolog-insert-clause functor + (- arity (if (string= neck "-->") 2 0)) + neck) t))) (defun sweeprolog-insert-term-dwim (&optional point) -- 2.39.2