Skip to content

Commit

Permalink
Implicit map now allows omitting commas
Browse files Browse the repository at this point in the history
This commit changes implicit map parsing to be its own logic, and forces
implicit map keys to be identifiers. Forcing keys to be identifiers
removes a little bit of ambiguity since all whitespace is insignificant
beyond providing a way to separate tokens.
  • Loading branch information
ecton committed Apr 18, 2023
1 parent 0463012 commit 27ee655
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 18 deletions.
11 changes: 9 additions & 2 deletions src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,8 +507,15 @@ mod tests {
b: i32,
}
let parsed = BasicNamed::deserialize(&mut crate::de::Deserializer::new(
r#"a: 1,
b: -1"#,
r#"a: 1 b: -1"#,
Config {
allow_implicit_map: true,
},
))
.unwrap();
assert_eq!(parsed, BasicNamed { a: 1, b: -1 });
let parsed = BasicNamed::deserialize(&mut crate::de::Deserializer::new(
r#"a: 1, b: -1,"#,
Config {
allow_implicit_map: true,
},
Expand Down
79 changes: 63 additions & 16 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,6 @@ impl<'s> Parser<'s> {
}
}

fn pop_nested_state(&mut self) {
if self.nested.pop().is_none() {
debug_assert!(matches!(self.root_state, State::ImplicitMap(_)));
self.root_state = State::Finished;
}
}

fn parse_token(
&mut self,
token: Token<'s>,
Expand Down Expand Up @@ -127,7 +120,7 @@ impl<'s> Parser<'s> {
})
}
TokenKind::Close(closed) if Some(closed) == allowed_close => {
self.pop_nested_state();
self.nested.pop();
Ok(Event::EndNested)
}
TokenKind::Colon | TokenKind::Comma | TokenKind::Close(_) => {
Expand All @@ -150,7 +143,7 @@ impl<'s> Parser<'s> {
let token = self.next_or_eof()?;
match token.kind {
TokenKind::Close(closed) if closed == end => {
self.pop_nested_state();
self.nested.pop();
Ok(Event::EndNested)
}
TokenKind::Comma => {
Expand Down Expand Up @@ -195,22 +188,73 @@ impl<'s> Parser<'s> {
*self.map_state_mut() = MapState::ExpectingComma;

let token = self.next_or_eof()?;
self.parse_token(token, Some(Balanced::Brace))
self.parse_token(token, None)
}
MapState::ExpectingComma => {
let token = self.next_token().transpose()?.map(|token| token.kind);
match token {
Some(TokenKind::Close(closed)) if closed == Balanced::Brace => {
self.pop_nested_state();
self.nested.pop();
Ok(Event::EndNested)
}
Some(TokenKind::Comma) => {
*self.map_state_mut() = MapState::ExpectingKey;
self.parse_map(MapState::ExpectingKey)
}
None if self.nested.is_empty() => {
// Implicit map
self.pop_nested_state();
_ => todo!("expected comma or end"),
}
}
}
}

fn parse_implicit_map(&mut self, state: MapState) -> Result<Event<'s>, Error> {
match state {
MapState::ExpectingKey => {
let token = self.next_token().transpose()?;
match token.map(|token| token.kind) {
Some(TokenKind::Identifier(key)) => {
self.root_state = State::ImplicitMap(MapState::ExpectingColon);
Ok(Event::Primitive(Primitive::Identifier(key)))
}
None => {
self.root_state = State::Finished;
Ok(Event::EndNested)
}
_ => todo!("expected map key"),
}
}
MapState::ExpectingColon => {
let token = self.next_or_eof()?;
if matches!(token.kind, TokenKind::Colon) {
self.root_state = State::ImplicitMap(MapState::ExpectingValue);
self.parse_implicit_map(MapState::ExpectingValue)
} else {
todo!("expected colon, got {token:?}")
}
}
MapState::ExpectingValue => {
self.root_state = State::ImplicitMap(MapState::ExpectingComma);

let token = self.next_or_eof()?;
self.parse_token(token, None)
}
MapState::ExpectingComma => {
let token = self.next_token().transpose()?.map(|token| token.kind);
match token {
Some(TokenKind::Close(closed)) if closed == Balanced::Brace => {
self.root_state = State::Finished;
Ok(Event::EndNested)
}
Some(TokenKind::Comma) => {
self.root_state = State::ImplicitMap(MapState::ExpectingKey);
self.parse_implicit_map(MapState::ExpectingKey)
}
Some(TokenKind::Identifier(key)) => {
self.root_state = State::ImplicitMap(MapState::ExpectingColon);
Ok(Event::Primitive(Primitive::Identifier(key)))
}
None => {
self.root_state = State::Finished;
Ok(Event::EndNested)
}
_ => todo!("expected comma or end"),
Expand Down Expand Up @@ -280,8 +324,11 @@ impl<'s> Iterator for Parser<'s> {
else { unreachable!("just matched") };
Ok(Event::Primitive(Primitive::Identifier(first_key)))
}
State::ImplicitMap(state) => self.parse_map(*state),
State::Finished => todo!("error: trailing junk"),
State::ImplicitMap(state) => self.parse_implicit_map(*state),
State::Finished => match self.next_token()? {
Ok(token) => todo!("expected eof, found {token:?}"),
Err(err) => return Some(Err(err.into())),
},
},

Some(NestedState::Tuple(list)) => self.parse_sequence(*list, Balanced::Paren),
Expand Down

0 comments on commit 27ee655

Please sign in to comment.