added support for instring variables and made character IDs

be always stored in lowercase
This commit is contained in:
2026-05-26 21:41:36 +01:00
parent cc3eca857d
commit 148fb73f7f
12 changed files with 69 additions and 70 deletions
+1
View File
@@ -461,6 +461,7 @@ version = "0.0.3"
dependencies = [ dependencies = [
"env_logger", "env_logger",
"log", "log",
"regex",
"serde", "serde",
"serde-patch", "serde-patch",
"serde_json", "serde_json",
+1
View File
@@ -25,6 +25,7 @@ unwrap_used = "warn"
[dependencies] [dependencies]
env_logger = "0.11.10" env_logger = "0.11.10"
log = "0.4.29" log = "0.4.29"
regex = "1.12.3"
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
serde-patch = "0.2.3" serde-patch = "0.2.3"
serde_json = "1.0.149" serde_json = "1.0.149"
+5 -2
View File
@@ -71,8 +71,11 @@ pub fn character_parse(archive: &mut ZipArchive<File>)
// Serialise this to a HashMap // Serialise this to a HashMap
let characters: HashMap<String, Character> = let characters: HashMap<String, Character> =
serde_json::from_str(&file_contents) serde_json::from_str::<HashMap<String,Character>>(&file_contents)
.map_err (|err| format!("Invalid JSON in characters.json: {err}"))?; .map_err (|err| format!("Invalid JSON in characters.json: {err}"))?
.into_iter()
.map(|(k,v)| (k.to_lowercase(), v))
.collect();
info!("Parsed characters from characters.json"); info!("Parsed characters from characters.json");
debug!("{characters:?}"); debug!("{characters:?}");
Ok(Arc::new(Mutex::new(characters))) Ok(Arc::new(Mutex::new(characters)))
+1
View File
@@ -40,6 +40,7 @@ use crate::
{ {
traits::UnwrapOrExit, traits::UnwrapOrExit,
}; };
use regex::Regex;
#[tokio::main] #[tokio::main]
async fn main() async fn main()
+3
View File
@@ -10,6 +10,7 @@ use crate::
VecDeque, VecDeque,
warn, warn,
debug, debug,
info,
mpsc::Receiver, mpsc::Receiver,
}; };
use super::keyword_parse; use super::keyword_parse;
@@ -62,6 +63,7 @@ pub fn identifier_parse
"input" => "input" =>
{ {
api::modify_data(happening_queue, "input".to_string(), String::new(), String::new(), Vec::new()); api::modify_data(happening_queue, "input".to_string(), String::new(), String::new(), Vec::new());
info!("Waiting for client input");
let input = match rx.recv() let input = match rx.recv()
{ {
Ok((_,input)) => input, Ok((_,input)) => input,
@@ -72,6 +74,7 @@ pub fn identifier_parse
} }
}; };
variables.insert(identifier.to_owned(), tokenise::Value::String(input)); variables.insert(identifier.to_owned(), tokenise::Value::String(input));
sum_index += 1;
}, },
_ => _ =>
{ {
+11 -2
View File
@@ -89,6 +89,7 @@ pub fn keyword_parse(
// Jump to a particular index based on a label eg GOTO character_check // Jump to a particular index based on a label eg GOTO character_check
"goto" => "goto" =>
{ {
info!("GOTO command, jumping there");
index += 1; index += 1;
let label = tokenise::get_keyword_token(tokens, index)?; let label = tokenise::get_keyword_token(tokens, index)?;
index = if let Some(label_index) = labels.get(&label) { *label_index } index = if let Some(label_index) = labels.get(&label) { *label_index }
@@ -98,10 +99,17 @@ pub fn keyword_parse(
index + 1 index + 1
}; };
debug!("Jumping to {index}"); debug!("Jumping to {index}");
} },
"pan" =>
{
info!("PAN command, informing client");
index += 1;
let location = tokenise::get_keyword_token(tokens, index)?;
api::modify_data(happening_queue, "pan".to_string(), location, String::new(), Vec::new());
},
_ => _ =>
{ {
warn!("Invalid command: {token}"); warn!("Invalid command: {token}, index {index}");
index += 1; index += 1;
} }
} }
@@ -142,6 +150,7 @@ pub fn choice_parse
} }
}; };
api::modify_data(happening_queue, "choice".to_string(), String::new(), String::new(), choices.clone()); api::modify_data(happening_queue, "choice".to_string(), String::new(), String::new(), choices.clone());
info!("Waiting for client choice");
debug!("{choice_indeces:?}"); debug!("{choice_indeces:?}");
let choice = match rx.recv() let choice = match rx.recv()
{ {
+40 -3
View File
@@ -1,6 +1,7 @@
use crate:: use crate::
{ {
HashMap, HashMap,
Regex,
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -49,6 +50,19 @@ pub enum Comparison
LessOrEqual, LessOrEqual,
} }
impl Value
{
pub fn as_string(&self) -> String
{
match self
{
Value::String(s) => s.clone(),
Value::Integer(i) => i.to_string(),
Value::Bool(b) => b.to_string(),
Value::Null => "".to_string(),
}
}
}
// TODO error reporting for all of these // TODO error reporting for all of these
pub fn get_operator_token(tokens: &[Token], index: usize) pub fn get_operator_token(tokens: &[Token], index: usize)
@@ -105,7 +119,11 @@ pub fn get_string_token(tokens: &[Token], index: usize, variables: &HashMap<Stri
{ {
match val match val
{ {
Value::String(s) => Ok(s.clone()), Value::String(s) =>
{
let string = insert_variables_into_string(s, variables);
Ok(string)
},
_ => Err("Unexpected value type".to_string()), _ => Err("Unexpected value type".to_string()),
} }
}, },
@@ -116,7 +134,11 @@ pub fn get_string_token(tokens: &[Token], index: usize, variables: &HashMap<Stri
.ok_or(format!("Variable {iden} not initialised"))?; .ok_or(format!("Variable {iden} not initialised"))?;
match val match val
{ {
Value::String(s) => Ok(s.clone()), Value::String(s) =>
{
let string = insert_variables_into_string(s, variables);
Ok(string)
},
_ => Err("Unexpected value type".to_string()), _ => Err("Unexpected value type".to_string()),
} }
}, },
@@ -125,6 +147,19 @@ pub fn get_string_token(tokens: &[Token], index: usize, variables: &HashMap<Stri
} }
} }
fn insert_variables_into_string(string: &str, variables: &HashMap<String, Value>)
-> String
{
let re = Regex::new(r"\$([A-Za-z0-9-_]*)").unwrap();
re.replace_all(string, |caps: &regex::Captures| {
let key = &caps[1].to_lowercase();
variables.get(key)
.map(|v| v.as_string())
.unwrap_or_else(|| caps[0].to_string()) // leave unchanged if missing
})
.to_string()
}
// Tokenise the story to Vec<Token> // Tokenise the story to Vec<Token>
// It can contain sub-objects (using recursive calls) // It can contain sub-objects (using recursive calls)
// TODO check for END // TODO check for END
@@ -165,7 +200,7 @@ pub fn tokenise(file_contents: &str)
{ {
let mut chars = item.chars(); let mut chars = item.chars();
chars.next(); chars.next();
tokenised_data.push(Token::Character(chars.as_str().to_string())); tokenised_data.push(Token::Character(chars.as_str().to_lowercase().to_string())); // Force character to be lowecase
} }
// Strings // Strings
else if item.starts_with('"') // TODO support ' else if item.starts_with('"') // TODO support '
@@ -227,6 +262,8 @@ pub fn tokenise(file_contents: &str)
Ok((tokenised_data, labels)) Ok((tokenised_data, labels))
} }
// TODO support strings that contain " in them by using a backslash
fn tokenise_string(space_seperated: &[&str], mut index: usize) fn tokenise_string(space_seperated: &[&str], mut index: usize)
-> Option<(usize, String)> -> Option<(usize, String)>
{ {
-4
View File
@@ -1,4 +0,0 @@
{
"title": "Once upon a Test",
"description": "This story is for testing purposes"
}
-27
View File
@@ -1,27 +0,0 @@
{
"tim": {
"name": "Timothy Sharpshooter",
"gender": "Male",
"skin_color": [0,0,0],
"eye_color": [0,0,0],
"hair_color": [0,0,0],
"pronoun_subject": "He",
"pronoun_object": "Him",
"pronoun_deppos": "His",
"pronoun_indpos": "His",
"pronoun_reflex": "Himself",
"head_shape": "normal-male",
"hair_style": "",
"torso_shape": "",
"arm_shape": "",
"leg_shape": "",
"clothing": {
"top": "",
"bottom": "",
"shoes": "",
"hat": "",
"gloves": "",
"neck": ""
}
}
}
-27
View File
@@ -1,27 +0,0 @@
$name = input
@tim says "hello"
@tim says $name
$x = 3
$x + 1
if $x == 4 {
@tim says "5"
}
elif $x < 5 {
@tim says "<5"
}
else {
@tim says "whar"
}
label:
$choice_string = choice "choice numero uno" {
@tim says "super sad"
}
or "choice numero duo" {
@tim says "super unsad"
}
or "choice numero tres" {
@tim says "hola mi amigos"
goto label
}
END
BIN
View File
Binary file not shown.
+7 -5
View File
@@ -2,11 +2,11 @@ import requests
import os import os
import time import time
import sys import sys
debug = False debug = True
try: try:
if sys.argv[1] == "debug": debug = True if sys.argv[1] == "silent": debug = False
except: except:
debug = False debug = True
# Loop and get new api # Loop and get new api
def main(): def main():
@@ -22,7 +22,9 @@ def main():
output(character, response["content"]) output(character, response["content"])
case "choice": case "choice":
user_choice = choice(response["choices"]) user_choice = choice(response["choices"])
time.sleep(0.5) continue
case "input":
get_input()
continue continue
case "end": case "end":
print("Exitting successfully") print("Exitting successfully")
@@ -46,7 +48,7 @@ def choice(choices):
def get_input(): def get_input():
api_url = "http://localhost:20264/input" api_url = "http://localhost:20264/input"
input_string = input() input_string = input("Input: ")
requests.post(api_url, json=input_string); requests.post(api_url, json=input_string);