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}