all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* Re: Writing to buffer/file
       [not found] <87eicwfpjq.fsf@cecilia.trollope>
@ 2010-09-14 14:13 ` Pascal J. Bourguignon
  2010-09-14 14:14 ` Pascal J. Bourguignon
  2010-09-14 14:33 ` Pascal J. Bourguignon
  2 siblings, 0 replies; 4+ messages in thread
From: Pascal J. Bourguignon @ 2010-09-14 14:13 UTC (permalink / raw)
  To: help-gnu-emacs

Michael Powe <michael+gnus@trollope.org> writes:

> Hello,
>
> I am writing a function in elisp to do the following:
>
> open and read an .ini file
> search the file for header lines of format [someheader]
> put header value as key of hash
> continue to search for string value (e.g., "profilename=.*")
> add found string value to list which is set as value for the header key 
> for each header, a small number of items will be added to the list
> after reading through ini file and filling the hash:
> open another buffer and write the contents of the hash to the buffer
> write the buffer to file
>
> Here is my function:
>
> (defun extract-ini-settings ()
>   (interactive)
>   (let (myhash mybuff myfile header strmatch results output strvalues mylist)
> 	(setq myfile "c:\\share\\reports_default.ini")
> 	(setq myhash (make-hash-table :test 'equal))
> 	(setq mybuff (find-file myfile))
> 	(set-buffer mybuff)
> 	(setq strvalues "")
> 	(while
> 		(re-search-forward
> 		 "\\[customtableprofile[0-9]+\\]"
> 		 nil t)
> 	  (when (match-string 0)
> 		(setq header (match-string 0))
> 		(puthash header '() myhash)
> 		(while 
> 			(re-search-forward
> 			 "profilename=.*" nil t)
> 		  (when (match-string 0)
> 			(setq strmatch (match-string 0))
> 			(puthash header (cons strmatch (gethash header myhash)) myhash)))))
> 	(kill-buffer mybuff)
> 	(message (number-to-string (hash-table-count myhash)))
> 	(setq output "c:\\share\\reports_headers.txt")
> 	(setq results (find-file output))
> 	(set-buffer results)
> 	(point-min)
>     ;; this works, i.e. writes the string to the `results' buffer
>     ;; and each time I run the function, a new copy of the string
>     ;; is appended to the buffer.
> 	(insert "Start of file\n")
> 	(point-max)
> 	(hash-to-list (myhash mylist))
> 	(insert mylist)
> 	))
>
> Here is the helper function hash-to-list:
>
> (defun hash-to-list (hashtable mylist)
>   "Return a list that represent the hashtable."
>     (maphash (lambda (kk vv) (setq mylist (cons (list kk vv) mylist))) hashtable)
>     mylist)
>
> I am evidently grossly misunderstanding the mechanism for writing to
> file. 


Even before that, you're misunderstanding the basic syntaxis for Lisp.

To call a function in lisp, we write a LIST, whose first element is the
name of the function, and whose other elements are the arguments to
that function.

To write a list, we write an open parenthesis, the elements of the list
separated by spaces, and a close parenthesis.


sexp ::= atom | '(' {sexp} ')' .

function-call ::= '(' function-name {argument} ')'






    (hash-to-list (myhash mylist))

Since myhash is not the name of a function the subexpression

    (myhash mylist) 

is meaningless, and since hash-to-list is defined to take two arguments,
this function call is wrong since you're trying to pass only one
(erronous) argument.



Finally, as you have noted, arguments are passed by value, so when you
call:

    (hash-to-list myhash mylist)

hash-to-list has no way to modify mylist.  I assume this is why you're
correctly returning from hash-to-list the local variable mylist which
contains the result of hash-to-list.   In any case, you will have to
rebind this result to the mylist of extract-ini-settings:

   (setf mylist (hash-to-list myhash mylist))
   (insert mylist)

or even better, just insert it directly:

   (insert (prin1-to-string (hash-to-list myhash mylist)))

Notice that insert can insert only strings, so you have to convert your
list into a string first.


Since mylist is bound to nil, there's not really any point in passing it
to hash-to-list.  


So you could write:

