The Simplifications Paid Off
The UCL simplifications have been implemented, and they seem to be
largely successful.
Ripped out all the streaming types, and
changed pipes to simply pass the result of the left command as first
argument of the right.
"Hello" | echo ", world" --> "Hello, world"
This has dramatically improved the use of pipes. Previously, pipes could
only be used to connect streams. But now, with pretty much anything
flowing through a pipe, that list of commands has extended to pretty
much every builtins and user-defined procs. Furthermore, a command no
longer needs to know that it's being used in a pipeline: whatever flows
through the pipe is passed transparently via the first argument to the
function call. This has made pipes more useful, and usable in more
situations.
Macros can still know whether there exist a pipe
argument, which can make for some interesting constructs. Consider this
variant of the foreach
macro, which can "hang off" the end of a pipe:
["1" "2" "3"] | foreach { |x| echo $x } --> 1 --> 2 --> 3
Not sure if this variant is useful, but I think it could be. It seems
like a natural way to iterate items passed through the pipe. I'm
wondering if this could extend to the if
macro as well, but that
variant might not be as natural to read.
Another
simplification was changing the map
builtin to accept anonymous
blocks, as well as an "invokable" commands by name. Naturally, this also
works with pipes too:
[a b c] | map { |x| toUpper $x } --> [A B C] [a b c] | map toUpper --> [A B C]
As for other language features, I finally got around to adding support for integer literals. They look pretty much how you expect:
set n 123 echo $n --> 123
One side effect of this is that an identifier can no longer start with a
dash followed by a digit, as that would be parsed as the start of a
negative integer. This probably isn't a huge deal, but it could affect
command switches, which are essentially just identifiers that start with
a dash.
Most of the other work done was behind the scenes
trying to make UCL easier to embed. I added the notion of "listable" and
"hashable" proxies objects, which allow the UCL user to treat a Go slice
or a Go struct as a list or hash respectively, without the embedder
doing anything other than return them from a function (I've yet to add
this support to maps just yet).
A lot of the native API is
still a huge mess, and I really need to tidy it up before I'd be
comfortable opening the source. Given that the language is pretty
featureful now to be useful, I'll probably start working on this next.
Plus adding builtins. Really need to start adding useful builtins.
Anyway, more to come on this topic I'm sure.
Oh, one
last thing: I've put together an online playground where you can try the
language out in the browser. It's basically a WASM build of the language
running in a JavaScript terminal
emulator. It was a little bit of a rush job and there's no reason
for building this other than it being a fun little thing to do.
You can try it out here, if
you're curious.