create serializer project which autogenerates serialization code

This commit is contained in:
Niko Matsakis 2012-02-07 21:19:53 -08:00
parent 5d57fa3403
commit dbcb54f4dc
5 changed files with 1010 additions and 1 deletions

View file

@ -0,0 +1,17 @@
// -*- rust -*-
// serializer --- Rust metadata encoder/decoder generator.
//
// This tool is not intended to generate usable modules (just yet),
// though perhaps it will be beefed up. But it can do a lot of the
// grunt work for you.
#[link(name = "serializer",
vers = "0.1",
uuid = "9ff87a04-8fed-4295-9ff8-f99bb802650b",
url = "http://rust-lang.org/doc/serializer")];
#[crate_type = "bin"];
use std;
use rustc;

View file

@ -0,0 +1,294 @@
import rustc::driver::{driver,session};
import rustc::syntax::{ast, codemap};
import rustc::syntax::parse::parser;
import rustc::driver::diagnostic;
import rustc::syntax::print::pprust;
import rustc::syntax::codemap::span;
import rustc::middle::ty;
import rustc::middle::ast_map;
import rustc::util::ppaux;
import std::map::{hashmap, map, new_int_hash};
import std::getopts;
import std::io;
import std::io::writer_util;
import driver::build_session_options;
import driver::build_session;
import driver::build_configuration;
type parse_result = {
crate: @ast::crate,
tcx: ty::ctxt,
roots: [str]
};
fn parse(argv: [str]) -> parse_result {
let argv = argv;
let argv0 = vec::shift(argv);
let match = result::get(getopts::getopts(argv, driver::opts()));
let sessopts = build_session_options(match, diagnostic::emit);
let sess = build_session(sessopts, "", diagnostic::emit);
let (ifile, roots) = {
let free = match.free;
if check vec::is_not_empty(free) {
let t = vec::tail(free);
(free[0], t)
} else {
fail "No input filename given.";
}
};
let cfg = build_configuration(sess, argv0, ifile);
alt driver::compile_upto(sess, cfg, ifile, driver::cu_typeck, none) {
{crate, tcx: some(tcx)} { {crate:crate, tcx:tcx, roots:roots} }
_ { fail "no type context"; }
}
}
type ast_expr = str;
type ast_stmt = str;
type ast_blk = str;
type ast_pat = str;
type ast_ty = str;
type ast_item = str;
type tp_map = map<ast::node_id, ty::t>;
type serialize_ctx = {
crate: @ast::crate,
tcx: ty::ctxt,
tyfns: hashmap<ty::t, str>,
mutable item_fns: [ast_item],
mutable constants: [str]
// needed for #ast:
// opts: @{cfg: ast::crate_cfg},
// psess: parser::parse_sess
};
fn item_has_name(&&item: @ast::item, &&name: str) -> bool {
item.ident == name
}
fn lookup(_mod: ast::_mod, idx: uint, names: [str]) -> @ast::item {
let name = names[idx];
alt vec::find(_mod.items, bind item_has_name(_, name)) {
none { fail #fmt["cannot resolve name %s at index %u", name, idx]; }
some(item) if idx == vec::len(names) - 1u { item }
some(item) {
alt item.node {
ast::item_mod(_m) { lookup(_m, idx + 1u, names) }
_ { fail #fmt["name %s at index %u not a module", name, idx]; }
}
}
}
}
impl serialize_ctx for serialize_ctx {
// fn session() -> parser::parse_sess { self.psess }
fn add_item(item: ast_item) {
self.item_fns += [item];
}
fn mk_serialize_named_item_fn(name: str) -> str {
let names = str::split_str(name, "::");
let item = lookup(self.crate.node.module, 0u, names);
let def_id = {crate: ast::local_crate, node: item.id};
self.mk_serialize_item_fn(def_id, [])
}
fn tp_map(ty_params: [ast::ty_param], tps: [ty::t]) -> tp_map {
assert vec::len(tps) == vec::len(ty_params);
let tps_map = new_int_hash();
vec::iter2(ty_params, tps) {|tp_def,tp_val|
tps_map.insert(tp_def.id, tp_val);
}
ret tps_map;
}
fn path(mod_: [str], id: str) -> str {
str::connect(mod_ + [id], "::")
}
fn ident(mod_: [str], id: str) -> str {
str::connect(mod_ + [id], "_")
}
fn instantiate(id: ast::def_id, args: [ty::t]) -> ty::t {
let {bounds, ty} = ty::lookup_item_type(self.tcx, id);
// typeck should guarantee this
assert vec::len(*bounds) == vec::len(args);
ret if vec::len(args) == 0u {
ty
} else {
ty::substitute_type_params(self.tcx, args, ty)
};
}
fn mk_serialize_item_fn(id: ast::def_id,
tps: [ty::t]) -> str {
let item_ty = self.instantiate(id, tps);
self.mk_serialize_ty_fn(item_ty)
}
fn blk(stmts: [ast_stmt]) -> ast_blk {
"{" + str::connect(stmts, ";") + "}"
}
fn blk_expr(stmts: [ast_stmt]) -> ast_expr {
self.blk(stmts)
}
// Generates a function to serialize the given type.
// Returns an AST fragment that names this function.
fn serialize_ty(ty0: ty::t, v: ast_expr) -> ast_expr {
let fname = self.mk_serialize_ty_fn(ty0);
#fmt["%s(cx, %s)", fname, v]
}
fn mk_serialize_ty_fn(ty0: ty::t) -> str {
// check for existing function
alt self.tyfns.find(ty0) {
some(name) { ret name; }
none { /* fallthrough */ }
}
// define the name and insert into the hashtable
// in case of recursive calls:
let id = self.tyfns.size();
let ty0_str = ppaux::ty_to_str(self.tcx, ty0);
let name = #fmt["serialize_%u /*%s*/", id, ty0_str];
self.tyfns.insert(ty0, name);
let v = "v";
let body_node = alt ty::get(ty0).struct {
ty::ty_nil | ty::ty_bot { "()" }
ty::ty_int(_) { #fmt["serialize_i64(cx, %s as i64)", v] }
ty::ty_uint(_) { #fmt["serialize_u64(cx, %s as u64)", v] }
ty::ty_float(_) { #fmt["serialize_float(cx, %s as float)", v] }
ty::ty_bool { #fmt["serialize_bool(cx, %s)", v] }
ty::ty_str { #fmt["serialize_str(cx, %s)", v] }
ty::ty_enum(def_id, tps) { self.serialize_enum(v, def_id, tps) }
ty::ty_box(mt) | ty::ty_uniq(mt) | ty::ty_ptr(mt) {
self.serialize_ty(mt.ty, #fmt["*%s", v])
}
ty::ty_vec(mt) {
let selem = self.serialize_ty(mt.ty, "i");
#fmt["start_vec(cx); \
vec::iter(v) {|i| \
start_vec_item(cx); \
%s; \
end_vec_item(cx); \
} \
end_vec(cx);", selem]
}
ty::ty_rec(fields) {
let stmts = vec::map(fields) {|field|
let f_name = field.ident;
let f_ty = field.mt.ty;
self.serialize_ty(f_ty, #fmt["%s.%s", v, f_name])
};
self.blk_expr(stmts)
}
ty::ty_tup(tys) {
let (pat, stmts) = self.serialize_arm("", tys);
#fmt["alt %s { \
%s %s \
}", v, pat, self.blk_expr(stmts)]
}
ty::ty_constr(t, _) {
self.serialize_ty(t, v)
}
ty::ty_fn(_) |
ty::ty_iface(_, _) |
ty::ty_res(_, _, _) |
ty::ty_var(_) | ty::ty_param(_, _) |
ty::ty_self(_) | ty::ty_type | ty::ty_send_type |
ty::ty_opaque_closure_ptr(_) | ty::ty_opaque_box {
fail #fmt["Unhandled type %s", ty0_str]
}
};
let item = #fmt["fn %s(cx: ctxt, v: %s) {\
%s;\
}", name, ty0_str, body_node];
self.add_item(item);
ret name;
}
fn serialize_enum(v: ast_expr,
id: ast::def_id,
tps: [ty::t]) -> ast_expr {
let path = [];
let variants = ty::substd_enum_variants(self.tcx, id, tps);
let arms = vec::map(variants) {|variant|
let v_path = self.path(path, variant.name);
let n_args = vec::len(variant.args);
let (v_pat, stmts) = {
if n_args == 0u {
(v_path, [])
} else {
self.serialize_arm(v_path, variant.args)
}
};
let v_const = #fmt["at_%s", self.ident(path, variant.name)];
#fmt["%s { \
start_variant(cx, %s); \
%s \
end_variant(cx, %s); \
}", v_pat, v_const, self.blk(stmts), v_const]
};
#fmt["alt %s { \
%s \
}", v, str::connect(arms, "\n")]
}
fn serialize_arm(v_path: str, args: [ty::t]) -> (ast_pat, [ast_stmt]) {
let n_args = vec::len(args);
let arg_nms = vec::init_fn(n_args) {|i| #fmt["v%u", i] };
let v_pat =
#fmt["%s(%s)", v_path, str::connect(arg_nms, ", ")];
let stmts = vec::init_fn(n_args) {|i|
let arg_ty = args[i];
let serialize_expr =
self.serialize_ty(arg_ty, arg_nms[i]);
#fmt["%s;", serialize_expr]
};
(v_pat, stmts)
}
}
fn main(argv: [str]) {
let {crate, tcx, roots} = parse(argv);
let sctx: serialize_ctx = {
// let cm = codemap::new_codemap();
// let handler = diagnostic::mk_handler(option::none);
// let psess: parser::parse_sess = @{
// cm: cm,
// mutable next_id: 1,
// span_diagnostic: diagnostic::mk_span_handler(handler, cm),
// mutable chpos: 0u,
// mutable byte_pos: 0u
// };
{crate: crate,
tcx: tcx,
tyfns: ty::new_ty_hash::<str>(),
mutable item_fns: [],
mutable constants: []}
};
vec::iter(roots) {|root|
sctx.mk_serialize_named_item_fn(root);
}
let stdout = io::stdout();
vec::iter(copy sctx.item_fns) {|item|
stdout.write_str(#fmt["%s\n", item])
}
}

31
src/serializer/stest.rs Normal file
View file

@ -0,0 +1,31 @@
// Testing types for the serializer. This should be made more formal.
enum test1 {
t1_a(int), t1_b(str)
}
type test2 = {
f: int, g: str
};
enum test3 {
t3_a, t3_b
}
enum test4 {
t4_a(test1), t4_b(test2), t4_c(@test2), t4_d(@test4)
}
type spanned<A> = {
node: A,
span: { lo: uint, hi: uint }
};
type test5 = {
s1: spanned<test4>,
s2: spanned<uint>
};
type test6 = option<int>;
fn main() {}