<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use rustc::middle::allocator::AllocatorKind;
use rustc_errors;
use syntax::abi::Abi;
use syntax::ast::{Crate, Attribute, LitKind, StrStyle};
use syntax::ast::{Unsafety, Constness, Generics, Mutability, Ty, Mac, Arg};
use syntax::ast::{self, Ident, Item, ItemKind, TyKind, VisibilityKind, Expr};
use syntax::attr;
use syntax::codemap::{dummy_spanned, respan};
use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute};
use syntax::ext::base::ExtCtxt;
use syntax::ext::base::Resolver;
use syntax::ext::build::AstBuilder;
use syntax::ext::expand::ExpansionConfig;
use syntax::ext::hygiene::{Mark, SyntaxContext};
use syntax::fold::{self, Folder};
use syntax::parse::ParseSess;
use syntax::ptr::P;
use syntax::symbol::Symbol;
use syntax::util::small_vector::SmallVector;
use syntax_pos::{Span, DUMMY_SP};

use {AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS};

pub fn modify(sess: &ParseSess,
              resolver: &mut Resolver,
              krate: Crate,
              handler: &rustc_errors::Handler) -> ast::Crate {
    ExpandAllocatorDirectives {
        handler,
        sess,
        resolver,
        found: false,
    }.fold_crate(krate)
}

struct ExpandAllocatorDirectives<'a> {
    found: bool,
    handler: &'a rustc_errors::Handler,
    sess: &'a ParseSess,
    resolver: &'a mut Resolver,
}

impl<'a> Folder for ExpandAllocatorDirectives<'a> {
    fn fold_item(&mut self, item: P<Item>) -> SmallVector<P<Item>> {
        let name = if attr::contains_name(&item.attrs, "global_allocator") {
            "global_allocator"
        } else {
            return fold::noop_fold_item(item, self)
        };
        match item.node {
            ItemKind::Static(..) => {}
            _ => {
                self.handler.span_err(item.span, "allocators must be statics");
                return SmallVector::one(item)
            }
        }

        if self.found {
            self.handler.span_err(item.span, "cannot define more than one \
                                              #[global_allocator]");
            return SmallVector::one(item)
        }
        self.found = true;

        let mark = Mark::fresh(Mark::root());
        mark.set_expn_info(ExpnInfo {
            call_site: DUMMY_SP,
            callee: NameAndSpan {
                format: MacroAttribute(Symbol::intern(name)),
                span: None,
                allow_internal_unstable: true,
                allow_internal_unsafe: false,
            }
        });
        let span = item.span.with_ctxt(SyntaxContext::empty().apply_mark(mark));
        let ecfg = ExpansionConfig::default(name.to_string());
        let mut f = AllocFnFactory {
            span,
            kind: AllocatorKind::Global,
            global: item.ident,
            core: Ident::from_str("core"),
            cx: ExtCtxt::new(self.sess, ecfg, self.resolver),
        };
        let super_path = f.cx.path(f.span, vec![
            Ident::from_str("super"),
            f.global,
        ]);
        let mut items = vec![
            f.cx.item_extern_crate(f.span, f.core),
            f.cx.item_use_simple(
                f.span,
                respan(f.span.shrink_to_lo(), VisibilityKind::Inherited),
                super_path,
            ),
        ];
        for method in ALLOCATOR_METHODS {
            items.push(f.allocator_fn(method));
        }
        let name = f.kind.fn_name("allocator_abi");
        let allocator_abi = Ident::with_empty_ctxt(Symbol::gensym(&name));
        let module = f.cx.item_mod(span, span, allocator_abi, Vec::new(), items);
        let module = f.cx.monotonic_expander().fold_item(module).pop().unwrap();

        let mut ret = SmallVector::new();
        ret.push(item);
        ret.push(module);
        return ret
    }

    fn fold_mac(&mut self, mac: Mac) -> Mac {
        fold::noop_fold_mac(mac, self)
    }
}

struct AllocFnFactory<'a> {
    span: Span,
    kind: AllocatorKind,
    global: Ident,
    core: Ident,
    cx: ExtCtxt<'a>,
}

impl<'a> AllocFnFactory<'a> {
    fn allocator_fn(&self, method: &AllocatorMethod) -> P<Item> {
        let mut abi_args = Vec::new();
        let mut i = 0;
        let ref mut mk = || {
            let name = Ident::from_str(&format!("arg{}", i));
            i += 1;
            name
        };
        let args = method.inputs.iter().map(|ty| {
            self.arg_ty(ty, &mut abi_args, mk)
        }).collect();
        let result = self.call_allocator(method.name, args);
        let (output_ty, output_expr) = self.ret_ty(&method.output, result);
        let kind = ItemKind::Fn(self.cx.fn_decl(abi_args, ast::FunctionRetTy::Ty(output_ty)),
                                Unsafety::Unsafe,
                                dummy_spanned(Constness::NotConst),
                                Abi::Rust,
                                Generics::default(),
                                self.cx.block_expr(output_expr));
        self.cx.item(self.span,
                     Ident::from_str(&self.kind.fn_name(method.name)),
                     self.attrs(),
                     kind)
    }

    fn call_allocator(&self, method: &str, mut args: Vec<P<Expr>>) -> P<Expr> {
        let method = self.cx.path(self.span, vec![
            self.core,
            Ident::from_str("alloc"),
            Ident::from_str("GlobalAlloc"),
            Ident::from_str(method),
        ]);
        let method = self.cx.expr_path(method);
        let allocator = self.cx.path_ident(self.span, self.global);
        let allocator = self.cx.expr_path(allocator);
        let allocator = self.cx.expr_addr_of(self.span, allocator);
        args.insert(0, allocator);

        self.cx.expr_call(self.span, method, args)
    }

    fn attrs(&self) -> Vec<Attribute> {
        let key = Symbol::intern("linkage");
        let value = LitKind::Str(Symbol::intern("external"), StrStyle::Cooked);
        let linkage = self.cx.meta_name_value(self.span, key, value);

        let no_mangle = Symbol::intern("no_mangle");
        let no_mangle = self.cx.meta_word(self.span, no_mangle);

        let special = Symbol::intern("rustc_std_internal_symbol");
        let special = self.cx.meta_word(self.span, special);
        vec![
            self.cx.attribute(self.span, linkage),
            self.cx.attribute(self.span, no_mangle),
            self.cx.attribute(self.span, special),
        ]
    }

    fn arg_ty(&self,
              ty: &AllocatorTy,
              args: &mut Vec<Arg>,
              ident: &mut FnMut() -> Ident) -> P<Expr> {
        match *ty {
            AllocatorTy::Layout => {
                let usize = self.cx.path_ident(self.span, Ident::from_str("usize"));
                let ty_usize = self.cx.ty_path(usize);
                let size = ident();
                let align = ident();
                args.push(self.cx.arg(self.span, size, ty_usize.clone()));
                args.push(self.cx.arg(self.span, align, ty_usize));

                let layout_new = self.cx.path(self.span, vec![
                    self.core,
                    Ident::from_str("alloc"),
                    Ident::from_str("Layout"),
                    Ident::from_str("from_size_align_unchecked"),
                ]);
                let layout_new = self.cx.expr_path(layout_new);
                let size = self.cx.expr_ident(self.span, size);
                let align = self.cx.expr_ident(self.span, align);
                let layout = self.cx.expr_call(self.span,
                                               layout_new,
                                               vec![size, align]);
                layout
            }

            AllocatorTy::Ptr => {
                let ident = ident();
                args.push(self.cx.arg(self.span, ident, self.ptr_u8()));
                let arg = self.cx.expr_ident(self.span, ident);
                self.cx.expr_cast(self.span, arg, self.ptr_opaque())
            }

            AllocatorTy::Usize => {
                let ident = ident();
                args.push(self.cx.arg(self.span, ident, self.usize()));
                self.cx.expr_ident(self.span, ident)
            }

            AllocatorTy::ResultPtr |
            AllocatorTy::Bang |
            AllocatorTy::Unit => {
                panic!("can't convert AllocatorTy to an argument")
            }
        }
    }

    fn ret_ty(&self, ty: &AllocatorTy, expr: P<Expr>) -> (P<Ty>, P<Expr>) {
        match *ty {
            AllocatorTy::ResultPtr => {
                // We're creating:
                //
                //      #expr as *mut u8

                let expr = self.cx.expr_cast(self.span, expr, self.ptr_u8());
                (self.ptr_u8(), expr)
            }

            AllocatorTy::Bang => {
                (self.cx.ty(self.span, TyKind::Never), expr)
            }

            AllocatorTy::Unit => {
                (self.cx.ty(self.span, TyKind::Tup(Vec::new())), expr)
            }

            AllocatorTy::Layout |
            AllocatorTy::Usize |
            AllocatorTy::Ptr => {
                panic!("can't convert AllocatorTy to an output")
            }
        }
    }

    fn usize(&self) -> P<Ty> {
        let usize = self.cx.path_ident(self.span, Ident::from_str("usize"));
        self.cx.ty_path(usize)
    }

    fn ptr_u8(&self) -> P<Ty> {
        let u8 = self.cx.path_ident(self.span, Ident::from_str("u8"));
        let ty_u8 = self.cx.ty_path(u8);
        self.cx.ty_ptr(self.span, ty_u8, Mutability::Mutable)
    }

    fn ptr_opaque(&self) -> P<Ty> {
        let opaque = self.cx.path(self.span, vec![
            self.core,
            Ident::from_str("alloc"),
            Ident::from_str("Opaque"),
        ]);
        let ty_opaque = self.cx.ty_path(opaque);
        self.cx.ty_ptr(self.span, ty_opaque, Mutability::Mutable)
    }
}
<commit_msg>Run rustfmt<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use rustc::middle::allocator::AllocatorKind;
use rustc_errors;
use syntax::abi::Abi;
use syntax::ast::{Attribute, Crate, LitKind, StrStyle};
use syntax::ast::{Arg, Constness, Generics, Mac, Mutability, Ty, Unsafety};
use syntax::ast::{self, Expr, Ident, Item, ItemKind, TyKind, VisibilityKind};
use syntax::attr;
use syntax::codemap::{dummy_spanned, respan};
use syntax::codemap::{ExpnInfo, MacroAttribute, NameAndSpan};
use syntax::ext::base::ExtCtxt;
use syntax::ext::base::Resolver;
use syntax::ext::build::AstBuilder;
use syntax::ext::expand::ExpansionConfig;
use syntax::ext::hygiene::{Mark, SyntaxContext};
use syntax::fold::{self, Folder};
use syntax::parse::ParseSess;
use syntax::ptr::P;
use syntax::symbol::Symbol;
use syntax::util::small_vector::SmallVector;
use syntax_pos::{Span, DUMMY_SP};

