/
mtwt.rs
173 lines (150 loc) · 5.97 KB
/
mtwt.rs
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
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Machinery for hygienic macros, as described in the MTWT[1] paper.
//!
//! [1] Matthew Flatt, Ryan Culpepper, David Darais, and Robert Bruce Findler.
//! 2012. *Macros that work together: Compile-time bindings, partial expansion,
//! and definition contexts*. J. Funct. Program. 22, 2 (March 2012), 181-216.
//! DOI=10.1017/S0956796812000093 http://dx.doi.org/10.1017/S0956796812000093
pub use self::SyntaxContext_::*;
use ast::{Ident, Mrk, SyntaxContext};
use std::cell::RefCell;
use std::collections::HashMap;
/// The SCTable contains a table of SyntaxContext_'s. It
/// represents a flattened tree structure, to avoid having
/// managed pointers everywhere (that caused an ICE).
/// The `marks` ensures that adding the same mark to the
/// same context gives you back the same context as before.
pub struct SCTable {
table: RefCell<Vec<SyntaxContext_>>,
marks: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
}
#[derive(PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy, Clone)]
pub enum SyntaxContext_ {
EmptyCtxt,
Mark (Mrk,SyntaxContext),
}
/// Extend a syntax context with a given mark
pub fn apply_mark(m: Mrk, ctxt: SyntaxContext) -> SyntaxContext {
with_sctable(|table| apply_mark_internal(m, ctxt, table))
}
/// Extend a syntax context with a given mark and sctable (explicit memoization)
fn apply_mark_internal(m: Mrk, ctxt: SyntaxContext, table: &SCTable) -> SyntaxContext {
let ctxts = &mut *table.table.borrow_mut();
match ctxts[ctxt.0 as usize] {
// Applying the same mark twice is a no-op.
Mark(outer_mark, prev_ctxt) if outer_mark == m => return prev_ctxt,
_ => *table.marks.borrow_mut().entry((ctxt, m)).or_insert_with(|| {
SyntaxContext(idx_push(ctxts, Mark(m, ctxt)))
}),
}
}
/// Fetch the SCTable from TLS, create one if it doesn't yet exist.
pub fn with_sctable<T, F>(op: F) -> T where
F: FnOnce(&SCTable) -> T,
{
thread_local!(static SCTABLE_KEY: SCTable = new_sctable_internal());
SCTABLE_KEY.with(move |slot| op(slot))
}
// Make a fresh syntax context table with EmptyCtxt in slot zero.
fn new_sctable_internal() -> SCTable {
SCTable {
table: RefCell::new(vec![EmptyCtxt]),
marks: RefCell::new(HashMap::new()),
}
}
/// Print out an SCTable for debugging
pub fn display_sctable(table: &SCTable) {
error!("SC table:");
for (idx,val) in table.table.borrow().iter().enumerate() {
error!("{:4} : {:?}",idx,val);
}
}
/// Clear the tables from TLD to reclaim memory.
pub fn clear_tables() {
with_sctable(|table| {
*table.table.borrow_mut() = Vec::new();
*table.marks.borrow_mut() = HashMap::new();
});
}
/// Reset the tables to their initial state
pub fn reset_tables() {
with_sctable(|table| {
*table.table.borrow_mut() = vec![EmptyCtxt];
*table.marks.borrow_mut() = HashMap::new();
});
}
/// Add a value to the end of a vec, return its index
fn idx_push<T>(vec: &mut Vec<T>, val: T) -> u32 {
vec.push(val);
(vec.len() - 1) as u32
}
/// Return the outer mark for a context with a mark at the outside.
/// FAILS when outside is not a mark.
pub fn outer_mark(ctxt: SyntaxContext) -> Mrk {
with_sctable(|sctable| {
match (*sctable.table.borrow())[ctxt.0 as usize] {
Mark(mrk, _) => mrk,
_ => panic!("can't retrieve outer mark when outside is not a mark")
}
})
}
/// If `ident` is macro expanded, return the source ident from the macro definition
/// and the mark of the expansion that created the macro definition.
pub fn source(ident: Ident) -> Option<(Ident /* source ident */, Mrk /* source macro */)> {
with_sctable(|sctable| {
let ctxts = sctable.table.borrow();
if let Mark(_expansion_mark, macro_ctxt) = ctxts[ident.ctxt.0 as usize] {
if let Mark(definition_mark, orig_ctxt) = ctxts[macro_ctxt.0 as usize] {
return Some((Ident::new(ident.name, orig_ctxt), definition_mark));
}
}
None
})
}
#[cfg(test)]
mod tests {
use ast::{EMPTY_CTXT, Ident, Mrk, Name, SyntaxContext};
use super::{resolve, apply_mark_internal, new_sctable_internal};
use super::{SCTable, Mark};
fn id(n: u32, s: SyntaxContext) -> Ident {
Ident::new(Name(n), s)
}
// extend a syntax context with a sequence of marks given
// in a vector. v[0] will be the outermost mark.
fn unfold_marks(mrks: Vec<Mrk> , tail: SyntaxContext, table: &SCTable)
-> SyntaxContext {
mrks.iter().rev().fold(tail, |tail:SyntaxContext, mrk:&Mrk|
{apply_mark_internal(*mrk,tail,table)})
}
#[test] fn unfold_marks_test() {
let mut t = new_sctable_internal();
assert_eq!(unfold_marks(vec!(3,7),EMPTY_CTXT,&mut t),SyntaxContext(2));
{
let table = t.table.borrow();
assert!((*table)[1] == Mark(7,EMPTY_CTXT));
assert!((*table)[2] == Mark(3,SyntaxContext(1)));
}
}
#[test]
fn mtwt_resolve_test(){
let a = 40;
assert_eq!(resolve(id(a,EMPTY_CTXT)),Name(a));
}
#[test]
fn hashing_tests () {
let mut t = new_sctable_internal();
assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),SyntaxContext(1));
assert_eq!(apply_mark_internal(13,EMPTY_CTXT,&mut t),SyntaxContext(2));
// using the same one again should result in the same index:
assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),SyntaxContext(1));
// I'm assuming that the rename table will behave the same....
}
}