<commit_before>// Copyright 2013 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 lib::llvm::{llvm, TypeRef, Bool, False, True, TypeKind};
use lib::llvm::{Float, Double, X86_FP80, PPC_FP128, FP128};

use middle::ty;

use middle::trans::context::CrateContext;
use middle::trans::base;

use syntax::ast;
use syntax::abi::{Architecture, X86, X86_64, Arm, Mips};

use std::c_str::ToCStr;
use std::vec;
use std::cast;

use std::libc::{c_uint};

#[deriving(Clone, Eq)]
pub struct Type {
    priv rf: TypeRef
}

macro_rules! ty (
    ($e:expr) => ( Type::from_ref(unsafe { $e }))
)

/**
 * Wrapper for LLVM TypeRef
 */
impl Type {
    #[inline(always)]
    pub fn from_ref(r: TypeRef) -> Type {
        Type {
            rf: r
        }
    }

    #[inline(always)] // So it doesn't kill --opt-level=0 builds of the compiler
    pub fn to_ref(&self) -> TypeRef {
        self.rf
    }

    pub fn void() -> Type {
        ty!(llvm::LLVMVoidTypeInContext(base::task_llcx()))
    }

    pub fn nil() -> Type {
        Type::empty_struct()
    }

    pub fn metadata() -> Type {
        ty!(llvm::LLVMMetadataTypeInContext(base::task_llcx()))
    }

    pub fn i1() -> Type {
        ty!(llvm::LLVMInt1TypeInContext(base::task_llcx()))
    }

    pub fn i8() -> Type {
        ty!(llvm::LLVMInt8TypeInContext(base::task_llcx()))
    }

    pub fn i16() -> Type {
        ty!(llvm::LLVMInt16TypeInContext(base::task_llcx()))
    }

    pub fn i32() -> Type {
        ty!(llvm::LLVMInt32TypeInContext(base::task_llcx()))
    }

    pub fn i64() -> Type {
        ty!(llvm::LLVMInt64TypeInContext(base::task_llcx()))
    }

    pub fn f32() -> Type {
        ty!(llvm::LLVMFloatTypeInContext(base::task_llcx()))
    }

    pub fn f64() -> Type {
        ty!(llvm::LLVMDoubleTypeInContext(base::task_llcx()))
    }

    pub fn bool() -> Type {
        Type::i8()
    }

    pub fn char() -> Type {
        Type::i32()
    }

    pub fn i8p() -> Type {
        Type::i8().ptr_to()
    }

    pub fn int(arch: Architecture) -> Type {
        match arch {
            X86 | Arm | Mips => Type::i32(),
            X86_64 => Type::i64()
        }
    }

    pub fn float(_: Architecture) -> Type {
        // All architectures currently just use doubles as the default
        // float size
        Type::f64()
    }

    pub fn int_from_ty(ctx: &CrateContext, t: ast::int_ty) -> Type {
        match t {
            ast::ty_i => ctx.int_type,
            ast::ty_i8 => Type::i8(),
            ast::ty_i16 => Type::i16(),
            ast::ty_i32 => Type::i32(),
            ast::ty_i64 => Type::i64()
        }
    }

    pub fn uint_from_ty(ctx: &CrateContext, t: ast::uint_ty) -> Type {
        match t {
            ast::ty_u => ctx.int_type,
            ast::ty_u8 => Type::i8(),
            ast::ty_u16 => Type::i16(),
            ast::ty_u32 => Type::i32(),
            ast::ty_u64 => Type::i64()
        }
    }

    pub fn float_from_ty(ctx: &CrateContext, t: ast::float_ty) -> Type {
        match t {
            ast::ty_f => ctx.float_type,
            ast::ty_f32 => Type::f32(),
            ast::ty_f64 => Type::f64()
        }
    }

    pub fn size_t(arch: Architecture) -> Type {
        Type::int(arch)
    }

    pub fn func(args: &[Type], ret: &Type) -> Type {
        let vec : &[TypeRef] = unsafe { cast::transmute(args) };
        ty!(llvm::LLVMFunctionType(ret.to_ref(), vec::raw::to_ptr(vec),
                                   args.len() as c_uint, False))
    }

    pub fn func_pair(cx: &CrateContext, fn_ty: &Type) -> Type {
        Type::struct_([fn_ty.ptr_to(), Type::opaque_cbox_ptr(cx)], false)
    }

    pub fn ptr(ty: Type) -> Type {
        ty!(llvm::LLVMPointerType(ty.to_ref(), 0 as c_uint))
    }

    pub fn struct_(els: &[Type], packed: bool) -> Type {
        let els : &[TypeRef] = unsafe { cast::transmute(els) };
        ty!(llvm::LLVMStructTypeInContext(base::task_llcx(), vec::raw::to_ptr(els),
                                          els.len() as c_uint, packed as Bool))
    }

    pub fn named_struct(name: &str) -> Type {
        let ctx = base::task_llcx();
        ty!(name.with_c_str(|s| llvm::LLVMStructCreateNamed(ctx, s)))
    }

    pub fn empty_struct() -> Type {
        Type::struct_([], false)
    }

    pub fn vtable() -> Type {
        Type::array(&Type::i8().ptr_to(), 1)
    }

    pub fn generic_glue_fn(cx: &mut CrateContext) -> Type {
        match cx.tn.find_type("glue_fn") {
            Some(ty) => return ty,
            None => ()
        }

        let ty = Type::glue_fn(Type::i8p());
        cx.tn.associate_type("glue_fn", &ty);

        return ty;
    }

    pub fn glue_fn(t: Type) -> Type {
        Type::func([ Type::nil().ptr_to(), t ],
            &Type::void())
    }

    pub fn tydesc(arch: Architecture) -> Type {
        let mut tydesc = Type::named_struct("tydesc");
        let glue_fn_ty = Type::glue_fn(Type::i8p()).ptr_to();

        let int_ty = Type::int(arch);

        // Must mirror:
        //
        // std::unstable::intrinsics::TyDesc
        // type_desc in rt

        let elems = [int_ty,     // size
                     int_ty,     // align
                     glue_fn_ty, // take
                     glue_fn_ty, // drop
                     glue_fn_ty, // free
                     glue_fn_ty, // visit
                     int_ty, // borrow_offset
                     Type::struct_([Type::i8p(), Type::int(arch)], false)]; // name
        tydesc.set_struct_body(elems, false);

        return tydesc;
    }

    pub fn array(ty: &Type, len: u64) -> Type {
        ty!(llvm::LLVMArrayType(ty.to_ref(), len as c_uint))
    }

    pub fn vector(ty: &Type, len: u64) -> Type {
        ty!(llvm::LLVMVectorType(ty.to_ref(), len as c_uint))
    }

    pub fn vec(arch: Architecture, ty: &Type) -> Type {
        Type::struct_(
            [ Type::int(arch), Type::int(arch), Type::array(ty, 0) ],
        false)
    }

    pub fn opaque_vec(arch: Architecture) -> Type {
        Type::vec(arch, &Type::i8())
    }

    #[inline]
    pub fn box_header_fields(ctx: &CrateContext) -> ~[Type] {
        ~[
            ctx.int_type, ctx.tydesc_type.ptr_to(),
            Type::i8().ptr_to(), Type::i8().ptr_to()
        ]
    }

    pub fn box_header(ctx: &CrateContext) -> Type {
        Type::struct_(Type::box_header_fields(ctx), false)
    }

    pub fn box(ctx: &CrateContext, ty: &Type) -> Type {
        Type::struct_(Type::box_header_fields(ctx) + &[*ty], false)
    }

    pub fn opaque() -> Type {
        Type::i8()
    }

    pub fn opaque_box(ctx: &CrateContext) -> Type {
        Type::box(ctx, &Type::opaque())
    }

    pub fn unique(ctx: &CrateContext, ty: &Type) -> Type {
        Type::box(ctx, ty)
    }

    pub fn opaque_cbox_ptr(cx: &CrateContext) -> Type {
        Type::opaque_box(cx).ptr_to()
    }

    pub fn enum_discrim(cx: &CrateContext) -> Type {
        cx.int_type
    }

    pub fn opaque_trait(ctx: &CrateContext, store: ty::TraitStore) -> Type {
        let tydesc_ptr = ctx.tydesc_type.ptr_to();
        match store {
            ty::BoxTraitStore => {
                Type::struct_(
                    [ tydesc_ptr, Type::opaque_box(ctx).ptr_to() ],
                false)
            }
            ty::UniqTraitStore => {
                Type::struct_(
                    [ tydesc_ptr, Type::unique(ctx, &Type::i8()).ptr_to()],
                false)
            }
            ty::RegionTraitStore(*) => {
                Type::struct_(
                    [ tydesc_ptr, Type::i8().ptr_to() ],
                false)
            }
        }
    }

    pub fn kind(&self) -> TypeKind {
        unsafe {
            llvm::LLVMGetTypeKind(self.to_ref())
        }
    }

    pub fn set_struct_body(&mut self, els: &[Type], packed: bool) {
        unsafe {
            let vec : &[TypeRef] = cast::transmute(els);
            llvm::LLVMStructSetBody(self.to_ref(), vec::raw::to_ptr(vec),
                                    els.len() as c_uint, packed as Bool)
        }
    }

    pub fn ptr_to(&self) -> Type {
        ty!(llvm::LLVMPointerType(self.to_ref(), 0))
    }

    pub fn get_field(&self, idx: uint) -> Type {
        unsafe {
            let num_fields = llvm::LLVMCountStructElementTypes(self.to_ref()) as uint;
            let mut elems = vec::from_elem(num_fields, 0 as TypeRef);

            llvm::LLVMGetStructElementTypes(self.to_ref(), vec::raw::to_mut_ptr(elems));

            Type::from_ref(elems[idx])
        }
    }

    pub fn is_packed(&self) -> bool {
        unsafe {
            llvm::LLVMIsPackedStruct(self.to_ref()) == True
        }
    }

    pub fn element_type(&self) -> Type {
        unsafe {
            Type::from_ref(llvm::LLVMGetElementType(self.to_ref()))
        }
    }

    pub fn array_length(&self) -> uint {
        unsafe {
            llvm::LLVMGetArrayLength(self.to_ref()) as uint
        }
    }

    pub fn field_types(&self) -> ~[Type] {
        unsafe {
            let n_elts = llvm::LLVMCountStructElementTypes(self.to_ref()) as uint;
            if n_elts == 0 {
                return ~[];
            }
            let mut elts = vec::from_elem(n_elts, 0 as TypeRef);
            llvm::LLVMGetStructElementTypes(self.to_ref(), &mut elts[0]);
            cast::transmute(elts)
        }
    }

    pub fn return_type(&self) -> Type {
        ty!(llvm::LLVMGetReturnType(self.to_ref()))
    }

