1use crate::{Error, Warning};
2use aiken_lang::{
3 IdGenerator,
4 ast::{
5 DataType, DataTypeKey, Definition, Function, FunctionAccessKey, Located, ModuleKind,
6 Tracing, TypedDataType, TypedFunction, TypedModule, TypedValidator, UntypedModule,
7 Validator,
8 },
9 expr::TypedExpr,
10 line_numbers::LineNumbers,
11 parser::extra::{Comment, ModuleExtra, comments_before},
12 tipo::TypeInfo,
13};
14use indexmap::IndexMap;
15use miette::NamedSource;
16use petgraph::{Direction, Graph, algo, graph::NodeIndex};
17use std::{
18 collections::{BTreeSet, HashMap},
19 io,
20 ops::{Deref, DerefMut},
21 path::PathBuf,
22};
23
24#[derive(Debug)]
25pub struct ParsedModule {
26 pub path: PathBuf,
27 pub name: String,
28 pub code: String,
29 pub kind: ModuleKind,
30 pub package: String,
31 pub ast: UntypedModule,
32 pub extra: ModuleExtra,
33}
34
35impl ParsedModule {
36 pub fn deps_for_graph(&self, env_modules: &[String]) -> (String, Vec<String>) {
37 let name = self.name.clone();
38 let deps: Vec<_> = self.ast.dependencies(env_modules);
39 (name, deps)
40 }
41
42 #[allow(clippy::too_many_arguments)]
43 #[allow(clippy::result_large_err)]
44 pub fn infer(
45 self,
46 id_gen: &IdGenerator,
47 package: &str,
48 tracing: Tracing,
49 env: Option<&str>,
50 validate_module_name: bool,
51 module_sources: &mut HashMap<String, (String, LineNumbers)>,
52 module_types: &mut HashMap<String, TypeInfo>,
53 functions: &mut IndexMap<FunctionAccessKey, TypedFunction>,
54 constants: &mut IndexMap<FunctionAccessKey, TypedExpr>,
55 data_types: &mut IndexMap<DataTypeKey, TypedDataType>,
56 ) -> Result<(CheckedModule, Vec<Warning>), Error> {
57 let mut warnings = Vec::new();
58
59 let ast = self
60 .ast
61 .infer(
62 id_gen,
63 self.kind,
64 package,
65 module_types,
66 tracing,
67 &mut warnings,
68 env,
69 )
70 .map_err(|error| Error::Type {
71 path: self.path.clone(),
72 src: self.code.clone(),
73 named: NamedSource::new(self.path.display().to_string(), self.code.clone()),
74 error: error.into(),
75 })?;
76
77 let warnings = warnings
78 .into_iter()
79 .map(|w| Warning::from_type_warning(w, self.path.clone(), self.code.clone()))
80 .collect::<Vec<_>>();
81
82 if validate_module_name {
84 ast.validate_module_name()?;
85 }
86
87 module_sources.insert(
89 self.name.clone(),
90 (self.code.clone(), LineNumbers::new(&self.code)),
91 );
92
93 module_types.insert(self.name.clone(), ast.type_info.clone());
96
97 ast.register_definitions(functions, constants, data_types);
99
100 Ok((
101 CheckedModule {
102 ast,
103 kind: self.kind,
104 extra: self.extra,
105 name: self.name,
106 code: self.code,
107 package: self.package,
108 input_path: self.path,
109 },
110 warnings,
111 ))
112 }
113}
114
115pub struct ParsedModules(HashMap<String, ParsedModule>);
116
117impl ParsedModules {
118 pub fn new() -> Self {
119 Self(HashMap::new())
120 }
121
122 #[allow(clippy::result_large_err)]
123 pub fn sequence(&self, our_modules: &BTreeSet<String>) -> Result<Vec<String>, Error> {
124 let env_modules = self
125 .0
126 .values()
127 .filter_map(|m| match m.kind {
128 ModuleKind::Env => Some(m.name.clone()),
129 ModuleKind::Lib | ModuleKind::Validator | ModuleKind::Config => None,
130 })
131 .collect::<Vec<String>>();
132
133 let inputs = self
134 .0
135 .values()
136 .map(|m| m.deps_for_graph(&env_modules))
137 .collect::<Vec<(String, Vec<String>)>>();
138
139 let capacity = inputs.len();
140
141 let mut graph = Graph::<String, ()>::with_capacity(capacity, capacity * 5);
142
143 let mut indices = HashMap::with_capacity(capacity);
144
145 let mut our_indices = BTreeSet::new();
146
147 for (value, _) in &inputs {
148 let index = graph.add_node(value.to_string());
149 indices.insert(value.clone(), index);
150 if our_modules.contains(value) {
151 our_indices.insert(index);
152 }
153 }
154
155 for (value, deps) in inputs {
156 if let Some(from_index) = indices.get(&value) {
157 let deps = deps.into_iter().filter_map(|dep| indices.get(&dep));
158
159 for to_index in deps {
160 graph.add_edge(*from_index, *to_index, ());
161 }
162 }
163 }
164
165 let mut messed_up_indices = false;
166
167 graph.retain_nodes(|graph, ix| {
171 if messed_up_indices {
175 our_indices = BTreeSet::new();
176 for j in graph.node_indices() {
177 if our_modules.contains(graph[j].as_str()) {
178 our_indices.insert(j);
179 }
180 }
181 }
182
183 for start in our_indices.iter() {
184 if algo::astar(&*graph, *start, |end| end == ix, |_| 1, |_| 0).is_some() {
185 messed_up_indices = false;
186 return true;
187 }
188 }
189
190 messed_up_indices = true;
191 false
192 });
193
194 match algo::toposort(&graph, None) {
195 Ok(sequence) => {
196 let sequence = sequence
197 .iter()
198 .filter_map(|i| graph.node_weight(*i))
199 .rev()
200 .cloned()
201 .collect();
202
203 Ok(sequence)
204 }
205 Err(cycle) => {
206 let origin = cycle.node_id();
207
208 let mut path = vec![];
209
210 find_cycle(origin, origin, &graph, &mut path, &mut BTreeSet::new());
211
212 let modules = path
213 .iter()
214 .filter_map(|i| graph.node_weight(*i))
215 .cloned()
216 .collect();
217
218 Err(Error::ImportCycle { modules })
219 }
220 }
221 }
222}
223
224impl Default for ParsedModules {
225 fn default() -> Self {
226 Self::new()
227 }
228}
229
230impl From<HashMap<String, ParsedModule>> for ParsedModules {
231 fn from(parsed_modules: HashMap<String, ParsedModule>) -> Self {
232 ParsedModules(parsed_modules)
233 }
234}
235
236impl From<ParsedModules> for HashMap<String, ParsedModule> {
237 fn from(parsed_modules: ParsedModules) -> Self {
238 parsed_modules.0
239 }
240}
241
242impl Deref for ParsedModules {
243 type Target = HashMap<String, ParsedModule>;
244
245 fn deref(&self) -> &Self::Target {
246 &self.0
247 }
248}
249
250impl DerefMut for ParsedModules {
251 fn deref_mut(&mut self) -> &mut Self::Target {
252 &mut self.0
253 }
254}
255
256fn find_cycle<W>(
257 origin: NodeIndex,
258 parent: NodeIndex,
259 graph: &petgraph::Graph<W, ()>,
260 path: &mut Vec<NodeIndex>,
261 seen: &mut BTreeSet<NodeIndex>,
262) -> bool {
263 seen.insert(parent);
264
265 for node in graph.neighbors_directed(parent, Direction::Outgoing) {
266 if node == origin {
267 path.push(node);
268
269 return true;
270 }
271
272 if seen.contains(&node) {
273 continue;
274 }
275
276 if find_cycle(origin, node, graph, path, seen) {
277 path.push(node);
278
279 return true;
280 }
281 }
282
283 false
284}
285
286#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
287pub struct CheckedModule {
288 pub name: String,
289 pub code: String,
290 pub input_path: PathBuf,
291 pub kind: ModuleKind,
292 pub package: String,
293 pub ast: TypedModule,
294 pub extra: ModuleExtra,
295}
296
297impl CheckedModule {
298 pub fn skip_doc_generation(&self) -> bool {
299 self.ast
300 .docs
301 .first()
302 .map(|s| s.as_str().trim())
303 .unwrap_or_default()
304 == "@hidden"
305 }
306
307 pub fn to_cbor(&self) -> Vec<u8> {
308 let mut module_bytes = vec![];
309
310 ciborium::into_writer(&self, &mut module_bytes)
311 .expect("modules should not fail to serialize");
312
313 module_bytes
314 }
315
316 pub fn from_cbor(bytes: &[u8]) -> Result<Self, ciborium::de::Error<io::Error>> {
317 ciborium::from_reader(bytes)
318 }
319
320 pub fn to_cbor_hex(&self) -> (String, Vec<u8>) {
321 let module_bytes = self.to_cbor();
322 let hex_str = hex::encode(&module_bytes);
323
324 (hex_str, module_bytes)
325 }
326
327 pub fn find_node(&self, byte_index: usize) -> Option<Located<'_>> {
328 self.ast.find_node(byte_index)
329 }
330
331 pub fn attach_doc_and_module_comments(&mut self) {
332 self.ast.docs = self
334 .extra
335 .module_comments
336 .iter()
337 .map(|span| {
338 Comment::from((span, self.code.as_str()))
339 .content
340 .to_string()
341 })
342 .collect();
343
344 let mut definitions: Vec<_> = self.ast.definitions.iter_mut().collect();
346 definitions.sort_by(|a, b| a.location().start.cmp(&b.location().start));
347
348 let mut doc_comments = self.extra.doc_comments.iter().peekable();
350 for def in &mut definitions {
351 let docs: Vec<&str> =
352 comments_before(&mut doc_comments, def.location().start, &self.code);
353 if !docs.is_empty() {
354 let doc = docs.join("\n");
355 def.put_doc(doc);
356 }
357
358 match def {
359 Definition::DataType(DataType { constructors, .. }) => {
360 for constructor in constructors {
361 let docs: Vec<&str> = comments_before(
362 &mut doc_comments,
363 constructor.location.start,
364 &self.code,
365 );
366 if !docs.is_empty() {
367 let doc = docs.join("\n");
368 constructor.put_doc(doc);
369 }
370
371 for argument in constructor.arguments.iter_mut() {
372 let docs: Vec<&str> = comments_before(
373 &mut doc_comments,
374 argument.location.start,
375 &self.code,
376 );
377 if !docs.is_empty() {
378 let doc = docs.join("\n");
379 argument.put_doc(doc);
380 }
381 }
382 }
383 }
384 Definition::Fn(Function { arguments, .. }) => {
385 for argument in arguments {
386 let docs: Vec<&str> =
387 comments_before(&mut doc_comments, argument.location.start, &self.code);
388
389 if !docs.is_empty() {
390 let doc = docs.join("\n");
391 argument.put_doc(doc);
392 }
393 }
394 }
395 Definition::Validator(Validator {
396 params,
397 handlers,
398 fallback,
399 ..
400 }) => {
401 for param in params {
402 let docs: Vec<&str> =
403 comments_before(&mut doc_comments, param.location.start, &self.code);
404
405 if !docs.is_empty() {
406 let doc = docs.join("\n");
407 param.put_doc(doc);
408 }
409 }
410
411 for handler in handlers.iter_mut() {
412 for argument in handler.arguments.iter_mut() {
413 let docs: Vec<&str> = comments_before(
414 &mut doc_comments,
415 argument.location.start,
416 &self.code,
417 );
418
419 if !docs.is_empty() {
420 let doc = docs.join("\n");
421 argument.put_doc(doc);
422 }
423 }
424 }
425
426 for argument in fallback.arguments.iter_mut() {
427 let docs: Vec<&str> =
428 comments_before(&mut doc_comments, argument.location.start, &self.code);
429
430 if !docs.is_empty() {
431 let doc = docs.join("\n");
432 argument.put_doc(doc);
433 }
434 }
435 }
436 _ => (),
437 }
438 }
439 }
440}
441
442#[derive(Default, Debug, Clone)]
443pub struct CheckedModules(HashMap<String, CheckedModule>);
444
445impl From<HashMap<String, CheckedModule>> for CheckedModules {
446 fn from(checked_modules: HashMap<String, CheckedModule>) -> Self {
447 CheckedModules(checked_modules)
448 }
449}
450
451impl From<CheckedModules> for HashMap<String, CheckedModule> {
452 fn from(checked_modules: CheckedModules) -> Self {
453 checked_modules.0
454 }
455}
456
457impl<'a> From<&'a CheckedModules> for &'a HashMap<String, CheckedModule> {
458 fn from(checked_modules: &'a CheckedModules) -> Self {
459 &checked_modules.0
460 }
461}
462
463impl CheckedModules {
464 pub fn singleton(module: CheckedModule) -> Self {
465 let mut modules = Self::default();
466 modules.insert(module.name.clone(), module);
467 modules
468 }
469
470 pub fn validators(&self) -> impl Iterator<Item = (&CheckedModule, &TypedValidator)> {
472 let mut items = vec![];
473
474 for validator_module in self.0.values().filter(|module| module.kind.is_validator()) {
475 for some_definition in validator_module.ast.definitions() {
476 if let Definition::Validator(def) = some_definition {
477 items.push((validator_module, def));
478 }
479 }
480 }
481
482 items.sort_by(|left, right| {
483 (
484 left.0.package.to_string(),
485 left.0.name.to_string(),
486 left.1.name.to_string(),
487 )
488 .cmp(&(
489 right.0.package.to_string(),
490 right.0.name.to_string(),
491 right.1.name.to_string(),
492 ))
493 });
494
495 items.into_iter()
496 }
497
498 pub fn functions(&self) -> impl Iterator<Item = (&CheckedModule, &TypedFunction)> {
499 let mut items = vec![];
500
501 for module in self.0.values() {
502 for some_definition in module.ast.definitions() {
503 if let Definition::Fn(def) = some_definition {
504 items.push((module, def));
505 }
506 }
507 }
508
509 items.into_iter()
510 }
511
512 pub fn into_validators(self) -> impl Iterator<Item = CheckedModule> {
513 self.0
514 .into_values()
515 .filter(|module| module.kind.is_validator())
516 }
517}
518
519impl Deref for CheckedModules {
520 type Target = HashMap<String, CheckedModule>;
521
522 fn deref(&self) -> &Self::Target {
523 &self.0
524 }
525}
526
527impl DerefMut for CheckedModules {
528 fn deref_mut(&mut self) -> &mut Self::Target {
529 &mut self.0
530 }
531}