(require 'cl)

(defun hash-to-list (hashtable)
  "Return a list that represent the hashtable."
  (let ((result '()))
     (maphash (lambda (kk vv) (push (list kk vv) result))
              hashtable)
     result))



(let ((h (make-hash-table)))
   (setf (gethash :one h) 1
         (gethash :two h) 2
         (gethash :six h) 6)
   (insert (prin1-to-string (hash-to-list h))))

inserts:  ((:six 6) (:two 2) (:one 1))





In general, it is better of you can write functionnal code, that is, if
you can avoid modifying the bindings of the variables (avoid using setq
of setf).    This means that when you use let, you SHOULD give a value
to be bound to each variable, and not change it thereafter.

Also, it would  be better if you parameterized  your functions, ie. avoid
putting constants, such as pathnames inside your functions.

It would be nice if you avoided to to define variables that you don't
use.  (But do define variables that you use!).




(defun extract-ini-settings (ini-file dictionary-file)
  (interactive)
  (let ((dictionary (make-hash-table :test (function equal))))
    
    (with-temp-buffer
      (insert-file-contents ini-file)
      (while (re-search-forward "\\[customtableprofile[0-9]+\\]" nil t)
        (when (match-string 0)
          (let ((section  (match-string 0))
                (profiles '())
                (start    (point)) ; save the beginning of the section
                (end   (if (re-search-forward "\\[customtableprofile[0-9]+\\]" nil t)
                           (point) ; don't search profilename beyond the next section!
                           (point-max))))
            (goto-char start)
            (while (re-search-forward "profilename=.*" end t)
              (when (match-string 0)
                (push (match-string 0) profiles)))
            (setf (gethash section dictionary) profiles)))))

    ;; Are you sure you want to accumulate dictionaries in the output
    ;; file?
    ;; Here, I'm just replacing the whole contents with a single list:
    
    (find-file dictionary-file)
    (erase-buffer)
    (insert ";; -*- mode:lisp; coding: utf-8 -*-\n")
    (insert (format ";; Data extracted from %s\n\n" ini-file))
    (insert (prin1-to-string (hash-to-list dictionary)))
    (save-buffer)
    (kill-buffer)))


(extract-ini-settings
 "c:\\share\\reports_default.ini"
 "c:\\share\\reports_headers.txt")

(extract-ini-settings "/tmp/a.ini" "/tmp/a.txt")
(insert-file-contents "/tmp/a.txt")

;; -*- mode:lisp; coding: utf-8 -*-
;; Data extracted from /tmp/a.ini

(("[customtableprofile1]" ("profilename=tutu\"" "profilename=tata")) ("[customtableprofile0]" ("profilename=titi\"" "profilename=toto")))


> Any help in correcting my errors would be greatly appreciated.

Finally, as you can see with my test file, using regular expressions is
a big source of errors.  

Well, if your ini file don't contain such "pathological" cases, they
could work, but in general, it would be better to write a parser.


-- 
__Pascal Bourguignon__
http://www.informatimago.com


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Writing to buffer/file
       [not found] <87eicwfpjq.fsf@cecilia.trollope>
  2010-09-14 14:13 ` Writing to buffer/file Pascal J. Bourguignon
@ 2010-09-14 14:14 ` Pascal J. Bourguignon
       [not found]   ` <0388da54-19f4-4aaa-90c6-8f6262c4164f@c21g2000vba.googlegroups.com>
  2010-09-14 14:33 ` Pascal J. Bourguignon
  2 siblings, 1 reply; 4+ messages in thread
From: Pascal J. Bourguignon @ 2010-09-14 14:14 UTC (permalink / raw)
  To: help-gnu-emacs

Michael Powe <michael+gnus@trollope.org> writes:

> Hello,
>
> I am writing a function in elisp to do the following:
>
> open and read an .ini file
> search the file for header lines of format [someheader]
> put header value as key of hash
> continue to search for string value (e.g., "profilename=.*")
> add found string value to list which is set as value for the header key 
> for each header, a small number of items will be added to the list
> after reading through ini file and filling the hash:
> open another buffer and write the contents of the hash to the buffer
> write the buffer to file
>
> Here is my function:
>
> (defun extract-ini-settings ()
>   (interactive)
>   (let (myhash mybuff myfile header strmatch results output strvalues mylist)
> 	(setq myfile "c:\\share\\reports_default.ini")
> 	(setq myhash (make-hash-table :test 'equal))
> 	(setq mybuff (find-file myfile))
> 	(set-buffer mybuff)
> 	(setq strvalues "")
> 	(while
> 		(re-search-forward
> 		 "\\[customtableprofile[0-9]+\\]"
> 		 nil t)
> 	  (when (match-string 0)
> 		(setq header (match-string 0))
> 		(puthash header '() myhash)
> 		(while 
> 			(re-search-forward
> 			 "profilename=.*" nil t)
> 		  (when (match-string 0)
> 			(setq strmatch (match-string 0))
> 			(puthash header (cons strmatch (gethash header myhash)) myhash)))))
> 	(kill-buffer mybuff)
> 	(message (number-to-string (hash-table-count myhash)))
> 	(setq output "c:\\share\\reports_headers.txt")
> 	(setq results (find-file output))
> 	(set-buffer results)
> 	(point-min)
>     ;; this works, i.e. writes the string to the `results' buffer
>     ;; and each time I run the function, a new copy of the string
>     ;; is appended to the buffer.
> 	(insert "Start of file\n")
> 	(point-max)
> 	(hash-to-list (myhash mylist))
> 	(insert mylist)
> 	))
>
> Here is the helper function hash-to-list:
>
> (defun hash-to-list (hashtable mylist)
>   "Return a list that represent the hashtable."
>     (maphash (lambda (kk vv) (setq mylist (cons (list kk vv) mylist))) hashtable)
>     mylist)
>
> I am evidently grossly misunderstanding the mechanism for writing to
> file. 


Even before that, you're misunderstanding the basic syntaxis for Lisp.

To call a function in lisp, we write a LIST, whose first element is the
name of the function, and whose other elements are the arguments to
that function.

To write a list, we write an open parenthesis, the elements of the list
separated by spaces, and a close parenthesis.


sexp ::= atom | '(' {sexp} ')' .

function-call ::= '(' function-name {argument} ')'






    (hash-to-list (myhash mylist))

Since myhash is not the name of a function the subexpression

    (myhash mylist) 

is meaningless, and since hash-to-list is defined to take two arguments,
this function call is wrong since you're trying to pass only one
(erronous) argument.



Finally, as you have noted, arguments are passed by value, so when you
call:

    (hash-to-list myhash mylist)

hash-to-list has no way to modify mylist.  I assume this is why you're
correctly returning from hash-to-list the local variable mylist which
contains the result of hash-to-list.   In any case, you will have to
rebind this result to the mylist of extract-ini-settings:

   (setf mylist (hash-to-list myhash mylist))
   (insert mylist)

or even better, just insert it directly:

   (insert (prin1-to-string (hash-to-list myhash mylist)))

Notice that insert can insert only strings, so you have to convert your
list into a string first.


Since mylist is bound to nil, there's not really any point in passing it
to hash-to-list.  


So you could write:

(require 'cl)

(defun hash-to-list (hashtable)
  "Return a list that represent the hashtable."
  (let ((result '()))
     (maphash (lambda (kk vv) (push (list kk vv) result))
              hashtable)
     result))



(let ((h (make-hash-table)))
   (setf (gethash :one h) 1
         (gethash :two h) 2
         (gethash :six h) 6)
   (insert (prin1-to-string (hash-to-list h))))

inserts:  ((:six 6) (:two 2) (:one 1))





In general, it is better of you can write functionnal code, that is, if
you can avoid modifying the bindings of the variables (avoid using setq
of setf).    This means that when you use let, you SHOULD give a value
to be bound to each variable, and not change it thereafter.

Also, it would  be better if you parameterized  your functions, ie. avoid
putting constants, such as pathnames inside your functions.

It would be nice if you avoided to to define variables that you don't
use.  (But do define variables that you use!).




(defun extract-ini-settings (ini-file dictionary-file)
  (interactive)
  (let ((dictionary (make-hash-table :test (function equal))))
    
    (with-temp-buffer
      (insert-file-contents ini-file)
      (while (re-search-forward "\\[customtableprofile[0-9]+\\]" nil t)
        (when (match-string 0)
          (let ((section  (match-string 0))
                (profiles '())
                (start    (point)) ; save the beginning of the section
                (end   (if (re-search-forward "\\[customtableprofile[0-9]+\\]" nil t)
                           (point) ; don't search profilename beyond the next section!
                           (point-max))))
            (goto-char start)
            (while (re-search-forward "profilename=.*" end t)
              (when (match-string 0)
                (push (match-string 0) profiles)))
            (setf (gethash section dictionary) profiles)))))

    ;; Are you sure you want to accumulate dictionaries in the output
    ;; file?
    ;; Here, I'm just replacing the whole contents with a single list:
    
    (find-file dictionary-file)
    (erase-buffer)
    (insert ";; -*- mode:lisp; coding: utf-8 -*-\n")
    (insert (format ";; Data extracted from %s\n\n" ini-file))
    (insert (prin1-to-string (hash-to-list dictionary)))
    (save-buffer)
    (kill-buffer)))


(extract-ini-settings
 "c:\\share\\reports_default.ini"
 "c:\\share\\reports_headers.txt")

(extract-ini-settings "/tmp/a.ini" "/tmp/a.txt")
(insert-file-contents "/tmp/a.txt")

;; -*- mode:lisp; coding: utf-8 -*-
;; Data extracted from /tmp/a.ini

(("[customtableprofile1]" ("profilename=tutu\"" "profilename=tata")) ("[customtableprofile0]" ("profilename=titi\"" "profilename=toto")))


> Any help in correcting my errors would be greatly appreciated.

Finally, as you can see with my test file, using regular expressions is
a big source of errors.  

Well, if your ini file don't contain such "pathological" cases, they
could work, but in general, it would be better to write a parser.


-- 
__Pascal Bourguignon__
http://www.informatimago.com


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Writing to buffer/file
       [not found] <87eicwfpjq.fsf@cecilia.trollope>
  2010-09-14 14:13 ` Writing to buffer/file Pascal J. Bourguignon
  2010-09-14 14:14 ` Pascal J. Bourguignon
@ 2010-09-14 14:33 ` Pascal J. Bourguignon
  2 siblings, 0 replies; 4+ messages in thread
From: Pascal J. Bourguignon @ 2010-09-14 14:33 UTC (permalink / raw)
  To: help-gnu-emacs

Michael Powe <michael+gnus@trollope.org> writes:

> I sort of took this up as a challenge after reading an article the
> other day comparing perl vs elisp for text processing, in which elisp
> was given the nod as the superior language.  I could write this text
> processor pretty easily in Python.  But it would not be nearly as
> compact as what I have so far.  


Locally (ie. for a small function like yours), lisp may not be more
concise or easier to use than perl or python.   But given the ability of
lisp for abstraction (data abstraction, and functional abstraction, most
languages have it, but lisp thanks to its homoiconicity and its macro,
provides also syntactic and metalinguistic abstraction), as soon as you
have more complex processing to implement, you can better factorize
things our, and obtain a more concise and cleaner way to express your
processings.  In the mean while, you will have developed a little
library or language designed to implement your specific kind of
processing.  (Or put to use an existing library or language extension).


See for example:
http://lichteblau.blogspot.com/2009/04/xslt-10-implemented-in-common-lisp.html
http://common-lisp.net/project/xuriella/



-- 
__Pascal Bourguignon__
http://www.informatimago.com


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Writing to buffer/file
       [not found]   ` <0388da54-19f4-4aaa-90c6-8f6262c4164f@c21g2000vba.googlegroups.com>
@ 2010-09-14 17:46     ` Pascal J. Bourguignon
  0 siblings, 0 replies; 4+ messages in thread
From: Pascal J. Bourguignon @ 2010-09-14 17:46 UTC (permalink / raw)
  To: help-gnu-emacs

naugiedoggie <michael.a.powe@gmail.com> writes:

> On Sep 14, 10:14 am, p...@informatimago.com (Pascal J. Bourguignon)
> wrote:
>> Michael Powe <michael+g...@trollope.org> writes:
>
>> Even before that, you're misunderstanding the basic syntaxis for Lisp.
>
>> To call a function in lisp, we write a LIST, whose first element is the
>> name of the function, and whose other elements are the arguments to
>> that function.
>
> Every journey begins with a step.
>
>> To write a list, we write an open parenthesis, the elements of the list
>> separated by spaces, and a close parenthesis.
>>
>> sexp ::= atom | '(' {sexp} ')' .
>>
>> function-call ::= '(' function-name {argument} ')'
>>
>>     (hash-to-list (myhash mylist))
>>
>> Since myhash is not the name of a function the subexpression
>>
>>     (myhash mylist)
>
> Why doesn't it through an error? e.g. '(void-function myhash)'?

Indeed this is what I would expect.  Unless you've defined a function
named myhash.

> I took that hash-to-list function from Xah Lee's emacs pages.  I did
> change it to pass in the list variable.

You just cannot in lisp pass 'variables' as argument to a
function. You can only pass values.  

In lisp, arguments are always passed by value.

(It happens that some values are references, and some values may be
names (ie. symbols), but in both case, they're values, and it's the
value that's passed to the function).


So, since mylist is always bound to nil when you call hash-to-list,
you always pass nil as second argument, and this is pointless.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2010-09-14 17:46 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <87eicwfpjq.fsf@cecilia.trollope>
2010-09-14 14:13 ` Writing to buffer/file Pascal J. Bourguignon
2010-09-14 14:14 ` Pascal J. Bourguignon
     [not found]   ` <0388da54-19f4-4aaa-90c6-8f6262c4164f@c21g2000vba.googlegroups.com>
2010-09-14 17:46     ` Pascal J. Bourguignon
2010-09-14 14:33 ` Pascal J. Bourguignon

Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.