    pub fn func_params(&self) -> ~[Type] {
        unsafe {
            let n_args = llvm::LLVMCountParamTypes(self.to_ref()) as uint;
            let args = vec::from_elem(n_args, 0 as TypeRef);
            llvm::LLVMGetParamTypes(self.to_ref(), vec::raw::to_ptr(args));
            cast::transmute(args)
        }
    }

    pub fn float_width(&self) -> uint {
        match self.kind() {
            Float => 32,
            Double => 64,
            X86_FP80 => 80,
            FP128 | PPC_FP128 => 128,
            _ => fail!("llvm_float_width called on a non-float type")
        }
    }
}
<commit_msg>minor Type::opaque_trait code cleanup<commit_after>// Copyright 2013 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 lib::llvm::{llvm, TypeRef, Bool, False, True, TypeKind};
use lib::llvm::{Float, Double, X86_FP80, PPC_FP128, FP128};

use middle::ty;

use middle::trans::context::CrateContext;
use middle::trans::base;

use syntax::ast;
use syntax::abi::{Architecture, X86, X86_64, Arm, Mips};

use std::c_str::ToCStr;
use std::vec;
use std::cast;

use std::libc::{c_uint};

#[deriving(Clone, Eq)]
pub struct Type {
    priv rf: TypeRef
}

macro_rules! ty (
    ($e:expr) => ( Type::from_ref(unsafe { $e }))
)

/**
 * Wrapper for LLVM TypeRef
 */
impl Type {
    #[inline(always)]
    pub fn from_ref(r: TypeRef) -> Type {
        Type {
            rf: r
        }
    }

    #[inline(always)] // So it doesn't kill --opt-level=0 builds of the compiler
    pub fn to_ref(&self) -> TypeRef {
        self.rf
    }

    pub fn void() -> Type {
        ty!(llvm::LLVMVoidTypeInContext(base::task_llcx()))
    }

    pub fn nil() -> Type {
        Type::empty_struct()
    }

    pub fn metadata() -> Type {
        ty!(llvm::LLVMMetadataTypeInContext(base::task_llcx()))
    }

    pub fn i1() -> Type {
        ty!(llvm::LLVMInt1TypeInContext(base::task_llcx()))
    }

    pub fn i8() -> Type {
        ty!(llvm::LLVMInt8TypeInContext(base::task_llcx()))
    }

    pub fn i16() -> Type {
        ty!(llvm::LLVMInt16TypeInContext(base::task_llcx()))
    }

    pub fn i32() -> Type {
        ty!(llvm::LLVMInt32TypeInContext(base::task_llcx()))
    }

    pub fn i64() -> Type {
        ty!(llvm::LLVMInt64TypeInContext(base::task_llcx()))
    }

    pub fn f32() -> Type {
        ty!(llvm::LLVMFloatTypeInContext(base::task_llcx()))
    }

    pub fn f64() -> Type {
        ty!(llvm::LLVMDoubleTypeInContext(base::task_llcx()))
    }

    pub fn bool() -> Type {
        Type::i8()
    }

    pub fn char() -> Type {
        Type::i32()
    }

    pub fn i8p() -> Type {
        Type::i8().ptr_to()
    }

    pub fn int(arch: Architecture) -> Type {
        match arch {
            X86 | Arm | Mips => Type::i32(),
            X86_64 => Type::i64()
        }
    }

    pub fn float(_: Architecture) -> Type {
        // All architectures currently just use doubles as the default
        // float size
        Type::f64()
    }

    pub fn int_from_ty(ctx: &CrateContext, t: ast::int_ty) -> Type {
        match t {
            ast::ty_i => ctx.int_type,
            ast::ty_i8 => Type::i8(),
            ast::ty_i16 => Type::i16(),
            ast::ty_i32 => Type::i32(),
            ast::ty_i64 => Type::i64()
        }
    }

    pub fn uint_from_ty(ctx: &CrateContext, t: ast::uint_ty) -> Type {
        match t {
            ast::ty_u => ctx.int_type,
            ast::ty_u8 => Type::i8(),
            ast::ty_u16 => Type::i16(),
            ast::ty_u32 => Type::i32(),
            ast::ty_u64 => Type::i64()
        }
    }

    pub fn float_from_ty(ctx: &CrateContext, t: ast::float_ty) -> Type {
        match t {
            ast::ty_f => ctx.float_type,
            ast::ty_f32 => Type::f32(),
            ast::ty_f64 => Type::f64()
        }
    }

    pub fn size_t(arch: Architecture) -> Type {
        Type::int(arch)
    }

    pub fn func(args: &[Type], ret: &Type) -> Type {
        let vec : &[TypeRef] = unsafe { cast::transmute(args) };
        ty!(llvm::LLVMFunctionType(ret.to_ref(), vec::raw::to_ptr(vec),
                                   args.len() as c_uint, False))
    }

    pub fn func_pair(cx: &CrateContext, fn_ty: &Type) -> Type {
        Type::struct_([fn_ty.ptr_to(), Type::opaque_cbox_ptr(cx)], false)
    }

    pub fn ptr(ty: Type) -> Type {
        ty!(llvm::LLVMPointerType(ty.to_ref(), 0 as c_uint))
    }

    pub fn struct_(els: &[Type], packed: bool) -> Type {
        let els : &[TypeRef] = unsafe { cast::transmute(els) };
        ty!(llvm::LLVMStructTypeInContext(base::task_llcx(), vec::raw::to_ptr(els),
                                          els.len() as c_uint, packed as Bool))
    }

    pub fn named_struct(name: &str) -> Type {
        let ctx = base::task_llcx();
        ty!(name.with_c_str(|s| llvm::LLVMStructCreateNamed(ctx, s)))
    }

    pub fn empty_struct() -> Type {
        Type::struct_([], false)
    }

    pub fn vtable() -> Type {
        Type::array(&Type::i8().ptr_to(), 1)
    }

    pub fn generic_glue_fn(cx: &mut CrateContext) -> Type {
        match cx.tn.find_type("glue_fn") {
            Some(ty) => return ty,
            None => ()
        }

        let ty = Type::glue_fn(Type::i8p());
        cx.tn.associate_type("glue_fn", &ty);

        return ty;
    }

    pub fn glue_fn(t: Type) -> Type {
        Type::func([ Type::nil().ptr_to(), t ],
            &Type::void())
    }

    pub fn tydesc(arch: Architecture) -> Type {
        let mut tydesc = Type::named_struct("tydesc");
        let glue_fn_ty = Type::glue_fn(Type::i8p()).ptr_to();

        let int_ty = Type::int(arch);

        // Must mirror:
        //
        // std::unstable::intrinsics::TyDesc
        // type_desc in rt

        let elems = [int_ty,     // size
                     int_ty,     // align
                     glue_fn_ty, // take
                     glue_fn_ty, // drop
                     glue_fn_ty, // free
                     glue_fn_ty, // visit
                     int_ty, // borrow_offset
                     Type::struct_([Type::i8p(), Type::int(arch)], false)]; // name
        tydesc.set_struct_body(elems, false);

        return tydesc;
    }

    pub fn array(ty: &Type, len: u64) -> Type {
        ty!(llvm::LLVMArrayType(ty.to_ref(), len as c_uint))
    }

    pub fn vector(ty: &Type, len: u64) -> Type {
        ty!(llvm::LLVMVectorType(ty.to_ref(), len as c_uint))
    }

    pub fn vec(arch: Architecture, ty: &Type) -> Type {
        Type::struct_(
            [ Type::int(arch), Type::int(arch), Type::array(ty, 0) ],
        false)
    }

    pub fn opaque_vec(arch: Architecture) -> Type {
        Type::vec(arch, &Type::i8())
    }

    #[inline]
    pub fn box_header_fields(ctx: &CrateContext) -> ~[Type] {
        ~[
            ctx.int_type, ctx.tydesc_type.ptr_to(),
            Type::i8().ptr_to(), Type::i8().ptr_to()
        ]
    }

    pub fn box_header(ctx: &CrateContext) -> Type {
        Type::struct_(Type::box_header_fields(ctx), false)
    }

    pub fn box(ctx: &CrateContext, ty: &Type) -> Type {
        Type::struct_(Type::box_header_fields(ctx) + &[*ty], false)
    }

    pub fn opaque() -> Type {
        Type::i8()
    }

    pub fn opaque_box(ctx: &CrateContext) -> Type {
        Type::box(ctx, &Type::opaque())
    }

    pub fn unique(ctx: &CrateContext, ty: &Type) -> Type {
        Type::box(ctx, ty)
    }

    pub fn opaque_cbox_ptr(cx: &CrateContext) -> Type {
        Type::opaque_box(cx).ptr_to()
    }

    pub fn enum_discrim(cx: &CrateContext) -> Type {
        cx.int_type
    }

    pub fn opaque_trait(ctx: &CrateContext, store: ty::TraitStore) -> Type {
        let tydesc_ptr = ctx.tydesc_type.ptr_to();
        let box_ty = match store {
            ty::BoxTraitStore => Type::opaque_box(ctx),
            ty::UniqTraitStore => Type::unique(ctx, &Type::i8()),
            ty::RegionTraitStore(*) => Type::i8()
        };
        Type::struct_([tydesc_ptr, box_ty.ptr_to()], false)
    }

    pub fn kind(&self) -> TypeKind {
        unsafe {
            llvm::LLVMGetTypeKind(self.to_ref())
        }
    }

    pub fn set_struct_body(&mut self, els: &[Type], packed: bool) {
        unsafe {
            let vec : &[TypeRef] = cast::transmute(els);
            llvm::LLVMStructSetBody(self.to_ref(), vec::raw::to_ptr(vec),
                                    els.len() as c_uint, packed as Bool)
        }
    }

    pub fn ptr_to(&self) -> Type {
        ty!(llvm::LLVMPointerType(self.to_ref(), 0))
    }

    pub fn get_field(&self, idx: uint) -> Type {
        unsafe {
            let num_fields = llvm::LLVMCountStructElementTypes(self.to_ref()) as uint;
            let mut elems = vec::from_elem(num_fields, 0 as TypeRef);

            llvm::LLVMGetStructElementTypes(self.to_ref(), vec::raw::to_mut_ptr(elems));

            Type::from_ref(elems[idx])
        }
    }

    pub fn is_packed(&self) -> bool {
        unsafe {
            llvm::LLVMIsPackedStruct(self.to_ref()) == True
        }
    }

    pub fn element_type(&self) -> Type {
        unsafe {
            Type::from_ref(llvm::LLVMGetElementType(self.to_ref()))
        }
    }

    pub fn array_length(&self) -> uint {
        unsafe {
            llvm::LLVMGetArrayLength(self.to_ref()) as uint
        }
    }

    pub fn field_types(&self) -> ~[Type] {
        unsafe {
            let n_elts = llvm::LLVMCountStructElementTypes(self.to_ref()) as uint;
            if n_elts == 0 {
                return ~[];
            }
            let mut elts = vec::from_elem(n_elts, 0 as TypeRef);
            llvm::LLVMGetStructElementTypes(self.to_ref(), &mut elts[0]);
            cast::transmute(elts)
        }
    }