use {AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS};

pub fn modify(
    sess: &ParseSess,
    resolver: &mut Resolver,
    krate: Crate,
    handler: &rustc_errors::Handler,
) -> ast::Crate {
    ExpandAllocatorDirectives {
        handler,
        sess,
        resolver,
        found: false,
    }.fold_crate(krate)
}

struct ExpandAllocatorDirectives<'a> {
    found: bool,
    handler: &'a rustc_errors::Handler,
    sess: &'a ParseSess,
    resolver: &'a mut Resolver,
}

impl<'a> Folder for ExpandAllocatorDirectives<'a> {
    fn fold_item(&mut self, item: P<Item>) -> SmallVector<P<Item>> {
        let name = if attr::contains_name(&item.attrs, "global_allocator") {
            "global_allocator"
        } else {
            return fold::noop_fold_item(item, self);
        };
        match item.node {
            ItemKind::Static(..) => {}
            _ => {
                self.handler
                    .span_err(item.span, "allocators must be statics");
                return SmallVector::one(item);
            }
        }

        if self.found {
            self.handler.span_err(
                item.span,
                "cannot define more than one \
                 #[global_allocator]",
            );
            return SmallVector::one(item);
        }
        self.found = true;

        let mark = Mark::fresh(Mark::root());
        mark.set_expn_info(ExpnInfo {
            call_site: DUMMY_SP,
            callee: NameAndSpan {
                format: MacroAttribute(Symbol::intern(name)),
                span: None,
                allow_internal_unstable: true,
                allow_internal_unsafe: false,
            },
        });
        let span = item.span.with_ctxt(SyntaxContext::empty().apply_mark(mark));
        let ecfg = ExpansionConfig::default(name.to_string());
        let mut f = AllocFnFactory {
            span,
            kind: AllocatorKind::Global,
            global: item.ident,
            core: Ident::from_str("core"),
            cx: ExtCtxt::new(self.sess, ecfg, self.resolver),
        };
        let super_path = f.cx.path(f.span, vec![Ident::from_str("super"), f.global]);
        let mut items = vec![
            f.cx.item_extern_crate(f.span, f.core),
            f.cx.item_use_simple(
                f.span,
                respan(f.span.shrink_to_lo(), VisibilityKind::Inherited),
                super_path,
            ),
        ];
        for method in ALLOCATOR_METHODS {
            items.push(f.allocator_fn(method));
        }
        let name = f.kind.fn_name("allocator_abi");
        let allocator_abi = Ident::with_empty_ctxt(Symbol::gensym(&name));
        let module = f.cx.item_mod(span, span, allocator_abi, Vec::new(), items);
        let module = f.cx.monotonic_expander().fold_item(module).pop().unwrap();

        let mut ret = SmallVector::new();
        ret.push(item);
        ret.push(module);
        return ret;
    }

    fn fold_mac(&mut self, mac: Mac) -> Mac {
        fold::noop_fold_mac(mac, self)
    }
}

struct AllocFnFactory<'a> {
    span: Span,
    kind: AllocatorKind,
    global: Ident,
    core: Ident,
    cx: ExtCtxt<'a>,
}

impl<'a> AllocFnFactory<'a> {
    fn allocator_fn(&self, method: &AllocatorMethod) -> P<Item> {
        let mut abi_args = Vec::new();
        let mut i = 0;
        let ref mut mk = || {
            let name = Ident::from_str(&format!("arg{}", i));
            i += 1;
            name
        };
        let args = method
            .inputs
            .iter()
            .map(|ty| self.arg_ty(ty, &mut abi_args, mk))
            .collect();
        let result = self.call_allocator(method.name, args);
        let (output_ty, output_expr) = self.ret_ty(&method.output, result);
        let kind = ItemKind::Fn(
            self.cx.fn_decl(abi_args, ast::FunctionRetTy::Ty(output_ty)),
            Unsafety::Unsafe,
            dummy_spanned(Constness::NotConst),
            Abi::Rust,
            Generics::default(),
            self.cx.block_expr(output_expr),
        );
        self.cx.item(
            self.span,
            Ident::from_str(&self.kind.fn_name(method.name)),
            self.attrs(),
            kind,
        )
    }

    fn call_allocator(&self, method: &str, mut args: Vec<P<Expr>>) -> P<Expr> {
        let method = self.cx.path(
            self.span,
            vec![
                self.core,
                Ident::from_str("alloc"),
                Ident::from_str("GlobalAlloc"),
                Ident::from_str(method),
            ],
        );
        let method = self.cx.expr_path(method);
        let allocator = self.cx.path_ident(self.span, self.global);
        let allocator = self.cx.expr_path(allocator);
        let allocator = self.cx.expr_addr_of(self.span, allocator);
        args.insert(0, allocator);

        self.cx.expr_call(self.span, method, args)
    }

    fn attrs(&self) -> Vec<Attribute> {
        let key = Symbol::intern("linkage");
        let value = LitKind::Str(Symbol::intern("external"), StrStyle::Cooked);
        let linkage = self.cx.meta_name_value(self.span, key, value);

        let no_mangle = Symbol::intern("no_mangle");
        let no_mangle = self.cx.meta_word(self.span, no_mangle);

        let special = Symbol::intern("rustc_std_internal_symbol");
        let special = self.cx.meta_word(self.span, special);
        vec![
            self.cx.attribute(self.span, linkage),
            self.cx.attribute(self.span, no_mangle),
            self.cx.attribute(self.span, special),
        ]
    }

    fn arg_ty(
        &self,
        ty: &AllocatorTy,
        args: &mut Vec<Arg>,
        ident: &mut FnMut() -> Ident,
    ) -> P<Expr> {
        match *ty {
            AllocatorTy::Layout => {
                let usize = self.cx.path_ident(self.span, Ident::from_str("usize"));
                let ty_usize = self.cx.ty_path(usize);
                let size = ident();
                let align = ident();
                args.push(self.cx.arg(self.span, size, ty_usize.clone()));
                args.push(self.cx.arg(self.span, align, ty_usize));

                let layout_new = self.cx.path(
                    self.span,
                    vec![
                        self.core,
                        Ident::from_str("alloc"),
                        Ident::from_str("Layout"),
                        Ident::from_str("from_size_align_unchecked"),
                    ],
                );
                let layout_new = self.cx.expr_path(layout_new);
                let size = self.cx.expr_ident(self.span, size);
                let align = self.cx.expr_ident(self.span, align);
                let layout = self.cx.expr_call(self.span, layout_new, vec![size, align]);
                layout
            }

            AllocatorTy::Ptr => {
                let ident = ident();
                args.push(self.cx.arg(self.span, ident, self.ptr_u8()));
                let arg = self.cx.expr_ident(self.span, ident);
                self.cx.expr_cast(self.span, arg, self.ptr_opaque())
            }

            AllocatorTy::Usize => {
                let ident = ident();
                args.push(self.cx.arg(self.span, ident, self.usize()));
                self.cx.expr_ident(self.span, ident)
            }

            AllocatorTy::ResultPtr | AllocatorTy::Bang | AllocatorTy::Unit => {
                panic!("can't convert AllocatorTy to an argument")
            }
        }
    }

    fn ret_ty(&self, ty: &AllocatorTy, expr: P<Expr>) -> (P<Ty>, P<Expr>) {
        match *ty {
            AllocatorTy::ResultPtr => {
                // We're creating:
                //
                //      #expr as *mut u8

                let expr = self.cx.expr_cast(self.span, expr, self.ptr_u8());
                (self.ptr_u8(), expr)
            }

            AllocatorTy::Bang => (self.cx.ty(self.span, TyKind::Never), expr),

            AllocatorTy::Unit => (self.cx.ty(self.span, TyKind::Tup(Vec::new())), expr),

            AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
                panic!("can't convert AllocatorTy to an output")
            }
        }
    }

    fn usize(&self) -> P<Ty> {
        let usize = self.cx.path_ident(self.span, Ident::from_str("usize"));
        self.cx.ty_path(usize)
    }

    fn ptr_u8(&self) -> P<Ty> {
        let u8 = self.cx.path_ident(self.span, Ident::from_str("u8"));
        let ty_u8 = self.cx.ty_path(u8);
        self.cx.ty_ptr(self.span, ty_u8, Mutability::Mutable)
    }

    fn ptr_opaque(&self) -> P<Ty> {
        let opaque = self.cx.path(
            self.span,
            vec![
                self.core,
                Ident::from_str("alloc"),
                Ident::from_str("Opaque"),
            ],
        );
        let ty_opaque = self.cx.ty_path(opaque);
        self.cx.ty_ptr(self.span, ty_opaque, Mutability::Mutable)
    }
}
<|endoftext|>


<commit_before>use crate::spec::{LinkerFlavor, Target, TargetResult};

pub fn target() -> TargetResult {
    let mut base = super::illumos_base::opts();
    base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string(), "-std=c99".to_string()]);
    base.cpu = "x86-64".to_string();
    base.max_atomic_width = Some(64);

    Ok(Target {
        // LLVM does not currently have a separate illumos target,
        // so we still pass Solaris to it
        llvm_target: "x86_64-pc-solaris".to_string(),
        target_endian: "little".to_string(),
        target_pointer_width: "64".to_string(),
        target_c_int_width: "32".to_string(),
        data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
        arch: "x86_64".to_string(),
        target_os: "illumos".to_string(),
        target_env: String::new(),
        target_vendor: "unknown".to_string(),
        linker_flavor: LinkerFlavor::Gcc,
        options: base,
    })
}
<commit_msg>update data layout for illumos x86<commit_after>use crate::spec::{LinkerFlavor, Target, TargetResult};

