<commit_before>use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::env;
use std::fmt;
use std::io;
use std::io::Write;
use std::sync::Mutex;

extern crate syn;
use syn::*;

mod rust_lib;

#[derive(Debug, Clone)]
struct ConvertedType {
    /// The type converted to C (e.g. `uint32_t`)
    prefix: String,
    /// Stuff that might need to go after the identifier (e.g. `[3]` for an array).
    postfix: String,
    /// The types this type depends upon. For example a `prefix` of
    /// Foo<Bar> would have both `Foo` and `Bar` in the `deps`.
    deps: BTreeSet<String>,
}

impl ConvertedType {
    fn append_prefix(&self, str: &str) -> ConvertedType {
        let mut clone = self.clone();
        clone.prefix.push_str(str);
        clone
    }

    fn append_postfix(&self, str: &str) -> ConvertedType {
        let mut clone = self.clone();
        clone.postfix.push_str(str);
        clone
    }

    fn add_dep(&self, dep: String) -> ConvertedType {
        let mut clone = self.clone();
        clone.deps.insert(dep);
        clone
    }

    fn format_with_ident(&self, ident: &Ident) -> String {
        format!("{} {}{}", &self.prefix, &ident.to_string(), &self.postfix)
    }
}

impl From<String> for ConvertedType {
    fn from(str: String) -> ConvertedType {
        let mut initial_set = BTreeSet::new();
        initial_set.insert(str.clone());
        ConvertedType {
            prefix: str,
            postfix: String::new(),
            deps: initial_set,
        }
    }
}

impl fmt::Display for ConvertedType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", &self.prefix, &self.postfix)
    }
}

fn has_attribute(target: MetaItem, attrs: &Vec<Attribute>) -> bool {
    return attrs
               .iter()
               .any(|ref attr| attr.style == AttrStyle::Outer && attr.value == target);
}

fn has_no_mangle(attrs: &Vec<Attribute>) -> bool {
    has_attribute(MetaItem::Word(Ident::new("no_mangle")), attrs)
}

fn wr_func_body(attrs: &Vec<Attribute>) -> String {
    if has_attribute(MetaItem::Word(Ident::new("destructor_safe")), attrs) {
        String::from("WR_DESTRUCTOR_SAFE_FUNC")
    } else {
        String::from("WR_FUNC")
    }
}

fn is_repr_c(attrs: &Vec<Attribute>) -> bool {
    let repr_args = vec![NestedMetaItem::MetaItem(MetaItem::Word(Ident::new("C")))];
    has_attribute(MetaItem::List(Ident::new("repr"), repr_args), attrs)
}

fn is_repr_u32(attrs: &Vec<Attribute>) -> bool {
    let repr_args = vec![NestedMetaItem::MetaItem(MetaItem::Word(Ident::new("u32")))];
    has_attribute(MetaItem::List(Ident::new("repr"), repr_args), attrs)
}

fn is_c_abi(abi: &Option<Abi>) -> bool {
    abi == &Some(Abi::Named(String::from("C")))
}

fn map_path(p: &Path) -> ConvertedType {
    let l = p.segments[0].ident.to_string();
    let mut c = ConvertedType::from(match l.as_ref() {
        "usize" => "size_t".to_string(),
        "u8" => "uint8_t".to_string(),
        "u32" => "uint32_t".to_string(),
        "u64" => "uint64_t".to_string(),
        "i8" => "int8_t".to_string(),
        "i32" => "int32_t".to_string(),
        "i64" => "int64_t".to_string(),
        "f32" => "float".to_string(),
        "c_void" => "void".to_string(),
        _ => l,
    });
    if let PathParameters::AngleBracketed(ref d) = p.segments[0].parameters {
        let template_args = d.types
            .iter()
            .map(|ty| {
                let conv = map_ty(ty);
                c = c.add_dep(conv.to_string());
                conv.to_string()
            })
            .collect::<Vec<String>>()
            .join(", ");
        if !template_args.is_empty() {
            c = c.append_prefix(&format!("<{}>", &template_args));
        }
    }
    c
}

fn map_mut_ty(mut_ty: &MutTy) -> ConvertedType {
    map_ty(&mut_ty.ty)
}

fn map_ty(ty: &Ty) -> ConvertedType {
    match ty {
        &Ty::Path(_, ref p) => map_path(p),
        &Ty::Ptr(ref p) => map_ty(&p.ty).append_prefix("*"),
        &Ty::Rptr(_, ref mut_ty) => map_mut_ty(mut_ty).append_prefix("*"),
        &Ty::Array(ref p, ConstExpr::Lit(Lit::Int(sz, _))) => map_ty(&p).append_postfix(&format!("[{}]", sz)),
        _ => ConvertedType::from(format!("unknown {:?}", ty)),
    }
}

fn map_return_type(ret: &FunctionRetTy) -> ConvertedType {
    match ret {
        &FunctionRetTy::Default => ConvertedType::from("void".to_string()),
        &FunctionRetTy::Ty(ref ty) => map_ty(ty),
    }
}

fn map_arg(f: &FnArg, dep_set: &mut BTreeSet<String>) -> String {
    match f {
        &FnArg::Captured(Pat::Ident(_, ref ident, _), ref ty) => {
            let mut converted = map_ty(ty);
            dep_set.append(&mut converted.deps);
            converted.format_with_ident(ident)
        }
        _ => "unknown".to_string(),
    }
}

fn map_field(f: &Field, dep_set: &mut BTreeSet<String>) -> String {
    let mut ret = String::from("  ");
    let mut converted = map_ty(&f.ty);
    dep_set.append(&mut converted.deps);
    ret.push_str(&converted.format_with_ident(f.ident.as_ref().expect("Struct fields must have idents")));
    ret.push_str(";\n");
    ret
}

fn map_generic_param(t: &TyParam) -> String {
    let mut ret = String::from("typename ");
    ret.push_str(&t.ident.to_string());
    ret
}

fn fold_enum_variants(accum: (String, i32), v: &Variant) -> (String, i32) {
    // `accum` contains the combined string of converted enum variants so far, to which
    // we will append the converted version of `v`. The other thing in `accum` is the
    // value of the previous variant. If `v` has an explicit value we keep that, otherwise
    // we increment the value of the previous variant to get the new one. This is all
    // so that we properly support enums with explicitly-specified and discontinuous
    // values.
    let mut ret = accum.0;
    ret.push_str("  ");
    ret.push_str(&v.ident.to_string());
    ret.push_str(" = ");
    let new_value = match &v.discriminant {
        &None => accum.1 + 1,
        &Some(ConstExpr::Lit(Lit::Int(ref specified_value, _))) => *specified_value as i32,
        &Some(_) => {
            // we don't handle this yet, so just put in something that will fail C compilation
            writeln!(io::stderr(), "warning, unsupported enum discriminant").unwrap();
            ret.push_str("???");
            accum.1 + 1
        }
    };
    ret.push_str(&new_value.to_string());
    ret.push_str(",\n");
    (ret, new_value)
}

/// Convenience struct to group stuff associated with a converted
/// item.
struct ConvertedItem {
    /// This holds the C code that needs to go into the header file.
    c_code: String,
    /// This holds a set of identifiers that the C code depends on.
    /// So for example if a struct A contains another struct B, the
    /// `ConvertedItem` instance for A will have the string "B" in
    /// `deps`.
    deps: BTreeSet<String>,
}

impl ConvertedItem {
    fn new(c_code: String, deps: BTreeSet<String>) -> ConvertedItem {
        ConvertedItem {
            c_code: c_code,
            deps: deps,
        }
    }
}

/// A structure to store all the results of converting Rust stuff to C
/// stuff, so that we can pick and choose what we output.
struct ConversionResults {
    /// This holds the C function signatures of no_mangle rust functions,
    /// in the order that they were encountered in Rust.
    funcs: Vec<ConvertedItem>,
    /// This holds the C conversions of repr(C) structs and repr(u32) enums
    /// from Rust. The 'ds' in the name stands for 'data structures'. The
    /// key of the map is the name of the type.
    ds: BTreeMap<String, ConvertedItem>,
    /// This holds type aliases we find in Rust code. This way we can do
    /// something like:
    ///  type WrFoo = Foo;  // the type alias
    ///  fn wr_func(foo: WrFoo) { ... }
    /// where `Foo` is repr(C), and have the corresponding C struct
    /// generated with the name WrFoo so that it's not polluting the
    /// global namespace and everything works ok. In the above example
    /// we would store WrFoo -> Foo in `type_map` and use it when
    /// generating the C definition of `WrFoo`.
    type_map: BTreeMap<String, String>,
}

/// Recursive function to collect the dependencies we need. Deps are collected
/// into `dst`, using the "database" of dependencies in `results_ref`, and
/// using `deps` as the starting set of dependencies we want.
fn collect_deps(dst: &mut BTreeSet<String>, results_ref: &ConversionResults, deps: &BTreeSet<String>) {
    for dep in deps {
        dst.insert(dep.to_string());
        results_ref.ds.get(dep).map(|converted| collect_deps(dst, results_ref, &converted.deps));
    }
}

fn main() {
    let p = env::args().nth(1).unwrap();

    let results = Mutex::new(ConversionResults {
        funcs: Vec::new(),
        ds: BTreeMap::new(),
        type_map: BTreeMap::new(),
    });

    rust_lib::parse(p, &|mod_name, items| {
        for item in items {
            match item.node {
                ItemKind::Fn(ref decl,
                             ref _unsafe,
                             ref _const,
                             ref abi,
                             ref _generic,
                             ref _block) => {
                    writeln!(io::stderr(), "processing function {}::{}", mod_name, &item.ident).unwrap();
                    if has_no_mangle(&item.attrs) && is_c_abi(&abi) {
                        let mut deps = BTreeSet::new();
                        let mut return_type = map_return_type(&decl.output);
                        deps.append(&mut return_type.deps);
                        let c_code =
                            format!("WR_INLINE {}\n{}({})\n{};\n",
                                    return_type,
                                    item.ident,
                                    decl.inputs
                                        .iter()
                                        .map(|f| map_arg(f, &mut deps))
                                        .collect::<Vec<_>>()
                                        .join(", "),
                                    wr_func_body(&item.attrs));
                        results.lock().unwrap().funcs.push(ConvertedItem::new(c_code, deps));
                    }
                }
                ItemKind::Struct(ref variant,
                                 ref generics) => {
                    writeln!(io::stderr(), "processing struct {}::{}", mod_name, &item.ident).unwrap();
                    if is_repr_c(&item.attrs) {
                        if let &VariantData::Struct(ref fields) = variant {
                            let mut deps = BTreeSet::new();
                            let mut c_code = String::new();
                            if !generics.ty_params.is_empty() {
                                c_code.push_str(&(
                                    format!("template<{}>\n",
                                            generics.ty_params
                                                    .iter()
                                                    .map(map_generic_param)
                                                    .collect::<Vec<_>>()
                                                    .join(", "))));
                            }
                            c_code.push_str(&(
                                format!("struct {} {{\n{}}};\n",
                                        item.ident,
                                        fields.iter()
                                              .map(|f| map_field(f, &mut deps))
                                              .collect::<String>())));
                            results.lock().unwrap().ds.insert(
                                item.ident.to_string(),
                                ConvertedItem::new(c_code, deps));
                        }
                    }
                }
                ItemKind::Enum(ref variants, ref _generics) => {
                    writeln!(io::stderr(), "processing enum {}::{}", mod_name, &item.ident).unwrap();
                    if is_repr_u32(&item.attrs) {
                        let c_code = format!("enum class {}: uint32_t {{\n{}\n  Sentinel /* this must be last for serialization purposes. */\n}};\n",
                                             item.ident,
                                             variants.iter()
                                                     .fold((String::new(), -1), fold_enum_variants)
                                                     .0);
                        results.lock().unwrap().ds.insert(
                            item.ident.to_string(),
                            ConvertedItem::new(c_code, BTreeSet::new()));
                    }
                }
                ItemKind::Ty(ref ty, ref _generics) => {
                    results.lock().unwrap().type_map.insert(
                        item.ident.to_string(), map_ty(ty).to_string());
                }
                _ => {}
            }
        }
    });

    // Collect all the recursive type dependencies using the functions
    // as the roots. (i.e. all the structs/enums referred to by the function
    // return types and arguments, plus all the structs/enums referred to
    // by *those* and so on). We just skip any types that we don't have
    // instead of returning an error. This way we don't have to add special
    // handling for things like void and int32_t and whatnot.
    // Collect into a BTreeSet to strip duplicates and get a deterministic order.
    let mut all_func_deps = BTreeSet::new();
    let results_ref = &results.lock().unwrap();
    for converted in &results_ref.funcs {
        collect_deps(&mut all_func_deps, results_ref, &converted.deps);
    }

    // Showtime!
    for dep in all_func_deps {
        // Check for `dep` in the ds map. If we don't find it, look up `dep`
        // in the type map, and if we find an alias, look that up in the ds
        // map instead. If we find the alias, we need to print it out, but
        // replace the alias (`mapped`) with `dep`, because the function
        // signature uses `dep`.
        if let Some(converted) = results_ref.ds.get(&dep) {
            println!("{}", converted.c_code);
        } else if let Some(mapped) = results_ref.type_map.get(&dep) {
            if let Some(converted) = results_ref.ds.get(mapped) {
                println!("{}", converted.c_code.replace(mapped, &dep));
            }
        }
    }
    for converted in &results_ref.funcs {
        println!("{}", converted.c_code);
    }
}
<commit_msg>Put function parameters one per line for readability<commit_after>use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::env;
use std::fmt;
use std::io;
use std::io::Write;
use std::sync::Mutex;

