prolog.el 14.7 KB
Newer Older
Eric S. Raymond's avatar
Eric S. Raymond committed
1 2
;;; prolog.el --- major mode for editing and running Prolog under Emacs

3
;; Copyright (C) 1986, 1987, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
Glenn Morris's avatar
Glenn Morris committed
4
;;   2008, 2009, 2010  Free Software Foundation, Inc.
Eric S. Raymond's avatar
Eric S. Raymond committed
5

Richard M. Stallman's avatar
Richard M. Stallman committed
6
;; Author: Masanobu UMEDA <umerin@mse.kyutech.ac.jp>
Eric S. Raymond's avatar
Eric S. Raymond committed
7
;; Keywords: languages
Eric S. Raymond's avatar
Eric S. Raymond committed
8

root's avatar
root committed
9 10
;; This file is part of GNU Emacs.

11
;; GNU Emacs is free software: you can redistribute it and/or modify
root's avatar
root committed
12
;; it under the terms of the GNU General Public License as published by
13 14
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
root's avatar
root committed
15 16 17 18 19 20 21

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
22
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
root's avatar
root committed
23

24 25 26 27
;;; Commentary:

;; This package provides a major mode for editing Prolog.  It knows
;; about Prolog syntax and comments, and can send regions to an inferior
28
;; Prolog interpreter process.  Font locking is tuned towards GNU Prolog.
29

Eric S. Raymond's avatar
Eric S. Raymond committed
30 31
;;; Code:

32 33
(defvar comint-prompt-regexp)
(defvar comint-process-echoes)
34

