[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Fwd: [ELPA] New package: pspp-mode.el for PSPP/SPSS syntax highlight
From: |
Stefan Monnier |
Subject: |
Re: Fwd: [ELPA] New package: pspp-mode.el for PSPP/SPSS syntax highlighting |
Date: |
Sat, 04 Jul 2020 18:45:56 -0400 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux) |
Hi John,
So pspp-mode.el is now in GNU ELPA: https://elpa.gnu.org/packages/pspp-mode.html
> Probably it would be - but I don't think a decision has been made yet.
Fair enough. But to avoid divergence, it would be good to do it.
> Yes. That list doesn't change very often. Perhaps we should check
> that it is up to date now.
I was looking at the code a bit and am about to install some changes to
it, but I bumped into some questions about the PSPP syntax of comments.
The patch below does:
* pspp-mode.el: Prefer `setq` to `set '`.
(pspp-mode-hook): Let define-derived-mode declare it for us.
(pspp-mode-map): Don't bind `C-j` since this is not specific to PSPP
but is a user preference. Nowadays `electric-indent-mode` is used
instead anyway. Also, use a local var for the temp.
(pspp--downcase-list, pspp--upcase-list, pspp--updown-list):
Use `mapcar` instead of an inefficient recursion.
(pspp-indent-line): Comment out unused var `verbatim`.
(pspp-comment-start-line-p): Fix incomplete escaping.
(pspp-mode-syntax-table): Don't use `w` for non-word symbol constituents
since it breaks the expected behavior of forward-word.
Use a short non-prefixed name for the local var.
Tweak the syntax-table for comments.
(pspp--syntax-propertize): New var.
(pspp-font-lock-keywords): Use [:alnum:] and [:alpha:].
(pspp-mode): Use define-derived-mode.
But the main issue is the comment syntax. I'm trying to handle them
right using `syntax-propertize`, but the patch can't handle the
t-test-sps I found in PSPP's Git. I don't understand what the real
syntax should be. The doc seems to suggest that comments start with `*`
or `comment` (in the position of the beginning of a command?)
and end with a `.` at an end of line or with an empty line, but in
t-test.sps I see:
* Females have gender 0
* Create 8 female cases
loop #i = 1 to 8.
where the comment does not seem to be terminated (neither by a `.` nor
by an empty line). What am I missing?
Stefan
diff --git a/pspp-mode.el b/pspp-mode.el
index ca9bbc931..157475234 100644
--- a/pspp-mode.el
+++ b/pspp-mode.el
@@ -1,4 +1,4 @@
-;;; pspp-mode.el --- Major mode for editing PSPP files
+;;; pspp-mode.el --- Major mode for editing PSPP files -*- lexical-binding:
t; -*-
;; Copyright (C) 2005,2018,2020 Free Software Foundation, Inc.
;; Author: Scott Andrew Borton <scott@pp.htv.fi>
@@ -25,13 +25,10 @@
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Code:
-(defvar pspp-mode-hook nil)
-
(defvar pspp-mode-map
- (let ((pspp-mode-map (make-keymap)))
- (define-key pspp-mode-map "\C-j" 'newline-and-indent)
- pspp-mode-map)
+ (let ((map (make-sparse-keymap)))
+ map)
"Keymap for PSPP major mode")
@@ -48,37 +45,33 @@
(while (not (or
(or (bobp) pspp-end-of-block-found)
pspp-start-of-block-found))
- (set 'pspp-end-of-block-found
- (looking-at "^[ \t]*\\(END\\|end\\)[\t ]+\\(DATA\\|data\\)\."))
- (set 'pspp-start-of-block-found
- (looking-at "^[ \t]*\\(BEGIN\\|begin\\)[\t ]+\\(DATA\\|data\\)"))
+ (setq pspp-end-of-block-found
+ (looking-at "^[ \t]*\\(END\\|end\\)[\t ]+\\(DATA\\|data\\)\."))
+ (setq pspp-start-of-block-found
+ (looking-at "^[ \t]*\\(BEGIN\\|begin\\)[\t ]+\\(DATA\\|data\\)"))
(forward-line -1))
(and pspp-start-of-block-found (not pspp-end-of-block-found)))))
-(defconst pspp-indent-width
+(defconst pspp-indent-width ;FIXME: Should be a defcustom.
2
"size of indent")
(defun pspp--downcase-list (l)
- "Takes a list of strings and returns that list with all elements downcased"
- (if l
- (cons (downcase (car l)) (pspp--downcase-list (cdr l)))
- nil))
+ "Take a list of strings and return that list with all elements downcased."
+ (mapcar #'downcase l))
(defun pspp--upcase-list (l)
- "Takes a list of strings and returns that list with all elements upcased"
- (if l
- (cons (upcase (car l)) (pspp--upcase-list (cdr l)))
- nil))
+ "Take a list of strings and return that list with all elements upcased."
+ (mapcar #'upcase l))
(defun pspp--updown-list (l)
- "Takes a list of strings and returns that list with all elements upcased
-and downcased"
+ "Take a list of strings and return that list with all elements upcased
+and downcased."
(append (pspp--upcase-list l) (pspp--downcase-list l)))
@@ -87,9 +80,10 @@ and downcased"
(regexp-opt (pspp--updown-list '("DO"
"BEGIN"
"LOOP"
- "INPUT")) t)
+ "INPUT"))
+ t)
"[\t ]+")
- "constructs which cause indentation")
+ "Constructs which cause indentation.")
(defconst pspp-unindenters
@@ -98,16 +92,17 @@ and downcased"
"DATA"
"LOOP"
"REPEAT"
- "INPUT")) t)
+ "INPUT"))
+ t)
"[\t ]*")
;; Note that "END CASE" and "END FILE" do not unindent.
- "constructs which cause end of indentation")
+ "Constructs which cause end of indentation.")
(defun pspp-indent-line ()
"Indent current line as PSPP code."
(beginning-of-line)
- (let ((verbatim nil)
+ (let (;; (verbatim nil)
(the-indent 0) ; Default indent to column 0
(case-fold-search t))
(if (bobp)
@@ -127,7 +122,7 @@ and downcased"
(setq within-command t))))))
;; If we're not at the start of a new command, then add an indent.
(if within-command
- (set 'the-indent (+ 1 the-indent))))
+ (setq the-indent (+ 1 the-indent))))
;; Set the indentation according to the DO - END blocks
(save-excursion
(beginning-of-line)
@@ -137,37 +132,38 @@ and downcased"
(cond ((save-excursion
(forward-line -1)
(looking-at pspp-indenters))
- (set 'the-indent (+ the-indent 1)))
+ (setq the-indent (+ the-indent 1)))
((looking-at pspp-unindenters)
- (set 'the-indent (- the-indent 1)))))
+ (setq the-indent (- the-indent 1)))))
(forward-line -1)))
(save-excursion
(beginning-of-line)
(if (looking-at "^[\t ]*ELSE")
- (set 'the-indent (- the-indent 1))))
+ (setq the-indent (- the-indent 1))))
;; Stuff in the data-blocks should be untouched
(if (not (pspp-data-block-p)) (indent-line-to (* pspp-indent-width
the-indent)))))
(defun pspp-comment-start-line-p ()
- "Returns t if the current line is the first line of a comment, nil otherwise"
+ "Return t if the current line is the first line of a comment, nil otherwise"
(beginning-of-line)
- (or (looking-at "^\*")
+ (or (looking-at "^\\*")
(looking-at "^[\t ]*comment[\t ]")
(looking-at "^[\t ]*COMMENT[\t ]")))
(defun pspp-comment-end-line-p ()
- "Returns t if the current line is the candidate for the last line of a
comment, nil otherwise"
+ "Return t if the current line is the candidate for the last line of a
comment, nil otherwise"
(beginning-of-line)
(looking-at ".*\\.[\t ]*$"))
(defun pspp-comment-p ()
- "Returns t if point is in a comment. Nil otherwise."
+ "Return t if point is in a comment. Nil otherwise."
+ ;; FIXME: Use `syntax-ppss'?
(if (pspp-data-block-p)
nil
(let ((pspp-comment-start-found nil)
@@ -180,16 +176,16 @@ and downcased"
(not pspp-comment-start-found)
(not pspp-comment-end-found))
(beginning-of-line)
- (if (pspp-comment-start-line-p) (set 'pspp-comment-start-found t))
+ (if (pspp-comment-start-line-p) (setq pspp-comment-start-found t))
(if (bobp)
- (set 'pspp-comment-end-found nil)
+ (setq pspp-comment-end-found nil)
(save-excursion
(forward-line -1)
- (if (pspp-comment-end-line-p) (set 'pspp-comment-end-found t))))
- (set 'lines (forward-line -1))))
+ (if (pspp-comment-end-line-p) (setq pspp-comment-end-found t))))
+ (setq lines (forward-line -1))))
(save-excursion
- (set 'pspp-single-line-comment (and
+ (setq pspp-single-line-comment (and
(pspp-comment-start-line-p)
(pspp-comment-end-line-p))))
@@ -198,30 +194,49 @@ and downcased"
(defvar pspp-mode-syntax-table
- (let ((x-pspp-mode-syntax-table (make-syntax-table)))
+ (let ((st (make-syntax-table)))
;; Special chars allowed in variables
- (modify-syntax-entry ?# "w" x-pspp-mode-syntax-table)
- (modify-syntax-entry ?@ "w" x-pspp-mode-syntax-table)
- (modify-syntax-entry ?$ "w" x-pspp-mode-syntax-table)
+ (modify-syntax-entry ?# "_" st)
+ (modify-syntax-entry ?@ "_" st)
+ (modify-syntax-entry ?$ "_" st)
;; Comment syntax
- ;; This is incomplete, because:
- ;; a) Comments can also be given by COMMENT
- ;; b) The sequence .\n* is interpreted incorrectly.
-
- (modify-syntax-entry ?* ". 2" x-pspp-mode-syntax-table)
- (modify-syntax-entry ?. ". 3" x-pspp-mode-syntax-table)
- (modify-syntax-entry ?\n "- 41" x-pspp-mode-syntax-table)
+ ;; See `pspp--syntax-propertize' for the details.
+ (modify-syntax-entry ?* "<" st)
+ (modify-syntax-entry ?. ". 3" st)
+ (modify-syntax-entry ?\n " 34" st)
;; String delimiters
- (modify-syntax-entry ?' "\"" x-pspp-mode-syntax-table)
- (modify-syntax-entry ?\" "\"" x-pspp-mode-syntax-table)
-
- x-pspp-mode-syntax-table)
-
- "Syntax table for pspp-mode")
-
+ (modify-syntax-entry ?' "\"" st)
+ (modify-syntax-entry ?\" "\"" st)
+
+ st)
+
+ "Syntax table for pspp-mode.")
+
+(defconst pspp--syntax-propertize
+ (syntax-propertize-rules
+ ("\\*"
+ (0 (unless (save-excursion
+ (goto-char (match-beginning 0))
+ (skip-chars-backward " \t\n")
+ (memq (char-before) '(nil ?\.)))
+ (string-to-syntax "."))))
+ ("\\_<\\([Cc]\\)[Oo][Mm][Mm][Ee][Nn][Tt]\\_>"
+ (1 (when (save-excursion
+ (goto-char (match-beginning 0))
+ (skip-chars-backward " \t\n")
+ (memq (char-before) '(nil ?\.)))
+ (string-to-syntax "<"))))
+ ;; PSPP, like Pascal, uses '' and "" rather than \' and \" to escape quotes.
+ ("''\\|\"\"" (0 (if (save-excursion
+ (nth 3 (syntax-ppss (match-beginning 0))))
+ (string-to-syntax ".")
+ ;; In case of 3 or more quotes in a row, only advance
+ ;; one quote at a time.
+ (forward-char -1)
+ nil)))))
(defconst pspp-font-lock-keywords
(list (cons
@@ -627,29 +642,28 @@ and downcased"
"YRMODA"))
t) "\\>") 'font-lock-function-name-face)
- '( "\\<[#$@a-zA-Z][a-zA-Z0-9_]*\\>" . font-lock-variable-name-face))
+ ;; FIXME: The doc at
+ ;; https://www.gnu.org/software/pspp/manual/html_node/Tokens.html
+ ;; does not include `$' in the allowed first chars and it includes
+ ;; `. $ # @' additionally to `_' in the subsequent chars.
+ '( "\\<[#$@[:alpha:]][[:alnum:]_]*\\>" . font-lock-variable-name-face))
"Highlighting expressions for PSPP mode.")
;;;###autoload
-(defun pspp-mode ()
- (interactive)
- (kill-all-local-variables)
- (use-local-map pspp-mode-map)
- (set-syntax-table pspp-mode-syntax-table)
-
+(define-derived-mode pspp-mode prog-mode "PSPP"
+ "Major mode to edit PSPP files."
(set (make-local-variable 'font-lock-keywords-case-fold-search) t)
(set (make-local-variable 'font-lock-defaults) '(pspp-font-lock-keywords))
- (set (make-local-variable 'indent-line-function) 'pspp-indent-line)
+ (set (make-local-variable 'indent-line-function) #'pspp-indent-line)
(set (make-local-variable 'comment-start) "* ")
(set (make-local-variable 'compile-command)
(concat "pspp "
buffer-file-name))
- (setq major-mode 'pspp-mode)
- (setq mode-name "PSPP")
- (run-hooks 'pspp-mode-hook))
+ (set (make-local-variable 'syntax-propertize-function)
+ pspp--syntax-propertize))
(provide 'pspp-mode)
- Re: Fwd: [ELPA] New package: pspp-mode.el for PSPP/SPSS syntax highlighting, John Darrington, 2020/07/04
- Re: [ELPA] New package: pspp-mode.el for PSPP/SPSS syntax highlighting, Friedrich Beckmann, 2020/07/05
- Re: [ELPA] New package: pspp-mode.el for PSPP/SPSS syntax highlighting, John Darrington, 2020/07/05
- Re: [ELPA] New package: pspp-mode.el for PSPP/SPSS syntax highlighting, Sean Whitton, 2020/07/05
- Re: [ELPA] New package: pspp-mode.el for PSPP/SPSS syntax highlighting, Friedrich Beckmann, 2020/07/05