Skip to content

Commit 05691de

Browse files
committed
adjust documentation to match current state
1 parent 0643708 commit 05691de

File tree

6 files changed

+51
-31
lines changed

6 files changed

+51
-31
lines changed

book/src/SUMMARY.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
- [`formality_core`: the Formality system](./formality_core.md)
55
- [Defining your lang](./formality_core/lang.md)
66
- [Defining terms with the `term` macro](./formality_core/terms.md)
7-
- [Parsing reference](./formality_core/parse.md)
8-
- [Implementing `Fold` and `Visit` by hand](./formality_core/impl_fold_visit.md)
9-
- [Implementing `Parse` by hand](./formality_core/impl_parse.md)
7+
- [Parsing](./formality_core/parse.md)
8+
- [Customizing debug](./formality_core/debug.md)
109
- [Variables](./formality_core/variables.md)
1110
- [Collections](./formality_core/collections.md)
1211
- [Judgment functions and inference rules](./formality_core/judgment_fn.md)

book/src/formality_core/debug.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Customizing the debug
2+
3+
By default, the `#[term]` macro will generate a `Debug` impl that is guided by the `#[grammar]` attributes on your type (see the [parsing](./parse.md) section for more details). But sometimes you want to generate custom logic. You can include a `#[customize(debug)]` declaration to allow that. Most of the type, when you do this, you will also want to [customize parsing](./parse.md#customizing-the-parse), as the `RigidTy` does:
4+
5+
```rust
6+
{{#include ../../../crates/formality-types/src/grammar/ty.rs:RigidTy_decl}}
7+
```
8+
9+
Now you must simply implement `Debug` in the usual way. Here is the `RigidTy` declaration:
10+
11+
12+
```rust
13+
{{#include ../../../crates/formality-types/src/grammar/ty/debug_impls.rs:RigidTy_impl}}
14+
```

book/src/formality_core/impl_fold_visit.md

Lines changed: 0 additions & 22 deletions
This file was deleted.

book/src/formality_core/parse.md

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@ struct MyEnum {
1919
}
2020
```
2121

22-
### Ambiguity,
22+
### Ambiguity and precedence
2323

24-
When parsing an enum there will be multiple possibilities. We will attempt to parse them all. If more than one succeeds, the parse is deemed ambiguous and an error is reported. If zero succeed, we will report back a parsing error, attempting to localize the problem as best we can.
24+
When parsing an enum there will be multiple possibilities. We will attempt to parse them all. If more than one succeeds, the parser will attempt to resolve the ambiguity. Ambiguity can be resolved in two ways:
25+
26+
* Explicit precedence: By default, every variant has precedence 0, but you can override this by annotating variants with `#[precedence(N)]` (where `N` is some integer). This will override the precedence for that variant. Variants with higher precedences are preferred.
27+
* Reduction prefix: When parsing, we track the list of things we had to parse. If there are two variants at the same precedence level, but one of them had to parse strictly more things than the other and in the same way, we'll prefer the longer one. So for example if one variant parsed a `Ty` and the other parsed a `Ty Ty`, we'd take the `Ty Ty`.
28+
29+
Otherwise, the parser will panic and report ambiguity. The parser panics rather than returning an error because ambiguity doesn't mean that there is no way to parse the given text as the nonterminal -- rather that there are multiple ways. Errors mean that the text does not match the grammar for that nonterminal.
2530

2631
### Symbols
2732

@@ -37,19 +42,33 @@ A grammar consists of a series of *symbols*. Each symbol matches some text in th
3742
* Nonterminals can also accept modes:
3843
* `$field` -- just parse the field's type
3944
* `$*field` -- the field must be a `Vec<T>` -- parse any number of `T` instances. Something like `[ $*field ]` would parse `[f1 f2 f3]`, assuming `f1`, `f2`, and `f3` are valid values for `field`.
40-
* If the next token after `$*field` is a terminal, `$*` uses it as lookahead. The grammar `[ $*field ]`, for example, would stop parsing once a `]` is observed.
41-
* If the `$*field` appears at the end of the grammar or the next symbol is a nonterminal, then the type of `field` must define a grammar that begins with some terminal or else your parsing will likely be ambiguous or infinite.
4245
* `$,field` -- similar to the above, but uses a comma separated list (with optional trailing comma). So `[ $,field ]` will parse something like `[f1, f2, f3]`.
43-
* Lookahead is required for `$,field` (this could be changed).
4446
* `$?field` -- will parse `field` and use `Default::default()` value if not present.
4547

4648
### Greediness
4749

48-
Parsing is generally greedy. So `$*x` and `$,x`, for example, consume as many entries as they can, subject to lookahead. In a case like `$*f $*g`, the parser would parse as many `f` values as it could before parsing `g`. If some entry can be parsed as either `f` or `g`, the parser will not explore all possibilities. The caveat here is lookahead: given `$*f #`, for example, parsing will stop when `#` is observed, even if `f` could parse `#`.
50+
Parsing is generally greedy. So `$*x` and `$,x`, for example, consume as many entries as they can. Typically this works best if `x` begins with some symbol that indicates whether it is present.
4951

5052
### Default grammar
5153

5254
If no grammar is supplied, the default grammar is determined as follows:
5355

5456
* If a `#[cast]` or `#[variable]` annotation is present, then the default grammar is just `$v0`.
5557
* Otherwise, the default grammar is the name of the type (for structs) or variant (for enums), followed by `()`, with the values for the fields in order. So `Mul(Expr, Expr)` would have a default grammar `mul($v0, $v1)`.
58+
59+
### Customizing the parse
60+
61+
If you prefer, you can customize the parse by annotating your term with `#[customize(parse)]`. In the Rust case, for example, the parsing of `RigidTy` is customized ([as is the debug impl](./debug.md)):
62+
63+
```rust
64+
{{#include ../../../crates/formality-types/src/grammar/ty.rs:RigidTy_decl}}
65+
```
66+
67+
You must then supply an impl of `Parse` yourself. Because `Parse` is a trait alias, you actually want to implement `CoreParse<L>` for your language type `L`. Inside the code you will want to instantiate a `Parser` and then invoke `parse_variant` for every variant, finally invoking `finish`.
68+
69+
In the Rust code, the impl for `RigidTy` looks as follows:
70+
71+
72+
```rust
73+
{{#include ../../../crates/formality-types/src/grammar/ty/parse_impls.rs:RigidTy_impl}}
74+
```

crates/formality-types/src/grammar/ty/debug_impls.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::{AliasName, AliasTy, AssociatedTyName, Parameter, RefKind, RigidName, RigidTy};
22
use std::fmt::Debug;
33

4+
// ANCHOR: RigidTy_impl
45
impl Debug for RigidTy {
56
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67
let RigidTy { name, parameters } = self;
@@ -42,6 +43,7 @@ impl Debug for RigidTy {
4243
}
4344
}
4445
}
46+
// ANCHOR_END: RigidTy_impl
4547

