Announcing uLisp Release 4.8


#1

The latest release of uLisp, 4.8 features an improved implementation of streams, Lisp’s way of providing a unified interface to input and output protocols such as Serial, I2C, SPI, SD Cards, Wi-Fi, strings, or graphics.

Following a suggestion made by user @dragoncoder047 streams now use a lookup table. This is not only more efficient, but provides the additional benefit that you can extend uLisp by defining custom streams.

For example, you could write an Extensions File that implements the 1-Wire protocol and a 1-Wire stream, allowing you to use functions such as print, format, and read to communicate over 1-Wire. I have provided an example Extensions File that implements the Lisp form with-input-from-string, not currently a standard part of uLisp, by implementing a string input stream.

For more information about the implementation see Streams.


#2

That’s a very good explanation and now I understand how to work with ulisp streams better than I did before. with-input-from-string looks really useful. That said I have several requests and questions. The first thing I thought of was having a function that let you read up to your chosen delimeter instead of just a newline. It turns out ulisp has this internally as readstring but its not exposed. So here’s my attempt at providing it:

object *fn_readuntil (object *args, object *env) {
  (void) env;
  object *obj = first(args);
  gfun_t gfun = gstreamfun(rest(args)); 
  char delim = checkchar(obj);
  return readstring(delim, false, gfun);
}

const char string_readuntil[] PROGMEM = "read-until";

const char docreaduntil[] PROGMEM = "(read-until delim [stream])\n"
"Reads characters from the serial input up to the specifed delimiter character,\n"
"and returns them as a string, excluding the delimiter.\n"
"If stream is specified the line is read from the specified stream.";

//const tbl_entry_t lookup_table2[] PROGMEM = {
  { string_readuntil, fn_readuntil, 0212, docreaduntil },
//};

My second issue is with the way read-line (and read-until) handles string streams that don’t terminate in a newline (or delimiter), where they will show an error and return nothing. I’m not sure if that’s the most useful behavior because I would think that not finding a line or delimiter would be something that the function would expect. I suppose one can always append a newline or delimiter to the end if they don’t want the function to fail, but that seems awkward. I don’t know how to handle that scenario any better though, and that seems to be how Common Lisp handles it as well? For Arduino you get functions like find, findUntil, peek, and available. Would it make sense to implement that functionality here to help deal with that? Or what’s a good way to deal with this?


#3

Yes, I wasn’t sure how best to handle reading past the end of a string (or any other stream), so I copied what Common Lisp does and gave an error, which as you say may not be the most useful option in an Arduino world.

The Wi-Fi functions already provide available. Perhaps that should be provided for all streams? However that wouldn’t help with using read-line on a string-input-stream with no final newline.

Alternatively you could perhaps use ignore-errors to catch the end-of-stream error.


#4

So I came ended up coming up with this function for StringRead, which just returns nil when there’s nothing left in the buffer. Apparently you have to return -1 to the readstring function for it to return nil. The other streams like the SD card, I2C, and SPI do that as well from what I understand.

int StringRead () {
  char c = 0;
  if(InputString != NULL){
    c =  nthchar(InputString, InputStringIndex);
  }
  if (c == 0) { 
    InputString = NULL;
    InputStringIndex = 0;
    return -1;
  }
  else { InputStringIndex++; }
  return c;
}