@@ -56,18 +56,30 @@ impl Parse<Program> for ProgramParser {
5656 ctx. import_root . clone ( )
5757 } ;
5858 let raw_path = Path :: new ( & base_dir) . join ( & relative_path) ;
59- let filepath = raw_path
60- . canonicalize ( )
61- . map_err ( |e| {
62- SemanticError :: new ( format ! (
63- "Cannot resolve import '{}' (resolved to '{}'): {}" ,
64- relative_path,
65- raw_path. display( ) ,
66- e
59+ let filepath = if let Some ( dir) = ctx. embedded_dir {
60+ let key = lexical_normalize ( & raw_path) ;
61+ if dir. get_file ( Path :: new ( & key) ) . is_none ( ) {
62+ return Err ( SemanticError :: new ( format ! (
63+ "Cannot resolve embedded import '{}' (resolved to '{}')" ,
64+ relative_path, key
6765 ) )
68- } ) ?
69- . to_string_lossy ( )
70- . to_string ( ) ;
66+ . into ( ) ) ;
67+ }
68+ key
69+ } else {
70+ raw_path
71+ . canonicalize ( )
72+ . map_err ( |e| {
73+ SemanticError :: new ( format ! (
74+ "Cannot resolve import '{}' (resolved to '{}'): {}" ,
75+ relative_path,
76+ raw_path. display( ) ,
77+ e
78+ ) )
79+ } ) ?
80+ . to_string_lossy ( )
81+ . to_string ( )
82+ } ;
7183
7284 // Check for circular imports
7385 if ctx. import_stack . contains ( & filepath) {
@@ -100,7 +112,15 @@ impl Parse<Program> for ProgramParser {
100112 }
101113 ctx. imported_filepaths . insert ( filepath. clone ( ) ) ;
102114 ctx. import_stack . push ( filepath. clone ( ) ) ;
103- ctx. current_source_code = ProgramSource :: Filepath ( filepath) . get_content ( & ctx. flags ) ?;
115+ let import_source = if let Some ( dir) = ctx. embedded_dir {
116+ ProgramSource :: Embedded {
117+ entry : filepath. clone ( ) ,
118+ dir,
119+ }
120+ } else {
121+ ProgramSource :: Filepath ( filepath. clone ( ) )
122+ } ;
123+ ctx. current_source_code = import_source. get_content ( & ctx. flags ) ?;
104124 let subprogram = parse_program_helper ( ctx) ?;
105125 ctx. import_stack . pop ( ) ;
106126 functions. extend ( subprogram. functions ) ;
@@ -143,6 +163,29 @@ impl Parse<Program> for ProgramParser {
143163 }
144164}
145165
166+ /// Lexically normalize a path for embedded-source lookups: collapse `.` and
167+ /// `..` components and join with `/` regardless of host OS, so the same key
168+ /// works on every platform.
169+ fn lexical_normalize ( path : & Path ) -> String {
170+ use std:: path:: Component ;
171+ let mut parts: Vec < String > = Vec :: new ( ) ;
172+ for c in path. components ( ) {
173+ match c {
174+ Component :: CurDir => { }
175+ Component :: ParentDir => {
176+ if matches ! ( parts. last( ) . map( String :: as_str) , Some ( ".." ) | None ) {
177+ parts. push ( ".." . to_string ( ) ) ;
178+ } else {
179+ parts. pop ( ) ;
180+ }
181+ }
182+ Component :: Normal ( s) => parts. push ( s. to_string_lossy ( ) . into_owned ( ) ) ,
183+ Component :: RootDir | Component :: Prefix ( _) => { }
184+ }
185+ }
186+ parts. join ( "/" )
187+ }
188+
146189/// Parser for import statements.
147190pub struct ImportStatementParser ;
148191
0 commit comments