2014-02-24 20:47:19 +00:00
|
|
|
// 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
|
|
|
|
|
2014-11-06 08:05:53 +00:00
|
|
|
pub use self::SyntaxContext_::*;
|
|
|
|
|
2016-06-22 02:50:05 +00:00
|
|
|
use ast::{Mrk, SyntaxContext};
|
2014-02-24 20:47:19 +00:00
|
|
|
|
|
|
|
use std::cell::RefCell;
|
2014-05-30 02:03:06 +00:00
|
|
|
use std::collections::HashMap;
|
2014-02-24 20:47:19 +00:00
|
|
|
|
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).
|
2016-06-22 02:50:05 +00:00
|
|
|
/// The `marks` ensures that adding the same mark to the
|
|
|
|
/// same context gives you back the same context as before.
|
2014-02-24 20:47:19 +00:00
|
|
|
pub struct SCTable {
|
|
|
|
table: RefCell<Vec<SyntaxContext_>>,
|
2016-06-20 10:28:32 +00:00
|
|
|
marks: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
|
2014-02-24 20:47:19 +00:00
|
|
|
}
|
|
|
|
|
2015-03-30 13:38:59 +00:00
|
|
|
#[derive(PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy, Clone)]
|
2014-02-24 20:47:19 +00:00
|
|
|
pub enum SyntaxContext_ {
|
|
|
|
EmptyCtxt,
|
|
|
|
Mark (Mrk,SyntaxContext),
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Extend a syntax context with a given mark
|
2014-07-03 21:38:59 +00:00
|
|
|
pub fn apply_mark(m: Mrk, ctxt: SyntaxContext) -> SyntaxContext {
|
|
|
|
with_sctable(|table| apply_mark_internal(m, ctxt, table))
|
2014-02-24 20:47:19 +00:00
|
|
|
}
|
|
|
|
|
2014-06-09 20:12:30 +00:00
|
|
|
/// Extend a syntax context with a given mark and sctable (explicit memoization)
|
2014-07-03 21:38:59 +00:00
|
|
|
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)))
|
|
|
|
}),
|
|
|
|
}
|
2014-02-24 20:47:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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,
|
|
|
|
{
|
2014-11-14 17:18:10 +00:00
|
|
|
thread_local!(static SCTABLE_KEY: SCTable = new_sctable_internal());
|
2014-12-08 18:28:32 +00:00
|
|
|
SCTABLE_KEY.with(move |slot| op(slot))
|
2014-02-24 20:47:19 +00:00
|
|
|
}
|
|
|
|
|
2016-06-22 02:49:31 +00:00
|
|
|
// Make a fresh syntax context table with EmptyCtxt in slot zero.
|
2014-02-24 20:47:19 +00:00
|
|
|
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()),
|
2014-02-24 20:47:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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() {
|
2014-12-20 08:09:35 +00:00
|
|
|
error!("{:4} : {:?}",idx,val);
|
2014-02-24 20:47:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-08 22:18:58 +00:00
|
|
|
/// 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();
|
2014-03-08 22:18:58 +00:00
|
|
|
});
|
|
|
|
}
|
2014-02-24 20:47:19 +00:00
|
|
|
|
2014-11-29 04:56:09 +00:00
|
|
|
/// 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-11-29 04:56:09 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
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 {
|
2014-02-24 20:47:19 +00:00
|
|
|
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| {
|
2015-09-24 20:05:02 +00:00
|
|
|
match (*sctable.table.borrow())[ctxt.0 as usize] {
|
2014-02-24 20:47:19 +00:00
|
|
|
Mark(mrk, _) => mrk,
|
2014-10-09 19:17:22 +00:00
|
|
|
_ => panic!("can't retrieve outer mark when outside is not a mark")
|
2014-02-24 20:47:19 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2014-07-03 05:38:30 +00:00
|
|
|
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-02-24 20:47:19 +00:00
|
|
|
|
2014-07-09 05:28:52 +00:00
|
|
|
fn id(n: u32, s: SyntaxContext) -> Ident {
|
2015-09-24 20:05:02 +00:00
|
|
|
Ident::new(Name(n), s)
|
2014-02-24 20:47:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2014-03-16 23:04:29 +00:00
|
|
|
mrks.iter().rev().fold(tail, |tail:SyntaxContext, mrk:&Mrk|
|
2014-07-03 21:38:59 +00:00
|
|
|
{apply_mark_internal(*mrk,tail,table)})
|
2014-02-24 20:47:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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));
|
2014-02-24 20:47:19 +00:00
|
|
|
{
|
|
|
|
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)));
|
2014-02-24 20:47:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-25 23:27:36 +00:00
|
|
|
#[test]
|
|
|
|
fn mtwt_resolve_test(){
|
2014-02-24 20:47:19 +00:00
|
|
|
let a = 40;
|
2014-07-09 05:28:52 +00:00
|
|
|
assert_eq!(resolve(id(a,EMPTY_CTXT)),Name(a));
|
2014-02-24 20:47:19 +00:00
|
|
|
}
|
|
|
|
|
2014-05-25 23:27:36 +00:00
|
|
|
#[test]
|
|
|
|
fn hashing_tests () {
|
2014-02-24 20:47:19 +00:00
|
|
|
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));
|
2014-02-24 20:47:19 +00:00
|
|
|
// 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));
|
2014-02-24 20:47:19 +00:00
|
|
|
// I'm assuming that the rename table will behave the same....
|
|
|
|
}
|
|
|
|
}
|