Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

scripting input methods (was back (batch) translate chars to keyboard events)

25 views
Skip to first unread message

Rustom Mody

unread,
Feb 13, 2012, 2:20:28 AM2/13/12
to nichola...@hp.com, help-gn...@gnu.org
Rustom Mody <rusto...@gmail.com> wrote:
I have some bunch of sanskrit (devanagari) to type.  It would be easiest for me if I could have the
English (roman) as well as the sanskrit (devanagari).

For example using the devanagari-itrans input method I can write the gayatri mantra using

OM bhUrbhuvaH suvaH
tatsaviturvarenyam
bhargo devasya dhImahi
dhiyo yonaH prachodayAt

and emacs produces *on the fly* (ie I cant see/edit the above)

  ॐ भूर्भुवः सुवः
  तत्सवितुर्वरेण्यम्
  भर्गो देवस्य धीमहि
  धियो योनः प्रचोदयात्

Can I do it in batch mode? ie write the first in a file and run some command on it to produce the second?






On Mon, Feb 13, 2012 at 9:45 AM, Nick Dokos <nichola...@hp.com> wrote:


Yup, it can be done, probably in multiple ways but here is one.

I saw your question on the python list and did a bit of digging: I came
up with a method that probably will work but will require more work to
flesh out. The key was that input methods read events one at a time and
(from the Elisp manual):

 -- Function: read-event &optional prompt inherit-input-method seconds
    This function reads and returns the next event of command input,
    waiting if necessary until an event is available.  Events can come
    directly from the user or from a keyboard macro.

So if you could get the text to become the body of a keyboard macro,
you could change the input method, execute the macro and that would
submit the text to the input method as if you had typed it.

Trying the theory, I started a keyboard macro, typed in OM and a newline
and ended the keyboard macro. I can then switch the input method to
devanagari-itrans, execute the macro and presto! I get the proper symbol
(at least to my untrained eyes).

