Skip to content

Commit bddb2d0

Browse files
committed
modify #[variable] to take kind explicitly
The `variable` parser method is now simpler and just returns a variable; this also lets `Variable` implement `Parse`. We now have the helper `variable_of_kind`. This is all useful in Dada which wants to parse variables in slightly more flexible ways and in particular *doesn't* want to force them to be downcasted. Overall it seems more obvious.
1 parent 1dfb53b commit bddb2d0

File tree

12 files changed

+122
-63
lines changed

12 files changed

+122
-63
lines changed

crates/formality-core/src/language.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use crate::cast::UpcastFrom;
22
use crate::term::CoreTerm;
33
use crate::variable::{CoreBoundVar, CoreExistentialVar, CoreUniversalVar, CoreVariable};
4-
use crate::DowncastTo;
54
use std::fmt::Debug;
65
use std::hash::Hash;
76

@@ -21,8 +20,7 @@ pub trait Language: 'static + Copy + Ord + Hash + Debug + Default {
2120
+ UpcastFrom<CoreVariable<Self>>
2221
+ UpcastFrom<CoreUniversalVar<Self>>
2322
+ UpcastFrom<CoreExistentialVar<Self>>
24-
+ UpcastFrom<CoreBoundVar<Self>>
25-
+ DowncastTo<CoreVariable<Self>>;
23+
+ UpcastFrom<CoreBoundVar<Self>>;
2624

2725
/// The token (typically `<`) used to open binders.
2826
const BINDING_OPEN: char;