extern crate syn;
use syn::*;

mod rust_lib;

#[derive(Debug, Clone)]
struct ConvertedType {
    /// The type converted to C (e.g. `uint32_t`)
    prefix: String,
    /// Stuff that might need to go after the identifier (e.g. `[3]` for an array).
    postfix: String,
    /// The types this type depends upon. For example a `prefix` of
    /// Foo<Bar> would have both `Foo` and `Bar` in the `deps`.
    deps: BTreeSet<String>,
}

impl ConvertedType {
    fn append_prefix(&self, str: &str) -> ConvertedType {
        let mut clone = self.clone();
        clone.prefix.push_str(str);
        clone
    }

    fn append_postfix(&self, str: &str) -> ConvertedType {
        let mut clone = self.clone();
        clone.postfix.push_str(str);
        clone
    }

    fn add_dep(&self, dep: String) -> ConvertedType {
        let mut clone = self.clone();
        clone.deps.insert(dep);
        clone
    }

    fn format_with_ident(&self, ident: &Ident) -> String {
        format!("{} {}{}", &self.prefix, &ident.to_string(), &self.postfix)
    }
}

impl From<String> for ConvertedType {
    fn from(str: String) -> ConvertedType {
        let mut initial_set = BTreeSet::new();
        initial_set.insert(str.clone());
        ConvertedType {
            prefix: str,
            postfix: String::new(),
            deps: initial_set,
        }
    }
}

impl fmt::Display for ConvertedType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", &self.prefix, &self.postfix)
    }
}

fn has_attribute(target: MetaItem, attrs: &Vec<Attribute>) -> bool {
    return attrs
               .iter()
               .any(|ref attr| attr.style == AttrStyle::Outer && attr.value == target);
}

fn has_no_mangle(attrs: &Vec<Attribute>) -> bool {
    has_attribute(MetaItem::Word(Ident::new("no_mangle")), attrs)
}

fn wr_func_body(attrs: &Vec<Attribute>) -> String {
    if has_attribute(MetaItem::Word(Ident::new("destructor_safe")), attrs) {
        String::from("WR_DESTRUCTOR_SAFE_FUNC")
    } else {
        String::from("WR_FUNC")
    }
}

fn is_repr_c(attrs: &Vec<Attribute>) -> bool {
    let repr_args = vec![NestedMetaItem::MetaItem(MetaItem::Word(Ident::new("C")))];
    has_attribute(MetaItem::List(Ident::new("repr"), repr_args), attrs)
}

fn is_repr_u32(attrs: &Vec<Attribute>) -> bool {
    let repr_args = vec![NestedMetaItem::MetaItem(MetaItem::Word(Ident::new("u32")))];
    has_attribute(MetaItem::List(Ident::new("repr"), repr_args), attrs)
}

fn is_c_abi(abi: &Option<Abi>) -> bool {
    abi == &Some(Abi::Named(String::from("C")))
}

fn map_path(p: &Path) -> ConvertedType {
    let l = p.segments[0].ident.to_string();
    let mut c = ConvertedType::from(match l.as_ref() {
        "usize" => "size_t".to_string(),
        "u8" => "uint8_t".to_string(),
        "u32" => "uint32_t".to_string(),
        "u64" => "uint64_t".to_string(),
        "i8" => "int8_t".to_string(),
        "i32" => "int32_t".to_string(),
        "i64" => "int64_t".to_string(),
        "f32" => "float".to_string(),
        "c_void" => "void".to_string(),
        _ => l,
    });
    if let PathParameters::AngleBracketed(ref d) = p.segments[0].parameters {
        let template_args = d.types
            .iter()
            .map(|ty| {
                let conv = map_ty(ty);
                c = c.add_dep(conv.to_string());
                conv.to_string()
            })
            .collect::<Vec<String>>()
            .join(", ");
        if !template_args.is_empty() {
            c = c.append_prefix(&format!("<{}>", &template_args));
        }
    }
    c
}

fn map_mut_ty(mut_ty: &MutTy) -> ConvertedType {
    map_ty(&mut_ty.ty)
}

fn map_ty(ty: &Ty) -> ConvertedType {
    match ty {
        &Ty::Path(_, ref p) => map_path(p),
        &Ty::Ptr(ref p) => map_ty(&p.ty).append_prefix("*"),
        &Ty::Rptr(_, ref mut_ty) => map_mut_ty(mut_ty).append_prefix("*"),
        &Ty::Array(ref p, ConstExpr::Lit(Lit::Int(sz, _))) => map_ty(&p).append_postfix(&format!("[{}]", sz)),
        _ => ConvertedType::from(format!("unknown {:?}", ty)),
    }
}

fn map_return_type(ret: &FunctionRetTy) -> ConvertedType {
    match ret {
        &FunctionRetTy::Default => ConvertedType::from("void".to_string()),
        &FunctionRetTy::Ty(ref ty) => map_ty(ty),
    }
}

fn map_arg(f: &FnArg, dep_set: &mut BTreeSet<String>) -> String {
    match f {
        &FnArg::Captured(Pat::Ident(_, ref ident, _), ref ty) => {
            let mut converted = map_ty(ty);
            dep_set.append(&mut converted.deps);
            converted.format_with_ident(ident)
        }
        _ => "unknown".to_string(),
    }
}

fn map_field(f: &Field, dep_set: &mut BTreeSet<String>) -> String {
    let mut ret = String::from("  ");
    let mut converted = map_ty(&f.ty);
    dep_set.append(&mut converted.deps);
    ret.push_str(&converted.format_with_ident(f.ident.as_ref().expect("Struct fields must have idents")));
    ret.push_str(";\n");
    ret
}

fn map_generic_param(t: &TyParam) -> String {
    let mut ret = String::from("typename ");
    ret.push_str(&t.ident.to_string());
    ret
}

fn fold_enum_variants(accum: (String, i32), v: &Variant) -> (String, i32) {
    // `accum` contains the combined string of converted enum variants so far, to which
    // we will append the converted version of `v`. The other thing in `accum` is the
    // value of the previous variant. If `v` has an explicit value we keep that, otherwise
    // we increment the value of the previous variant to get the new one. This is all
    // so that we properly support enums with explicitly-specified and discontinuous
    // values.
    let mut ret = accum.0;
    ret.push_str("  ");
    ret.push_str(&v.ident.to_string());
    ret.push_str(" = ");
    let new_value = match &v.discriminant {
        &None => accum.1 + 1,
        &Some(ConstExpr::Lit(Lit::Int(ref specified_value, _))) => *specified_value as i32,
        &Some(_) => {
            // we don't handle this yet, so just put in something that will fail C compilation
            writeln!(io::stderr(), "warning, unsupported enum discriminant").unwrap();
            ret.push_str("???");
            accum.1 + 1
        }
    };
    ret.push_str(&new_value.to_string());
    ret.push_str(",\n");
    (ret, new_value)
}

/// Convenience struct to group stuff associated with a converted
/// item.
struct ConvertedItem {
    /// This holds the C code that needs to go into the header file.
    c_code: String,
    /// This holds a set of identifiers that the C code depends on.
    /// So for example if a struct A contains another struct B, the
    /// `ConvertedItem` instance for A will have the string "B" in
    /// `deps`.
    deps: BTreeSet<String>,
}

impl ConvertedItem {
    fn new(c_code: String, deps: BTreeSet<String>) -> ConvertedItem {
        ConvertedItem {
            c_code: c_code,
            deps: deps,
        }
    }
}

/// A structure to store all the results of converting Rust stuff to C
/// stuff, so that we can pick and choose what we output.
struct ConversionResults {
    /// This holds the C function signatures of no_mangle rust functions,
    /// in the order that they were encountered in Rust.
    funcs: Vec<ConvertedItem>,
    /// This holds the C conversions of repr(C) structs and repr(u32) enums
    /// from Rust. The 'ds' in the name stands for 'data structures'. The
    /// key of the map is the name of the type.
    ds: BTreeMap<String, ConvertedItem>,
    /// This holds type aliases we find in Rust code. This way we can do
    /// something like:
    ///  type WrFoo = Foo;  // the type alias
    ///  fn wr_func(foo: WrFoo) { ... }
    /// where `Foo` is repr(C), and have the corresponding C struct
    /// generated with the name WrFoo so that it's not polluting the
    /// global namespace and everything works ok. In the above example
    /// we would store WrFoo -> Foo in `type_map` and use it when
    /// generating the C definition of `WrFoo`.
    type_map: BTreeMap<String, String>,
}

/// Recursive function to collect the dependencies we need. Deps are collected
/// into `dst`, using the "database" of dependencies in `results_ref`, and
/// using `deps` as the starting set of dependencies we want.
fn collect_deps(dst: &mut BTreeSet<String>, results_ref: &ConversionResults, deps: &BTreeSet<String>) {
    for dep in deps {
        dst.insert(dep.to_string());
        results_ref.ds.get(dep).map(|converted| collect_deps(dst, results_ref, &converted.deps));
    }
}