You can insert the definition of a macro in a buffer (and name it, edit it,
save it to a file and load the file later, and execute the macro by name as
if it were a function (which it is, strictly speaking). The OM macro above
turns out to look like this :

(fset 'om
  (lambda (&optional arg) "Keyboard macro." (interactive "p") (kmacro-exec-ring-item (quote ([79 77 return] 0 "%d")) arg)))


 
I would change the python (or whatever) program to produce the whole
fset form into a file, then start emacs, load the file, switch input
method and execute the macro: M-x om.

Nick


Thanks for your efforts Nick!
Some questions:
1. Why fset? 

I can get the following to work

(defun om (&optional arg)
  "Keyboard macro."
  (interactive "p")
  (kmacro-exec-ring-item '([79 77 return] 0 "%d") arg))

so assuming its just a stylistic question. [If not please enlighten]
By 'work' I mean after this definition, M-x om inserts ॐ into the buffer if devanagari-itrans is active. So far so good

After that I am a bit stuck:
2. kmacro-exec-ring-item has just this much documentation.

kmacro-exec-ring-item is a compiled Lisp function in `kmacro.el'.

(kmacro-exec-ring-item ITEM ARG)

Execute item ITEM from the macro ring.

Does not take me far :-(

3.  I guess this is really the same as the above question...
You say
 
So all you need to do is produce that vector of ascii values in there. I
wrote a trivial python program to produce the ascii codes of your text
and stuffed the output into the vector, reevaluated the fset, and
executed the macro with a result that looks suspiciously like the one in
your email.


The python (or elisp) to a text (ASCII-only) string to its ASCII is 1 line:

>>> def Ascii(str): return [ord(c) for c in str]

I ran this on my file and got:
[79, 77, 32, 98, 104, 85, 114, 98, 104, 117, 118, 97, 72, 32, 115, 117, 118, 97, 72, 10, 116, 97, 116, 115, 97, 118, 105, 116, 117, 114, 118, 97, 114, 101, 110, 121, 97, 109, 10, 98, 104, 97, 114, 103, 111, 32, 100, 101, 118, 97, 115, 121, 97, 32, 100, 104, 73, 109, 97, 104, 105, 10, 100, 104, 105, 121, 111, 32, 121, 111, 110, 97, 72, 32, 112, 114, 97, 99, 104, 111, 100, 97, 121, 65, 116, 10]

Ok so remove the commas, ADD A 'return'  after to last 10 (Whats that??)
And it almost works
ie I get the devanagari output followed by a elisp error:

Debugger entered--Lisp error: (void-variable प्रचोदयात्)
  eval(प्रचोदयात्)
  eval-last-sexp-1(t)
  eval-last-sexp(t)
  eval-print-last-sexp()
  call-interactively(eval-print-last-sexp nil nil)
  execute-kbd-macro([79 77 32 98 104 85 114 98 104 117 118 97 72 32 115 117 118 97 72 116 97 116 115 97 118 105 116 117 114 118 97 114 101 110 121 97 109 98 104 97 114 103 111 32 100 101 118 97 115 121 ...] 1 kmacro-loop-setup-function)
  kmacro-exec-ring-item(([79 77 32 98 104 85 114 98 104 117 118 97 72 32 115 117 118 97 72 116 97 116 115 97 118 105 116 117 114 118 97 114 101 110 121 97 109 98 104 97 114 103 111 32 100 101 118 97 115 121 ...] 0 "%d") 1)
  om(1)
  call-interactively(om t nil)
  execute-extended-command(nil)
  call-interactively(execute-extended-command nil nil)

The प्रचोदयात् is the devanagari of the last line: prachodayAt
If I remove the 10 (newline?) it seems to work without the elisp error

I guess the problem would be solved if some more suitable function than
I wonder if there is some better function than kmacro-exec-ring-item could be found for this?

Rustom Mody

unread,
Feb 13, 2012, 2:41:41 AM2/13/12
to nichola...@hp.com, help-gn...@gnu.org
Some progress:

1. Replacing the kmacro-exec-ring-item with the lower level execute-kbd-macro works


(defun om ()
  "Keyboard macro."
  (interactive)
  (execute-kbd-macro [79  77  32  98  104  85  114  98  104  117  118  97  72  32  115  117  118  97  72  116  97  116  115  97  118  105  116  117  114  118  97  114  101  110  121  97  109  98  104  97  114  103  111  32  100  101  118  97  115  121  97  32  100  104  73  109  97  104  105    100  104  105  121  111  32  121  111  110  97  72  32  112  114  97  99  104  111  100  97  121  65  116 10]))

2. The misbehavior on the last 10 was because it was in Lisp Interaction mode <grin>

So now...
If I can produce that vector from elisp rather than python I guess its easy after that...

Thanks again Nick!

Jambunathan K

unread,
Feb 13, 2012, 3:17:42 AM2/13/12
to Rustom Mody, help-gn...@gnu.org, nichola...@hp.com

Rustom

Try this:

(defun translate-to-hindi (filename)
(interactive "fFile to be translated: ")
(with-temp-buffer
(switch-to-buffer (current-buffer))
(setq buffer-file-coding-system 'utf-8)
(set-input-method "devanagari-itrans" t)
(execute-kbd-macro
(with-temp-buffer
(insert-file-contents filename)
(buffer-string)))

(write-file
(concat (file-name-sans-extension filename)
"-hi" (file-name-extension filename t)))))
>   execute-kbd-macro([79 77 32 98 104 85 114 98 104 117 118 97 72 32
> 115 117 118 97 72 116 97 116 115 97 118 105 116 117 114 118 97 114
> 101 110 121 97 109 98 104 97 114 103 111 32 100 101 118 97 115 121
> ...] 1 kmacro-loop-setup-function)
>   kmacro-exec-ring-item(([79 77 32 98 104 85 114 98 104 117 118 97 72
> 32 115 117 118 97 72 116 97 116 115 97 118 105 116 117 114 118 97 114
> 101 110 121 97 109 98 104 97 114 103 111 32 100 101 118 97 115 121
> ...] 0 "%d") 1)
>   om(1)
>   call-interactively(om t nil)
>   execute-extended-command(nil)
>   call-interactively(execute-extended-command nil nil)
>
> The प्रचोदयात् is the devanagari of the last line: prachodayAt
> If I remove the 10 (newline?) it seems to work without the elisp
> error
>
> I guess the problem would be solved if some more suitable function
> than
> I wonder if there is some better function than kmacro-exec-ring-item
> could be found for this?
>
>

--

rusi

unread,
Feb 13, 2012, 3:43:07 AM2/13/12
to
On Feb 13, 1:17 pm, Jambunathan K <kjambunat...@gmail.com> wrote:
> Rustom
>
> Try this:
>
> (defun translate-to-hindi (filename)
>   (interactive "fFile to be translated: ")
>   (with-temp-buffer
>     (switch-to-buffer (current-buffer))
>     (setq buffer-file-coding-system 'utf-8)
>     (set-input-method "devanagari-itrans" t)
>     (execute-kbd-macro
>      (with-temp-buffer
>        (insert-file-contents filename)
>        (buffer-string)))
>
>     (write-file
>      (concat (file-name-sans-extension filename)
>              "-hi" (file-name-extension filename t)))))

Neat thanks.
I worked out this much. But I think ur code is cleaner!

------------------------------
(defvar input-method "devanagari-itrans")

(defun read-file-as-string (filename)
(interactive "f")
(with-temp-buffer
(insert-file-contents (expand-file-name filename "."))
(buffer-string)))

(defun transliterate (file)
(let ((inp (read-file-as-string file))
(filexlited (concat file "xx"))) ; transliterated file name
(find-file filexlited)
(set-input-method input-method)
(execute-kbd-macro inp)
(save-buffer)
(kill-buffer)))

Rustom Mody

unread,
Feb 13, 2012, 6:06:13 AM2/13/12
to Jambunathan K, help-gn...@gnu.org
On Mon, Feb 13, 2012 at 1:47 PM, Jambunathan K <kjambu...@gmail.com> wrote:

Rustom

Try this:

(defun translate-to-hindi (filename)
 (interactive "fFile to be translated: ")
 (with-temp-buffer
   (switch-to-buffer (current-buffer))
   (setq buffer-file-coding-system 'utf-8)
   (set-input-method "devanagari-itrans" t)
   (execute-kbd-macro
    (with-temp-buffer
      (insert-file-contents filename)
      (buffer-string)))

   (write-file
    (concat (file-name-sans-extension filename)
            "-hi" (file-name-extension filename t)))))


One question about the code above:
Why do you need the switch-to-buffer?
I tried removing it -- does not work
The doc for switch-to-buffer warns against using it in lisp suggesting set-buffer instead.
I tried that -- does not work. My current code is this:

(defvar input-method "devanagari-itrans")

(defun translate-to-hindi (filename)
 (interactive "fFile to be translated: ")
 (let ((inp (with-temp-buffer
        (insert-file-contents filename)
        (buffer-string)))
       (out-file-name (concat (file-name-sans-extension filename)
                   "-hi" (file-name-extension filename t))))

   (with-temp-buffer
     (switch-to-buffer (current-buffer))
     (setq buffer-file-coding-system 'utf-8)
     (set-input-method input-method t)
     (execute-kbd-macro inp)
     (write-file out-file-name))))


Stefan Monnier

unread,
Feb 13, 2012, 8:46:01 AM2/13/12
to
> (defun om (&optional arg)
> "Keyboard macro."
> (interactive "p")
> (kmacro-exec-ring-item '([79 77 return] 0 "%d") arg))

A key detail you seem to be missing is that [79 77 return] is simply
a vector of events, and that for reasons of compatibility to the time
Emacs worked in a context where the only source of events was `stdin',
this vector can also be a "vector of chars", aka a string:

(defun om (&optional arg)
"Keyboard macro."
(interactive "p")
(kmacro-exec-ring-item '("OM\r" 0 "%d") arg))

So you can probably get what you want with something like:

(defun sm-apply-input-method-to-region (beg end)
"Read the region as if it had been typed in."
(interactive "*r")
(goto-char beg)
(let ((text (delete-and-extract-region beg end)))
(kmacro-exec-ring-item `(,text 0 "%d") nil)))


-- Stefan

rusi

unread,
Feb 13, 2012, 11:00:31 AM2/13/12
to
Thats compact.
This (seems to be) simpler [Also I can understand it :-) ]

(defun sm-apply-input-method-to-region (beg end)
"Read the region as if it had been typed in."
(interactive "*r")
(goto-char beg)
(let ((text (delete-and-extract-region beg end)))
(execute-kbd-macro text)
))

Any reason to prefer kmacro-exec-ring-item to execute-kbd-macro?

Stefan Monnier

unread,
Feb 13, 2012, 11:25:42 AM2/13/12
to
> (defun sm-apply-input-method-to-region (beg end)
> "Read the region as if it had been typed in."
> (interactive "*r")
> (goto-char beg)
> (let ((text (delete-and-extract-region beg end)))
> (execute-kbd-macro text)
> ))

> Any reason to prefer kmacro-exec-ring-item to execute-kbd-macro?

No,


Stefan

Nick Dokos

unread,
Feb 13, 2012, 10:28:53 AM2/13/12
to Rustom Mody, help-gn...@gnu.org, nichola...@hp.com
That's how emacs stored the definition of the macro: I defined the macro
with C-x ( O M C-x ), named it with C-x C-k n om RET and inserted it in
the buffer with M-x insert-kbd-macro RET om RET. The result was as shown
above.

> I can get the following to work
>
> (defun om (&optional arg)
>   "Keyboard macro."
>   (interactive "p")
>   (kmacro-exec-ring-item '([79 77 return] 0 "%d") arg))
>
> so assuming its just a stylistic question. [If not please enlighten]

In some sense, fset is the lower level primitive: defun could be defined
in terms of fset (but not the other way round). But they do roughly the
"same thing": they attach a function to the function cell of a symbol.

> By 'work' I mean after this definition, M-x om inserts ॐ into the buffer if devanagari-itrans is
> active. So far so good
>
> After that I am a bit stuck:
> 2. kmacro-exec-ring-item has just this much documentation.
>
> kmacro-exec-ring-item is a compiled Lisp function in `kmacro.el'.
>
> (kmacro-exec-ring-item ITEM ARG)
>
> Execute item ITEM from the macro ring.
>
> Does not take me far :-(
>

Can't help here.

> 3.  I guess this is really the same as the above question...
> You say
>  
>
> So all you need to do is produce that vector of ascii values in there. I
> wrote a trivial python program to produce the ascii codes of your text
> and stuffed the output into the vector, reevaluated the fset, and
> executed the macro with a result that looks suspiciously like the one in
> your email.
>
> The python (or elisp) to a text (ASCII-only) string to its ASCII is 1 line:
>
> >>> def Ascii(str): return [ord(c) for c in str]
>
> I ran this on my file and got:
> [79, 77, 32, 98, 104, 85, 114, 98, 104, 117, 118, 97, 72, 32, 115, 117, 118, 97, 72, 10, 116, 97,
> 116, 115, 97, 118, 105, 116, 117, 114, 118, 97, 114, 101, 110, 121, 97, 109, 10, 98, 104, 97, 114,
> 103, 111, 32, 100, 101, 118, 97, 115, 121, 97, 32, 100, 104, 73, 109, 97, 104, 105, 10, 100, 104,
> 105, 121, 111, 32, 121, 111, 110, 97, 72, 32, 112, 114, 97, 99, 104, 111, 100, 97, 121, 65, 116, 10]
>
> Ok so remove the commas, ADD A 'return'  after to last 10 (Whats that??)

The 10 is a RET, so you shouldn't have to add an extra one.

> And it almost works
> ie I get the devanagari output followed by a elisp error:
>
> Debugger entered--Lisp error: (void-variable प्रचोदयात्)
>   eval(प्रचोदयात्)
>   eval-last-sexp-1(t)
>   eval-last-sexp(t)
>   eval-print-last-sexp()
>   call-interactively(eval-print-last-sexp nil nil)
>   execute-kbd-macro([79 77 32 98 104 85 114 98 104 117 118 97 72 32 115 117 118 97 72 116 97 116 115
> 97 118 105 116 117 114 118 97 114 101 110 121 97 109 98 104 97 114 103 111 32 100 101 118 97 115 121
> ...] 1 kmacro-loop-setup-function)
>   kmacro-exec-ring-item(([79 77 32 98 104 85 114 98 104 117 118 97 72 32 115 117 118 97 72 116 97
> 116 115 97 118 105 116 117 114 118 97 114 101 110 121 97 109 98 104 97 114 103 111 32 100 101 118 97
> 115 121 ...] 0 "%d") 1)
>   om(1)
>   call-interactively(om t nil)
>   execute-extended-command(nil)
>   call-interactively(execute-extended-command nil nil)
>
> The प्रचोदयात् is the devanagari of the last line: prachodayAt
> If I remove the 10 (newline?) it seems to work without the elisp error
>
As you point out in your subsequent post, that's an artifact of lisp-interaction
mode: it does not happen in a text file.

> I guess the problem would be solved if some more suitable function than
> I wonder if there is some better function than kmacro-exec-ring-item could be found for this?
>

And you seem to have found one of those as well.

Good luck!

Nick


Nick Dokos

unread,
Feb 13, 2012, 10:47:58 AM2/13/12
to Jambunathan K, Rustom Mody, help-gn...@gnu.org, nichola...@hp.com
Jambunathan K <kjambu...@gmail.com> wrote:

>
> Rustom
>
> Try this:
>
> (defun translate-to-hindi (filename)
> (interactive "fFile to be translated: ")
> (with-temp-buffer
> (switch-to-buffer (current-buffer))
> (setq buffer-file-coding-system 'utf-8)
> (set-input-method "devanagari-itrans" t)
> (execute-kbd-macro
> (with-temp-buffer
> (insert-file-contents filename)
> (buffer-string)))
>
> (write-file
> (concat (file-name-sans-extension filename)
> "-hi" (file-name-extension filename t)))))
>

Perfect. I haven't tried to see what happens if you just
input arbitrary junk to it, but it is at least idempotent.

Nick

rusi

unread,
Feb 14, 2012, 9:51:57 PM2/14/12
to
Thanks Nick, Jambunathan, Stefan

My code is as follows.
With this I can type Ascii in one frame and every time I press f4 the
other frame reflects the transliteration

(defvar input-method "devanagari-itrans")
(defun rpm-apply-input-method () ; buffer version
(interactive)
(let* ((inp (buffer-substring-no-properties (point-min)(point-max)))
(filename (file-name-nondirectory (buffer-file-name)))
(out-buf-name (concat (file-name-sans-extension filename)
"-hi" (file-name-extension filename t)))
(p))
(switch-to-buffer-other-frame out-buf-name)
(save-excursion
(save-window-excursion
(setq p (point))
(erase-buffer)
(setq buffer-file-coding-system 'utf-8)
(set-input-method input-method t)
(execute-kbd-macro inp)
))
(goto-char p)
(other-frame 1)
))
(global-set-key [f4] 'rpm-apply-input-method)
0 new messages