pdx_syntax/
lib.rs

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