fn main() {
    let p = env::args().nth(1).unwrap();

    let results = Mutex::new(ConversionResults {
        funcs: Vec::new(),
        ds: BTreeMap::new(),
        type_map: BTreeMap::new(),
    });

    rust_lib::parse(p, &|mod_name, items| {
        for item in items {
            match item.node {
                ItemKind::Fn(ref decl,
                             ref _unsafe,
                             ref _const,
                             ref abi,
                             ref _generic,
                             ref _block) => {
                    writeln!(io::stderr(), "processing function {}::{}", mod_name, &item.ident).unwrap();
                    if has_no_mangle(&item.attrs) && is_c_abi(&abi) {
                        let mut deps = BTreeSet::new();
                        let mut return_type = map_return_type(&decl.output);
                        deps.append(&mut return_type.deps);
                        let c_code =
                            format!("WR_INLINE {}\n{}({})\n{};\n",
                                    return_type,
                                    item.ident,
                                    decl.inputs
                                        .iter()
                                        .map(|f| map_arg(f, &mut deps))
                                        .collect::<Vec<_>>()
                                        .join(",\n    "),
                                    wr_func_body(&item.attrs));
                        results.lock().unwrap().funcs.push(ConvertedItem::new(c_code, deps));
                    }
                }
                ItemKind::Struct(ref variant,
                                 ref generics) => {
                    writeln!(io::stderr(), "processing struct {}::{}", mod_name, &item.ident).unwrap();
                    if is_repr_c(&item.attrs) {
                        if let &VariantData::Struct(ref fields) = variant {
                            let mut deps = BTreeSet::new();
                            let mut c_code = String::new();
                            if !generics.ty_params.is_empty() {
                                c_code.push_str(&(
                                    format!("template<{}>\n",
                                            generics.ty_params
                                                    .iter()
                                                    .map(map_generic_param)
                                                    .collect::<Vec<_>>()
                                                    .join(", "))));
                            }
                            c_code.push_str(&(
                                format!("struct {} {{\n{}}};\n",
                                        item.ident,
                                        fields.iter()
                                              .map(|f| map_field(f, &mut deps))
                                              .collect::<String>())));
                            results.lock().unwrap().ds.insert(
                                item.ident.to_string(),
                                ConvertedItem::new(c_code, deps));
                        }
                    }
                }
                ItemKind::Enum(ref variants, ref _generics) => {
                    writeln!(io::stderr(), "processing enum {}::{}", mod_name, &item.ident).unwrap();
                    if is_repr_u32(&item.attrs) {
                        let c_code = format!("enum class {}: uint32_t {{\n{}\n  Sentinel /* this must be last for serialization purposes. */\n}};\n",
                                             item.ident,
                                             variants.iter()
                                                     .fold((String::new(), -1), fold_enum_variants)
                                                     .0);
                        results.lock().unwrap().ds.insert(
                            item.ident.to_string(),
                            ConvertedItem::new(c_code, BTreeSet::new()));
                    }
                }
                ItemKind::Ty(ref ty, ref _generics) => {
                    results.lock().unwrap().type_map.insert(
                        item.ident.to_string(), map_ty(ty).to_string());
                }
                _ => {}
            }
        }
    });

    // Collect all the recursive type dependencies using the functions
    // as the roots. (i.e. all the structs/enums referred to by the function
    // return types and arguments, plus all the structs/enums referred to
    // by *those* and so on). We just skip any types that we don't have
    // instead of returning an error. This way we don't have to add special
    // handling for things like void and int32_t and whatnot.
    // Collect into a BTreeSet to strip duplicates and get a deterministic order.
    let mut all_func_deps = BTreeSet::new();
    let results_ref = &results.lock().unwrap();
    for converted in &results_ref.funcs {
        collect_deps(&mut all_func_deps, results_ref, &converted.deps);
    }

    // Showtime!
    for dep in all_func_deps {
        // Check for `dep` in the ds map. If we don't find it, look up `dep`
        // in the type map, and if we find an alias, look that up in the ds
        // map instead. If we find the alias, we need to print it out, but
        // replace the alias (`mapped`) with `dep`, because the function
        // signature uses `dep`.
        if let Some(converted) = results_ref.ds.get(&dep) {
            println!("{}", converted.c_code);
        } else if let Some(mapped) = results_ref.type_map.get(&dep) {
            if let Some(converted) = results_ref.ds.get(mapped) {
                println!("{}", converted.c_code.replace(mapped, &dep));
            }
        }
    }
    for converted in &results_ref.funcs {
        println!("{}", converted.c_code);
    }
}
<|endoftext|>


<commit_before>mod words;

mod sort;
use sort::{HeapPopDrain};

use std::env;
use std::collections::{BinaryHeap};

fn matches(pat: &String, line: &str) -> bool {
    fn check<P, L>(mut pc: P, lc: L) -> bool where P: Iterator<Item=char>, L: Iterator<Item=char> {
        let mut c = pc.next();
        for outer in lc {
            match c {
                Some(ch) => {
                    if outer == ch {
                        c = pc.next();
                        if c.is_none() {
                            return true;
                        }
                    }
                    else if words::consonant(outer) {
                        return false;
                    }
                },

                None => {
                    return true;
                }
            }
        }

        false
    }

    check(pat.chars(), line.chars()) || check(pat.chars().rev(), line.chars().rev())
}

fn usage(args: Vec<String>) -> ! {
    println!("Usage: {} <consonants>", args[0]);
    std::process::exit(2);
}

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() != 2 {
        usage(args);
    }
    if args[1].chars().any(|c| {!words::consonant(c)}) {
        usage(args);
    }

    let words = words::WORDS.lines().map(|line| {
        line.to_lowercase()
    }).filter(|line| {
        matches(&args[1], line)
    });

    let mut found = BinaryHeap::new();
    for word in words {
        found.push(sort::Length(word));
    }
    if found.len() == 0 {
        println!("No matches found.");
        return;
    }

    for word in found.pop_drain() {
        println!("{}", word.0);
    }
}
<commit_msg>Fast-path for when the line is shorter than the pattern.<commit_after>mod words;

mod sort;
use sort::{HeapPopDrain};

use std::env;
use std::collections::{BinaryHeap};

fn matches(pat: &String, line: &str) -> bool {
    fn check<P, L>(mut pc: P, lc: L) -> bool where P: Iterator<Item=char>, L: Iterator<Item=char> {
        let mut c = pc.next();
        for outer in lc {
            match c {
                Some(ch) => {
                    if outer == ch {
                        c = pc.next();
                        if c.is_none() {
                            return true;
                        }
                    }
                    else if words::consonant(outer) {
                        return false;
                    }
                },

                None => {
                    return true;
                }
            }
        }

        false
    }

    (pat.len() <= line.len()) && (
        check(pat.chars(), line.chars()) ||
        check(pat.chars().rev(), line.chars().rev())
    )
}

fn usage(args: Vec<String>) -> ! {
    println!("Usage: {} <consonants>", args[0]);
    std::process::exit(2);
}

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() != 2 {
        usage(args);
    }
    if args[1].chars().any(|c| {!words::consonant(c)}) {
        usage(args);
    }

    let words = words::WORDS.lines().map(|line| {
        line.to_lowercase()
    }).filter(|line| {
        matches(&args[1], line)
    });

    let mut found = BinaryHeap::new();
    for word in words {
        found.push(sort::Length(word));
    }
    if found.len() == 0 {
        println!("No matches found.");
        return;
    }

    for word in found.pop_drain() {
        println!("{}", word.0);
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

extern crate regex;
use regex::Regex;

use std::io;
use std::io::Write;

#[derive(Debug, PartialEq, Clone)]
pub enum Token {
    Unknown(String), // Invalid test (basically non-ascii)
    Literal(String), // Numeric literal number
    Func(Function), // Pre-defined function (like cos() )
    Const(Constant), // Constant like pi or e
    Var(String), // str arbitrary single char variable name
    Op(Operator), // Any of the 4 operators (+-*/)
    Open, // Open parens '('
    Close, // Closing parens ')'
    Comma,
}

#[derive(Debug, PartialEq, Clone)]
pub enum Function {
	Abs,
    Sqrt,
    Ln,
    Log,
    Sin,
    Csc,
    Cos,
    Sec,
    Tan,
    Cot,
    Asin,
    Acsc,
    Acos,
    Asec,
    Atan,
    Acot,
    Sinh,
    Csch,
    Cosh,
    Sech,
    Tanh,
    Coth,
    Asinh,
    Acsch,
    Acosh,
    Asech,
    Atanh,
    Acoth,
    Max,
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Constant {
	Pi,
	E,
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(u32)]
pub enum Operator {
    Add=0,
    Sub=1,
    Div=2,
    Mul=3,
    Pow=4, // Right
}

#[derive(Debug, PartialEq, Clone)]
struct Expression {
    tokens: Vec<Token>,
}

struct ExpressionIter<'a> {
	expr: &'a Expression,
	count: usize,
}

impl<'a> Iterator for ExpressionIter<'a> {
	type Item = &'a Token;
    fn next(&mut self) -> Option<&'a Token> {
    	let token = self.expr.get_tokens().get(self.count);
    	self.count += 1;
    	token
    }
}

impl Expression {
	fn new(tokens: Vec<Token>) -> Self {
		Expression {tokens: tokens}
	}

	fn push(&mut self, token: Token) {
		self.tokens.push(token);
	}

	fn get_tokens(&self) -> &[Token] {
		self.tokens.as_slice()
	}

	fn get_mut_tokens(&mut self) -> &mut [Token] {
		self.tokens.as_mut_slice()
	}

	fn get_token(&self, index: usize) -> &Token {
		self.tokens.get(index).unwrap()
	}

	fn get_mut_token(&mut self, index: usize) -> &mut Token {
		self.tokens.get_mut(index).unwrap()
	}

	fn replace_token(&mut self, index: usize, new_token: Token) {
		self.tokens.remove(index);
		self.tokens.insert(index, new_token);
	}

	fn replace_all_tokens(&mut self, new_token: Vec<Token>) {
		self.tokens = new_token
	}

	fn find_first(&self, token: &Token) -> Option<usize> {
		for i in 0..self.tokens.len() {
			if self.tokens.get(i).unwrap() == token {
				return Some(i);
			}
		}
		None
	}

	fn find_last(&self, token: &Token) -> Option<usize> {
		for i in (0..self.tokens.len()).rev() {
			if self.tokens.get(i).unwrap() == token {
				return Some(i);
			}
		}
		None
	}

	fn split_at(&self, index: usize) -> (&[Token], &[Token]) {
		self.tokens.split_at(index)
	}

	fn len(&self) -> usize {
		self.tokens.len()
	}

	fn iter(&self) -> ExpressionIter {
		ExpressionIter {expr: &self, count: 0}
	}
}

fn main() {
	let numeric_regex: Regex = Regex::new(r"\d+\.\d+|\d+").unwrap();
	let function_regex: Regex = Regex::new(r"\w{2,}").unwrap();
    println!("Welcome to Rust-Calculus!");
    println!("To evaluate an expression, simply type one in and hit RETURN.");
    println!("To set a variable, simply type VAR_NAME=EXPRESSION and hit RETURN.");
    println!("Valid commands are: sym_int, int, sym_def, and def.");
    println!("Type 'quit' to exit.");
    let mut input = String::new();
    let stdin = io::stdin();
    let mut stdout = io::stdout();
    loop {
    	input.clear();
    	print!(">>>> ");
    	stdout.flush().ok();
        if let Err(x) = stdin.read_line(&mut input) {
        	println!("There was a problem reading stdin: {:?}", x);
        	print!("Exiting...");;
        	break;
        }
        input = strip_white_space(&input).to_lowercase();
        if input == "quit" {
        	print!("Exiting...");
        	break;
        }
        let expr = parse_input(&input, &numeric_regex, &function_regex);
        if expr.is_ok() {
        	println!("{:?}", &expr.unwrap());
        } else {
        	println!("Encountered an error while parsing: {:?}", expr.unwrap_err());
        	println!("Try Again...(type 'quit' to exit)");
        	continue;
        }
    }
}

fn strip_white_space(input: &String) -> String {
	input.split_whitespace().collect::<Vec<&str>>().join("")
}