pub fn target() -> TargetResult {
    let mut base = super::illumos_base::opts();
    base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string(), "-std=c99".to_string()]);
    base.cpu = "x86-64".to_string();
    base.max_atomic_width = Some(64);

    Ok(Target {
        // LLVM does not currently have a separate illumos target,
        // so we still pass Solaris to it
        llvm_target: "x86_64-pc-solaris".to_string(),
        target_endian: "little".to_string(),
        target_pointer_width: "64".to_string(),
        target_c_int_width: "32".to_string(),
        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
            .to_string(),
        arch: "x86_64".to_string(),
        target_os: "illumos".to_string(),
        target_env: String::new(),
        target_vendor: "unknown".to_string(),
        linker_flavor: LinkerFlavor::Gcc,
        options: base,
    })
}
<|endoftext|>


<commit_before>// Zinc, the bare metal stack for rust.
// Copyright 2014 Ben Gamari <bgamari@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use syntax::ast;
use syntax::ptr::P;
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ToTokens;
use syntax::parse::token;

use super::Builder;
use super::utils;
use super::super::node;

/// A visitor to build accessor functions for each register struct
pub struct BuildAccessors<'a> {
  builder: &'a mut Builder,
  cx: &'a ExtCtxt<'a>,
}

impl<'a> node::RegVisitor for BuildAccessors<'a> {
  fn visit_prim_reg(&mut self, path: &Vec<String>, reg: &node::Reg,
                    fields: &Vec<node::Field>) {
    if fields.iter().any(|f| f.access != node::Access::WriteOnly) {
      let item = build_get_fn(self.cx, path, reg);
      self.builder.push_item(item);
    }
    if fields.iter().any(|f| f.access != node::Access::ReadOnly) {
      let item = build_ignoring_state_setter_fn(self.cx, path, reg);
      self.builder.push_item(item);
    }

    for field in fields.iter() {
      match build_field_accessors(self.cx, path, reg, field) {
        Some(item) => self.builder.push_item(item),
        None       => {}
      }
    }
  }
}

impl<'a> BuildAccessors<'a> {
  pub fn new(builder: &'a mut Builder, cx: &'a ExtCtxt<'a>)
             -> BuildAccessors<'a> {
    BuildAccessors {builder: builder, cx: cx}
  }
}

fn build_field_accessors(cx: &ExtCtxt, path: &Vec<String>,
                         reg: &node::Reg, field: &node::Field)
                         -> Option<P<ast::Item>>
{
  let reg_ty: P<ast::Ty> =
    cx.ty_ident(reg.name.span, utils::path_ident(cx, path));

  let items = match field.access {
    node::Access::ReadWrite => vec!(build_field_set_fn(cx, path, reg, field),
                            build_field_get_fn(cx, path, reg, field)),
    node::Access::ReadOnly  => vec!(build_field_get_fn(cx, path, reg, field)),
    node::Access::WriteOnly => vec!(build_field_set_fn(cx, path, reg, field)),
    node::Access::SetToClear => vec!(build_field_clear_fn(cx, path, reg, field),
                             build_field_get_fn(cx, path, reg, field)),
  };

  let access_tag = match field.access {
    node::Access::ReadWrite => "read/write",
    node::Access::ReadOnly  => "read-only",
    node::Access::WriteOnly => "write-only",
    node::Access::SetToClear => "set-to-clear",
  };

  let field_doc = match field.docstring {
    Some(ref d) => {
      let s = token::get_ident(d.node);
      s.to_string()
    },
    None => "no documentation".to_string()
  };
  let docstring = format!("*[{}]* `{}` field: {}",
                          access_tag,
                          field.name.node,
                          field_doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  quote_item!(cx,
    $doc_attr
    impl $reg_ty {
      $items
    }
  )
}

fn build_get_fn(cx: &ExtCtxt, path: &Vec<String>, reg: &node::Reg)
                -> P<ast::Item>
{
  let reg_ty: P<ast::Ty> =
    cx.ty_ident(reg.name.span, utils::path_ident(cx, path));
  let getter_ty = utils::getter_name(cx, path);

  let docstring = format!("Fetch the value of the `{}` register",
                          reg.name.node);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  let item = quote_item!(cx,
    impl $reg_ty {
      $doc_attr
      #[allow(dead_code)]
      pub fn get(&self) -> $getter_ty {
        $getter_ty::new(self)
      }
    }
    );
  item.unwrap()
}

fn build_ignoring_state_setter_fn(cx: &ExtCtxt, path: &Vec<String>, reg: &node::Reg)
                -> P<ast::Item>
{
  let reg_ty: P<ast::Ty> =
    cx.ty_ident(reg.name.span, utils::path_ident(cx, path));
  let setter_ty = utils::setter_name(cx, path);

  let docstring = format!("Create new updater that ignores current value of the `{}` register",
                          reg.name.node);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  let item = quote_item!(cx,
    impl $reg_ty {
      $doc_attr
      #[allow(dead_code)]
      pub fn ignoring_state(&self) -> $setter_ty {
        $setter_ty::new_ignoring_state(self)
      }
    }
    );
  item.unwrap()
}

fn build_field_set_fn(cx: &ExtCtxt, path: &Vec<String>,
                      reg: &node::Reg, field: &node::Field)
                      -> P<ast::ImplItem>
{
  let reg_ty = cx.ty_ident(reg.name.span, utils::path_ident(cx, path));
  let fn_name =
    cx.ident_of((String::from("set_")+field.name.node.as_str()).as_str());
  let field_ty: P<ast::Ty> =
    cx.ty_path(utils::field_type_path(cx, path, reg, field));
  let setter_ty = utils::setter_name(cx, path);
  if field.count.node == 1 {
    utils::unwrap_impl_item(quote_item!(cx,
      impl $reg_ty {
        #[allow(dead_code, missing_docs)]
        pub fn $fn_name<'a>(&'a self, new_value: $field_ty) -> $setter_ty<'a> {
          let mut setter: $setter_ty = $setter_ty::new(self);
          setter.$fn_name(new_value);
          setter
        }
      }
    ).unwrap())
  } else {
    utils::unwrap_impl_item(quote_item!(cx,
      impl $reg_ty {
        #[allow(dead_code, missing_docs)]
        pub fn $fn_name<'a>(&'a self, idx: usize, new_value: $field_ty) -> $setter_ty<'a> {
          let mut setter: $setter_ty = $setter_ty::new(self);
          setter.$fn_name(idx, new_value);
          setter
        }
      }
    ).unwrap())
  }
}

fn build_field_get_fn(cx: &ExtCtxt, path: &Vec<String>,
                      reg: &node::Reg, field: &node::Field)
                      -> P<ast::ImplItem>
{
  let reg_ty = cx.ty_ident(reg.name.span, utils::path_ident(cx, path));
  utils::unwrap_impl_item({
      let fn_name = cx.ident_of(field.name.node.as_str());
      let field_ty: P<ast::Ty> =
        cx.ty_path(utils::field_type_path(cx, path, reg, field));
      let getter_ty = utils::getter_name(cx, path);
      if field.count.node == 1 {
        quote_item!(cx,
          impl $reg_ty {
            #[allow(dead_code, missing_docs)]
            pub fn $fn_name(&self) -> $field_ty {
              $getter_ty::new(self).$fn_name()
            }
          }
        ).unwrap()
      } else {
        quote_item!(cx,
          impl $reg_ty {
            #[allow(dead_code, missing_docs)]
            pub fn $fn_name(&self, idx: usize) -> $field_ty {
              $getter_ty::new(self).$fn_name(idx)
            }
          }
        ).unwrap()
      }
  })
}

fn build_field_clear_fn(cx: &ExtCtxt, path: &Vec<String>,
                        reg: &node::Reg, field: &node::Field)
                        -> P<ast::ImplItem>
{
  let reg_ty = cx.ty_ident(reg.name.span, utils::path_ident(cx, path));
  let fn_name =
    cx.ident_of((String::from("clear_")+field.name.node.as_str()).as_str());
  let setter_ty = utils::setter_name(cx, path);
  utils::unwrap_impl_item(if field.count.node == 1 {
    quote_item!(cx,
      impl $reg_ty {
        #[allow(dead_code, missing_docs)]
        pub fn $fn_name<'a>(&'a self) -> $setter_ty<'a> {
          let mut setter: $setter_ty = $setter_ty::new(self);
          setter.$fn_name();
          setter
        }
      }
    ).unwrap()
  } else {
    quote_item!(cx,
      impl $reg_ty {
        #[allow(dead_code, missing_docs)]
        pub fn $fn_name<'a>(&'a self, idx: usize) -> $setter_ty<'a> {
          let mut setter: $setter_ty = $setter_ty::new(self);
          setter.$fn_name(idx);
          setter
        }
      }
    ).unwrap()
  })
}
<commit_msg>Updated ioreg/builder/accessors.rs<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Ben Gamari <bgamari@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use syntax::ast;
use syntax::ptr::P;
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ToTokens;
use syntax::parse::token;

use super::Builder;
use super::utils;
use super::super::node;

/// A visitor to build accessor functions for each register struct
pub struct BuildAccessors<'a> {
  builder: &'a mut Builder,
  cx: &'a ExtCtxt<'a>,
}

impl<'a> node::RegVisitor for BuildAccessors<'a> {
  fn visit_prim_reg(&mut self, path: &Vec<String>, reg: &node::Reg,
                    fields: &Vec<node::Field>) {
    if fields.iter().any(|f| f.access != node::Access::WriteOnly) {
      let item = build_get_fn(self.cx, path, reg);
      self.builder.push_item(item);
    }
    if fields.iter().any(|f| f.access != node::Access::ReadOnly) {
      let item = build_ignoring_state_setter_fn(self.cx, path, reg);
      self.builder.push_item(item);
    }

    for field in fields.iter() {
      match build_field_accessors(self.cx, path, reg, field) {
        Some(item) => self.builder.push_item(item),
        None       => {}
      }
    }
  }
}

impl<'a> BuildAccessors<'a> {
  pub fn new(builder: &'a mut Builder, cx: &'a ExtCtxt<'a>)
             -> BuildAccessors<'a> {
    BuildAccessors {builder: builder, cx: cx}
  }
}

