Skip to content

Commit 794325d

Browse files
committed
feat: add build_order function
1 parent c4a78a9 commit 794325d

File tree

7 files changed

+168
-30
lines changed

7 files changed

+168
-30
lines changed

examples/modules/main.simf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pub use temp::get_five::get_five;
1+
pub use temp::funcs::get_five;
22

33
fn seven() -> u32 {
44
7

src/ast.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use simplicity::jet::Elements;
1111
use crate::debug::{CallTracker, DebugSymbols, TrackedCallName};
1212
use crate::error::{Error, RichError, Span, WithSpan};
1313
use crate::num::{NonZeroPow2Usize, Pow2Usize};
14-
use crate::parse::{MatchPattern, UseDecl};
14+
use crate::parse::{MatchPattern, UseDecl, Visibility};
1515
use crate::pattern::Pattern;
1616
use crate::str::{AliasName, FunctionName, Identifier, ModuleName, WitnessName};
1717
use crate::types::{
@@ -307,6 +307,7 @@ pub enum CallName {
307307
/// Definition of a custom function.
308308
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
309309
pub struct CustomFunction {
310+
visibility: Visibility,
310311
params: Arc<[FunctionParam]>,
311312
body: Arc<Expression>,
312313
}
@@ -780,6 +781,7 @@ impl AbstractSyntaxTree for Function {
780781
assert!(scope.is_topmost(), "Items live in the topmost scope only");
781782

782783
if from.name().as_inner() != "main" {
784+
let visibility = from.visibility().clone();
783785
let params = from
784786
.params()
785787
.iter()
@@ -803,7 +805,11 @@ impl AbstractSyntaxTree for Function {
803805
let body = Expression::analyze(from.body(), &ret, scope).map(Arc::new)?;
804806
scope.pop_scope();
805807
debug_assert!(scope.is_topmost());
806-
let function = CustomFunction { params, body };
808+
let function = CustomFunction {
809+
visibility,
810+
params,
811+
body,
812+
};
807813
scope
808814
.insert_function(from.name().clone(), function)
809815
.with_span(from)?;

src/driver.rs

Lines changed: 136 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@ use std::collections::{HashMap, VecDeque};
22
use std::path::{Path, PathBuf};
33
use std::sync::Arc;
44

5-
use crate::error::ErrorCollector;
6-
use crate::parse::{self, Item, ParseFromStrWithErrors, Program, UseDecl};
5+
use crate::error::{ErrorCollector, Span};
6+
use crate::parse::{self, ParseFromStrWithErrors, UseDecl, Visibility};
7+
use crate::str::Identifier;
78
use crate::LibConfig;
89

910
/// Graph Node: One file = One module
1011
#[derive(Debug, Clone)]
11-
pub struct Module {
12+
struct Module {
1213
/// Parsed AST (your `parse::Program`)
1314
/// Using Option to first create the node, then add the AST
14-
pub parsed_program: Program,
15+
pub parsed_program: parse::Program,
1516
}
1617

1718
/// The Dependency Graph itself
@@ -22,11 +23,24 @@ pub struct ProjectGraph {
2223
/// Fast lookup: Path -> ID
2324
/// Solves the duplicate problem (so as not to parse a.simf twice)
2425
pub lookup: HashMap<PathBuf, usize>,
26+
pub paths: Vec<PathBuf>,
2527

2628
/// Adjacency list: Who depends on whom
2729
pub dependencies: HashMap<usize, Vec<usize>>,
2830
}
2931

32+
#[derive(Clone, Debug)]
33+
pub struct Resolution {
34+
pub visibility: Visibility,
35+
}
36+
37+
pub struct Program {
38+
//pub graph: ProjectGraph,
39+
pub items: Arc<[parse::Item]>,
40+
pub scope_items: Vec<HashMap<Identifier, Resolution>>,
41+
pub span: Span,
42+
}
43+
3044
#[derive(Debug)]
3145
pub enum C3Error {
3246
CycleDetected(Vec<usize>),
@@ -66,11 +80,12 @@ fn parse_and_get_program(prog_file: &Path) -> Result<parse::Program, String> {
6680
}
6781

6882
impl ProjectGraph {
69-
pub fn new(lib_cfg: &LibConfig, root_program: &Program) -> Result<Self, String> {
83+
pub fn new(lib_cfg: &LibConfig, root_program: &parse::Program) -> Result<Self, String> {
7084
let mut modules: Vec<Module> = vec![Module {
7185
parsed_program: root_program.clone(),
7286
}];
7387
let mut lookup: HashMap<PathBuf, usize> = HashMap::new();
88+
let mut paths: Vec<PathBuf> = vec![lib_cfg.root_path.clone()];
7489
let mut dependencies: HashMap<usize, Vec<usize>> = HashMap::new();
7590

7691
let root_id = 0;
@@ -86,7 +101,7 @@ impl ProjectGraph {
86101
let current_program = &modules[curr_id].parsed_program;
87102

88103
for elem in current_program.items() {
89-
if let Item::Use(use_decl) = elem {
104+
if let parse::Item::Use(use_decl) = elem {
90105
if let Ok(path) = get_full_path(&lib_cfg.libraries, use_decl) {
91106
pending_imports.push(path);
92107
}
@@ -112,6 +127,7 @@ impl ProjectGraph {
112127
parsed_program: program,
113128
});
114129
lookup.insert(path.clone(), last_ind);
130+
paths.push(path.clone());
115131
dependencies.entry(curr_id).or_default().push(last_ind);
116132

117133
queue.push_back(last_ind);
@@ -121,6 +137,7 @@ impl ProjectGraph {
121137
Ok(Self {
122138
modules,
123139
lookup,
140+
paths,
124141
dependencies,
125142
})
126143
}
@@ -148,9 +165,7 @@ impl ProjectGraph {
148165

149166
if visiting.contains(&module) {
150167
let cycle_start = visiting.iter().position(|m| *m == module).unwrap();
151-
return Err(C3Error::CycleDetected(
152-
visiting[cycle_start..].to_vec(),
153-
));
168+
return Err(C3Error::CycleDetected(visiting[cycle_start..].to_vec()));
154169
}
155170

156171
visiting.push(module);
@@ -167,8 +182,7 @@ impl ProjectGraph {
167182
seqs.push(parents.clone());
168183

169184
let mut result = vec![module];
170-
let merged = merge(seqs)
171-
.ok_or(C3Error::InconsistentLinearization { module })?;
185+
let merged = merge(seqs).ok_or(C3Error::InconsistentLinearization { module })?;
172186

173187
result.extend(merged);
174188

@@ -177,6 +191,113 @@ impl ProjectGraph {
177191

178192
Ok(result)
179193
}
194+
195+
// TODO: @Sdoba16 to implement
196+
fn build_ordering(&self) {}
197+
198+
fn process_use_item(
199+
scope_items: &mut [HashMap<Identifier, Resolution>],
200+
file_id: usize,
201+
ind: usize,
202+
elem: &Identifier,
203+
use_decl_visibility: Visibility,
204+
) -> Result<(), String> {
205+
if matches!(
206+
scope_items[ind][elem].visibility,
207+
parse::Visibility::Private
208+
) {
209+
return Err(format!(
210+
"Function {} is private and cannot be used.",
211+
elem.as_inner()
212+
));
213+
}
214+
215+
scope_items[file_id].insert(
216+
elem.clone(),
217+
Resolution {
218+
visibility: use_decl_visibility,
219+
},
220+
);
221+
222+
Ok(())
223+
}
224+
225+
// TODO: Change. Consider processing more than one errro at a time
226+
fn build_program(&self, order: &Vec<usize>) -> Result<Program, String> {
227+
let mut items: Vec<parse::Item> = Vec::new();
228+
let mut scope_items: Vec<HashMap<Identifier, Resolution>> = Vec::new();
229+
230+
for file_id in order {
231+
scope_items.push(HashMap::new());
232+
233+
for elem in self.modules[*file_id].parsed_program.items() {
234+
match elem {
235+
// If the function is private, we don't add it to the current scope.
236+
// If the function is public, we add it as public if we have 'pub use', or as private if we only have 'use'.
237+
// We can do this because all Items in the Arc<[Item]> array will be defined in advance.
238+
parse::Item::Use(use_decl) => {
239+
let ind = self.lookup[&use_decl.path_buf()];
240+
241+
match use_decl.items() {
242+
parse::UseItems::Single(elem) => {
243+
ProjectGraph::process_use_item(
244+
&mut scope_items,
245+
*file_id,
246+
ind,
247+
elem,
248+
use_decl.visibility().clone(),
249+
)?;
250+
}
251+
parse::UseItems::List(elems) => {
252+
for elem in elems {
253+
ProjectGraph::process_use_item(
254+
&mut scope_items,
255+
*file_id,
256+
ind,
257+
elem,
258+
use_decl.visibility().clone(),
259+
)?;
260+
}
261+
}
262+
}
263+
}
264+
parse::Item::TypeAlias(alias) => {
265+
items.push(elem.clone());
266+
scope_items[*file_id].insert(
267+
Identifier::from(alias.name().clone()),
268+
Resolution {
269+
visibility: alias.visibility().clone(),
270+
},
271+
);
272+
}
273+
parse::Item::Function(function) => {
274+
items.push(elem.clone());
275+
scope_items[*file_id].insert(
276+
Identifier::from(function.name().clone()),
277+
Resolution {
278+
visibility: function.visibility().clone(),
279+
},
280+
);
281+
}
282+
parse::Item::Module => {}
283+
}
284+
}
285+
}
286+
287+
Ok(Program {
288+
items: items.into(),
289+
scope_items,
290+
span: self.modules[0].parsed_program.as_ref().clone(),
291+
})
292+
}
293+
294+
pub fn resolve_complication_order(&self) -> Result<Program, String> {
295+
// TODO: Resolve errors more appropriately
296+
let mut order = self.c3_linearize().unwrap();
297+
order.reverse();
298+
// self.build_ordering();
299+
self.build_program(&order)
300+
}
180301
}
181302

182303
fn merge(mut seqs: Vec<Vec<usize>>) -> Option<Vec<usize>> {
@@ -219,8 +340,7 @@ mod tests {
219340
use std::path::Path;
220341
use tempfile::TempDir;
221342

222-
// --- Helper to setup environment ---
223-
343+
// ProjectGraph::new tests
224344
// Creates a file with specific content in the temp directory
225345
fn create_simf_file(dir: &Path, rel_path: &str, content: &str) -> PathBuf {
226346
let full_path = dir.join(rel_path);
@@ -238,7 +358,7 @@ mod tests {
238358

239359
// Helper to mock the initial root program parsing
240360
// (Assuming your parser works via a helper function)
241-
fn parse_root(path: &Path) -> Program {
361+
fn parse_root(path: &Path) -> parse::Program {
242362
parse_and_get_program(path).expect("Root parsing failed")
243363
}
244364

@@ -273,10 +393,8 @@ mod tests {
273393

274394
#[test]
275395
fn test_c3_simple_import() {
276-
277396
let temp_dir = TempDir::new().unwrap();
278-
let root_path =
279-
create_simf_file(temp_dir.path(), "root.simf", "use std::math::some_func;");
397+
let root_path = create_simf_file(temp_dir.path(), "root.simf", "use std::math::some_func;");
280398
create_simf_file(temp_dir.path(), "libs/std/math.simf", "");
281399

282400
let mut lib_map = HashMap::new();
@@ -377,12 +495,9 @@ mod tests {
377495

378496
let order = graph.c3_linearize().expect("C3 failed");
379497

380-
assert_eq!(
381-
order, vec![0, 1, 2, 3],
382-
);
498+
assert_eq!(order, vec![0, 1, 2, 3],);
383499
}
384500

385-
386501
#[test]
387502
fn test_cyclic_dependency() {
388503
// Setup:
@@ -409,8 +524,6 @@ mod tests {
409524
let config = LibConfig::new(lib_map, &a_path);
410525
let graph = ProjectGraph::new(&config, &root_program).expect("Graph build failed");
411526

412-
println!("Graph dependencies: {:?}", graph.dependencies);
413-
println!("lookup: {:?}", graph.lookup);
414527
assert_eq!(graph.modules.len(), 2, "Should only have A and B");
415528

416529
// A depends on B
@@ -449,7 +562,6 @@ mod tests {
449562
matches!(order, C3Error::CycleDetected(_));
450563
}
451564

452-
453565
#[test]
454566
fn test_missing_file_error() {
455567
// Setup:
@@ -497,5 +609,4 @@ mod tests {
497609
"Root should have no resolved dependencies"
498610
);
499611
}
500-
501612
}

src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,12 @@ impl TemplateProgram {
7777
let parse_program = parse::Program::parse_from_str_with_errors(&file, &mut error_handler);
7878

7979
if let Some(program) = parse_program {
80-
let _ = if let Some(lib_cfg) = lib_cfg {
81-
Some(ProjectGraph::new(lib_cfg, &program)?)
80+
// TODO: Consider a proper resolution strategy later.
81+
let _: Option<driver::Program> = if let Some(lib_cfg) = lib_cfg {
82+
let graph = ProjectGraph::new(lib_cfg, &program)?;
83+
84+
// TODO: Perhaps add an `error_handler` here, too.
85+
Some(graph.resolve_complication_order()?)
8286
} else {
8387
None
8488
};

src/parse.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ impl UseDecl {
7979
&self.path
8080
}
8181

82+
pub fn path_buf(&self) -> std::path::PathBuf {
83+
self.path().iter().map(|s| s.as_ref()).collect()
84+
}
85+
8286
/// Access the visibility of the function.
8387
pub fn items(&self) -> &UseItems {
8488
&self.items

src/str.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,19 @@ impl<'a> arbitrary::Arbitrary<'a> for FunctionName {
115115
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
116116
pub struct Identifier(Arc<str>);
117117

118+
impl From<AliasName> for Identifier {
119+
fn from(alias: AliasName) -> Self {
120+
// We move the inner Arc, so this is cheap
121+
Self(alias.0)
122+
}
123+
}
124+
125+
impl From<FunctionName> for Identifier {
126+
fn from(func: FunctionName) -> Self {
127+
Self(func.0)
128+
}
129+
}
130+
118131
impl AsRef<str> for Identifier {
119132
fn as_ref(&self) -> &str {
120133
&self.0

0 commit comments

Comments
 (0)