[gnu.emacs] Looking for Pascal mode

mike@mecazh.UUCP (Michele Ravani) (07/04/89)

Does anyone have a Pascal mode for gnumacs?
Better still would be one for Hewlett_Packard Pascal.

Thanks in advance


Michele Ravani
Mecasoft Sa
Zurich
Switzerland     ..mcvax!cernvax!mecazh!mike

dsill@RELAY.NSWC.NAVY.MIL (07/05/89)

>From: mcvax!cernvax!mecazh!mike@uunet.uu.net  (Michele Ravani)
>
>Does anyone have a Pascal mode for gnumacs?
>Better still would be one for Hewlett_Packard Pascal.
>

pascal-mode	  
     Mosur Mohan, <mntgfx!mosurm>
     Pascal major mode.
pascal (2.1)	  88-02-??
     Vincent Broman, <broman@nosc.mil>
     Pascal major mode.

Neither of these is currently in the elisp archive, and won't be until
someone (hopefully the author) sends me a copy.

-Dave Sill
 dsill@relay.nswc.navy.mil
 Elisp Archive Coordinator

gjditchfield@watmsg.waterloo.edu (Glen Ditchfield) (07/05/89)

This Pascal mode differs from other Pascal modes (and Modula-2 and Ada
modes) that I've heard about in that template insertion is handled by the
abbreviation expansion mechanism instead of control key sequences.  This
means that
- customizing templates to your indentation style is easier.
- template slots can be filled in any order you find convenient.
- template abbreviations are (usually) the corresponding Pascal keyword,
  and hence are easy to remember.

Part 2 will be a texinfo file describing Pascal mode.

    Glen Ditchfield  gjditchfield@violet.uwaterloo.ca  Office: DC 2517
Dept. of Computer Science, U of Waterloo, Waterloo, Ontario, Canada, N2L 3G1
	"... and the rest, we will spend foolishly!" -- _Svengali_
-------------------------------------------------------------------------------
;; Pascal code editing support for Gnu Emacs
;; Copyright (c) 1989 Glen J. Ditchfield
;; Last known address: gjditchfield@violet.uwaterloo.ca

;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY.  Neither the author nor any distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing.  Refer to the GNU Emacs General Public
;; License for full details.

;; Everyone is granted permission to copy, modify and redistribute
;; this file, but only under the conditions described in the
;; GNU Emacs General Public License.   A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities.  It should be in a
;; file named COPYING.  Among other things, the copyright notice
;; and this notice must be preserved on all copies.

;; Please mail bug reports and feature requests (and bug fixes!) to the
;; above address. I would be delighted to see any utility functions you
;; write for this mode. If I add them to future versions, you will get
;; credit for them.

;; This package was inspired by published descriptions of DEC's Language
;; Sensitive Editor and the Cedar editor, (neither of which have I ever
;; seen running, mind you) and by a Gosling Emacs package written by Erik
;; Fortune and distributed by Scott Menter.

;; 87-06-29: added `\<' (beginning of word) and `/>' (end of word) to
;;   keywords used in pascal-in-parm, to prevent false matches.
;; 87-07-06: if the first thing in a file was a routine, and a nested routine
;;   was created inside it, pascal-in-parm claimed that the nested routine
;;   was in a parameter list. Fixed.
;; 87-07-21: do-pascal-fill replaces do-auto-fill as the fill hook. It keeps
;;   fill mode from breaking lines inside strings.
;;   High voltage functions replaced by pascal-auto-newline.
;; 87-08-05: Pascal-forward-sexp, pascal-backward-sexp added and bound to
;;   keys. pascal-indent-line modified to adjust indentation after `end'
;;   and `until' statements.
;; 87-08-13: Hooks no longer know what abbreviation invoked them; they just
;;   use last-abbrev-text.  `record' template added.
;; 87-09-04: pascal-beginnify-region stirred in.
;; 89-06-05 Elision support moved out to pascal-elide.  Pascal-edit-string
;;   removed.

(defconst pascal-comment-start "{ "  "*Preferred comment opener")
(defconst pascal-comment-end   "\t}" "*Preferred comment closer")
(defconst pascal-indent-level  3     "*Indentation depth")
(defconst pascal-auto-newline  nil
  "*If non-nil, `;' at the end of a line causes a newline to be inserted")

(defvar pascal-mode-map (make-sparse-keymap)
  "Keymap used in Pascal mode.")
