Commit graph

689 commits

Author SHA1 Message Date
Ruud van Asseldonk
c104f06b8a Simplify the parser by adding an Eof token
I learned about this in one of Matklad's posts, it's a nice idea that
simplifies things a bit.
2024-08-24 12:30:59 +02:00
Ruud van Asseldonk
c51b77a59e Add 'rcl re' and 'rcl rq' shorthands for -fraw
This is somewhat common, especially when used as jq replacement, so
let's add a shorthand for them.
2024-08-23 22:14:41 +02:00
Dennis Frenken
8f2b9bcfc6
Simplify message 2024-08-02 12:17:46 +02:00
Dennis Frenken
9d31417c50
Hint at using record syntax
In the expression `{ key: true }` (json syntax) `key` is interpreted as an
identifier whereas in `{ key = true }` (record syntax) `key` is a string literal.

This hint should highlight that possible syntax mixup.
2024-08-01 14:40:11 +02:00
Ruud van Asseldonk
9270454952 Make autoformatter add colon after else by default 2024-07-31 21:46:23 +02:00
Ruud van Asseldonk
816a00745f Optionally allow a colon after "else" in grammar
When I initially converted from the "if-then-else" keyword syntax to the
colon-based one, during development I put the colon in, then later I
removed it again, but I was still unsure. Now, after having used the
syntax for some time, my feeling is that there should be a colon after
all. Nim got it right. So put it back.

Because it's easy to stay backwards compatible here, make the colon
optional. We can make it mandatory at some point in the future, but even
making the autoformatter put it there is probably a strong enough push.
2024-07-31 21:46:23 +02:00
Ruud van Asseldonk
14cedbe90e Add friendly message for build IO error 2024-07-27 23:03:05 +02:00
Ruud van Asseldonk
75114ade17 Correct help text for "rcl build" schema 2024-07-27 23:03:05 +02:00
Ruud van Asseldonk
12ffe62cf4 Address a few minor issues in the new build code
Caught in self-review, and I don't feel like turning them into fixups
for all of the commits that introduced these.
2024-07-27 23:03:05 +02:00
Ruud van Asseldonk
768d44546f Simplify highlighting output paths in errors 2024-07-27 23:03:05 +02:00
Ruud van Asseldonk
73413a99d5 Handle sandbox policy and IO errors in build output
Report errors properly. This is quite verbose, I should make some
shorthands for formatting paths. Also check the sandbox policy while
we create the output path.
2024-07-27 23:03:05 +02:00
Ruud van Asseldonk
c611c0f28d Add more goldens to cover build subcommand 2024-07-27 23:03:05 +02:00
Ruud van Asseldonk
eabf7f267c Add tests for "rcl build" CLI parsing 2024-07-27 23:03:05 +02:00
Ruud van Asseldonk
0831fda447 Add --dry-run option to "rcl build"
As expected, the golden tests fail to run under Nix because the test
directory is not writable. And it's better to not write in my opinion,
let's not hack that and have a dry run output mode.

For now the output format is not structured, this is good enough for the
thests. It could be nice to do structured output in RCL format, but we
can do that later if needed.
2024-07-27 23:03:05 +02:00
Ruud van Asseldonk
db69504ccf Allow banner in build config to be null
I don't like this distinction that in the build file, we don't
implicitly add a newline, but on the command line we do. But making
users specify a trailing newline on the command line is annoying. So
then the RCL build spec will have to match the CLI, and append the
newline. But then empty string cannot be the default, so let's accept
null too.
2024-07-27 23:03:05 +02:00
Ruud van Asseldonk
4a2a58c5ff Restructure the 'rcl build' help text 2024-07-27 23:03:05 +02:00
Ruud van Asseldonk
56a0737349 Remove default value for 'format' in build
There is no good default value, and there is no reason this should have
a default, if you generate files, you are expecting some particular
format!
2024-07-27 23:03:05 +02:00
Ruud van Asseldonk
207d8c75bd Simplify banner support in 'rcl build'
I couldn't add a banner to the GitHub Actions yaml because the output
format is json which does not support comments. By making the banner
just a string that is up to the user to fill, everything becomes both
simpler and more general.
2024-07-27 23:03:05 +02:00
Ruud van Asseldonk
6b1a8bce1c Print status from 'rcl build'
Classic Unix tools are silent on success, but I do like to know how much
it wrote. I can imagine the output would become verbose and I'd rather
have one status line like Ninja. But for now this is okay.
2024-07-27 23:03:05 +02:00
Ruud van Asseldonk
00374ab6a0 Implement first rudimentary version of 'rcl build'
It can now build the build file in the examples!
2024-07-27 23:03:05 +02:00
Ruud van Asseldonk
80ba83e678 Add build target parsing
Wow, writing a proper deserializer for an RCL value is still a lot of
work, even in Rust! I should write a #[derive] macro for it. But having
record types would make this a lot easier already, so let's wait with
that. For now it's hand-written with a dozen error messages that I will
need to add goldens for ...
2024-07-27 23:03:05 +02:00
Ruud van Asseldonk
f9014cb1ef Add entry point for 'rcl build' command 2024-07-27 23:03:05 +02:00
Ruud van Asseldonk
85cba66c7d Add docs for new 'rcl build' subcommand
Readme-driven development here, the command doesn't exist yet!
2024-07-27 23:03:05 +02:00
Ruud van Asseldonk
4aaab52879 Add CLI option to specify --banner in output
For the 'build' subcommand, I am adding a banner setting, so for
consistency, let's have it for all evaluation commands.
2024-07-13 12:57:34 +02:00
Ruud van Asseldonk
02fd69b511 Accept Clippy advice 2024-07-13 10:44:22 +02:00
Ruud van Asseldonk
acaab0dfae Add goldens for List.sum and Set.sum
And change the way the errors are reported slightly.
2024-07-12 23:56:56 +02:00
Ruud van Asseldonk
a95959146f Add List.sum and Set.sum methods 2024-07-12 23:10:11 +02:00
Ruud van Asseldonk
38e20744d2 Define List.flat_map and Set.flat_map 2024-07-12 21:54:47 +02:00
Ruud van Asseldonk
56e9615b3a Implement List.filter and Map.filter 2024-07-11 21:14:14 +02:00
Ruud van Asseldonk
c7a7ff0902 Add builtin name to missing places
I should really make a central list of these, or generate them somehow.
2024-07-11 19:29:52 +02:00
Ruud van Asseldonk
3b9d76d317 Define List.map and Set.map builtins
When using RCL as a jq replacement, often I have some pipeline and I
want to edit the last part of the query on the command line. I don't
want to have to move my cursor all the way back to wrap the entire
expression in braces. So even though comprehensions can already do map
and filter and flatmap, I still want to add those.

