yarnspinner_compiler/
compiler.rs

1//! Adapted from <https://github.com/YarnSpinnerTool/YarnSpinner/blob/da39c7195107d8211f21c263e4084f773b84eaff/YarnSpinner.Compiler/Compiler.cs>
2//! and <https://github.com/YarnSpinnerTool/YarnSpinner/blob/da39c7195107d8211f21c263e4084f773b84eaff/YarnSpinner.Compiler/CompilationJob.cs>
3
4use crate::prelude::*;
5use std::path::Path;
6use yarnspinner_core::prelude::*;
7
8mod add_tags_to_lines;
9pub(crate) mod antlr_rust_ext;
10pub(crate) mod run_compilation;
11pub(crate) mod utils;
12
13#[allow(missing_docs)]
14pub type Result<T> = std::result::Result<T, CompilerError>;
15
16/// An object that contains Yarn source code to compile, and instructions on
17/// how to compile it.
18///
19/// Consume this information using [`Compiler::compile`] to produce a [`Compilation`] result.
20///
21/// ## Implementation note
22///
23/// This type is a combination of the original `CompilationStep` and `Compiler` types, optimized for easier, fluent calling.
24#[derive(Debug, Clone, PartialEq, Default)]
25#[cfg_attr(feature = "bevy", derive(Reflect))]
26#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
27#[cfg_attr(feature = "bevy", reflect(Debug, PartialEq))]
28#[cfg_attr(
29    all(feature = "bevy", feature = "serde"),
30    reflect(Serialize, Deserialize)
31)]
32pub struct Compiler {
33    /// The [`File`] structs that represent the content to parse..
34    pub files: Vec<File>,
35
36    /// The [`Library`] that contains declarations for functions.
37    #[cfg_attr(feature = "bevy", reflect(ignore))]
38    #[cfg_attr(feature = "serde", serde(skip))]
39    pub library: Library,
40
41    /// The types of compilation that the compiler will do.
42    pub compilation_type: CompilationType,
43
44    /// The declarations for variables.
45    pub variable_declarations: Vec<Declaration>,
46}
47
48impl Compiler {
49    /// Creates a new [`Compiler`] with the default settings and no files added yet.
50    pub fn new() -> Self {
51        Self::default()
52    }
53
54    /// Adds a file to the compilation.
55    pub fn add_file(&mut self, file: File) -> &mut Self {
56        self.files.push(file);
57        self
58    }
59
60    /// Adds multiple files to the compilation.
61    pub fn add_files(&mut self, files: impl IntoIterator<Item = File>) -> &mut Self {
62        self.files.extend(files);
63        self
64    }
65
66    /// Adds a file to the compilation by reading it from disk. Fallible version of [`Compiler::read_file`].
67    pub fn try_read_file(&mut self, file_path: impl AsRef<Path>) -> std::io::Result<&mut Self> {
68        let file_name = file_path.as_ref().to_string_lossy().to_string();
69        let file_content = std::fs::read_to_string(file_path)?;
70        self.files.push(File {
71            file_name,
72            source: file_content,
73        });
74        Ok(self)
75    }
76
77    /// Adds a file to the compilation by reading it from disk. For the fallible version, see [`Compiler::try_read_file`].
78    pub fn read_file(&mut self, file_path: impl AsRef<Path>) -> &mut Self {
79        self.try_read_file(file_path).unwrap()
80    }
81
82    /// Extends the Yarn function library with the given [`Library`]. The standard library is only added if this is called with [`Library::standard_library`].
83    pub fn extend_library(&mut self, library: Library) -> &mut Self {
84        self.library.extend(library);
85        self
86    }
87
88    /// Sets the compilation type, which allows premature stopping of the compilation process. By default, this is [`CompilationType::FullCompilation`].
89    pub fn with_compilation_type(&mut self, compilation_type: CompilationType) -> &mut Self {
90        self.compilation_type = compilation_type;
91        self
92    }
93
94    /// Adds a variable declaration to the compilation.
95    pub fn declare_variable(&mut self, declaration: Declaration) -> &mut Self {
96        self.variable_declarations.push(declaration);
97        self
98    }
99
100    /// Compiles the Yarn files previously added into a [`Compilation`].
101    pub fn compile(&self) -> Result<Compilation> {
102        run_compilation::compile(self)
103    }
104}
105
106/// Represents the contents of a file to compile.
107#[derive(Debug, Clone, Eq, PartialEq, Hash)]
108#[cfg_attr(feature = "bevy", derive(Reflect))]
109#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
110#[cfg_attr(feature = "bevy", reflect(Debug, PartialEq, Hash))]
111#[cfg_attr(
112    all(feature = "bevy", feature = "serde"),
113    reflect(Serialize, Deserialize)
114)]
115pub struct File {
116    /// The name of the file.
117    ///
118    /// This may be a full path, or just the filename or anything in
119    /// between. This is useful for diagnostics, and for attributing
120    /// dialogue lines to their original source files.
121    pub file_name: String,
122
123    /// The source code of this file.
124    pub source: String,
125}
126
127/// The types of compilation that the compiler will do.
128#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
129#[cfg_attr(feature = "bevy", derive(Reflect))]
130#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
131#[cfg_attr(feature = "bevy", reflect(Debug, PartialEq, Hash, Default))]
132#[cfg_attr(
133    all(feature = "bevy", feature = "serde"),
134    reflect(Serialize, Deserialize)
135)]
136pub enum CompilationType {
137    /// The compiler will do a full compilation, and generate a [`Program`],
138    /// function declaration set, and string table.
139    #[default]
140    FullCompilation,
141
142    /// The compiler will derive only the variable and function declarations,
143    /// and file tags, found in the script.
144    DeclarationsOnly,
145
146    /// The compiler will generate a string table only.
147    StringsOnly,
148}
149
150#[cfg(test)]
151mod test {
152    use super::*;
153
154    #[test]
155    fn can_call_compile_empty_without_crash() {
156        Compiler::new().compile().unwrap();
157    }
158
159    #[test]
160    fn can_call_compile_file_without_crash() {
161        let file = File {
162            file_name: "test.yarn".to_string(),
163            source: "title: test
164---
165foo
166bar
167a {1 + 3} cool expression
168==="
169            .to_string(),
170        };
171        Compiler::new().add_file(file).compile().unwrap();
172    }
173}