From: Xah Lee
Subject: How To Process A File Line By Line In Elisp
Date: 
Message-ID: <0b6784ee-7369-409e-ba94-87f288ee6ff6@r36g2000prf.googlegroups.com>
Lisp lesson hot from the press!

• How To Process A File Line By Line In Elisp
  http://xahlee.org/emacs/elisp_process_lines.html

plain text version follows:
-----------------------------------
How To Process A File Line By Line In Elisp

Xah Lee, 2008-12-05

This page gives a example of how to use emacs lisp to process a file
line by line.

The Problem

Summary

I need to process a file line by line. For each line, i need to parse
it into 2 parts, stringA and stringB, and i need to create a file
named stringA, with the content having stringA and stringB among other
texts.

Detail

I'm writing a major mode for Linden Script Language (LSL). LSL is a
scripting language used for the virtual world Second Life. It has few
hundred functions, and each one has parameters that is unusual as
compared to normal programing languages. So, i want a function
template feature in my major mode, so that a LSL programer can
automatically insert function templates and see what are the
parameters for that function and can fill it out as he code.

There is a great template system package for emacs, called yasnippet↗.
So, i decided to use this instead of implementing my own template
system. With yasnippet, it uses a plain text based template. For each
function template you want, you need to create a file. For example, in
LSL there's a function named “collision” with this syntax “collision
(integer num_detected) { ... }”. This means, i must have a file named
“collision” in the template dir, and the file content must be like
this:

# --
collision(integer num_detected)
{
$0
}
This means, when my mode xlsl-mode is on, and yasnippet minor mode is
on, then user can type “collision” followed by a keyboard shortcut for
template completion, then the function form will be inserted and
cursor will be placed between the braces.

I have prepared a file that is over 300 lines that are the LSL
functions and parameters. For example, part of the file looks like
this:

at_rot_target(integer tnum, rotation targetrot, rotation ourrot)
at_target(integer tnum, vector targetpos, vector ourpos)
attach(key id)
changed(integer change)
collision(integer num_detected)
collision_end(integer num_detected)
collision_start(integer num_detected)
control(key id, integer held, integer change)
(Save the above text in a file named “xx_event_forms.txt” for later
testing of elisp code.)

Now, my job is to parse this file, and for each line, create the
template file for it.

Solution

The task has these steps:

Open the file.
Read each line.
Parse the line into 2 parts. The first part is everything before the
opening paren (call it stringA). The second part is the rest of the
line (call it stringB).
Create a file and named stringA.
Insert into the file the whole line, and other text such as the
beginning “# --” and “{ $0 }”.
These are simple tasks. There are a lot ways to do this in elisp. We
can for example grab the whole file's text, then use split-string by
newline char to get a list of lines. Then we loop thru the line.
Another way, is to simply move cursor one line at a time, each time
grab the line and do what we need to do. Here we'll use this second
method.

First, we define few global vars.

;;;; input file
(setq inptuFile "xx_event_forms.txt")

(setq splitPoint 0) ;; point of split for each line
(setq fname "")
(setq restLine "")
(setq moreLines t ) ;; whether there are more lines to parse
Now, we open the file, like this:

;; open the file
(find-file inptuFile)
(goto-char (point-min))
;; goto beginnning of file in case the file is already open.
Now, we loop thru the lines, like this:

(while moreLines
  (search-forward "(" nil t)

  (setq splitPos (1- (point)))
  (move-beginning-of-line 1)
  (setq fname (buffer-substring-no-properties (point) splitPos))
  (move-end-of-line 1)
  (setq restLine (buffer-substring-no-properties splitPos (point) ))

  ;; create the file
  (find-file fname)
  (insert "# --\n")
  (insert fname restLine "\n{\n$0\n}" )
  (save-buffer)
  (kill-buffer (current-buffer))

  (setq moreLines (= 0 (forward-line 1)))
)
In the above, we use search-forward to move cursor to the opening
paren. Then, save the position to splitPos. Everything before that
should be the template file name, so we save it in fname. Everything
after that is restLine.

