TheSwamp

Code Red => AutoLISP (Vanilla / Visual) => Topic started by: Kerry on September 27, 2010, 01:25:55 AM

Title: AutoLoad/Demandload for Lisp Files
Post by: Kerry on September 27, 2010, 01:25:55 AM
Code  and Attachment Revised 2010.09.28
Code  and Attachment Revised 2010.09.30

Quote
How do I AutoLoad or demand load lisp files as required. ??
I've seen this question dozens of times in forums,

I needed to make some changes to some really old code today so I thought I'd post it.

The file kdub-Auto-LispLoad.Lsp should be pre-loaded via any means that suits the way you work.

Refer to the comments in the code header.

The following type of code is added to either a .MNL file or any Lisp file that loads automatically.
Code: [Select]

;; Define Stubs to load routine Loaders
(kdub:Auto-LispLoad
  "TestFunFile1"
  '(("NoNoNo") ("TestFun3" 3) ("TestFun1") ("c:TestCmd1") ("TestFun2" 2))
)
(kdub:Auto-LispLoad "TestFun22" '(("TestFun22")))
(kdub:Auto-LispLoad "TestFun33" '(("TestFun33" 1)))
;; < etc>
As you can see the DemandLoader handles c:xx commands, functions with and without parameters and allows for multiple functions in one source file.

In the examples above, the following stubs are auto-generated. into memory as follows ;
Code: [Select]

;;-------------------------------------------------
(Defun Testfun1 (/ Qfn)
  (If (Setq Qfn (Kdub:Auto-Lispload_Findqualifiedfile "Testfunfile1"))
    (Progn
      (Princ "\nInitializing...\n")
      (Setq Testfun1 Nil)
      (Load Qfn)
    )
    (Progn
       (Kdub:Auto-Lispload_Nofile "\"Testfunfile1\"")
       (Setq Testfun3 Nil)
    )    
  )
  (If  Testfun1
    (Eval (Testfun1))
    (Prompt (Strcat "\nerror : Unable To Evaluate " "Testfun1" "\n"))
  )
)
;;
(Defun Testfun2 (Arg0 Arg1 / Qfn)
  (If (Setq Qfn (Kdub:Auto-Lispload_Findqualifiedfile "Testfunfile1"))
    (Progn
      (Princ "\nInitializing...\n")
      (Setq Testfun2 Nil)
      (Load Qfn)
    )
    (Progn
       (Kdub:Auto-Lispload_Nofile "\"Testfunfile1\"")
       (Setq Testfun3 Nil)
    )    
  )
  (If Testfun2
    (Eval (Testfun2 Arg0 Arg1))
    (Prompt (Strcat "\nerror : Unable To Evaluate " "Testfun2" "\n"))
  )
)
;;
(Defun Testfun3 (Arg0 Arg1 Arg2 / Qfn)
  (If (Setq Qfn (Kdub:Auto-Lispload_Findqualifiedfile "Testfunfile1"))
    (Progn
      (Princ "\nInitializing...\n")
      (Setq Testfun3 Nil)
      (Load Qfn)
    )
    (Progn
      (Kdub:Auto-Lispload_Nofile "\"Testfunfile1\"")
      (Setq Testfun3 Nil)
    )      
  )
  (If Testfun3
    (Eval (Testfun3 Arg0 Arg1 Arg2))
    (Prompt (Strcat "\nerror : Unable To Evaluate " "Testfun3" "\n"))
  )
)
;;
(Defun C:Testcmd1 (/ Qfn)
  (If (Setq Qfn (Kdub:Auto-Lispload_Findqualifiedfile "Testfunfile1"))
    (Progn
      (Princ "\nInitializing...\n")
      (Setq C:Testcmd1 Nil)
      (Load Qfn)
    )
    (Progn
      (Kdub:Auto-Lispload_Nofile "\"Testfunfile1\"")
      (Setq Testfun3 Nil)
    )      
  )
  (If  C:Testcmd1
    (Eval (C:Testcmd1))
    (Prompt (Strcat "\nerror : Unable To Evaluate " "C:Testcmd1" "\n"))
  )
)
;;

;; and this one should spit the dummy because the function is not defined in the file.
;;-------------------------------------------------
(Defun Nonono (/ Qfn)
  (If (Setq Qfn (Kdub:Auto-Lispload_Findqualifiedfile "Testfunfile1"))
    (Progn
      (Princ "\nInitializing...\n")
      (Setq Nonono Nil)
      (Load Qfn)
    )
    (Progn
       (Kdub:Auto-Lispload_Nofile "\"Testfunfile1\"")
       (Setq Testfun3 Nil)
    )    
  )
  (If Nonono
    (Eval (Nonono))
    (Prompt (Strcat "\nerror : Unable To Evaluate " "Nonono" "\n"))
  )
)
;;-------------------------------------------------

Any subsequent use of any of these functions/Commands will Auto-load the correct source file and over-write the stub definition with the full definition from the file.

This can be tested by unzipping the sample files into a folder on your search path and progressively running each of these statements.

Code: [Select]

(vl-load-com)

;;Test .. ensure the Functions are not defined ... just for this test
(setq TestFun1 nil
      TestFun2 nil
      TestFun3 nil      
      TestFun22 nil
      c:TestCmd1 nil
      TestFun33 nil
      nonono nil
)
;; Define Stubs to load routine Loaders
(kdub:Auto-LispLoad
  "TestFunFile1"
  '(("NoNoNo") ("TestFun3" 3) ("TestFun1") ("c:TestCmd1") ("TestFun2" 2))
)
(kdub:Auto-LispLoad "TestFun22" '(("TestFun22")))
(kdub:Auto-LispLoad "TestFun33" '(("TestFun33" 1)))
;; If you highlight and right click on the functionName in the VLIDE then select [b]Inspect...[/b] you'll see that the functioin is defined.
;; Something like this will be returned  #<USUBR @0000000035984bd8 TESTFUN2>

;; Run a routine

(TestFun1)
(TestFun2 100 "Apples")
(setq mmage 82)
(TestFun3 "Mickey" "Mouse" mmage)
(c:TestCmd1)

(nonono)

(TestFun22)
(TestFun33 2)
;;-------------------------------------------------


... you'll see that each file is loaded as any function in it is required. Further calls to that function (or any others in the file) are not reloaded because the stub definition has been over-written by the full definition.


kdub-Auto-LispLoad.LSP
Code: [Select]

(vl-load-com)
;;-------------------------------------------------
;;-------------------------------------------------
(defun kdub:Auto-LispLoad_FindQualifiedFile (f / fn)
  ;; CodeHimBelonga kdub 2001.07.28
  ;; Revised 2010.09.29
  (if (or (setq fn (findfile f))
          (setq fn (findfile (strcat f ".VLX")))
          (setq fn (findfile (strcat f ".FAS")))
          (setq fn (findfile (strcat f ".LSP")))
      )
    (vl-string-translate "\\" "/" fn)    
  )
)
;;-------------------------------------------------
(defun kdub:Auto-LispLoad_NoFile (FileName)
  ;; CodeHimBelonga kdub 2001.07.28
  ;; Revised 2010.09.27
  (alert
    (strcat
      "The requested file " FileName " "
      (if (not (vl-filename-extension FileName))
        "(.lsp/.fas/.vlx) "
      )
      "\nwas not found in the Profile search path folders.\n"
      "\nPlease check the installation of the support files"
      "\nand 'Options->Files->Path."
    )
  )
  (princ)
)

;;-------------------------------------------------
;;-------------------------------------------------

(defun kdub:Auto-LispLoad (FileName ParamList /)
  ;; CodeHimBelonga kdub 2001.07.28
  ;; based on sample code from VitalLisp by Basis Software circa 1999
  ;; Revised 2010.09.27
  ;; Updated [Build:2010.09.29-2] (bugs and redundancies)
  ;;-------------------------------------------------
  ;; <fileName> - either
  ;;         fully qualified Path/Name/extension
  ;;             or Path/Name {no ext}
  ;;             or Name      {no ext}
  ;; <ParamList> a list of lists representing :-
  ;;             1) A cons List of FunctionName string  and Argument Count
  ;;                eg: ("TestFun2" 2)
  ;;             2) A list containing the FunctionName string
  ;;                eg: ("TestFun3" )
  ;; Example : (kdub:Auto-LispLoad "TestFunFile1"
  ;;                              '(("TestFun3" 3) ("TestFun1")  ("c:TestCmd1")))
  ;;-------------------------------------------------
  ;;
  (Setq FileName (strcat "\"" (vl-string-translate "\\" "/" FileName) "\""))
  ;;
  (mapcar
    (function
      (lambda (_cmd / CommandName a ArgS)
        (setq CommandName (Read (car _cmd))
              a           -1
              ArgS        '()
        )
        (if (not (vl-symbol-value CommandName))
          (progn
            (if (setq argsQty (cadr _cmd))
              (setq Args
                     (reverse
                       (While (< (Setq a (1+ a)) argsQty)
                         (Setq Args (cons (read (Strcat "Arg" (Itoa a))) Args))
                       )
                     )
              )
            )
            (eval
              (list
                'defun
                CommandName
                (append Args '(/ qfn))
                (list
                  'if
                  (list
                    'setq
                    'qfn
                    (list 'kdub:Auto-LispLoad_FindQualifiedFile (read FileName))
                  )
                  (list 'progn
                        '(princ "\nInitializing...\n")
                        (list 'setq CommandName 'nil)
                        (list 'load 'qfn)
                  )
                  ;; stub else
                  (list 'progn
                        (list 'kdub:Auto-LispLoad_NoFile FileName)
                        (list 'setq CommandName 'nil)
                  )
                )
                (list 'if
                      CommandName
                      (list 'eval (cons CommandName Args))
                      ;; stub else
                      (list 'prompt
                            (list 'strcat
                                  "\nError : Unable to evaluate "
                                  (vl-prin1-to-string CommandName)
                                  "\n"
                            )
                      )
                )
              )
            )
          )
        )
      )
    )
    ParamList
  )
  (princ)
)
;;-------------------------------------------------
;;-------------------------------------------------
(princ)

[edit:kdub] Attachment Revised 2010.09.28
[edit:kdub] Attachment Revised 2010.09.30  [Build:2010.09.29-2]
Title: Re: AutoLoad/Demandload for Lisp Files
Post by: xiaxiang on September 27, 2010, 02:26:13 AM
It's what I want.
Seriously study your routine.
Title: Re: AutoLoad/Demandload for Lisp Files
Post by: CAB on September 27, 2010, 08:41:24 AM
The arguments are a nice feature, well done. 8-)
Title: Re: AutoLoad/Demandload for Lisp Files
Post by: Kerry on September 27, 2010, 02:30:21 PM

Thanks Alan, I like it too.

//----------

Anyone have any problems with the functionality or with the code ??
Title: Re: AutoLoad/Demandload for Lisp Files
Post by: dgorsman on September 27, 2010, 03:03:18 PM
Depending on whats being loaded, the ...FindQualified file might be better off reorganized in the order of LSP/FAS/VLX to reduce the calls to (findfile...).  In my experience many sequential calls to (findfile...) can cause noticeable delays, so I would be tempted to completely ignore the file search if a fully qualified filename is passed, and let the (load...) error handling take care of things if its not a valid file.  Then again, doesn't (load...) automatically consider the standard AutoCAD search paths?
Title: Re: AutoLoad/Demandload for Lisp Files
Post by: Kerry on September 27, 2010, 03:18:31 PM

Quote
so I would be tempted to completely ignore the file search if a fully qualified filename is passed,

It pretty much does .. the qualified search is first, and it only takes a couple of ticks. I'd rather pass all code through the _FindQualifiedFile than try to determine if the FileName IS actually a qualified path ... and we have the bonus of confirming the file exists, even though in a production environment it SHOULD :)

The process is actually pretty quick.

Regarding the order for the extension search I've opted to have the fast loading files searched for first in the belief that once a code file is stable most people would compile the code so it loads faster and is less likely to be accidently modified.

Thanks for the comments
Title: Re: AutoLoad/Demandload for Lisp Files
Post by: Lee Mac on September 27, 2010, 06:50:25 PM
I shall definitely study the code when I get a minute, thanks for sharing Kerry as always.
Title: Re: AutoLoad/Demandload for Lisp Files
Post by: Kerry on September 27, 2010, 07:13:57 PM
There's a bug in there ..

If the function does not exist in the loaded file the stub doesn't get overwritten when the file is loaded, so we end up with the calls going around in ever-decreasing circles.

I'll have a look at it when I get a chance and update the code.

This won't affect definitions that are set up with due diligence.
Title: Re: AutoLoad/Demandload for Lisp Files
Post by: Kerry on September 27, 2010, 09:03:29 PM
Code  and Attachment in first post Revised 2010.09.28

bug fixed.
If function is not in loaded file the stub code handles the situation

Code was
 
Code: [Select]
               (eval
                  (list 'defun
                        CommandName
                        (append Args '(/ qfn))
                        (list 'if
                              (list 'setq
                                    'qfn
                                    (list 'kdub:Auto-LispLoad_FindQualifiedFile
                                          (read FileName)
                                    )
                              )
                              (list 'progn
                                    '(princ "\nInitializing...\n")
                                    (list 'load 'qfn)
                              )
                              (list 'kdub:Auto-LispLoad_NoFile FileName)
                        )
                        (list 'eval (cons CommandName Args))
                  )
                )

now :
Code: [Select]
                (eval
                  (list 'defun
                        CommandName
                        (append Args '(/ qfn))
                        (list 'if
                              (list 'setq
                                    'qfn
                                    (list 'kdub:Auto-LispLoad_FindQualifiedFile
                                          (read FileName)
                                    )
                              )
                              (list 'progn
                                    '(princ "\nInitializing...\n")
                                    (list 'setq CommandName 'nil)
                                    (list 'load 'qfn)
                              )
                              (list 'kdub:Auto-LispLoad_NoFile FileName)
                        )
                        (list 'if
                              (list 'eval CommandName)
                              (list 'eval (cons CommandName Args))
                              (list 'prompt
                                    (list 'strcat
                                          "\nError : Unable to evaluate "
                                          (vl-prin1-to-string CommandName)
                                          "\n"
                                    )
                              )
                        )
                  )
                )
Title: Re: AutoLoad/Demandload for Lisp Files
Post by: litss on September 28, 2010, 01:20:12 AM
Is that possible to load merely some functions in a vlx file when I need them, not the whole file? There're some shared vlx files on the internet. Sometimes I need to call for a small part of a special vlx, but don't want to load it all, for there will be some unexpected conflicts with my own routines or subs or variables... 

Title: Re: AutoLoad/Demandload for Lisp Files
Post by: Kerry on September 28, 2010, 01:49:39 AM
Is that possible to load merely some functions in a vlx file when I need them, not the whole file? There're some shared vlx files on the internet. Sometimes I need to call for a small part of a special vlx, but don't want to load it all, for there will be some unexpected conflicts with my own routines or subs or variables... 



No, not possible, sorry.
Title: Re: AutoLoad/Demandload for Lisp Files
Post by: Krushert on September 28, 2010, 03:49:19 PM
**Sigh**  :cry:

So much to learn and no time to learn it!  If I had the time, I could make use of this. 
Thanks for sharing Kerry.  I will try to remember this boulder of information.
Title: Re: AutoLoad/Demandload for Lisp Files
Post by: Lee Mac on September 28, 2010, 05:20:59 PM
Kerry,

Won't this error?

Code: [Select]
(nil)
Why not omit it (an 'else' expression) completely to return nil?

Also, why do you use a foreach loop, then a mapcar within this foreach loop? Is this not iterating the same data a number of times equal to the length of the 'paramlist'?

One more thing, couldn't

Code: [Select]
(if (eval CommandName)
...
)

Be replaced with:

Code: [Select]
(if CommandName
...
)
Title: Re: AutoLoad/Demandload for Lisp Files
Post by: Kerry on September 28, 2010, 06:58:48 PM
Kerry,

Won't this error?

Code: [Select]
(nil)

Yes, remove the (nil) in your build .. I'll update


One more thing, couldn't

Code: [Select]
(if (eval CommandName)
...
)
Be replaced with:
Code: [Select]
(if CommandName
...
)

Yes it could.


Also, why do you use a foreach loop, then a mapcar within this foreach loop? Is this not iterating the same data a number of times equal to the length of the 'paramlist'?


yes, that (the complete foreach statement)  is redundant
Remove the foreach statement and it's matching parenthesis

I was going to look at parts again because there is some other redundancy ..
and I haven't yet bomb-proofed all branches ...
For instance;
In the Stub, The CommandName variable needs to be set to nil if the file is not found.
The Progn following the (lambda (_cmd / CommandName a ArgS) is not mandatory


These issues are to be (sort of) expected when modifying old code and not testing all branches fully.
Thanks for having a look.
Title: Re: AutoLoad/Demandload for Lisp Files
Post by: Kerry on September 28, 2010, 09:23:03 PM
This is the current auto-generated stub.
I'll have another play tonight before posting the revised DemandLoad generator

Code: [Select]

[color=teal];; Stub produced in memory by the DemandLoader to AutoLoad Code file when required.[/color]
[color=teal];;[/color]
[color=navy]([/color][color=blue]Defun[/color] Testfun3 [color=navy]([/color]Arg0 Arg1 Arg2 [color=blue]/[/color] Qfn[color=navy])[/color]
  [color=navy]([/color][color=blue]If[/color] [color=navy]([/color][color=blue]Setq[/color] Qfn [color=navy]([/color][color=blue]Kdub:Auto-Lispload_Findqualifiedfile[/color] [color=brown]"Testfunfile1"[/color][color=navy]))[/color]
    [color=navy]([/color][color=blue]Progn[/color]
      [color=navy]([/color][color=blue]Princ[/color] [color=brown]"\nInitializing...\n"[/color][color=navy])[/color]
      [color=navy]([/color][color=blue]Setq[/color] Testfun3 Nil[color=navy])[/color]
    [color=navy]([/color][color=blue]Load[/color] Qfn[color=navy])[/color]
    [color=navy])[/color]
    [color=navy]([/color][color=blue]Progn[/color]
      [color=navy]([/color][color=blue]Kdub:Auto-Lispload_Nofile[/color] [color=brown]"\"Testfunfile1\""[/color][color=navy])[/color]
      [color=navy]([/color][color=blue]Setq[/color] Testfun3 Nil[color=navy])[/color]
    [color=navy])[/color]
  [color=navy])[/color]
  [color=navy]([/color][color=blue]If[/color] Testfun3
    [color=navy]([/color][color=blue]Eval[/color] [color=navy]([/color][color=blue]Testfun3[/color] Arg0 Arg1 Arg2[color=navy]))[/color]
    [color=navy]([/color][color=blue]Prompt[/color] [color=navy]([/color][color=blue]Strcat[/color] [color=brown]"\nError : Unable To Evaluate "[/color] [color=brown]"Testfun3"[/color] [color=brown]"\n"[/color][color=navy]))[/color]
  [color=navy])[/color]
[color=navy])[/color]
Title: Re: AutoLoad/Demandload for Lisp Files
Post by: Lee Mac on September 29, 2010, 05:02:35 AM
These issues are to be (sort of) expected when modifying old code and not testing all branches fully.
Thanks for having a look.

No worries, just giving you the heads up  :-)
Title: Re: AutoLoad/Demandload for Lisp Files
Post by: Kerry on September 29, 2010, 05:00:17 PM
Code  and Attachment in first post Revised 2010.09.30 [Build:2010.09.29-2]

Modified as noted in threads

Main stub evaluator is now :
Code: [Select]
            (eval
              (list
                'defun
                CommandName
                (append Args '(/ qfn))
                (list  'if (list 'setq 'qfn
                    (list 'kdub:Auto-LispLoad_FindQualifiedFile (read FileName))
                  )
                  (list 'progn
                        '(princ "\nInitializing...\n")
                        (list 'setq CommandName 'nil)
                        (list 'load 'qfn)
                  )
                  ;; stub else
                  (list 'progn
                        (list 'kdub:Auto-LispLoad_NoFile FileName)
                        (list 'setq CommandName 'nil)
                  )
                )
                (list 'if
                      CommandName
                      (list 'eval (cons CommandName Args))
                      ;; stub else
                      (list 'prompt
                            (list 'strcat
                                  "\nError : Unable to evaluate "
                                  (vl-prin1-to-string CommandName)
                                  "\n"
                            )
                      )
                )
              )
            )

which generates a stub similar to my previous  code post.