fn parse_input(input: &String, numeric_regex: &Regex, function_regex: &Regex) -> Result<Expression, String> {
	// 1. Replace everthing except letters/numbers with their enums
	// 2. Then go through and replace things with Literals or functions
	let mut expr: Expression = Expression::new(Vec::new());
	let mut builder: String = String::new();
	for c in input.chars() {
		match c {
		    '(' => {
		    	if builder.len() > 0 {
		    		if numeric_regex.is_match(&builder[..]) {
		    			expr.push(Token::Literal(builder.clone()));
		    		} else if function_regex.is_match(&builder[..]) {
		    			expr.push(map_string_to_func(&builder));
		    		}
		    		builder = String::new();
		    	}
		    	expr.push(Token::Open);
		    },
		    ')' => {
		    	if builder.len() > 0 {
		    		if numeric_regex.is_match(&builder[..]) {
		    			expr.push(Token::Literal(builder.clone()));
		    		} else if function_regex.is_match(&builder[..]) {
		    			expr.push(map_string_to_func(&builder));
		    		}
		    		builder = String::new();
		    	}
		    	expr.push(Token::Close);
		    },
		    '+' => {
		    	if builder.len() > 0 {
		    		if numeric_regex.is_match(&builder[..]) {
		    			expr.push(Token::Literal(builder.clone()));
		    		} else if function_regex.is_match(&builder[..]) {
		    			expr.push(map_string_to_func(&builder));
		    		}
		    		builder = String::new();
		    	}
		    	expr.push(Token::Op(Operator::Add));
		    },
		    '-' => {
		    	if builder.len() > 0 {
		    		if numeric_regex.is_match(&builder[..]) {
		    			expr.push(Token::Literal(builder.clone()));
		    		} else if function_regex.is_match(&builder[..]) {
		    			expr.push(map_string_to_func(&builder));
		    		}
		    		builder = String::new();
		    	}
		    	expr.push(Token::Op(Operator::Sub));
		    },
		    '*' => {
		    	if builder.len() > 0 {
		    		if numeric_regex.is_match(&builder[..]) {
		    			expr.push(Token::Literal(builder.clone()));
		    		} else if function_regex.is_match(&builder[..]) {
		    			expr.push(map_string_to_func(&builder));
		    		}
		    		builder = String::new();
		    	}
		    	expr.push(Token::Op(Operator::Mul));
		    },
		    '/' => {
		    	if builder.len() > 0 {
		    		if numeric_regex.is_match(&builder[..]) {
		    			expr.push(Token::Literal(builder.clone()));
		    		} else if function_regex.is_match(&builder[..]) {
		    			expr.push(map_string_to_func(&builder));
		    		}
		    		builder = String::new();
		    	}
		    	expr.push(Token::Op(Operator::Div));
		    },
		    '^' => {
		    	if builder.len() > 0 {
		    		if numeric_regex.is_match(&builder[..]) {
		    			expr.push(Token::Literal(builder.clone()));
		    		} else if function_regex.is_match(&builder[..]) {
		    			expr.push(map_string_to_func(&builder));
		    		}
		    		builder = String::new();
		    	}
		    	expr.push(Token::Op(Operator::Pow));
		    },
		    ',' => {
		    	if builder.len() > 0 {
		    		if numeric_regex.is_match(&builder[..]) {
		    			expr.push(Token::Literal(builder.clone()));
		    		} else if function_regex.is_match(&builder[..]) {
		    			expr.push(map_string_to_func(&builder));
		    		}
		    		builder = String::new();
		    	}
		    	expr.push(Token::Comma);
		    },
		    _ => {
		    	builder.push(c);
		    }
		}
	}
	if builder.len() > 0 {
		if numeric_regex.is_match(&builder[..]) {
			expr.push(Token::Literal(builder.clone()));
		} else if function_regex.is_match(&builder[..]) {
			expr.push(map_string_to_func(&builder));
		}
		builder = String::new();
	}
	let mut op_stack: Vec<Token> = Vec::with_capacity(input.len());
	let mut out_queue: Vec<Token> = Vec::with_capacity(input.len());
	//println!("EXPR {:?}", &expr);
	for i in 0..expr.len() {
		let current_token = expr.get_token(i);
		//println!("BEFORE current_token: {:?}, OPSTACK {:?}, out_queue {:?}", &current_token, &op_stack, &out_queue);
		match current_token {
		    &Token::Literal(ref x) => out_queue.push(Token::Literal(x.clone())),
		    &Token::Func(ref x) => op_stack.push(Token::Func(x.clone())),
		    &Token::Comma => {
		    	let mut found_open = false;
		    	loop {
			    	let stack_token = try!{op_stack.pop().ok_or("Malformed Expression")};
			    	match stack_token {
			    	    Token::Open => {op_stack.push(stack_token); found_open=true; break;},
			    	    _ => out_queue.push(stack_token),
			    	}
		    	}
		    	if !found_open {return Err(String::from("Mismatch Parenthesis"))}
			},
		    &Token::Op(ref o1) =>  {
		    	loop {
			    	if op_stack.len() < 1 {
						break;
					}
			    	let o2 = try!{op_stack.pop().ok_or("Malformed Expression")}; // top of stack
			    	match o1 {
			    	    &Operator::Pow => match o2 {
			    	    	_ => { op_stack.push(o2); break; },
			    	    },
			    	    &Operator::Mul | &Operator::Div => match o2 {
			    	    	Token::Op(Operator::Pow) | Token::Op(Operator::Mul) | Token::Op(Operator::Div) => out_queue.push(o2),
			    	    	_ => { op_stack.push(o2); break; },
			    	    },
			    	    &Operator::Add | &Operator::Sub => match o2 {
			    	    	Token::Op(Operator::Add) | Token::Op(Operator::Sub) => out_queue.push(o2),
			    	    	_ => { op_stack.push(o2); break; },
			    	    }
			    	}
		    	};
		    	op_stack.push(Token::Op(o1.clone()));
			},
			&Token::Open => op_stack.push(Token::Open),
			&Token::Close => {
				let mut found_open = false;
				loop {
					let stack_token = try!{op_stack.pop().ok_or("Malformed Expression")};
					match stack_token {
						Token::Open => {op_stack.push(stack_token); found_open=true; break;},
						_ => out_queue.push(stack_token),
					}
				};
				if !found_open {return Err(String::from("Mismatch Parenthesis"))}
				let stack_token = try!{op_stack.pop().ok_or("Malformed Expression")};
				let next_stack_token = try!{op_stack.pop().ok_or("Malformed Expression")};
				match next_stack_token {
					Token::Func(ref x) => out_queue.push(Token::Func(x.clone())),
					_ => op_stack.push(next_stack_token),
				}
			},
			&Token::Unknown(ref x) => {
				let mut message: String = "You either misspelled a function, or it is not yet implemented. The unknown string was: ".to_owned();
				message.push_str(x);
				return Err(message);
			},
		    _ => break,
		}
		//println!("AFTER current_token: {:?}, OPSTACK {:?}, out_queue {:?}\n\n\n", &current_token, &op_stack, &out_queue);
	}
	//println!("OPSTACK {:?}", &op_stack);
	while op_stack.len() > 0 {
	    out_queue.push(try!{op_stack.pop().ok_or("Malformed Expression")});
	}
	Ok(Expression::new(out_queue))
}

fn map_string_to_func(input: &String) -> Token {
	match &(input.to_lowercase())[..] {
		"abs" => Token::Func(Function::Abs),
	    "sqrt" => Token::Func(Function::Sqrt),
	    "ln" => Token::Func(Function::Ln),
	    "log" => Token::Func(Function::Log),
	    "sin" => Token::Func(Function::Sin),
	    "csc" => Token::Func(Function::Csc),
	    "cos" => Token::Func(Function::Cos),
	    "sec" => Token::Func(Function::Sec),
	    "tan" => Token::Func(Function::Tan),
	    "cot" => Token::Func(Function::Cot),
	    "asin" => Token::Func(Function::Asin),
	    "acsc" => Token::Func(Function::Acsc),
	    "acos" => Token::Func(Function::Acos),
	    "asec" => Token::Func(Function::Asec),
	    "atan" => Token::Func(Function::Atan),
	    "acot" => Token::Func(Function::Acot),
	    "sinh" => Token::Func(Function::Sinh),
	    "csch" => Token::Func(Function::Csch),
	    "cosh" => Token::Func(Function::Cosh),
	    "sech" => Token::Func(Function::Sech),
	    "tanh" => Token::Func(Function::Tanh),
	    "coth" => Token::Func(Function::Coth),
	    "asinh" => Token::Func(Function::Asinh),
	    "acsch" => Token::Func(Function::Acsch),
	    "acosh" => Token::Func(Function::Acosh),
	    "asech" => Token::Func(Function::Asech),
	    "atanh" => Token::Func(Function::Atanh),
	    "acoth" => Token::Func(Function::Acoth),
	    "max" => Token::Func(Function::Max),
	    _ => Token::Unknown(input.clone()),
	}
}<commit_msg>stuff<commit_after>#![allow(dead_code)]

extern crate regex;
use regex::Regex;

use std::io;
use std::io::Write;

#[derive(Debug, PartialEq, Clone)]
pub enum Token {
    Unknown(String), // Invalid test (basically non-ascii)
    Literal(String), // Numeric literal number
    Func(Function), // Pre-defined function (like cos() )
    Const(Constant), // Constant like pi or e
    Var(String), // str arbitrary single char variable name
    Op(Operator), // Any of the 4 operators (+-*/)
    Open, // Open parens '('
    Close, // Closing parens ')'
    Comma,
}

#[derive(Debug, PartialEq, Clone)]
pub enum Function {
	Abs,
    Sqrt,
    Ln,
    Log,
    Sin,
    Csc,
    Cos,
    Sec,
    Tan,
    Cot,
    Asin,
    Acsc,
    Acos,
    Asec,
    Atan,
    Acot,
    Sinh,
    Csch,
    Cosh,
    Sech,
    Tanh,
    Coth,
    Asinh,
    Acsch,
    Acosh,
    Asech,
    Atanh,
    Acoth,
    Max,
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Constant {
	Pi,
	E,
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(u32)]
pub enum Operator {
    Add=0,
    Sub=1,
    Div=2,
    Mul=3,
    Pow=4, // Right
}

#[derive(Debug, PartialEq, Clone)]
struct Expression {
    tokens: Vec<Token>,
}

struct ExpressionIter<'a> {
	expr: &'a Expression,
	count: usize,
}

impl<'a> Iterator for ExpressionIter<'a> {
	type Item = &'a Token;
    fn next(&mut self) -> Option<&'a Token> {
    	let token = self.expr.get_tokens().get(self.count);
    	self.count += 1;
    	token
    }
}

impl Expression {
	fn new(tokens: Vec<Token>) -> Self {
		Expression {tokens: tokens}
	}

	fn push(&mut self, token: Token) {
		self.tokens.push(token);
	}

	fn get_tokens(&self) -> &[Token] {
		self.tokens.as_slice()
	}

	fn get_mut_tokens(&mut self) -> &mut [Token] {
		self.tokens.as_mut_slice()
	}

	fn get_token(&self, index: usize) -> &Token {
		self.tokens.get(index).unwrap()
	}

	fn get_mut_token(&mut self, index: usize) -> &mut Token {
		self.tokens.get_mut(index).unwrap()
	}

	fn replace_token(&mut self, index: usize, new_token: Token) {
		self.tokens.remove(index);
		self.tokens.insert(index, new_token);
	}

	fn replace_all_tokens(&mut self, new_token: Vec<Token>) {
		self.tokens = new_token
	}

	fn find_first(&self, token: &Token) -> Option<usize> {
		for i in 0..self.tokens.len() {
			if self.tokens.get(i).unwrap() == token {
				return Some(i);
			}
		}
		None
	}

	fn find_last(&self, token: &Token) -> Option<usize> {
		for i in (0..self.tokens.len()).rev() {
			if self.tokens.get(i).unwrap() == token {
				return Some(i);
			}
		}
		None
	}

	fn split_at(&self, index: usize) -> (&[Token], &[Token]) {
		self.tokens.split_at(index)
	}