    pub fn return_type(&self) -> Type {
        ty!(llvm::LLVMGetReturnType(self.to_ref()))
    }

    pub fn func_params(&self) -> ~[Type] {
        unsafe {
            let n_args = llvm::LLVMCountParamTypes(self.to_ref()) as uint;
            let args = vec::from_elem(n_args, 0 as TypeRef);
            llvm::LLVMGetParamTypes(self.to_ref(), vec::raw::to_ptr(args));
            cast::transmute(args)
        }
    }

    pub fn float_width(&self) -> uint {
        match self.kind() {
            Float => 32,
            Double => 64,
            X86_FP80 => 80,
            FP128 | PPC_FP128 => 128,
            _ => fail!("llvm_float_width called on a non-float type")
        }
    }
}
<|endoftext|>


<commit_before>//! If you use enough force, you can actually use this GC to implement a toy VM.

#[macro_use] extern crate toy_gc;
use toy_gc::{Heap, with_heap};
use std::rc::Rc;

gc_ref_type! {
    pub struct Pair / PairRef / PairStorage / PairRefStorage <'a> {
        car / set_car: Value<'a>,
        cdr / set_cdr: Value<'a>
    }
}

gc_inline_enum! {
    pub enum Value / ValueStorage <'a> {
        Nil,
        Int(i32),
        Symbol(Rc<String>),
        Cons(PairRef<'a>),
        Lambda(PairRef<'a>),
        Builtin(Box<BuiltinFnPtr>)
    }
}

use Value::*;

type BuiltinFnTrait = for<'b> fn (Vec<Value<'b>>) -> Result<Value<'b>, String>;

#[derive(Clone)]
pub struct BuiltinFnPtr(&'static BuiltinFnTrait);

impl PartialEq for BuiltinFnPtr {
    fn eq(&self, other: &BuiltinFnPtr) -> bool {
        *self.0 as usize == *other.0 as usize
    }
}

impl std::fmt::Debug for BuiltinFnPtr {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "BuiltinFn({:p})", *self.0 as usize as *const ());
        Ok(())
    }
}

impl<'a> Value<'a> {
    fn push_env(&mut self, heap: &mut Heap<'a>, key: Rc<String>, value: Value<'a>) {
        let pair = Cons(heap.alloc(Pair {
            car: Symbol(key),
            cdr: value
        }));
        *self = Cons(heap.alloc(Pair {
            car: pair,
            cdr: self.clone()
        }));
    }
}

macro_rules! lisp {
    { ( ) , $_heap:expr } => {
        Nil
    };
    { ( $h:tt $($t:tt)* ) , $heap:expr } => {
        {
            let h = lisp!($h, $heap);
            let t = lisp!(($($t)*), $heap);
            Cons($heap.alloc(Pair { car: h, cdr: t }))
        }
    };
    ( {$v:expr} , $_heap:expr) => {  // lame, but nothing else matches after an `ident` match fails
        Int($v)
    };
    { $s:ident , $_heap:expr } => {
        Symbol(Rc::new(stringify!($s).to_string()))
    };
}

fn parse_pair<'a>(v: Value<'a>, msg: &'static str) -> Result<(Value<'a>, Value<'a>), String> {
    match v {
        Cons(r) => Ok((r.car(), r.cdr())),
        _ => Err(msg.to_string())
    }
}

fn lookup<'a>(mut env: Value<'a>, name: Rc<String>) -> Result<Value<'a>, String> {
    let v = Symbol(name.clone());
    while let Cons(p) = env {
        let (key, value) = try!(parse_pair(p.car(), "internal error: bad environment structure"));
        if key == v {
            return Ok(value);
        }
        env = p.cdr();
    }
    Err(format!("undefined symbol: {:?}", *name))
}

fn map_eval<'a>(heap: &mut Heap<'a>, mut exprs: Value<'a>, env: &Value<'a>)
    -> Result<Vec<Value<'a>>, String>
{
    let mut v = vec![];
    while let Cons(pair) = exprs {
        v.push(try!(eval(heap, pair.car(), env)));
        exprs = pair.cdr();
    }
    Ok(v)
}

fn apply<'a>(heap: &mut Heap<'a>, fval: Value<'a>, args: Vec<Value<'a>>) -> Result<Value<'a>, String> {
    match fval {
        Builtin(f) => (*f.0)(args),
        Lambda(pair) => {
            let mut env = pair.cdr();
            let (mut params, rest) = try!(parse_pair(pair.car(), "syntax error in lambda"));
            let (body, rest) = try!(parse_pair(rest, "syntax error in lambda"));
            if rest != Nil {
                return Err("syntax error in lambda".to_string());
            }

            let mut i = 0;
            while let Cons(pair) = params {
                if i > args.len() {
                    return Err("apply: not enough arguments".to_string());
                }
                if let Symbol(s) = pair.car() {
                    let pair = Cons(heap.alloc(Pair {
                        car: Symbol(s),
                        cdr: args[i].clone()
                    }));
                    env = Cons(heap.alloc(Pair {
                        car: pair,
                        cdr: env
                    }));
                } else {
                    return Err("syntax error in lambda arguments".to_string());
                }
                params = pair.cdr();
                i += 1;
            }
            if i < args.len() {
                return Err("apply: too many arguments".to_string());
            }
            eval(heap, body, &env)
        }
        _ => Err("apply: not a function".to_string())
    }
}

fn eval<'a>(heap: &mut Heap<'a>, expr: Value<'a>, env: &Value<'a>) -> Result<Value<'a>, String> {
    match expr {
        Symbol(s) => lookup(env.clone(), s),
        Cons(p) => {
            let f = p.car();
            if let Symbol(ref s) = f {
                if &**s == "lambda" {
                    return Ok(Lambda(heap.alloc(Pair {
                        car: p.cdr(),
                        cdr: env.clone()
                    })));
                } else if &**s == "if" {
                    let (cond, rest) = try!(parse_pair(p.cdr(), "(if) with no arguments"));
                    let (t_expr, rest) = try!(parse_pair(rest, "missing arguments after (if COND)"));
                    let (f_expr, rest) = try!(parse_pair(rest, "missing 'else' argument after (if COND X)"));
                    match rest {
                        Nil => {}
                        _ => return Err("too many arguments in (if) expression".to_string())
                    };
                    let cond_result = try!(eval(heap, cond, env));
                    let selected_expr = if cond_result == Nil { f_expr } else { t_expr };
                    return eval(heap, selected_expr, env);
                }
            }
            let fval = try!(eval(heap, f, env));
            let args = try!(map_eval(heap, p.cdr(), env));
            apply(heap, fval, args)
        }
        Builtin(_) => Err(format!("builtin function found in source code")),
        _ => Ok(expr)  // nil and numbers are self-evaluating
    }
}

fn add<'a>(args: Vec<Value<'a>>) -> Result<Value<'a>, String> {
    let mut total = 0;
    for v in args {
        if let Int(n) = v {
            total += n;
        } else {
            return Err("add: non-numeric argument".to_string());
        }
    }
    Ok(Int(total))
}

const add_ptr: &'static BuiltinFnTrait = &(add as BuiltinFnTrait);

fn main() {
    with_heap(|heap| {
        let mut env = Nil;
        env.push_env(heap, Rc::new("add".to_string()), Builtin(Box::new(BuiltinFnPtr(add_ptr))));
        let program = lisp!(
            ((lambda (x y z) (add x (add y z))) {3} {4} {5})
            , heap);
        let result = eval(heap, program, &env);
        assert_eq!(result, Ok(Int(12)));
    });
}
<commit_msg>Better `lisp!` macro in toy_vm.rs.<commit_after>//! If you use enough force, you can actually use this GC to implement a toy VM.

#[macro_use] extern crate toy_gc;
use toy_gc::{Heap, with_heap};
use std::rc::Rc;

gc_ref_type! {
    pub struct Pair / PairRef / PairStorage / PairRefStorage <'a> {
        car / set_car: Value<'a>,
        cdr / set_cdr: Value<'a>
    }
}

gc_inline_enum! {
    pub enum Value / ValueStorage <'a> {
        Nil,
        Int(i32),
        Symbol(Rc<String>),
        Cons(PairRef<'a>),
        Lambda(PairRef<'a>),
        Builtin(Box<BuiltinFnPtr>)
    }
}

use Value::*;

type BuiltinFnTrait = for<'b> fn (Vec<Value<'b>>) -> Result<Value<'b>, String>;

#[derive(Clone)]
pub struct BuiltinFnPtr(&'static BuiltinFnTrait);

impl PartialEq for BuiltinFnPtr {
    fn eq(&self, other: &BuiltinFnPtr) -> bool {
        *self.0 as usize == *other.0 as usize
    }
}

impl std::fmt::Debug for BuiltinFnPtr {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "BuiltinFn({:p})", *self.0 as usize as *const ());
        Ok(())
    }
}

impl<'a> Value<'a> {
    fn push_env(&mut self, heap: &mut Heap<'a>, key: Rc<String>, value: Value<'a>) {
        let pair = Cons(heap.alloc(Pair {
            car: Symbol(key),
            cdr: value
        }));
        *self = Cons(heap.alloc(Pair {
            car: pair,
            cdr: self.clone()
        }));
    }
}

macro_rules! lisp {
    { ( ) , $_heap:expr } => {
        Nil
    };
    { ( $h:tt $($t:tt)* ) , $heap:expr } => {
        {
            let h = lisp!($h, $heap);
            let t = lisp!(($($t)*), $heap);
            Cons($heap.alloc(Pair { car: h, cdr: t }))
        }
    };
    { $s:tt , $_heap:expr } => {
        {
            let s = stringify!($s);  // lame, but nothing else matches after an `ident` match fails
            if s.starts_with(|c: char| c.is_digit(10)) {
                Int(s.parse().expect("invalid numeric literal in `lisp!`"))
            } else {
                Symbol(Rc::new(s.to_string()))
            }
        }
    };
}

fn parse_pair<'a>(v: Value<'a>, msg: &'static str) -> Result<(Value<'a>, Value<'a>), String> {
    match v {
        Cons(r) => Ok((r.car(), r.cdr())),
        _ => Err(msg.to_string())
    }
}

fn lookup<'a>(mut env: Value<'a>, name: Rc<String>) -> Result<Value<'a>, String> {
    let v = Symbol(name.clone());
    while let Cons(p) = env {
        let (key, value) = try!(parse_pair(p.car(), "internal error: bad environment structure"));
        if key == v {
            return Ok(value);
        }
        env = p.cdr();
    }
    Err(format!("undefined symbol: {:?}", *name))
}

fn map_eval<'a>(heap: &mut Heap<'a>, mut exprs: Value<'a>, env: &Value<'a>)
    -> Result<Vec<Value<'a>>, String>
{
    let mut v = vec![];
    while let Cons(pair) = exprs {
        v.push(try!(eval(heap, pair.car(), env)));
        exprs = pair.cdr();
    }
    Ok(v)
}

