Saturday, April 29, 2006

Building Lisp, Qi and other Fun Hacks

At some point when using Qi, you may find yourself wanting to do a bit more than the system provides natively. Perhaps you want to double click a .qi file and have it run in Qi. Perhaps you want to do some text processing and you need the CL-PPCRE regular expression library. If you do have either of those needs, you are in luck. I’ll explain how to do both in this article. But I'll try to keep an eye towards what's going on under the hood in Qi so we can better understand the motivation for this code.

First, let us cover passing files to the Qi.bat file and having them run. This is equivalent to double clicking on a .qi file in windows explorer with the proper file type associations set. The current Qi.bat that comes with Qi 6.2 does not allow you to pass .qi files on the command line into Qi. If we look at Qi.bat, we find that it simply loads CLISP with the startup_qi.txt file on the command line as an argument. It has 2 lines


Terpri prints a newline to the output stream then calls the main (qi::qi) function. If we want our system to understand arguments, we will have to do it before the (qi::qi) line. This is because at that point, Qi takes over and we have lost our chance to get back to the arguments. What we need to do is initialize the qi environment ourselves and load our .qi file given on the command line. Finally, we can call (qi::qi) knowing our file has been loaded into the system. In the following, we find a load_n_launch function that does exactly this. Note that while the function is mostly written in Lisp, it can makes calls to the qi subsystem via the package operator. It is important to remember that even at this point we have entered a "Qi like" environment. To better understand why, we need to understand how Qi bootstraps itself.

The Qi Bootstrap - How to build your own Qi

  1. Creating a .lisp file from the language .qi file. Qi is written in Qi. It creates a Lisp file from the version in the Qi language. We will call this qi.lisp.
  2. Running the install.txt code on the new qi.lisp file. Qi has an "Install Qi" folder in the distribution where you should find a fresh CLISP exe and initialized memory file. It should also have the "Qi 6.2.txt" which corresponds to what I have called qi.lisp. It also has install.txt which is a lisp program that performs the following operations.

    1. Makes a "qi" package
    2. Loads the qi.lisp file and compiles it into memory.
    3. Saves the memory as a new startup image.

  3. Finally, you can run lisp.exe -M with the new memory image that was created. You include the startup_qi.txt file to launch the qi read-print-eval loop.

So we understand the qi bootstrap process better now and we can see why things in our qi_startup.txt can be a bit wierd. Let us specify an argument loading version of qi_startup.txt


(COND ((EQ X NIL) "Done Loading")
(T (qi::load (CAR X))


All we have to do now is create a version of the qi.bat file that launches qi. It should simply run the lisp.exe with the lispinit.mem file that the Qi bootstrapper created and pass along any arguments it recieves. I personally like to have it be able to load from anywhere so I include the full path in the specification of the executable and the memory image.

"D:\tools\Qi 6.2\lisp.exe" -M "D:\tools\Qi 6.2\lispinit.mem" "D:\tools\Qi 6.2\qi_launcher.txt" %1

Of course, when you are playing with your memory images, make sure to keep a backup around in case something goes awry. Now if you double click a .qi file (assuming you never tried before), will ask you to choose which program to run it with. Select the "Choose from a list" option, then select the
"Browse" option and find your version of Qi.bat. Your program should launch and include all the wonderful functions you have defined. It makes debugging much easier in my opinion.

Obviously, you could place whatever code you wanted in the load_n_launch spot. Just call qi::qi to get into the main qi loop at the end of it and everything should work fine. But this brings us to the second issue, what about lisp functions we want to load? Unfortunately, we could not place them in the load_n_launch spot because QI already has changed many important things like case sensitivity and which characters count as "comments" among other things. If we try to load a lisp file, we will likely encounter errors.

So how do we get CL-PPCRE into Qi? The easiest way is to include the entire package into our memory image. Once we have a lisp memory image with the functions included in it, we can then make a Qi image that will have access to the original functions. While the steps below work, they create monster images in terms of memory size. So be certain you only include the things you really need. Oh, and there are better ways of doing this but I don't know about them yet. Feel free to enlighten me.

  1. Download the latest version of CLISP
  2. Download the latest version of CL-PPCRE
  3. Run CLISP and type (load "path\\to\\CL-PPCRE\\load.lisp")
  4. Type (EXT:SAVEINITMEM) to save the lisp image. Then type (CD) if you don't know what directory it saved them in.
  5. Copy the lispinit.mem and the contents of the CLISP/full directory, into your Install Qi directory.
  6. Type lisp.exe -M lispinit.mem install.txt
  7. Copy lisp.exe and lispinit.mem into the main Qi directory.

And that will give you a customised version of Qi! You can now perform wonders such as

(1-) (CL-PPCRE:SCAN-TO-STRINGS "(a)*b" "xaaaab")

But remember, Qi only reads off one result at the top level. To see the rest of your results, you have to use multiple-value-list

["aaaab" #["a"]]

If you have anything else you need to load, you can do it in the above manner.

So in short, we can have our cake and eat it too. If you can get something working in CLISP, you should be able to get somethign working in Qi. I hope these tips help your Qi experience and let me know what you think!


Post a Comment