Now, we create the file fname using “(find-file fname)”, then, insert
the content we want, save it, close it.

Lastly, we move cursor to the next line by “(forward-line 1)”. Note
that if the cursor is at the last line, and when “forward-line” is
unable to move forward, it will return a number indicating how many
lines it failed to pass. So, normally it returns 0. If not, that means
we are on the last line.

After we processed the lines, we just close the input buffer, like
this:

(kill-buffer (current-buffer)) ;; close the input file
To test the above, first create a sample input file. Take the sample
input lines above and save it as “xx_event_forms.txt” if you haven't
done so already. Then, grab all the above lisp code and save in in a
file “test_line_process.el”. Now, open the lisp file and type “Alt+x
eval-buffer”. Then all the template files will be created in the same
dir.

Emacs is fantastic!

  Xah
∑ http://xahlee.org/

☄

From: blandest
Subject: Re: How To Process A File Line By Line In Elisp
Date: 
Message-ID: <3bb451a8-f80b-44ab-ab30-6f8070024c14@w1g2000prm.googlegroups.com>
On Dec 6, 11:34 am, Xah Lee <······@gmail.com> wrote:
> Lisp lesson hot from the press!
>
> • How To Process A File Line By Line In Elisp
>  http://xahlee.org/emacs/elisp_process_lines.html
>
> plain text version follows:
> -----------------------------------
> How To Process A File Line By Line In Elisp
>
> Xah Lee, 2008-12-05
>
> This page gives a example of how to use emacs lisp to process a file
> line by line.
>
> The Problem
>
> Summary
>
> I need to process a file line by line. For each line, i need to parse
> it into 2 parts, stringA and stringB, and i need to create a file
> named stringA, with the content having stringA and stringB among other
> texts.
>
> Detail
>
> I'm writing a major mode for Linden Script Language (LSL). LSL is a
> scripting language used for the virtual world Second Life. It has few
> hundred functions, and each one has parameters that is unusual as
> compared to normal programing languages. So, i want a function
> template feature in my major mode, so that a LSL programer can
> automatically insert function templates and see what are the
> parameters for that function and can fill it out as he code.
>
> There is a great template system package for emacs, called yasnippet↗.
> So, i decided to use this instead of implementing my own template
> system. With yasnippet, it uses a plain text based template. For each
> function template you want, you need to create a file. For example, in
> LSL there's a function named “collision” with this syntax “collision
> (integer num_detected) { ... }”. This means, i must have a file named
> “collision” in the template dir, and the file content must be like
> this:
>
> # --
> collision(integer num_detected)
> {
> $0}
>
> This means, when my mode xlsl-mode is on, and yasnippet minor mode is
> on, then user can type “collision” followed by a keyboard shortcut for
> template completion, then the function form will be inserted and
> cursor will be placed between the braces.
>
> I have prepared a file that is over 300 lines that are the LSL
> functions and parameters. For example, part of the file looks like
> this:
>
> at_rot_target(integer tnum, rotation targetrot, rotation ourrot)
> at_target(integer tnum, vector targetpos, vector ourpos)
> attach(key id)
> changed(integer change)
> collision(integer num_detected)
> collision_end(integer num_detected)
> collision_start(integer num_detected)
> control(key id, integer held, integer change)
> (Save the above text in a file named “xx_event_forms.txt” for later
> testing of elisp code.)
>
> Now, my job is to parse this file, and for each line, create the
> template file for it.
>
> Solution
>
> The task has these steps:
>
> Open the file.
> Read each line.
> Parse the line into 2 parts. The first part is everything before the
> opening paren (call it stringA). The second part is the rest of the
> line (call it stringB).
> Create a file and named stringA.
> Insert into the file the whole line, and other text such as the
> beginning “# --” and “{ $0 }”.
> These are simple tasks. There are a lot ways to do this in elisp. We
> can for example grab the whole file's text, then use split-string by
> newline char to get a list of lines. Then we loop thru the line.
> Another way, is to simply move cursor one line at a time, each time
> grab the line and do what we need to do. Here we'll use this second
> method.
>
> First, we define few global vars.
>
> ;;;; input file
> (setq inptuFile "xx_event_forms.txt")
>
> (setq splitPoint 0) ;; point of split for each line
> (setq fname "")
> (setq restLine "")
> (setq moreLines t ) ;; whether there are more lines to parse
> Now, we open the file, like this:
>
> ;; open the file
> (find-file inptuFile)
> (goto-char (point-min))
> ;; goto beginnning of file in case the file is already open.
> Now, we loop thru the lines, like this:
>
> (while moreLines
>   (search-forward "(" nil t)
>
>   (setq splitPos (1- (point)))
>   (move-beginning-of-line 1)
>   (setq fname (buffer-substring-no-properties (point) splitPos))
>   (move-end-of-line 1)
>   (setq restLine (buffer-substring-no-properties splitPos (point) ))
>
>   ;; create the file
>   (find-file fname)
>   (insert "# --\n")
>   (insert fname restLine "\n{\n$0\n}" )
>   (save-buffer)
>   (kill-buffer (current-buffer))
>
>   (setq moreLines (= 0 (forward-line 1)))
> )
> In the above, we use search-forward to move cursor to the opening
> paren. Then, save the position to splitPos. Everything before that
> should be the template file name, so we save it in fname. Everything
> after that is restLine.
>
> Now, we create the file fname using “(find-file fname)”, then, insert
> the content we want, save it, close it.
>
> Lastly, we move cursor to the next line by “(forward-line 1)”. Note
> that if the cursor is at the last line, and when “forward-line” is
> unable to move forward, it will return a number indicating how many
> lines it failed to pass. So, normally it returns 0. If not, that means
> we are on the last line.
>
> After we processed the lines, we just close the input buffer, like
> this:
>
> (kill-buffer (current-buffer)) ;; close the input file
> To test the above, first create a sample input file. Take the sample
> input lines above and save it as “xx_event_forms.txt” if you haven't
> done so already. Then, grab all the above lisp code and save in in a
> file “test_line_process.el”. Now, open the lisp file and type “Alt+x
> eval-buffer”. Then all the template files will be created in the same
> dir.
>
> Emacs is fantastic!
>
>   Xah
> ∑http://xahlee.org/
>
> ☄

