Compare commits

...

2 Commits

Author SHA1 Message Date
deadvey 13049309b2 Changed how the data is tokenised 2026-05-17 12:47:22 +01:00
deadvey 0c28bc113d started working on new tokeniser 2026-05-16 21:11:45 +01:00
12 changed files with 412 additions and 178 deletions
+18 -11
View File
@@ -1,23 +1,30 @@
import requests import requests
import os import os
import time
# Loop and get new api # Loop and get new api
def main(): def main():
response = {} response = {}
id = -1
while True: while True:
try: try:
response = api_get() response = api_get()
print(response) if response["id"] != id:
match response["action_type"]: id = response["id"]
case "output": print(response)
character = get_character(response["character"]) match response["action_type"]:
output(character, response["content"]) case "output":
case "choice": character = get_character(response["character"])
user_choice = choice(response["choices"]) output(character, response["content"])
continue case "choice":
case "end": user_choice = choice(response["choices"])
print("Exitting successfully") time.sleep(0.5)
os._exit(0) continue
case "end":
print("Exitting successfully")
os._exit(0)
else:
continue
except: except:
print("Server not up or cannot be reached") print("Server not up or cannot be reached")
input() # Enter to go to next loop (testing) input() # Enter to go to next loop (testing)
+184
View File
@@ -0,0 +1,184 @@
use std::collections::HashMap;
use crate::
{
// Internal code
character,
api,
tokenise,
// Libraries
mpsc::Receiver,
Arc,
Mutex,
info,
debug,
warn,
type_name,
};
mod strings;
mod character_parse;
// Parse the tokens in a file
// Returns success or an error string
pub fn token_parse(
tokens: &Vec<tokenise::Token>,
characters: &Arc<Mutex<HashMap::<String, character::Character>>>,
data_to_send: &Arc<Mutex<api::DataToSend>>,
rx: &Receiver<(bool,usize,String)>,
) -> Result<(),String>
{
info!("DSL parsing begun");
let mut index: usize = 0;
if rx.recv().is_err()
{
warn!("Some issue with api");
// TODO eh?
}
info!("Client has connected");
// Run an infinite loop
'parse_loop: loop
{
// Get the next token
let token = match tokens.get(index) {
Some(tokenise::Token::String(s)) => s.clone(),
Some(_) => return Err("Unexpected token".to_string()),
None => return Err("File unexpectedly reached termination point".to_string()),
};
debug!("{index}: {token}");
// The instructions are related to characters
if token.starts_with('@')
{
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)
{
Ok(increment) => increment,
Err((err,increment)) =>
{
warn!("{err}");
increment
},
};
}
// Miscelleneous instructions
else
{
match token.to_lowercase().as_str()
{
"end" =>
{
info!("END command, exiting");
return Ok(()) // quit successfully
},
"choice" =>
{
let mut choices: Vec<String> = Vec::new();
let mut choice_indeces: Vec<usize> = Vec::new();
index += 1;
choices.push
(match tokens.get(index) {
Some(tokenise::Token::String(s)) => s.clone(),
Some(_) => return Err("Unexpected token".to_string()),
None => return Err("File unexpectedly reached termination point".to_string()),
});
choice_indeces.push(index+1);
index += 2;
let next_token = match tokens.get(index) {
Some(tokenise::Token::String(s)) => s.clone(),
Some(_) => return Err("Unexpected token".to_string()),
None => return Err("File unexpectedly reached termination point".to_string()),
};
while next_token == "or"
{
index += 1
choices.push
(match tokens.get(index) {
Some(tokenise::Token::String(s)) => s.clone(),
Some(_) => return Err("Unexpected token".to_string()),
None => return Err("File unexpectedly reached termination point".to_string()),
});
}
},
/*
"choice" =>
{
let (_,jump_points) = match choice_parse(index+1, tokens, data_to_send)
{
Ok((increment,jump_point)) => (increment,jump_point),
Err(error) => return Err(error),
};
if rx.recv().is_err() { warn!("Error sending choices to client"); }
let (_, choice, _) = match rx.recv()
{
Ok((_,choice,_)) => (None::<bool>, choice, None::<String>),
Err(err) =>
{
warn!("Error receiving choice from client, defaulting to choice 0 {err}");
(None::<bool>, 0, None::<String>)
}
};
index = jump_points[choice];
info!("CHOICE command with {} choices",jump_points.len());
debug!("{jump_points:?} {choice} {index}");
continue 'parse_loop
},
*/
"or" =>
{
info!("OR command, jumping over");
index += 2;
continue
},
_ =>
{
warn!("Invalid command: {token}");
}
}
}
if rx.recv().is_err()
{
warn!("Some issue with api");
}
}
}
// Parse the options in a choice clause and returns the idexes of the code blocks
fn choice_parse
(
index: usize,
tokens: &[&str],
data_to_send: &Arc<Mutex<api::DataToSend>>,
) -> Result<(usize, Vec<usize>), String>
{
let mut sum_index: usize = index;
let mut choices: Vec<String> = Vec::new();
let mut choice_indeces: Vec<usize> = Vec::new();
// Ensure the index is valid (the index is not beyond the vector)
// Get the initial choice
let (choice_string, counter) = strings::extract_quoted(&tokens[sum_index..])
.ok_or_else(|| "No choice string".to_string())?;
sum_index += counter;
choices.push(choice_string);
choice_indeces.push(sum_index+1);
sum_index += strings::closing_char(&tokens[sum_index..], '{','}')
.ok_or_else(|| "No closing brace".to_string())? + 1;
// Find all the alternate choices labelled with OR
// Fill out the choices vector with all the choice strings
while tokens[sum_index].to_lowercase() == "or"
{
let (choice_string, counter) = strings::extract_quoted(&tokens[sum_index+1..])
.ok_or_else(|| "No choice string".to_string())?;
sum_index += counter;
choices.push(choice_string);
choice_indeces.push(sum_index+2);
sum_index += strings::closing_char(&tokens[sum_index..], '{','}')
.ok_or_else(|| "No closing brace".to_string())? + 1;
}
debug!("{choices:?}");
// Send the choices to the Client via the API
api::modify_data(data_to_send, "choice".to_string(), String::new(), String::new(), choices);
// Return the choice indeces
Ok((sum_index + 1, choice_indeces))
}
+20 -7
View File
@@ -20,6 +20,7 @@ use crate::
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
pub struct DataToSend { pub struct DataToSend {
pub id: u32,
pub action_type: String, pub action_type: String,
pub content: String, pub content: String,
pub character: String, pub character: String,
@@ -33,7 +34,7 @@ pub async fn api_process
( (
data_to_send: Arc<Mutex<DataToSend>>, data_to_send: Arc<Mutex<DataToSend>>,
characters: Arc<Mutex<HashMap::<String,character::Character>>>, characters: Arc<Mutex<HashMap::<String,character::Character>>>,
tx: Sender<(bool, usize)>, tx: Sender<(bool, usize, String)>,
) )
{ {
// This data must be passed through to the api route in order to be used // This data must be passed through to the api route in order to be used
@@ -41,6 +42,7 @@ pub async fn api_process
let characters_filter = warp::any().map(move || Arc::clone(&characters)); let characters_filter = warp::any().map(move || Arc::clone(&characters));
let tx_filter = warp::any().map(move || tx.clone()); let tx_filter = warp::any().map(move || tx.clone());
let tx_filter2 = tx_filter.clone(); let tx_filter2 = tx_filter.clone();
let tx_filter3 = tx_filter.clone();
info!("Running server"); info!("Running server");
@@ -50,11 +52,11 @@ pub async fn api_process
.and(data_filter) .and(data_filter)
.and(tx_filter) .and(tx_filter)
// Perform this code on a GET request // Perform this code on a GET request
.map(|state: Arc<Mutex<DataToSend>>, tx_handle: Sender<(bool,usize)>| .map(|state: Arc<Mutex<DataToSend>>, tx_handle: Sender<(bool,usize,String)>|
{ {
debug!("GET: {state:?}"); //debug!("GET: {state:?}");
let reply = state.as_ref(); let reply = state.as_ref();
let _ = tx_handle.send((true,0)); let _ = tx_handle.send((true,0,String::new()));
warp::reply::json(&reply) // Send the reply data (data_to_send formatted as JSON) warp::reply::json(&reply) // Send the reply data (data_to_send formatted as JSON)
}).boxed(); }).boxed();
let characters = warp::path("character") let characters = warp::path("character")
@@ -89,14 +91,24 @@ pub async fn api_process
.and(warp::post()) .and(warp::post())
.and(warp::body::json()) .and(warp::body::json())
.and(tx_filter2) .and(tx_filter2)
.map(|index: usize, tx_handle: Sender<(bool,usize)>| { .map(|index: usize, tx_handle: Sender<(bool,usize,String)>| {
debug!("Choice: {index}"); debug!("Choice: {index}");
let _ = tx_handle.send((true,index)); let _ = tx_handle.send((true,index,String::new()));
let reply = "ack";
warp::reply::json(&reply)
}).boxed();
let input = warp::path("input")
.and(warp::post())
.and(warp::body::json())
.and(tx_filter3)
.map(|input: String, tx_handle: Sender<(bool, usize, String)>|
{
let _ = tx_handle.send((true,0,input));
let reply = "ack"; let reply = "ack";
warp::reply::json(&reply) warp::reply::json(&reply)
}).boxed(); }).boxed();
let routes = main.or(characters).or(choice); let routes = main.or(characters).or(choice).or(input);
// Start the server // Start the server
warp::serve(routes) warp::serve(routes)
.run(([127, 0, 0, 1],config::API_PORT)) .run(([127, 0, 0, 1],config::API_PORT))
@@ -115,6 +127,7 @@ pub fn modify_data
) )
{ {
let mut data = data_to_send.lock().unwrap_or_exit("Data to send Mutex was poisoned",2); // TODO eh? let mut data = data_to_send.lock().unwrap_or_exit("Data to send Mutex was poisoned",2); // TODO eh?
data.id += 1;
data.action_type = action_type; data.action_type = action_type;
data.content = content; data.content = content;
data.character = character_name; data.character = character_name;
+18 -17
View File
@@ -39,25 +39,26 @@ pub struct Clothing
shoes: String, shoes: String,
} }
impl Character { impl Character {
// Big ass ugly function because rust doesn't support referencing struct entries by string
pub fn set_field(&mut self, field: &str, value: &str) -> Result<(), ()> { pub fn set_field(&mut self, field: &str, value: &str) -> Result<(), ()> {
match field { match field {
"name" => self.name = value.to_string(), "name" => self.name = value.to_string(),
"gender" => self.gender = value.to_string(), "gender" => self.gender = value.to_string(),
"eye_color" => self.eye_color = value.to_string(), "eye_color" => self.eye_color = value.to_string(),
"pronoun_subject" => self.pronoun_subject = value.to_string(), "pronoun_subject" => self.pronoun_subject = value.to_string(),
"pronoun_object" => self.pronoun_object = value.to_string(), "pronoun_object" => self.pronoun_object = value.to_string(),
"pronoun_deppos" => self.pronoun_deppos = value.to_string(), "pronoun_deppos" => self.pronoun_deppos = value.to_string(),
"pronoun_indpos" => self.pronoun_indpos = value.to_string(), "pronoun_indpos" => self.pronoun_indpos = value.to_string(),
"pronoun_reflex" => self.pronoun_reflex = value.to_string(), "pronoun_reflex" => self.pronoun_reflex = value.to_string(),
"head_shape" => self.head_shape = value.to_string(), "head_shape" => self.head_shape = value.to_string(),
"hair_style" => self.hair_style = value.to_string(), "hair_style" => self.hair_style = value.to_string(),
"torso_shape" => self.torso_shape = value.to_string(), "torso_shape" => self.torso_shape = value.to_string(),
"arm_shape" => self.arm_shape = value.to_string(), "arm_shape" => self.arm_shape = value.to_string(),
"leg_shape" => self.leg_shape = value.to_string(), "leg_shape" => self.leg_shape = value.to_string(),
"hair_color" => self.hair_color = value.to_string(), "hair_color" => self.hair_color = value.to_string(),
"clothing.top" => self.clothing.top = value.to_string(), "clothing.top" => self.clothing.top = value.to_string(),
"clothing.bottom" => self.clothing.bottom = value.to_string(), "clothing.bottom" => self.clothing.bottom = value.to_string(),
"clothing.shoes" => self.clothing.shoes = value.to_string(), "clothing.shoes" => self.clothing.shoes = value.to_string(),
_ => return Err(()), _ => return Err(()),
} }
+7 -6
View File
@@ -3,6 +3,7 @@ mod character;
mod config; mod config;
mod api; mod api;
mod traits; mod traits;
mod tokenise;
use std:: use std::
{ {
@@ -44,7 +45,7 @@ async fn main()
let (tx,rx) = mpsc::channel(); let (tx,rx) = mpsc::channel();
// Unzip zip archive to get data for story // Unzip zip archive to get data for story
let file_name = args().nth(1) // Get filename from arguments let file_name = args().nth(1) // Get filename from arguments
.unwrap_or_else .unwrap_or_else // TODO unwrap or exit
(|| { (|| {
error!("No filename specified"); error!("No filename specified");
exit(10); exit(10);
@@ -78,6 +79,7 @@ async fn main()
// Initialise the data strcut that will be sent out during API GET requests // Initialise the data strcut that will be sent out during API GET requests
let data_to_send = Arc::new(Mutex::new(api::DataToSend let data_to_send = Arc::new(Mutex::new(api::DataToSend
{ {
id: 0,
action_type: "begin".to_owned(), action_type: "begin".to_owned(),
content: String::new(), content: String::new(),
character: String::new(), character: String::new(),
@@ -96,7 +98,6 @@ async fn main()
api::api_process(data_clone1, characters_clone1, tx_clone).await; api::api_process(data_clone1, characters_clone1, tx_clone).await;
}); });
// setup the parsing stuff // // setup the parsing stuff //
// Read the file and split it into tokens // Read the file and split it into tokens
// file read from a zip file /story.ha // file read from a zip file /story.ha
@@ -113,16 +114,15 @@ async fn main()
error!("Unable to read story file to string: {err}"); error!("Unable to read story file to string: {err}");
exit(14); exit(14);
}); });
// Tokenise story file let space_seperated: Vec<&str> = file_contents
let tokens: Vec<&str> = file_contents
.split_whitespace() .split_whitespace()
.collect(); .collect();
if ! tokens.contains(&"END") if ! space_seperated.contains(&"END")
{ {
warn!("No END statement, story may exit unexpectedly"); warn!("No END statement, story may exit unexpectedly");
} }
let (tokens, labels,_) = tokenise::tokenise(&space_seperated, 0).unwrap();
debug!("{tokens:?}"); debug!("{tokens:?}");
let data_clone2 = Arc::clone(&data_to_send); let data_clone2 = Arc::clone(&data_to_send);
let characters_clone2 = Arc::clone(&characters); let characters_clone2 = Arc::clone(&characters);
// Run the parsing process for the DSL // Run the parsing process for the DSL
@@ -132,6 +132,7 @@ async fn main()
Ok(()) => Ok(()) =>
{ {
api::modify_data(&data_to_send, "end".to_string(), String::new(), String::new(), vec![]); api::modify_data(&data_to_send, "end".to_string(), String::new(), String::new(), vec![]);
// TODO fix quitting instantly
let _ = rx.recv(); // Wait for the client to respond let _ = rx.recv(); // Wait for the client to respond
info!("Program exited successfully"); info!("Program exited successfully");
exit(0); exit(0);
+34 -66
View File
@@ -4,6 +4,7 @@ use crate::
// Internal code // Internal code
character, character,
api, api,
tokenise,
// Libraries // Libraries
mpsc::Receiver, mpsc::Receiver,
@@ -14,16 +15,15 @@ use crate::
warn, warn,
}; };
mod strings;
mod character_parse; mod character_parse;
// Parse the tokens in a file // Parse the tokens in a file
// Returns success or an error string // Returns success or an error string
pub fn token_parse( pub fn token_parse(
tokens: &Vec<&str>, tokens: &Vec<tokenise::Token>,
characters: &Arc<Mutex<HashMap::<String, character::Character>>>, characters: &Arc<Mutex<HashMap::<String, character::Character>>>,
data_to_send: &Arc<Mutex<api::DataToSend>>, data_to_send: &Arc<Mutex<api::DataToSend>>,
rx: &Receiver<(bool,usize)>, rx: &Receiver<(bool,usize,String)>,
) -> Result<(),String> ) -> Result<(),String>
{ {
info!("DSL parsing begun"); info!("DSL parsing begun");
@@ -31,16 +31,14 @@ pub fn token_parse(
if rx.recv().is_err() if rx.recv().is_err()
{ {
warn!("Some issue with api"); warn!("Some issue with api");
// TOD eh? // TODO eh?
} }
info!("Client has connected"); info!("Client has connected");
// Run an infinite loop // Run an infinite loop
'parse_loop: loop 'parse_loop: loop
{ {
// Get the next token // Get the next token
let token = tokens let token = tokenise::get_string_token(tokens, index)?;
.get(index)
.ok_or_else(|| "File unexpectedly reached termination point".to_string())?;
debug!("{index}: {token}"); debug!("{index}: {token}");
// The instructions are related to characters // The instructions are related to characters
if token.starts_with('@') if token.starts_with('@')
@@ -70,39 +68,48 @@ pub fn token_parse(
}, },
"choice" => "choice" =>
{ {
let (_,jump_points) = match choice_parse(index+1, tokens, data_to_send) 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"
{ {
Ok((increment,jump_point)) => (increment,jump_point), index += 1;
Err(error) => return Err(error), 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"); } if rx.recv().is_err() { warn!("Error sending choices to client"); }
let (_, choice) = match rx.recv() let choice = match rx.recv()
{ {
Ok((_,choice)) => (None::<bool>, choice), Ok((_,choice,_)) => choice_indeces[choice],
Err(err) => Err(err) =>
{ {
warn!("Error receiving choice from client, defaulting to choice 0 {err}"); warn!("Error receiving choice from client, defaulting to choice 0 {err}");
(None::<bool>, 0) 0
} }
}; };
index = jump_points[choice]; let object = tokenise::get_object_token(tokens, choice)?; // TODO make more efficient
info!("CHOICE command with {} choices",jump_points.len()); debug!("{object:?}");
debug!("{jump_points:?} {choice} {index}"); // Don't propogate exit error
let _ = token_parse(object, characters, data_to_send, rx);
continue 'parse_loop continue 'parse_loop
}, },
"or" => "or" =>
{ {
info!("OR command, jumping over"); info!("OR command, jumping over");
index += match strings::closing_char(&tokens[index..], '{','}') index += 2;
{
Some(index) => index,
None => return Err(String::from("No closing brace")),
};
continue
},
"}" =>
{
index += 1;
continue continue
}, },
_ => _ =>
@@ -117,42 +124,3 @@ pub fn token_parse(
} }
} }
} }
// Parse the options in a choice clause and returns the idexes of the code blocks
fn choice_parse
(
index: usize,
tokens: &[&str],
data_to_send: &Arc<Mutex<api::DataToSend>>,
) -> Result<(usize, Vec<usize>), String>
{
let mut sum_index: usize = index;
let mut choices: Vec<String> = Vec::new();
let mut choice_indeces: Vec<usize> = Vec::new();
// Ensure the index is valid (the index is not beyond the vector)
// Get the initial choice
let (choice_string, counter) = strings::extract_quoted(&tokens[sum_index..])
.ok_or_else(|| "No choice string".to_string())?;
sum_index += counter;
choices.push(choice_string);
choice_indeces.push(sum_index+1);
sum_index += strings::closing_char(&tokens[sum_index..], '{','}')
.ok_or_else(|| "No closing brace".to_string())? + 1;
// Find all the alternate choices labelled with OR
// Fill out the choices vector with all the choice strings
while tokens[sum_index].to_lowercase() == "or"
{
let (choice_string, counter) = strings::extract_quoted(&tokens[sum_index+1..])
.ok_or_else(|| "No choice string".to_string())?;
sum_index += counter;
choices.push(choice_string);
choice_indeces.push(sum_index+2);
sum_index += strings::closing_char(&tokens[sum_index..], '{','}')
.ok_or_else(|| "No closing brace".to_string())? + 1;
}
debug!("{choices:?}");
// Send the choices to the Client via the API
api::modify_data(data_to_send, "choice".to_string(), String::new(), String::new(), choices);
// Return the choice indeces
Ok((sum_index + 1, choice_indeces))
}
+20 -28
View File
@@ -1,9 +1,9 @@
use super::strings;
use crate:: use crate::
{ {
// Internal code // Internal code
character, character,
api, api,
tokenise,
UnwrapOrExit, UnwrapOrExit,
//Libs //Libs
Mutex, Mutex,
@@ -20,7 +20,7 @@ use crate::
pub fn character_parse pub fn character_parse
( (
index: usize, index: usize,
tokens: &Vec<&str>, tokens: &Vec<tokenise::Token>,
character_name: String, character_name: String,
characters: &Arc<Mutex<HashMap::<String, character::Character>>>, characters: &Arc<Mutex<HashMap::<String, character::Character>>>,
data_to_send: &Arc<Mutex<api::DataToSend>>, data_to_send: &Arc<Mutex<api::DataToSend>>,
@@ -28,9 +28,11 @@ pub fn character_parse
{ {
let mut sum_index: usize = index; let mut sum_index: usize = index;
// Ensure the index is valid (the index is not beyond the vector) // Ensure the index is valid (the index is not beyond the vector)
let token = tokens let token = match tokens.get(sum_index) {
.get(sum_index) Some(tokenise::Token::String(s)) => s.clone(),
.ok_or_else(|| ("File unexpectedly reached termination point".to_string(), sum_index))?; 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() match token.to_lowercase().as_str()
{ {
// The character is saying something, so grab the text and pass it // The character is saying something, so grab the text and pass it
@@ -38,35 +40,26 @@ pub fn character_parse
"says" => "says" =>
{ {
info!("SAYS command with character {character_name}"); info!("SAYS command with character {character_name}");
match strings::extract_quoted(&tokens[sum_index+1..]) sum_index += 1;
{ let output = tokenise::get_string_token(tokens, sum_index)
Some((output_string, counter)) => .map_err(|err| (err, index))?;
{ debug!("Saying {output}");
debug!("{output_string}"); api::modify_data(data_to_send, "output".to_string(), output.to_string(), character_name, vec![]);
sum_index += counter;
api::modify_data(data_to_send, "output".to_string(), output_string, character_name, vec![]);
},
None => return Err(("Unable to read output string".to_string(), sum_index)),
}
}, },
// Change the property of the selected character eg @tim CHANGE name "Bill Buffins" // Change the property of the selected character eg @tim CHANGE name "Bill Buffins"
// will change the character with ID tim to "Bill Buffins"; a character's ID cannot change // will change the character with ID tim to "Bill Buffins"; a character's ID cannot change
"change" => "change" =>
{ {
sum_index += 1; sum_index += 1;
let feature = tokens let feature = tokenise::get_string_token(tokens, sum_index)
.get(sum_index) .map_err(|err| (err, index))?;
.ok_or_else(|| ("File unexpectedly reached termination point".to_string(), sum_index))?; sum_index += 1;
let output_string: String; let string = tokenise::get_string_token(tokens, sum_index)
(output_string, sum_index) = match strings::extract_quoted(&tokens[sum_index+1..]) .map_err(|err| (err, index))?;
{
Some((string,counter)) => (string,sum_index+counter),
None => return Err(("Unable to parse property to change character".to_string(),sum_index)),
};
info!("CHANGE command with character {character_name} feature {feature}"); info!("CHANGE command with character {character_name} feature {feature}");
let mut characters = characters.lock().unwrap_or_exit("Character Mutex was poisoned",3); let mut characters = characters.lock().unwrap_or_exit("Character Mutex was poisoned",3);
if let Some(character) = characters.get_mut(&character_name) if let Some(character) = characters.get_mut(&character_name)
&& character.set_field(feature, &output_string) && character.set_field(&feature, &string)
.is_err() { warn!("Feature {feature} does not exist") } .is_err() { warn!("Feature {feature} does not exist") }
drop(characters); drop(characters);
api::modify_data(data_to_send, "change".to_string(), String::new(), character_name, vec![]); api::modify_data(data_to_send, "change".to_string(), String::new(), character_name, vec![]);
@@ -76,9 +69,8 @@ pub fn character_parse
"to"|"animate" => "to"|"animate" =>
{ {
sum_index += 1; sum_index += 1;
let content = tokens let content = tokenise::get_string_token(tokens, sum_index)
.get(sum_index) .map_err(|err| (err, index))?;
.ok_or_else(|| ("File unexpectedly reached termination point".to_string(), sum_index))?;
api::modify_data(data_to_send, token.to_lowercase(), content.to_string(), character_name, vec![]); api::modify_data(data_to_send, token.to_lowercase(), content.to_string(), character_name, vec![]);
}, },
// Catch all condition, if the instruction is unrecognised as a // Catch all condition, if the instruction is unrecognised as a
-42
View File
@@ -1,42 +0,0 @@
pub fn closing_char(parts: &[&str], open: char, close: char)
-> Option<usize>
{
let mut indentation: usize = 0;
let mut flag = false; // flag to mark you've passed open
for (index, part) in parts.iter().enumerate()
{
if part.contains(open)
{
indentation += 1;
flag = true;
}
if part.contains(close) { indentation -= 1; }
if indentation == 0 && flag { return Some(index); }
}
None
}
pub fn extract_quoted(parts: &[&str])
-> Option<(String, usize)>
{
let mut vec_string = Vec::new();
let mut counter: usize = 0;
for part in parts
{
counter += 1;
vec_string.push(*part);
// End of the string
if part.ends_with('\"') // TODO allow for backslashes and '
{
let final_string: String = vec_string.join(" ");
let final_string: String = final_string
.chars()
.skip(1)
.take(
final_string.chars()
.count() - 2)
.collect();
return Some((final_string, counter));
}
}
None
}
+97
View File
@@ -0,0 +1,97 @@
use crate::
{
exit,
};
#[derive(Debug, Clone)]
pub enum Token
{
Null,
String(String),
Object(Vec<Token>),
}
pub fn get_string_token(tokens: &Vec<Token>, index: usize)
-> Result<String, String>
{
match tokens.get(index) {
Some(Token::String(s)) => return Ok(s.clone()),
Some(_) => return Err("Unexpected token".to_string()),
None => return Err("File unexpectedly reached termination point".to_string()),
}
}
pub fn get_object_token(tokens: &Vec<Token>, index: usize)
-> Result<&Vec<Token>, String>
{
match tokens.get(index) {
Some(Token::Object(v)) => return Ok(v),
Some(_) => return Err("Unexpected token".to_string()),
None => return Err("File unexpectedly reached termination point".to_string()),
}
}
// Tokenise the story to Vec<Token>
// 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>
{
let mut tokenised_data: Vec<Token> = Vec::new();
let mut labels: Vec<(String,usize)> = Vec::new();
while index < space_seperated.len()
{
let mut item = space_seperated[index].to_string();
let mut object: Vec<Token>;
if item.starts_with("\"")
{
(index, item) = match tokenise_string(&space_seperated, index)
{
Some((index,item)) => (index, item),
None => exit(1),
};
tokenised_data.push(Token::String(item));
}
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));
}
else if item == "}"
{
return Ok((tokenised_data,labels,index));
}
else { tokenised_data.push(Token::String(item)); }
index += 1;
}
return Ok((tokenised_data, labels, 0));
}
fn tokenise_string(space_seperated: &Vec<&str>, mut index: usize)
-> Option<(usize, String)>
{
let mut string = String::new();
let mut chars = space_seperated[index].chars();
chars.next();
string += chars.as_str();
for item in &space_seperated[index+1..]
{
if item.ends_with("\"")
{
let mut chars = item.chars();
chars.next_back();
string += format!(" {}", chars.as_str()).as_str();
break
}
else
{
string += format!(" {}", item).as_str();
}
index += 1;
}
return Some((index+1,string))
}
+13
View File
@@ -4,8 +4,21 @@
choice "choice numero uno" { choice "choice numero uno" {
@tim animate wave @tim animate wave
@tim says "super sad" @tim says "super sad"
choice "choice numero uno" {
@tim animate wave
@tim says "super sad"
}
or "choice numero duo" {
@tim says "super unsad"
}
or "choice numero tres" {
@tim says "hola mi amigos"
}
} }
or "choice numero duo" { or "choice numero duo" {
@tim says "super unsad" @tim says "super unsad"
} }
or "choice numero tres" {
@tim says "hola mi amigos"
}
END END
BIN
View File
Binary file not shown.