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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
//! The `Bindings` type, for mapping names to AST fragments.
use std::collections::hash_map::{Entry, HashMap};
use std::convert::{TryFrom, TryInto};

use derive_more::{From, TryInto};
use syntax::ast::{Expr, Ident, Item, Pat, Path, Stmt, Ty};
use syntax::parse::token::Token;
use syntax::ptr::P;
use syntax::symbol::Symbol;
use syntax::tokenstream::{Cursor, TokenStream, TokenStreamBuilder, TokenTree};

use crate::ast_manip::AstEquiv;
use c2rust_ast_builder::IntoSymbol;

/// A set of binding types, mapping names to binding types.
#[derive(Clone, Debug)]
pub struct BindingTypes {
    types: HashMap<Symbol, Type>,
}

impl BindingTypes {
    pub fn new() -> BindingTypes {
        BindingTypes {
            types: HashMap::new(),
        }
    }

    pub fn get(&self, name: &Symbol) -> Option<&Type> {
        self.types.get(name)
    }

    /// Set the type for a name, so that the name matches (and captures) only nodes of the
    /// appropriate typ.
    pub fn set_type<S: IntoSymbol>(&mut self, name: S, ty: Type) {
        let name = name.into_symbol();
        match self.types.entry(name.into_symbol()) {
            Entry::Vacant(e) => {
                e.insert(ty);
            }
            Entry::Occupied(mut e) => {
                let old_ty = *e.get();
                match (old_ty, ty) {
                    (xty, yty) if xty == yty => {}
                    (_, Type::Unknown) => {}
                    (Type::Unknown, yty) => {
                        e.insert(yty);
                    }
                    _ => {
                        assert!(false, "tried to set type of {:?} to {:?}, but its type is already set to {:?}",
                                name, ty, old_ty);
                    }
                }
            }
        }
    }

    pub fn merge(&mut self, other: Self) {
        for (name, ty) in other.types.into_iter() {
            self.set_type(name, ty);
        }
    }
}

/// A set of bindings, mapping names to AST fragments.
#[derive(Clone, Debug)]
pub struct Bindings {
    map: HashMap<Symbol, Value>,
}

impl Bindings {
    pub fn new() -> Bindings {
        Bindings {
            map: HashMap::new(),
        }
    }

    /// Try to add a binding, mapping `name` to `val`. Returns false if `name`
    /// is already bound to a value and that value is not equal to `val`.
    pub fn try_add<S, T>(&mut self, name: S, val: T) -> bool
    where
        S: IntoSymbol,
        T: Into<Value>,
    {
        let name = name.into_symbol();
        let val = val.into();
        match self.map.entry(name) {
            Entry::Vacant(e) => {
                e.insert(val);
                true
            }
            Entry::Occupied(e) => val.ast_equiv(e.get()),
        }
    }

    /// Unconditionally add a binding. Panics if the `name` is already bound to
    /// a value and that value is not equal to `val`.
    pub fn add<S, T>(&mut self, name: S, val: T)
    where
        S: IntoSymbol,
        T: Into<Value>,
    {
        let name = name.into_symbol();
        let ok = self.try_add(name, val);
        assert!(ok, "cannot alter existing binding {:?}", name);
    }

    pub fn try_add_none<S>(&mut self, name: S) -> bool
    where
        S: IntoSymbol,
    {
        self.try_add(name.into_symbol(), Value::Optional(None))
    }

    pub fn add_none<S>(&mut self, name: S)
    where
        S: IntoSymbol,
    {
        self.add(name, Value::Optional(None));
    }

    pub fn get<'a, S, T>(&'a self, name: S) -> Option<&'a T>
    where
        S: IntoSymbol,
        &'a T: TryFrom<&'a Value>,
    {
        let name = name.into_symbol();
        self.map.get(&name).and_then(|v| v.try_into().ok())
    }

    pub fn get_opt<'a, S, T>(&'a self, name: S) -> Option<Option<&'a T>>
    where
        S: IntoSymbol,
        &'a T: TryFrom<&'a Value>,
    {
        let name = name.into_symbol();
        self.map.get(&name).and_then(|v| match v {
            Value::Optional(Some(box val)) => Some(val.try_into().ok()),
            Value::Optional(None) => Some(None),
            _ => None,
        })
    }
}

impl<T> From<Option<T>> for Value
where
    T: Into<Value>,
{
    fn from(val: Option<T>) -> Value {
        match val {
            Some(v) => Value::Optional(Some(Box::new(v.into()))),
            None => Value::Optional(None),
        }
    }
}