(define-key pascal-mode-map "\C-j"	'newline-check-and-indent)
(define-key pascal-mode-map "\C-m"	'newline)
(define-key pascal-mode-map "\177"	'backward-delete-char-untabify)
(define-key pascal-mode-map ";"		'electric-pascal-semi)
(define-key pascal-mode-map "\t"	'pascal-indent-line)
(define-key pascal-mode-map "\e\t"	'pascal-outdent-line)
(define-key pascal-mode-map "\C-c\C-b"	'pascal-beginnify-region)
(define-key pascal-mode-map "\e\^f"	'pascal-forward-sexp)
(define-key pascal-mode-map "\e\^b"	'pascal-backward-sexp)
(define-key pascal-mode-map "\e\C-e"	'end-of-pascal-block)

(defun pascal-mode ()
  "Major mode for editing Pascal programs.
Indentation is done by abbreviation expansion; see M-x list-abbrevs for the
current abbreviations. Delete converts tabs to spaces as it moves back.
\\{pascal-mode-map}
"
  (interactive)
  (use-local-map pascal-mode-map)
  (setq major-mode 'pascal-mode)
  (setq mode-name "Pascal")
  (abbrev-mode 1)
  (if (not pascal-mode-abbrev-table)
      (progn
	(if (file-exists-p abbrev-file-name) (quietly-read-abbrev-file ""))
	(if (not pascal-mode-abbrev-table)
	    (define-abbrev-table 'pascal-mode-abbrev-table '
	      (("if" "" pascal-if-hook 0)     ("ie" "" pascal-if-else-hook 0)
	       ("else" "" pascal-else-hook 0) ("ei" "" pascal-else-if-hook 0)
	       ("for" "" pascal-for-hook 0)   ("while" "" pascal-while-hook 0)
	       ("with" "" pascal-with-hook 0) ("case" "" pascal-case-hook 0)
	       ("var" "" pascal-var-hook 0)   ("const" "" pascal-const-hook 0)
	       ("type" "" pascal-type-hook 0) ("begin" "" pascal-begin-hook 0)
	       ("int" "integer" nil 0)	      ("fun" "" pascal-function-hook 0)
	       ("repeat" "" pascal-repeat-hook 0)
	       ("prog" "" pascal-program-hook 0)
	       ("proc" "" pascal-procedure-hook 0))))))
  (setq local-abbrev-table pascal-mode-abbrev-table)
  (if (not pascal-syntax-table)
      (progn
	(setq pascal-syntax-table (make-syntax-table))
	(set-syntax-table pascal-syntax-table)
	(modify-syntax-entry ?\( "()1")
	(modify-syntax-entry ?\) ")(4")
	(modify-syntax-entry ?\* ".  23")
	(modify-syntax-entry ?\{ "(}")	; Making {} paired delimiters
	(modify-syntax-entry ?\} "){")	; makes forward-sexp skip over them.
	(modify-syntax-entry ?\[ "(]")
	(modify-syntax-entry ?\] ")[")
	(modify-syntax-entry ?\' "\"")
	(modify-syntax-entry ?/ ".")
	(modify-syntax-entry ?+ ".")
	(modify-syntax-entry ?- ".")
	(modify-syntax-entry ?= ".")
	(modify-syntax-entry ?< ".")
	(modify-syntax-entry ?> ".")
	)
    (set-syntax-table  pascal-syntax-table))
  
  (make-local-variable 'indent-line-function)
  (setq indent-line-function 'pascal-indent-line)
  (make-local-variable 'comment-start)
  (setq comment-start pascal-comment-start)
  (make-local-variable 'comment-end)
  (setq comment-end pascal-comment-end)
  (make-local-variable 'comment-start-skip)
  (setq comment-start-skip "{ *\\|(\\*+ *")
  (make-local-variable 'paragraph-separate)
  (setq paragraph-separate "^[ \t]*$\\|^^L")
  (make-local-variable 'paragraph-start)
  (setq paragraph-start "^[ \t]*$\\|^^L")
  (make-local-variable 'parse-sexp-ignore-comments)
  (setq parse-sexp-ignore-comments nil)	; ... because comments are sexps
  (run-hooks 'pascal-mode-hook)
  (if (eq auto-fill-hook 'do-auto-fill)
      (setq auto-fill-hook 'do-pascal-fill))
  )

(defvar pascal-syntax-table nil)

(defvar pascal-mode-abbrev-table nil
  "Abbrev table in use in Pascal-mode buffers.")

;; Search backwards for a line with 0 < indent < limit. Return it's indentation
;; (or 0 if no such line exists).
(defun pascal-old-indent (limit)
  (save-excursion
    (beginning-of-line 0)		;start of previous line
    (while (and (not (bobp))
		(or (zerop (current-indentation))
		    (<= limit (current-indentation))))
      (beginning-of-line 0))
    (current-indentation))
  )

(defun electric-pascal-semi ()
  "Insert ; and perform \\[newline-check-and-indent] if pascal-auto-newline."
  (interactive)
  (insert-string ";")
  (if (and pascal-auto-newline (eolp)) (newline-check-and-indent)))

(defun pascal-indent-line ()
  "Indent current line as Pascal code."
  (interactive)
  (let (indent) ;1st guess at proper indentation
    (save-excursion
      (skip-chars-backward " \t\n\^l\^m")
      (beginning-of-line 1)
      (if (looking-at "[ \t]*\\(end\\|until\\)\\>")
	  (progn
	    ;If the previous statement ended a compound statement, use the
	    ;indentation of the statement's starting line.
	    (re-search-forward "end\\|until")
	    (pascal-backward-sexp)
	    (setq indent (current-indentation))
	    )
	;Otherwise, use the previous non-zero indentation.
	;If the previous line does not appear to be a complete statement,
	;comment, or #include, increase the indentation.
	(forward-line 1)
	(setq indent (pascal-old-indent 9999))
	(beginning-of-line)
	(skip-chars-backward " \t\n\^l\^m")
	(backward-char 2)
	(if (not (looking-at ".;\\|.}\\|\*)"))
	    (progn
	      (beginning-of-line)
	      (if (/= ?# (char-after (point)))
		  (setq indent (+ indent pascal-indent-level))
		)
	      )
	  )
	)
      )
    (if (looking-at "^$")		;empty line?
	(indent-to indent)
      (let ((pos (- (point-max) (point))) ;distance from end of buffer
	    beginning)
	(beginning-of-line)		;If point was inside a line, the
	(setq beginning (point))	;text after it must be shifted right.
	(skip-chars-forward " \t")
	(delete-region beginning (point))
	(indent-to indent)
	(goto-char (- (point-max) pos) )
	)
      )
    )
  )

(defun pascal-outdent-line ()
  "Decrease the indentation of the current line. The indentation used is that
of the last line above the current one with an indentation less than the
current line's."
  (interactive)
  (let ((indent (pascal-old-indent (current-indentation)))
	(pos (- (point-max) (point)))
	beginning)
    (beginning-of-line)
    (setq beginning (point))
    (skip-chars-forward " \t")
    (delete-region beginning (point))
    (indent-to indent)
    (goto-char (- (point-max) pos))
    )
  )

;; Insert the given string into the buffer at point, and add the current
;; indentation of the line point was on to every new line in the inserted
;; area.  When finished, leave point at the position marked by ! in the
;; inserted string if there is one; otherwise, leave point at the end of
;; the inserted area.
(defun insert-indented-string (str)
  (let ((start-point (point))
	(start-indent (current-indentation))
	end-point)
    (insert-string str)
    (setq end-point (point))
    (beginning-of-line)

    ;if the end of the inserted string is not on the same line as the start,
    ;end-point must be adjusted because the line will be indented.
    (if (< start-point (point)) (setq end-point (+ start-indent end-point)))

    ;add the original indentation to every new line in the inserted string.
    (while (< start-point (point))
      (indent-to start-indent)
      (previous-line 1)
      (beginning-of-line)
      )
    (if (search-forward "!" end-point "")
	(delete-backward-char 1))
    )
  )

(defun re-search-back-skip-str (pattern)
  (let ((qpattern (concat "'\\|" pattern))
	search-result)
    (setq search-result (re-search-backward qpattern 1 t))
    (while (and search-result (looking-at "'"))
      (search-backward "'")
      (setq search-result (re-search-backward qpattern 1 t))
      )
    search-result)
  )

;; Point can be in a comment (state 1), a string (state 2), or program text
;; (state 4). Strings can't span line breaks, and comments shouldn't if
;; comment-multi-line is not t, so the beginning of a line can have state set
;; {4} or {1,4}. The function returns the state it thinks point is in; it
;; may not be accurate.
(defun pascal-point-state ()
  (let ((state-set (if comment-multi-line 5 4))
	(old-point (point))
	char-found
	)
    (beginning-of-line)
    ;look for string and comment delimiters
    (while (re-search-forward "'\\|(\\*\\|\\*)\\|{\\|}" old-point t)
      (setq char-found (char-after (- (point) 1)))
      (cond ((= char-found ?\' )
	     ;toggle between program and string states.
	     (setq state-set (elt '(t 1 4 5 2 3 6) state-set)))
	    ((or (= char-found ?\) ) (= char-found ?\} ))
	     ;change comment states to program states
	     (setq state-set (elt '(t 4 2 6 t 4 2) state-set)))
	    (t	;found comment start
	     ;change program states to comment states
	     (setq state-set (elt '(t 1 2 3 1 1 3) state-set)))
	    )
      )
    ; If state-set contains more than one state, look back for comment
    ;delimiters to decide whether point is in a multi-line comment.
    ;This doesn't handle nested comments.
    (if (memq state-set '(3 5 6))
	(progn
	  (beginning-of-line)
	  (re-search-back-skip-str "*)\\|(\\*\\|{\\|}")
	  (if (looking-at "{\\|(\\*")
	      (setq state-set (elt '(t t t 1 t 1 4) state-set))
	    (setq state-set (elt '(t t t 2 t 4 2) state-set)))
	  )
      )
    (goto-char old-point)
    state-set
    )
  )

(defun newline-check-and-indent ()
  "Notifies typist if the end of line is inside a character string, or if
it is inside a comment and comment-multi-line is nil; then it calls
newline-and-indent."
  (interactive "*")
  (let ((state (pascal-point-state)))
    (cond ((and (= state 1) (not comment-multi-line))
	   (beep)
	   (message "Closing comment delimiter needed."))
	  ((= state 2)
	   (beep)
	   (message "Closing quote needed."))
	  )
    )
  (newline-and-indent))

;; A fill hook that does not break strings across line boundaries.  If the
;; fill column is in a string, and there are no spaces on the line before
;; the string other than the indentation, the line should break at the end
;; of string, but doesn't. This seems to be a bug with do-auto-fill.
(defun do-pascal-fill ()
  (if (/= (pascal-point-state) 2)
      (let ((curr-pt (point)))
	(move-to-column fill-column)
	(if (= (pascal-point-state) 2)
	    (let ((old-fill-column fill-column))
	      (search-backward "'")
	      (setq fill-column (current-column))
	      (goto-char curr-pt)
	      (if (not (do-auto-fill)) (newline-and-indent))
	      (setq fill-column old-fill-column))
	  (goto-char curr-pt)
	  (do-auto-fill))
	)
    )
  )

;; Skip backwards over whitespace, including comments.
;; Point is left to the left of the first black character.
(defun skip-backward-whitespace ()
  (skip-chars-backward " \t\n\^l\^m")
  (backward-char 1)
  (while (= (pascal-point-state) 1)
    (re-search-backward "(\\*\\|{")
    (skip-chars-backward " \t\n\^l\^m")
    (backward-char 1))
  )

(defun pascal-beginnify-region (start end)
  "Wrap a begin statement around the region."
  (interactive "r")
  (copy-to-register 1 start end t)	; delete contents of region
  (insert-indented-string pascal-begin-str)
  (insert-string " ")			; replace ! with a space and the region
  (setq end (point))
  (insert-indented-string (get-register 1))
  (goto-char end)
  (skip-chars-forward " \t\n\^l\^m")
  (delete-region end (point))
  (goto-char start)
  (if (looking-at "\\B")		; if point is at the end of a token,
      (insert-string " "))		;   put a space between it and `begin'.
  )

;; move to the end of the next sexp (comment, (-) pair, [-] pair, begin-end,
;; case-end, repeat-until e, string, or record-end).
(defun pascal-forward-sexp ()
  "Move forward across one balanced expression, record type, begin statement,
case statement or repeat statement."
  (interactive)
  (let ((search-pat "\\<begin\\>\\|\\<case\\>\\|\\<end\\>\\|\\<repeat\\>\\|\\<until\\>\\|\\<record\\>\\|[{(']")
	open-list open-end)
    (skip-chars-forward " \t\n\^l\^m")
    (cond ((looking-at "\\<begin\\>\\|\\<case\\>\\|\\<repeat\\>\\|\\<record\\>")
	   (setq open-list (list (if (looking-at "rec") ?R
				   (char-after (point)))))
	   (forward-word 1)
	   (while (and open-list (re-search-forward search-pat nil t))
	     (setq open-end (match-end 0))
	     (goto-char (match-beginning 0))
	     (cond ((looking-at "[({']") ; string, or possible comment
		    (forward-sexp 1))
		   ((looking-at "rec")	; record type
		    (setq open-list (cons ?R open-list))
		    (goto-char open-end))
		   ((= (char-after (point)) ?c)	; case
		    ;Ignore `case' in (variant) records.
		    (if (/= (car open-list) ?R)
			(setq open-list (cons (char-after (point)) open-list)))
		    (goto-char open-end))
		   ((looking-at "[br]") ; begin or repeat
		    (setq open-list (cons (char-after (point)) open-list))
		    (goto-char open-end))
		   ((= (char-after (point)) ?u)	; until
		    (if (= (car open-list) ?r)
			(setq open-list (cdr open-list))
		      (error "unmatched `begin' or `case'?"))
		    (goto-char open-end))
		   (t			; end
		    (if (or (= (car open-list) ?b) (= (car open-list) ?c)
			    (= (car open-list) ?R))
			(setq open-list (cdr open-list))
		      (error "unmatched `repeat'?"))
		    (goto-char open-end))
		   )
	     )
	   (if open-list (error "unbalanced parenthesis or statements"))
	   )
	  (t
	   (forward-sexp 1)
	   )
	  )
    )
  )

;; move back to the start of the previous sexp (comment, (-) pair, [-] pair,
;; string, begin-end, repeat-until, case-end, or record-end). It doesn't
;; handle variant records correctly, but the error is conservative.
(defun pascal-backward-sexp ()
  "Move backward across one balanced expression, record type, begin statement,
case statement, or repeat statement."
  (interactive)
  (let ((search-pat "\\<begin\\>\\|\\<case\\>\\|\\<end\\>\\|\\<repeat\\>\\|\\<until\\>\\|\\<record\\>\\|[})']")
  	(open-list nil))
    (backward-sexp 1)
    (if (looking-at "end\\>\\|until\\>")
      ;Point was at the end of a statement.
      (progn
	(setq open-list (cons (char-after (point)) open-list))
	(while (and open-list (re-search-backward search-pat nil t))
	  (cond ((looking-at "[eu]")	; end or until
		 (setq open-list (cons (char-after (point)) open-list)))
		((looking-at "[)}']")	; string, or possible comment
		 (forward-char 1)
		 (backward-sexp 1))
		((looking-at "rep") ; repeat
		 (if (= (car open-list) ?u)
		     (setq open-list (cdr open-list))
		   (error "unmatched `until'?")))
		(t			; begin, record or case
		 (if (= (car open-list) ?e)
		     (setq open-list (cdr open-list))
		   (error "unmatched `end'?")))
		)
	  )
	(if open-list (error "unbalanced parenthesis or statements"))
	)
      )
    )
  )

;; Guess whether point is inside a routine's formal parameter list or not.
;; This is used to decide whether to expand the abbreviation 'var' as a var
;; parameter or a variable declaration section.  This is tricky; consider
;;   procedure foo( var p1:t1; procedure bar( var p2:t2); var p3:t3);
;;      var
;;         v1:t4;
;; I consider point to be in a parameter list if it is inside a () pair and if
;; the pair is preceded by 'procedure' or 'function' and a word.  Various
;; other symbols are taken as signs that point could not have been in a
;; parameter list.
(defun pascal-in-parm ()
  "Return nil if point is not in a formal parameter list."
  (let ((escapes     "(\\|'\\|\\<const\\>\\|\\<type\\>\\|\\<end\\>")
	(pat ")\\|}\\|(\\|'\\|\\<const\\>\\|\\<type\\>\\|\\<end\\>"))
    (save-excursion
      ;look back for an unmatched "(", a keyword that can't be in a
      ;parameter list, or the beginning of the buffer.
      (while (and (re-search-backward pat 1 1)
		  (not (looking-at escapes)))
	(forward-char 1)
	(pascal-backward-sexp)
	)
      (cond ((looking-at "(")
	     (backward-word 1)
	     (condition-case nil	;in case point is at start of buffer
		 (skip-backward-whitespace)
	       (error nil))
	     (backward-word 1)
	     (looking-at "\\<function\\>\\|\\<procedure\\>")))
      )
    )
  )

;; If the previous statement ends with a `;', remove that `;'. Intended for
;; use when inserting 'else' clauses.
(defun no-prev-semi ()
  (save-excursion
    (skip-backward-whitespace)
    (if (looking-at ";") (delete-char 1)))
  )

;;; Structure templates.
(defvar pascal-if-else-str
  "if! then begin\n   \n   end\nelse begin\n   \n   end;"
  "*If-then-else statement template; abbreviated 'ie'.")
(defun pascal-if-else-hook ()
  (if (= 4 (pascal-point-state))
      (insert-indented-string pascal-if-else-str)
    (insert-string last-abbrev-text))
  )

(defvar pascal-if-str
  "if! then begin\n   \n   end;"
  "*If-then statement template; abbreviated 'if'.")
(defun pascal-if-hook ()
  (if (= 4 (pascal-point-state))
      (insert-indented-string pascal-if-str)
    (insert-string last-abbrev-text))
  )

(defvar pascal-else-str
  "else begin\n  !\n   end;"
  "*else clause template; abbreviated 'else'.")
(defun pascal-else-hook()
  (if (= 4 (pascal-point-state))
      (progn
	(no-prev-semi)
	(insert-indented-string pascal-else-str))
    (insert-string last-abbrev-text))
  )

(defvar pascal-else-if-str
  "else if! then begin\n   \n   end;"
  "*else-if clause template; abbreviated 'ei.'")
(defun pascal-else-if-hook ()
  (if (= 4 (pascal-point-state))
      (progn
	(no-prev-semi)
	(insert-indented-string pascal-else-if-str))
    (insert-string last-abbrev-text))
  )

(defvar pascal-while-str
  "while! do begin\n   \n   end;"
  "*while statement template; abbreviated 'while'.")
(defun pascal-while-hook ()
  (if (= 4 (pascal-point-state))
      (insert-indented-string pascal-while-str)
    (insert-string last-abbrev-text))
  )

(defvar pascal-for-str
  "for! := to do begin\n   \n   end;"
  "*for statement template; abbreviated 'for'.")
(defun pascal-for-hook ()
  (if (= 4 (pascal-point-state))
      (insert-indented-string pascal-for-str)
    (insert-string last-abbrev-text))
  )

(defvar pascal-repeat-str
  "repeat\n  !\nuntil ;"
  "*repeat statement template; abbreviated 'repeat'.")
(defun pascal-repeat-hook ()
  (if (= 4 (pascal-point-state))
      (insert-indented-string pascal-repeat-str)
    (insert-string last-abbrev-text))
  )

(defvar pascal-with-str
  "with! do begin\n   \n   end;"
  "*with statement template; abbreviated 'with'.")
(defun pascal-with-hook ()
  (if (= 4 (pascal-point-state))
      (insert-indented-string pascal-with-str)
    (insert-string last-abbrev-text))
  )

(defvar pascal-case-str
  "case! of\n   \n   end;"
  "*case statement template; abbreviated 'case'.")
(defun pascal-case-hook ()
  (if (= 4 (pascal-point-state))
      (insert-indented-string pascal-case-str)
    (insert-string last-abbrev-text))
  )

(defvar pascal-begin-str
  "begin\n  !\n   end;"
  "*begin statement template; abbreviated 'begin'.")
(defun pascal-begin-hook ()
  (if (= 4 (pascal-point-state))
      (insert-indented-string pascal-begin-str)
    (insert-string last-abbrev-text))
  )

(defvar pascal-program-str
  "program!( input, output);\n   \n   begin\n      \n      end."
  "*Program template; abbreviated 'prog'.")
(defun pascal-program-hook ()
  (if (= 4 (pascal-point-state))
      (insert-indented-string pascal-program-str)
    (insert-string last-abbrev-text))
  )

(defvar pascal-procedure-str
  "procedure!();\n   \n   begin\n      \n      end;"
  "*Procedure template; abbreviated 'proc'.")
(defvar pascal-procedure-parm-str
  "procedure!()"
  "*Procedure parameter template; abbreviated 'proc'.")
(defun pascal-procedure-hook ()
  (if (= 4 (pascal-point-state))
      (if (pascal-in-parm)
	  (insert-indented-string pascal-procedure-parm-str)
	(insert-indented-string pascal-procedure-str))
    (insert-string last-abbrev-text))
  )

(defvar pascal-function-str
  "function!():;\n   \n   begin\n      \n      end;"
  "*Function template; abbreviated 'fun'.")
(defvar pascal-function-parm-str
  "function!():"
  "*Function parameter template; abbreviated 'fun'.")
(defun pascal-function-hook ()
  (if (= 4 (pascal-point-state))
      (if (pascal-in-parm)
	  (insert-indented-string pascal-function-parm-str)
	(insert-indented-string pascal-function-str))
    (insert-string last-abbrev-text))
  )

(defvar pascal-const-str
  "const\n  "
  "*const declaration section template; abbreviated 'const'.")
(defun pascal-const-hook ()
  (if (= 4 (pascal-point-state))
      (insert-indented-string pascal-const-str)
    (insert-string last-abbrev-text))
  )

(defvar pascal-type-str
  "type\n  "
  "*type declaration section template; abbreviated 'type'.")
(defun pascal-type-hook ()
  (if (= 4 (pascal-point-state))
      (insert-indented-string pascal-type-str)
    (insert-string last-abbrev-text))
  )

(defvar pascal-var-str
  "var\n  "
  "*Variable declaration section template; abbreviated 'var'.")
(defvar pascal-var-parm-str
  "var"
  "*Var parameter template; abbreviated 'var'.")
(defun pascal-var-hook ()
  (if (= 4 (pascal-point-state))
      (if (pascal-in-parm)
	  (insert-indented-string pascal-var-parm-str)
	(insert-indented-string pascal-var-str))
    (insert-string last-abbrev-text))
  )

(defvar pascal-record-str
  "record\n  !\n   end;"
  "*Record declaration template; abbreviated 'record'.")
(defun pascal-record-hook ()
  (if (= 4 (pascal-point-state))
      (insert-indented-string pascal-record-str)
    (insert-string last-abbrev-text))
  )

gjditchfield@watmsg.waterloo.edu (Glen Ditchfield) (07/05/89)

This is a texinfo file describing a (separately posted) Pascal mode.

    Glen Ditchfield  gjditchfield@violet.uwaterloo.ca  Office: DC 2517
Dept. of Computer Science, U of Waterloo, Waterloo, Ontario, Canada, N2L 3G1
	"... and the rest, we will spend foolishly!" -- _Svengali_
-------------------------------------------------------------------------------
@setfilename Pascal

@node Pascal, Commands, (dir), (dir)
@section Pascal Mode

Pascal mode is a collection of functions which make the task of typing
Pascal source easier by automating indentation and by inserting `noise'
words for you.

Complex Pascal structures are handled by template insertion.  Skeletons
of Pascal structures are inserted, and the typist fills in the missing
parts.  Template insertion is invoked by Emacs' abbreviation expansion
mechanism, to minimize the number of key sequences used.  In most cases,
the abbreviations chosen are Pascal reserved words.  Different
indentation styles can be accommodated by changing the templates used.

To use pascal mode, you should make sure that @code{pascal-mode.elc} is
in one of the directories in your load path, and you should put these
lines in your Emacs initialization file:@refill
@example
(autoload 'pascal-mode "pascal-mode" t)
(setq auto-mode-alist (cons (cons "\\.p$" 'pascal-mode) auto-mode-alist))
@end example

@menu
* Commands::		Commands defined by pascal-mode.
* Abbreviations::	Abbreviation expansion; default abbreviations.
* Variables::		Variables that control Pascal mode behavior.
* Customization::	How to adjust Pascal mode behavior.
* Elision::		Support for selective hiding of Pascal text.
@end menu

Pascal mode and it's documentation is distributed under the terms of the
GNU Emacs General Public License (type @key{C-h} @key{C-c}) and the
statement of no warranty (type @key{C-h} @key{C-w}).@refill

I am sure that Pascal mode has bugs.  Please send bug reports, bug fixes,
and suggestions to me.  I don't promise to fix them, but you never
know@dots{}.  At the time of writing, my addresses are
@quotation
Glen Ditchfield
Department of Computer Science,
University of Waterloo,
Waterloo, Ontario

gjditchfield@violet.uwaterloo.ca
@end quotation

@node Commands, Abbreviations, Pascal, Pascal
@subsection Commands

The following commands are defined by, or particularly useful in, Pascal mode.

@table @kbd
@item M-x pascal-mode
Switch to Pascal mode in the current buffer.  
@item @key{RET}
Create a new line, and leave point at the left margin on the new line.
(@code{newline})
@item @key{LFD}
Create a new line, and indent the line. (@code{newline-check-and-indent})
@item @key{TAB}
Reindent the current line.  (@code{pascal-indent-line})
@item M-@key{TAB}
``Outdent'' the current line.  (@code{pascal-outdent-line}).
@item C-c C-b
Wrap a @code{begin @dots{} end} statement around the region.
(@code{pascal-beginnify-region}).
@item M-C-f
@itemx M-C-b
Skip forward and backward, respectively, over matching parentheses,
@code{record} declarations, and @code{begin}, @code{case}, and @code{repeat} statements.
(Skipping backwards doesn't handle variant records properly.)
(@code{pascal-forward-sexp}, @code{pascal-backward-sexp})
@item @key{DEL}
Delete the previous character.  Tabs are converted to spaces as the
cursor moves backwards. (@code{backward-delete-char-untabify})
@item C-x '
Expand the abbreviation before point, even if Abbrev mode is turned
off.  (@code{expand-abbrev})
@item M-x list-abbrevs
List all defined abbreviations, including those used by Pascal mode.
@end table

@code{pascal-mode} sets up data structures used by Pascal mode, and calls
the value of @code{pascal-mode-hook} if that variable is non-nil.  You may
have to run this function to edit Pascal .h files, since Emacs assumes that
.h files contain C code. (To avoid this, put the line `{-*-Pascal-*-}' at
the start of all of your Pascal .h files.)@refill

@key{LFD} is bound to @code{newline-check-and-indent}, which creates a new,
properly indented line (see below).  If the previous line contains an
un-closed string, or if it contains an unclosed comment and you have
defined `comment-multi-line' to be NIL, a warning message is printed in the
minibuffer.@refill

@key{TAB} reindents the current line.  For both @key{TAB} and @key{LFD},
the indentation of the nearest previous line that is not flush with the
left margin is used as an initial guess.  If the previous line was not a
complete statement, the indentation is increased by the value of
`pascal-indent-level'.  The heuristics used here are crude, so the
indentation level may be increased unnecessarily.@refill

@kbd{M-@key{TAB}} Reduces the current line's indentation.  It does this by
looking backwards for a line that is less indented than the current line,
and using that indentation.  This fixes most mistakes made by @key{TAB}.@refill

@node Abbreviations, Variables, Commands, Pascal
@subsection Abbreviations

When Abbrev mode is on (as it normally will be), typing an abbreviation
followed by a punctuation character or blank causes the abbreviation to be
expanded.  If the abbreviation stands for a Pascal template, Pascal mode
will generally check to see if the abbreviation was typed in a comment or
string.  If it was, the abbreviation is not expanded.  If it was typed in
program text, the proper template is inserted.

It is hard to decide whether the abbreviation is inside a comment or
string, especially since there is no guarantee that the source code is
syntactically correct.  The scheme used will expand abbreviations that
occur inside comments, but after nested comments. (This may not be a bad
thing, since a typist in that situation is probably editing source code
that has been ``commented out''.)

A final problem: if the cursor just happens to be positioned at the end
of a word that just happens to be an abbreviation, and you type a space,
linefeed or punctuation character, the abbreviation gets expanded.  If all
this starts annoying you, you can turn off Abbrev-mode and use @kbd{C-x '}
(`expand-abbrev') to explicitly insert templates.

The default abbreviations and the Pascal structures that they insert are:

@table @kbd
@item ie
If-then-else statement.
@item if
If-then statement.
@item else
Else clause.  If the previous statement ended with a `;', that `;' is
removed.
@item ei
Else-if-then clause.  This is useful for building code sequences that
look like
@example
if a then
   s1
else if b then
   s2
else if c then
   @dots{}
@end example
Again, any previous `;' is removed.
@item while
While-do statement.
@item for
For-to-do statement.
@item with
With statement.
@item repeat
Repeat-until statement.
@item begin
Begin statement.
@item const
Const declaration section.
@item type
Type declaration section.
@item record
Record type template.
@item var
Variable declaration section.  Which template is inserted depends on
whether the abbreviation occurs in a routine's formal parameter list or a
declaration section.
@item fun
Function declaration.  This abbreviation is also treated differently in
parameter lists.
@item proc
Procedure declaration.  This abbreviation is also treated differently in
parameter lists.
@item prog
Program template.
@item int
Abbreviation for `integer'. (This makes life easier for C programmers).
@end table

This set of abbreviations will be installed the first time you use Pascal
mode.  When you leave Emacs, you will be given the option of saving them in
a file called `.abbrev_defs'.  Say yes, and it won't bother you again.

Lots of documentation on abbreviations is stored in the Info tree; use
C-h i if you are curious.

@node Variables, Customization, Abbreviations, Pascal
@subsection Variables

You can re-define these values in your init file to adjust certain features
to taste.

@table @kbd
@item comment-multi-line
The default value, @code{nil}, means that comments should not span line
boundaries.  Set it to @code{t} if you let comments cross line boundaries.

If it is @code{nil}, Pascal mode is more efficient, and newline-check-
and-indent will warn you about unclosed comments.  If you are typing a
comment and use @kbd{M-@key{LFD}} to continue the comment on a new line, or
if Fill mode is on and it starts a new line for you, comment delimiters
will be inserted if comment-multi-line is nil.  See the Info section on
comments for a better explanation.
@item pascal-comment-start
The string you like comments to start with (default @kbd{"@{ "}).
@item pascal-comment-end
The string you like comments to end with (default @kbd{"\t@}"}).
@item pascal-indent-level
The number of columns that continued statements should be indented
(default 3).
@item pascal-mode-hook
This variable's value should be @code{nil} or a function with no arguments.
The function will be called whenever a pascal file is opened.  You might
use this function to turn on Fill mode, or to turn off Abbrev mode.
(default @code{nil})
@item pascal-auto-newline
If this variable is non-nil, typing ``;'' at the end of a line causes
a new line to be started.  (Default @code{nil})
@item pascal-if-else-str	        pascal-begin-str
@itemx pascal-if-str			pascal-program-str
@itemx pascal-else-str		pascal-procedure-str
@itemx pascal-else-if-str		pascal-procedure-parm-str
@itemx pascal-while-str		pascal-function-str
@itemx pascal-for-str			pascal-function-parm-str
@itemx pascal-repeat-str		pascal-const-str
@itemx pascal-with-str		pascal-type-str
@itemx pascal-case-str		pascal-var-str
@itemx pascal-record-str		pascal-var-parm-str
These strings contain the templates for the associated abbreviation.  See
below for instructions on how to modify them.
@end table

@node Customization, Elision, Variables, Pascal
@subsection Customization

You probably hate the way everyone else indents Pascal code.  Fortunately
for you, Pascal mode can be adjusted.

Each template is stored in a defined constant.  For instance, the
if-then-else template is stored in @code{pascal-if-else-str}, which looks like
@example
   "if! then begin\n   \n   end\nelse begin\n   \n   end;"
@end example
If the typist enters
@example
   ie<@key{SPC}>
@end example
into a Pascal mode buffer, then the result is
@example
   if *then begin		(`*' marks the location of point)

      end
   else begin

      end;
@end example

Notes:
@itemize @bullet
@item
Formatting is controlled by putting spaces, tabs, and \n in the string.
Each ``line'' in the string is inserted with the same indentation that the
line containing the abbreviation had.
@item
The ``!'' in the string marks the approximate final location of point.  When
the abbreviation is expanded, the character that caused the expansion (in
this case, @key{SPC}) replaces the ``!'', and point is left there.  If the
string contains no ``!'', point is left at the end of the inserted text.
@item
The @code{begin}s and @code{end}s are not necessary, but there is no good reason to
leave them out of the template, since the typist won't have to type them.
Blank lines with 3 ``extra'' spaces are provided to make it easy to
enter correctly indented statements.  Typing C-n C-e with point in
the indicated spot moves point to the point immediately above the
`e' in the first @code{end}.
@end itemize

Now, let's assume that Fred likes his if-then-else statements arranged
like this:
@example
if condition then begin
      statement;
end else begin
      statement;
end;
@end example
In that case, Fred's init file should contain the lines
@example
      (defconst pascal-if-else-str
         "if! then begin\n        \nend else begin\n        \nend;")
@end example

You can also define new templates.  If you want an abbreviation to expand
into a string that contains no newlines, use the normal Abbrev-mode
functions as described in the Info sections on abbreviation.  This is the
way Pascal mode expands `int' into `integer'.

If you want a multi-line template, you will have to write an abbreviation
hook.  The hook for `ie' looks like this:
@example
	     (defun pascal-if-else-hook ()
	       (if (= 4 (pascal-point-state))
	           (insert-indented-string pascal-if-else-str)
		 (insert-string last-abbrev-text))
	       )
@end example
The function works like this:
@itemize @bullet
@item
pascal-point-state returns 1 if point is in a comment, 2 if it is in
a string, and 4 if it is in program text.
@item
If point is in program text, @code{insert-indented-string} inserts the
template string with proper indentation.  Otherwise, expansion shouldn't
be done, so the abbreviation that invoked the hook is restored.
@end itemize

Other useful functions are @code{pascal-in-parm}, which returns @code{t} if
the cursor is in a formal parameter list, and @code{no-prev-semi}, which
deletes the non-whitespace character before the cursor if it is `;'.

Once you have written your hook, you can install it by putting a line
similar to
@example
   ("ie" "" pascal-if-else-hook 0)
@end example
in the @code{pascal-mode-abbrev-table} section of your @code{.abbrev_defs}
file.

@node Elision, , Customization, Pascal    
@subsection Elision
The Elide minor mode provides support for selective hiding of text; see
the Info section on Elide for further details.  Elision can be tuned for
Pascal text with the @file{pascal-elide} library.  To use this library,
you should make sure that @file{pascal-elide.elc} is in one of the
directories in your load path.  (This will normally be true, since
@file{pascal-elide} and @file{pascal-mode} should be in the same
directory.)  You should put the line@refill
@example
(autoload 'pascal-elide-setup "pascal-elide" t);
@end example
in your Emacs initialization file, and the lines
@example
(pascal-elide-setup)
(elide-mode 1)
@end example
in your Pascal mode-hook function.

@code{pascal-elide-setup} causes functions and procedures to be treated
as sections, regardless of their indentation.  Hence, typing @kbd{C-c
C-h} when point is in the header of a function causes the declaration
and statement sections of the function to disappear.

@bye

bob@allosaur.cis.ohio-state.edu (Bob Sutterfield) (07/05/89)

This was posted many moons ago, and we've been using it ever since.
From the comments, it's got quite a long and varied pedigree, but
since it originally appeared on the net it can at least be distributed
via the same mechanisms and with similar (non?)restrictions.

Article 638 of comp.emacs:
Relay-Version: version B 2.10.3 alpha 5/22/85; site osu-eddie.UUCP
Path: osu-eddie!cbatt!ihnp4!ptsfa!lll-lcc!seismo!mnetor!utzoo!utgpu!water!watnot!watcgl!pjmbaker
From: pjmbaker@watcgl.UUCP
Newsgroups: comp.emacs
Subject: Re: Pascal mode for GNU emacs (here is one)
Message-ID: <807@watcgl.UUCP>
Date: 30 Mar 87 15:01:01 GMT
Date-Received: 4 Apr 87 07:35:30 GMT
References: <27@gt-eedsp.UUCP>
Reply-To: pjmbaker@watcgl.UUCP (Peter J M Baker)
Distribution: world
Organization: U. of Waterloo, Ontario
Lines: 389


Here is a pascal mode that was sent to me awhile back after I made the
same request.  I have subsequently had a number of requests for it so
I decided to post it.  I did NOT write it.  I do not even *like*
Pascal but was required to use it for a graphics project at U. of
Waterloo.  I believe the GNU people are including something derived
from this in their current (or next?) release of GNU emacs.

				Happy Editting
Peter Baker, Computer Science Dept., University of Waterloo
USENET:  {ihnp4|allegra|utzoo|utcsri}!watmath!watcgl!pjmbaker
CSNET :  pjmbaker%watcgl@waterloo.csnet
BITNET :  pjmbaker@water.bitnet
arpa  :  pjmbaker%watcgl%waterloo.csnet@csnet-relay.arpa


---------------- cut here ----------------
; Pascal editing support package
; Author Mick Jordan for Modula-2
; amended Peter Robinson
; ported to GNU Michael Schmidt <michael@pbinfo.uucp>
; Modified by Tom Perrine <Perrin@LOGICON.ARPA> (TEP)
; analogue for pascal by Vincent Broman <broman@bugs.nosc.mil>

(setq auto-mode-alist (cons (cons "\\.p$" 'pascal-mode) auto-mode-alist))
(setq auto-mode-alist (cons (cons "\\.h$" 'pascal-mode) auto-mode-alist))

;;; Added by TEP
(defvar pascal-mode-syntax-table nil
  "Syntax table in use in Pascal-mode buffers.")

(if pascal-mode-syntax-table
    ()
  (let ((table (make-syntax-table)))
    (modify-syntax-entry ?\\ "." table)
    (modify-syntax-entry ?\{ "<" table)
    (modify-syntax-entry ?\} ">" table)
    (modify-syntax-entry ?\( "()1" table)
    (modify-syntax-entry ?\) ")(4" table)
    (modify-syntax-entry ?\[ "(]" table)
    (modify-syntax-entry ?\] ")[" table)
    (modify-syntax-entry ?* ". 23" table)
    (modify-syntax-entry ?/ "." table)
    (modify-syntax-entry ?+ "." table)
    (modify-syntax-entry ?- "." table)
    (modify-syntax-entry ?= "." table)
    (modify-syntax-entry ?% "." table)
    (modify-syntax-entry ?\& "." table)
    (modify-syntax-entry ?\| "." table)
    (modify-syntax-entry ?\$ "_" table)
    (modify-syntax-entry ?< "." table)
    (modify-syntax-entry ?> "." table)
    (modify-syntax-entry ?\' "\"" table)
    (modify-syntax-entry ?\" "\"" table)
    (setq pascal-mode-syntax-table table)))

;;; Added by TEP
(defvar pascal-mode-map nil
  "Keymap used in Pascal mode.")

(if pascal-mode-map ()
  (let ((map (make-sparse-keymap)))
    (define-key map "\C-i" 'pascal-tab)
    (define-key map "\C-m" 'pascal-newline)
    (define-key map "\C-cb" 'pascal-begin)
    (define-key map "\C-cc" 'pascal-case)
    (define-key map "\C-c\C-c" 'pascal-const)
    (define-key map "\C-ce" 'pascal-else)
    (define-key map "\C-cf" 'pascal-for)
    (define-key map "\C-c\C-f" 'pascal-function)
    (define-key map "\C-ch" 'pascal-header)
    (define-key map "\C-ci" 'pascal-if)
    (define-key map "\C-c\C-i" 'pascal-include)
    (define-key map "\C-c\C-p" 'pascal-procedure)
    (define-key map "\C-cp" 'pascal-program)
    (define-key map "\C-cr" 'pascal-repeat)
    (define-key map "\C-c\C-r" 'pascal-record)
    (define-key map "\C-c\C-t" 'pascal-type)
    (define-key map "\C-c\C-v" 'pascal-var)
    (define-key map "\C-cw" 'pascal-while)
    (define-key map "\C-c\C-w" 'pascal-with)
    (define-key map "\C-c*" 'pascal-star-display-comment)
    (define-key map "\C-c{" 'pascal-display-comment)
    (define-key map "\C-c}" 'pascal-inline-comment)
    (define-key map "\C-c(" 'pascal-paired-parens)
    (define-key map "\C-c[" 'pascal-paired-brackets)
    (define-key map "\C-ct" 'pascal-toggle)
    (define-key map "\C-cL" 'pascal-link)
    (define-key map "\C-cC" 'pascal-compile)
    (setq pascal-mode-map map)))

(defvar pascal-indent 4 "*This variable gives the indentation in Pascal-Mode")
  
(defun pascal-mode ()
"This is a mode intended to support program development in Pascal.
Most control constructs of Pascal can be created by typing
Control-C followed by the first character of the construct.

C-c p    program        C-c b    begin-end
C-c C-c  const          C-c c    case-do
C-c C-t  type           C-c t    toggle between .p-.h
C-c C-v  var            C-c {    enter matched braces
C-c C-r  record         C-c r    repeat-until
C-c C-w  with-do        C-c w    while-do
C-c C-i  #include       C-c i    if-then
C-c C-p  procedure      C-c e    else
C-c C-f  function       C-c f    for-do

\\{pascal-mode-map}

Variable pascal-indent controls the number of spaces for each indentation."
  (interactive)
  (kill-all-local-variables)
  (use-local-map pascal-mode-map)
  (setq major-mode 'pascal-mode)
  (setq mode-name "Pascal")
  (make-local-variable 'comment-column)
  (setq comment-column 41)
  (make-local-variable 'end-comment-column)
  (setq end-comment-column 72)
  (set-syntax-table pascal-mode-syntax-table)
  (make-local-variable 'paragraph-start)
  (setq paragraph-start (concat "^$\\|" page-delimiter))
  (make-local-variable 'paragraph-separate)
  (setq paragraph-separate paragraph-start)
;  (make-local-variable 'indent-line-function)
;  (setq indent-line-function 'c-indent-line)
  (make-local-variable 'require-final-newline)
  (setq require-final-newline t)
  (make-local-variable 'comment-start)
  (setq comment-start "{\n")
  (make-local-variable 'comment-end)
  (setq comment-end "\n}")
  (make-local-variable 'comment-column)
  (setq comment-column 41)
  (make-local-variable 'comment-start-skip)
  (setq comment-start-skip "/\\*+ *")
  (make-local-variable 'comment-indent-hook)
  (setq comment-indent-hook 'c-comment-indent)
  (make-local-variable 'parse-sexp-ignore-comments)
  (setq parse-sexp-ignore-comments t)
  (run-hooks 'pascal-mode-hook))

(defun pascal-newline ()
  "Start new line and indent to current tab stop."
  (interactive)
  (setq cc (current-indentation))
  (newline)
  (indent-to cc)
  )

(defun pascal-tab ()
  "Indent to next tab stop."
  (interactive)
  (indent-to (* (1+ (/ (current-indentation) pascal-indent)) pascal-indent)))

(defun pascal-begin ()
  "Insert a BEGIN-END pair and indent for the line between."
  (interactive)
  (insert "BEGIN")
  (pascal-newline)
  (pascal-newline)
  (insert "END;")
  (let ((comment (read-string "comment about block: ")))
    (cond ((not (string-equal comment "")) (insert " {" comment "}"))))
  (end-of-line 0))

(defun pascal-case ()
  "Build skeleton CASE statment, prompting for the <expression>."
  (interactive)
  (insert "CASE ")
  (let ((selector (read-string "selector-expr: ")))
    (progn
      (insert selector " OF")
      (pascal-newline)
      (pascal-newline)
      (insert "END; {case " selector "}")))
  (end-of-line 0)
  (pascal-tab))

(defun pascal-else ()
  "Insert ELSE keyword and indent for next line."
  (interactive)
  (insert "ELSE")
  (pascal-newline)
  (pascal-tab))

(defun pascal-for ()
  "Build skeleton FOR loop statment, prompting for the loop parameters."
  (interactive)
  (insert "FOR ")
  (insert (read-string "init: ") " TO ")
  (insert (read-string "limit: ") " DO")
  (pascal-newline)
  (pascal-tab))

(defun pascal-header ()
  "Insert a comment block containing the module title, author, etc."
  (interactive)
  (insert "(*\n    Title: \t")
  (insert (read-string "Title: "))
  (insert "\n    Created:\t")
  (insert (current-time-string))
  (insert "\n    Author: \t")
  (insert (user-full-name))
  (insert (concat "\n\t\t<" (user-login-name) "@" (system-name) ">\n"))
  (insert "*)\n\n"))

(defun pascal-if ()
  "Insert skeleton IF statment, prompting for a boolean-expression."
  (interactive)
  (insert "IF ")
  (insert (read-string "condition: ") " THEN")
  (pascal-newline)
  (pascal-tab))

(defun pascal-program ()
  (interactive)
  (insert "PROGRAM ")
  (let ((name (read-string "program name: " )))
    (insert name " (input, output")
    (let ((arglist (read-string "other file vars: ")))
      (cond ((not (string-equal "" arglist)) (insert ", " arglist)))
      (insert ");"))
    (pascal-newline)
    (pascal-newline)
    (insert "BEGIN")
    (pascal-newline)
    (pascal-newline)
    (insert "END. {")
    (insert name)
    (insert "}")
    (end-of-line 0)
    (pascal-tab)))

(defun pascal-procedure ()
  (interactive)
  (insert "PROCEDURE ")
  (let ((name (read-string "name: " )))
    (insert name "(")
    (insert (read-string "argument list: ") ");")
    (pascal-newline)
    (pascal-newline)
    (pascal-tab)
    (insert "BEGIN")
    (pascal-newline)
    (pascal-newline)
    (insert "END; {")
    (insert name)
    (insert "}")
    (end-of-line 0)
    (pascal-tab)))

(defun pascal-function ()
  (interactive)
  (insert "FUNCTION ")
  (let ((name (read-string "name: " )))
    (insert name "(")
    (insert (read-string "argument list: ") "): ")
    (insert (read-string "result type: ") ";")
    (pascal-newline)
    (pascal-newline)
    (pascal-tab)
    (insert "BEGIN")
    (pascal-newline)
    (pascal-newline)
    (insert "END; {")
    (insert name)
    (insert "}")
    (end-of-line 0)
    (pascal-tab)))

(defun pascal-with ()
  (interactive)
  (insert "WITH ")
  (insert (read-string "idents: "))
  (insert " DO")
  (pascal-newline)
  (pascal-tab))

(defun pascal-record ()
  (interactive)
  (insert "RECORD")
  (pascal-newline)
  (pascal-newline)
  (insert "END;")
  (let ((comment (read-string "comment about record: ")))
    (cond ((not (string-equal comment "")) (insert " {" comment "}"))))
  (end-of-line 0)
  (pascal-tab))

(defun pascal-type ()
  (interactive)
  (insert "TYPE")
  (pascal-newline)
  (pascal-tab))

(defun pascal-const ()
  (interactive)
  (insert "CONST")
  (pascal-newline)
  (pascal-tab))

(defun pascal-repeat ()
  (interactive)
  (insert "REPEAT")
  (pascal-newline)
  (pascal-newline)
  (insert "UNTIL ")
  (insert (read-string "exit cond: ") ";")
  (end-of-line 0)
  (pascal-tab))

(defun pascal-var ()
  (interactive)
  (insert "VAR")
  (pascal-newline)
  (pascal-tab))

(defun pascal-while ()
  (interactive)
  (insert "WHILE ")
  (insert (read-string "entry cond: "))
  (insert " DO")
  (pascal-newline)
  (pascal-tab))

(defun pascal-include ()
  (interactive)
  (insert "\n#include \"")
  (insert (read-string "header file: "))
  (insert "\"")
  (pascal-newline)
  (pascal-newline))


(defun pascal-paired-parens ()
  (interactive)
  (insert "()")
  (backward-char))


(defun pascal-paired-brackets ()
  (interactive)
  (insert "[]")
  (backward-char))

(defun pascal-inline-comment ()
  (interactive)
  (insert "{}")
  (backward-char))

(defun pascal-display-comment ()
"puts comment delimiters around a blank line, making a display comment."
  (interactive)
  (insert "{\n\n}")
  (end-of-line 0))

(defun pascal-star-display-comment ()
"starts a UNIX-style display comment."
  (interactive)
  (insert "(*\n *\n *)")
  (end-of-line 0)
  (pascal-tab))

(defun pascal-compile ()
  (interactive)
  (setq modulename (buffer-name))
  (compile (concat "pc -c " modulename)))

(defun pascal-link ()
  (interactive)
  (compile "make"))

;UNUSED?
;(defun execute-monitor-command (command)
;  (let* ((shell shell-file-name)
;	 (csh (equal (file-name-nondirectory shell) "csh")))
;    (call-process shell nil t t "-cf" (concat "exec " command))))

(defun pascal-toggle ()
  "Toggle between .p and .h files for the module."
  (interactive)
  (cond ((string-equal (substring (buffer-name) -2) ".h")
	 (find-file-other-window
	   (concat (substring (buffer-name) 0 -2) ".p")))
	((string-equal (substring (buffer-name) -2) ".p")
	 (find-file-other-window
	   (concat (substring (buffer-name) 0 -2)  ".h")))))