Paolo Amoroso's Journal

Lisp

I'm back to work on Braincons, a Brainfuck implementation I'm developing with Medley Interlisp.

The new code I wrote implements the virtual machine (VM) and executes the Braunfuck instructions. Now Braincons can create and reset a VM, load a program into it, and execute the program until completion or for a designated number of steps.

The Lisp record BRC.VM represents the VM and holds the Brainfuck program, memory, machine state such as the instruction and memory pointers, and virtual I/O devices. All the functions that operate on the VM accept a BRC.VM record as an argument, update it as necessary, and return the record.

To decouple as much as possible I/O from other subsystems, BRC.VM contains the IN and OUT fields to hold a Lisp input stream and a Lisp output stream.

Any functions that need to read data or display output on behalf of the VM can do I/O to the relevant VM streams. This adds flexibility as most Interlisp text and window output functions accept streams as arguments.

Having the book A Philosophy of Software Design fresh in my mind after recently reading it, to design the Braincons VM I deliberately sat down to think about and write a specification of how the features needed to work. Although the book presents a lot of material I haven't absorbed yet, this preliminary work helped me design a relatively small and clean VM interface and saved some code rewriting.

Now that Braincons can parse, compile, and execute Braunfuck programs, the next step will be to build the user interface for editing and running programs, as well as inspecting the VM and its state.

#braincons #Interlisp #Lisp

Discuss... Email | Reply @amoroso@fosstodon.org

I finished implementing the commands of Stringscope, the string listing tool I'm developing with Medley Interlisp.

After Sort, Reset, and Exit I wrote the code for Min Len, Find, and Get. I also added the new command Info to show in the prompt area some statistics about the strings. Coding the other commands gave me the idea and Implementing Info seemed easy, so I did it.

Now Stringscope looks like this, with the prompt area attached to the top side of the main window and the menu at the right side:

Window of the Interlisp program Stringscope with an advanced version of the command menu.

I renamed the menu item Set threshold to Min Len as the former was too wide and stood out. Min Len is more consistent with the documentation and code of Stringscope. In addition, I centered the items instead of aligning them flush left. This is the more idiomatic way most traditional Interlisp programs and system tools lay out menus.

To explain how the commands work I recorded a screencast that walks through the features and input validation of Stringscope.

I demonstrated the program by opening and interacting with two binary files, the DOCTEST.TEDIT document created with the TEdit rich text editor and the program's compiled executable STRINGSCOPE.LCOM. I also threw in some invalid data as input to show input validation in action.

After writing the code of the commands I realized Stringscope grew to support all the features I initially planned, plus a couple more I thought of along the way.

Medley Interlisp seemed overwhelming and intimidating at first but it was too much fun, so I pressed on using and studying the system. The effort paid off and now I can not only find my way around, but also create a small yet complete program with a GUI. This is a milestone to celebrate.

It's probably time to ship version 1.0 and start thinking of how to improve Stringscope and its design.

#stringscope #Interlisp #Lisp

Discuss... Email | Reply @amoroso@fosstodon.org

I'm having a lot of fun working on the commands of Stringscope, the string listing tool I'm developing with Medley Interlisp.

I implemented the Sort menu item with its subitems Ascending and Descending, as well as the Reset item. I added also the Exit item for quitting the program, a low-hanging fruit that just required calling the CLOSEW window manager function for closing the main window.

The main window of Stringscope, the command menu, and the prompt area now look like this:

Window of the Interlisp program Stringscope with an early version of the command menu.

The commands didn't require too much new code. I take it as a sign the foundation is solid, or at least not too shaky. And Interlisp's powerful menu system made things easier. But there's some code duplication I'm not satisfied with that will need refactoring.

The next commands to implement are the ones that take input from and output to the prompt area: Get, Find, and Set threshold.

#stringscope #Interlisp #Lisp

Discuss... Email | Reply @amoroso@fosstodon.org

I'm enhancing Stringscope with a permanent command menu and a prompt area. The menu, an item of which holds a submenu, attaches to the right side of the main window, the prompt area to the top of the main window above the title bar. This is what the main window looks like now:

