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