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