1
1
mirror of https://github.com/tweag/nickel.git synced 2024-08-16 15:10:25 +03:00

Update lsp notes (#1660)

This commit is contained in:
jneem 2023-10-02 09:29:20 -05:00 committed by GitHub
parent 432983cd10
commit 7f1be1812f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -79,59 +79,32 @@ Let's describe a few simple cases first:
The algorithm seems to be mainly type-directed. For example, annotating `foo()`
as `function foo(): any { ... }` breaks going to the definition of `foo`
### Proposed algorithm
### Proposed field resolution algorithm
The core part of the algorithm is a function mapping terms to sets of
record literals, giving for each term the set of record literals that might
"contribute" (through, e.g., merging) to that term. This will only be an
approximation, of course, because we aren't evaluating. Let's call this process
"record literal resolution," and it is defined by:
One of the main tasks is resolving field references: what does `bar`
refer to in `foo.bar`? To answer this, we describe an algorithm for mapping
terms to sets of records (more precisely, `RecordData` structs). For
each term, this algorithm computes the set of records that might
"contribute" (through, e.g., merging) to that term. To answer what `bar`
refers to in `foo.bar`, we first figure out the records that `foo` maps to
and then see which of those records has a `bar` field. This extends naturally
to a recursive algorithm for resolving `foo.bar.baz`.
- a record literal resolves to itself
- a variable resolves to the term that it's bound to (e.g. in a let binding)
- an import statement resolves to the contents of the file that was imported
- a merge resolves to the union of the resolutions of the two merged terms
- an if-then-else resolves to the union of the resolutions of the two branches
- a function application resolves to the resolution of its body (with the
function arguments bound appropriately). There probably needs to be some
protection against recursion (TBD)
- everything else resolves to the empty set (but this can be extended later)
Here is some pseudocode roughly corresponding to what's above: we have
a struct `DefInfo` that represents some object with fields having locations:
```rust
struct DefInfo {
/// What are the definitions available on this field? There could be many:
/// in the presence of a merge, a field might be defined in both branches.
definitions: HashMap<Ident, Vec<DefInfo>>,
// The location of the definition, if any.
body: TermPos,
}
```
Then we have a function `field_infos(RichTerm or Type) -> DefInfo` that looks something like:
Let's call this process "record resolution." In pseudocode, we have a function
`resolve(Term) -> RecordData` that looks something like:
```text
field_infos(e1 & e2) = field_infos(e1) U field_infos(e2)
field_infos(let x = e1 in e2) = field_infos(e2)
field_infos(fun x => body) = field_infos(body)
field_infos(head x) = field_infos(head)
field_infos(var) = field_infos(goto_definition(var))
field_infos(e1 | C) = field_infos(e1) U field_infos(C)
field_infos(foo.bar) = field_infos(goto_definition(bar in foo.bar))
field_infos(Term::RecRecord) = ... the actual fields defined on the record
field_infos(TypeF::Record) = ... the actual fields defined on the record type
field_infos(TypeF::Dict) = ... every possible identifier as a field (represented
lazily, obviously)
resolve(Term::RecRecord) -> itself
resolve(e1 & e2) = resolve(e1) U resolve(e2)
resolve(e1 | C) = resolve(e1) U resolve(C)
resolve(let x = e1 in e2) = resolve(e2)
resolve(fun x => body) = resolve(body)
resolve(head x) = resolve(head)
resolve(var) = look up var in the environment, and call resolve on the answer
resolve(foo.bar) = figure out which term(s) foo.bar refers to, and flat_map resolve over them
other cases => empty
```
Now to find the definition of `baz` in `foo.bar.baz`, we first resolve `foo`
to a set of record literals. For each of those literals containing a `bar` field,
we resolve their values to a set of record literals. For each of those literals
containing a `baz` field, we report it as a definition of `baz`.
## Goto type definition
Since we cannot (yet) name custom types, it probably doesn't make sense to use "goto type definition"