fn build_field_accessors(cx: &ExtCtxt, path: &Vec<String>,
                         reg: &node::Reg, field: &node::Field)
                         -> Option<P<ast::Item>>
{
  let reg_ty: P<ast::Ty> =
    cx.ty_ident(reg.name.span, utils::path_ident(cx, path));

  let items = match field.access {
    node::Access::ReadWrite => vec!(build_field_set_fn(cx, path, reg, field),
                            build_field_get_fn(cx, path, reg, field)),
    node::Access::ReadOnly  => vec!(build_field_get_fn(cx, path, reg, field)),
    node::Access::WriteOnly => vec!(build_field_set_fn(cx, path, reg, field)),
    node::Access::SetToClear => vec!(build_field_clear_fn(cx, path, reg, field),
                             build_field_get_fn(cx, path, reg, field)),
  };

  let access_tag = match field.access {
    node::Access::ReadWrite => "read/write",
    node::Access::ReadOnly  => "read-only",
    node::Access::WriteOnly => "write-only",
    node::Access::SetToClear => "set-to-clear",
  };

  let field_doc = match field.docstring {
    Some(ref d) => {
      let s = d.node.name.as_str();
      s.to_string()
    },
    None => "no documentation".to_string()
  };
  let docstring = format!("*[{}]* `{}` field: {}",
                          access_tag,
                          field.name.node,
                          field_doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  quote_item!(cx,
    $doc_attr
    impl $reg_ty {
      $items
    }
  )
}

fn build_get_fn(cx: &ExtCtxt, path: &Vec<String>, reg: &node::Reg)
                -> P<ast::Item>
{
  let reg_ty: P<ast::Ty> =
    cx.ty_ident(reg.name.span, utils::path_ident(cx, path));
  let getter_ty = utils::getter_name(cx, path);

  let docstring = format!("Fetch the value of the `{}` register",
                          reg.name.node);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  let item = quote_item!(cx,
    impl $reg_ty {
      $doc_attr
      #[allow(dead_code)]
      pub fn get(&self) -> $getter_ty {
        $getter_ty::new(self)
      }
    }
    );
  item.unwrap()
}

fn build_ignoring_state_setter_fn(cx: &ExtCtxt, path: &Vec<String>, reg: &node::Reg)
                -> P<ast::Item>
{
  let reg_ty: P<ast::Ty> =
    cx.ty_ident(reg.name.span, utils::path_ident(cx, path));
  let setter_ty = utils::setter_name(cx, path);

  let docstring = format!("Create new updater that ignores current value of the `{}` register",
                          reg.name.node);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  let item = quote_item!(cx,
    impl $reg_ty {
      $doc_attr
      #[allow(dead_code)]
      pub fn ignoring_state(&self) -> $setter_ty {
        $setter_ty::new_ignoring_state(self)
      }
    }
    );
  item.unwrap()
}

fn build_field_set_fn(cx: &ExtCtxt, path: &Vec<String>,
                      reg: &node::Reg, field: &node::Field)
                      -> P<ast::ImplItem>
{
  let reg_ty = cx.ty_ident(reg.name.span, utils::path_ident(cx, path));
  let fn_name =
    cx.ident_of((String::from("set_")+field.name.node.as_str()).as_str());
  let field_ty: P<ast::Ty> =
    cx.ty_path(utils::field_type_path(cx, path, reg, field));
  let setter_ty = utils::setter_name(cx, path);
  if field.count.node == 1 {
    utils::unwrap_impl_item(quote_item!(cx,
      impl $reg_ty {
        #[allow(dead_code, missing_docs)]
        pub fn $fn_name<'a>(&'a self, new_value: $field_ty) -> $setter_ty<'a> {
          let mut setter: $setter_ty = $setter_ty::new(self);
          setter.$fn_name(new_value);
          setter
        }
      }
    ).unwrap())
  } else {
    utils::unwrap_impl_item(quote_item!(cx,
      impl $reg_ty {
        #[allow(dead_code, missing_docs)]
        pub fn $fn_name<'a>(&'a self, idx: usize, new_value: $field_ty) -> $setter_ty<'a> {
          let mut setter: $setter_ty = $setter_ty::new(self);
          setter.$fn_name(idx, new_value);
          setter
        }
      }
    ).unwrap())
  }
}

fn build_field_get_fn(cx: &ExtCtxt, path: &Vec<String>,
                      reg: &node::Reg, field: &node::Field)
                      -> P<ast::ImplItem>
{
  let reg_ty = cx.ty_ident(reg.name.span, utils::path_ident(cx, path));
  utils::unwrap_impl_item({
      let fn_name = cx.ident_of(field.name.node.as_str());
      let field_ty: P<ast::Ty> =
        cx.ty_path(utils::field_type_path(cx, path, reg, field));
      let getter_ty = utils::getter_name(cx, path);
      if field.count.node == 1 {
        quote_item!(cx,
          impl $reg_ty {
            #[allow(dead_code, missing_docs)]
            pub fn $fn_name(&self) -> $field_ty {
              $getter_ty::new(self).$fn_name()
            }
          }
        ).unwrap()
      } else {
        quote_item!(cx,
          impl $reg_ty {
            #[allow(dead_code, missing_docs)]
            pub fn $fn_name(&self, idx: usize) -> $field_ty {
              $getter_ty::new(self).$fn_name(idx)
            }
          }
        ).unwrap()
      }
  })
}

fn build_field_clear_fn(cx: &ExtCtxt, path: &Vec<String>,
                        reg: &node::Reg, field: &node::Field)
                        -> P<ast::ImplItem>
{
  let reg_ty = cx.ty_ident(reg.name.span, utils::path_ident(cx, path));
  let fn_name =
    cx.ident_of((String::from("clear_")+field.name.node.as_str()).as_str());
  let setter_ty = utils::setter_name(cx, path);
  utils::unwrap_impl_item(if field.count.node == 1 {
    quote_item!(cx,
      impl $reg_ty {
        #[allow(dead_code, missing_docs)]
        pub fn $fn_name<'a>(&'a self) -> $setter_ty<'a> {
          let mut setter: $setter_ty = $setter_ty::new(self);
          setter.$fn_name();
          setter
        }
      }
    ).unwrap()
  } else {
    quote_item!(cx,
      impl $reg_ty {
        #[allow(dead_code, missing_docs)]
        pub fn $fn_name<'a>(&'a self, idx: usize) -> $setter_ty<'a> {
          let mut setter: $setter_ty = $setter_ty::new(self);
          setter.$fn_name(idx);
          setter
        }
      }
    ).unwrap()
  })
}
<|endoftext|>


<commit_before>use super::*;

//! Built-in functions and macros.

pub fn add_builtins(env: @mut Rusp) {
    macro_rules! rust(
        (NoEnv: $ty:ident, $name:ident) => (
            @FnEnv(ExternFn(|params, env| $name(params, env), $ty), None)
        );
        ($ty:ident, $name:ident) => (
            @FnEnv(ExternFn(|params, env| $name(params, env), $ty), Some(env))
        );
    );

    for [
        // special forms

        // most of these need to capture the environment where they
        // are called (e.g. to create a lexically-scoped closure, or
        // to unquote local variables), *not* the global environment,
        // hence NoEnv.
        (~"fn",                     rust!(NoEnv: Macro, builtin_fn)),
        (~"macro",                  rust!(NoEnv: Macro, builtin_macro)),

        (~"if",                     rust!(NoEnv: Macro, builtin_if)),
        (~"do",                     rust!(NoEnv: Macro, builtin_do)),
        (~"quote",                  @FnEnv(RuspFn(~[~"a"], @Symbol(~"a"), Macro), None)),
        (~"quasiquote",             rust!(NoEnv: Macro, builtin_quasiquote)),

        // this changes the global scope always
        (~"def",                    rust!(Macro, builtin_def)),

        // builtin functions
        (~"eval",                   rust!(Fn, builtin_eval)),
        (~"parse",                  rust!(Fn, builtin_parse)),
        (~"list",                   rust!(Fn, builtin_list)),
        (~"print",                  rust!(Fn, builtin_print)),

        (~"bool?",                  rust!(Fn, builtin_is_bool)),
        (~"int?",                   rust!(Fn, builtin_is_int)),
        (~"float?",                 rust!(Fn, builtin_is_float)),
        (~"string?",                rust!(Fn, builtin_is_string)),
        (~"list?",                  rust!(Fn, builtin_is_list)),
        (~"symbol?",                rust!(Fn, builtin_is_symbol)),
        (~"fn?",                    rust!(Fn, builtin_is_fn)),
        (~"macro?",                 rust!(Fn, builtin_is_macro)),
        (~"extern-fn?",             rust!(Fn, builtin_is_extern_fn)),
        (~"extern-macro?",          rust!(Fn, builtin_is_extern_macro)),

        (~"+",                      rust!(Fn, builtin_add)),
        (~"-",                      rust!(Fn, builtin_sub)),
        (~"*",                      rust!(Fn, builtin_mul)),
        (~"/",                      rust!(Fn, builtin_div)),
        (~"%",                      rust!(Fn, builtin_rem)),
        (~"=",                      rust!(Fn, builtin_eq)),
    ].each |&(name, func)| {
        env.define(name, func);
    }
}

/// Define a new symbol in the environment
fn builtin_def(params: &[@Value], env: @mut Rusp) -> EvalResult {
    match params {
        [@Symbol(ref id), .. rest] => {
            match rest {
                [] => {
                    env.define(id.clone(), @List(~[]));
                    Ok(@List(~[]))
                }
                [ref v] => {
                    env.eval(*v).chain(|res| env.define(id.clone(), res))
                }
                _ => Err(~"`def` expects 1 or 2 arguments")
            }
        }
        _ => Err(~"first argument to `def` should be an identifier")
    }
}

/// Evaluate an S-expression in the current environment
fn builtin_eval(params: &[@Value], env: @mut Rusp) -> EvalResult {
    match params {
        [ref p] => env.eval(*p),
        _ => Err(~"`eval` expects 1 argument")
    }
}

/// Parse S-expressions from a string
fn builtin_parse(params: &[@Value], _: @mut Rusp) -> EvalResult {
    match params {
        [@Str(ref s)] => match parse(*s) {
            Ok(vals) => Ok(@List(vals)),
            Err(parser::ParseFailure { description, _  }) => Err(description)
        },
        _ => Err(~"`parse` expects 1 argument")
    }
}