This is step one, adding map.
2024-07-11 19:03:05 +02:00
Ruud van Asseldonk
44ec338877 Highlight null and bool as keyword in fmt output 2024-06-25 23:13:00 +02:00
Ruud van Asseldonk
bdd1aae4c2 Handle escaping of & into & in html output 2024-06-23 21:25:19 +02:00
Ruud van Asseldonk
107db66c59 Ensure types are colored in formatter output 2024-06-23 21:12:45 +02:00
Ruud van Asseldonk
a7201e4051 Escape < as &lt; in --color=html output 2024-06-23 21:10:25 +02:00
Ruud van Asseldonk
250a59fc9e Add an html output mode
So I can highlight stuff on my blog as long as there is no highlighting
in Pandoc/Skylighting. With the change to MarkupString, this was really
easy to do!
2024-06-23 20:51:08 +02:00
Ruud van Asseldonk
ed6542eeba Highlight "import" token as keyword
Somehow I forgot this previously.
2024-06-23 11:04:11 +02:00
Ruud van Asseldonk
7f8ae1f479 Rewrite highlight command using MarkupString
This will enable it to be used from the webassembly module, and also
gets rid of the duplication for the ansi escape codes. I tried hard to
get the Tree-sitter highlight crate to work with webassembly, but it's
just too much of a mess and debugging linker errors is one of those
things that always takes an entire night, where you feel on the verge
of a breakthrough, but in the end it fails and you end up feeling
miserable. So no Tree-sitter wasm, maybe I can just use this
highlighter, it is less robust and less fancy, but it's better than
nothing.

This temporarily removes highlighting built-ins, maybe I can restore it
again later. Or maybe I can just highlight any function call by looking
ahead for a paren.
2024-06-23 11:04:11 +02:00
Ruud van Asseldonk
b951a51ae4 Fix formatter non-idempotency due to trailing comma
Respecting the trailing comma only for collections of >= 2 elements
previously masked this issue. One special case in the formatter is that
singleton list comprehensions don't get the trailing comma. But that
means that we should ignore it for the purpose of wide/tall.

This fixes the regression introduced in a070021. The problem was
discovered by the fuzzer.
2024-06-20 21:22:32 +02:00
Ruud van Asseldonk
84523fd33c Save body span info in the loader
This fixes a longstanding issue where reporting errors that we have to
blame on just the document's result in general got blamed on its full
span, which is often a comment and not the offending value. Now we blame
it on the inner body expression, which is more natural.
2024-06-18 20:22:33 +02:00
Ruud van Asseldonk
a070021547 Make a trailing comma always force tall
At first I thought, “but a single-element collection should always fit,
right?” And sure it fits, but then I reformatted a larger experimental
config I have, including some GitHub Actions, and it turns out that
sometimes I prefer even single-element litst to be tall. Black is right
about this. I should be less opinionated, leave it to the user.
2024-06-18 19:23:40 +02:00
Ruud van Asseldonk
29c1982ec4 Format long statement chains tall even if they fit
Maybe I am nitpicking here, but I think this will benefit readability.
It's cool that you can have let bindings anywhere, but it doesn't
necessarily make things more readable when you cram everything on one
line.
2024-06-16 23:25:00 +02:00
Ruud van Asseldonk
933712cb8c Add parse error help for certain keywords
Now that full expressions are no longer allowed in some places, the
changelog mentions how to fix it, but we can actually put the help
straight into the code. And then it's helpful in all cases where you
try to use an expression and the keyword would be valid if you added
paretheses.
2024-06-16 23:16:01 +02:00
Ruud van Asseldonk
1828e566b6 Use expr_op for collection in for loops too
This change is similar to the one for conditionals. Like them, it stems
from the formatter printing a raw space before the collection, which is
problematic for non-code prefixes. Only here it did not surface as a non-
idempotency in the formatter because no non-code is allowed before the
collection. Still I think if you want to write this:

    [for x in let y = [1, 2, 3]; y: y * 2]

