Skip to content

Commit

Permalink
Reworked mixed object parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
TCA166 committed Dec 29, 2024
1 parent be3f18b commit eb7fcdb
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 54 deletions.
7 changes: 6 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ I am entirely open to creating a toggleable output theme switch within the progr

This is the main area where help is most needed I would say.
Overall within the Rust source code you can help by: squashing bugs, optimising what I have written, writing documentation, improving accuracy with the in game state and adding new features.
Within the source code itself I mark areas that need special attention by adding comments that contain specific markers like ```TODO```, ```FIXME``` and ```MAYBE```.
Within the source code itself I mark areas that need special attention by adding comments that contain specific markers like ```TODO```, ```FIXME``` and ```MAYBE``` [here](https://github.com/search?q=repo%3ATCA166%2FCK3-history-extractor+%28TODO+OR+FIXME+OR+MAYBE%29&type=code) are all instances of me using them in the source code.
If you work in VsCode I would advise you get [this](https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.todo-tree) extension to mark them.

## Code guidelines
Expand All @@ -37,3 +37,8 @@ Here there are a few more rules I would like you to follow.
1. No lint warnings - do not commit code that has any warnings
2. Each entity within the code must have it's comment documenting what it does - that goes for structs, traits and functions
3. Try to write optimal code - bit of a blanket statement, too vague to be actionable but still worth pointing out

## Branches

Generally speaking moving forward code will be organized into the ```main``` and ```dev``` branches.
You should only do pull requests to the dev branch. Your changes will be merged with main on release.
5 changes: 5 additions & 0 deletions src/parser/game_object.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{
any::type_name,
collections::hash_map::Keys,
error,
fmt::{self, Debug, Display},
num::{ParseFloatError, ParseIntError},
Expand Down Expand Up @@ -454,6 +455,10 @@ impl GameObject<HashMap<String, SaveFileValue>> {
}
}

pub fn keys(&self) -> Keys<'_, String, SaveFileValue> {
self.inner.keys()
}

