OSH is a bash-compatible shell: https://www.oilshell.org/
reader.sh:
- Put the constant regex pattern in a string literal. This simplifies
it by removing mixed regex vs. shell quoting, and implicit
concatenation with $'\n'.
This is suggested by the bash manual:
https://www.gnu.org/software/bash/manual/bash.html#Conditional-Constructs
"Storing the regular expression in a shell variable is often a useful
way to avoid problems with quoting characters that are special to the
shell."
- Initialize __reader_tokens as an array, not a string.
https://www.oilshell.org/release/0.8.pre6/doc/known-differences.html#values-are-tagged-with-types-not-cells
env.sh:
Simplify quoting in 'eval' expressions. This quotes associative array keys,
which is required by OSH to avoid dynamic parsing.
https://www.oilshell.org/release/0.8.pre6/doc/known-differences.html#strings-vs-bare-words-in-array-indiceshttp://www.oilshell.org/blog/2016/10/20.html
core.sh:
Quote associative array keys. '<' and '>' are shell operators and OSH doesn't
have a special case when inside [].
----
With this change, OSH can run tests just like bash, e.g.:
$ osh impls/bash/stepA_mal.sh tests/step4_if_fn_do.mal
----
Test results are the same before and after this change:
$ NO_DOCKER=1 ./.travis_test.sh test bash
FAILURES:
SOFT FAILED TEST (line 295): (f (+ 1 1)) -> ['',true]:
Expected : '.*\ntrue'
Got : '(f (+ 1 1))\nfalse'
TEST RESULTS (for ../tests/stepA_mal.mal):
1: soft failing tests
0: failing tests
106: passing tests
107: total tests
by booting up multiple instancees of saxon, waiting for a signal
This doesn't really help much, since most of our tests are very small
however, it does lower the overall boot time (assuming travis can do
real concurrency)
Atoms still need some ironing out, something is not updating them
properly
Also unknown is an issue where `def!` is not updating the env, probably
related to the above problem
This is achieved simply by propagating the code to detect empty vectors
(and lists, though I suspect that is unused) back from step 4 to the
earlier steps.
An empty vector or hash-map should evaluate to itself, but I reached
the test of (empty? []) in step 4 before I discovered that I'd got []
evaluating to 0. Add tests for both cases to step 2.
PostScript's exception handling doesn't restore the dictionary or
operand stacks to the state they were in when the "stopped" operator
started, so mal's EVAL needs to do that itself. To do this, it records
the current height of the stacks, but of course it does that in a
dictionary. This means that when catching at exception, it relies on
the highest instance of "dictcnt" on the dictionary stack being the
correct one. EVAL, however, was failing to restore the dictionary stack
at the right time.
For instance, conside this code (from the tests):
(try* (try* (throw "e1") (catch* e (throw "e2"))) (catch* e "c2"))
Each "try*" clause saves "dictcnt" into a dictionary on the dictionary
stack. When the inner "catch*" clause fires, it pops the dictionary and
operand stacks to the correct point, but then calls the second "throw"
with the wrong value of "dictcnt" still visible. The result is that the
"catch*" clause ends up running with the wrong value of "dictcnt" and
restoring the stacks to the wrong place, essentially executing the
_inner_ "catch*" clause again, whereupon the error doesn't get caught
because there's no "stopped" left on the PostScript execution stack.
The fix is to add another dictionary that's just used to hold "dictcnt"
and "stackcnt", and to pop that from the dictionary stack as soon as the
stacks have been restored (or when it becomes unnecessary because the
"try*" clause has returned normally).
When implementing exceptions on top of longjmp() or similar, it's
necessary to make sure that exception handlers get properly restored
after the try* clause, and none of the existing step 9 tests covered
this.
There are two cases covered here: throwing an exception after a
successful try*, and throwing and exception from within a catch*. In
both cases we make sure that an outer catch* does its job.
All of the other read-string tests test forms whose end can be detected
before the end of the input string, either because they're naturally
self-terminating (lists, strings) or because there's white space after
them. All of the uses of the reader from the REPL are similarly likely
to have input with a newline or similar at the end.
My BCPL implementation turned out to read one character past the end of
the input when the input ended inside an atom, and to incorporate that
one byte into the output. The consequences of a buffer over-read are a
little unpredictable, so I've just written a test that was good enough
to catch the bug for me.
- Reorder README to have implementation list after "learning tool"
bullet.
- This also moves tests/ and libs/ into impls. It would be preferrable
to have these directories at the top level. However, this causes
difficulties with the wasm implementations which need pre-open
directories and have trouble with paths starting with "../../". So
in lieu of that, symlink those directories to the top-level.
- Move the run_argv_test.sh script into the tests directory for
general hygiene.