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 use ast;
12 use ast::{MetaItem, Item, Expr};
13 use codemap::Span;
14 use ext::base::ExtCtxt;
15 use ext::build::AstBuilder;
16 use ext::deriving::generic::*;
17 use parse::token::InternedString;
18
19 use std::cmp::{Ordering, Equal, Less, Greater};
20
21 pub fn expand_deriving_totalord(cx: &mut ExtCtxt,
22 span: Span,
23 mitem: @MetaItem,
24 item: @Item,
25 push: |@Item|) {
26 let inline = cx.meta_word(span, InternedString::new("inline"));
27 let attrs = vec!(cx.attribute(span, inline));
28 let trait_def = TraitDef {
29 span: span,
30 attributes: Vec::new(),
31 path: Path::new(vec!("std", "cmp", "TotalOrd")),
32 additional_bounds: Vec::new(),
33 generics: LifetimeBounds::empty(),
34 methods: vec!(
35 MethodDef {
36 name: "cmp",
37 generics: LifetimeBounds::empty(),
38 explicit_self: borrowed_explicit_self(),
39 args: vec!(borrowed_self()),
40 ret_ty: Literal(Path::new(vec!("std", "cmp", "Ordering"))),
41 attributes: attrs,
42 const_nonmatching: false,
43 combine_substructure: combine_substructure(|a, b, c| {
44 cs_cmp(a, b, c)
45 }),
46 }
47 )
48 };
49
50 trait_def.expand(cx, mitem, item, push)
51 }
52
53
54 pub fn ordering_const(cx: &mut ExtCtxt, span: Span, cnst: Ordering) -> ast::Path {
55 let cnst = match cnst {
56 Less => "Less",
57 Equal => "Equal",
58 Greater => "Greater"
59 };
60 cx.path_global(span,
61 vec!(cx.ident_of("std"),
62 cx.ident_of("cmp"),
63 cx.ident_of(cnst)))
64 }
65
66 pub fn cs_cmp(cx: &mut ExtCtxt, span: Span,
67 substr: &Substructure) -> @Expr {
68 let test_id = cx.ident_of("__test");
69 let equals_path = ordering_const(cx, span, Equal);
70
71 /*
72 Builds:
73
74 let __test = self_field1.cmp(&other_field2);
75 if other == ::std::cmp::Equal {
76 let __test = self_field2.cmp(&other_field2);
77 if __test == ::std::cmp::Equal {
78 ...
79 } else {
80 __test
81 }
82 } else {
83 __test
84 }
85
86 FIXME #6449: These `if`s could/should be `match`es.
87 */
88 cs_same_method_fold(
89 // foldr nests the if-elses correctly, leaving the first field
90 // as the outermost one, and the last as the innermost.
91 false,
92 |cx, span, old, new| {
93 // let __test = new;
94 // if __test == ::std::cmp::Equal {
95 // old
96 // } else {
97 // __test
98 // }
99
100 let assign = cx.stmt_let(span, false, test_id, new);
101
102 let cond = cx.expr_binary(span, ast::BiEq,
103 cx.expr_ident(span, test_id),
104 cx.expr_path(equals_path.clone()));
105 let if_ = cx.expr_if(span,
106 cond,
107 old, Some(cx.expr_ident(span, test_id)));
108 cx.expr_block(cx.block(span, vec!(assign), Some(if_)))
109 },
110 cx.expr_path(equals_path.clone()),
111 |cx, span, list, _| {
112 match list {
113 // an earlier nonmatching variant is Less than a
114 // later one.
115 [(self_var, _, _),
116 (other_var, _, _)] => {
117 let order = ordering_const(cx, span, self_var.cmp(&other_var));
118 cx.expr_path(order)
119 }
120 _ => cx.span_bug(span, "not exactly 2 arguments in `deriving(TotalOrd)`")
121 }
122 },
123 cx, span, substr)
124 }