Window of the Interlisp program Stringscope with an early version of the command menu.

This way of arranging menus and secondary windows by attaching them to a main window is typical of Interlisp programs with a GUI. The system supports this design with functions like the ones I used, CREATEMENUEDWINDOW to create and attach a menu and GETPROMPTWINDOW for doing the same with a prompt window.

The menu comprises these initial items and subitems:

  • Get: reads the strings of a new file
  • Find: searches for strings matching a specific text
  • Sort: sorts the strings in the following order
    • Ascending
    • Descending
  • Set threshold: changes the minimum length of strings
  • Reset: redisplays the strings read from the current file

The prompt window is an area for displaying status messages and receiving user input such as the name of a new file to read.

So far the Stringscope code sets up the menu and the prompt window. The menu handling function, however, is just a stub that prints to the main prompt window the selected menu item.

The next step will be to implement the commands the menu calls.

#stringscope #Interlisp #Lisp

Discuss... Email | Reply @amoroso@fosstodon.org

I moved the Stringscope project infrastructure from a GitHub gist to a full repo. The gist is now unmaintained and links to the repo.

Announcing my new Interlisp project Braincons I motivated setting up its infrastructure as a repo with a number of reasons boiling down to convenience. Code is still readble despite the formatting Interlisp encodes, source files no longer need conversion, and a full repo is more flexible. I moved Stringscope to a repo for the same reasons.

#stringscope #Interlisp #Lisp

Discuss... Email | Reply @amoroso@fosstodon.org

After Stringscope I've been working on Braincons, an Interlisp implementation of the esoteric programming language Brainfuck. The project goals are to further learn the Medley Interlisp environment I'm developing Braincons with, as well as experiment with the compilation and implementation of a very simple language.

Braincons data structures inspected in Medley Interlisp.

Status

Braincons is in an early stage of development and largely incomplete, so I don't have much to show yet.

There's no program, output window, or Executive command to run. But preliminary versions of some of the main functions and data structures are in place, which can be called and inspected from Lisp.

So far the system can only parse Brainfuck program sources, check for errors, and produce an intermediate representation a virtual machine will eventually execute.

Installation

To try out Braincons download the file BRAINCONS from the project repo, copy it to a file system your Medley Interlisp installation has access to, and evaluate the following expression from the Lisp Executive:

(FILESLOAD BRAINCONS)

Usage

At this point you can call the parser function BRC.PARSE and pass it a Brainfuck source string:

(SETQ PARSED.PROGRAM (BRC.PARSE "<>[.,]+-"))

BRC.PARSE returns the intermidiate representation of the source, then assigned to the variable PARSED.PROGRAM which you can inspect as a BRC.PROGRAM record:

(INSPECT PARSED.PROGRAM)

The inspected record and its fields should look similar to the above screenshot. This is all I have for now.

Design

I initially focused on designing the main data structures and functions upon which to build the rest of Braincons.

First, I assigned descriptive mnemonics to the Brainfuck commands as in this table:

Command Mnemonic
> NEXT
< PREV
+ INC
- DEC
. OUT
, IN
[ JZ
] JNZ

Braincons represents the source of a Brainfuck program as a string. Two data structures implemented as Interlisp records hold the intermediate representation the parser produces.

The record BRC.PROGRAM consists of an array of instructions, the index of the last instruction in the array, a list of any branch instructions in the source, and a list of any errors encountered while parsing the program. Each array entry holds a Lisp symbol of the mnemonic of the corresponding instruction, except for the branch instructions JZ (jump if zero) and JNZ (jump if not zero).

Branches are records with fields for the mnemonic, the source address in the instruction array, the destination address, and the index of the branch command in the source string.

So far Braincons has only one entry point, function BRC.PARSE, which takes a Brainfuck source string as the only argument and returns the parsed intermediate representation record BRC.PROGRAM. BRC.PARSE calls directly or indirectly some helper functions to parse individual instructions, create branch records, and check for errors.

Sharing