fn apply<'a>(heap: &mut Heap<'a>, fval: Value<'a>, args: Vec<Value<'a>>) -> Result<Value<'a>, String> {
    match fval {
        Builtin(f) => (*f.0)(args),
        Lambda(pair) => {
            let mut env = pair.cdr();
            let (mut params, rest) = try!(parse_pair(pair.car(), "syntax error in lambda"));
            let (body, rest) = try!(parse_pair(rest, "syntax error in lambda"));
            if rest != Nil {
                return Err("syntax error in lambda".to_string());
            }

            let mut i = 0;
            while let Cons(pair) = params {
                if i > args.len() {
                    return Err("apply: not enough arguments".to_string());
                }
                if let Symbol(s) = pair.car() {
                    let pair = Cons(heap.alloc(Pair {
                        car: Symbol(s),
                        cdr: args[i].clone()
                    }));
                    env = Cons(heap.alloc(Pair {
                        car: pair,
                        cdr: env
                    }));
                } else {
                    return Err("syntax error in lambda arguments".to_string());
                }
                params = pair.cdr();
                i += 1;
            }
            if i < args.len() {
                return Err("apply: too many arguments".to_string());
            }
            eval(heap, body, &env)
        }
        _ => Err("apply: not a function".to_string())
    }
}

fn eval<'a>(heap: &mut Heap<'a>, expr: Value<'a>, env: &Value<'a>) -> Result<Value<'a>, String> {
    match expr {
        Symbol(s) => lookup(env.clone(), s),
        Cons(p) => {
            let f = p.car();
            if let Symbol(ref s) = f {
                if &**s == "lambda" {
                    return Ok(Lambda(heap.alloc(Pair {
                        car: p.cdr(),
                        cdr: env.clone()
                    })));
                } else if &**s == "if" {
                    let (cond, rest) = try!(parse_pair(p.cdr(), "(if) with no arguments"));
                    let (t_expr, rest) = try!(parse_pair(rest, "missing arguments after (if COND)"));
                    let (f_expr, rest) = try!(parse_pair(rest, "missing 'else' argument after (if COND X)"));
                    match rest {
                        Nil => {}
                        _ => return Err("too many arguments in (if) expression".to_string())
                    };
                    let cond_result = try!(eval(heap, cond, env));
                    let selected_expr = if cond_result == Nil { f_expr } else { t_expr };
                    return eval(heap, selected_expr, env);
                }
            }
            let fval = try!(eval(heap, f, env));
            let args = try!(map_eval(heap, p.cdr(), env));
            apply(heap, fval, args)
        }
        Builtin(_) => Err(format!("builtin function found in source code")),
        _ => Ok(expr)  // nil and numbers are self-evaluating
    }
}

fn add<'a>(args: Vec<Value<'a>>) -> Result<Value<'a>, String> {
    let mut total = 0;
    for v in args {
        if let Int(n) = v {
            total += n;
        } else {
            return Err("add: non-numeric argument".to_string());
        }
    }
    Ok(Int(total))
}

const add_ptr: &'static BuiltinFnTrait = &(add as BuiltinFnTrait);

fn main() {
    with_heap(|heap| {
        let mut env = Nil;
        env.push_env(heap, Rc::new("+".to_string()), Builtin(Box::new(BuiltinFnPtr(add_ptr))));
        let program = lisp!(
            ((lambda (x y z) (+ x (+ y z))) 3 4 5)
            , heap);
        let result = eval(heap, program, &env);
        assert_eq!(result, Ok(Int(12)));
    });
}
<|endoftext|>


<commit_before>use core::prelude::*;
use core::default::Default;
use boxed::Box;
use fn_box::FnBox;
use collections::RingBuf;
use global::global;
use alloc::heap::allocate;

pub fn spawn<F, R>(f:F) -> Future<R> where F: FnOnce()->R, F:Send {
    global().scheduler.lock().spawn(f)
}

pub unsafe fn reschedule(current_rsp: uint) -> ! {
    // switch stack so we can park current thread

    call_on_stack(inner, current_rsp, scheduler_stack_top());

    fn inner(current_rsp: uint) -> ! {
        // park current thread
        let current = Thread::from_rsp(current_rsp);
        global().scheduler.lock().park(current);

        schedule_inner();
    }
}

pub unsafe fn schedule() -> ! {
    call_on_stack(inner, (), scheduler_stack_top());

    fn inner(_:()) -> ! {
        schedule_inner();
    }
}

fn schedule_inner() -> ! {
    #[allow(improper_ctypes)]
    extern {
        fn pop_registers_and_iret(rsp: uint) -> !;
    }

    fn new_stack() -> uint {
        const NEW_STACK_SIZE: uint = 4096*2;
        let stack_bottom = unsafe{allocate(NEW_STACK_SIZE, 4096)};
        let stack_top = stack_bottom as uint + NEW_STACK_SIZE;
        stack_top
    }

    fn invoke(function: Box<FnBox() + Send>) -> ! {
        function.call_once(());
        unsafe{asm!("int $$66" :::: "volatile")};
        unreachable!();
    }

    // schedule new thread
    print!("_");
    let new_thread = unsafe{global().scheduler.lock().schedule().unwrap_or_default()};

    match new_thread {
        Thread{state: ThreadState::Active{rsp}} => {
            unsafe{pop_registers_and_iret(rsp)}
        },
        Thread{state: ThreadState::New{function}} => {
            println!("new");
            let new_stack_top = new_stack();
            let invoke_fn : fn(Box<FnBox() + Send>) -> ! = invoke;
            unsafe{call_on_stack(invoke_fn, function, new_stack_top)}
        },
    }
}

fn scheduler_stack_top() -> uint {
    const SCHEDULER_STACK_SIZE: uint = 4096;
    static SCHEDULER_STACK: [u8; SCHEDULER_STACK_SIZE] = [0; SCHEDULER_STACK_SIZE];

    &SCHEDULER_STACK as *const [u8; 4096] as uint + SCHEDULER_STACK_SIZE
}

unsafe fn call_on_stack<Arg>(function: fn(Arg) -> !, arg: Arg, stack_top: uint) -> ! {
    asm!("call $0;" :: "r"(function), "{rdi}"(arg), "{rsp}"(stack_top) :
        : "intel", "volatile");
    panic!("diverging fn returned");
}

pub type GlobalScheduler = Scheduler;
impl GlobalScheduler {
    pub fn new() -> GlobalScheduler {
        Scheduler::new()
    }
}

pub struct Future<T>;

pub struct Thread {
    state: ThreadState,
}

impl Thread {
    fn new<F>(f: F) -> Thread where F : FnOnce(), F: Send {
        Thread {
            state: ThreadState::New {
                function: box f,
            }
        }
    }

    pub fn from_rsp(rsp: uint) -> Thread {
        Thread {
            state: ThreadState::Active {
                rsp: rsp,
            }
        }
    }
}

impl Default for Thread {
    fn default() -> Thread {
        Thread::new(|| print!("."))
    }
}

enum ThreadState {
    New {
        function: Box<FnBox() + Send>,
    },
    Active {
        rsp: uint,
    }
}

pub struct Scheduler {
    threads: RingBuf<Thread>,
}

impl Scheduler {
    pub fn new() -> Scheduler {
        Scheduler{
            threads: RingBuf::new(),
        }
    }

    fn spawn<F, R>(&mut self, f:F) -> Future<R> where F: FnOnce()->R, F: Send {

        //let (tx, rx) = channel();

        self.threads.push_back(Thread::new(move |:| {
            /*tx.send_opt(*/ f();
        }));

        Future/*::from_receiver(rx)*/
    }

    fn park(&mut self, thread: Thread) {
        self.threads.push_back(thread)
    }

    unsafe fn schedule(&mut self) -> Option<Thread> {
        self.threads.pop_front()
    }
}<commit_msg>redefine call_on_stack without arguments to avoid bug<commit_after>use core::prelude::*;
use core::default::Default;
use boxed::Box;
use fn_box::FnBox;
use collections::RingBuf;
use global::global;
use alloc::heap::allocate;

pub fn spawn<F, R>(f:F) -> Future<R> where F: FnOnce()->R, F:Send {
    global().scheduler.lock().spawn(f)
}

pub unsafe fn reschedule(current_rsp: uint) -> ! {
    // switch stack so we can park current thread

    call_on_stack(inner, current_rsp, scheduler_stack_top());

    fn inner(current_rsp: uint) -> ! {
        // park current thread
        let current = Thread::from_rsp(current_rsp);
        global().scheduler.lock().park(current);

        schedule_inner();
    }
}

pub unsafe fn schedule() -> ! {
    call_on_stack(schedule_inner, scheduler_stack_top());

    unsafe fn call_on_stack(function: fn() -> !, stack_top: uint) -> ! {
        asm!("call $0;" :: "r"(function), "{rsp}"(stack_top) :: "intel", "volatile");
        panic!("diverging fn returned");
    }
}

fn schedule_inner() -> ! {
    #[allow(improper_ctypes)]
    extern {
        fn pop_registers_and_iret(rsp: uint) -> !;
    }

    fn new_stack() -> uint {
        const NEW_STACK_SIZE: uint = 4096*2;
        let stack_bottom = unsafe{allocate(NEW_STACK_SIZE, 4096)};
        let stack_top = stack_bottom as uint + NEW_STACK_SIZE;
        stack_top
    }

    fn invoke(function: Box<FnBox() + Send>) -> ! {
        function.call_once(());
        unsafe{asm!("int $$66" :::: "volatile")};
        unreachable!();
    }

    // schedule new thread
    print!("_");
    let new_thread = unsafe{global().scheduler.lock().schedule().unwrap_or_default()};

    match new_thread {
        Thread{state: ThreadState::Active{rsp}} => {
            unsafe{pop_registers_and_iret(rsp)}
        },
        Thread{state: ThreadState::New{function}} => {
            println!("new");
            let new_stack_top = new_stack();
            let invoke_fn : fn(Box<FnBox() + Send>) -> ! = invoke;
            unsafe{call_on_stack(invoke_fn, function, new_stack_top)}
        },
    }
}

fn scheduler_stack_top() -> uint {
    const SCHEDULER_STACK_SIZE: uint = 4096;
    static SCHEDULER_STACK: [u8; SCHEDULER_STACK_SIZE] = [0; SCHEDULER_STACK_SIZE];

    &SCHEDULER_STACK as *const [u8; 4096] as uint + SCHEDULER_STACK_SIZE
}

unsafe fn call_on_stack<Arg>(function: fn(Arg) -> !, arg: Arg, stack_top: uint) -> ! {
    asm!("call $0;" :: "r"(function), "{rdi}"(arg), "{rsp}"(stack_top) :
        : "intel", "volatile");
    panic!("diverging fn returned");
}

