I've added a few things that might be interesting


I’ve been hacking on uLisp for a couple months and have a few things that might be of interest. For now this is only in my fork at https://github.com/dastels/ulisp-arm.

Some LispLibrary magic to provide a “load-file” function as well as autoloading a list of files on startup.

Support for the Bounce2 library to allow adding debouncers on input pins.

LIS3DH support in C, but I want to move that to lisp if/as possible. It’s mainly for a project I’m working on.

A relatively complete implementation of Scheme’s format function.

Simple macros: quasiquote, unquote, unquote-splicing, defmacro, macro, and expand. The first 3 have syntactic shortcuts as usual (backquote, comma, at - at is used instead of the usual comma-at due to complication in the lexer).

Added tinyusb support along side SDCard support (used in exactly the same way, using “flashfs” in place of “sd”. Tiny USB provides a flash file system using the external SPI flash storage present on many boards, notably Adafruit’s “Express” boards. Not only that, but it provides access to that flash filesystem directly via mounting as a USB drive. You can copy files to it by drag & drop, etc. as well as edit files directly on it. Eventually reload-on-change support will be added.

Added gensym and intern functions.

Added keywords (symbols that begin with a colon). There always evaluate to themselves and can’t be rebound.

Using several of the above additions, I ported/simplified a common lisp logging package:

(defvar *LOGGER:LEVEL* 3)

(defun logger:set-level (level)
  (setf *LOGGER:LEVEL* level))

(defun logger:do-log (level-name log-level format-str &rest args)
  (when (<= log-level *LOGGER:LEVEL*)
    (let ((full-format (concatenate 'string "~A - ~A (~A): " format-str)))
      (dolist (arg (list log-level level-name (millis)))
        (push arg args))
      (eval `(format t ,full-format @args)))))

(defmacro logger:define-level (name level-value)
  (let ((macro-name (intern (format nil "log~a" name))))
    `(defmacro ,macro-name (format-str &rest args)
       `(logger:do-log ,,name ,,level-value ,format-str @args))))

(logger:define-level :emerg 1)
(logger:define-level :alert 2)
(logger:define-level :crit 3)
(logger:define-level :error 4)
(logger:define-level :warn 5)
(logger:define-level :notice 6)
(logger:define-level :info 7)
(logger:define-level :debug 8)
(logger:define-level :debug1 9)
(logger:define-level :debug2 10)
(logger:define-level :debug3 11)
(logger:define-level :debug4 12)


Serpente - an ideal mini board for uLisp?

Hmmm… @dastels, @johnsondavies, the ability to manipulate strings<->symbols may, indeed, be useful. A friend of mine and I recently had a discussion on how to parse a string into a list, and I quickly came up with the below, which returns a list of space-separated words, with “remainder” if the last word is not followed by a space. Long story short: INTERN & character treatment really allows “parsing to symbols”.

(defun proto-tkn (listr remstr toks)

    ((null listr) (return (list (reverse toks) (coerce (reverse remstr) 'string))))

    ((eq (car listr) #\Space)
       (cond ((not (null remstr))
              (push (intern (coerce (reverse remstr) 'string)) toks)
              (setq remstr '())))))

    (t (push (car listr) remstr)))

   (pop listr)))

(defun tkn (instri)
   (coerce (string-upcase instri) 'list) '() '()))

(print (tkn "hi there now"))

(print (tkn "hi there now "))


There’s a neat trick for doing something similar in uLisp using read-from-string:

(defun tokenise (str) (read-from-string (concatenate 'string "(" str ")")))

For example:

> (tokenise "hi there now")
(hi there now)


David, you admirable genius, this is AWESOME! I assume case will not be an issue with the symbols, is that correct?

I am fiddling here with my next AI, that is why I was interested… now the user no longer needs to input necessarily a “list”…


Most symbols will be OK, but the user could confuse the tokeniser by entering unmatched parentheses.