Skip to main content

lady_deirdre/syntax/
morphism.rs

1////////////////////////////////////////////////////////////////////////////////
2// This file is part of "Lady Deirdre", a compiler front-end foundation       //
3// technology.                                                                //
4//                                                                            //
5// This work is proprietary software with source-available code.              //
6//                                                                            //
7// To copy, use, distribute, or contribute to this work, you must agree to    //
8// the terms of the General License Agreement:                                //
9//                                                                            //
10// https://github.com/Eliah-Lakhin/lady-deirdre/blob/master/EULA.md           //
11//                                                                            //
12// The agreement grants a Basic Commercial License, allowing you to use       //
13// this work in non-commercial and limited commercial products with a total   //
14// gross revenue cap. To remove this commercial limit for one of your         //
15// products, you must acquire a Full Commercial License.                      //
16//                                                                            //
17// If you contribute to the source code, documentation, or related materials, //
18// you must grant me an exclusive license to these contributions.             //
19// Contributions are governed by the "Contributions" section of the General   //
20// License Agreement.                                                         //
21//                                                                            //
22// Copying the work in parts is strictly forbidden, except as permitted       //
23// under the General License Agreement.                                       //
24//                                                                            //
25// If you do not or cannot agree to the terms of this Agreement,              //
26// do not use this work.                                                      //
27//                                                                            //
28// This work is provided "as is", without any warranties, express or implied, //
29// except where such disclaimers are legally invalid.                         //
30//                                                                            //
31// Copyright (c) 2024 Ilya Lakhin (Илья Александрович Лахин).                 //
32// All rights reserved.                                                       //
33////////////////////////////////////////////////////////////////////////////////
34
35use std::{
36    borrow::Borrow,
37    fmt::{Debug, Display, Formatter},
38};
39
40use crate::{
41    arena::{Id, Identifiable},
42    format::{AnnotationPriority, SnippetConfig, SnippetFormatter},
43    lexis::{SiteSpan, ToSpan, Token, TokenRef, NIL_TOKEN_REF},
44    report::ld_unreachable,
45    syntax::{AbstractNode, NodeRef, NIL_NODE_REF},
46    units::CompilationUnit,
47};
48
49/// An owned wrapper of [NodeRef] and [TokenRef].
50///
51/// This is a helper object that wraps both kinds of syntax and lexical
52/// component references into a single one.
53#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
54pub enum PolyVariant {
55    /// This polymorphic variant represents a [TokenRef] reference.
56    Token(TokenRef),
57
58    /// This polymorphic variant represents a [NodeRef] reference.
59    Node(NodeRef),
60}
61
62impl Debug for PolyVariant {
63    #[inline(always)]
64    fn fmt(&self, formatter: &mut Formatter) -> std::fmt::Result {
65        match self {
66            Self::Token(variant) => Debug::fmt(variant, formatter),
67            Self::Node(variant) => Debug::fmt(variant, formatter),
68        }
69    }
70}
71
72impl Identifiable for PolyVariant {
73    #[inline(always)]
74    fn id(&self) -> Id {
75        match self {
76            Self::Token(child) => child.id,
77            Self::Node(child) => child.id,
78        }
79    }
80}
81
82impl Borrow<dyn PolyRef> for PolyVariant {
83    #[inline(always)]
84    fn borrow(&self) -> &dyn PolyRef {
85        self
86    }
87}
88
89impl AsRef<dyn PolyRef> for PolyVariant {
90    #[inline(always)]
91    fn as_ref(&self) -> &dyn PolyRef {
92        self
93    }
94}
95
96impl PolyRef for PolyVariant {
97    #[inline(always)]
98    fn kind(&self) -> RefKind {
99        match self {
100            Self::Token(..) => RefKind::Token,
101            Self::Node(..) => RefKind::Node,
102        }
103    }
104
105    #[inline(always)]
106    fn is_nil(&self) -> bool {
107        match self {
108            Self::Token(variant) => variant.is_nil(),
109            Self::Node(variant) => variant.is_nil(),
110        }
111    }
112
113    #[inline(always)]
114    fn as_variant(&self) -> PolyVariant {
115        *self
116    }
117
118    #[inline(always)]
119    fn as_token_ref(&self) -> &TokenRef {
120        match self {
121            Self::Token(variant) => variant,
122            Self::Node(..) => &NIL_TOKEN_REF,
123        }
124    }
125
126    #[inline(always)]
127    fn as_node_ref(&self) -> &NodeRef {
128        match self {
129            Self::Token(..) => &NIL_NODE_REF,
130            Self::Node(variant) => variant,
131        }
132    }
133
134    #[inline(always)]
135    fn span(&self, unit: &impl CompilationUnit) -> Option<SiteSpan> {
136        match self {
137            Self::Token(variant) => variant.span(unit),
138            Self::Node(variant) => variant.span(unit),
139        }
140    }
141}
142
143/// A generic interface for the [NodeRef] and the [TokenRef].
144///
145/// This trait is implemented for the [NodeRef], [TokenRef], and
146/// the [PolyVariant], and provides functions common to all of them.
147pub trait PolyRef: Identifiable + Debug + 'static {
148    /// Returns a discriminant of the underlying reference kind.
149    fn kind(&self) -> RefKind;
150
151    /// Returns true, if the underlying reference intentionally does not refer
152    /// to any node or token within any compilation unit.
153    fn is_nil(&self) -> bool;
154
155    /// Returns an owned wrapper of the [NodeRef] and [TokenRef].
156    fn as_variant(&self) -> PolyVariant;
157
158    /// Returns a [TokenRef] if this PolyRef represents a TokenRef; otherwise
159    /// returns a [TokenRef::nil].
160    fn as_token_ref(&self) -> &TokenRef;
161
162    /// Returns a [NodeRef] if this PolyRef represents a NodeRef; otherwise
163    /// returns a [NodeRef::nil].
164    fn as_node_ref(&self) -> &NodeRef;
165
166    /// Computes a [site span](SiteSpan) of the underlying object.
167    ///
168    /// Returns None if the instance referred to by the underlying reference
169    /// does not exist in the `unit`.
170    ///
171    /// If the underlying object is a token, the function returns a span
172    /// of its char bounds.
173    ///
174    /// If the underlying object is a node, the function delegates span
175    /// computation to the [AbstractNode::span] function.
176    fn span(&self, unit: &impl CompilationUnit) -> Option<SiteSpan>
177    where
178        Self: Sized;
179
180    /// Returns a displayable object that prints the underlying object metadata
181    /// for debugging purposes.
182    ///
183    /// If the underlying reference is not valid for the specified `unit`,
184    /// the returning object would [Debug] the [NodeRef] or a [TokenRef].
185    #[inline(always)]
186    fn display<'unit>(&self, unit: &'unit impl CompilationUnit) -> impl Debug + Display + 'unit
187    where
188        Self: Sized,
189    {
190        DisplayPolyRef {
191            unit,
192            variant: self.as_variant(),
193        }
194    }
195}
196
197impl ToOwned for dyn PolyRef {
198    type Owned = PolyVariant;
199
200    #[inline(always)]
201    fn to_owned(&self) -> Self::Owned {
202        self.as_variant()
203    }
204}
205
206/// A discriminant of the [PolyRef].
207#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
208pub enum RefKind {
209    /// The underlying polymorphic reference is a [TokenRef].
210    Token,
211
212    /// The underlying polymorphic reference is a [NodeRef].
213    Node,
214}
215
216impl RefKind {
217    /// Returns true, if `self == Self::Token`.
218    #[inline(always)]
219    pub fn is_token(&self) -> bool {
220        match self {
221            Self::Token => true,
222            _ => false,
223        }
224    }
225
226    /// Returns true, if `self == Self::Node`.
227    #[inline(always)]
228    pub fn is_node(&self) -> bool {
229        match self {
230            Self::Node => true,
231            _ => false,
232        }
233    }
234}
235
236struct DisplayPolyRef<'unit, U: CompilationUnit> {
237    unit: &'unit U,
238    variant: PolyVariant,
239}
240
241impl<'unit, U: CompilationUnit> Debug for DisplayPolyRef<'unit, U> {
242    #[inline(always)]
243    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
244        Display::fmt(self, formatter)
245    }
246}
247
248impl<'unit, U: CompilationUnit> Display for DisplayPolyRef<'unit, U> {
249    fn fmt(&self, formatter: &mut Formatter) -> std::fmt::Result {
250        let mut summary = String::new();
251        let span;
252
253        match self.variant {
254            PolyVariant::Token(variant) => {
255                let chunk = match variant.chunk(self.unit) {
256                    None => return Debug::fmt(&variant, formatter),
257                    Some(chunk) => chunk,
258                };
259
260                span = match chunk.to_site_span(self.unit) {
261                    Some(span) => span,
262
263                    // Safety: Chunks are always valid spans.
264                    None => unsafe { ld_unreachable!("Invalid chunk span.") },
265                };
266
267                let token = chunk.token;
268
269                summary.push_str("Token: ");
270                summary.push_str(token.name().unwrap_or("?"));
271                summary.push_str("\nDescription: ");
272                summary.push_str(token.describe(true).unwrap_or("?"));
273                summary.push_str("\nEntry: ");
274                summary.push_str(&format!("{:?}", variant.entry));
275                summary.push_str("\nLength: ");
276                summary.push_str(&chunk.length.to_string());
277                summary.push_str("\nSite span: ");
278                summary.push_str(&span.start.to_string());
279                summary.push_str("..");
280                summary.push_str(&span.end.to_string());
281                summary.push_str(&format!("\nPosition span: {}", span.display(self.unit)));
282                summary.push_str(&format!("\nString: {:?}", chunk.string));
283            }
284
285            PolyVariant::Node(variant) => {
286                let node = match variant.deref(self.unit) {
287                    None => return Debug::fmt(&variant, formatter),
288                    Some(chunk) => chunk,
289                };
290
291                span = match node.span(self.unit) {
292                    None => return Debug::fmt(&variant, formatter),
293                    Some(span) => span,
294                };
295
296                summary.push_str("Node: ");
297                summary.push_str(node.name().unwrap_or("?"));
298                summary.push_str("\nDescription: ");
299                summary.push_str(node.describe(true).unwrap_or("?"));
300                summary.push_str("\nNode entry: ");
301                summary.push_str(&format!("{:?}", variant.entry));
302                summary.push_str("\nSite span: ");
303                summary.push_str(&span.start.to_string());
304                summary.push_str("..");
305                summary.push_str(&span.end.to_string());
306                summary.push_str(&format!("\nPosition span: {}", span.display(self.unit)));
307            }
308        }
309
310        static CONFIG: SnippetConfig = SnippetConfig::verbose();
311
312        formatter
313            .snippet(self.unit)
314            .set_config(&CONFIG)
315            .set_caption(format!("Unit({})", self.unit.id()))
316            .set_summary(summary)
317            .annotate(span, AnnotationPriority::Default, "")
318            .finish()
319    }
320}