/// Conditional expression
fn builtin_if(params: &[@Value], env: @mut Rusp) -> EvalResult {
    match params {
        [ref pred, ref conseq, ref alt] => {
            do env.eval(*pred).chain |val| {
                match *val {
                    Bool(true) => env.eval(*conseq),
                    Bool(false) => env.eval(*alt),
                    _ => Err(fmt!("Expected boolean expression, found: %s", val.to_str())),
                }
            }
        }
        _ => Err(~"`if` expects 3 arguments"), // TODO
    }
}

/// Evaluate each expression in turn, returning the value of the last expression
fn builtin_do(params: &[@Value], env: @mut Rusp) -> EvalResult {
    match params {
        [..fst, ref lst] => {
            for fst.each |val| {
                match env.eval(*val) {
                    Ok(@List(ref l)) if l.is_empty() => (),
                    Ok(v) => return Err(fmt!("Expected unit expression, found: %s", v.to_str())),
                    Err(e) => return Err(e),
                }
            }
            env.eval(*lst)
        }
        _ => Err(~""), // TODO
    }
}

/// Return the arguments as a list
fn builtin_list(params: &[@Value], _: @mut Rusp) -> EvalResult {
    Ok(@List(params.to_owned()))
}

/// Print to stdout
fn builtin_print(params: &[@Value], _: @mut Rusp) -> EvalResult {
    let elements = params.map(|v| v.to_str());
    println(str::connect(elements, " "));
    Ok(@List(~[]))
}

/// Shared functionality between creating functions and macros
fn fn_macro(params: &[@Value], callee_env: @mut Rusp, ty: FnMode) -> EvalResult {
    match params {
        [@List(ref symbols), ref val] => {
            let mut idents = ~[];
            for symbols.each |symbol| {
                match **symbol {
                    Symbol(ref ident) => idents.push(ident.clone()),
                    _ => return Err(fmt!("Expected symbol identifier, found: \
                                          %s", symbol.to_str())),
                }
            };
            Ok(@FnEnv(RuspFn(idents, *val, ty), Some(callee_env)))
        }
        _ => Err(fmt!("`%s` expects 2 arguments", ty.to_str())), // TODO
    }
}

/// Evaluate to an anonymous function
fn builtin_fn(params: &[@Value], callee_env: @mut Rusp) -> EvalResult {
    fn_macro(params, callee_env, Fn)
}

/// Evaluate to an anonymous macro
fn builtin_macro(params: &[@Value], callee_env: @mut Rusp) -> EvalResult {
    fn_macro(params, callee_env, Macro)
}

/// Quasiquotation, to allow substitutions inside a quoted structure, e.g.
/// `(def a 1) (quasiquote (a (unquote (list a a a))))`
/// expands to (a (1 1 1))
fn builtin_quasiquote(params: &[@Value], env: @mut Rusp) -> EvalResult {
    return match params {
        [ref p] => unquote(*p, env),
        _ => Err(~"`quasiquote` expects 1 argument")
    };

    // traverse the tree, checking for `(unquote ..)`
    fn unquote(v: @Value, env: @mut Rusp) -> EvalResult {
        match *v {
            List([@Symbol(~"unquote"), .. rest]) => {
                match rest {
                    [ref p] => env.eval(*p),
                    _ => Err(~"`unquote` expects 1 argument")
                }
            }
            List(ref vals) => {
                let mut new = ~[];

                for vals.each |v| {
                    match unquote(*v, env) {
                        Ok(v) => new.push(v),
                        Err(e) => return Err(e)
                    }
                }
                Ok(@List(new))
            }
            FnEnv(RuspFn(ref args, ref body, ref fn_type), ref subenv) => {
                match unquote(*body, subenv.get_or_default(env)) {
                    Ok(b) => Ok(@FnEnv(RuspFn(args.clone(), b, fn_type.clone()),
                                       subenv.clone())),
                    Err(e) => Err(e)
                }
            }
            _ => Ok(v)
        }
    }
}

macro_rules! type_pred(
    ($name:ident, $pattern:pat) => (
        fn $name(params: &[@Value], _: @mut Rusp) -> EvalResult {
            match do params.map |&val| {
                match *val {
                    $pattern => @Bool(true),
                    _ => @Bool(false),
                }
            } {
                [ref result] => Ok(*result),
                results => Ok(@List(results)),
            }
        }
    )
)

type_pred!(builtin_is_bool, Bool(*))
type_pred!(builtin_is_int, Int(*))
type_pred!(builtin_is_float, Float(*))
type_pred!(builtin_is_string, Str(*))
type_pred!(builtin_is_list, List(*))
type_pred!(builtin_is_symbol, Symbol(*))
type_pred!(builtin_is_fn, FnEnv(RuspFn(_, _, Fn), _))
type_pred!(builtin_is_macro, FnEnv(RuspFn(_, _, Macro), _))
type_pred!(builtin_is_extern_fn, FnEnv(ExternFn(_, Fn), _))
type_pred!(builtin_is_extern_macro, FnEnv(ExternFn(_, Macro), _))

macro_rules! arithmetic_op(
    ($name:ident, $symbol:expr, $method:ident) => (
        fn $name(params: &[@Value], _: @mut Rusp) -> EvalResult {
            if params.len() < 2 {
                return Err(fmt!("`%s` expects at least 2 arguments", $symbol));
            }
            let mut result = *params.head();
            for params.tail().each |&val| {
                match (result, val) {
                    // Basic operations between values of the same type
                    (@Int(prev), @Int(i)) => result = @Int(prev.$method(&i)),
                    (@Float(prev), @Float(f)) => result = @Float(prev.$method(&f)),
                    // Handle numeric promotions
                    (@Int(prev), @Float(f)) => result = @Float((prev as float).$method(&f)),
                    (@Float(prev), @Int(i)) => result = @Float(prev.$method(&(i as float))),
                    // Type errors
                    (@Float(_), ref v) => return Err(fmt!("Expected float but found: %s", v.to_str())),
                    (@Int(_), ref v) => return Err(fmt!("Expected integer but found: %s", v.to_str())),
                    (ref prev, _) => return Err(fmt!("Expected number but found: %s", prev.to_str())),
                }
            }
            Ok(result)
        }
    )
)

arithmetic_op!(builtin_add, "+", add)
arithmetic_op!(builtin_sub, "-", sub)
arithmetic_op!(builtin_mul, "*", mul)
arithmetic_op!(builtin_div, "/", div)
arithmetic_op!(builtin_rem, "%", rem)

fn builtin_eq(params: &[@Value], _: @mut Rusp) -> EvalResult {
    match params {
        [head, .. rest] => {
            for rest.each |&val| {
                // TODO handle numeric promotions? i.e. 1.0 == 1?
                if !(val == head) { return Ok(@Bool(false)); }
            }
            Ok(@Bool(true))
        }
        [] => Err(fmt!("`=` expects at least 1 argument"))
    }
}

#[cfg(test)]
mod tests {
    use super::super::*;
    use super::super::parser::Parser;

    macro_rules! test_ok(
        ($src:expr, $expected:expr) => (
            assert_eq!(Rusp::new().eval(
                @Parser::new($src).parse().get()
            ).get().to_str(), $expected);
        )
    )

    macro_rules! test_err(
        ($src:expr) => (
            assert!(Rusp::new().eval(
                @Parser::new($src).parse().get()
            ).is_err());
        )
    )

    #[test]
    fn test_type_preds() {
        test_ok!("(bool? true)", ~"true");
        test_ok!("(bool? 1)", ~"false");
        test_ok!("(int? 1)", ~"true");
        test_ok!("(int? 1.0)", ~"false");
        test_ok!("(float? 1.0)", ~"true");
        test_ok!("(float? \"hi\")", ~"false");
        test_ok!("(string? \"hi\")", ~"true");
        test_ok!("(string? (list 1 2))", ~"false");
        test_ok!("(list? (list 1 2))", ~"true");
        test_ok!("(list? (list))", ~"true");
        test_ok!("(list? 1.0)", ~"false");
        test_ok!("(symbol? (quote x))", ~"true");
        test_ok!("(symbol? (fn () 1))", ~"false");
        test_ok!("(fn? (fn () 1))", ~"true");
        test_ok!("(fn? (macro () 1))", ~"false");
        test_ok!("(macro? (macro () 1))", ~"true");
        test_ok!("(macro? false)", ~"false");
    }

    #[test]
    fn test_arithmetic() {
        test_ok!("(+ 1 2)", ~"3");
        test_ok!("(+ 1 2 4)", ~"7");
        test_ok!("(+ 1.5 3 4)", ~"8.5");
        test_ok!("(+ 3 1.67)", ~"4.67");
        test_ok!("(- 3 0.5)", ~"2.5");
        test_ok!("(- 3 1.5 (+ 2 4))", ~"-4.5");

        test_err!("(+)");
        test_err!("(-)");
        test_err!("(*)");
        test_err!("(/)");
        test_err!("(%)");

        test_err!("(+ 1)");
        test_err!("(- 1)");
        test_err!("(* 1)");
        test_err!("(/ 1)");
        test_err!("(% 1)");

        test_err!("(+ 1 \"hi\")");
        test_err!("(- 1 \"hi\")");
        test_err!("(* 1 \"hi\")");
        test_err!("(/ 1 \"hi\")");
        test_err!("(% 1 \"hi\")");

        test_err!("(+ \"hi\" 1)");
        test_err!("(- \"hi\" 1)");
        test_err!("(* \"hi\" 1)");
        test_err!("(/ \"hi\" 1)");
        test_err!("(% \"hi\" 1)");
    }
}<commit_msg>Move misplaced comment<commit_after>//! Built-in functions and macros.

use super::*;

