Skip to content

bagnalla/holyc_mal

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
March 1, 2018 00:39
March 1, 2018 00:39
November 25, 2017 15:26
December 28, 2021 13:17
November 25, 2017 15:26
March 1, 2018 00:39
November 30, 2017 23:22
November 26, 2017 00:26
December 3, 2017 12:04
November 25, 2017 15:26
December 13, 2017 02:00
December 3, 2017 12:04
March 1, 2018 00:39
March 1, 2018 00:39
November 25, 2017 15:21
December 3, 2017 12:04
November 25, 2017 15:26
November 30, 2017 23:22
November 25, 2017 15:26
December 3, 2017 12:04
December 3, 2017 12:04
March 22, 2019 13:37
December 3, 2017 12:04
December 3, 2017 12:04
December 13, 2017 02:00
December 3, 2017 16:32
March 1, 2018 00:39
November 25, 2017 15:26

Mal for TempleOS.

A complete implementation (with garbage collection) of the Mal dialect of Lisp for TempleOS v5.03 written in HolyC. Mal includes macro support, tail-call optimization, file I/O, metadata on values, Clojure-style mutable reference atoms, and more. For more information about Mal, visit the main repository.

Easy setup

A Docker image is available and can be run with the following command:

sudo docker run -it --privileged --rm -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix -v /root/.Xauthority:/root/.Xauthority:rw bagnalla/mal-holyc:v1

If you see something like "could not initialize SDL", run the command xhost + and try again. Afterward, do xhost - to restore the original setting.

If you don't want to use Docker, follow the steps below for manual setup.

Installation

Here is one way to copy the files over to a TOS installation using qemu-nbd. First, mount the disk image:

sudo modprobe nbd max_part=16
sudo qemu-nbd -c /dev/nbd0 TempleOS.vdi
sudo partprobe /dev/nbd0
sudo mount /dev/nbd0p1 /mnt

Where 'TempleOS.vdi' is replaced with the name of your image. Then, copy the files to /mnt/Home/Mal or wherever you would like.

Finally, unmount the image:

sudo umount /mnt
sudo qemu-nbd -d /dev/nbd0

Running Mal

Include the file "Interp.HC" at the command line to bring Mal into scope.

Run the REPL:

mal;

Run a mal program 'prog.mal':

mal("prog.mal");

Implementation info

All tests pass and self-interpreting is successful (dramatized demonstration).

The garbage collector uses a simple mark and sweep strategy with the global environment as the root. It requires some cooperation from other parts of the code: since we must allow garbage collection to run during evaluation of terms (e.g., when self-interpreting, the main term never terminates), intermediate values not reachable from the global environment must be pushed onto a special GC stack to prevent them from being erroneously collected.

There's a rudimentary regular expression engine in Regex.HC based on Brzozowski derivatives (no finite automata).

Array.HC provides a generic dynamic array which is used internally by PArray (arrays of pointers), and String.

Lists are implemented with cons cells. Hashmaps are just association lists (but backed by arrays), so performance could probably be improved by implementing actual hash tables or some balanced binary tree structure with string interning.

There are a bunch of "unnecessary" safety checks for null pointers, but they're useful for debugging.

One problem is that the call stack for programs is relatively small in TempleOS, so the maximum recursion depth is limited. It may sometimes be necessary to write functions in tail-recursive form when it wouldn't be an issue in other implementations. I haven't found a way to increase the stack size yet.

GetStr is a convenient way to get user input, but it doesn't support ctrl+d. You can do shift+esc instead, but it kills the entire terminal session, so there is also a 'quit!' special form for cleanly exiting the REPL without closing the terminal. Just type '(quit!)'.

HolyC interop

There are two built-in functions to support HolyC interop:

  • run-holyc: JIT compile and run a HolyC source file.
  • load-extern: look up a function in the current task's symbol table and create a closure pointing to it.

The intent is to use 'run-holyc' to compile a source file containing function definitions, and then use 'load-extern' to reify them into first-class Mal values. An external function must take a list of Malvals as the argument and return a Malval. See extern/test.HC or any of the functions in Intrinsics.HC for an example. 'run-holyc' is obviously not safe since it allows execution of arbitrary HolyC code, so use at your own peril.

Performance benchmarks

Running in a VirtualBox VM. CPU is i7-4790k.

  • perf1.mal: 11 ms
  • perf2.mal: 66 ms
  • perf3.mal: 192 iters/s

Releases

No releases published

Packages

No packages published