HTML output driver: Correct typo
[pspp] / pspp-mode.el
1 ;;; pspp-mode.el --- Major mode for editing PSPP files
2
3 ;; Copyright (C) 2005,2018,2020 Free Software Foundation
4 ;; Author: Scott Andrew Borton <scott@pp.htv.fi>
5 ;; Created: 05 March 2005
6 ;; Version: 1.1
7 ;; Keywords: PSPP major-mode
8 ;; This file is not part of GNU Emacs.
9
10 ;;; Commentary:
11 ;; Based on the example wpdl-mode.el by Scott Borton
12
13 ;; Copyright (C) 2000, 2003 Scott Andrew Borton <scott@pp.htv.fi>
14
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.
19 ;;
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.
24 ;;
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/>.
27
28 ;;; Code:
29 (defvar pspp-mode-hook nil)
30
31
32 (defvar pspp-mode-map
33   (let ((pspp-mode-map (make-keymap)))
34     (define-key pspp-mode-map "\C-j" 'newline-and-indent)
35     pspp-mode-map)
36   "Keymap for PSPP major mode")
37
38
39 ;;;###autoload
40 (add-to-list 'auto-mode-alist '("\\.sps\\'" . pspp-mode))
41
42
43 (defun pspp-data-block-p ()
44   "Returns t if current line is inside a data block."
45   (save-excursion
46     (let ((pspp-end-of-block-found nil)
47           (pspp-start-of-block-found nil))
48       (beginning-of-line)
49       (while (not (or
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\\)"))
56         (forward-line -1))
57
58       (and pspp-start-of-block-found (not pspp-end-of-block-found)))))
59
60
61 (defconst pspp-indent-width
62   2
63   "size of indent")
64
65
66 (defun downcase-list (l)
67   "Takes a list of strings and returns that list with all elements downcased"
68   (if l
69       (cons (downcase (car l)) (downcase-list (cdr l)))
70     nil))
71
72
73 (defun upcase-list (l)
74   "Takes a list of strings and returns that list with all elements upcased"
75   (if l
76       (cons (upcase (car l)) (upcase-list (cdr l)))
77     nil))
78
79
80 (defun updown-list (l)
81   "Takes a list of strings and returns that list with all elements upcased
82 and downcased"
83   (append (upcase-list l) (downcase-list l)))
84
85
86 (defconst pspp-indenters
87   (concat "^[\t ]*"
88           (regexp-opt (updown-list '("DO"
89                                      "BEGIN"
90                                      "LOOP"
91                                      "INPUT")) t)
92           "[\t ]+")
93   "constructs which cause indentation")
94
95
96 (defconst pspp-unindenters
97   (concat "^[\t ]*\\(END\\|end\\)[\t ]+"
98           (regexp-opt (updown-list '("IF"
99                                      "DATA"
100                                      "LOOP"
101                                      "REPEAT"
102                                      "INPUT")) t)
103           "[\t ]*")
104   ;; Note that "END CASE" and "END FILE" do not unindent.
105   "constructs which cause end of indentation")
106
107
108 (defun pspp-indent-line ()
109   "Indent current line as PSPP code."
110   (beginning-of-line)
111   (let ((verbatim nil)
112         (the-indent 0)    ; Default indent to column 0
113         (case-fold-search t))
114     (if (bobp)
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.
119       (save-excursion
120         (while (and blank-line (not (bobp)))
121           (forward-line -1)
122
123           (if (and (not (pspp-data-block-p)) (not (looking-at "^[ \t]*$")))
124               (progn
125                 (setq blank-line nil)
126
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.
130       (if within-command
131           (set 'the-indent (+ 1 the-indent))))
132     ;; Set the indentation according to the DO - END blocks
133     (save-excursion
134       (beginning-of-line)
135       (while (not (bobp))
136         (beginning-of-line)
137         (if (not (pspp-comment-p))
138             (cond ((save-excursion
139                      (forward-line -1)
140                      (looking-at pspp-indenters))
141                    (set 'the-indent (+ the-indent 1)))
142
143                   ((looking-at pspp-unindenters)
144                    (set 'the-indent (- the-indent 1)))))
145         (forward-line -1)))
146
147     (save-excursion
148       (beginning-of-line)
149       (if (looking-at "^[\t ]*ELSE")
150           (set 'the-indent (- the-indent 1))))
151
152     ;; Stuff in the data-blocks should be untouched
153     (if (not (pspp-data-block-p)) (indent-line-to (* pspp-indent-width the-indent)))))
154
155
156 (defun pspp-comment-start-line-p ()
157   "Returns t if the current line is the first line of a comment, nil otherwise"
158   (beginning-of-line)
159   (or (looking-at "^\*")
160       (looking-at "^[\t ]*comment[\t ]")
161       (looking-at "^[\t ]*COMMENT[\t ]")))
162
163
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"
166   (beginning-of-line)
167   (looking-at ".*\\.[\t ]*$"))
168
169
170 (defun pspp-comment-p ()
171   "Returns t if point is in a comment.  Nil otherwise."
172   (if (pspp-data-block-p)
173       nil
174     (let ((pspp-comment-start-found nil)
175           (pspp-comment-end-found nil)
176           (pspp-single-line-comment nil)
177           (lines 1))
178       (save-excursion
179         (end-of-line)
180         (while (and (>= lines 0)
181                     (not pspp-comment-start-found)
182                     (not pspp-comment-end-found))
183           (beginning-of-line)
184           (if (pspp-comment-start-line-p) (set 'pspp-comment-start-found t))
185           (if (bobp)
186               (set 'pspp-comment-end-found nil)
187             (save-excursion
188               (forward-line -1)
189               (if (pspp-comment-end-line-p) (set 'pspp-comment-end-found t))))
190           (set 'lines (forward-line -1))))
191
192       (save-excursion
193         (set 'pspp-single-line-comment (and
194                                         (pspp-comment-start-line-p)
195                                         (pspp-comment-end-line-p))))
196
197       (or pspp-single-line-comment
198           (and pspp-comment-start-found (not pspp-comment-end-found))))))
199
200
201 (defvar pspp-mode-syntax-table
202   (let ((x-pspp-mode-syntax-table (make-syntax-table)))
203
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)
208
209     ;; Comment syntax
210     ;;  This is incomplete, because:
211     ;;  a) Comments can also be given by COMMENT
212     ;;  b) The sequence .\n* is interpreted incorrectly.
213
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)
217
218     ;; String delimiters
219     (modify-syntax-entry ?'  "\"" x-pspp-mode-syntax-table)
220     (modify-syntax-entry ?\"  "\"" x-pspp-mode-syntax-table)
221
222     x-pspp-mode-syntax-table)
223
224   "Syntax table for pspp-mode")
225
226
227 (defconst pspp-font-lock-keywords
228   (list (cons
229          (concat "\\<"
230                  (regexp-opt (updown-list '(
231                                "END DATA"
232                                "ACF"
233                                "ADD FILES"
234                                "ADD VALUE LABELS"
235                                "AGGREGATE"
236                                "ANOVA"
237                                "APPLY DICTIONARY"
238                                "AREG"
239                                "ARIMA"
240                                "AUTORECODE"
241                                "BEGIN DATA"
242                                "BREAK"
243                                "CASEPLOT"
244                                "CASESTOVARS"
245                                "CCF"
246                                "CLEAR TRANSFORMATIONS"
247                                "CLUSTER"
248                                "COMPUTE"
249                                "CONJOINT"
250                                "CORRELATIONS"
251                                "COXREG"
252                                "COUNT"
253                                "CREATE"
254                                "CROSSTABS"
255                                "CURVEFIT"
256                                "DATA LIST"
257                                "DATE"
258                                "DEBUG CASEFILE"
259                                "DEBUG EVALUATE"
260                                "DEBUG MOMENTS"
261                                "DEBUG POOL"
262                                "DELETE VARIABLES"
263                                "DESCRIPTIVES"
264                                "DISCRIMINANT"
265                                "DISPLAY"
266                                "DOCUMENT"
267                                "DO IF"
268                                "DO REPEAT"
269                                "DROP DOCUMENTS"
270                                "ECHO"
271                                "EDIT"
272                                "ELSE"
273                                "ELSE IF"
274                                "END CASE"
275                                "END FILE"
276                                "END FILE TYPE"
277                                "END IF"
278                                "END INPUT PROGRAM"
279                                "END LOOP"
280                                "END REPEAT"
281                                "ERASE"
282                                "EXAMINE"
283                                "EXECUTE"
284                                "EXIT"
285                                "EXPORT"
286                                "FACTOR"
287                                "FILE HANDLE"
288                                "FILE LABEL"
289                                "FILE TYPE"
290                                "FILTER"
291                                "FINISH"
292                                "FIT"
293                                "FLIP"
294                                "FORMATS"
295                                "FREQUENCIES"
296                                "GENLOG"
297                                "GET"
298                                "GET TRANSLATE"
299                                "GLM"
300                                "GRAPH"
301                                "HILOGLINEAR"
302                                "HOST"
303                                "IF"
304                                "IGRAPH"
305                                "IMPORT"
306                                "INCLUDE"
307                                "INFO"
308                                "INPUT MATRIX"
309                                "INPUT PROGRAM"
310                                "KEYED DATA LIST"
311                                "LEAVE"
312                                "LIST"
313                                "LOGLINEAR"
314                                "LOGISITIC REGRESSION"
315                                "LOOP"
316                                "MATCH FILES"
317                                "MATRIX DATA"
318                                "MCONVERT"
319                                "MEANS"
320                                "MISSING VALUES"
321                                "MODIFY VARS"
322                                "MULT RESPONSE"
323                                "MVA"
324                                "NEW FILE"
325                                "N"
326                                "N OF CASES"
327                                "NLR"
328                                "NONPAR CORR"
329                                "NPAR TESTS"
330                                "NUMBERED"
331                                "NUMERIC"
332                                "OLAP CUBES"
333                                "OMS"
334                                "ONEWAY"
335                                "ORTHOPLAN"
336                                "PACF"
337                                "PARTIAL CORR"
338                                "PEARSON CORRELATIONS"
339                                "PERMISSIONS"
340                                "PLOT"
341                                "POINT"
342                                "PPLOT"
343                                "PREDICT"
344                                "PRESERVE"
345                                "PRINT EJECT"
346                                "PRINT"
347                                "PRINT FORMATS"
348                                "PRINT SPACE"
349                                "PROCEDURE OUTPUT"
350                                "PROXIMITIES"
351                                "Q"
352                                "QUICK CLUSTER"
353                                "QUIT"
354                                "RANK"
355                                "RECODE"
356                                "RECORD TYPE"
357                                "REFORMAT"
358                                "REGRESSION"
359                                "RENAME VARIABLES"
360                                "REPEATING DATA"
361                                "REPORT"
362                                "REREAD"
363                                "RESTORE"
364                                "RMV"
365                                "SAMPLE"
366                                "SAVE"
367                                "SAVE TRANSLATE"
368                                "SCRIPT"
369                                "SELECT IF"
370                                "SET"
371                                "SHOW"
372                                "SORT CASES"
373                                "SORT"
374                                "SPCHART"
375                                "SPLIT FILE"
376                                "STRING"
377                                "SUBTITLE"
378                                "SUMMARIZE"
379                                "SURVIVAL"
380                                "SYSFILE INFO"
381                                "TEMPORARY"
382                                "TITLE"
383                                "TSET"
384                                "TSHOW"
385                                "TSPLOT"
386                                "T-TEST"
387                                "UNIANOVA"
388                                "UNNUMBERED"
389                                "UPDATE"
390                                "USE"
391                                "VALUE LABELS"
392                                "VARIABLE ALIGNMENT"
393                                "VARIABLE LABELS"
394                                "VARIABLE LEVEL"
395                                "VARIABLE WIDTH"
396                                "VARSTOCASES"
397                                "VECTOR"
398                                "VERIFY"
399                                "WEIGHT"
400                                "WRITE"
401                                "WRITE FORMATS"
402                                "XSAVE")) t) "\\>")
403          'font-lock-builtin-face)
404
405         (cons
406          (concat "\\<" (regexp-opt (updown-list
407                         '("ALL" "AND" "BY" "EQ" "GE" "GT" "LE" "LT" "NE" "NOT" "OR" "TO" "WITH"))
408                         t) "\\>")
409          'font-lock-keyword-face)
410
411         (cons
412          (concat "\\<"
413                  (regexp-opt (updown-list '(
414                                "ABS"
415                                "ACOS"
416                                "ANY"
417                                "ANY"
418                                "ARCOS"
419                                "ARSIN"
420                                "ARTAN"
421                                "ASIN"
422                                "ATAN"
423                                "CDF.BERNOULLI"
424                                "CDF.BETA"
425                                "CDF.BINOM"
426                                "CDF.BVNOR"
427                                "CDF.CAUCHY"
428                                "CDF.CHISQ"
429                                "CDF.EXP"
430                                "CDF.F"
431                                "CDF.GAMMA"
432                                "CDF.GEOM"
433                                "CDF.HALFNRM"
434                                "CDF.HYPER"
435                                "CDF.IGAUSS"
436                                "CDF.LAPLACE"
437                                "CDF.LNORMAL"
438                                "CDF.LOGISTIC"
439                                "CDF.NEGBIN"
440                                "CDF.NORMAL"
441                                "CDF.PARETO"
442                                "CDF.POISSON"
443                                "CDF.RAYLEIGH"
444                                "CDF.SMOD"
445                                "CDF.SRANGE"
446                                "CDF.T"
447                                "CDF.T1G"
448                                "CDF.T2G"
449                                "CDF.UNIFORM"
450                                "CDF.WEIBULL"
451                                "CDFNORM"
452                                "CFVAR"
453                                "CONCAT"
454                                "COS"
455                                "CTIME.DAYS"
456                                "CTIME.HOURS"
457                                "CTIME.MINUTES"
458                                "CTIME.SECONDS"
459                                "DATE.DMY"
460                                "DATE.MDY"
461                                "DATE.MOYR"
462                                "DATE.QYR"
463                                "DATE.WKYR"
464                                "DATE.YRDAY"
465                                "EXP"
466                                "IDF.BETA"
467                                "IDF.CAUCHY"
468                                "IDF.CHISQ"
469                                "IDF.EXP"
470                                "IDF.F"
471                                "IDF.GAMMA"
472                                "IDF.HALFNRM"
473                                "IDF.IGAUSS"
474                                "IDF.LAPLACE"
475                                "IDF.LNORMAL"
476                                "IDF.LOGISTIC"
477                                "IDF.NORMAL"
478                                "IDF.PARETO"
479                                "IDF.RAYLEIGH"
480                                "IDF.SMOD"
481                                "IDF.SRANGE"
482                                "IDF.T"
483                                "IDF.T1G"
484                                "IDF.T2G"
485                                "IDF.UNIFORM"
486                                "IDF.WEIBULL"
487                                "INDEX"
488                                "INDEX"
489                                "LAG"
490                                "LAG"
491                                "LAG"
492                                "LAG"
493                                "LENGTH"
494                                "LG10"
495                                "LN"
496                                "LNGAMMA"
497                                "LOWER"
498                                "LPAD"
499                                "LPAD"
500                                "LTRIM"
501                                "LTRIM"
502                                "MAX"
503                                "MAX"
504                                "MBLEN.BYTE"
505                                "MEAN"
506                                "MIN"
507                                "MIN"
508                                "MISSING"
509                                "MOD"
510                                "MOD10"
511                                "NCDF.BETA"
512                                "NCDF.CHISQ"
513                                "NCDF.F"
514                                "NCDF.T"
515                                "NMISS"
516                                "NORMAL"
517                                "NPDF.BETA"
518                                "NPDF.CHISQ"
519                                "NPDF.F"
520                                "NPDF.T"
521                                "NUMBER"
522                                "NVALID"
523                                "PDF.BERNOULLI"
524                                "PDF.BETA"
525                                "PDF.BINOM"
526                                "PDF.BVNOR"
527                                "PDF.CAUCHY"
528                                "PDF.CHISQ"
529                                "PDF.EXP"
530                                "PDF.F"
531                                "PDF.GAMMA"
532                                "PDF.GEOM"
533                                "PDF.HALFNRM"
534                                "PDF.HYPER"
535                                "PDF.IGAUSS"
536                                "PDF.LANDAU"
537                                "PDF.LAPLACE"
538                                "PDF.LNORMAL"
539                                "PDF.LOG"
540                                "PDF.LOGISTIC"
541                                "PDF.NEGBIN"
542                                "PDF.NORMAL"
543                                "PDF.NTAIL"
544                                "PDF.PARETO"
545                                "PDF.POISSON"
546                                "PDF.RAYLEIGH"
547                                "PDF.RTAIL"
548                                "PDF.T"
549                                "PDF.T1G"
550                                "PDF.T2G"
551                                "PDF.UNIFORM"
552                                "PDF.WEIBULL"
553                                "PDF.XPOWER"
554                                "PROBIT"
555                                "RANGE"
556                                "RANGE"
557                                "RINDEX"
558                                "RINDEX"
559                                "RND"
560                                "RPAD"
561                                "RPAD"
562                                "RTRIM"
563                                "RTRIM"
564                                "RV.BERNOULLI"
565                                "RV.BETA"
566                                "RV.BINOM"
567                                "RV.CAUCHY"
568                                "RV.CHISQ"
569                                "RV.EXP"
570                                "RV.F"
571                                "RV.GAMMA"
572                                "RV.GEOM"
573                                "RV.HALFNRM"
574                                "RV.HYPER"
575                                "RV.IGAUSS"
576                                "RV.LANDAU"
577                                "RV.LAPLACE"
578                                "RV.LEVY"
579                                "RV.LNORMAL"
580                                "RV.LOG"
581                                "RV.LOGISTIC"
582                                "RV.LVSKEW"
583                                "RV.NEGBIN"
584                                "RV.NORMAL"
585                                "RV.NTAIL"
586                                "RV.PARETO"
587                                "RV.POISSON"
588                                "RV.RAYLEIGH"
589                                "RV.RTAIL"
590                                "RV.T"
591                                "RV.T1G"
592                                "RV.T2G"
593                                "RV.UNIFORM"
594                                "RV.WEIBULL"
595                                "RV.XPOWER"
596                                "SD"
597                                "SIG.CHISQ"
598                                "SIG.F"
599                                "SIN"
600                                "SQRT"
601                                "STRING"
602                                "SUBSTR"
603                                "SUBSTR"
604                                "SUM"
605                                "SYSMIS"
606                                "SYSMIS"
607                                "TAN"
608                                "TIME.DAYS"
609                                "TIME.HMS"
610                                "TRUNC"
611                                "UNIFORM"
612                                "UPCASE"
613                                "VALUE"
614                                "VARIANCE"
615                                "XDATE.DATE"
616                                "XDATE.HOUR"
617                                "XDATE.JDAY"
618                                "XDATE.MDAY"
619                                "XDATE.MINUTE"
620                                "XDATE.MONTH"
621                                "XDATE.QUARTER"
622                                "XDATE.SECOND"
623                                "XDATE.TDAY"
624                                "XDATE.TIME"
625                                "XDATE.WEEK"
626                                "XDATE.WKDAY"
627                                "XDATE.YEAR"
628                                "YRMODA"))
629                              t) "\\>")  'font-lock-function-name-face)
630
631         '( "\\<[#$@a-zA-Z][a-zA-Z0-9_]*\\>" . font-lock-variable-name-face))
632   "Highlighting expressions for PSPP mode.")
633
634
635 ;;;###autoload
636 (defun pspp-mode ()
637   (interactive)
638   (kill-all-local-variables)
639   (use-local-map pspp-mode-map)
640   (set-syntax-table pspp-mode-syntax-table)
641
642   (set (make-local-variable 'font-lock-keywords-case-fold-search) t)
643   (set (make-local-variable 'font-lock-defaults) '(pspp-font-lock-keywords))
644
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)
648        (concat "pspp "
649                buffer-file-name))
650
651   (setq major-mode 'pspp-mode)
652   (setq mode-name "PSPP")
653   (run-hooks 'pspp-mode-hook))
654
655 (provide 'pspp-mode)
656
657 ;;; pspp-mode.el ends here