pub fn add_builtins(env: @mut Rusp) {
    macro_rules! rust(
        (NoEnv: $ty:ident, $name:ident) => (
            @FnEnv(ExternFn(|params, env| $name(params, env), $ty), None)
        );
        ($ty:ident, $name:ident) => (
            @FnEnv(ExternFn(|params, env| $name(params, env), $ty), Some(env))
        );
    );

    for [
        // special forms

        // most of these need to capture the environment where they
        // are called (e.g. to create a lexically-scoped closure, or
        // to unquote local variables), *not* the global environment,
        // hence NoEnv.
        (~"fn",                     rust!(NoEnv: Macro, builtin_fn)),
        (~"macro",                  rust!(NoEnv: Macro, builtin_macro)),

        (~"if",                     rust!(NoEnv: Macro, builtin_if)),
        (~"do",                     rust!(NoEnv: Macro, builtin_do)),
        (~"quote",                  @FnEnv(RuspFn(~[~"a"], @Symbol(~"a"), Macro), None)),
        (~"quasiquote",             rust!(NoEnv: Macro, builtin_quasiquote)),

        // this changes the global scope always
        (~"def",                    rust!(Macro, builtin_def)),

        // builtin functions
        (~"eval",                   rust!(Fn, builtin_eval)),
        (~"parse",                  rust!(Fn, builtin_parse)),
        (~"list",                   rust!(Fn, builtin_list)),
        (~"print",                  rust!(Fn, builtin_print)),

        (~"bool?",                  rust!(Fn, builtin_is_bool)),
        (~"int?",                   rust!(Fn, builtin_is_int)),
        (~"float?",                 rust!(Fn, builtin_is_float)),
        (~"string?",                rust!(Fn, builtin_is_string)),
        (~"list?",                  rust!(Fn, builtin_is_list)),
        (~"symbol?",                rust!(Fn, builtin_is_symbol)),
        (~"fn?",                    rust!(Fn, builtin_is_fn)),
        (~"macro?",                 rust!(Fn, builtin_is_macro)),
        (~"extern-fn?",             rust!(Fn, builtin_is_extern_fn)),
        (~"extern-macro?",          rust!(Fn, builtin_is_extern_macro)),

        (~"+",                      rust!(Fn, builtin_add)),
        (~"-",                      rust!(Fn, builtin_sub)),
        (~"*",                      rust!(Fn, builtin_mul)),
        (~"/",                      rust!(Fn, builtin_div)),
        (~"%",                      rust!(Fn, builtin_rem)),
        (~"=",                      rust!(Fn, builtin_eq)),
    ].each |&(name, func)| {
        env.define(name, func);
    }
}

/// Define a new symbol in the environment
fn builtin_def(params: &[@Value], env: @mut Rusp) -> EvalResult {
    match params {
        [@Symbol(ref id), .. rest] => {
            match rest {
                [] => {
                    env.define(id.clone(), @List(~[]));
                    Ok(@List(~[]))
                }
                [ref v] => {
                    env.eval(*v).chain(|res| env.define(id.clone(), res))
                }
                _ => Err(~"`def` expects 1 or 2 arguments")
            }
        }
        _ => Err(~"first argument to `def` should be an identifier")
    }
}

/// Evaluate an S-expression in the current environment
fn builtin_eval(params: &[@Value], env: @mut Rusp) -> EvalResult {
    match params {
        [ref p] => env.eval(*p),
        _ => Err(~"`eval` expects 1 argument")
    }
}

/// Parse S-expressions from a string
fn builtin_parse(params: &[@Value], _: @mut Rusp) -> EvalResult {
    match params {
        [@Str(ref s)] => match parse(*s) {
            Ok(vals) => Ok(@List(vals)),
            Err(parser::ParseFailure { description, _  }) => Err(description)
        },
        _ => Err(~"`parse` expects 1 argument")
    }
}

/// Conditional expression
fn builtin_if(params: &[@Value], env: @mut Rusp) -> EvalResult {
    match params {
        [ref pred, ref conseq, ref alt] => {
            do env.eval(*pred).chain |val| {
                match *val {
                    Bool(true) => env.eval(*conseq),
                    Bool(false) => env.eval(*alt),
                    _ => Err(fmt!("Expected boolean expression, found: %s", val.to_str())),
                }
            }
        }
        _ => Err(~"`if` expects 3 arguments"), // TODO
    }
}

/// Evaluate each expression in turn, returning the value of the last expression
fn builtin_do(params: &[@Value], env: @mut Rusp) -> EvalResult {
    match params {
        [..fst, ref lst] => {
            for fst.each |val| {
                match env.eval(*val) {
                    Ok(@List(ref l)) if l.is_empty() => (),
                    Ok(v) => return Err(fmt!("Expected unit expression, found: %s", v.to_str())),
                    Err(e) => return Err(e),
                }
            }
            env.eval(*lst)
        }
        _ => Err(~""), // TODO
    }
}

/// Return the arguments as a list
fn builtin_list(params: &[@Value], _: @mut Rusp) -> EvalResult {
    Ok(@List(params.to_owned()))
}

/// Print to stdout
fn builtin_print(params: &[@Value], _: @mut Rusp) -> EvalResult {
    let elements = params.map(|v| v.to_str());
    println(str::connect(elements, " "));
    Ok(@List(~[]))
}

/// Shared functionality between creating functions and macros
fn fn_macro(params: &[@Value], callee_env: @mut Rusp, ty: FnMode) -> EvalResult {
    match params {
        [@List(ref symbols), ref val] => {
            let mut idents = ~[];
            for symbols.each |symbol| {
                match **symbol {
                    Symbol(ref ident) => idents.push(ident.clone()),
                    _ => return Err(fmt!("Expected symbol identifier, found: \
                                          %s", symbol.to_str())),
                }
            };
            Ok(@FnEnv(RuspFn(idents, *val, ty), Some(callee_env)))
        }
        _ => Err(fmt!("`%s` expects 2 arguments", ty.to_str())), // TODO
    }
}

/// Evaluate to an anonymous function
fn builtin_fn(params: &[@Value], callee_env: @mut Rusp) -> EvalResult {
    fn_macro(params, callee_env, Fn)
}

/// Evaluate to an anonymous macro
fn builtin_macro(params: &[@Value], callee_env: @mut Rusp) -> EvalResult {
    fn_macro(params, callee_env, Macro)
}

/// Quasiquotation, to allow substitutions inside a quoted structure, e.g.
/// `(def a 1) (quasiquote (a (unquote (list a a a))))`
/// expands to (a (1 1 1))
fn builtin_quasiquote(params: &[@Value], env: @mut Rusp) -> EvalResult {
    return match params {
        [ref p] => unquote(*p, env),
        _ => Err(~"`quasiquote` expects 1 argument")
    };

    // traverse the tree, checking for `(unquote ..)`
    fn unquote(v: @Value, env: @mut Rusp) -> EvalResult {
        match *v {
            List([@Symbol(~"unquote"), .. rest]) => {
                match rest {
                    [ref p] => env.eval(*p),
                    _ => Err(~"`unquote` expects 1 argument")
                }
            }
            List(ref vals) => {
                let mut new = ~[];

                for vals.each |v| {
                    match unquote(*v, env) {
                        Ok(v) => new.push(v),
                        Err(e) => return Err(e)
                    }
                }
                Ok(@List(new))
            }
            FnEnv(RuspFn(ref args, ref body, ref fn_type), ref subenv) => {
                match unquote(*body, subenv.get_or_default(env)) {
                    Ok(b) => Ok(@FnEnv(RuspFn(args.clone(), b, fn_type.clone()),
                                       subenv.clone())),
                    Err(e) => Err(e)
                }
            }
            _ => Ok(v)
        }
    }
}

macro_rules! type_pred(
    ($name:ident, $pattern:pat) => (
        fn $name(params: &[@Value], _: @mut Rusp) -> EvalResult {
            match do params.map |&val| {
                match *val {
                    $pattern => @Bool(true),
                    _ => @Bool(false),
                }
            } {
                [ref result] => Ok(*result),
                results => Ok(@List(results)),
            }
        }
    )
)

type_pred!(builtin_is_bool, Bool(*))
type_pred!(builtin_is_int, Int(*))
type_pred!(builtin_is_float, Float(*))
type_pred!(builtin_is_string, Str(*))
type_pred!(builtin_is_list, List(*))
type_pred!(builtin_is_symbol, Symbol(*))
type_pred!(builtin_is_fn, FnEnv(RuspFn(_, _, Fn), _))
type_pred!(builtin_is_macro, FnEnv(RuspFn(_, _, Macro), _))
type_pred!(builtin_is_extern_fn, FnEnv(ExternFn(_, Fn), _))
type_pred!(builtin_is_extern_macro, FnEnv(ExternFn(_, Macro), _))

macro_rules! arithmetic_op(
    ($name:ident, $symbol:expr, $method:ident) => (
        fn $name(params: &[@Value], _: @mut Rusp) -> EvalResult {
            if params.len() < 2 {
                return Err(fmt!("`%s` expects at least 2 arguments", $symbol));
            }
            let mut result = *params.head();
            for params.tail().each |&val| {
                match (result, val) {
                    // Basic operations between values of the same type
                    (@Int(prev), @Int(i)) => result = @Int(prev.$method(&i)),
                    (@Float(prev), @Float(f)) => result = @Float(prev.$method(&f)),
                    // Handle numeric promotions
                    (@Int(prev), @Float(f)) => result = @Float((prev as float).$method(&f)),
                    (@Float(prev), @Int(i)) => result = @Float(prev.$method(&(i as float))),
                    // Type errors
                    (@Float(_), ref v) => return Err(fmt!("Expected float but found: %s", v.to_str())),
                    (@Int(_), ref v) => return Err(fmt!("Expected integer but found: %s", v.to_str())),
                    (ref prev, _) => return Err(fmt!("Expected number but found: %s", prev.to_str())),
                }
            }
            Ok(result)
        }
    )
)

arithmetic_op!(builtin_add, "+", add)
arithmetic_op!(builtin_sub, "-", sub)
arithmetic_op!(builtin_mul, "*", mul)
arithmetic_op!(builtin_div, "/", div)
arithmetic_op!(builtin_rem, "%", rem)

fn builtin_eq(params: &[@Value], _: @mut Rusp) -> EvalResult {
    match params {
        [head, .. rest] => {
            for rest.each |&val| {
                // TODO handle numeric promotions? i.e. 1.0 == 1?
                if !(val == head) { return Ok(@Bool(false)); }
            }
            Ok(@Bool(true))
        }
        [] => Err(fmt!("`=` expects at least 1 argument"))
    }
}

#[cfg(test)]
mod tests {
    use super::super::*;
    use super::super::parser::Parser;

    macro_rules! test_ok(
        ($src:expr, $expected:expr) => (
            assert_eq!(Rusp::new().eval(
                @Parser::new($src).parse().get()
            ).get().to_str(), $expected);
        )
    )

