1414// You should have received a copy of the GNU General Public License
1515// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
1616
17- use crate :: { Expression , Identifier , Location , Node , NodeID , simple_node_impl} ;
17+ use crate :: { Expression , Identifier , Location , Node , NodeID , ProgramId , simple_node_impl} ;
1818
1919use leo_span:: { Span , Symbol } ;
2020
21+ use indexmap:: IndexSet ;
2122use itertools:: Itertools ;
2223use serde:: { Deserialize , Serialize } ;
2324use std:: { fmt, hash:: Hash } ;
@@ -26,7 +27,7 @@ use std::{fmt, hash::Hash};
2627#[ derive( Clone , Hash , Eq , PartialEq , Serialize , Deserialize ) ]
2728pub struct Path {
2829 /// The program this path belongs to, if set by the user
29- user_program : Option < Identifier > ,
30+ user_program : Option < ProgramId > ,
3031
3132 /// The qualifying namespace segments written by the user, excluding the item itself.
3233 /// e.g., in `foo::bar::baz`, this would be `[foo, bar]`.
@@ -63,7 +64,7 @@ impl Path {
6364 /// - `span`: The source code span for this path.
6465 /// - `id`: The node ID.
6566 pub fn new (
66- user_program : Option < Identifier > ,
67+ user_program : Option < ProgramId > ,
6768 qualifier : Vec < Identifier > ,
6869 identifier : Identifier ,
6970 span : Span ,
@@ -93,12 +94,12 @@ impl Path {
9394 }
9495
9596 /// Returns the optional program identifier.
96- pub fn user_program ( & self ) -> Option < & Identifier > {
97+ pub fn user_program ( & self ) -> Option < & ProgramId > {
9798 self . user_program . as_ref ( )
9899 }
99100
100101 /// Returns `self` after setting it `user_program` field to `user_program`.
101- pub fn with_user_program ( mut self , user_program : Identifier ) -> Self {
102+ pub fn with_user_program ( mut self , user_program : ProgramId ) -> Self {
102103 self . user_program = Some ( user_program) ;
103104 self
104105 }
@@ -131,7 +132,7 @@ impl Path {
131132 /// 3. None (unresolved or local)
132133 pub fn program ( & self ) -> Option < Symbol > {
133134 if let Some ( id) = & self . user_program {
134- return Some ( id. name ) ;
135+ return Some ( id. as_symbol ( ) ) ;
135136 }
136137
137138 match & self . target {
@@ -215,35 +216,79 @@ impl Path {
215216 Self { user_program, qualifier, identifier, target, span, id }
216217 }
217218
218- /// Resolves this path as a global path using the current module context.
219+ /// Resolves this path as a global path within the current module context.
219220 ///
220- /// This method constructs an absolute global `Location` by combining:
221- /// 1) the current module path,
222- /// 2) any user-written qualifier segments, and
223- /// 3) the final identifier.
221+ /// This function converts a user-written path into a fully qualified
222+ /// [`PathTarget::Global`] by determining which program the path belongs to
223+ /// and constructing the corresponding module path.
224224 ///
225- /// The resolution only affects the `target` field and preserves
226- /// the original user-written syntax of the path.
227- pub fn resolve_as_global_in_module < I > ( self , program : Symbol , current_module : I ) -> Self
225+ /// Resolution follows two main cases:
226+ ///
227+ /// 1. **External library access**
228+ /// If the path does not explicitly specify a program (`user_program` is `None`)
229+ /// and the first qualifier segment matches a known external library name,
230+ /// that segment is interpreted as the target program. The remaining qualifier
231+ /// segments and identifier form the path inside that program.
232+ ///
233+ /// 2. **Local or explicitly-qualified program access**
234+ /// Otherwise, the path is resolved relative to the current module context.
235+ /// The final location is constructed by combining:
236+ /// - the current module path,
237+ /// - any user-written qualifier segments, and
238+ /// - the final identifier.
239+ ///
240+ /// If the user explicitly wrote a program (via `user_program`), it overrides
241+ /// the default `program` parameter. Otherwise, the current program is used.
242+ ///
243+ /// Importantly, this transformation **does not modify the user-written syntax**
244+ /// (`user_program`, `qualifier`, `identifier`). It only determines the internal
245+ /// `target` used during later compilation stages.
246+ pub fn resolve_as_global_in_module < I > (
247+ self ,
248+ program : Symbol ,
249+ external_libs : & IndexSet < Symbol > ,
250+ current_module : I ,
251+ ) -> Self
228252 where
229253 I : IntoIterator < Item = Symbol > ,
230254 {
231255 let Path { user_program, qualifier, identifier, span, id, .. } = self ;
232256
233- let mut path: Vec < Symbol > = Vec :: new ( ) ;
234-
235- // 1. Current module
236- path. extend ( current_module) ;
237-
238- // 2. User-written qualifier
239- path. extend ( qualifier. iter ( ) . map ( |id| id. name ) ) ;
240-
241- // 3. Final identifier
242- path. push ( identifier. name ) ;
243-
244- let target = PathTarget :: Global ( Location { program : user_program. map ( |id| id. name ) . unwrap_or ( program) , path } ) ;
245-
246- Self { user_program, qualifier, identifier, target, span, id }
257+ // Case 1: The path starts with a known external library name and the user
258+ // did not explicitly specify a program. In this situation we interpret
259+ // the first qualifier segment as the program name.
260+ if let Some ( first) = qualifier. first ( )
261+ && user_program. is_none ( )
262+ && external_libs. contains ( & first. name )
263+ {
264+ // Build the path within the external library by skipping the
265+ // first qualifier (the library name itself).
266+ let mut path: Vec < Symbol > = qualifier. iter ( ) . skip ( 1 ) . map ( |id| id. name ) . collect ( ) ;
267+ path. push ( identifier. name ) ;
268+
269+ let target = PathTarget :: Global ( Location { program : first. name , path } ) ;
270+
271+ Self { user_program : None , qualifier, identifier, target, span, id }
272+ } else {
273+ // Case 2: Resolve relative to the current module.
274+ //
275+ // Construct the path by concatenating:
276+ // current_module + user qualifier + identifier.
277+ let mut path: Vec < Symbol > = Vec :: new ( ) ;
278+ path. extend ( current_module) ;
279+ path. extend ( qualifier. iter ( ) . map ( |id| id. name ) ) ;
280+ path. push ( identifier. name ) ;
281+
282+ // Determine which program this location belongs to:
283+ // - use the explicitly written program if provided
284+ // - otherwise fall back to the current program.
285+ let target = PathTarget :: Global ( Location {
286+ program : user_program. map ( |id| id. as_symbol ( ) ) . unwrap_or ( program) ,
287+ path,
288+ } ) ;
289+
290+ Self { user_program, qualifier, identifier, target, span, id }
291+ }
247292 }
248293}
249294
@@ -253,12 +298,12 @@ impl fmt::Display for Path {
253298 let program: Option < Symbol > = self
254299 . user_program
255300 . as_ref ( )
256- . map ( |id| id. name ) // Convert Identifier -> Symbol
301+ . map ( |id| id. as_symbol ( ) ) // Convert Identifier -> Symbol
257302 . or_else ( || self . try_global_location ( ) . map ( |global| global. program ) ) ;
258303
259304 // Program prefix
260305 if let Some ( program) = program {
261- write ! ( f, "{}.aleo /" , program) ?;
306+ write ! ( f, "{}/" , program) ?;
262307 }
263308
264309 // Qualifiers
0 commit comments