From: Stefan Nobis
Subject: string to symbol
Date: 
Message-ID: <87fyz45vjo.fsf@snobis.de>
Hi.

I want to read something like a file which contains some
lines. The first or second word on each line represents a command
(and may be followed by some arguments).

So my idea is to read a line, find the position of the command,
add a prefix to this string, convert it to a symbol and then do a
funcall with the symbol. I search a little bit and found something
like this:

  (funcall (find-symbol (string-upcase "hi")))

I know from my experience with imperative languages big case
statements, but i want to avoid these in lisp. So is the above the
right way (a good idea) to solve this problem in lisp? Any hints
to better strategies?

-- 
Stefan.

From: Christopher C. Stacy
Subject: Re: string to symbol
Date: 
Message-ID: <uekeo1lvm.fsf@news.dtpq.com>
Stefan Nobis <······@gmx.de> writes:

> Hi.
> 
> I want to read something like a file which contains some
> lines. The first or second word on each line represents a command
> (and may be followed by some arguments).
> 
> So my idea is to read a line, find the position of the command,
> add a prefix to this string, convert it to a symbol and then do a
> funcall with the symbol. I search a little bit and found something
> like this:
> 
>   (funcall (find-symbol (string-upcase "hi")))
> 
> I know from my experience with imperative languages big case
> statements, but i want to avoid these in lisp. So is the above the
> right way (a good idea) to solve this problem in lisp? Any hints
> to better strategies?

I would name your one-line function:  TROJAN-FUNCALL.

Maybe it would be better if you looked in a more specific place for
the word/function association.  Instead of the namespace of all
function names, you would register valid word-function associations.

And once you've done that, you no longer need to convert the input to
a symbol name.  It can just be a string.  The lookup function can use
a case-insensitive comparison rather than converting the case of the
input string.  The lookup function could also implement some other
lexigraphic smarts about looking up words.

When no function is not found to correspond to the word, you could
signal a condition that can be programmatically handled (or else
just get dumped into the debugger).   Using your own condition,
or at least your own error message, will make your program run in
a much more comprehensible way.  The message, "No function was
found for the word TOWEL" points to the real problem, whereas 
a bare "Undefined function TOWEL called with arguments ()"
is just a mysterious bug.

A good data structure for looking up the words would be a hash table.

To register the words, write a function that takes a string (the word)
and a function (a function name symbol, or an actual function object).
You can have, if your registration function allows it, more than one
mapping from word to function.   Maybe "a", "and", "the" should all
be handled by the same function.

You could then also easily write programs that read input files 
and make new word/function associations.   

You could also write a trivial program that prints out (to a file)
Lisp source code that will re-create the registrations.  This source
code output file would just be a bunch of function calls to the
registration function.  This source file would in effect be a
database, and you could use LOAD (by hand, or programmatically, 
on one or more files) to dynamically make it take effect.

Those are the obvious suggestions that immediately occur to me.

This sounds like a lot more work than what you originally proposed,
but if you go at it, you'll find that it's beginner-level stuff to
write, and not very much code (unlike if you tried to get all this
functionality by implementing it in C++ or Java or whatever).
From: Stefan Nobis
Subject: Re: string to symbol
Date: 
Message-ID: <877jkfyko1.fsf@snobis.de>
······@news.dtpq.com (Christopher C. Stacy) writes:

> I would name your one-line function:  TROJAN-FUNCALL.

I see. :)

But it's only for personal use and to try to better understand CL
(i just started to learn). So security is surley an issue but not
the most important at the moment to me.

> Maybe it would be better if you looked in a more specific place for
> the word/function association.  Instead of the namespace of all
> function names, you would register valid word-function associations.

I thought about this already before my post. Maybe i'm too much
used to think in C++, because this solution looked to me too
inflexible (or a bit too much work for a first try).

But thanks to your suggestions i will play a while with this
idea. Thank you very much for pushing me in the right direction.

-- 
Stefan.
From: Pascal Bourguignon
Subject: Re: string to symbol
Date: 
Message-ID: <87is3zok5k.fsf@thalassa.informatimago.com>
Stefan Nobis <······@gmx.de> writes:

> ······@news.dtpq.com (Christopher C. Stacy) writes:
> 
> > I would name your one-line function:  TROJAN-FUNCALL.
> 
> I see. :)
> 
> But it's only for personal use and to try to better understand CL
> (i just started to learn). So security is surley an issue but not
> the most important at the moment to me.

If security is not a concern, why don't you just put:

    (command argument)
    (command argumnet)

in your file and just use LOAD?

(eg. what emacs does with ~/.emacs)


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
Wanna go outside.
Oh, no! Help! I got outside!
Let me back inside!
From: Stefan Nobis
Subject: Re: string to symbol
Date: 
Message-ID: <871xan4tlq.fsf@snobis.de>
Pascal Bourguignon <····@mouse-potato.com> writes:

> If security is not a concern, why don't you just put:

>     (command argument)
>     (command argumnet)

> in your file and just use LOAD?

The file format ist externally defined. To be more precise: I want
to implement a text based protocol (not defined by me) and at the
same time i'm learning lisp. My first steps are only done to
understand better, how these kinds of problems are (best) solved
in lisp.

I think, given the advices here and the examples of Emacs lisp i
know, i'll go and experiment with hashes and association lists.

-- 
Stefan.
From: Andreas Thiele
Subject: Re: string to symbol
Date: 
Message-ID: <d0p83n$1i0$01$1@news.t-online.com>
"Stefan Nobis" <······@gmx.de> schrieb im Newsbeitrag
···················@snobis.de...
> Hi.
>
> I want to read something like a file which contains some
> lines. The first or second word on each line represents a command
> (and may be followed by some arguments).
>
> So my idea is to read a line, find the position of the command,
> add a prefix to this string, convert it to a symbol and then do a
> funcall with the symbol. I search a little bit and found something
> like this:
>
>   (funcall (find-symbol (string-upcase "hi")))
>
> I know from my experience with imperative languages big case
> statements, but i want to avoid these in lisp. So is the above the
> right way (a good idea) to solve this problem in lisp? Any hints
> to better strategies?
>
> --
> Stefan.

Stefan,

probably these things are so easy in Lisp, maybe you don't see the forest
for the trees.

Example:

Our program contains the following function which will be used from our
command file:

(defun test (x)
  (format t "the argument x is: ~a~%" x))

Now we use the following command file ("demo.dat"):

(test 42)
(test 49)

To execute these commands, we use LOAD:

CL-USER 14 > (load "demo.dat")
; Loading text file C:\Programme\Xanalys\LispWorks\demo.dat
the argument x is: 42
the argument x is: 49
#P"C:/Programme/Xanalys/LispWorks/demo.dat"

OK - this might not be what you wanted, because maybe you hate parentheses
:)
I'd read the file line by line, attache parentheses and evaluate each line.
(But to be honest, I would just type the parentheses in the file and use
load to do the job.)

If our data file ("demo1.dat") is free of parentheses and looks like

test 42
test 49

I'd use the following function to process the data

