1mod cache;
7mod loading;
8mod resolution;
9#[cfg(all(test, feature = "deep-tests"))]
10mod resolution_deep_tests;
11mod resolver;
12
13use crate::project::{DependencySpec, ProjectRoot, find_project_root, normalize_package_identity};
14use shape_ast::ast::{AnnotationDef, FunctionDef, ImportStmt, Program};
15use shape_ast::error::{Result, ShapeError};
16use shape_ast::parser::parse_program;
17use shape_value::KindedSlot;
18use std::collections::HashMap;
19use std::path::{Path, PathBuf};
20use std::sync::Arc;
21
22use cache::ModuleCache;
23pub use resolver::{
24 FilesystemResolver, InMemoryResolver, ModuleCode, ModuleResolver, ResolvedModuleArtifact,
25};
26
27include!(concat!(env!("OUT_DIR"), "/embedded_stdlib_modules.rs"));
28
29const KNOWN_STDLIB_LEAF_NAMES: &[&str] = &[
34 "file", "json", "http", "crypto", "env", "toml", "yaml", "xml", "compress", "archive",
35 "unicode", "csv", "msgpack", "regex", "parallel", "time", "io", "set", "state", "transport",
36 "remote",
37];
38
39pub fn bare_name_migration_hint(module_path: &str) -> Option<String> {
42 if module_path.contains("::") {
44 return None;
45 }
46 if KNOWN_STDLIB_LEAF_NAMES.contains(&module_path) {
47 let canonical = format!("std::core::{}", module_path);
48 Some(format!(
49 "Module '{}' not found. Did you mean '{}'?\n Hint: use {}",
50 module_path, canonical, canonical
51 ))
52 } else {
53 None
54 }
55}
56
57#[derive(Debug, Clone)]
59pub struct Module {
60 pub name: String,
61 pub path: String,
62 pub exports: HashMap<String, Export>,
63 pub ast: Program,
64}
65
66impl Module {
67 pub fn get_export(&self, name: &str) -> Option<&Export> {
69 self.exports.get(name)
70 }
71
72 pub fn export_names(&self) -> Vec<&str> {
74 self.exports.keys().map(|s| s.as_str()).collect()
75 }
76}
77
78#[derive(Debug, Clone)]
85pub enum Export {
86 Function(Arc<FunctionDef>),
87 TypeAlias(Arc<shape_ast::ast::TypeAliasDef>),
88 Annotation(Arc<AnnotationDef>),
89 Value(KindedSlot),
90}
91
92pub use shape_ast::module_utils::{ModuleExportKind, ModuleExportSymbol};
96
97pub fn collect_exported_symbols(program: &Program) -> Result<Vec<ModuleExportSymbol>> {
101 shape_ast::module_utils::collect_exported_symbols(program)
102}
103
104pub fn collect_exported_function_names_from_source(
110 module_path: &str,
111 source: &str,
112) -> Result<Vec<String>> {
113 let ast = parse_program(source).map_err(|e| ShapeError::ModuleError {
114 message: format!("Failed to parse module source '{}': {}", module_path, e),
115 module_path: None,
116 })?;
117
118 let module = loading::compile_module(module_path, ast)?;
119 let mut names: Vec<String> = module
120 .exports
121 .into_iter()
122 .filter_map(|(name, export)| match export {
123 Export::Function(_) => Some(name),
124 _ => None,
125 })
126 .collect();
127 names.sort();
128 names.dedup();
129 Ok(names)
130}
131
132pub struct ModuleLoader {
134 stdlib_path: PathBuf,
136 module_paths: Vec<PathBuf>,
138 current_project_root: Option<PathBuf>,
140 cache: ModuleCache,
142 dependency_paths: HashMap<String, PathBuf>,
145 extension_resolver: InMemoryResolver,
147 bundle_resolver: InMemoryResolver,
149 embedded_stdlib_resolver: InMemoryResolver,
151 keychain: Option<crate::crypto::Keychain>,
153 blob_store: Option<Arc<dyn crate::blob_store::BlobStore>>,
156}
157
158impl ModuleLoader {
159 pub fn new() -> Self {
161 let mut loader = Self {
162 stdlib_path: Self::default_stdlib_path(),
163 module_paths: Self::default_module_paths(),
164 current_project_root: None,
165 cache: ModuleCache::new(),
166 dependency_paths: HashMap::new(),
167 extension_resolver: InMemoryResolver::default(),
168 bundle_resolver: InMemoryResolver::default(),
169 embedded_stdlib_resolver: InMemoryResolver::default(),
170 keychain: None,
171 blob_store: None,
172 };
173
174 if let Ok(shape_path) = std::env::var("SHAPE_PATH") {
176 for path in shape_path.split(':') {
177 loader.add_module_path(PathBuf::from(path));
178 }
179 }
180
181 for (module_path, source) in EMBEDDED_STDLIB_MODULES {
182 loader.register_embedded_stdlib_module(
183 (*module_path).to_string(),
184 ModuleCode::Source(Arc::from(*source)),
185 );
186 }
187
188 loader
189 }
190
191 pub fn clone_without_cache(&self) -> Self {
193 Self {
194 stdlib_path: self.stdlib_path.clone(),
195 module_paths: self.module_paths.clone(),
196 current_project_root: self.current_project_root.clone(),
197 cache: ModuleCache::new(),
198 dependency_paths: self.dependency_paths.clone(),
199 extension_resolver: self.extension_resolver.clone(),
200 bundle_resolver: self.bundle_resolver.clone(),
201 embedded_stdlib_resolver: self.embedded_stdlib_resolver.clone(),
202 keychain: None,
203 blob_store: self.blob_store.clone(),
204 }
205 }
206
207 fn default_stdlib_path() -> PathBuf {
209 crate::stdlib_metadata::default_stdlib_path()
210 }
211
212 fn default_module_paths() -> Vec<PathBuf> {
214 let mut paths = vec![];
215
216 paths.push(PathBuf::from("."));
218
219 paths.push(PathBuf::from(".shape"));
221 paths.push(PathBuf::from("shape_modules"));
222 paths.push(PathBuf::from("modules"));
223
224 if let Some(home) = dirs::home_dir() {
226 paths.push(home.join(".shape/modules"));
227 paths.push(home.join(".local/share/shape/modules"));
228 }
229
230 paths.push(PathBuf::from("/usr/local/share/shape/modules"));
232 paths.push(PathBuf::from("/usr/share/shape/modules"));
233
234 paths
235 }
236
237 pub fn add_module_path(&mut self, path: PathBuf) {
239 if !self.module_paths.contains(&path) {
240 self.module_paths.push(path);
241 }
242 }
243
244 pub fn set_project_root(&mut self, root: &std::path::Path, extra_paths: &[PathBuf]) {
250 let root_buf = root.to_path_buf();
251 self.current_project_root = Some(root_buf.clone());
252 let mut to_prepend = vec![root_buf];
254 to_prepend.extend(extra_paths.iter().cloned());
255 self.module_paths.retain(|p| !to_prepend.contains(p));
257 to_prepend.extend(self.module_paths.drain(..));
258 self.module_paths = to_prepend;
259 }
260
261 pub fn configure_for_context(&mut self, current_file: &Path, workspace_root: Option<&Path>) {
263 if let Some(project) = resolve_project_root(current_file, workspace_root) {
264 let module_paths = project.resolved_module_paths();
265 self.set_project_root(&project.root_path, &module_paths);
266 self.set_dependency_paths(resolve_path_dependencies(&project));
267 }
268 }
269
270 pub fn configure_for_context_with_source(
280 &mut self,
281 current_file: &Path,
282 workspace_root: Option<&Path>,
283 current_source: Option<&str>,
284 extension_schema_cache: &crate::extension_context::ExtensionModuleSchemaCache,
285 ) {
286 self.configure_for_context(current_file, workspace_root);
287
288 if self.dependency_paths.is_empty() {
290 if let Some(source) = current_source {
291 let (project, _rest) = crate::frontmatter::parse_frontmatter(source);
292 if let Some(config) = project {
293 if !config.dependencies.is_empty() {
294 let root = ProjectRoot {
295 root_path: current_file
296 .parent()
297 .unwrap_or(Path::new("."))
298 .to_path_buf(),
299 config,
300 };
301 let module_paths = root.resolved_module_paths();
302 self.set_project_root(&root.root_path, &module_paths);
303 self.set_dependency_paths(resolve_path_dependencies(&root));
304 }
305 }
306 }
307 }
308
309 crate::extension_context::register_declared_extensions_in_loader(
310 self,
311 Some(current_file),
312 workspace_root,
313 current_source,
314 extension_schema_cache,
315 );
316 }
317
318 pub fn set_dependency_paths(&mut self, deps: HashMap<String, PathBuf>) {
325 let mut regular_deps = HashMap::new();
326
327 for (name, path) in deps {
328 if path.extension().and_then(|e| e.to_str()) == Some("shapec") && path.is_file() {
329 match crate::package_bundle::PackageBundle::read_from_file(&path) {
331 Ok(bundle) => {
332 self.load_bundle(&bundle, Some(&name));
333 }
334 Err(e) => {
335 eprintln!(
336 "Warning: failed to load bundle dependency '{}' from '{}': {}",
337 name,
338 path.display(),
339 e
340 );
341 regular_deps.insert(name, path);
343 }
344 }
345 } else {
346 regular_deps.insert(name, path);
347 }
348 }
349
350 self.dependency_paths = regular_deps;
351 }
352
353 pub fn register_extension_module(&mut self, module_path: impl Into<String>, code: ModuleCode) {
355 self.extension_resolver.register(module_path, code);
356 }
357
358 pub fn register_embedded_stdlib_module(
360 &mut self,
361 module_path: impl Into<String>,
362 code: ModuleCode,
363 ) {
364 self.embedded_stdlib_resolver.register(module_path, code);
365 }
366
367 pub fn load_bundle(
373 &mut self,
374 bundle: &crate::package_bundle::PackageBundle,
375 prefix: Option<&str>,
376 ) {
377 for manifest in &bundle.manifests {
379 let path = if let Some(prefix) = prefix {
380 format!("{}::{}", prefix, manifest.name)
381 } else {
382 manifest.name.clone()
383 };
384
385 let mut module_blobs = HashMap::new();
388 for hash in manifest.exports.values() {
389 if let Some(data) = bundle.blob_store.get(hash) {
390 module_blobs.insert(*hash, data.clone());
391 }
392 if let Some(deps) = manifest.dependency_closure.get(hash) {
394 for dep_hash in deps {
395 if let Some(data) = bundle.blob_store.get(dep_hash) {
396 module_blobs.insert(*dep_hash, data.clone());
397 }
398 }
399 }
400 }
401 for hash in manifest.type_schemas.values() {
402 if let Some(data) = bundle.blob_store.get(hash) {
403 module_blobs.insert(*hash, data.clone());
404 }
405 }
406
407 self.register_content_addressed_module(path, manifest, module_blobs);
408 }
409
410 for module in &bundle.modules {
412 let path = if let Some(prefix) = prefix {
413 if module.module_path.is_empty() {
414 prefix.to_string()
415 } else {
416 format!("{}::{}", prefix, module.module_path)
417 }
418 } else {
419 module.module_path.clone()
420 };
421
422 self.bundle_resolver.register(
423 path,
424 ModuleCode::Compiled(Arc::from(module.bytecode_bytes.clone().into_boxed_slice())),
425 );
426 }
427 }
428
429 pub fn register_content_addressed_module(
436 &mut self,
437 module_path: impl Into<String>,
438 manifest: &crate::module_manifest::ModuleManifest,
439 blobs: HashMap<[u8; 32], Vec<u8>>,
440 ) {
441 let manifest_bytes =
442 rmp_serde::to_vec(manifest).expect("ModuleManifest serialization should not fail");
443 self.bundle_resolver.register(
444 module_path,
445 ModuleCode::ContentAddressed {
446 manifest_bytes: Arc::from(manifest_bytes.into_boxed_slice()),
447 blob_cache: Arc::new(blobs),
448 },
449 );
450 }
451
452 pub fn register_bundle_modules(&mut self, modules: Vec<(String, ModuleCode)>) {
454 for (path, code) in modules {
455 self.bundle_resolver.register(path, code);
456 }
457 }
458
459 pub fn set_blob_store(&mut self, store: Arc<dyn crate::blob_store::BlobStore>) {
462 self.blob_store = Some(store);
463 }
464
465 pub fn has_extension_module(&self, module_path: &str) -> bool {
467 self.extension_resolver.has(module_path)
468 }
469
470 pub fn extension_module_paths(&self) -> Vec<String> {
472 self.extension_resolver.module_paths()
473 }
474
475 pub fn embedded_stdlib_module_paths(&self) -> Vec<String> {
477 self.embedded_stdlib_resolver.module_paths()
478 }
479
480 pub fn get_dependency_paths(&self) -> &HashMap<String, PathBuf> {
482 &self.dependency_paths
483 }
484
485 pub fn get_module_paths(&self) -> &[PathBuf] {
487 &self.module_paths
488 }
489
490 pub fn get_stdlib_path(&self) -> &PathBuf {
492 &self.stdlib_path
493 }
494
495 pub fn set_stdlib_path(&mut self, path: PathBuf) {
497 self.stdlib_path = path;
498 }
499
500 pub fn set_keychain(&mut self, keychain: crate::crypto::Keychain) {
506 self.keychain = Some(keychain);
507 }
508
509 pub fn keychain(&self) -> Option<&crate::crypto::Keychain> {
511 self.keychain.as_ref()
512 }
513
514 pub fn clear_module_paths(&mut self) {
516 self.module_paths.clear();
517 }
518
519 pub fn reset_module_paths(&mut self) {
521 self.module_paths = Self::default_module_paths();
522 }
523
524 pub fn load_module(&mut self, module_path: &str) -> Result<Arc<Module>> {
526 self.load_module_with_context(module_path, None)
527 }
528
529 pub fn resolve_module_path(&self, module_path: &str) -> Result<PathBuf> {
531 self.resolve_module_path_with_context(module_path, None)
532 }
533
534 pub fn resolve_module_path_with_context(
536 &self,
537 module_path: &str,
538 context_path: Option<&PathBuf>,
539 ) -> Result<PathBuf> {
540 resolve_module_path_with_settings(
541 module_path,
542 context_path.map(|p| p.as_path()),
543 self.stdlib_path.as_path(),
544 &self.module_paths,
545 &self.dependency_paths,
546 )
547 }
548
549 fn load_module_from_resolved_path(
550 &mut self,
551 cache_key: String,
552 compile_module_path: &str,
553 file_path: PathBuf,
554 ) -> Result<Arc<Module>> {
555 let content = std::fs::read_to_string(&file_path).map_err(|e| ShapeError::ModuleError {
556 message: format!("Failed to read module file: {}: {}", file_path.display(), e),
557 module_path: Some(file_path.clone()),
558 })?;
559
560 let ast = parse_program(&content).map_err(|e| ShapeError::ModuleError {
562 message: format!("Failed to parse module: {}: {}", compile_module_path, e),
563 module_path: None,
564 })?;
565 let mut ast = ast;
566 annotate_program_declaring_module_path(&mut ast, compile_module_path);
567 annotate_program_native_abi_package_key(
568 &mut ast,
569 self.package_key_for_origin_path(Some(&file_path))
570 .as_deref(),
571 );
572
573 let dependencies = resolution::extract_dependencies(&ast);
575 self.cache
576 .store_dependencies(cache_key.clone(), dependencies.clone());
577
578 let module_dir = file_path.parent().map(|p| p.to_path_buf());
580 for dep in &dependencies {
581 self.load_module_with_context(dep, module_dir.as_ref())?;
582 }
583
584 let module = loading::compile_module(compile_module_path, ast)?;
586 let module = Arc::new(module);
587
588 self.cache.insert(cache_key, module.clone());
590
591 Ok(module)
592 }
593
594 fn load_module_from_source_artifact(
595 &mut self,
596 cache_key: String,
597 compile_module_path: &str,
598 source: &str,
599 origin_path: Option<PathBuf>,
600 context_path: Option<&PathBuf>,
601 ) -> Result<Arc<Module>> {
602 let ast = parse_program(source).map_err(|e| ShapeError::ModuleError {
604 message: format!("Failed to parse module: {}: {}", compile_module_path, e),
605 module_path: origin_path.clone(),
606 })?;
607 let mut ast = ast;
608 annotate_program_declaring_module_path(&mut ast, compile_module_path);
609 annotate_program_native_abi_package_key(
610 &mut ast,
611 self.package_key_for_origin_path(origin_path.as_deref())
612 .as_deref(),
613 );
614
615 let dependencies = resolution::extract_dependencies(&ast);
617 self.cache
618 .store_dependencies(cache_key.clone(), dependencies.clone());
619
620 let module = loading::compile_module(compile_module_path, ast)?;
625 let module = Arc::new(module);
626 self.cache.insert(cache_key, module.clone());
627
628 let module_dir = origin_path
630 .as_ref()
631 .and_then(|path| path.parent().map(|p| p.to_path_buf()))
632 .or_else(|| context_path.cloned());
633 for dep in &dependencies {
634 self.load_module_with_context(dep, module_dir.as_ref())?;
635 }
636
637 Ok(module)
638 }
639
640 fn resolve_module_artifact_with_context(
641 &self,
642 module_path: &str,
643 context_path: Option<&PathBuf>,
644 ) -> Result<ResolvedModuleArtifact> {
645 let context = context_path.map(|p| p.as_path());
646
647 if let Some(artifact) = self.extension_resolver.resolve(module_path, context)? {
648 return Ok(artifact);
649 }
650
651 if let Some(artifact) = self.bundle_resolver.resolve(module_path, context)? {
653 return Ok(artifact);
654 }
655
656 if let Some(artifact) = self
657 .embedded_stdlib_resolver
658 .resolve(module_path, context)?
659 {
660 return Ok(artifact);
661 }
662
663 let filesystem = FilesystemResolver {
664 stdlib_path: self.stdlib_path.as_path(),
665 module_paths: &self.module_paths,
666 dependency_paths: &self.dependency_paths,
667 };
668
669 filesystem
670 .resolve(module_path, context)?
671 .ok_or_else(|| {
672 let message = if let Some(hint) = bare_name_migration_hint(module_path) {
674 hint
675 } else {
676 format!("Module not found: {}", module_path)
677 };
678 ShapeError::ModuleError {
679 message,
680 module_path: None,
681 }
682 })
683 }
684
685 pub fn load_module_with_context(
687 &mut self,
688 module_path: &str,
689 context_path: Option<&PathBuf>,
690 ) -> Result<Arc<Module>> {
691 self.cache.check_circular_dependency(module_path)?;
696
697 if let Some(module) = self.cache.get(module_path) {
699 return Ok(module);
700 }
701
702 let artifact = self.resolve_module_artifact_with_context(module_path, context_path)?;
704 self.cache.push_loading(module_path.to_string());
706 let result = match artifact.code {
707 ModuleCode::Source(source) => self.load_module_from_source_artifact(
708 module_path.to_string(),
709 module_path,
710 source.as_ref(),
711 artifact.origin_path,
712 context_path,
713 ),
714 ModuleCode::Both { source, .. } => self.load_module_from_source_artifact(
715 module_path.to_string(),
716 module_path,
717 source.as_ref(),
718 artifact.origin_path,
719 context_path,
720 ),
721 ModuleCode::Compiled(_compiled) => {
722 let module = Module {
725 name: module_path
726 .split("::")
727 .last()
728 .unwrap_or(module_path)
729 .to_string(),
730 path: module_path.to_string(),
731 exports: HashMap::new(), ast: shape_ast::ast::Program {
733 items: vec![],
734 docs: shape_ast::ast::ProgramDocs::default(),
735 },
736 };
737 let module = Arc::new(module);
738 self.cache.insert(module_path.to_string(), module.clone());
739 Ok(module)
740 }
741 ModuleCode::ContentAddressed {
742 manifest_bytes,
743 blob_cache,
744 } => {
745 let manifest: crate::module_manifest::ModuleManifest =
747 rmp_serde::from_slice(&manifest_bytes).map_err(|e| {
748 ShapeError::ModuleError {
749 message: format!(
750 "Failed to deserialize manifest for '{}': {}",
751 module_path, e
752 ),
753 module_path: None,
754 }
755 })?;
756
757 if !manifest.verify_integrity() {
759 return Err(ShapeError::ModuleError {
760 message: format!(
761 "Manifest integrity check failed for '{}': content hash mismatch",
762 module_path
763 ),
764 module_path: None,
765 });
766 }
767
768 if let Some(keychain) = &self.keychain {
770 let sig_data =
771 manifest
772 .signature
773 .as_ref()
774 .map(|sig| crate::crypto::ModuleSignatureData {
775 author_key: sig.author_key,
776 signature: sig.signature.clone(),
777 signed_at: sig.signed_at,
778 });
779 let result = keychain.verify_module(
780 &manifest.name,
781 &manifest.manifest_hash,
782 sig_data.as_ref(),
783 );
784 if let crate::crypto::VerifyResult::Rejected(reason) = result {
785 return Err(ShapeError::ModuleError {
786 message: format!(
787 "Signature verification failed for '{}': {}",
788 module_path, reason
789 ),
790 module_path: None,
791 });
792 }
793 }
794
795 let mut exports = HashMap::new();
800 for export_name in manifest.exports.keys() {
801 let placeholder_fn = shape_ast::ast::FunctionDef {
804 name: export_name.clone(),
805 name_span: shape_ast::ast::Span::default(),
806 declaring_module_path: None,
807 doc_comment: None,
808 params: vec![],
809 body: vec![],
810 return_type: None,
811 is_async: false,
812 is_comptime: false,
813 type_params: None,
814 where_clause: None,
815 annotations: vec![],
816 };
817 exports.insert(
818 export_name.clone(),
819 Export::Function(Arc::new(placeholder_fn)),
820 );
821 }
822
823 for (hash, data) in blob_cache.iter() {
826 let hex_key = format!("__blob__{}", hex::encode(hash));
827 self.bundle_resolver.register(
828 hex_key,
829 ModuleCode::Compiled(Arc::from(data.clone().into_boxed_slice())),
830 );
831 }
832
833 if let Some(ref store) = self.blob_store {
836 for (_name, hash) in manifest.exports.iter() {
837 let all_hashes: Vec<&[u8; 32]> = std::iter::once(hash)
838 .chain(
839 manifest
840 .dependency_closure
841 .get(hash)
842 .into_iter()
843 .flat_map(|v| v.iter()),
844 )
845 .collect();
846 for h in all_hashes {
847 let hex_key = format!("__blob__{}", hex::encode(h));
848 if !self.bundle_resolver.has(&hex_key) {
849 if let Some(data) = store.get(h) {
850 self.bundle_resolver.register(
851 hex_key,
852 ModuleCode::Compiled(Arc::from(data.into_boxed_slice())),
853 );
854 }
855 }
856 }
857 }
858 }
859
860 let module = Module {
861 name: manifest.name.clone(),
862 path: module_path.to_string(),
863 exports,
864 ast: shape_ast::ast::Program {
865 items: vec![],
866 docs: shape_ast::ast::ProgramDocs::default(),
867 },
868 };
869 let module = Arc::new(module);
870 self.cache.insert(module_path.to_string(), module.clone());
871 Ok(module)
872 }
873 };
874 self.cache.pop_loading();
875 result
876 }
877
878 pub fn load_module_from_file(&mut self, file_path: &Path) -> Result<Arc<Module>> {
883 let canonical = file_path
884 .canonicalize()
885 .unwrap_or_else(|_| file_path.to_path_buf());
886 let cache_key = canonical.to_string_lossy().to_string();
887
888 if let Some(module) = self.cache.get(&cache_key) {
890 return Ok(module);
891 }
892
893 self.cache.check_circular_dependency(&cache_key)?;
895
896 self.cache.push_loading(cache_key.clone());
897 let result = self.load_module_from_resolved_path(cache_key.clone(), &cache_key, canonical);
898 self.cache.pop_loading();
899
900 result
901 }
902
903 pub fn list_core_stdlib_module_imports(&self) -> Result<Vec<String>> {
905 let mut embedded: Vec<String> = self
906 .embedded_stdlib_resolver
907 .module_paths()
908 .into_iter()
909 .filter(|name| name.starts_with("std::core::"))
910 .collect();
911 if !embedded.is_empty() {
912 embedded.sort();
913 embedded.dedup();
914 return Ok(embedded);
915 }
916
917 if !self.stdlib_path.exists() || !self.stdlib_path.is_dir() {
918 return Err(ShapeError::ModuleError {
919 message: format!(
920 "Could not find stdlib directory at {}",
921 self.stdlib_path.display()
922 ),
923 module_path: Some(self.stdlib_path.clone()),
924 });
925 }
926
927 resolution::list_core_stdlib_module_imports(self.stdlib_path.as_path())
928 }
929
930 pub fn list_stdlib_module_imports(&self) -> Result<Vec<String>> {
932 let mut embedded: Vec<String> = self
933 .embedded_stdlib_resolver
934 .module_paths()
935 .into_iter()
936 .filter(|name| name.starts_with("std::"))
937 .collect();
938 if !embedded.is_empty() {
939 embedded.sort();
940 embedded.dedup();
941 return Ok(embedded);
942 }
943
944 if !self.stdlib_path.exists() || !self.stdlib_path.is_dir() {
945 return Err(ShapeError::ModuleError {
946 message: format!(
947 "Could not find stdlib directory at {}",
948 self.stdlib_path.display()
949 ),
950 module_path: Some(self.stdlib_path.clone()),
951 });
952 }
953
954 resolution::list_stdlib_module_imports(self.stdlib_path.as_path())
955 }
956
957 pub fn list_importable_modules_with_context(
966 &self,
967 current_file: &Path,
968 workspace_root: Option<&Path>,
969 ) -> Vec<String> {
970 let mut modules = self.list_stdlib_module_imports().unwrap_or_default();
971
972 modules.extend(self.embedded_stdlib_resolver.module_paths());
973 modules.extend(self.extension_resolver.module_paths());
974
975 if let Some(project) = resolve_project_root(current_file, workspace_root) {
976 modules.extend(
977 resolution::list_modules_from_root(&project.root_path, None).unwrap_or_default(),
978 );
979
980 for module_path in project.resolved_module_paths() {
981 modules.extend(
982 resolution::list_modules_from_root(&module_path, None).unwrap_or_default(),
983 );
984 }
985
986 for (dep_name, dep_root) in resolve_path_dependencies(&project) {
987 modules.extend(
988 resolution::list_modules_from_root(&dep_root, Some(dep_name.as_str()))
989 .unwrap_or_default(),
990 );
991 }
992 } else if let Some(context_dir) = current_file.parent() {
993 modules
994 .extend(resolution::list_modules_from_root(context_dir, None).unwrap_or_default());
995 }
996
997 modules.sort();
998 modules.dedup();
999 modules.retain(|m| !m.is_empty());
1000 modules
1001 }
1002
1003 pub fn load_core_stdlib_modules(&mut self) -> Result<Vec<Arc<Module>>> {
1005 let mut modules = Vec::new();
1006 for import_path in self.list_core_stdlib_module_imports()? {
1007 modules.push(self.load_module(&import_path)?);
1008 }
1009 Ok(modules)
1010 }
1011
1012 pub fn load_stdlib(&mut self) -> Result<()> {
1014 let _ = self.load_core_stdlib_modules()?;
1015 Ok(())
1016 }
1017
1018 pub fn loaded_modules(&self) -> Vec<&str> {
1020 self.cache.loaded_modules()
1021 }
1022
1023 pub fn get_export(&self, module_path: &str, export_name: &str) -> Option<&Export> {
1025 self.cache.get_export(module_path, export_name)
1026 }
1027
1028 pub fn get_module(&self, module_path: &str) -> Option<&Arc<Module>> {
1030 self.cache.get_module(module_path)
1031 }
1032
1033 pub fn resolve_import(&mut self, import_stmt: &ImportStmt) -> Result<HashMap<String, Export>> {
1035 let module = self.load_module(&import_stmt.from)?;
1036 cache::resolve_import(import_stmt, &module)
1037 }
1038
1039 pub fn clear_cache(&mut self) {
1041 self.cache.clear();
1042 }
1043
1044 pub fn get_dependencies(&self, module_path: &str) -> Option<&Vec<String>> {
1046 self.cache.get_dependencies(module_path)
1047 }
1048
1049 pub fn get_all_dependencies(&self, module_path: &str) -> Vec<String> {
1051 self.cache.get_all_dependencies(module_path)
1052 }
1053
1054 fn package_key_for_origin_path(&self, origin_path: Option<&Path>) -> Option<String> {
1055 let origin_path = origin_path?;
1056 let origin = origin_path
1057 .canonicalize()
1058 .unwrap_or_else(|_| origin_path.to_path_buf());
1059
1060 for dep_root in self.dependency_paths.values() {
1061 let dep_root = dep_root.canonicalize().unwrap_or_else(|_| dep_root.clone());
1062 if origin.starts_with(&dep_root)
1063 && let Some(project) = find_project_root(&dep_root)
1064 {
1065 return Some(normalize_package_identity(&project.root_path, &project.config).2);
1066 }
1067 }
1068
1069 if let Some(project_root) = &self.current_project_root {
1070 let project_root = project_root
1071 .canonicalize()
1072 .unwrap_or_else(|_| project_root.clone());
1073 if origin.starts_with(&project_root)
1074 && let Some(project) = find_project_root(&project_root)
1075 {
1076 return Some(normalize_package_identity(&project.root_path, &project.config).2);
1077 }
1078 }
1079
1080 None
1081 }
1082}
1083
1084fn annotate_program_native_abi_package_key(program: &mut Program, package_key: Option<&str>) {
1085 let Some(package_key) = package_key else {
1086 return;
1087 };
1088 for item in &mut program.items {
1089 annotate_item_native_abi_package_key(item, package_key);
1090 }
1091}
1092
1093fn annotate_program_declaring_module_path(program: &mut Program, module_path: &str) {
1094 for item in &mut program.items {
1095 annotate_item_declaring_module_path(item, module_path);
1096 }
1097}
1098
1099fn annotate_item_native_abi_package_key(item: &mut shape_ast::ast::Item, package_key: &str) {
1100 use shape_ast::ast::{ExportItem, Item};
1101
1102 match item {
1103 Item::ForeignFunction(def, _) => {
1104 if let Some(native) = def.native_abi.as_mut()
1105 && native.package_key.is_none()
1106 {
1107 native.package_key = Some(package_key.to_string());
1108 }
1109 }
1110 Item::Export(export, _) => {
1111 if let ExportItem::ForeignFunction(def) = &mut export.item
1112 && let Some(native) = def.native_abi.as_mut()
1113 && native.package_key.is_none()
1114 {
1115 native.package_key = Some(package_key.to_string());
1116 }
1117 }
1118 Item::Module(module, _) => {
1119 for nested in &mut module.items {
1120 annotate_item_native_abi_package_key(nested, package_key);
1121 }
1122 }
1123 _ => {}
1124 }
1125}
1126
1127fn annotate_item_declaring_module_path(item: &mut shape_ast::ast::Item, module_path: &str) {
1128 use shape_ast::ast::{ExportItem, Item};
1129
1130 match item {
1131 Item::Function(def, _) => {
1132 if def.declaring_module_path.is_none() {
1133 def.declaring_module_path = Some(module_path.to_string());
1134 }
1135 }
1136 Item::Export(export, _) => match &mut export.item {
1137 ExportItem::Function(def) => {
1138 if def.declaring_module_path.is_none() {
1139 def.declaring_module_path = Some(module_path.to_string());
1140 }
1141 }
1142 ExportItem::ForeignFunction(_) => {}
1143 _ => {}
1144 },
1145 Item::Extend(extend, _) => {
1146 for method in &mut extend.methods {
1147 if method.declaring_module_path.is_none() {
1148 method.declaring_module_path = Some(module_path.to_string());
1149 }
1150 }
1151 }
1152 Item::Impl(impl_block, _) => {
1153 for method in &mut impl_block.methods {
1154 if method.declaring_module_path.is_none() {
1155 method.declaring_module_path = Some(module_path.to_string());
1156 }
1157 }
1158 }
1159 Item::Module(module, _) => {
1160 let nested_path = format!("{}::{}", module_path, module.name);
1161 for nested in &mut module.items {
1162 annotate_item_declaring_module_path(nested, &nested_path);
1163 }
1164 }
1165 _ => {}
1166 }
1167}
1168
1169impl Default for ModuleLoader {
1170 fn default() -> Self {
1171 Self::new()
1172 }
1173}
1174
1175pub fn resolve_module_path_with_settings(
1177 module_path: &str,
1178 context_path: Option<&Path>,
1179 stdlib_path: &Path,
1180 module_paths: &[PathBuf],
1181 dependency_paths: &HashMap<String, PathBuf>,
1182) -> Result<PathBuf> {
1183 resolution::resolve_module_path_with_context(
1184 module_path,
1185 context_path,
1186 stdlib_path,
1187 module_paths,
1188 dependency_paths,
1189 )
1190}
1191
1192fn resolve_project_root(current_file: &Path, workspace_root: Option<&Path>) -> Option<ProjectRoot> {
1193 workspace_root
1194 .and_then(find_project_root)
1195 .or_else(|| current_file.parent().and_then(find_project_root))
1196}
1197
1198fn resolve_path_dependencies(project: &ProjectRoot) -> HashMap<String, PathBuf> {
1199 let mut resolved = HashMap::new();
1200
1201 for (name, spec) in &project.config.dependencies {
1202 if let DependencySpec::Detailed(detailed) = spec {
1203 if let Some(path) = &detailed.path {
1204 let dep_path = project.root_path.join(path);
1205 let canonical = dep_path.canonicalize().unwrap_or(dep_path);
1206 resolved.insert(name.clone(), canonical);
1207 }
1208 }
1209 }
1210
1211 resolved
1212}
1213
1214#[cfg(test)]
1215mod tests {
1216 use super::*;
1217 use std::sync::Arc;
1218
1219 #[test]
1220 fn test_compile_module_exports_function() {
1221 let source = r#"
1222pub fn greet(name) {
1223 return "Hello, " + name
1224}
1225"#;
1226 let ast = parse_program(source).unwrap();
1227 let module = loading::compile_module("test_module", ast).unwrap();
1228
1229 assert!(
1230 module.exports.contains_key("greet"),
1231 "Expected 'greet' export, got: {:?}",
1232 module.exports.keys().collect::<Vec<_>>()
1233 );
1234
1235 match module.exports.get("greet") {
1236 Some(Export::Function(func)) => {
1237 assert_eq!(func.name, "greet");
1238 }
1239 other => panic!("Expected Function export, got: {:?}", other),
1240 }
1241 }
1242
1243 #[test]
1244 fn test_collect_exported_function_names_from_source() {
1245 let source = r#"
1246fn hidden() { 0 }
1247pub fn connect(uri) { uri }
1248pub fn ping() { 1 }
1249"#;
1250 let names = collect_exported_function_names_from_source("duckdb", source)
1251 .expect("should collect exported functions");
1252 assert_eq!(names, vec!["connect".to_string(), "ping".to_string()]);
1253 }
1254
1255 #[test]
1256 fn test_stdlib_methods_are_annotated_with_declaring_module_path() {
1257 let mut loader = ModuleLoader::new();
1258 let module = loader
1259 .load_module("std::core::json_value")
1260 .expect("load stdlib module");
1261
1262 let extend = module
1263 .ast
1264 .items
1265 .iter()
1266 .find_map(|item| match item {
1267 shape_ast::ast::Item::Extend(extend, _) => Some(extend),
1268 _ => None,
1269 })
1270 .expect("json_value module should contain an extend block");
1271 let method = extend
1272 .methods
1273 .iter()
1274 .find(|method| method.name == "get")
1275 .expect("json_value extend block should contain get()");
1276
1277 assert_eq!(
1278 method.declaring_module_path.as_deref(),
1279 Some("std::core::json_value")
1280 );
1281 }
1282
1283 #[test]
1284 fn test_load_module_from_temp_file() {
1285 use std::io::Write;
1286
1287 let temp_dir = std::env::temp_dir();
1289 let module_path = temp_dir.join("test_load_module.shape");
1290 let mut file = std::fs::File::create(&module_path).unwrap();
1291 writeln!(
1292 file,
1293 r#"
1294pub fn add(a, b) {{
1295 return a + b
1296}}
1297"#
1298 )
1299 .unwrap();
1300
1301 let mut loader = ModuleLoader::new();
1303 loader.add_module_path(temp_dir.clone());
1304
1305 let result = loader.load_module_with_context("test_load_module", Some(&temp_dir));
1307
1308 std::fs::remove_file(&module_path).ok();
1310
1311 let module = result.expect("Module should load");
1313 assert!(
1314 module.exports.contains_key("add"),
1315 "Expected 'add' export, got: {:?}",
1316 module.exports.keys().collect::<Vec<_>>()
1317 );
1318 }
1319
1320 #[test]
1321 fn test_load_module_from_file_path() {
1322 use std::io::Write;
1323
1324 let temp_dir = tempfile::tempdir().expect("temp dir");
1325 let module_path = temp_dir.path().join("helpers.shape");
1326 let mut file = std::fs::File::create(&module_path).expect("create module");
1327 writeln!(
1328 file,
1329 r#"
1330pub fn helper(x) {{
1331 x
1332}}
1333"#
1334 )
1335 .expect("write module");
1336
1337 let mut loader = ModuleLoader::new();
1338 let module = loader
1339 .load_module_from_file(&module_path)
1340 .expect("module should load from file path");
1341 assert!(
1342 module.exports.contains_key("helper"),
1343 "Expected 'helper' export, got: {:?}",
1344 module.exports.keys().collect::<Vec<_>>()
1345 );
1346 }
1347
1348 #[test]
1349 fn test_loaded_dependency_module_annotates_native_abi_with_package_key() {
1350 let root = tempfile::tempdir().expect("tempdir");
1351 let dep_root = root.path().join("dep_pkg");
1352
1353 std::fs::create_dir_all(&dep_root).expect("create dep root");
1354 std::fs::write(
1355 root.path().join("shape.toml"),
1356 r#"
1357[project]
1358name = "app"
1359version = "0.1.0"
1360
1361[dependencies]
1362dep_pkg = { path = "./dep_pkg" }
1363"#,
1364 )
1365 .expect("write root shape.toml");
1366 std::fs::write(
1367 dep_root.join("shape.toml"),
1368 r#"
1369[project]
1370name = "dep_pkg"
1371version = "1.2.3"
1372"#,
1373 )
1374 .expect("write dep shape.toml");
1375 std::fs::write(
1376 dep_root.join("index.shape"),
1377 r#"
1378extern C fn dep_call() -> i32 from "shared";
1379"#,
1380 )
1381 .expect("write dep source");
1382
1383 let mut loader = ModuleLoader::new();
1384 loader.set_project_root(root.path(), &[]);
1385 loader.set_dependency_paths(HashMap::from([("dep_pkg".to_string(), dep_root.clone())]));
1386
1387 let module = loader.load_module("dep_pkg").expect("load dep module");
1388 let foreign = module
1389 .ast
1390 .items
1391 .iter()
1392 .find_map(|item| match item {
1393 shape_ast::ast::Item::ForeignFunction(def, _) => Some(def),
1394 _ => None,
1395 })
1396 .expect("foreign function should exist");
1397 let native = foreign
1398 .native_abi
1399 .as_ref()
1400 .expect("native abi should exist");
1401 assert_eq!(native.package_key.as_deref(), Some("dep_pkg@1.2.3"));
1402 }
1403
1404 #[test]
1405 fn test_collect_exported_symbols_detects_pub_function_and_enum() {
1406 let source = r#"
1407pub fn helper() { 1 }
1408pub enum Side { Buy, Sell }
1409"#;
1410 let ast = parse_program(source).unwrap();
1411 let exports = collect_exported_symbols(&ast).unwrap();
1412
1413 let helper = exports
1414 .iter()
1415 .find(|e| e.name == "helper")
1416 .expect("expected helper export");
1417 assert_eq!(helper.name, "helper");
1418 assert!(helper.alias.is_none());
1419 assert_eq!(helper.kind, ModuleExportKind::Function);
1420
1421 let side = exports
1422 .iter()
1423 .find(|e| e.name == "Side")
1424 .expect("expected Side export");
1425 assert_eq!(side.kind, ModuleExportKind::Enum);
1426 }
1427
1428 #[test]
1429 fn test_collect_exported_symbols_detects_pub_annotation_and_builtin_exports() {
1430 let source = r#"
1431pub builtin fn execute(addr: string, code: string) -> string;
1432pub builtin type RemoteHandle;
1433pub annotation remote(addr) {
1434 metadata() { return { addr: addr }; }
1435}
1436"#;
1437 let ast = parse_program(source).unwrap();
1438 let exports = collect_exported_symbols(&ast).unwrap();
1439
1440 let execute = exports
1441 .iter()
1442 .find(|e| e.name == "execute")
1443 .expect("expected execute export");
1444 assert_eq!(execute.kind, ModuleExportKind::BuiltinFunction);
1445
1446 let handle = exports
1447 .iter()
1448 .find(|e| e.name == "RemoteHandle")
1449 .expect("expected RemoteHandle export");
1450 assert_eq!(handle.kind, ModuleExportKind::BuiltinType);
1451
1452 let remote = exports
1453 .iter()
1454 .find(|e| e.name == "remote")
1455 .expect("expected remote annotation export");
1456 assert_eq!(remote.kind, ModuleExportKind::Annotation);
1457 }
1458
1459 #[test]
1460 fn test_compile_module_exports_annotation() {
1461 let source = r#"
1462pub annotation remote(addr) {
1463 metadata() { return { addr: addr }; }
1464}
1465"#;
1466 let ast = parse_program(source).unwrap();
1467 let module = loading::compile_module("test_module", ast).unwrap();
1468
1469 match module.exports.get("remote") {
1470 Some(Export::Annotation(annotation)) => {
1471 assert_eq!(annotation.name, "remote");
1472 }
1473 other => panic!("Expected Annotation export, got: {:?}", other),
1474 }
1475 }
1476
1477 #[test]
1478 fn test_list_core_stdlib_module_imports_contains_core_modules() {
1479 let loader = ModuleLoader::new();
1480 let modules = loader
1481 .list_core_stdlib_module_imports()
1482 .expect("should list std.core modules");
1483
1484 assert!(
1485 !modules.is_empty(),
1486 "expected non-empty std.core module list"
1487 );
1488 assert!(
1489 modules.iter().all(|m| m.starts_with("std::core::")),
1490 "expected std::core::* import paths, got: {:?}",
1491 modules
1492 );
1493 assert!(
1494 modules.iter().any(|m| m == "std::core::math"),
1495 "expected std::core::math in core module list"
1496 );
1497 }
1498
1499 #[test]
1500 fn test_list_stdlib_module_imports_includes_non_core_namespaces() {
1501 let loader = ModuleLoader::new();
1502 let modules = loader
1503 .list_stdlib_module_imports()
1504 .expect("should list stdlib modules");
1505
1506 assert!(
1507 modules.iter().any(|m| m.starts_with("std::finance::")),
1508 "expected finance stdlib modules in list, got: {:?}",
1509 modules
1510 );
1511 }
1512
1513 #[test]
1514 fn test_embedded_stdlib_loads_without_filesystem_path() {
1515 let mut loader = ModuleLoader::new();
1516 loader.set_stdlib_path(std::env::temp_dir().join("shape_missing_stdlib_dir"));
1517
1518 let module = loader
1519 .load_module("std::core::snapshot")
1520 .expect("embedded stdlib module should load without filesystem stdlib");
1521 assert!(
1522 module.exports.contains_key("snapshot"),
1523 "expected snapshot export from std::core::snapshot"
1524 );
1525 }
1526
1527 #[test]
1528 fn test_list_importable_modules_with_context_includes_project_and_deps() {
1529 let tmp = tempfile::tempdir().unwrap();
1530 let root = tmp.path();
1531
1532 std::fs::write(
1533 root.join("shape.toml"),
1534 r#"
1535[modules]
1536paths = ["lib"]
1537
1538[dependencies]
1539mydep = { path = "deps/mydep" }
1540"#,
1541 )
1542 .unwrap();
1543
1544 std::fs::create_dir_all(root.join("src")).unwrap();
1545 std::fs::create_dir_all(root.join("lib")).unwrap();
1546 std::fs::create_dir_all(root.join("deps/mydep")).unwrap();
1547
1548 std::fs::write(root.join("src/main.shape"), "let x = 1").unwrap();
1549 std::fs::write(root.join("lib/tools.shape"), "pub fn tool() { 1 }").unwrap();
1550 std::fs::write(root.join("deps/mydep/index.shape"), "pub fn root() { 1 }").unwrap();
1551 std::fs::write(root.join("deps/mydep/util.shape"), "pub fn util() { 1 }").unwrap();
1552
1553 let loader = ModuleLoader::new();
1554 let modules =
1555 loader.list_importable_modules_with_context(&root.join("src/main.shape"), None);
1556
1557 assert!(
1558 modules.iter().any(|m| m == "tools"),
1559 "expected module path from [modules].paths, got: {:?}",
1560 modules
1561 );
1562 assert!(
1563 modules.iter().any(|m| m == "mydep"),
1564 "expected dependency index module path, got: {:?}",
1565 modules
1566 );
1567 assert!(
1568 modules.iter().any(|m| m == "mydep::util"),
1569 "expected dependency submodule path, got: {:?}",
1570 modules
1571 );
1572 }
1573
1574 #[test]
1575 fn test_load_in_memory_extension_module() {
1576 let mut loader = ModuleLoader::new();
1577 loader.register_extension_module(
1578 "duckdb",
1579 ModuleCode::Source(Arc::from(
1580 r#"
1581pub fn connect(uri) { uri }
1582"#,
1583 )),
1584 );
1585
1586 let module = loader
1587 .load_module("duckdb")
1588 .expect("in-memory extension module should load");
1589 assert!(
1590 module.exports.contains_key("connect"),
1591 "expected connect export, got {:?}",
1592 module.exports.keys().collect::<Vec<_>>()
1593 );
1594 }
1595
1596 #[test]
1597 fn test_load_in_memory_extension_module_with_dependency() {
1598 let mut loader = ModuleLoader::new();
1599 loader.register_extension_module(
1600 "b",
1601 ModuleCode::Source(Arc::from(
1602 r#"
1603pub fn answer() { 42 }
1604"#,
1605 )),
1606 );
1607 loader.register_extension_module(
1608 "a",
1609 ModuleCode::Source(Arc::from(
1610 r#"
1611from b use { answer }
1612pub fn use_answer() { answer() }
1613"#,
1614 )),
1615 );
1616
1617 let module = loader
1618 .load_module("a")
1619 .expect("in-memory module with dependency should load");
1620 assert!(
1621 module.exports.contains_key("use_answer"),
1622 "expected use_answer export"
1623 );
1624 assert!(
1625 loader.get_module("b").is_some(),
1626 "dependency module b should load"
1627 );
1628 }
1629
1630 #[test]
1631 fn test_load_bundle_modules() {
1632 use crate::package_bundle::{BundleMetadata, BundledModule, PackageBundle};
1633
1634 let bundle = PackageBundle {
1635 metadata: BundleMetadata {
1636 name: "test".to_string(),
1637 version: "0.1.0".to_string(),
1638 compiler_version: "0.5.0".to_string(),
1639 source_hash: "abc".to_string(),
1640 bundle_kind: "portable-bytecode".to_string(),
1641 build_host: "x86_64-linux".to_string(),
1642 native_portable: true,
1643 entry_module: None,
1644 built_at: 0,
1645 readme: None,
1646 },
1647 modules: vec![BundledModule {
1648 module_path: "helpers".to_string(),
1649 bytecode_bytes: vec![1, 2, 3],
1650 export_names: vec!["helper".to_string()],
1651 source_hash: "def".to_string(),
1652 }],
1653 dependencies: std::collections::HashMap::new(),
1654 blob_store: std::collections::HashMap::new(),
1655 manifests: vec![],
1656 native_dependency_scopes: vec![],
1657 docs: std::collections::HashMap::new(),
1658 };
1659
1660 let mut loader = ModuleLoader::new();
1661 loader.load_bundle(&bundle, Some("mylib"));
1662
1663 let artifact = loader.resolve_module_artifact_with_context("mylib::helpers", None);
1665 assert!(artifact.is_ok(), "bundle module should be resolvable");
1666 }
1667
1668 #[test]
1669 fn test_frontmatter_dependencies_resolve_without_shape_toml() {
1670 use std::io::Write;
1671
1672 let temp_dir = tempfile::tempdir().expect("temp dir");
1673 let dep_dir = temp_dir.path().join("my_dep");
1674 std::fs::create_dir_all(&dep_dir).expect("create dep dir");
1675
1676 let mut dep_file = std::fs::File::create(dep_dir.join("index.shape")).expect("create dep");
1678 writeln!(dep_file, "pub fn helper(x) {{ x + 1 }}").expect("write dep");
1679
1680 let main_path = temp_dir.path().join("main.shape");
1682 let source = format!(
1683 "---\n[dependencies]\nmy_dep = {{ path = \"{}\" }}\n---\nimport my_dep\nmy_dep::helper(1)\n",
1684 dep_dir.display()
1685 );
1686
1687 let mut loader = ModuleLoader::new();
1688 let cache = crate::extension_context::ExtensionModuleSchemaCache::new();
1689 loader.configure_for_context_with_source(&main_path, None, Some(&source), &cache);
1690
1691 assert!(
1693 loader.dependency_paths.contains_key("my_dep"),
1694 "frontmatter dependency should be registered, got: {:?}",
1695 loader.dependency_paths.keys().collect::<Vec<_>>()
1696 );
1697 }
1698}