mirror of
https://codeberg.org/icewind/vdf-reader.git
synced 2026-06-03 18:14:07 +02:00
unquote strings
This commit is contained in:
parent
f492a63ab1
commit
c7d87804f8
4 changed files with 96 additions and 69 deletions
|
|
@ -31,8 +31,8 @@ impl Table {
|
|||
pub fn load(reader: &mut Reader) -> Result<Table> {
|
||||
let mut map = HashMap::new();
|
||||
|
||||
loop {
|
||||
match reader.event()? {
|
||||
while let Some(event) = reader.event() {
|
||||
match event? {
|
||||
Event::Entry {
|
||||
key: Item::Statement { .. },
|
||||
span,
|
||||
|
|
@ -58,7 +58,7 @@ impl Table {
|
|||
insert(&mut map, name.into(), Table::load(reader)?.into())
|
||||
}
|
||||
|
||||
Event::GroupEnd { .. } | Event::End { .. } => break,
|
||||
Event::GroupEnd { .. } => break,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,11 +16,19 @@ pub enum Token {
|
|||
#[display("end of group")]
|
||||
GroupEnd,
|
||||
/// An enclosed or bare item.
|
||||
#[regex("(\"([^\"\\\\]|\\\\.)*\")|([^# \t\n{}\"][^ \"\t\n]*)", priority = 0)]
|
||||
#[regex("[^# \t\n{}\"][^ \"\t\n]*", priority = 0)]
|
||||
#[display("item")]
|
||||
Item,
|
||||
/// An enclosed or bare item.
|
||||
#[regex("\"([^\"\\\\]|\\\\.)*\"")]
|
||||
#[display("item")]
|
||||
QuotedItem,
|
||||
/// An enclosed or bare statement.
|
||||
#[regex("(\"#([^\"\\\\]|\\\\.)*\")|(#[^ \"\t\n]+)")]
|
||||
#[regex("\"#([^\"\\\\]|\\\\.)*\"")]
|
||||
#[display("statement")]
|
||||
QuotedStatement,
|
||||
/// An enclosed or bare statement.
|
||||
#[regex("#[^ \"\t\n]+")]
|
||||
#[display("statement")]
|
||||
Statement,
|
||||
}
|
||||
|
|
@ -46,11 +54,11 @@ mod tests {
|
|||
#[test]
|
||||
fn next() {
|
||||
assert_eq!(get_token("test"), Some(Ok(Token::Item)));
|
||||
assert_eq!(get_token("\"test\""), Some(Ok(Token::Item)));
|
||||
assert_eq!(get_token("\"\""), Some(Ok(Token::Item)));
|
||||
assert_eq!(get_token("\"\" "), Some(Ok(Token::Item)));
|
||||
assert_eq!(get_token("\"test\""), Some(Ok(Token::QuotedItem)));
|
||||
assert_eq!(get_token("\"\""), Some(Ok(Token::QuotedItem)));
|
||||
assert_eq!(get_token("\"\" "), Some(Ok(Token::QuotedItem)));
|
||||
assert_eq!(get_token("#test"), Some(Ok(Token::Statement)));
|
||||
assert_eq!(get_token("\"#test\""), Some(Ok(Token::Statement)));
|
||||
assert_eq!(get_token("\"#test\""), Some(Ok(Token::QuotedStatement)));
|
||||
assert_eq!(get_token("{"), Some(Ok(Token::GroupStart)));
|
||||
assert_eq!(get_token("}"), Some(Ok(Token::GroupEnd)));
|
||||
assert_eq!(get_token("//test more"), None);
|
||||
|
|
@ -67,12 +75,12 @@ mod tests {
|
|||
assert_eq!(get_token("lol}"), Some(Ok(Token::Item)));
|
||||
assert_eq!(get_token("#lol}"), Some(Ok(Token::Statement)));
|
||||
|
||||
assert_eq!(get_token("\"test\""), Some(Ok(Token::Item)));
|
||||
assert_eq!(get_token("\"#test\""), Some(Ok(Token::Statement)));
|
||||
assert_eq!(get_token("\"test\""), Some(Ok(Token::QuotedItem)));
|
||||
assert_eq!(get_token("\"#test\""), Some(Ok(Token::QuotedStatement)));
|
||||
|
||||
assert_eq!(get_token("\"te\\\"st\""), Some(Ok(Token::Item)));
|
||||
assert_eq!(get_token("\"te\\st\""), Some(Ok(Token::Item)));
|
||||
assert_eq!(get_token("\"#te\\\"st\""), Some(Ok(Token::Statement)));
|
||||
assert_eq!(get_token("\"te\\\"st\""), Some(Ok(Token::QuotedItem)));
|
||||
assert_eq!(get_token("\"te\\st\""), Some(Ok(Token::QuotedItem)));
|
||||
assert_eq!(get_token("\"#te\\\"st\""), Some(Ok(Token::QuotedStatement)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -89,12 +97,12 @@ mod tests {
|
|||
Ok(vec![
|
||||
(Token::Item, "foo"),
|
||||
(Token::GroupStart, "{"),
|
||||
(Token::Item, r#""asd""#),
|
||||
(Token::Item, r#""bar""#),
|
||||
(Token::QuotedItem, r#""asd""#),
|
||||
(Token::QuotedItem, r#""bar""#),
|
||||
(Token::Statement, r#"#include"#),
|
||||
(Token::Item, r#"other"#),
|
||||
(Token::Item, r#"empty"#),
|
||||
(Token::Item, r#""""#),
|
||||
(Token::QuotedItem, r#""""#),
|
||||
(Token::GroupEnd, "}")
|
||||
])
|
||||
)
|
||||
|
|
|
|||
107
src/reader.rs
107
src/reader.rs
|
|
@ -44,9 +44,6 @@ pub enum Event<'a> {
|
|||
value: Item<'a>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
/// EOF has been reached.
|
||||
End { span: Span },
|
||||
}
|
||||
|
||||
impl Event<'_> {
|
||||
|
|
@ -56,7 +53,6 @@ impl Event<'_> {
|
|||
Event::GroupStart { span, .. } => span.clone(),
|
||||
Event::GroupEnd { span, .. } => span.clone(),
|
||||
Event::Entry { span, .. } => span.clone(),
|
||||
Event::End { span, .. } => span.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -79,96 +75,119 @@ impl<'a> From<&'a str> for Reader<'a> {
|
|||
impl<'a> Reader<'a> {
|
||||
/// Get the next event, this does copies.
|
||||
#[allow(dead_code)]
|
||||
pub fn event(&mut self) -> Result<Event> {
|
||||
pub fn event(&mut self) -> Option<Result<Event>> {
|
||||
const VALID_KEY: &[Token] = &[
|
||||
Token::Item,
|
||||
Token::QuotedItem,
|
||||
Token::GroupEnd,
|
||||
Token::Statement,
|
||||
Token::QuotedStatement,
|
||||
];
|
||||
|
||||
let key = match self.lexer.next() {
|
||||
None => {
|
||||
return Ok(Event::End {
|
||||
span: self.lexer.span(),
|
||||
})
|
||||
return None;
|
||||
}
|
||||
Some((Err(_), span)) => {
|
||||
return Err(NoValidTokenError::new(
|
||||
&[Token::Item, Token::GroupEnd, Token::Statement],
|
||||
return Some(Err(NoValidTokenError::new(
|
||||
VALID_KEY,
|
||||
span.into(),
|
||||
self.content.into(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
Some((Ok(Token::GroupEnd), span)) => return Ok(Event::GroupEnd { span }),
|
||||
Some((Ok(Token::GroupStart), span)) => {
|
||||
return Err(UnexpectedTokenError::new(
|
||||
&[Token::Item, Token::GroupEnd, Token::Statement],
|
||||
Some(Token::GroupStart),
|
||||
span.into(),
|
||||
self.content.into(),
|
||||
)
|
||||
.into())
|
||||
.into()));
|
||||
}
|
||||
Some((Ok(Token::GroupEnd), span)) => return Some(Ok(Event::GroupEnd { span })),
|
||||
|
||||
Some((Ok(Token::Item), span)) => Item::Value {
|
||||
content: string(self.lexer.slice()),
|
||||
span,
|
||||
},
|
||||
|
||||
Some((Ok(Token::QuotedItem), span)) => Item::Value {
|
||||
content: quoted_string(self.lexer.slice()),
|
||||
span,
|
||||
},
|
||||
|
||||
Some((Ok(Token::Statement), span)) => Item::Statement {
|
||||
content: string(self.lexer.slice()),
|
||||
span,
|
||||
},
|
||||
|
||||
Some((Ok(Token::QuotedStatement), span)) => Item::Statement {
|
||||
content: quoted_string(self.lexer.slice()),
|
||||
span,
|
||||
},
|
||||
|
||||
Some((Ok(token), span)) => {
|
||||
return Some(Err(UnexpectedTokenError::new(
|
||||
VALID_KEY,
|
||||
Some(token),
|
||||
span.into(),
|
||||
self.content.into(),
|
||||
)
|
||||
.into()))
|
||||
}
|
||||
};
|
||||
|
||||
const VALID_VALUE: &[Token] = &[Token::Item, Token::QuotedItem, Token::GroupStart];
|
||||
|
||||
let value = match self.lexer.next() {
|
||||
None => {
|
||||
return Err(UnexpectedTokenError::new(
|
||||
&[Token::Item, Token::GroupEnd, Token::Statement],
|
||||
return Some(Err(UnexpectedTokenError::new(
|
||||
VALID_VALUE,
|
||||
None,
|
||||
self.lexer.span().into(),
|
||||
self.content.into(),
|
||||
)
|
||||
.into());
|
||||
.into()));
|
||||
}
|
||||
|
||||
Some((Err(_), span)) => {
|
||||
return Err(NoValidTokenError::new(
|
||||
&[Token::Item, Token::GroupEnd, Token::Statement],
|
||||
return Some(Err(NoValidTokenError::new(
|
||||
VALID_VALUE,
|
||||
span.into(),
|
||||
self.content.into(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
Some((Ok(Token::GroupEnd), span)) => {
|
||||
return Err(UnexpectedTokenError::new(
|
||||
&[Token::Item, Token::GroupStart, Token::Statement],
|
||||
Some(Token::GroupEnd),
|
||||
span.into(),
|
||||
self.content.into(),
|
||||
)
|
||||
.into())
|
||||
.into()));
|
||||
}
|
||||
|
||||
Some((Ok(Token::GroupStart), span)) => {
|
||||
return Ok(Event::GroupStart {
|
||||
return Some(Ok(Event::GroupStart {
|
||||
name: key.into_content(),
|
||||
span,
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
Some((Ok(Token::QuotedItem), span)) => Item::Value {
|
||||
content: quoted_string(self.lexer.slice()),
|
||||
span,
|
||||
},
|
||||
|
||||
Some((Ok(Token::Item), span)) => Item::Value {
|
||||
content: string(self.lexer.slice()),
|
||||
span,
|
||||
},
|
||||
|
||||
Some((Ok(Token::Statement), span)) => Item::Statement {
|
||||
content: string(self.lexer.slice()),
|
||||
span,
|
||||
},
|
||||
Some((Ok(token), span)) => {
|
||||
return Some(Err(UnexpectedTokenError::new(
|
||||
VALID_VALUE,
|
||||
Some(token),
|
||||
span.into(),
|
||||
self.content.into(),
|
||||
)
|
||||
.into()))
|
||||
}
|
||||
};
|
||||
|
||||
let span = key.span().start..value.span().end;
|
||||
Ok(Event::Entry { key, value, span })
|
||||
Some(Ok(Event::Entry { key, value, span }))
|
||||
}
|
||||
}
|
||||
|
||||
fn quoted_string(source: &str) -> Cow<str> {
|
||||
string(&source[1..source.len() - 1])
|
||||
}
|
||||
|
||||
fn string(source: &str) -> Cow<str> {
|
||||
if source.contains(r#"\""#) || source.contains(r#"\\"#) {
|
||||
let mut buffer = source.bytes();
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ source: tests/parse.rs
|
|||
expression: parsed
|
||||
---
|
||||
Table({
|
||||
"\"LightmappedGeneric\"": Table(Table({
|
||||
"\"$detailblendfactor\"": Value(Value("\"1\"")),
|
||||
"\"%keywords\"": Value(Value("\"tf\"")),
|
||||
"\"$ssbump\"": Value(Value("\"1\"")),
|
||||
"\"$baseTexture\"": Value(Value("\"cp_mountainlab/concrete/concretefloor003\"")),
|
||||
"\"$detailblendmode\"": Value(Value("\"0\"")),
|
||||
"\"$detailscale\"": Value(Value("\"1.9\"")),
|
||||
"\"$bumpmap\"": Value(Value("\"concrete/concretefloor007b_height-ssbump\"")),
|
||||
"\"$detail\"": Value(Value("\"overlays/detail001\"")),
|
||||
"LightmappedGeneric": Table(Table({
|
||||
"$detailblendmode": Value(Value("0")),
|
||||
"$detailscale": Value(Value("1.9")),
|
||||
"%keywords": Value(Value("tf")),
|
||||
"$bumpmap": Value(Value("concrete/concretefloor007b_height-ssbump")),
|
||||
"$detail": Value(Value("overlays/detail001")),
|
||||
"$baseTexture": Value(Value("cp_mountainlab/concrete/concretefloor003")),
|
||||
"$detailblendfactor": Value(Value("1")),
|
||||
"$ssbump": Value(Value("1")),
|
||||
})),
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue