(defvar comint-prompt-regexp)
(defvar comint-process-echoes)
+(require 'smie)
(defgroup prolog nil
"Major mode for editing and running Prolog under Emacs."
(defvar prolog-mode-abbrev-table nil)
(define-abbrev-table 'prolog-mode-abbrev-table ())
+(defun prolog-smie-forward-token ()
+ (forward-comment (point-max))
+ (buffer-substring-no-properties
+ (point)
+ (progn (cond
+ ((looking-at "[!;]") (forward-char 1))
+ ((not (zerop (skip-chars-forward "#&*+-./:<=>?@\\^`~"))))
+ ((not (zerop (skip-syntax-forward "w_'"))))
+ ;; In case of non-ASCII punctuation.
+ ((not (zerop (skip-syntax-forward ".")))))
+ (point))))
+
+(defun prolog-smie-backward-token ()
+ (forward-comment (- (point-max)))
+ (buffer-substring-no-properties
+ (point)
+ (progn (cond
+ ((memq (char-before) '(?! ?\;)) (forward-char -1))
+ ((not (zerop (skip-chars-backward "#&*+-./:<=>?@\\^`~"))))
+ ((not (zerop (skip-syntax-backward "w_'"))))
+ ;; In case of non-ASCII punctuation.
+ ((not (zerop (skip-syntax-backward ".")))))
+ (point))))
+
+(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 (7.14.10 op/3). 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.")
+
+(defun prolog-smie-rules (kind token)
+ (pcase (cons kind token)
+ (`(:elem . basic) prolog-indent-width)
+ (`(:after . ".") 0) ;; To work around smie-closer-alist.
+ (`(:after . ,(or `":-" `"->")) prolog-indent-width)))
+
(defun prolog-mode-variables ()
(make-local-variable 'paragraph-separate)
(setq paragraph-separate (concat "%%\\|$\\|" page-delimiter)) ;'%%..'
(setq paragraph-ignore-fill-prefix t)
(make-local-variable 'imenu-generic-expression)
(setq imenu-generic-expression '((nil "^\\sw+" 0)))
- (make-local-variable 'indent-line-function)
- (setq indent-line-function 'prolog-indent-line)
+
+ ;; Setup SMIE.
+ (smie-setup prolog-smie-op-levels #'prolog-smie-rules
+ :forward-token #'prolog-smie-forward-token
+ :backward-token #'prolog-smie-backward-token)
+ (set (make-local-variable 'smie-blink-matching-triggers) '(?.))
+ (set (make-local-variable 'smie-closer-alist) '((t . ".")))
+ (add-hook 'post-self-insert-hook #'smie-blink-matching-open 'append 'local)
+ ;; There's no real closer in Prolog anyway.
+ (set (make-local-variable 'smie-blink-matching-inners) t)
+
(make-local-variable 'comment-start)
(setq comment-start "%")
(make-local-variable 'comment-start-skip)
(define-key map "\C-c\C-l" 'inferior-prolog-load-file)
(define-key map "\C-c\C-z" 'switch-to-prolog)
map))
-
+
(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.
nil nil nil
beginning-of-line)))
-(defun prolog-indent-line ()
- "Indent current line as Prolog code.
-With argument, indent any additional lines of the same clause
-rigidly along with this one (not yet)."
- (interactive "p")
- (let ((indent (prolog-indent-level))
- (pos (- (point-max) (point))))
- (beginning-of-line)
- (indent-line-to indent)
- (if (> (- (point-max) pos) (point))
- (goto-char (- (point-max) pos)))))
-
-(defun prolog-indent-level ()
- "Compute Prolog indentation level."
- (save-excursion
- (beginning-of-line)
- (skip-chars-forward " \t")
- (cond
- ((looking-at "%%%") 0) ;Large comment starts
- ((looking-at "%[^%]") comment-column) ;Small comment starts
- ((bobp) 0) ;Beginning of buffer
- (t
- (let ((empty t) ind more less)
- (if (looking-at ")")
- (setq less t) ;Find close
- (setq less nil))
- ;; See previous indentation
- (while empty
- (forward-line -1)
- (beginning-of-line)
- (if (bobp)
- (setq empty nil)
- (skip-chars-forward " \t")
- (if (not (or (looking-at "%[^%]") (looking-at "\n")))
- (setq empty nil))))
- (if (bobp)
- (setq ind 0) ;Beginning of buffer
- (setq ind (current-column))) ;Beginning of clause
- ;; See its beginning
- (if (looking-at "%%[^%]")
- ind
- ;; Real prolog code
- (if (looking-at "(")
- (setq more t) ;Find open
- (setq more nil))
- ;; See its tail
- (end-of-prolog-clause)
- (or (bobp) (forward-char -1))
- (cond ((looking-at "[,(;>]")
- (if (and more (looking-at "[^,]"))
- (+ ind prolog-indent-width) ;More indentation
- (max tab-width ind))) ;Same indentation
- ((looking-at "-") tab-width) ;TAB
- ((or less (looking-at "[^.]"))
- (max (- ind prolog-indent-width) 0)) ;Less indentation
- (t 0)) ;No indentation
- )))
- )))
-
(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))))
+ (let* ((eolpos (line-end-position)))
(if (re-search-forward comment-start-skip eolpos 'move)
(goto-char (match-beginning 0)))
(skip-chars-backward " \t")))
(provide 'prolog)
-;; arch-tag: f3ec6748-1272-4ab6-8826-c50cb1607636
;;; prolog.el ends here