Along with ASCII characters, Interlisp source files contain special characters such as and control codes that encode font changes. Visualizing these files in editors and tools that expect ASCII thus shows spurious characters.

To work around this I didn't directly share the raw code of Stringscope. Instead I stripped the control and formatting codes from the source exported from Medley Interlisp, replaced the special characters with more readable versions, and published it to a GitHub gist. The code in the gist is nicely formatted for reading but Medley Interlisp can't load it as is, so I have to provide the raw source separately.

Since Interlisp programmers are used to the formatting, and cleaning it up involves additional steps, I decided to store the raw source of Braincons in a GitHub repo. This also provides the additional flexibility of a full repo.

Further development

I'm actively changing and enhancing the code of the only working feature, the parser. Although the basic design of the main data structures is stabilizing, everything else will likely need a lot of refactoring and cleanup.

Next, I'll implement the virtual machine that processes and executes the parsed program.

In the long term I'd like to build a minimal Brainfuck development environment on top of the parsing and program execution functionality. The environment will comprise a source editor based on TEdit and an interactive visualization with controls for loading and starting programs, showing the state of the virtual machine and the executing program, reading user input, and displaying the output.

#braincons #Interlisp #Lisp

Discuss... Email | Reply @amoroso@fosstodon.org

Reporting on the implementation of new commands for Stringscope I noted my confusion on why File Manager building commands such as (CLEANUP 'STRINGSCOPE) sometimes don't write to a file the compiled Lisp code.

It turns out this occurred when loading Stringscope with FILESLOAD, which loads a compiled file if available. So, even after modifying the Lisp source, the File Manager somehow skipped the compilation step as it assumed the file was up to date. Loading Stringscope with LOAD instead, which pulls the source, is usually enough to make CLEANUP write the compiled file.

It's a step forward. But I still don't understand why (CLEANUP 'STRINGSCOPE) or (MAKEFILE 'STRINGSCOPE 'C) don't always compile to a file when the source is edited in memory.

#stringscope #Interlisp #Lisp

Discuss... Email | Reply @amoroso@fosstodon.org

In addition to blogging about Medley Interlisp I also post about it on my Mastodon profile with the hashtag #interlisp. I share links, screenshots, project updates, videos, and other short content.

#Interlisp #Lisp

Discuss... Email | Reply @amoroso@fosstodon.org

I continued working on Stringscope, the string listing tool I'm developing with Medley Interlisp. I added the function STRINGS to print the strings to the primary output stream rather than in a new window, as well as an Executive command that does the same.

To reflect this work I updated the Stringscope code and documentation hosted on a GitHub gist, describing also the function EXTRACT.STRINGS as part of the interface. The code in the gist is intended for publication and can't be loaded as is into Interlisp.

The functions STRINGS and STRINGSCOPE

In addition to the same arguments as STRINGSCOPE, i.e. a file name FILENAME and the minimum length MIN.LEN strings must have, STRINGS accepts a third optional boolean argument NEWWIN. By default NEWWIN is NIL and makes STRINGS print to the primary output, otherwise the function opens a new window and works exactly like STRINGSCOPE. In fact, STRINGSCOPE is now just a wrapper that calls STRINGS with NEWWIN set to T.

STRINGSCOPE and STRINGS return a window if the functions open it otherwise the primary output stream. In case the input file can not be opened or contains no strings, the functions return NIL and print a warning to the prompt window.

Interlisp functions that create or manipulate a window usually return the window itself as their value, so I adopted a similar convention.

The macro WITH.INPUT-FILE

An early version of STRINGS duplicated a lot of the code of STRINGSCOPE for opening the input stream, checking for errors, evaluating some forms with the stream bound to a variable, and closing the stream. I factored that boilerplate into the macro WITH.INPUT.FILE:

(DEFMACRO WITH.INPUT.FILE ((STREAM FILE)
                           &BODY BODY &AUX (RESULT (GENSYM))
                           (VALUE (GENSYM)))

         (* Opens an input stream to FILE and evaluates the forms in BODY with the stream 
         bound to STREAM. Returns the value of the last form in BODY, or NIL if FILE can 
         not be opened.)

   `(PROG NIL
          [SETQ ,RESULT (NLSETQ (OPENSTREAM ,FILE 'INPUT]
          (if ,RESULT
              then (SETQ ,STREAM (CAR ,RESULT))
                   (SETQ ,VALUE (PROGN ,@BODY))
                   (CLOSEF ,STREAM)
                   (RETURN ,VALUE)
            else (RETURN NIL))))

The Executive command STRINGS

The reason I implemented a function like STRINGS that prints the output to the console is I wanted to try out creating an Executive command.

Also known as a listener in other Lisp environments, an Executive is an Interlisp window that provides a read-eval-print loop. In addition to Lisp expressions, an Executive accepts commands with a non-parenthesized syntax such as DIR (the equivalent of DIR on MS-DOS and ls on Unix) or CONN directory (cd directory on MS-DOS and Unix).

So I defined the Stringscope Executive command STRINGS that works exactly like the function STRINGS but takes only the file name and the minimum length arguments:

STRINGS FILENAME MIN.LEN

The macro DEFCOMMAND defines an Executive command and is really easy to use, here's the definition of STRINGS:

(DEFCOMMAND (STRINGS :EVAL) (FILE &OPTIONAL (MIN.LENGTH SSCOPE.MIN.LEN))
   (STRINGS FILE MIN.LENGTH)
   (CL:VALUES))

The STRINGS command is just a wrapper that calls the function STRINGS with appropriate arguments.

Compiling Stringscope

I initially run Stringscope compiled but it was time to start using the File Manager commands for compiling the code, (MAKEFILE 'STRINGSCOPE 'C) and (CLEANUP 'STRINGSCOPE). However, these commands apparently compile only in memory and don't write a compiled file like (TCOMPL 'STRINGSCOPE), which I have to call manually.

The File Manager is an Interlisp subsystem conceptually similar to the Unix tool Make. It's a collection of tools to notice, keep track of, and write to files the code changes to a Lisp system under development.

Another confusing aspect of compilation is when I load Stringscope with (FILESLOAD STRINGSCOPE), I get a warning I don't understand concerning a comment in the macro WITH.INPUT.FILE: Warning: Possible comment not stripped. I actually see the compiler couldn't strip a comment from the compiled code to save space, but not why.

Further development

Some of these new features were on my initial roadmap, others emerged while coding and using Stringscope.

Since this is an exploration and learning project, going forward I'll go with the flow and implement what I need or find interesting, and consider the initial roadmap more like a source of ideas than an action plan. Which is the kind of exploratory development Lisp supports and makes enjoyable.

#stringscope #Interlisp #Lisp

Discuss... Email | Reply @amoroso@fosstodon.org

I'm still exploring and learning Medley Interlisp but I finally grasp the basics of structure editing.

The increasing familiarity with the SEdit structure editor is making me more productive. I begin to appreciate the efficiency and elegance of the set of mouse gestures which, combined with keypresses, allow the selection and manipulation of a wide variety of Lisp and text structures.

The core of these gestures is a series of system-wide features and conventions available also in other Interlisp tools such as the TEdit rich text editor.

To demonstrate the basic structure gestures of SEdit, the default Medley Interlisp code editor, I recorded on my Chromebox this short screencast of an Interlisp Online session.

How do structure selection and manipulation work?

The video is supposed to need no explanation. I designed a few visual techniques that hopefully make the video self explanatory while reducing production time and effort.

I recorded a single cut screencast with no audio, in which the mouse pointer moves across the Medley Interlisp desktop highlighting the sequence of steps indicated in one window and carried out in another. The first is a TEdit window showing a document with the outline of the script of the video. Next to TEdit, a SEdit window is open on a Lisp source file in which I carry out the editing tasks listed in the script. Mouse gestures are deliberately slow to allow enought time to register and interpret the changes.

If you follow the mouse pointer, the only thing that moves on the screen, it should be clear what's going to happen next and what triggers an action.

#Interlisp #Lisp

Discuss... Email | Reply @amoroso@fosstodon.org