Hi,
I'm trying to write a very simple utility to parse a config file of
this format (more on the format later):
-name
"<html><body>some HTML code.."
The goal is to associate "name" with the code that follows it. The
config file will only be a series of these names with their HTML
snippets.
I'm having trouble finding a neat way to do this. This is my
initial attempt:
(defparameter *tag-to-content* nil)
(defun read-config (filename)
(let* ((config-path (make-pathname :name filename))
(a-tag "-"))
(with-open-file (config-in config-path :direction :input)
(let* ((line (read-line config-in nil 'eof)))
(if (string= a-tag (aref line 0))
(progn
;;
;; I thought it would be silly to have yet another let
;; here, but I don't need tag-name and tag-content
;; to be global.
;;
(setf tag-name (subseq line 1))
(setf tag-content (read-line config-in nil 'eof))
(print (cons (cons tag-name tag-content)
*tag-to-content*))))))))
(read-config "site.config")
This "works" because it prints
(("name" . "\"<html><body>some HTML code..\""))
But it has many problems that I don't know how to solve, the most
important one being how would I go about handling more than one
-name label neatly, (I'm sure there are much cleaner ways than my
pathetic hack) and how would I neatly handle HTML snippets that span
over more than one line?
And finally, I've been hearing a lot that one of Lisp's great
advantages is that it can be very easy to create "sub" languages for
it to parse that (usually?) have Lisp-like syntax. Because of this,
I initially wanted my configuration file to look like this:
(define-label name "HTML snippet here")
But I have no idea how to work with that. I thought maybe I could
defun the define-label function in my code and then use (read)
instead of read-line but I doubt that's the way--regardless, is this
approach better? And assuming I use it, what elements of the
language do I have to look at to go about parsing it?
Thanks a lot for the help, sorry for asking so many questions at once :),
--
Jordan Katz <····@underlevel.net> | Mind the gap
Jordan Katz <····@underlevel.net> writes:
> And finally, I've been hearing a lot that one of Lisp's great
> advantages is that it can be very easy to create "sub" languages for
> it to parse that (usually?) have Lisp-like syntax. Because of this,
> I initially wanted my configuration file to look like this:
>
> (define-label name "HTML snippet here")
>
> But I have no idea how to work with that. I thought maybe I could
> defun the define-label function in my code and then use (read)
> instead of read-line but I doubt that's the way--regardless, is this
> approach better? And assuming I use it, what elements of the
> language do I have to look at to go about parsing it?
Why don't you simply do something like
(defvar *config* '((foo . "blark")
(bar . "quux")))
(defun save-config (filename)
(with-open-file (str filename
:direction :output
:if-exists :supersede)
(pprint *config* str)))
(defun load-config (filename)
(with-open-file (str filename)
(setq *config* (read str))))
Regards,
--
Nils Goesche
Ask not for whom the <CONTROL-G> tolls.
PGP key ID #xC66D6E6F
Nils Goesche <···@cartan.de> writes:
> Jordan Katz <····@underlevel.net> writes:
>
> > And finally, I've been hearing a lot that one of Lisp's great
> > advantages is that it can be very easy to create "sub" languages for
> > it to parse that (usually?) have Lisp-like syntax. Because of this,
> > I initially wanted my configuration file to look like this:
> >
> > (define-label name "HTML snippet here")
> >
> > But I have no idea how to work with that. I thought maybe I could
> > defun the define-label function in my code and then use (read)
> > instead of read-line but I doubt that's the way--regardless, is this
> > approach better? And assuming I use it, what elements of the
> > language do I have to look at to go about parsing it?
>
> Why don't you simply do something like
>
> (defvar *config* '((foo . "blark")
> (bar . "quux")))
>
> (defun save-config (filename)
> (with-open-file (str filename
> :direction :output
> :if-exists :supersede)
> (pprint *config* str)))
>
> (defun load-config (filename)
> (with-open-file (str filename)
> (setq *config* (read str))))
Because the configuration file is written by a regular user with a
text editor, not from the program itself. I want someone to be able
to save site.config and have my program grok it.
Thanks a lot,
--
Jordan Katz <····@underlevel.net> | Mind the gap
"Jordan Katz" <····@underlevel.net> wrote in message
···················@underlevel.underlevel.net...
> Because the configuration file is written by a regular user with a
> text editor, not from the program itself. I want someone to be able
> to save site.config and have my program grok it.
>
> Thanks a lot,
Why is it easier for the user to type
-name
"<html><body>some HTML code.."
instead of
(name "<html><body>some HTML code..")
?
The first method above is full of potential errors that your parsing
routines have to account for, the second (Lisp) format is fully protected by
the reader's error detection.
Basically I am saying, why make your life difficult when it does not matter
to the user?
Wade
Can your user find these keys?: ( . ) "
<g>
--
kenny tilton
clinisys, inc
---------------------------------------------------------------
"Harvey has overcome not only time and space but any objections."
Elwood P. Dowd
Jordan Katz <····@underlevel.net> writes:
> Nils Goesche <···@cartan.de> writes:
>
> > Why don't you simply do something like
> >
> > (defvar *config* '((foo . "blark")
> > (bar . "quux")))
> >
> > (defun save-config (filename)
> > (with-open-file (str filename
> > :direction :output
> > :if-exists :supersede)
> > (pprint *config* str)))
> >
> > (defun load-config (filename)
> > (with-open-file (str filename)
> > (setq *config* (read str))))
>
> Because the configuration file is written by a regular user with a
> text editor, not from the program itself. I want someone to be able
> to save site.config and have my program grok it.
Well, you could READ in seperate forms in a loop then; look at
the car of each to know what kind of form it is and interpret it
somehow; where is the problem?
Regards,
--
Nils Goesche
Ask not for whom the <CONTROL-G> tolls.
PGP key ID #xC66D6E6F
Jordan Katz <····@underlevel.net> writes:
> (defparameter *tag-to-content* nil)
Consider whether you need a global variable to keep track of this. You
could just accumulate this internally in your function and return the
result.
> (defun read-config (filename)
> (let* ((config-path (make-pathname :name filename))
This is cute and our resident pathname spec debaters will love you for
it but with-open-file can take a string and will do the pathname
parsing by itself.
> (a-tag "-"))
> (with-open-file (config-in config-path :direction :input)
> (let* ((line (read-line config-in nil 'eof)))
let* is a bit overkill here since you only introduce one
variable. It's ok though, there are reasons why some people take let*
as the default of the let/let* pair.
> (if (string= a-tag (aref line 0))
This works, but I guess you don't realize why. You compare a string of
length one to a character, and the string comparison functions will
convert a character to a string of length one. You could use char= and
change your definition of a-tag to match or use the :end2 keyword
argument of string= to keep it correct if you define a-tag to be "+++"
e.g.
> (progn
> ;;
> ;; I thought it would be silly to have yet another let
> ;; here, but I don't need tag-name and tag-content
> ;; to be global.
> ;;
> (setf tag-name (subseq line 1))
> (setf tag-content (read-line config-in nil 'eof))
This assigns to variables you haven't defined. In most implementations
it will create special (global) variables but it's not a good
idea. You could use PROG to create those scoped variables, to get an
oldtimer feel to your code, introduce a let or just put them in the
let* that created line. In that case you might want to use WHEN
instead of IF to be able to use multiple forms in the implicit progn.
> (print (cons (cons tag-name tag-content)
> *tag-to-content*))))))))
>
> (read-config "site.config")
This gives us
(defun read-config (filename)
(let* ((a-tag #\-)
(tag-to-content '()))
(with-open-file (config-in filename :direction :input)
(let* ((line (read-line config-in nil 'eof))
(tag-name "")
(tag-content ""))
(when (char= a-tag (aref line 0))
(setf tag-name (subseq line 1))
(setf tag-content (read-line config-in nil 'eof))
(push (cons tag-name tag-content) tag-to-content))))
tag-to-content))
> This "works" because it prints
>
> (("name" . "\"<html><body>some HTML code..\""))
>
> But it has many problems that I don't know how to solve, the most
> important one being how would I go about handling more than one
> -name label neatly, (I'm sure there are much cleaner ways than my
> pathetic hack) and how would I neatly handle HTML snippets that span
> over more than one line?
Just start looping over the above. Since I'm not sure whether this is
homework, I'll only give you a hint. loop reading the file line by
line, if it starts a new tag, output the previously saved name +
collected content, otherwise add the line to the content being
collected.
> And finally, I've been hearing a lot that one of Lisp's great
> advantages is that it can be very easy to create "sub" languages for
> it to parse that (usually?) have Lisp-like syntax. Because of this,
> I initially wanted my configuration file to look like this:
>
> (define-label name "HTML snippet here")
>
> But I have no idea how to work with that. I thought maybe I could
> defun the define-label function in my code and then use (read)
> instead of read-line but I doubt that's the way--regardless, is this
> approach better? And assuming I use it, what elements of the
> language do I have to look at to go about parsing it?
You would typically compile the configuration file and defining
define-label as a macro.
(defvar *label-definitions* '())
(defmacro define-label (name value)
`(push (cons ,name ,value) *label-definitions*))
--
Lieven Marchand <···@wyrd.be>
She says, "Honey, you're a Bastard of great proportion."
He says, "Darling, I plead guilty to that sin."
Cowboy Junkies -- A few simple words
Lieven Marchand <···@wyrd.be> writes:
[snip other comments]
> You would typically compile the configuration file and defining
> define-label as a macro.
>
> (defvar *label-definitions* '())
>
> (defmacro define-label (name value)
> `(push (cons ,name ,value) *label-definitions*))
That's initially what I was thinking of, and I was under the
misconception that read evaluates the object it reads, so
(define-label foo "bar") in the configuration would call (define-label
...) in my source, but that's clearly not the case.
Thanks for your other comments and for everyone else who responded, I
now have a solution to my problem.
--
Jordan Katz <····@underlevel.net> | Mind the gap