Blob


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