;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Name:        parallaxis.el
;; Author:      Hartmut Keller
;; Version:     1.3
;; Last Change: Thu Oct 13 18:18:10 1994
;;
;; Description:
;; ============
;; Major mode for editing Parallaxis-III Programs. It provides
;; abbreviations for many statements and standard procedures as well
;; as control sequences for entering whole language constructs.
;;
;; For documentation: C-h m   in Parallaxis-Mode
;;
;; Installation:
;; If the mode isn't already globally defined, you can do the
;; following steps:
;;
;; 1. Prepare a local emacs directory:
;;    If you don't have a local directory yet, where you hold your
;;    additional emacs related stuff then you may want to create one: 
;;    1a: create the directory in a shell  (e.g. ~/emacstools)
;;    1b: edit .emacs and add a line:
;;        (setq load-path (cons (expand-file-name "~/emacstools") load-path))
;; 2. Autoload the mode:
;;    If you want to load the mode automatically when entering
;;    "parallaxis-mode", you have to add the following to your
;;    .emacs file:
;;    (autoload 'parallaxis-mode "parallaxis" "\
;;    Mode for entering Parallaxis-III source programs." t)
;; 3. Recognize the new file name extensions '.pd' and '.pm':
;;    If you want emacs to start parallaxis-mode automatically when
;;    visiting a file ending in '.pd' or '.pm', then add this to your
;;    .emacs file:
;;    (setq auto-mode-alist
;;          (append
;;           '(("\\.pd$" . parallaxis-mode)
;;             ("\\.pm$" . parallaxis-mode))
;;           auto-mode-alist))
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Version history:
;; ----------------
;; 1.0:  first working version
;; 1.1:  replaced the partly used system abbreviation expansion by own
;;       expansion routine
;; 1.2:  TRUE and FALSE added to keywords; corrected comments.
;; 1.3:  Backspace/Delete don't get confused by Tabs at beginning of line
;;       Added some more keywords of standard functions/procedures.
;;
;; In work:
;; 1.4:  Check and correct syntax table (already done for parantheses,
;;       emacs seems to have a bug if parantheses match AND are part
;;       of 2-character comment start/end sequences; so I removed `*'
;;       from the comment sequences)
;;
;; To-do List:
;; -----------
;; - Bei Backspace Tabulatoren aufheben.
;; - alle m2-Kommandos auf Parallaxis-Kommandos aendern
;; - Schauen, ob Syntax-Table stimmt. Ggf. aendern und erweitern
;; - Schauen, ob key-map stimmt. Ggf. aendern oder erweitern
;; - Funktionalitaet vom C-Mode dazunehmen. Pruefen, ob es bei TAB
;;   Konflikte gibt (--> teilweise schon auf M-TAB geaendert)
;; - Eine Funktion, die das gerade aktuelle Konstrukt beendet, also
;;   wenn es eine WHILE-Schleife ist, dann das zugehoerige END dazu
;;   (wenn moeglich mit der Bedingung am Anfang), wenn es IF ist, das
;;   passende END; (* IF *) etc. Vielleicht im LaTeX-Mode abschauen.
;; - Alle Funktionen genau dokumentieren, hauptsaechlich parallaxis-mode 
;;   selbst.
;; - TAB nur dann abbrev-expansion durchfuehren lassen, wenn eine
;;   best. Variable gesetzt ist. Sonst geht aber immer M-TAB
;; - ALL-Konstrukt einbauen, LOOP um parallele Version erweitern
;; - Compiler-Kommandos pruefen und korrigieren. Evtl. nur ein
;;   Kommando setzen (z.B. make)

(provide 'parallaxis-mode)

(defvar parallaxis-indent 2
  "*This variable gives the indentation in Parallaxis-Mode")

(defvar parallaxis-compile-command "p3"
  "*Command to compile Parallaxis-III programs")

(defvar parallaxis-link-command "p3"
  "*Command to link Parallaxis-III programs")

(defvar parallaxis-link-name nil
  "Name of the executable.")



;;The following syntax-table and key-map are from the Modula-2-mode and
;;Emacs Lisp documentation

(defvar parallaxis-mode-syntax-table nil
  "Syntax table used while in parallaxis mode.")

(if parallaxis-mode-syntax-table
    ()                                   ;Do not change if there exists one
  (let ((table (make-syntax-table)))
    (modify-syntax-entry ?\\ "\\" table)
    (modify-syntax-entry ?\( "()1" table)
    (modify-syntax-entry ?\) ")(4" table)
    (modify-syntax-entry ?* "." table)	;Should really be ". 23", but this doesn't 
                                        ;work with blink-matching-open or paren
    (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 ?_ "w" table)    ;"_" also word class in Parallaxis
    (setq parallaxis-mode-syntax-table table)))


(defvar parallaxis-mode-map nil
  "Keymap used while in parallaxis mode.")

(if parallaxis-mode-map 
    ()                                    ;Do not change if there exists one
  (let ((map (make-sparse-keymap)))
    (define-key map "\t" 'parallaxis-tabkey)
    (define-key map "\177" 'parallaxis-delete)
    (define-key map "\M-\t" 'parallaxis-keyword-expand)
    (define-key map "\C-cb" 'parallaxis-begin)
    (define-key map "\C-cc" 'parallaxis-case)
    (define-key map "\C-cd" 'parallaxis-definition)
    (define-key map "\C-ce" 'parallaxis-else)
    (define-key map "\C-cf" 'parallaxis-for)
    (define-key map "\C-ch" 'parallaxis-header)
    (define-key map "\C-ci" 'parallaxis-if)
    (define-key map "\C-cm" 'parallaxis-imodule)
    (define-key map "\C-cl" 'parallaxis-loop)
    (define-key map "\C-co" 'parallaxis-or)
    (define-key map "\C-cp" 'parallaxis-procedure)
    (define-key map "\C-c\C-w" 'parallaxis-with)
    (define-key map "\C-cr" 'parallaxis-record)
    (define-key map "\C-cs" 'parallaxis-stdio)
    (define-key map "\C-ct" 'parallaxis-type)
    (define-key map "\C-cu" 'parallaxis-until)
    (define-key map "\C-cv" 'parallaxis-var)
    (define-key map "\C-cw" 'parallaxis-while)
    (define-key map "\C-cx" 'parallaxis-export)
    (define-key map "\C-cy" 'parallaxis-import)
    (define-key map "\C-c{" 'parallaxis-begin-comment)
    (define-key map "\C-c}" 'parallaxis-end-comment)
    (define-key map "\C-j"  'parallaxis-newline)
    (define-key map "\C-m"  'parallaxis-newline)
    (define-key map "\C-c\C-z" 'suspend-emacs)
    (define-key map "\C-c\C-v" 'parallaxis-visit)
    (define-key map "\C-c\C-t" 'parallaxis-toggle)
    (define-key map "\C-c\C-l" 'parallaxis-link)
    (define-key map "\C-c\C-c" 'parallaxis-compile)
    (setq parallaxis-mode-map map)))


;; This is a list of the most of the Parallaxis-III keywords and standard
;; functions. It serves as a look up table for expanding keywords from
;; prefixes.

(defvar parallaxis-keywords 
  '(
   "ALL " "ADR(" "ABS(" "AND " "ARRAY " "arcsin(" "arccos(" "arctan(" "arctan2("
      "argc" "argv("
   "BEGIN" "BITSET" "BOOLEAN" "BY "
   "CHAR" "CHR(" "CAP(" "CASE " "CARDINAL" "CONST " "CONFIGURATION " "CONNECTION "
      "CloseInput" "CloseOutput" "cos(" "ceiling("
   "DIV " "DEC(" "DIM(" "DIM2ID(" "DISPOSE(" "DEFINITION " "DO "
   "ELSE" "ELSIF " "END " "EVEN(" "EXIT" "EXCL(" "exp(" "EXPORT "
   "FOR " "FOREIGN " "FALSE" "FROM " "FLOAT(" "FIRST" "floor("
   "HIGH(" "HALT"
   "IF " "IN " "ID(" "ID2DIM(" "INC(" "INCL(" "INTEGER" "IMPORT " "IMPLEMENTATION "
   "LOOP" "LOAD(" "LEN(" "LAST" "LOWER(" "ln("
   "MOD " "MODULE " "MOVE." "MAX" "MIN"
   "NEW(" "NOT"
   "OR " "ORD(" "ODD(" "OF " "OpenInput(" "OpenOutput("
   "PROC" "PROCEDURE " "POINTER TO " "PRODUCT" "pi"
   "QUALIFIED "
   "REAL" "Read(" "ReadBool(" "ReadCard(" "ReadInt(" "ReadReal(" "ReadString("
      "RETURN" "REPEAT" "REDUCE." "RANK(" "RECORD" "RECEIVE" "round(" "RandomInt("
      "RandomCard(" "RandomReal(" "RandomChar(" "RandomBool("
   "SEND." "SET OF " "SUM" "sin(" "SIZE(" "STORE(" "sqrt("
   "THEN" "TO " "TRUE" "TRUNC(" "TYPE " "TSIZE(" "tan("
   "UNTIL " "UPPER("
   "VAR " "VAL(" "VECTOR OF "
   "Write(" "WriteBool(" "WriteCard(" "WriteFixPt(" "WriteInt(" "WriteLn"
      "WriteString(" "WriteReal(" "WHILE " "WITH "
   )
  "The keywords of Parallaxis-III; used for abbreviation expansion."
)

;; Some abbreviations are not a prefix of the keyword. Hence they must
;; be checked first before the usual prefix checking takes place. This
;; is *not* done by the normal abbrev-mode due to three reasons: 
;; 1. abbrev-mode expands case-sensitive which is not wanted
;; 2. abbrev-mode cannot handle abbrevs with non-word characters like "r+"
;; 3. abbrev-mode expands abbrevs which are not directly before the
;;    cursor, e.g. "xy(" expands to "expansion-of-xy("; this is unwanted.
;; So we define our own abbreviation expansion.

(defvar parallaxis-abbrevs
  '(
    ("AC" . "arccos(")
    ("AS" . "arcsin(")
    ("AT" . "arctan(")
    ("AT2" . "arctan2(");
    ("A2" . "arctan2(");
    ("BS" . "BITSET")
    ("C" . "CHR(")
    ("CI" . "CloseInput")
    ("CO" . "CloseOutput")
    ("DM" . "DEFINITION MODULE ")
    ("EA" . "END; (*ALL*)")                  ;We may change this behaviour with
    ("EC" . "END; (*CASE*)")                 ;some hooks to get the correct
    ("ECA" . "END; (*CASE*)")                ;conditions in the END-statements
    ("EF" . "END; (*FOR*)")
    ("EI" . "END; (*IF*)")
    ("EL" . "END; (*LOOP*)")
    ("ER" . "END; (*RECORD*)")
    ("EW" . "END; (*WHILE*)")
    ("EWH" . "END; (*WHILE*)")
    ("EWI" . "END; (*WITH*)")
    ("EWT" . "END; (*WITH*)")
    ("EQ" . "EXPORT QUALIFIED ")
    ("ES" . "ELSIF ")
    ("FM" . "FOREIGN MODULE ")
    ("IM" . "IMPLEMENTATION MODULE ")
    ("LD" . "LOAD(")
    ("MV" . "MOVE.")
    ("OI" . "OpenInput(")
    ("OO" . "OpenOutput(")
    ("O" . "ORD(")
    ("P" . "PROCEDURE ")
    ("PT" . "POINTER TO ")
    ("R" . "Read(")
    ("RB" . "ReadBool(")
    ("RC" . "ReadCard(")
    ("RI" . "ReadInt(")
    ("RR" . "ReadReal(")
    ("RS" . "ReadString(")
    ("RAI" . "RandomInt(")
    ("RAC" . "RandomCard(");
    ("RACH" . "RandomChar(");
    ("RAB" . "RandomBool(");
    ("RSUM" . "REDUCE.SUM(")
    ("R+" . "REDUCE.SUM(")
    ("RP" . "REDUCE.PRODUCT(")
    ("RPROD" . "REDUCE.PRODUCT(")
    ("R*" . "REDUCE.PRODUCT(")
    ("RAND" . "REDUCE.AND(")
    ("R&" . "REDUCE.AND(")
    ("ROR" . "REDUCE.OR(")
    ("R|" . "REDUCE.OR(")
    ("R1" . "REDUCE.FIRST(")
    ("RF" . "REDUCE.FIRST(")
    ("RL" . "REDUCE.LAST(")
    ("RMAX" . "REDUCE.MAX(")
    ("R^" . "REDUCE.MAX(")
    ("RMIN" . "REDUCE.MIN(")
    ("R_" . "REDUCE.MIN(")
    ("VO" . "VECTOR OF ")
    ("WT" . "WITH ")
    ("WB" . "WriteBool(")
    ("WC" . "WriteCard(")
    ("WF" . "WriteFixPt(")
    ("WI" . "WriteInt(")
    ("WL" . "WriteLn")
    ("WR" . "WriteReal(")
    ("WS" . "WriteString(")
    )
  "List of abbreviations used in Parallaxis mode. In addition each
keyword can be abbreviated by an unambiguous prefix."
  )


(defun parallaxis-mode ()
  "Mode for entering Parallaxis-III programs.
(This mode is still under development)

Turning on parallaxis-mode runs the hook `parallaxis-mode-hook'.

Special commands:
\\<parallaxis-mode-map>
  \\[parallaxis-begin] begin               \\[parallaxis-case] case
  \\[parallaxis-definition] definition          \\[parallaxis-else] else
  \\[parallaxis-for] for                 \\[parallaxis-header] header
  \\[parallaxis-if] if                  \\[parallaxis-imodule] module
  \\[parallaxis-loop] loop                \\[parallaxis-or] or
  \\[parallaxis-procedure] procedure           \\[parallaxis-with] with
  \\[parallaxis-record] record              \\[parallaxis-stdio] insert stdio
  \\[parallaxis-type] type                \\[parallaxis-until] until
  \\[parallaxis-var] var                 \\[parallaxis-while] while
  \\[parallaxis-export] export              \\[parallaxis-import] import
  \\[parallaxis-begin-comment] begin-comment       \\[parallaxis-end-comment] end-comment
  \\[suspend-emacs] suspend Emacs     \\[parallaxis-toggle] toggle .pd <-> .pm
  \\[indent-for-comment] line comment
  \\[parallaxis-compile] compile           \\[parallaxis-next-error] next-error
  \\[parallaxis-link] link

  \\[parallaxis-keyword-expand] expand abbreviation

In addition this mode provides several abbreviations for the most
Parallaxis-III keywords and standard procedures. Do 'list-abbrevs' for
a list of those abbreviations. In addition to these abbreviations,
every keyword may be abbreviated by a prefix identifying the keyword
unambiguous.

Example:  i   --> IF
          in  --> IN
          int --> INTEGER

The rule how to get the abbreviation for a keyword is this: If the
keyword consists of two words (e.g. `WriteInt') then the abbreviation
is the two starting characters of the two words (i.e. `wi'). All other
words are abbreviated by a prefix, in case of doubt in favour of the
more often used keywords (e.g. 'e' for 'ELSE'). There are a few
exceptions: every `END' has its own form (e.g. 'ef' for 'END; (*FOR*)')
and some often used words get own abbreviations when the prefix would
be too long (e.g. 'es' for 'ELSIF').

To expand an abbreviation you usually just have to type \\[parallaxis-tabkey]. 
If there's no expansion possible, \\[parallaxis-tabkey] indents the line (at the
beginning) or inserts a TAB-char at point. If you just want
abbreviation expansion and no TAB semantics, use \\[parallaxis-keyword-expand].

Hilighting in emacs-19 is also supported.

There are some variables to control the behaviour of the Parallaxis
Mode. (There are more to come in the future.)

   `parallaxis-indent' controls the number of spaces for each indentation.
   `parallaxis-compile-command' holds the command to compile a Parallaxis-III program.
   `parallaxis-link-command' holds the command to link a Parallaxis-III program.

"
  (interactive)
  (kill-all-local-variables)
  (use-local-map parallaxis-mode-map)
  (setq mode-name "Parallaxis")
  (setq major-mode 'parallaxis-mode)
  (set-syntax-table parallaxis-mode-syntax-table)

  (make-local-variable 'comment-column)
  (setq comment-column 42)
  (make-local-variable 'end-comment-column)
  (setq end-comment-column 75)
  (make-local-variable 'comment-start)
  (setq comment-start "(* ")
  (make-local-variable 'comment-end)
  (setq comment-end " *)")
  (make-local-variable 'comment-start-skip)
  (setq comment-start-skip "(\\*+ *")
  (make-local-variable 'comment-indent-function)
  (setq comment-indent-function 'c-comment-indent)

;  (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 'fill-column)
  (setq fill-column (+ end-comment-column 2)) ;end-comment-column + 2

;; muss das auch in den Parallaxis-Mode?
  (make-local-variable 'paragraph-start)
  (setq paragraph-start (concat "^$\\|" page-delimiter))
  (make-local-variable 'paragraph-separate)
  (setq paragraph-separate paragraph-start)
  (make-local-variable 'paragraph-ignore-fill-prefix)
  (setq paragraph-ignore-fill-prefix t)
  (make-local-variable 'parse-sexp-ignore-comments)
  (setq parse-sexp-ignore-comments t)
;; bis hierher


;; Define the patterns ruling the highlighting of the Parallaxis keywords.
(if (fboundp 'hilit-set-mode-patterns)
    (hilit-set-mode-patterns
     'parallaxis-mode
     '(("(\\*" "\\*)" comment)
       ("CASE.*\\(OF\\)" 1 keyword)
       ("\\<LOOP\\([\t ]+OF\\)?" nil keyword)
       ("\\<\\(RECORD\\|ARRAY\\|POINTER[\t ]+TO\\|OF\\|SET\\|VECTOR\\|INTEGER\\|BOOLEAN\\|CHAR\\|CARDINAL\\|REAL\\|BITSET\\|PROC\\|WORD\\|ADDRESS\\)\\>" nil define)  ;well, should be type
       ("^\\s *\\(\\(DEFINITION\\|IMPLEMENTATION\\|FOREIGN\\)\\s +\\)?MODULE[^;]*" nil defun)
       ("\\<BEGIN\\>" nil defun)
       ("^\\s *END[ 	]+\\w+[^;\\.]*" nil defun)
       ("\\<\\(FROM\\|IMPORT\\|EXPORT\\|QUALIFIED\\)\\>" nil include)
       ("\\<\\(VAR\\|CONST\\|TYPE\\|CONFIGURATION\\|CONNECTION\\)\\>" nil define) ;well, should be decl; probably CONST is a define
       ("\\<\\(TO\\|BY\\|END\\|FOR\\|IF\\|THEN\\|ELSE\\|ELSIF\\|CASE\\|WHILE\\|DO\\|RETURN\\|REPEAT\\|UNTIL\\|AND\\|OR\\|NOT\\|DIV\\|MOD\\|IN\\|REDUCE\\|MOVE\\|RECEIVE\\|SEND\\|WITH\\|EXIT\\|LOAD\\|STORE\\|ALL\\)\\>" nil keyword)
       ("^\\s *PROCEDURE\\s +\\w+[^\\s (;]*" nil defun)
       ("\"[^\"]*\"" nil string)
       ("'[^']*'" nil string)
     ))
  nil)

;; As last action run the hooks.
  (run-hooks 'parallaxis-mode-hook))


(defun parallaxis-delete ()
  "Delete character before point; changes TABs to spaces at beginning of line."
  (interactive)
  (if (> (current-column) (current-indentation))
      (delete-backward-char 1)
    (backward-delete-char-untabify 1)))

(defun parallaxis-newline ()
  "Insert a newline and indent following line like previous line."
  (interactive)
  (let ((hpos (current-indentation)))
    (newline)
    (indent-to hpos)))


;; hier jetzt wie im C-mode die Einrueckung einfuegen anstatt immer auf konstanten indent einruecken. Die Erweiterung der Abkuerzung evtl. von einer Variablen abhaengig machen.

(defun parallaxis-tabkey ()
  "Expand abbreviation or indent to next tab stop."
  (interactive)

  (if (parallaxis-keyword-expand)
      ()				;if expanded, do nothing more
    (parallaxis-tab)))			;else indent line


;;M-TAB expands abbreviation if appropriate

(defun parallaxis-keyword-expand ()
  "Expand abbreviation for Parallaxis-III keywords and standard procedures.\
Returns the expanded string if expansion took place, else nil."
  (interactive)

  (let ((start (point))
	(str nil)
	(estr nil))

    ;find the prefix string

    (beginning-of-line)
    (if (and (re-search-forward "\\(\\sw+\\Sw+\\)*\\(\\<\\sw+[*_+^&]?\\)"
				start t)
	     (= start (point)))
	(goto-char (match-beginning 2))
      (goto-char start)
      )

;    (if (re-search-backward "[^\\sw*_+^&]" nil t) ;search backward for first non-word char
;	(forward-char)			;if no such char is found assume prefix
;      (beginning-of-line))		;begins at left end of this line

    (setq str (buffer-substring (point) start))

;    (message "Found: %S" str)
;    (sit-for 2)

    (if (equal str "")			;don't do anything if no valid prefix
	(progn				;found
	  (goto-char start) 
	  nil )

      ;check for an expansion either as an abbrev or as a keyword prefix
      (setq estr (or (cdr (assoc (upcase str) parallaxis-abbrevs))
					;check for abbreviation
		     (parallaxis-check-prefix str)))
					;check for a completion in the keyword-list

      (if (null estr)			;if nothing found, don't change anything
	  (progn
	    (goto-char start) 
	    nil)

	(delete-region (point) start)	;replace the abbreviation with its
	(insert estr)			;expansion
	estr				;return expansion string
	)
      )
    )
  )


(defun parallaxis-check-abbrev (abbrev)
  "Check if argument is an abbreviation listed in `parallaxis-abbrevs'.
If yes, the expanded string is returned, else nil."

  (interactive)

  (cdr (assoc (upcase abbrev) parallaxis-abbrevs))
)


(defun parallaxis-check-prefix (prefix)
  "Scan variable `parallaxis-keywords' and check if the argument is a prefix\
of any of the strings. If a match is found it is returned, else the result\
is nil."

  (interactive)

  (if (not (string-match "\\Sw" prefix)) ;only word components, no wildcards
      (let ((found nil)
	    (pos nil)
	    ;;append ".*" to string to get a regular expression for a prefix
	    (prefixre (concat (upcase prefix) ".*")) 
	    (current nil)
	    (keywords parallaxis-keywords))

	(while (and (car keywords)	;scan list
		    (not found))
	  (setq current (car keywords))
	  (setq keywords (cdr keywords))

	  (setq pos (string-match prefixre current)) ;check for prefix
	  (setq found (if (and pos (= 0 pos)) current ())) ;found a match?
	  )

	found				;return string or nil
	)
    )
)


;; TAB-key and no keyword expansion took place: Insert a TAB-char if
;; in the middle of the text, else indent line.
;; This will be changed in the future where TAB indents a line like in
;; c-mode

(defun parallaxis-tab ()
  "Indent to next tab stop."
  (interactive)
  (let ((tcol (* (1+ (/ (current-indentation) parallaxis-indent)) 
		 parallaxis-indent)))	;calculate next tab stop
    (if (> (current-column) (current-indentation))

	(insert "	")		;insert a TAB

;; This may be useful later when TAB indents the line always
;; correctly, no matter where the point is.
;;	(save-excursion			;indent when point within text
;;	  (move-to-column (current-indentation))
;;	  (indent-to tcol))

      (if (= (current-column) (current-indentation))
	  (indent-to tcol)		;indent when point at start of text

	(move-to-column (current-indentation)))) ;else goto start of text
    )
  )


(defun parallaxis-begin ()
  "Insert a BEGIN keyword and indent for the next line."
  (interactive)
  (insert "BEGIN")
  (parallaxis-newline)
  (parallaxis-tab))

(defun parallaxis-case ()
  "Build skeleton CASE statment, prompting for the <expression>."
  (interactive)
  (let ((name (read-string "Case-Expression: ")))
    (insert "CASE " name " OF")
    (parallaxis-newline)
    (parallaxis-newline)
    (insert "END; (* case " name " *)"))
  (end-of-line 0)
  (parallaxis-tab))

(defun parallaxis-definition ()
  "Build skeleton DEFINITION MODULE, prompting for the <module name>."
  (interactive)
  (insert "DEFINITION MODULE ")
  (let ((name (read-string "Name: ")))
    (insert name ";\n\n\n\nEND " name ".\n"))
  (previous-line 3))

(defun parallaxis-else ()
  "Insert ELSE keyword and indent for next line."
  (interactive)
  (parallaxis-newline)
  (backward-delete-char-untabify parallaxis-indent ())
  (insert "ELSE")
  (parallaxis-newline)
  (parallaxis-tab))

(defun parallaxis-for ()
  "Build skeleton FOR loop statment, prompting for the loop parameters."
  (interactive)
  (insert "FOR ")
  (let ((name (read-string "Loop Initialiser: ")) limit by)
    (insert name " TO ")
    (setq limit (read-string "Limit: "))
    (insert limit)
    (setq by (read-string "Step: "))
    (if (not (string-equal by ""))
	(insert " BY " by))
    (insert " DO")
    (parallaxis-newline)
    (parallaxis-newline)
    (insert "END; (* for " name " to " limit " *)"))
  (end-of-line 0)
  (parallaxis-tab))

(defun parallaxis-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 parallaxis-if ()
  "Insert skeleton IF statment, prompting for <boolean-expression>."
  (interactive)
  (insert "IF ")
  (let ((thecondition (read-string "<boolean-expression>: ")))
    (insert thecondition " THEN")
    (parallaxis-newline)
    (parallaxis-newline)
    (insert "END; (* if " thecondition " *)"))
  (end-of-line 0)
  (parallaxis-tab))

(defun parallaxis-loop ()
  "Build skeleton LOOP (with END)."
  (interactive)
  (insert "LOOP")
  (parallaxis-newline)
  (parallaxis-newline)
  (insert "END; (* loop *)")
  (end-of-line 0)
  (parallaxis-tab))

(defun parallaxis-imodule ()
  "Build skeleton IMPLEMENTATION MODULE, prompting for <module-name>."
  (interactive)
  (insert "IMPLEMENTATION MODULE ")
  (let ((name (read-string "Name: ")))
    (insert name ";\n\n\n\nEND " name ".\n")
    (previous-line 3)
    (parallaxis-header)
    (parallaxis-type)
    (newline)
    (parallaxis-var)
    (newline)
    (parallaxis-begin)
    (parallaxis-begin-comment)
    (insert " Module " name " Initialisation Code "))
  (parallaxis-end-comment)
  (newline)
  (parallaxis-tab))

(defun parallaxis-or ()
  (interactive)
  (parallaxis-newline)
  (backward-delete-char-untabify parallaxis-indent)
  (insert "|")
  (parallaxis-newline)
  (parallaxis-tab))

(defun parallaxis-procedure ()
  (interactive)
  (insert "PROCEDURE ")
  (let ((name (read-string "Name: " ))
	args)
    (insert name " (")
    (insert (read-string "Arguments: ") ")")
    (setq args (read-string "Result Type: "))
    (if (not (string-equal args ""))
	(insert " : " args))
    (insert ";")
    (parallaxis-newline)
    (insert "BEGIN")
    (parallaxis-newline)
    (parallaxis-newline)
    (insert "END ")
    (insert name)
    (insert ";")
    (end-of-line 0)
    (parallaxis-tab)))

(defun parallaxis-with ()
  (interactive)
  (insert "WITH ")
  (let ((name (read-string "Record-Type: ")))
    (insert name)
    (insert " DO")
    (parallaxis-newline)
    (parallaxis-newline)
    (insert "END; (* with " name " *)"))
  (end-of-line 0)
  (parallaxis-tab))

(defun parallaxis-record ()
  (interactive)
  (insert "RECORD")
  (parallaxis-newline)
  (parallaxis-newline)
  (insert "END; (* record *)")
  (end-of-line 0)
  (parallaxis-tab))

(defun parallaxis-stdio ()
  (interactive)
  (insert "
FROM TextIO IMPORT 
   WriteCHAR, ReadCHAR, WriteINTEGER, ReadINTEGER,
   WriteCARDINAL, ReadCARDINAL, WriteBOOLEAN, ReadBOOLEAN,
   WriteREAL, ReadREAL, WriteBITSET, ReadBITSET,
   WriteBasedCARDINAL, ReadBasedCARDINAL, WriteChars, ReadChars,
   WriteString, ReadString, WhiteSpace, EndOfLine;

FROM SysStreams IMPORT sysIn, sysOut, sysErr;

"))

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

(defun parallaxis-until ()
  (interactive)
  (insert "REPEAT")
  (parallaxis-newline)
  (parallaxis-newline)
  (insert "UNTIL ")
  (insert (read-string "<boolean-expression>: ") ";")
  (end-of-line 0)
  (parallaxis-tab))

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

(defun parallaxis-while ()
  (interactive)
  (insert "WHILE ")
  (let ((name (read-string "<boolean-expression>: ")))
    (insert name " DO" )
    (parallaxis-newline)
    (parallaxis-newline)
    (insert "END (* while " name " *);"))
  (end-of-line 0)
  (parallaxis-tab))

(defun parallaxis-export ()
  (interactive)
  (insert "EXPORT QUALIFIED "))

(defun parallaxis-import ()
  (interactive)
  (insert "FROM ")
  (insert (read-string "Module: "))
  (insert " IMPORT "))

(defun parallaxis-begin-comment ()
  (interactive)
  (if (not (bolp))
      (indent-to comment-column 0))
  (insert "(* "))

(defun parallaxis-end-comment ()
  (interactive)
  (if (not (bolp))
      (indent-to end-comment-column))
  (insert "*)"))

(defun parallaxis-compile ()
  (interactive)
  (setq modulename (buffer-name))
  (compile (concat parallaxis-compile-command " " modulename)))

(defun parallaxis-link ()
  (interactive)
  (setq modulename (buffer-name))
  (if parallaxis-link-name
      (compile (concat parallaxis-link-command " " parallaxis-link-name))
    (compile (concat parallaxis-link-command " "
		     (setq parallaxis-link-name (read-string "Name of executable: "
						     modulename))))))

(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))))

;##### visit tut noch nicht

(defun parallaxis-visit ()
  (interactive)
  (let ((deffile nil)
	(modfile nil)
	modulename)
    (save-excursion
      (setq modulename
	    (read-string "Module name: "))
      (switch-to-buffer "*Command Execution*")
      (execute-monitor-command (concat "parallaxiswhereis " modulename))
      (goto-char (point-min))
      (condition-case ()
	  (progn (re-search-forward "\\(.*\\.pd\\) *$")
		 (setq deffile (buffer-substring (match-beginning 1)
						 (match-end 1))))
	(search-failed ()))
      (condition-case ()
	  (progn (re-search-forward "\\(.*\\.pm\\) *$")
		 (setq modfile (buffer-substring (match-beginning 1)
						 (match-end 1))))
	(search-failed ()))
      (if (not (or deffile modfile))
	  (error "I can find neither definition nor implementation of %s"
		 modulename)))
    (cond (deffile
	    (find-file deffile)
	    (if modfile
		(save-excursion
		  (find-file modfile))))
	  (modfile
	   (find-file modfile)))))

(defun parallaxis-toggle ()
  "Toggle between .pm (implementation) and .pd (definition) files for the module."
  (interactive)
  (cond ((string-equal (substring (buffer-name) -3) ".pd")
	 (find-file-other-window
	  (concat (substring (buffer-name) 0 -3) ".pm")))
	((string-equal (substring (buffer-name) -3) ".pm")
	 (find-file-other-window
	  (concat (substring (buffer-name) 0 -3)  ".pd")))
))

