20260524

Getting HEΛP, Finally!

As I wrote in my last blog entry, I went back hacking on HEΛP.

HEΛP is the Common Lisp code documentation tool I started writing many years ago.

Apart from a little necessary Javascript and CSS, HEΛP is a full Common Lisp program, geared towards producing static documentation sites for CL code. I finally got around to modernize it and it is now ready for testing.

Evolution from (X)HTML to HTML5

The original HEΛP release was producing only (X)HTML output, moreover based on FRAMESETs.

Alas, when the first HEΛP release was made, FRAMESETs were falling out of fashion, and they were eventually deprecated with the advent of HTML5. An "upgrade" to HTML5 became then a necessity.

After a very long process, I finally finished the HTML5 port, plus some bells and whistles. All in all, the implementation uses <div ... > sections plus CSS to lay out the display, as I understand it is the proper coding fashion nowadays. The port uses the W3.CSS styles, which facilitated a number of choices. The result is rather pleasing, as far as I am concerned.

Example: Producing the HEΛP documentation

The HEΛP documentation (a form of it) is produced with the following command (hlp is one of the package nicknames):

(hlp:document #P"./" ; Just a "top directory"...
              :documentation-title "HE&Lambda;P"
              :format :html5
              :exclude-directories
              (list "doc/"
                    "js/"
                    "css/"
                    ".git/"
                    "tests/"
                    "tmp/"
                    "tools/"
                    )

              :exclude-files ; I run this from LW.
              (list "impl-dependent/ccl.lisp"
                    "impl-dependent/sbcl.lisp"
                    "utilities/document-helambdap.lisp"
                    "utilities/lambda-list-parsing.lisp"
                    )
              :only-documented t
              :only-exported t
              )

After much printing, the resulting static web pages are deposited in docs/html5/, unless overridden. The system also relies on some defaults which are handled by CLAD library.

Viewing the result.
For the time being, you can find the main page of HEΛP here. Navigating the bar on the left will allow you to see different bits and pieces fo the documentation. You will notice that you have different views of the documentation: a system view and a package view. The system view gives you also a view of the files and folders (modules) it contains.

Documentation strings are mostly left alone.
Unike for Emacs Lisp, there is no real agreement in the CL community about how to format documentation strings (if there is, I do not agree with it by definition - obviously). HEΛP wants to be able to document code that does not adopt any documentation string convention, therefore it treats documentation strings pretty much as they are, only adding some text in the guise of the Hyperspec entries.

Screenshot

Here are a couple of screenshots. Apologies for the bad resolution.

The Main View

The HEΛP Main View

The DIctionary View

The HEΛP Dictionary View

Missing Pieces

There is one thing that is missing from HEΛP: the generation of proper crossreferencing. To do it correctly it will be necessary to somehow make some educated guesses about the content of documentation strings or agreeing on some markup to tag linkable items. Apart from that, at the time of this writing the doc strings are handled as enties in an has table, and that could be improved, as more indexes may be needed.

Of course, a major rewrite may also help, but time is a tyrant.

Any suggestion is welcome.

Try it!

Again, HEΛP is ready for you to try. You can clone the repository from Sourceforge.

... and remember: no Python or Ruby or Shell dependencies: pure CL (plus some Javascript, which is a functional language after all, whose first implementation was done in CL).

Enjoy!


'(cheers)

20260402

An Update on MK-DEFSYSTEM

There are still a few of us (at least two) who are using MK:DEFSYSTEM. The venerable system construction tool has accumulated a lot of ancient cruft, some of which quite convoluted.

Recently I went back to MK:DEFSYSTEM and "cleaned up" some of the code, especially regarding the pathname construction for each component.  I also used some simpler hierarchical tricks using defstruct only.

The result should be more solid and clearer in the steps that comprise some "macro tasks". Of course, a rewrite using CLOS would change the coding style, but the choice has been made to keep the MK:DEFSYSTEM code base quite... retro (and somewhat simple).

Why did I went back to MK:DEFSYSTEM? As usual, it is because of a rabbit-hole I fell into: I will blog about it later on (hint: HEΛP).

MK-DEFSYSTEM quick history as of March 2026

MK-DEFSYSTEM (or MK:DEFSYSTEM, or MAKE:DEFSYSTEM) was originally written by Mark Kantrowitz as part of the original "CMU Lisp Utilities" collection; an early "public" set of Common Lisp code and utilities that, in the writer's opinion form one of the basis of most Common Lisp writing to date.

As stated (by M. Kantrowitz himself) in this file header, the original version of MK-DEFSYSTEM was inspired by the Symbolics DEFSYSTEM (or DEFSYS) tool. Yet, MK-DEFSYSTEM differs significantly from it.

In its original form, MK-DEFSYSTEM was built in the CLtL1 era, accommodated a lot of variance among filesystems and CL implementations and it still bears those idiosycrasies. CLtL2 (1992) first and ANSI (1994) next, started reshaping the code base then.

MK-DEFSYSTEM was originally distributed under a license agreement that made redistribution tricky. In 1999, the writer - that'd be me, Marco Antoniotti - contacted Mark Kantrowitz offering to become a maintainer while reworking the distribution license to hammer some FOSS into it. Mark Kantrowitz graciously agreed and, after that, the writer got literally and physically hugged by a few Common Lisp developers because they could use MK-DEFSYSTEM more freely.

