Blame


1 6c5ab754 2021-11-07 op ;;; a68-mode.el --- Major mode for editing Algol 68 code -*- lexical-binding: t; -*-
2 364d8905 2021-11-07 op
3 364d8905 2021-11-07 op ;; Copyright (C) 2011 Jose E. Marchesi
4 364d8905 2021-11-07 op ;; Copyright (C) 2021 Omar Polo <op@omarpolo.com>
5 364d8905 2021-11-07 op
6 364d8905 2021-11-07 op ;; Author: Jose E. Marchesi
7 364d8905 2021-11-07 op ;; Omar Polo <op@omarpolo.com>
8 364d8905 2021-11-07 op ;; Maintainer: Omar Polo
9 364d8905 2021-11-07 op ;; URL: https://git.omarpolo.com/a68-mode
10 364d8905 2021-11-07 op ;; Keywords: languages
11 364d8905 2021-11-07 op ;; Version: 0
12 364d8905 2021-11-07 op ;; Package-Requires: ((emacs "24.3"))
13 364d8905 2021-11-07 op
14 364d8905 2021-11-07 op ;; This file is NOT part of GNU Emacs.
15 364d8905 2021-11-07 op
16 364d8905 2021-11-07 op ;; This program is free software; you can redistribute it and/or modify
17 364d8905 2021-11-07 op ;; it under the terms of the GNU General Public License as published by
18 364d8905 2021-11-07 op ;; the Free Software Foundation; either version 3, or (at your option)
19 364d8905 2021-11-07 op ;; any later version.
20 364d8905 2021-11-07 op
21 364d8905 2021-11-07 op ;; This program is distributed in the hope that it will be useful,
22 364d8905 2021-11-07 op ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
23 364d8905 2021-11-07 op ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 364d8905 2021-11-07 op ;; GNU General Public License for more details.
25 364d8905 2021-11-07 op
26 364d8905 2021-11-07 op ;; You should have received a copy of the GNU General Public License
27 364d8905 2021-11-07 op ;; along with this program; see the file COPYING. If not, write to the
28 364d8905 2021-11-07 op ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
29 364d8905 2021-11-07 op ;; Boston, MA 02110-1301, USA.
30 364d8905 2021-11-07 op
31 364d8905 2021-11-07 op ;;; Commentary:
32 364d8905 2021-11-07 op
33 364d8905 2021-11-07 op ;; A major mode for editing Algol 68 code.
34 364d8905 2021-11-07 op ;;
35 bb4b3a62 2021-11-07 op ;; This is an improved and modernized version of the a68-mode written
36 bb4b3a62 2021-11-07 op ;; by Jose E. Marchesi. The original code was taken from
37 bb4b3a62 2021-11-07 op ;;
38 bb4b3a62 2021-11-07 op ;; https://github.com/lachrymology/me/blob/master/.emacs.d/extras/algol-mode.el
39 bb4b3a62 2021-11-07 op ;;
40 364d8905 2021-11-07 op ;; TODO: support quote and dot stropping.
41 364d8905 2021-11-07 op
42 364d8905 2021-11-07 op ;;; Code:
43 364d8905 2021-11-07 op
44 364d8905 2021-11-07 op (require 'syntax)
45 364d8905 2021-11-07 op (require 'font-lock)
46 364d8905 2021-11-07 op
47 364d8905 2021-11-07 op (eval-when-compile
48 364d8905 2021-11-07 op (require 'rx))
49 364d8905 2021-11-07 op
50 caaad6ea 2021-11-07 op (defgroup a68 nil
51 caaad6ea 2021-11-07 op "Major mode for editing Algol68 code."
52 caaad6ea 2021-11-07 op :prefix "a68-"
53 caaad6ea 2021-11-07 op :group 'languages)
54 364d8905 2021-11-07 op
55 caaad6ea 2021-11-07 op (defcustom a68-indent-level 3
56 caaad6ea 2021-11-07 op "Indentation step for Algol 68."
57 caaad6ea 2021-11-07 op :type 'integer
58 caaad6ea 2021-11-07 op :safe #'integerp)
59 caaad6ea 2021-11-07 op
60 18be0e13 2021-11-07 op (defcustom a68-comment-style "#"
61 29b924fd 2021-11-07 op "Default comment style used by e.g. `comment-dwim'."
62 18be0e13 2021-11-07 op :type '(choice (const "#")
63 18be0e13 2021-11-07 op (const "CO")
64 18be0e13 2021-11-07 op (const "COMMENT"))
65 29b924fd 2021-11-07 op :safe #'consp)
66 29b924fd 2021-11-07 op
67 364d8905 2021-11-07 op (defvar a68-mode-hook '()
68 364d8905 2021-11-07 op "Hook run when entering Algol68 mode.")
69 364d8905 2021-11-07 op
70 364d8905 2021-11-07 op (defvar a68-mode-map
71 8cd9fb9c 2021-11-07 op (let ((map (make-sparse-keymap)))
72 364d8905 2021-11-07 op (define-key map (kbd "C-j") #'newline-and-indent)
73 364d8905 2021-11-07 op (define-key map (kbd "RET") #'a68-electric-terminate-line)
74 364d8905 2021-11-07 op map)
75 364d8905 2021-11-07 op "Keymap for Algol 68 major mode.")
76 364d8905 2021-11-07 op
77 364d8905 2021-11-07 op (defconst a68-font-lock-keywords
78 364d8905 2021-11-07 op (list
79 364d8905 2021-11-07 op (cons (rx word-start
80 364d8905 2021-11-07 op (or "DECS" "PROGRAM" "CONTEXT" "USE" "FINISH" "KEEP"
81 364d8905 2021-11-07 op "ALIEN"
82 364d8905 2021-11-07 op "MODE" "OP" "PRIO" "PROC"
83 364d8905 2021-11-07 op "OF" "AT" "IS" "ISNT" "EMPTY" "SKIP"
84 364d8905 2021-11-07 op "PR" "PRAGMAT"
85 364d8905 2021-11-07 op "CASE" "IN" "OUSE" "OUT" "ESAC"
86 364d8905 2021-11-07 op "FOR" "FORALL" "FROM" "TO" "BY" "WHILE" "DO" "OD"
87 364d8905 2021-11-07 op "IF" "THEN" "ELIF" "THEN" "ELSE" "FI"
88 364d8905 2021-11-07 op "PAR" "BEGIN" "END" "GOTO" "EXIT"
89 364d8905 2021-11-07 op "LWB" "UPB" "NOT" "ABS" "BIN" "REPR" "LENG"
90 364d8905 2021-11-07 op "SHORTEN" "ODD" "SIGN" "ROUND" "ENTIER" "AND" "OR"
91 364d8905 2021-11-07 op "DIV" "OVER" "MOD" "ELEM" "SHL" "SHR" "OVERAB" "DIVAB" "MODAB"
92 364d8905 2021-11-07 op "REF")
93 364d8905 2021-11-07 op word-end)
94 364d8905 2021-11-07 op 'font-lock-keyword-face)
95 364d8905 2021-11-07 op (cons (rx word-start
96 364d8905 2021-11-07 op (or "TRUE" "FALSE")
97 364d8905 2021-11-07 op word-end)
98 364d8905 2021-11-07 op 'font-lock-constant-face)
99 364d8905 2021-11-07 op ;; only valid for bold stropping
100 364d8905 2021-11-07 op (cons (concat "\\<[A-Z]+\\>") 'font-lock-type-face)
101 364d8905 2021-11-07 op (cons "\\('\\w*'\\)"
102 364d8905 2021-11-07 op 'font-lock-variable-name-face))
103 364d8905 2021-11-07 op "Highlighting expressions for Algol 68 mode.")
104 364d8905 2021-11-07 op
105 364d8905 2021-11-07 op (defsubst a68-within-string ()
106 364d8905 2021-11-07 op "Check if inside a string."
107 364d8905 2021-11-07 op (nth 3 (syntax-ppss)))
108 364d8905 2021-11-07 op
109 364d8905 2021-11-07 op (defsubst a68-within-comment ()
110 364d8905 2021-11-07 op "Check if inside a comment."
111 364d8905 2021-11-07 op (nth 4 (syntax-ppss)))
112 364d8905 2021-11-07 op
113 364d8905 2021-11-07 op ;; Indentation rules:
114 364d8905 2021-11-07 op ;;
115 364d8905 2021-11-07 op ;; - If we are at the beginning of the buffer, or looking at some
116 364d8905 2021-11-07 op ;; indent-0 content, indent to column 0.
117 364d8905 2021-11-07 op ;;
118 364d8905 2021-11-07 op ;; - If we are currently at an END, ), FI or OD, then de-indent
119 364d8905 2021-11-07 op ;; relative to the previous line.
120 364d8905 2021-11-07 op ;;
121 364d8905 2021-11-07 op ;; - If we first see and "end line" before our current line,
122 364d8905 2021-11-07 op ;; then we should indent our current line to the same indentation as
123 364d8905 2021-11-07 op ;; the end line.
124 364d8905 2021-11-07 op ;;
125 364d8905 2021-11-07 op ;; - If we first see a "start line" like IF, then we need to increase
126 364d8905 2021-11-07 op ;; our indentation relative to that start line.
127 364d8905 2021-11-07 op ;;
128 364d8905 2021-11-07 op ;; - If into a balanced expression, we should indent to the column
129 364d8905 2021-11-07 op ;; where the start of the innermost parenthetical group.
130 364d8905 2021-11-07 op ;;
131 364d8905 2021-11-07 op ;; - If none of the above apply, then do not indent at all.
132 364d8905 2021-11-07 op
133 364d8905 2021-11-07 op (defun a68-indent-line ()
134 364d8905 2021-11-07 op "Indent current line as Algol 68 code."
135 364d8905 2021-11-07 op (interactive)
136 364d8905 2021-11-07 op (let ((case-fold-search nil))
137 364d8905 2021-11-07 op (save-excursion
138 364d8905 2021-11-07 op (beginning-of-line)
139 364d8905 2021-11-07 op (if (nth 1 (syntax-ppss)) ; Check for rule 5
140 364d8905 2021-11-07 op (let ((offset (save-excursion (goto-char (+ (nth 1 (syntax-ppss)) 1))
141 364d8905 2021-11-07 op (current-column))))
142 364d8905 2021-11-07 op (indent-line-to offset))
143 364d8905 2021-11-07 op (if (or (bobp) ; Check for rule 1
144 364d8905 2021-11-07 op (looking-at "^[ \t]*\\<\\(KEEP\\|FINISH\\|DECS\\|USE\\|PROGRAM\\)\\>"))
145 364d8905 2021-11-07 op (indent-line-to 0)
146 364d8905 2021-11-07 op (let ((not-indented t)
147 364d8905 2021-11-07 op (begin-indent-re "^[ \t]*\\<\\(PAR\\|BEGIN\\|KEEP\\|IF\\|DO\\|ELSE\\|ELIF\\|THEN\\)")
148 364d8905 2021-11-07 op (deindent-line-re "^[ \t]*\\<\\(END\\|FI\\|OD\\|ELSE\\|ELIF\\)\\>")
149 364d8905 2021-11-07 op (eqindent-line-re "^[ \t]*\\<\\(THEN\\)\\>")
150 364d8905 2021-11-07 op (end-line-re "^[ \t]*\\(END\\|FI\\|OD\\)")
151 364d8905 2021-11-07 op cur-indent)
152 364d8905 2021-11-07 op (if (looking-at eqindent-line-re)
153 364d8905 2021-11-07 op (save-excursion
154 364d8905 2021-11-07 op (forward-line -1)
155 364d8905 2021-11-07 op (setq cur-indent (current-indentation)))
156 364d8905 2021-11-07 op (if (looking-at deindent-line-re) ; Check for rule 2
157 364d8905 2021-11-07 op (progn
158 364d8905 2021-11-07 op (save-excursion
159 364d8905 2021-11-07 op (forward-line -1)
160 8621ed2a 2021-11-07 op (setq cur-indent (- (current-indentation) a68-indent-level)))
161 364d8905 2021-11-07 op (when (< cur-indent 0)
162 364d8905 2021-11-07 op (setq cur-indent 0)))
163 364d8905 2021-11-07 op (save-excursion
164 364d8905 2021-11-07 op (while not-indented
165 364d8905 2021-11-07 op (forward-line -1)
166 364d8905 2021-11-07 op (if (looking-at end-line-re) ; Check for rule 3
167 364d8905 2021-11-07 op (progn
168 364d8905 2021-11-07 op (setq cur-indent (current-indentation))
169 364d8905 2021-11-07 op (setq not-indented nil))
170 364d8905 2021-11-07 op ;; Check for rule 4
171 364d8905 2021-11-07 op (if (looking-at begin-indent-re)
172 364d8905 2021-11-07 op (progn
173 8621ed2a 2021-11-07 op (setq cur-indent (+ (current-indentation) a68-indent-level))
174 364d8905 2021-11-07 op (setq not-indented nil))
175 364d8905 2021-11-07 op (when (bobp) ; Check for rule 5
176 364d8905 2021-11-07 op (setq not-indented nil))))))))
177 364d8905 2021-11-07 op (if cur-indent
178 364d8905 2021-11-07 op (indent-line-to cur-indent)
179 364d8905 2021-11-07 op ;; If we didn't see an indentation hint, then allow no
180 364d8905 2021-11-07 op ;; indentation.
181 364d8905 2021-11-07 op (indent-line-to 0)))))))
182 364d8905 2021-11-07 op (when (< (current-column) (current-indentation))
183 364d8905 2021-11-07 op (move-to-column (current-indentation))))
184 364d8905 2021-11-07 op
185 364d8905 2021-11-07 op (defvar a68-mode-syntax-table
186 364d8905 2021-11-07 op (let ((st (make-syntax-table)))
187 364d8905 2021-11-07 op (modify-syntax-entry ?# "<" st)
188 364d8905 2021-11-07 op (modify-syntax-entry ?# ">" st)
189 364d8905 2021-11-07 op (modify-syntax-entry ?\\ "." st)
190 364d8905 2021-11-07 op ;; define parentheses to match
191 364d8905 2021-11-07 op (modify-syntax-entry ?\( "()" st)
192 364d8905 2021-11-07 op (modify-syntax-entry ?\) ")(" st)
193 364d8905 2021-11-07 op st))
194 364d8905 2021-11-07 op
195 364d8905 2021-11-07 op (defconst a68-autoindent-lines-re
196 364d8905 2021-11-07 op (rx word-start
197 364d8905 2021-11-07 op (or "BEGIN" "END" "ELSE" "ELIF" "DO" "OD" "CASE" "ESAC" "IN" "OUT")
198 364d8905 2021-11-07 op word-end))
199 364d8905 2021-11-07 op
200 364d8905 2021-11-07 op (defun a68-electric-terminate-line ()
201 364d8905 2021-11-07 op "Terminate line and indent next line."
202 364d8905 2021-11-07 op (interactive)
203 364d8905 2021-11-07 op ;; First, check if current line should be indented
204 364d8905 2021-11-07 op (save-excursion
205 364d8905 2021-11-07 op (beginning-of-line)
206 364d8905 2021-11-07 op (skip-chars-forward " \t")
207 364d8905 2021-11-07 op (when (looking-at a68-autoindent-lines-re)
208 364d8905 2021-11-07 op (a68-indent-line)))
209 364d8905 2021-11-07 op (delete-horizontal-space) ; Removes triling whitespaces
210 364d8905 2021-11-07 op (newline)
211 364d8905 2021-11-07 op ;; Indent next line if we are not in a string
212 364d8905 2021-11-07 op (unless (a68-within-string)
213 364d8905 2021-11-07 op (a68-indent-line)))
214 364d8905 2021-11-07 op
215 e9c672cf 2021-11-07 op (defvar a68-mode-abbrev-table nil
216 e9c672cf 2021-11-07 op "Abbreviation table used in `a68-mode' buffers.")
217 e9c672cf 2021-11-07 op
218 e9c672cf 2021-11-07 op (define-abbrev-table 'a68-mode-abbrev-table
219 e9c672cf 2021-11-07 op '())
220 e9c672cf 2021-11-07 op
221 364d8905 2021-11-07 op ;;;###autoload
222 364d8905 2021-11-07 op (define-derived-mode a68-mode prog-mode "Algol68"
223 364d8905 2021-11-07 op "Major mode for editing Alogl68 files."
224 e9c672cf 2021-11-07 op :abbrev-table a68-mode-abbrev-table
225 364d8905 2021-11-07 op (setq-local font-lock-defaults '(a68-font-lock-keywords))
226 364d8905 2021-11-07 op (setq-local indent-line-function #'a68-indent-line)
227 18be0e13 2021-11-07 op (setq-local comment-start a68-comment-style)
228 18be0e13 2021-11-07 op (setq-local comment-end a68-comment-style)
229 4fcb92ee 2021-11-07 op (setq-local syntax-propertize-function
230 4fcb92ee 2021-11-07 op (syntax-propertize-rules ((rx (group bow "COMMENT" eow)
231 4fcb92ee 2021-11-07 op (group (*? anychar))
232 4fcb92ee 2021-11-07 op (group bow "COMMENT" eow))
233 4fcb92ee 2021-11-07 op (1 "<")
234 4fcb92ee 2021-11-07 op (3 ">"))
235 4fcb92ee 2021-11-07 op ((rx (group bow "CO" eow)
236 4fcb92ee 2021-11-07 op (group (*? anychar))
237 4fcb92ee 2021-11-07 op (group bow "CO" eow))
238 4fcb92ee 2021-11-07 op (1 "<")
239 4fcb92ee 2021-11-07 op (3 ">"))
240 4fcb92ee 2021-11-07 op ((rx (group "#")
241 4fcb92ee 2021-11-07 op (group (*? anychar))
242 4fcb92ee 2021-11-07 op (group "#"))
243 4fcb92ee 2021-11-07 op (1 "<")
244 4fcb92ee 2021-11-07 op (3 ">")))))
245 364d8905 2021-11-07 op
246 364d8905 2021-11-07 op ;;;###autoload
247 364d8905 2021-11-07 op (add-to-list 'auto-mode-alist '("\\.a68\\'" . a68-mode))
248 364d8905 2021-11-07 op
249 48415058 2021-11-07 op (provide 'a68-mode)
250 48415058 2021-11-07 op ;;; a68-mode.el ends here