commit 61b0b3831af8c08b51a76f348bfa7247603c8921 from: Omar Polo date: Mon Nov 08 09:29:13 2021 UTC start rewriting the indentation engine with SMIE 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