crates/formality-core/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ macro_rules! declare_language {
164164
where
165165
T: Parse,
166166
{
167-
term_with(None::<(String, $param)>, text)
167+
term_with(None::<(String, grammar::Variable)>, text)
168168
}
169169

170170
/// Parses `text` as a term with the given bindings in scope.
@@ -175,7 +175,7 @@ macro_rules! declare_language {
175175
pub fn term_with<T, B>(bindings: impl IntoIterator<Item = B>, text: &str) -> $crate::Fallible<T>
176176
where
177177
T: Parse,
178-
B: $crate::Upcast<(String, <FormalityLang as Language>::Parameter)>,
178+
B: $crate::Upcast<(String, grammar::Variable)>,
179179
{
180180
$crate::parse::core_term_with::<FormalityLang, T, B>(bindings, text)
181181
}

crates/formality-core/src/parse.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ use std::sync::Arc;
33
use crate::{
44
binder::CoreBinder,
55
collections::Set,
6-
language::{CoreKind, CoreParameter, Language},
6+
language::{CoreKind, Language},
77
set,
88
term::CoreTerm,
9-
variable::CoreBoundVar,
9+
variable::{CoreBoundVar, CoreVariable},
1010
Fallible, Upcast, UpcastFrom, Upcasted,
1111
};
1212
use std::fmt::Debug;
@@ -35,7 +35,7 @@ pub fn core_term_with<L, T, B>(bindings: impl IntoIterator<Item = B>, text: &str
3535
where
3636
T: CoreParse<L>,
3737
L: Language,
38-
B: Upcast<(String, CoreParameter<L>)>,
38+
B: Upcast<(String, CoreVariable<L>)>,
3939
{
4040
let scope = Scope::new(bindings.into_iter().map(|b| b.upcast()));
4141
let parse = match T::parse(&scope, text) {
@@ -170,19 +170,19 @@ pub type TokenResult<'t, T> = Result<(T, &'t str), Set<ParseError<'t>>>;
170170
/// Tracks the variables in scope at this point in parsing.
171171
#[derive(Clone, Debug, Default)]
172172
pub struct Scope<L: Language> {
173-
bindings: Vec<(String, CoreParameter<L>)>,
173+
bindings: Vec<(String, CoreVariable<L>)>,
174174
}
175175

176176
impl<L: Language> Scope<L> {
177177
/// Creates a new scope with the given set of bindings.
178-
pub fn new(bindings: impl IntoIterator<Item = (String, CoreParameter<L>)>) -> Self {
178+
pub fn new(bindings: impl IntoIterator<Item = (String, CoreVariable<L>)>) -> Self {
179179
Self {
180180
bindings: bindings.into_iter().collect(),
181181
}
182182
}
183183

184184
/// Look for a variable with the given name.
185-
pub fn lookup(&self, name: &str) -> Option<CoreParameter<L>> {
185+
pub fn lookup(&self, name: &str) -> Option<CoreVariable<L>> {
186186
self.bindings
187187
.iter()
188188
.rev()
@@ -193,7 +193,7 @@ impl<L: Language> Scope<L> {
193193
/// Create a new scope that extends `self` with `bindings`.
194194
pub fn with_bindings(
195195
&self,
196-
bindings: impl IntoIterator<Item = impl Upcast<(String, CoreParameter<L>)>>,
196+
bindings: impl IntoIterator<Item = impl Upcast<(String, CoreVariable<L>)>>,
197197
) -> Self {
198198
let mut s = self.clone();
199199
s.bindings.extend(bindings.into_iter().upcasted());
@@ -367,3 +367,9 @@ impl<L: Language, A: CoreParse<L>, B: CoreParse<L>, C: CoreParse<L>> CoreParse<L
367367
})
368368
}
369369
}
370+
371+
impl<L: Language> CoreParse<L> for CoreVariable<L> {
372+
fn parse<'t>(scope: &Scope<L>, text: &'t str) -> ParseResult<'t, Self> {
373+
Parser::single_variant(scope, text, "variable", |p| p.variable())
374+
}
375+
}

crates/formality-core/src/parse/parser.rs

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ use std::fmt::Debug;
22
use std::str::FromStr;
33

44
use crate::{
5-
language::{CoreParameter, HasKind, Language},
5+
language::Language,
66
parse::parser::left_recursion::{CurrentState, LeftRight},
77
set,
88
variable::CoreVariable,
9-
Downcast, DowncastFrom, Set, Upcast,
9+
Set, Upcast,
1010
};
1111

1212
use super::{CoreParse, ParseError, ParseResult, Scope, SuccessfulParse, TokenResult};
@@ -406,6 +406,11 @@ where
406406
self.current_text
407407
}
408408

409+
/// Return the set of variables in scope
410+
pub fn scope(&self) -> &Scope<L> {
411+
&self.scope
412+
}
413+
409414
/// Skips whitespace in the input, producing no reduction.
410415
pub fn skip_whitespace(&mut self) {
411416
self.current_text = skip_whitespace(self.current_text);
@@ -616,53 +621,48 @@ where
616621
)
617622
}
618623

619-
/// Parses the next identifier as a variable in scope
620-
/// with the kind appropriate for the return type `R`.
624+
/// Parses the next identifier as a variable in scope.
621625
///
622626
/// **NB:** This departs from the limits of context-free
623627
/// grammars -- the scope is a piece of context that affects how
624628
/// our parsing proceeds!
625-
///
626-
/// Variables don't implement Parse themselves
627-
/// because you can't parse a variable into a *variable*,
628-
/// instead, you parse it into a `Parameter`.
629-
/// The parsing code then downcasts that parameter into the
630-
/// type it wants (e.g., in Rust, a `Ty`).
631-
/// This downcast will fail if the `Parameter` is not of the appropriate
632-
/// kind (which will result in a parse error).
633-
///
634-
/// This avoids "miskinding", i.e., parsing a `Ty` that contains
635-
/// a lifetime variable.
636-
///
637-
/// It also allows parsing where you use variables to stand for
638-
/// more complex parameters, which is kind of combining parsing
639-
/// and substitution and can be convenient in tests.
640629
#[tracing::instrument(level = "trace", skip(self), ret)]
641-
pub fn variable<R>(&mut self) -> Result<R, Set<ParseError<'t>>>
642-
where
643-
R: Debug + DowncastFrom<CoreParameter<L>>,
644-
{
630+
pub fn variable(&mut self) -> Result<CoreVariable<L>, Set<ParseError<'t>>> {
645631
self.skip_whitespace();
646-
let type_name = std::any::type_name::<R>();
647632
let text0 = self.current_text;
648633
let id = self.identifier()?;
649634
match self.scope.lookup(&id) {
650-
Some(parameter) => match parameter.downcast() {
651-
Some(v) => Ok(v),
652-
None => Err(ParseError::at(
653-
text0,
654-
format!(
655-
"wrong kind, expected a {}, found `{:?}`, which is a `{:?}`",
656-
type_name,
657-
parameter,
658-
parameter.kind()
659-
),
660-
)),
661-
},
635+
Some(v) => Ok(v),
662636
None => Err(ParseError::at(text0, format!("unrecognized variable"))),
663637
}
664638
}
665639

640+
/// Parses the next identifier as a [variable in scope](`Self::variable`)
641+
/// and checks that it has the right kind. This is a useful
642+
/// helper method to avoid mis-kinding variables.
643+
#[tracing::instrument(level = "trace", skip(self), ret)]
644+
pub fn variable_of_kind(
645+
&mut self,
646+
kind: L::Kind,
647+
) -> Result<CoreVariable<L>, Set<ParseError<'t>>> {
648+
self.skip_whitespace();
649+
let text0 = self.current_text;
650+
let v = self.variable()?;
651+
if v.kind() == kind {
652+
Ok(v)
653+
} else {
654+
Err(ParseError::at(
655+
text0,
656+
format!(
657+
"expected a variable of kind `{:?}`, found `{:?}`, which has kind `{:?}`",
658+
kind,
659+
v,
660+
v.kind()
661+
),
662+
))
663+
}
664+
}
665+
666666
/// Extract a number from the input, erroring if the input does not start with a number.
667667
#[tracing::instrument(level = "trace", skip(self), ret)]
668668
pub fn number<T>(&mut self) -> Result<T, Set<ParseError<'t>>>

crates/formality-macros/src/attrs.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use syn::{spanned::Spanned, Attribute, DeriveInput};
44

5-
use crate::{custom::Customize, precedence::Precedence};
5+
use crate::{custom::Customize, precedence::Precedence, variable::Variable};
66

77
/// Checks for any kind of attribute that indicates an "is-a" relationship,
88
/// e.g. `#[cast]` and `#[variable]`.
@@ -39,6 +39,11 @@ pub(crate) fn has_variable_attr(attrs: &[Attribute]) -> bool {
3939
attrs.iter().any(|a| a.path().is_ident("variable"))
4040
}
4141

42+
/// Extract a `#[variable]` attribute (if any)
43+
pub(crate) fn variable(attrs: &[Attribute]) -> syn::Result<Option<Variable>> {
44+
parse_attr_named(attrs, "variable")
45+
}
46+
4247
/// Extract a `#[precedence]` level, defaults to 0
4348
pub(crate) fn precedence(attrs: &[Attribute]) -> syn::Result<Precedence> {
4449
Ok(parse_attr_named(attrs, "precedence")?.unwrap_or_default())

crates/formality-macros/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ mod precedence;
1818
mod spec;
1919
mod term;
2020
mod test;
21+
mod variable;
2122
mod visit;
2223

2324
// synstructure::decl_derive!([Fold] => fold::derive_fold);

crates/formality-macros/src/parse.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ use syn::{spanned::Spanned, Attribute};
77
use synstructure::BindingInfo;
88

99
use crate::{
10-
attrs::{has_cast_attr, has_variable_attr, precedence},
10+
attrs::{has_cast_attr, has_variable_attr, precedence, variable},
1111
spec::{self, FieldMode, FormalitySpec},
12+
variable::Variable,
1213
};
1314

1415
/// Derive the `Parse` impl, using an optional grammar supplied "from the outside".
@@ -76,13 +77,13 @@ fn parse_variant(
7677
any_variable_variant: bool,
7778
) -> syn::Result<TokenStream> {
7879
let ast = variant.ast();
79-
let has_variable_attr = has_variable_attr(ast.attrs);
80+
let variable_attr = variable(ast.attrs)?;
8081
let mut stream = TokenStream::default();
8182

8283
// If there are variable variants -- but this is not one -- then we always begin by rejecting
8384
// variable names. This avoids ambiguity in a case like `for<ty X> { X : Debug }`, where we
8485
// want to parse `X` as a type variable.
85-
if any_variable_variant && !has_variable_attr {
86+
if any_variable_variant && variable_attr.is_none() {
8687
stream.extend(quote_spanned!(ast.ident.span() => __p.reject_variable()?;));
8788
}
8889

@@ -107,13 +108,22 @@ fn parse_variant(
107108
__p.expect_keyword(#literal)?;
108109
Ok(#construct)
109110
});
110-
} else if has_variable_attr {
111+
} else if let Some(Variable { kind }) = variable_attr {
111112
// Has the `#[variable]` attribute -- parse an identifier and then check to see if it is present
112113
// in the scope. If so, downcast it and check that it has the correct kind.
114+
if variant.bindings().len() != 1 {
115+
return Err(syn::Error::new(
116+
variant.ast().ident.span(),
117+
"can only automatically parse variable variants with one binding",
118+
));
119+
}
120+
let v0_binding = &variant.bindings()[0];
121+
let v0 = field_ident(v0_binding.ast(), 0);
122+
let construct = variant.construct(field_ident);
113123
stream.extend(quote_spanned! {
114124
ast.ident.span() =>
115-
let v = __p.variable()?;
116-
Ok(v)
125+
let #v0 = __p.variable_of_kind(#kind)?;
126+
Ok(#construct)
117127
});
118128
} else if has_cast_attr(variant.ast().attrs) {
119129
// Has the `#[cast]` attribute -- just parse the bindings (comma separated, if needed)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use proc_macro2::TokenStream;
2+
3+
/// The contents of a `#[variable]` attribute. Currently just a kind.
4+
pub struct Variable {
5+
pub kind: syn::Expr,
6+
}
7+
8+
impl syn::parse::Parse for Variable {
9+
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
10+
let kind: syn::Expr = input.parse()?;
11+
12+
let tokens: TokenStream = input.parse()?;
13+
let mut tokens = tokens.into_iter();
14+
if let Some(token) = tokens.next() {
15+
return Err(syn::Error::new(token.span(), "extra tokens"));
16+
}
17+
18+
Ok(Variable { kind })
19+
}
20+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ pub enum TyData {
107107
AliasTy(AliasTy),
108108
#[cast]
109109
PredicateTy(PredicateTy),
110-
#[variable]
110+
#[variable(ParameterKind::Ty)]
111111
Variable(Variable),
112112
}
113113

@@ -339,7 +339,7 @@ impl DowncastTo<LtData> for Lt {
339339
pub enum LtData {
340340
Static,
341341

342-
#[variable]
342+
#[variable(ParameterKind::Lt)]
343343
Variable(Variable),
344344
}
345345

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::grammar::{
1010
AdtId, AssociatedItemId, Bool, ConstData, RefKind, RigidName, Scalar, TraitId,
1111
};
1212

13-
use super::{AliasTy, AssociatedTyName, Lt, Parameter, RigidTy, ScalarId, Ty};
13+
use super::{AliasTy, AssociatedTyName, Lt, Parameter, ParameterKind, RigidTy, ScalarId, Ty};
1414

1515
use crate::rust::FormalityLang as Rust;
1616

@@ -124,7 +124,9 @@ fn parse_parameters<'t>(
124124
impl CoreParse<Rust> for ConstData {
125125
fn parse<'t>(scope: &Scope<Rust>, text: &'t str) -> ParseResult<'t, Self> {
126126
Parser::multi_variant(scope, text, "ConstData", |parser| {
127-
parser.parse_variant("Variable", Precedence::default(), |p| p.variable());
127+
parser.parse_variant("Variable", Precedence::default(), |p| {
128+
p.variable_of_kind(ParameterKind::Const).upcast()
129+
});
128130

129131
parser.parse_variant_cast::<Bool>(Precedence::default());
130132

0 commit comments

Comments
 (0)