Of course, ASDF came along and it solved the same problems that Symbolics (and Kent Pitman's) DEFSYS and MK-DEFSYSTEM solve, plus much more.

Yet, MK-DEFSYSTEM has some nice features (in the eye of the beholder).

MK-DEFSYSTEM still ships in one file - defsystem.lisp - that you can LOAD in your Common Lisp init file. Of course, a big chunk of its current code base is "backward compatibility" and new ok-we-miss-UIOP-and-or-at-least-CL-FAD functionality, plus an ever growing ongoing commentary like this one.

Given this background, the writer has been maintaining MK-DEFSYSTEM for a long time, and more recently, Madhu has made significant changes (and maintains himself a fork with some bells and whistles of his own) since 2008.

Of course, many other contributors helped over the years, and are acknowledged in the early Change Log and in comments in the code.

In early 2026, the writer cleaned up the code and reworked some of the logic, by factoring out some code from main functions. In particular, the CREATE-COMPONENT-PATHNAMES, GENERATE-COMPONENT-PATHNAMES, COMPONENT-FULL-PATHNAME, COMPONENT-FULL-NAMESTRING interplay is better organized; plus new structures, leveraging DEFSTRUCT :INCLUDE feature have been introduced, rendering the code TYPECASE-able.

MK-DEFSYSTEM is old, but it works. It is quirky but it works (at least for the two or three known users - which, in 2026, is already a big chunk of the Common Lisp users' community). Moreover, it does have, at least in the eye of the beholder, some more user friendly user API, for most use case, especially for plain Common Lisp code.

The current MK-DEFSYSTEM repository is at https://gitlab.common-lisp.net/mantoniotti/mk-defsystem

(*) It is assumed that the reader knows about all the acronyms, tools and systems referred to in the text.


'(cheers)

20251226

Retro (?) Computing in Common Lisp: the CL3270 Library

Come the Winter Holidays and, between too much and a lot of food, I do some hacking and maintainance of my libraries.

Some time ago, I wrote a CL library to set up a server accepting and managing "applications" written for a IBM 3270 terminal.

Why did I do this? Because I like to waste time hacking, and because I got a (insane) fascination with mainframe computing. On top of that, on the Mainframe Enthusiasts Discord channel, Matthew R. Wilson posted a recently updated version of my inspiration, the go3270 GO library.

Of course, I had to fall in the rabbit..., ahem, raise to the occasion, and updated the CL3270 library. This required learing a lot about several things, but rendering the GO code in CL is not difficult, once you undestrand how the GO creators applied Greenspun's Tenth Rule of Programming.

Of course there were some quirks that had to be addressed, but the result is pretty nice.

Screenshots

Here are a couple of screenshots.

"Example 3": The Time Ticker

Yes, it works as advertised.

This is how the server is started from **CL** (Lispworks in this case).

... and this is how the c3270 connects and interacts with the server.

"Example 4": The Mock Database

This example has many panels which fake a database application. The underlying implementation use "transactions", that is, a form of continuations.

Starting the server...

... and two of the screens.

It has been fun developing the library and keeping up with the talented Matthew R. Wilson.

Download the CL3270 library (the development branch is more up to speed) and give it a spin if you like.


'(cheers)

20250721

EMC makes into melpa

Hello

Emacs Make Compile (EMC) made it into MELPA.

You can now install it directly from the Emacs package manager.

Many thanks to the MELPA curators for accomdating an old, annoying, geezer.

Give it a spin; submit bug reports and suggestions.


'(cheers)

20250512

Getting into a rabbit's hole and - maybe - getting out: Emacs Make Compile (EMC)

In the past years I fell form one rabbit's hole into another. For the first time in a rather long time, I feel I am getting out of one.

First of all let me tell you what I produced. I built the "Emacs Make Compile", or "Emacs Master of Ceremonies", package EMC. Soon it will be available in melpa. The EMC package is a wrapper around compile.el that allows you to semi-transparently invoke make or cmake from Emacs. Either on UN*X, MacOS or Windows.

Once you have loaded the emc library in the usual ways, you can just issue the Emacs command emc:run (yes: Common Lisp naming conventions). The command is just the most general one available in EMC; other ones are the more specialized emc:make and emc:cmake. Emacs will then ask you for the necessary bits and pieces to ensure that you can run, say, make. The README file included in the distribution explains what is available in more details.

Where did it all begin?

Why not stick with compile.el? Because it does not have "out-of-the-box" decent defaults under Windows. At least, that was my original excuse.

I fell into this rabbit's hole coming from another one of course.

Some time ago, I started fiddling around with Emacs Dynamic Modules. I wanted to compile them directly from Emacs in order to "simplify" their deployment. Therefore, I set out to write a make function that would hide the compile setup.

Alas, I found out that, because of the necessary setup, invoking the Microsoft Visual Studio toolchain is not easy before you can get to cl and nmake. That was not all that difficult as a problem to solve, but then I made the mistake of learning to cmake. You know; to ensure that the building process was "more portable". The basic machinery for make and nmake worked to also get cmake up and running. But then I made another mistake: I started to want to get EMC to be usable in the "Emacs" way: at a minimum getting interactive commands working. That got me deeper and deeper in the rabbit's hole.

At the bottom of the hole (yep: I got there!)

I found out many things on my way to the bottom. That is, I learned many things about the Emacs Lisp ecosystem and wasted a lot of time in the process. I never was a fast learner. All in all, I think I can now say two things.

  • Making a command, i.e., an interactive function is not trivial, especially if your function has many arguments. Bottom line: your Emacs commands should have *few* arguments. I should have known better.
  • The Emacs widget library is woefully underdocumented (which, of course, brings up the question: why did you want to use it?)

In any case, what I was able to concot is that hitting M-x emc:make does what you expect, assuming you have a Makefile in the directory; if not you will be asked for a "makefile", say stuff.mk to be used as in

make -f stuff.mk
or
nmake /F stuff.mk

Issuing C-u M-x emc:make will ask you for the "makefile", the "source directory", the "build directory", "macros", and "targets".

In what other ways could I have wasted some time? By coming up with a widget-based UI! (See my previous post about DeepSeek and the widget library). The result can be invoked by using the command emc:emc, which pops up the window below.

Getting out of the rabbit hole by popping the stack

I kind of consider EMC finished. I am pleased by the result; it was fun to solve all the problems I encountered, although the code is not exaclty nice or nicely organized. Is EMC useful? Probabiy not so much, but I have the luxury of wasting hacking time. I just hope somebody will like it: please try it out and report bugs and suggestions (the minor mode and associated menu need work for sure, as well as emc:emc).

Having said so, I can now go back to play with Emacs Dynamic Modules, which is where I was coming from. After being satisfied with that, I will be able to climb back up a bit more from the rabbit's hole; that is, I will be able to go back to the magiciel library (which kind of works already). You may ask why I am writing magiciel, but you will have to reach down several levels in the rabbit's hole.

In any case, I finished one thing. It's progress.


'(cheers)

20250424

Emacs Lisp Programming with DeepSeek: A New Widget

The Emacs widget library is useful; alas its guts are ... semi-documented and most of its inner working a bit mysterious. I wanted a column widget where I could insert and remove a few "line-like" widgets. The editable-list widget does not cut it (too many extra things: the INS and DEL buttons) and the group widget is too inflexible.

After too much time trying to understand all the intricacies of the widget library (see my rant in my previous blog post, which perfectly applies in this case) I asked DeepSeek to help me out. The result, the dynamic-group widget (after several iterations and mistakes on part of DeepSeek) is below. It works satisfactorlly, although it could be improved by anybody with a better understanding of the widget library. What is does is to manage a colimn of line-like widgets adding and removing from the end of the :children list. Check the demo-dynamic-group for a test run.

It has been fun. Although I still want a better widget! That's why I am posting this for anybody to pitch in. Any help is welcome.

BTW. There still are some warts in the code. Can you spot them?

;;; -*- Mode: ELisp; lexical-binding: t -*-
;;; emc-dynamic-group.el
;;;
;;; `flycheck-mode' does not like the above.  `flycheck-mode' is wrong.

;;; Code:

(require 'widget)
(require 'wid-edit)


(define-widget 'dynamic-group 'default
  "A container widget that dynamically manages child widgets in a column."
  :format "%v"
  :value ()
  :tag "Dynamic Group"
  :args nil
  
  ;; Core widget methods
  :create (lambda (widget)
            (let ((inhibit-read-only t))
              (widget-put widget :from (point))
              (dolist (child (reverse (widget-get widget :children)))
                (widget-create child))
              (widget-put widget :to (point))))

  :value-get (lambda (widget)
               (mapcar (lambda (child)
                         (widget-apply child :value-get))
                       (widget-get widget :children)))
  
  :value-set (lambda (widget value)
               (widget-put widget :value value))
  
  :value-delete (lambda (widget)
                  (dolist (child (widget-get widget :children))
                    (widget-apply child :value-delete)))
  
  :validate (lambda (widget)
              (let ((children (widget-get widget :children)))
                (catch :invalid
                  (dolist (child children)
                    (when (widget-apply child :validate)
                      (throw :invalid child)))
                  nil)))
  )


(defun dynamic-group-add (widget type &rest args)
  "Add a new widget (of TYPE and ARGS to the WIDGET group."
  (let ((inhibit-read-only t))
    (save-excursion
      (goto-char (widget-get widget :to))
      (let ((child (apply 'widget-create (append (list type) args))))
        (widget-put widget
		    :children (cons child (widget-get widget :children)))
        (widget-put widget
		    :to (point))
        (widget-value-set widget
          (cons (widget-value child) (widget-value widget)))))
    (widget-setup)))

  
(defun dynamic-group-remove (widget)
  "Remove the last widget from the WIDGET group."
  (when-let ((children (widget-get widget :children)))
    (let ((inhibit-read-only t)
          ;; (child (car children))
	  )
      (save-excursion
        (goto-char (widget-get widget :from))
        (delete-region (point) (widget-get widget :to))
        (widget-put widget :children (cdr children))
        (dolist (c (reverse (widget-get widget :children)))
          (widget-create c))
        (widget-put widget :to (point))
        (widget-value-set widget
			  (mapcar 'widget-value
				  (widget-get widget :children)))
        (widget-setup)))))

  
(defun demo-dynamic-group ()
  "Test the dynamic-group widget."
  (interactive)
  (switch-to-buffer "*Dynamic Group Demo*")
  (kill-all-local-variables)
  (let ((inhibit-read-only t))
    (erase-buffer)

    (widget-insert "* Dynamic Group Demo\n\n")

    ;; Now I create the `dynamic-group'.
    
    (let ((group (widget-create 'dynamic-group)))
      (widget-insert "\n")

      ;; The rest are just two buttons testing the widget's behavior,
      ;; invoking`dynamic-group-add' and `dynamic-group-remove'.
      
      (widget-create
       'push-button
       :notify
       (lambda (&rest _)
         (dynamic-group-add group 'string
			    :format "Text: %v\n"
			    :value (format "Item %d"
					   (1+ (length (widget-get group :children))))))
       "(+) Add Field (Click Anywhere)")
      
      (widget-insert " ")
      
      (widget-create
       'push-button
       :notify (lambda (&rest _)
		 (dynamic-group-remove group))
       "(-) Remove Last")
      
      (widget-insert "\n"))

    ;; Wrap everything up using the `widget-keymap' and `widget-setup'
    ;; functions.
    
    (use-local-map widget-keymap)
    (widget-setup)))


(provide 'emc-dynamic-group)


'(cheers)

20240804

Helping HEΛP Again! ... and Again!

In the heat of the summer (the coolest summer of the next ones), it is never a good thing to get an email from Xach telling you that "something does not compile on SBCL". In this case the issue was the usual, fascist STYLE-WARNING, that prevented a clean build on Quicklisp.

The fix was relatively easy, but it lead to a number of extra changes to properly test the fix itself.

Bottom line, a new version of HEΛP is available at helambdap.sf.net. Soon in Quicklisp as well.

Stay cool, hydrated and enjoy.


(cheers)

20240627

Helping HEΛP Again!

In a flurry of ... free time, I also went back to HEΛP and fixed a few bugs that were exposed by some of the things I did with CLAST. Recording the documentation strings from the pesky

  (setf (documentation 'foo 'function) "Foo Fun!")
  

are now all working as expected, at least at top-level and within PROGN-like constructs, e.g., EVAL-WHEN.

Meanwhile, I also updated the documentation and the web page adding a few caveats about how to run the DOCUMENT function, and how to work around issues I have seen in my (not so) extensive tests.

Of course, I put my money where my mouth is: the HEΛP documentation web pages are built with HEΛP.


(cheers)

20240626

CLAST reworked

Prompted by a post on one of the various Common Lisp fora, I finally got my act together and went back to CLAST, i.e., the Common Lisp Abstract Syntax Tree library that I had in the works for ... some time.

The library has an interesting origin, which I will recount in a different post. Suffice to say that eventually I needed a code walker which did a few complicated things. NIH sydrome immediately kicked in.

The main think I needed were functions inspecting code, as in the example below.

cl-prompt> (clast:find-free-variables '(let ((x 42)) (+ x y)))
(Y)

To achieve this (apparently) simple goal, a (mostly) portable environment library had to be developed and a full set of AST node structures had to be provided.

The result is now finally ready for prime time. In Lispworks you can also see how things actually get parsed. As an example, the picture below shows the result of the following command.

cl-prompt> (clast:parse '(loop for i in '(1 2 3)
                               count (oddp (+ qd i)) into odds))
#<LOOP-FORM  24ECE77F>
NIL

Please try it, report bugs, blast my design choices and suggest improvements.

Thank you


(cheers)

20240508

ELS 2024 in Vienna

I just got back from the 2024 European Lisp Symposium in Vienna. After many years it was good to meet again in person with so many talented and interesting people (all interested in parentheses).

Many, many thanks to Beppe Attardi, Philipp Marek, Georgiy Tugai, Yukari Hafner, and, especially, Didier Verna, for pulling this together.

20240303

A Rant about R and Python (and anything with a-lists, p-lists, tuples and "dictionaries").

R and Python come from Lisp (R for sure, Python will deny it). Early Lisp. Even before the first edition of "AI Programming" by Charniak, Riesbek and McDermott.

At that time, there were a-lists and p-lists. Then Charniak, Riesbeck and McDermott taught us (not only them of course) how to create records in Lisp. You know... those things like:

    DECLARE
      1 STUDENT,
        2 NAME     CHAR (30),
        2 SURNAME  CHAR (50),
        2 ID FIXED DECIMAL (8),
        2 ADDRESS,
          3 STREET  CHAR (80),
          3 NUMBER  FIXED DECIMAL (5),
          3 CITY    CHAR (80),
          3 PRST    CHAR (20),
          3 ZIP     CHAR (10),
          3 COUNTRY CHAR (80);

Using a-lists the above may become:

    (defvar student '((name . "John")
                      (surname . "Blutarski")
                      (id . 42)
                      (address . ((street . "United State Senate")
                                  (number . 0)
                                  (city . "Washington")
                                  (prst . "DC")
                                  (zip . "20510")
                                  (country . "U.S.A.")))
                      ))

In Python you can do the following.

    student = {}    # a 'Dict'; i.e., a hash table.
    student['name'] = "John"
    student['surname'] = "Blutarski"
    student['id'] = 42
    student['address'] = {}
    student['address']['street'] = "United State Senate"
    student['address']['number'] = 0
    student['address']['city'] = "Washington"
    student['address']['prst'] = "DC"
    student['address']['zip'] = "20510"
    student['address']['country'] = "U.S.A."

Not that you must, but surely you can; and this, as in the case of R below, is the root of my rant.

In R you use lists; a misnomer for something that is essentially a dictionary like in Python, patterned, ça va sans dire, after a-lists.

    student = list()
    student$name = "John"
    student$surname = "Blutarsky"
    student$id = 42
    student$address = list()
    student$address$street = "United State Senate"
    student$address$number = 0
    student$address$city = "Washington"
    student$address$prst = "DC"
    student$address$zip = "20150"
    student$address$country = "U.S.A."

This of course gives you a lot of flexibility; e.g., if - in the middle of your code - you need to deal with the student's nickname, you just write the following.

In (Common) Lisp:

    (defun tracking-student ()
        ...
        ...
        (setf student (acons 'nickname "Bluto" student))
        ...
        )

In Python:

    def tracking_student():
        ...
        ...
        student['nickname'] = "Bluto"
        ...

In R:

    tracking_student <- function() {
        ...
        ...
        student$nickname = "Bluto"
        student <<- student     # Yes, R scoping rules are ... interesting.
        ...
    }

The example is relatively simple and innocuous, but it has some consequences when you have to actually read some code.

The problem I have (it may be just me) is that this programming style does not give me an overview of what a data structure (a record) actually is. The flexibility that this style allows for is usually not accompanied by the necessary documentation effort telling the reader what is going into an "object". The reader is therefore left wondering, while taking copious notes about what is what and where.

Bottom line: don't do that. Use defstruct and defclass in CL, classes in Python, struct in Julia, etc. etc. etc. In R, please document your stuff.

You may feel that your code is less malleable, but, in the end it becomes easier to read. At least for me. Sorry.


(cheers)

20240104

A Parenthetical Year and New Open Parentheses

It has been a few years that I have spent the New Year Holiday to update my Common Lisp libraries and to think about all the open parentheses that are left unclosed. This year is no different, only I have had even less time to work on CL. Therefore, my updates to my libraries on common-lisp.net and Sourceforge all have had some minor repairs and copyright updates. As usual, I direct you to HEΛP and CLAST; most of my other libraries are support for these two.

One effort, I would like to finalize in the coming year (that is; closing this parenthesis) is the CL-LIA layer/library. Help is wanted for this! Drop me a line if you want to chip in. The main issues are API designs around the floating point environment condition handling.

Apart from that, I have been looking more and more to the future, that is, Julia (I should have listened more to some people in the CL community years ago), and... in the past.

Julia and Cancer Research

I have advised a student to work on Julia and the result is this nice (very rough) simulator for solid tumors with input from images and tracking of cells lineages: SOPHYSM. SOPHYSM builds on J-Space which is the simulator core; you can find the publication at BMC Bioinformatics.

I am currently pushing Julia to my research group, especially for new AI and ML applications to Data Analisys in Cancer Research (cfr., Data and Computational Biology Laboratory at Università degli Studi di Milano-Bicocca). This is because new Julia ML and AI frameworks have become much more solid in recent months; in particular Flux and SciML.ai.

Programming Archeology

If you have followed my older posts, you know I fell into a rabbit hole (with several side tunnels). I have been dabbling with very old technology, which turned out to be ... fun (don't tell my Department Head!).

I will just say that now I can understand the vagaries of PL/I (where do you think that loop and generic functions come from?) and that I can write decent Fortran IV (66) (downgrading from Fortran 77). And yes! I can compile my program on Hercules (of course using Emacs to submit jobs to MVS TK5; some work required).

Happy Hacking and Science Year!

That's it. These are my report for 2023, and my wishes for you all in 2024.

Just one more thing: you reader know that I am becoming and old geezer. I somehow rationalize my dabbling with parentheses and even older stuff as "taking care" of things that should not be lost; even if now we have Julia.

Let's all try to take care of each other and the world, of the old things and of the new ones that will inherit it.


'(peace)

20230816

Documenting/debugging HEΛP

Hello

just a quick summer update about some documentation cleanup and some checks on debugging HEΛP.

Have a look at the (small) changes and keep sending feedback.



Cheers

20230309

The LETV and LETV* Macros

Hello,

It has been a long time since I posted something here.  I have been busy with my day job and bogged down in a major rewrite of something (more on this hopefully very soon now (tm)) that is full of rabbit's holes.

I was able to get out of one of these rabbit's holes with this little hack I cooked up that allows you to, possibly, write more concise code.

This little hack introduces two handy Common Lisp macros, LETV and LETV* that allow you to mix regular LET and MULTIPLE-VALUE-BIND forms in a less verbose way. The amount of indentation needed is also reduced.

The syntax of LETV (or LETV*) is very "loopy", with a nod to SML/OCaml/F#/Haskell/Julia. The current syntax is the following:

    letv     ::= 'LETV' [vars] [IN [body]]
    letvstar ::= 'LETV*' [vars] [IN [body]]

    vars     ::= var | var vars

    var      ::= ids '=' <form> [decls]

    decls    ::= decl | decl decls
    decl     ::= OF-TYPE idtypes

    ids      ::= <symbol> | '(' <symbol> + ')'
    idtypes  ::= <type designator> | '(' <type designator> + ')'

    body     ::= [<declarations>] <form> * 

(I know: the grammar is not completely kosher, but I trust you will understand it.)

The two macros expand in forms that mimic the semantics of let, LET* and MULTIPLE-VALUE-BIND. All type declarations, if present, are properly handled via locally forms. LETV expands into a LET, with variable initialized by PSETQs and multiple-VALUE-SETQs. LETV* expands into a form that is an interleaving of LET* and MULTIPLE-VALUE-BIND.

The library exports only the two symbols LETV and LETV*. The other "symbols" mentioned (=, IN, OF-TYPE) are checked as in LOOP; therefore you can use different styles to write your code, as you would when writing LOOPs.

The library is available here.

Examples

  1. Simple case:
      (letv x = 42 in (format t "The answer is ~D.~%" x))
      
  2. Same with declaraion:
      (letv x = 42 of-type fixnum
    	in (format t "The answer is ~D." x))
      
  3. Simple case with MULTIPLE-VALUE-BIND:
      (letv (v found) = (gethash 'the-key *my-hash-table*)
            in (if found
                   (format t "Found THE-KEY, doing stuff.~%")
                   (error "THE-KEY not found.")))
      
  4. Mixing things up:
      (letv (v found) = (gethash 'the-key *my-hash-table*)
                      of-type (fixnum boolean)
             x = 42
             in (if found
                     (format t "Found THE-KEY, adding to the answer ~D.~%"
                             (+ v x))
                     (error "THE-KEY not found.")))
      
  5. With LETV*:
      (letv* (v found) = (gethash 'the-key *my-hash-table*)
                       of-type (fixnum boolean)
             x = (when found (+ v 42))
             in (if found
                    (format t "Found THE-KEY, adding to the answer ~D.~%"
    			 	        x)
                    (error "THE-KEY not found.")))
      
  6. The other way around.
      (letv x = 42 of-type integer
            (v found) = (gethash 'the-key *my-hash-table*)
                      of-type (fixnum boolean)
            in (if found
                   (format t "Found THE-KEY, adding to the answer ~D.~%"
                           (+ v x))
                   (error "THE-KEY not found.")))
      
  7. A more compelling example.
      (letv* (p found) = (gethash 'the-key *points-table*) of-type (point)
             (x y) = (if found
                         (unpack-point p)
                         (values :missing :missing))
             in
             (declare (type point p)
                      (type real x y))  ; Adding declarations here also works.
    
             (do-stuff-with x y)
             (do-things-with p))
      

All the examples are meant to illustrate the use of LETV and LETV*.

Notes

LETV and LETV* are obviously not the first macros of this kind floating around: others are available and all have their niceties. But I never claimed not to suffer from NIH syndrome.

LETV and LETV* do not do "destructuring" or "pattern matching". That is a different can of worms; but you can check cl-unification for a library (always by yours truly) that provides facilities in that sense.


'(Cheers)

20220121

CDR is Next...

 Hi

as many may know, I have been nursing (almost to death!) the Common Lisp Document Repository (CDR).  Pascal Costanza et al., started the project many years ago and then I sat on top of it for many more.

I finally found some time to work on it and the result is a revamped site (cdr.common-lisp.dev) with the addition of stashing documents in a Zenodo Community (CDR), which has the benefit of producing a DOI for each write-up.

Moreover, Pascal Bourguignon, Michał "phoe" Herda and Didier Verna have agreed to become CDR editors.  Many thanks to them.

So, if anyone wants to submit a "specification" for something of interests to the CL community, she/he is most welcome to do so.  Just remember that good specifications are not so easy to write.

20220105

My List of Common Lisp Libraries and SW 2022 (Plus a Couple of Other Things)

Hello

I have been bumping on a few "lists of Common Lisp libraries and tools" written by many people.  I feel like rantin... pardon, blogging about this state of affairs.

First of all we have CLiki, which is a rather comprehensive list of CL libraries and whatnot, and then we have a few, unnamed, "state of the CL ecosystem", "preferred list of Common Lisp libraries", etc, etc.

I have nothing against people blogging or making lists of course, but I tend not to make generalized statements.  Especially in order to avoid disrespecting some people's work, just by not knowing of its existence.  This is a hint.

So, in order to proceed with my ran... blog post, here is the list of CL libraries I use.  Turns out there is quite a bit of NIH syndrome here, but I never claimed not to suffer from it.  Also remember that I have a Mac, a Windows and a Linux system at hand.  I always try to have stuff that works on all of them.

Distribution Systems

I use, obviously, Quicklisp.  It works.  It can be improved, but hey!  Let's just give Zach our love! 💓 (And money!)

Implementations

  • I work mostly on Lispworks.  I have the luck to be able to afford the Enterprise edition (or better, my funding does) and I am very happy with it.  The folks at Lispworks know that I can be a pest (environments? rounding modes?) but they are just doing a fantastic job.
  • I use, of course, SBCL.  The implementation is rock solid and it has all the bells and whistles you need for a world class system.  Recompiling and deploying for SBCL usually uncovers bugs and potential pitfalls in your code.  I have a small rant about it though.  Guys, do not write code that "works on SBCL" and just assume all is fine. It is not.
  • I have also CMUCL installed and I use it as well.  It still has a few good things to it.
  • Next I use Armed Bear Common Lisp, an excellent testbed for checking portability.
  • Of course, I have the free edition of Allegro CL: the other excellent commercial alternative.
  • Last but not least (with memories going back decades) I have CCL installed and always ready to use.
  • I am very intrigued by CLASP, but I admit, I have not had the strength to install it (too much of a production; remember that I try to get things working on three platforms); any help will be appreciated.
  • Same for the latest incarnation of ECL.  Sorry guys, I will get back to installing it soon.
  • Finally, we always have CLisp: old but good.
I know I forgot many.  Apologies for my senescence.

System Building

If you checked any of my libraries, you will have noticed that I still have .system files everywhere.  I use ASDF of course, but I have been nursing (with Madhu) a version of MK:DEFSYSTEM; it is clunkier (and old) internally, but I feel it has a simpler and more straightforward interface, especially when it comes to its footprint on your system (i.e., somewhat simpler working of the registry).

Concurrency

I use Bordeaux Threads for portability.  There are a few open issues about it that are of very difficult solution but it is Good Enough (it has a couple of warts: a bad .asd file for example - but this is a separate rant, pardon, blog post).  Of course I also like very much Lispworks multiprocessing library, especially mailboxes, but I understand that not everybody has access to it.

Testing

I must say that none of the libraries I checked does ONE thing I really need: running something and stopping after a timeout (see the open issue I mentioned just before for Bordeaux Threads).
Having said that, I use FiveAM; I have to muck a bit around it to get the timeout I need, and the documentation, especially about macr... pardon, fixtures is lacking, but it does the job.  I am experimenting with Parachute, but have not made the switch yet.

Portability

Most of my libraries try to be portable.  One major library that helps you a lot with this thankless job ("let's have filename case insensitive anyone?") is of course UIOP.  You have it pretty much by default, but it would be nice if it were eventually decoupled from ASDF.
Another little library I advocate (yes, Virginia, here they come!) is CLAD: it does not do much, but it gives you a bedrock upon which to build your work.

Editing

I use the Lispworks IDE or the Editor (as in "thou shalt not have other Editor than...") with SLIME.  End of story.

HTML Generation

Here I start with the rantin..., thoughtful exposition of various libraries.
To generate (X)HTML (or HTML5) I use (X)HTMΛ.  It is a pretty solid library with a couple of bells and whistles: mainly, it has an object model and it leverages the sorrily underleveraged Common Lisp pretty printer to produce readable HTML.  The web page for (X)HTMΛ is produced using itself as a building block; see below.

Web Server and Client

In this case I stick with the tried and true Hunchentoot and its counterpart Drakma, although for simpler, one-shot things I do use Lispworks facilities when needed.

Documentation Generation

As I said, all my libraries suffer from a severe case of NIH, which is reflected in my use of the hacked up HEΛP documentation system.  It is still not perfect, but, again, by using (X)HTMΛ and, again, leveraging the pretty printer, I find that the results are quite pleasant (for the neapolitans: ogni scarrafone...).  Of course, if you agree to format your doc string in a Hyperspec way.  How can it be improved?  Four things.
  1. Incorporating Eclector as a "reader" (as of now the tool uses more than a kludge to get past the non standardized way of dealing with READER- and PACKAGE- errors).
  2. Finishing the HTML5 generation scheme.
  3. Adding Texinfo generation (as per Didier Verna's declt library).
  4. Fixing cross-referencing, especially with standard CL (cfr., Hyperspec).

Mathematics

Why did I start HEΛP?  Many years ago (or many Kgs ago) a post on comp.lang.lisp stated that you could shadow +, -, *, and / in a package and make them generic functions (as an aside, I recently found out that the idea of generics dates back to PL/I - but this is a different rabbit's hole).
The result was what is now a forever embryonic Common Math, which led to my forays in "let's do what R can do" -- Ρ (greek Rho) -- and to the stuff I am talking about in "Why you cannot write an Interval Arithmetic Library...".  All of this another rabbit hole: bottom line, this is what I am working on and off right now, apart from my day job.

Other Libraries

Laziness as a Way of Life

Of course, rabbits' holes are never linear.  They branch in several directions at once.  E.g., once you look at other "modern" languages (I am partial to Haskell) you want to have some of these features in Common Lisp as well); hence the CLAZY library (laziness as a way of life).

Code Handling

Needless to say, CLAZY eventually needed some proper code walking, or better it needed some proper Abstract Syntax Tree (AST) handling, which led to the CLAST library: this last library allows you to code walk and to properly inspect a piece of Common Lisp code, striving very hard to portably manage the environments that were present in CLtL2 but did not make it in the ANSI spec.

Programming Common Lisp

As we all know, Lisp is like a ball of mud.  Over the years I threw a lot of mud at it.
Some of the mud I liked the most is embedded in the following libraries
  • definer is a small hack to have Pythonesque def available in Common Lisp, of course, in an extensible way (documentation being updated soon).
  • cl-unification is a full blown unification machine for Common Lisp objects and "templates"; not as fast as a simpler pattern matcher (like several listed in CLiki Pattern Matching), but very general (it also relies on CL-PPCRE).
  • cl-enumeration was the first full blown Java-like enumeration/iterator library for Common Lisp; it works.
  • defenum is another Java-ism ported to Common Lisp; it essentially creates enums that work like Java, i.e., with quite a bit of underlying machinery.  Of course you do not have Haskell data definitions available, but it was fun to write.
  • with-contexts was a bout of Python-envy, but then again, I think I was able to show that you really can program Common Lisp in a fun way.

Literary Programming in Common Lisp

One final library I am quite fond of expresses my love of (certain) books.  And to deal with books, you need a Library and a Librarian.  Hence the OOK compiler in Common Lisp.  Don't say Monkey!

Not Done Yet...

Although I am dabbling with Julia (a Common Lisp in a drag) and Rust (I have a day job which also involves programming languages and not only Cancer Research) I still have a lot of "not-quite-there" stuff I wrote in Common Lisp.
All I can say about it is... stay tuned,  I do put out stuff and I do pester people about Common Lisp.  You may find some of the stuff interesting.



(cheers)





20210320

HEΛPing ASDF

 ... more fixing and, ça va sans dire, more creeping features.

I got prodded to integrate HEΛP with other tools; mostly, of course, ASDF.  A simple solution was to define a document-op for a system.  After jumping through a few hoops, the solution was to use the :properties of a system to pile up arguments for the main HEΛP document function (well, only one for the time being).  Bottom line, suppose you have:

  (asdf:defsystem "foosys"
     :pathname #P"D:/Common Lisp/Systems/foosys/")

now you just issue

  (asdf:operate 'hlp:document-op "foosys")

and the documentation for the system "foosys" will appear in the "docs/html/" subfolder.

If you want to pass a title to the document function, you set up your system as:

  (asdf:defsystem "foosys"
     :properties (:documentation-title "The FOO Omnipotent Tool")
     :pathname #P"D:/Common Lisp/Systems/foosys/")

and the parameter will be used (instead of the bare system name).

It works! 😁

Some more fixing and more extensions may be needed (hlp:document takes a lot of parameters) but it is already usable.

All the necessary bits and pieces are in the HEΛP repository, and they should get into Quicklisp in the next release.

Enjoy


(cheers)

20210316

Need more HEΛP?

Just a quick note for people following these... parentheses.

I have carved out some time to do some more Lisp hacking and this lead me to look at the very nice usocket library (I want to do some network programming).  The usocket library documentation page has a bit of an "old" and "handcrafted" look and feel to it, so I tried to produce a version of the documentation with help from my HEΛP library.

Well, it turns out that usocket has some more than legitimate code within it that my HEΛP library was not handling; even worse, it unearthed a bug in the Lambda List parsing routines.

As an example, usocket uses the following idiom to set some of the documentation strings.

    (setf (documentation 'fun 'function) "Ain't this fun?") 

This is perfectly fine, but it needed some extra twist to get HEΛP do what is, IMHO, the right thing: in this case it meant ensuring that the lambda list of the function was properly rendered in the final documentation.

Apart from that, a few not so nice buglets were exposed in the code parsing lambda lists.  The result is that now the logic of that piece of code is simpler and somewhat cleaner.

So, if you want to get HEΛP to document your Common Lisp code, give it a spin.



(cheers)

20201212

With what are we contextualizing?

Common Lisp programmers may write many with-something overt their careers; the language specification itself is ripe with such constructs: witness with-open-file. Many other libraries also introduce a slew of with- macros dealing with this or that case.

So, if this is the case, what prevents Common Lisp programmers from coming up with a generalized with macro?

It appears that the question has been answered, rather satisfactorily, in Python and Julia (at least). Python offers the with statement, alongside a library of "contexts" (Python introduced the with statement in 2005 with PEP 343) and Julia offers its do blocks.

In the following I will present WITH-CONTEXTS, a Common Lisp answer to the question. The library is patterned after the ideas embodied in the Python solution, but with several (common) "lispy" twists.

Here is the standard - underwhelming - example:

      (with f = (open "foo.bar") do
         (do-something-with f))
    

That's it as far as syntax is concerned (the 'var =' being optional, obviously not in this example; the syntax was chosen to be loop-like, instead of using Python's as keyword). Things become more interesting when you look under the hood.

Traditional Common Lisp with- macros expand in variations of unwind-protect or handle-case (and friends). The example above, if written with with-open-file would probably expand into something like the following:

      (let ((f nil))
         (unwind-protect
              (progn
                 (setq f (open "foo.bar"))
                 (do-something-with f))
            (when f (close f))))
    

Python generalizes this scheme by introducing a enter/exit protocol that is invoked by the with statement. Please refer to the Python documentation on contexts and their __enter__ and __exit__ methods.

The "WITH" Macro in Common Lisp: Contexts and Protocol

In order to introduce a with macro in Common Lisp that mimicked what Python programmers expect and what Common Lisp programmers are used to some twists are necessary. To achieve this goal, a protocol of three generic functions is provided alongside a library of contexts.

The ENTER/HANDLE/EXIT Context Protocol

The WITH-CONTEXTS library provides three generic functions that are called at different times within the code resulting from the expansion of the onvocation of the with macro.

  • enter: this generic function is invoked when the with macro "enters" the context; its main argument is the result of the expression that is the argument of the with macro.
  • handle: this generic function is called to take care of exceptional situations that may arise during the call to enter or during the execution of the body of the with macro.
  • exit: this generic function is called to "clean up" before exiting the context entered by means of the with macro.

Given the protocol (from now on referred to as the "EHE-C protocol"), the (undewhelming) "open file" example expands in the following:

      (let ((f nil))
	(unwind-protect
	    (progn
	      (setq f (enter (open "contexts.lisp")))
	      (handler-case (open-stream-p f)
		(error (#:ctcx-err-e-41883)
		  (handle f #:ctcx-err-e-41883))))
	  (exit f)))
    

Apart from the gensymmed variable the expansion is pretty straightforward. The function enter is called on the newly opened stream (and is essentially an identity function) and sets the variable. If some error happens while the body of the macro is executing then control is passed to the handle function (which, in its most basic form just re-signals the condition). Finally, the unwind-protect has a chance to clean up by calling exit (which, when passed an open stream, just closes it).

One unexpected behavior for Common Lisp programmers is that the variable (f in the case above) escapes the with constructs. This is in line with what Python does, and it may have its uses. The file opening example thus has the following behavior:

    CL-prompt > (with f = (open "contexts.lisp") do
                    (open-stream-p f))
    T

    CL-prompt > (open-stream-p f)
    NIL
  

To ensure that this behavior is reflected in the implementation, the actual macroexpansion of the with call becomes the following.

      (let ((#:ctxt-esc-val-41882 nil))
        (multiple-value-prog1
	    (let ((f nil))
	      (unwind-protect
		  (progn
		    (setq f (enter (open "contexts.lisp")))
		    (handler-case
			(open-stream-p f)
		      (error (#:ctcx-err-e-41883)
			(handle f #:ctcx-err-e-41883))))
		(multiple-value-prog1
		    (exit f)
		  (setf #:ctxt-esc-val-41882 f))))
	  (setf f #:ctxt-esc-val-41882)))
    

This "feature" will help in - possibly - porting some Python code to Common Lisp.

"Contexts"

Python attaches to the with statement the notion of contexts. In Common Lisp, far as the with macro is concerned, anything that is passed as the expression to it, must respect the enter/handle/exit. protocol. The three generic functions enter, handle, exit, have simple defaults that essentially let everything "pass through", but specialized context classes have been defined that parallel the Python context library classes.

First of all, the current library defines the EHE-C protocol for streams. This is the strightforward way to obtain the desired behavior for opening and closing files as with with-open-file.

Next, the library defines the following "contexts" (as Python does).

  • null-context:
    this is a full "pass through" context, just encapsulating the expression passed to it.
  • managed-resource-context:
    this is a first cut implementation of a "managed resource", which implements also an acquire/release protocol; of course, this would become more useful in presence of mutltiprocessing (see notes in Limitations).
  • redirect-context:
    this is a context that redirects output to a different stream.
  • suppress-context:
    this is a context that selectively handles some conditions, while ignoring other ones.
  • exit-stack-context:
    this is a context that essentially allows a programmer to manipulate the "state of the computation" within it body and combine other "contexts"; to achieve its design goal, it leverages the functions of a protocol comprising the enter-context, push-context, callback, pop-all and unwind (this is equivalent to the Python close() context method).

This should be a good enough base to start working with contexts in Common Lisp. It is unclear whether the Python decorator interface would provide some extra functionality in this Common Lisp implementation of contexts and the with macro.

Limitations

The current implementation has a semantics that is obviously not the same as the corresponding Python one, but it is hoped that it still provided useful functionality. There are some obvious limitations that should be taken into account.

The current implementation of the library does not take into consideration threading issues. It could, by providing a locking-context based on a portable multiprocessing API (e.g., bordeaux-threads).

The Python implementation of contexts relies heavily on the yield statement. Again, the current implementation does not provide similar functionality, although it could possibly be implemented using a delimited continuation library (e.g., cl-cont).

Disclaimer

The code associated to these documents is not completely tested and it is bound to contain errors and omissions. This documentation may contain errors and omissions as well. Moreover, some design choices are recognized as sub-optimal and may change in the future.

License

The file COPYING that accompanies the library contains a Berkeley-style license. You are advised to use the code at your own risk. No warranty whatsoever is provided, the author will not be held responsible for any effect generated by your use of the library, and you can put here the scariest extra disclaimer you can think of.

Repository and Downloads

The with-contexts library is available on Quicklisp (not yet).

The with-contexts library. is hosted at common-lisp.net.

The git repository can be gotten from the common-lisp.net Gitlab instance in the with-macro project page.


(cheers)

20201206

Iron handling (with Emacs Lisp)

At the beginning of the pandemic I stumbled upon an article regarding the problems that the State of New Jersey was having in issuing relief checks and funding due to the lack of ... COBOL programmers.  At the time I followed a couple of links, landing on this "Hello World on z/OS" blog post.  I was curious and obviously looking for something other than my usual day job, plus, I swear, I had never written some COBOL code.

What follows is a report of the things I learned and how I solved them.  If you are easily bored, just jump to the end of this (long) post to check out the IRON MAIN Emacs Lisp package.

A Foray in the Big Iron Internet

Well, to make a long story short, I eventually installed the Hercules emulator (and other ones - more on this maybe later) in its SDL/Hyperion incarnation and installed MVS on it; the versions I installed are TK4- and a "Jay Moseley" build (special thanks to Jay, who is one of the most gracious and patient people I interacted with over the Internet).  I also installed other "big iron" OSes, e.g., MTS, on the various emulators and experimented a bit (again, maybe I will report on this later).

It has been a lot of fun, and I discovered a very lively (if grizzled) community of enthusiasts, who mostly gathers around a few groups.io groups, e.g., H390-MVS.  The community is very helpful and, at this point, very similar, IMHO, to the "Lisp" communities out there, if you get my drift.

Anyway, Back to hacking

One way to interact with "the mainframe" (i.e., MVS running on Hercules) is to write your JCL in your host system (Linux, Windows, Mac OS) and then to submit it to a simulated card reader listening over a socket (port 3505, which is meaningful to the IBM mainframe crowd).  JCL code is interesting, as is the overall forma mentis that is required to interact with the mainframe, especially for somebody who was initially taught UNIX, saw some VMS and a few hours of Univac Exec 8. In any case, you can write your JCL, where you can embed whole Assembler, COBOL, Fortran, PL/I etc code, using some editor on Windows, Linux or Mac OS etc.

Of course, Lisp guys know that there is one Editor, with its church. So, what one does is to list-all-packages and install jcl-mo...  Wait...

To the best of my knowledge, as of December 2020, there is no jcl-mode to edit JCL code in Emacs.

It immediately became a categorical imperative to build one, which I did, while learning a bit of Emacs Lisp, that is, all the intricacies of writing modes and eventually post them on MELPA.

Writing the IRON MAIN Emacs Lisp Package

Writing a major mode for Emacs in 2020 is simple in principle, but tricky in practice, especially, if, like me, you start with only a basic knowledge of the system as a user.

One starts with define-derived-mode and, in theory, things should be relatively easy from there on.  The first thing you want to do is to get your font-lock-mode specifications right.  Next you want to add some other nice visual tools to your mode.  Finally you want to package your code to play nice with the Emacs ecosystem.

Font Lock

Font Lock mode (a minor mode) does have some quirks that make it a bit difficult to understand without in depth reading of the manual and of the (sparse) examples one finds over the Internet.  Of course, one never does enough RTFM, but I believe a few key points should be reported here.

Font Lock mode does two "fontification" operations/passes.  At least this seem the way to interpret them.

  1. A search based one: where "keywords" are "searched" and "highlighted" (read: they are rendered according to the face declared for them).
  2. A syntax table one: where fontification is performed based on properties set for a given character in a syntax table.

To interact with Font Lock, a mode must eventually set the variable font-lock-defaults.  The specification of the object contained in this variable is complicated.  This variable is eventually a list with at least one element (the "keywords"); the optional second one controls whether the syntax table pass (2) is performed or not. I found that the interaction between the first two elements must be carefully planned.  Essentially you must decide whether you want only the search based ("keyword") fontification or the syntax table based (2) fontification too.

If you do not want the syntax table based (2) fontification then you want to have the second element of font-lock-defaults set to non-NIL.

The first element of font-lock-defaults is where most of the action is.  Eventually it becomes the value of the variable font-lock-keywords that Font Lock uses to perform search based fontification (1).  The full range of values that font-lock-keywords may assume is quite rich; eventually its structure is just a list of "fontificators". There are two things to note however, which I found very useful.

First, Font Lock applies each element of font-lock-keywords (i.e., (first font-lock-defaults)) in order.  This means that a certain chunk of text may be fontified more than once.  Which brings us to the second bit of useful information.

Each element that eventually ends up in font-lock-keywords may have the form

(matcher . subexp-highlighter)
where subexp-highligther = (subexp facespec [override [laxmatch]])

(see the full documentation for more details).

Fontification is not applied to chunks of text that have already been fontified, unless override is set to non-NIL.  In this case the current fontification is applied.  This is very important for things like strings and comments, which may interact in unexpected ways, unless you are careful with the order of font-lock-keywords.

I suggest you download and use the wonderful library font-lock-studio by Anders Lindgren to debug your Font Lock specifications.

Ruler mode

When you write lines, pardon, cards for MVS or z/OS it is nice to have a ruler to count on that tells you at what column you are (and remember that once you hit column 72 you'd better... continue).  Emacs has a built in nice little utility that does just that: a minor mode named ruler-mode, which shows a ruler in the top row of your buffer.

There is a snag.

Emacs counts columns from 0.  MVS, z/OS and friends count columns from 1.  Popping up the ruler of ruler-mode in a buffer containing JCL (or COBOL, or Fortran) shows that you are "one off": not nice.

Almost luckily, in Emacs 27.x (which is what I am using) you can control this behavior using the variable column-number-indicator-zero-based, which is available when you turn on the minor mode column-number-mode. Its default is t, but if you set it to nil, the columns in the buffer will start at 1, which is "mainframe friendly".  Alas, this change does not percolate (yet - it needs to be fixed in Emacs) to ruler-mode, which insists on counting from 0.

End of story: some - very minor - hacking was needed to fix the rather long "ruler function" to convince it to count columns from 1.

Packaging

Is there a good way to do this?

It appears that most Emacs "packages" are one-file affairs.  The package I wrote needs to be split up in a few files, but it is unclear (remember that I never do enough RTFM) how to keep thinks together for distribution, e.g., on MELPA or, more simply in your Emacs load-path.

What I would like to achieve is to just do a load (or a load-library) of a single file that caused the loading of the other bits and pieces.  It appears that Emacs Lisp does not have an ASDF or a MK:DEFSYSTEM as you have in Common Lisp (I will be glad to be proven wrong), so, as my package is rather small after all, I resorted to writing a main file that is named after the library and which can be thus referenced in the -pkg.el file that Emacs packaging requires.  I could have used use-package, but its intent appear to be dealing with packages that are already "installed" in your Emacs environment.

MELPA comes with it recipes format to register your package; it is a description of your folder structure and it is useful, but it is something you need to submit separately to the main site, let me add, in a rather cumbersome way. Quicklisp is far friendlier.

One other rant I have with the Emacs package distribution sites (e.g., MELPA and El-Get) is that eventually they assume you are on UN*X (Linux) and require you to have installed bits and pieces of the traditional UN*X toolchain (read: make) or worse.  I am running on W10 these days and there must be a better way.

Bottom line: I created a top file (iron-main.el) which just sets up a few things and requires and/or loads the other files that are part of or needed by the package.  One of the files contains the definition of a minor mode called iron-main-mode (in an eponymous .el file).

I am wondering whether this is the best way of doing things in Emacs Lisp.  Please tell me in the comments section.

The IRON MAIN Emacs Lisp Package

At the end of the story, here is the link to the GitHub repository for the IRON MAIN Emacs package to interact with the mainframe.

As you see the package is rather simple.

It is essentially three files plus the "main" one and a few ancillary ones.

  • iron-main.el: the main "loader" file.
  • iron-main-mode.el: the minor mode invoked by the other major modes defined below.
  • jcl-mode.el: a major mode to handle JCL files (pardon, datasets).
  • asmibm-mode.el: a major mode to handle IBM Assemblers.

One of the nice things I was able to include in jcl-mode is the ability to submit the buffer content (or another .jcl file, pardon, dataset) to the mainframe card reader listening on port 3505 (by default, assuming such a card reader has been configured).

This turns out to be useful, because it allows you to avoid using netcat, nc.exe or nc64.exe, which, at least on W10, always trigger Windows Defender.  Plus everything remains integrated with Emacs.  Remember: there's an Emacs command for that!

To conclude here are two screenshots (one "light", one "dark") of a test JCL included in the release. Submitting it from Emacs to  TK4- and to a "Jay Moseley's build" seems to work pretty well.  Just select the Submit menu under JCL OS or invoke the submit function via M-x.



What's next?  A few things apart from cleaning up, like exploring polymode; after all, embedding code in JCL is not unheard of.

That's it.  It has been fun and I literally learned a lot of new things.  Possibly useful.

If you are a mainframe person, do jump on the Emacs bandwagon.  Hey, you may want to write a ISPF editor emulator for it  😏😄