;;;; convert68k.el
;;;; A program to process a m68000 assembler file and
;;;; convert local labels in the form 3$: to non local labels L37:
;;;; Main use: to convert the mp.s file in the PARI package to
;;;; something that gcc can understand. The local labels has caused problems
;;;; both on my amiga and on a Sun3 that I have tried.

(defvar global-label-expr "^[a-zA-Z_]+[a-zA-Z0-9_]*:"
  "Regular expression that matches a global label, for example
_cget:" )

(defvar local-label-expr "^\\([0-9]+\\$\\)"
  "Regular expression that matches a local label, for example
3$:" )

(defvar convert68k-label-prefix "L_"
  "Prefix that is added to every generated label.")

(defvar convert68k-indirect-expr "sp@(\\([0-9]+\\))@\\([^(]\\)"
  "Regular expression that matches the double indirect
construction sp@(number)@")

(defvar convert68k-indirect-replace "sp@(\\1)@(0)\\2"
  "Replace string for replace-regexp. Depending on
convert68k-indirect-expt")

(defvar convert68k-buffer-name "*convert68k-output*"
  "Name of buffer created by convert68k.")

(defun convert68k ()
  "Processes buffer with 68k assembler, to remove all
local labels. Opens a new buffer for output. Also converts
some double-indirect expressions"
  (interactive)
  (let ((convert68k-buffer (get-buffer-create convert68k-buffer-name))
	(label-number 1)
	(source-buffer (current-buffer))
	(global-list nil)
	(last (point-max)))
	;; Create list of positions for global labels.
	;; For convenience, the list has the format
	;; ((1 . 10) (10 . 90) (90 . 400))
	;; if the labels are located at positions 10 and 90,
	;; or if the file starts with a label and they are at
	;; 1, 10 and 90.
    (message "Scanning input for global labels...")
    (goto-char last)
    (while (re-search-backward global-label-expr nil t)
      (setq global-list (cons (cons (point) last)
			      global-list))
      (setq last (point)))
    (cond ((> last (point-min))
	   (setq global-list (cons (cons (point-min) last)
				   global-list))
	   (setq last (point))))
    (switch-to-buffer-other-window convert68k-buffer)
    (erase-buffer)
    (mapcar '(lambda (pair)
	       (convert68k-locals (car pair)
				  (cdr pair)))
	    global-list)
    (convert68k-indirect)))

(defun convert68k-locals (source-start source-end)
  "Copies the start end region from source-buffer, and converts all
local labels in that region. Assumes that there are no global labels in
the middle of the region."
  (let ((local-list nil)
	(start (point))
	(end (make-marker)))
    (insert-buffer-substring source-buffer
			     source-start
			     source-end)
    (set-marker end (point))
    ;; Create list of local labels. 
    ;; Format: (("1$" . "L_1") ("9$" . "L_2"))
    ;; The car of each pair is the name of the local label,
    ;; the cdr is the new global name.
    (goto-char start)
    (while (re-search-forward local-label-expr end t)
      (setq local-list
	    (cons (cons (buffer-substring (match-beginning 1)
					  (match-end 1))
			(format "%s%d" convert68k-label-prefix
				label-number))
		  local-list))
      (setq label-number (+ 1 label-number)))
    (narrow-to-region start end)
    (mapcar '(lambda (pair)
	       (goto-char (point-min))
	       (replace-string (car pair) (cdr pair) t )
	       nil)
	    local-list)
    (widen)
    (goto-char end)
    (set-marker end nil)))

(defun convert68k-indirect ()
  "Converts the construction sp@(number)@
to sp(number)@"
  (interactive)
  (goto-char (point-min))
  (query-replace-regexp convert68k-indirect-expr
			convert68k-indirect-replace))
