Skip to content

Commit bae899e

Browse files
committed
feat: implemented loading one lib from another and tested its functionality
1 parent a9c5550 commit bae899e

File tree

14 files changed

+534
-337
lines changed

14 files changed

+534
-337
lines changed

examples/multiple_libs/main.simf

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
use merkle::build_root::get_root;
2-
use math::simple_op::hash;
1+
use merkle::build_root::{get_root, hash as and_hash};
2+
use base_math::simple_op::hash as or_hash;
33

44
pub fn get_block_value_hash(prev_hash: u32, tx1: u32, tx2: u32) -> u32 {
55
let root: u32 = get_root(tx1, tx2);
6-
hash(prev_hash, root);
6+
or_hash(prev_hash, root)
77
}
88

99
fn main() {
10-
let block_val_hash: u32 = get_block_value(5, 10, 20);
10+
let block_val_hash: u32 = get_block_value_hash(5, 10, 20);
1111
assert!(jet::eq_32(block_val_hash, 27));
12+
13+
let first_value: u32 = 15;
14+
let second_value: u32 = 22;
15+
assert!(jet::eq_32(and_hash(first_value, second_value), 6));
1216
}
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
use math::simple_op::hash;
1+
use math::simple_op::hash as temp_hash;
22

33
pub fn get_root(tx1: u32, tx2: u32) -> u32 {
4-
hash(tx1, tx2)
4+
temp_hash(tx1, tx2)
5+
}
6+
7+
pub fn hash(tx1: u32, tx2: u32) -> u32 {
8+
jet::and_32(tx1, tx2)
59
}

examples/single_lib/main.simf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pub use temp::two::two as smth;
1+
pub use temp::constants::utils::two as smth;
22
use temp::funcs::{get_five, Smth};
33

