<commit_before>;;; pir-mode.el --- pir-mode.el --- editing IMCC source files under Emacs

;;; Commentary:

;; This package provides Emacs support for PIR.
;; It defines PIR mode, a major mode for editing PIR code.

;; See the documentation of `octave-mode' and `octave-help' for further
;; information. 

(require 'regexp-opt)
;;; Code:
(add-to-list 'auto-mode-alist '("\\.imc\\'" . pir-mode))

(defvar pir-mode-hook nil)

(defgroup pir nil
  "Mode for editing PIR code."
  :group 'languages)

(defcustom pir-comment-char ?#
  "*The `comment-start' character assumed by PIR mode."
  :type 'character
  :group 'pir)

(defcustom pir-comment-column comment-column
  "*The default comment column for PIR code."
  :type 'integer
  :group 'pir)
  
(defvar pir-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map "\C-c;"     'pir-comment-region)
    (define-key map "\C-c:"     'pir-uncomment-region)
    (define-key map (kbd "C-j") 'newline-and-indent)
    (define-key map " "         'pir-electric-space)
    (define-key map (kbd ":")   'pir-electric-colon)
    (define-key map (kbd "RET") 'pir-reindent-then-newline-and-indent)
    (define-key map (kbd "TAB") 'indent-according-to-mode)
    (define-key map "\e\t"      'pir-complete-symbol)
    (define-key map "\M-\C-a"   'pir-beginning-of-defun)
    (define-key map "\M-\C-e"   'pir-end-of-defun)
    (define-key map "\M-\C-h"   'pir-end-of-defun)
    (define-key map "\M-\C-q"   'pir-indent-defun)
    (define-key map "\C-c\C-p" 'pir-previous-code-line)
    (define-key map "\C-c\C-n" 'pir-next-code-line)
    (define-key map "\C-c\C-a" 'pir-beginning-of-line)
    (define-key map "\C-c\C-e" 'pir-end-of-line)
    (define-key map "\C-c\M-\C-n" 'pir-forward-block)
    (define-key map "\C-c\M-\C-p" 'pir-backward-block)
    (define-key map "\C-c\M-\C-u" 'pir-backward-up-block)
    (define-key map "\C-c\M-\C-d" 'pir-down-block)
    (define-key map "\C-c\M-\C-h" 'pir-mark-block)
    (define-key map "\C-c]" 'pir-close-block)
    (define-key map "\C-c\C-f" 'pir-insert-defun)
    
    map)
  "Keymap for PIR major mode.")

(defvar pir-mode-menu
  (list "PIR"
	(list "Lines"
	      ["Previous Code Line"	pir-previous-code-line t]
	      ["Next Code Line"		pir-next-code-line t]
	      ["Begin of Continuation"	pir-beginning-of-line t]
	      ["End of Continuation"	pir-end-of-line t]
	      ["Split Line at Point"	pir-indent-new-comment-line t])
	(list "Blocks"
	      ["Next Block"		pir-forward-block t]
	      ["Previous Block"		pir-backward-block t]
	      ["Down Block"		pir-down-block t]
	      ["Up Block"		pir-backward-up-block t]
	      ["Mark Block"		pir-mark-block t]
	      ["Close Block"		pir-close-block t])
	(list "Functions"
	      ["Begin of Function"	pir-beginning-of-defun t]
	      ["End of Function"	pir-end-of-defun t]
	      ["Mark Function"		pir-mark-defun t]
	      ["Indent Function"	pir-indent-defun t]
	      ["Insert Function"	pir-insert-defun t])
	"-"
	["Indent Line"			indent-according-to-mode t]
	["Complete Symbol"		pir-complete-symbol t]
	"-"
	["Toggle Abbrev Mode"		abbrev-mode t]
	"-"
	["Describe PIR Mode"		pir-describe-major-mode t])
  "Menu for PIR mode.")


(defvar pir-mode-syntax-table nil
  "Syntax table for PIR major mode.")
(setq   pir-mode-syntax-table
  (let ((st (make-syntax-table)))
    (modify-syntax-entry ?_  "w"  st)
    (modify-syntax-entry ?\\ "\\" st)
    (modify-syntax-entry ?#  "<"  st)
    (modify-syntax-entry ?\n ">"  st)
    (modify-syntax-entry ?$  "'" st)
    (modify-syntax-entry ?,  "."  st)
    (modify-syntax-entry ?.  ". p"  st)
    st))

(defvar pir-PMC-keyword-symbols
  '("Array" "Boolean" "Compiler" "Continuation" "Coroutine" "CSub"
    "Eval" "IntList" "Iterator" "Key" "ManagedStruct" "MultiArray"
    "NCI" "PerlArray" "PerlHash" "PerlInt" "PerlNum" "PerlString"
    "PerlUndef" "Pointer" "Scratchpad" "Sub" "UnManagedStruct"))

(defvar pir-ops
  '("call" "goto" "if" "unless" "global" "clone" "addr"
    "band" "bands" "bnot" "bnots" "bor" "bors" "shl" "shr" "lsr" "bxor" "bxors"
    "eq" "eq_str" "eq_num" "eq_addr" "isnull" "ne" "ne_str" "ne_num"
    "ne_addr" "lt" "lt_str" "lt_num" "le" "le_str" "le_num" "gt" "gt_str"
    "gt_num" "ge" "ge_str" "ge_num" "cmp" "cmp_str" "cmp_num" "and" "not" "or"
    "xor" "end" "noop" "cpu_ret" "check_events" "check_events__"
    "wrapper__" "prederef__" "reserved" "load_bytecode" "branch"
    "branch_cs" "bsr" "ret" "jsr" "jump" "enternative" "if" "unless" "invoke"
    "invokecc" "updatecc" "set_addr" "get_addr" "newsub" "set_eh"
    "clear_eh" "throw" "rethrow" "die_hard" "exit" "debug" "bounds"
    "profile" "trace" "gc_debug" "interpinfo" "warningson"
    "warningsoff" "errorson" "errorsoff" "runinterp" "getinterp"
    "setline" "getline" "setfile" "getfile" "setpackage" "getpackage"
    "sweep" "collect" "sweepoff" "sweepon" "collectoff" "collecton"
    "needs_destroy" "foldup" "loadlib" "dlfunc" "dlvar" "compile"
    "compreg" "bogus" "new_callback" "debug_init" "debug_load"
    "debug_break" "debug_print" "die" "conv_i1" "conv_i1_ovf" "conv_u1"
    "conv_u1_ovf" "conv_i2" "conv_i2_ovf" "conv_u2" "conv_u2_ovf"
    "conv_i4" "conv_u4" "conv_i8" "conv_r4" "conv_r8" "gcd" "issame"
    "isntsame" "istrue" "isgt" "isge" "isle" "islt" "iseq" "isne" "rand"
    "srand" "close" "fdopen" "getfd" "getstdin" "getstdout" "getstderr"
    "pioctl" "open" "print" "write" "printerr" "read" "readline" "peek"
    "stat" "seek" "tell" "socket" "sockaddr" "connect" "recv" "send" "poll"
    "abs" "add" "cmod" "dec" "div" "fdiv" "ceil" "floor" "inc" "mod" "mul" "neg"
    "pow" "sub" "sqrt" "acos" "asec" "asin" "atan" "cos" "cosh" "exp" "ln" "log10"
    "log2" "sec" "sech" "sin" "sinh" "tan" "tanh" "lcm" "fact" "callmethod"
    "callmethodcc" "fetchmethod" "can" "does" "isa" "newclass" "subclass"
    "findclass" "getclass" "singleton" "class" "classname" "addparent"
    "removeparent" "addattribute" "getattribute" "setattribute"
    "classoffset" "covers" "exsec" "hav" "vers" "new" "morph" "typeof"
    "find_type" "valid_type" "find_method" "defined" "exists" "delete"
    "elements" "push" "pop" "unshift" "shift" "splice" "setprop" "getprop"
    "delprop" "prophash" "freeze" "thaw" "mmdvtregister" "mmdvtfind"
    "register" "unregister" "print_item" "print_newline" "rx_compile"
    "rx_pushindex" "rx_initstack" "rx_clearstack" "rx_pushmark"
    "rx_popindex" "rx_advance" "rx_literal" "rx_char" "rx_is_w"
    "rx_is_d" "rx_is_s" "rx_oneof" "rx_oneof_bmp" "rx_dot"
    "rx_zwa_boundary" "rx_zwa_atend" "rx_search" "rx_search_char"
    "rx_literal_all" "rx_char_all" "rx_oneof_bmp_all" "rx_makebmp"
    "clone" "exchange" "set" "assign" "setref" "deref" "setp_ind"
    "setn_ind" "sets_ind" "seti_ind" "slice" "iter" "null" "cleari"
    "clearn" "clears" "clearp" "popi" "popn" "pops" "popp" "popbottomi"
    "popbottomn" "popbottoms" "popbottomp" "poptopi" "poptopn"
    "poptops" "poptopp" "pushi" "pushn" "pushs" "pushp" "pushtopi"
    "pushtopn" "pushtops" "pushtopp" "pushbottomi" "pushbottomn"
    "pushbottoms" "pushbottomp" "saveall" "restoreall" "savetop"
    "restoretop" "entrytype" "depth" "lookback" "save" "savec" "restore"
    "rotate_up" "intsave" "intrestore" "intdepth" "ord" "chr" "chopn"
    "concat" "repeat" "length" "pin" "unpin" "substr" "substr_r" "index"
    "pack" "sprintf" "find_encoding" "stringinfo" "upcase" "downcase"
    "titlecase" "join" "split" "spawnw" "exec" "err" "time" "gmtime"
    "localtime" "decodetime" "decodelocaltime" "sysinfo" "sleep"
    "sizeof" "new_pad" "push_pad" "pop_pad" "peek_pad" "store_lex"
    "find_lex" "store_global" "find_global"))

(defvar pir-mode-abbrev-table nil
  "Abbrev table used when in PIR mode.")

(defvar pir-completion-alist nil
  "Completion table used for PIR mode.")

(defvar pir-type-keywords
  '("int" "float" "string" "pmc" "object"))

(defvar pir-register-regexp "[INPS]\\([12][0-9]\\|3[01]\\|[0-9]\\)")
(defvar pir-dollar-register-regexp "\\$[INPS][0-9]+")

(defvar pir-directives)
(setq pir-directives
      '("method" "non_prototyped" "prototyped" "@LOAD" "@MAIN"))

(defvar pir-dotted-directives)
(setq pir-dotted-directives
  '(".sub" ".pcc_sub" ".end" ".emit" ".eom" ".const" ".namespace"
    ".endnamespace" ".result" ".arg" ".return" ".include"
    ".pcc_begin" ".pcc_end" ".pcc_begin_return" ".pcc_end_return"
    ".pcc_begin_yield" ".pcc_end_yield"))

(defvar pir-variable-declarations)
(setq pir-variable-declarations
      '(".local" ".sym" ".param"))

(defvar pir-begin-keywords)
(setq pir-begin-keywords
  '(".sub" ".emit" ".pcc_begin_yield" ".pcc_begin_return"
    ".pcc_begin" ".namespace"))

(defvar pir-end-keywords)
(setq pir-end-keywords
  '(".end" ".eom" ".pcc_end_yield" ".pcc_end_return"
    ".pcc_end" ".endnamespace"))

(defvar pir-block-match-alist)
(setq   pir-block-match-alist
  '((".sub" ".end" 1)
    (".emit" ".eom" 1)
    (".pcc_begin_yield" ".pcc_end_yield" 0)
    (".pcc_begin_return" ".pcc_end_return" 0)
    (".pcc_begin" ".pcc_end" 0)
    (".namespace" ".endnamespace" 0)))
;  "Alist with IMCC's matching block keywords.
;Has IMCC's begin keywords as keys and a list of the matching end keywords as
;associated values.")

(defvar pir-block-offset-alist)
(setq pir-block-offset-alist
      (mapcan (lambda (blockspec)
		(let ((offset (caddr blockspec)))
		  `((,(car blockspec) . ,offset)
		    (,(cadr blockspec) . ,offset))))
	      pir-block-match-alist))

(defvar pir-open-directives)
(setq pir-open-directives
  (mapcar #'car pir-block-match-alist))

(defvar pir-close-directives)
(setq pir-close-directives
  (mapcar #'cadr pir-block-match-alist))

(defvar pir-block-begin-regexp)
(setq pir-block-begin-regexp
      (regexp-opt pir-begin-keywords 'paren))
(defvar pir-block-end-regexp)
(setq pir-block-end-regexp
  (regexp-opt pir-end-keywords 'paren))
(defvar pir-block-begin-or-end-regexp)
(setq pir-block-begin-or-end-regexp
   (concat "\\(?:"pir-block-begin-regexp "\\|"
	          pir-block-end-regexp "\\)"))

(defvar pir-function-header-regexp)
(setq pir-function-header-regexp
  "\\.\\(.sub\\)\\s-+\\(\\sw+\\)")
;  "Regexp to match a PIR function header.")


(defvar pir-font-lock-keywords)
(setq pir-font-lock-keywords
      `((,(concat "^\\s *\\(.sub\\)\\s +\\(\\sw+\\)"
		  "\\(\\s +" (regexp-opt pir-directives 'paren) "\\)?")
	 (1 font-lock-keyword-face)
	 (2 font-lock-function-name-face t t)
	 (4 font-lock-keyword-face t t))
	(,(concat "\\s-*" (regexp-opt pir-variable-declarations 'paren)
		  "\\(?:\\s +" (regexp-opt pir-type-keywords 'paren)
		  "\\(?:\\s +\\(\\sw+\\)\\)?\\)?")
	 (1 font-lock-keyword-face)
	 (2 font-lock-type-face nil t)
	 (3 font-lock-variable-name-face nil t))
	(,(concat "^\\s *\\(.const\\)\\s +"
		  (regexp-opt pir-type-keywords 'paren)
		  "\\(\\s +\\(\\sw+\\)\\)?")
	 (1 font-lock-keyword-face)
	 (2 font-lock-type-face nil t)
	 (4 font-lock-constant-face nil t))
	(,pir-block-begin-or-end-regexp . font-lock-keyword-face)
	  
	(,pir-dollar-register-regexp . font-lock-variable-name-face)
	(,pir-register-regexp . font-lock-variable-name-face)
	(,(regexp-opt pir-dotted-directives 'paren) . font-lock-keyword-face)
	(,(regexp-opt pir-ops 'words) . font-lock-keyword-face)
	("\\s-*\\(\\sw+\\)\\s=*"
	 (1 font-lock-variable-name-face))
	))

(defcustom pir-auto-indent-flag nil
  "*Non-nil means indent line after a semicolon or space in PIR mode."
  :type 'boolean
  :group 'pir)

(defcustom pir-blink-matching-block t
  "*Control the blinkin of matching PIR block keywords.
Non-nil means show matching begin of block when inserting a space,
newline or semicolon after an end keyword."
  :type 'boolean
  :group 'pir)

(defcustom pir-basic-indent 8
  "*Extra indentation applied to statements in PIR block structures."
  :type 'integer
  :group 'pir)

(defvar pir-imenu-generic-expression
  (list
   (list nil pir-function-header-regexp 2))
  "Imenu expression for PIR mode.  See `imenu-generic-expression'.")
  
(defun pir-comment ()
  "Convert an empty comment to a `larger' kind, or start a new one.
These are the known comment classes:

   1 -- comment to the right of the code (at the `comment-column')
   2 -- comment on its own line, indented like code
   3 -- comment on its own line, beginning at the left-most column.

Suggested usage: while writing your code, trigger asm-comment
repeatedly until you are satisfied with the kind of comment."
  (interactive)
  (comment-normalize-vars)
  (let (comempty comment)
    (save-excursion
      (beginning-of-line)
      (setq comment (comment-search-forward (line-end-position) t))
      (setq comempty (looking-at "[ \t]*$")))

    (cond

     ;; Blank line, start comment at code indent level.
     ((save-excursion (beginning-of-line) (looking-at "^[ \t]*$"))
      (indent-according-to-mode)
      (insert pir-comment-char pir-comment-char ?\ ))

     ;; Nonblank line w/o comment => start a comment at comment-column
     ;; Also: point before the comment => jump inside.
     ((or (null comment) (< (point) comment))
      (indent-for-comment))

     ;; Empty code-level comment => upgrade to next comment level.
     ((save-excursion (goto-char comment) (skip-chars-backward " \t") (bolp))
      (goto-char comment)
      (insert pir-comment-char)
      (indent-for-comment))

     ;; Empty comment ends non-empty code line => new comment above.
     (t
      (goto-char comment)
      (skip-chars-backward " \t")
      (delete-region (point) (line-end-position))
      (beginning-of-line) (insert "\n") (backward-char)
      (pir-comment)))))

(defun pir-electric-colon ()
  "Automatically indent labels as soon as the colon is added."
  (interactive)
  (let ((labelp nil))
    (save-excursion
      (skip-syntax-backward "w_")
      (skip-syntax-backward " ")
      (if (setq labelp (bolp)) (delete-horizontal-space)))
    (call-interactively 'self-insert-command)
    (when labelp
      (delete-horizontal-space)
      (tab-to-tab-stop))))

(defmacro setlocalq (sym val)
  "Localize SYM and set it to the value of VAL."
  `(set (make-local-variable (quote ,sym)) ,val))

(defun pir-mode ()
  "Major mode for editing PIR files.

This mode makes it easier to write PIR code by helping with
indentation, doing some of the typing for you (with Abbrev mode) and by
showing keywords, comments, strings etc. in different faces (with Font
Lock mode on terminals that support it).

PIR (Parrot Intermediate Representation) is a friendlier way of
programming parrot than raw PASM (Parrot Assembly). In general,
compilers (and certainly humans) should code in PIR and let Parrot
handle register allocation and all that good stuff.

To find out more, hie thee to `http://www.parrotcode.org/'.

Type \\[list-abbrevs] to display the built in abbrevs for PIR
keywords. (When I've set them up.)

Keybindings
===========
\\{octave-mode-map}

Variables you can use to customize PIR mode
===========================================

pir-auto-indent
  Non-nil means indent current line after a space.
  Default is nil.

pir-blink-matching-block
  Non-nil means show matching begin of block when inserting a space or
  newline after the end of a block.  Default is t.

pir-basic-indent
  PIR mode's basic level of indentation. Default is 8.

Turning on PIR mode runs the hook `pir-mode-hook'.

To begin using this mode for all `.imc' files that you edit, add the
following lines to your `.emacs' file:

  (autoload 'pir-mode \"pir-mode\" nil t)
  (setq auto-mode-alist
        (cons '(\"\\\\.imc$\" . pir-mode) auto-mode-alist))

If you have any problems with this, you're on your own. You could always
try asking on <EMAIL>."
  (interactive)
  (kill-all-local-variables)
  (use-local-map pir-mode-map)
  (setq major-mode 'pir-mode)
  (setq mode-name "PIR")
  
  (set-syntax-table pir-mode-syntax-table)
  
  (setlocalq font-lock-defaults '(pir-font-lock-keywords))
  (setlocalq indent-line-function 'pir-indent-line)
  (setlocalq pir-mode-basic-indent 'pir-mode-basic-indent)
  (setlocalq require-final-newline t)
  
  (setlocalq comment-start "# ")
  (setlocalq comment-end "")
  (setlocalq comment-column pir-comment-column)
  (setlocalq comment-start-skip "#+ *")
  (setlocalq comment-indent-function 'pir-comment-indent)

  (setlocalq parse-sexp-ignore-comments t)
  (setlocalq paragraph-start (concat "\\s-*$\\|" page-delimiter))
  (setlocalq paragraph-separate paragraph-start)
  (setlocalq paragraph-ignore-fill-prefix t)
  (setlocalq fill-paragraph-function 'pir-fill-paragraph)
  (setlocalq adaptive-fill-regexp nil)
  (setlocalq fill-column 72)
  
  (setlocalq font-lock-defaults '(pir-font-lock-keywords))

  (setlocalq imenu-generic-expression pir-imenu-generic-expression)
  (setq      imenu-case-fold-search nil)

  (setlocalq defun-prompt-regexp "^\\s_*\\.\\(sub\\|emit\\)\\s_+")

  (pir-initialize-completions)
  (pir-add-pir-menu)
  
  (run-hooks 'pir-mode-hook))

(defun pir-describe-major-mode ()
  "Describe the current major mode."
  (interactive)
  (describe-function major-mode))

(defsubst pir-in-comment-p ()
  "Return t if point is inside a PIR comment, nil otherwise."
  (interactive)
  (save-excursion
    (nth 4 (parse-partial-sexp (line-beginning-position) (point)))))

(defsubst pir-in-string-p ()
  "Return t if point is inside a PIR string, nil otherwise."
  (interactive)
  (save-excursion
    (nth 3 (parse-partial-sexp (line-beginning-position) (point)))))

(defsubst pir-not-in-string-or-comment-p ()
  "Return t iff point is not inside a PIR string or comment."
  (let ((pps (parse-partial-sexp (line-beginning-position) (point))))
    (not (or (nth 3 pps) (nth 4 pps)))))

(defun pir-in-block-p ()
  "Return t if point is inside a PIR block, nil otherwise.
The block is taken to start at the first letter of the begin keyword and
to end after the end keyword."
  (let ((pos (point)))
    (save-excursion
      (condition-case nil
	  (progn
	    (skip-syntax-forward "w")
	    (pir-up-block -1)
	    (pir-forward-block)
	    t)
	(error nil))
      (< pos (point)))))

(defun pir-in-defun-p ()
  "Return t iff point is inside a PIR function declaration.
The function is taken to start at the `.' of `.sub' and to end after
the `.end' keyword."
  (let ((pos (point)))
    (save-excursion
      (or (and (looking-at "\\<.sub\\>")
	       (pir-not-in-string-or-comment-p))
	  (and (pir-beginning-of-defun)
	       (condition-case nil
		   (progn
		     (pir-forward-block)
		     t)
		 (error nil))
	       (< pos (point)))))))



;;; Comments
(defun pir-comment-region (beg end &optional arg)
  "Comment or uncomment each line in the region as PIR code.
See `comment-region'."
  (interactive "r\nP")
  (let ((comment-start (char-to-string pir-comment-char)))
    (comment-region beg end arg)))

(defun pir-uncomment-region (beg end &optional arg)
  "Uncomment each line in the region as PIR code."
  (interactive "r\nP")
  (or arg (setq arg1))
  (octave-comment-region beg end (- arg)))



;;; Indentation
(defun calculate-pir-indent (&optional ignore-labelp)
  "Calculate the correct indentation for a line of PIR code.
Optional argument IGNORE-LABELP if set, labels are ignored for the purposes of calculating the indent."
  (let ((icol 0))
    (save-excursion
      (beginning-of-line)
      (cond ((condition-case nil
		 (progn (up-list -1)
			t)
	       (error nil))
	     (setq icol (1+ (current-column))))
	    ((and (not ignore-labelp)
		  (looking-at "\\s-*\\sw+:"))
	     (setq icol 0))
	    ((zerop (pir-previous-code-line))
	     (let ((labelp (looking-at "\\s-*\\sw+:")))
	       (pir-beginning-of-line)
	       (back-to-indentation)
	       (setq icol (current-column))
	       (let ((bot (point))
		     (eol (line-end-position)))
		 (while (< (point) eol)
		   (if (pir-not-in-string-or-comment-p)
		       (cond
			((looking-at pir-block-begin-regexp)
			 (setq icol (+ icol
				       (pir-block-offset (match-string 1)))))
			((looking-at pir-block-end-regexp)
			 (if (not (= bot (point)))
			     (setq icol (- icol
					   (pir-block-offset
					    (match-string 1))))))))
		   (forward-char))
		 (if (and labelp
			  (= 0 icol))
		     (setq icol (calculate-pir-indent 'ignore-label)))
		 )))))
    (save-excursion
	(back-to-indentation)
	(cond
	 ((and (looking-at pir-block-end-regexp)
	       (pir-not-in-string-or-comment-p))
	  (looking-at pir-block-end-regexp)
	  (setq icol (- icol (pir-block-offset (match-string 1)))))
	 ((or (looking-at "\\s<\\s<\\s<\\S<")
	      (pir-before-magic-comment-p))
	  (setq icol (list 0 icol)))
	 ((looking-at "\\s\\S<")
	  (setq icol (list comment-column icol)))))
      icol))

(defun pir-block-offset (string)
  (* pir-basic-indent
     (cdr (assoc string pir-block-offset-alist))))

(defun pir-before-magic-comment-p ()
  "Return t if point is before the shebang."
  (save-excursion
    (beginning-of-line)
    (and (bobp) (looking-at "\\s-*#!"))))

(defun pir-comment-indent ()
  "Calculate the correct comment indent."
  (if (or (looking-at "\\s<\\s<\\s<")
	  (pir-before-magic-comment-p))
      0
    (if (looking-at "\\<\\<")
	(calculate-pir-indent)
      (skip-syntax-backward " ")
      (max (if (bolp) 0 (1+ (current-column)))
	   comment-column))))

(defun pir-indent-for-comment ()
  "Maybe insert and indent a PIR comment.
If there is no comment already on this line, create a code-level comment<commit_msg>Fix comment wrapping<commit_after>;;; pir-mode.el --- pir-mode.el --- editing IMCC source files under Emacs

;;; Commentary:

;; This package provides Emacs support for PIR.
;; It defines PIR mode, a major mode for editing PIR code.

;; See the documentation of `octave-mode' and `octave-help' for further
;; information. 

(require 'regexp-opt)
;;; Code:
(add-to-list 'auto-mode-alist '("\\.imc\\'" . pir-mode))

(defvar pir-mode-hook nil)

(defgroup pir nil
  "Mode for editing PIR code."
  :group 'languages)

(defcustom pir-comment-char ?#
  "*The `comment-start' character assumed by PIR mode."
  :type 'character
  :group 'pir)

(defcustom pir-comment-column comment-column
  "*The default comment column for PIR code."
  :type 'integer
  :group 'pir)
  
(defvar pir-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map "\C-c;"     'pir-comment-region)
    (define-key map "\C-c:"     'pir-uncomment-region)
    (define-key map (kbd "C-j") 'newline-and-indent)
    (define-key map " "         'pir-electric-space)
    (define-key map (kbd ":")   'pir-electric-colon)
    (define-key map (kbd "RET") 'pir-reindent-then-newline-and-indent)
    (define-key map (kbd "TAB") 'indent-according-to-mode)
    (define-key map "\e\t"      'pir-complete-symbol)
    (define-key map "\M-\C-a"   'pir-beginning-of-defun)
    (define-key map "\M-\C-e"   'pir-end-of-defun)
    (define-key map "\M-\C-h"   'pir-end-of-defun)
    (define-key map "\M-\C-q"   'pir-indent-defun)
    (define-key map "\C-c\C-p" 'pir-previous-code-line)
    (define-key map "\C-c\C-n" 'pir-next-code-line)
    (define-key map "\C-c\C-a" 'pir-beginning-of-line)
    (define-key map "\C-c\C-e" 'pir-end-of-line)
    (define-key map "\C-c\M-\C-n" 'pir-forward-block)
    (define-key map "\C-c\M-\C-p" 'pir-backward-block)
    (define-key map "\C-c\M-\C-u" 'pir-backward-up-block)
    (define-key map "\C-c\M-\C-d" 'pir-down-block)
    (define-key map "\C-c\M-\C-h" 'pir-mark-block)
    (define-key map "\C-c]" 'pir-close-block)
    (define-key map "\C-c\C-f" 'pir-insert-defun)
    
    map)
  "Keymap for PIR major mode.")

(defvar pir-mode-menu
  (list "PIR"
	(list "Lines"
	      ["Previous Code Line"	pir-previous-code-line t]
	      ["Next Code Line"		pir-next-code-line t]
	      ["Begin of Continuation"	pir-beginning-of-line t]
	      ["End of Continuation"	pir-end-of-line t]
	      ["Split Line at Point"	pir-indent-new-comment-line t])
	(list "Blocks"
	      ["Next Block"		pir-forward-block t]
	      ["Previous Block"		pir-backward-block t]
	      ["Down Block"		pir-down-block t]
	      ["Up Block"		pir-backward-up-block t]
	      ["Mark Block"		pir-mark-block t]
	      ["Close Block"		pir-close-block t])
	(list "Functions"
	      ["Begin of Function"	pir-beginning-of-defun t]
	      ["End of Function"	pir-end-of-defun t]
	      ["Mark Function"		pir-mark-defun t]
	      ["Indent Function"	pir-indent-defun t]
	      ["Insert Function"	pir-insert-defun t])
	"-"
	["Indent Line"			indent-according-to-mode t]
	["Complete Symbol"		pir-complete-symbol t]
	"-"
	["Toggle Abbrev Mode"		abbrev-mode t]
	"-"
	["Describe PIR Mode"		pir-describe-major-mode t])
  "Menu for PIR mode.")


(defvar pir-mode-syntax-table nil
  "Syntax table for PIR major mode.")
(setq   pir-mode-syntax-table
  (let ((st (make-syntax-table)))
    (modify-syntax-entry ?_  "w"  st)
    (modify-syntax-entry ?\\ "\\" st)
    (modify-syntax-entry ?#  "<"  st)
    (modify-syntax-entry ?\n ">"  st)
    (modify-syntax-entry ?$  "'" st)
    (modify-syntax-entry ?,  "."  st)
    (modify-syntax-entry ?.  ". p"  st)
    st))

(defvar pir-PMC-keyword-symbols
  '("Array" "Boolean" "Compiler" "Continuation" "Coroutine" "CSub"
    "Eval" "IntList" "Iterator" "Key" "ManagedStruct" "MultiArray"
    "NCI" "PerlArray" "PerlHash" "PerlInt" "PerlNum" "PerlString"
    "PerlUndef" "Pointer" "Scratchpad" "Sub" "UnManagedStruct"))

(defvar pir-ops
  '("call" "goto" "if" "unless" "global" "clone" "addr"
    "band" "bands" "bnot" "bnots" "bor" "bors" "shl" "shr" "lsr" "bxor" "bxors"
    "eq" "eq_str" "eq_num" "eq_addr" "isnull" "ne" "ne_str" "ne_num"
    "ne_addr" "lt" "lt_str" "lt_num" "le" "le_str" "le_num" "gt" "gt_str"
    "gt_num" "ge" "ge_str" "ge_num" "cmp" "cmp_str" "cmp_num" "and" "not" "or"
    "xor" "end" "noop" "cpu_ret" "check_events" "check_events__"
    "wrapper__" "prederef__" "reserved" "load_bytecode" "branch"
    "branch_cs" "bsr" "ret" "jsr" "jump" "enternative" "if" "unless" "invoke"
    "invokecc" "updatecc" "set_addr" "get_addr" "newsub" "set_eh"
    "clear_eh" "throw" "rethrow" "die_hard" "exit" "debug" "bounds"
    "profile" "trace" "gc_debug" "interpinfo" "warningson"
    "warningsoff" "errorson" "errorsoff" "runinterp" "getinterp"
    "setline" "getline" "setfile" "getfile" "setpackage" "getpackage"
    "sweep" "collect" "sweepoff" "sweepon" "collectoff" "collecton"
    "needs_destroy" "foldup" "loadlib" "dlfunc" "dlvar" "compile"
    "compreg" "bogus" "new_callback" "debug_init" "debug_load"
    "debug_break" "debug_print" "die" "conv_i1" "conv_i1_ovf" "conv_u1"
    "conv_u1_ovf" "conv_i2" "conv_i2_ovf" "conv_u2" "conv_u2_ovf"
    "conv_i4" "conv_u4" "conv_i8" "conv_r4" "conv_r8" "gcd" "issame"
    "isntsame" "istrue" "isgt" "isge" "isle" "islt" "iseq" "isne" "rand"
    "srand" "close" "fdopen" "getfd" "getstdin" "getstdout" "getstderr"
    "pioctl" "open" "print" "write" "printerr" "read" "readline" "peek"
    "stat" "seek" "tell" "socket" "sockaddr" "connect" "recv" "send" "poll"
    "abs" "add" "cmod" "dec" "div" "fdiv" "ceil" "floor" "inc" "mod" "mul" "neg"
    "pow" "sub" "sqrt" "acos" "asec" "asin" "atan" "cos" "cosh" "exp" "ln" "log10"
    "log2" "sec" "sech" "sin" "sinh" "tan" "tanh" "lcm" "fact" "callmethod"
    "callmethodcc" "fetchmethod" "can" "does" "isa" "newclass" "subclass"
    "findclass" "getclass" "singleton" "class" "classname" "addparent"
    "removeparent" "addattribute" "getattribute" "setattribute"
    "classoffset" "covers" "exsec" "hav" "vers" "new" "morph" "typeof"
    "find_type" "valid_type" "find_method" "defined" "exists" "delete"
    "elements" "push" "pop" "unshift" "shift" "splice" "setprop" "getprop"
    "delprop" "prophash" "freeze" "thaw" "mmdvtregister" "mmdvtfind"
    "register" "unregister" "print_item" "print_newline" "rx_compile"
    "rx_pushindex" "rx_initstack" "rx_clearstack" "rx_pushmark"
    "rx_popindex" "rx_advance" "rx_literal" "rx_char" "rx_is_w"
    "rx_is_d" "rx_is_s" "rx_oneof" "rx_oneof_bmp" "rx_dot"
    "rx_zwa_boundary" "rx_zwa_atend" "rx_search" "rx_search_char"
    "rx_literal_all" "rx_char_all" "rx_oneof_bmp_all" "rx_makebmp"
    "clone" "exchange" "set" "assign" "setref" "deref" "setp_ind"
    "setn_ind" "sets_ind" "seti_ind" "slice" "iter" "null" "cleari"
    "clearn" "clears" "clearp" "popi" "popn" "pops" "popp" "popbottomi"
    "popbottomn" "popbottoms" "popbottomp" "poptopi" "poptopn"
    "poptops" "poptopp" "pushi" "pushn" "pushs" "pushp" "pushtopi"
    "pushtopn" "pushtops" "pushtopp" "pushbottomi" "pushbottomn"
    "pushbottoms" "pushbottomp" "saveall" "restoreall" "savetop"
    "restoretop" "entrytype" "depth" "lookback" "save" "savec" "restore"
    "rotate_up" "intsave" "intrestore" "intdepth" "ord" "chr" "chopn"
    "concat" "repeat" "length" "pin" "unpin" "substr" "substr_r" "index"
    "pack" "sprintf" "find_encoding" "stringinfo" "upcase" "downcase"
    "titlecase" "join" "split" "spawnw" "exec" "err" "time" "gmtime"
    "localtime" "decodetime" "decodelocaltime" "sysinfo" "sleep"
    "sizeof" "new_pad" "push_pad" "pop_pad" "peek_pad" "store_lex"
    "find_lex" "store_global" "find_global"))

(defvar pir-mode-abbrev-table nil
  "Abbrev table used when in PIR mode.")

(defvar pir-completion-alist nil
  "Completion table used for PIR mode.")

(defvar pir-type-keywords
  '("int" "float" "string" "pmc" "object"))

(defvar pir-register-regexp "[INPS]\\([12][0-9]\\|3[01]\\|[0-9]\\)")
(defvar pir-dollar-register-regexp "\\$[INPS][0-9]+")

(defvar pir-directives)
(setq pir-directives
      '("method" "non_prototyped" "prototyped" "@LOAD" "@MAIN"))

(defvar pir-dotted-directives)
(setq pir-dotted-directives
  '(".sub" ".pcc_sub" ".end" ".emit" ".eom" ".const" ".namespace"
    ".endnamespace" ".result" ".arg" ".return" ".include"
    ".pcc_begin" ".pcc_end" ".pcc_begin_return" ".pcc_end_return"
    ".pcc_begin_yield" ".pcc_end_yield"))

(defvar pir-variable-declarations)
(setq pir-variable-declarations
      '(".local" ".sym" ".param"))

(defvar pir-begin-keywords)
(setq pir-begin-keywords
  '(".sub" ".emit" ".pcc_begin_yield" ".pcc_begin_return"
    ".pcc_begin" ".namespace"))

(defvar pir-end-keywords)
(setq pir-end-keywords
  '(".end" ".eom" ".pcc_end_yield" ".pcc_end_return"
    ".pcc_end" ".endnamespace"))

(defvar pir-block-match-alist)
(setq   pir-block-match-alist
  '((".sub" ".end" 1)
    (".emit" ".eom" 1)
    (".pcc_begin_yield" ".pcc_end_yield" 0)
    (".pcc_begin_return" ".pcc_end_return" 0)
    (".pcc_begin" ".pcc_end" 0)
    (".namespace" ".endnamespace" 0)))
;  "Alist with IMCC's matching block keywords.
;Has IMCC's begin keywords as keys and a list of the matching end keywords as
;associated values.")

(defvar pir-block-offset-alist)
(setq pir-block-offset-alist
      (mapcan (lambda (blockspec)
		(let ((offset (caddr blockspec)))
		  `((,(car blockspec) . ,offset)
		    (,(cadr blockspec) . ,offset))))
	      pir-block-match-alist))

(defvar pir-open-directives)
(setq pir-open-directives
  (mapcar #'car pir-block-match-alist))

(defvar pir-close-directives)
(setq pir-close-directives
  (mapcar #'cadr pir-block-match-alist))

(defvar pir-block-begin-regexp)
(setq pir-block-begin-regexp
      (regexp-opt pir-begin-keywords 'paren))
(defvar pir-block-end-regexp)
(setq pir-block-end-regexp
  (regexp-opt pir-end-keywords 'paren))
(defvar pir-block-begin-or-end-regexp)
(setq pir-block-begin-or-end-regexp
   (concat "\\(?:"pir-block-begin-regexp "\\|"
	          pir-block-end-regexp "\\)"))

(defvar pir-function-header-regexp)
(setq pir-function-header-regexp
  "\\.\\(.sub\\)\\s-+\\(\\sw+\\)")
;  "Regexp to match a PIR function header.")


(defvar pir-font-lock-keywords)
(setq pir-font-lock-keywords
      `((,(concat "^\\s *\\(.sub\\)\\s +\\(\\sw+\\)"
		  "\\(\\s +" (regexp-opt pir-directives 'paren) "\\)?")
	 (1 font-lock-keyword-face)
	 (2 font-lock-function-name-face t t)
	 (4 font-lock-keyword-face t t))
	(,(concat "\\s-*" (regexp-opt pir-variable-declarations 'paren)
		  "\\(?:\\s +" (regexp-opt pir-type-keywords 'paren)
		  "\\(?:\\s +\\(\\sw+\\)\\)?\\)?")
	 (1 font-lock-keyword-face)
	 (2 font-lock-type-face nil t)
	 (3 font-lock-variable-name-face nil t))
	(,(concat "^\\s *\\(.const\\)\\s +"
		  (regexp-opt pir-type-keywords 'paren)
		  "\\(\\s +\\(\\sw+\\)\\)?")
	 (1 font-lock-keyword-face)
	 (2 font-lock-type-face nil t)
	 (4 font-lock-constant-face nil t))
	(,pir-block-begin-or-end-regexp . font-lock-keyword-face)
	  
	(,pir-dollar-register-regexp . font-lock-variable-name-face)
	(,pir-register-regexp . font-lock-variable-name-face)
	(,(regexp-opt pir-dotted-directives 'paren) . font-lock-keyword-face)
	(,(regexp-opt pir-ops 'words) . font-lock-keyword-face)
	("\\s-*\\(\\sw+\\)\\s=*"
	 (1 font-lock-variable-name-face))
	))

(defcustom pir-auto-indent-flag nil
  "*Non-nil means indent line after a semicolon or space in PIR mode."
  :type 'boolean
  :group 'pir)

(defcustom pir-blink-matching-block t
  "*Control the blinkin of matching PIR block keywords.
Non-nil means show matching begin of block when inserting a space,
newline or semicolon after an end keyword."
  :type 'boolean
  :group 'pir)

(defcustom pir-basic-indent 8
  "*Extra indentation applied to statements in PIR block structures."
  :type 'integer
  :group 'pir)

(defvar pir-imenu-generic-expression
  (list
   (list nil pir-function-header-regexp 2))
  "Imenu expression for PIR mode.  See `imenu-generic-expression'.")
  
(defun pir-comment ()
  "Convert an empty comment to a `larger' kind, or start a new one.
These are the known comment classes:

   1 -- comment to the right of the code (at the `comment-column')
   2 -- comment on its own line, indented like code
   3 -- comment on its own line, beginning at the left-most column.

Suggested usage: while writing your code, trigger asm-comment
repeatedly until you are satisfied with the kind of comment."
  (interactive)
  (comment-normalize-vars)
  (let (comempty comment)
    (save-excursion
      (beginning-of-line)
      (setq comment (comment-search-forward (line-end-position) t))
      (setq comempty (looking-at "[ \t]*$")))

    (cond

     ;; Blank line, start comment at code indent level.
     ((save-excursion (beginning-of-line) (looking-at "^[ \t]*$"))
      (indent-according-to-mode)
      (insert pir-comment-char pir-comment-char ?\ ))

     ;; Nonblank line w/o comment => start a comment at comment-column
     ;; Also: point before the comment => jump inside.
     ((or (null comment) (< (point) comment))
      (indent-for-comment))

     ;; Empty code-level comment => upgrade to next comment level.
     ((save-excursion (goto-char comment) (skip-chars-backward " \t") (bolp))
      (goto-char comment)
      (insert pir-comment-char)
      (indent-for-comment))

     ;; Empty comment ends non-empty code line => new comment above.
     (t
      (goto-char comment)
      (skip-chars-backward " \t")
      (delete-region (point) (line-end-position))
      (beginning-of-line) (insert "\n") (backward-char)
      (pir-comment)))))

(defun pir-electric-colon ()
  "Automatically indent labels as soon as the colon is added."
  (interactive)
  (let ((labelp nil))
    (save-excursion
      (skip-syntax-backward "w_")
      (skip-syntax-backward " ")
      (if (setq labelp (bolp)) (delete-horizontal-space)))
    (call-interactively 'self-insert-command)
    (when labelp
      (delete-horizontal-space)
      (tab-to-tab-stop))))

(defmacro setlocalq (sym val)
  "Localize SYM and set it to the value of VAL."
  `(set (make-local-variable (quote ,sym)) ,val))

(defun pir-mode ()
  "Major mode for editing PIR files.

This mode makes it easier to write PIR code by helping with
indentation, doing some of the typing for you (with Abbrev mode) and by
showing keywords, comments, strings etc. in different faces (with Font
Lock mode on terminals that support it).

PIR (Parrot Intermediate Representation) is a friendlier way of
programming parrot than raw PASM (Parrot Assembly). In general,
compilers (and certainly humans) should code in PIR and let Parrot
handle register allocation and all that good stuff.

To find out more, hie thee to `http://www.parrotcode.org/'.

Type \\[list-abbrevs] to display the built in abbrevs for PIR
keywords. (When I've set them up.)

Keybindings
===========
\\{octave-mode-map}

Variables you can use to customize PIR mode
===========================================

pir-auto-indent
  Non-nil means indent current line after a space.
  Default is nil.

pir-blink-matching-block
  Non-nil means show matching begin of block when inserting a space or
  newline after the end of a block.  Default is t.

pir-basic-indent
  PIR mode's basic level of indentation. Default is 8.

Turning on PIR mode runs the hook `pir-mode-hook'.

To begin using this mode for all `.imc' files that you edit, add the
following lines to your `.emacs' file:

  (autoload 'pir-mode \"pir-mode\" nil t)
  (setq auto-mode-alist
        (cons '(\"\\\\.imc$\" . pir-mode) auto-mode-alist))

If you have any problems with this, you're on your own. You could always
try asking on <EMAIL>."
  (interactive)
  (kill-all-local-variables)
  (use-local-map pir-mode-map)
  (setq major-mode 'pir-mode)
  (setq mode-name "PIR")
  
  (set-syntax-table pir-mode-syntax-table)
  
  (setlocalq font-lock-defaults '(pir-font-lock-keywords))
  (setlocalq indent-line-function 'pir-indent-line)
  (setlocalq pir-mode-basic-indent 'pir-mode-basic-indent)
  (setlocalq require-final-newline t)
  
  (setlocalq comment-start "# ")
  (setlocalq comment-end "")
  (setlocalq comment-column pir-comment-column)
  (setlocalq comment-start-skip "#+ *")
  (setlocalq comment-indent-function 'pir-comment-indent)

  (setlocalq parse-sexp-ignore-comments t)
  (setlocalq paragraph-start (concat "\\s-*$\\|" page-delimiter))
  (setlocalq paragraph-separate paragraph-start)
  (setlocalq paragraph-ignore-fill-prefix t)
  (setlocalq fill-paragraph-function 'pir-fill-paragraph)
  (setlocalq adaptive-fill-regexp nil)
  (setlocalq fill-column 72)
  
  (setlocalq font-lock-defaults '(pir-font-lock-keywords))

  (setlocalq imenu-generic-expression pir-imenu-generic-expression)
  (setq      imenu-case-fold-search nil)

  (setlocalq defun-prompt-regexp "^\\s_*\\.\\(sub\\|emit\\)\\s_+")

  (pir-initialize-completions)
  (pir-add-pir-menu)
  
  (run-hooks 'pir-mode-hook))

(defun pir-describe-major-mode ()
  "Describe the current major mode."
  (interactive)
  (describe-function major-mode))

(defsubst pir-in-comment-p ()
  "Return t if point is inside a PIR comment, nil otherwise."
  (interactive)
  (save-excursion
    (nth 4 (parse-partial-sexp (line-beginning-position) (point)))))

(defsubst pir-in-string-p ()
  "Return t if point is inside a PIR string, nil otherwise."
  (interactive)
  (save-excursion
    (nth 3 (parse-partial-sexp (line-beginning-position) (point)))))

(defsubst pir-not-in-string-or-comment-p ()
  "Return t iff point is not inside a PIR string or comment."
  (let ((pps (parse-partial-sexp (line-beginning-position) (point))))
    (not (or (nth 3 pps) (nth 4 pps)))))

(defun pir-in-block-p ()
  "Return t if point is inside a PIR block, nil otherwise.
The block is taken to start at the first letter of the begin keyword and
to end after the end keyword."
  (let ((pos (point)))
    (save-excursion
      (condition-case nil
	  (progn
	    (skip-syntax-forward "w")
	    (pir-up-block -1)
	    (pir-forward-block)
	    t)
	(error nil))
      (< pos (point)))))

(defun pir-in-defun-p ()
  "Return t iff point is inside a PIR function declaration.
The function is taken to start at the `.' of `.sub' and to end after
the `.end' keyword."
  (let ((pos (point)))
    (save-excursion
      (or (and (looking-at "\\<.sub\\>")
	       (pir-not-in-string-or-comment-p))
	  (and (pir-beginning-of-defun)
	       (condition-case nil
		   (progn
		     (pir-forward-block)
		     t)
		 (error nil))
	       (< pos (point)))))))



;;; Comments
(defun pir-comment-region (beg end &optional arg)
  "Comment or uncomment each line in the region as PIR code.
See `comment-region'."
  (interactive "r\nP")
  (let ((comment-start (char-to-string pir-comment-char)))
    (comment-region beg end arg)))

(defun pir-uncomment-region (beg end &optional arg)
  "Uncomment each line in the region as PIR code."
  (interactive "r\nP")
  (or arg (setq arg1))
  (octave-comment-region beg end (- arg)))



;;; Indentation
(defun calculate-pir-indent (&optional ignore-labelp)
  "Calculate the correct indentation for a line of PIR code.
Optional argument IGNORE-LABELP if set, labels are ignored for the purposes of calculating the indent."
  (let ((icol 0))
    (save-excursion
      (beginning-of-line)
      (cond ((condition-case nil
		 (progn (up-list -1)
			t)
	       (error nil))
	     (setq icol (1+ (current-column))))
	    ((and (not ignore-labelp)
		  (looking-at "\\s-*\\sw+:"))
	     (setq icol 0))
	    ((zerop (pir-previous-code-line))
	     (let ((labelp (looking-at "\\s-*\\sw+:")))
	       (pir-beginning-of-line)
	       (back-to-indentation)
	       (setq icol (current-column))
	       (let ((bot (point))
		     (eol (line-end-position)))
		 (while (< (point) eol)
		   (if (pir-not-in-string-or-comment-p)
		       (cond
			((looking-at pir-block-begin-regexp)
			 (setq icol (+ icol
				       (pir-block-offset (match-string 1)))))
			((looking-at pir-block-end-regexp)
			 (if (not (= bot (point)))
			     (setq icol (- icol
					   (pir-block-offset
					    (match-string 1))))))))
		   (forward-char))
		 (if (and labelp
			  (= 0 icol))
		     (setq icol (calculate-pir-indent 'ignore-label)))
		 )))))
    (save-excursion
	(back-to-indentation)
	(cond
	 ((and (looking-at pir-block-end-regexp)
	       (pir-not-in-string-or-comment-p))
	  (looking-at pir-block-end-regexp)
	  (setq icol (- icol (pir-block-offset (match-string 1)))))
	 ((or (looking-at "\\s<\\s<\\s<\\S<")
	      (pir-before-magic-comment-p))
	  (setq icol (list 0 icol)))
	 ((looking-at "\\s<\\S<")
	  (setq icol (list comment-column icol)))))
      icol))

(defun pir-block-offset (string)
  (* pir-basic-indent
     (cdr (assoc string pir-block-offset-alist))))

(defun pir-before-magic-comment-p ()
  "Return t if point is before the shebang."
  (save-excursion
    (beginning-of-line)
    (and (bobp) (looking-at "\\s-*#!"))))

(defun pir-comment-indent ()
  "Calculate the correct comment indent."
  (if (or (looking-at "\\s<\\s<\\s<")
	  (pir-before-magic-comment-p))
      0
    (if (looking-at "\\<\\<")
	(calculate-pir-indent)
      (skip-syntax-backward " ")
      (max (if (bolp) 0 (1+ (current-column)))
	   comment-column))))

(defun pir-indent-for-comment ()
  "Maybe insert and indent a PIR comment.
If there is no comment already on this line, create a code-level comment
{'fraction_non_alphanumeric': 0.1743255332171948, 'fraction_numerical': 0.0032487405245068034, 'mean_word_length': 3.320923608991964, 'pattern_counts': {'":': 2, '<': 38, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1156523', 'n_tokens_mistral': 16769, 'n_tokens_neox': 15945, 'n_words': 4063}