1use super::{
2 Definition, InterfaceDcl, ModuleDcl, ParserProperties, Specification, TypeDcl,
3 expand_annotations, include, interface_codegen, parse_xidlc_pragma,
4};
5use serde_json::Value;
6use std::fs;
7use std::path::{Path, PathBuf};
8
9impl From<crate::typed_ast::Specification> for Specification {
10 fn from(value: crate::typed_ast::Specification) -> Self {
11 spec_from_typed_ast(value, true)
12 }
13}
14
15impl Specification {
16 pub fn from_typed_ast_with_properties(
17 value: crate::typed_ast::Specification,
18 properties: ParserProperties,
19 ) -> Self {
20 spec_from_typed_ast(value, expand_interface(&properties))
21 }
22
23 pub fn from_typed_ast_with_properties_and_path(
24 value: crate::typed_ast::Specification,
25 properties: ParserProperties,
26 path: impl AsRef<Path>,
27 ) -> crate::error::ParserResult<Self> {
28 spec_from_typed_ast_with_path(value, expand_interface(&properties), path.as_ref())
29 }
30
31 pub fn from_typed_ast_with_path(
32 value: crate::typed_ast::Specification,
33 path: impl AsRef<Path>,
34 ) -> crate::error::ParserResult<Self> {
35 spec_from_typed_ast_with_path(value, true, path.as_ref())
36 }
37}
38
39pub(crate) fn spec_from_typed_ast(
40 value: crate::typed_ast::Specification,
41 expand_interfaces: bool,
42) -> Specification {
43 let mut definitions = Vec::new();
44 collect_defs_with_context(
45 value.0,
46 &mut Vec::new(),
47 expand_interfaces,
48 &mut definitions,
49 None,
50 None,
51 )
52 .expect("pathless HIR conversion should not fail");
53 Specification(definitions)
54}
55
56fn spec_from_typed_ast_with_path(
57 value: crate::typed_ast::Specification,
58 expand_interfaces: bool,
59 path: &Path,
60) -> crate::error::ParserResult<Specification> {
61 let root = include::normalize_path(path);
62 let mut definitions = Vec::new();
63 let mut include_stack = vec![root.clone()];
64 collect_defs_with_context(
65 value.0,
66 &mut Vec::new(),
67 expand_interfaces,
68 &mut definitions,
69 Some(root.as_path()),
70 Some(&mut include_stack),
71 )?;
72 Ok(Specification(definitions))
73}
74
75fn collect_defs_with_context(
76 defs: Vec<crate::typed_ast::Definition>,
77 modules: &mut Vec<String>,
78 expand_interfaces: bool,
79 out: &mut Vec<Definition>,
80 current_file: Option<&Path>,
81 mut include_stack: Option<&mut Vec<PathBuf>>,
82) -> crate::error::ParserResult<()> {
83 for def in defs {
84 match def {
85 crate::typed_ast::Definition::ModuleDcl(module) => {
86 let ident = module.ident.0;
87 let annotations = expand_annotations(module.annotations);
88 modules.push(ident.clone());
89 let mut inner = Vec::new();
90 collect_defs_with_context(
91 module.definition,
92 modules,
93 expand_interfaces,
94 &mut inner,
95 current_file,
96 include_stack.as_deref_mut(),
97 )?;
98 modules.pop();
99 out.push(Definition::ModuleDcl(ModuleDcl {
100 annotations,
101 ident,
102 definition: inner,
103 }));
104 }
105 crate::typed_ast::Definition::PreprocCall(call) => {
106 if let Some(pragma) = parse_xidlc_pragma(&call) {
107 out.push(Definition::Pragma(pragma));
108 }
109 }
110 crate::typed_ast::Definition::TypeDcl(value) => {
111 out.push(Definition::TypeDcl(TypeDcl::from(value)))
112 }
113 crate::typed_ast::Definition::ConstDcl(value) => {
114 out.push(Definition::ConstDcl(value.into()))
115 }
116 crate::typed_ast::Definition::ExceptDcl(value) => {
117 out.push(Definition::ExceptDcl(value.into()))
118 }
119 crate::typed_ast::Definition::InterfaceDcl(value) => {
120 let interface = InterfaceDcl::from(value);
121 if expand_interfaces {
122 let generated = interface_codegen::expand_interface(&interface, modules)
123 .unwrap_or_else(|err| panic!("interface expansion failed: {err}"));
124 out.extend(generated);
125 }
126 out.push(Definition::InterfaceDcl(interface));
127 }
128 crate::typed_ast::Definition::PreprocInclude(include_def) => {
129 let Some(current_file) = current_file else {
130 continue;
131 };
132 let path = include::resolve_include_path(current_file, &include_def)?;
133 let typed = parse_included_specification(&path)?;
134 let stack = include_stack
135 .as_deref_mut()
136 .expect("include stack must exist when current file path is set");
137 guard_include_cycle(stack, &path)?;
138 stack.push(path.clone());
139 collect_defs_with_context(
140 typed.0,
141 modules,
142 expand_interfaces,
143 out,
144 Some(path.as_path()),
145 Some(stack),
146 )?;
147 stack.pop();
148 }
149 crate::typed_ast::Definition::TemplateModuleDcl(_)
150 | crate::typed_ast::Definition::TemplateModuleInst(_)
151 | crate::typed_ast::Definition::PreprocDefine(_) => {}
152 }
153 }
154
155 Ok(())
156}
157
158fn expand_interface(properties: &ParserProperties) -> bool {
159 properties
160 .get("expand_interface")
161 .and_then(Value::as_bool)
162 .unwrap_or(true)
163}
164
165fn parse_included_specification(
166 path: &Path,
167) -> crate::error::ParserResult<crate::typed_ast::Specification> {
168 let source = fs::read_to_string(path).map_err(|err| {
169 crate::error::ParseError::Message(format!(
170 "failed to read include '{}': {err}",
171 path.display()
172 ))
173 })?;
174 crate::parser::parser_text(&source).map_err(|err| {
175 crate::error::ParseError::Message(format!(
176 "failed to parse include '{}': {err}",
177 path.display()
178 ))
179 })
180}
181
182fn guard_include_cycle(stack: &[PathBuf], path: &Path) -> crate::error::ParserResult<()> {
183 if stack.contains(&path.to_path_buf()) {
184 let chain = stack
185 .iter()
186 .chain(std::iter::once(&path.to_path_buf()))
187 .map(|path| path.display().to_string())
188 .collect::<Vec<_>>()
189 .join(" -> ");
190 return Err(crate::error::ParseError::Message(format!(
191 "cyclic include detected: {chain}"
192 )));
193 }
194 Ok(())
195}