pub fn to_array(&self) -> Result<GameObjectArray, ConversionError> {
let mut keys = self
.inner
Expand Down
2 changes: 1 addition & 1 deletion src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ mod tests {
.unwrap();
let mut reader = SectionReader::new(&tape);
let object = reader.next().unwrap();
assert!(object.unwrap().parse().is_err())
assert!(object.unwrap().parse().is_ok())
}

#[test]
Expand Down
1 change: 0 additions & 1 deletion src/parser/save_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ impl<'a> SaveFile {

/// Get the tape from the save file.
pub fn tape(&'a self) -> Result<Tape<'a>, jomini::Error> {
// FIXME is broken
if self.binary {
Ok(Tape::Binary(BinaryTape::from_slice(&self.contents)?))
} else {
Expand Down
166 changes: 115 additions & 51 deletions src/parser/section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use super::{
use std::{
error,
fmt::{self, Debug, Display},
num::ParseIntError,
};

/// An error that occured while processing a specific section
Expand Down Expand Up @@ -39,6 +40,12 @@ impl From<ConversionError> for SectionError<'_> {
}
}

impl From<ParseIntError> for SectionError<'_> {
fn from(value: ParseIntError) -> Self {
Self::ConversionError(value.into())
}
}

impl From<ScalarError> for SectionError<'_> {
fn from(value: ScalarError) -> Self {
Self::ScalarError(value)
Expand Down Expand Up @@ -79,28 +86,27 @@ impl<'tape, 'data> Section<'tape, 'data> {
}

pub fn parse(&self) -> Result<SaveFileObject, SectionError> {
let mut stack = vec![];
let mut stack: Vec<(SaveFileObject, bool)> = vec![];
let mut offset = self.offset;
let mut key = false;
match self.tape {
Tokens::Text(text) => {
// TODO the mixed object handling is totally borked. Need a total redesign
while offset < self.length {
match &text[offset] {
TextToken::Array { .. } => {
TextToken::Array { end: _, mixed } => {
let arr = if offset == 0 {
GameObjectArray::from_name(self.name.clone())
} else if let Some(scalar) = &text[offset - 1].as_scalar() {
GameObjectArray::from_name(scalar.to_string())
} else {
GameObjectArray::new()
};
stack.push(SaveFileObject::Array(arr));
stack.push((SaveFileObject::Array(arr), *mixed));
key = false;
}
TextToken::End(_) => {
let new = stack.pop().unwrap();
let last = stack.last_mut().unwrap();
let new = stack.pop().unwrap().0;
let last = &mut stack.last_mut().unwrap().0;
match last {
SaveFileObject::Array(arr) => {
arr.push(SaveFileValue::Object(new));
Expand All @@ -115,39 +121,71 @@ impl<'tape, 'data> Section<'tape, 'data> {
key = false;
}
TextToken::Header(_) => {}
TextToken::Object { .. } => {
TextToken::Object { end: _, mixed } => {
let obj = if offset == 0 {
GameObjectMap::from_name(self.name.clone())
} else if let Some(scalar) = &text[offset - 1].as_scalar() {
GameObjectMap::from_name(scalar.to_string())
} else {
GameObjectMap::new()
};
stack.push(SaveFileObject::Map(obj));
stack.push((SaveFileObject::Map(obj), *mixed));
key = false;
}
TextToken::Quoted(string) | TextToken::Unquoted(string) => {
// value of some kind
let val = SaveFileValue::String(GameString::wrap(string.to_string()));
let obj = stack.last_mut().unwrap();
match obj {
SaveFileObject::Array(arr) => {
arr.push(val);
let (obj, mixed) = stack.last_mut().unwrap();
if *mixed {
if let TextToken::Operator(_) = &text[offset + 1] {
// this is terrible, but probably least verbose
} else {
match obj {
SaveFileObject::Array(arr) => {
arr.push(SaveFileValue::String(GameString::wrap(
string.to_string(),
)));
}
SaveFileObject::Map(map) => {
// find the largest key in the map
let mut largest_key = 0;
for key in map.keys() {
if let Ok(k) = key.parse::<i64>() {
if k > largest_key {
largest_key = k;
}
}
}
map.insert(
key.to_string(),
SaveFileValue::String(GameString::wrap(
string.to_string(),
)),
);
}
}
}
SaveFileObject::Map(map) => {
if key {
if let Some(scalar) = &text[offset - 1].as_scalar() {
map.insert(scalar.to_string(), val);
} else {
// value of some kind
let val =
SaveFileValue::String(GameString::wrap(string.to_string()));
match obj {
SaveFileObject::Array(arr) => {
arr.push(val);
}
SaveFileObject::Map(map) => {
if key {
if let Some(scalar) = &text[offset - 1].as_scalar() {
map.insert(scalar.to_string(), val);
} else {
return Err(SectionError::UnexpectedToken(
offset,
Token::from_text(&text[offset - 1]),
"expected key is non scalar",
));
}
key = false;
} else {
return Err(SectionError::UnexpectedToken(
offset,
Token::from_text(&text[offset - 1]),
"expected key is non scalar",
));
key = true;
}
key = false;
} else {
key = true;
}
}
}
Expand All @@ -160,20 +198,39 @@ impl<'tape, 'data> Section<'tape, 'data> {
"encountered non = operator",
));
}
if let SaveFileObject::Array(arr) = stack.last_mut().unwrap() {
let index = arr.pop().unwrap().as_integer()?;
if let Some(value) = &text[offset + 1].as_scalar() {
arr.insert(
index as usize,
SaveFileValue::String(GameString::wrap(value.to_string())),
);
offset += 1;
let (obj, mixed) = stack.last_mut().unwrap();
if *mixed {
let key = if let Some(scalar) = &text[offset - 1].as_scalar() {
scalar.to_string()
} else {
return Err(SectionError::UnexpectedToken(
offset,
Token::from_text(&text[offset - 1]),
"expected key is non scalar",
));
};
let val = if let Some(scalar) = &text[offset + 1].as_scalar() {
SaveFileValue::String(GameString::wrap(scalar.to_string()))
} else {
return Err(SectionError::UnexpectedToken(
offset + 1,
Token::from_text(&text[offset + 1]),
"array assignment value non scalar",
"expected value is non scalar",
));
};
offset += 1;
match obj {
SaveFileObject::Array(arr) => {
let index: usize = key.parse()?;
if index > arr.len() {
arr.push(val); // MAYBE bad
} else {
arr.insert(index, val);
}
}
SaveFileObject::Map(map) => {
map.insert(key, val);
}
}
} else {
return Err(SectionError::UnexpectedToken(
Expand All @@ -183,15 +240,7 @@ impl<'tape, 'data> Section<'tape, 'data> {
));
}
}
TextToken::MixedContainer => {
if let SaveFileObject::Map(_) = stack.last().unwrap() {
return Err(SectionError::UnexpectedToken(
offset,
Token::from_text(&text[offset]),
"MixedContainer in object",
));
}
}
TextToken::MixedContainer => {}
TextToken::Parameter(_) | TextToken::UndefinedParameter(_) => {
return Err(SectionError::UnexpectedToken(
offset,
Expand All @@ -205,13 +254,13 @@ impl<'tape, 'data> Section<'tape, 'data> {
}
Tokens::Binary(binary) => {
fn add_key_value<'a, 'tok: 'a>(
stack: &mut Vec<SaveFileObject>,
stack: &mut Vec<(SaveFileObject, bool)>,
val: SaveFileValue,
key: &mut bool,
binary: &[BinaryToken<'tok>],
offset: usize,
) -> Result<(), SectionError<'a>> {
let obj = stack.last_mut().unwrap();
let (obj, _mixed) = stack.last_mut().unwrap();
match obj {
SaveFileObject::Array(arr) => {
arr.push(val);
Expand Down Expand Up @@ -244,15 +293,15 @@ impl<'tape, 'data> Section<'tape, 'data> {
} else {
GameObjectArray::new()
};
stack.push(SaveFileObject::Array(arr));
stack.push((SaveFileObject::Array(arr), false));
}
BinaryToken::Bool(b) => {
let val = SaveFileValue::Boolean(*b);
add_key_value(&mut stack, val, &mut key, binary, offset)?;
}
BinaryToken::End(_) => {
let new = stack.pop().unwrap();
let last = stack.last_mut().unwrap();
let new = stack.pop().unwrap().0;
let last = &mut stack.last_mut().unwrap().0;
match last {
SaveFileObject::Array(arr) => {
arr.push(SaveFileValue::Object(new));
Expand Down Expand Up @@ -290,6 +339,7 @@ impl<'tape, 'data> Section<'tape, 'data> {
add_key_value(&mut stack, val, &mut key, binary, offset)?;
}
BinaryToken::MixedContainer | BinaryToken::Equal => {
// TODO fix alongside the rest of the binary parser
return Err(SectionError::UnexpectedToken(
offset,
Token::from_binary(&binary[offset]),
Expand All @@ -302,7 +352,7 @@ impl<'tape, 'data> Section<'tape, 'data> {
} else {
GameObjectMap::new()
};
stack.push(SaveFileObject::Map(obj));
stack.push((SaveFileObject::Map(obj), false));
}
BinaryToken::Quoted(string) | BinaryToken::Unquoted(string) => {
let val = SaveFileValue::String(GameString::wrap(string.to_string()));
Expand Down Expand Up @@ -331,7 +381,7 @@ impl<'tape, 'data> Section<'tape, 'data> {
self.name.clone(),
)));
} else {
return Ok(stack.pop().unwrap());
return Ok(stack.pop().unwrap().0);
}
}
}
Expand Down Expand Up @@ -364,4 +414,18 @@ mod tests {
let obj = section.parse();
assert!(obj.is_err());
}

#[test]
fn test_mixed() {
let tape = Tape::Text(TextTape::from_slice(b"test={a b 1=c 2=d}").unwrap());
let tokens = tape.tokens();
let section = Section::new(tokens, "test".to_string(), 1, 11);
let obj = section.parse().unwrap();
assert_eq!(obj.get_name(), "test");
if let SaveFileObject::Array(arr) = obj {
assert_eq!(arr.len(), 4);
} else {
panic!("expected array");
}
}
}

0 comments on commit eb7fcdb

Please sign in to comment.