rust/src/libsyntax/ext/mtwt.rs

160 lines
5.4 KiB
Rust
Raw Normal View History

// 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::{Mrk, SyntaxContext};
use std::cell::RefCell;
use std::collections::HashMap;
2014-06-09 20:12:30 +00:00
/// 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_>>,
2016-06-20 10:28:32 +00:00
marks: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
}
2015-03-30 13:38:59 +00:00
#[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))
}
2014-06-09 20:12:30 +00:00
/// Extend a syntax context with a given mark and sctable (explicit memoization)
fn apply_mark_internal(m: Mrk, ctxt: SyntaxContext, table: &SCTable) -> SyntaxContext {
2016-06-20 10:28:32 +00:00
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.
2014-12-08 18:28:32 +00:00
pub fn with_sctable<T, F>(op: F) -> T where
F: FnOnce(&SCTable) -> T,
{
thread_local!(static SCTABLE_KEY: SCTable = new_sctable_internal());
2014-12-08 18:28:32 +00:00
SCTABLE_KEY.with(move |slot| op(slot))
}
2016-06-22 02:49:31 +00:00
// Make a fresh syntax context table with EmptyCtxt in slot zero.
fn new_sctable_internal() -> SCTable {
SCTable {
2016-06-22 02:49:31 +00:00
table: RefCell::new(vec![EmptyCtxt]),
2016-06-20 10:28:32 +00:00
marks: RefCell::new(HashMap::new()),
}
}
/// Print out an SCTable for debugging
pub fn display_sctable(table: &SCTable) {
error!("SC table:");
2014-03-20 22:05:37 +00:00
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| {
2014-03-20 22:05:37 +00:00
*table.table.borrow_mut() = Vec::new();
2016-06-20 10:28:32 +00:00
*table.marks.borrow_mut() = HashMap::new();
});
}
/// Reset the tables to their initial state
pub fn reset_tables() {
with_sctable(|table| {
2016-06-22 02:49:31 +00:00
*table.table.borrow_mut() = vec![EmptyCtxt];
2016-06-20 10:28:32 +00:00
*table.marks.borrow_mut() = HashMap::new();
});
}
2014-06-09 20:12:30 +00:00
/// Add a value to the end of a vec, return its index
2014-07-06 08:17:59 +00:00
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")
}
})
}
#[cfg(test)]
mod tests {
use ast::{EMPTY_CTXT, Ident, Mrk, Name, SyntaxContext};
2016-06-20 10:51:48 +00:00
use super::{resolve, apply_mark_internal, new_sctable_internal};
use super::{SCTable, Mark};
2014-07-09 05:28:52 +00:00
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();
2016-06-22 02:49:31 +00:00
assert_eq!(unfold_marks(vec!(3,7),EMPTY_CTXT,&mut t),SyntaxContext(2));
{
let table = t.table.borrow();
2016-06-22 02:49:31 +00:00
assert!((*table)[1] == Mark(7,EMPTY_CTXT));
assert!((*table)[2] == Mark(3,SyntaxContext(1)));
}
}
#[test]
fn mtwt_resolve_test(){
let a = 40;
2014-07-09 05:28:52 +00:00
assert_eq!(resolve(id(a,EMPTY_CTXT)),Name(a));
}
#[test]
fn hashing_tests () {
let mut t = new_sctable_internal();
2016-06-22 02:49:31 +00:00
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:
2016-06-22 02:49:31 +00:00
assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),SyntaxContext(1));
// I'm assuming that the rename table will behave the same....
}
}