use super::{TokenError, Tokenizer};
use crate::fmt::FormatIter;
use crate::syntax::{Event, Token, TokenKind};
use crate::types::{LocResult, Located};
use crate::Flavor;
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ParseError {
#[error("{0}")]
Token(#[from] TokenError),
#[error("unexpected {0}, expected one of {choices}", choices=FormatIter::new(.1.iter(), ", "))]
UnexpectedToken(TokenKind, &'static [TokenKind]),
#[error("maximum recursion limit exceeded")]
MaxRecursion,
#[error("unmatched braces")]
UnmatchedBraces,
#[error("incomplete field")]
IncompleteField,
}
#[derive(Clone, Copy, Default)]
pub struct ParserState(State);
impl ParserState {
pub const fn zero() -> Self {
Self(
State::Value,
)
}
}
#[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
enum State {
#[default]
Value = 0,
ValueClose,
FieldEquals,
FieldValue,
BareEnum,
ListItem,
ListSep,
MapItem,
MapSep,
Enum,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Parser<'de, S> {
tokens: Tokenizer<'de>,
initial_state: State,
state: S,
state_top: usize,
unused_token: Option<Located<'de, Token<'de>>>,
initial_state_sent: bool,
unused_event: Option<LocResult<'de, Event<'de>, ParseError>>,
}
impl<'de, S> Parser<'de, S>
where
S: AsRef<[ParserState]> + AsMut<[ParserState]>,
{
pub fn new(flavor: Flavor, input: &'de [u8], state: S) -> Self {
let initial_state = match flavor {
Flavor::Value => State::Value,
Flavor::List => State::ListItem,
Flavor::Map => State::MapItem,
};
Self {
tokens: Tokenizer::new(input),
initial_state,
state,
state_top: 0,
unused_token: None,
initial_state_sent: false,
unused_event: None,
}
}
pub fn location(&self) -> Located<'de, ()> {
if let Some(ev) = self.unused_event {
match ev {
Ok(v) => v.wrap(()),
Err(e) => e.wrap(()),
}
} else {
self.tokens.location()
}
}
pub fn is_eof(&mut self) -> bool {
let decide = |ev: &LocResult<Event, ParseError>| match ev {
Ok(_) => false,
Err(e) => matches!(**e, ParseError::Token(TokenError::Eof)),
};
if let Some(ev) = self.unused_event.as_ref() {
return decide(ev);
}
let ev = self.next_event();
let eof = decide(&ev);
self.unused_event = Some(ev);
eof
}
fn unexpected(
&self,
t: Token<'de>,
expected: &'static [TokenKind],
) -> Result<Option<Event<'de>>, ParseError> {
Err(ParseError::UnexpectedToken(t.kind(), expected))
}
fn only_stack_state(&self) -> Option<State> {
self.state_top
.checked_sub(1)
.and_then(|i| self.state.as_ref().get(i))
.copied()
.map(|s| s.0)
}
fn state(&self) -> State {
self.state_top
.checked_sub(1)
.and_then(|i| self.state.as_ref().get(i))
.map(|s| s.0)
.unwrap_or(self.initial_state)
}
fn transition(&mut self, state: State) {
if let Some(dest) = self
.state_top
.checked_sub(1)
.and_then(|i| self.state.as_mut().get_mut(i))
{
dest.0 = state;
} else {
self.initial_state = state;
}
}
fn push(&mut self, state: State) -> Result<(), ParseError> {
if self.state_top < self.state.as_ref().len() {
self.state.as_mut()[self.state_top] = ParserState(state);
self.state_top += 1;
Ok(())
} else {
Err(ParseError::MaxRecursion)
}
}
fn pop(&mut self) -> Result<(), ParseError> {
if self.state_top > 0 {
self.state_top -= 1;
Ok(())
} else {
Err(ParseError::UnmatchedBraces)
}
}
fn step(
&mut self,
loc: &Located<'de, ()>,
tok: Token<'de>,
) -> Result<Option<Event<'de>>, ParseError> {
use TokenKind::*;
match self.state() {
State::Value => match tok {
Token::Newline => Ok(None),
Token::ParenOpen => {
self.transition(State::ValueClose);
self.push(State::Value)?;
Ok(None)
}
Token::ListOpen => {
self.transition(State::ValueClose);
self.push(State::ListItem)?;
Ok(Some(Event::ListOpen))
}
Token::MapOpen => {
self.transition(State::ValueClose);
self.push(State::MapItem)?;
Ok(Some(Event::MapOpen))
}
Token::Ident(name) => {
self.transition(State::ValueClose);
self.push(State::Enum)?;
Ok(Some(Event::EnumOpen(name)))
}
Token::Value(v) => {
self.transition(State::ValueClose);
Ok(Some(Event::Value(v)))
}
t => self.unexpected(t, &[Newline, ParenOpen, ListOpen, MapOpen, Ident, Value]),
},
State::ValueClose => match tok {
Token::Newline => Ok(None),
Token::ParenClose => {
self.pop()?;
Ok(None)
}
t => self.unexpected(t, &[Newline, ParenClose]),
},
State::FieldEquals => match tok {
Token::Equals => {
self.transition(State::FieldValue);
Ok(None)
}
t => self.unexpected(t, &[Equals]),
},
State::FieldValue => match tok {
Token::ParenOpen => {
self.pop()?;
self.push(State::Value)?;
Ok(None)
}
Token::ListOpen => {
self.pop()?;
self.push(State::ListItem)?;
Ok(Some(Event::ListOpen))
}
Token::MapOpen => {
self.pop()?;
self.push(State::MapItem)?;
Ok(Some(Event::MapOpen))
}
Token::Ident(name) => {
self.pop()?;
if matches!(self.state(), State::Enum) {
self.push(State::BareEnum)?;
} else {
self.push(State::Enum)?;
}
Ok(Some(Event::EnumOpen(name)))
}
Token::Value(v) => {
self.pop()?;
Ok(Some(Event::Value(v)))
}
t => self.unexpected(t, &[ParenOpen, ListOpen, MapOpen, Ident, Value]),
},
State::BareEnum => {
self.pop()?;
self.unused_token = Some(loc.wrap(tok));
Ok(Some(Event::EnumClose))
}
State::ListItem => match tok {
Token::Newline => Ok(None),
Token::ParenOpen => {
self.transition(State::ListSep);
self.push(State::Value)?;
Ok(None)
}
Token::ListOpen => {
self.transition(State::ListSep);
self.push(State::ListItem)?;
Ok(Some(Event::ListOpen))
}
Token::ListClose => {
self.pop()?;
Ok(Some(Event::ListClose))
}
Token::MapOpen => {
self.transition(State::ListSep);
self.push(State::MapItem)?;
Ok(Some(Event::MapOpen))
}
Token::Ident(name) => {
self.transition(State::ListSep);
self.push(State::Enum)?;
Ok(Some(Event::EnumOpen(name)))
}
Token::Value(v) => {
self.transition(State::ListSep);
Ok(Some(Event::Value(v)))
}
t => self.unexpected(
t,
&[
Newline, ParenOpen, ListOpen, ListClose, MapOpen, Ident, Value,
],
),
},
State::ListSep => match tok {
Token::Newline | Token::Comma => {
self.transition(State::ListItem);
Ok(None)
}
Token::ListClose => {
self.pop()?;
Ok(Some(Event::ListClose))
}
t => self.unexpected(t, &[Newline, Comma, ListClose]),
},
State::MapItem => match tok {
Token::Newline => Ok(None),
Token::MapClose => {
self.pop()?;
Ok(Some(Event::MapClose))
}
Token::Ident(name) => {
self.transition(State::MapSep);
self.push(State::FieldEquals)?;
Ok(Some(Event::Key(name)))
}
t => self.unexpected(t, &[Newline, MapClose, Ident]),
},
State::MapSep => match tok {
Token::Newline | Token::Comma => {
self.transition(State::MapItem);
Ok(None)
}
Token::MapClose => {
self.pop()?;
Ok(Some(Event::MapClose))
}
t => self.unexpected(t, &[Newline, Comma, MapClose]),
},
State::Enum => match tok {
Token::Newline
| Token::Comma
| Token::ParenClose
| Token::ListClose
| Token::MapClose => {
self.pop()?;
self.unused_token = Some(loc.wrap(tok));
Ok(Some(Event::EnumClose))
}
Token::ParenOpen => {
self.push(State::Value)?;
Ok(None)
}
Token::ListOpen => {
self.push(State::ListItem)?;
Ok(Some(Event::ListOpen))
}
Token::MapOpen => {
self.push(State::MapItem)?;
Ok(Some(Event::MapOpen))
}
Token::Ident(name) => {
let (subloc, subtok) = Located::from_result(self.tokens.next_token()).split();
match subtok {
Ok(subtok @ Token::Equals) => {
self.push(State::FieldEquals)?;
self.unused_token = Some(subloc.wrap(subtok));
Ok(Some(Event::Key(name)))
}
Ok(subtok) => {
self.push(State::BareEnum)?;
self.unused_token = Some(subloc.wrap(subtok));
Ok(Some(Event::EnumOpen(name)))
}
Err(TokenError::Eof) => {
self.push(State::BareEnum)?;
Ok(Some(Event::EnumOpen(name)))
}
Err(e) => Err(e)?,
}
}
Token::Value(v) => Ok(Some(Event::Value(v))),
t => self.unexpected(
t,
&[
Newline, Comma, ParenClose, ListClose, MapClose, ParenOpen, ListOpen,
MapOpen, Ident, Value,
],
),
},
}
}
pub fn next_event(&mut self) -> LocResult<'de, Event<'de>, ParseError> {
let ev = self
.unused_event
.take()
.unwrap_or_else(|| self.next_event_inner());
if ev.is_err() {
self.unused_event = Some(ev);
}
ev
}
fn next_event_inner(&mut self) -> LocResult<'de, Event<'de>, ParseError> {
if !self.initial_state_sent {
self.initial_state_sent = true;
match self.initial_state {
State::ListItem | State::ListSep => {
return Ok(self.location().wrap(Event::ListOpen));
}
State::MapItem | State::MapSep => {
return Ok(self.location().wrap(Event::MapOpen));
}
_ => (),
}
}
loop {
let tok = if let Some(tok) = self.unused_token.take() {
Ok(tok)
} else {
self.tokens.next_token()
};
let (loc, tok) = Located::from_result(tok).split();
let tok = match tok {
Ok(tok) => tok,
Err(e) => match e.into() {
ParseError::Token(TokenError::Eof) => {
let val = match self.only_stack_state() {
Some(State::Enum | State::BareEnum) => {
let _ = self.pop();
Ok(Event::EnumClose)
}
Some(State::FieldEquals | State::FieldValue) => {
Err(ParseError::IncompleteField)
}
Some(_) => Err(ParseError::UnmatchedBraces),
None => match self.initial_state {
State::ListItem | State::ListSep => {
self.initial_state = State::Value;
Ok(Event::ListClose)
}
State::MapItem | State::MapSep => {
self.initial_state = State::Value;
Ok(Event::MapClose)
}
_ => Err(ParseError::Token(TokenError::Eof)),
},
};
return loc.replace(val).to_result();
}
e => return loc.replace(Err(e)).to_result(),
},
};
match self.step(&loc, tok) {
Ok(None) => continue,
Ok(Some(ev)) => return loc.replace(Ok(ev)).to_result(),
Err(e) => return loc.replace(Err(e)).to_result(),
}
}
}
}
impl<'de, S> Iterator for Parser<'de, S>
where
S: AsRef<[ParserState]> + AsMut<[ParserState]>,
{
type Item = LocResult<'de, Event<'de>, ParseError>;
fn next(&mut self) -> Option<Self::Item> {
match self.next_event() {
Ok(ev) => Some(Ok(ev)),
Err(e) if matches!(*e, ParseError::Token(TokenError::Eof)) => None,
Err(e) => Some(Err(e)),
}
}
}
#[cfg(test)]
mod test {
use crate::types::Escaped;
#[test]
fn is_eof_then_location() {
use super::{Flavor, Parser, ParserState};
let mut state = [ParserState::zero(); 64];
let mut parser = Parser::new(Flavor::Value, b"[0]", &mut state);
let loc = parser.location();
assert!(!parser.is_eof());
assert_eq!(loc, parser.location());
assert!(parser.next_event().is_ok());
assert_ne!(loc, parser.location());
}
#[test]
fn is_eof_then_location_err() {
use super::{Flavor, Parser, ParserState};
let mut state = [ParserState::zero(); 64];
let mut parser = Parser::new(Flavor::Value, b"?0]", &mut state);
let loc = parser.location();
assert!(!parser.is_eof());
assert_eq!(loc, parser.location());
assert!(parser.next_event().is_err());
}
#[test]
fn max_recursion() {
use super::{Flavor, ParseError, Parser, ParserState};
let mut state = [ParserState::zero(); 2];
let parser = Parser::new(Flavor::Value, b"[[[]]]", &mut state);
for (i, ev) in parser.enumerate() {
if i >= 2 {
assert!(matches!(*ev.unwrap_err(), ParseError::MaxRecursion));
if i >= 5 {
break;
}
} else {
assert!(ev.is_ok());
}
}
}
macro_rules! parse_test {
($(#[$attr:meta])* $name:ident, $flavor:ident, $src:literal $(,$ev:expr)* $(,)?) => {
#[test]
$(#[$attr])*
fn $name() {
#[allow(unused)]
use super::{Event, Event::*, Parser, ParseError, TokenError, Flavor, ParserState};
#[allow(unused)]
use crate::syntax::Value::*;
let events: &[Event] = &[$($ev,)*];
let mut state = [ParserState::zero(); 64];
let mut parser = Parser::new(Flavor::$flavor, $src.as_ref(), &mut state);
for ev in events {
assert!(!parser.is_eof());
assert_eq!(*ev, *parser.next_event().unwrap());
}
assert!(parser.is_eof());
assert_eq!(ParseError::Token(TokenError::Eof), *parser.next_event().unwrap_err());
}
}
}
macro_rules! parse_all_test {
($(#[$attr:meta])* $name:ident, $flavor:ident, $src:literal $(,)?) => {
#[test]
$(#[$attr])*
fn $name() {
#[allow(unused)]
use super::{Event, Event::*, Parser, ParseError, TokenError, Flavor, ParserState};
#[allow(unused)]
use crate::syntax::Value::*;
let mut state = [ParserState::zero(); 64];
let mut parser = Parser::new(Flavor::$flavor, $src.as_ref(), &mut state);
while !parser.is_eof() {
parser.next_event().unwrap();
}
assert!(parser.is_eof());
assert_eq!(ParseError::Token(TokenError::Eof), *parser.next_event().unwrap_err());
}
}
}
parse_all_test!(
#[should_panic(expected = "UnknownToken")]
unknown_token,
Value,
" ? ",
);
parse_all_test!(
#[should_panic(expected = "InvalidUtf8")]
invalid_utf8,
Value,
b" '\xf0' ",
);
parse_all_test!(
#[should_panic(expected = "UnknownEscape")]
unknown_escape,
Value,
" '\\?' ",
);
parse_all_test!(
#[should_panic(expected = "UnexpectedToken")]
bare_key,
Value,
" key=0 ",
);
parse_all_test!(
#[should_panic(expected = "UnexpectedToken")]
bare_comma,
Value,
" , ",
);
parse_all_test!(bare_newline, Value, " \n ");
parse_all_test!(
#[should_panic(expected = "UnmatchedBraces")]
unmatched_braces_close,
List,
"]",
);
parse_all_test!(
#[should_panic(expected = "UnmatchedBraces")]
unmatched_braces_open,
Value,
"[",
);
parse_test!(newline_at_beginning, Value, " \n 0 ", Value(Int(0)));
parse_test!(newline_at_end, Value, " 0 \n ", Value(Int(0)));
parse_test!(val_unit, Value, " () ", Value(Unit));
parse_test!(val_paren_unit, Value, " ( \n ()) ", Value(Unit));
parse_test!(val_none, Value, " none ", Value(None));
parse_test!(val_true, Value, " true ", Value(Bool(true)));
parse_test!(val_false, Value, " false ", Value(Bool(false)));
parse_test!(val_int, Value, " 42 ", Value(Int(42)));
parse_test!(val_float, Value, " 42.1 ", Value(Float(42.1)));
parse_test!(val_char, Value, " 'A' ", Value(Char('A')));
parse_test!(
val_string,
Value,
" \"hello\" ",
Value(Str(Escaped::new("hello").unwrap()))
);
parse_test!(
val_bytes,
Value,
" b\"hello\" ",
Value(Bytes(Escaped::new(&b"hello"[..]).unwrap()))
);
parse_test!(list_empty, Value, " [] ", ListOpen, ListClose);
parse_test!(list_empty_space, Value, " [ ] ", ListOpen, ListClose);
parse_test!(list_empty_newline, Value, " [ \n ] ", ListOpen, ListClose);
parse_test!(
#[should_panic(expected = "UnexpectedToken")]
list_empty_comma,
Value,
" [ , ] ",
ListOpen,
ListClose, );
parse_test!(
#[should_panic(expected = "UnexpectedToken")]
list_two_values,
Value,
" [ 0 0 ] ",
ListOpen,
Value(Int(0)),
ListClose, );
parse_test!(
list_simple,
Value,
" [1, (2), 3] ",
ListOpen,
Value(Int(1)),
Value(Int(2)),
Value(Int(3)),
ListClose,
);
parse_test!(
list_trailing_comma,
Value,
" [1 , ] ",
ListOpen,
Value(Int(1)),
ListClose,
);
parse_test!(
list_newlines,
Value,
" [1, \n 2 \n 3] ",
ListOpen,
Value(Int(1)),
Value(Int(2)),
Value(Int(3)),
ListClose,
);
parse_test!(
list_compound,
Value,
" [[], {}, variant] ",
ListOpen,
ListOpen,
ListClose,
MapOpen,
MapClose,
EnumOpen("variant"),
EnumClose,
ListClose,
);
parse_test!(
list_flavor_simple,
List,
" 1, 2, 3 ",
ListOpen,
Value(Int(1)),
Value(Int(2)),
Value(Int(3)),
ListClose,
);
parse_test!(
list_flavor_trailing_comma,
List,
" 1 , ",
ListOpen,
Value(Int(1)),
ListClose,
);
parse_test!(
list_flavor_newlines,
List,
" 1, \n 2 \n 3 ",
ListOpen,
Value(Int(1)),
Value(Int(2)),
Value(Int(3)),
ListClose,
);
parse_test!(
list_flavor_compound,
List,
" [], {}, variant ",
ListOpen,
ListOpen,
ListClose,
MapOpen,
MapClose,
EnumOpen("variant"),
EnumClose,
ListClose,
);
parse_test!(map_empty, Value, " {} ", MapOpen, MapClose);
parse_test!(map_empty_space, Value, " { } ", MapOpen, MapClose);
parse_test!(map_empty_newline, Value, " { \n } ", MapOpen, MapClose);
parse_test!(
#[should_panic(expected = "UnexpectedToken")]
map_empty_comma,
Value,
" { , } ",
MapOpen,
MapClose, );
parse_test!(
#[should_panic(expected = "UnexpectedToken")]
map_two_values,
Value,
" { a=0 b=1 } ",
MapOpen,
Key("a"),
Value(Int(0)),
MapClose, );
parse_test!(
map_simple,
Value,
" {a=1, b = 2} ",
MapOpen,
Key("a"),
Value(Int(1)),
Key("b"),
Value(Int(2)),
MapClose,
);
parse_test!(
map_trailing_comma,
Value,
" {a=1 , } ",
MapOpen,
Key("a"),
Value(Int(1)),
MapClose,
);
parse_test!(
#[should_panic(expected = "UnexpectedToken")]
map_field_newline1,
Value,
" { a \n = 1 } ",
MapOpen,
Key("a"),
Value(None), );
parse_test!(
#[should_panic(expected = "UnexpectedToken")]
map_field_newline2,
Value,
" { a = \n 1 } ",
MapOpen,
Key("a"),
Value(None), );
parse_test!(
map_newlines,
Value,
" {a = 1, \n b = 2\n c = 3 } ",
MapOpen,
Key("a"),
Value(Int(1)),
Key("b"),
Value(Int(2)),
Key("c"),
Value(Int(3)),
MapClose,
);
parse_test!(
map_compound,
Value,
" {a=[], b ={}, c= variant} ",
MapOpen,
Key("a"),
ListOpen,
ListClose,
Key("b"),
MapOpen,
MapClose,
Key("c"),
EnumOpen("variant"),
EnumClose,
MapClose,
);
parse_test!(
map_bare_enum,
Value,
" {a = enum} ",
MapOpen,
Key("a"),
EnumOpen("enum"),
EnumClose,
MapClose,
);
parse_test!(
map_bare_enum_arg,
Value,
" {a = enum 0} ",
MapOpen,
Key("a"),
EnumOpen("enum"),
Value(Int(0)),
EnumClose,
MapClose,
);
parse_test!(
map_paren_enum_arg,
Value,
" {a = (enum 0)} ",
MapOpen,
Key("a"),
EnumOpen("enum"),
Value(Int(0)),
EnumClose,
MapClose,
);
parse_test!(
map_flavor_simple,
Map,
" a=1, b = 2 ",
MapOpen,
Key("a"),
Value(Int(1)),
Key("b"),
Value(Int(2)),
MapClose,
);
parse_test!(
map_flavor_trailing_comma,
Map,
" a=1 , ",
MapOpen,
Key("a"),
Value(Int(1)),
MapClose,
);
parse_test!(
map_flavor_newlines,
Map,
" a = 1, \n b = 2 \n c = 3 ",
MapOpen,
Key("a"),
Value(Int(1)),
Key("b"),
Value(Int(2)),
Key("c"),
Value(Int(3)),
MapClose,
);
parse_test!(
map_flavor_compound,
Map,
" a = [], b={}, c =variant ",
MapOpen,
Key("a"),
ListOpen,
ListClose,
Key("b"),
MapOpen,
MapClose,
Key("c"),
EnumOpen("variant"),
EnumClose,
MapClose,
);
parse_test!(
#[should_panic(expected = "IncompleteField")]
map_incomplete_field,
Map,
" a= ",
MapOpen,
Key("a"),
Value(None), );
parse_test!(enum_empty, Value, " var ", EnumOpen("var"), EnumClose);
parse_test!(
#[should_panic(expected = "UnexpectedToken")]
enum_empty_comma,
Value,
" var , ",
EnumOpen("var"),
EnumClose,
Value(None), );
parse_test!(
enum_simple_seq,
Value,
" var 1 2 ",
EnumOpen("var"),
Value(Int(1)),
Value(Int(2)),
EnumClose,
);
parse_test!(
enum_simple_map,
Value,
" var a=1 b=2 ",
EnumOpen("var"),
Key("a"),
Value(Int(1)),
Key("b"),
Value(Int(2)),
EnumClose,
);
parse_test!(
enum_simple_mixed,
Value,
" var 1 b=2 ",
EnumOpen("var"),
Value(Int(1)),
Key("b"),
Value(Int(2)),
EnumClose,
);
parse_test!(
#[should_panic(expected = "UnexpectedToken")]
enum_field_newline1,
Value,
" var a \n = 1 ",
EnumOpen("var"),
EnumOpen("a"),
EnumClose,
EnumClose,
Value(None), );
parse_test!(
#[should_panic(expected = "UnexpectedToken")]
enum_field_newline2,
Value,
" var a = \n 1 ",
EnumOpen("var"),
Key("a"),
Value(None), );
parse_test!(
enum_newlines,
Value,
" var 1 \n ",
EnumOpen("var"),
Value(Int(1)),
EnumClose,
);
parse_test!(
enum_compound,
Value,
" var [] {} variant ",
EnumOpen("var"),
ListOpen,
ListClose,
MapOpen,
MapClose,
EnumOpen("variant"),
EnumClose,
EnumClose,
);
parse_test!(
enum_map_bare_enum,
Value,
" var a = enum ",
EnumOpen("var"),
Key("a"),
EnumOpen("enum"),
EnumClose,
EnumClose,
);
parse_test!(
enum_map_bare_enum_arg,
Value,
" var a = enum 0 ",
EnumOpen("var"),
Key("a"),
EnumOpen("enum"),
EnumClose,
Value(Int(0)),
EnumClose,
);
parse_test!(
enum_map_paren_enum_arg,
Value,
" var a = (enum 0) ",
EnumOpen("var"),
Key("a"),
EnumOpen("enum"),
Value(Int(0)),
EnumClose,
EnumClose,
);
parse_test!(
enum_seq_bare_enum,
Value,
" var enum ",
EnumOpen("var"),
EnumOpen("enum"),
EnumClose,
EnumClose,
);
parse_test!(
enum_seq_bare_enum_arg,
Value,
" var enum 0 ",
EnumOpen("var"),
EnumOpen("enum"),
EnumClose,
Value(Int(0)),
EnumClose,
);
parse_test!(
enum_seq_paren_enum_arg,
Value,
" var (enum 0) ",
EnumOpen("var"),
EnumOpen("enum"),
Value(Int(0)),
EnumClose,
EnumClose,
);
parse_test!(
#[should_panic(expected = "UnknownToken")]
enum_bare_ident_bad_tok,
Value,
" var enum ? ",
EnumOpen("var"),
EnumOpen("enum"),
Value(None), );
parse_test!(
enum_variant_none,
Value,
" \\none ",
EnumOpen("none"),
EnumClose,
);
parse_test!(
enum_variant_true,
Value,
" \\true ",
EnumOpen("true"),
EnumClose,
);
parse_test!(
enum_variant_false,
Value,
" \\false ",
EnumOpen("false"),
EnumClose,
);
}