	fn len(&self) -> usize {
		self.tokens.len()
	}

	fn iter(&self) -> ExpressionIter {
		ExpressionIter {expr: &self, count: 0}
	}
}

fn main() {
	let numeric_regex: Regex = Regex::new(r"\d+\.\d+|\d+").unwrap();
	let function_regex: Regex = Regex::new(r"\w{2,}").unwrap();
    println!("Welcome to Rust-Calculus!");
    println!("To evaluate an expression, simply type one in and hit RETURN.");
    println!("To set a variable, simply type VAR_NAME=EXPRESSION and hit RETURN.");
    println!("Valid commands are: sym_int, int, sym_def, and def.");
    println!("Type 'quit' to exit.");
    let mut input = String::new();
    let stdin = io::stdin();
    let mut stdout = io::stdout();
    loop {
    	input.clear();
    	print!(">>>> ");
    	stdout.flush().ok();
        if let Err(x) = stdin.read_line(&mut input) {
        	println!("There was a problem reading stdin: {:?}", x);
        	print!("Exiting...");;
        	break;
        }
        input = strip_white_space(&input).to_lowercase();
        if input == "quit" {
        	print!("Exiting...");
        	break;
        }
        let expr = parse_input(&input, &numeric_regex, &function_regex);
        if expr.is_ok() {
        	println!("{:?}", &expr.unwrap());
        } else {
        	println!("Encountered an error while parsing: {:?}", expr.unwrap_err());
        	println!("Try Again...(type 'quit' to exit)");
        	continue;
        }
    }
}

fn strip_white_space(input: &String) -> String {
	input.split_whitespace().collect::<Vec<&str>>().join("")
}

fn parse_input(input: &String, numeric_regex: &Regex, function_regex: &Regex) -> Result<Expression, String> {
	// 1. Replace everthing except letters/numbers with their enums
	// 2. Then go through and replace things with Literals or functions
	let mut expr: Expression = Expression::new(Vec::new());
	let mut builder: String = String::new();
	for c in input.chars() {
		match c {
		    '(' => {
		    	if builder.len() > 0 {
		    		if numeric_regex.is_match(&builder[..]) {
		    			expr.push(Token::Literal(builder.clone()));
		    		} else if function_regex.is_match(&builder[..]) {
		    			expr.push(map_string_to_func(&builder));
		    		}
		    		builder = String::new();
		    	}
		    	expr.push(Token::Open);
		    },
		    ')' => {
		    	if builder.len() > 0 {
		    		if numeric_regex.is_match(&builder[..]) {
		    			expr.push(Token::Literal(builder.clone()));
		    		} else if function_regex.is_match(&builder[..]) {
		    			expr.push(map_string_to_func(&builder));
		    		}
		    		builder = String::new();
		    	}
		    	expr.push(Token::Close);
		    },
		    '+' => {
		    	if builder.len() > 0 {
		    		if numeric_regex.is_match(&builder[..]) {
		    			expr.push(Token::Literal(builder.clone()));
		    		} else if function_regex.is_match(&builder[..]) {
		    			expr.push(map_string_to_func(&builder));
		    		}
		    		builder = String::new();
		    	}
		    	expr.push(Token::Op(Operator::Add));
		    },
		    '-' => {
		    	if builder.len() > 0 {
		    		if numeric_regex.is_match(&builder[..]) {
		    			expr.push(Token::Literal(builder.clone()));
		    		} else if function_regex.is_match(&builder[..]) {
		    			expr.push(map_string_to_func(&builder));
		    		}
		    		builder = String::new();
		    	}
		    	expr.push(Token::Op(Operator::Sub));
		    },
		    '*' => {
		    	if builder.len() > 0 {
		    		if numeric_regex.is_match(&builder[..]) {
		    			expr.push(Token::Literal(builder.clone()));
		    		} else if function_regex.is_match(&builder[..]) {
		    			expr.push(map_string_to_func(&builder));
		    		}
		    		builder = String::new();
		    	}
		    	expr.push(Token::Op(Operator::Mul));
		    },
		    '/' => {
		    	if builder.len() > 0 {
		    		if numeric_regex.is_match(&builder[..]) {
		    			expr.push(Token::Literal(builder.clone()));
		    		} else if function_regex.is_match(&builder[..]) {
		    			expr.push(map_string_to_func(&builder));
		    		}
		    		builder = String::new();
		    	}
		    	expr.push(Token::Op(Operator::Div));
		    },
		    '^' => {
		    	if builder.len() > 0 {
		    		if numeric_regex.is_match(&builder[..]) {
		    			expr.push(Token::Literal(builder.clone()));
		    		} else if function_regex.is_match(&builder[..]) {
		    			expr.push(map_string_to_func(&builder));
		    		}
		    		builder = String::new();
		    	}
		    	expr.push(Token::Op(Operator::Pow));
		    },
		    ',' => {
		    	if builder.len() > 0 {
		    		if numeric_regex.is_match(&builder[..]) {
		    			expr.push(Token::Literal(builder.clone()));
		    		} else if function_regex.is_match(&builder[..]) {
		    			expr.push(map_string_to_func(&builder));
		    		}
		    		builder = String::new();
		    	}
		    	expr.push(Token::Comma);
		    },
		    _ => {
		    	builder.push(c);
		    }
		}
	}
	if builder.len() > 0 {
		if numeric_regex.is_match(&builder[..]) {
			expr.push(Token::Literal(builder.clone()));
		} else if function_regex.is_match(&builder[..]) {
			expr.push(map_string_to_func(&builder));
		}
		builder = String::new();
	}
	let mut op_stack: Vec<Token> = Vec::with_capacity(input.len());
	let mut out_queue: Vec<Token> = Vec::with_capacity(input.len());
	//println!("EXPR {:?}", &expr);
	for i in 0..expr.len() {
		let current_token = expr.get_token(i);
		//println!("BEFORE current_token: {:?}, OPSTACK {:?}, out_queue {:?}", &current_token, &op_stack, &out_queue);
		match current_token {
		    &Token::Literal(ref x) => out_queue.push(Token::Literal(x.clone())),
		    &Token::Func(ref x) => op_stack.push(Token::Func(x.clone())),
		    &Token::Comma => {
		    	let mut found_open = false;
		    	loop {
			    	let stack_token = try!{op_stack.pop().ok_or("Malformed Expression")};
			    	match stack_token {
			    	    Token::Open => {op_stack.push(stack_token); found_open=true; break;},
			    	    _ => out_queue.push(stack_token),
			    	}
		    	}
		    	if !found_open {return Err(String::from("Mismatch Parenthesis"))}
			},
		    &Token::Op(ref o1) =>  {
		    	loop {
			    	if op_stack.len() < 1 {
						break;
					}
			    	let o2 = try!{op_stack.pop().ok_or("Malformed Expression")}; // top of stack
			    	match o1 {
			    	    &Operator::Pow => match o2 {
			    	    	_ => { op_stack.push(o2); break; },
			    	    },
			    	    &Operator::Mul | &Operator::Div => match o2 {
			    	    	Token::Op(Operator::Pow) | Token::Op(Operator::Mul) | Token::Op(Operator::Div) => out_queue.push(o2),
			    	    	_ => { op_stack.push(o2); break; },
			    	    },
			    	    &Operator::Add | &Operator::Sub => match o2 {
			    	    	Token::Op(Operator::Add) | Token::Op(Operator::Sub) => out_queue.push(o2),
			    	    	_ => { op_stack.push(o2); break; },
			    	    }
			    	}
		    	};
		    	op_stack.push(Token::Op(o1.clone()));
			},
			&Token::Open => op_stack.push(Token::Open),
			&Token::Close => {
				let mut found_open = false;
				loop {
					let stack_token = try!{op_stack.pop().ok_or("Malformed Expression")};
					match stack_token {
						Token::Open => {op_stack.push(stack_token); found_open=true; break;},
						_ => out_queue.push(stack_token),
					}
				};
				if !found_open {return Err(String::from("Mismatch Parenthesis"))}
				let stack_token = try!{op_stack.pop().ok_or("Malformed Expression")};
				let next_stack_token = try!{op_stack.pop().ok_or("Malformed Expression")};
				match next_stack_token {
					Token::Func(ref x) => out_queue.push(Token::Func(x.clone())),
					_ => op_stack.push(next_stack_token),
				}
			},
			&Token::Unknown(ref x) => {
				let mut message: String = "You either misspelled a function, or it is not yet implemented. The unknown string was: ".to_owned();
				message.push_str(x);
				return Err(message);
			},
		    _ => break,
		}
		//println!("AFTER current_token: {:?}, OPSTACK {:?}, out_queue {:?}\n\n\n", &current_token, &op_stack, &out_queue);
	}
	//println!("OPSTACK {:?}", &op_stack);
	while op_stack.len() > 0 {
	    out_queue.push(try!{op_stack.pop().ok_or("Malformed Expression")});
	}
	Ok(Expression::new(out_queue))
}

fn map_string_to_func(input: &String) -> Token {
	match &(input.to_lowercase())[..] {
		"abs" => Token::Func(Function::Abs),
	    "sqrt" => Token::Func(Function::Sqrt),
	    "ln" => Token::Func(Function::Ln),
	    "log" => Token::Func(Function::Log),
	    "sin" => Token::Func(Function::Sin),
	    "csc" => Token::Func(Function::Csc),
	    "cos" => Token::Func(Function::Cos),
	    "sec" => Token::Func(Function::Sec),
	    "tan" => Token::Func(Function::Tan),
	    "cot" => Token::Func(Function::Cot),
	    "asin" => Token::Func(Function::Asin),
	    "acsc" => Token::Func(Function::Acsc),
	    "acos" => Token::Func(Function::Acos),
	    "asec" => Token::Func(Function::Asec),
	    "atan" => Token::Func(Function::Atan),
	    "acot" => Token::Func(Function::Acot),
	    "sinh" => Token::Func(Function::Sinh),
	    "csch" => Token::Func(Function::Csch),
	    "cosh" => Token::Func(Function::Cosh),
	    "sech" => Token::Func(Function::Sech),
	    "tanh" => Token::Func(Function::Tanh),
	    "coth" => Token::Func(Function::Coth),
	    "asinh" => Token::Func(Function::Asinh),
	    "acsch" => Token::Func(Function::Acsch),
	    "acosh" => Token::Func(Function::Acosh),
	    "asech" => Token::Func(Function::Asech),
	    "atanh" => Token::Func(Function::Atanh),
	    "acoth" => Token::Func(Function::Acoth),
	    "max" => Token::Func(Function::Max),
	    _ => Token::Unknown(input.clone()),
	}
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(docopt_macros)]

#![feature(test)]

extern crate jsrs_common;
extern crate jsrs_parser;
extern crate french_press;

extern crate walkdir;
extern crate unescape;
extern crate rustyline;
extern crate rustc_serialize;
extern crate docopt;

extern crate test;

mod eval;
mod native;
mod number;
mod bench;
mod preprocess;
mod var;

use std::cell::RefCell;
use std::fs::{File, metadata};
use std::io::{self, BufReader};
use std::io::prelude::*;
use std::path::Path;
use std::process::exit;
use std::rc::Rc;

use rustyline::error::ReadlineError;
use rustyline::Editor;
use walkdir::WalkDir;

use jsrs_common::types::coerce::AsString;
use french_press::{init_gc, ScopeManager};

use eval::eval_string;
use native::add_pervasives;
use preprocess::{clean_string, add_semicolon};
use jsrs_common::js_error::{self, JsError};

