1 ;;; pspp-mode.el --- Major mode for editing PSPP files
3 ;; Copyright (C) 2005,2018,2020 Free Software Foundation
4 ;; Author: Scott Andrew Borton <scott@pp.htv.fi>
5 ;; Created: 05 March 2005
7 ;; Keywords: PSPP major-mode
8 ;; This file is not part of GNU Emacs.
11 ;; Based on the example wpdl-mode.el by Scott Borton
13 ;; Copyright (C) 2000, 2003 Scott Andrew Borton <scott@pp.htv.fi>
15 ;; This program is free software: you can redistribute it and/or modify
16 ;; it under the terms of the GNU General Public License as published by
17 ;; the Free Software Foundation, either version 3 of the License, or
18 ;; (at your option) any later version.
20 ;; This program is distributed in the hope that it will be useful,
21 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 ;; GNU General Public License for more details.
25 ;; You should have received a copy of the GNU General Public License
26 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
29 (defvar pspp-mode-hook nil)
33 (let ((pspp-mode-map (make-keymap)))
34 (define-key pspp-mode-map "\C-j" 'newline-and-indent)
36 "Keymap for PSPP major mode")
40 (add-to-list 'auto-mode-alist '("\\.sps\\'" . pspp-mode))
43 (defun pspp-data-block-p ()
44 "Returns t if current line is inside a data block."
46 (let ((pspp-end-of-block-found nil)
47 (pspp-start-of-block-found nil))
50 (or (bobp) pspp-end-of-block-found)
51 pspp-start-of-block-found))
52 (set 'pspp-end-of-block-found
53 (looking-at "^[ \t]*\\(END\\|end\\)[\t ]+\\(DATA\\|data\\)\."))
54 (set 'pspp-start-of-block-found
55 (looking-at "^[ \t]*\\(BEGIN\\|begin\\)[\t ]+\\(DATA\\|data\\)"))
58 (and pspp-start-of-block-found (not pspp-end-of-block-found)))))
61 (defconst pspp-indent-width
66 (defun downcase-list (l)
67 "Takes a list of strings and returns that list with all elements downcased"
69 (cons (downcase (car l)) (downcase-list (cdr l)))
73 (defun upcase-list (l)
74 "Takes a list of strings and returns that list with all elements upcased"
76 (cons (upcase (car l)) (upcase-list (cdr l)))
80 (defun updown-list (l)
81 "Takes a list of strings and returns that list with all elements upcased
83 (append (upcase-list l) (downcase-list l)))
86 (defconst pspp-indenters
88 (regexp-opt (updown-list '("DO"
93 "constructs which cause indentation")
96 (defconst pspp-unindenters
97 (concat "^[\t ]*\\(END\\|end\\)[\t ]+"
98 (regexp-opt (updown-list '("IF"
104 ;; Note that "END CASE" and "END FILE" do not unindent.
105 "constructs which cause end of indentation")
108 (defun pspp-indent-line ()
109 "Indent current line as PSPP code."
112 (the-indent 0) ; Default indent to column 0
113 (case-fold-search t))
115 (setq the-indent 0)) ; First line is always non-indented
116 (let ((within-command nil) (blank-line t))
117 ;; If the most recent non blank line ended with a `.' then
118 ;; we're at the start of a new command.
120 (while (and blank-line (not (bobp)))
123 (if (and (not (pspp-data-block-p)) (not (looking-at "^[ \t]*$")))
125 (setq blank-line nil)
127 (if (not (looking-at ".*\\.[ \t]*$"))
128 (setq within-command t))))))
129 ;; If we're not at the start of a new command, then add an indent.
131 (set 'the-indent (+ 1 the-indent))))
132 ;; Set the indentation according to the DO - END blocks
137 (if (not (pspp-comment-p))
138 (cond ((save-excursion
140 (looking-at pspp-indenters))
141 (set 'the-indent (+ the-indent 1)))
143 ((looking-at pspp-unindenters)
144 (set 'the-indent (- the-indent 1)))))
149 (if (looking-at "^[\t ]*ELSE")
150 (set 'the-indent (- the-indent 1))))
152 ;; Stuff in the data-blocks should be untouched
153 (if (not (pspp-data-block-p)) (indent-line-to (* pspp-indent-width the-indent)))))
156 (defun pspp-comment-start-line-p ()
157 "Returns t if the current line is the first line of a comment, nil otherwise"
159 (or (looking-at "^\*")
160 (looking-at "^[\t ]*comment[\t ]")
161 (looking-at "^[\t ]*COMMENT[\t ]")))
164 (defun pspp-comment-end-line-p ()
165 "Returns t if the current line is the candidate for the last line of a comment, nil otherwise"
167 (looking-at ".*\\.[\t ]*$"))
170 (defun pspp-comment-p ()
171 "Returns t if point is in a comment. Nil otherwise."
172 (if (pspp-data-block-p)
174 (let ((pspp-comment-start-found nil)
175 (pspp-comment-end-found nil)
176 (pspp-single-line-comment nil)
180 (while (and (>= lines 0)
181 (not pspp-comment-start-found)
182 (not pspp-comment-end-found))
184 (if (pspp-comment-start-line-p) (set 'pspp-comment-start-found t))
186 (set 'pspp-comment-end-found nil)
189 (if (pspp-comment-end-line-p) (set 'pspp-comment-end-found t))))
190 (set 'lines (forward-line -1))))
193 (set 'pspp-single-line-comment (and
194 (pspp-comment-start-line-p)
195 (pspp-comment-end-line-p))))
197 (or pspp-single-line-comment
198 (and pspp-comment-start-found (not pspp-comment-end-found))))))
201 (defvar pspp-mode-syntax-table
202 (let ((x-pspp-mode-syntax-table (make-syntax-table)))
204 ;; Special chars allowed in variables
205 (modify-syntax-entry ?# "w" x-pspp-mode-syntax-table)
206 (modify-syntax-entry ?@ "w" x-pspp-mode-syntax-table)
207 (modify-syntax-entry ?$ "w" x-pspp-mode-syntax-table)
210 ;; This is incomplete, because:
211 ;; a) Comments can also be given by COMMENT
212 ;; b) The sequence .\n* is interpreted incorrectly.
214 (modify-syntax-entry ?* ". 2" x-pspp-mode-syntax-table)
215 (modify-syntax-entry ?. ". 3" x-pspp-mode-syntax-table)
216 (modify-syntax-entry ?\n "- 41" x-pspp-mode-syntax-table)
219 (modify-syntax-entry ?' "\"" x-pspp-mode-syntax-table)
220 (modify-syntax-entry ?\" "\"" x-pspp-mode-syntax-table)
222 x-pspp-mode-syntax-table)
224 "Syntax table for pspp-mode")
227 (defconst pspp-font-lock-keywords
230 (regexp-opt (updown-list '(
246 "CLEAR TRANSFORMATIONS"
314 "LOGISITIC REGRESSION"
338 "PEARSON CORRELATIONS"
403 'font-lock-builtin-face)
406 (concat "\\<" (regexp-opt (updown-list
407 '("ALL" "AND" "BY" "EQ" "GE" "GT" "LE" "LT" "NE" "NOT" "OR" "TO" "WITH"))
409 'font-lock-keyword-face)
413 (regexp-opt (updown-list '(
629 t) "\\>") 'font-lock-function-name-face)
631 '( "\\<[#$@a-zA-Z][a-zA-Z0-9_]*\\>" . font-lock-variable-name-face))
632 "Highlighting expressions for PSPP mode.")
638 (kill-all-local-variables)
639 (use-local-map pspp-mode-map)
640 (set-syntax-table pspp-mode-syntax-table)
642 (set (make-local-variable 'font-lock-keywords-case-fold-search) t)
643 (set (make-local-variable 'font-lock-defaults) '(pspp-font-lock-keywords))
645 (set (make-local-variable 'indent-line-function) 'pspp-indent-line)
646 (set (make-local-variable 'comment-start) "* ")
647 (set (make-local-variable 'compile-command)
651 (setq major-mode 'pspp-mode)
652 (setq mode-name "PSPP")
653 (run-hooks 'pspp-mode-hook))
657 ;;; pspp-mode.el ends here