All pastes #969225 Raw Edit

esolangs.el

public text v1 · immutable
·published 2008-04-03 14:48 UTC
rendered paste body
; esolangs.el: Emacs modes for esoteric programming languages.

;; Things that apply for multiple languages.
(defvar esolangs-recognize-extensions-run nil
  "Whether esolangs-recognize-extensions has been run.")

(defun esolangs-add-auto-mode (a)
  "Adds an association between files and extensions given by a."
  (setq auto-mode-alist (cons a auto-mode-alist)))

(defun esolangs-recognize-extensions ()
  "Associates various esoteric programming language major modes with file
extensions:

`brainfuck-mode' .b
`forte-mode' .4te
`thue-mode' .t
`thutu-mode' .t2 .t22
`underload-mode' .ul
`unlambda-mode' .unl"
  (interactive)
  (if (null esolangs-recognize-extensions-run)
      (progn
	(setq esolangs-recognize-extensions-run t)
	(esolangs-add-auto-mode '("\\.b\\'" . brainfuck-mode))
	(esolangs-add-auto-mode '("\\.4te\\'" . forte-mode))
	;(esolangs-add-auto-mode '("\\.t\\'" . thue-mode))
	(esolangs-add-auto-mode '("\\.t2\\'" . thutu-mode))
	(esolangs-add-auto-mode '("\\.t22\\'" . thutu-mode))
	(esolangs-add-auto-mode '("\\.ul\\'" . underload-mode))
	(esolangs-add-auto-mode '("\\.unl\\'" . unlambda-mode)))))

(defun esolangs-indent-newline-indent ()
  "Add a newline, indenting both before and after the addition of the newline."
  (interactive)
  (save-excursion (indent-according-to-mode))
  (newline)
  (indent-according-to-mode))

(defun esolangs-self-insert-and-indent ()
  "Add the character typed to run this, and indent the current line."
  (interactive)
  (self-insert-command 1)
  (save-excursion
    (indent-according-to-mode)))

;; (brainfuck-mode), for brainfuck.

; The keymap is simple; just the usual \t \r \n for indenting, the usual
; backspace behaviour, and [ and ] autoindent. C-c C-s and C-c C-b allow
; for comment stripping in this buffer and to a new buffer respectively.
(defvar brainfuck-mode-map nil
  "Keymap for brainfuck mode.")

(defun brainfuck-define-mode-map ()
  "Define the brainfuck mode keymap."
  (if (null brainfuck-mode-map)
      (progn
	(setq brainfuck-mode-map (make-sparse-keymap))
	(define-key brainfuck-mode-map "\C-C\C-S" 'brainfuck-strip-comments-from-buffer)
	(define-key brainfuck-mode-map "\C-C\C-B" 'brainfuck-strip-comments-to-new-buffer)
	(define-key brainfuck-mode-map "\t" 'indent-according-to-mode)
	(define-key brainfuck-mode-map "\r" 'esolangs-indent-newline-indent)
	(define-key brainfuck-mode-map "\n" 'esolangs-indent-newline-indent)
	(define-key brainfuck-mode-map "\C-J" 'esolangs-indent-newline-indent)
	(define-key brainfuck-mode-map "[" 'esolangs-self-insert-and-indent)
	(define-key brainfuck-mode-map "]" 'esolangs-self-insert-and-indent)
	(define-key brainfuck-mode-map "\177" 'backward-delete-char-untabify))))

; Provide brainfuck-mode.
(defun brainfuck-mode ()
  "A major editing mode for the language brainfuck.

The only differences between this and fundamental-mode are the presence of the
information that allows Font Lock to work, and automatic indentation.

Two utility functions are also provided, for removing comments from programs:
C-c C-s strips comments from the current buffer, and C-c C-b strips the
comments into a new buffer.

The hook brainfuck-mode-hook will run, if it's non-null."
  (interactive)
  (kill-all-local-variables)
  (brainfuck-define-mode-map)
  (use-local-map brainfuck-mode-map)
  (setq mode-name "Brainfuck")
  (make-local-variable 'require-final-newline)
  (setq require-final-newline nil)
  (make-local-variable 'indent-line-function)
  (setq indent-line-function 'brainfuck-indent-line-function)
  (run-hooks 'brainfuck-mode-hook))

(provide 'brainfuck-mode)

; Font Lock settings.
; font-lock-comment-face is used for comments.
; The font-lock recognizes setting of constants using [-]++++++ (etc.) and
; will mark them in font-lock-constant-face; otherwise keyword-face
; is used for + and - and variable-name-face is used for < and > and I/O
; is shown in builtin-face.
(defvar brainfuck-font-lock-keywords
  '(("[^][<>,.+-]" . font-lock-comment-face)
    ("\\[-\\]\\+*" . font-lock-constant-face)
    ("[,.]" . font-lock-builtin-face)
    ("[+-]" . font-lock-keyword-face)
    ("[<>]" . font-lock-variable-name-face))
  "Default expressions to highlight in brainfuck mode.")
(add-hook 'brainfuck-mode-hook
	  (function (lambda ()
		      (make-local-variable 'font-lock-defaults)
		      (setq font-lock-defaults
			    '(brainfuck-font-lock-keywords t)))))

; Indentation.
(defvar brainfuck-indentation-level 2
  "The amount that one [ or ] indents in brainfuck-mode.")

(defun brainfuck-indent-line-function ()
  "Indent the current line according to its nesting level with [ and ] and
the amount set in `brainfuck-indentation-level'."
  (interactive)
  (let ((prev-line 0))
    (save-excursion
      (forward-line -1)
      (if (null (bobp))
	  (progn
	    (back-to-indentation)
	    (setq prev-line (current-column))
	    (move-beginning-of-line 1)
	    (if (looking-at "^ *\\]") ; to cancel out special handling of a
                                      ; line-start ]
		(setq prev-line
		      (+ prev-line brainfuck-indentation-level)))
	    (back-to-indentation)
	    (while (not (eolp))
	      (progn
		(if (looking-at "\\[")
		    (setq prev-line
			  (+ prev-line brainfuck-indentation-level)))
		(if (looking-at "\\]")
		    (setq prev-line
			  (- prev-line brainfuck-indentation-level)))
		(forward-char 1)))))
      (move-beginning-of-line 2)
      (if (looking-at "^ *\\]")
	  (setq prev-line
		(- prev-line brainfuck-indentation-level))))
    (back-to-indentation)
    (if (>= prev-line 0)
	(indent-line-to prev-line)
      (error "Indentation would be inside left margin"))))

; Stripping comments.
(defun brainfuck-strip-comments-to-new-buffer ()
"Removes all comments from the current Brainfuck program, placing the result
in a new buffer."
  (interactive)
  (let ((tb (get-buffer-create "*Commentless*")))
    (with-current-buffer tb (erase-buffer) (fundamental-mode))
    (append-to-buffer tb (point-min) (point-max))
    (with-current-buffer tb
      (goto-char (point-min))
      (brainfuck-strip-comments-from-buffer)
      (brainfuck-mode)
      (goto-char (point-min)))
    (display-buffer tb t nil)))

(defun brainfuck-strip-comments-from-buffer ()
"Removes all comments from the current buffer, interpreting it as a Brainfuck
program."
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (while (re-search-forward "\\([^][,.<>+-]\\)" nil t) (replace-match "" t t nil 1))
    (goto-char (point-min))
    (while (re-search-forward "\\]\\(\\[[^][]*\\]\\)" nil t) (replace-match "" t t nil 1))
    (goto-char (point-min))
    (while (re-search-forward "^\\(\\[[^][]*\\]\\)" nil t) (replace-match "" t t nil 1))))

;; (forte-mode), for Forte.

; Forte programs are generally indented with the line numbers running 'into
; the margin', so to speak, and everything else past the margin. This means
; that we need to reindent on every digit, and on newline.
(defvar forte-mode-map nil
  "Keymap for Forte mode.")

(defun forte-define-mode-map ()
  "Define the Forte mode keymap."
  (if (null forte-mode-map)
      (progn
	(setq forte-mode-map (make-sparse-keymap))
	(define-key forte-mode-map "\t" 'indent-according-to-mode)
	(define-key forte-mode-map "\r" 'esolangs-indent-newline-indent)
	(define-key forte-mode-map "\n" 'esolangs-indent-newline-indent)
	(define-key forte-mode-map "\C-J" 'esolangs-indent-newline-indent)
	(define-key forte-mode-map "0" 'esolangs-self-insert-and-indent)
	(define-key forte-mode-map "1" 'esolangs-self-insert-and-indent)
	(define-key forte-mode-map "2" 'esolangs-self-insert-and-indent)
	(define-key forte-mode-map "3" 'esolangs-self-insert-and-indent)
	(define-key forte-mode-map "4" 'esolangs-self-insert-and-indent)
	(define-key forte-mode-map "5" 'esolangs-self-insert-and-indent)
	(define-key forte-mode-map "6" 'esolangs-self-insert-and-indent)
	(define-key forte-mode-map "7" 'esolangs-self-insert-and-indent)
	(define-key forte-mode-map "8" 'esolangs-self-insert-and-indent)
	(define-key forte-mode-map "9" 'esolangs-self-insert-and-indent)
	(define-key forte-mode-map "\177" 'backward-delete-char-untabify))))

; Provide forte-mode.
(defun forte-mode ()
  "A major editing mode for the language Forte.

The only differences between this and fundamental-mode are the presence of the
information that allows Font Lock to work, and automatic indentation; a final
newline is also forced on the file so it works with the interpreter.

The hook forte-mode-hook will run, if it's non-null."
  (interactive)
  (kill-all-local-variables)
  (forte-define-mode-map)
  (use-local-map forte-mode-map)
  (setq mode-name "Forte")
  (make-local-variable 'require-final-newline)
  (setq require-final-newline t)
  (make-local-variable 'indent-line-function)
  (setq indent-line-function 'forte-indent-line-function)
  (run-hooks 'forte-mode-hook))

(provide 'forte-mode)

; Font Lock settings.
; Unlike some other esolangs here, Forte has recognisable keywords, comments,
; and 'constants'. We split the constants into constant-face for numbers and
; string-face for strings.
(defvar forte-font-lock-keywords
  '(("REM[^\n:]*" . font-lock-comment-face)
    ("\"[^\"]*\"" . font-lock-string-face)
    ("[0-9]+" . font-lock-constant-face)
    ("\\(PRINT\\|LET\\|PUT\\|GET\\|END\\)" . font-lock-keyword-face))
  "Default expressions to highlight in Forte mode.")
(add-hook 'forte-mode-hook
	  (function (lambda ()
		      (make-local-variable 'font-lock-defaults)
		      (setq font-lock-defaults
			    '(forte-font-lock-keywords t)))))

; Indentation.
(defvar forte-indentation-column 6
  "The column in which line numbers end when indenting Forte programs.")

(defun forte-indent-line-function ()
  "Indent the current line so that its line number (if any) ends in column
`forte-indentation-column'; if it has no line number, it's indented to leave
space for an imaginary line number."
  (interactive)
  (back-to-indentation)
  (if (looking-at "[0-9]+")
      (indent-line-to (- forte-indentation-column (length (match-string 0))))
    (indent-line-to (1+ forte-indentation-column))))

;; (thutu-mode), for Thutu and Thutu2.

; Keymap. We just assign \t to our indent function, and auto-indent on \n and
; ..
(defvar thutu-mode-map nil
  "Keymap for Thutu mode.")

(defun thutu-define-mode-map ()
  "Define the Thutu mode keymap."
  (if (null thutu-mode-map)
      (progn
	(setq thutu-mode-map (make-sparse-keymap))
	(define-key thutu-mode-map "\t" 'indent-according-to-mode)
	(define-key thutu-mode-map "\r" 'esolangs-indent-newline-indent)
	(define-key thutu-mode-map "\n" 'esolangs-indent-newline-indent)
	(define-key thutu-mode-map "\C-J" 'esolangs-indent-newline-indent)
	(define-key thutu-mode-map "." 'esolangs-self-insert-and-indent)
	(define-key thutu-mode-map "\177" 'backward-delete-char-untabify))))

; Provide Thutu-mode.
(defun thutu-mode ()
  "A major editing mode for the languages Thutu and Thutu2.

At present, this only sets Font Lock to settings appropriate for Thutu, and
sets the indentation functions to indent each line like the previous one,
except that @*^! indent the line 2 spaces more, and . indents the line 2 spaces
less, like the example Thutu and Thutu2 programs. Remember to mark dedentations
with . when using this indent mode, or the indentations will be wrong! (This is
needed because there is no information intrinsic to the program that says where
the indentations are otherwise.)

Thutu-mode will run the hook thutu-mode-hook, if it's non-nil."
  (interactive)
  (kill-all-local-variables)
  (thutu-define-mode-map)
  (use-local-map thutu-mode-map)
  (setq mode-name "Thutu")
  (make-local-variable 'require-final-newline)
  (setq require-final-newline t)
  (make-local-variable 'indent-line-function)
  (setq indent-line-function 'thutu-indent-line-function)
  (run-hooks 'thutu-mode-hook))

(provide 'thutu-mode)

; Font Lock settings.
; font-lock-comment-face is used for comments.
; font-lock-builtin-face is used for \-quoted things.
; font-lock-variable-name-face is used for $n expressions and \n expressions.
; font-lock-keyword-face is used for separating /s and the command modifier.
; font-lock-constant-face is used for [...] ranges.
(defvar thutu-font-lock-keywords
  '(("^[ \t]*#.*$" . font-lock-comment-face)
    ("[\\\\][^0-9]" . font-lock-builtin-face)
    ("\\(\\$\\|[\\\\]\\)[0-9]" . font-lock-variable-name-face)
    ("\\(/\\|[#$|,<>@^!*.]$\\)" . font-lock-keyword-face)
    ("\\[[^]\n]*\\]" . font-lock-constant-face))
  "Default expressions to highlight in Thutu mode.")
(add-hook 'thutu-mode-hook
	  (function (lambda ()
		      (make-local-variable 'font-lock-defaults)
		      (setq font-lock-defaults
			    '(thutu-font-lock-keywords t)))))

; Indentation function.
(defun thutu-indent-line-function ()
  "Indent the current line like the last one, except that the indentation is
increased by two spaces if the previous line ended with *@^! and decreased by
two spaces if this line is .."
  (interactive)
  (let ((prev-line 0))
    (save-excursion
      (forward-line -1)
      (if (null (bobp))
	  (progn
	    (back-to-indentation)
	    (setq prev-line (current-column))
	    (if (looking-at "\\([^#].*/\\)?[@*^!]$")
		(setq prev-line (+ prev-line 2))))))
    (back-to-indentation)
    (if (looking-at "\\.$") (setq prev-line (- prev-line 2)))
    (indent-line-to prev-line)))

;; (underload-mode), for Underload.

; The keymap is even simpler than for Brainfuck, because auto-indentation
; is definitely not a good idea for Underload!

(defvar underload-mode-map nil
  "Keymap for Underload mode.")

(defun underload-define-mode-map ()
  "Define the Underload mode keymap."
  (if (null underload-mode-map)
      (progn
	(setq underload-mode-map (make-sparse-keymap))
	(define-key underload-mode-map "\t" 'indent-according-to-mode)
	(define-key underload-mode-map "\177" 'backward-delete-char-untabify))))

; Provide underload-mode.
(defun underload-mode ()
  "A major editing mode for the language Underload.

The only differences between this and fundamental-mode are the presence of the
information that allows Font Lock to work, and some indentation rules have
been implemented. They are not used by default, though, because they will
break programs unless all newlines are properly commented out, and they
will break newlines inside strings.

The hook underload-mode-hook will run, if it's non-null."
  (interactive)
  (kill-all-local-variables)
  (underload-define-mode-map)
  (use-local-map underload-mode-map)
  (setq mode-name "Underload")
  (make-local-variable 'require-final-newline)
  (setq require-final-newline nil)
  (make-local-variable 'indent-line-function)
  (setq indent-line-function 'underload-indent-line-function)
  (run-hooks 'underload-mode-hook))

(provide 'underload-mode)

; Font Lock settings.
; font-lock-comment-face is used for comments, i.e. (comment)!
; font-lock-string-face is used for innermost paren groups that contain
; non-Underload symbols; font-lock-constant-face is used for the first
; few Church numerals (up to 20), because the general rule can't be written
; as an Emacs regexp. Otherwise, font-lock-string-face is used for characters
; with no Underload meaning.
(defvar underload-font-lock-keywords
  '(("([^()]*)!" . font-lock-comment-face)
    ("(!())" . font-lock-constant-face)
    ("()" . font-lock-constant-face)
    ("(:\\*)" . font-lock-constant-face)
    ("(::\\*\\*)" . font-lock-constant-face)
    ("(:::\\*\\*\\*)" . font-lock-constant-face)
    ("(::::\\*\\*\\*\\*)" . font-lock-constant-face)
    ("(:::::\\*\\*\\*\\*\\*)" . font-lock-constant-face)
    ("(::::::\\*\\*\\*\\*\\*\\*)" . font-lock-constant-face)
    ("(:::::::\\*\\*\\*\\*\\*\\*\\*)" . font-lock-constant-face)
    ("(::::::::\\*\\*\\*\\*\\*\\*\\*\\*)" . font-lock-constant-face)
    ("(:::::::::\\*\\*\\*\\*\\*\\*\\*\\*\\*)" . font-lock-constant-face)
    ("(::::::::::\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*)" . font-lock-constant-face)
    ("(:::::::::::\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*)" . font-lock-constant-face)
    ("(::::::::::::\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*)" . font-lock-constant-face)
    ("(:::::::::::::\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*)" . font-lock-constant-face)
    ("(::::::::::::::\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*)" . font-lock-constant-face)
    ("(:::::::::::::::\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*)" . font-lock-constant-face)
    ("(::::::::::::::::\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*)" . font-lock-constant-face)
    ("(:::::::::::::::::\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*)" . font-lock-constant-face)
    ("(::::::::::::::::::\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*)" . font-lock-constant-face)
    ("(:::::::::::::::::::\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*)" . font-lock-constant-face)
    ("([^()]*[^aS()~^*:!][^()]*)" . font-lock-string-face)
    ("[^aS()~^*:!]" . font-lock-string-face))
  "Default expressions to highlight in Underload mode.")
(add-hook 'underload-mode-hook
	  (function (lambda ()
		      (make-local-variable 'font-lock-defaults)
		      (setq font-lock-defaults
			    '(underload-font-lock-keywords t)))))

; Indentation.
(defvar underload-indentation-level 2
  "The amount that one ( or ) indents in underload-mode. Note that using
indentation can be dangerous unless you properly comment out newlines.")

(defun underload-indent-line-function ()
  "Indent the current line according to its nesting level with ( and ) and
the amount set in `underload-indentation-level'. Note that using indentation
can be dangerous unless you properly comment out newlines."
  (interactive)
  (let ((prev-line 0))
    (save-excursion
      (forward-line -1)
      (if (null (bobp))
	  (progn
	    (back-to-indentation)
	    (setq prev-line (current-column))
	    (while (not (eolp))
	      (progn
		(if (looking-at "(")
		    (setq prev-line (+ prev-line underload-indentation-level)))
		(if (looking-at ")")
		    (setq prev-line (- prev-line underload-indentation-level)))
		(forward-char 1))))))
    (back-to-indentation)
    (indent-line-to prev-line)))


;; (unlambda-mode), for Unlambda.
(defvar unlambda-mode-map nil
  "Keymap for Unlambda mode.")
(if unlambda-mode-map nil
  (setq unlambda-mode-map (make-sparse-keymap))
  (define-key unlambda-mode-map "\C-C\C-E" 'unlambda-eliminate-lambdas)
  (define-key unlambda-mode-map "\C-C\C-B" 'unlambda-eliminate-lambdas-to-new-buffer))

(defun unlambda-eliminate-lambdas-to-new-buffer (noopt)
  "Eliminates lambdas from the current buffer into a new buffer."
  (interactive "P")
  (let ((tb (get-buffer-create "*Unabstraction*")))
    (with-current-buffer tb (erase-buffer) (fundamental-mode))
    (append-to-buffer tb (point-min) (point-max))
    (with-current-buffer tb
      (goto-char (point-min))
      (unlambda-eliminate-lambdas (point-min) (point-max) noopt)
      (unlambda-mode)
      (goto-char (point-min))
      (while (re-search-forward "^[^#].*[^?.^$]\\(!\\)" nil t) (replace-match "i" t t nil 1) (goto-char (point-min))))
    (display-buffer tb t nil))
)

(defun unlambda-eliminate-lambdas (beg end noopt)
  "Eliminates lambdas from region."
  (interactive "*r
P")
  (let ((op (point)) (ee nil))
    (goto-char end)
    (while (re-search-backward "\\^" beg t)
      (let ((soe (point)))
	(while (not (equal ?` (char-after (point))))
	  (if (equal (char-after (point)) ?#) (while (not (equal ?\n (char-after (point)))) (forward-char 1)))
	  (forward-char 1))
	(forward-char 1)
	(setq ee (unlambda-novar-check nil))
	(if (< ee 0) (setq ee (- 0 ee))) ;ee is now the end of the expression
	(unlambda-eliminate-lambdas-1 soe ee noopt ee)
	(setq end (+ (point) (- end ee))) ;shift end to allow for the change in length
	(goto-char end)))
    (if (= op beg) (progn (goto-char beg) (set-mark end)))) ;change mark so that the region appears to stay the same
)

(defun unlambda-eliminate-lambdas-1 (beg end noopt &optional pt)
  "Eliminates lambdas from region, assuming that it's all one function.
This is called by `unlambda-eliminate-lambdas', a better function to use
interactively."
  (interactive "*r
P")
  (let ((op (if pt pt (point))))
    (goto-char end)
    (while (re-search-backward "\\^" beg t)
      (let ((rl ?`))
	(while (not (>= (point) end))
	  (while (looking-at "\\s-") (forward-char 1))
	  (let ((lc (char-after (point))))
	    (delete-char 1)
	    (if (equal lc ?`) (let ((te (unlambda-novar-check rl)))
				(if (and te (or (null noopt) (> te 0)))
				    (if (< te 0)
					(progn (insert "`k`d`") (setq end (+ end 4)) (goto-char (- 5 te)))
				      (progn (insert "`k`") (setq end (+ end 2)) (goto-char (+ 3 (abs te)))))
				  (progn (insert "``s") (setq end (+ end 2)))))
	      (if (equal lc ?^) (progn (setq rl (char-after (point))) (delete-char 1) (setq end (- end 2)))
		(if (equal lc ?$) (if (equal (char-after (point)) rl)
				    (progn (delete-char 1) (insert "i") (setq end (- end 1)))
				    (progn (insert "`k$") (forward-char 1) (setq end (+ end 2))))
		  (if (equal lc ?.) (progn (insert "`k.") (forward-char 1) (setq end (+ end 2)))
		    (if (equal lc ??) (progn (insert "`k?") (forward-char 1) (setq end (+ end 2)))
		      (if (equal lc ?#) (progn (insert "#") (while (not (equal ?\n (char-after (point)))) (forward-char 1)))
			(progn (insert "`k") (insert-char lc 1) (setq end (+ end 2))))))))))))
      (goto-char end))
    (if (= op beg) (progn (goto-char beg) (set-mark end)))) ;change mark so that the region appears to stay the same
)

(defun unlambda-novar-check (rl)
  "Checks the Unlambda expression at point for the variable rl.
If found, it returns nil.
If rl is not found, it returns the end of the expression if it contains
only safe characters ($variable backquote s k i v)
and minus the end of the expression if it contains dangerous characters.
This assumes that point starts just after the initial backquote.
The ! function acts just like i, except that it is considered dangerous
by this check."
  (save-excursion
    (let ((b 2) (d nil) (nv t))
      (while (> b 0)
	(setq b (1- b))
	(if (looking-at "\\s-") (setq b (1+ b))
	  (if (equal (char-after (point)) ?`) (setq b (+ b 2))
	    (if (equal (char-after (point)) ?$) (progn (forward-char 1)
						       (if (equal (char-after (point)) rl) (setq nv nil)))
	      (if (equal (char-after (point)) ?.) (progn (setq d t) (forward-char 1))
		(if (equal (char-after (point)) ?d) (setq d t)
		  (if (equal (char-after (point)) ?c) (setq d t)
		    (if (equal (char-after (point)) ?r) (setq d t)
		      (if (equal (char-after (point)) ?e) (setq d t)
			(if (equal (char-after (point)) ?@) (setq d t)
			  (if (equal (char-after (point)) ?!) (setq d t)
			    (if (equal (char-after (point)) ??) (progn (setq d t) (forward-char 1))
			      (if (equal (char-after (point)) ?|) (setq d t)
				(if (equal (char-after (point)) ?^) (forward-char 1)
				  (if (equal (char-after (point)) ?#)
				      (progn (setq b (1+ b)) (while (not (equal ?\n (char-after (point)))) (forward-char 1)))))))))))))))))
	(forward-char 1))
      ; dangerous checking malfunctions; so just use t rather than d here for now.
      (if nv (if t (- 0 (point)) (point)) nil)))
)

(defun unlambda-mode ()
  "A major editing mode for the language Unlambda."
  (interactive)
  (kill-all-local-variables)
  (setq major-mode 'unlambda-mode)
  (setq mode-name "Unlambda")
  (make-local-variable 'require-final-newline)
  (setq require-final-newline t)
  (use-local-map unlambda-mode-map)
  (run-hooks 'unlambda-mode-hook))

(provide 'unlambda-mode)

(defvar unlambda-font-lock-keywords
  '(("^#[^^\n]*$" . font-lock-comment-face)
    ("^#.*\\^.*$" . font-lock-warning-face)
    ("\\(\\.\\|\\?\\)." . font-lock-constant-face)
    ("\\(\\^\\|\\$\\)." . font-lock-variable-name-face)
    ("[!']" . font-lock-warning-face)
    ("[cdeikrsv@|]" . font-lock-keyword-face))
  "Default expressions to highlight in Unlambda mode.")

(add-hook 'unlambda-mode-hook
	  (function (lambda ()
		      (make-local-variable 'font-lock-defaults)
		      (setq font-lock-defaults '(unlambda-font-lock-keywords t)))))