rem_utils/
annotation.rs

1use std::collections::HashMap;
2use std::fs;
3use std::fs::OpenOptions;
4use std::io::Write;
5
6use syn::visit::Visit;
7use syn::{Expr, ExprPath, ItemFn, Path};
8
9use crate::labelling::ASTKey;
10use crate::labelling::Label;
11
12/// Annotations of an AST
13pub type Annotations<'a> = HashMap<&'a dyn ASTKey, Label>;
14
15/// A pair of an AST and its annotations
16pub type Annotated<'a, T> = (Annotations<'a>, T);
17
18pub const LOOKUP_FILE: &str = "/tmp/annotation_rev_lookup";
19
20/// Internal helper struct to annotate an AST
21struct ASTAnnotator<'a> {
22    annotations: Annotations<'a>,
23    next_label: Label,
24    env: crate::labelling::ScopedContext<syn::Ident, Label>,
25}
26
27impl<'a> ASTAnnotator<'a> {
28    pub fn init() -> Self {
29        let map = HashMap::new();
30        let label = Label::new();
31        let context = Default::default();
32        fs::write(LOOKUP_FILE, "").unwrap();
33        ASTAnnotator {
34            annotations: map,
35            next_label: label,
36            env: context,
37        }
38    }
39
40    // return annotations
41    pub fn annotations(self) -> HashMap<&'a dyn ASTKey, Label> {
42        self.annotations
43    }
44
45    fn add_binding(&mut self, var: &'a syn::Ident, value: Label) {
46        self.env.add_binding(var.clone(), value)
47    }
48
49   // check if Ident, if not look up expr in AST map
50    #[allow(dead_code)]
51    pub fn lookup_expr(&mut self, expr: &'a syn::Expr) -> Option<Label> {
52        if let Expr::Path(syn::ExprPath {
53            path: syn::Path { segments, .. },
54            ..
55        }) = expr
56        {
57            let ident = &segments.last().unwrap().ident;
58            self.lookup(ident)
59        } else {
60            self.lookup_ast(expr)
61        }
62    }
63
64    #[allow(dead_code)]
65    pub fn lookup_ast<'b>(&self, ident: &'b dyn ASTKey) -> Option<Label> {
66        self.annotations.get(&ident).map(|v| *v)
67    }
68
69    fn lookup(&mut self, ident: &'a syn::Ident) -> Option<Label> {
70        self.env.lookup(ident)
71    }
72
73    pub fn open_scope(&mut self) {
74        self.env.open_scope()
75    }
76
77    pub fn close_scope(&mut self) {
78        self.env.close_scope()
79    }
80
81    fn new_label(&mut self) -> Label {
82        let label = self.next_label;
83        self.next_label.incr();
84        label
85    }
86}
87
88impl<'a> syn::visit::Visit<'a> for ASTAnnotator<'a> {
89    fn visit_item_fn(&mut self, f: &'a syn::ItemFn) {
90        for arg in f.sig.inputs.iter() {
91            match arg {
92                // item functions are standalone functions - we should never see a self
93                syn::FnArg::Receiver(_) => unreachable!(),
94                syn::FnArg::Typed(syn::PatType {
95                    pat:
96                        box syn::Pat::Ident(syn::PatIdent {
97                            ident,
98                            subpat: None,
99                            ..
100                        }),
101                    ..
102                }) => {
103                    let value = self.new_label();
104                    self.annotations.insert(ident, value);
105                    // println!("{} -> {}", value, ident);
106                    let mut file = OpenOptions::new()
107                        .write(true)
108                        .append(true)
109                        .open(LOOKUP_FILE)
110                        .unwrap();
111                    writeln!(file, "{} -> {}", value, ident).unwrap();
112                    self.add_binding(ident, value)
113                }
114                _ => (),
115            }
116        }
117        self.visit_block(&f.block);
118    }
119
120    // handle scopes
121    fn visit_block(&mut self, i: &'a syn::Block) {
122        self.open_scope();
123        let res = syn::visit::visit_block(self, i);
124        self.close_scope();
125        res
126    }
127
128    // update local mapping if dealing with a let binding
129    fn visit_local(&mut self, i: &'a syn::Local) {
130        syn::visit::visit_local(self, i);
131        let label = self.new_label();
132        match &i.pat {
133            // Case of the form `let lhs : T = rhs`
134            syn::Pat::Type(syn::PatType {
135                pat: box syn::Pat::Ident(p),
136                ty: box _ty,
137                ..
138            }) => {
139                let ident = &p.ident;
140                // bind LHS identifier with new label
141                self.add_binding(ident, label);
142                self.annotations.insert(ident, label);
143                // println!("{} -> {}", label, ident);
144                let mut file = OpenOptions::new()
145                    .write(true)
146                    .append(true)
147                    .open(LOOKUP_FILE)
148                    .unwrap();
149                writeln!(file, "{} -> {}", label, ident).unwrap();
150                self.annotations.insert(&i.pat, label);
151            }
152            // Case of the form `let lhs = rhs`
153            syn::Pat::Ident(syn::PatIdent {
154                by_ref: _,
155                mutability: _,
156                ident,
157                ..
158            }) => {
159                self.add_binding(ident, label);
160                self.annotations.insert(ident, label);
161                // println!("{} -> {}", label, ident);
162                let mut file = OpenOptions::new()
163                    .write(true)
164                    .append(true)
165                    .open(LOOKUP_FILE)
166                    .unwrap();
167                writeln!(file, "{} -> {}", label, ident).unwrap();
168                self.annotations.insert(&i.pat, label);
169            }
170            _lb => {
171                /*panic!(
172                    "use of unsupported syntactic form {:#?}",
173                    lb.into_token_stream().to_string()
174                )*/
175                let label = self.new_label();
176                self.annotations.insert(i, label);
177            }
178        }
179    }
180
181    fn visit_expr(&mut self, i: &'a Expr) {
182        // first visit children
183        syn::visit::visit_expr(self, i);
184        match i {
185            // special case identifiers
186            Expr::Path(ExprPath {
187                path: Path { segments, .. },
188                ..
189            }) => {
190                // lookup identifier in context
191                let elt = &segments[0];
192                let ident = &elt.ident;
193                let old_label = self.lookup(ident);
194                match old_label {
195                    Some(label) => {
196                        self.annotations.insert(i, label);
197                    }
198                    None => {
199                        // identifier that isn't in the context => refers to a function call
200                        // add a label for posterity
201                        let label = self.new_label();
202                        self.annotations.insert(i, label);
203                        self.add_binding(ident, label);
204                    }
205                }
206            }
207            _ => {
208                // otherwise, some arbitrary expression, add label to it
209                let label = self.new_label();
210                self.annotations.insert(i, label);
211            }
212        }
213    }
214}
215
216/// Annotates a Rust AST
217pub fn annotate_ast<'a>(ast: &'a ItemFn) -> Annotated<'a, &'a ItemFn> {
218    let mut ast_annotation = ASTAnnotator::init();
219
220    ast_annotation.visit_item_fn(ast);
221
222    (ast_annotation.annotations(), ast)
223}