docopt!(Args derive Debug, "
js.rs - a javascript interpreter

Usage:
jsrs
jsrs <file>
jsrs --test
");

fn eval_file(filename: String, debug: bool, should_repl: bool,
             scope_manager: Rc<RefCell<ScopeManager>>) -> js_error::Result<()> {
    if debug {
        println!("Reading from \"{}\"", filename);
    }

    let path = Path::new(&filename);
    let file = File::open(&path)
        .expect(&format!("Cannot open \"{}\": no such file or directory", filename));
    let file_buffer = BufReader::new(file);

    let mut line_builder = String::new();
    let mut braces = Vec::new();

    let mut file_iter = file_buffer.lines();
    let mut negative_test = false;
    loop {
        if let Some(line) = file_iter.next() {
            let input = String::from(line.expect(&format!("Cannot read from {}", filename)));
            let input = clean_string(input);
            if input.contains("@negative") {
                negative_test = true;
            }

            let mut last = '\0';
            // Match braces to see if we should wait for more input
            for c in input.chars() {
                if c == '(' {
                    braces.push('(');
                } else if c == '{' {
                    braces.push('{');
                } else if c == '*' && last == '/' {
                    braces.push('/');
                } else if c == ')' {
                    if braces.pop() != Some('(') {
                        return Err(JsError::ParseError(format!("Unexpected token {}", c)));
                    }
                } else if c == '}' {
                    if braces.pop() != Some('{') {
                        return Err(JsError::ParseError(format!("Unexpected token {}", c)));
                    }
                } else if c == '/' && last == '*' {
                    if braces.pop() != Some('/') {
                        return Err(JsError::ParseError(format!("Unexpected token {}", c)));
                    }
                }
                last = c;
            }

            line_builder.push_str(&input);

            if braces.len() == 0 {
                let js_string = clean_string(line_builder.clone());
                line_builder = String::new();
                if js_string == "" {
                    continue;
                }

                if debug {
                    println!("\n{}", js_string);
                }

                let ret;
                let eval_result = eval_string(&js_string, scope_manager.clone());
                if negative_test {
                    match eval_result {
                        Ok((var, ptr)) => ret = (var, ptr),
                        Err(e) => {
                            if !e.is_meta_error() {
                                continue;
                            } else {
                                return Err(e);
                            }
                        }
                    }
                } else {
                    ret = try!(eval_result);
                }

                if debug {
                    println!("=> {:?}", ret);
                }
            }
        } else {
            break;
        }
    }
    if should_repl {
        repl(scope_manager);
    }
    Ok(())
}

fn test_dir(dir_name: String) {
    for entry in WalkDir::new(dir_name) {
        let entry = entry.unwrap();
        if !entry.path().is_dir() {
            let entry_path = entry.path().display().to_string();
            let scope_manager = Rc::new(RefCell::new(init_gc()));
            add_pervasives(scope_manager.clone());
            println!("{}", entry_path);
            match eval_file(entry_path.clone(), false, false, scope_manager.clone()) {
                Ok(_) => {
                    println!("{}: {}", entry_path, "OK");
                }
                Err(e) => {
                    println!("{}: {}", entry_path, e);
                }
            }
        }
    }
}

fn repl(scope_manager: Rc<RefCell<ScopeManager>>) -> i32 {
    let mut rl = Editor::new();
    let mut stderr = io::stderr();

    if metadata(".history").is_ok() && rl.load_history(".history").is_err() {
        writeln!(stderr, "Error: unable to load history on startup").unwrap();
    }

    loop {
        // prompt
        let readline = rl.readline(">> ");

        match readline {
            Ok(line) => {
                rl.add_history_entry(&line);
                let input = add_semicolon(clean_string(String::from(line)));
                if input == "" {
                    continue;
                }
                rl.add_history_entry(&input);

                match eval_string(&input, scope_manager.clone()) {
                    Ok((var, ptr)) => println!("{}", ptr.map(|p| p.as_string()).unwrap_or(var.t.as_string())),
                    Err(e) => println!("{:?}", e),
                }
            },
            Err(ReadlineError::Interrupted) => {
                if rl.save_history(".history").is_err() {
                    writeln!(stderr, "Error: unable to save history on exit").unwrap();
                    return 2;
                }
                return 1;
            },
            Err(ReadlineError::Eof) => {
                if rl.save_history(".history").is_err() {
                    writeln!(stderr, "Error: unable to save history on exit").unwrap();
                    return 2;
                }
                break
            },
            Err(err) => {
                println!("Error: {:?}", err);

                if rl.save_history(".history").is_err() {
                    writeln!(stderr, "Error: unable to save history on exit").unwrap();
                    return 2;
                }
                return 3;
            }
        }
    }
    0
}

fn main() {
    let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());
    if args.flag_test {
        let dir_name = "sputnik";

        test_dir(String::from(dir_name))
    } else {
        let scope_manager = Rc::new(RefCell::new(init_gc()));
        add_pervasives(scope_manager.clone());

        if args.arg_file == "" {
            let ret = repl(scope_manager.clone());
            exit(ret)
        } else {
            eval_file(args.arg_file, true, true, scope_manager.clone())
                .expect("Error evaluating file");
        }
    }
}
<commit_msg>Replace loop {} with while let {}<commit_after>#![feature(plugin)]
#![plugin(docopt_macros)]

#![feature(test)]

extern crate jsrs_common;
extern crate jsrs_parser;
extern crate french_press;

extern crate walkdir;
extern crate unescape;
extern crate rustyline;
extern crate rustc_serialize;
extern crate docopt;

extern crate test;

mod eval;
mod native;
mod number;
mod bench;
mod preprocess;
mod var;

use std::cell::RefCell;
use std::fs::{File, metadata};
use std::io::{self, BufReader};
use std::io::prelude::*;
use std::path::Path;
use std::process::exit;
use std::rc::Rc;

use rustyline::error::ReadlineError;
use rustyline::Editor;
use walkdir::WalkDir;

use jsrs_common::types::coerce::AsString;
use french_press::{init_gc, ScopeManager};

use eval::eval_string;
use native::add_pervasives;
use preprocess::{clean_string, add_semicolon};
use jsrs_common::js_error::{self, JsError};

docopt!(Args derive Debug, "
js.rs - a javascript interpreter

Usage:
jsrs
jsrs <file>
jsrs --test
");

fn eval_file(filename: String, debug: bool, should_repl: bool,
             scope_manager: Rc<RefCell<ScopeManager>>) -> js_error::Result<()> {
    if debug {
        println!("Reading from \"{}\"", filename);
    }

    let path = Path::new(&filename);
    let file = File::open(&path)
        .expect(&format!("Cannot open \"{}\": no such file or directory", filename));
    let file_buffer = BufReader::new(file);

    let mut line_builder = String::new();
    let mut braces = Vec::new();

    let mut file_iter = file_buffer.lines();
    let mut negative_test = false;
    while let Some(line) = file_iter.next() {
        let input = String::from(line.expect(&format!("Cannot read from {}", filename)));
        let input = clean_string(input);
        if input.contains("@negative") {
            negative_test = true;
        }

        let mut last = '\0';
        // Match braces to see if we should wait for more input
        for c in input.chars() {
            if c == '(' {
                braces.push('(');
            } else if c == '{' {
                braces.push('{');
            } else if c == '*' && last == '/' {
                braces.push('/');
            } else if c == ')' {
                if braces.pop() != Some('(') {
                    return Err(JsError::ParseError(format!("Unexpected token {}", c)));
                }
            } else if c == '}' {
                if braces.pop() != Some('{') {
                    return Err(JsError::ParseError(format!("Unexpected token {}", c)));
                }
            } else if c == '/' && last == '*' {
                if braces.pop() != Some('/') {
                    return Err(JsError::ParseError(format!("Unexpected token {}", c)));
                }
            }
            last = c;
        }

        line_builder.push_str(&input);

        if braces.len() == 0 {
            let js_string = clean_string(line_builder.clone());
            line_builder = String::new();
            if js_string == "" {
                continue;
            }

            if debug {
                println!("\n{}", js_string);
            }

            let ret;
            let eval_result = eval_string(&js_string, scope_manager.clone());
            if negative_test {
                match eval_result {
                    Ok((var, ptr)) => ret = (var, ptr),
                    Err(e) => {
                        if !e.is_meta_error() {
                            continue;
                        } else {
                            return Err(e);
                        }
                    }
                }
            } else {
                ret = try!(eval_result);
            }

            if debug {
                println!("=> {:?}", ret);
            }
        }
    }
    if should_repl {
        repl(scope_manager);
    }
    Ok(())
}

fn test_dir(dir_name: String) {
    for entry in WalkDir::new(dir_name) {
        let entry = entry.unwrap();
        if !entry.path().is_dir() {
            let entry_path = entry.path().display().to_string();
            let scope_manager = Rc::new(RefCell::new(init_gc()));
            add_pervasives(scope_manager.clone());
            println!("{}", entry_path);
            match eval_file(entry_path.clone(), false, false, scope_manager.clone()) {
                Ok(_) => {
                    println!("{}: {}", entry_path, "OK");
                }
                Err(e) => {
                    println!("{}: {}", entry_path, e);
                }
            }
        }
    }
}

fn repl(scope_manager: Rc<RefCell<ScopeManager>>) -> i32 {
    let mut rl = Editor::new();
    let mut stderr = io::stderr();

    if metadata(".history").is_ok() && rl.load_history(".history").is_err() {
        writeln!(stderr, "Error: unable to load history on startup").unwrap();
    }

    loop {
        // prompt
        let readline = rl.readline(">> ");

        match readline {
            Ok(line) => {
                rl.add_history_entry(&line);
                let input = add_semicolon(clean_string(String::from(line)));
                if input == "" {
                    continue;
                }
                rl.add_history_entry(&input);

                match eval_string(&input, scope_manager.clone()) {
                    Ok((var, ptr)) => println!("{}", ptr.map(|p| p.as_string()).unwrap_or(var.t.as_string())),
                    Err(e) => println!("{:?}", e),
                }
            },
            Err(ReadlineError::Interrupted) => {
                if rl.save_history(".history").is_err() {
                    writeln!(stderr, "Error: unable to save history on exit").unwrap();
                    return 2;
                }
                return 1;
            },
            Err(ReadlineError::Eof) => {
                if rl.save_history(".history").is_err() {
                    writeln!(stderr, "Error: unable to save history on exit").unwrap();
                    return 2;
                }
                break
            },
            Err(err) => {
                println!("Error: {:?}", err);

                if rl.save_history(".history").is_err() {
                    writeln!(stderr, "Error: unable to save history on exit").unwrap();
                    return 2;
                }
                return 3;
            }
        }
    }
    0
}

fn main() {
    let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());
    if args.flag_test {
        let dir_name = "sputnik";

        test_dir(String::from(dir_name))
    } else {
        let scope_manager = Rc::new(RefCell::new(init_gc()));
        add_pervasives(scope_manager.clone());

        if args.arg_file == "" {
            let ret = repl(scope_manager.clone());
            exit(ret)
        } else {
            eval_file(args.arg_file, true, true, scope_manager.clone())
                .expect("Error evaluating file");
        }
    }
}
<|endoftext|>


<commit_before>#![allow(non_upper_case_globals)]

#![allow(dead_code)]
#![allow(unused_assignments)]
#![allow(unused_variables)]

#[macro_use] extern crate lazy_static;

use std::ffi::OsStr;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
use std::path::{ Path, PathBuf };

lazy_static! {
    static ref HOME: PathBuf = std::env::home_dir().unwrap();

    static ref build_props: PathBuf = PathBuf::from("project/build.properties");

    static ref sbt_launch_dir: PathBuf = { let mut p = PathBuf::from(&*HOME); p.push(".sbt/launchers"); p };
}

const sbt_launch_ivy_release_repo: &'static str = "http://repo.typesafe.com/typesafe/ivy-releases";
const sbt_launch_mvn_release_repo: &'static str = "http://repo.scala-sbt.org/scalasbt/maven-releases";