macro_rules! define_binding_values {
    ($( $Thing:ident($Repr:ty) ),*) => {
        /// An AST fragment, of any of the supported node types.
        #[derive(Clone, Debug, From, TryInto)]
        pub enum Value {
            Optional(Option<Box<Value>>),
            $( $Thing($Repr), )*
        }

        /// The types of AST fragments that can be used in `Bindings`.
        #[derive(Clone, Copy, PartialEq, Eq, Debug)]
        pub enum Type {
            Unknown,
            Optional(&'static Type),
            $( $Thing, )*
        }

        static INTERNED_TYPES: &[Type] = &[
            Type::Unknown,
            $( Type::$Thing, )*
        ];

        impl Type {
            fn from_ast_ident(ty_ident: Ident) -> Option<Type> {
                match &*ty_ident.as_str() {
                    $(stringify!($Thing) => Some(Type::$Thing),)*
                    _ => None
                }
            }
        }

        impl Bindings {
            /// Get the type of fragment associated with `name`, if any.
            pub fn get_type<S: IntoSymbol>(&self, name: S) -> Option<Type> {
                self.map.get(&name.into_symbol()).map(|v| {
                    match v {
                        $( &Value::Optional(Some(box Value::$Thing(_))) =>
                           Type::Optional(Type::$Thing.interned()), )*
                        $( &Value::$Thing(_) => Type::$Thing, )*
                        &Value::Optional(None) => Type::Unknown,
                        &Value::Optional(Some(box Value::Optional(_))) => {
                            panic!("nested Optional values")
                        }
                    }
                })
            }
        }

        impl AstEquiv for Value {
            fn ast_equiv(&self, other: &Self) -> bool {
                match (self, other) {
                    $(
                        (&Value::$Thing(ref x1),
                         &Value::$Thing(ref x2)) => {
                            x1.ast_equiv(x2)
                        },
                        (&Value::$Thing(ref x1),
                         &Value::Optional(Some(box Value::$Thing(ref x2)))) => {
                            x1.ast_equiv(x2)
                        },
                        (&Value::Optional(Some(box Value::$Thing(ref x1))),
                         &Value::$Thing(ref x2)) => {
                            x1.ast_equiv(x2)
                        },
                        (&Value::Optional(Some(box Value::$Thing(ref x1))),
                         &Value::Optional(Some(box Value::$Thing(ref x2)))) => {
                            x1.ast_equiv(x2)
                        },
                    )*
                    (&Value::Optional(None), &Value::Optional(None)) => true,
                    (_, _) => false,
                }
            }
        }

        $(
            impl<'a> TryFrom<&'a Value> for &'a $Repr {
                type Error = String;
                fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
                    match value {
                        Value::$Thing(ref x) => Ok(x),
                        _ => Err(format!("Only &Value::{} can be converted to &{}", stringify!($Thing), stringify!($Repr))),
                    }
                }
            }
        )*
    };
}

// To allow bindings to contain more types of AST nodes, add more lines to this macro.
define_binding_values! {
    Ident(Ident),
    Path(Path),
    Expr(P<Expr>),
    Pat(P<Pat>),
    Ty(P<Ty>),
    Stmt(Stmt),
    MultiStmt(Vec<Stmt>),
    Item(P<Item>)
}

impl Type {
    fn interned(self) -> &'static Self {
        for ty in INTERNED_TYPES {
            if *ty == self {
                return ty;
            }
        }
        panic!("Type::interned() for invalid type: {:?}", self);
    }
}

fn maybe_get_type(c: &mut Cursor) -> Type {
    let mut c_idx = 0;
    if let Some(TokenTree::Token(_, Token::Colon)) = c.look_ahead(c_idx) {
        c_idx += 1;
        let is_optional = match c.look_ahead(c_idx) {
            Some(TokenTree::Token(_, Token::Question)) => {
                c_idx += 1;
                true
            }
            _ => false,
        };
        match c.look_ahead(c_idx) {
            Some(TokenTree::Token(_, Token::Ident(ty_ident, _))) => {
                if let Some(ty) = Type::from_ast_ident(ty_ident) {
                    c.nth(c_idx);
                    if is_optional {
                        return Type::Optional(ty.interned());
                    } else {
                        return ty;
                    }
                }
            }
            _ => {}
        }
    }
    Type::Unknown
}

/// Rewrite tokens like `$foo:ty` into `$foo` and extract the types
fn rewrite_token_stream(ts: TokenStream, bt: &mut BindingTypes) -> TokenStream {
    let mut tsb = TokenStreamBuilder::new();
    let mut c = ts.into_trees();
    while let Some(tt) = c.next() {
        let new_tt = match tt {
            TokenTree::Token(sp, Token::Dollar) => match c.look_ahead(0) {
                Some(TokenTree::Token(sp, Token::Ident(ident, is_raw))) => {
                    c.next();
                    let dollar_sym = Symbol::intern(&format!("${}", ident));
                    let ident_ty = maybe_get_type(&mut c);
                    bt.set_type(dollar_sym, ident_ty);
                    TokenTree::Token(sp, Token::Ident(Ident::new(dollar_sym, ident.span), is_raw))
                }

                Some(TokenTree::Token(sp, Token::Lifetime(ident))) => {
                    c.next();
                    let ident_str = &*ident.as_str();
                    let (prefix, label) = ident_str.split_at(1);
                    assert!(
                        prefix == "'",
                        "Lifetime identifier does not start with ': {}",
                        ident
                    );
                    let dollar_sym = Symbol::intern(&format!("'${}", label));
                    let label_ty = maybe_get_type(&mut c);
                    bt.set_type(dollar_sym, label_ty);
                    TokenTree::Token(sp, Token::Lifetime(Ident::new(dollar_sym, ident.span)))
                }

                _ => TokenTree::Token(sp, Token::Dollar),
            },

            TokenTree::Delimited(sp, delim, tts) => {
                let dts = rewrite_token_stream(tts.into(), bt);
                TokenTree::Delimited(sp, delim, dts.into())
            }

            tt @ _ => tt,
        };
        tsb.push(new_tt);
    }
    tsb.build()
}

pub fn parse_bindings(ts: TokenStream) -> (TokenStream, BindingTypes) {
    let mut bt = BindingTypes::new();
    let new_ts = rewrite_token_stream(ts, &mut bt);
    (new_ts, bt)
}