diff options
| author | John Clements <clements@racket-lang.org> | 2013-05-08 15:27:29 -0700 |
|---|---|---|
| committer | John Clements <clements@racket-lang.org> | 2013-06-05 12:01:36 -0700 |
| commit | d7638f9dba5cef5c4db7b9008196ede4450d8521 (patch) | |
| tree | ca96d50b78c7defd451ef9807a7f7554acc3436f /src/libsyntax | |
| parent | 77c2c0900f975e1fa9179c3e7492d62656734295 (diff) | |
| download | rust-d7638f9dba5cef5c4db7b9008196ede4450d8521.tar.gz rust-d7638f9dba5cef5c4db7b9008196ede4450d8521.zip | |
change to newer macro escape mechanism, using uints in more places
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ext/base.rs | 125 | ||||
| -rw-r--r-- | src/libsyntax/ext/expand.rs | 128 | ||||
| -rw-r--r-- | src/libsyntax/parse/mod.rs | 28 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 7 | ||||
| -rw-r--r-- | src/libsyntax/parse/token.rs | 6 |
5 files changed, 166 insertions, 128 deletions
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 5fae6ff3c18..48cfe61309c 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -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)] diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 6ea1160c267..c23e34e0f91 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -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 diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 9c716f5631f..8306b0ff1a9 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -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)}, diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 23e3f145398..4635db0e10f 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -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 diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index b716384c6cc..cd274817a16 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -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 } /** |