macro_rules! echoerr(($($arg:tt)*) => (writeln!(&mut ::std::io::stderr(), $($arg)*).unwrap();));

fn build_props_sbt() -> String {
    if let Ok(f) = File::open(&*build_props) {
        let f = BufReader::new(f);
        for line in f.lines() {
            let line = line.unwrap();
            if line.starts_with("sbt.version") {
                let line = line.replace("=", " ");
                let line = line.replace("\r", " ");
                return line.split_whitespace().nth(1).unwrap().to_owned();
            }
        }
    }
    "".to_owned()
}

fn jar_file(version: &str) -> PathBuf {
    let mut p = PathBuf::from(&*sbt_launch_dir);
    p.push(version);
    p.push("sbt-launch.jar");
    p
}

fn url_base(version: &str) -> &'static str { match version {
    s if s.starts_with("0.7.")  => "http://simple-build-tool.googlecode.com",
    s if s.starts_with("0.10.") => sbt_launch_ivy_release_repo,
    "0.11.1" | "0.11.2"         => sbt_launch_ivy_release_repo,
 // "0.*-yyyymmdd-hhMMss"       => sbt_launch_ivy_snapshot_repo, // https://repo.scala-sbt.org/scalasbt/ivy-snapshots
    s if s.starts_with("0.")    => sbt_launch_ivy_release_repo,
 // "*-yyyymmdd-hhMMss"         => sbt_launch_mvn_snapshot_repo, // http://repo.scala-sbt.org/scalasbt/maven-snapshots
    _                           => sbt_launch_mvn_release_repo,
} }

fn make_url(version: &str) -> String {
    let base = url_base(version);
    match version {
        s if s.starts_with("0.7.")  => format!("{}/files/sbt-launch-0.7.7.jar", base),
        s if s.starts_with("0.10.") => format!("{}/org.scala-tools.sbt/sbt-launch/{}/sbt-launch.jar", base, version),
        "0.11.1" | "0.11.2"         => format!("{}/org.scala-tools.sbt/sbt-launch/{}/sbt-launch.jar", base, version),
        s if s.starts_with("0.")    => format!("{}/org.scala-sbt/sbt-launch/{}/sbt-launch.jar", base, version),
        _                           => format!("{}/org/scala-sbt/sbt-launch/{}/sbt-launch.jar", base, version),
    }
}

fn download_url(sbt_version: &str, url: &str, jar: &Path) {
    echoerr!("Downloading sbt launcher for {}:", sbt_version);
    echoerr!("  From  {}", url);
    echoerr!("    To  {}", jar.display());

    std::fs::create_dir_all(jar.parent().unwrap()).unwrap();

    extern crate hyper;
    let mut r = BufReader::new(hyper::client::Client::new().get(url).send().unwrap());
    let mut buf = [0; 16384];
    let mut jar = std::io::BufWriter::new(File::create(jar).unwrap());
    while {
        let bc = r.read(&mut buf).unwrap();
        jar.write(&buf[0..bc]).unwrap();
        bc > 0
    } {}
    jar.flush().unwrap();
}

fn exec_runner<S: AsRef<OsStr>>(args: &[S]) {
    use std::os::unix::process::CommandExt;
    let err = std::process::Command::new(&args[0]).args(&args[1..]).exec();
    println!("error: {}", err);
    if let Some(err) = err.raw_os_error() {
        std::process::exit(err);
    }
    std::process::exit(-1)
}

fn main() {
    // declare -r script_path="$(get_script_path "$BASH_SOURCE")"
    // declare -r script_name="${script_path##*/}"
    let script_name = "sbtl";

    let mut verbose = false;

    fn vlog(s: &str) { /* if verbose { echoerr!("{}", s); } */ }

    let java_cmd = "java";
    let extra_jvm_opts = ["-Xms512m", "-Xmx1536m", "-Xss2m"];
    let java_args: [&str; 0] = [];
    let sbt_commands: [&str; 0] = [];
    let mut residual_args: Vec<&OsStr> = Vec::new();

    // addResidual() { vlog "[residual] arg = '$1'"  ; residual_args+=("$1"); }
    fn add_residual(s: &str) { vlog(&format!("[residual] arg = {}", s)); /* residual_args.push(s); */ }

    for arg in std::env::args() {
        match arg.as_ref() {
            "-v" => verbose = true,
            s    => add_residual(s),
        }
    }

    let argument_count = std::env::args().len();
    if argument_count > 0 {
        vlog(&format!("Starting {}: invoke with -help for other options", script_name));
        residual_args = vec!["shell".as_ref()];
    }

    let sbt_version = build_props_sbt();
    let mut sbt_jar = jar_file(&sbt_version);
    if !sbt_jar.exists() {
        sbt_jar = PathBuf::from(&*HOME);
        sbt_jar.push(format!(".ivy2/local/org.scala-sbt/sbt-launch/{}/jars/sbt-launch.jar", sbt_version));
    }
    if !sbt_jar.exists() {
        sbt_jar = jar_file(&sbt_version);
        download_url(&sbt_version, &make_url(&sbt_version), &sbt_jar);
    }
    let sbt_jar = sbt_jar.as_path();

    let mut exec_args: Vec<&OsStr> = Vec::new();
    exec_args.push(java_cmd.as_ref());
    exec_args.append(&mut extra_jvm_opts.iter().map(AsRef::as_ref).collect());
    exec_args.append(&mut java_args.iter().map(AsRef::as_ref).collect());
    exec_args.append(&mut vec!["-jar".as_ref(), sbt_jar.as_ref()]);
    exec_args.append(&mut sbt_commands.iter().map(AsRef::as_ref).collect());
    exec_args.append(&mut residual_args.iter().map(AsRef::as_ref).collect());

    exec_runner(&exec_args)
}
<commit_msg>Fix script_name<commit_after>#![allow(non_upper_case_globals)]

#![allow(dead_code)]
#![allow(unused_assignments)]
#![allow(unused_variables)]

#[macro_use] extern crate lazy_static;

use std::ffi::OsStr;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
use std::path::{ Path, PathBuf };

lazy_static! {
    static ref HOME: PathBuf = std::env::home_dir().unwrap();

    static ref build_props: PathBuf = PathBuf::from("project/build.properties");

    static ref sbt_launch_dir: PathBuf = { let mut p = PathBuf::from(&*HOME); p.push(".sbt/launchers"); p };
}

const sbt_launch_ivy_release_repo: &'static str = "http://repo.typesafe.com/typesafe/ivy-releases";
const sbt_launch_mvn_release_repo: &'static str = "http://repo.scala-sbt.org/scalasbt/maven-releases";

macro_rules! echoerr(($($arg:tt)*) => (writeln!(&mut ::std::io::stderr(), $($arg)*).unwrap();));

fn build_props_sbt() -> String {
    if let Ok(f) = File::open(&*build_props) {
        let f = BufReader::new(f);
        for line in f.lines() {
            let line = line.unwrap();
            if line.starts_with("sbt.version") {
                let line = line.replace("=", " ");
                let line = line.replace("\r", " ");
                return line.split_whitespace().nth(1).unwrap().to_owned();
            }
        }
    }
    "".to_owned()
}

fn jar_file(version: &str) -> PathBuf {
    let mut p = PathBuf::from(&*sbt_launch_dir);
    p.push(version);
    p.push("sbt-launch.jar");
    p
}

fn url_base(version: &str) -> &'static str { match version {
    s if s.starts_with("0.7.")  => "http://simple-build-tool.googlecode.com",
    s if s.starts_with("0.10.") => sbt_launch_ivy_release_repo,
    "0.11.1" | "0.11.2"         => sbt_launch_ivy_release_repo,
 // "0.*-yyyymmdd-hhMMss"       => sbt_launch_ivy_snapshot_repo, // https://repo.scala-sbt.org/scalasbt/ivy-snapshots
    s if s.starts_with("0.")    => sbt_launch_ivy_release_repo,
 // "*-yyyymmdd-hhMMss"         => sbt_launch_mvn_snapshot_repo, // http://repo.scala-sbt.org/scalasbt/maven-snapshots
    _                           => sbt_launch_mvn_release_repo,
} }

fn make_url(version: &str) -> String {
    let base = url_base(version);
    match version {
        s if s.starts_with("0.7.")  => format!("{}/files/sbt-launch-0.7.7.jar", base),
        s if s.starts_with("0.10.") => format!("{}/org.scala-tools.sbt/sbt-launch/{}/sbt-launch.jar", base, version),
        "0.11.1" | "0.11.2"         => format!("{}/org.scala-tools.sbt/sbt-launch/{}/sbt-launch.jar", base, version),
        s if s.starts_with("0.")    => format!("{}/org.scala-sbt/sbt-launch/{}/sbt-launch.jar", base, version),
        _                           => format!("{}/org/scala-sbt/sbt-launch/{}/sbt-launch.jar", base, version),
    }
}

fn download_url(sbt_version: &str, url: &str, jar: &Path) {
    echoerr!("Downloading sbt launcher for {}:", sbt_version);
    echoerr!("  From  {}", url);
    echoerr!("    To  {}", jar.display());

    std::fs::create_dir_all(jar.parent().unwrap()).unwrap();

    extern crate hyper;
    let mut r = BufReader::new(hyper::client::Client::new().get(url).send().unwrap());
    let mut buf = [0; 16384];
    let mut jar = std::io::BufWriter::new(File::create(jar).unwrap());
    while {
        let bc = r.read(&mut buf).unwrap();
        jar.write(&buf[0..bc]).unwrap();
        bc > 0
    } {}
    jar.flush().unwrap();
}

fn exec_runner<S: AsRef<OsStr>>(args: &[S]) {
    use std::os::unix::process::CommandExt;
    let err = std::process::Command::new(&args[0]).args(&args[1..]).exec();
    println!("error: {}", err);
    if let Some(err) = err.raw_os_error() {
        std::process::exit(err);
    }
    std::process::exit(-1)
}

fn main() {
    let script_name = std::env::args().nth(0).unwrap();
    let script_name = Path::new(&script_name).file_name().unwrap().to_str().unwrap();

    let mut verbose = false;

    fn vlog(s: &str) { /* if verbose { echoerr!("{}", s); } */ }

    let java_cmd = "java";
    let extra_jvm_opts = ["-Xms512m", "-Xmx1536m", "-Xss2m"];
    let java_args: [&str; 0] = [];
    let sbt_commands: [&str; 0] = [];
    let mut residual_args: Vec<&OsStr> = Vec::new();

    // addResidual() { vlog "[residual] arg = '$1'"  ; residual_args+=("$1"); }
    fn add_residual(s: &str) { vlog(&format!("[residual] arg = {}", s)); /* residual_args.push(s); */ }

    for arg in std::env::args() {
        match arg.as_ref() {
            "-v" => verbose = true,
            s    => add_residual(s),
        }
    }

    let argument_count = std::env::args().len();
    if argument_count > 0 {
        vlog(&format!("Starting {}: invoke with -help for other options", script_name));
        residual_args = vec!["shell".as_ref()];
    }

    let sbt_version = build_props_sbt();
    let mut sbt_jar = jar_file(&sbt_version);
    if !sbt_jar.exists() {
        sbt_jar = PathBuf::from(&*HOME);
        sbt_jar.push(format!(".ivy2/local/org.scala-sbt/sbt-launch/{}/jars/sbt-launch.jar", sbt_version));
    }
    if !sbt_jar.exists() {
        sbt_jar = jar_file(&sbt_version);
        download_url(&sbt_version, &make_url(&sbt_version), &sbt_jar);
    }
    let sbt_jar = sbt_jar.as_path();

    let mut exec_args: Vec<&OsStr> = Vec::new();
    exec_args.push(java_cmd.as_ref());
    exec_args.append(&mut extra_jvm_opts.iter().map(AsRef::as_ref).collect());
    exec_args.append(&mut java_args.iter().map(AsRef::as_ref).collect());
    exec_args.append(&mut vec!["-jar".as_ref(), sbt_jar.as_ref()]);
    exec_args.append(&mut sbt_commands.iter().map(AsRef::as_ref).collect());
    exec_args.append(&mut residual_args.iter().map(AsRef::as_ref).collect());

    exec_runner(&exec_args)
}
<|endoftext|>


