1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
//! # PDX Syntax
//!
//! This is a crate that defines the syntax of Paradox game files. It can be used to parse
//! the plain text files into an AST it defines. The main purpose of this crate is to provide
//! a convenient way to parse the files, which can be used for further tools, particularly,
//! tools for modders.
//!
//! ## Usage
//!
//! ```rust
//! /// Parse a given paradox script file.
//! use pdx_syntax::script::parse_file;
//! fn main() {
//! let mut args = std::env::args();
//! args.next();
//! let path = args.next().expect("Missing argument: path");
//! let ast = parse_file(&path).unwrap();
//! // Do something with the AST.
//! ast.push(Unit::SingleValue(Value::Primitive(Entry::Ident(
//! "TEST".to_string(),
//! ))));
//! // Dump to file in origin syntax.
//! println!("{}", ast);
//! }
//! ```
//!
//! ## Supported Syntax
//!
//! - Script: Paradox script. You can find the files in paths like `${game_root_dir}/game/common`.
//! Most of the files are in this format. See [`crate::script`] for details.
//! - Localization: Paradox localization file. You can find the files in `${game_root_dir}/game/localization`.
//! See [`crate::localization`] for details.
//!
//! ## Serialization
//!
//! The AST defined in this crate can be serialized via [`serde`]. You can use this feature
//! to convert the AST into other formats, like JSON, and use the parsed results in other
//! languages, like Python.
//!
pub mod result;
pub mod script;
pub mod localization;
/// File parser template. Submodes that defines a syntax may use this to add a parser fn.
#[macro_export]
macro_rules! fn_parse_file {
($unit:ty, $parser:ty) => {
/// Parse a given paradox file.
///
/// # See
/// This function is generated by macros. See [`crate::fn_parse_file`].
pub fn parse_file(path: &str) -> super::result::Result<$unit> {
use strip_bom::*;
let raw = std::fs::read_to_string(path)?;
let content = raw.strip_bom();
let unit = <$parser>::new()
.parse(&content)
// LALRPOP err cannot be directly returned for it borrows the content.
.or_else(|e| match e {
lalrpop_util::ParseError::InvalidToken { location }
| lalrpop_util::ParseError::UnrecognizedToken {
token: (location, _, _),
..
} => {
let win_left = 0.max(location as isize - 5) as usize;
let win_right = content.len().min(location + 5);
Err(super::result::Error::ParseError(format!(
"Invalid token \'{}\' at {}: ...{}...",
&content[location..location + 1],
location,
&content[win_left..win_right],
)))
}
_ => Err(super::result::Error::ParseError(format!("{}", e))),
})?;
Ok(unit)
}
};
}