;;; -*- lexical-binding: t; -*- ;; Author: Andrea Corallo ;; Package: mdcompact ;; Keywords: languages, extensions ;; Package-Requires: ((emacs "29")) ;; This file is part of GCC. ;; GCC is free software: you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GCC is distributed in the hope that it will be useful, but WITHOUT ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public ;; License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GCC. If not, see . ;;; Commentary: ;; Convert multi choice GCC machine description patterns to compact ;; syntax. ;;; Usage: ;; With the point on a pattern run 'M-x mdcomp-run-at-point' to ;; convert that pattern. ;; Run 'M-x mdcomp-run-buffer' to convert all convertible patterns in ;; the current buffer. ;; Run 'M-x mdcomp-run-directory' to convert all convertible patterns ;; in a directory. ;; One can invoke the tool from shell as well, ex for running it on ;; the arm backend from the GCC checkout directory: ;; emacs -batch -l ./contrib/mdcompact/mdcompact.el -f mdcomp-run-directory ./gcc/config/arm/ ;;; Code: (require 'cl-lib) (require 'rx) (defconst mdcomp-constr-rx (rx "(match_operand" (? ":" (1+ (or punct alnum))) (1+ space) (group-n 1 num) (1+ space) "\"" (1+ (or alnum "_" "<" ">")) "\"" (group-n 2 (1+ space) "\"" (group-n 3 (0+ (not "\""))) "\"") ")")) (cl-defstruct mdcomp-operand num cstr) (cl-defstruct mdcomp-attr name vals) ;; A reasonable name (rx-define mdcomp-name (1+ (or alnum "_"))) (defconst mdcomp-attr-rx (rx "(set_attr" (1+ space) "\"" (group-n 1 mdcomp-name) "\"" (1+ space) "\"" (group-n 2 (1+ (not ")"))) "\"" (0+ space) ")")) (defun mdcomp-parse-delete-attr () (save-match-data (when (re-search-forward mdcomp-attr-rx nil t) (let ((res (save-match-data (make-mdcomp-attr :name (match-string-no-properties 1) :vals (cl-delete-if #'string-empty-p (split-string (replace-regexp-in-string (rx "\\") "" (match-string-no-properties 2)) (rx (1+ (or space ","))))))))) (if (length= (mdcomp-attr-vals res) 1) 'short (delete-region (match-beginning 0) (match-end 0)) res))))) (defun mdcomp-parse-attrs () (save-excursion (let* ((res (cl-loop for x = (mdcomp-parse-delete-attr) while x collect x)) (beg (re-search-backward (rx bol (1+ space) "[")))) (unless (memq 'short res) (when res (delete-region beg (re-search-forward (rx "]"))))) (cl-delete 'short res)))) (defun mdcomp-remove-quoting (beg) (save-excursion (save-match-data (replace-regexp-in-region (regexp-quote "\\\\") "\\\\" beg (point-max)) (replace-regexp-in-region (regexp-quote "\\\"") "\"" beg (point-max))))) (defun mdcomp-remove-escaped-newlines (beg) (save-excursion (save-match-data (replace-regexp-in-region (rx "\\" eol (0+ space)) " " beg (point-max))))) (defun mdcomp-parse-delete-cstr () (cl-loop while (re-search-forward mdcomp-constr-rx nil t) unless (string= "" (match-string-no-properties 3)) collect (save-match-data (make-mdcomp-operand :num (string-to-number (match-string-no-properties 1)) :cstr (cl-delete-if #'string-empty-p (split-string (replace-regexp-in-string " " "" (match-string-no-properties 3)) (rx (1+ ",")))))) do (delete-region (match-beginning 2) (match-end 2)))) (defun mdcomp-run* () (let* ((ops (mdcomp-parse-delete-cstr)) (attrs (mdcomp-parse-attrs)) (beg (re-search-forward "\"@"))) (cl-sort ops (lambda (x y) (< (mdcomp-operand-num x) (mdcomp-operand-num y)))) (mdcomp-remove-escaped-newlines beg) (save-match-data (save-excursion (left-char 2) (forward-sexp) (left-char 1) (delete-char 1) (insert "\n }"))) (mdcomp-remove-quoting beg) (replace-match "{@") (re-search-forward (rx (or "\"" ")"))) (re-search-backward "@") (right-char 1) (insert "[ cons: ") (cl-loop for op in ops when (string-match "=" (cl-first (mdcomp-operand-cstr op))) do (insert "=") do (insert (number-to-string (mdcomp-operand-num op)) ", ") finally (progn ;; In case add attributes names (when attrs (delete-char -2) (insert "; attrs: ") (cl-loop for attr in attrs do (insert (mdcomp-attr-name attr) ", "))) (delete-char -2) (insert "]"))) (cl-loop while (re-search-forward (rx bol (0+ space) (or (group-n 1 "* return") (group-n 2 "}") "#" alpha "<")) nil t) for i from 0 when (match-string 2) do (cl-return) when (match-string 1) do (progn (delete-region (match-beginning 1) (+ (match-beginning 1) (length "* return"))) (insert "<<") (left-char 1)) do (progn (left-char 1) (cl-loop initially (insert " [ ") for op in ops for c = (nth i (mdcomp-operand-cstr op)) unless c do (cl-return) do (insert (if (string-match "=" c) (substring c 1 nil) c) ", ") finally (progn (when attrs (delete-char -2) (insert "; ") (cl-loop for attr in attrs for str = (nth i (mdcomp-attr-vals attr)) when str do (insert str) do (insert ", "))) (delete-char -2) (insert " ] ") (move-end-of-line 1))))) ;; remove everything after ] align what needs to be aligned ;; and re-add the asm template (re-search-backward (regexp-quote "@[ cons:")) (let* ((n (length (mdcomp-operand-cstr (car ops)))) (asms (cl-loop initially (re-search-forward "]") repeat n collect (let* ((beg (re-search-forward "]")) (end (re-search-forward (rx eol))) (str (buffer-substring-no-properties beg end))) (delete-region beg end) str))) (beg (re-search-backward (regexp-quote "@[ cons:"))) (indent-tabs-mode nil)) (re-search-forward "}") (align-regexp beg (point) (rx (group-n 1 "") "[")) (align-regexp beg (point) (rx (group-n 1 "") (or "," ";")) nil nil t) (align-regexp beg (point) (rx (group-n 1 "") "]")) (goto-char beg) (cl-loop initially (re-search-forward "]") for i below n do (progn (re-search-forward "]") (insert (nth i asms)))) (when (re-search-forward (rx (1+ (or space eol)) ")") nil t) (replace-match "\n)" nil t))))) (defun mdcomp-narrow-to-md-pattern () (condition-case nil (let ((beg (re-search-forward "\n(")) (end (re-search-forward (rx bol (1+ ")"))))) (narrow-to-region beg end)) (error (narrow-to-defun)))) (defun mdcomp-run-at-point () "Convert the multi choice top-level form around point to compact syntax." (interactive) (save-restriction (save-mark-and-excursion (mdcomp-narrow-to-md-pattern) (goto-char (point-min)) (let ((pattern-name (save-excursion (re-search-forward (rx "\"" (group-n 1 (1+ (not "\""))) "\"")) (match-string-no-properties 1))) (orig-text (buffer-substring-no-properties (point-min) (point-max)))) (condition-case nil (progn (mdcomp-run*) (message "Converted: %s" pattern-name)) (error (message "Skipping convertion for: %s" pattern-name) (delete-region (point-min) (point-max)) (insert orig-text) 'fail)))))) (defun mdcomp-run-buffer () "Convert the multi choice top-level forms in the buffer to compact syntax." (interactive) (save-excursion (message "Conversion for buffer %s started" (buffer-file-name)) (goto-char (point-min)) (while (re-search-forward (rx "match_operand" (1+ any) letter (0+ space) "," (0+ space) letter) nil t) (when (eq (mdcomp-run-at-point) 'fail) (condition-case nil (forward-sexp) (error ;; If forward-sexp fails falls back. (re-search-forward (rx ")" eol eol)))))) (message "Conversion done"))) (defconst mdcomp-file-rx (rx bol alpha (0+ not-newline) ".md" eol)) (defun mdcomp-run-directory (folder &optional recursive) "Run el mdcompact on a FOLDER possibly in a RECURSIVE fashion." (interactive "D") (let ((before-save-hook nil) (init-time (current-time))) (mapc (lambda (f) (with-temp-file f (message "Working on %s" f) (insert-file-contents f) (mdcomp-run-buffer) (message "Done with %s" f))) (if recursive (directory-files-recursively folder mdcomp-file-rx) (directory-files folder t mdcomp-file-rx))) (message "Converted in %f sec" (float-time (time-since init-time))))) (defun mdcomp-batch-run-directory () "Same as `mdcomp-run-directory' but use cmd line args." (mdcomp-run-directory (nth 0 argv) (nth 1 argv))) (provide 'mdcompact) ;;; mdcompact.el ends here