commit - 6c694b4198f401e793237afa8578af6f4631f66d
commit + 61b0b3831af8c08b51a76f348bfa7247603c8921
blob - 8dae6ad55be7a655071467da291e4ba26e741059
blob + d0eb239c8ac0030835144733d12bfe0619bac6df
--- a68-mode.el
+++ a68-mode.el
;;; Code:
-(require 'syntax)
(require 'font-lock)
+(require 'smie)
+(require 'syntax)
(eval-when-compile
(require 'rx))
(defvar a68-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-j") #'newline-and-indent)
- (define-key map (kbd "RET") #'a68-electric-terminate-line)
+ ;; (define-key map (kbd "RET") #'a68-electric-terminate-line)
map)
"Keymap for Algol 68 major mode.")
"Check if inside a comment."
(nth 4 (syntax-ppss)))
-;; Indentation rules:
-;;
-;; - If we are at the beginning of the buffer, or looking at some
-;; indent-0 content, indent to column 0.
-;;
-;; - If we are currently at an END, ), FI or OD, then de-indent
-;; relative to the previous line.
-;;
-;; - If we first see and "end line" before our current line,
-;; then we should indent our current line to the same indentation as
-;; the end line.
-;;
-;; - If we first see a "start line" like IF, then we need to increase
-;; our indentation relative to that start line.
-;;
-;; - If into a balanced expression, we should indent to the column
-;; where the start of the innermost parenthetical group.
-;;
-;; - If none of the above apply, then do not indent at all.
+(defvar a68--keywords-regexp
+ (regexp-opt '("+" "*" ";" ">" "<" ":=" "=")))
-(defun a68-indent-line ()
- "Indent current line as Algol 68 code."
- (interactive)
- (let ((case-fold-search nil))
- (save-excursion
- (beginning-of-line)
- (if (nth 1 (syntax-ppss)) ; Check for rule 5
- (let ((offset (save-excursion (goto-char (+ (nth 1 (syntax-ppss)) 1))
- (current-column))))
- (indent-line-to offset))
- (if (or (bobp) ; Check for rule 1
- (looking-at "^[ \t]*\\<\\(KEEP\\|FINISH\\|DECS\\|USE\\|PROGRAM\\)\\>"))
- (indent-line-to 0)
- (let ((not-indented t)
- (begin-indent-re "^[ \t]*\\<\\(PAR\\|BEGIN\\|KEEP\\|IF\\|DO\\|ELSE\\|ELIF\\|THEN\\)")
- (deindent-line-re "^[ \t]*\\<\\(END\\|FI\\|OD\\|ELSE\\|ELIF\\)\\>")
- (eqindent-line-re "^[ \t]*\\<\\(THEN\\)\\>")
- (end-line-re "^[ \t]*\\(END\\|FI\\|OD\\)")
- cur-indent)
- (if (looking-at eqindent-line-re)
- (save-excursion
- (forward-line -1)
- (setq cur-indent (current-indentation)))
- (if (looking-at deindent-line-re) ; Check for rule 2
- (progn
- (save-excursion
- (forward-line -1)
- (setq cur-indent (- (current-indentation) a68-indent-level)))
- (when (< cur-indent 0)
- (setq cur-indent 0)))
- (save-excursion
- (while not-indented
- (forward-line -1)
- (if (looking-at end-line-re) ; Check for rule 3
- (progn
- (setq cur-indent (current-indentation))
- (setq not-indented nil))
- ;; Check for rule 4
- (if (looking-at begin-indent-re)
- (progn
- (setq cur-indent (+ (current-indentation) a68-indent-level))
- (setq not-indented nil))
- (when (bobp) ; Check for rule 5
- (setq not-indented nil))))))))
- (if cur-indent
- (indent-line-to cur-indent)
- ;; If we didn't see an indentation hint, then allow no
- ;; indentation.
- (indent-line-to 0)))))))
- (when (< (current-column) (current-indentation))
- (move-to-column (current-indentation))))
+(defvar a68--smie-grammar
+ (smie-prec2->grammar
+ (smie-bnf->prec2 '((id)
+ (inst ("BEGIN" inst "END")
+ ("(" inst ")")
+ ("IF" insts "THEN" insts "ELSE" insts "FI")
+ (id "=" exp)
+ (id ":=" exp)
+ (exp))
+ (insts (insts ";" insts)
+ (inst))
+ (exp ("-" exp)
+ (exp "+" exp)
+ (exp "*" exp)
+ (exp "/" exp))
+ (exps (exps "," exps)
+ (inst)
+ (exp)))
+ '((assoc ";"))
+ '((assoc ","))
+ '((assoc "+") (assoc "-") (assoc "*") (assoc "/")))))
+(defun a68--smie-rules (kind token)
+ (pcase (cons kind token)
+ (`(:elem . basic) a68-indent-level)
+ ;; (`(,_ . ",") (smie-rule-separator kind))
+ (`(,_ . ",") (smie-rule-separator kind))
+ (`(,_ . ";") (when (smie-rule-parent-p)
+ (smie-rule-parent)))
+ (`(:after . ":=") a68-indent-level)
+ (`(:after . "=") a68-indent-level)
+ (`(:before . ,(or `"BEGIN" '"(")) (when (smie-rule-hanging-p)
+ (smie-rule-parent)))
+ (`(:before . "IF")
+ (and (not (smie-rule-bolp))
+ (smie-rule-prev-p "ELSE")
+ (smie-rule-parent)))))
+
+(defun a68--smie-forward-token ()
+ (forward-comment (point-max))
+ (cond
+ ((looking-at a68--keywords-regexp)
+ (goto-char (match-end 0))
+ (match-string-no-properties 0))
+ (t (buffer-substring-no-properties (point)
+ (progn (skip-syntax-forward "w_")
+ (point))))))
+
+(defun a68--smie-backward-token ()
+ (forward-comment (- (point)))
+ (cond
+ ((looking-back a68--keywords-regexp (- (point) 2) t)
+ (goto-char (match-beginning 0))
+ (match-string-no-properties 0))
+ (t (buffer-substring-no-properties (point)
+ (progn (skip-syntax-backward "w_")
+ (point))))))
+
(defvar a68-mode-syntax-table
(let ((st (make-syntax-table)))
(modify-syntax-entry ?# "<" st)
(modify-syntax-entry ?\) ")(" st)
st))
-(defconst a68-autoindent-lines-re
- (rx word-start
- (or "BEGIN" "END" "ELSE" "ELIF" "DO" "OD" "CASE" "ESAC" "IN" "OUT")
- word-end))
-
-(defun a68-electric-terminate-line ()
- "Terminate line and indent next line."
- (interactive)
- ;; First, check if current line should be indented
- (save-excursion
- (beginning-of-line)
- (skip-chars-forward " \t")
- (when (looking-at a68-autoindent-lines-re)
- (a68-indent-line)))
- (delete-horizontal-space) ; Removes triling whitespaces
- (newline)
- ;; Indent next line if we are not in a string
- (unless (a68-within-string)
- (a68-indent-line)))
-
(defvar a68-mode-abbrev-table nil
"Abbreviation table used in `a68-mode' buffers.")
"Major mode for editing Alogl68 files."
:abbrev-table a68-mode-abbrev-table
(setq-local font-lock-defaults '(a68-font-lock-keywords))
- (setq-local indent-line-function #'a68-indent-line)
+ (smie-setup a68--smie-grammar #'a68--smie-rules
+ :forward-token #'a68--smie-forward-token
+ :backward-token #'a68--smie-backward-token)
(setq-local comment-start a68-comment-style)
(setq-local comment-end a68-comment-style)
(setq-local syntax-propertize-function