4648
impl Debug for AliasTy {
4749
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {

crates/formality-types/src/grammar/ty/parse_impls.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,18 @@ use super::{AliasTy, AssociatedTyName, Lt, Parameter, RigidTy, ScalarId, Ty};
1212

1313
use crate::rust::FormalityLang as Rust;
1414

15+
// ANCHOR: RigidTy_impl
16+
// Implement custom parsing for rigid types.
1517
impl CoreParse<Rust> for RigidTy {
1618
fn parse<'t>(scope: &Scope<Rust>, text: &'t str) -> ParseResult<'t, Self> {
1719
let mut parser: Parser<'_, '_, RigidTy, Rust> = Parser::new(scope, text, "AliasTy");
1820

21+
// Parse a `ScalarId` (and upcast it to `RigidTy`) with the highest
22+
// precedence. If someone writes `u8`, we always interpret it as a
23+
// scalar-id.
1924
parser.parse_variant_cast::<ScalarId>(1);
2025

26+
// Parse something like `Id<...>` as an ADT.
2127
parser.parse_variant("Adt", 0, |p| {
2228
let name: AdtId = p.nonterminal()?;
2329
let parameters: Vec<Parameter> = parse_parameters(p)?;
@@ -27,6 +33,7 @@ impl CoreParse<Rust> for RigidTy {
2733
})
2834
});
2935

36+
// Parse `&`
3037
parser.parse_variant("Ref", 0, |p| {
3138
p.expect_char('&')?;
3239
let lt: Lt = p.nonterminal()?;
@@ -64,6 +71,7 @@ impl CoreParse<Rust> for RigidTy {
6471
parser.finish()
6572
}
6673
}
74+
// ANCHOR_END: RigidTy_impl
6775

6876
impl CoreParse<Rust> for AliasTy {
6977
fn parse<'t>(scope: &Scope<Rust>, text: &'t str) -> ParseResult<'t, Self> {

0 commit comments

Comments
 (0)