We've never been using those 'expected' tokens captured during
parsing, which is lame because they contain useful information!
This is much better than merely showing our infamous
"Try removing it!"
- Trace-if-false are now completely discarded in compact mode.
- Only the label (i.e. first trace argument) is preserved.
- When compiling with tracing _compact_, the first label MUST unify to
a string. This shouldn't be an issue generally speaking and would
enforce that traces follow the pattern
```
label: arg_0[, arg_1, ..., arg_n]
```
Note that what isn't obvious with these changes is that we now support
what the "emit" keyword was trying to achieve; as we compile now with
user-defined traces only, and in compact mode to only keep event
labels in the final contract; while allowing larger payloads with
verbose tracing.
Actually, this has been a bug for a long time it seems. Calling any
prelude functions using a qualified import would result in a codegen
crash. Whoopsie.
This is now fixed as shown by the regression test.
This is not fully satisfactory as it pollutes a bit the prelude. Ideally, those functions should only be visible
and usable by the underlying trace code. But for now, we'll just go with it.
mk_starts was not yielding enough values. It's originally a translation of a double list comprehension in Haskell, which cannot simply be translated to a map2. The latter combine elements two by two, but the former works through all possible permutations.
This commit introduces a new feature into
the parser, typechecker, and formatter.
The work for code gen will be in the next commit.
I was able to leverage some existing infrastructure
by making using of `AssignmentPattern`. A new field
`is` was introduced into `IfBranch`. This field holds
a generic `Option<Is>` meaning a new generic has to be
introduced into `IfBranch`. When used in `UntypedExpr`,
`IfBranch` must use `AssignmentPattern`. When used in
`TypedExpr`, `IfBranch` must use `TypedPattern`.
The parser was updated such that we can support this
kind of psuedo grammar:
`if <expr:condition> [is [<pattern>: ]<annotation>]`
This can be read as, when parsing an `if` expression,
always expect an expression after the keyword `if`. And then
optionally there may be this `is` stuff, and within that you
may optionally expect a pattern followed by a colon. We will
always expect an annotation.
This first expression is still saved as the field
`condition` in `IfBranch`. If `pattern` is not there
AND `expr:condition` is `UntypedExpr::Var` we can set
the pattern to be `Pattern::Var` with the same name. From
there shadowing should allow this syntax sugar to feel
kinda magical within the `IfBranch` block that follow.
The typechecker doesn't need to be aware of the sugar
described above. The typechecker looks at `branch.is`
and if it's `Some(is)` then it'll use `infer_assignment`
for some help. Because of the way that `is` can inject
variables into the scope of the branch's block and since
it's basically just like how `expect` works minus the error
we get to re-use that helper method.
It's important to note that in the typechecker, if `is`
is `Some(_)` then we do not enforce that `condition` is
of type `Bool`. This is because the bool itself will be
whether or not the `is` itself holds true given a PlutusData
payload.
When `is` is None, we do exactly what was being done
previously so that plain `if` expressions remain unaffected
with no semantic changes.
The formatter had to be made aware of the new changes with
some simple changes that need no further explanation.