machine_check_machine/
lib.rs1#![doc = include_str!("../README.md")]
2
3use std::path::PathBuf;
4
5use proc_macro2::{Ident, Span};
6use quote::quote;
7use support::error_list::ErrorList;
8use syn::spanned::Spanned;
9use syn::visit_mut::{self, VisitMut};
10use syn::{parse_quote, Attribute, Item, ItemFn, ItemMod, Meta, MetaList, PathSegment};
11use syn_path::path;
12use wir::IntoSyn;
13
14use crate::util::create_item_mod;
15use crate::wir::WSpan;
16
17mod abstr;
18mod concr;
19mod description;
20mod refin;
21mod support;
22mod util;
23mod wir;
24
25pub use support::machine_error::{Error, ErrorType};
26
27pub type Errors = ErrorList<Error>;
28
29#[derive(Clone)]
30struct Description {
31 pub items: Vec<Item>,
32}
33
34pub fn process_file(mut file: syn::File) -> Result<syn::File, Errors> {
35 process_items(&mut file.items)?;
36 Ok(file)
37}
38
39pub fn process_module(mut module: ItemMod) -> Result<ItemMod, Errors> {
40 let Some((_, items)) = &mut module.content else {
41 return Err(Errors::single(Error::new(
42 ErrorType::ModuleWithoutContent,
43 WSpan::from_syn(&module),
44 )));
45 };
46 process_items(items)?;
47 Ok(module)
48}
49
50pub fn default_main() -> Item {
51 let main_fn: ItemFn = parse_quote!(
52 fn main() {
53 ::machine_check_exec::run::<refin::Input, refin::State, refin::Machine>()
54 }
55 );
56 Item::Fn(main_fn)
57}
58
59fn out_dir() -> Option<PathBuf> {
60 let mut args = std::env::args();
61
62 let mut out_dir = None;
63 while let Some(arg) = args.next() {
64 if arg == "--out-dir" {
65 out_dir = args.next();
66 }
67 }
68 let out_dir = out_dir?;
69 let mut out_dir = PathBuf::from(out_dir);
71 while !out_dir.ends_with("target") {
72 if !out_dir.pop() {
73 return None;
74 }
75 }
76
77 Some(out_dir)
78}
79
80#[allow(dead_code)]
81fn unparse(items: Vec<Item>) -> String {
82 prettyplease::unparse(&syn::File {
83 shebang: None,
84 attrs: vec![],
85 items,
86 })
87}
88
89fn process_items(items: &mut Vec<Item>) -> Result<(), Errors> {
90 let out_dir: Option<PathBuf> = if cfg!(feature = "write_machine") {
91 out_dir()
92 } else {
93 None
94 };
95
96 let (description, panic_messages) = description::create_description(items.clone())?;
97
98 if let Some(out_dir) = &out_dir {
99 std::fs::write(
100 out_dir.join("description.rs"),
101 unparse(wir::IntoSyn::into_syn(description.clone()).items),
102 )
103 .expect("SSA machine file should be writable");
104 }
105
106 let (abstract_description, misc_abstract_items) =
107 abstr::create_abstract_description(description);
108
109 if let Some(out_dir) = &out_dir {
110 std::fs::write(
111 out_dir.join("description_abstr.rs"),
112 unparse(wir::IntoSyn::into_syn(abstract_description.clone()).items),
113 )
114 .expect("Abstract machine file should be writable");
115 }
116
117 let (refinement_description, misc_refinement_items) =
118 refin::create_refinement_description(&abstract_description);
119
120 let mut refinement_description = Description {
121 items: refinement_description.into_syn().items,
122 };
123
124 refinement_description.items.extend(misc_refinement_items);
125
126 let refinement_module = create_machine_module("__mck_mod_refin", refinement_description);
128
129 let mut abstract_description = Description {
130 items: abstract_description.into_syn().items,
131 };
132
133 abstract_description.items.extend(misc_abstract_items);
134 abstract_description.items.push(refinement_module);
135
136 support::strip_machine::strip_machine(&mut abstract_description)?;
137
138 concr::process_items(items, &panic_messages)?;
139
140 let abstract_module = create_machine_module("__mck_mod_abstr", abstract_description);
141 items.push(abstract_module);
142
143 redirect_mck(items)?;
144
145 if let Some(out_dir) = &out_dir {
146 std::fs::write(out_dir.join("description_full.rs"), unparse(items.clone()))
147 .expect("Full machine file should be writable");
148 }
149
150 Ok(())
151}
152
153fn redirect_mck(items: &mut [Item]) -> Result<(), Error> {
154 let mut redirect_visitor = RedirectVisitor;
155 for item in items.iter_mut() {
156 redirect_visitor.visit_item_mut(item);
157 }
158
159 Ok(())
160}
161
162struct RedirectVisitor;
163
164impl VisitMut for RedirectVisitor {
165 fn visit_path_mut(&mut self, path: &mut syn::Path) {
166 if path.leading_colon.is_some() {
167 let first_segment = path
168 .segments
169 .first()
170 .expect("Path should have first segment");
171 if first_segment.ident == "mck" {
172 let span = first_segment.span();
173 path.segments.insert(
175 0,
176 PathSegment {
177 ident: Ident::new("machine_check", span),
178 arguments: syn::PathArguments::None,
179 },
180 );
181 }
182 }
183 visit_mut::visit_path_mut(self, path);
184 }
185}
186
187fn create_machine_module(name: &str, machine: Description) -> Item {
188 let mut module = create_item_mod(
189 syn::Visibility::Public(Default::default()),
190 Ident::new(name, Span::call_site()),
191 machine.items,
192 );
193 module.attrs.push(Attribute {
196 pound_token: Default::default(),
197 style: syn::AttrStyle::Outer,
198 bracket_token: Default::default(),
199 meta: Meta::List(MetaList {
200 path: path!(allow),
201 delimiter: syn::MacroDelimiter::Paren(Default::default()),
202 tokens: quote!(
203 non_snake_case, clippy::all, ),
206 }),
207 });
208
209 Item::Mod(module)
210}