yarnspinner_compiler/output/
declaration.rs

1//! Adapted from <https://github.com/YarnSpinnerTool/YarnSpinner/blob/da39c7195107d8211f21c263e4084f773b84eaff/YarnSpinner.Compiler/Declaration.cs>
2//!
3//! ## Implementation notes
4//!
5//! [`Range`] has been replaced with the more idiomatic [`Range<Position>`].
6
7use crate::prelude::*;
8use antlr_rust::rule_context::CustomRuleContext;
9use antlr_rust::token::Token;
10use antlr_rust::token_factory::TokenFactory;
11use std::fmt::{Debug, Display};
12use std::ops::Range;
13use yarnspinner_core::prelude::*;
14use yarnspinner_core::types::Type;
15
16/// Information about a declaration. Stored inside a declaration table,
17/// which is produced from the Compiler.
18///
19/// You do not create instances of this class yourself. They are
20/// generated by the [`Compiler`].
21#[derive(Debug, Clone, PartialEq)]
22#[cfg_attr(feature = "bevy", derive(Reflect))]
23#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
24#[cfg_attr(feature = "bevy", reflect(Debug, PartialEq))]
25#[cfg_attr(
26    all(feature = "bevy", feature = "serde"),
27    reflect(Serialize, Deserialize)
28)]
29pub struct Declaration {
30    /// The name of this declaration.
31    pub name: String,
32
33    /// The default value of this declaration, if no value has been
34    /// specified in code or is available from a Dialogue's variable storage.
35    pub default_value: Option<YarnValue>,
36
37    /// A string describing the purpose of this declaration.
38    pub description: Option<String>,
39
40    /// The name of the file in which this declaration was found.
41    ///
42    /// If this declaration was not found in a Yarn source file, this
43    /// will be [`DeclarationSource::External`].
44    pub source_file_name: DeclarationSource,
45
46    /// The name of the node in which this declaration was found.
47    ///
48    /// If this declaration was not found in a Yarn source file, this
49    /// will be [`None`].
50    pub source_node_name: Option<String>,
51
52    /// A value indicating whether this declaration was implicitly
53    /// inferred from usage.
54    ///
55    /// If `true`, this declaration was implicitly inferred from usage.
56    /// If `false`, this declaration appears in the source code.
57    pub is_implicit: bool,
58
59    /// The type of the variable, as represented by an object found
60    /// in a variant of [`Type`].
61    pub r#type: Type,
62
63    /// The range of text at which this declaration occurs.
64    ///
65    /// This range refers to the declaration of the symbol itself, and
66    /// not any syntax surrounding it. For example, the declaration
67    /// `<<declare $x = 1>>` would have a range referring to the `$x`
68    /// symbol.
69    pub range: Option<Range<Position>>,
70}
71
72impl Declaration {
73    /// Gets the line number at which this Declaration was found in the
74    /// source file.
75    ///
76    /// If this [`Declaration`] was not found in a Yarn source file,
77    /// this will be [`None`].
78    pub fn source_file_line(&self) -> Option<usize> {
79        self.range.as_ref()?.start.line.into()
80    }
81
82    #[doc(hidden)]
83    pub fn new(name: impl Into<String>, r#type: impl Into<Type>) -> Self {
84        Self {
85            name: name.into(),
86            r#type: r#type.into(),
87            default_value: Default::default(),
88            description: Default::default(),
89            source_file_name: Default::default(),
90            source_node_name: Default::default(),
91            is_implicit: Default::default(),
92            range: Default::default(),
93        }
94    }
95
96    #[doc(hidden)]
97    pub fn with_type(mut self, r#type: impl Into<Type>) -> Self {
98        self.r#type = r#type.into();
99        self
100    }
101
102    #[doc(hidden)]
103    pub fn with_default_value(mut self, default_value: impl Into<YarnValue>) -> Self {
104        self.default_value = Some(default_value.into());
105        self
106    }
107
108    #[doc(hidden)]
109    pub fn with_name(mut self, name: impl Into<String>) -> Self {
110        self.name = name.into();
111        self
112    }
113
114    #[doc(hidden)]
115    pub fn with_description(mut self, description: impl Into<String>) -> Self {
116        self.description = Some(description.into());
117        self
118    }
119
120    #[doc(hidden)]
121    pub fn with_description_optional(mut self, description: impl Into<Option<String>>) -> Self {
122        self.description = description.into();
123        self
124    }
125
126    #[doc(hidden)]
127    pub fn with_source_file_name(mut self, source_file_name: impl Into<DeclarationSource>) -> Self {
128        self.source_file_name = source_file_name.into();
129        self
130    }
131
132    #[doc(hidden)]
133    pub fn with_source_node_name(mut self, source_node_name: impl Into<String>) -> Self {
134        self.source_node_name = Some(source_node_name.into());
135        self
136    }
137
138    #[doc(hidden)]
139    pub fn with_source_node_name_optional(
140        mut self,
141        source_node_name: impl Into<Option<String>>,
142    ) -> Self {
143        self.source_node_name = source_node_name.into();
144        self
145    }
146
147    #[doc(hidden)]
148    pub fn with_implicit(mut self) -> Self {
149        self.is_implicit = true;
150        self
151    }
152
153    #[doc(hidden)]
154    pub fn with_range(mut self, range: impl Into<Range<Position>>) -> Self {
155        self.range = Some(range.into());
156        self
157    }
158
159    #[doc(hidden)]
160    pub fn eq(&self, other: &Self, epsilon: f32) -> bool {
161        self.name == other.name
162            && self.description == other.description
163            && self.source_file_name == other.source_file_name
164            && self.source_node_name == other.source_node_name
165            && self.is_implicit == other.is_implicit
166            && self.r#type == other.r#type
167            && self.range == other.range
168            && match (&self.default_value, &other.default_value) {
169                (Some(a), Some(b)) => a.eq(b, epsilon),
170                (None, None) => true,
171                _ => false,
172            }
173    }
174}
175
176/// The source of a declaration.
177///
178/// ## Implementation notes
179///
180/// In the original implementation, [`DeclarationSource::External`] is just a magic string.
181#[derive(Debug, Clone, PartialEq, Eq, Default)]
182#[cfg_attr(feature = "bevy", derive(Reflect))]
183#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
184#[cfg_attr(feature = "bevy", reflect(Debug, PartialEq, Default))]
185#[cfg_attr(
186    all(feature = "bevy", feature = "serde"),
187    reflect(Serialize, Deserialize)
188)]
189pub enum DeclarationSource {
190    /// The declaration was generated by the compiler.
191    #[default]
192    External,
193    /// The name of the file in which the declaration was found.
194    File(String),
195}
196
197impl<T> From<T> for DeclarationSource
198where
199    T: Into<String>,
200{
201    fn from(file_name: T) -> Self {
202        Self::File(file_name.into())
203    }
204}
205
206pub(crate) trait ParserRuleContextExtRangeSource<'input>:
207    ParserRuleContextExt<'input>
208where
209    <<<<Self as CustomRuleContext<'input>>::TF as TokenFactory<'input>>::Inner as Token>::Data as ToOwned>::Owned:
210        Into<String>,
211{
212    fn range(&self) -> Range<Position> {
213        let start = Position {
214            line: self.start().get_line_as_usize().saturating_sub(1),
215            character: self.start().get_column_as_usize(),
216        };
217        let text: String = self.stop().get_text().to_owned().into();
218        let stop = Position {
219            line: self.stop().get_line_as_usize().saturating_sub(1),
220            character: self.stop().get_column_as_usize() + text.len(),
221        };
222        start..stop
223    }
224}
225
226impl<'input, T: ParserRuleContextExt<'input>> ParserRuleContextExtRangeSource<'input> for T where
227    <<<<T as CustomRuleContext<'input>>::TF as TokenFactory<'input>>::Inner as Token>::Data as ToOwned>::Owned:
228        Into<String>
229{
230}
231
232impl Display for DeclarationSource {
233    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234        match self {
235            Self::External => write!(f, "(External)"),
236            Self::File(file_name) => write!(f, "{}", file_name),
237        }
238    }
239}
240
241#[derive(Debug, Clone, PartialEq, Eq, Hash)]
242pub(crate) struct DeferredTypeDiagnostic {
243    pub(crate) name: String,
244    pub(crate) diagnostic: Diagnostic,
245}