diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index ddf58ce0fef1..3f5d0f13b31c 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -96,6 +96,8 @@ fn syntax_expander_table() -> HashMap<~str, syntax_extension> { ext::log_syntax::expand_syntax_ext)); syntax_expanders.insert(~"ast", builtin(ext::qquote::expand_ast)); + syntax_expanders.insert(~"quote", + builtin_expr_tt(ext::quote::expand_quote)); syntax_expanders.insert(~"line", builtin(ext::source_util::expand_line)); syntax_expanders.insert(~"col", diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index a43b0cb69f4b..10f853495e3c 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -34,12 +34,14 @@ fn mk_unary(cx: ext_ctxt, sp: span, op: ast::unop, e: @ast::expr) cx.next_id(); // see ast_util::op_expr_callee_id mk_expr(cx, sp, ast::expr_unary(op, e)) } +fn mk_raw_path(sp: span, idents: ~[ast::ident]) -> @ast::path { + let p : @ast::path = @{span: sp, global: false, idents: idents, + rp: None, types: ~[]}; + return p; +} fn mk_path(cx: ext_ctxt, sp: span, idents: ~[ast::ident]) -> @ast::expr { - let path = @{span: sp, global: false, idents: idents, - rp: None, types: ~[]}; - let pathexpr = ast::expr_path(path); - mk_expr(cx, sp, pathexpr) + mk_expr(cx, sp, ast::expr_path(mk_raw_path(sp, idents))) } fn mk_access_(cx: ext_ctxt, sp: span, p: @ast::expr, m: ast::ident) -> @ast::expr { @@ -53,7 +55,6 @@ fn mk_access(cx: ext_ctxt, sp: span, p: ~[ast::ident], m: ast::ident) fn mk_addr_of(cx: ext_ctxt, sp: span, e: @ast::expr) -> @ast::expr { return mk_expr(cx, sp, ast::expr_addr_of(ast::m_imm, e)); } - fn mk_call_(cx: ext_ctxt, sp: span, fn_expr: @ast::expr, args: ~[@ast::expr]) -> @ast::expr { mk_expr(cx, sp, ast::expr_call(fn_expr, args, false)) @@ -90,19 +91,54 @@ fn mk_base_str(cx: ext_ctxt, sp: span, s: ~str) -> @ast::expr { fn mk_uniq_str(cx: ext_ctxt, sp: span, s: ~str) -> @ast::expr { mk_vstore_e(cx, sp, mk_base_str(cx, sp, s), ast::expr_vstore_uniq) } - +fn mk_field(sp: span, f: &{ident: ast::ident, ex: @ast::expr}) + -> ast::field { + {node: {mutbl: ast::m_imm, ident: f.ident, expr: f.ex}, span: sp} +} +fn mk_fields(sp: span, fields: ~[{ident: ast::ident, ex: @ast::expr}]) -> + ~[ast::field] { + move fields.map(|f| mk_field(sp, f)) +} fn mk_rec_e(cx: ext_ctxt, sp: span, fields: ~[{ident: ast::ident, ex: @ast::expr}]) -> @ast::expr { - let mut astfields: ~[ast::field] = ~[]; - for fields.each |field| { - let ident = field.ident; - let val = field.ex; - let astfield = - {node: {mutbl: ast::m_imm, ident: ident, expr: val}, span: sp}; - astfields.push(astfield); - } - let recexpr = ast::expr_rec(astfields, option::None::<@ast::expr>); - mk_expr(cx, sp, recexpr) + mk_expr(cx, sp, ast::expr_rec(mk_fields(sp, fields), + option::None::<@ast::expr>)) +} +fn mk_struct_e(cx: ext_ctxt, sp: span, + ctor_path: ~[ast::ident], + fields: ~[{ident: ast::ident, ex: @ast::expr}]) -> + @ast::expr { + mk_expr(cx, sp, + ast::expr_struct(mk_raw_path(sp, ctor_path), + mk_fields(sp, fields), + option::None::<@ast::expr>)) +} +fn mk_glob_use(cx: ext_ctxt, sp: span, + path: ~[ast::ident]) -> @ast::view_item { + let glob = @{node: ast::view_path_glob(mk_raw_path(sp, path), + cx.next_id()), + span: sp}; + @{node: ast::view_item_import(~[glob]), + attrs: ~[], + vis: ast::private, + span: sp} +} +fn mk_block(cx: ext_ctxt, sp: span, + view_items: ~[@ast::view_item], + stmts: ~[@ast::stmt], + expr: Option<@ast::expr>) -> @ast::expr { + let blk = {node: {view_items: view_items, + stmts: stmts, + expr: expr, + id: cx.next_id(), + rules: ast::default_blk }, + span: sp }; + mk_expr(cx, sp, ast::expr_block(blk)) +} +fn mk_copy(cx: ext_ctxt, sp: span, e: @ast::expr) -> @ast::expr { + mk_expr(cx, sp, ast::expr_copy(e)) +} +fn mk_managed(cx: ext_ctxt, sp: span, e: @ast::expr) -> @ast::expr { + mk_expr(cx, sp, ast::expr_unary(ast::box(ast::m_imm), e)) } - diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs new file mode 100644 index 000000000000..68a3c4a13996 --- /dev/null +++ b/src/libsyntax/ext/quote.rs @@ -0,0 +1,307 @@ +use mod ast; +use mod parse::token; + +use codemap::span; +use ext::base::ext_ctxt; +use token::*; + +/** +* +* Quasiquoting works via token trees. +* +* This is registered as a expression syntax extension called quote! that lifts +* its argument token-tree to an AST representing the construction of the same +* token tree, with ast::tt_nonterminal nodes interpreted as antiquotes +* (splices). +* +*/ + +pub fn expand_quote(cx: ext_ctxt, + sp: span, + tts: ~[ast::token_tree]) -> base::mac_result +{ + + // NB: It appears that the main parser loses its mind if we consider + // $foo as a tt_nonterminal during the main parse, so we have to re-parse + // under quote_depth > 0. This is silly and should go away; the _guess_ is + // it has to do with transition away from supporting old-style macros, so + // try removing it when enough of them are gone. + let p = parse::new_parser_from_tt(cx.parse_sess(), cx.cfg(), tts); + p.quote_depth += 1u; + let tq = dvec::DVec(); + while p.token != token::EOF { + tq.push(p.parse_token_tree()); + } + let tts = tq.get(); + + // We want to emit a block expression that does a sequence of 'use's to + // import the AST and token constructors, followed by a tt expression. + let uses = ~[ build::mk_glob_use(cx, sp, ids_ext(cx, ~[~"syntax", + ~"ast"])), + build::mk_glob_use(cx, sp, ids_ext(cx, ~[~"syntax", + ~"parse", + ~"token"])) ]; + base::mr_expr(build::mk_block(cx, sp, uses, ~[], + Some(mk_tt(cx, sp, &ast::tt_delim(tts))))) +} + +fn ids_ext(cx: ext_ctxt, strs: ~[~str]) -> ~[ast::ident] { + strs.map(|str| cx.parse_sess().interner.intern(@*str)) +} + +fn id_ext(cx: ext_ctxt, str: ~str) -> ast::ident { + cx.parse_sess().interner.intern(@str) +} + +fn mk_option_span(cx: ext_ctxt, + qsp: span, + sp: Option) -> @ast::expr { + match sp { + None => build::mk_path(cx, qsp, ids_ext(cx, ~[~"None"])), + Some(sp) => { + build::mk_call(cx, qsp, + ids_ext(cx, ~[~"Some"]), + ~[build::mk_managed(cx, qsp, + mk_span(cx, qsp, sp))]) + } + } +} + +fn mk_span(cx: ext_ctxt, qsp: span, sp: span) -> @ast::expr { + + let e_expn_info = match sp.expn_info { + None => build::mk_path(cx, qsp, ids_ext(cx, ~[~"None"])), + Some(@codemap::expanded_from(cr)) => { + let e_callee = + build::mk_rec_e( + cx, qsp, + ~[{ident: id_ext(cx, ~"name"), + ex: build::mk_uniq_str(cx, qsp, + cr.callie.name)}, + {ident: id_ext(cx, ~"span"), + ex: mk_option_span(cx, qsp, cr.callie.span)}]); + + let e_expn_info_ = + build::mk_call( + cx, qsp, + ids_ext(cx, ~[~"expanded_from"]), + ~[build::mk_rec_e( + cx, qsp, + ~[{ident: id_ext(cx, ~"call_site"), + ex: mk_span(cx, qsp, cr.call_site)}, + {ident: id_ext(cx, ~"callie"), + ex: e_callee}])]); + + build::mk_call(cx, qsp, + ids_ext(cx, ~[~"Some"]), + ~[build::mk_managed(cx, qsp, e_expn_info_)]) + } + }; + + build::mk_rec_e(cx, qsp, + ~[{ident: id_ext(cx, ~"lo"), + ex: build::mk_uint(cx, qsp, sp.lo) }, + + {ident: id_ext(cx, ~"hi"), + ex: build::mk_uint(cx, qsp, sp.hi) }, + + {ident: id_ext(cx, ~"expn_info"), + ex: e_expn_info}]) +} + +// Lift an ident to the expr that evaluates to that ident. +// +// NB: this identifies the interner used when re-parsing the token tree +// with the interner used during initial parse. This is _wrong_ and we +// should be emitting a &str here and the token type should be ok with +// &static/str or &session/str. Longer-term issue. +fn mk_ident(cx: ext_ctxt, sp: span, ident: ast::ident) -> @ast::expr { + build::mk_struct_e(cx, sp, + ids_ext(cx, ~[~"ident"]), + ~[{ident: id_ext(cx, ~"repr"), + ex: build::mk_uint(cx, sp, ident.repr) }]) +} + + +fn mk_binop(cx: ext_ctxt, sp: span, bop: token::binop) -> @ast::expr { + let name = match bop { + PLUS => "PLUS", + MINUS => "MINUS", + STAR => "STAR", + SLASH => "SLASH", + PERCENT => "PERCENT", + CARET => "CARET", + AND => "AND", + OR => "OR", + SHL => "SHL", + SHR => "SHR" + }; + build::mk_path(cx, sp, + ids_ext(cx, ~[name.to_owned()])) +} + +fn mk_token(cx: ext_ctxt, sp: span, tok: token::Token) -> @ast::expr { + + match tok { + BINOP(binop) => { + return build::mk_call(cx, sp, + ids_ext(cx, ~[~"BINOP"]), + ~[mk_binop(cx, sp, binop)]); + } + BINOPEQ(binop) => { + return build::mk_call(cx, sp, + ids_ext(cx, ~[~"BINOPEQ"]), + ~[mk_binop(cx, sp, binop)]); + } + + LIT_INT(i, ity) => { + let s_ity = match ity { + ast::ty_i => ~"ty_i", + ast::ty_char => ~"ty_char", + ast::ty_i8 => ~"ty_i8", + ast::ty_i16 => ~"ty_i16", + ast::ty_i32 => ~"ty_i32", + ast::ty_i64 => ~"ty_i64" + }; + let e_ity = + build::mk_path(cx, sp, + ids_ext(cx, ~[s_ity])); + + let e_i64 = build::mk_lit(cx, sp, ast::lit_int(i, ast::ty_i64)); + + return build::mk_call(cx, sp, + ids_ext(cx, ~[~"LIT_INT"]), + ~[e_i64, e_ity]); + } + + LIT_UINT(u, uty) => { + let s_uty = match uty { + ast::ty_u => ~"ty_u", + ast::ty_u8 => ~"ty_u8", + ast::ty_u16 => ~"ty_u16", + ast::ty_u32 => ~"ty_u32", + ast::ty_u64 => ~"ty_u64" + }; + let e_uty = + build::mk_path(cx, sp, + ids_ext(cx, ~[s_uty])); + + let e_u64 = build::mk_lit(cx, sp, ast::lit_uint(u, ast::ty_u64)); + + return build::mk_call(cx, sp, + ids_ext(cx, ~[~"LIT_UINT"]), + ~[e_u64, e_uty]); + } + + LIT_INT_UNSUFFIXED(i) => { + let e_i64 = build::mk_lit(cx, sp, + ast::lit_int(i, ast::ty_i64)); + + return build::mk_call(cx, sp, + ids_ext(cx, ~[~"LIT_INT_UNSUFFIXED"]), + ~[e_i64]); + } + + LIT_FLOAT(fident, fty) => { + let s_fty = match fty { + ast::ty_f => ~"ty_f", + ast::ty_f32 => ~"ty_f32", + ast::ty_f64 => ~"ty_f64" + }; + let e_fty = + build::mk_path(cx, sp, + ids_ext(cx, ~[s_fty])); + + let e_fident = mk_ident(cx, sp, fident); + + return build::mk_call(cx, sp, + ids_ext(cx, ~[~"LIT_FLOAT"]), + ~[e_fident, e_fty]); + } + + LIT_STR(ident) => { + return build::mk_call(cx, sp, + ids_ext(cx, ~[~"LIT_STR"]), + ~[mk_ident(cx, sp, ident)]); + } + + IDENT(ident, b) => { + return build::mk_call(cx, sp, + ids_ext(cx, ~[~"IDENT"]), + ~[mk_ident(cx, sp, ident), + build::mk_lit(cx, sp, ast::lit_bool(b))]); + } + + DOC_COMMENT(ident) => { + return build::mk_call(cx, sp, + ids_ext(cx, ~[~"DOC_COMMENT"]), + ~[mk_ident(cx, sp, ident)]); + } + + INTERPOLATED(_) => fail ~"quote! with interpolated token", + + _ => () + } + + let name = match tok { + EQ => "EQ", + LT => "LT", + LE => "LE", + EQEQ => "EQEQ", + NE => "NE", + GE => "GE", + GT => "GT", + ANDAND => "ANDAND", + OROR => "OROR", + NOT => "NOT", + TILDE => "TILDE", + AT => "AT", + DOT => "DOT", + DOTDOT => "DOTDOT", + ELLIPSIS => "ELLIPSIS", + COMMA => "COMMA", + SEMI => "SEMI", + COLON => "COLON", + MOD_SEP => "MOD_SEP", + RARROW => "RARROW", + LARROW => "LARROW", + DARROW => "DARROW", + FAT_ARROW => "FAT_ARROW", + LPAREN => "LPAREN", + RPAREN => "RPAREN", + LBRACKET => "LBRACKET", + RBRACKET => "RBRACKET", + LBRACE => "LBRACE", + RBRACE => "RBRACE", + POUND => "POUND", + DOLLAR => "DOLLAR", + UNDERSCORE => "UNDERSCORE", + EOF => "EOF", + _ => fail + }; + build::mk_path(cx, sp, + ids_ext(cx, ~[name.to_owned()])) +} + + +fn mk_tt(cx: ext_ctxt, sp: span, tt: &ast::token_tree) -> @ast::expr { + match *tt { + ast::tt_tok(sp, tok) => + build::mk_call(cx, sp, + ids_ext(cx, ~[~"tt_tok"]), + ~[mk_span(cx, sp, sp), + mk_token(cx, sp, tok)]), + + ast::tt_delim(tts) => { + let e_tts = tts.map(|tt| mk_tt(cx, sp, tt)); + build::mk_call(cx, sp, + ids_ext(cx, ~[~"tt_delim"]), + ~[build::mk_uniq_vec_e(cx, sp, e_tts)]) + } + + ast::tt_seq(*) => fail ~"tt_seq in quote!", + + ast::tt_nonterminal(sp, ident) => + build::mk_copy(cx, sp, build::mk_path(cx, sp, ~[ident])) + } +} \ No newline at end of file diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index f02ab97921af..c5f3b1b53061 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1084,7 +1084,7 @@ impl Parser { self.bump(); let tts = match self.token { token::LPAREN | token::LBRACE => { - let ket = token::flip_delimiter(self.token); + let ket = token::flip_delimiter(copy self.token); self.parse_unspanned_seq(copy self.token, ket, seq_sep_none(), |p| p.parse_token_tree()) @@ -1279,6 +1279,7 @@ impl Parser { maybe_whole!(deref self, nt_tt); fn parse_tt_tok(p: Parser, delim_ok: bool) -> token_tree { + maybe_whole!(deref p, nt_tt); match p.token { token::RPAREN | token::RBRACE | token::RBRACKET if !delim_ok => { @@ -3419,7 +3420,7 @@ impl Parser { let id = self.parse_ident(); let tts = match self.token { token::LPAREN | token::LBRACE => { - let ket = token::flip_delimiter(self.token); + let ket = token::flip_delimiter(copy self.token); self.parse_unspanned_seq(copy self.token, ket, seq_sep_none(), |p| p.parse_token_tree()) diff --git a/src/libsyntax/syntax.rc b/src/libsyntax/syntax.rc index 7c73deed1a91..d0a9154b00e2 100644 --- a/src/libsyntax/syntax.rc +++ b/src/libsyntax/syntax.rc @@ -102,6 +102,9 @@ mod ext { mod expand; #[legacy_exports] mod qquote; + + mod quote; + #[legacy_exports] mod build;