]> git.lizzy.rs Git - rust.git/commitdiff
rustpkg: Massage for landing.
authorGraydon Hoare <graydon@mozilla.com>
Tue, 5 Feb 2013 01:12:31 +0000 (17:12 -0800)
committerGraydon Hoare <graydon@mozilla.com>
Sat, 16 Feb 2013 02:50:51 +0000 (18:50 -0800)
src/libcore/option.rs
src/librustpkg/rustpkg.rc
src/librustpkg/util.rs
src/libstd/semver.rs

index a90364c7d8cedb537cee6c70eab40be5c4398317..13d4ec6e10df66c9dc6daadc2f2811a89fd08b7a 100644 (file)
@@ -41,7 +41,7 @@
 
 */
 
-use cmp::Eq;
+use cmp::{Eq,Ord};
 use kinds::Copy;
 use option;
 use ptr;
@@ -56,6 +56,34 @@ pub enum Option<T> {
     Some(T),
 }
 
+pub impl<T:Ord> Ord for Option<T> {
+    pure fn lt(&self, other: &Option<T>) -> bool {
+        match (self, other) {
+            (&None, &None) => false,
+            (&None, &Some(_)) => true,
+            (&Some(_), &None) => false,
+            (&Some(ref a), &Some(ref b)) => *a < *b
+        }
+    }
+
+    pure fn le(&self, other: &Option<T>) -> bool {
+        match (self, other) {
+            (&None, &None) => true,
+            (&None, &Some(_)) => true,
+            (&Some(_), &None) => false,
+            (&Some(ref a), &Some(ref b)) => *a <= *b
+        }
+    }
+
+    pure fn ge(&self, other: &Option<T>) -> bool {
+        ! (self < other)
+    }
+
+    pure fn gt(&self, other: &Option<T>) -> bool {
+        ! (self <= other)
+    }
+}
+
 #[inline(always)]
 pub pure fn get<T: Copy>(opt: Option<T>) -> T {
     /*!
index 1d537976e5fbcd67b616bc03efe8f650f1683eff..5b610bbb1f8ea0c6dbf9105a3cc9f048af210c66 100644 (file)
@@ -20,6 +20,8 @@
 #[allow(vecs_implicitly_copyable,
         non_implicitly_copyable_typarams)];
 
+#[legacy_records];
+
 extern mod core(vers = "0.6");
 extern mod std(vers = "0.6");
 extern mod rustc(vers = "0.6");
@@ -29,10 +31,11 @@ use core::*;
 use io::{ReaderUtil, WriterUtil};
 use std::{json, semver, getopts};
 use std::net::url;
-use send_map::linear::LinearMap;
+use hashmap::linear::LinearMap;
 use rustc::metadata::filesearch;
 use rustc::driver::{driver, session};
 use syntax::{ast, attr, codemap, diagnostic, parse, visit};
+use syntax::codemap::spanned;
 
 mod usage;
 mod util;
@@ -83,7 +86,7 @@ impl PackageScript {
 
             for mis.each |a| {
                 match a.node {
-                    ast::meta_name_value(v, ast::spanned {
+                    ast::meta_name_value(v, spanned {
                                                 node: ast::lit_str(s),
                                                 span: _}) => {
                         match v {
@@ -106,7 +109,7 @@ impl PackageScript {
 
             for mis.each |a| {
                 match a.node {
-                    ast::meta_name_value(v, ast::spanned {
+                    ast::meta_name_value(v, spanned {
                                                 node: ast::lit_str(s),
                                                 span: _}) => {
                         match v {
@@ -127,7 +130,7 @@ impl PackageScript {
 
             for mis.each |a| {
                 match a.node {
-                    ast::meta_name_value(v, ast::spanned {
+                    ast::meta_name_value(v, spanned {
                                                 node: ast::lit_str(s),
                                                 span: _}) => {
                         match v {
@@ -156,7 +159,7 @@ impl PackageScript {
                             let (u, t) = load_pkg_dep_attr(mis);
 
                             if u.is_none() {
-                                fail ~"pkg_dep attr without a url value";
+                                fail!(~"pkg_dep attr without a url value");
                             }
 
                             deps.push((u.get(), t));
@@ -165,7 +168,7 @@ impl PackageScript {
                             let f = load_pkg_crate_attr(mis);
 
                             if f.is_none() {
-                                fail ~"pkg_file attr without a file value";
+                                fail!(~"pkg_file attr without a file value");
                             }
 
                             crates.push(f.get());
@@ -222,7 +225,7 @@ impl PackageScript {
 
     // Build the bootstrap and run a command
     // FIXME (#4432): Use workcache to only compile the script when changed
-    fn run(cmd: ~str, test: bool) -> int {
+    fn run(&self, cmd: ~str, test: bool) -> int {
         let work_dir = self.work_dir();
         let input = self.input;
         let sess = self.sess;
@@ -238,12 +241,12 @@ impl PackageScript {
         run::run_program(exe.to_str(), ~[root.to_str(), cmd, test.to_str()])
     }
 
-    fn hash() -> ~str {
+    fn hash(&self) -> ~str {
         fmt!("%s-%s-%s", self.name, util::hash(self.id + self.vers.to_str()),
                          self.vers.to_str())
     }
 
-    fn work_dir() -> Path {
+    fn work_dir(&self) -> Path {
         util::root().push(~"work").push(self.hash())
     }
 }
@@ -255,7 +258,7 @@ struct Ctx {
 }
 
 impl Ctx {
-    fn run(cmd: ~str, args: ~[~str]) {
+    fn run(&self, cmd: ~str, args: ~[~str]) {
         let root = util::root();
 
         util::need_dir(&root);
@@ -333,11 +336,11 @@ impl Ctx {
 
                 self.unprefer(name.get(), vers);
             }
-            _ => fail ~"reached an unhandled command"
+            _ => fail!(~"reached an unhandled command")
         }
     }
 
-    fn do_cmd(cmd: ~str) -> bool {
+    fn do_cmd(&self, cmd: ~str) -> bool {
         match cmd {
             ~"build" | ~"test" => {
                 util::error(~"that command cannot be manually called");
@@ -367,7 +370,7 @@ impl Ctx {
         status == 0
     }
 
-    fn build(dir: &Path, verbose: bool, opt: bool,
+    fn build(&self, dir: &Path, verbose: bool, opt: bool,
              test: bool) -> Option<PackageScript> {
         let cwd = &os::getcwd();
         let script = match PackageScript::parse(dir) {
@@ -450,12 +453,12 @@ impl Ctx {
         Some(script)
     }
 
-    fn compile(crate: &Path, dir: &Path, flags: ~[~str],
+    fn compile(&self, crate: &Path, dir: &Path, flags: ~[~str],
                cfgs: ~[~str], opt: bool, test: bool) -> bool {
         util::compile_crate(None, crate, dir, flags, cfgs, opt, test)
     }
 
-    fn clean() -> bool {
+    fn clean(&self) -> bool {
         let script = match PackageScript::parse(&os::getcwd()) {
             result::Ok(script) => script,
             result::Err(err) => {
@@ -480,23 +483,24 @@ impl Ctx {
         true
     }
 
-    fn info() {
+    fn info(&self) {
         if self.json {
             match PackageScript::parse(&os::getcwd()) {
                 result::Ok(script) => {
-                    let mut map = ~LinearMap();
+                    let mut map = ~LinearMap::new();
 
                     map.insert(~"id", json::String(script.id));
                     map.insert(~"name", json::String(script.name));
                     map.insert(~"vers", json::String(script.vers.to_str()));
                     map.insert(~"deps", json::List(do script.deps.map |&dep| {
                         let (url, target) = dep;
-                        let mut inner = ~LinearMap();
+                        let mut inner = ~LinearMap::new();
 
                         inner.insert(~"url", json::String(url));
 
                         if !target.is_none() {
-                            inner.insert(~"target", json::String(target.get()));
+                            inner.insert(~"target",
+                                         json::String(target.get()));
                         }
 
                         json::Object(inner)
@@ -519,7 +523,12 @@ impl Ctx {
             util::note(fmt!("id: %s", script.id));
             util::note(fmt!("name: %s", script.name));
             util::note(fmt!("vers: %s", script.vers.to_str()));
-            util::note(fmt!("deps: %s", if script.deps.len() > 0 { ~"" } else { ~"none" }));
+            util::note(fmt!("deps: %s",
+                            if script.deps.len() > 0 {
+                                ~""
+                            } else {
+                                ~"none"
+                            }));
 
             for script.deps.each |&dep| {
                 let (url, target) = dep;
@@ -532,7 +541,8 @@ impl Ctx {
         }
     }
 
-    fn install(url: Option<~str>, target: Option<~str>, cache: bool) -> bool {
+    fn install(&self, url: Option<~str>,
+               target: Option<~str>, cache: bool) -> bool {
         let mut success;
         let mut dir;
 
@@ -608,7 +618,7 @@ impl Ctx {
         true
     }
 
-    fn fetch(dir: &Path, url: ~str, target: Option<~str>) -> bool {
+    fn fetch(&self, dir: &Path, url: ~str, target: Option<~str>) -> bool {
         let url = if str::find_str(url, "://").is_none() {
             ~"http://" + url }
         else { url };
@@ -641,7 +651,7 @@ impl Ctx {
         }
     }
 
-    fn fetch_curl(dir: &Path, url: ~str) -> bool {
+    fn fetch_curl(&self, dir: &Path, url: ~str) -> bool {
         util::note(fmt!("fetching from %s using curl", url));
 
         let tar = dir.dir_path().push(&dir.file_path().to_str() + ~".tar");
@@ -666,7 +676,7 @@ impl Ctx {
         true
     }
 
-    fn fetch_git(dir: &Path, url: ~str, target: Option<~str>) -> bool {
+    fn fetch_git(&self, dir: &Path, url: ~str, target: Option<~str>) -> bool {
         util::note(fmt!("fetching from %s using git", url));
 
         // Git can't clone into a non-empty directory
@@ -698,7 +708,7 @@ impl Ctx {
         true
     }
 
-    fn prefer(id: ~str, vers: Option<~str>) -> bool {
+    fn prefer(&self, id: ~str, vers: Option<~str>) -> bool {
         let package = match util::get_pkg(id, vers) {
             result::Ok(package) => package,
             result::Err(err) => {
@@ -735,7 +745,7 @@ impl Ctx {
         true
     }
 
-    fn test() -> bool {
+    fn test(&self) -> bool {
         let script = match self.build(&os::getcwd(), false, false, true) {
             Some(script) => script,
             None => {
@@ -773,7 +783,7 @@ impl Ctx {
         true
     }
 
-    fn uninstall(id: ~str, vers: Option<~str>) -> bool {
+    fn uninstall(&self, id: ~str, vers: Option<~str>) -> bool {
         let package = match util::get_pkg(id, vers) {
             result::Ok(package) => package,
             result::Err(err) => {
@@ -812,7 +822,7 @@ impl Ctx {
         true
     }
 
-    fn unprefer(id: ~str, vers: Option<~str>) -> bool {
+    fn unprefer(&self, id: ~str, vers: Option<~str>) -> bool {
         let package = match util::get_pkg(id, vers) {
             result::Ok(package) => package,
             result::Err(err) => {
@@ -904,7 +914,7 @@ pub fn main() {
     Ctx {
         cfgs: cfgs,
         json: json,
-        mut dep_cache: LinearMap()
+        mut dep_cache: LinearMap::new()
     }.run(cmd, args);
 }
 
@@ -942,31 +952,31 @@ pub fn run(listeners: ~[Listener]) {
 }
 
 pub impl Crate {
-    pub fn flag(flag: ~str) -> Crate {
+    pub fn flag(&self, flag: ~str) -> Crate {
         Crate {
             flags: vec::append(copy self.flags, ~[flag]),
-            .. copy self
+            .. copy *self
         }
     }
 
-    pub fn flags(flags: ~[~str]) -> Crate {
+    pub fn flags(&self, flags: ~[~str]) -> Crate {
         Crate {
             flags: vec::append(copy self.flags, flags),
-            .. copy self
+            .. copy *self
         }
     }
 
-    pub fn cfg(cfg: ~str) -> Crate {
+    pub fn cfg(&self, cfg: ~str) -> Crate {
         Crate {
             cfgs: vec::append(copy self.cfgs, ~[cfg]),
-            .. copy self
+            .. copy *self
         }
     }
 
-    pub fn cfgs(cfgs: ~[~str]) -> Crate {
+    pub fn cfgs(&self, cfgs: ~[~str]) -> Crate {
         Crate {
             cfgs: vec::append(copy self.cfgs, cfgs),
-            .. copy self
+            .. copy *self
         }
     }
 }
index e19eb5e41d81be9ef679e72dc6a3da93deb28df9..1ad706742a8f6ec31b4ce44ba059ce2fe337f565 100644 (file)
@@ -9,17 +9,20 @@
 // except according to those terms.
 
 use core::*;
-use send_map::linear::LinearMap;
+use hashmap::linear::LinearMap;
 use rustc::metadata::filesearch;
 use rustc::driver::{driver, session};
 use syntax::ast_util::*;
 use syntax::{ast, attr, codemap, diagnostic, fold, parse, visit};
-use codemap::span;
+use codemap::{span, dummy_sp, spanned};
 use std::semver;
 use std::{json, term, sort, getopts};
 use getopts::groups::getopts;
 use Listener;
 
+use syntax::ext::base::{mk_ctxt, ext_ctxt};
+use syntax::ext::build;
+
 pub struct Package {
     id: ~str,
     vers: semver::Version,
@@ -30,7 +33,7 @@ pub struct Package {
 pub fn root() -> Path {
     match filesearch::get_rustpkg_root() {
         result::Ok(path) => path,
-        result::Err(err) => fail err
+        result::Err(err) => fail!(err)
     }
 }
 
@@ -59,23 +62,6 @@ pub fn parse_name(id: ~str) -> result::Result<~str, ~str> {
     result::Ok(parts.last())
 }
 
-fn mk_rustpkg_use(ctx: @ReadyCtx) -> @ast::view_item {
-    let vers = ast::lit_str(@~"0.6");
-    let vers = no_span(vers);
-    let mi = ast::meta_name_value(~"vers", vers);
-    let mi = no_span(mi);
-    let vi = ast::view_item_use(ctx.sess.ident_of(~"rustpkg"),
-                                ~[@mi],
-                                ctx.sess.next_node_id());
-
-    @ast::view_item {
-        node: vi,
-        attrs: ~[],
-        vis: ast::private,
-        span: dummy_sp()
-    }
-}
-
 struct ListenerFn {
     cmds: ~[~str],
     span: codemap::span,
@@ -85,11 +71,12 @@ struct ListenerFn {
 struct ReadyCtx {
     sess: session::Session,
     crate: @ast::crate,
+    ext_cx: ext_ctxt,
     mut path: ~[ast::ident],
     mut fns: ~[ListenerFn]
 }
 
-fn fold_mod(ctx: @ReadyCtx, m: ast::_mod,
+fn fold_mod(_ctx: @ReadyCtx, m: ast::_mod,
             fold: fold::ast_fold) -> ast::_mod {
     fn strip_main(item: @ast::item) -> @ast::item {
         @ast::item {
@@ -101,23 +88,13 @@ fn strip_main(item: @ast::item) -> @ast::item {
     }
 
     fold::noop_fold_mod(ast::_mod {
-        view_items: vec::append_one(m.view_items, mk_rustpkg_use(ctx)),
         items: do vec::map(m.items) |item| {
             strip_main(*item)
-        }
+        },
+        .. m
     }, fold)
 }
 
-fn fold_crate(ctx: @ReadyCtx, crate: ast::crate_,
-              fold: fold::ast_fold) -> ast::crate_ {
-    let folded = fold::noop_fold_crate(crate, fold);
-
-    ast::crate_ {
-        module: add_pkg_module(ctx, /*bad*/copy folded.module),
-        .. folded
-    }
-}
-
 fn fold_item(ctx: @ReadyCtx, item: @ast::item,
              fold: fold::ast_fold) -> Option<@ast::item> {
 
@@ -156,315 +133,50 @@ fn fold_item(ctx: @ReadyCtx, item: @ast::item,
     res
 }
 
-fn mk_rustpkg_import(ctx: @ReadyCtx) -> @ast::view_item {
-    let path = @ast::path {
-        span: dummy_sp(),
-        global: false,
-        idents: ~[ctx.sess.ident_of(~"rustpkg")],
-        rp: None,
-        types: ~[]
-    };
-    let vp = @no_span(ast::view_path_simple(ctx.sess.ident_of(~"rustpkg"),
-                                            path, ast::type_value_ns,
-                                            ctx.sess.next_node_id()));
-
-    @ast::view_item {
-        node: ast::view_item_import(~[vp]),
-        attrs: ~[],
-        vis: ast::private,
-        span: dummy_sp()
-    }
-}
-
 fn add_pkg_module(ctx: @ReadyCtx, m: ast::_mod) -> ast::_mod {
-    let listeners = mk_listeners(ctx);
-    let main = mk_main(ctx);
-    let pkg_mod = @ast::_mod {
-        view_items: ~[mk_rustpkg_import(ctx)],
-        items: ~[main, listeners]
-    };
-    let resolve_unexported_attr =
-        attr::mk_attr(attr::mk_word_item(~"!resolve_unexported"));
-    let item_ = ast::item_mod(*pkg_mod);
-    let item = @ast::item {
-        ident: ctx.sess.ident_of(~"__pkg"),
-        attrs: ~[resolve_unexported_attr],
-        id: ctx.sess.next_node_id(),
-        node: item_,
-        vis: ast::public,
-        span: dummy_sp(),
-    };
-
+    let listeners = mk_listener_vec(ctx);
+    let ext_cx = ctx.ext_cx;
+    let item = quote_item! (
+        mod __pkg {
+            extern mod rustpkg (vers="0.6");
+            const listeners : &[rustpkg::Listener] = $listeners;
+            #[main]
+            fn main() {
+                rustpkg::run(listeners);
+            }
+        }
+    );
     ast::_mod {
-        items: vec::append_one(/*bad*/copy m.items, item),
+        items: vec::append_one(/*bad*/copy m.items, item.get()),
         .. m
     }
 }
 
-fn no_span<T: Copy>(t: T) -> ast::spanned<T> {
-    ast::spanned {
-        node: t,
-        span: dummy_sp()
-    }
-}
-
-fn path_node(ids: ~[ast::ident]) -> @ast::path {
-    @ast::path {
-        span: dummy_sp(),
-        global: false,
-        idents: ids,
-        rp: None,
-        types: ~[]
-    }
-}
-
-fn path_node_global(ids: ~[ast::ident]) -> @ast::path {
-    @ast::path {
-        span: dummy_sp(),
-        global: true,
-        idents: ids,
-        rp: None,
-        types: ~[]
-    }
-}
-
-fn mk_listeners(ctx: @ReadyCtx) -> @ast::item {
-    let ret_ty = mk_listener_vec_ty(ctx);
-    let decl = ast::fn_decl {
-        inputs: ~[],
-        output: ret_ty,
-        cf: ast::return_val
-    };
-    let listeners = mk_listener_vec(ctx);
-    let body_ = default_block(~[], option::Some(listeners),
-                              ctx.sess.next_node_id());
-    let body = no_span(body_);
-    let item_ = ast::item_fn(decl, ast::impure_fn, ~[], body);
-
-    @ast::item {
-        ident: ctx.sess.ident_of(~"listeners"),
-        attrs: ~[],
-        id: ctx.sess.next_node_id(),
-        node: item_,
-        vis: ast::public,
-        span: dummy_sp(),
-    }
-}
-
-fn mk_path(ctx: @ReadyCtx, path: ~[ast::ident]) -> @ast::path {
-    path_node(~[ctx.sess.ident_of(~"rustpkg")] + path)
-}
-
-fn mk_listener_vec_ty(ctx: @ReadyCtx) -> @ast::Ty {
-    let listener_ty_path = mk_path(ctx, ~[ctx.sess.ident_of(~"Listener")]);
-    let listener_ty = ast::Ty {
-        id: ctx.sess.next_node_id(),
-        node: ast::ty_path(listener_ty_path,
-                           ctx.sess.next_node_id()),
-        span: dummy_sp()
-    };
-    let vec_mt = ast::mt {
-        ty: @listener_ty,
-        mutbl: ast::m_imm
-    };
-    let inner_ty = @ast::Ty {
-        id: ctx.sess.next_node_id(),
-        node: ast::ty_vec(vec_mt),
-        span: dummy_sp()
-    };
-
-    @ast::Ty {
-        id: ctx.sess.next_node_id(),
-        node: ast::ty_uniq(ast::mt {
-            ty: inner_ty,
-            mutbl: ast::m_imm
-        }),
-        span: dummy_sp()
-    }
-}
-
 fn mk_listener_vec(ctx: @ReadyCtx) -> @ast::expr {
     let fns = ctx.fns;
-
     let descs = do fns.map |listener| {
         mk_listener_rec(ctx, *listener)
     };
-    let inner_expr = @ast::expr {
-        id: ctx.sess.next_node_id(),
-        callee_id: ctx.sess.next_node_id(),
-        node: ast::expr_vec(descs, ast::m_imm),
-        span: dummy_sp()
-    };
-
-    @ast::expr {
-        id: ctx.sess.next_node_id(),
-        callee_id: ctx.sess.next_node_id(),
-        node: ast::expr_vstore(inner_expr, ast::expr_vstore_uniq),
-        span: dummy_sp()
-    }
+    build::mk_slice_vec_e(ctx.ext_cx, dummy_sp(), descs)
 }
 
 fn mk_listener_rec(ctx: @ReadyCtx, listener: ListenerFn) -> @ast::expr {
+
     let span = listener.span;
-    let path = /*bad*/copy listener.path;
-    let descs = do listener.cmds.map |&cmd| {
-        let inner = @ast::expr {
-            id: ctx.sess.next_node_id(),
-            callee_id: ctx.sess.next_node_id(),
-            node: ast::expr_lit(@no_span(ast::lit_str(@cmd))),
-            span: span
-        };
-
-        @ast::expr {
-            id: ctx.sess.next_node_id(),
-            callee_id: ctx.sess.next_node_id(),
-            node: ast::expr_vstore(inner, ast::expr_vstore_uniq),
-            span: dummy_sp()
-        }
-    };
-    let cmd_expr_inner = @ast::expr {
-        id: ctx.sess.next_node_id(),
-        callee_id: ctx.sess.next_node_id(),
-        node: ast::expr_vec(descs, ast::m_imm),
-        span: dummy_sp()
-    };
-    let cmd_expr = ast::expr {
-        id: ctx.sess.next_node_id(),
-        callee_id: ctx.sess.next_node_id(),
-        node: ast::expr_vstore(cmd_expr_inner, ast::expr_vstore_uniq),
-        span: dummy_sp()
-    };
-    let cmd_field = no_span(ast::field_ {
-        mutbl: ast::m_imm,
-        ident: ctx.sess.ident_of(~"cmds"),
-        expr: @cmd_expr,
-    });
-
-    let cb_path = path_node_global(path);
-    let cb_expr = ast::expr {
-        id: ctx.sess.next_node_id(),
-        callee_id: ctx.sess.next_node_id(),
-        node: ast::expr_path(cb_path),
-        span: span
+    let cmds = do listener.cmds.map |&cmd| {
+        build::mk_base_str(ctx.ext_cx, span, cmd)
     };
-    let cb_wrapper_expr = mk_fn_wrapper(ctx, cb_expr, span);
-    let cb_field = no_span(ast::field_ {
-        mutbl: ast::m_imm,
-        ident: ctx.sess.ident_of(~"cb"),
-        expr: cb_wrapper_expr
-    });
-
-    let listener_path = mk_path(ctx, ~[ctx.sess.ident_of(~"Listener")]);
-    let listener_rec_ = ast::expr_struct(listener_path,
-                                         ~[cmd_field, cb_field],
-                                         option::None);
-    @ast::expr {
-        id: ctx.sess.next_node_id(),
-        callee_id: ctx.sess.next_node_id(),
-        node: listener_rec_,
-        span: span
-    }
-}
 
-fn mk_fn_wrapper(ctx: @ReadyCtx, fn_path_expr: ast::expr,
-                 span: span) -> @ast::expr {
-    let call_expr = ast::expr {
-        id: ctx.sess.next_node_id(),
-        callee_id: ctx.sess.next_node_id(),
-        node: ast::expr_call(@fn_path_expr, ~[], false),
-        span: span
-    };
-    let call_stmt = no_span(ast::stmt_semi(@call_expr,
-                                           ctx.sess.next_node_id()));
-    let wrapper_decl = ast::fn_decl {
-        inputs: ~[],
-        output: @ast::Ty {
-            id: ctx.sess.next_node_id(),
-            node: ast::ty_nil, span: span
-        },
-        cf: ast::return_val
-    };
-    let wrapper_body = no_span(ast::blk_ {
-        view_items: ~[],
-        stmts: ~[@call_stmt],
-        expr: option::None,
-        id: ctx.sess.next_node_id(),
-        rules: ast::default_blk
-    });
-
-    @ast::expr {
-        id: ctx.sess.next_node_id(),
-        callee_id: ctx.sess.next_node_id(),
-        node: ast::expr_fn(ast::ProtoBare, wrapper_decl,
-                           wrapper_body, @~[]),
-        span: span
-    }
-}
+    let cmds_expr = build::mk_slice_vec_e(ctx.ext_cx, span, cmds);
+    let cb_expr = build::mk_path(ctx.ext_cx, span, copy listener.path);
+    let ext_cx = ctx.ext_cx;
 
-fn mk_main(ctx: @ReadyCtx) -> @ast::item {
-    let ret_ty = ast::Ty {
-        id: ctx.sess.next_node_id(),
-        node: ast::ty_nil,
-        span: dummy_sp()
-    };
-    let decl = ast::fn_decl {
-        inputs: ~[],
-        output: @ret_ty,
-        cf: ast::return_val
-    };
-    let run_call_expr = mk_run_call(ctx);
-    let body_ = default_block(~[], option::Some(run_call_expr),
-                              ctx.sess.next_node_id());
-    let body = ast::spanned {
-        node: body_,
-        span: dummy_sp()
-    };
-    let item_ = ast::item_fn(decl, ast::impure_fn, ~[], body);
-
-    @ast::item {
-        ident: ctx.sess.ident_of(~"main"),
-        attrs: ~[attr::mk_attr(attr::mk_word_item(~"main"))],
-        id: ctx.sess.next_node_id(),
-        node: item_,
-        vis: ast::public,
-        span: dummy_sp(),
-    }
-}
-
-fn mk_run_call(ctx: @ReadyCtx) -> @ast::expr {
-    let listener_path = path_node(~[ctx.sess.ident_of(~"listeners")]);
-    let listener_path_expr_ = ast::expr_path(listener_path);
-    let listener_path_expr = ast::expr {
-        id: ctx.sess.next_node_id(),
-        callee_id: ctx.sess.next_node_id(),
-        node: listener_path_expr_,
-        span: dummy_sp()
-    };
-    let listener_call_expr_ = ast::expr_call(@listener_path_expr, ~[], false);
-    let listener_call_expr = ast::expr {
-        id: ctx.sess.next_node_id(),
-        callee_id: ctx.sess.next_node_id(),
-        node: listener_call_expr_,
-        span: dummy_sp()
-    };
-    let rustpkg_run_path = mk_path(ctx, ~[ctx.sess.ident_of(~"run")]);
-
-    let rustpkg_run_path_expr_ = ast::expr_path(rustpkg_run_path);
-    let rustpkg_run_path_expr = ast::expr {
-        id: ctx.sess.next_node_id(),
-        callee_id: ctx.sess.next_node_id(),
-        node: rustpkg_run_path_expr_,
-        span: dummy_sp()
-    };
-    let rustpkg_run_call_expr_ = ast::expr_call(@rustpkg_run_path_expr,
-                                               ~[@listener_call_expr],
-                                               false);
-    @ast::expr {
-        id: ctx.sess.next_node_id(),
-        callee_id: ctx.sess.next_node_id(),
-        node: rustpkg_run_call_expr_,
-        span: dummy_sp()
-    }
+    quote_expr!(
+        Listener {
+            cmds: $cmds_expr,
+            cb: $cb_expr
+        }
+    )
 }
 
 /// Generate/filter main function, add the list of commands, etc.
@@ -473,11 +185,12 @@ pub fn ready_crate(sess: session::Session,
     let ctx = @ReadyCtx {
         sess: sess,
         crate: crate,
+        ext_cx: mk_ctxt(sess.parse_sess, copy sess.opts.cfg),
         mut path: ~[],
         mut fns: ~[]
     };
     let precursor = @fold::AstFoldFns {
-        fold_crate: fold::wrap(|a, b| fold_crate(ctx, a, b)),
+        // fold_crate: fold::wrap(|a, b| fold_crate(ctx, a, b)),
         fold_item: |a, b| fold_item(ctx, a, b),
         fold_mod: |a, b| fold_mod(ctx, a, b),
         .. *fold::default_ast_fold()
@@ -497,7 +210,7 @@ pub fn parse_vers(vers: ~str) -> result::Result<semver::Version, ~str> {
 
 pub fn need_dir(s: &Path) {
     if !os::path_is_dir(s) && !os::make_dir(s, 493_i32) {
-        fail fmt!("can't create dir: %s", s.to_str());
+        fail!(fmt!("can't create dir: %s", s.to_str()));
     }
 }
 
@@ -509,7 +222,9 @@ pub fn note(msg: ~str) {
         out.write_str(~"note: ");
         term::reset(out);
         out.write_line(msg);
-    } else { out.write_line(~"note: " + msg); }
+    } else {
+        out.write_line(~"note: " + msg);
+    }
 }
 
 pub fn warn(msg: ~str) {
@@ -520,7 +235,9 @@ pub fn warn(msg: ~str) {
         out.write_str(~"warning: ");
         term::reset(out);
         out.write_line(msg);
-    }else { out.write_line(~"warning: " + msg); }
+    } else {
+        out.write_line(~"warning: " + msg);
+    }
 }
 
 pub fn error(msg: ~str) {
@@ -531,8 +248,9 @@ pub fn error(msg: ~str) {
         out.write_str(~"error: ");
         term::reset(out);
         out.write_line(msg);
+    } else {
+        out.write_line(~"error: " + msg);
     }
-    else { out.write_line(~"error: " + msg); }
 }
 
 pub fn hash(data: ~str) -> ~str {
@@ -590,13 +308,13 @@ pub fn wait_for_lock(path: &Path) {
 
 fn _add_pkg(packages: ~[json::Json], pkg: &Package) -> ~[json::Json] {
     for packages.each |&package| {
-        match package {
-            json::Object(map) => {
+        match &package {
+            &json::Object(ref map) => {
                 let mut has_id = false;
 
                 match map.get(&~"id") {
-                    json::String(str) => {
-                        if pkg.id == str {
+                    &json::String(ref str) => {
+                        if pkg.id == *str {
                             has_id = true;
                         }
                     }
@@ -604,9 +322,9 @@ fn _add_pkg(packages: ~[json::Json], pkg: &Package) -> ~[json::Json] {
                 }
 
                 match map.get(&~"vers") {
-                    json::String(str) => {
-                        if has_id && pkg.vers.to_str() == str {
-                            return packages;
+                    &json::String(ref str) => {
+                        if has_id && pkg.vers.to_str() == *str {
+                            return copy packages;
                         }
                     }
                     _ => {}
@@ -616,7 +334,7 @@ fn _add_pkg(packages: ~[json::Json], pkg: &Package) -> ~[json::Json] {
         }
     }
 
-    let mut map = ~LinearMap();
+    let mut map = ~LinearMap::new();
 
     map.insert(~"id", json::String(pkg.id));
     map.insert(~"vers", json::String(pkg.vers.to_str()));
@@ -631,13 +349,13 @@ fn _add_pkg(packages: ~[json::Json], pkg: &Package) -> ~[json::Json] {
 }
 
 fn _rm_pkg(packages: ~[json::Json], pkg: &Package) -> ~[json::Json] {
-    do packages.filter_map |&package| {
-        match package {
-            json::Object(map) => {
+    do packages.filter_mapped |&package| {
+        match &package {
+            &json::Object(ref map) => {
                 let mut has_id = false;
 
                 match map.get(&~"id") {
-                    json::String(str) => {
+                    &json::String(str) => {
                         if pkg.id == str {
                             has_id = true;
                         }
@@ -646,14 +364,17 @@ fn _rm_pkg(packages: ~[json::Json], pkg: &Package) -> ~[json::Json] {
                 }
 
                 match map.get(&~"vers") {
-                    json::String(str) => {
-                        if has_id && pkg.vers.to_str() == str { None }
-                        else { Some(package) }
+                    &json::String(ref str) => {
+                        if has_id && pkg.vers.to_str() == *str {
+                            None
+                        } else {
+                            Some(copy package)
+                        }
                     }
-                    _ => { Some(package) }
+                    _ => { Some(copy package) }
                 }
             }
-            _ => { Some(package) }
+            _ => { Some(copy package) }
         }
     }
 }
@@ -722,7 +443,7 @@ pub fn get_pkg(id: ~str,
         match package {
             json::Object(map) => {
                 let pid = match map.get(&~"id") {
-                    json::String(str) => str,
+                    &json::String(str) => str,
                     _ => loop
                 };
                 let pname = match parse_name(pid) {
@@ -734,12 +455,12 @@ pub fn get_pkg(id: ~str,
                     }
                 };
                 let pvers = match map.get(&~"vers") {
-                    json::String(str) => str,
+                    &json::String(str) => str,
                     _ => loop
                 };
                 if pid == id || pname == name {
                     let bins = match map.get(&~"bins") {
-                        json::List(list) => {
+                        &json::List(ref list) => {
                             do list.map |&bin| {
                                 match bin {
                                     json::String(str) => str,
@@ -750,7 +471,7 @@ pub fn get_pkg(id: ~str,
                         _ => ~[]
                     };
                     let libs = match map.get(&~"libs") {
-                        json::List(list) => {
+                        &json::List(ref list) => {
                             do list.map |&lib| {
                                 match lib {
                                     json::String(str) => str,
@@ -916,7 +637,7 @@ fn load_link_attr(mis: ~[@ast::meta_item]) -> (Option<~str>,
 
         for mis.each |a| {
             match a.node {
-                ast::meta_name_value(v, ast::spanned {node: ast::lit_str(s),
+                ast::meta_name_value(v, spanned {node: ast::lit_str(s),
                                          span: _}) => {
                     match v {
                         ~"name" => name = Some(*s),
@@ -934,7 +655,7 @@ fn load_link_attr(mis: ~[@ast::meta_item]) -> (Option<~str>,
 
     for crate.node.attrs.each |a| {
         match a.node.value.node {
-            ast::meta_name_value(v, ast::spanned {node: ast::lit_str(s),
+            ast::meta_name_value(v, spanned {node: ast::lit_str(s),
                                      span: _}) => {
                 match v {
                     ~"crate_type" => crate_type = Some(*s),
@@ -1051,11 +772,13 @@ pub fn link_exe(_src: &Path, _dest: &Path) -> bool {
 #[cfg(target_os = "android")]
 #[cfg(target_os = "freebsd")]
 #[cfg(target_os = "macos")]
-pub fn link_exe(src: &Path, dest: &Path) -> bool unsafe {
-    do str::as_c_str(src.to_str()) |src_buf| {
-        do str::as_c_str(dest.to_str()) |dest_buf| {
-            libc::link(src_buf, dest_buf) == 0 as libc::c_int &&
-            libc::chmod(dest_buf, 755) == 0 as libc::c_int
+pub fn link_exe(src: &Path, dest: &Path) -> bool {
+    unsafe {
+        do str::as_c_str(src.to_str()) |src_buf| {
+            do str::as_c_str(dest.to_str()) |dest_buf| {
+                libc::link(src_buf, dest_buf) == 0 as libc::c_int &&
+                    libc::chmod(dest_buf, 755) == 0 as libc::c_int
+            }
         }
     }
 }
index 9de336437d292648ad67c81d894f478dbba656b3..97c60330a7e31d60f290e73ec0a4340678074b03 100644 (file)
 use char;
 use core::cmp;
 
+#[deriving_eq]
+pub enum Identifier {
+    Numeric(uint),
+    AlphaNumeric(~str)
+}
+
+impl cmp::Ord for Identifier {
+    #[inline(always)]
+    pure fn lt(&self, other: &Identifier) -> bool {
+        match (self, other) {
+            (&Numeric(a), &Numeric(b)) => a < b,
+            (&Numeric(_), _) => true,
+            (&AlphaNumeric(ref a), &AlphaNumeric(ref b)) => *a < *b,
+            (&AlphaNumeric(_), _) => false
+        }
+    }
+    #[inline(always)]
+    pure fn le(&self, other: &Identifier) -> bool {
+        ! (other < self)
+    }
+    #[inline(always)]
+    pure fn gt(&self, other: &Identifier) -> bool {
+        other < self
+    }
+    #[inline(always)]
+    pure fn ge(&self, other: &Identifier) -> bool {
+        ! (self < other)
+    }
+}
+
+impl ToStr for Identifier {
+    #[inline(always)]
+    pure fn to_str(&self) -> ~str {
+        match self {
+            &Numeric(n) => n.to_str(),
+            &AlphaNumeric(ref s) => s.to_str()
+        }
+    }
+}
+
+
+#[deriving_eq]
 pub struct Version {
     major: uint,
     minor: uint,
     patch: uint,
-    tag: Option<~str>,
+    pre: ~[Identifier],
+    build: ~[Identifier],
 }
 
-impl Version: ToStr {
+impl ToStr for Version {
     #[inline(always)]
-    pure fn to_str() -> ~str {
-        let suffix = match copy self.tag {
-            Some(tag) => ~"-" + tag,
-            None => ~""
+    pure fn to_str(&self) -> ~str {
+        let s = fmt!("%u.%u.%u", self.major, self.minor, self.patch);
+        let s = if self.pre.is_empty() {
+            s
+        } else {
+            s + "-" + str::connect(self.pre.map(|i| i.to_str()), ".")
         };
-
-        fmt!("%u.%u.%u%s", self.major, self.minor, self.patch, suffix)
+        if self.build.is_empty() {
+            s
+        } else {
+            s + "+" + str::connect(self.build.map(|i| i.to_str()), ".")
+        }
     }
 }
 
-impl Version: cmp::Ord {
+impl cmp::Ord for Version {
     #[inline(always)]
     pure fn lt(&self, other: &Version) -> bool {
+
         self.major < other.major ||
-        self.minor < other.minor ||
-        self.patch < other.patch ||
-        (match self.tag {
-            Some(stag) => match other.tag {
-                Some(otag) => stag < otag,
-                None => true
-            },
-            None => false
-        })
+
+            (self.major == other.major &&
+             self.minor < other.minor) ||
+
+            (self.major == other.major &&
+             self.minor == other.minor &&
+             self.patch < other.patch) ||
+
+            (self.major == other.major &&
+             self.minor == other.minor &&
+             self.patch == other.patch &&
+             // NB: semver spec says 0.0.0-pre < 0.0.0
+             // but the version of ord defined for vec
+             // says that [] < [pre], so we alter it
+             // here.
+             (match (self.pre.len(), other.pre.len()) {
+                 (0, 0) => false,
+                 (0, _) => false,
+                 (_, 0) => true,
+                 (_, _) => self.pre < other.pre
+             })) ||
+
+            (self.major == other.major &&
+             self.minor == other.minor &&
+             self.patch == other.patch &&
+             self.pre == other.pre &&
+             self.build < other.build)
     }
+
     #[inline(always)]
     pure fn le(&self, other: &Version) -> bool {
-        self.major <= other.major ||
-        self.minor <= other.minor ||
-        self.patch <= other.patch ||
-        (match self.tag {
-            Some(stag) => match other.tag {
-                Some(otag) => stag <= otag,
-                None => true
-            },
-            None => false
-        })
+        ! (other < self)
     }
     #[inline(always)]
     pure fn gt(&self, other: &Version) -> bool {
-        self.major > other.major ||
-        self.minor > other.minor ||
-        self.patch > other.patch ||
-        (match self.tag {
-            Some(stag) => match other.tag {
-                Some(otag) => stag > otag,
-                None => false
-            },
-            None => true
-        })
+        other < self
     }
     #[inline(always)]
     pure fn ge(&self, other: &Version) -> bool {
-        self.major >= other.major ||
-        self.minor >= other.minor ||
-        self.patch >= other.patch ||
-        (match self.tag {
-            Some(stag) => match other.tag {
-                Some(otag) => stag >= otag,
-                None => false
-            },
-            None => true
-        })
+        ! (self < other)
     }
 }
 
-fn read_whitespace(rdr: io::Reader, ch: char) -> char {
-    let mut nch = ch;
+condition! {
+    bad_parse: () -> ();
+}
 
-    while char::is_whitespace(nch) {
-        nch = rdr.read_char();
+fn take_nonempty_prefix(rdr: io::Reader,
+                        ch: char,
+                        pred: fn(char) -> bool) -> (~str, char) {
+    let mut buf = ~"";
+    let mut ch = ch;
+    while pred(ch) {
+        str::push_char(&mut buf, ch);
+        ch = rdr.read_char();
     }
-
-    nch
+    if buf.is_empty() {
+        bad_parse::cond.raise(())
+    }
+    debug!("extracted nonempty prefix: %s", buf);
+    (buf, ch)
 }
 
-fn parse_reader(rdr: io::Reader) -> Option<(Version, char)> {
-    fn read_digits(rdr: io::Reader, ch: char) -> Option<(uint, char)> {
-        let mut buf = ~"";
-        let mut nch = ch;
-
-        while nch != -1 as char {
-            match nch {
-              '0' .. '9' => buf += str::from_char(nch),
-              _ => break
-            }
+fn take_num(rdr: io::Reader, ch: char) -> (uint, char) {
+    let (s, ch) = take_nonempty_prefix(rdr, ch, char::is_digit);
+    match uint::from_str(s) {
+        None => { bad_parse::cond.raise(()); (0, ch) },
+        Some(i) => (i, ch)
+    }
+}
 
-            nch = rdr.read_char();
+fn take_ident(rdr: io::Reader, ch: char) -> (Identifier, char) {
+    let (s,ch) = take_nonempty_prefix(rdr, ch, char::is_alphanumeric);
+    if s.all(char::is_digit) {
+        match uint::from_str(s) {
+            None => { bad_parse::cond.raise(()); (Numeric(0), ch) },
+            Some(i) => (Numeric(i), ch)
         }
+    } else {
+        (AlphaNumeric(s), ch)
+    }
+}
 
-        do uint::from_str(buf).chain_ref |&i| {
-            Some((i, nch))
-        }
+fn expect(ch: char, c: char) {
+    if ch != c {
+        bad_parse::cond.raise(())
     }
+}
 
-    fn read_tag(rdr: io::Reader) -> Option<(~str, char)> {
-        let mut ch = rdr.read_char();
-        let mut buf = ~"";
+fn parse_reader(rdr: io::Reader) -> Version {
 
-        while ch != -1 as char {
-            match ch {
-                '0' .. '9' | 'A' .. 'Z' | 'a' .. 'z' | '-' => {
-                    buf += str::from_char(ch);
-                }
-                _ => break
-            }
+    let (major, ch) = take_num(rdr, rdr.read_char());
+    expect(ch, '.');
+    let (minor, ch) = take_num(rdr, rdr.read_char());
+    expect(ch, '.');
+    let (patch, ch) = take_num(rdr, rdr.read_char());
 
-            ch = rdr.read_char();
-        }
+    let mut pre = ~[];
+    let mut build = ~[];
 
-        if buf == ~"" { return None; }
-        else { Some((buf, ch)) }
+    let mut ch = ch;
+    if ch == '-' {
+        loop {
+            let (id, c) = take_ident(rdr, rdr.read_char());
+            pre.push(id);
+            ch = c;
+            if ch != '.' { break; }
+        }
     }
 
-    let ch = read_whitespace(rdr, rdr.read_char());
-    let (major, ch) = match read_digits(rdr, ch) {
-        None => return None,
-        Some(item) => item
-    };
-
-    if ch != '.' { return None; }
-
-    let (minor, ch) = match read_digits(rdr, rdr.read_char()) {
-        None => return None,
-        Some(item) => item
-    };
-
-    if ch != '.' { return None; }
-
-    let (patch, ch) = match read_digits(rdr, rdr.read_char()) {
-        None => return None,
-        Some(item) => item
-    };
-    let (tag, ch) = if ch == '-' {
-        match read_tag(rdr) {
-            None => return None,
-            Some((tag, ch)) => (Some(tag), ch)
+    if ch == '+' {
+        loop {
+            let (id, c) = take_ident(rdr, rdr.read_char());
+            build.push(id);
+            ch = c;
+            if ch != '.' { break; }
         }
-    } else {
-        (None, ch)
-    };
+    }
 
-    Some((Version { major: major, minor: minor, patch: patch, tag: tag },
-          ch))
+    Version {
+        major: major,
+        minor: minor,
+        patch: patch,
+        pre: pre,
+        build: build,
+    }
 }
 
-pub fn parse(s: &str) -> Option<Version> {
-    do io::with_str_reader(s) |rdr| {
-        do parse_reader(rdr).chain_ref |&item| {
-            let (version, ch) = item;
 
-            if read_whitespace(rdr, ch) != -1 as char {
+pub fn parse(s: &str) -> Option<Version> {
+    if ! str::is_ascii(s) {
+        return None;
+    }
+    let s = s.trim();
+    let mut bad = false;
+    do bad_parse::cond.trap(|_| { debug!("bad"); bad = true }).in {
+        do io::with_str_reader(s) |rdr| {
+            let v = parse_reader(rdr);
+            if bad || v.to_str() != s {
                 None
             } else {
-                Some(version)
+                Some(v)
             }
         }
     }
@@ -204,26 +255,68 @@ fn test_parse() {
         major: 1u,
         minor: 2u,
         patch: 3u,
-        tag: None,
+        pre: ~[],
+        build: ~[],
     });
     assert parse("  1.2.3  ") == Some(Version {
         major: 1u,
         minor: 2u,
         patch: 3u,
-        tag: None,
+        pre: ~[],
+        build: ~[],
     });
     assert parse("1.2.3-alpha1") == Some(Version {
         major: 1u,
         minor: 2u,
         patch: 3u,
-        tag: Some("alpha1")
+        pre: ~[AlphaNumeric(~"alpha1")],
+        build: ~[]
     });
     assert parse("  1.2.3-alpha1  ") == Some(Version {
         major: 1u,
         minor: 2u,
         patch: 3u,
-        tag: Some("alpha1")
+        pre: ~[AlphaNumeric(~"alpha1")],
+        build: ~[]
     });
+    assert parse("1.2.3+build5") == Some(Version {
+        major: 1u,
+        minor: 2u,
+        patch: 3u,
+        pre: ~[],
+        build: ~[AlphaNumeric(~"build5")]
+    });
+    assert parse("  1.2.3+build5  ") == Some(Version {
+        major: 1u,
+        minor: 2u,
+        patch: 3u,
+        pre: ~[],
+        build: ~[AlphaNumeric(~"build5")]
+    });
+    assert parse("1.2.3-alpha1+build5") == Some(Version {
+        major: 1u,
+        minor: 2u,
+        patch: 3u,
+        pre: ~[AlphaNumeric(~"alpha1")],
+        build: ~[AlphaNumeric(~"build5")]
+    });
+    assert parse("  1.2.3-alpha1+build5  ") == Some(Version {
+        major: 1u,
+        minor: 2u,
+        patch: 3u,
+        pre: ~[AlphaNumeric(~"alpha1")],
+        build: ~[AlphaNumeric(~"build5")]
+    });
+    assert parse("1.2.3-1.alpha1.9+build5.7.3aedf  ") == Some(Version {
+        major: 1u,
+        minor: 2u,
+        patch: 3u,
+        pre: ~[Numeric(1),AlphaNumeric(~"alpha1"),Numeric(9)],
+        build: ~[AlphaNumeric(~"build5"),
+                 Numeric(7),
+                 AlphaNumeric(~"3aedf")]
+    });
+
 }
 
 #[test]
@@ -245,9 +338,8 @@ fn test_lt() {
     assert parse("0.0.0")        < parse("1.2.3-alpha2");
     assert parse("1.0.0")        < parse("1.2.3-alpha2");
     assert parse("1.2.0")        < parse("1.2.3-alpha2");
-    assert parse("1.2.3")        < parse("1.2.3-alpha2");
+    assert parse("1.2.3-alpha1") < parse("1.2.3");
     assert parse("1.2.3-alpha1") < parse("1.2.3-alpha2");
-
     assert !(parse("1.2.3-alpha2") < parse("1.2.3-alpha2"));
 }
 
@@ -256,7 +348,6 @@ fn test_le() {
     assert parse("0.0.0")        <= parse("1.2.3-alpha2");
     assert parse("1.0.0")        <= parse("1.2.3-alpha2");
     assert parse("1.2.0")        <= parse("1.2.3-alpha2");
-    assert parse("1.2.3")        <= parse("1.2.3-alpha2");
     assert parse("1.2.3-alpha1") <= parse("1.2.3-alpha2");
     assert parse("1.2.3-alpha2") <= parse("1.2.3-alpha2");
 }
@@ -266,9 +357,8 @@ fn test_gt() {
     assert parse("1.2.3-alpha2") > parse("0.0.0");
     assert parse("1.2.3-alpha2") > parse("1.0.0");
     assert parse("1.2.3-alpha2") > parse("1.2.0");
-    assert parse("1.2.3-alpha2") > parse("1.2.3");
     assert parse("1.2.3-alpha2") > parse("1.2.3-alpha1");
-
+    assert parse("1.2.3")        > parse("1.2.3-alpha2");
     assert !(parse("1.2.3-alpha2") > parse("1.2.3-alpha2"));
 }
 
@@ -277,7 +367,29 @@ fn test_ge() {
     assert parse("1.2.3-alpha2") >= parse("0.0.0");
     assert parse("1.2.3-alpha2") >= parse("1.0.0");
     assert parse("1.2.3-alpha2") >= parse("1.2.0");
-    assert parse("1.2.3-alpha2") >= parse("1.2.3");
     assert parse("1.2.3-alpha2") >= parse("1.2.3-alpha1");
     assert parse("1.2.3-alpha2") >= parse("1.2.3-alpha2");
 }
+
+#[test]
+fn test_spec_order() {
+
+    let vs = ["1.0.0-alpha",
+              "1.0.0-alpha.1",
+              "1.0.0-beta.2",
+              "1.0.0-beta.11",
+              "1.0.0-rc.1",
+              "1.0.0-rc.1+build.1",
+              "1.0.0",
+              "1.0.0+0.3.7",
+              "1.3.7+build",
+              "1.3.7+build.2.b8f12d7",
+              "1.3.7+build.11.e0f985a"];
+    let mut i = 1;
+    while i < vs.len() {
+        let a = parse(vs[i-1]).get();
+        let b = parse(vs[i]).get();
+        assert a < b;
+        i += 1;
+    }
+}
\ No newline at end of file