Then instead you should write this:

    [for x in (let y = [1, 2, 3]; y): y * 2]

It's much clearer and it creates fewer problems with formatting.
2024-06-16 22:54:24 +02:00
Ruud van Asseldonk
2a65496325 Simplify the top-level formatter
All expressions that can be multi-line are groups now, so the outer
group is no longer needed.
2024-06-16 22:49:40 +02:00
Ruud van Asseldonk
0e8c3e33c4 Handle multi-line holes in f-strings better
This is again a non-idempotency discovered by the fuzzer. Now an
expression in a hole can have blank lines or comments preceding it.

I am starting to see a pattern: all expressions (that can contain non-
code) need to be preceded by a separator. When they are preceded by
nothing (like was the case for the hole) or by a hard-coded space (like
was the case for the conditional), we may put a break or comment
directly after some content while it should go on its own line.

Are there more cases where an expression is not preceded by a separator?
I guess the fuzzer will find out!
2024-06-16 22:49:38 +02:00
Ruud van Asseldonk
43fd48d106 Restrict what expressions can be conditions
The fuzzer now discovered a non-idempotency in the formatter, in case
there is a non-code prefix for the condition. This has something to do
with the space between "if" and the condition, there is no separator
there, whatever follows goes on the same line, which is usually not the
case.

For evaluation everything works fine, but how do you format this? We can
try to repair it, but it's hard. A solution that sidesteps all this is
to restrict what kind of expressions we can have after an if. Just don't
allow statements and ifs there. We don't lose any expressivity, if you
want that it still works, just put parens around it. With parens it is
also possible to format the expression properly, e.g.

if (
  // Comments are fine, everything is indented here, etc.
  condition
):
  then_value
else
  else_value

Oh, and unrelated, I think I am convinced that I want the colon after
"else" back. But let's do that in a follow-up. Or maybe it can be
optional but the formatter always puts it there?

The implementation of this forces propagating spans in more places,
which ended up being a drive-by fix for one place where spans were
computed incorrectly. This fix shows up in the golden tests.
2024-06-16 22:49:36 +02:00
Ruud van Asseldonk
c16274de42 Remove SpanPrefixedExpr from parser
Hmm, at this point its not unanimously more elegant. When parsing a
sequence of things that may have trailing non-code, it was nice to parse
the non-code and then look at the separator. Now we have to peek over it
instead. Alternatively, I could parse it, and have a way to pass it in
to parse_expr as "seed non-code", but that is also a bit clumsy. For now
the "peek past" will do.
2024-06-16 22:49:31 +02:00
Ruud van Asseldonk
48cae78393 Remove now-redundant cases of Prefixed<Expr>
Wow! My change to make a statement expr be the top-level one that
optionally includes a prefix was a great discovery! Everything becomes
much simpler now! No need to store those prefixes separately everywhere
any more! I should have done this much earlier. And all of the golden
tests still pass with this change, it's almost magical. Also a good
demonstration of how something that ends up looking simple may not be
simple to discover.

I suspect that a similar transformation is possible with what is
currently Prefixed<Seq>, I'll look into that next.
2024-06-16 22:49:27 +02:00
Ruud van Asseldonk
a903e7610e Parse statements as a list
This changes the CST to keep a list of statements instead of making it
a degenerate tree that nests deeper for every statement. The primary
reason for doing this is to enable better pretty-printing by formatting
either the entire chain as wide or tall, and not breaking up a chain of
let bindings or assertions where some are on a line and some are not.

This is quite a deep change in one sense, but the code changes ended
up being smaller than I expected. It also ends up enabling non-code
prefixes in more places, so I think this is a good change in general.
I need to audit the parser and CST because I think there are now a few
places where a prefix is stored separately that would now be parsed into
an Expr::Statements node instead.

But what surprises me most, after I got everything to compile, all the
golden tests still pass aside from a few reformattings, and the new
format looks universally better than the old one. Wow! I think I really
discovered the "right" way to implement this!
2024-06-16 22:44:40 +02:00