<commit_before>use std::path::Path;
use std::fs::File;
use std::error::Error;
use std::env;
use std::io::BufReader;
use std::io::BufRead;
use std::io::Lines;

use CommandType::{ACommand, CCommand, LCommand};

enum CommandType {
	ACommand,
	CCommand,
	LCommand,
}

struct Parser {
	input_lines: Lines<BufReader<File>>,
	current_command: String,
}

impl Parser {
	/// Opens the input file/stream and gets ready to parse it.
	fn new(filename: &str) -> Parser {
		let path = Path::new(filename);
		let file = match File::open(&path) {
			Err(why) => panic!("couldn't open {}: {}", path.display(), Error::description(&why)),
			Ok(file) => file,
		};

		let lines = BufReader::new(file).lines(); // iterator
		Parser {
			input_lines: lines,
			current_command: String::new(),
		}
	}

	/// Encapsulates has_more_command and avance method from the proposed API.
	///
	///		Are there any more commands in the input?
	///			Reads the next command from the input and makes it the current command
	fn parse(&mut self) {
		loop {
			match self.input_lines.next() {
				Some(line) => {
					let content = line.unwrap().trim().to_string();

					// ignore line comments and empty lines
					if content.starts_with("//") || content == "\n" || content == "" {
						continue
					}

					// remove inline commnents
					let mut content_without_inline = String::new();
					if content.contains("//") {
						let v: Vec<&str> = content.split("//").collect();
						content_without_inline = v[0].trim().to_string();
					}

					// decide whether to use content_with_inline or content
					if content_without_inline != String::new() {
						self.current_command = content_without_inline;
					} else {
						self.current_command = content;
					}

					match self.command_type() {
						ACommand => {
							println!("ACommand: {0}", self.current_command);
							println!("\tsymbol: {0}", self.symbol());
						},
						CCommand => {
							println!("CCommand: {0}", self.current_command);
							println!("\tdest: {0}", self.dest());
							println!("\tcomp: {0}", self.comp());
							println!("\tjump: {0}", self.jump());
						},
						LCommand => {
							println!("LCommand: {0}", self.current_command);
							println!("\tsymbol: {0}", self.symbol());
						},
					}
				},
				None => break,
			}
		}
	}

	/// Returns the type of the current command.
	///
	/// * ACommand: For @xxx where xxx is either a symbol or a decimal number
	/// * CCommand: For dest=comp;jump
	/// * LCommand: Pseudo-Command. For (xxx) where xxx is a symbol
	fn command_type(&self) -> CommandType {
		if self.current_command.starts_with("@") {
			return ACommand
		} else if self.current_command.starts_with("(") {
			return LCommand
		}
		CCommand
	}

	/// Returns the symbol or decimal xxx of the current command @xxx or (xxx).
	///
	/// Should be called only when command_type() is ACommand or LCommand.
	fn symbol(&self) -> String {
		let pattern: &[_] = &['(', ')', '@'];
		self.current_command.trim_matches(pattern).to_string()
	}

	/// Returns the dest mnemonic in the current CCommand. dest=comp;jump
	///
	///(8 posibilities.)
	/// Should only be called when cammand_type() is CCommand.
	fn dest(&self) -> String {
		// dest or jump field may be empy
		// if dest is empty, the '=' is omitted.
		// if jump is empty, the ';' is omitted.
		match self.current_command.contains('=') {
			true => {
				let v: Vec<&str> = self.current_command.split('=').collect();
				return v[0].to_string()
			},
			false => "null".to_string(),
		}
	}

	// /// Returns the comp mnemonic in the current CCommand. dest=comp;jump
	// ///
	// /// 28 possibilities.
	// /// Should only be called when cammand_type() is CCommand.
	fn comp(&self) -> String {
		match self.current_command.contains('=') {
			true => {
				match self.current_command.contains(';') {
					true => { // dest=comp;jump
						let v: Vec<&str> = self.current_command.split(|c: char| c == ';' || c == '=' ).collect();
						v[1].to_string()
					},
					false => { // dest=comp
						let v: Vec<&str> = self.current_command.split('=').collect();
						v[1].to_string()
					}
				}
			},
			false => {
				match self.current_command.contains(';') {
					true => { // comp;jump
						let v: Vec<&str> = self.current_command.split(';').collect();
						v[0].to_string()
					},
					false => { // comp
						self.current_command.clone()
					},
				}
			}
		}
	}

	/// Returns the jump mnemonic in the current CCommand. dest=com;jump
	///
	/// 8-possibilities.
	/// Should only be called when cammand_type() is CCommand.
	fn jump(&self) -> String {
		match self.current_command.contains(';') {
			true => {
				let v: Vec<&str> = self.current_command.split(';').collect();
				return v[1].to_string()
			},
			false => "null".to_string(),
		}
	}
}

fn main() {
	let args: Vec<String> = env::args().collect();
	if args.len() != 2 {
		println!("HACK Assembler. Translates assembly (mnemonic) into binary machine code.\n\nUsage:\n\tassembler [PATH_TO_ASM_FILE]");
		return;
	}
	let path_to_asm = &args[1];
	let mut parser = Parser::new(path_to_asm);
	parser.parse();
}
<commit_msg>parser module is feature complete<commit_after>use std::path::Path;
use std::fs::File;
use std::error::Error;
use std::env;
use std::io::BufReader;
use std::io::BufRead;
use std::io::Lines;

use CommandType::{ACommand, CCommand, LCommand};

enum CommandType {
	ACommand,
	CCommand,
	LCommand,
}

struct Parser {
	input_lines: Lines<BufReader<File>>,
	current_command: String,
}

impl Parser {
	/// Opens the input file/stream and gets ready to parse it.
	fn new(filename: &str) -> Parser {
		let path = Path::new(filename);
		let file = match File::open(&path) {
			Err(why) => panic!("couldn't open {}: {}", path.display(), Error::description(&why)),
			Ok(file) => file,
		};

		let lines = BufReader::new(file).lines(); // iterator
		Parser {
			input_lines: lines,
			current_command: String::new(),
		}
	}

	/// Encapsulates has_more_command and avance method from the proposed API.
	///
	///		Are there any more commands in the input?
	///			Reads the next command from the input and makes it the current command
	fn parse(&mut self) {
		loop {
			match self.input_lines.next() {
				Some(line) => {
					let content = line.unwrap().trim().to_string();

					// ignore line comments and empty lines
					if content.starts_with("//") || content == "\n" || content == "" {
						continue
					}

					// remove inline commnents
					let mut content_without_inline = String::new();
					if content.contains("//") {
						let v: Vec<&str> = content.split("//").collect();
						content_without_inline = v[0].trim().to_string();
					}

					// decide whether to use content_with_inline or content
					if content_without_inline != String::new() {
						self.current_command = content_without_inline;
					} else {
						self.current_command = content;
					}

					match self.command_type() {
						ACommand => {
							println!("ACommand: {0}", self.current_command);
							println!("\tsymbol: {0}", self.symbol());
						},
						CCommand => {
							println!("CCommand: {0}", self.current_command);
							println!("\tdest: {0}", self.dest());
							println!("\tcomp: {0}", self.comp());
							println!("\tjump: {0}", self.jump());
						},
						LCommand => {
							println!("LCommand: {0}", self.current_command);
							println!("\tsymbol: {0}", self.symbol());
						},
					}
				},
				None => break,
			}
		}
	}

	/// Returns the type of the current command.
	///
	/// * ACommand: For @xxx where xxx is either a symbol or a decimal number
	/// * CCommand: For dest=comp;jump
	/// * LCommand: Pseudo-Command. For (xxx) where xxx is a symbol
	fn command_type(&self) -> CommandType {
		if self.current_command.starts_with("@") {
			return ACommand
		} else if self.current_command.starts_with("(") {
			return LCommand
		}
		CCommand
	}

	/// Returns the symbol or decimal xxx of the current command @xxx or (xxx).
	///
	/// Should be called only when command_type() is ACommand or LCommand.
	fn symbol(&self) -> String {
		let pattern: &[_] = &['(', ')', '@'];
		self.current_command.trim_matches(pattern).to_string()
	}

	/// Returns the dest mnemonic in the current CCommand. dest=comp;jump
	///
	///(8 posibilities.)
	/// Should only be called when cammand_type() is CCommand.
	fn dest(&self) -> String {
		// dest or jump field may be empy
		// if dest is empty, the '=' is omitted.
		// if jump is empty, the ';' is omitted.
		match self.current_command.contains('=') {
			true => {
				let v: Vec<&str> = self.current_command.split('=').collect();
				return v[0].to_string()
			},
			false => "null".to_string(),
		}
	}

	// /// Returns the comp mnemonic in the current CCommand. dest=comp;jump
	// ///
	// /// 28 possibilities.
	// /// Should only be called when cammand_type() is CCommand.
	fn comp(&self) -> String {
		match self.current_command.contains('=') {
			true => {
				match self.current_command.contains(';') {
					true => { // dest=comp;jump
						let v: Vec<&str> = self.current_command.split(|c: char| c == ';' || c == '=' ).collect();
						v[1].to_string()
					},
					false => { // dest=comp
						let v: Vec<&str> = self.current_command.split('=').collect();
						v[1].to_string()
					}
				}
			},
			false => {
				match self.current_command.contains(';') {
					true => { // comp;jump
						let v: Vec<&str> = self.current_command.split(';').collect();
						v[0].to_string()
					},
					false => { // comp
						self.current_command.clone()
					},
				}
			}
		}
	}

	/// Returns the jump mnemonic in the current CCommand. dest=com;jump
	///
	/// 8-possibilities.
	/// Should only be called when cammand_type() is CCommand.
	fn jump(&self) -> String {
		match self.current_command.contains(';') {
			true => {
				let v: Vec<&str> = self.current_command.split(';').collect();
				return v[1].to_string()
			},
			false => "null".to_string(),
		}
	}
}

/// Translate Hack assembly language mnemonic into binary codes
struct Code {
    Field: typ
}

impl Code {
	fn new() -> Code {
		Code {}
	}

	/// Returns the binary code of the dest mnemonic
	///
	/// returns 3 bits
	fn dest(mnemonic: &str) -> u8 {
		match mnemonic {
			"null" 	=> 0x00,
			"M"		=> 0x01,
			"D"		=> 0x02,
			"MD"	=> 0x03,
			"A"		=> 0x04,
			"AM"	=> 0x05,
			"AD"	=> 0x06,
			"AMD"	=> 0x07,
			_		=> 0x08,
		}
	}

	/// Returns the binary code of the comp mnemonic
	///
	/// returns 7 bits
	fn comp(mnemonic: &str) -> u8 {

	}

	/// Returns the binary code of the jump mnemonic
	///
	/// returns 3 bits
	fn jump(mnemonic: &str) -> u8 {

	}
}

fn main() {
	let args: Vec<String> = env::args().collect();
	if args.len() != 2 {
		println!("HACK Assembler. Translates assembly (mnemonic) into binary machine code.\n\nUsage:\n\tassembler [PATH_TO_ASM_FILE]");
		return;
	}
	let path_to_asm = &args[1];
	let mut parser = Parser::new(path_to_asm);
	parser.parse();
}
<|endoftext|>