Created an include script

Started by hotcore, February 29, 2016, 07:38:01 AM

Previous topic - Next topic

hotcore

I discovered the hard way that, when creating a Windows executable, only the 1 script mentioned will be part of that executable, so (load "script.lsp") will only be satisfied at run time of the exe, and not at generate time.



Because I wanted to create executables having (load ...) in them, I created a script which

     1) copies each input line of a given input script to an output file except when the first thing on the line is "(load ". In that case the contents of the lsp file in the "(load " will be written to the output

     2) start NLINC with only the script name (no path and no extension)

     3) the work should be done in the source directory (in my case all sources in one dir) see Windows script



Updates per 20160301:

- some error handling added

- avoiding recursive includes now



The way it works I now can use (load ...) in my scripts and use them as a normal script or as an (expanded) script turned into an executable.



Any comments and remarks are welcome



Regretting now to have been side tracted from newLISP. It is a pleasure writing code in!



Thx,

   Arie

 

Code of newLISP script:
; ################################################################################
; # NLINC
; #
; # Purpose:
; #    - read a newLISP script xxx.lsp
; #    - if there are LOAD functions (on the utter left of a line)
; #      these will be replaced by the loaded MEMBER itself (INCLUDED)
; #    - output will be written to xxx-i.lsp
; ################################################################################

(define (mainargs , osargs srch)
(setq osargs (main-args))
(setq srch   (lower-case (osargs 0)))
(if (or (= srch "newlisp") (= srch "newlisp.exe"))
(2 osargs)
(1 osargs)))

(define (check-include line , incmem)
(setq line (lower-case (trim line)))
(if (!= (0 6 line) "(load ")
(setq imcmem nil)
(begin
(setq line (6 line))
(setq incmem ((parse line {"}) 1))))
incmem)

(define (write-incmem incmem out , memfile)
(setq memfile (open incmem "read"))
(write-line out (append "; NLINC: member " incmem " included here") )
(while (read-line memfile)
(write-line out (current-line)))
(close memfile)
)

(define (pass , in out incmem extra-pass)
(setq in        (open wf1 "read"))
(setq out       (open wf2 "write"))
(setq extra-pass nil)
(println "NLINC: entering pass " (inc passnum) " ...")
(while (read-line in)
(setq line (current-line))
(setq incmem (check-include line))
(cond ((nil? incmem) (write-line out line))
((find incmem inclist) (write-line out (append "; " line)))
(true
(push incmem inclist)
(write-incmem incmem out)
(setq extra-pass true))))
(close in)
(close out)
extra-pass)

(define (nlinc , extra-pass scriptname script wf1 wf2 tmp)
(setq extra-pass true)
(setq scriptname ((mainargs) 0))
(setq script     (append scriptname ".lsp"))
(setq wf1        (append scriptname "-i1.lsp"))
(setq wf2        (append scriptname "-i2.lsp"))
(setq scriptout  (append scriptname "-i.lsp"))
(setq swapped    nil)
(setq inclist    '())
(setq passnum    0)
(setq tmp        "")
(if (not (file? script))
(begin
(println "NLINC: you may only specify the name part of the script")
(println "       without path and extension!")
(exit 999)))
(println {NLINC: processing started for script "} script {"})
(copy-file script wf1)
; Loop over the script and include
; If any script included, repeat until nothing included
; Avoid duplicate includes
(while extra-pass
(setq extra-pass (pass wf1 wf2))
(setq tmp wf2)
(setq wf2 wf1)
(setq wf1 tmp)
(if swapped
(setq swapped nil)
(setq swapped true)))
(delete-file scriptout)
(if swapped
(begin
(rename-file wf1 scriptout)
  (delete-file wf2))
(begin
(rename-file wf1 scriptout)
(delete-file wf2))))

(nlinc)
(exit)


Windows script to build an executable and copy it to a directory in PATH env var:
e:
cd srcnewlisp
newlisp nlinc.lsp %1
newlisp -x %1-i.lsp %1.exe
move /y %1.exe e:scripts
del %1-i.lsp

Dario Giacomelli

#1
Hello.

Your system works if i load an extranal .lsp that loads an external .lsp too? in a chain of a script that load another script that load another script, i mean ...



Thanks for the tips. I'm newbie in Newlisp ... :-)

Dario

rickyboy

#2
Quote from: "Dario Giacomelli"Hello.

Your system works if i load an extranal .lsp that loads an external .lsp too? in a chain of a script that load another script that load another script, i mean ...

Yes.  Reviewing the author's (hotcore's) code reveals 'include's are effectively recursive.  Hence, you should try the script for yourself and report back if it worked for you.



(BTW, there is a typo that should be fixed (s/imcmem/incmem/ in function check-include), but it doesn't affect the correctness of the program, as incmem is already set to nil by default.)
(λx. x x) (λx. x x)

rickyboy

#3
hotcore, nice job!  This is a very useful script.  I haven't tested it but I enjoyed reading it.



If I could offer anything, it would be small changes, not design-level changes.



For instance, I'd make the end of function nlinc look like this:


   ...
    (while extra-pass
      (setq extra-pass (pass wf1 wf2))
      (swap wf1 wf2)
      (setq swapped (not swapped)))
    (delete-file scriptout)
    (rename-file (if swapped wf1 wf2) scriptout)
    (delete-file (if swapped wf2 wf1))))

And because we use the (intrinsic) swap function, we can get rid of the variable tmp.  However, please check my code for correctness.



I also would use let/letn for the local symbols of each function, but that's just me.  What you have here works fine.



Also, I have a version of your program which only has inclist as a global -- you also had passnum and a few others as globals too -- and I renamed it to *inclist* so it stands out.



check-include I just rewrote to this:


(define (check-include line)
  (let (line (lower-case (trim line)))
    (if (= (0 6 line) "(load ")
      ((parse (6 line) """) 1))))

It should still return nil if it's not looking at a load line.  The use of lower-case might be problematic though, because it will transform the filename.  This will be a problem on, e.g., Unix-like systems that have case-sensitive filenames.  And of course, the load function will always be in lower case.  So, you might get away with one less line in the definition:


(define (check-include line)
  (if (= (0 6 line) "(load ")
    ((parse (6 line) """) 1)))

(And the effect of the trim function is obviated by the fact that you would only be grabbing the filename (between the quotes) in the load call anyway.)



UPDATE: Scratch that.  Leave trim in (but not lower-case) would be my vote, because what if somebody writes:


   (load "my.lsp")
My bad. :)



Finally, write-incmem I'd write like this:


(define (write-incmem incmem out)
  (write-line out (string ";; NLINC: BEGIN include " incmem))
  (write      out (read-file incmem))
  (write-line out (string ";; NLINC: END include " incmem)))

which gets rid of another local variable.



Thanks for sharing your code!  Again, it was a pleasure to read; it was very clear to me what the script was doing.  I never ever ran it (!) but I could still tell that it was good!  Cheers!
(λx. x x) (λx. x x)

hotcore

#4
Hi rickyboy,



thx for the kind words!  I'll dig in into your comments.



Would you be so kind to publish your amended version?



Best wishes,

   Arie