| /* |
| |
| The compiler code necessary to implement the #[auto_serialize] and |
| #[auto_deserialize] extension. The idea here is that type-defining items may |
| be tagged with #[auto_serialize] and #[auto_deserialize], which will cause |
| us to generate a little companion module with the same name as the item. |
| |
| For example, a type like: |
| |
| #[auto_serialize] |
| #[auto_deserialize] |
| struct Node {id: uint} |
| |
| would generate two implementations like: |
| |
| impl Node: Serializable { |
| fn serialize<S: Serializer>(s: &S) { |
| do s.emit_struct("Node") { |
| s.emit_field("id", 0, || s.emit_uint(self)) |
| } |
| } |
| } |
| |
| impl node_id: Deserializable { |
| static fn deserialize<D: Deserializer>(d: &D) -> Node { |
| do d.read_struct("Node") { |
| Node { |
| id: d.read_field(~"x", 0, || deserialize(d)) |
| } |
| } |
| } |
| } |
| |
| Other interesting scenarios are whe the item has type parameters or |
| references other non-built-in types. A type definition like: |
| |
| #[auto_serialize] |
| #[auto_deserialize] |
| type spanned<T> = {node: T, span: span}; |
| |
| would yield functions like: |
| |
| impl<T: Serializable> spanned<T>: Serializable { |
| fn serialize<S: Serializer>(s: &S) { |
| do s.emit_rec { |
| s.emit_field("node", 0, || self.node.serialize(s)); |
| s.emit_field("span", 1, || self.span.serialize(s)); |
| } |
| } |
| } |
| |
| impl<T: Deserializable> spanned<T>: Deserializable { |
| static fn deserialize<D: Deserializer>(d: &D) -> spanned<T> { |
| do d.read_rec { |
| { |
| node: d.read_field(~"node", 0, || deserialize(d)), |
| span: d.read_field(~"span", 1, || deserialize(d)), |
| } |
| } |
| } |
| } |
| |
| FIXME (#2810)--Hygiene. Search for "__" strings. We also assume "std" is the |
| standard library. |
| |
| Misc notes: |
| ----------- |
| |
| I use move mode arguments for ast nodes that will get inserted as is |
| into the tree. This is intended to prevent us from inserting the same |
| node twice. |
| |
| */ |
| |
| use base::*; |
| use codemap::span; |
| use std::map; |
| use std::map::HashMap; |
| |
| export expand_auto_serialize; |
| export expand_auto_deserialize; |
| |
| // Transitional reexports so qquote can find the paths it is looking for |
| mod syntax { |
| pub use ext; |
| pub use parse; |
| } |
| |
| fn expand_auto_serialize( |
| cx: ext_ctxt, |
| span: span, |
| _mitem: ast::meta_item, |
| in_items: ~[@ast::item] |
| ) -> ~[@ast::item] { |
| fn is_auto_serialize(a: &ast::attribute) -> bool { |
| attr::get_attr_name(*a) == ~"auto_serialize" |
| } |
| |
| fn filter_attrs(item: @ast::item) -> @ast::item { |
| @{attrs: vec::filter(item.attrs, |a| !is_auto_serialize(a)), |
| .. *item} |
| } |
| |
| do vec::flat_map(in_items) |item| { |
| if item.attrs.any(is_auto_serialize) { |
| match item.node { |
| ast::item_ty(@{node: ast::ty_rec(fields), _}, tps) => { |
| let ser_impl = mk_rec_ser_impl( |
| cx, |
| item.span, |
| item.ident, |
| fields, |
| tps |
| ); |
| |
| ~[filter_attrs(*item), ser_impl] |
| }, |
| ast::item_class(@{ fields, _}, tps) => { |
| let ser_impl = mk_struct_ser_impl( |
| cx, |
| item.span, |
| item.ident, |
| fields, |
| tps |
| ); |
| |
| ~[filter_attrs(*item), ser_impl] |
| }, |
| ast::item_enum(enum_def, tps) => { |
| let ser_impl = mk_enum_ser_impl( |
| cx, |
| item.span, |
| item.ident, |
| enum_def, |
| tps |
| ); |
| |
| ~[filter_attrs(*item), ser_impl] |
| }, |
| _ => { |
| cx.span_err(span, ~"#[auto_serialize] can only be \ |
| applied to structs, record types, \ |
| and enum definitions"); |
| ~[*item] |
| } |
| } |
| } else { |
| ~[*item] |
| } |
| } |
| } |
| |
| fn expand_auto_deserialize( |
| cx: ext_ctxt, |
| span: span, |
| _mitem: ast::meta_item, |
| in_items: ~[@ast::item] |
| ) -> ~[@ast::item] { |
| fn is_auto_deserialize(a: &ast::attribute) -> bool { |
| attr::get_attr_name(*a) == ~"auto_deserialize" |
| } |
| |
| fn filter_attrs(item: @ast::item) -> @ast::item { |
| @{attrs: vec::filter(item.attrs, |a| !is_auto_deserialize(a)), |
| .. *item} |
| } |
| |
| do vec::flat_map(in_items) |item| { |
| if item.attrs.any(is_auto_deserialize) { |
| match item.node { |
| ast::item_ty(@{node: ast::ty_rec(fields), _}, tps) => { |
| let deser_impl = mk_rec_deser_impl( |
| cx, |
| item.span, |
| item.ident, |
| fields, |
| tps |
| ); |
| |
| ~[filter_attrs(*item), deser_impl] |
| }, |
| ast::item_class(@{ fields, _}, tps) => { |
| let deser_impl = mk_struct_deser_impl( |
| cx, |
| item.span, |
| item.ident, |
| fields, |
| tps |
| ); |
| |
| ~[filter_attrs(*item), deser_impl] |
| }, |
| ast::item_enum(enum_def, tps) => { |
| let deser_impl = mk_enum_deser_impl( |
| cx, |
| item.span, |
| item.ident, |
| enum_def, |
| tps |
| ); |
| |
| ~[filter_attrs(*item), deser_impl] |
| }, |
| _ => { |
| cx.span_err(span, ~"#[auto_deserialize] can only be \ |
| applied to structs, record types, \ |
| and enum definitions"); |
| ~[*item] |
| } |
| } |
| } else { |
| ~[*item] |
| } |
| } |
| } |
| |
| priv impl ext_ctxt { |
| fn expr(span: span, node: ast::expr_) -> @ast::expr { |
| @{id: self.next_id(), callee_id: self.next_id(), |
| node: node, span: span} |
| } |
| |
| fn path(span: span, strs: ~[ast::ident]) -> @ast::path { |
| @{span: span, global: false, idents: strs, rp: None, types: ~[]} |
| } |
| |
| fn path_tps(span: span, strs: ~[ast::ident], |
| tps: ~[@ast::ty]) -> @ast::path { |
| @{span: span, global: false, idents: strs, rp: None, types: tps} |
| } |
| |
| fn ty_path(span: span, strs: ~[ast::ident], |
| tps: ~[@ast::ty]) -> @ast::ty { |
| @{id: self.next_id(), |
| node: ast::ty_path(self.path_tps(span, strs, tps), self.next_id()), |
| span: span} |
| } |
| |
| fn binder_pat(span: span, nm: ast::ident) -> @ast::pat { |
| let path = @{span: span, global: false, idents: ~[nm], |
| rp: None, types: ~[]}; |
| @{id: self.next_id(), |
| node: ast::pat_ident(ast::bind_by_implicit_ref, |
| path, |
| None), |
| span: span} |
| } |
| |
| fn stmt(expr: @ast::expr) -> @ast::stmt { |
| @{node: ast::stmt_semi(expr, self.next_id()), |
| span: expr.span} |
| } |
| |
| fn lit_str(span: span, s: @~str) -> @ast::expr { |
| self.expr( |
| span, |
| ast::expr_vstore( |
| self.expr( |
| span, |
| ast::expr_lit( |
| @{node: ast::lit_str(s), |
| span: span})), |
| ast::expr_vstore_uniq)) |
| } |
| |
| fn lit_uint(span: span, i: uint) -> @ast::expr { |
| self.expr( |
| span, |
| ast::expr_lit( |
| @{node: ast::lit_uint(i as u64, ast::ty_u), |
| span: span})) |
| } |
| |
| fn lambda(blk: ast::blk) -> @ast::expr { |
| let ext_cx = self; |
| let blk_e = self.expr(blk.span, ast::expr_block(blk)); |
| #ast{ || $(blk_e) } |
| } |
| |
| fn blk(span: span, stmts: ~[@ast::stmt]) -> ast::blk { |
| {node: {view_items: ~[], |
| stmts: stmts, |
| expr: None, |
| id: self.next_id(), |
| rules: ast::default_blk}, |
| span: span} |
| } |
| |
| fn expr_blk(expr: @ast::expr) -> ast::blk { |
| {node: {view_items: ~[], |
| stmts: ~[], |
| expr: Some(expr), |
| id: self.next_id(), |
| rules: ast::default_blk}, |
| span: expr.span} |
| } |
| |
| fn expr_path(span: span, strs: ~[ast::ident]) -> @ast::expr { |
| self.expr(span, ast::expr_path(self.path(span, strs))) |
| } |
| |
| fn expr_var(span: span, var: ~str) -> @ast::expr { |
| self.expr_path(span, ~[self.ident_of(var)]) |
| } |
| |
| fn expr_field( |
| span: span, |
| expr: @ast::expr, |
| ident: ast::ident |
| ) -> @ast::expr { |
| self.expr(span, ast::expr_field(expr, ident, ~[])) |
| } |
| |
| fn expr_call( |
| span: span, |
| expr: @ast::expr, |
| args: ~[@ast::expr] |
| ) -> @ast::expr { |
| self.expr(span, ast::expr_call(expr, args, false)) |
| } |
| |
| fn lambda_expr(expr: @ast::expr) -> @ast::expr { |
| self.lambda(self.expr_blk(expr)) |
| } |
| |
| fn lambda_stmts(span: span, stmts: ~[@ast::stmt]) -> @ast::expr { |
| self.lambda(self.blk(span, stmts)) |
| } |
| } |
| |
| fn mk_impl( |
| cx: ext_ctxt, |
| span: span, |
| ident: ast::ident, |
| path: @ast::path, |
| tps: ~[ast::ty_param], |
| f: fn(@ast::ty) -> @ast::method |
| ) -> @ast::item { |
| // All the type parameters need to bound to the trait. |
| let trait_tps = do tps.map |tp| { |
| let t_bound = ast::bound_trait(@{ |
| id: cx.next_id(), |
| node: ast::ty_path(path, cx.next_id()), |
| span: span, |
| }); |
| |
| { |
| ident: tp.ident, |
| id: cx.next_id(), |
| bounds: @vec::append(~[t_bound], *tp.bounds) |
| } |
| }; |
| |
| let opt_trait = Some(@{ |
| path: path, |
| ref_id: cx.next_id(), |
| impl_id: cx.next_id(), |
| }); |
| |
| let ty = cx.ty_path( |
| span, |
| ~[ident], |
| tps.map(|tp| cx.ty_path(span, ~[tp.ident], ~[])) |
| ); |
| |
| @{ |
| // This is a new-style impl declaration. |
| // XXX: clownshoes |
| ident: ast::token::special_idents::clownshoes_extensions, |
| attrs: ~[], |
| id: cx.next_id(), |
| node: ast::item_impl(trait_tps, opt_trait, ty, ~[f(ty)]), |
| vis: ast::public, |
| span: span, |
| } |
| } |
| |
| fn mk_ser_impl( |
| cx: ext_ctxt, |
| span: span, |
| ident: ast::ident, |
| tps: ~[ast::ty_param], |
| body: @ast::expr |
| ) -> @ast::item { |
| // Make a path to the std::serialization::Serializable trait. |
| let path = cx.path( |
| span, |
| ~[ |
| cx.ident_of(~"std"), |
| cx.ident_of(~"serialization"), |
| cx.ident_of(~"Serializable"), |
| ] |
| ); |
| |
| mk_impl( |
| cx, |
| span, |
| ident, |
| path, |
| tps, |
| |_ty| mk_ser_method(cx, span, cx.expr_blk(body)) |
| ) |
| } |
| |
| fn mk_deser_impl( |
| cx: ext_ctxt, |
| span: span, |
| ident: ast::ident, |
| tps: ~[ast::ty_param], |
| body: @ast::expr |
| ) -> @ast::item { |
| // Make a path to the std::serialization::Deserializable trait. |
| let path = cx.path( |
| span, |
| ~[ |
| cx.ident_of(~"std"), |
| cx.ident_of(~"serialization"), |
| cx.ident_of(~"Deserializable"), |
| ] |
| ); |
| |
| mk_impl( |
| cx, |
| span, |
| ident, |
| path, |
| tps, |
| |ty| mk_deser_method(cx, span, ty, cx.expr_blk(body)) |
| ) |
| } |
| |
| fn mk_ser_method( |
| cx: ext_ctxt, |
| span: span, |
| ser_body: ast::blk |
| ) -> @ast::method { |
| let ser_bound = cx.ty_path( |
| span, |
| ~[ |
| cx.ident_of(~"std"), |
| cx.ident_of(~"serialization"), |
| cx.ident_of(~"Serializer"), |
| ], |
| ~[] |
| ); |
| |
| let ser_tps = ~[{ |
| ident: cx.ident_of(~"__S"), |
| id: cx.next_id(), |
| bounds: @~[ast::bound_trait(ser_bound)], |
| }]; |
| |
| let ty_s = @{ |
| id: cx.next_id(), |
| node: ast::ty_rptr( |
| @{ |
| id: cx.next_id(), |
| node: ast::re_anon, |
| }, |
| { |
| ty: cx.ty_path(span, ~[cx.ident_of(~"__S")], ~[]), |
| mutbl: ast::m_imm |
| } |
| ), |
| span: span, |
| }; |
| |
| let ser_inputs = ~[{ |
| mode: ast::infer(cx.next_id()), |
| ty: ty_s, |
| ident: cx.ident_of(~"__s"), |
| id: cx.next_id(), |
| }]; |
| |
| let ser_output = @{ |
| id: cx.next_id(), |
| node: ast::ty_nil, |
| span: span, |
| }; |
| |
| let ser_decl = { |
| inputs: ser_inputs, |
| output: ser_output, |
| cf: ast::return_val, |
| }; |
| |
| @{ |
| ident: cx.ident_of(~"serialize"), |
| attrs: ~[], |
| tps: ser_tps, |
| self_ty: { node: ast::sty_region(ast::m_imm), span: span }, |
| purity: ast::impure_fn, |
| decl: ser_decl, |
| body: ser_body, |
| id: cx.next_id(), |
| span: span, |
| self_id: cx.next_id(), |
| vis: ast::public, |
| } |
| } |
| |
| fn mk_deser_method( |
| cx: ext_ctxt, |
| span: span, |
| ty: @ast::ty, |
| deser_body: ast::blk |
| ) -> @ast::method { |
| let deser_bound = cx.ty_path( |
| span, |
| ~[ |
| cx.ident_of(~"std"), |
| cx.ident_of(~"serialization"), |
| cx.ident_of(~"Deserializer"), |
| ], |
| ~[] |
| ); |
| |
| let deser_tps = ~[{ |
| ident: cx.ident_of(~"__D"), |
| id: cx.next_id(), |
| bounds: @~[ast::bound_trait(deser_bound)], |
| }]; |
| |
| let ty_d = @{ |
| id: cx.next_id(), |
| node: ast::ty_rptr( |
| @{ |
| id: cx.next_id(), |
| node: ast::re_anon, |
| }, |
| { |
| ty: cx.ty_path(span, ~[cx.ident_of(~"__D")], ~[]), |
| mutbl: ast::m_imm |
| } |
| ), |
| span: span, |
| }; |
| |
| let deser_inputs = ~[{ |
| mode: ast::infer(cx.next_id()), |
| ty: ty_d, |
| ident: cx.ident_of(~"__d"), |
| id: cx.next_id(), |
| }]; |
| |
| let deser_decl = { |
| inputs: deser_inputs, |
| output: ty, |
| cf: ast::return_val, |
| }; |
| |
| @{ |
| ident: cx.ident_of(~"deserialize"), |
| attrs: ~[], |
| tps: deser_tps, |
| self_ty: { node: ast::sty_static, span: span }, |
| purity: ast::impure_fn, |
| decl: deser_decl, |
| body: deser_body, |
| id: cx.next_id(), |
| span: span, |
| self_id: cx.next_id(), |
| vis: ast::public, |
| } |
| } |
| |
| fn mk_rec_ser_impl( |
| cx: ext_ctxt, |
| span: span, |
| ident: ast::ident, |
| fields: ~[ast::ty_field], |
| tps: ~[ast::ty_param] |
| ) -> @ast::item { |
| let fields = mk_ser_fields(cx, span, mk_rec_fields(fields)); |
| |
| // ast for `__s.emit_rec(|| $(fields))` |
| let body = cx.expr_call( |
| span, |
| cx.expr_field( |
| span, |
| cx.expr_var(span, ~"__s"), |
| cx.ident_of(~"emit_rec") |
| ), |
| ~[cx.lambda_stmts(span, fields)] |
| ); |
| |
| mk_ser_impl(cx, span, ident, tps, body) |
| } |
| |
| fn mk_rec_deser_impl( |
| cx: ext_ctxt, |
| span: span, |
| ident: ast::ident, |
| fields: ~[ast::ty_field], |
| tps: ~[ast::ty_param] |
| ) -> @ast::item { |
| let fields = mk_deser_fields(cx, span, mk_rec_fields(fields)); |
| |
| // ast for `read_rec(|| $(fields))` |
| let body = cx.expr_call( |
| span, |
| cx.expr_field( |
| span, |
| cx.expr_var(span, ~"__d"), |
| cx.ident_of(~"read_rec") |
| ), |
| ~[ |
| cx.lambda_expr( |
| cx.expr( |
| span, |
| ast::expr_rec(fields, None) |
| ) |
| ) |
| ] |
| ); |
| |
| mk_deser_impl(cx, span, ident, tps, body) |
| } |
| |
| fn mk_struct_ser_impl( |
| cx: ext_ctxt, |
| span: span, |
| ident: ast::ident, |
| fields: ~[@ast::struct_field], |
| tps: ~[ast::ty_param] |
| ) -> @ast::item { |
| let fields = mk_ser_fields(cx, span, mk_struct_fields(fields)); |
| |
| // ast for `__s.emit_struct($(name), || $(fields))` |
| let ser_body = cx.expr_call( |
| span, |
| cx.expr_field( |
| span, |
| cx.expr_var(span, ~"__s"), |
| cx.ident_of(~"emit_struct") |
| ), |
| ~[ |
| cx.lit_str(span, @cx.str_of(ident)), |
| cx.lambda_stmts(span, fields), |
| ] |
| ); |
| |
| mk_ser_impl(cx, span, ident, tps, ser_body) |
| } |
| |
| fn mk_struct_deser_impl( |
| cx: ext_ctxt, |
| span: span, |
| ident: ast::ident, |
| fields: ~[@ast::struct_field], |
| tps: ~[ast::ty_param] |
| ) -> @ast::item { |
| let fields = mk_deser_fields(cx, span, mk_struct_fields(fields)); |
| |
| // ast for `read_struct($(name), || $(fields))` |
| let body = cx.expr_call( |
| span, |
| cx.expr_field( |
| span, |
| cx.expr_var(span, ~"__d"), |
| cx.ident_of(~"read_struct") |
| ), |
| ~[ |
| cx.lit_str(span, @cx.str_of(ident)), |
| cx.lambda_expr( |
| cx.expr( |
| span, |
| ast::expr_struct( |
| cx.path(span, ~[ident]), |
| fields, |
| None |
| ) |
| ) |
| ), |
| ] |
| ); |
| |
| mk_deser_impl(cx, span, ident, tps, body) |
| } |
| |
| // Records and structs don't have the same fields types, but they share enough |
| // that if we extract the right subfields out we can share the serialization |
| // generator code. |
| type field = { span: span, ident: ast::ident, mutbl: ast::mutability }; |
| |
| fn mk_rec_fields(fields: ~[ast::ty_field]) -> ~[field] { |
| do fields.map |field| { |
| { |
| span: field.span, |
| ident: field.node.ident, |
| mutbl: field.node.mt.mutbl, |
| } |
| } |
| } |
| |
| fn mk_struct_fields(fields: ~[@ast::struct_field]) -> ~[field] { |
| do fields.map |field| { |
| let (ident, mutbl) = match field.node.kind { |
| ast::named_field(ident, mutbl, _) => (ident, mutbl), |
| _ => fail ~"[auto_serialize] does not support \ |
| unnamed fields", |
| }; |
| |
| { |
| span: field.span, |
| ident: ident, |
| mutbl: match mutbl { |
| ast::class_mutable => ast::m_mutbl, |
| ast::class_immutable => ast::m_imm, |
| }, |
| } |
| } |
| } |
| |
| fn mk_ser_fields( |
| cx: ext_ctxt, |
| span: span, |
| fields: ~[field] |
| ) -> ~[@ast::stmt] { |
| do fields.mapi |idx, field| { |
| // ast for `|| self.$(name).serialize(__s)` |
| let expr_lambda = cx.lambda_expr( |
| cx.expr_call( |
| span, |
| cx.expr_field( |
| span, |
| cx.expr_field( |
| span, |
| cx.expr_var(span, ~"self"), |
| field.ident |
| ), |
| cx.ident_of(~"serialize") |
| ), |
| ~[cx.expr_var(span, ~"__s")] |
| ) |
| ); |
| |
| // ast for `__s.emit_field($(name), $(idx), $(expr_lambda))` |
| cx.stmt( |
| cx.expr_call( |
| span, |
| cx.expr_field( |
| span, |
| cx.expr_var(span, ~"__s"), |
| cx.ident_of(~"emit_field") |
| ), |
| ~[ |
| cx.lit_str(span, @cx.str_of(field.ident)), |
| cx.lit_uint(span, idx), |
| expr_lambda, |
| ] |
| ) |
| ) |
| } |
| } |
| |
| fn mk_deser_fields( |
| cx: ext_ctxt, |
| span: span, |
| fields: ~[{ span: span, ident: ast::ident, mutbl: ast::mutability }] |
| ) -> ~[ast::field] { |
| do fields.mapi |idx, field| { |
| // ast for `|| std::serialization::deserialize(__d)` |
| let expr_lambda = cx.lambda( |
| cx.expr_blk( |
| cx.expr_call( |
| span, |
| cx.expr_path(span, ~[ |
| cx.ident_of(~"std"), |
| cx.ident_of(~"serialization"), |
| cx.ident_of(~"deserialize"), |
| ]), |
| ~[cx.expr_var(span, ~"__d")] |
| ) |
| ) |
| ); |
| |
| // ast for `__d.read_field($(name), $(idx), $(expr_lambda))` |
| let expr: @ast::expr = cx.expr_call( |
| span, |
| cx.expr_field( |
| span, |
| cx.expr_var(span, ~"__d"), |
| cx.ident_of(~"read_field") |
| ), |
| ~[ |
| cx.lit_str(span, @cx.str_of(field.ident)), |
| cx.lit_uint(span, idx), |
| expr_lambda, |
| ] |
| ); |
| |
| { |
| node: { mutbl: field.mutbl, ident: field.ident, expr: expr }, |
| span: span, |
| } |
| } |
| } |
| |
| fn mk_enum_ser_impl( |
| cx: ext_ctxt, |
| span: span, |
| ident: ast::ident, |
| enum_def: ast::enum_def, |
| tps: ~[ast::ty_param] |
| ) -> @ast::item { |
| let body = mk_enum_ser_body( |
| cx, |
| span, |
| ident, |
| enum_def.variants |
| ); |
| |
| mk_ser_impl(cx, span, ident, tps, body) |
| } |
| |
| fn mk_enum_deser_impl( |
| cx: ext_ctxt, |
| span: span, |
| ident: ast::ident, |
| enum_def: ast::enum_def, |
| tps: ~[ast::ty_param] |
| ) -> @ast::item { |
| let body = mk_enum_deser_body( |
| cx, |
| span, |
| ident, |
| enum_def.variants |
| ); |
| |
| mk_deser_impl(cx, span, ident, tps, body) |
| } |
| |
| fn ser_variant( |
| cx: ext_ctxt, |
| span: span, |
| v_name: ast::ident, |
| v_idx: uint, |
| args: ~[ast::variant_arg] |
| ) -> ast::arm { |
| // Name the variant arguments. |
| let names = args.mapi(|i, _arg| cx.ident_of(fmt!("__v%u", i))); |
| |
| // Bind the names to the variant argument type. |
| let pats = args.mapi(|i, arg| cx.binder_pat(arg.ty.span, names[i])); |
| |
| let pat_node = if pats.is_empty() { |
| ast::pat_ident( |
| ast::bind_by_implicit_ref, |
| cx.path(span, ~[v_name]), |
| None |
| ) |
| } else { |
| ast::pat_enum( |
| cx.path(span, ~[v_name]), |
| Some(pats) |
| ) |
| }; |
| |
| let pat = @{ |
| id: cx.next_id(), |
| node: pat_node, |
| span: span, |
| }; |
| |
| let stmts = do args.mapi |a_idx, _arg| { |
| // ast for `__s.emit_enum_variant_arg` |
| let expr_emit = cx.expr_field( |
| span, |
| cx.expr_var(span, ~"__s"), |
| cx.ident_of(~"emit_enum_variant_arg") |
| ); |
| |
| // ast for `|| $(v).serialize(__s)` |
| let expr_serialize = cx.lambda_expr( |
| cx.expr_call( |
| span, |
| cx.expr_field( |
| span, |
| cx.expr_path(span, ~[names[a_idx]]), |
| cx.ident_of(~"serialize") |
| ), |
| ~[cx.expr_var(span, ~"__s")] |
| ) |
| ); |
| |
| // ast for `$(expr_emit)($(a_idx), $(expr_serialize))` |
| cx.stmt( |
| cx.expr_call( |
| span, |
| expr_emit, |
| ~[cx.lit_uint(span, a_idx), expr_serialize] |
| ) |
| ) |
| }; |
| |
| // ast for `__s.emit_enum_variant($(name), $(idx), $(sz), $(lambda))` |
| let body = cx.expr_call( |
| span, |
| cx.expr_field( |
| span, |
| cx.expr_var(span, ~"__s"), |
| cx.ident_of(~"emit_enum_variant") |
| ), |
| ~[ |
| cx.lit_str(span, @cx.str_of(v_name)), |
| cx.lit_uint(span, v_idx), |
| cx.lit_uint(span, stmts.len()), |
| cx.lambda_stmts(span, stmts), |
| ] |
| ); |
| |
| { pats: ~[pat], guard: None, body: cx.expr_blk(body) } |
| } |
| |
| fn mk_enum_ser_body( |
| cx: ext_ctxt, |
| span: span, |
| name: ast::ident, |
| variants: ~[ast::variant] |
| ) -> @ast::expr { |
| let arms = do variants.mapi |v_idx, variant| { |
| match variant.node.kind { |
| ast::tuple_variant_kind(args) => |
| ser_variant(cx, span, variant.node.name, v_idx, args), |
| ast::struct_variant_kind(*) => |
| fail ~"struct variants unimplemented", |
| ast::enum_variant_kind(*) => |
| fail ~"enum variants unimplemented", |
| } |
| }; |
| |
| // ast for `match *self { $(arms) }` |
| let match_expr = cx.expr( |
| span, |
| ast::expr_match( |
| cx.expr( |
| span, |
| ast::expr_unary(ast::deref, cx.expr_var(span, ~"self")) |
| ), |
| arms |
| ) |
| ); |
| |
| // ast for `__s.emit_enum($(name), || $(match_expr))` |
| cx.expr_call( |
| span, |
| cx.expr_field( |
| span, |
| cx.expr_var(span, ~"__s"), |
| cx.ident_of(~"emit_enum") |
| ), |
| ~[ |
| cx.lit_str(span, @cx.str_of(name)), |
| cx.lambda_expr(match_expr), |
| ] |
| ) |
| } |
| |
| fn mk_enum_deser_variant_nary( |
| cx: ext_ctxt, |
| span: span, |
| name: ast::ident, |
| args: ~[ast::variant_arg] |
| ) -> @ast::expr { |
| let args = do args.mapi |idx, _arg| { |
| // ast for `|| std::serialization::deserialize(__d)` |
| let expr_lambda = cx.lambda_expr( |
| cx.expr_call( |
| span, |
| cx.expr_path(span, ~[ |
| cx.ident_of(~"std"), |
| cx.ident_of(~"serialization"), |
| cx.ident_of(~"deserialize"), |
| ]), |
| ~[cx.expr_var(span, ~"__d")] |
| ) |
| ); |
| |
| // ast for `__d.read_enum_variant_arg($(a_idx), $(expr_lambda))` |
| cx.expr_call( |
| span, |
| cx.expr_field( |
| span, |
| cx.expr_var(span, ~"__d"), |
| cx.ident_of(~"read_enum_variant_arg") |
| ), |
| ~[cx.lit_uint(span, idx), expr_lambda] |
| ) |
| }; |
| |
| // ast for `$(name)($(args))` |
| cx.expr_call(span, cx.expr_path(span, ~[name]), args) |
| } |
| |
| fn mk_enum_deser_body( |
| cx: ext_ctxt, |
| span: span, |
| name: ast::ident, |
| variants: ~[ast::variant] |
| ) -> @ast::expr { |
| let mut arms = do variants.mapi |v_idx, variant| { |
| let body = match variant.node.kind { |
| ast::tuple_variant_kind(args) => { |
| if args.is_empty() { |
| // for a nullary variant v, do "v" |
| cx.expr_path(span, ~[variant.node.name]) |
| } else { |
| // for an n-ary variant v, do "v(a_1, ..., a_n)" |
| mk_enum_deser_variant_nary( |
| cx, |
| span, |
| variant.node.name, |
| args |
| ) |
| } |
| }, |
| ast::struct_variant_kind(*) => |
| fail ~"struct variants unimplemented", |
| ast::enum_variant_kind(*) => |
| fail ~"enum variants unimplemented", |
| }; |
| |
| let pat = @{ |
| id: cx.next_id(), |
| node: ast::pat_lit(cx.lit_uint(span, v_idx)), |
| span: span, |
| }; |
| |
| { |
| pats: ~[pat], |
| guard: None, |
| body: cx.expr_blk(body), |
| } |
| }; |
| |
| let impossible_case = { |
| pats: ~[@{ id: cx.next_id(), node: ast::pat_wild, span: span}], |
| guard: None, |
| |
| // FIXME(#3198): proper error message |
| body: cx.expr_blk(cx.expr(span, ast::expr_fail(None))), |
| }; |
| |
| arms.push(impossible_case); |
| |
| // ast for `|i| { match i { $(arms) } }` |
| let expr_lambda = cx.expr( |
| span, |
| ast::expr_fn_block( |
| { |
| inputs: ~[{ |
| mode: ast::infer(cx.next_id()), |
| ty: @{ |
| id: cx.next_id(), |
| node: ast::ty_infer, |
| span: span |
| }, |
| ident: cx.ident_of(~"i"), |
| id: cx.next_id(), |
| }], |
| output: @{ |
| id: cx.next_id(), |
| node: ast::ty_infer, |
| span: span, |
| }, |
| cf: ast::return_val, |
| }, |
| cx.expr_blk( |
| cx.expr( |
| span, |
| ast::expr_match(cx.expr_var(span, ~"i"), arms) |
| ) |
| ), |
| @~[] |
| ) |
| ); |
| |
| // ast for `__d.read_enum_variant($(expr_lambda))` |
| let expr_lambda = cx.lambda_expr( |
| cx.expr_call( |
| span, |
| cx.expr_field( |
| span, |
| cx.expr_var(span, ~"__d"), |
| cx.ident_of(~"read_enum_variant") |
| ), |
| ~[expr_lambda] |
| ) |
| ); |
| |
| // ast for `__d.read_enum($(e_name), $(expr_lambda))` |
| cx.expr_call( |
| span, |
| cx.expr_field( |
| span, |
| cx.expr_var(span, ~"__d"), |
| cx.ident_of(~"read_enum") |
| ), |
| ~[ |
| cx.lit_str(span, @cx.str_of(name)), |
| expr_lambda |
| ] |
| ) |
| } |