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 -> // ...
│ }
│ }
```