Skip to content

Commit 8baf996

Browse files
authored
Multi file support + Dependency graph ordering (#103)
* Multi-file support For generating larger projects it may be desirable to generate into different files. Now --input can take a directory. If it is given a directory it will read all .cddl files and generate each in its own .rs file for export. Serialization/*Encodings are still all in one file. * Multi-file fix for cddl lib parsing errors cddl doesn't support parsing incomplete cddl strings so will error on some inputs. This works around it by merging it into 1 file with scope markers then using those instead to mark scope. * Dependency graph ordering of rules Fixes #93 This is extra important with multi file support now, as it can be impossible in some cases to order things properly when the circular dependencies were across multiple files. Now `babbage.cddl` processes totally fine even when separated across many files. This commit also includes some fixes for multi file support with inter-file dependencies (e.g. fixes for visibility/module use/etc). * removing useless code in intermediate form creation
1 parent c7132d1 commit 8baf996

File tree

6 files changed

+508
-234
lines changed

6 files changed

+508
-234
lines changed

src/cli.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use once_cell::sync::Lazy;
66
#[derive(Debug, Parser)]
77
#[clap()]
88
pub struct Cli {
9-
/// Input .cddl file to generate from.
10-
#[clap(short, long, parse(from_os_str), value_name = "INPUT_FILE")]
9+
/// Input .cddl file to generate from. If this is a directory then it will read all *.cddl files and generate one output for each.
10+
#[clap(short, long, parse(from_os_str), value_name = "INPUT_FILE/INPUT_DIR")]
1111
pub input: std::path::PathBuf,
1212

1313
/// Output directory for the generated code.

src/dep_graph.rs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
use std::collections::{BTreeMap, BTreeSet};
2+
3+
use cddl::ast::*;
4+
5+
pub fn topological_rule_order<'a>(rules: &'a Vec<&'a Rule<'a>>) -> Vec<&'a Rule<'a>> {
6+
let mut adj_list = BTreeMap::new();
7+
for cddl_rule in rules.iter() {
8+
let (ident, refs) = find_references(cddl_rule);
9+
adj_list.insert(ident.ident, (*cddl_rule, refs));
10+
}
11+
let mut unvisited = adj_list.iter().map(|(k, _v)| *k).collect::<BTreeSet<&str>>();
12+
let mut topo_order = Vec::new();
13+
let mut processing: BTreeSet<&'a str> = BTreeSet::new();
14+
while let Some(u) = unvisited.iter().next().map(|u| *u) {
15+
dfs_visit(&mut topo_order, &mut unvisited, &mut processing, &adj_list, u);
16+
}
17+
topo_order
18+
}
19+
20+
fn dfs_visit<'a>(
21+
topo_order: &mut Vec<&'a Rule<'a>>,
22+
unvisited: &mut BTreeSet<&'a str>,
23+
processing: &mut BTreeSet<&'a str>,
24+
adj_list: &BTreeMap<&'a str, (&'a cddl::ast::Rule<'a>, Vec<&'a Identifier<'a>>)>,
25+
u: &'a str) {
26+
processing.insert(u);
27+
let (rule, neighbors) = adj_list.get(u).unwrap();
28+
for v in neighbors.iter() {
29+
if processing.contains(v.ident) {
30+
eprintln!("Recursive type: '{}' / '{}' - code will possibly need to be edited by hand to use Box/etc", u, v);
31+
continue;
32+
}
33+
if unvisited.contains(v.ident) {
34+
dfs_visit(topo_order, unvisited, processing, adj_list, v.ident);
35+
}
36+
}
37+
processing.remove(u);
38+
unvisited.remove(u);
39+
topo_order.push(rule);
40+
}
41+
42+
fn find_references<'a>(cddl_rule: &'a Rule<'a>) -> (&'a Identifier, Vec<&'a Identifier<'a>>) {
43+
let mut refs = Vec::new();
44+
let ident = match cddl_rule {
45+
Rule::Type{ rule, .. } => {
46+
rule.value.type_choices.iter().for_each(|tc| find_refs_type1(&mut refs, &tc.type1));
47+
&rule.name
48+
},
49+
Rule::Group{ rule, .. } => {
50+
assert_eq!(rule.generic_params, None, "{}: Generics not supported on plain groups", rule.name);
51+
match &rule.entry {
52+
cddl::ast::GroupEntry::InlineGroup{ group, .. } => find_refs_group(&mut refs, group),
53+
x => panic!("Group rule with non-inline group? {:?}", x),
54+
}
55+
&rule.name
56+
},
57+
};
58+
(ident, refs)
59+
}
60+
61+
fn find_refs_type1<'a>(refs: &mut Vec<&'a Identifier<'a>>, type1: &'a Type1<'a>) {
62+
match &type1.type2 {
63+
Type2::Typename{ ident, generic_args, .. } => {
64+
refs.push(ident);
65+
find_refs_generic_args(refs, generic_args);
66+
},
67+
Type2::ParenthesizedType{ pt, .. } => pt.type_choices.iter().for_each(|tc| find_refs_type1(refs, &tc.type1)),
68+
Type2::Map{ group, .. } => find_refs_group(refs, group),
69+
Type2::Array{ group, .. } => find_refs_group(refs, group),
70+
Type2::Unwrap{ ident, generic_args, .. } => {
71+
refs.push(ident);
72+
find_refs_generic_args(refs, generic_args);
73+
},
74+
Type2::ChoiceFromInlineGroup{ group, .. } => find_refs_group(refs, group),
75+
Type2::ChoiceFromGroup{ ident, generic_args, .. } => {
76+
refs.push(ident);
77+
find_refs_generic_args(refs, generic_args);
78+
},
79+
Type2::TaggedData{ t, .. } => t.type_choices.iter().for_each(|tc| find_refs_type1(refs, &tc.type1)),
80+
_ => (),
81+
}
82+
}
83+
84+
fn find_refs_group<'a>(refs: &mut Vec<&'a Identifier<'a>>, group: &'a Group<'a>) {
85+
for group_choice in group.group_choices.iter() {
86+
for (group_entry, _) in group_choice.group_entries.iter() {
87+
match group_entry {
88+
GroupEntry::InlineGroup{ group, .. } => find_refs_group(refs, group),
89+
GroupEntry::TypeGroupname{ ge, .. } => {
90+
refs.push(&ge.name);
91+
find_refs_generic_args(refs, &ge.generic_args);
92+
},
93+
GroupEntry::ValueMemberKey{ ge, .. } => {
94+
ge.entry_type.type_choices.iter().for_each(|tc| find_refs_type1(refs, &tc.type1));
95+
match &ge.member_key {
96+
Some(MemberKey::Type1{ t1, .. }) => find_refs_type1(refs, t1),
97+
Some(MemberKey::NonMemberKey{ .. }) => unimplemented!("Please open a github issue with repro steps"),
98+
_ => (),
99+
}
100+
},
101+
}
102+
}
103+
}
104+
}
105+
106+
fn find_refs_generic_args<'a>(refs: &mut Vec<&'a Identifier<'a>>, generic_arg: &'a Option<GenericArgs<'a>>) {
107+
if let Some(arg) = generic_arg {
108+
arg.args.iter().for_each(|arg| find_refs_type1(refs, arg.arg.as_ref()));
109+
}
110+
}

0 commit comments

Comments
 (0)