change to newer macro escape mechanism, using uints in more places

This commit is contained in:
John Clements 2013-05-08 15:27:29 -07:00
parent 77c2c0900f
commit d7638f9dba
5 changed files with 166 additions and 128 deletions

View file

@ -11,6 +11,7 @@
use core::prelude::*;
use ast;
use ast::Name;
use codemap;
use codemap::{CodeMap, span, ExpnInfo, ExpandedFrom};
use codemap::CallInfo;
@ -18,6 +19,7 @@ use diagnostic::span_handler;
use ext;
use parse;
use parse::token;
use parse::token::{intern};
use core::hashmap::HashMap;
use core::vec;
@ -91,29 +93,33 @@ pub enum SyntaxExtension {
IdentTT(SyntaxExpanderTTItem),
}
// The SyntaxEnv is the environment that's threaded through the expansion
// of macros. It contains bindings for macros, and also a special binding
// for " block" (not a legal identifier) that maps to a BlockInfo
pub type SyntaxEnv = @mut MapChain<Name, Transformer>;
// Name : the domain of SyntaxEnvs
// want to change these to uints....
// note that we use certain strings that are not legal as identifiers
// to indicate, for instance, how blocks are supposed to behave.
type Name = @~str;
// Transformer : the codomain of SyntaxEnvs
// NB: it may seem crazy to lump both of these into one environment;
// what would it mean to bind "foo" to BlockLimit(true)? The idea
// is that this follows the lead of MTWT, and accommodates growth
// toward a more uniform syntax syntax (sorry) where blocks are just
// another kind of transformer.
pub enum Transformer {
// this identifier maps to a syntax extension or macro
SE(SyntaxExtension),
// should blocks occurring here limit macro scopes?
ScopeMacros(bool)
// blockinfo : this is ... well, it's simpler than threading
// another whole data stack-structured data structure through
// expansion. Basically, there's an invariant that every
// map must contain a binding for " block".
BlockInfo(BlockInfo)
}
pub struct BlockInfo {
// should macros escape from this scope?
macros_escape : bool,
// what are the pending renames?
pending_renames : @mut RenameList
}
// a list of ident->name renamings
type RenameList = ~[(ast::ident,Name)];
// The base map of methods for expanding syntax extension
// AST nodes into full ASTs
pub fn syntax_expander_table() -> SyntaxEnv {
@ -127,77 +133,80 @@ pub fn syntax_expander_table() -> SyntaxEnv {
}
let mut syntax_expanders = HashMap::new();
// NB identifier starts with space, and can't conflict with legal idents
syntax_expanders.insert(@~" block",
@ScopeMacros(true));
syntax_expanders.insert(@~"macro_rules",
syntax_expanders.insert(intern(&" block"),
@BlockInfo(BlockInfo{
macros_escape : false,
pending_renames : @mut ~[]
}));
syntax_expanders.insert(intern(&"macro_rules"),
builtin_item_tt(
ext::tt::macro_rules::add_new_extension));
syntax_expanders.insert(@~"fmt",
syntax_expanders.insert(intern(&"fmt"),
builtin_normal_tt(ext::fmt::expand_syntax_ext));
syntax_expanders.insert(
@~"auto_encode",
intern(&"auto_encode"),
@SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));
syntax_expanders.insert(
@~"auto_decode",
intern(&"auto_decode"),
@SE(ItemDecorator(ext::auto_encode::expand_auto_decode)));
syntax_expanders.insert(@~"env",
syntax_expanders.insert(intern(&"env"),
builtin_normal_tt(ext::env::expand_syntax_ext));
syntax_expanders.insert(@~"bytes",
syntax_expanders.insert(intern("bytes"),
builtin_normal_tt(ext::bytes::expand_syntax_ext));
syntax_expanders.insert(@~"concat_idents",
syntax_expanders.insert(intern("concat_idents"),
builtin_normal_tt(
ext::concat_idents::expand_syntax_ext));
syntax_expanders.insert(@~"log_syntax",
syntax_expanders.insert(intern(&"log_syntax"),
builtin_normal_tt(
ext::log_syntax::expand_syntax_ext));
syntax_expanders.insert(@~"deriving",
syntax_expanders.insert(intern(&"deriving"),
@SE(ItemDecorator(
ext::deriving::expand_meta_deriving)));
// Quasi-quoting expanders
syntax_expanders.insert(@~"quote_tokens",
syntax_expanders.insert(intern(&"quote_tokens"),
builtin_normal_tt(ext::quote::expand_quote_tokens));
syntax_expanders.insert(@~"quote_expr",
syntax_expanders.insert(intern(&"quote_expr"),
builtin_normal_tt(ext::quote::expand_quote_expr));
syntax_expanders.insert(@~"quote_ty",
syntax_expanders.insert(intern(&"quote_ty"),
builtin_normal_tt(ext::quote::expand_quote_ty));
syntax_expanders.insert(@~"quote_item",
syntax_expanders.insert(intern(&"quote_item"),
builtin_normal_tt(ext::quote::expand_quote_item));
syntax_expanders.insert(@~"quote_pat",
syntax_expanders.insert(intern(&"quote_pat"),
builtin_normal_tt(ext::quote::expand_quote_pat));
syntax_expanders.insert(@~"quote_stmt",
syntax_expanders.insert(intern(&"quote_stmt"),
builtin_normal_tt(ext::quote::expand_quote_stmt));
syntax_expanders.insert(@~"line",
syntax_expanders.insert(intern(&"line"),
builtin_normal_tt(
ext::source_util::expand_line));
syntax_expanders.insert(@~"col",
syntax_expanders.insert(intern(&"col"),
builtin_normal_tt(
ext::source_util::expand_col));
syntax_expanders.insert(@~"file",
syntax_expanders.insert(intern(&"file"),
builtin_normal_tt(
ext::source_util::expand_file));
syntax_expanders.insert(@~"stringify",
syntax_expanders.insert(intern(&"stringify"),
builtin_normal_tt(
ext::source_util::expand_stringify));
syntax_expanders.insert(@~"include",
syntax_expanders.insert(intern(&"include"),
builtin_normal_tt(
ext::source_util::expand_include));
syntax_expanders.insert(@~"include_str",
syntax_expanders.insert(intern(&"include_str"),
builtin_normal_tt(
ext::source_util::expand_include_str));
syntax_expanders.insert(@~"include_bin",
syntax_expanders.insert(intern(&"include_bin"),
builtin_normal_tt(
ext::source_util::expand_include_bin));
syntax_expanders.insert(@~"module_path",
syntax_expanders.insert(intern(&"module_path"),
builtin_normal_tt(
ext::source_util::expand_mod));
syntax_expanders.insert(@~"proto",
syntax_expanders.insert(intern(&"proto"),
builtin_item_tt(ext::pipes::expand_proto));
syntax_expanders.insert(@~"asm",
syntax_expanders.insert(intern(&"asm"),
builtin_normal_tt(ext::asm::expand_asm));
syntax_expanders.insert(
@~"trace_macros",
intern(&"trace_macros"),
builtin_normal_tt(ext::trace_macros::expand_trace_macros));
MapChain::new(~syntax_expanders)
}
@ -478,7 +487,39 @@ impl <K: Eq + Hash + IterBytes ,V: Copy> MapChain<K,V>{
ConsMapChain (~ref mut map,_) => map.insert(key,ext)
}
}
// insert the binding into the topmost frame for which the binding
// associated with 'n' exists and satisfies pred
// ... there are definitely some opportunities for abstraction
// here that I'm ignoring. (e.g., manufacturing a predicate on
// the maps in the chain, and using an abstract "find".
fn insert_into_frame(&mut self, key: K, ext: @V, n: K, pred: &fn(&@V)->bool) {
match *self {
BaseMapChain (~ref mut map) => {
if satisfies_pred(map,&n,pred) {
map.insert(key,ext);
} else {
fail!(~"expected map chain containing satisfying frame")
}
},
ConsMapChain (~ref mut map, rest) => {
if satisfies_pred(map,&n,pred) {
map.insert(key,ext);
} else {
rest.insert_into_frame(key,ext,n,pred)
}
}
}
}
}
fn satisfies_pred<K : Eq + Hash + IterBytes,V>(map : &mut HashMap<K,V>,
n: &K,
pred: &fn(&V)->bool)
-> bool {
match map.find(n) {
Some(ref v) => (pred(*v)),
None => false
}
}
#[cfg(test)]

View file

@ -25,6 +25,7 @@ use ext::base::*;
use fold::*;
use parse;
use parse::{parse_item_from_source_str};
use parse::token::{get_ident_interner,intern};
use core::vec;
@ -48,15 +49,14 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
fmt!("expected macro name without module \
separators"));
}
/* using idents and token::special_idents would make the
the macro names be hygienic */
let extname = cx.parse_sess().interner.get(pth.idents[0]);
let extname = pth.idents[0];
let extnamestr = get_ident_interner().get(extname);
// leaving explicit deref here to highlight unbox op:
match (*extsbox).find(&extname) {
match (*extsbox).find(&extname.repr) {
None => {
cx.span_fatal(
pth.span,
fmt!("macro undefined: '%s'", *extname))
fmt!("macro undefined: '%s'", *extnamestr))
}
Some(@SE(NormalTT(SyntaxExpanderTT{
expander: exp,
@ -65,7 +65,7 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
cx.bt_push(ExpandedFrom(CallInfo {
call_site: s,
callee: NameAndSpan {
name: copy *extname,
name: copy *extnamestr,
span: exp_sp,
},
}));
@ -78,7 +78,7 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
pth.span,
fmt!(
"non-expr macro in expr pos: %s",
*extname
*extnamestr
)
)
}
@ -94,7 +94,7 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
_ => {
cx.span_fatal(
pth.span,
fmt!("'%s' is not a tt-style macro", *extname)
fmt!("'%s' is not a tt-style macro", *extnamestr)
)
}
}
@ -131,7 +131,7 @@ pub fn expand_mod_items(extsbox: @mut SyntaxEnv,
do vec::foldr(item.attrs, ~[*item]) |attr, items| {
let mname = attr::get_attr_name(attr);
match (*extsbox).find(&mname) {
match (*extsbox).find(&intern(*mname)) {
Some(@SE(ItemDecorator(dec_fn))) => {
cx.bt_push(ExpandedFrom(CallInfo {
call_site: attr.span,
@ -155,16 +155,20 @@ pub fn expand_mod_items(extsbox: @mut SyntaxEnv,
// eval $e with a new exts frame:
macro_rules! with_exts_frame (
($extsboxexpr:expr,$e:expr) =>
($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
({let extsbox = $extsboxexpr;
let oldexts = *extsbox;
*extsbox = oldexts.push_frame();
extsbox.insert(intern(special_block_name),
@BlockInfo(BlockInfo{macros_escape:$macros_escape,pending_renames:@mut ~[]}));
let result = $e;
*extsbox = oldexts;
result
})
)
static special_block_name : &'static str = " block";
// When we enter a module, record it, for the sake of `module!`
pub fn expand_item(extsbox: @mut SyntaxEnv,
cx: @ExtCtxt,
@ -182,14 +186,8 @@ pub fn expand_item(extsbox: @mut SyntaxEnv,
match it.node {
ast::item_mod(_) | ast::item_foreign_mod(_) => {
cx.mod_push(it.ident);
let result =
// don't push a macro scope for macro_escape:
if contains_macro_escape(it.attrs) {
orig(it,fld)
} else {
// otherwise, push a scope:
with_exts_frame!(extsbox,orig(it,fld))
};
let macro_escape = contains_macro_escape(it.attrs);
let result = with_exts_frame!(extsbox,macro_escape,orig(it,fld));
cx.mod_pop();
result
}
@ -205,31 +203,6 @@ pub fn contains_macro_escape (attrs: &[ast::attribute]) -> bool {
attrs.any(|attr| "macro_escape" == *attr::get_attr_name(attr))
}
// this macro disables (one layer of) macro
// scoping, to allow a block to add macro bindings
// to its parent env
macro_rules! without_macro_scoping(
($extsexpr:expr,$exp:expr) =>
({
// only evaluate this once:
let exts = $extsexpr;
// capture the existing binding:
let existingBlockBinding =
match exts.find(&@~" block"){
Some(binding) => binding,
None => cx.bug("expected to find \" block\" binding")
};
// this prevents the block from limiting the macros' scope:
exts.insert(@~" block",@ScopeMacros(false));
let result = $exp;
// reset the block binding. Note that since the original
// one may have been inherited, this procedure may wind
// up introducing a block binding where one didn't exist
// before.
exts.insert(@~" block",existingBlockBinding);
result
}))
// Support for item-position macro invocations, exactly the same
// logic as for expression-position macro invocations.
pub fn expand_item_mac(extsbox: @mut SyntaxEnv,
@ -243,22 +216,24 @@ pub fn expand_item_mac(extsbox: @mut SyntaxEnv,
_ => cx.span_bug(it.span, "invalid item macro invocation")
};
let extname = cx.parse_sess().interner.get(pth.idents[0]);
let expanded = match (*extsbox).find(&extname) {
let extname = pth.idents[0];
let interner = get_ident_interner();
let extnamestr = interner.get(extname);
let expanded = match (*extsbox).find(&extname.repr) {
None => cx.span_fatal(pth.span,
fmt!("macro undefined: '%s!'", *extname)),
fmt!("macro undefined: '%s!'", *extnamestr)),
Some(@SE(NormalTT(ref expand))) => {
if it.ident != parse::token::special_idents::invalid {
cx.span_fatal(pth.span,
fmt!("macro %s! expects no ident argument, \
given '%s'", *extname,
*cx.parse_sess().interner.get(it.ident)));
given '%s'", *extnamestr,
*interner.get(it.ident)));
}
cx.bt_push(ExpandedFrom(CallInfo {
call_site: it.span,
callee: NameAndSpan {
name: copy *extname,
name: copy *extnamestr,
span: expand.span
}
}));
@ -268,29 +243,29 @@ pub fn expand_item_mac(extsbox: @mut SyntaxEnv,
if it.ident == parse::token::special_idents::invalid {
cx.span_fatal(pth.span,
fmt!("macro %s! expects an ident argument",
*extname));
*extnamestr));
}
cx.bt_push(ExpandedFrom(CallInfo {
call_site: it.span,
callee: NameAndSpan {
name: copy *extname,
name: copy *extnamestr,
span: expand.span
}
}));
((*expand).expander)(cx, it.span, it.ident, tts)
}
_ => cx.span_fatal(
it.span, fmt!("%s! is not legal in item position", *extname))
it.span, fmt!("%s! is not legal in item position", *extnamestr))
};
let maybe_it = match expanded {
MRItem(it) => fld.fold_item(it),
MRExpr(_) => cx.span_fatal(pth.span,
~"expr macro in item position: "
+ *extname),
+ *extnamestr),
MRAny(_, item_maker, _) => item_maker().chain(|i| {fld.fold_item(i)}),
MRDef(ref mdef) => {
extsbox.insert(@/*bad*/ copy mdef.name, @SE((*mdef).ext));
insert_macro(*extsbox,intern(mdef.name), @SE((*mdef).ext));
None
}
};
@ -298,6 +273,23 @@ pub fn expand_item_mac(extsbox: @mut SyntaxEnv,
return maybe_it;
}
// insert a macro into the innermost frame that doesn't have the
// macro_escape tag.
fn insert_macro(exts: SyntaxEnv, name: ast::Name, transformer: @Transformer) {
let block_err_msg = "special identifier ' block' was bound to a non-BlockInfo";
let is_non_escaping_block =
|t : &@Transformer| -> bool{
match t {
&@BlockInfo(BlockInfo {macros_escape:false,_}) => true,
&@BlockInfo(BlockInfo {_}) => false,
_ => fail!(block_err_msg)
}
};
exts.insert_into_frame(name,transformer,intern(special_block_name),
is_non_escaping_block)
}
// expand a stmt
pub fn expand_stmt(extsbox: @mut SyntaxEnv,
cx: @ExtCtxt,
@ -323,16 +315,17 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
fmt!("expected macro name without module \
separators"));
}
let extname = cx.parse_sess().interner.get(pth.idents[0]);
let (fully_expanded, sp) = match (*extsbox).find(&extname) {
let extname = pth.idents[0];
let extnamestr = get_ident_interner().get(extname);
let (fully_expanded, sp) = match (*extsbox).find(&extname.repr) {
None =>
cx.span_fatal(pth.span, fmt!("macro undefined: '%s'", *extname)),
cx.span_fatal(pth.span, fmt!("macro undefined: '%s'", *extnamestr)),
Some(@SE(NormalTT(
SyntaxExpanderTT{expander: exp, span: exp_sp}))) => {
cx.bt_push(ExpandedFrom(CallInfo {
call_site: sp,
callee: NameAndSpan { name: copy *extname, span: exp_sp }
callee: NameAndSpan { name: copy *extnamestr, span: exp_sp }
}));
let expanded = match exp(cx, mac.span, tts) {
MRExpr(e) =>
@ -341,7 +334,7 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
MRAny(_,_,stmt_mkr) => stmt_mkr(),
_ => cx.span_fatal(
pth.span,
fmt!("non-stmt macro in stmt pos: %s", *extname))
fmt!("non-stmt macro in stmt pos: %s", *extnamestr))
};
//keep going, outside-in
@ -362,7 +355,7 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
_ => {
cx.span_fatal(pth.span,
fmt!("'%s' is not a tt-style macro", *extname))
fmt!("'%s' is not a tt-style macro", *extnamestr))
}
};
@ -382,19 +375,8 @@ pub fn expand_block(extsbox: @mut SyntaxEnv,
fld: @ast_fold,
orig: @fn(&blk_, span, @ast_fold) -> (blk_, span))
-> (blk_, span) {
match (*extsbox).find(&@~" block") {
// no scope limit on macros in this block, no need
// to push an exts frame:
Some(@ScopeMacros(false)) => {
orig (blk,sp,fld)
},
// this block should limit the scope of its macros:
Some(@ScopeMacros(true)) => {
// see note below about treatment of exts table
with_exts_frame!(extsbox,orig(blk,sp,fld))
},
_ => cx.span_bug(sp, "expected ScopeMacros binding for \" block\"")
}
// see note below about treatment of exts table
with_exts_frame!(extsbox,false,orig(blk,sp,fld))
}
// given a mutable list of renames, return a tree-folder that applies those

View file

@ -351,6 +351,7 @@ mod test {
use codemap::{span, BytePos, spanned};
use opt_vec;
use ast;
use ast_util::new_ident;
use abi;
use parse::parser::Parser;
use parse::token::intern;
@ -377,9 +378,14 @@ mod test {
span{lo:BytePos(a),hi:BytePos(b),expn_info:None}
}
// compose new_ident and intern:
fn intern_ident(str : &str) -> ast::ident {
new_ident(intern(str))
}
// convert a vector of uints to a vector of ast::idents
fn ints_to_idents(ids: ~[~str]) -> ~[ast::ident] {
ids.map(|u| intern(*u))
ids.map(|u| intern_ident(*u))
}
#[test] fn path_exprs_1 () {
@ -387,7 +393,7 @@ mod test {
@ast::expr{id:1,
node:ast::expr_path(@ast::Path {span:sp(0,1),
global:false,
idents:~[intern("a")],
idents:~[intern_ident("a")],
rp:None,
types:~[]}),
span:sp(0,1)})
@ -449,7 +455,7 @@ mod test {
node:ast::expr_path(
@ast::Path{span:sp(7,8),
global:false,
idents:~[intern("d")],
idents:~[intern_ident("d")],
rp:None,
types:~[]
}),
@ -466,7 +472,7 @@ mod test {
@ast::Path{
span:sp(0,1),
global:false,
idents:~[intern("b")],
idents:~[intern_ident("b")],
rp:None,
types: ~[]}),
span: sp(0,1)},
@ -487,7 +493,7 @@ mod test {
@ast::Path{
span:sp(0,1),
global:false,
idents:~[intern("b")],
idents:~[intern_ident("b")],
rp: None,
types: ~[]},
None // no idea
@ -506,7 +512,7 @@ mod test {
span:sp(4,4), // this is bizarre...
// check this in the original parser?
global:false,
idents:~[intern("int")],
idents:~[intern_ident("int")],
rp: None,
types: ~[]},
2),
@ -516,7 +522,7 @@ mod test {
@ast::Path{
span:sp(0,1),
global:false,
idents:~[intern("b")],
idents:~[intern_ident("b")],
rp: None,
types: ~[]},
None // no idea
@ -532,7 +538,7 @@ mod test {
// assignment order of the node_ids.
assert_eq!(string_to_item(@~"fn a (b : int) { b; }"),
Some(
@ast::item{ident:intern("a"),
@ast::item{ident:intern_ident("a"),
attrs:~[],
id: 9, // fixme
node: ast::item_fn(ast::fn_decl{
@ -542,7 +548,7 @@ mod test {
node: ast::ty_path(@ast::Path{
span:sp(10,13),
global:false,
idents:~[intern("int")],
idents:~[intern_ident("int")],
rp: None,
types: ~[]},
2),
@ -553,7 +559,7 @@ mod test {
@ast::Path{
span:sp(6,7),
global:false,
idents:~[intern("b")],
idents:~[intern_ident("b")],
rp: None,
types: ~[]},
None // no idea
@ -583,7 +589,7 @@ mod test {
@ast::Path{
span:sp(17,18),
global:false,
idents:~[intern("b")],
idents:~[intern_ident("b")],
rp:None,
types: ~[]}),
span: sp(17,18)},

View file

@ -2628,6 +2628,13 @@ impl Parser {
// to the macro clause of parse_item_or_view_item. This
// could use some cleanup, it appears to me.
// whoops! I now have a guess: I'm guessing the "parens-only"
// rule here is deliberate, to allow macro users to use parens
// for things that should be parsed as stmt_mac, and braces
// for things that should expand into items. Tricky, and
// somewhat awkward... and probably undocumented. Of course,
// I could just be wrong.
check_expected_item(self, item_attrs);
// Potential trouble: if we allow macros with paths instead of

View file

@ -394,6 +394,8 @@ pub struct ident_interner {
}
impl ident_interner {
// I'm torn as to whether these should produce idents or
// just uints.
pub fn intern(&self, val: &str) -> ast::ident {
ast::ident { repr: self.interner.intern(val), ctxt: 0 }
}
@ -530,9 +532,9 @@ pub fn mk_fake_ident_interner() -> @ident_interner {
}
// maps a string to its interned representation
pub fn intern(str : &str) -> ast::ident {
pub fn intern(str : &str) -> uint {
let interner = get_ident_interner();
interner.intern(str)
interner.intern(str).repr
}
/**