    macro_rules! test_err(
        ($src:expr) => (
            assert!(Rusp::new().eval(
                @Parser::new($src).parse().get()
            ).is_err());
        )
    )

    #[test]
    fn test_type_preds() {
        test_ok!("(bool? true)", ~"true");
        test_ok!("(bool? 1)", ~"false");
        test_ok!("(int? 1)", ~"true");
        test_ok!("(int? 1.0)", ~"false");
        test_ok!("(float? 1.0)", ~"true");
        test_ok!("(float? \"hi\")", ~"false");
        test_ok!("(string? \"hi\")", ~"true");
        test_ok!("(string? (list 1 2))", ~"false");
        test_ok!("(list? (list 1 2))", ~"true");
        test_ok!("(list? (list))", ~"true");
        test_ok!("(list? 1.0)", ~"false");
        test_ok!("(symbol? (quote x))", ~"true");
        test_ok!("(symbol? (fn () 1))", ~"false");
        test_ok!("(fn? (fn () 1))", ~"true");
        test_ok!("(fn? (macro () 1))", ~"false");
        test_ok!("(macro? (macro () 1))", ~"true");
        test_ok!("(macro? false)", ~"false");
    }

    #[test]
    fn test_arithmetic() {
        test_ok!("(+ 1 2)", ~"3");
        test_ok!("(+ 1 2 4)", ~"7");
        test_ok!("(+ 1.5 3 4)", ~"8.5");
        test_ok!("(+ 3 1.67)", ~"4.67");
        test_ok!("(- 3 0.5)", ~"2.5");
        test_ok!("(- 3 1.5 (+ 2 4))", ~"-4.5");

        test_err!("(+)");
        test_err!("(-)");
        test_err!("(*)");
        test_err!("(/)");
        test_err!("(%)");

        test_err!("(+ 1)");
        test_err!("(- 1)");
        test_err!("(* 1)");
        test_err!("(/ 1)");
        test_err!("(% 1)");

        test_err!("(+ 1 \"hi\")");
        test_err!("(- 1 \"hi\")");
        test_err!("(* 1 \"hi\")");
        test_err!("(/ 1 \"hi\")");
        test_err!("(% 1 \"hi\")");

        test_err!("(+ \"hi\" 1)");
        test_err!("(- \"hi\" 1)");
        test_err!("(* \"hi\" 1)");
        test_err!("(/ \"hi\" 1)");
        test_err!("(% \"hi\" 1)");
    }
}<|endoftext|>


<commit_before>extern crate gimli;

use gimli::{AttributeValue, DebugAbbrev, DebugAranges, DebugInfo, DebugLine, DebugPubNames,
            DebugPubTypes, DebugStr, LittleEndian};
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

fn read_section(section: &str) -> Vec<u8> {
    let mut path = PathBuf::new();
    if let Ok(dir) = env::var("CARGO_MANIFEST_DIR") {
        path.push(dir);
    }
    path.push("fixtures/self");
    path.push(section);

    println!("Reading section \"{}\" at path {:?}", section, path);
    assert!(path.is_file());
    let mut file = File::open(path).unwrap();

    let mut buf = Vec::new();
    file.read_to_end(&mut buf).unwrap();
    buf
}

#[test]
fn test_parse_self_debug_info() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let mut iter = debug_info.units();
    while let Some(unit) = iter.next().expect("Should parse compilation unit") {
        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);

        while cursor.next_dfs().expect("Should parse next dfs").is_some() {
            let entry = cursor.current().expect("Should have a current entry");

            let mut attrs = entry.attrs();
            while let Some(_) = attrs.next().expect("Should parse entry's attribute") {
            }
        }
    }
}

#[test]
fn test_parse_self_debug_line() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let debug_line = read_section("debug_line");
    let debug_line = DebugLine::<LittleEndian>::new(&debug_line);

    let debug_str = read_section("debug_str");
    let debug_str = DebugStr::<LittleEndian>::new(&debug_str);

    let mut iter = debug_info.units();
    while let Some(unit) = iter.next().expect("Should parse compilation unit") {
        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);
        cursor.next_dfs().expect("Should parse next dfs");

        let unit_entry = cursor.current()
            .expect("Should have a root entry");

        let comp_dir = unit_entry.attr(gimli::DW_AT_comp_dir)
            .expect("Should parse comp_dir attribute")
            .and_then(|attr| attr.string_value(&debug_str));
        let comp_name = unit_entry.attr(gimli::DW_AT_name)
            .expect("Should parse name attribute")
            .and_then(|attr| attr.string_value(&debug_str));

        if let Some(AttributeValue::DebugLineRef(offset)) =
            unit_entry.attr_value(gimli::DW_AT_stmt_list).expect("Should parse stmt_list") {
            let program = debug_line.program(offset, unit.address_size(), comp_dir, comp_name)
                .expect("should parse line number program header");

            let mut results = Vec::new();
            let mut rows = program.rows();
            while let Some((_, row)) = rows.next_row()
                .expect("Should parse and execute all rows in the line number program") {
                results.push(*row);
            }
            results.reverse();

            let program = debug_line.program(offset, unit.address_size(), comp_dir, comp_name)
                .expect("should parse line number program header");
            let (program, sequences) = program.sequences()
                .expect("should parse and execute the entire line number program");
            assert!(!sequences.is_empty()); // Should be at least one sequence.
            for sequence in sequences {
                let mut rows = program.resume_from(&sequence);
                while let Some((_, row)) = rows.next_row()
                    .expect("Should parse and execute all rows after resuming") {
                    let other_row = results.pop().unwrap();
                    assert!(row.address() >= sequence.start);
                    assert!(row.address() <= sequence.end);
                    assert_eq!(row.address(), other_row.address());
                    assert_eq!(row.line(), other_row.line());
                }
            }
            assert!(results.is_empty());
        }
    }
}

#[test]
fn test_parse_self_debug_aranges() {
    let debug_aranges = read_section("debug_aranges");
    let debug_aranges = DebugAranges::<LittleEndian>::new(&debug_aranges);

    let mut aranges = debug_aranges.items();
    while let Some(_) = aranges.next().expect("Should parse arange OK") {
        // Not really anything else we can check right now.
    }
}

#[test]
fn test_parse_self_debug_pubnames() {
    let debug_pubnames = read_section("debug_pubnames");
    let debug_pubnames = DebugPubNames::<LittleEndian>::new(&debug_pubnames);

    let mut pubnames = debug_pubnames.items();
    while let Some(_) = pubnames.next().expect("Should parse pubname OK") {
        // Not really anything else we can check right now.
    }
}

#[test]
fn test_parse_self_debug_pubtypes() {
    let debug_pubtypes = read_section("debug_pubtypes");
    let debug_pubtypes = DebugPubTypes::<LittleEndian>::new(&debug_pubtypes);

    let mut pubtypes = debug_pubtypes.items();
    while let Some(_) = pubtypes.next().expect("Should parse pubtype OK") {
        // Not really anything else we can check right now.
    }
}

// Because `.eh_frame` doesn't contain address sizes, we need to assume the
// native word size, so this test is only valid on 64-bit machines (as the
// `.eh_frame` fixture data was created on a 64-bit machine).
#[cfg(target_pointer_width="64")]
#[test]
fn test_parse_self_eh_frame() {
    use gimli::{BaseAddresses, CieOrFde, EhFrame, UnwindSection};

    let eh_frame = read_section("eh_frame");
    let eh_frame = EhFrame::<LittleEndian>::new(&eh_frame);

    let bases = BaseAddresses::default()
        .set_cfi(0)
        .set_data(0)
        .set_text(0);
    let mut entries = eh_frame.entries(&bases);
    while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
        match entry {
            CieOrFde::Cie(cie) => {
                let mut instrs = cie.instructions();
                while let Some(_) = instrs.next().expect("Can parse next CFI instruction OK") {
                    // TODO FITZGEN
                }
            }
            CieOrFde::Fde(partial) => {
                let fde = partial.parse(|offset| eh_frame.cie_from_offset(&bases, offset))
                    .expect("Should be able to get CIE for FDE");

                let mut instrs = fde.instructions();
                while let Some(_) = instrs.next().expect("Can parse next CFI instruction OK") {
                    // TODO FITZGEN
                }
            }
        }
    }
}
<commit_msg>Add self-parse test for .debug_ranges<commit_after>extern crate gimli;

use gimli::{AttributeValue, DebugAbbrev, DebugAranges, DebugInfo, DebugLine, DebugPubNames,
            DebugPubTypes, DebugRanges, DebugStr, LittleEndian};
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

fn read_section(section: &str) -> Vec<u8> {
    let mut path = PathBuf::new();
    if let Ok(dir) = env::var("CARGO_MANIFEST_DIR") {
        path.push(dir);
    }
    path.push("fixtures/self");
    path.push(section);

    println!("Reading section \"{}\" at path {:?}", section, path);
    assert!(path.is_file());
    let mut file = File::open(path).unwrap();

    let mut buf = Vec::new();
    file.read_to_end(&mut buf).unwrap();
    buf
}

#[test]
fn test_parse_self_debug_info() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let mut iter = debug_info.units();
    while let Some(unit) = iter.next().expect("Should parse compilation unit") {
        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);

        while cursor.next_dfs().expect("Should parse next dfs").is_some() {
            let entry = cursor.current().expect("Should have a current entry");

            let mut attrs = entry.attrs();
            while let Some(_) = attrs.next().expect("Should parse entry's attribute") {
            }
        }
    }
}