You could use `find-file-noselect' with RAWFILE set to t and then
just :

(split-string (buffer-substring (point-min) (point-max)) "\n" t)

thus avoiding the imperative approach and also gaining some speed for
parsing raw text instead of passing through whatever mode loads for
your template file.
From: Xah Lee
Subject: Re: How To Process A File Line By Line In Elisp
Date: 
Message-ID: <d8993c47-a06b-4354-bda0-440b52a37dcc@z28g2000prd.googlegroups.com>
> On Dec 6, 11:34 am, Xah Lee <······@gmail.com> wrote:
> > • How To Process A File Line By Line In Elisp
> >  http://xahlee.org/emacs/elisp_process_lines.html


On Dec 8, 1:55 pm, blandest <··············@gmail.com> wrote:
> You could use `find-file-noselect' with RAWFILE set to t and then
> just :
>
> (split-string (buffer-substring (point-min) (point-max)) "\n" t)
>
> thus avoiding the imperative approach and also gaining some speed for
> parsing raw text instead of passing through whatever mode loads for
> your template file.

Hi,

find-file-noselect is a good suggestion... (i haven't used it.
typically i use with-temp-buffer and insert-file-contents)

though, the reading file in whole into a list then process each line
is too much like Perl, Python, etc. I wanted to show the emacs's
unique advantage of freely having the point move and processing any
part of the file. Not applicable in this task, but good for elisp
leaners to pick up this methodology ...

maybe i'll add a alnernative solution using split-string as you
suggest to contrast... Thanks.

  Xah
∑ http://xahlee.org/

☄