1 ;;; a68-mode.el --- Major mode for editing Algol 68 code
3 ;; Copyright (C) 2011 Jose E. Marchesi
5 ;; Maintainer: Jose E. Marchesi
7 ;; This file is NOT part of GNU Emacs.
9 ;; This program is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 3, or (at your option)
14 ;; This program is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with this program; see the file COPYING. If not, write to the
21 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 ;; Boston, MA 02110-1301, USA.
26 ;; A major mode for editing Algol 68 code.
28 ;; TODO: support quote and dot stropping.
35 (defvar a68-indent-step 3
36 "Indentation step for Algol 68.")
38 (defvar a68-mode-hook nil)
41 (let ((map (make-keymap)))
42 (define-key map "\C-j" 'newline-and-indent)
43 (define-key map "\r" 'electric-a68-terminate-line)
44 (define-key map "\t" 'electric-a68-tab)
46 "Keymap for Algol 68 major mode.")
49 (add-to-list 'auto-mode-alist '("\\.a68\\'" . a68-mode))
51 (defconst a68-font-lock-keywords
53 (cons (concat "\\<\\("
54 "DECS\\|PROGRAM\\|CONTEXT\\|USE\\|FINISH\\|KEEP"
56 "\\|MODE\\|OP\\|PRIO\\|PROC"
58 "\\|OF\\|AT\\|IS\\|ISNT\\|EMPTY\\|SKIP"
60 "\\|CASE\\|IN\\|OUSE\\|OUT\\|ESAC\\|"
61 "\\|FOR\\|FORALL\\|FROM\\|TO\\|BY\\|WHILE\\|DO\\|OD"
62 "\\|IF\\|THEN\\|ELIF\\|THEN\\|ELSE\\|FI"
63 "\\|PAR\\|BEGIN\\|END\\|GOTO\\|EXIT"
64 "\\|LWB\\|UPB\\|NOT\\|ABS\\|BIN\\|REPR\\|LENG\\|SHORTEN\\|ODD\\|SIGN\\|ROUND\\ENTIER"
65 "\\|AND\\|OR\\|DIV\\|OVER\\|MOD\\|ELEM\\|SHL\\|SHR\\|IS\\|ISNT"
66 "\\|OVERAB\\|DIVAB\\|MODAB"
69 'font-lock-keyword-face)
70 (cons (concat "\\<\\("
73 'font-lock-constant-face)
74 ;; Note that the following rule is only valid for bold stropping.
75 (cons (concat "\\<[A-Z]+\\>") 'font-lock-type-face)
78 font-lock-variable-name-face))
79 "Highlighting expressions for Algol 68 mode.")
81 (defun a68-within-string ()
82 "Check if inside a string."
83 (nth 3 (syntax-ppss)))
85 (defun a68-within-comment ()
86 "Check if inside a comment."
87 (nth 4 (syntax-ppss)))
91 ;; - If we are at the beginning of the buffer, or looking at some
92 ;; indent-0 content, indent to column 0.
94 ;; - If we are currently at an END, ), FI or OD, then de-indent
95 ;; relative to the previous line.
97 ;; - If we first see and "end line" before our current line,
98 ;; then we should indent our current line to the same indentation as
101 ;; - If we first see a "start line" like IF, then we need to increase
102 ;; our indentation relative to that start line.
104 ;; - If into a balanced expression, we should indent to the column
105 ;; where the start of the innermost parenthetical group.
107 ;; - If none of the above apply, then do not indent at all.
109 (defun a68-indent-line ()
110 "Indent current line as Algol 68 code."
112 (let ((case-fold-search nil))
115 (if (nth 1 (syntax-ppss)) ; Check for rule 5
116 (let ((offset (save-excursion (goto-char (+ (nth 1 (syntax-ppss)) 1))
118 (indent-line-to offset))
119 (if (or (bobp) ; Check for rule 1
120 (looking-at "^[ \t]*\\<\\(KEEP\\|FINISH\\|DECS\\|USE\\|PROGRAM\\)\\>"))
122 (let ((not-indented t)
123 (prev-indent (current-indentation))
124 (begin-indent-re "^[ \t]*\\<\\(PAR\\|BEGIN\\|KEEP\\|IF\\|DO\\|ELSE\\|ELIF\\|THEN\\)")
125 (deindent-line-re "^[ \t]*\\<\\(END\\|FI\\|OD\\|ELSE\\|ELIF\\)\\>")
126 (eqindent-line-re "^[ \t]*\\<\\(THEN\\)\\>")
127 (end-line-re "^[ \t]*\\(END\\|FI\\|OD\\)")
129 (if (looking-at eqindent-line-re)
132 (setq cur-indent (current-indentation)))
133 (if (looking-at deindent-line-re) ; Check for rule 2
137 (setq cur-indent (- (current-indentation) a68-indent-step)))
139 (setq cur-indent 0)))
143 (if (looking-at end-line-re) ; Check for rule 3
145 (setq cur-indent (current-indentation))
146 (setq not-indented nil))
148 (if (looking-at begin-indent-re)
150 (setq cur-indent (+ (current-indentation) a68-indent-step))
151 (setq not-indented nil))
152 (if (bobp) ; Check for rule 5
153 (setq not-indented nil))))))))
155 (indent-line-to cur-indent)
156 ;; If we didn't see an indentation hint, then allow no
158 (indent-line-to 0)))))))
159 (when (< (current-column) (current-indentation))
160 (move-to-column (current-indentation))))
162 (defvar a68-mode-syntax-table
163 (let ((st (make-syntax-table)))
164 (modify-syntax-entry ?{ "<" st)
165 (modify-syntax-entry ?# "<" st)
166 (modify-syntax-entry ?} ">" st)
167 (modify-syntax-entry ?# ">" st)
168 (modify-syntax-entry ?\\ "." st)
169 ;; (modify-syntax-entry ?C "< 13" st)
170 ;; (modify-syntax-entry ?O "> 24" st)
171 ;; define parentheses to match
172 (modify-syntax-entry ?\( "()" st)
173 (modify-syntax-entry ?\) ")(" st)
177 ;;; Electric functions
180 (defconst a68-autoindent-lines-re
181 "\\<\\(BEGIN\\|END\\|ELSE\\|ELIF\\|DO\\|OD\\|CASE\\|ESAC\\|IN\\|OUT\\)\\>")
183 (defun electric-a68-terminate-line ()
184 "Terminate line and indent next line."
186 ;; First, check if current line should be indented
189 (skip-chars-forward " \t")
190 (if (looking-at a68-autoindent-lines-re)
192 (delete-horizontal-space) ; Removes triling whitespaces
193 ;; Indent next line if we are not in a string
194 (let ((in-string (a68-within-string)))
199 (defun electric-a68-tab ()
200 "Function called when TAB is pressed in Algol68 mode."
202 (unless (save-excursion
208 (define-derived-mode a68-mode prog-mode "Algol68"
209 "Major mode for editing Alogl68 files."
210 (setq-local font-lock-defaults '(a68-font-lock-keywords))
211 (setq-local indent-line-function #'a68-indent-line)
212 (setq-local comment-start "#")
213 (setq-local comment-end "#"))
216 (add-to-list 'auto-mode-alist '("\\.a68\\'" . a68-mode))
218 (provide 'algol-mode)
219 ;;; algol-mode.el ends here