35
(defgroup prolog nil
36
  "Major mode for editing and running Prolog under Emacs."
37
  :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
38 39
  :group 'languages)

40

41
(defcustom prolog-program-name
Stefan Monnier's avatar
Stefan Monnier committed
42
  (let ((names '("prolog" "gprolog" "swipl")))
43 44 45 46
    (while (and names
		(not (executable-find (car names))))
      (setq names (cdr names)))
    (or (car names) "prolog"))
Stefan Monnier's avatar
Stefan Monnier committed
47
  "Program name for invoking an inferior Prolog with `run-prolog'."
48 49
  :type 'string
  :group 'prolog)
root's avatar
root committed
50

51
(defcustom prolog-consult-string "reconsult(user).\n"
Stefan Monnier's avatar
Stefan Monnier committed
52
  "(Re)Consult mode (for C-Prolog and Quintus Prolog). "
53 54
  :type 'string
  :group 'prolog)
root's avatar
root committed
55

56
(defcustom prolog-compile-string "compile(user).\n"
Stefan Monnier's avatar
Stefan Monnier committed
57
  "Compile mode (for Quintus Prolog)."
58 59
  :type 'string
  :group 'prolog)
root's avatar
root committed
60

61
(defcustom prolog-eof-string "end_of_file.\n"
Stefan Monnier's avatar
Stefan Monnier committed
62
  "String that represents end of file for Prolog.
63
When nil, send actual operating system end of file."
64 65
  :type 'string
  :group 'prolog)
root's avatar
root committed
66

67 68 69 70
(defcustom prolog-indent-width 4
  "Level of indentation in Prolog buffers."
  :type 'integer
  :group 'prolog)
root's avatar
root committed
71

72 73 74 75 76 77 78 79 80 81
(defvar prolog-font-lock-keywords
  '(("\\(#[<=]=>\\|:-\\)\\|\\(#=\\)\\|\\(#[#<>\\/][=\\/]*\\|!\\)"
     0 font-lock-keyword-face)
    ("\\<\\(is\\|write\\|nl\\|read_\\sw+\\)\\>"
     1 font-lock-keyword-face)
    ("^\\(\\sw+\\)\\s-*\\((\\(.+\\))\\)*"
     (1 font-lock-function-name-face)
     (3 font-lock-variable-name-face)))
  "Font-lock keywords for Prolog mode.")

82
(defvar prolog-mode-syntax-table
root's avatar
root committed
83 84 85
  (let ((table (make-syntax-table)))
    (modify-syntax-entry ?_ "w" table)
    (modify-syntax-entry ?\\ "\\" table)
86 87
    (modify-syntax-entry ?/ ". 14" table)
    (modify-syntax-entry ?* ". 23" table)
root's avatar
root committed
88 89 90 91
    (modify-syntax-entry ?+ "." table)
    (modify-syntax-entry ?- "." table)
    (modify-syntax-entry ?= "." table)
    (modify-syntax-entry ?% "<" table)
92
    (modify-syntax-entry ?\n ">" table)
root's avatar
root committed
93 94 95
    (modify-syntax-entry ?< "." table)
    (modify-syntax-entry ?> "." table)
    (modify-syntax-entry ?\' "\"" table)
96
    table))
root's avatar
root committed
97

98
(defvar prolog-mode-abbrev-table nil)
root's avatar
root committed
99 100
(define-abbrev-table 'prolog-mode-abbrev-table ())

101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
(defconst prolog-smie-op-levels
  ;; Rather than construct the operator levels table from the BNF,
  ;; we directly provide the operator precedences from GNU Prolog's
  ;; manual.  The only problem is that GNU Prolog's manual uses
  ;; precedence levels in the opposite sense (higher numbers bind less
  ;; tightly) than SMIE, so we use negative numbers.
  '(("." -10000 -10000)
    (":-" -1200 -1200)
    ("-->" -1200 -1200)
    (";" -1100 -1100)
    ("->" -1050 -1050)
    ("," -1000 -1000)
    ("\\+" -900 -900)
    ("=" -700 -700)
    ("\\=" -700 -700)
    ("=.." -700 -700)
    ("==" -700 -700)
    ("\\==" -700 -700)
    ("@<" -700 -700)
    ("@=<" -700 -700)
    ("@>" -700 -700)
    ("@>=" -700 -700)
    ("is" -700 -700)
    ("=:=" -700 -700)
    ("=\\=" -700 -700)
    ("<" -700 -700)
    ("=<" -700 -700)
    (">" -700 -700)
    (">=" -700 -700)
    (":" -600 -600)
    ("+" -500 -500)
    ("-" -500 -500)
    ("/\\" -500 -500)
    ("\\/" -500 -500)
    ("*" -400 -400)
    ("/" -400 -400)
    ("//" -400 -400)
    ("rem" -400 -400)
    ("mod" -400 -400)
    ("<<" -400 -400)
    (">>" -400 -400)
    ("**" -200 -200)
    ("^" -200 -200)
    ;; Prefix
    ;; ("+" 200 200)
    ;; ("-" 200 200)
    ;; ("\\" 200 200)
    )
  "Precedence levels of infix operators.")

(defconst prolog-smie-indent-rules
  '((":-")
    ("->"))
  "Prolog indentation rules.")

root's avatar
root committed
156 157
(defun prolog-mode-variables ()
  (make-local-variable 'paragraph-separate)
158
  (setq paragraph-separate (concat "%%\\|$\\|" page-delimiter)) ;'%%..'
root's avatar
root committed
159 160
  (make-local-variable 'paragraph-ignore-fill-prefix)
  (setq paragraph-ignore-fill-prefix t)
Karl Heuer's avatar
Karl Heuer committed
161
  (make-local-variable 'imenu-generic-expression)
162
  (setq imenu-generic-expression '((nil "^\\sw+" 0)))
163 164 165 166
  (smie-setup prolog-smie-op-levels prolog-smie-indent-rules)
  (set (make-local-variable 'forward-sexp-function)
       'smie-forward-sexp-command)
  (set (make-local-variable 'smie-indent-basic) prolog-indent-width)
root's avatar
root committed
167 168 169
  (make-local-variable 'comment-start)
  (setq comment-start "%")
  (make-local-variable 'comment-start-skip)
170 171 172
  (setq comment-start-skip "\\(?:%+\\|/\\*+\\)[ \t]*")
  (make-local-variable 'comment-end-skip)
  (setq comment-end-skip "[ \t]*\\(\n\\|\\*+/\\)")
root's avatar
root committed
173
  (make-local-variable 'comment-column)
174
  (setq comment-column 48))
root's avatar
root committed
175

176 177 178
(defvar prolog-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map "\e\C-x" 'prolog-consult-region)
Stefan Monnier's avatar
Stefan Monnier committed
179 180
    (define-key map "\C-c\C-l" 'inferior-prolog-load-file)
    (define-key map "\C-c\C-z" 'switch-to-prolog)
181
    map))
Stefan Monnier's avatar
Stefan Monnier committed
182 183 184 185 186 187 188 189 190 191 192 193
 
(easy-menu-define prolog-mode-menu prolog-mode-map "Menu for Prolog mode."
  ;; Mostly copied from scheme-mode's menu.
  ;; Not tremendously useful, but it's a start.
  '("Prolog"
    ["Indent line" indent-according-to-mode t]
    ["Indent region" indent-region t]
    ["Comment region" comment-region t]
    ["Uncomment region" uncomment-region t]
    "--"
    ["Run interactive Prolog session" run-prolog t]
    ))
root's avatar
root committed
194

Jim Blandy's avatar
Jim Blandy committed
195
;;;###autoload
196
(define-derived-mode prolog-mode prog-mode "Prolog"
root's avatar
root committed
197 198 199 200
  "Major mode for editing Prolog code for Prologs.
Blank lines and `%%...' separate paragraphs.  `%'s start comments.
Commands:
\\{prolog-mode-map}
201
Entry to this mode calls the value of `prolog-mode-hook'
root's avatar
root committed
202 203
if that value is non-nil."
  (prolog-mode-variables)
Stefan Monnier's avatar
Stefan Monnier committed
204
  (set (make-local-variable 'comment-add) 1)
205 206
  (setq font-lock-defaults '(prolog-font-lock-keywords
                             nil nil nil
207
                             beginning-of-line)))
root's avatar
root committed
208 209 210 211 212 213 214 215 216 217 218 219

(defun end-of-prolog-clause ()
  "Go to end of clause in this line."
  (beginning-of-line 1)
  (let* ((eolpos (save-excursion (end-of-line) (point))))
    (if (re-search-forward comment-start-skip eolpos 'move)
	(goto-char (match-beginning 0)))
    (skip-chars-backward " \t")))

;;;
;;; Inferior prolog mode
;;;
220 221 222 223
(defvar inferior-prolog-mode-map
  (let ((map (make-sparse-keymap)))
    ;; This map will inherit from `comint-mode-map' when entering
    ;; inferior-prolog-mode.
224 225
    (define-key map [remap self-insert-command]
      'inferior-prolog-self-insert-command)
226 227 228 229
    map))

(defvar inferior-prolog-mode-syntax-table prolog-mode-syntax-table)
(defvar inferior-prolog-mode-abbrev-table prolog-mode-abbrev-table)
root's avatar
root committed
230

231 232 233 234 235
(defvar inferior-prolog-error-regexp-alist
  ;; GNU Prolog used to not follow the GNU standard format.
  '(("^\\(.*?\\):\\([0-9]+\\) error: .*(char:\\([0-9]+\\)" 1 2 3)
    gnu))

236 237 238 239
(declare-function comint-mode "comint")
(declare-function comint-send-string "comint" (process string))
(declare-function comint-send-region "comint" (process start end))
(declare-function comint-send-eof "comint" ())
240
(defvar compilation-error-regexp-alist)
241

242
(define-derived-mode inferior-prolog-mode comint-mode "Inferior Prolog"
root's avatar
root committed
243 244 245 246 247
  "Major mode for interacting with an inferior Prolog process.

The following commands are available:
\\{inferior-prolog-mode-map}

248 249 250
Entry to this mode calls the value of `prolog-mode-hook' with no arguments,
if that value is non-nil.  Likewise with the value of `comint-mode-hook'.
`prolog-mode-hook' is called after `comint-mode-hook'.
root's avatar
root committed
251

252 253
You can send text to the inferior Prolog from other buffers using the commands
`process-send-region', `process-send-string' and \\[prolog-consult-region].
root's avatar
root committed
254 255 256 257

Commands:
Tab indents for Prolog; with argument, shifts rest
 of expression rigidly with the current line.
258 259
Paragraphs are separated only by blank lines and '%%'.
'%'s start comments.
root's avatar
root committed
260 261 262 263 264 265

Return at end of buffer sends line as input.
Return not at end copies rest of line to end and sends it.
\\[comint-kill-input] and \\[backward-kill-word] are kill commands, imitating normal Unix input editing.
\\[comint-interrupt-subjob] interrupts the shell or its current subjob if any.
\\[comint-stop-subjob] stops. \\[comint-quit-subjob] sends quit signal."
266
  (setq comint-prompt-regexp "^| [ ?][- ] *")
267 268 269
  (set (make-local-variable 'compilation-error-regexp-alist)
       inferior-prolog-error-regexp-alist)
  (compilation-shell-minor-mode)
270
  (prolog-mode-variables))
root's avatar
root committed
271

Stefan Monnier's avatar
Stefan Monnier committed
272 273
(defvar inferior-prolog-buffer nil)

274 275 276 277 278 279
(defvar inferior-prolog-flavor 'unknown
  "Either a symbol or a buffer position offset by one.
If a buffer position, the flavor has not been determined yet and
it is expected that the process's output has been or will
be inserted at that position plus one.")

Stefan Monnier's avatar
Stefan Monnier committed
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
(defun inferior-prolog-run (&optional name)
  (with-current-buffer (make-comint "prolog" (or name prolog-program-name))
    (inferior-prolog-mode)
    (setq-default inferior-prolog-buffer (current-buffer))
    (make-local-variable 'inferior-prolog-buffer)
    (when (and name (not (equal name prolog-program-name)))
      (set (make-local-variable 'prolog-program-name) name))
    (set (make-local-variable 'inferior-prolog-flavor)
         ;; Force re-detection.
         (let* ((proc (get-buffer-process (current-buffer)))
                (pmark (and proc (marker-position (process-mark proc)))))
           (cond
            ((null pmark) (1- (point-min)))
            ;; The use of insert-before-markers in comint.el together with
            ;; the potential use of comint-truncate-buffer in the output
            ;; filter, means that it's difficult to reliably keep track of
            ;; the buffer position where the process's output started.
            ;; If possible we use a marker at "start - 1", so that
            ;; insert-before-marker at `start' won't shift it.  And if not,
            ;; we fall back on using a plain integer.
            ((> pmark (point-min)) (copy-marker (1- pmark)))
            (t (1- pmark)))))
    (add-hook 'comint-output-filter-functions
              'inferior-prolog-guess-flavor nil t)))

(defun inferior-prolog-process (&optional dontstart)
  (or (and (buffer-live-p inferior-prolog-buffer)
           (get-buffer-process inferior-prolog-buffer))
      (unless dontstart
        (inferior-prolog-run)
        ;; Try again.
        (inferior-prolog-process))))

313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
(defun inferior-prolog-guess-flavor (&optional ignored)
  (save-excursion
    (goto-char (1+ inferior-prolog-flavor))
    (setq inferior-prolog-flavor
          (cond
           ((looking-at "GNU Prolog") 'gnu)
           ((looking-at "Welcome to SWI-Prolog") 'swi)
           ((looking-at ".*\n") 'unknown) ;There's at least one line.
           (t inferior-prolog-flavor))))
  (when (symbolp inferior-prolog-flavor)
    (remove-hook 'comint-output-filter-functions
                 'inferior-prolog-guess-flavor t)
    (if (eq inferior-prolog-flavor 'gnu)
        (set (make-local-variable 'comint-process-echoes) t))))

Jim Blandy's avatar
Jim Blandy committed
328
;;;###autoload
Stefan Monnier's avatar
Stefan Monnier committed
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
(defalias 'run-prolog 'switch-to-prolog)
;;;###autoload
(defun switch-to-prolog (&optional name)
  "Run an inferior Prolog process, input and output via buffer *prolog*.
With prefix argument \\[universal-prefix], prompt for the program to use."
  (interactive
   (list (when current-prefix-arg
           (let ((proc (inferior-prolog-process 'dontstart)))
             (if proc
                 (if (yes-or-no-p "Kill current process before starting new one? ")
                     (kill-process proc)
                   (error "Abort")))
             (read-string "Run Prolog: " prolog-program-name)))))
  (unless (inferior-prolog-process 'dontstart)
    (inferior-prolog-run name))
  (pop-to-buffer inferior-prolog-buffer))
root's avatar
root committed
345

346 347 348 349 350 351 352 353 354 355 356 357 358
(defun inferior-prolog-self-insert-command ()
  "Insert the char in the buffer or pass it directly to the process."
  (interactive)
  (let* ((proc (get-buffer-process (current-buffer)))
         (pmark (and proc (marker-position (process-mark proc)))))
    (if (and (eq inferior-prolog-flavor 'gnu)
             pmark
             (null current-prefix-arg)
             (eobp)
             (eq (point) pmark)
             (save-excursion
               (goto-char (- pmark 3))
               (looking-at " \\? ")))
359 360 361 362
        ;; This is GNU prolog waiting to know whether you want more answers
        ;; or not (or abort, etc...).  The answer is a single char, not
        ;; a line, so pass this char directly rather than wait for RET to
        ;; send a whole line.
363
        (comint-send-string proc (string last-command-event))
364 365
      (call-interactively 'self-insert-command))))

root's avatar
root committed
366
(defun prolog-consult-region (compile beg end)
367 368
  "Send the region to the Prolog process made by \"M-x run-prolog\".
If COMPILE (prefix arg) is not nil, use compile mode rather than consult mode."
root's avatar
root committed
369
  (interactive "P\nr")
Stefan Monnier's avatar
Stefan Monnier committed
370 371 372 373 374 375
  (let ((proc (inferior-prolog-process)))
    (comint-send-string proc
                        (if compile prolog-compile-string
                          prolog-consult-string))
    (comint-send-region proc beg end)
    (comint-send-string proc "\n")		;May be unnecessary
root's avatar
root committed
376
    (if prolog-eof-string
Stefan Monnier's avatar
Stefan Monnier committed
377 378 379
        (comint-send-string proc prolog-eof-string)
      (with-current-buffer (process-buffer proc)
        (comint-send-eof))))) ;Send eof to prolog process.
root's avatar
root committed
380 381 382

(defun prolog-consult-region-and-go (compile beg end)
  "Send the region to the inferior Prolog, and switch to *prolog* buffer.
383
If COMPILE (prefix arg) is not nil, use compile mode rather than consult mode."
root's avatar
root committed
384 385
  (interactive "P\nr")
  (prolog-consult-region compile beg end)
386
  (pop-to-buffer inferior-prolog-buffer))
Eric S. Raymond's avatar
Eric S. Raymond committed
387

388 389 390
;; inferior-prolog-mode uses the autoloaded compilation-shell-minor-mode.
(declare-function compilation-forget-errors "compile" ())

Stefan Monnier's avatar
Stefan Monnier committed
391 392 393 394 395 396 397
(defun inferior-prolog-load-file ()
  "Pass the current buffer's file to the inferior prolog process."
  (interactive)
  (save-buffer)
  (let ((file buffer-file-name)
        (proc (inferior-prolog-process)))
    (with-current-buffer (process-buffer proc)
398
      (compilation-forget-errors)
Stefan Monnier's avatar
Stefan Monnier committed
399 400 401
      (comint-send-string proc (concat "['" (file-relative-name file) "'].\n"))
      (pop-to-buffer (current-buffer)))))

Richard M. Stallman's avatar
Richard M. Stallman committed
402 403
(provide 'prolog)

Stefan Monnier's avatar
Stefan Monnier committed
404
;; arch-tag: f3ec6748-1272-4ab6-8826-c50cb1607636
Eric S. Raymond's avatar
Eric S. Raymond committed
405
;;; prolog.el ends here