Commit Diff


commit - 6c694b4198f401e793237afa8578af6f4631f66d
commit + 61b0b3831af8c08b51a76f348bfa7247603c8921
blob - 8dae6ad55be7a655071467da291e4ba26e741059
blob + d0eb239c8ac0030835144733d12bfe0619bac6df
--- a68-mode.el
+++ a68-mode.el
@@ -41,8 +41,9 @@
 
 ;;; Code:
 
-(require 'syntax)
 (require 'font-lock)
+(require 'smie)
+(require 'syntax)
 
 (eval-when-compile
   (require 'rx))
@@ -70,7 +71,7 @@
 (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.")
 
@@ -110,78 +111,67 @@
   "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)
@@ -192,26 +182,6 @@
     (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.")
 
@@ -223,7 +193,9 @@
   "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