Do some arithmetics with results of regexp searches in elisp - emacs

Some time ago #Oleg Pavliv explained in https://unix.stackexchange.com/questions/47615/emacs-simple-arithmetics-in-query-replace how to do simple arithmetics in query replace (interactively) in emacs.
Now I want to use the same method for a small elisp program but it doesn't work. Consider for example the following minimal example of elisp code:
(defun Nshift ()
(interactive)
(query-replace-regexp "\\([0-9]+\\)\\.Number" "\\,((+ 3 \\#1)).Number")
)
Now suppose I run Nshift in a buffer which contains for example the string 4.Number then I get the following error message.
match-substitute-replacement: Invalid use of `\' in replacement text
How would a correct elisp implementation of Nshift look like?
Edit:
I don't see how Seans answer generalizes with easy and readable syntax to more complicated replacements (which I need in my application), so for example what would be the correct (and easy to read) equivalent to
(query-replace-regexp "\\([0-9]+\\)\\.Number.\\([0-9]+\\)" "\\,((+ 3 \\#1)).Number.\\,((+ 8 \\#2))")

Like this:
(defun Nshift ()
(interactive)
(while (search-forward-regexp "\\([0-9]+\\)\\.Number" nil t)
(replace-match (format "%s.Number" (+ 3 (string-to-number (match-string 1)))))))
EDITED TO ADD:
Your expanded example could be implemented in this way:
(defun Nshift ()
(interactive)
(while (search-forward-regexp "\\([0-9]+\\)\\.Number\\.\\([0-9]+\\)" nil t)
(replace-match
(number-to-string (+ 3 (string-to-number (match-string 1))))
nil nil nil 1)
(replace-match
(number-to-string (+ 8 (string-to-number (match-string 2))))
nil nil nil 2)))
It's actually even easier than my original solution, because I forgot that replace-match has an optional fifth argument that causes it to replace just a single subexpression, and saves you from having to duplicate the fixed text (".Number.") in the replacement text.
There's some refactoring that could be done here:
(defun increment-match-string (match-index increment)
(replace-match
(number-to-string (+ increment (string-to-number (match-string match-index))))
nil nil nil match-index))
Then Nshift could be implemented like so:
(defun Nshift ()
(interactive)
(while (search-forward-regexp "\\([0-9]+\\)\\.Number\\.\\([0-9]+\\)" nil t)
(increment-match-string 1 3)
(increment-match-string 2 8)))

Related

Emacs cond, possible to have things happen between clauses?

I programmed some months ago some code with a lot of if statements. If region-active-p, if beginning-of-line, those kind of things.
Having learned about the cond lisp, I was wondering if I could improve my code a lot.
The problem is that this cond is only doing things when "true" as far as I see it, while I actually need the move back-to-indentation in between these checks.
In order to properly skip the last clause, I even have to set variable values.
(defun uncomment-mode-specific ()
"Uncomment region OR uncomment beginning of line comment OR uncomment end"
(interactive)
(let ((scvar 0) (scskipvar 0))
(save-excursion
(if (region-active-p)
(progn (uncomment-region (region-beginning) (region-end))
(setq scskipvar 1))
(back-to-indentation)) ; this is that "else" part that doesn't fit in cond
(while (string= (byte-to-string (following-char)) comment-start)
(delete-char 1)
(setq scskipvar 1))
(indent-for-tab-command)
(when (= scskipvar 0)
(search-forward comment-start nil t)
(backward-char 1)
(kill-line))
)))
)
So basically my question is, I would kind of like to have some consequences of not giving "true" to a clause, before the check of another clause. Is this possible? If not, what would be the best thing to do?
EDIT: Since we are using this as the example case for a solution, I wrote it down so it is easier to understand.
If region is active, remove comments from region. If not, move point to intendation.
For as long as the following character is a comment character, delete it. Afterwards, indent this line.
If it didn't do any of the above, search forward for a comment character, and kill that line.
(defun delete-on-this-line (regex)
(replace-regexp regex "" nil (line-beginning-position) (line-end-position)))
(defun delete-leading-comment-chars ()
(delete-on-this-line (eval `(rx bol (* space) (group (+ ,comment-start))))))
(defun delete-trailing-comment-chars ()
(delete-on-this-line (eval `(rx (group (+ ,comment-end)) (* space) eol))))
(defun delete-trailing-comment ()
(delete-on-this-line (eval `(rx (group (+ ,comment-start) (* anything) eol)))))
(defun uncomment-dwim ()
(interactive)
(save-excursion
(if (region-active-p)
(uncomment-region (region-beginning) (region-end))
(or (delete-leading-comment-chars)
(delete-trailing-comment-chars)
(delete-trailing-comment)))))
Edit: A little explanation:
It's a lot easier to do regex replacements than manage loops to do deletion, so that gets rid of the state. And the steps are all mutually exclusive, so you can just use or for each option.
The rx macro is a little DSL that compiles down to valid regexes, and it's also amenable to lispy syntax transforms, so I can dynamically build a regex using the comment chars for the current mode.
(defmacro fcond (&rest body)
(labels ((%substitute-last-or-fail
(new old seq)
(loop for elt on seq
nconc
(if (eql (car elt) old)
(when (cdr elt)
(error "`%S' must be the last experssion in the clause"
(car elt)))
(list new)
(list (car elt))))))
(loop with matched = (gensym)
with catcher = (gensym)
for (head . rest) in body
collect
`(when (or ,head ,matched)
(setq ,matched t)
,#(%substitute-last-or-fail `(throw ',catcher nil) 'return rest))
into clauses
finally
(return `(let (,matched) (catch ',catcher ,#clauses))))))
(macroexpand '(fcond
((= 1 2) (message "1 = 2"))
((= 1 1) (message "1 = 1"))
((= 1 3) (message "1 = 3") return)
((= 1 4) (message "1 = 4"))))
(let (G36434)
(catch (quote G36435)
(when (or (= 1 2) G36434)
(setq G36434 t)
(message "1 = 2"))
(when (or (= 1 1) G36434)
(setq G36434 t)
(message "1 = 1"))
(when (or (= 1 3) G36434)
(setq G36434 t)
(message "1 = 3")
(throw (quote G36435) nil))
(when (or (= 1 4) G36434)
(setq G36434 t)
(message "1 = 4"))))
Here's something quick to do, what I think you may be after, i.e. something that would mimic the behaviour switch in C.
The idea is that all clauses are tested sequentially for equality, and if one matches, then all following clauses are executed, until the return keyword (it would be break in C, but Lisp uses return for the similar purpose in the loop, so I thought that return would be better). The code above thus will print:
1 = 1
1 = 3
Technically, this is not how switch works in C, but it will produce the same effect.
One thing I did here for simplicity, which you want to avoid / solve differently - the use of return keyword, you probably want to impose stricter rules on how it should be searched for.
cond
Cond evaluates a series of conditions in a list, each item in a list can be a condition, and then executable instructions.
The example in the Emacs Lisp manual is adequate to demonstrate how it works, I've annotated it here to help you understand how it works.
(cond ((numberp x) x) ;; is x a number? return x
((stringp x) x) ;; is x a string? return x
((bufferp x) ;; is x a buffer?
(setq temporary-hack x) ;; set temporary-hack to buffer x
(buffer-name x)) ;; return the buffer-name for buffer x
((symbolp x) (symbol-value x))) ;; is x a symbol? return the value of x
Each part of the condition can be evaluated any way you like, the fact x above is in each condition is coincidental.
For example:
(cond ((eq 1 2) "Omg equality borked!") ;; Will never be true
(t "default")) ;; always true
So comparisons with switch are a bit limited, it's essentially a list of if statements, that executes/returns the first true condition's body list.
Hopefully this helps you understand cond a bit better.
(cond (condition body ... ) ;; execute body of 1st passing
(condition body ... ) ;; condition and return result
(condition body ... ) ;; of the final evaluation.
;; etc
)
OR
You can do things similar to switch with OR, depending on how you structure the code.
This isn't functional style, because it relies on side-effects to do what you want, then returns a boolean value for flow control, here's an example in pseudo lisp.
(or)
(or
(lambda() (do something)
(evaluate t or nil) ; nil to continue; t to quit.
)
(lambda() (do something)
(evaluate t or nil) ; nil to continue; t to quit.
)
(lambda() (do something)
(evaluate t or nil) ; nil to continue; t to quit.
)
(lambda() (do something)
(evaluate t or nil) ; nil to continue; t to quit.
)
)
Here's working example of a switch like structure using or
(or
(when (= 1 1)
(progn
(insert "hello\n")
nil))
(when (= 1 2) ;; condition fails.
(progn
(insert "hello\n")
nil)) ;; returns false (nil)
(when (= 1 1)
(progn
(insert "hello\n")
t)) ;; returns true, so we bail.
(when (= 1 1)
(progn
(insert "hello\n")
nil))
)
Inserts :
hello
hello
(and)
The and operator (not just in Lisp) is also very useful, instead of evaluating everything until true, it evaluates conditions that are true, until a false is evaluated.
Both or & and can be used to build useful logic trees.
This is how I did it now according to Chris' idea that breaking it down into seperate functions would make it easier.
EDIT: Now also applied the or knowledge gained in this thread gained from Slomojo (no more variables!)
(defun sc-uncomment ()
(interactive)
(or
(if (region-active-p)
(uncomment-region (region-beginning) (region-end))
(back-to-indentation)
nil)
(if (string= (byte-to-string (following-char)) comment-start)
(sc-check-start)
(sc-end))))
(defun sc-check-start ()
(interactive)
(while (string= (byte-to-string (following-char)) comment-start)
(delete-char 1))
)
(defun sc-end ()
(interactive)
(search-forward comment-start nil t)
(backward-char 1)
(kill-line))
)

In elisp, how to evaluate a string of "var=value\n..." into lisp variables of the same name?

An mplayer tool (midentify) outputs "shell-ready" lines intended to be evaluated by a bash/sh/whatever interpreter.
How can I assign these var-names to their corresponding values as elisp var-names in emacs?
The data is in a string (via shell-command-to-string)
Here is the data
ID_AUDIO_ID=0
ID_FILENAME=/home/axiom/abc.wav
ID_DEMUXER=audio
ID_AUDIO_FORMAT=1
ID_AUDIO_BITRATE=512000
ID_AUDIO_RATE=0
ID_AUDIO_NCH=1
ID_LENGTH=3207.00
ID_SEEKABLE=1
ID_CHAPTERS=0
ID_AUDIO_BITRATE=512000
ID_AUDIO_RATE=32000
ID_AUDIO_NCH=1
ID_AUDIO_CODEC=pcm
ID_EXIT=EOF
Here's a routine that takes a string containing midentify output, and returns an association list of the key-value pairs (which is safer than setting Emacs variables willy-nilly). It also has the advantage that it parses numeric values into actual numbers:
(require 'cl) ; for "loop"
(defun midentify-output-to-alist (str)
(setq str (replace-regexp-in-string "\n+" "\n" str))
(setq str (replace-regexp-in-string "\n+\\'" "" str))
(loop for index = 0 then (match-end 0)
while (string-match "^\\(?:\\([A-Z_]+\\)=\\(?:\\([0-9]+\\(?:\\.[0-9]+\\)?\\)\\|\\(.*\\)\\)\\|\\(.*\\)\\)\n?" str index)
if (match-string 4 str)
do (error "Invalid line: %s" (match-string 4 str))
collect (cons (match-string 1 str)
(if (match-string 2 str)
(string-to-number (match-string 2 str))
(match-string 3 str)))))
You'd use this function like so:
(setq alist (midentify-output-to-alist my-output))
(if (assoc "ID_LENGTH" alist)
(setq id-length (cdr (assoc "ID_LENGTH" alist)))
(error "Didn't find an ID_LENGTH!"))
EDIT: Modified function to handle blank lines and trailing newlines correctly.
The regexp is indeed a beast; Emacs regexps are not known for their easiness on the eyes. To break it down a bit:
The outermost pattern is ^(?:valid-line)|(.*). It tries to match a valid line, or else matches the entire line (the .*) in match-group 4. If (match-group 4 str) is not nil, that indicates that an invalid line was encountered, and an error is raised.
valid-line is (word)=(?:(number)|(.*)). If this matches, then the name part of the name-value pair is in match-string 1, and if the rest of the line matches a number, then the number is in match-string 2, otherwise the entire rest of the line is in match-string 3.
There's probably a better way but this should do it:
(require 'cl)
(let ((s "ID_AUDIO_ID=0
ID_FILENAME=/home/axiom/abc.wav
ID_DEMUXER=audio
ID_AUDIO_FORMAT=1
ID_AUDIO_BITRATE=512000
ID_AUDIO_RATE=0
ID_AUDIO_NCH=1
ID_LENGTH=3207.00
ID_SEEKABLE=1
ID_CHAPTERS=0
ID_AUDIO_BITRATE=512000
ID_AUDIO_RATE=32000
ID_AUDIO_NCH=1
ID_AUDIO_CODEC=pcm
ID_EXIT=EOF"))
(loop for p in (split-string s "\n")
do
(let* ((elements (split-string p "="))
(key (elt elements 0))
(value (elt elements 1)))
(set (intern key) value))))
Here's a function you can run on the output buffer:
(defun set-variables-from-shell-assignments ()
(goto-char (point-min))
(while (< (point) (point-max))
(and (looking-at "\\([A-Z_]+\\)=\\(.*\\)$")
(set (intern (match-string 1)) (match-string 2)))
(forward-line 1)))
I don't think regexp is what really need. You need to split your string by \n and =, so you just say exactly the same to interpreter.
I think you can also use intern to get symbol from string(and set variables). I use it for the first time, so comment here if i am wrong. Anyways, if list is what you want, just remove top-level mapcar.
(defun set=(str)
(mapcar (lambda(arg)
(set
(intern (car arg))
(cadr arg)))
(mapcar (lambda(arg)
(split-string arg "=" t))
(split-string
str
"\n" t))))
(set=
"ID_AUDIO_ID=0
ID_FILENAME=/home/axiom/abc.wav
ID_DEMUXER=audio
ID_AUDIO_FORMAT=1
ID_AUDIO_BITRATE=512000
ID_AUDIO_RATE=0
ID_AUDIO_NCH=1
ID_LENGTH=3207.00
ID_SEEKABLE=1
ID_CHAPTERS=0
ID_AUDIO_BITRATE=512000
ID_AUDIO_RATE=32000
ID_AUDIO_NCH=1
ID_AUDIO_CODEC=pcm
ID_EXIT=EOF")

Need a Elisp function to push back an input

I wrote a new version of "zap-to-char".It just highlights the region instead of kill it.I think this would be more flexible,because we can choose to kill,or copy,or just go to this char.
Here is the snippet:
(defun new-zap-to-char (arg char)
(interactive "p\ncZap to char: ")
(push-mark)
(setq mark-active t)
(defun iter-zap ()
(if (< arg 0)
(search-forward (char-to-string char) nil nil -1)
(search-forward (char-to-string char) nil nil 1))
(if (char-equal char (setq c (read-char)))
(iter-zap)
(>>>>here is the "push-back-to-input" function"<<<<))
(iter-zap))
As you see, I need a function to push the result of 'read-char' back to input,when you type input except for the "char". But I don't know if Emacs offered one. So I need your help.
I hope I've made this clear.
You can try unread-command-events.
For example:
(push ?a unread-command-events)

Emacs: how do I replace-regexp with a lisp function in a defun?

For example I want to make all text in parenthesis, (), UPCASE. It's trivial to do the following interactively:
M-x query-replace-regexp
replace: "(\(.+?\))"
with : "(\,(upcase \1))"
Instead I want to write a defun which will do that:
(defun upcs ()
(interactive)
(goto-char 1)
(while (search-forward "(\\(.+?\\))" nil t) (replace-match "(\\,(upcase \\1))" t nil)))
but it doesn't work! While the following works (it appends foo and bar to the parenthesized texts):
(defun HOOK ()
(interactive)
(goto-char 1)
(while (search-forward-regexp "(\\(.+?\\))" nil t) (replace-match "(foo \\1 bar)" t nil)))
Luke's answer almost does the job but not quite. The original poster wanted all the text that was enclosed in parenthesis converted to upper case while Luke's code converts the code to upper case AND ALSO removes the parenthesis. A slight modification to the regex provides the correct solution:
(defun upcs ()
(interactive)
(goto-char 1)
(while (search-forward-regexp "\\([^\\)]+\\)" nil t)
(replace-match (upcase (match-string 1)) t nil)))
First of all, you're using search-forward in your first function. This takes a string literal rather than a regular expression. You should be using search-forward-regexp, as you do in your second function.
Secondly, while this code is valid as a replace value for query-replace-regexp, I don't think you can pass it to replace-match:
(\\,(upcase \\1))
You can get the value of the match found by search-forward-regexp using the match-string function.
Finally, I'm not sure your search regular expression is correct.
I think you need something along these lines:
(defun upcs ()
(interactive)
(goto-char 1)
(while (search-forward-regexp "(\\([^\\)]+\\))" nil t)
(replace-match (upcase (match-string 1)) t nil)))
So this solves the problem.
(defun put-in-par (str)
(concat "(" str ")"))
(defun upcs-luke ()
(interactive)
(goto-char 1)
(while (search-forward-regexp "(\\([^\\)]+\\))" nil t)
(replace-match (put-in-par (upcase (match-string 1))) t nil)))
Thanks to BillC and Luke Girvin for help.
This was very useful, thanks all.
In the interest of putting more examples on the web, I went from this:
(replace-regexp "\([\%\)\”\"]\..?\)[0-9]+" "\1")
(which didn't work, but which used the regexps that did work in interactive mode)
to this:
(while (re-search-forward "\\([\\%\\\"\\”]\\)\\.?[0-9]+" nil t)
(replace-match (match-string 1) t nil))
I needed three backslashes before the internal quotation mark.
The interactive regex-based replacement functions cannot change the case but otherwise work fine by default: the case-replace variable needs to be set to nil (default: t). Then interactive replacements then will properly work with ,(upcase \1) and friends.
Reference: See discussion on the emacs-berlin mailing list: https://mailb.org/pipermail/emacs-berlin/2021/000840.html

How to define a function which repeats itself when passed an argument

Is there an easy way to define a function which repeats itself when passed an argument?
For example, I've defined the following function
(defun swap-sign ()
(interactive)
(search-forward-regexp "[+-]")
(if (equal (match-string 0) "-")
(replace-match "+")
(replace-match "-"))
)
I'd like C-u swap-sign to call swap-sign four times.
I've tried
(defun swap-sign (&optional num)
(interactive)
(let ((counter 0)
(num (if num (string-to-number num) 0)))
(while (<= counter num)
(search-forward-regexp "[+-]")
(if (equal (match-string 0) "-")
(replace-match "+")
(replace-match "-"))
(setq counter (1+ counter)))))
but C-u swap-sign still only runs swap-sign (or perhaps more precisely, the body of the while-loop) once. I'm guessing it is because if num is not the right way to test if num is an empty string.
Am I on the right track, or is there a better/easier way to extend swap-sign?
(defun swap-sign (arg)
(interactive "p")
(dotimes (i arg)
(search-forward-regexp "[+-]")
(if (equal (match-string 0) "-")
(replace-match "+")
(replace-match "-"))))
See the documentation of the interactive special form for more details:
C-h finteractiveRET.
You need to tell emacs to expect, and pass the parameter in, by adding a "p" as the parameter specification for interactive (M-x apropos interactive to get the documentation). Here I've made the minimal change to your code to get it to work - note, however, that you don't need the let/while to do the iteration, and the arg doesn't need to be optional.
(defun swap-sign (&optional num)
(interactive "p")
(let ((counter 1))
(while (<= counter num)
(search-forward-regexp "[+-]")
(if (equal (match-string 0) "-")
(replace-match "+")
(replace-match "-"))
(setq counter (1+ counter)))))
Note that you don't need to convert the parameter from a string - using "p" tells emacs to do this for you.