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}