When testing `foo.mal`, it makes sense to report parsing errors in
`foo.mal`.
Move tests of `->` and `->>` from `tests/step8_macros.mal` to
`tests/lib/theading.mal`.
Add `lib/trivial.mal`.
I split the string-recognizing part of the read_atom regexp in two, one part for
recognising valid strings (using a fragment of the tokenising regexp) and
another to recognize invalid strings. This follows the practice of other
implementations with a single read_atom regexp.
Rather than treating anything beginning and ending with '"' as a valid
string, we now use a regexp to detect a valid string and treat any other
token starting with '"' as an error.
This does not fix all of the bugs found by #359, though: there remains a
problem with long strings of backslashes in the input getting expanded
into too many backslashes in the resulting string.
Rather than treating anything beginning and ending with '"' as a valid
string, we now use a regexp to detect a valid string and treat any other
token starting with '"' as an error.
The graphical versions work perfectly well here, so I don't think the
variable name should suggest the don't. For instance, you might run:
BRANDY=brandy make repl^bbc-basic
Brandy supports strings up to 65536 characters long, but ARM BBC BASIC V
only allows strings up to 255 characters. Mal needs strings longer than
255 characters, so we implement them by chaining together several
strings in S$() to make one logical string from mal's point of view.
This has entailed far-reaching changes as lots of routines that used to
take BASIC strings (especially in the reader/printer area) now have to
take references to mal strings instead. There are still a lot of places
that don't, though, so you can't use long strings as hash-map keys, for
instance.
This commit passes all the tests, but I wouldn't be surprised if there
are still bugs lurking somewhere.
Now that keys are sorted, it's just case of comparing the list of keys
and the list of values. In fact, two equal hash-maps will have
precisely the same internal structure, but the core library need not
know that.
Objects that have a direct reference into S$() can't safely be cloned,
because the GC assumes that when such an object is freed, the string
in S$() can be freed as well. This assumption would be broken by
cloning, so the cloning of such types by FNwith_meta must be
disallowed.
This has the unexpected, but in retrospect obvious, consequence that
single-member hashmaps can't have metadata. That's obviously a bit
silly, so a more comprehensive solution is probably warranted in due
course.
This happens when the garbage collector runs but frees no memory.
Raising a polite error explains what happens and offers a theoretical
possiblity of an application's recovering.
Rather than printing an error message and ending the program, it's
better to turn off our error handler and then re-raise the error. This
ensures that if, for instance, mal is started from the Supervisor in
RISC OS, the error gets propagated back to the Supervisor error handler
and displayed properly.
This required a slight adjustment to the top-level ON ERROR handler.
The dockerfile uses the Ubuntu source package of Brandy to build a
text-only version for testing. The "run" script no longer depends on
the existence of sbrandy in a particular place in my home directory.
Rather that recognising them by address, they now have a special bit
set in their type words. That means that adding metadata to an empty
collection doesn't cause it to cease being properly empty.
Z%(x,2) and Z%(x,3) are always pointers or nil, so there's no need to
record whether they're used: FNgc_mark can just unconditionally mark
them. This means that the only interesting question is whether Z%(x,1)
is a pointer, a string, or something else, which can be encoded in two
bits. This still leaves us using five bits for the type code, but I
think the avoidance of special-case code is worth spending a bit on.
Rather than special-casing the empty list/vector, arrange that the
dedicated empty list/vector objects have the correct values in their
first and rest pointers. Still dithering about whether to do that
to 'nil' as well.
This does mean that the internal FNfirst and FNrest don't error on an
empty list any more. Nothing should have depended on that, but it might
mask some bugs.
Functions were the only type using all three columns of Z%(). By
reducing that to two (at the expense of spreading a function over two
cells), we make space for storing a metadata pointer in Z%(x,3) of all
types.