parsing ambiguous explicit future types. Ex: Future<u32, Future<u32>>

This commit is contained in:
evan-schott 2024-04-09 11:38:41 -07:00
parent 5903635fa3
commit bf95c4966a
3 changed files with 72 additions and 1 deletions

View File

@ -41,6 +41,8 @@ pub(crate) struct ParserContext<'a> {
pub(crate) disallow_struct_construction: bool,
/// The name of the program being parsed.
pub(crate) program_name: Option<Symbol>,
/// Whether traversing an ambiguous Shr token.
pub in_angle: bool,
}
/// Dummy span used to appease borrow checker.
@ -63,6 +65,7 @@ impl<'a> ParserContext<'a> {
token,
tokens,
program_name: None,
in_angle: false,
};
p.bump();
p
@ -248,6 +251,51 @@ impl<'a> ParserContext<'a> {
self.parse_list(Delimiter::Bracket, Some(Token::Comma), f)
}
/// Parse a list separated by `,` and delimited by angle brackets.
/// Since the `>>` token is ambiguous, we need to create special state checks.
pub (super) fn parse_angle_comma_list<T>(
&mut self,
sep: Option<Token>,
mut f: impl FnMut(&mut Self) -> Result<Option<T>>,
) -> Result<(Vec<T>, bool, Span)> {
let (open, close) = Delimiter::AngleBracket.open_close_pair();
let mut list = Vec::new();
let mut trailing = false;
// Parse opening delimiter.
let open_span = self.expect(&open)?;
while !self.check(&close) && !self.check(&Token::Shr){
// Parse the element. We allow inner parser recovery through the `Option`.
if let Some(elem) = f(self)? {
list.push(elem);
}
// Parse the separator, if any.
if sep.as_ref().filter(|sep| !self.eat(sep)).is_some() {
trailing = false;
break;
}
trailing = true;
}
if self.token.token == Token::Shr {
return if self.in_angle {
self.in_angle = false;
let end_span = self.expect(&Token::Shr)?;
Ok((list, trailing, open_span + end_span))
} else {
self.in_angle = true;
Ok((list, trailing, open_span + self.prev_token.span))
};
}
// Parse closing delimiter.
let span = open_span + self.expect(&close)?;
Ok((list, trailing, span))
}
/// Returns true if the current token is `(`.
pub(super) fn peek_is_left_par(&self) -> bool {
matches!(self.token.token, Token::LeftParen)

View File

@ -66,7 +66,6 @@ impl ParserContext<'_> {
Token::Address => Type::Address,
Token::Bool => Type::Boolean,
Token::Field => Type::Field,
Token::Future => Type::Future(Default::default()),
Token::Group => Type::Group,
Token::Scalar => Type::Scalar,
Token::Signature => Type::Signature,
@ -131,6 +130,23 @@ impl ParserContext<'_> {
// Note: This is the only place where `Tuple` type is constructed in the parser.
_ => Ok((Type::Tuple(TupleType::new(types.into_iter().map(|t| t.0).collect())), span)),
}
}
else if self.token.token == Token::Future {
// Parse the `Future` token.
let span = self.expect(&Token::Future)?;
// Parse the angle bracket list.
if self.token.token == Token::Lt {
let (types, _, full_span) = self.parse_angle_comma_list(Some(Token::Comma),|p| p.parse_type().map(Some))?;
match types.len() {
0 => return Err(ParserError::future_must_have_at_least_one_element(span).into()),
// `Future<()>` corresponds to explicitly specifying a `Future` type with no inputs.
1 if matches!(types.get(0).unwrap().0, Type::Unit) => return Ok((Type::Future(FutureType::new(vec![], None, true)), span + full_span)),
_ => {},
}
Ok((Type::Future(FutureType::new(types.into_iter().map(|t| t.0).collect(), None, true)), span + full_span))
} else {
Ok((Type::Future(Default::default()), span))
}
} else {
self.parse_primitive_type()
}

View File

@ -342,4 +342,11 @@ create_messages!(
msg: format!("Cannot create an external record. Records can only be created in the program that they are defined in."),
help: None,
}
@formatted
future_must_have_at_least_one_element {
args: (),
msg: "Future type must have at least one element.".to_string(),
help: Some("Write `Future<()>` to explicitly type a future with no inputs.".to_string()),
}
);