]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/deriving/cmp/ord.rs
Add a doctest for the std::string::as_string method.
[rust.git] / src / libsyntax / ext / deriving / cmp / ord.rs
1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 pub use self::OrderingOp::*;
12
13 use ast;
14 use ast::{MetaItem, Item, Expr};
15 use codemap::Span;
16 use ext::base::ExtCtxt;
17 use ext::build::AstBuilder;
18 use ext::deriving::generic::*;
19 use ext::deriving::generic::ty::*;
20 use parse::token::InternedString;
21 use ptr::P;
22
23 pub fn expand_deriving_ord(cx: &mut ExtCtxt,
24                            span: Span,
25                            mitem: &MetaItem,
26                            item: &Item,
27                            push: |P<Item>|) {
28     macro_rules! md (
29         ($name:expr, $op:expr, $equal:expr) => { {
30             let inline = cx.meta_word(span, InternedString::new("inline"));
31             let attrs = vec!(cx.attribute(span, inline));
32             MethodDef {
33                 name: $name,
34                 generics: LifetimeBounds::empty(),
35                 explicit_self: borrowed_explicit_self(),
36                 args: vec!(borrowed_self()),
37                 ret_ty: Literal(Path::new(vec!("bool"))),
38                 attributes: attrs,
39                 combine_substructure: combine_substructure(|cx, span, substr| {
40                     cs_op($op, $equal, cx, span, substr)
41                 })
42             }
43         } }
44     );
45
46     let ordering_ty = Literal(Path::new(vec!["std", "cmp", "Ordering"]));
47     let ret_ty = Literal(Path::new_(vec!["std", "option", "Option"],
48                                     None,
49                                     vec![box ordering_ty],
50                                     true));
51
52     let inline = cx.meta_word(span, InternedString::new("inline"));
53     let attrs = vec!(cx.attribute(span, inline));
54
55     let partial_cmp_def = MethodDef {
56         name: "partial_cmp",
57         generics: LifetimeBounds::empty(),
58         explicit_self: borrowed_explicit_self(),
59         args: vec![borrowed_self()],
60         ret_ty: ret_ty,
61         attributes: attrs,
62         combine_substructure: combine_substructure(|cx, span, substr| {
63             cs_partial_cmp(cx, span, substr)
64         })
65     };
66
67     let trait_def = TraitDef {
68         span: span,
69         attributes: vec![],
70         path: Path::new(vec!["std", "cmp", "PartialOrd"]),
71         additional_bounds: vec![],
72         generics: LifetimeBounds::empty(),
73         methods: vec![
74             partial_cmp_def,
75             md!("lt", true, false),
76             md!("le", true, true),
77             md!("gt", false, false),
78             md!("ge", false, true)
79         ]
80     };
81     trait_def.expand(cx, mitem, item, push)
82 }
83
84 pub enum OrderingOp {
85     PartialCmpOp, LtOp, LeOp, GtOp, GeOp,
86 }
87
88 pub fn some_ordering_collapsed(cx: &mut ExtCtxt,
89                                span: Span,
90                                op: OrderingOp,
91                                self_arg_tags: &[ast::Ident]) -> P<ast::Expr> {
92     let lft = cx.expr_ident(span, self_arg_tags[0]);
93     let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1]));
94     let op_str = match op {
95         PartialCmpOp => "partial_cmp",
96         LtOp => "lt", LeOp => "le",
97         GtOp => "gt", GeOp => "ge",
98     };
99     cx.expr_method_call(span, lft, cx.ident_of(op_str), vec![rgt])
100 }
101
102 pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span,
103               substr: &Substructure) -> P<Expr> {
104     let test_id = cx.ident_of("__test");
105     let ordering = cx.path_global(span,
106                                   vec!(cx.ident_of("std"),
107                                        cx.ident_of("cmp"),
108                                        cx.ident_of("Ordering"),
109                                        cx.ident_of("Equal")));
110     let ordering = cx.expr_path(ordering);
111     let equals_expr = cx.expr_some(span, ordering);
112
113     let partial_cmp_path = vec![
114         cx.ident_of("std"),
115         cx.ident_of("cmp"),
116         cx.ident_of("PartialOrd"),
117         cx.ident_of("partial_cmp"),
118     ];
119
120     /*
121     Builds:
122
123     let __test = ::std::cmp::PartialOrd::partial_cmp(&self_field1, &other_field1);
124     if __test == ::std::option::Option::Some(::std::cmp::Ordering::Equal) {
125         let __test = ::std::cmp::PartialOrd::partial_cmp(&self_field2, &other_field2);
126         if __test == ::std::option::Option::Some(::std::cmp::Ordering::Equal) {
127             ...
128         } else {
129             __test
130         }
131     } else {
132         __test
133     }
134
135     FIXME #6449: These `if`s could/should be `match`es.
136     */
137     cs_fold(
138         // foldr nests the if-elses correctly, leaving the first field
139         // as the outermost one, and the last as the innermost.
140         false,
141         |cx, span, old, self_f, other_fs| {
142             // let __test = new;
143             // if __test == Some(::std::cmp::Ordering::Equal) {
144             //    old
145             // } else {
146             //    __test
147             // }
148
149             let new = {
150                 let other_f = match other_fs {
151                     [ref o_f] => o_f,
152                     _ => cx.span_bug(span, "not exactly 2 arguments in `deriving(PartialOrd)`"),
153                 };
154
155                 let args = vec![
156                     cx.expr_addr_of(span, self_f),
157                     cx.expr_addr_of(span, other_f.clone()),
158                 ];
159
160                 cx.expr_call_global(span, partial_cmp_path.clone(), args)
161             };
162
163             let assign = cx.stmt_let(span, false, test_id, new);
164
165             let cond = cx.expr_binary(span, ast::BiEq,
166                                       cx.expr_ident(span, test_id),
167                                       equals_expr.clone());
168             let if_ = cx.expr_if(span,
169                                  cond,
170                                  old, Some(cx.expr_ident(span, test_id)));
171             cx.expr_block(cx.block(span, vec!(assign), Some(if_)))
172         },
173         equals_expr.clone(),
174         |cx, span, (self_args, tag_tuple), _non_self_args| {
175             if self_args.len() != 2 {
176                 cx.span_bug(span, "not exactly 2 arguments in `deriving(PartialOrd)`")
177             } else {
178                 some_ordering_collapsed(cx, span, PartialCmpOp, tag_tuple)
179             }
180         },
181         cx, span, substr)
182 }
183
184 /// Strict inequality.
185 fn cs_op(less: bool, equal: bool, cx: &mut ExtCtxt,
186          span: Span, substr: &Substructure) -> P<Expr> {
187     let op = if less {ast::BiLt} else {ast::BiGt};
188     cs_fold(
189         false, // need foldr,
190         |cx, span, subexpr, self_f, other_fs| {
191             /*
192             build up a series of chain ||'s and &&'s from the inside
193             out (hence foldr) to get lexical ordering, i.e. for op ==
194             `ast::lt`
195
196             ```
197             self.f1 < other.f1 || (!(other.f1 < self.f1) &&
198                 (self.f2 < other.f2 || (!(other.f2 < self.f2) &&
199                     (false)
200                 ))
201             )
202             ```
203
204             The optimiser should remove the redundancy. We explicitly
205             get use the binops to avoid auto-deref dereferencing too many
206             layers of pointers, if the type includes pointers.
207             */
208             let other_f = match other_fs {
209                 [ref o_f] => o_f,
210                 _ => cx.span_bug(span, "not exactly 2 arguments in `deriving(PartialOrd)`")
211             };
212
213             let cmp = cx.expr_binary(span, op, self_f.clone(), other_f.clone());
214
215             let not_cmp = cx.expr_unary(span, ast::UnNot,
216                                         cx.expr_binary(span, op, other_f.clone(), self_f));
217
218             let and = cx.expr_binary(span, ast::BiAnd, not_cmp, subexpr);
219             cx.expr_binary(span, ast::BiOr, cmp, and)
220         },
221         cx.expr_bool(span, equal),
222         |cx, span, (self_args, tag_tuple), _non_self_args| {
223             if self_args.len() != 2 {
224                 cx.span_bug(span, "not exactly 2 arguments in `deriving(PartialOrd)`")
225             } else {
226                 let op = match (less, equal) {
227                     (true,  true) => LeOp, (true,  false) => LtOp,
228                     (false, true) => GeOp, (false, false) => GtOp,
229                 };
230                 some_ordering_collapsed(cx, span, op, tag_tuple)
231             }
232         },
233         cx, span, substr)
234 }