pub type GlobalScheduler = Scheduler;
impl GlobalScheduler {
    pub fn new() -> GlobalScheduler {
        Scheduler::new()
    }
}

pub struct Future<T>;

pub struct Thread {
    state: ThreadState,
}

impl Thread {
    fn new<F>(f: F) -> Thread where F : FnOnce(), F: Send {
        Thread {
            state: ThreadState::New {
                function: box f,
            }
        }
    }

    pub fn from_rsp(rsp: uint) -> Thread {
        Thread {
            state: ThreadState::Active {
                rsp: rsp,
            }
        }
    }
}

impl Default for Thread {
    fn default() -> Thread {
        Thread::new(|| print!("."))
    }
}

enum ThreadState {
    New {
        function: Box<FnBox() + Send>,
    },
    Active {
        rsp: uint,
    }
}

pub struct Scheduler {
    threads: RingBuf<Thread>,
}

impl Scheduler {
    pub fn new() -> Scheduler {
        Scheduler{
            threads: RingBuf::new(),
        }
    }

    fn spawn<F, R>(&mut self, f:F) -> Future<R> where F: FnOnce()->R, F: Send {

        //let (tx, rx) = channel();

        self.threads.push_back(Thread::new(move |:| {
            /*tx.send_opt(*/ f();
        }));

        Future/*::from_receiver(rx)*/
    }

    fn park(&mut self, thread: Thread) {
        self.threads.push_back(thread)
    }

    unsafe fn schedule(&mut self) -> Option<Thread> {
        self.threads.pop_front()
    }
}<|endoftext|>


<commit_before>#![crate_name = "uu_basename"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Jimmy Lu <jimmy.lu.2011@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;
extern crate libc;

#[macro_use]
extern crate uucore;

use getopts::Options;
use std::io::Write;
use std::path::{is_separator, PathBuf};

static NAME: &'static str = "basename";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

pub fn uumain(args: Vec<String>) -> i32 {
    //
    // Argument parsing
    //
    let mut opts = Options::new();
    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m)  => m,
        Err(f) => crash!(1, "Invalid options\n{}", f)
    };

    if matches.opt_present("help") {
        let msg = format!("Usage: {0} NAME [SUFFIX]\n   or: {0} OPTION\n\n\
        Print NAME with any leading directory components removed.\n\
        If specified, also remove a trailing SUFFIX.", NAME);

        print!("{}", opts.usage(&msg));

        return 0;
    }

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    // too few arguments
    if args.len() < 2 {
        println!("{}: {}", NAME, "missing operand");
        println!("Try '{} --help' for more information.", NAME);
        return 1;
    }
    // too many arguments
    else if args.len() > 3 {
        println!("{}: extra operand '{}'", NAME, args[3]);
        println!("Try '{} --help' for more information.", NAME);
        return 1;
    }

    //
    // Main Program Processing
    //

    let mut name = strip_dir(&args[1]);

    if args.len() > 2 {
        let suffix = args[2].clone();
        name = strip_suffix(name.as_ref(), suffix.as_ref());
    }

    println!("{}", name);

    0
}

fn strip_dir(fullname: &str) -> String {
    // Remove all platform-specific path separators from the end
    let mut path: String = fullname.chars().rev().skip_while(|&ch| is_separator(ch)).collect();

    // Undo reverse
    path = path.chars().rev().collect();

    // Convert to path buffer and get last path component
    let pb = PathBuf::from(path);
    match pb.components().last() {
        Some(c) => c.as_os_str().to_str().unwrap().to_owned(),
        None => "".to_owned()
    }
}

fn strip_suffix(name: &str, suffix: &str) -> String {
    if name == suffix {
        return name.to_owned();
    }

    if name.ends_with(suffix) {
        return name[..name.len() - suffix.len()].to_owned();
    }

    name.to_owned()
}
<commit_msg>basename: too few or many args messages go to stderr<commit_after>#![crate_name = "uu_basename"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Jimmy Lu <jimmy.lu.2011@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;
extern crate libc;

#[macro_use]
extern crate uucore;

use getopts::Options;
use std::io::Write;
use std::path::{is_separator, PathBuf};

static NAME: &'static str = "basename";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

pub fn uumain(args: Vec<String>) -> i32 {
    //
    // Argument parsing
    //
    let mut opts = Options::new();
    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m)  => m,
        Err(f) => crash!(1, "Invalid options\n{}", f)
    };

    if matches.opt_present("help") {
        let msg = format!("Usage: {0} NAME [SUFFIX]\n   or: {0} OPTION\n\n\
        Print NAME with any leading directory components removed.\n\
        If specified, also remove a trailing SUFFIX.", NAME);

        print!("{}", opts.usage(&msg));

        return 0;
    }

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    // too few arguments
    if args.len() < 2 {
        crash!(
            1,
            "{0}: {1}\nTry '{0} --help' for more information.",
            NAME,
            "missing operand"
        );
    }
    // too many arguments
    else if args.len() > 3 {
        crash!(
            1,
            "{0}: extra operand '{1}'\nTry '{0} --help' for more information.",
            NAME,
            args[3]
        );
    }

    //
    // Main Program Processing
    //

    let mut name = strip_dir(&args[1]);

    if args.len() > 2 {
        let suffix = args[2].clone();
        name = strip_suffix(name.as_ref(), suffix.as_ref());
    }

    println!("{}", name);

    0
}

fn strip_dir(fullname: &str) -> String {
    // Remove all platform-specific path separators from the end
    let mut path: String = fullname.chars().rev().skip_while(|&ch| is_separator(ch)).collect();

    // Undo reverse
    path = path.chars().rev().collect();

    // Convert to path buffer and get last path component
    let pb = PathBuf::from(path);
    match pb.components().last() {
        Some(c) => c.as_os_str().to_str().unwrap().to_owned(),
        None => "".to_owned()
    }
}

fn strip_suffix(name: &str, suffix: &str) -> String {
    if name == suffix {
        return name.to_owned();
    }

    if name.ends_with(suffix) {
        return name[..name.len() - suffix.len()].to_owned();
    }

    name.to_owned()
}
<|endoftext|>


<commit_before>// Copyright 2015 Ilkka Rauta
//
// 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.

extern crate linuxdvb;

use std::fs;
use std::error::Error;
use std::path::Path;

use linuxdvb::{Frontend,ReadWriteMode,BlockMode};
use linuxdvb::properties::GetProperty as GP;
use linuxdvb::properties::GetPropertyValue as GPV;

type SimpleResult = Result<(), Box<Error>>;

fn show_frontend_info(path: &Path) -> SimpleResult {
    let frontend = Frontend::open("/dev/dvb/adapter0/frontend0", ReadWriteMode::ReadOnly, BlockMode::NonBlocking).unwrap();
    println!("Device: {}", path.to_string_lossy());
    let info = try!(frontend.get_info());
    println!("Name: {}", info.name);

    let properties = try!(frontend.get_properties(&[
        GP::DtvEnumDelsys
        ]));
    if let GPV::DtvEnumDelsys(delsys) = properties[0] {
        println!("Supported delivery systems: {:?}", delsys.delivery_systems());
    }

    println!("Minimal frequency: {}", info.frequency_min);
    println!("Maximal frequency: {}", info.frequency_max);
    println!("Frequency step: {}", info.frequency_stepsize);
    println!("Frequency tolerance: {}", info.frequency_tolerance);
    println!("Minimal symbol rate: {}", info.symbol_rate_min);
    println!("Maximal symbol rate: {}", info.symbol_rate_max);
    println!("Maximal symbol rate tolerance: {}", info.symbol_rate_tolerance);

    let caps: Vec<linuxdvb::caps::FrontendCapsEnum> = info.caps.into();
    println!("Frontend capabilities: {:?}", caps);

    Ok(())
}

fn iterate_devices(adapter_dir: &Path) -> SimpleResult {
    for device in try!(fs::read_dir(adapter_dir)) {
        let device = try!(device);
        let is_frontend = device.path().file_name()
            .map(|f| f.to_str())
            .unwrap_or(None)
            .map_or(false, |f| f.starts_with("frontend"));
        if is_frontend {
            try!(show_frontend_info(&device.path()));
        }
    }
    Ok(())
}

fn iterate_adapters() -> SimpleResult {
    for adapter_dir in try!(fs::read_dir("/dev/dvb")) {
        let adapter_dir = try!(adapter_dir);
        try!(iterate_devices(&adapter_dir.path()));
    }
    Ok(())
}

fn main() {
    match iterate_adapters() {
        Ok(_) => (),
        Err(error) => println!("Error: {}", error.description())
    }
}<commit_msg>Add separator between multiple frontend devices.<commit_after>// Copyright 2015 Ilkka Rauta
//
// 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.

extern crate linuxdvb;

use std::fs;
use std::error::Error;
use std::path::Path;

use linuxdvb::{Frontend,ReadWriteMode,BlockMode};
use linuxdvb::properties::GetProperty as GP;
use linuxdvb::properties::GetPropertyValue as GPV;

type SimpleResult<T> = Result<T, Box<Error>>;

fn show_frontend_info(path: &Path, yet_another_one: bool) -> SimpleResult<()> {
    let frontend = Frontend::open("/dev/dvb/adapter0/frontend0", ReadWriteMode::ReadOnly, BlockMode::NonBlocking).unwrap();
    if yet_another_one {
        println!("--");
    }
    println!("Device: {}", path.to_string_lossy());
    let info = try!(frontend.get_info());
    println!("Name: {}", info.name);

    let properties = try!(frontend.get_properties(&[
        GP::DtvEnumDelsys
        ]));
    if let GPV::DtvEnumDelsys(delsys) = properties[0] {
        println!("Supported delivery systems: {:?}", delsys.delivery_systems());
    }

    println!("Minimal frequency: {}", info.frequency_min);
    println!("Maximal frequency: {}", info.frequency_max);
    println!("Frequency step: {}", info.frequency_stepsize);
    println!("Frequency tolerance: {}", info.frequency_tolerance);
    println!("Minimal symbol rate: {}", info.symbol_rate_min);
    println!("Maximal symbol rate: {}", info.symbol_rate_max);
    println!("Maximal symbol rate tolerance: {}", info.symbol_rate_tolerance);

    let caps: Vec<linuxdvb::caps::FrontendCapsEnum> = info.caps.into();
    println!("Frontend capabilities: {:?}", caps);

    Ok(())
}

fn iterate_devices(adapter_dir: &Path, yet_another_one: bool) -> SimpleResult<bool> {
    let mut has_frontend = yet_another_one;
    for device in try!(fs::read_dir(adapter_dir)) {
        let device = try!(device);
        let is_frontend = device.path().file_name()
            .map(|f| f.to_str())
            .unwrap_or(None)
            .map_or(false, |f| f.starts_with("frontend"));
        if is_frontend {
            try!(show_frontend_info(&device.path(), yet_another_one));
            has_frontend = true;
        }
    }
    Ok(has_frontend)
}

