From c1af3290d53fda15e6d690073ef6b9086be4134d Mon Sep 17 00:00:00 2001 From: Chris Mounce Date: Sat, 8 Jun 2024 19:50:57 -0700 Subject: [PATCH] Implement a working %include macro --- src/encoding.rs | 4 ++-- src/main.rs | 31 +++++++++++++++++++------------ src/preprocess/eval.rs | 19 ++++++++++++++++--- src/world.rs | 5 ----- 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/encoding.rs b/src/encoding.rs index d25da16..4b6c657 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, Result}; use codepage_437::CP437_WINGDINGS; // Serialize multi-line content with CR-terminated lines -fn encode_multiline(input: &str) -> Result> { +pub fn encode_multiline(input: &str) -> Result> { input .chars() .map(|c| { @@ -17,7 +17,7 @@ fn encode_multiline(input: &str) -> Result> { .collect() } -fn decode_multiline(input: &[u8]) -> String { +pub fn decode_multiline(input: &[u8]) -> String { input .iter() .map(|&x| { diff --git a/src/main.rs b/src/main.rs index 8f8061a..e2ac414 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,11 +2,12 @@ mod encoding; mod preprocess; mod world; -use std::{env, error::Error, fs, process::exit}; +use anyhow::anyhow; +use encoding::{decode_multiline, encode_multiline}; +use preprocess::eval::Context; +use std::{env, error::Error, fs, path::PathBuf, process::exit}; use world::World; -use crate::preprocess::scan::scan; - fn to_latin1(bytes: &[u8]) -> String { bytes.iter().map(|&x| x as char).collect() } @@ -18,16 +19,20 @@ fn main() -> Result<(), Box> { exit(1); } - let bytes = fs::read(&args[1])?; + let world_filename = &args[1]; + let bytes = fs::read(world_filename)?; let mut world = World::from_bytes(&bytes)?; - // Print some of the data parsed. - // TODO: Implement some macros, or other code modification - // Then, we can try writing a copy to disk. + // Prepare to evaluate macros from the world file's directory + let world_pathbuf = PathBuf::from(&world_filename); + let world_dir = world_pathbuf.parent().ok_or(anyhow!("Couldn't get world's directory"))?; + let eval_context = Context::new(&world_dir); + println!("num boards: {}", &world.boards.len()); - for board in &world.boards { + for board in &mut world.boards { println!("board: {}", to_latin1(&board.name)); - for stat in &board.stats { + for stat in &mut board.stats { + // Print some of the data parsed. let (x, y) = (stat.x as usize, stat.y as usize); println!( " stat at ({}, {}): {:?}, {:?}", @@ -36,13 +41,15 @@ fn main() -> Result<(), Box> { board.terrain[(x - 1) + (y - 1) * 60], to_latin1(&stat.code) ); - scan("todo: convert [u8] to strings and scan here"); + + // Evaluate macros + let old_code = decode_multiline(&stat.code); + let new_code = eval_context.eval_program(&old_code)?; + stat.code = encode_multiline(&new_code)?; } } // Try to write a modified world file - world.boards.reverse(); - world.starting_board = (world.boards.len() as i16 - 1) - world.starting_board; fs::write("tmp.zzt", world.to_bytes()?)?; Ok(()) diff --git a/src/preprocess/eval.rs b/src/preprocess/eval.rs index 244008e..7eef7cb 100644 --- a/src/preprocess/eval.rs +++ b/src/preprocess/eval.rs @@ -1,4 +1,4 @@ -use std::{fs, path::Path}; +use std::{fs, path::{Path, PathBuf}}; use anyhow::{anyhow, bail, Result}; @@ -15,14 +15,19 @@ trait FileLoaderTrait { fn load(&self, path: &Path) -> Result; } -struct FileLoader; +struct FileLoader { + working_dir: PathBuf, +} struct MockFileLoader { content: String, } impl FileLoaderTrait for FileLoader { fn load(&self, path: &Path) -> Result { - fs::read_to_string(path).map_err(|e| anyhow!("Couldn't load file: {}", e)) + // let mut full_path = self.working_dir.clone(); + // full_path.extend(path); + let full_path = self.working_dir.join(path); + fs::read_to_string(full_path).map_err(|e| anyhow!("Couldn't load {:?}: {}", path, e)) } } @@ -33,6 +38,14 @@ impl FileLoaderTrait for MockFileLoader { } impl Context { + pub fn new(working_directory: &Path) -> Self { + Context { + file_loader: Box::new(FileLoader { + working_dir: working_directory.into() + }), + } + } + pub fn eval_program(&self, input: &str) -> Result { let tokens = scan(input).0; let exprs = parse(tokens)?; diff --git a/src/world.rs b/src/world.rs index e5bb110..c9d63f9 100644 --- a/src/world.rs +++ b/src/world.rs @@ -394,7 +394,6 @@ fn board_slice(bytes: &[u8]) -> IResult<&[u8], &[u8], LoadError> { trait SerializationHelpers { fn push_bool(&mut self, value: bool); fn push_i16(&mut self, value: i16); - fn push_u16(&mut self, value: u16); fn push_string(&mut self, cap: u8, value: &[u8]) -> Result<(), &'static str>; fn push_padding(&mut self, size: usize); } @@ -408,10 +407,6 @@ impl SerializationHelpers for Vec { self.extend(value.to_le_bytes()); } - fn push_u16(&mut self, value: u16) { - self.extend(value.to_le_bytes()); - } - fn push_string(&mut self, cap: u8, value: &[u8]) -> Result<(), &'static str> { if value.len() > cap as usize { return Err("string too long");