path.rs
1 // Copyright (C) 2019-2025 ADnet Contributors 2 // This file is part of the ADL library. 3 4 // The ADL library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 9 // The ADL library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 14 // You should have received a copy of the GNU General Public License 15 // along with the ADL library. If not, see <https://www.gnu.org/licenses/>. 16 17 use crate::{Expression, Identifier, Node, NodeID, simple_node_impl}; 18 19 use adl_span::{Span, Symbol}; 20 21 use itertools::Itertools; 22 use serde::{Deserialize, Serialize}; 23 use std::{fmt, hash::Hash}; 24 25 /// A Path in a program. 26 #[derive(Clone, Default, Hash, Eq, PartialEq, Serialize, Deserialize)] 27 pub struct Path { 28 /// The qualifying namespace segments written by the user, excluding the item itself. 29 /// e.g., in `foo::bar::baz`, this would be `[foo, bar]`. 30 qualifier: Vec<Identifier>, 31 32 /// The final item in the path, e.g., `baz` in `foo::bar::baz`. 33 identifier: Identifier, 34 35 /// Is this path an absolute path? e.g. `::foo::bar::baz`. 36 is_absolute: bool, 37 38 /// The fully resolved path. We may not know this until the pass PathResolution pass runs. 39 /// For path that refer to global items (composites, consts, functions), `absolute_path` is 40 /// guaranteed to be set after the pass `PathResolution`. 41 absolute_path: Option<Vec<Symbol>>, 42 43 /// A span locating where the path occurred in the source. 44 pub span: Span, 45 46 /// The ID of the node. 47 pub id: NodeID, 48 } 49 50 simple_node_impl!(Path); 51 52 impl Path { 53 /// Creates a new `Path` from the given components. 54 /// 55 /// - `qualifier`: The namespace segments (e.g., `foo::bar` in `foo::bar::baz`). 56 /// - `identifier`: The final item in the path (e.g., `baz`). 57 /// - `is_absolute`: Whether the path is absolute (starts with `::`). 58 /// - `absolute_path`: Optionally, the fully resolved symbolic path. 59 /// - `span`: The source code span for this path. 60 /// - `id`: The node ID. 61 pub fn new( 62 qualifier: Vec<Identifier>, 63 identifier: Identifier, 64 is_absolute: bool, 65 absolute_path: Option<Vec<Symbol>>, 66 span: Span, 67 id: NodeID, 68 ) -> Self { 69 Self { qualifier, identifier, is_absolute, absolute_path, span, id } 70 } 71 72 /// Returns the final identifier of the path (e.g., `baz` in `foo::bar::baz`). 73 pub fn identifier(&self) -> Identifier { 74 self.identifier 75 } 76 77 /// Returns a slice of the qualifier segments (e.g., `[foo, bar]` in `foo::bar::baz`). 78 pub fn qualifier(&self) -> &[Identifier] { 79 self.qualifier.as_slice() 80 } 81 82 /// Returns `true` if the path is absolute (i.e., starts with `::`). 83 pub fn is_absolute(&self) -> bool { 84 self.is_absolute 85 } 86 87 /// Returns a `Vec<Symbol>` representing the full symbolic path: 88 /// the qualifier segments followed by the final identifier. 89 /// 90 /// Note: this refers to the user path which is not necessarily the absolute path. 91 pub fn as_symbols(&self) -> Vec<Symbol> { 92 self.qualifier.iter().map(|segment| segment.name).chain(std::iter::once(self.identifier.name)).collect() 93 } 94 95 /// Returns an optional vector of `Symbol`s representing the resolved absolute path, 96 /// or `None` if resolution has not yet occurred. 97 pub fn try_absolute_path(&self) -> Option<Vec<Symbol>> { 98 if self.is_absolute { Some(self.as_symbols()) } else { self.absolute_path.clone() } 99 } 100 101 /// Returns a vector of `Symbol`s representing the resolved absolute path. 102 /// 103 /// If the path is not an absolute path, this method panics if the absolute path has not been resolved yet. 104 /// For relative paths, this is expected to be called only after path resolution has occurred. 105 pub fn absolute_path(&self) -> Vec<Symbol> { 106 if self.is_absolute { 107 self.as_symbols() 108 } else { 109 self.absolute_path.as_ref().expect("absolute path must be known at this stage").to_vec() 110 } 111 } 112 113 /// Converts this `Path` into an absolute path by setting its `is_absolute` flag to `true`. 114 /// 115 /// This does not alter the qualifier or identifier, nor does it compute or modify 116 /// the resolved `absolute_path`. 117 pub fn into_absolute(mut self) -> Self { 118 self.is_absolute = true; 119 self 120 } 121 122 /// Returns a new `Path` instance with the last segment's `Symbol` and the last symbol 123 /// in the `absolute_path` (if present) replaced with `new_symbol`. 124 /// 125 /// Other fields remain unchanged. 126 pub fn with_updated_last_symbol(mut self, new_symbol: Symbol) -> Self { 127 // Update identifier 128 self.identifier.name = new_symbol; 129 130 // Update absolute_path's last symbol if present 131 if let Some(ref mut abs_path) = self.absolute_path 132 && let Some(last) = abs_path.last_mut() 133 { 134 *last = new_symbol; 135 } 136 137 self 138 } 139 140 /// Sets `self.absolute_path` to `absolute_path` 141 pub fn with_absolute_path(mut self, absolute_path: Option<Vec<Symbol>>) -> Self { 142 self.absolute_path = absolute_path; 143 self 144 } 145 146 /// Sets the `absolute_path` by prepending the given `module_prefix` to the path's 147 /// own qualifier and identifier. Returns the updated `Path`. 148 pub fn with_module_prefix(mut self, module_prefix: &[Symbol]) -> Self { 149 let full_path = module_prefix 150 .iter() 151 .cloned() 152 .chain(self.qualifier.iter().map(|id| id.name)) 153 .chain(std::iter::once(self.identifier.name)) 154 .collect(); 155 156 self.absolute_path = Some(full_path); 157 self 158 } 159 } 160 161 impl fmt::Display for Path { 162 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 163 if self.is_absolute { 164 write!(f, "::")?; 165 } 166 if self.qualifier.is_empty() { 167 write!(f, "{}", self.identifier) 168 } else { 169 write!(f, "{}::{}", self.qualifier.iter().format("::"), self.identifier) 170 } 171 } 172 } 173 174 impl fmt::Debug for Path { 175 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 176 // Print user path (Display impl) 177 write!(f, "{self}")?; 178 179 // Print resolved absolute path if available 180 if let Some(abs_path) = &self.absolute_path { 181 write!(f, "(::{})", abs_path.iter().format("::")) 182 } else { 183 write!(f, "()") 184 } 185 } 186 } 187 188 impl From<Path> for Expression { 189 fn from(value: Path) -> Self { 190 Expression::Path(value) 191 } 192 }