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;
pub type Annotations<'a> = HashMap<&'a dyn ASTKey, Label>;
pub type Annotated<'a, T> = (Annotations<'a>, T);
pub const LOOKUP_FILE: &str = "/tmp/annotation_rev_lookup";
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,
}
}
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)
}
#[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 {
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);
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);
}
fn visit_block(&mut self, i: &'a syn::Block) {
self.open_scope();
let res = syn::visit::visit_block(self, i);
self.close_scope();
res
}
fn visit_local(&mut self, i: &'a syn::Local) {
syn::visit::visit_local(self, i);
let label = self.new_label();
match &i.pat {
syn::Pat::Type(syn::PatType {
pat: box syn::Pat::Ident(p),
ty: box _ty,
..
}) => {
let ident = &p.ident;
self.add_binding(ident, label);
self.annotations.insert(ident, label);
let mut file = OpenOptions::new()
.write(true)
.append(true)
.open(LOOKUP_FILE)
.unwrap();
writeln!(file, "{} -> {}", label, ident).unwrap();
self.annotations.insert(&i.pat, label);
}
syn::Pat::Ident(syn::PatIdent {
by_ref: _,
mutability: _,
ident,
..
}) => {
self.add_binding(ident, label);
self.annotations.insert(ident, label);
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 => {
let label = self.new_label();
self.annotations.insert(i, label);
}
}
}
fn visit_expr(&mut self, i: &'a Expr) {
syn::visit::visit_expr(self, i);
match i {
Expr::Path(ExprPath {
path: Path { segments, .. },
..
}) => {
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 => {
let label = self.new_label();
self.annotations.insert(i, label);
self.add_binding(ident, label);
}
}
}
_ => {
let label = self.new_label();
self.annotations.insert(i, label);
}
}
}
}
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)
}