What's wrong with this ? (unexpected stack overflow)

Started by Maurizio, February 07, 2005, 01:32:08 AM

Previous topic - Next topic

Maurizio

the  following program gives an overflow error

I'm using vers 8.3.6 on win2k

Regards

Maurizio





(define (tostring argos)
  (join (map eval argos) " "))
 
(define-macro (packleft)
  (tk "pack " (tostring (args))))
 
(define (test)
  (tk "toplevel .main")
  (set 'aname  (tk "button .main.1"))
  (set 'asecond (tk "button .main.2"))
  (packleft aname asecond))
 
(test)




call stack overflow in function eval : tostring

called from user defined function tostring

called from user defined function tostring ...[/code]

Lutz

#1
The 'tk' macro gets the expression '(tostring (args))' as an argument and tries to evaluate it inside the 'tk' with an '(map eval (args))' which in turn calls the 'tostring' function' which now does a '(map eval argos)' where argos contains the 'tostring' function again. What happens now is the 'tostring' function calling itself over and over again recursevely. You can fix the code changing the second macro

(define-macro (packleft)
  (let (p (tostring (args)))
    (tk "pack " p)))


Lutz

Maurizio

#2
Thanks, I'll do that.

Anyway I suppose there is something wrong about it.

I'm required to know the internal details of any macros

before I'm  able to use them ?

What if I use someone other's code ?

Perhaps simply  I cannot  use directly any of my macros as a parameter

to someone other's code ?



Regards

Maurizio

Lutz

#3
One should never use (args) in an argument to another macro call. I will mention this in the manual.



Lutz

eddier

#4
I discovered this accidently Lutz. I have a bunch of html macros that can take any number of arguments of the sort  (a href="blahblah" style="blahblah" "Goto blahblah"). I wanted to embed these in some more macros for auto generating documentation. SHEEZE! I gave up on it. I was able to do something similar in mzscheme, since multiple arguments are named after the ".", for example (define (f x . v) ...) where v is a list of arguments. I know that newLISP doesn't have a dotted pair but there should be a way to do multiple args on a (define ) or be able to pass them on to more macros. Note that multiple arguments on a define would be nice since all of the arguments would be evaluated before entering the function.



Eddie

Lutz

#5
The (args) problem in macros is a variation of the hygenic macros problem all LISPs have. (args) simply means something different by definition in each different macro similar to passing a symbol, so when it is passed as an argument it gets a different meaning in the called routines. The only work-around for define would be to supply multiple parameters and test inside the define for 'nil":



(define (foo p1 p2 p3 p4 ...)

   (if p1 ...)

   ...

)



The other possibility would be to pass parameters evaluated in a list:



(define (foo lst)

  (dolist (e lst) ...))



(foo (list x y z))



The way (args) is implemented in macros now is not hygenic but very fast and efficient. To implement it in 'define' in a hygenic fashion would mean too much additional overhead for evaluating lambda expressions. There are Schemes with hygenic macros but they are very slow. Its the old trade off of doing things safe or efficient ;(.



I added a comment to the description of (args) in the documentation warning against this problem.



Lutz

eddier

#6
I thought about using the second possibility you gave. Unfortunately, I would have to rewrite many of my current cgi programs to take (:a '(href="blahblah" style="blahblah") "blahblah"). Not bad notation, but I don't want to rewrite my cgi programs. I should have used this as the solution in the first place.  I just thought it would be cool to write

(:p color="blue" ... style="text-align:center" ...  "hello" " world" ...).



Yes on the hygenic macros. The reason I could get around the multi args problem on mzscheme was that I was using a regular function with multiple args and not a macro. The reason this worked was because all of the arguments got evaluated before the function was called. Ie

(:div style="background:blue"
  (:p style="text-align:center"
    "hello"
    (:br)
    "world"))

(:br) got evaluated before (:p ...) before (:div ...).



Eddie

Lutz

#7
With only little change I could bring the (args) functionality into normal functions and without affecting performance in newlisp-8.4.2.tgz



The way (args) works has changed for the case that the maco or function specifies locals variable and uses (args) to retrieve the argument list. In this case (args) only returns the unbound arguments not already taken by local vars:



(define (foo) (args))



(foo 1 2 3 4 5)  => (1 2 3 4 5)



;; but



(define (foo a b) (args))



(foo 1 2 3 4 5) => (3 4 5)     ; only args not already bound to locals



This makes it necessary to rewrite all macros having both, locals vars and use (args). In most cases the changes will lead to simpler code, as normally (args) is only wanted for the unbound arguments not already passed by local vars. I.e. this changes the definition of 'defun' and 'edit' in init.lsp. In some instances macros are not necessary at all, when only used to get the (args) functionality which is now also available in normal functions.



See also changed doc of 'args' in the manual. Look for the files in the development directoy http://newlisp.org/downloads/development">http://newlisp.org/downloads/development



This development release contains also many fixes/changes in the manual, doing a complete pass through all built-in function descriptions.



If possible I want to make this version a release next week.



Lutz

eddier

#8
I believe this is a good change since I have been miss-using the macro facility to define what should be functions that use multiple arguments. This of course has led to in some cases, a brick wall.



I would rather change the macro definitions in my libraries than change all of my cgi programs.



Thanks for all of your hard work!



Eddie

Maurizio

#9
Great little change !

thank you very much.

Maurizio