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