The blueprint is generated at the root of the repository and is
intended to be versioned with the rest. It acts as a business card
that contains many practical information. There's a variety of tools
we can then build on top of open-source contracts. And, quite
importantly, the blueprint is language-agnostic; it isn't specific to
Aiken. So it is really meant as an interop format within the
ecosystem.
With pretty parse errors on failures. The type-checker was already
implemented for those, so it now only requires some work in the code
generation.
Fixes#297.
This is a bit annoying as we are forced to use #[related] here which isn't quite what we want.
Ideally, this would use #[diagnostic_source] but, there's a bug upstream. See: zkat/miette#172.
In the similar spirit to what we did for sequences. Yet, we need to handle the case of body being just an assignment -- or a trace of an assignment which is basically the same thing.
Far less verbose than defining classes by hand, plus, it allows to have everything about a single error be co-located. And finally, it allows to use 'related', 'label' and so on more easily.
While Gleam originally allowed various kinds of expressions to be discarded in a sequence, we simply do not allow expressions to be discarded implicitly. So any non-final expression in a sequence must be a let-binding. This prevents silly mistakes.
- Display function's signature next to the function name
(instead of being repeated below the function documentation).
- Same for module constants
- Display record constructors in a more concise manner, with
constructors fields next to constructors.
- Display generic parameters, if any, next to the type
- Plus some minor color and icon rework.
Somehow missed it when reworking tuples. We need to allow the new
'NewLineLeftParen' token in this situation as well. Especially because
this is what the formatter outputs.
Due to how PlutusData works it doesn't make sense
to allow user defined types to contain
functions.
```
type Foo {
bar: fn(Int) -> Int
}
```
The above definition will now return an error.
There are restrictions regarding how modules are called, but given that packages are tight to repositories anyway; there's no way someone can publish and use an aiken package on 'aiken-lang' without being part of the organization. So the restriction on the command-line is pointless. Plus, it prevents us from using 'aiken-lang' as a placeholder name for tutorials.
This makes it easier to add new dependencies, without having to
manually edit the `aiken.toml` file.
The command is accessible via two different paths:
- aiken deps add
or simply
- aiken add
for this is quite common to find at the top-level of the command-line,
and, we still want to keep commands for managing dependencies grouped
under a command sub-group and not all at the top-level. So we're
merely promoting that one for visibility.
This is a bit cleaner, as the 'cmd/new' had many on-the-fly functions
which are better scoped inside this module.
Plus, it plays nicely with the std::str::FromStr trait definition.
This possibly breaks many Aiken programs out there, but it's for the
best. We haven't released the alpha yet so we still have a bit of
freedom when it comes to breaking change.
Plus, the migration path is easy, simply run:
```
find . -name "*.ak" | xargs sed -i "s/#(/(/g"
```
(or `-i ''` on MacOS).
This allows in case of issues with dependencies to at least safely
remove cached packages. Before that, it could be hard to know where
are even located the cached files without looking at the source code.
```
Clearing /Users/ktorz/Library/Caches/aiken/packages
Removing aiken-lang-stdlib-7ca9e659688ea88e1cfdc439b6c20c4c7fae9985.zip
Removing aiken-lang-stdlib-main@04eb45df3c77f6611bbdff842a0e311be2c56390f0fa01f020d69c93ff567fe5.zip
Removing aiken-lang-stdlib-6b482fa00ec37fe936c93155e8c670f32288a686.zip
Removing aiken-lang-stdlib-1cedbe85b7c7e9c4036d63d45cad4ced27b0d50b.zip
Done
```
Aiken's build system uses an internal global cache system to avoid
downloading the same packages over and over across projects. However,
prior to this commit, the cache key would be based of the dependency
version which can be either:
- A commit hash
- A branch or tag name
However, in the latter case, it means that the very first time we end
up fetching a dependency will lock its version forever (or until the
cache is cleared). This was inconvenient.
This commit changes that so that we use not only a branch name as
cache key, but additionally, the etag returned by the GitHub API
server. The etag is part of the HTTP headers, so it can be fetched
quickly using a simple HEAD request. It changes whenever the content
behind the endpoint changes -- which happens to be exactly what we
want. With this, we can quickly check whether an upstream package has
been updated and download the latest version should users have
specified a branch name as a version number.
For example, my current cache now looks as follow:
```
/Users/ktorz/Library/Caches/aiken/packages/
├── aiken-lang-stdlib-1cedbe85b7c7e9c4036d63d45cad4ced27b0d50b.zip
├── aiken-lang-stdlib-6b482fa00ec37fe936c93155e8c670f32288a686.zip
├── aiken-lang-stdlib-7ca9e659688ea88e1cfdc439b6c20c4c7fae9985.zip
└── aiken-lang-stdlib-main@04eb45df3c77f6611bbdff842a0e311be2c56390f0fa01f020d69c93ff567fe5.zip
```
This changes allow to use parenthesis `(` `)` to encapsulate
expressions in addition to braces `{` `}` used to define blocks.
The main use-case is for arithmetic and boolean expressions for which
developers are used to using parenthesis. For example:
```
{ 14 + 42 } * 1337
```
can now be written as:
```
( 14 + 42 ) * 1337
```
This may sound straightforward at first but wasn't necessarily trivial
in Aiken given that (a) everything is an expression, (b) whitespaces
do not generally matter and (c) there's no symbol indicating the end
of a 'statement' (because there's no statement).
Thus, we have to properly disambiguate between:
```
let foo = bar(14 + 42)
```
and
```
let foo = bar
(14 + 42)
```
Before this commit, the latter would be interpreted as a function call
and would lead to a somewhat puzzling error. Now, the newline serves
as a delimiting symbol. The trade-off being that for a function call,
the left parenthesis has to be on the same line as the function name
identifier -- which is a fair trade off. So this is still allowed:
```
let foo = bar(
14 + 42
)
```
As there's very little ambiguity about it.
This fixes#236 and would seemingly allow us to get rid of the leading
`#` in front of tuples.
* add unary op
* parse, typecheck, and code gen it
* express boolean not as unary op as well, previously called negate
Co-authored-by: rvcas <x@rvcas.dev>
## Before
```
× Type-checking
╰─▶ Unknown module field 'ValidityRaneg' in module 'aiken/transaction'
```
## After
```
× Type-checking
╰─▶ Unknown import 'ValidityRaneg' from module 'aiken/transaction'
╭─[../stdlib/validators/tmp.ak:2:1]
2 │ use aiken/interval.{Interval, IntervalBound, IntervalBoundType}
3 │ use aiken/transaction.{ScriptContext, ValidityRaneg}
· ─────────────
4 │
╰────
help: Did you mean to import 'ValidityRange'?
```
## Before
```
× Checking
╰─▶ Unexpected labeled argument
t
╭─[/Users/mati/Devel/OpenSource/time_lock_aiken/validators/time_lock.ak:13:1]
13 │ let now = when context.transaction.validity_range.lower_bound.bound_type is {
14 │ Finite { t } -> t
· ─
15 │ NegativeInfinity -> 0
╰────
```
## After
```
× Type-checking
╰─▶ Unexpected labeled argument 't'
╭─[../stdlib/validators/tmp.ak:10:1]
10 │ let now = when context.transaction.validity_range.lower_bound.bound_type is {
11 │ interval.Finite { t } -> t
· ─
12 │ interval.NegativeInfinity -> 0
╰────
help: The constructor 'Finite' does not have any labeled field. Its fields
must therefore be matched only by position.
Perhaps, try the following:
╰─▶ interval.Finite(t)
```
## Before
```
× Checking
╰─▶ Unknown variable
Finite
╭─[../stdlib/validators/tmp.ak:10:1]
10 │ let now = when context.transaction.validity_range.lower_bound.bound_type is {
11 │ Finite { t } -> t
· ────────────
12 │ NegativeInfinity -> 0
╰────
```
## After
```
× Type-checking
╰─▶ Unknown data-type constructor 'Finite'
╭─[../stdlib/validators/tmp.ak:10:1]
10 │ let now = when context.transaction.validity_range.lower_bound.bound_type is {
11 │ Finite { t } -> t
· ────────────
12 │ NegativeInfinity -> 0
╰────
help: Did you forget to import it?
Data-type constructors are not automatically imported, even if their type is
imported. So, if a module `aiken/pet` defines the following type:
┍━ aiken/pet.ak ━━━━━━━━
│ pub type Pet {
│ Cat
│ Dog
│ }
You must import its constructors explicitly to use them, or prefix them
with the module's name.
┍━ foo.ak ━━━━━━━━
│ use aiken/pet.{Pet, Dog}
│
│ fn foo(pet : Pet) {
│ when pet is {
│ pet.Cat -> // ...
│ Dog -> // ...
│ }
│ }
```
I am not entirely sure what the intent was for that keyword, but
nothing really matched between the parser, the formatter and the uplc
code gen. I don't think there's any need for a keyword here, trace is
already readily available from the builtins.
Before:
```
❯ aiken check
Error:
× No such file or directory (os error 2)
```
After:
```
❯ aiken check
Error:
× Missing 'aiken.toml' manifest in /Users/ktorz/Documents/Projects/aiken-lang/aiken
help: Try running `aiken new <REPOSITORY/PROJECT>` to initialise a project with an example manifest.
```
Co-authored-by: KtorZ <matthias.benkort@gmail.com>
This is the most intuitive thing I could come up with: since the
problem is mainly due to the order in which we try declaring the
aliases, then it suffices to simply try as much as we can, and retry
on failure until there's no more failure.
Note that it's important to detect cycles if we do such thing (which
we can by noticing that a given iteration didn't make any progress).
It works pretty well in the end and even allow us to define a new kind
of type error should there be a cyclic definition.
It's best to keep builtin as-close-as possible to their standard name
because they're hard to document. We can then leverage the prelude and
the standard lib for convenient names.
This is because there's no proper way to catch panics in Rust, which
makes it hard to know _which_ test did cause the panic when this
happen. The stack trace gives little detail about this, but we can
print this information before it happens -- making it easier to
identify the culprit.
- [x] Display function arguments using a newline-multiline strategy
when the signature gets too long. For example:
```
union_with
( left left: AssocList<key, value>
, right right: AssocList<key, value>
, with with: fn(key, value, value) -> value
) -> AssocList<key, value>
```
- [x] Show type-aliases as type-aliases in signatures; provided
they've been specified as type annotations. Otherwise, fallback to
the inferred type.
- [x] Do not show argument names in signatures, but show labels when
they're present. This reflects more the original intent behind
labels (which are meant as public-facing documentation).
Was introduced as a work-around to get some debugging info out of scripts, but tests do now provide the same capability with a better output and, do so automatically.
- Update generics syntax
- Add required args to default validator function
This allows running a successful aiken build from
files generated by aiken new.
Pretty useful for debbugging. Though, on second-thoughts, this is
something we may want to review later and maybe have that done by
default for tests.
At the moment, we expects tests to unify to `bool`, and treat `false`
values as failing tests. Yet, on failures, this gives little
information about what's wrong with the test.
It'd be nice to either have better way to assert in tests, or, to
simply accept non-bool tests, and show whatever the test evaluates
to as a debug output.
This is an example of output from the formatter now:
```
//// Some module documentation
// foo
const a: Int = 42
// Some comment block
// For which newlines are respected.
// Foo
// Another one
/// add_one documentation
pub fn add_one(n: Int) -> Int {
// n + 1
n + 1
}
```
before this commit, comments would all be collapsed into one group
above the function as:
```
// Some comment block
// For which newlines are respected.
// Foo
// Another one
/// add_one documentation
pub fn add_one(n: Int) -> Int {
```
Tests are basically functions for which the return type should unify with bool. In principle, the type checker could also check that a test function has no arguments but, a test function with arguments wouldn't parse in the first place; feels a bit hacky but it works when considering the pipeline as a whole.
Note that the code generation is still to be done.
Supersedes #35.
The syntax for these elements isn't "set in stone"; in the sense that it is unspecified in [input-output-hk/plutus](https://github.com/input-output-hk/plutus). There's no visible plan from IOG to extend the Haskell parser to support this syntax, though there are samples of imagined syntax in the code. Thus, we can lead the way and simply choose a suitable syntax and let the Haskell implementation align to it later.
This syntax is thus inspired from input-output-hk/plutus' samples, with only a small change: we use `<` and `>` for encapsulating type declaration instead of `(`, `)`. There are already enough parentheses in the UPLC syntax, adding more reduces visibility.
Doing this, I've also added a lot more test cases for the UPLC parser. There could be more, but this is a good start.
Here are some example programs (taken from test cases) utilizing this syntax:
```
(program 0.0.0 (con list<bytestring> [#00, #01]))
```
```
(program 0.0.0
(con pair
<integer, integer>
[14, 42]
)
)
```
```
(program 0.0.0
(con pair<string, list<integer>> ["foo", [14, 42]])
)
```
_(Note that this was mainly done as an exercise to get more familiar with Rust and parts of Aiken.)_