redesigned how file is tokenised into string, keyword, identifier, bracket and character
added support for GOTOs and removed object recursive calls
This commit is contained in:
+12
-2
@@ -17,7 +17,9 @@ pub struct Character
|
||||
{
|
||||
name: String,
|
||||
gender: String,
|
||||
eye_color: String,
|
||||
eye_color: Colour,
|
||||
hair_color: Colour,
|
||||
skin_color: Colour,
|
||||
pronoun_subject: String,
|
||||
pronoun_object: String,
|
||||
pronoun_deppos: String,
|
||||
@@ -28,7 +30,6 @@ pub struct Character
|
||||
torso_shape: String,
|
||||
arm_shape: String,
|
||||
leg_shape: String,
|
||||
hair_color: String, // TODO RGB enum
|
||||
clothing: Clothing,
|
||||
}
|
||||
#[derive(Debug,Deserialize,Serialize,Clone,Default)]
|
||||
@@ -39,6 +40,15 @@ pub struct Clothing
|
||||
bottom: String,
|
||||
shoes: String,
|
||||
}
|
||||
#[derive(Debug,Deserialize,Serialize,Clone,Default)]
|
||||
#[serde(default)]
|
||||
pub struct Colour
|
||||
{
|
||||
red: u8,
|
||||
green: u8,
|
||||
blue: u8,
|
||||
}
|
||||
|
||||
impl Character {
|
||||
// Big ass ugly match case
|
||||
pub fn set_field(&mut self, field: &str, value: &str) -> Result<(), String> {
|
||||
|
||||
+3
-3
@@ -106,18 +106,18 @@ async fn main()
|
||||
let space_seperated: Vec<&str> = file_contents
|
||||
.split_whitespace()
|
||||
.collect();
|
||||
if ! space_seperated.contains(&"END")
|
||||
if ! space_seperated.contains(&"END") // TODO remove
|
||||
{
|
||||
warn!("No END statement, story may exit unexpectedly");
|
||||
}
|
||||
let (tokens, labels,_) = tokenise::tokenise(&space_seperated, 0)
|
||||
.unwrap_or_exit("Unable to tokenise data", 15);
|
||||
debug!("{tokens:?}");
|
||||
debug!("{tokens:?}\n{labels:?}");
|
||||
let data_clone2 = Arc::clone(&data_to_send);
|
||||
let characters_clone2 = Arc::clone(&characters);
|
||||
// Run the parsing process for the DSL
|
||||
info!("DSL parsing begun");
|
||||
match parsing::token_parse(&tokens, &characters_clone2, &data_clone2, &rx)
|
||||
match parsing::token_parse(&tokens, &labels, &characters_clone2, &data_clone2, &rx)
|
||||
{
|
||||
// Exit with error or success
|
||||
Ok(()) =>
|
||||
|
||||
+111
-69
@@ -21,6 +21,7 @@ mod character_parse;
|
||||
// Returns success or an error string
|
||||
pub fn token_parse(
|
||||
tokens: &[tokenise::Token],
|
||||
labels: &HashMap<String,usize>,
|
||||
characters: &Arc<Mutex<HashMap::<String, character::Character>>>,
|
||||
data_to_send: &Arc<Mutex<api::DataToSend>>,
|
||||
rx: &Receiver<(bool,usize,String)>,
|
||||
@@ -36,85 +37,126 @@ pub fn token_parse(
|
||||
// Run an infinite loop
|
||||
'parse_loop: loop
|
||||
{
|
||||
debug!("Reading {index}");
|
||||
// Get the next token
|
||||
let token = tokenise::get_string_token(tokens, index)?;
|
||||
let token: String = match tokens.get(index)
|
||||
{
|
||||
Some(tokenise::Token::Keyword(s)) => s.to_string(),
|
||||
// Ignore closing braces and jump over opening brace blocks
|
||||
Some(tokenise::Token::Bracket((bracket,new_index))) =>
|
||||
{
|
||||
if bracket == &tokenise::Bracket::Closing
|
||||
{
|
||||
index += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
warn!("Unexpected brace block, jumping over...");
|
||||
index = new_index + 1;
|
||||
}
|
||||
continue 'parse_loop
|
||||
},
|
||||
// Handle a character
|
||||
Some(tokenise::Token::Character(character_name)) =>
|
||||
{
|
||||
index = match character_parse::character_parse(index+1,tokens,character_name.to_string(),characters,data_to_send)
|
||||
{
|
||||
Ok(increment) => increment,
|
||||
Err((err,increment)) =>
|
||||
{
|
||||
warn!("{err}");
|
||||
increment
|
||||
},
|
||||
};
|
||||
continue 'parse_loop
|
||||
}
|
||||
Some(_) =>
|
||||
{
|
||||
warn!("Unexpected token");
|
||||
index += 1;
|
||||
continue 'parse_loop
|
||||
},
|
||||
None => return Err("File unexpectedly reached termination point".to_string()),
|
||||
};
|
||||
debug!("{index}: {token}");
|
||||
// The instructions are related to characters
|
||||
if token.starts_with('@')
|
||||
match token.to_lowercase().as_str()
|
||||
{
|
||||
let character_name: String = token.chars().skip(1).collect();
|
||||
debug!("Doing something with a character: {character_name}");
|
||||
// The index is incremented to after the character's instructions
|
||||
index = match character_parse::character_parse(index+1, tokens, character_name, characters, data_to_send)
|
||||
"end" =>
|
||||
{
|
||||
Ok(increment) => increment,
|
||||
Err((err,increment)) =>
|
||||
{
|
||||
warn!("{err}");
|
||||
increment
|
||||
},
|
||||
};
|
||||
}
|
||||
// Miscelleneous instructions
|
||||
else
|
||||
{
|
||||
match token.to_lowercase().as_str()
|
||||
info!("END command, exiting");
|
||||
return Ok(()) // quit successfully
|
||||
},
|
||||
"choice" =>
|
||||
{
|
||||
"end" =>
|
||||
let mut next_token: String = token;
|
||||
let mut choices: Vec<String> = Vec::new();
|
||||
let mut choice_indeces: Vec<usize> = Vec::new();
|
||||
while next_token == "or" || next_token == "choice"
|
||||
{
|
||||
info!("END command, exiting");
|
||||
return Ok(()) // quit successfully
|
||||
},
|
||||
"choice" =>
|
||||
{
|
||||
debug!("Doing CHOICE");
|
||||
let mut next_token = token;
|
||||
let mut choices: Vec<String> = Vec::new();
|
||||
let mut choice_indeces: Vec<usize> = Vec::new();
|
||||
while next_token == "or" || next_token == "choice"
|
||||
index += 1;
|
||||
choices.push
|
||||
(
|
||||
tokenise::get_string_token(tokens, index)?
|
||||
);
|
||||
index += 1;
|
||||
choice_indeces.push(index+1);
|
||||
index = match tokenise::get_closing_index(tokens,index)
|
||||
{
|
||||
index += 1;
|
||||
choices.push
|
||||
(
|
||||
tokenise::get_string_token(tokens, index)?
|
||||
);
|
||||
choice_indeces.push(index+1);
|
||||
index += 2;
|
||||
next_token = match tokenise::get_string_token(tokens, index)
|
||||
{
|
||||
Ok(string) => string,
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
debug!("{choices:?}");
|
||||
debug!("{choice_indeces:?}");
|
||||
api::modify_data(data_to_send, "choice".to_string(), String::new(), String::new(), choices);
|
||||
if rx.recv().is_err() { warn!("Error sending choices to client"); }
|
||||
let choice = match rx.recv()
|
||||
{
|
||||
Ok((_,choice,_)) => choice_indeces[choice],
|
||||
Err(err) =>
|
||||
{
|
||||
warn!("Error receiving choice from client, defaulting to choice 0 {err}");
|
||||
0
|
||||
}
|
||||
Ok(new_index) => new_index + 1,
|
||||
Err(_) => break,
|
||||
};
|
||||
let object = tokenise::get_object_token(tokens, choice)?; // TODO make more efficient
|
||||
debug!("{object:?}");
|
||||
// Don't propogate exit error
|
||||
let _ = token_parse(object, characters, data_to_send, rx);
|
||||
continue 'parse_loop
|
||||
},
|
||||
"or" =>
|
||||
{
|
||||
info!("OR command, jumping over");
|
||||
index += 2;
|
||||
continue
|
||||
},
|
||||
_ =>
|
||||
{
|
||||
warn!("Invalid command: {token}");
|
||||
next_token = match tokenise::get_keyword_token(tokens, index)
|
||||
{
|
||||
Ok(string) => string,
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
debug!("{choices:?}");
|
||||
debug!("{choice_indeces:?}");
|
||||
api::modify_data(data_to_send, "choice".to_string(), String::new(), String::new(), choices);
|
||||
if rx.recv().is_err() { warn!("Error sending choices to client"); }
|
||||
let choice = match rx.recv()
|
||||
{
|
||||
Ok((_,choice,_)) => choice_indeces[choice],
|
||||
Err(err) =>
|
||||
{
|
||||
warn!("Error receiving choice from client, defaulting to choice 0 {err}");
|
||||
0
|
||||
}
|
||||
};
|
||||
index = choice;
|
||||
continue 'parse_loop
|
||||
},
|
||||
"or" =>
|
||||
{
|
||||
info!("OR command, jumping over");
|
||||
index += 2;
|
||||
let new_index = tokenise::get_closing_index(tokens, index)?;
|
||||
index = new_index;
|
||||
continue 'parse_loop
|
||||
},
|
||||
// Jump to a particular index based on a label eg GOTO character_check
|
||||
"goto" =>
|
||||
{
|
||||
index += 1;
|
||||
let label = tokenise::get_keyword_token(tokens, index)?;
|
||||
index = match labels.get(&label)
|
||||
{
|
||||
Some(label_index) => *label_index,
|
||||
None =>
|
||||
{
|
||||
warn!("Label {label} does not exist");
|
||||
index
|
||||
}
|
||||
};
|
||||
debug!("Jumping to {index}");
|
||||
continue 'parse_loop
|
||||
}
|
||||
_ =>
|
||||
{
|
||||
warn!("Invalid command: {token}");
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
if rx.recv().is_err()
|
||||
|
||||
@@ -28,12 +28,9 @@ pub fn character_parse
|
||||
{
|
||||
let mut sum_index: usize = index;
|
||||
// Ensure the index is valid (the index is not beyond the vector)
|
||||
let token = match tokens.get(sum_index) {
|
||||
Some(tokenise::Token::String(s)) => s.clone(),
|
||||
Some(_) => return Err(("Unexpected token".to_string(), sum_index + 1)),
|
||||
None => return Err(("File unexpectedly reached termination point".to_string(), sum_index)),
|
||||
};
|
||||
match token.to_lowercase().as_str()
|
||||
let keyword = tokenise::get_keyword_token(tokens, sum_index)
|
||||
.map_err(|err| (err, sum_index))?;
|
||||
match keyword.to_lowercase().as_str()
|
||||
{
|
||||
// The character is saying something, so grab the text and pass it
|
||||
// to the client
|
||||
@@ -51,7 +48,7 @@ pub fn character_parse
|
||||
"change" =>
|
||||
{
|
||||
sum_index += 1;
|
||||
let feature = tokenise::get_string_token(tokens, sum_index)
|
||||
let feature = tokenise::get_keyword_token(tokens, sum_index)
|
||||
.map_err(|err| (err, index))?;
|
||||
sum_index += 1;
|
||||
let string = tokenise::get_string_token(tokens, sum_index)
|
||||
@@ -69,13 +66,13 @@ pub fn character_parse
|
||||
"to"|"animate" =>
|
||||
{
|
||||
sum_index += 1;
|
||||
let content = tokenise::get_string_token(tokens, sum_index)
|
||||
let content = tokenise::get_keyword_token(tokens, sum_index)
|
||||
.map_err(|err| (err, index))?;
|
||||
api::modify_data(data_to_send, token.to_lowercase(), content, character_name, vec![]);
|
||||
api::modify_data(data_to_send, keyword.to_lowercase(), content, character_name, vec![]);
|
||||
},
|
||||
// Catch all condition, if the instruction is unrecognised as a
|
||||
// character command
|
||||
_ => return Err((format!("Invalid command: {token}"),sum_index)),
|
||||
_ => return Err((format!("Invalid command: {keyword}"),sum_index)),
|
||||
}
|
||||
sum_index += 1;
|
||||
Ok(sum_index)
|
||||
|
||||
+59
-23
@@ -1,13 +1,23 @@
|
||||
use crate::
|
||||
{
|
||||
exit,
|
||||
HashMap,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Token
|
||||
{
|
||||
String(String),
|
||||
Object(Vec<Self>),
|
||||
String(String),
|
||||
Keyword(String), // Keywords aren't checked for validity in this stage
|
||||
Identifier(String),
|
||||
Bracket((Bracket,usize)), // Stores the index of the matching deliminator
|
||||
Character(String),
|
||||
}
|
||||
#[derive(Debug,Clone,PartialEq)]
|
||||
pub enum Bracket
|
||||
{
|
||||
Opening,
|
||||
Closing,
|
||||
}
|
||||
|
||||
pub fn get_string_token(tokens: &[Token], index: usize)
|
||||
@@ -19,12 +29,20 @@ pub fn get_string_token(tokens: &[Token], index: usize)
|
||||
None => Err("File unexpectedly reached termination point".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_object_token(tokens: &[Token], index: usize)
|
||||
-> Result<&Vec<Token>, String>
|
||||
pub fn get_keyword_token(tokens: &[Token], index: usize)
|
||||
-> Result<String, String>
|
||||
{
|
||||
match tokens.get(index) {
|
||||
Some(Token::Object(v)) => Ok(v),
|
||||
Some(Token::Keyword(s)) => Ok(s.clone()),
|
||||
Some(_) => Err("Unexpected token".to_string()),
|
||||
None => Err("File unexpectedly reached termination point".to_string()),
|
||||
}
|
||||
}
|
||||
pub fn get_closing_index(tokens: &[Token], index: usize)
|
||||
-> Result<usize, String>
|
||||
{
|
||||
match tokens.get(index) {
|
||||
Some(Token::Bracket((_, index))) => Ok(*index), // TODO check for closing
|
||||
Some(_) => Err("Unexpected token".to_string()),
|
||||
None => Err("File unexpectedly reached termination point".to_string()),
|
||||
}
|
||||
@@ -34,37 +52,55 @@ pub fn get_object_token(tokens: &[Token], index: usize)
|
||||
// It can contain sub-objects (using recursive calls)
|
||||
// TODO check for END
|
||||
pub fn tokenise(space_seperated: &Vec<&str>, mut index: usize)
|
||||
-> Result<(Vec<Token>,Vec<(String,usize)>,usize),String>
|
||||
-> Result<(Vec<Token>,HashMap<String,usize>,usize),String>
|
||||
{
|
||||
let mut tokenised_data: Vec<Token> = Vec::new();
|
||||
let mut labels: Vec<(String,usize)> = Vec::new();
|
||||
let mut labels: HashMap<String, usize> = HashMap::new();
|
||||
let mut bracket_stack: Vec<usize> = Vec::new();
|
||||
while index < space_seperated.len()
|
||||
{
|
||||
let mut item = space_seperated[index].to_string();
|
||||
let object: Vec<Token>;
|
||||
if item.starts_with('"') // TODO support '
|
||||
// Characters
|
||||
if item.starts_with('@')
|
||||
{
|
||||
(index, item) = match tokenise_string(space_seperated, index)
|
||||
{
|
||||
Some((index,item)) => (index,item),
|
||||
None => exit(1),
|
||||
};
|
||||
let mut chars = item.chars();
|
||||
chars.next();
|
||||
tokenised_data.push(Token::Character(chars.as_str().to_string()));
|
||||
}
|
||||
// Strings
|
||||
else if item.starts_with('"') // TODO support '
|
||||
{
|
||||
let Some((new_index, new_item)) = tokenise_string(space_seperated, index)
|
||||
else { exit(1) };
|
||||
index = new_index;
|
||||
item = new_item;
|
||||
tokenised_data.push(Token::String(item));
|
||||
}
|
||||
// Labels
|
||||
else if item.ends_with(':')
|
||||
{
|
||||
let mut chars = item.chars();
|
||||
chars.next_back();
|
||||
let label = chars.as_str();
|
||||
labels.insert
|
||||
(
|
||||
label.to_string(),
|
||||
tokenised_data.len(),
|
||||
);
|
||||
}
|
||||
// On a new indentation level, do a recursive call
|
||||
else if item == "{"
|
||||
{
|
||||
(object, labels, index) = match tokenise(space_seperated, index+1) // !WARNING! recursive call
|
||||
{
|
||||
Ok((object,labels,index)) => (object,labels,index),
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
tokenised_data.push(Token::Object(object));
|
||||
bracket_stack.push(tokenised_data.len());
|
||||
tokenised_data.push(Token::Bracket((Bracket::Opening,0))); // TODO fix no closing brace edge case
|
||||
}
|
||||
else if item == "}"
|
||||
{
|
||||
return Ok((tokenised_data,labels,index));
|
||||
let prev_index = bracket_stack.pop().unwrap(); // TODO eh
|
||||
tokenised_data[prev_index] = Token::Bracket((Bracket::Opening, tokenised_data.len()));
|
||||
tokenised_data.push(Token::Bracket((Bracket::Closing,prev_index)));
|
||||
}
|
||||
else { tokenised_data.push(Token::String(item)); }
|
||||
else { tokenised_data.push(Token::Keyword(item)); }
|
||||
index += 1;
|
||||
}
|
||||
Ok((tokenised_data, labels, 0))
|
||||
|
||||
Reference in New Issue
Block a user