#[test]
fn test_parse_self_debug_line() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let debug_line = read_section("debug_line");
    let debug_line = DebugLine::<LittleEndian>::new(&debug_line);

    let debug_str = read_section("debug_str");
    let debug_str = DebugStr::<LittleEndian>::new(&debug_str);

    let mut iter = debug_info.units();
    while let Some(unit) = iter.next().expect("Should parse compilation unit") {
        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);
        cursor.next_dfs().expect("Should parse next dfs");

        let unit_entry = cursor.current()
            .expect("Should have a root entry");

        let comp_dir = unit_entry.attr(gimli::DW_AT_comp_dir)
            .expect("Should parse comp_dir attribute")
            .and_then(|attr| attr.string_value(&debug_str));
        let comp_name = unit_entry.attr(gimli::DW_AT_name)
            .expect("Should parse name attribute")
            .and_then(|attr| attr.string_value(&debug_str));

        if let Some(AttributeValue::DebugLineRef(offset)) =
            unit_entry.attr_value(gimli::DW_AT_stmt_list).expect("Should parse stmt_list") {
            let program = debug_line.program(offset, unit.address_size(), comp_dir, comp_name)
                .expect("should parse line number program header");

            let mut results = Vec::new();
            let mut rows = program.rows();
            while let Some((_, row)) = rows.next_row()
                .expect("Should parse and execute all rows in the line number program") {
                results.push(*row);
            }
            results.reverse();

            let program = debug_line.program(offset, unit.address_size(), comp_dir, comp_name)
                .expect("should parse line number program header");
            let (program, sequences) = program.sequences()
                .expect("should parse and execute the entire line number program");
            assert!(!sequences.is_empty()); // Should be at least one sequence.
            for sequence in sequences {
                let mut rows = program.resume_from(&sequence);
                while let Some((_, row)) = rows.next_row()
                    .expect("Should parse and execute all rows after resuming") {
                    let other_row = results.pop().unwrap();
                    assert!(row.address() >= sequence.start);
                    assert!(row.address() <= sequence.end);
                    assert_eq!(row.address(), other_row.address());
                    assert_eq!(row.line(), other_row.line());
                }
            }
            assert!(results.is_empty());
        }
    }
}

#[test]
fn test_parse_self_debug_ranges() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let debug_ranges = read_section("debug_ranges");
    let debug_ranges = DebugRanges::<LittleEndian>::new(&debug_ranges);

    let mut iter = debug_info.units();
    while let Some(unit) = iter.next().expect("Should parse compilation unit") {
        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);
        cursor.next_dfs().expect("Should parse next dfs");

        let mut low_pc = 0;

        {
            let unit_entry = cursor.current().expect("Should have a root entry");
            let low_pc_attr = unit_entry
                .attr_value(gimli::DW_AT_low_pc)
                .expect("Should parse low_pc");
            if let Some(gimli::AttributeValue::Addr(address)) = low_pc_attr {
                low_pc = address;
            }
        }

        while cursor.next_dfs().expect("Should parse next dfs").is_some() {
            let entry = cursor.current().expect("Should have a current entry");
            let mut attrs = entry.attrs();
            while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
                if let AttributeValue::DebugRangesRef(offset) = attr.value() {
                    let mut ranges = debug_ranges
                        .ranges(offset, unit.address_size(), low_pc)
                        .expect("Should parse ranges OK");
                    while let Some(range) = ranges.next().expect("Should parse next range") {
                        assert!(range.begin <= range.end);
                    }
                }
            }
        }
    }
}

#[test]
fn test_parse_self_debug_aranges() {
    let debug_aranges = read_section("debug_aranges");
    let debug_aranges = DebugAranges::<LittleEndian>::new(&debug_aranges);

    let mut aranges = debug_aranges.items();
    while let Some(_) = aranges.next().expect("Should parse arange OK") {
        // Not really anything else we can check right now.
    }
}

#[test]
fn test_parse_self_debug_pubnames() {
    let debug_pubnames = read_section("debug_pubnames");
    let debug_pubnames = DebugPubNames::<LittleEndian>::new(&debug_pubnames);

    let mut pubnames = debug_pubnames.items();
    while let Some(_) = pubnames.next().expect("Should parse pubname OK") {
        // Not really anything else we can check right now.
    }
}

#[test]
fn test_parse_self_debug_pubtypes() {
    let debug_pubtypes = read_section("debug_pubtypes");
    let debug_pubtypes = DebugPubTypes::<LittleEndian>::new(&debug_pubtypes);

    let mut pubtypes = debug_pubtypes.items();
    while let Some(_) = pubtypes.next().expect("Should parse pubtype OK") {
        // Not really anything else we can check right now.
    }
}

// Because `.eh_frame` doesn't contain address sizes, we need to assume the
// native word size, so this test is only valid on 64-bit machines (as the
// `.eh_frame` fixture data was created on a 64-bit machine).
#[cfg(target_pointer_width="64")]
#[test]
fn test_parse_self_eh_frame() {
    use gimli::{BaseAddresses, CieOrFde, EhFrame, UnwindSection};

    let eh_frame = read_section("eh_frame");
    let eh_frame = EhFrame::<LittleEndian>::new(&eh_frame);

    let bases = BaseAddresses::default()
        .set_cfi(0)
        .set_data(0)
        .set_text(0);
    let mut entries = eh_frame.entries(&bases);
    while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
        match entry {
            CieOrFde::Cie(cie) => {
                let mut instrs = cie.instructions();
                while let Some(_) = instrs.next().expect("Can parse next CFI instruction OK") {
                    // TODO FITZGEN
                }
            }
            CieOrFde::Fde(partial) => {
                let fde = partial.parse(|offset| eh_frame.cie_from_offset(&bases, offset))
                    .expect("Should be able to get CIE for FDE");

                let mut instrs = fde.instructions();
                while let Some(_) = instrs.next().expect("Can parse next CFI instruction OK") {
                    // TODO FITZGEN
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// spell-checker:ignore (URL) appspot

use crate::numeric::*;

// Small set of bases for the Miller-Rabin prime test, valid for all 64b integers;
//  discovered by Jim Sinclair on 2011-04-20, see miller-rabin.appspot.com
#[allow(clippy::unreadable_literal)]
const BASIS: [u64; 7] = [2, 325, 9375, 28178, 450775, 9780504, 1795265022];

#[derive(Eq, PartialEq)]
pub(crate) enum Result {
    Prime,
    Pseudoprime,
    Composite(u64),
}

impl Result {
    pub(crate) fn is_prime(&self) -> bool {
        *self == Result::Prime
    }
}

// Deterministic Miller-Rabin primality-checking algorithm, adapted to extract
// (some) dividers; it will fail to factor strong pseudoprimes.
#[allow(clippy::many_single_char_names)]
pub(crate) fn test<A: Arithmetic>(n: u64) -> Result {
    use self::Result::*;

    if n < 2 {
        return Pseudoprime;
    }
    if n % 2 == 0 {
        return if n == 2 { Prime } else { Composite(2) };
    }

    // n-1 = r 2ⁱ
    let i = (n - 1).trailing_zeros();
    let r = (n - 1) >> i;

    for a in BASIS.iter() {
        let a = a % n;
        if a == 0 {
            break;
        }

        // x = a^r mod n
        let mut x = A::pow(a, r, n);

        {
            // y = ((x²)²...)² i times = x ^ (2ⁱ) = a ^ (r 2ⁱ) = x ^ (n - 1)
            let mut y = x;
            for _ in 0..i {
                y = A::mul(y, y, n)
            }
            if y != 1 {
                return Pseudoprime;
            };
        }

        if x == 1 || x == n - 1 {
            break;
        }

        loop {
            let y = A::mul(x, x, n);
            if y == 1 {
                return Composite(gcd(x - 1, n));
            }
            if y == n - 1 {
                // This basis element is not a witness of `n` being composite.
                // Keep looking.
                break;
            }
            x = y;
        }
    }

    Prime
}

// Used by build.rs' tests
#[allow(dead_code)]
pub(crate) fn is_prime(n: u64) -> bool {
    if n < 1 << 63 {
        test::<Small>(n)
    } else {
        test::<Big>(n)
    }
    .is_prime()
}
<commit_msg>factor::miller_rabin: Add tests<commit_after>// spell-checker:ignore (URL) appspot

use crate::numeric::*;

// Small set of bases for the Miller-Rabin prime test, valid for all 64b integers;
//  discovered by Jim Sinclair on 2011-04-20, see miller-rabin.appspot.com
#[allow(clippy::unreadable_literal)]
const BASIS: [u64; 7] = [2, 325, 9375, 28178, 450775, 9780504, 1795265022];

#[derive(Eq, PartialEq)]
pub(crate) enum Result {
    Prime,
    Pseudoprime,
    Composite(u64),
}

impl Result {
    pub(crate) fn is_prime(&self) -> bool {
        *self == Result::Prime
    }
}

// Deterministic Miller-Rabin primality-checking algorithm, adapted to extract
// (some) dividers; it will fail to factor strong pseudoprimes.
#[allow(clippy::many_single_char_names)]
pub(crate) fn test<A: Arithmetic>(n: u64) -> Result {
    use self::Result::*;

    if n < 2 {
        return Pseudoprime;
    }
    if n % 2 == 0 {
        return if n == 2 { Prime } else { Composite(2) };
    }

    // n-1 = r 2ⁱ
    let i = (n - 1).trailing_zeros();
    let r = (n - 1) >> i;

    for a in BASIS.iter() {
        let a = a % n;
        if a == 0 {
            break;
        }

        // x = a^r mod n
        let mut x = A::pow(a, r, n);

        {
            // y = ((x²)²...)² i times = x ^ (2ⁱ) = a ^ (r 2ⁱ) = x ^ (n - 1)
            let mut y = x;
            for _ in 0..i {
                y = A::mul(y, y, n)
            }
            if y != 1 {
                return Pseudoprime;
            };
        }

        if x == 1 || x == n - 1 {
            break;
        }

        loop {
            let y = A::mul(x, x, n);
            if y == 1 {
                return Composite(gcd(x - 1, n));
            }
            if y == n - 1 {
                // This basis element is not a witness of `n` being composite.
                // Keep looking.
                break;
            }
            x = y;
        }
    }

    Prime
}

// Used by build.rs' tests
#[allow(dead_code)]
pub(crate) fn is_prime(n: u64) -> bool {
    if n < 1 << 63 {
        test::<Small>(n)
    } else {
        test::<Big>(n)
    }
    .is_prime()
}

#[cfg(test)]
mod tests {
    use super::is_prime;
    const LARGEST_U64_PRIME: u64 = 0xFFFFFFFFFFFFFFC5;

    #[test]
    fn largest_prime() {
        assert!(is_prime(LARGEST_U64_PRIME));
    }

    #[test]
    fn first_primes() {
        use crate::table::{NEXT_PRIME, P_INVS_U64};
        assert!(P_INVS_U64.iter().all(|(p, _, _)| is_prime(*p)));
        assert!(is_prime(NEXT_PRIME));
    }
}
<|endoftext|>