fn iterate_adapters() -> SimpleResult<bool> {
    let mut has_frontend = false;
    for adapter_dir in try!(fs::read_dir("/dev/dvb")) {
        let adapter_dir = try!(adapter_dir);
        has_frontend = has_frontend || try!(iterate_devices(&adapter_dir.path(), has_frontend));
    }
    Ok(has_frontend)
}

fn main() {
    let has_frontend = match iterate_adapters() {
        Ok(has_frontend) => has_frontend,
        Err(error) => {
            println!("Error: {}", error.description());
            return;
        }
    };
    if !has_frontend {
        println!("No DVB frontend devices were found");
    }
}<|endoftext|>


<commit_before>// This file is part of Grust, GObject introspection bindings for Rust
//
// Copyright (C) 2013, 2014  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use gio::{File, FileInputStream, IOErrorEnum};
use grust::refcount::{Ref,SyncRef};
use grust::mainloop::{LoopRunner,MainLoop};
use grust::object;
use grust::error::Match as ErrorMatch;
use std::error::Error;

fn run_on_mainloop(setup: |SyncRef<MainLoop>|) {
    let runner = LoopRunner::new();
    runner.run_after(setup);
}

#[test]
fn new_ref() {
    let mut f = File::new_for_path("/dev/null");
    let mut g = Ref::new(f.deref_mut());
    let path = g.get_path();
    assert_eq!(path.parse_as_utf8().unwrap(), "/dev/null");
}

#[test]
fn clone() {
    let rf = File::new_for_path("/dev/null");
    let mut rg = rf.clone();
    let path = rg.get_path();
    assert_eq!(path.parse_as_utf8().unwrap(), "/dev/null");
}

#[test]
#[should_fail]
fn cast_fail() {
    let rf = File::new_for_path("/dev/null");
    let _: &FileInputStream = object::cast(&*rf);
}

#[test]
fn async() {
    run_on_mainloop(|mut mainloop| {
        let mut f = File::new_for_path("/dev/null");
        f.read_async(0, None,
            move |: obj, res| {
                let f: &mut File = object::cast_mut(obj);
                match f.read_finish(res) {
                    Ok(_)  => {}
                    Err(e) => { println!("Error: {}", e.description()) }
                }
                mainloop.quit();
            });
    })
}

#[test]
fn error_to_domain() {
    run_on_mainloop(|mut mainloop| {
        let mut f = File::new_for_path("./does-not-exist");
        f.read_async(0, None,
            move |: obj, res| {
                let f: &mut File = object::cast_mut(obj);
                match f.read_finish(res) {
                    Ok(_)  => { unreachable!() }
                    Err(e) => {
                        match IOErrorEnum::from_error(&e) {
                            ErrorMatch::Known(code) => {
                                assert_eq!(code, IOErrorEnum::NotFound);
                            }
                            ErrorMatch::Unknown(code) => {
                                panic!("unknown error code {}", code)
                            }
                            _ => unreachable!()
                        }
                    }
                }
                mainloop.quit();
            });
    })
}

#[test]
fn error_match_partial_eq() {
    run_on_mainloop(|mut mainloop| {
        let mut f = File::new_for_path("./does-not-exist");
        f.read_async(0, None,
            move |: obj, res| {
                let f: &mut File = object::cast_mut(obj);
                match f.read_finish(res) {
                    Ok(_)  => { unreachable!() }
                    Err(e) => {
                        assert_eq!(IOErrorEnum::from_error(&e),
                                   ErrorMatch::Known(IOErrorEnum::NotFound));
                    }
                }
                mainloop.quit();
            });
    })
}
<commit_msg>Test a cast trait and the Deref implementation on refcount::Ref<commit_after>// This file is part of Grust, GObject introspection bindings for Rust
//
// Copyright (C) 2013, 2014  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use gio::{File, FileInputStream, IOErrorEnum};
use gio::cast::AsFile;
use grust::refcount::{Ref,SyncRef};
use grust::mainloop::{LoopRunner,MainLoop};
use grust::object;
use grust::error::Match as ErrorMatch;
use std::error::Error;

fn run_on_mainloop(setup: |SyncRef<MainLoop>|) {
    let runner = LoopRunner::new();
    runner.run_after(setup);
}

#[test]
fn as_file() {
    let mut f = File::new_for_path("/dev/null");
    let mut g = f.as_mut_gio_file();
    let path = g.get_path();
    assert_eq!(path.parse_as_utf8().unwrap(), "/dev/null");
}

#[test]
fn deref() {
    let mut f = File::new_for_path("/dev/null");
    let path = f.get_path();
    assert_eq!(path.parse_as_utf8().unwrap(), "/dev/null");
}

#[test]
fn new_ref() {
    let mut f = File::new_for_path("/dev/null");
    let mut g = Ref::new(&mut *f);
    let path = g.get_path();
    assert_eq!(path.parse_as_utf8().unwrap(), "/dev/null");
}

#[test]
fn clone() {
    let rf = File::new_for_path("/dev/null");
    let mut rg = rf.clone();
    let path = rg.get_path();
    assert_eq!(path.parse_as_utf8().unwrap(), "/dev/null");
}

#[test]
#[should_fail]
fn cast_fail() {
    let rf = File::new_for_path("/dev/null");
    let _: &FileInputStream = object::cast(&*rf);
}

#[test]
fn async() {
    run_on_mainloop(|mut mainloop| {
        let mut f = File::new_for_path("/dev/null");
        f.read_async(0, None,
            move |: obj, res| {
                let f: &mut File = object::cast_mut(obj);
                match f.read_finish(res) {
                    Ok(_)  => {}
                    Err(e) => { println!("Error: {}", e.description()) }
                }
                mainloop.quit();
            });
    })
}

#[test]
fn error_to_domain() {
    run_on_mainloop(|mut mainloop| {
        let mut f = File::new_for_path("./does-not-exist");
        f.read_async(0, None,
            move |: obj, res| {
                let f: &mut File = object::cast_mut(obj);
                match f.read_finish(res) {
                    Ok(_)  => { unreachable!() }
                    Err(e) => {
                        match IOErrorEnum::from_error(&e) {
                            ErrorMatch::Known(code) => {
                                assert_eq!(code, IOErrorEnum::NotFound);
                            }
                            ErrorMatch::Unknown(code) => {
                                panic!("unknown error code {}", code)
                            }
                            _ => unreachable!()
                        }
                    }
                }
                mainloop.quit();
            });
    })
}

#[test]
fn error_match_partial_eq() {
    run_on_mainloop(|mut mainloop| {
        let mut f = File::new_for_path("./does-not-exist");
        f.read_async(0, None,
            move |: obj, res| {
                let f: &mut File = object::cast_mut(obj);
                match f.read_finish(res) {
                    Ok(_)  => { unreachable!() }
                    Err(e) => {
                        assert_eq!(IOErrorEnum::from_error(&e),
                                   ErrorMatch::Known(IOErrorEnum::NotFound));
                    }
                }
                mainloop.quit();
            });
    })
}
<|endoftext|>


<commit_before>use std::env;
use std::path::Path;
use argv::*;

fn args(strs: Vec<&str>) -> Vec<String> {
    let mut v = vec!["git-brws".to_string()];
    for s in strs {
        v.push(s.to_string());
    }
    v
}

#[test]
fn no_option() {
    match parse_options(args(vec![])).unwrap() {
        ParsedArgv::Parsed(o, false) => {
            assert!(
                vec![
                    "https://github.com/rhysd/git-brws.git",
                    "ssh://git@github.com:22/rhysd/git-brws.git",
                ]
                .iter()
                .any(|u| o.repo == u.to_string())
            );
            assert_eq!(o.branch, None);
            assert!(o.git_dir.ends_with(".git"));
            assert!(o.args.is_empty());
        },
        _ => assert!(false),
    };

    match parse_options(args(vec!["foo", "bar"])).unwrap() {
        ParsedArgv::Parsed(o, false) => {
            assert_eq!(o.args.len(), 2);
        },
        _ => assert!(false),
    };
}

#[test]
fn with_options() {
    match parse_options(args(vec!["foo", "-u", "-r", "foo/bar", "--dir", ".", "bar", "-b", "dev"])).unwrap() {
        ParsedArgv::Parsed(o, true) => {
            assert_eq!(o.repo, "https://github.com/foo/bar.git");
            assert_eq!(o.branch, Some("dev".to_string()));
            assert!(o.git_dir.ends_with(".git"));
            assert_eq!(o.args.len(), 2);
        },
        _ => assert!(false),
    };
}

#[test]
fn repo_formatting() {
    let p = |r| parse_options(args(vec!["-r", r])).unwrap();
    match p("bitbucket.org/foo/bar") {
        ParsedArgv::Parsed(o, false) => assert_eq!(o.repo, "https://bitbucket.org/foo/bar.git"),
        _ => assert!(false),
    }
    match p("https://gitlab.com/foo/bar") {
        ParsedArgv::Parsed(o, false) => assert_eq!(o.repo, "https://gitlab.com/foo/bar.git"),
        _ => assert!(false),
    }
}

#[test]
fn help_option() {
    match parse_options(args(vec!["-h"])).unwrap() {
        ParsedArgv::Help(s) => {
            assert!(s.starts_with("Usage:"));
        },
        _ => assert!(false),
    }
}

#[test]
fn version_option() {
    match parse_options(args(vec!["-v"])).unwrap() {
        ParsedArgv::Version(s) => {
            assert!(!s.is_empty());
        },
        _ => assert!(false),
    }
}

#[test]
fn unknown_options() {
    assert!(parse_options(args(vec!["--unknown"])).is_err());
}

#[test]
fn detect_git_dir() {
    let mut p = env::current_dir().unwrap();
    p.push(Path::new("src/test/assets/test1/dir1"));
    match parse_options(args(vec!["-d", p.to_str().unwrap()])).unwrap() {
        ParsedArgv::Parsed(o, false) => {
            assert!(o.git_dir.ends_with("src/test/assets/test1/.git"));
        },
        _ => assert!(false),
    }
}
<commit_msg>stop making inner repository for test<commit_after>use std::env;
use std::path::Path;
use argv::*;

fn args(strs: Vec<&str>) -> Vec<String> {
    let mut v = vec!["git-brws".to_string()];
    for s in strs {
        v.push(s.to_string());
    }
    v
}

#[test]
fn no_option() {
    match parse_options(args(vec![])).unwrap() {
        ParsedArgv::Parsed(o, false) => {
            assert!(
                vec![
                    "https://github.com/rhysd/git-brws.git",
                    "ssh://git@github.com:22/rhysd/git-brws.git",
                ]
                .iter()
                .any(|u| o.repo == u.to_string())
            );
            assert_eq!(o.branch, None);
            assert!(o.git_dir.ends_with(".git"));
            assert!(o.args.is_empty());
        },
        _ => assert!(false),
    };

    match parse_options(args(vec!["foo", "bar"])).unwrap() {
        ParsedArgv::Parsed(o, false) => {
            assert_eq!(o.args.len(), 2);
        },
        _ => assert!(false),
    };
}

