Implement quasi-quoting of multiple syntatic categories.
This commit is contained in:
parent
35a199c036
commit
6dcd12dc22
@ -8,7 +8,7 @@ import vec;
|
||||
import syntax::ast::{crate, expr_, expr_mac, mac_invoc, mac_qq};
|
||||
import syntax::fold::*;
|
||||
import syntax::ext::base::*;
|
||||
import syntax::ext::qquote::expand_qquote;
|
||||
import syntax::ext::qquote::{expand_qquote,qq_helper};
|
||||
import syntax::parse::parser::parse_expr_from_source_str;
|
||||
|
||||
import codemap::span;
|
||||
|
@ -9,7 +9,7 @@ import syntax::visit::*;
|
||||
import syntax::ext::base::*;
|
||||
import syntax::ext::build::*;
|
||||
import syntax::parse::parser;
|
||||
import syntax::parse::parser::{parse_from_source_str};
|
||||
import syntax::parse::parser::{parser, parse_from_source_str};
|
||||
|
||||
import syntax::print::*;
|
||||
import std::io::*;
|
||||
@ -19,12 +19,53 @@ import codemap::span;
|
||||
type aq_ctxt = @{lo: uint,
|
||||
mutable gather: [{lo: uint, hi: uint, e: @ast::expr}]};
|
||||
|
||||
fn gather_anti_quotes(lo: uint, e: @ast::expr) -> aq_ctxt
|
||||
iface qq_helper {
|
||||
fn span() -> span;
|
||||
fn visit(aq_ctxt, vt<aq_ctxt>);
|
||||
fn mk_parse_fn(ext_ctxt,span) -> @ast::expr;
|
||||
}
|
||||
impl of qq_helper for @ast::expr {
|
||||
fn span() -> span {self.span}
|
||||
fn visit(cx: aq_ctxt, v: vt<aq_ctxt>) {visit_expr(self, cx, v);}
|
||||
fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr {
|
||||
mk_path(cx, sp, ["syntax", "parse", "parser", "parse_expr"])
|
||||
}
|
||||
}
|
||||
impl of qq_helper for @ast::ty {
|
||||
fn span() -> span {self.span}
|
||||
fn visit(cx: aq_ctxt, v: vt<aq_ctxt>) {visit_ty(self, cx, v);}
|
||||
fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr {
|
||||
mk_path(cx, sp, ["syntax", "ext", "qquote", "parse_ty"])
|
||||
}
|
||||
}
|
||||
impl of qq_helper for @ast::item {
|
||||
fn span() -> span {self.span}
|
||||
fn visit(cx: aq_ctxt, v: vt<aq_ctxt>) {visit_item(self, cx, v);}
|
||||
fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr {
|
||||
mk_path(cx, sp, ["syntax", "ext", "qquote", "parse_item"])
|
||||
}
|
||||
}
|
||||
impl of qq_helper for @ast::stmt {
|
||||
fn span() -> span {self.span}
|
||||
fn visit(cx: aq_ctxt, v: vt<aq_ctxt>) {visit_stmt(self, cx, v);}
|
||||
fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr {
|
||||
mk_path(cx, sp, ["syntax", "ext", "qquote", "parse_stmt"])
|
||||
}
|
||||
}
|
||||
impl of qq_helper for @ast::pat {
|
||||
fn span() -> span {self.span}
|
||||
fn visit(cx: aq_ctxt, v: vt<aq_ctxt>) {visit_pat(self, cx, v);}
|
||||
fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr {
|
||||
mk_path(cx, sp, ["syntax", "parse", "parser", "parse_pat"])
|
||||
}
|
||||
}
|
||||
|
||||
fn gather_anti_quotes<N: qq_helper>(lo: uint, node: N) -> aq_ctxt
|
||||
{
|
||||
let v = @{visit_expr: visit_expr_aq
|
||||
with *default_visitor()};
|
||||
let cx = @{lo:lo, mutable gather: []};
|
||||
visit_expr_aq(e, cx, mk_vt(v));
|
||||
node.visit(cx, mk_vt(v));
|
||||
ret cx;
|
||||
}
|
||||
|
||||
@ -43,31 +84,78 @@ fn is_space(c: char) -> bool {
|
||||
syntax::parse::lexer::is_whitespace(c)
|
||||
}
|
||||
|
||||
fn expand_ast(ecx: ext_ctxt, _sp: span, _arg:
|
||||
ast::mac_arg, body: ast::mac_body)
|
||||
fn expand_ast(ecx: ext_ctxt, _sp: span,
|
||||
arg: ast::mac_arg, body: ast::mac_body)
|
||||
-> @ast::expr
|
||||
{
|
||||
let what = "expr";
|
||||
option::may(arg) {|arg|
|
||||
let args: [@ast::expr] =
|
||||
alt arg.node {
|
||||
ast::expr_vec(elts, _) { elts }
|
||||
_ {
|
||||
ecx.span_fatal
|
||||
(_sp, "#ast requires arguments of the form `[...]`.")
|
||||
}
|
||||
};
|
||||
if vec::len::<@ast::expr>(args) != 1u {
|
||||
ecx.span_fatal(_sp, "#ast requires exactly one arg");
|
||||
}
|
||||
alt (args[0].node) {
|
||||
ast::expr_path(@{node: {idents: id, _},_}) if vec::len(id) == 1u
|
||||
{what = id[0]}
|
||||
_ {ecx.span_fatal(args[0].span, "expected an identifier");}
|
||||
}
|
||||
}
|
||||
let body = get_mac_body(ecx,_sp,body);
|
||||
let cm = ecx.session().parse_sess.cm;
|
||||
let str = @codemap::span_to_snippet(body.span, cm);
|
||||
let (fname, ss) = codemap::get_substr_info(cm,
|
||||
body.span.lo, body.span.hi);
|
||||
let {node: e, _} = parse_from_source_str(parser::parse_expr,
|
||||
fname, some(ss), str,
|
||||
ecx.session().opts.cfg,
|
||||
ecx.session().parse_sess);
|
||||
ret expand_qquote(ecx, e.span, some(*str), e);
|
||||
fn finish<T: qq_helper>(ecx: ext_ctxt, body: ast::mac_body_,
|
||||
f: fn (p: parser) -> T)
|
||||
-> @ast::expr
|
||||
{
|
||||
let cm = ecx.session().parse_sess.cm;
|
||||
let str = @codemap::span_to_snippet(body.span, cm);
|
||||
let (fname, ss) = codemap::get_substr_info
|
||||
(cm, body.span.lo, body.span.hi);
|
||||
let node = parse_from_source_str
|
||||
(f, fname, some(ss), str,
|
||||
ecx.session().opts.cfg, ecx.session().parse_sess);
|
||||
ret expand_qquote(ecx, node.span(), some(*str), node);
|
||||
}
|
||||
|
||||
ret alt what {
|
||||
"expr" {finish(ecx, body, parser::parse_expr)}
|
||||
"ty" {finish(ecx, body, parse_ty)}
|
||||
"item" {finish(ecx, body, parse_item)}
|
||||
"stmt" {finish(ecx, body, parse_stmt)}
|
||||
"pat" {finish(ecx, body, parser::parse_pat)}
|
||||
_ {ecx.span_fatal(_sp, "unsupported ast type")}
|
||||
};
|
||||
}
|
||||
|
||||
fn expand_qquote(ecx: ext_ctxt, sp: span, maybe_str: option::t<str>,
|
||||
e: @ast::expr)
|
||||
fn parse_ty(p: parser) -> @ast::ty {
|
||||
parser::parse_ty(p, false)
|
||||
}
|
||||
|
||||
fn parse_stmt(p: parser) -> @ast::stmt {
|
||||
parser::parse_stmt(p, [])
|
||||
}
|
||||
|
||||
fn parse_item(p: parser) -> @ast::item {
|
||||
alt (parser::parse_item(p, [])) {
|
||||
some(item) {item}
|
||||
none {fail; /* FIXME: Error message, somehow */}
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_qquote<N: qq_helper>
|
||||
(ecx: ext_ctxt, sp: span, maybe_str: option::t<str>, node: N)
|
||||
-> @ast::expr
|
||||
{
|
||||
let str = alt(maybe_str) {
|
||||
some(s) {s}
|
||||
none {codemap::span_to_snippet(sp, ecx.session().parse_sess.cm)}
|
||||
};
|
||||
let qcx = gather_anti_quotes(sp.lo, e);
|
||||
let qcx = gather_anti_quotes(sp.lo, node);
|
||||
let cx = qcx;
|
||||
let prev = 0u;
|
||||
for {lo: lo, _} in cx.gather {
|
||||
@ -107,8 +195,10 @@ fn expand_qquote(ecx: ext_ctxt, sp: span, maybe_str: option::t<str>,
|
||||
[]);
|
||||
let pcall = mk_call(cx,sp,
|
||||
["syntax", "parse", "parser",
|
||||
"parse_expr_from_source_str"],
|
||||
[mk_str(cx,sp, "<anon>"),
|
||||
"parse_from_source_str"],
|
||||
[node.mk_parse_fn(cx,sp),
|
||||
mk_str(cx,sp, "<anon>"),
|
||||
mk_path(cx,sp, ["option","none"]),
|
||||
mk_unary(cx,sp, ast::box(ast::imm),
|
||||
mk_str(cx,sp, str2)),
|
||||
mk_access_(cx,sp,
|
||||
|
@ -2548,13 +2548,13 @@ fn parse_from_source_str<T>(f: fn (p: parser) -> T,
|
||||
name: str, ss: codemap::file_substr,
|
||||
source: @str, cfg: ast::crate_cfg,
|
||||
sess: parse_sess)
|
||||
-> {node: T, fm: codemap::filemap}
|
||||
-> T
|
||||
{
|
||||
let p = new_parser_from_source_str(sess, cfg, name, ss, source);
|
||||
let r = f(p);
|
||||
sess.chpos = p.reader.chpos;
|
||||
sess.byte_pos = sess.byte_pos + p.reader.pos;
|
||||
ret {node: r, fm: option::get(vec::last(sess.cm.files))};
|
||||
ret r;
|
||||
}
|
||||
|
||||
fn parse_crate_from_source_str(name: str, source: @str, cfg: ast::crate_cfg,
|
||||
|
Loading…
x
Reference in New Issue
Block a user