(defun execute-weird-data ()
  (let (line)
    (with-open-file (f "demo1.dat" :direction :input)
      (loop
        (setf line (read-line f nil nil))
        (unless line (return))
        (eval (read-from-string (concatenate 'string "(" line ")")))))
    "Ready"))

Callings this from the REPL gives:

CL-USER 15 > (execute-weird-data)
the argument x is: 42
the argument x is: 49
"Ready"

Hope this helps.

Andreas
From: Andreas Thiele
Subject: Re: string to symbol
Date: 
Message-ID: <d0p987$ekb$00$1@news.t-online.com>
"Andreas Thiele" <······@nospam.com> schrieb im Newsbeitrag
····················@news.t-online.com...
...
> If our data file ("demo1.dat") is free of parentheses and looks like
>
> test 42
> test 49
>
> I'd use the following function to process the data
>
> (defun execute-weird-data ()
>   (let (line)
>     (with-open-file (f "demo1.dat" :direction :input)
>       (loop
>         (setf line (read-line f nil nil))
>         (unless line (return))
>         (eval (read-from-string (concatenate 'string "(" line ")")))))
>     "Ready"))
>
> Callings this from the REPL gives:
>
> CL-USER 15 > (execute-weird-data)
> the argument x is: 42
> the argument x is: 49
> "Ready"
...

Another idea: To explain the dynamic character of lisp, we can write a
compiler for the "demo1.dat" (non-parentheses) file.
First stage is a precompiler which simply adds the needed parentheses:

(defun precompile-weird-data (base-name)
  (let ((in-name  (concatenate 'string base-name ".dat"))
        (out-name (concatenate 'string base-name ".lsp"))
        line)
    (with-open-file (i in-name :direction :input)
      (with-open-file (o out-name :direction :output :if-exists :supersede)
        (loop
          (setf line (read-line i nil nil))
          (unless line (return))
          (format o "~a~%" (concatenate 'string "(" line ")")))))))

Now the full compiler can be:

(defun compile-data (base-name)
  (precompile-weird-data base-name)
  (compile-file base-name))

At the REPL you'd get

CL-USER 4 : 1 > (compile-data "demo1")
;;; Compiling file demo1 ...
;;; Safety = 3, Speed = 1, Space = 1, Float = 1, Interruptible = 0
;;; Compilation speed = 1, Debug = 2, Fixnum safety = 3
;;; Source level debugging is on
;;; Source file recording is  on
;;; Cross referencing is on
; (TOP-LEVEL-FORM 1)
; (TOP-LEVEL-FORM 2)
; (TOP-LEVEL-FORM 3)
; (TOP-LEVEL-FORM 4)
#P"C:/Programme/Xanalys/LispWorks/demo1.fsl"
NIL
NIL

From now on, you have you file compiled and you can use LOAD on the prior
non-parentheses file

CL-USER 5 : 1 > (load "demo1")
; Loading fasl file C:\Programme\Xanalys\LispWorks\demo1.fsl
the argument x is: 42
the argument x is: 49
#P"C:/Programme/Xanalys/LispWorks/demo1.fsl"

Andreas
From: Stefan Nobis
Subject: Re: string to symbol
Date: 
Message-ID: <87wtsf3enk.fsf@snobis.de>
"Andreas Thiele" <······@nospam.com> writes:

> probably these things are so easy in Lisp, maybe you don't see
> the forest for the trees.

I have this problem more often than not (not only with new
languages). :)

> OK - this might not be what you wanted, because maybe you hate
> parentheses

No, i've no problem with parens. :)

> I'd read the file line by line, attache parentheses and evaluate
> each line.

Hmmm... nice idea. What about performance? I think i'll try this,
too, and compare it with the hash/assoc solution.

I don't like your compile-data example too much, because one have
to remember to recompile the data file after each change (for my
planned use in a protocol parser it's not the right direction
anyway).

Thank you very much for your ideas!

-- 
Stefan.
From: Pascal Costanza
Subject: Re: string to symbol
Date: 
Message-ID: <39auccF5vuoedU1@individual.net>
Stefan Nobis wrote:

> I don't like your compile-data example too much, because one have
> to remember to recompile the data file after each change (for my
> planned use in a protocol parser it's not the right direction
> anyway).

No, you don't have to. Instead of compile-file, you could use compile.

? (defun hi ()
     (princ "Hello, World!"))
HI
? (defvar *code*
     (compile nil '(lambda () (hi))))
*CODE*
? (funcall *code*)
Hello, World!



Pascal
From: Andreas Thiele
Subject: Re: string to symbol
Date: 
Message-ID: <d0pkpj$hk3$01$1@news.t-online.com>
"Stefan Nobis" <······@gmx.de> schrieb im Newsbeitrag
···················@snobis.de...
...
> I don't like your compile-data example too much, because one have
> to remember to recompile the data file after each change (for my
> planned use in a protocol parser it's not the right direction
> anyway).
...
It is just an extremely simple example how you can create embedded languages
and compile them. Recompilation could be done automatically. Just compare
the file dates of the .dat and .fsl file.

Andreas
From: Svein Ove Aas
Subject: Re: string to symbol
Date: 
Message-ID: <d0plh5$bg9$1@services.kq.no>
Stefan Nobis wrote:

> "Andreas Thiele" <······@nospam.com> writes:

>> I'd read the file line by line, attache parentheses and evaluate
>> each line.
> 
> Hmmm... nice idea. What about performance? I think i'll try this,
> too, and compare it with the hash/assoc solution.
> 
You're invoking the compiler (possibly byte-compiler) to run each line. This
will be *slow*.

On the other hand, chances are you're calling the file manually, in which
case the still sub-second delay will matter very little, and the
convenience of using load will matter quite a lot.

It's your call, really.
From: Andreas Thiele
Subject: Re: string to symbol
Date: 
Message-ID: <d0pmej$f0j$02$1@news.t-online.com>
"Stefan Nobis" <······@gmx.de> schrieb im Newsbeitrag
···················@snobis.de...
...
> Hmmm... nice idea. What about performance? I think i'll try this,
> too, and compare it with the hash/assoc solution.
...
OK, you are concerned about performance. Thus I assume you have to evaluate
your file some million times.

I'd suggest the following: read in the file, generate a function
'set-defaults, compile this function. Now you can call set-defaults and
execute a compiled function.

(defun make-resetter ()
  (let (line code)
    (with-open-file (f "demo1.dat" :direction :input)
      (loop
        (setf line (read-line f nil nil))
        (unless line (return))
        (push (read-from-string (concatenate 'string "(" line ")")) code)))
    (setf code (nreverse code))
    (push nil code)
    (push 'set-defaults code)
    (push 'defun code)
    (eval code)) ;; generate function 'set-defaults
  (compile 'set-defaults)) ;; and compile it

CL-USER 9 > (make-resetter)
SET-DEFAULTS
NIL
NIL

Now compilation takes place in memory for example when you start your
application and call 'make-resetter.

CL-USER 13 : 1 > (set-defaults)
the argument x is: 42
the argument x is: 49
NIL

Of course you could create a compiled lambda as well as Pascal pointed out.

Andreas