#[test]
fn with_options() {
    match parse_options(args(vec!["foo", "-u", "-r", "foo/bar", "--dir", ".", "bar", "-b", "dev"])).unwrap() {
        ParsedArgv::Parsed(o, true) => {
            assert_eq!(o.repo, "https://github.com/foo/bar.git");
            assert_eq!(o.branch, Some("dev".to_string()));
            assert!(o.git_dir.ends_with(".git"));
            assert_eq!(o.args.len(), 2);
        },
        _ => assert!(false),
    };
}

#[test]
fn repo_formatting() {
    let p = |r| parse_options(args(vec!["-r", r])).unwrap();
    match p("bitbucket.org/foo/bar") {
        ParsedArgv::Parsed(o, false) => assert_eq!(o.repo, "https://bitbucket.org/foo/bar.git"),
        _ => assert!(false),
    }
    match p("https://gitlab.com/foo/bar") {
        ParsedArgv::Parsed(o, false) => assert_eq!(o.repo, "https://gitlab.com/foo/bar.git"),
        _ => assert!(false),
    }
}

#[test]
fn help_option() {
    match parse_options(args(vec!["-h"])).unwrap() {
        ParsedArgv::Help(s) => {
            assert!(s.starts_with("Usage:"));
        },
        _ => assert!(false),
    }
}

#[test]
fn version_option() {
    match parse_options(args(vec!["-v"])).unwrap() {
        ParsedArgv::Version(s) => {
            assert!(!s.is_empty());
        },
        _ => assert!(false),
    }
}

#[test]
fn unknown_options() {
    assert!(parse_options(args(vec!["--unknown"])).is_err());
}

#[test]
fn detect_git_dir() {
    let current = env::current_dir().unwrap();
    let mut p = current.clone();
    p.push(Path::new("src/test/"));
    match parse_options(args(vec!["-d", p.to_str().unwrap()])).unwrap() {
        ParsedArgv::Parsed(o, false) => {
            let mut expected = current.clone();
            expected.push(".git");
            assert_eq!(o.git_dir, expected);
        },
        _ => assert!(false),
    }
}
<|endoftext|>


<commit_before>use event::{Event, Entity, State, SassRule, SassVariable, TopLevelEvent};
use std::borrow::Cow::Borrowed;

fn is_ascii_whitespace(c: u8) -> bool {
   is_newline(c) || is_ascii_whitespace_no_nl(c)
}

fn is_ascii_whitespace_no_nl(c: u8) -> bool {
    c == b'\t' || c == 0x0b || c == 0x0c || c == b' '
}

fn is_newline(c: u8) -> bool {
    c == b'\n' || c == b'\r'
}

fn isnt_newline(c: u8) -> bool {
    !is_newline(c)
}

// unusual among "scan" functions in that it scans from the _back_ of the string
// TODO: should also scan unicode whitespace?
fn scan_trailing_whitespace(data: &str) -> usize {
    match data.as_bytes().iter().rev().position(|&c| !is_ascii_whitespace_no_nl(c)) {
        Some(i) => i,
        None => data.len()
    }
}

#[derive(Debug)]
pub struct Tokenizer<'a> {
    sass: &'a str,
    bytes: &'a [u8],
    offset: usize,
    stack: Vec<Entity>,
    state: State,
    sass_rule_stack: Vec<SassRule<'a>>,
    current_sass_rule: SassRule<'a>,
}

impl<'a> Tokenizer<'a> {
    pub fn new(sass: &'a str) -> Tokenizer<'a> {
        Tokenizer {
            sass: &sass,
            bytes: &sass.as_bytes(),
            offset: 0,
            stack: Vec::new(),
            state: State::OutsideRules,
            sass_rule_stack: Vec::new(),
            current_sass_rule: SassRule::new(),
        }
    }

    fn start_something(&mut self) -> Option<TopLevelEvent<'a>> {
        self.current_sass_rule = SassRule::new();

        self.pick_something();
        while self.state != State::OutsideRules {
            let curr_state = self.state.clone();
            match curr_state {
                State::Eof => {
                    return None
                },
                State::InSelectors => {
                    let next_sel = self.next_selector();
                    println!("next sel = {:?}", next_sel);
                    if next_sel.is_some() {
                        self.current_sass_rule.selectors.push(next_sel.unwrap());
                    }
                },
                State::InProperties => {
                    let next_prop = self.next_property();
                    if next_prop.is_some() {
                        self.current_sass_rule.children.push(next_prop.unwrap());
                    }
                },
                State::EndRule => {
                    println!("in endrule");
                    // consume '}', should probably be checking that's what we actually have
                    self.offset += 1;

                    match self.sass_rule_stack.pop() {
                        Some(ref mut rule) => {
                            rule.children.push(Event::ChildRule(self.current_sass_rule.clone()));
                            self.current_sass_rule = (*rule).clone();
                            self.pick_something();
                        },
                        None => self.state = State::OutsideRules,
                    }
                },
                State::InRule => {
                    self.sass_rule_stack.push(self.current_sass_rule.clone());
                    self.current_sass_rule = SassRule::new();
                    self.pick_something();
                },
                State::InVariable => {
                    let next_var = self.next_variable();
                    if next_var.is_some() {
                        return Some(TopLevelEvent::Variable(SassVariable { variable: next_var.unwrap() }))
                    } else {
                       self.state = State::Eof;
                    }
                },
                unknown_state => {
                    println!("i dont know what to do for {:?}", unknown_state);
                    println!("current sass rule = {:?}", self.current_sass_rule);
                    self.state = State::Eof;
                },
            }
        }
        //
        //     State::OutsideRules => {
        //         println!("picked {:?}", self.state);
        //         return self.start_something();
        //     },
        //     State::InRule => return Some(self.next_rule()),
        //     State::InProperties => return Some(self.next_property()),
        //     State::InVariable => return Some(self.next_variable()),
        //     State::InComment => return Some(self.next_comment()),
        //     State::Eof => return None,
        // }

        Some(TopLevelEvent::Rule(self.current_sass_rule.clone()))
    }

    fn pick_something(&mut self) {
        self.skip_leading_whitespace();

        if self.offset == self.sass.len() {
            self.state = State::Eof;
            println!("picked eof");
            return
        }

        let c = self.bytes[self.offset];

        if c == b'}' {
            self.state = State::EndRule;
            println!("picked endrule");
            return
        }

        if c == b'$' {
            self.state = State::InVariable;
            println!("picked variable");
            return
        }

        if c == b'/' && (self.offset + 1) < self.sass.len() {
            let d = self.bytes[self.offset + 1];
            if d == b'*' {
                self.state = State::InComment;
                println!("picked comment");
                return
            }
        }

        println!("picked selectors");

        self.state = State::InSelectors;
    }

    fn scan_while<F>(&mut self, data: &str, f: F) -> usize
            where F: Fn(u8) -> bool {
        match data.as_bytes().iter().position(|&c| !f(c)) {
            Some(i) => i,
            None => data.len()
        }
    }

    fn skip_leading_whitespace(&mut self) {
        let mut i = self.offset;
        let limit = self.sass.len();

        while i < limit {
            let c = self.bytes[i];
            if is_ascii_whitespace(c) {
                i += self.scan_while(&self.sass[i..self.sass.len()], is_ascii_whitespace);
            } else if c == b'/' && i + 1 < limit && self.bytes[i + 1] == b'/' {
                i += self.scan_while(&self.sass[i..self.sass.len()], isnt_newline);
            } else {
                self.offset = i;
                return
            }
        }
        self.offset = limit;
    }

    // fn next_comment(&mut self) -> Event<'a> {
    //     let comment_body_beginning = self.offset;
    //     let mut i = comment_body_beginning + 2;
    //     let limit = self.sass.len();
    //
    //     while i < limit {
    //         match self.bytes[i..limit].iter().position(|&c| c == b'*' ) {
    //             Some(pos) => { i += pos; },
    //             None => { break; },
    //         }
    //
    //         if self.bytes[i+1] == b'/' {
    //             self.offset = i + 2;
    //             return Event::Comment(Borrowed(&self.sass[comment_body_beginning..i + 2]))
    //         } else {
    //             i += 1;
    //         }
    //     }
    //     unreachable!()
    // }

    fn next_variable(&mut self) -> Option<Event<'a>> {
        // TODO: can parts of this be deduplicated with properties?
        let name_beginning = self.offset;
        let mut i = name_beginning;
        let limit = self.sass.len();

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b':' ) {
                Some(pos) => { i += pos; },
                None => { break; },
            }

            let name_end = i;

            i += 1;
            self.offset = i;
            self.skip_leading_whitespace();

            let value_beginning = self.offset;
            i = value_beginning;

            while i < limit {
                match self.bytes[i..limit].iter().position(|&c| c == b';') {
                    Some(pos) => { i += pos; },
                    None => { i = limit; break; },
                }

                let value_end = i;
                self.offset = i + 1;

                self.skip_leading_whitespace();

                return Some(Event::Variable(
                    Borrowed(&self.sass[name_beginning..name_end]),
                    Borrowed(&self.sass[value_beginning..value_end])
                ))
            }
        }
        self.offset = self.sass.len();
        None
    }

    fn next_property(&mut self) -> Option<Event<'a>> {
        self.skip_leading_whitespace();

        let name_beginning = self.offset;
        let mut i = name_beginning;
        let limit = self.sass.len();

        let c = self.bytes[i];
        if c == b'}' {
            self.state = State::EndRule;
            return None
        }

        let d = self.bytes[i + 1];
        if c == b'/' && d == b'*' {
            self.state = State::InComment;
            return None
        }

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b':' || c == b'{') {
                Some(pos) => { i += pos; },
                None => { break; },
            }

            // Inefficient since we already skipped the whitespace and we'll have to
            // do it again but oh well
            let c = self.bytes[i];
            if c == b'{' {
                self.state = State::InRule;
                return None
            }

            let name_end = i;

            i += 1;
            self.offset = i;
            self.skip_leading_whitespace();

            let value_beginning = self.offset;
            i = value_beginning;

            while i < limit {
                match self.bytes[i..limit].iter().position(|&c| c == b';') {
                    Some(pos) => { i += pos; },
                    None => { i = limit; break; },
                }

                let value_end = i;
                self.offset = i + 1;

                self.skip_leading_whitespace();

                return Some(Event::Property(
                    Borrowed(&self.sass[name_beginning..name_end]),
                    Borrowed(&self.sass[value_beginning..value_end])
                ))

            }
        }
        self.offset = self.sass.len();
        None
    }

    fn next_selector(&mut self) -> Option<Event<'a>> {
        let beginning = self.offset;
        let mut i = beginning;
        let limit = self.sass.len();

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b',' || c == b'{' || c == b':') {
                Some(pos) => { i += pos; },
                None => { i = limit; break; },
            }

            let c = self.bytes[i];

            if c == b':' {
                self.state = State::InProperties;
                return None
            }

            if c == b',' || c == b'{' {
                let n = scan_trailing_whitespace(&self.sass[beginning..i]);
                let end = i - n;
                if end > beginning {
                    if c == b'{' {
                        self.state = State::InProperties;
                    }
                    self.offset = i + 1;
                    return Some(Event::Selector(Borrowed(&self.sass[beginning..end])))
                }
            }

            self.offset = i;
            if i > beginning {
                return Some(Event::Selector(Borrowed(&self.sass[beginning..i])))
            }
            i += 1;
        }

        if i > beginning {
            self.offset = i;
            Some(Event::Selector(Borrowed(&self.sass[beginning..i])))
        } else {
            self.state = State::Eof;
            None
        }
    }
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = TopLevelEvent<'a>;

    fn next(&mut self) -> Option<TopLevelEvent<'a>> {
        if self.offset < self.sass.len() {
            return self.start_something()
        }
        None
    }
}
<commit_msg>Rename some variables to be less confusing<commit_after>use event::{Event, Entity, State, SassRule, SassVariable, TopLevelEvent};
use std::borrow::Cow::Borrowed;

fn is_ascii_whitespace(c: u8) -> bool {
   is_newline(c) || is_ascii_whitespace_no_nl(c)
}

fn is_ascii_whitespace_no_nl(c: u8) -> bool {
    c == b'\t' || c == 0x0b || c == 0x0c || c == b' '
}

fn is_newline(c: u8) -> bool {
    c == b'\n' || c == b'\r'
}

fn isnt_newline(c: u8) -> bool {
    !is_newline(c)
}

// unusual among "scan" functions in that it scans from the _back_ of the string
// TODO: should also scan unicode whitespace?
fn scan_trailing_whitespace(data: &str) -> usize {
    match data.as_bytes().iter().rev().position(|&c| !is_ascii_whitespace_no_nl(c)) {
        Some(i) => i,
        None => data.len()
    }
}

#[derive(Debug)]
pub struct Tokenizer<'a> {
    sass: &'a str,
    bytes: &'a [u8],
    offset: usize,
    stack: Vec<Entity>,
    state: State,
    sass_rule_stack: Vec<SassRule<'a>>,
    current_sass_rule: SassRule<'a>,
}

impl<'a> Tokenizer<'a> {
    pub fn new(sass: &'a str) -> Tokenizer<'a> {
        Tokenizer {
            sass: &sass,
            bytes: &sass.as_bytes(),
            offset: 0,
            stack: Vec::new(),
            state: State::OutsideRules,
            sass_rule_stack: Vec::new(),
            current_sass_rule: SassRule::new(),
        }
    }

    fn start_something(&mut self) -> Option<TopLevelEvent<'a>> {
        self.current_sass_rule = SassRule::new();

        self.pick_something();
        while self.state != State::OutsideRules {
            let curr_state = self.state.clone();
            match curr_state {
                State::Eof => {
                    return None
                },
                State::InSelectors => {
                    let sel = self.next_selector();
                    println!("next sel = {:?}", sel);
                    if sel.is_some() {
                        self.current_sass_rule.selectors.push(sel.unwrap());
                    }
                },
                State::InProperties => {
                    let prop = self.next_property();
                    if prop.is_some() {
                        self.current_sass_rule.children.push(prop.unwrap());
                    }
                },
                State::EndRule => {
                    println!("in endrule");
                    // consume '}', should probably be checking that's what we actually have
                    self.offset += 1;

                    match self.sass_rule_stack.pop() {
                        Some(ref mut rule) => {
                            rule.children.push(Event::ChildRule(self.current_sass_rule.clone()));
                            self.current_sass_rule = (*rule).clone();
                            self.pick_something();
                        },
                        None => self.state = State::OutsideRules,
                    }
                },
                State::InRule => {
                    self.sass_rule_stack.push(self.current_sass_rule.clone());
                    self.current_sass_rule = SassRule::new();
                    self.pick_something();
                },
                State::InVariable => {
                    let var = self.next_variable();
                    if var.is_some() {
                        return Some(TopLevelEvent::Variable(SassVariable { variable: var.unwrap() }))
                    } else {
                        // is this really what we should be doing here? reachable?
                        self.state = State::Eof;
                    }
                },
                // State::InComment => {
                //     let comment
                // }
                unknown_state => {
                    println!("i dont know what to do for {:?}", unknown_state);
                    println!("current sass rule = {:?}", self.current_sass_rule);
                    self.state = State::Eof;
                },
            }
        }
        //
        //     State::OutsideRules => {
        //         println!("picked {:?}", self.state);
        //         return self.start_something();
        //     },
        //     State::InRule => return Some(self.next_rule()),
        //     State::InProperties => return Some(self.next_property()),
        //     State::InVariable => return Some(self.next_variable()),
        //     State::InComment => return Some(self.next_comment()),
        //     State::Eof => return None,
        // }

        Some(TopLevelEvent::Rule(self.current_sass_rule.clone()))
    }

    fn pick_something(&mut self) {
        self.skip_leading_whitespace();

        if self.offset == self.sass.len() {
            self.state = State::Eof;
            println!("picked eof");
            return
        }

        let c = self.bytes[self.offset];

        if c == b'}' {
            self.state = State::EndRule;
            println!("picked endrule");
            return
        }

        if c == b'$' {
            self.state = State::InVariable;
            println!("picked variable");
            return
        }

        if c == b'/' && (self.offset + 1) < self.sass.len() {
            let d = self.bytes[self.offset + 1];
            if d == b'*' {
                self.state = State::InComment;
                println!("picked comment");
                return
            }
        }

        println!("picked selectors");

        self.state = State::InSelectors;
    }

    fn scan_while<F>(&mut self, data: &str, f: F) -> usize
            where F: Fn(u8) -> bool {
        match data.as_bytes().iter().position(|&c| !f(c)) {
            Some(i) => i,
            None => data.len()
        }
    }

    fn skip_leading_whitespace(&mut self) {
        let mut i = self.offset;
        let limit = self.sass.len();

        while i < limit {
            let c = self.bytes[i];
            if is_ascii_whitespace(c) {
                i += self.scan_while(&self.sass[i..self.sass.len()], is_ascii_whitespace);
            } else if c == b'/' && i + 1 < limit && self.bytes[i + 1] == b'/' {
                i += self.scan_while(&self.sass[i..self.sass.len()], isnt_newline);
            } else {
                self.offset = i;
                return
            }
        }
        self.offset = limit;
    }

    // fn next_comment(&mut self) -> Event<'a> {
    //     let comment_body_beginning = self.offset;
    //     let mut i = comment_body_beginning + 2;
    //     let limit = self.sass.len();
    //
    //     while i < limit {
    //         match self.bytes[i..limit].iter().position(|&c| c == b'*' ) {
    //             Some(pos) => { i += pos; },
    //             None => { break; },
    //         }
    //
    //         if self.bytes[i+1] == b'/' {
    //             self.offset = i + 2;
    //             return Event::Comment(Borrowed(&self.sass[comment_body_beginning..i + 2]))
    //         } else {
    //             i += 1;
    //         }
    //     }
    //     unreachable!()
    // }

    fn next_variable(&mut self) -> Option<Event<'a>> {
        // TODO: can parts of this be deduplicated with properties?
        let name_beginning = self.offset;
        let mut i = name_beginning;
        let limit = self.sass.len();

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b':' ) {
                Some(pos) => { i += pos; },
                None => { break; },
            }

            let name_end = i;

            i += 1;
            self.offset = i;
            self.skip_leading_whitespace();

            let value_beginning = self.offset;
            i = value_beginning;

            while i < limit {
                match self.bytes[i..limit].iter().position(|&c| c == b';') {
                    Some(pos) => { i += pos; },
                    None => { i = limit; break; },
                }

                let value_end = i;
                self.offset = i + 1;

                self.skip_leading_whitespace();

                return Some(Event::Variable(
                    Borrowed(&self.sass[name_beginning..name_end]),
                    Borrowed(&self.sass[value_beginning..value_end])
                ))
            }
        }
        self.offset = self.sass.len();
        None
    }

    fn next_property(&mut self) -> Option<Event<'a>> {
        self.skip_leading_whitespace();

        let name_beginning = self.offset;
        let mut i = name_beginning;
        let limit = self.sass.len();

        let c = self.bytes[i];
        if c == b'}' {
            self.state = State::EndRule;
            return None
        }

        let d = self.bytes[i + 1];
        if c == b'/' && d == b'*' {
            self.state = State::InComment;
            return None
        }

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b':' || c == b'{') {
                Some(pos) => { i += pos; },
                None => { break; },
            }

            // Inefficient since we already skipped the whitespace and we'll have to
            // do it again but oh well
            let c = self.bytes[i];
            if c == b'{' {
                self.state = State::InRule;
                return None
            }

            let name_end = i;

            i += 1;
            self.offset = i;
            self.skip_leading_whitespace();

            let value_beginning = self.offset;
            i = value_beginning;

            while i < limit {
                match self.bytes[i..limit].iter().position(|&c| c == b';') {
                    Some(pos) => { i += pos; },
                    None => { i = limit; break; },
                }

                let value_end = i;
                self.offset = i + 1;

                self.skip_leading_whitespace();

                return Some(Event::Property(
                    Borrowed(&self.sass[name_beginning..name_end]),
                    Borrowed(&self.sass[value_beginning..value_end])
                ))

            }
        }
        self.offset = self.sass.len();
        None
    }

    fn next_selector(&mut self) -> Option<Event<'a>> {
        let beginning = self.offset;
        let mut i = beginning;
        let limit = self.sass.len();

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b',' || c == b'{' || c == b':') {
                Some(pos) => { i += pos; },
                None => { i = limit; break; },
            }

            let c = self.bytes[i];

            if c == b':' {
                self.state = State::InProperties;
                return None
            }

            if c == b',' || c == b'{' {
                let n = scan_trailing_whitespace(&self.sass[beginning..i]);
                let end = i - n;
                if end > beginning {
                    if c == b'{' {
                        self.state = State::InProperties;
                    }
                    self.offset = i + 1;
                    return Some(Event::Selector(Borrowed(&self.sass[beginning..end])))
                }
            }

            self.offset = i;
            if i > beginning {
                return Some(Event::Selector(Borrowed(&self.sass[beginning..i])))
            }
            i += 1;
        }

        if i > beginning {
            self.offset = i;
            Some(Event::Selector(Borrowed(&self.sass[beginning..i])))
        } else {
            self.state = State::Eof;
            None
        }
    }
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = TopLevelEvent<'a>;

    fn next(&mut self) -> Option<TopLevelEvent<'a>> {
        if self.offset < self.sass.len() {
            return self.start_something()
        }
        None
    }
}
<|endoftext|>