44
fn seven() -> u32 {
File renamed without changes.

examples/temp/libs/lib/math.simf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fn add(a: u32, b: u32) {}

examples/temp/main.simf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
use lib::math::add;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub fn get_some_value() -> u64 {
2+
1
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
use r#jet::r#fn::r#let;
2+
3+
pub fn main() {}

src/driver.rs

Lines changed: 134 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::error::{Error, ErrorCollector, RichError, Span};
77
use crate::parse::{self, AliasedIdentifier, ParseFromStrWithErrors, Visibility};
88
use crate::str::{AliasName, FunctionName, Identifier};
99
use crate::types::AliasedType;
10-
use crate::{get_full_path, impl_eq_hash, LibTable, SourceFile, SourceName};
10+
use crate::{impl_eq_hash, DependencyMap, SourceFile, SourceName};
1111

1212
/// Graph Node: One file = One module
1313
#[derive(Debug, Clone)]
@@ -25,7 +25,7 @@ pub struct ProjectGraph {
2525

2626
/// Fast lookup: Path -> ID
2727
/// Solves the duplicate problem (so as not to parse a.simf twice)
28-
pub libraries: Arc<LibTable>,
28+
pub dependency_map: Arc<DependencyMap>,
2929
pub lookup: HashMap<SourceName, usize>,
3030
pub paths: Arc<[SourceName]>,
3131

@@ -381,7 +381,7 @@ impl ProjectGraph {
381381

382382
pub fn new(
383383
root_source: SourceFile,
384-
libraries: Arc<LibTable>,
384+
dependency_map: Arc<DependencyMap>,
385385
root_program: &parse::Program,
386386
handler: &mut ErrorCollector,
387387
) -> Option<Self> {
@@ -416,11 +416,10 @@ impl ProjectGraph {
416416
// PHASE 1: Resolve Imports
417417
for elem in current_program.items() {
418418
if let parse::Item::Use(use_decl) = elem {
419-
match get_full_path(&libraries, use_decl) {
419+
match resolve_single_import(importer_source.clone(), use_decl, &dependency_map)
420+
{
420421
Ok(path) => valid_imports.push((path, *use_decl.span())),
421-
Err(err) => {
422-
resolution_errors.push(err.with_source(importer_source.clone()))
423-
}
422+
Err(err) => resolution_errors.push(err),
424423
}
425424
}
426425
}
@@ -464,7 +463,7 @@ impl ProjectGraph {
464463
} else {
465464
Some(Self {
466465
modules,
467-
libraries,
466+
dependency_map,
468467
lookup,
469468
paths: paths.into(),
470469
dependencies,
@@ -673,13 +672,27 @@ impl ProjectGraph {
673672
let mut errors: Vec<RichError> = Vec::new();
674673
match elem {
675674
parse::Item::Use(use_decl) => {
675+
let full_path = match resolve_single_import(
676+
importer_source.clone(),
677+
use_decl,
678+
&self.dependency_map,
679+
) {
680+
Ok(path) => path,
681+
Err(err) => {
682+
handler.push(err);
683+
continue;
684+
}
685+
};
686+
687+
/*
676688
let full_path = match get_full_path(&self.libraries, use_decl) {
677689
Ok(path) => path,
678690
Err(err) => {
679691
handler.push(err.with_source(importer_source.clone()));
680692
continue;
681693
}
682694
};
695+
*/
683696
let source_full_path = SourceName::Real(Arc::from(full_path));
684697
let ind = self.lookup[&source_full_path];
685698

@@ -758,6 +771,33 @@ impl ProjectGraph {
758771
}
759772
}
760773

774+
/// Resolves a single `use` declaration into a physical file path.
775+
fn resolve_single_import(
776+
importer_source: SourceFile,
777+
use_decl: &parse::UseDecl,
778+
dependency_map: &DependencyMap,
779+
) -> Result<PathBuf, RichError> {
780+
// TODO: @LesterEvSe or someone else, reconsider this architectural approach.
781+
// Consider removing this `match` statement, or dropping `SourceName` from `paths` and `lookup`.
782+
let curr_path = match importer_source.name() {
783+
SourceName::Real(path) => path,
784+
SourceName::Virtual(name) => {
785+
// Notice we use `return Err(...)` here instead of `continue`
786+
return Err(RichError::new(
787+
Error::Resolution(format!(
788+
"Virtual source '{name}' cannot be used to resolve library imports"
789+
)),
790+
*use_decl.span(),
791+
));
792+
}
793+
};
794+
795+
match dependency_map.resolve_path(&curr_path, use_decl) {
796+
Ok(path) => Ok(path),
797+
Err(err) => Err(err.with_source(importer_source.clone())),
798+
}
799+
}
800+
761801
/// C3 Merge Algorithm
762802
///
763803
/// Merges a list of sequences (parent linearizations) into a single sequence.
@@ -970,9 +1010,30 @@ pub(crate) mod tests {
9701010
(program.expect("Root parsing failed internally"), source)
9711011
}
9721012

973-
/// Sets up a graph with "lib" mapped to "libs/lib".
974-
/// Files format: vec![("main.simf", "content"), ("libs/lib/A.simf", "content")]
975-
fn setup_graph(files: Vec<(&str, &str)>) -> (ProjectGraph, HashMap<String, usize>, TempDir) {
1013+
/// Bootstraps a mock file system and attempts to construct a `ProjectGraph`.
1014+
///
1015+
/// This is the low-level, non-panicking test helper. It is designed specifically for
1016+
/// "negative tests" where you expect the graph construction to fail (e.g., due to syntax
1017+
/// errors in an imported dependency).
1018+
///
1019+
/// The mock environment automatically maps the alias `"lib"` to the `"libs/lib"` directory.
1020+
///
1021+
/// # Arguments
1022+
/// * `files` - A vector of tuples containing `(file_path, file_content)`.
1023+
/// **Note:** One of the files *must* be named exactly `"main.simf"`.
1024+
///
1025+
/// # Returns
1026+
/// A tuple containing:
1027+
/// 1. `Option<ProjectGraph>` - `Some` if construction succeeded, `None` if compilation failed.
1028+
/// 2. `ErrorCollector` - Contains all diagnostics emitted during parsing and resolution.
1029+
/// 3. `TempDir` - The temporary directory. It must be kept alive until the test completes.
1030+
///
1031+
/// # Panics
1032+
/// Panics if the `files` vector does not contain a `"main.simf"` entry, or if writing
1033+
/// the mock files to the OS filesystem fails.
1034+
fn setup_graph_raw(
1035+
files: Vec<(&str, &str)>,
1036+
) -> (Option<ProjectGraph>, ErrorCollector, TempDir) {
9761037
let temp_dir = TempDir::new().unwrap();
9771038

9781039
// 1. Create Files
@@ -986,20 +1047,55 @@ pub(crate) mod tests {
9861047
let root_p = root_path.expect("Tests must define 'main.simf'");
9871048

9881049
// 2. Setup Libraries (Hardcoded "lib" -> "libs/lib" for simplicity in tests)
989-
let mut lib_map = HashMap::new();
990-
lib_map.insert("lib".to_string(), temp_dir.path().join("libs/lib"));
1050+
let mut dependency_map = DependencyMap::new();
1051+
dependency_map.test_insert_without_canonicalize(
1052+
temp_dir.path(), // The root of mock project
1053+
"lib".to_string(),
1054+
&temp_dir.path().join("libs/lib"),
1055+
);
9911056

9921057
// 3. Parse & Build
9931058
let (root_program, source) = parse_root(&root_p);
994-
9951059
let mut handler = ErrorCollector::new();
9961060

997-
let graph = ProjectGraph::new(source, Arc::from(lib_map), &root_program, &mut handler)
998-
.expect(
999-
"setup_graph expects a valid graph construction. Use manual setup for error tests.",
1000-
);
1061+
let result = ProjectGraph::new(
1062+
source,
1063+
Arc::from(dependency_map),
1064+
&root_program,
1065+
&mut handler,
1066+
);
1067+
1068+
// Return the raw result and the handler so the test can inspect the errors
1069+
(result, handler, temp_dir)
1070+
}
10011071

1002-
// 4. Create Lookup (File Name -> ID) for easier asserting
1072+
/// Bootstraps a mock file system and constructs a valid `ProjectGraph`.
1073+
///
1074+
/// This is the standard test helper for "happy path" scenarios. It wraps [`setup_graph_raw`]
1075+
/// and mathematically guarantees that the graph construction succeeds. It also generates a
1076+
/// convenient filename-to-ID lookup map to make asserting on specific files easier.
1077+
///
1078+
/// # Arguments
1079+
/// * `files` - A vector of tuples containing `(file_path, file_content)`.
1080+
/// **Note:** One of the files *must* be named exactly `"main.simf"`.
1081+
///
1082+
/// # Returns
1083+
/// A tuple containing:
1084+
/// 1. `ProjectGraph` - The fully constructed, valid dependency graph.
1085+
/// 2. `HashMap<String, usize>` - A mapping of simple filenames (e.g., `"math.simf"`) to their node IDs.
1086+
/// 3. `TempDir` - The temporary directory. It must be kept alive until the test completes.
1087+
///
1088+
/// # Panics
1089+
/// Panics if the compiler encounters any errors during parsing or resolution,
1090+
/// or if `"main.simf"` is missing. For testing compiler errors, use [`setup_graph_raw`] instead.
1091+
fn setup_graph(files: Vec<(&str, &str)>) -> (ProjectGraph, HashMap<String, usize>, TempDir) {
1092+
let (graph_result, _handler, temp_dir) = setup_graph_raw(files);
1093+
1094+
let graph = graph_result.expect(
1095+
"setup_graph expects a valid graph construction. Use manual setup for error tests.",
1096+
);
1097+
1098+
// Create Lookup (File Name -> ID) for easier asserting
10031099
let mut file_ids = HashMap::new();
10041100
for (source_name, id) in &graph.lookup {
10051101
let simple_name = match source_name {
@@ -1300,37 +1396,7 @@ pub(crate) mod tests {
13001396
assert!(graph.dependencies[&ids["main"]].is_empty());
13011397
}
13021398

1303-
#[test]
1304-
fn test_missing_file_error() {
1305-
// MANUAL SETUP REQUIRED
1306-
// We cannot use `setup_graph` here because we expect `ProjectGraph::new` to fail/return None.
1307-
1308-
let temp_dir = TempDir::new().unwrap();
1309-
let root_path = create_simf_file(temp_dir.path(), "main.simf", "use lib::ghost::Phantom;");
1310-
// We purposefully DO NOT create ghost.simf
1311-
1312-
let mut lib_map = HashMap::new();
1313-
lib_map.insert("lib".to_string(), temp_dir.path().join("libs/lib"));
1314-
1315-
let (root_program, root_source) = parse_root(&root_path);
1316-
let mut handler = ErrorCollector::new();
1317-
1318-
let result =
1319-
ProjectGraph::new(root_source, Arc::from(lib_map), &root_program, &mut handler);
1320-
1321-
assert!(result.is_none(), "Graph construction should fail");
1322-
assert!(!handler.get().is_empty());
1323-
1324-
let error_msg = handler.to_string();
1325-
assert!(
1326-
error_msg.contains("File not found") || error_msg.contains("ghost.simf"),
1327-
"Error message should mention 'ghost.simf' or 'File not found'. Got: {}",
1328-
error_msg
1329-
);
1330-
}
1331-
13321399
// Tests for aliases
1333-
// TODO: @LesterEvSe, @Sdoba16 add more tests for alias
13341400
#[test]
13351401
fn test_renaming_with_use() {
13361402
// Scenario: Renaming imports.
@@ -1555,27 +1621,29 @@ Try reordering your `use` statements to avoid cross-wiring."#
15551621
}
15561622

15571623
// --- Dependent File Error Display Tests ---
1558-
15591624
#[test]
1560-
#[ignore = "TODO(Error_Formatting): The compiler currently strips the .simf extension from file paths during graph construction. This test expects the extension to be preserved."]
1561-
fn test_display_error_in_imported_dependency() {
1562-
let temp_dir = TempDir::new().unwrap();
1563-
let root_path = create_simf_file(temp_dir.path(), "main.simf", "use lib::math::add;");
1564-
1565-
create_simf_file(
1566-
temp_dir.path(),
1567-
"libs/lib/math.simf",
1568-
"pub fn add(a: u32 b: u32) {}",
1569-
);
1625+
fn test_missing_file_error() {
1626+
let (result, handler, _dir) =
1627+
setup_graph_raw(vec![("main.simf", "use lib::ghost::Phantom;")]);
15701628

1571-
let mut lib_map = HashMap::new();
1572-
lib_map.insert("lib".to_string(), temp_dir.path().join("libs/lib"));
1629+
assert!(result.is_none(), "Graph construction should fail");
1630+
assert!(handler.has_errors());
15731631

1574-
let (root_program, root_source) = parse_root(&root_path);
1575-
let mut handler = ErrorCollector::new();
1632+
let error_msg = handler.to_string();
1633+
assert!(
1634+
error_msg.contains("File not found") || error_msg.contains("ghost.simf"),
1635+
"Error message should mention 'ghost.simf' or 'File not found'. Got: {}",
1636+
error_msg
1637+
);
1638+
}
15761639

1577-
let result =
1578-
ProjectGraph::new(root_source, Arc::from(lib_map), &root_program, &mut handler);
1640+
#[test]
1641+
#[ignore = "TODO(Error_Formatting): The compiler currently strips the .simf extension from file paths during graph construction. This test expects the extension to be preserved."]
1642+
fn test_display_error_in_imported_dependency() {
1643+
let (result, handler, _dir) = setup_graph_raw(vec![
1644+
("main.simf", "use lib::math::add;"),
1645+
("libs/lib/math.simf", "pub fn add(a: u32 b: u32) {}"), // NOTE: The comma is missing on purpose.
1646+
]);
15791647

15801648
assert!(
15811649
result.is_none(),

src/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ pub enum Error {
427427
ListBoundPow2(usize),
428428
BitStringPow2(usize),
429429
CannotParse(String),
430+
Resolution(String),
430431
Grammar(String),
431432
Syntax {
432433
expected: Vec<String>,
@@ -496,6 +497,10 @@ impl fmt::Display for Error {
496497
f,
497498
"Cannot parse: {description}"
498499
),
500+
Error::Resolution(description) => write!(
501+
f,
502+
"Resolution error: {description}"
503+
),
499504
Error::Grammar(description) => write!(
500505
f,
501506
"Grammar error: {description}"

0 commit comments

Comments
 (0)