UP | HOME

Funções primitivas

Table of Contents

Arquivo: axioms/primitives.rs.

1. Importações

//use std::fs::File;
use gc::Gc;
use crate::core::{
    Maj,
    MajState
};
use super::predicates::{
    maj_eq,
    maj_nilp,
    maj_consp,
    maj_errorp,
    maj_stringp,
    maj_numberp,
    maj_vectorp,
    maj_proper_list_p
};
use crate::{ maj_list, maj_destructure_args };
use super::MajRawSym;
use num_traits::FromPrimitive;
use crate::axioms::utils::{ simplify_frac, simplify_frac_coerce };
use crate::core::types::MajVectorType;

2. (cons x y)

pub fn maj_cons(x: Gc<Maj>, y: Gc<Maj>) -> Gc<Maj> {
    if maj_errorp(x.clone()).to_bool() {
        x
    } else if maj_errorp(y.clone()).to_bool() {
        y
    } else {
        Maj::cons(x, y)
    }
}

3. (car x)

pub fn maj_car(x: Gc<Maj>) -> Gc<Maj> {
    match &*x.clone() {
        Maj::Sym(_) => {
            if maj_nilp(x.clone()).to_bool() {
                return Maj::nil();
            }
        },
        Maj::Cons { car, cdr: _ } => {
            return car.clone();
        },
        _ => {}
    }
    maj_err(Maj::string("{} is not a cons cell"),
            maj_list!(x))
}

4. (cdr x)

pub fn maj_cdr(x: Gc<Maj>) -> Gc<Maj> {
    match &*x.clone() {
        Maj::Sym(_) => {
            if maj_nilp(x.clone()).to_bool() {
                return Maj::nil();
            }
        },
        Maj::Cons { car: _, cdr } => {
            return cdr.clone();
        },
        _ => {}
    }
    maj_err(Maj::string("{} is not a cons cell"),
            maj_list!(x))
}

5. (copy x)

pub fn maj_copy(x: Gc<Maj>) -> Gc<Maj> {
    match &*x.clone() {
        Maj::Cons { car, cdr } => {
            Maj::cons(car.clone(), cdr.clone())
        }
        _ => maj_err(
            Maj::string("{} is not a cons cell"),
            maj_list!(x))
    }
}

6. (length x)

pub fn maj_length(x: Gc<Maj>) -> Gc<Maj> {
    if maj_nilp(x.clone()).to_bool() {
        Maj::integer(0)
    } else if !maj_consp(x.clone()).to_bool() {
        maj_err(
            Maj::string("{} is not a proper list"),
            maj_list!(x.clone()))
    } else {
        let mut itr    = x.clone();
        let mut length = 0;
        while maj_consp(itr.clone()).to_bool() {
            length += 1;
            itr = maj_cdr(itr);
        }
        Maj::integer(length)
    }
}

7. (depth x)

fn maj_depth_helper(x: Gc<Maj>) -> i64 {
    use std::cmp;
    if !maj_consp(x.clone()).to_bool() {
        0
    } else {
        1 + cmp::max(maj_depth_helper(maj_car(x.clone())),
                     maj_depth_helper(maj_cdr(x)))
    }
}
pub fn maj_depth(x: Gc<Maj>) -> Gc<Maj> {
    use crate::axioms::predicates::maj_atomp;
    if maj_nilp(x.clone()).to_bool() {
        Maj::integer(0)
    } else if maj_atomp(x.clone()).to_bool() {
        maj_err(Maj::string(
            "{} is an atom"), maj_list!(x))
    } else {
        Maj::integer(maj_depth_helper(x))
    }
}

8. (type x)

pub fn maj_type(mut state: &mut MajState, x: Gc<Maj>) -> Gc<Maj> {
    use crate::core::types::MajNumber;
    Maj::symbol(
        &mut state,
        match &*x {
            Maj::Sym(_)              =>"symbol",
            Maj::Cons {
                car: _, cdr: _
            }                        => "cons",
            Maj::Stream(_)           => "stream",
            Maj::Char(_)             => "char",
            Maj::Number(num) => {
                match num.clone() {
                    MajNumber::Integer(_)     => "integer",
                    MajNumber::Float(_)       => "float",
                    MajNumber::Fraction(_, _) => "fraction",
                    MajNumber::Complex {
                        real: _, imag: _
                    }                         => "complex",
                }
            },
            Maj::Vector(_)            => "vector",
        })
}

9. (intern x)

pub fn maj_intern(mut state: &mut MajState, x: Gc<Maj>) -> Gc<Maj> {
    match x.clone().stringify() {
        Some(string) => {
            if string == "" {
                Maj::nil()
            } else {
                Maj::symbol(&mut state, &string)
            }
        },
        None => {
            maj_err(Maj::string("{} is not a string"),
                    maj_list!(x))
        },
    }
}

10. (name x)

pub fn maj_name(state: &MajState, x: Gc<Maj>) -> Gc<Maj> {
    use crate::printing::maj_format;
    if let Maj::Sym(_) = &*x.clone() {
        Maj::string(&maj_format(&state, x))
    } else {
        maj_err(Maj::string("{} is not a symbol"),
                maj_list!(x.clone()))
    }
}

11. (get-environment type)

pub fn maj_get_environment(
    mut state: &mut MajState,
    type_sym: Gc<Maj>,
    env: Gc<Maj>
) -> Gc<Maj> {
    if maj_eq(type_sym.clone(),
              Maj::symbol(&mut state, "lexical"))
        .to_bool()
    {
        env
    } else if maj_eq(type_sym.clone(),
                     Maj::symbol(&mut state, "global"))
        .to_bool()
    {
        state.get_global_env()
    } else {
        maj_err(
            Maj::string("Unknown environment type {}"),
            maj_list!(type_sym))
    }
}

12. (coin)

pub fn maj_coin() -> Gc<Maj> {
    use rand::random;

    if random() {
        Maj::t()
    } else {
        Maj::nil()
    }
}

13. (sys com . args)

pub fn maj_sys(com: Gc<Maj>, args: Gc<Maj>) -> Gc<Maj> {
    use std::process::Command;

    if !maj_stringp(com.clone()).to_bool() {
        return maj_err(
            Maj::string("{} is not a string"),
            maj_list!(com));
    }

    let mut comm = Command::new(com.stringify().unwrap());

    let mut itr = args.clone();
    while !maj_nilp(itr.clone()).to_bool() {
        if let Maj::Cons { car, cdr } = &*itr.clone() {
            if !maj_stringp(car.clone()).to_bool() {
                return maj_err(
                    Maj::string("{} is not a string"),
                    maj_list!(car.clone()));
            } else {
                let _ = comm.arg(car.clone()
                                 .stringify()
                                 .unwrap());
                itr = cdr.clone();
            }
        } else {};
    }

    let result = match comm.status() {
        Ok(res) => res.code(),
        Err(_)  => None,
    };

    match result {
        Some(code) => Maj::integer(code as i64),
        None => maj_err(
            Maj::string("Error executing command {} {}"),
            maj_list!(com, args)),
    }
}

14. (format fmt . rest)

pub fn maj_format_prim(
    state: &MajState,
    fmt: Gc<Maj>,
    rest: Gc<Maj>
) -> Gc<Maj> {
    if let Some(rfmt) = fmt.stringify() {
        let mut buffer = String::new();
        let mut expect_closebracket = false;
        let mut gulp_next = false;
        let mut curr_arg = rest.clone();
        for c in rfmt.chars() {
            if gulp_next {
                buffer.push(c);
                gulp_next = false;
            } else if expect_closebracket {
                if c == '}' {
                    use crate::printing::maj_format;
                    expect_closebracket = false;
                    if maj_nilp(curr_arg.clone()).to_bool() {
                        return maj_err(
                            Maj::string("Missing arguments on format"),
                            Maj::nil());
                    }
                    let cafirst = maj_car(curr_arg.clone());
                    let mut formatted =
                        if let Maj::Char(c) = &*cafirst.clone() {
                            String::from(format!("{}", c))
                        } else {
                            maj_format(&state, cafirst)
                        };
                    let len = formatted.len();
                    if (len >= 2)
                        && (formatted.chars().nth(0).unwrap() == '"')
                        && (formatted.chars().last().unwrap() == '"') {
                            formatted =
                                String::from(&formatted[1..(len-1)]);
                        }
                    buffer.push_str(formatted.as_str());
                    curr_arg = maj_cdr(curr_arg);
                }
            } else {
                match c {
                    '{' => expect_closebracket = true,
                    '\\' => gulp_next = true,
                    '}' => {
                        return maj_err(
                            Maj::string(
                                "Unmatched closing curly brace in {}"),
                            maj_list!(fmt));
                    },
                    _ => buffer.push(c),
                }
            }
        }
        if expect_closebracket {
            return maj_err(
                Maj::string("Unmatched opening curly brace in {}"),
                maj_list!(fmt));
        }
        Maj::string(buffer.as_ref())
    } else {
        maj_err(Maj::string("{} is not a string"),
                maj_list!(fmt))
    }
}

15. (err fmt . rest)

pub fn maj_err(fmt: Gc<Maj>, rest: Gc<Maj>) -> Gc<Maj> {
    use crate::maj_dotted_list;

    if !maj_stringp(fmt.clone()).to_bool() {
        panic!("Cannot throw error: {} is not a string", fmt);
    } else if !maj_proper_list_p(rest.clone()).to_bool() {
        panic!("Cannot throw error: {} is not a proper list", rest);
    }

    maj_dotted_list!(Maj::lit(), Maj::error(), fmt, rest)
}

16. (warn fmt . rest)

pub fn maj_warn(mut state: &mut MajState, fmt: Gc<Maj>,
                rest: Gc<Maj>, env: Gc<Maj>
) -> Gc<Maj> {
    let format = maj_format_prim(&state, fmt, rest);
    if maj_errorp(format.clone()).to_bool() {
        format
    } else {
        let stderr = Maj::symbol(&mut state, "*stderr*");
        let stderr = state.lookup(env, stderr);
        if maj_errorp(stderr.clone()).to_bool() {
            stderr
        } else {
            maj_write_string(
                &mut state, format, stderr.clone());
            maj_write_char(
                &mut state, Maj::character('\n'), stderr);
            Maj::nil()
        }
    }
}

17. (list . rest)

#[inline]
pub fn maj_list(rest: Gc<Maj>) -> Gc<Maj> {
    rest
}

18. (append . rest)

pub fn maj_append(rest: Gc<Maj>) -> Gc<Maj> {
    use crate::maj_dotted_list;
    use crate::axioms::predicates::maj_atomp;
    let car  = maj_car(rest.clone());
    let cdr  = maj_cdr(rest.clone());
    let cadr = maj_car(cdr.clone());
    let cddr = maj_cdr(cdr.clone());

    if maj_nilp(cdr.clone()).to_bool() {
        return car;
    }

    let mut itr = car.clone();
    let mut v = Vec::new();
    while !maj_nilp(itr.clone()).to_bool() {
        let cdar = maj_cdr(itr.clone());
        if maj_atomp(cdar.clone()).to_bool()
            && !maj_nilp(cdar.clone()).to_bool() {
                return maj_err(
                    Maj::string("Cannot append to dotted list"),
                    Maj::nil());
            }
        v.push(maj_car(itr.clone()));
        itr = cdar;
    }
    itr = cadr.clone();
    for obj in v.iter().rev() {
        itr = Maj::cons(obj.clone(), itr.clone());
    }
    maj_append(maj_dotted_list!(itr.clone(), cddr))
}

19. (last x)

pub fn maj_last(x: Gc<Maj>) -> Gc<Maj> {
    if !maj_consp(x.clone()).to_bool() {
        return maj_err(
            Maj::string("{} is not a cons"),
            maj_list!(x));
    }

    let mut itr = x;
    loop {
        let cdr = maj_cdr(itr.clone());
        if !maj_consp(cdr.clone()).to_bool() {
            return itr;
        }
        itr = cdr;
    }
}

20. (reverse x)

pub fn maj_reverse(x: Gc<Maj>) -> Gc<Maj> {
    use crate::axioms::predicates::maj_atomp;
    if !maj_consp(x.clone()).to_bool() {
        return maj_err(
            Maj::string("{} is not a cons"),
            maj_list!(x));
    }

    let mut itr = x.clone();
    let mut newlist = Maj::nil();
    while !maj_nilp(itr.clone()).to_bool() {
        newlist = Maj::cons(maj_car(itr.clone()), newlist);
        itr = maj_cdr(itr);
        let is_atom = maj_atomp(itr.clone()).to_bool();
        if is_atom && !maj_nilp(itr.clone()).to_bool() {
            return maj_err(
                Maj::string("Not a proper list: {}"),
                maj_list!(x));
        }
    }
    newlist
}

21. (nthcdr n lst)

pub fn maj_nthcdr(n: Gc<Maj>, lst: Gc<Maj>) -> Gc<Maj> {
    use crate::axioms::predicates::maj_integerp;
    if maj_nilp(lst.clone()).to_bool() {
        return Maj::nil();
    }

    if !maj_integerp(n.clone()).to_bool() {
        return maj_err(Maj::string("{} is not an integer"),
                       maj_list!(n))
    }

    let mut num = n.to_integer().unwrap();
    if num < 0 {
        return maj_err(Maj::string("{} is not a valid index"),
                       maj_list!(n));
    }

    let mut iter = lst.clone();
    loop {
        if !maj_consp(iter.clone()).to_bool() &&
            !maj_nilp(iter.clone()).to_bool()
        {
            return maj_err(Maj::string("{} is not a list"),
                           maj_list!(iter));
        }
        if num <= 0 { break; }
        num -= 1;
        iter = maj_cdr(iter);
    }
    iter
}

22. (nth n lst)

pub fn maj_nth(n: Gc<Maj>, lst: Gc<Maj>) -> Gc<Maj> {
    let cons = maj_nthcdr(n, lst);
    if maj_errorp(cons.clone()).to_bool() {
        cons
    } else {
        maj_car(cons)
    }
}

23. (macroexpand-1 x)

pub fn maj_macroexpand_1(mut state: &mut MajState,
                         expr: Gc<Maj>,
                         env: Gc<Maj>) -> (Gc<Maj>, bool) {
    use crate::evaluator::application::expand_macro;

    if !maj_consp(expr.clone()).to_bool() {
        return (expr, true);
    }

    let mac = maj_car(expr.clone());
    let args = maj_cdr(expr.clone());

    let mac = state.lookup(env.clone(), mac);
    expand_macro(&mut state, mac, args, env)
}

24. TODO (macroexpand x)

25. (not x)

#[inline]
pub fn maj_not(x: Gc<Maj>) -> Gc<Maj> {
    maj_nilp(x)
}

26. (gensym)

#[inline]
pub fn maj_gensym(mut state: &mut MajState) -> Gc<Maj> {
    Maj::gensym(&mut state)
}

27. Funções numéricas

27.1. (number-coerce x subtype)

pub fn maj_number_coerce(
    mut state: &mut MajState,
    subtype: Gc<Maj>,
    number: Gc<Maj>
) -> Gc<Maj> {
    use crate::printing::maj_format;
    use crate::axioms::predicates::maj_symbolp;

    if !maj_numberp(number.clone()).to_bool() {
        return maj_err(Maj::string("{} is not a number"),
                       maj_list!(number));
    }

    if !maj_symbolp(subtype.clone()).to_bool() {
        return maj_err(Maj::string("{} is not a symbol"),
                       maj_list!(subtype));
    }


    let number_type = maj_type(&mut state, number.clone());
    let number_type = number_type.to_raw_sym().unwrap();
    let number_type = FromPrimitive::from_u64(number_type)
        .unwrap();

    let coerce_type = subtype.to_raw_sym().unwrap();
    let coerce_type = FromPrimitive::from_u64(coerce_type);

    match number_type {
        MajRawSym::Integer => match coerce_type {
            Some(MajRawSym::Integer) => number,
            Some(MajRawSym::Float) => {
                Maj::float(number.to_forced_float().unwrap())
            },
            Some(MajRawSym::Fraction) => {
                Maj::fraction(number.to_integer().unwrap(), 1)
            },
            Some(MajRawSym::Complex) => {
                Maj::complex(number.clone(), Maj::float(0.0))
            },
            _ => maj_err(
                Maj::string("{} is not a number subtype"),
                maj_list!(subtype)),
        },
        MajRawSym::Float => match coerce_type {
            Some(MajRawSym::Integer) => {
                Maj::integer(number
                             .to_float()
                             .unwrap()
                             .trunc()
                             as i64)
            },
            Some(MajRawSym::Float) => number,
            Some(MajRawSym::Fraction) => {
                let mut buffer = maj_format(&state, number);
                let dot_index = buffer.find('.').unwrap();
                let denom_pow = (buffer.len() - dot_index - 1) as u32;
                let denom = 10_i64.pow(denom_pow);
                buffer.remove(dot_index);
                let frac =
                    Maj::fraction(buffer.parse().unwrap(), denom);
                simplify_frac(frac).unwrap()
            },
            Some(MajRawSym::Complex) => {
                Maj::complex(number.clone(), Maj::float(0.0))
            },
            _ => maj_err(
                Maj::string("{} is not a number subtype"),
                maj_list!(subtype)),
        },
        MajRawSym::Fraction => match coerce_type {
            Some(MajRawSym::Integer) => {
                Maj::integer(number.to_forced_float()
                             .unwrap()
                             .trunc()
                             as i64)
            },
            Some(MajRawSym::Float) => {
                Maj::float(number.to_forced_float()
                           .unwrap())
            },
            Some(MajRawSym::Fraction) => number,
            Some(MajRawSym::Complex) => {
                Maj::complex(number.clone(), Maj::float(0.0))
            },
            _ => maj_err(
                Maj::string("{} is not a number subtype"),
                maj_list!(subtype)),
        },
        MajRawSym::Complex => match coerce_type {
            Some(MajRawSym::Integer) |
            Some(MajRawSym::Float)   |
            Some(MajRawSym::Fraction) => {
                let realpart = maj_real_part(number);
                maj_number_coerce(&mut state, subtype, realpart)
            },
            Some(MajRawSym::Complex) => number,
            _ => maj_err(
                Maj::string("{} is not a number subtype"),
                maj_list!(subtype)),
        },
        _ => panic!("Symbol is not a number subtype"),
    }
}

27.2. (real-part x)

pub fn maj_real_part(x: Gc<Maj>) -> Gc<Maj> {
    use crate::core::types::MajNumber;
    match &*x.clone() {
        Maj::Number(num) => {
            match &num.clone() {
                MajNumber::Complex {
                    real,
                    imag: _
                } => {
                    let real = &*real.clone();
                    Gc::new(Maj::Number(real.clone()))
                },
                _ => maj_err(Maj::string(
                    "{} is not a complex number"),
                    maj_list!(x)),
            }
        },
        _ => maj_err(Maj::string("{} is not a number"),
                     maj_list!(x)),
    }
}

27.3. (imag-part x)

pub fn maj_imag_part(x: Gc<Maj>) -> Gc<Maj> {
    use crate::core::types::MajNumber;
    match &*x.clone() {
        Maj::Number(num) => {
            match &num.clone() {
                MajNumber::Complex {
                    real: _,
                    imag
                } => {
                    let imag = &*imag.clone();
                    Gc::new(Maj::Number(imag.clone()))
                },
                _ => maj_err(Maj::string(
                    "{} is not a complex number"),
                    maj_list!(x)),
            }
        },
        _ => maj_err(Maj::string("{} is not a number"),
                     maj_list!(x)),
    }
}

27.4. (numer x)

pub fn maj_numer(x: Gc<Maj>) -> Gc<Maj> {
    if !maj_numberp(x.clone()).to_bool() {
        maj_err(Maj::string("{} is not a number"),
                maj_list!(x))
    } else {
        match x.clone().to_fraction() {
            Some((numer, _)) => Maj::integer(numer),
            None => maj_err(Maj::string("{} is not a fraction"),
                            maj_list!(x)),
        }
    }
}

27.5. (denom x)

pub fn maj_denom(x: Gc<Maj>) -> Gc<Maj> {
    if !maj_numberp(x.clone()).to_bool() {
        maj_err(Maj::string("{} is not a number"),
                maj_list!(x))
    } else {
        match x.clone().to_fraction() {
            Some((_, denom)) => Maj::integer(denom),
            None => maj_err(Maj::string("{} is not a fraction"),
                            maj_list!(x)),
        }
    }
}

27.6. (richest-number-type x y)

pub fn maj_richest_number_type(
    mut state: &mut MajState,
    x: Gc<Maj>,
    y: Gc<Maj>
) -> Gc<Maj> {
    if !maj_numberp(x.clone()).to_bool() {
        return maj_err(Maj::string("{} is not a number"),
                       maj_list!(x));
    }
    if !maj_numberp(y.clone()).to_bool() {
        return maj_err(Maj::string("{} is not a number"),
                       maj_list!(y));
    }

    let x_type = maj_type(&mut state, x.clone());
    let x_type_sym = x_type.to_raw_sym().unwrap();
    let x_type_sym = FromPrimitive::from_u64(x_type_sym).unwrap();

    let y_type = maj_type(&mut state, y.clone());
    let y_type_sym = y_type.to_raw_sym().unwrap();
    let y_type_sym = FromPrimitive::from_u64(y_type_sym).unwrap();

    match x_type_sym {
        MajRawSym::Integer => {
            match y_type_sym {
                MajRawSym::Integer  |
                MajRawSym::Float    |
                MajRawSym::Fraction |
                MajRawSym::Complex  => y_type,
                _ => unimplemented!(
                    "Usage of unknown number subtype"),
            }
        },
        MajRawSym::Float => {
            match y_type_sym {
                MajRawSym::Integer  => x_type,
                MajRawSym::Float    |
                MajRawSym::Fraction |
                MajRawSym::Complex  => y_type,
                _ => unimplemented!(
                    "Usage of unknown number subtype"),
            }
        },
        MajRawSym::Fraction => {
            match y_type_sym {
                MajRawSym::Integer |
                MajRawSym::Float   |
                MajRawSym::Fraction => x_type,
                MajRawSym::Complex  => y_type,
                _ => unimplemented!(
                    "Usage of unknown number subtype"),
            }
        },
        MajRawSym::Complex => {
            match y_type_sym {
                MajRawSym::Integer  |
                MajRawSym::Float    |
                MajRawSym::Fraction |
                MajRawSym::Complex  => x_type,
                _ => unimplemented!(
                    "Usage of unknown number subtype"),
            }
        },
        _ => unimplemented!("Usage of unknown number subtype"),
    }
}

27.7. (rich-number-coerce x y)

pub fn maj_rich_number_coerce(
    mut state: &mut MajState,
    x: Gc<Maj>,
    y: Gc<Maj>
) -> Gc<Maj> {
    let best_type = maj_richest_number_type(&mut state,
                                            x.clone(),
                                            y.clone());
    if maj_errorp(best_type.clone()).to_bool() {
        return best_type;
    }
    maj_list!(maj_number_coerce(&mut state,
                                best_type.clone(), x),
              maj_number_coerce(&mut state, best_type, y))
}

27.8. (iota n)

#[inline]
pub fn maj_iota(n: Gc<Maj>) -> Gc<Maj> {
    use crate::axioms::predicates::maj_integerp;
    let common_err = maj_err(
        Maj::string("iota expects a positive integer number"),
        Maj::nil());
    if !maj_integerp(n.clone()).to_bool() {
        common_err
    } else {
        let mut num = n.to_integer().unwrap();
        if num < 0 {
            common_err
        } else {
            let mut list = Maj::nil();
            while num > 0 {
                list = Maj::cons(Maj::integer(num - 1), list);
                num -= 1;
            }
            list
        }
    }
}

28. TODO Funções de comparação

use crate::axioms::predicates::maj_zerop;

28.1. Funções de ajuda e ferramentas

28.1.1. Ajudante de coerções aritméticas

fn maj_arithm_coercion_helper(
    mut state: &mut MajState,
    x: Gc<Maj>,
    y: Gc<Maj>
) -> Result<(Gc<Maj>, Gc<Maj>, MajRawSym), Gc<Maj>> {
    if !maj_numberp(x.clone()).to_bool() {
        return Err(maj_err(
            Maj::string("{} is not a number"),
            maj_list!(x)));
    }
    if !maj_numberp(y.clone()).to_bool() {
        return Err(maj_err(
            Maj::string("{} is not a number"),
            maj_list!(y)));
    }
    // Perform rich coercion
    let coerced = maj_rich_number_coerce(&mut state, x, y);
    if maj_errorp(coerced.clone()).to_bool() {
        return Err(coerced);
    }
    let x = maj_car(coerced.clone());
    let y = maj_car(maj_cdr(coerced));
    let ntype = maj_type(&mut state, x.clone());

    Ok((x, y,
        FromPrimitive::from_u64(ntype.to_raw_sym()
                                .unwrap())
        .unwrap()))
}

28.1.2. Tipos de despacho de aritmética

#[derive(Debug)]
enum MajArithmFnType {
    Equals,
    Greater,
    Lesser,
    Plus,
    Minus,
    Multiply,
    Divide,
}

28.1.3. Ajudante de despacho de aritmética

fn maj_arithm_dispatch(
    mut state: &mut MajState,
    env: Gc<Maj>,
    fntype: MajArithmFnType,
    x: Gc<Maj>,
    y: Gc<Maj>,
    rest: Gc<Maj>,
    accum: bool
) -> Gc<Maj> {
    let mut origy = y.clone();
    match maj_arithm_coercion_helper(&mut state, x, y.clone()) {
        Err(e) => e,
        Ok((x, y, ntype)) => {
            let result =
                match fntype {
                    MajArithmFnType::Equals => {
                        maj_arithm_internal_eq(
                            &mut state, env.clone(),
                            x, y, ntype)
                    },
                    MajArithmFnType::Greater => {
                        maj_arithm_internal_gl(
                            x, y, ntype, true)
                    },
                    MajArithmFnType::Lesser => {
                        maj_arithm_internal_gl(
                            x, y, ntype, false)
                    },
                    MajArithmFnType::Plus => {
                        maj_arithm_internal_sum(
                            &mut state, env.clone(),
                            x, y, ntype)
                    },
                    MajArithmFnType::Minus => {
                        maj_arithm_internal_subtract(
                            &mut state, env.clone(),
                            x, y, ntype)
                    },
                    MajArithmFnType::Multiply => {
                        maj_arithm_internal_multiply(
                            &mut state, env.clone(),
                            x, y, ntype)
                    },
                    MajArithmFnType::Divide => {
                        maj_arithm_internal_divide(
                            &mut state, env.clone(),
                            x, y, ntype)
                    },
                };

            let result = match result {
                Ok(b) => b,
                Err(e) => return e,
            };

            if accum {
                origy = result.clone();
            }

            if !maj_nilp(rest.clone()).to_bool() {
                maj_arithm_dispatch(
                    &mut state, env,
                    fntype,
                    origy,
                    maj_car(rest.clone()),
                    maj_cdr(rest),
                    accum)
            } else {
                result
            }
        }
    }
}

28.1.4. Comparação por igualdade

fn maj_arithm_internal_eq(
    mut state: &mut MajState,
    env: Gc<Maj>,
    x: Gc<Maj>,
    y: Gc<Maj>,
    ntype: MajRawSym
) -> Result<Gc<Maj>, Gc<Maj>> {
    match ntype {
        MajRawSym::Integer => {
            let x = x.to_integer().unwrap();
            let y = y.to_integer().unwrap();
            Ok(if x == y { Maj::t() } else { Maj::nil() })
        },
        MajRawSym::Fraction => {
            let (n1, d1) = x.to_fraction().unwrap();
            let (n2, d2) = y.to_fraction().unwrap();
            Ok(if (n1 == n2) && (d1 == d2) {
                Maj::t()
            } else {
                Maj::nil()
            })
        },
        MajRawSym::Complex => {
            // Comparação recursiva.
            // Ah... eu posso realmente comparar esses
            // números dessa forma?
            let r1 = maj_real_part(x.clone());
            let i1 = maj_imag_part(x);
            let r2 = maj_real_part(y.clone());
            let i2 = maj_imag_part(y);
            let res =
                maj_arithm_eq(&mut state, env.clone(),
                              r1, r2,
                              Maj::nil()).to_bool() &&
                maj_arithm_eq(&mut state, env.clone(),
                              i1, i2,
                              Maj::nil()).to_bool();
            Ok(if res { Maj::t() } else { Maj::nil() })
        },
        MajRawSym::Float => {
            let fres =
                maj_arithm_floateq(&mut state, env.clone(),
                                   x, y);
            if maj_errorp(fres.clone()).to_bool() {
                return Err(fres);
            }
            Ok(fres)
        },
        t => panic!("{:?} is not a number subtype", t),
    }
}

28.1.5. Comparação de maior que / menor que

fn maj_arithm_internal_gl(
    x: Gc<Maj>,
    y: Gc<Maj>,
    ntype: MajRawSym,
    is_greater: bool
) -> Result<Gc<Maj>, Gc<Maj>> {
    match ntype {
        MajRawSym::Integer => {
            let x = x.to_integer().unwrap();
            let y = y.to_integer().unwrap();
            let res = if is_greater {
                x > y
            } else {
                x < y
            };
            Ok(if res { Maj::t() } else { Maj::nil() })
        },
        MajRawSym::Float => {
            let x = x.to_float().unwrap();
            let y = y.to_float().unwrap();
            let res = if is_greater {
                x > y
            } else {
                x < y
            };
            Ok(if res { Maj::t() } else { Maj::nil() })
        },
        MajRawSym::Fraction => {
            // Use common denominator test
            let (n1, d1) = x.to_fraction().unwrap();
            let (n2, d2) = y.to_fraction().unwrap();
            let res = if is_greater {
                (n1 * d2) > (n2 * d1)
            } else {
                (n1 * d2) < (n2 * d1)
            };
            Ok(if res { Maj::t() } else { Maj::nil() })
        },
        MajRawSym::Complex =>
            Err(maj_err(Maj::string(
                "The set of complex numbers can't be an ordered field"),
                        Maj::nil())),
        t => panic!("{:?} is not a number subtype", t),
    }
}

28.2. (= x y . rest)

pub fn maj_arithm_eq(
    mut state: &mut MajState,
    env: Gc<Maj>,
    x: Gc<Maj>,
    y: Gc<Maj>,
    rest: Gc<Maj>
) -> Gc<Maj> {
    maj_arithm_dispatch(&mut state, env,
                        MajArithmFnType::Equals,
                        x, y, rest, false)
}

28.3. (float= x y)

pub fn maj_arithm_floateq(
    mut state: &mut MajState,
    env: Gc<Maj>,
    x: Gc<Maj>,
    y: Gc<Maj>
) -> Gc<Maj> {
    use crate::axioms::predicates::maj_floatp;
    use core::f64;
    use float_cmp::approx_eq;
    if !maj_floatp(x.clone()).to_bool() {
        return maj_err(Maj::string("{} is not a float number"),
                       maj_list!(x));
    } else if !maj_floatp(y.clone()).to_bool() {
        return maj_err(Maj::string("{} is not a float number"),
                       maj_list!(y));
    }

    let ulps = Maj::symbol(&mut state, "*ulps*");
    let ulps = state.lookup(env, ulps);
    if maj_errorp(ulps.clone()).to_bool() {
        return ulps;
    }
    if let Some(u) = ulps.to_integer() {
        if u < 0 {
            maj_err(
                Maj::string("*ulps* cannot be smaller than 0"),
                Maj::nil())
        } else {
            let x = x.to_float().unwrap();
            let y = y.to_float().unwrap();
            if approx_eq!(f64, x, y, ulps = u) {
                Maj::t()
            } else {
                Maj::nil()
            }
        }
    } else {
        maj_err(Maj::string("*ulps* is not an integer"),
                Maj::nil())
    }
}

28.4. (> x y . rest)

pub fn maj_arithm_greater(
    mut state: &mut MajState,
    x: Gc<Maj>,
    y: Gc<Maj>,
    rest: Gc<Maj>
) -> Gc<Maj> {
    maj_arithm_dispatch(&mut state, Maj::nil(),
                        MajArithmFnType::Greater,
                        x, y, rest, false)
}

28.5. (< x y . rest)

pub fn maj_arithm_lesser(
    mut state: &mut MajState,
    x: Gc<Maj>,
    y: Gc<Maj>,
    rest: Gc<Maj>
) -> Gc<Maj> {
    maj_arithm_dispatch(&mut state, Maj::nil(),
                        MajArithmFnType::Lesser,
                        x, y, rest, false)
}

28.6. (>= x y . rest)

pub fn maj_arithm_geq(
    mut state: &mut MajState,
    env: Gc<Maj>,
    x: Gc<Maj>,
    y: Gc<Maj>,
    rest: Gc<Maj>
) -> Gc<Maj> {
    let result = maj_arithm_greater(&mut state,
                                    x.clone(), y.clone(),
                                    rest.clone());
    if maj_errorp(result.clone()).to_bool() {
        result
    } else if result.to_bool() {
        Maj::t()
    } else {
        maj_arithm_eq(&mut state, env, x, y, rest)
    }
}

28.7. (<= x y . rest)

pub fn maj_arithm_leq(
    mut state: &mut MajState,
    env: Gc<Maj>,
    x: Gc<Maj>,
    y: Gc<Maj>,
    rest: Gc<Maj>
) -> Gc<Maj> {
    let result = maj_arithm_lesser(&mut state,
                                   x.clone(), y.clone(),
                                   rest.clone());
    if maj_errorp(result.clone()).to_bool() {
        result
    } else if result.to_bool() {
        Maj::t()
    } else {
        maj_arithm_eq(&mut state, env, x, y, rest)
    }
}

29. Funções aritméticas

29.1. Funções de ajuda e ferramentas

29.1.1. Ajudante de soma

fn maj_arithm_internal_sum(
    mut state: &mut MajState,
    env: Gc<Maj>,
    x: Gc<Maj>,
    y: Gc<Maj>,
    ntype: MajRawSym
) -> Result<Gc<Maj>, Gc<Maj>> {
    match ntype {
        MajRawSym::Integer => {
            let x = x.to_integer().unwrap();
            let y = y.to_integer().unwrap();
            Ok(Maj::integer(x + y))
        },
        MajRawSym::Float => {
            let x = x.to_float().unwrap();
            let y = y.to_float().unwrap();
            Ok(Maj::float(x + y))
        },
        MajRawSym::Fraction => {
            let (nx, dx) = x.to_fraction().unwrap();
            let (ny, dy) = y.to_fraction().unwrap();
            let dr = dx * dy;
            let nr = (nx * dy) + (ny * dx);
            simplify_frac_coerce(Maj::fraction(nr, dr))
        },
        MajRawSym::Complex => {
            let xr = maj_real_part(x.clone());
            let xi = maj_imag_part(x);
            let yr = maj_real_part(y.clone());
            let yi = maj_imag_part(y);
            let real = maj_arithm_plus(
                &mut state, env.clone(),
                maj_list!(xr, yr));
            let imag = maj_arithm_plus(
                &mut state, env, maj_list!(xi, yi));
            Ok(Maj::complex(real, imag))
        },
        t => panic!("{:?} is not a number subtype", t),
    }
}
  1. Ajudante de conjugado
    fn maj_conjugate(
        mut state: &mut MajState,
        env: Gc<Maj>,
        x: Gc<Maj>
    ) -> Gc<Maj> {
        use crate::axioms::predicates::maj_complexp;
        if !maj_complexp(x.clone()).to_bool() {
            x
        } else {
            let realpart = maj_real_part(x.clone());
            let imagpart = maj_imag_part(x);
            let imagpart = maj_arithm_times(
                &mut state, env,
                maj_list!(imagpart, Maj::integer(-1)));
            if maj_errorp(imagpart.clone()).to_bool() {
                imagpart
            } else {
                Maj::complex(realpart, imagpart)
            }
        }
    }
    

29.1.2. Ajudante de subtração

fn maj_arithm_internal_subtract(
    mut state: &mut MajState,
    env: Gc<Maj>,
    x: Gc<Maj>,
    y: Gc<Maj>,
    ntype: MajRawSym
) -> Result<Gc<Maj>, Gc<Maj>> {
    match ntype {
        MajRawSym::Integer => {
            let x = x.to_integer().unwrap();
            let y = y.to_integer().unwrap();
            Ok(Maj::integer(x - y))
        },
        MajRawSym::Float => {
            let x = x.to_float().unwrap();
            let y = y.to_float().unwrap();
            Ok(Maj::float(x - y))
        },
        MajRawSym::Fraction => {
            let (nx, dx) = x.to_fraction().unwrap();
            let (ny, dy) = y.to_fraction().unwrap();
            let dr = dx * dy;
            let nr = (nx * dy) - (ny * dx);
            simplify_frac_coerce(Maj::fraction(nr, dr))
        },
        MajRawSym::Complex => {
            let xr = maj_real_part(x.clone());
            let xi = maj_imag_part(x);
            let yr = maj_real_part(y.clone());
            let yi = maj_imag_part(y);
            let real = maj_arithm_minus(
                &mut state, env.clone(),
                maj_list!(xr, yr));
            let imag = maj_arithm_minus(
                &mut state, env, maj_list!(xi, yi));
            Ok(Maj::complex(real, imag))
        },
        t => panic!("{:?} is not a number subtype", t),
    }
}
  1. Ajudante de negação
    fn maj_negate(
        mut state: &mut MajState,
        env: Gc<Maj>,
        x: Gc<Maj>
    ) -> Gc<Maj> {
        maj_arithm_times(
            &mut state, env,
            maj_list!(x, Maj::integer(-1)))
    }
    

29.1.3. Ajudante de multiplicação

fn maj_arithm_internal_multiply(
    mut state: &mut MajState,
    env: Gc<Maj>,
    x: Gc<Maj>,
    y: Gc<Maj>,
    ntype: MajRawSym
) -> Result<Gc<Maj>, Gc<Maj>> {
    match ntype {
        MajRawSym::Integer => {
            let x = x.to_integer().unwrap();
            let y = y.to_integer().unwrap();
            Ok(Maj::integer(x * y))
        },
        MajRawSym::Float => {
            let x = x.to_float().unwrap();
            let y = y.to_float().unwrap();
            Ok(Maj::float(x * y))
        },
        MajRawSym::Fraction => {
            let (nx, dx) = x.to_fraction().unwrap();
            let (ny, dy) = y.to_fraction().unwrap();
            let nr = nx * ny;
            let dr = dx * dy;
            simplify_frac_coerce(Maj::fraction(nr, dr))
        },
        MajRawSym::Complex => {
            Ok(maj_arithm_internal_complex_mult(
                &mut state, env, x, y))
        },
        t => panic!("{:?} is not a number subtype", t),
    }
}
fn maj_arithm_internal_complex_mult(
    mut state: &mut MajState,
    env: Gc<Maj>,
    x: Gc<Maj>,
    y: Gc<Maj>
) -> Gc<Maj> {
    let xr = maj_real_part(x.clone());
    let xi = maj_imag_part(x);
    let yr = maj_real_part(y.clone());
    let yi = maj_imag_part(y);

    let firsts = maj_arithm_times(
        &mut state, env.clone(),
        maj_list!(xr.clone(), yr.clone()));
    let outers = Maj::complex(
        Maj::integer(0),
        maj_arithm_times(
            &mut state, env.clone(),
            maj_list!(xr.clone(), yi.clone())));
    let inners = Maj::complex(
        Maj::integer(0),
        maj_arithm_times(
            &mut state, env.clone(),
            maj_list!(xi.clone(), yr.clone())));
    let lasts = maj_arithm_times(
        &mut state, env.clone(),
        maj_list!(xi, yi));
    let lasts = maj_negate(&mut state, env.clone(), lasts);

    maj_arithm_plus(
        &mut state, env,
        maj_list!(firsts, outers, inners, lasts))
}
  1. Ajudante de dedução de sinal
    fn maj_signum(
        mut state: &mut MajState,
        env: Gc<Maj>,
        x: Gc<Maj>
    ) -> Gc<Maj> {
        if maj_zerop(&mut state, env, x.clone()).to_bool() {
            x
        } else if maj_arithm_lesser(
            &mut state,
            x.clone(),
            Maj::integer(0),
            Maj::nil()).to_bool() {
            Maj::integer(-1)
        } else {
            Maj::integer(1)
        }
    }
    

29.1.4. Ajudante de divisão

fn maj_arithm_internal_divide(
    mut state: &mut MajState,
    env: Gc<Maj>,
    x: Gc<Maj>,
    y: Gc<Maj>,
    ntype: MajRawSym
) -> Result<Gc<Maj>, Gc<Maj>> {
    match ntype {
        MajRawSym::Integer => {
            let x = x.to_integer().unwrap();
            let y = y.to_integer().unwrap();
            if y == 0 {
                Err(maj_err(Maj::string("Division by zero"),
                            Maj::nil()))
            } else {
                Ok(if x % y == 0 {
                    Maj::integer(x / y)
                } else {
                    simplify_frac_coerce(Maj::fraction(x, y)).unwrap()
                })
            }
        },
        MajRawSym::Float => {
            if maj_zerop(&mut state, env, y.clone()).to_bool() {
                Err(maj_err(Maj::string("Division by zero"),
                            Maj::nil()))
            } else {
                let x = x.to_float().unwrap();
                let y = y.to_float().unwrap();
                Ok(Maj::float(x / y))
            }
        },
        MajRawSym::Fraction => {
            let (nx, dx) = x.to_fraction().unwrap();
            let (ny, dy) = y.to_fraction().unwrap();
            let nr = nx * dy;
            let dr = dx * ny;
            // Division by zero verified by this function
            simplify_frac_coerce(Maj::fraction(nr, dr))
        },
        MajRawSym::Complex => {
            return maj_arithm_internal_complex_div(
                &mut state, env, x, y);
        },
        t => panic!("{:?} is not a number subtype", t),
    }
}
fn maj_arithm_internal_complex_div(
    mut state: &mut MajState,
    env: Gc<Maj>,
    x: Gc<Maj>,
    y: Gc<Maj>
) -> Result<Gc<Maj>, Gc<Maj>> {
    let yconj = maj_arithm_plus(
        &mut state, env.clone(),
        maj_list!(y.clone()));

    let dividend = maj_arithm_times(
        &mut state, env.clone(),
        maj_list!(x, yconj.clone()));
    let divisor = maj_arithm_times(
        &mut state, env.clone(),
        maj_list!(y, yconj.clone()));

    let divisor = maj_real_part(divisor);
    if maj_zerop(&mut state,
                 env.clone(),
                 divisor.clone()
    ).to_bool() {
        return Err(maj_err(Maj::string("Division by zero"),
                           Maj::nil()));
    }

    let dividend_r = maj_real_part(dividend.clone());
    let dividend_i = maj_imag_part(dividend);

    let realpart = maj_arithm_divide(
        &mut state, env.clone(),
        maj_list!(dividend_r, divisor.clone()));
    let imagpart = maj_arithm_divide(
        &mut state, env.clone(),
        maj_list!(dividend_i, divisor.clone()));
    Ok(Maj::complex(realpart, imagpart))
}
  1. Ajudante de recíproco
    fn maj_reciprocal(
        mut state: &mut MajState,
        env: Gc<Maj>,
        x: Gc<Maj>
    ) -> Gc<Maj> {
        if maj_zerop(&mut state, env.clone(), x.clone()).to_bool() {
            maj_err(Maj::string("Division by zero"),
                    Maj::nil())
        } else {
            maj_arithm_divide(
                &mut state, env,
                maj_list!(Maj::fraction(1, 1), x.clone()))
        }
    }
    

29.2. (+ . rest)

pub fn maj_arithm_plus(
    mut state: &mut MajState,
    env: Gc<Maj>,
    rest: Gc<Maj>
) -> Gc<Maj> {
    let len = maj_length(rest.clone());
    if maj_errorp(len.clone()).to_bool() {
        return len;
    }
    match len.to_integer().unwrap() {
        0 => Maj::integer(1),
        1 => {
            maj_destructure_args!(rest, x);
            if !maj_numberp(x.clone()).to_bool() {
                maj_err(Maj::string("{} is not a number"),
                        maj_list!(x))
            } else {
                maj_conjugate(&mut state, env, x)
            }
        },
        _ => {
            maj_destructure_args!(rest, x, r1, y, rest);
            if !maj_numberp(x.clone()).to_bool() {
                maj_err(Maj::string("{} is not a number"),
                        maj_list!(x))
            } else if !maj_numberp(y.clone()).to_bool() {
                maj_err(Maj::string("{} is not a number"),
                        maj_list!(y))
            } else {
                maj_arithm_dispatch(
                    &mut state, Maj::nil(),
                    MajArithmFnType::Plus,
                    x, y, rest, true)
            }
        },
    }
}

29.3. (- . rest)

pub fn maj_arithm_minus(
    mut state: &mut MajState,
    env: Gc<Maj>,
    rest: Gc<Maj>
) -> Gc<Maj> {
    let len = maj_length(rest.clone());
    if maj_errorp(len.clone()).to_bool() {
        return len;
    }
    match len.to_integer().unwrap() {
        0 => Maj::integer(0),
        1 => {
            maj_destructure_args!(rest, x);
            if !maj_numberp(x.clone()).to_bool() {
                maj_err(Maj::string("{} is not a number"),
                        maj_list!(x))
            } else {
                maj_negate(&mut state, env, x)
            }
        },
        _ => {
            maj_destructure_args!(rest, x, r1, y, rest);
            if !maj_numberp(x.clone()).to_bool() {
                maj_err(Maj::string("{} is not a number"),
                        maj_list!(x))
            } else if !maj_numberp(y.clone()).to_bool() {
                maj_err(Maj::string("{} is not a number"),
                        maj_list!(y))
            } else {
                maj_arithm_dispatch(
                    &mut state, Maj::nil(),
                    MajArithmFnType::Minus,
                    x, y, rest, true)
            }
        },
    }
}

29.4. (* . rest)

pub fn maj_arithm_times(
    mut state: &mut MajState,
    env: Gc<Maj>,
    rest: Gc<Maj>
) -> Gc<Maj> {
    let len = maj_length(rest.clone());
    if maj_errorp(len.clone()).to_bool() {
        return len;
    }
    match len.to_integer().unwrap() {
        0 => Maj::integer(1),
        1 => {
            maj_destructure_args!(rest, x);
            if !maj_numberp(x.clone()).to_bool() {
                maj_err(Maj::string("{} is not a number"),
                        maj_list!(x))
            } else {
                maj_signum(&mut state, env, x)
            }
        },
        _ => {
            maj_destructure_args!(rest, x, r1, y, rest);
            if !maj_numberp(x.clone()).to_bool() {
                maj_err(Maj::string("{} is not a number"),
                        maj_list!(x))
            } else if !maj_numberp(y.clone()).to_bool() {
                maj_err(Maj::string("{} is not a number"),
                        maj_list!(y))
            } else {
                maj_arithm_dispatch(
                    &mut state, Maj::nil(),
                    MajArithmFnType::Multiply,
                    x, y, rest, true)
            }
        },
    }
}

29.5. (/ . rest)

pub fn maj_arithm_divide(
    mut state: &mut MajState,
    env: Gc<Maj>,
    rest: Gc<Maj>
) -> Gc<Maj> {
    let len = maj_length(rest.clone());
    if maj_errorp(len.clone()).to_bool() {
        return len;
    }
    match len.to_integer().unwrap() {
        0 => Maj::integer(1),
        1 => {
            maj_destructure_args!(rest, x);
            if !maj_numberp(x.clone()).to_bool() {
                maj_err(Maj::string("{} is not a number"),
                        maj_list!(x))
            } else {
                maj_reciprocal(&mut state, env, x)
            }
        },
        _ => {
            maj_destructure_args!(rest, x, r1, y, rest);
            if !maj_numberp(x.clone()).to_bool() {
                maj_err(Maj::string("{} is not a number"),
                        maj_list!(x))
            } else if !maj_numberp(y.clone()).to_bool() {
                maj_err(Maj::string("{} is not a number"),
                        maj_list!(y))
            } else {
                maj_arithm_dispatch(
                    &mut state, Maj::nil(),
                    MajArithmFnType::Divide,
                    x, y, rest, true)
            }
        },
    }
}

30. Funções de streams

30.1. Funções auxiliares

use crate::core::types::{
    MajStreamType,
    MajStreamDirection
};

30.1.1. Desempacotamento de um File

fn get_raw_stream(
    mut state: &mut MajState,
    stream: Gc<Maj>,
    expected_dir: MajStreamDirection
) -> Result<&std::fs::File, Gc<Maj>> {
    use std::io::Seek;
    if let Maj::Stream(mstream) = &*stream.clone() {
        if mstream.is_internal() {
            panic!("Never try to use *stdin* or *stdout* as ordinary streams!");
        }

        if maj_nilp(state.stat_stream(mstream.handle)).to_bool() {
            Err(maj_err(
                Maj::string("The stream {} is closed"),
                maj_list!(stream)))
        }
        else if mstream.direction == expected_dir {
            let eof_sym = Maj::symbol(&mut state, "eof");
            let file = state.borrow_stream(mstream.handle);
            let mut file = file.unwrap();
            if expected_dir == MajStreamDirection::In {
                // Check if eof.
                // If fails, position is unspecified and should
                // error out anyway. So we use unwrap.
                let length = file.stream_len().unwrap();
                let pos    = file.stream_position().unwrap();
                if pos >= length {
                    return Err(eof_sym)
                }
            }
            Ok(file)
        }
        else {
            Err(maj_err(
                Maj::string("{} is not an {} stream"),
                maj_list!(
                    stream,
                Maj::string(
                    if expected_dir ==
                        MajStreamDirection::In {
                            "input"
                        } else {
                            "output"
                        }))))
        }
    } else {
        Err(maj_err(
            Maj::string("{} is not a stream"),
            maj_list!(stream)))
    }
}

30.1.2. Testes de stream padrão

fn stdstreamp(x: Gc<Maj>) -> bool {
    if let Maj::Stream(mstream) = &*x.clone() {
        mstream.is_internal()
    } else {
        false
    }
}
fn stdstreamdirp(x: Gc<Maj>, dir: MajStreamDirection) -> bool {
    if let Maj::Stream(mstream) = &*x.clone() {
        if !mstream.is_internal() {
            panic!("Not a standard stream being tested");
        }
        dir == mstream.direction
    } else {
        false
    }    
}
fn stdstreamtype(x: Gc<Maj>) -> MajStreamType {
    if let Maj::Stream(mstream) = &*x.clone() {
        if !mstream.is_internal() {
            panic!("Not a standard stream being tested");
        }
        mstream.stype.clone()
    } else {
        panic!("Not a standard stream being tested");
    }
}

30.2. (open-stream dir path)

pub fn maj_open_stream(
    mut state: &mut MajState,
    dir: Gc<Maj>,
    path: Gc<Maj>
) -> Gc<Maj> {
    let path_opt = path.stringify();
    if path_opt.is_none() {
        return maj_err(
            Maj::string("{} is not a string"),
            maj_list!(path));
    }

    let direction =
        if maj_eq(Maj::symbol(&mut state, "in"),
                  dir.clone()).to_bool() {
            MajStreamDirection::In
        } else if maj_eq(Maj::symbol(&mut state, "out"),
                         dir.clone()).to_bool() {
            MajStreamDirection::Out
        } else {
            return maj_err(
                Maj::string("{} should be one of symbols 'in or 'out"),
                maj_list!(dir));
        };

    let stream =
        state.make_stream(&path_opt.unwrap(), direction);

    match stream {
        Some(obj) => obj,
        None => maj_err(
            Maj::string("Cannot open stream to path {}"),
            maj_list!(path)),
    }
}

30.3. (close-stream x)

pub fn maj_close_stream(
    state: &mut MajState,
    x: Gc<Maj>
) -> Gc<Maj> {
    state.close_stream(x)
}

30.4. (stat x)

pub fn maj_stat(mut state: &mut MajState, x: Gc<Maj>) -> Gc<Maj> {
    if let Maj::Stream(mstream) = &*x.clone() {
        let index  = mstream.handle;
        let result = state.stat_stream(index);

        if maj_errorp(result.clone()).to_bool() {
            result
        } else if maj_nilp(result.clone()).to_bool() {
            Maj::symbol(&mut state, "closed")
        } else {
            Maj::symbol(&mut state, "open")
        }
    } else {
        maj_err(
            Maj::string("Not a stream: {}"),
            maj_list!(x))
    }
}

30.5. (read-char stream)

pub fn maj_read_char(mut state: &mut MajState,
                     stream: Gc<Maj>
) -> Gc<Maj> {
    use std::io::Read;
    let mut buffer = [0; 1];
    if stdstreamp(stream.clone()) {
        if stdstreamdirp(stream.clone(), MajStreamDirection::In) {
            let peekopt = state.pop_stdin_peeked();
            match peekopt {
                Some(c) => {
                    return Maj::character(c);
                },
                None => {
                    use std::io;
                    match io::stdin().read(&mut buffer) {
                        Ok(_) => {
                            return Maj::character(buffer[0] as char);
                        },
                        Err(_) => {
                            return maj_err(
                                Maj::string(
                                    "Could not read from stream *stdin*"),
                                Maj::nil());
                        }
                    }
                },
            }
        } else {
            return maj_err(
                Maj::string("{} is not an input stream"),
                maj_list!(stream));
        }
    }

    match get_raw_stream(&mut state,
                         stream.clone(),
                         MajStreamDirection::In) {
        Ok(mut file) => {
            match file.read(&mut buffer) {
                Ok(_) => {
                    Maj::character(buffer[0] as char)
                },
                Err(_) => {
                    maj_err(
                        Maj::string(
                            "Could not read from stream {}"),
                        maj_list!(stream))
                }
            }
        },
        Err(expr) => expr,
    }
}

30.6. (peek-char stream)

pub fn maj_peek_char(mut state: &mut MajState,
                     stream: Gc<Maj>
) -> Gc<Maj> {
    use std::io::{ Read, Seek, SeekFrom };
    let mut buffer = [0; 1];
    if stdstreamp(stream.clone()) {
        if stdstreamdirp(stream.clone(), MajStreamDirection::In) {
            let peekopt = state.pop_stdin_peeked();
            match peekopt {
                Some(c) => {
                    state.push_stdin_peeked(c);
                    return Maj::character(c);
                },
                None => {
                    use std::io;
                    match io::stdin().read(&mut buffer) {
                        Ok(_) => {
                            state.push_stdin_peeked(buffer[0] as char);
                            return Maj::character(buffer[0] as char);
                        },
                        Err(_) => {
                            return maj_err(
                                Maj::string(
                                    "Could not read from stream *stdin*"),
                                Maj::nil());
                        }
                    }
                },
            }
        } else {
            return maj_err(
                Maj::string("{} is not an input stream"),
                maj_list!(stream));
        }
    }

    match get_raw_stream(&mut state,
                         stream.clone(),
                         MajStreamDirection::In) {
        Ok(mut file) => {
            match file.read(&mut buffer) {
                Ok(_) => {
                    // Since a character was read,
                    // seek back to a point before it.
                    // If unable, this should fail anyway
                    file.seek(SeekFrom::Current(-1))
                        .unwrap();
                    Maj::character(buffer[0] as char)
                },
                Err(_) => {
                    maj_err(
                        Maj::string(
                            "Could not read from stream {}"),
                        maj_list!(stream))
                }
            }
        },
        Err(expr) => expr,
    }
}

30.7. (write-char c stream)

pub fn maj_write_char(mut state: &mut MajState,
                      c: Gc<Maj>,
                      stream: Gc<Maj>
) -> Gc<Maj> {
    use std::io::Write;
    use crate::axioms::predicates::maj_charp;
    if !maj_charp(c.clone()).to_bool() {
        return maj_err(
            Maj::string("{} is not a character"),
            maj_list!(c));
    }
    let c = c.to_char().unwrap();

    if stdstreamp(stream.clone()) {
        if stdstreamdirp(stream.clone(), MajStreamDirection::Out) {
            match stdstreamtype(stream) {
                MajStreamType::Stdout =>
                    print!("{}", c),
                MajStreamType::Stderr =>
                    eprint!("{}", c),
                _ => panic!("write char to stream of wrong type"),
            };
            return Maj::nil();
        } else {
            return maj_err(
                Maj::string("{} is not an output stream"),
                maj_list!(stream));
        }
    }

    match get_raw_stream(&mut state,
                         stream.clone(),
                         MajStreamDirection::Out) {
        Ok(mut file) => {
            match write!(file, "{}", c) {
                Ok(_) => {
                    let _ = file.flush();
                    Maj::nil()
                },
                Err(_) => {
                    maj_err(
                        Maj::string(
                            "Could not write to stream {}"),
                        maj_list!(stream))
                }
            }
        },
        Err(expr) => expr,
    }
}

30.8. (write-string str stream)

pub fn maj_write_string(mut state: &mut MajState,
                        strn: Gc<Maj>,
                        stream: Gc<Maj>
) -> Gc<Maj> {
    use std::io::Write;
    if !maj_stringp(strn.clone()).to_bool() {
        return maj_err(
            Maj::string("{} is not a string"),
            maj_list!(strn));
    }
    let strn = strn.stringify().unwrap();
    
    if stdstreamp(stream.clone()) {
        if stdstreamdirp(stream.clone(), MajStreamDirection::Out) {
            match stdstreamtype(stream) {
                MajStreamType::Stdout =>
                    print!("{}", strn),
                MajStreamType::Stderr =>
                    eprint!("{}", strn),
                _ => panic!("write string to stream of wrong type"),
            };
            return Maj::nil();
        } else {
            return maj_err(
                Maj::string("{} is not an output stream"),
                maj_list!(stream));
        }
    }

    match get_raw_stream(&mut state,
                         stream.clone(),
                         MajStreamDirection::Out) {
        Ok(mut file) => {
            match write!(file, "{}", strn) {
                Ok(_) => {
                    let _ = file.flush();
                    Maj::nil()
                },
                Err(_) => {
                    maj_err(
                        Maj::string(
                            "Could not write to stream {}"),
                        maj_list!(stream))
                }
            }
        },
        Err(expr) => expr,
    }
}

30.9. (write x stream)

pub fn maj_write(mut state: &mut MajState,
                 x: Gc<Maj>,
                 stream: Gc<Maj>
) -> Gc<Maj> {
    use crate::printing::maj_format;
    let string = Maj::string(&maj_format(&state, x));
    maj_write_string(&mut state, string, stream)
}

30.10. TODO (read stream)

31. Funções de entrada e saída

31.1. (terpri)

pub fn maj_terpri(mut state: &mut MajState,
                  env: Gc<Maj>) -> Gc<Maj> {
    // Lookup dynamically bound stdout
    let stdout = Maj::symbol(&mut state, "*stdout*");
    let stdout = state.lookup(env.clone(), stdout);
    if maj_errorp(stdout.clone()).to_bool() {
        return stdout;
    }

    maj_write_char(&mut state, Maj::character('\n'),
                   stdout)
}

31.2. (display x)

pub fn maj_display(mut state: &mut MajState,
                   x: Gc<Maj>,
                   env: Gc<Maj>
) -> Gc<Maj> {
    // Lookup dynamically bound stdout
    let stdout = Maj::symbol(&mut state, "*stdout*");
    let stdout = state.lookup(env.clone(), stdout);
    if maj_errorp(stdout.clone()).to_bool() {
        return stdout;
    }

    maj_write(&mut state, x, stdout)
}

31.3. TODO (pretty-display x)

pub fn maj_pretty_display(mut state: &mut MajState,
                          x: Gc<Maj>,
                          env: Gc<Maj>
) -> Gc<Maj> {
    use crate::printing::maj_pretty_format;
    // Lookup dynamically bound stdout
    let stdout = Maj::symbol(&mut state, "*stdout*");
    let stdout = state.lookup(env.clone(), stdout);
    if maj_errorp(stdout.clone()).to_bool() {
        return stdout;
    }

    let string = maj_pretty_format(&state, x);
    let string = Maj::string(&string);
    maj_write_string(&mut state, string, stdout)
}

31.4. (print fmt . args)

pub fn maj_print(
    mut state: &mut MajState,
    fmt: Gc<Maj>,
    rest: Gc<Maj>,
    env: Gc<Maj>
) -> Gc<Maj> {
    // Lookup dynamically bound stdout
    let stdout = Maj::symbol(&mut state, "*stdout*");
    let stdout = state.lookup(env.clone(), stdout);
    if maj_errorp(stdout.clone()).to_bool() {
        return stdout;
    }

    let formatted = maj_format_prim(&state, fmt, rest);
    if maj_errorp(formatted.clone()).to_bool() {
        formatted
    } else {
        maj_write_string(&mut state, formatted,
                         stdout.clone());
        maj_write_char(&mut state, Maj::character('\n'),
                   stdout)
    }
}

31.5. TODO (load path)

pub fn maj_load(
    mut state: &mut MajState,
    env: Gc<Maj>,
    path: Gc<Maj>
) -> Gc<Maj> {
    use crate::reader::parser::maj_parse;
    use crate::reader::tokenizer::maj_tokenize_file;
    use crate::evaluator::maj_eval;
    match path.clone().stringify() {
        Some(pathstr) => {
            let mut buffer = String::new();
            match maj_tokenize_file(&pathstr, &mut buffer) {
                Ok(tokens) => {
                    match maj_parse(&mut state, tokens.clone()) {
                        Ok(expressions) => {
                            // TODO: Iterate over forms and yield errors
                            // depending on them
                            let results = maj_eval(&mut state, Maj::cons(
                                Maj::do_sym(), expressions), env);
                            if maj_errorp(results.clone()).to_bool() {
                                maj_err(
                                    Maj::string(
                                        "On evaluation of file {}: {}"),
                                    maj_list!(path, results))
                            } else {
                                results
                            }
                        },
                        Err(msg) => {
                            maj_err(
                                Maj::string("While parsing file {}: {}"),
                                maj_list!(path, Maj::string(msg)))
                        },
                    }
                },
                Err((line, msg)) => {
                    if line != 0 {
                        maj_err(
                            Maj::string("While reading file {}:{}: {}"),
                            maj_list!(path, Maj::integer(line as i64),
                                      Maj::string(msg)))
                    } else {
                        maj_err(
                            Maj::string("While reading file {}: {}"),
                            maj_list!(path, Maj::string(msg)))
                    }
                },
            }
        },
        None => maj_err(Maj::string("{} is not a string"),
                        maj_list!(path)),
    }
}

32. Funções de vetores

32.1. (vec-type vec)

pub fn maj_vec_type(mut state: &mut MajState,
                    vec: Gc<Maj>) -> Gc<Maj> {
    use crate::core::types::MajVector;
    if !maj_vectorp(vec.clone()).to_bool() {
        return maj_err(
            Maj::string("{} is not a vector"),
            maj_list!(vec));
    }
    if let Maj::Vector(v) = &*vec {
        return Maj::symbol(&mut state, match v {
            MajVector::Integer(_) => "integer",
            MajVector::Float(_)   => "float",
            MajVector::Char(_)    => "char",
            MajVector::Any(_)     => "any",
        });
    }
    panic!("vec-type: Unknown vector type");
}

32.2. (vec-push x vec)

pub fn maj_vec_push(mut state: &mut MajState, x: Gc<Maj>, vec: Gc<Maj>) -> Gc<Maj> {
    let pos = maj_vec_length(vec.clone());
    if maj_errorp(pos.clone()).to_bool() {
        return pos;
    }

    maj_vec_insert(&mut state, pos, x, vec)
}

32.3. (vec-insert pos x vec)

pub fn maj_vec_insert(mut state: &mut MajState,
                      pos: Gc<Maj>,
                      x: Gc<Maj>,
                      vec: Gc<Maj>) -> Gc<Maj> {
    use crate::core::types::MajVector;

    if !maj_vectorp(vec.clone()).to_bool() {
        return maj_err(
            Maj::string("{} is not a vector"),
            maj_list!(vec));
    }

    let xtype = maj_type(&mut state, x.clone());
    let vectype = maj_vec_type(&mut state, vec.clone());
    let any = Maj::symbol(&mut state, "any");

    let index =
        match pos.clone().to_integer() {
            Some(x) => {
                if x < 0 {
                    return maj_err(
                        Maj::string(
                            "Index {} is out of bounds in {}"),
                        maj_list!(pos, vec));
                }
                x as usize
            },
            None => {
                return maj_err(
                    Maj::string("{} is not an integer"),
                    maj_list!(pos));
            },
        };

    if !maj_eq(vectype.clone(), any).to_bool()
        && !maj_eq(xtype.clone(), vectype.clone()).to_bool() {
        return maj_err(
            Maj::string(
                "{} has type {}, which is incompatible with insertion on vector of type {}"),
            maj_list!(x, xtype, vectype));
    }

    if let Maj::Vector(v) = &*vec.clone() {
        match v {
            MajVector::Integer(v) => {
                let val = x.to_integer().unwrap();
                let len = v.borrow().len();
                if index > len {
                    maj_err(
                        Maj::string(
                            "Index {} is out of bounds in {}"),
                        maj_list!(pos, vec))
                } else {
                    v.borrow_mut().insert(index, val);
                    vec
                }
            },
            MajVector::Float(v) => {
                let val = x.to_float().unwrap();
                let len = v.borrow().len();
                if index > len {
                    maj_err(
                        Maj::string(
                            "Index {} is out of bounds in {}"),
                        maj_list!(pos, vec))
                } else {
                    v.borrow_mut().insert(index, val);
                    vec
                }
            },
            MajVector::Char(s) => {
                let val = x.to_char().unwrap();
                let len = s.borrow().chars().count();
                if index > len {
                    maj_err(
                        Maj::string(
                            "Index {} is out of bounds in {}"),
                        maj_list!(pos, vec))
                } else {
                    s.borrow_mut().insert(index, val);
                    vec
                }
            },
            MajVector::Any(v) => {
                let len = v.borrow().len();
                if index > len {
                    maj_err(
                        Maj::string(
                            "Index {} is out of bounds in {}"),
                        maj_list!(pos, vec))
                } else {
                    v.borrow_mut().insert(index, x.clone());
                    vec
                }
            }
        }
    } else {
        panic!("vec-insert: Not a vector");
    }
}

32.4. (vec-coerce vec type)

fn sym_to_vectype(mut state: &mut MajState,
                  vtype: Gc<Maj>) -> MajVectorType {
    if maj_eq(vtype.clone(),
              Maj::symbol(&mut state, "integer")).to_bool() {
        MajVectorType::Integer
    } else if maj_eq(
        vtype.clone(),
        Maj::symbol(&mut state, "float")).to_bool() {
        MajVectorType::Float
    } else if maj_eq(
        vtype.clone(),
        Maj::symbol(&mut state, "char")).to_bool() {
        MajVectorType::Char
    } else {
        MajVectorType::Any
    }
}
pub fn maj_vec_coerce(mut state: &mut MajState,
                      vtype: Gc<Maj>,
                      vec: Gc<Maj>) -> Gc<Maj> {
    use crate::core::types::MajVector;
    if !maj_vectorp(vec.clone()).to_bool() {
        return maj_err(
            Maj::string("{} is not a vector"),
            maj_list!(vec));
    }
    let intended_type = sym_to_vectype(&mut state, vtype.clone());
    let newvec = Maj::vector(intended_type);
    let is_any_intended = maj_eq(vtype.clone(),
                                 Maj::symbol(&mut state, "any"))
        .to_bool();

    if let Maj::Vector(v) = &*vec {
        match v {
            MajVector::Integer(v) => {
                for n in v.borrow().iter() {
                    let num = Maj::integer(*n);
                    if !is_any_intended {
                        let num = maj_number_coerce(
                            &mut state, vtype.clone(), num.clone());
                        if maj_errorp(num.clone()).to_bool() {
                            return num;
                        }
                    }
                    let result =
                        maj_vec_push(&mut state,
                                     num.clone(),
                                     newvec.clone());
                    if maj_errorp(result.clone()).to_bool() {
                        return result;
                    }
                }
            },
            MajVector::Float(v) => {
                for n in v.borrow().iter() {
                    let num = Maj::float(*n);
                    if !is_any_intended {
                        let num = maj_number_coerce(
                            &mut state, vtype.clone(), num.clone());
                        if maj_errorp(num.clone()).to_bool() {
                            return num;
                        }
                    }
                    let result =
                        maj_vec_push(&mut state,
                                     num.clone(),
                                     newvec.clone());
                    if maj_errorp(result.clone()).to_bool() {
                        return result;
                    }
                }
            },
            MajVector::Char(s) => {
                for c in s.borrow().chars() {
                    let result =
                        maj_vec_push(&mut state,
                                     Maj::character(c),
                                     newvec.clone());
                    if maj_errorp(result.clone()).to_bool() {
                        return result;
                    }
                }
            },
            MajVector::Any(v) => {
                for elt in v.borrow().iter() {
                    let result =
                        maj_vec_push(&mut state,
                                     elt.clone(),
                                     newvec.clone());
                    if maj_errorp(result.clone()).to_bool() {
                        return result;
                    }
                }
            }
        }
    } else {
        panic!("vec-coerce: Not a vector");
    }
    newvec
}

32.5. (vector . rest)

fn vectype_compatible(mut state: &mut MajState,
                      vtype: MajVectorType,
                      xtype: Gc<Maj>) -> bool {
    match vtype {
        MajVectorType::Any => true,
        MajVectorType::Integer =>
            maj_eq(xtype,
                   Maj::symbol(&mut state, "integer"))
            .to_bool(),
        MajVectorType::Float =>
            maj_eq(xtype,
                   Maj::symbol(&mut state, "float"))
            .to_bool(),
        MajVectorType::Char =>
            maj_eq(xtype,
                   Maj::symbol(&mut state, "char"))
            .to_bool(),
    }
}
fn best_vectype(mut state: &mut MajState,
                xtype: Gc<Maj>) -> MajVectorType {
    let integer = Maj::symbol(&mut state, "integer");
    let float   = Maj::symbol(&mut state, "float");
    let charsym = Maj::symbol(&mut state, "char");
    
    let sym =
        if maj_eq(xtype.clone(), integer).to_bool()
        || maj_eq(xtype.clone(), float).to_bool()
        || maj_eq(xtype.clone(), charsym).to_bool() {
            xtype
        } else {
            Maj::symbol(&mut state, "any")
        };
    sym_to_vectype(&mut state, sym)
}
pub fn maj_vector(mut state: &mut MajState, rest: Gc<Maj>) -> Gc<Maj> {
    if maj_nilp(rest.clone()).to_bool() {
        Maj::vector(MajVectorType::Any)
    } else {
        let mut iter = rest.clone();

        let first = maj_car(iter.clone());
        let fsttype = maj_type(&mut state, first);
        let mut vector = Maj::vector(
            best_vectype(&mut state, fsttype));

        while !maj_nilp(iter.clone()).to_bool() {
            let first = maj_car(iter.clone());
            let fsttype = maj_type(&mut state, first.clone());

            let vtype = maj_vec_type(&mut state, vector.clone());
            let vtype = sym_to_vectype(&mut state, vtype);

            if !vectype_compatible(
                &mut state, vtype, fsttype) {
                let any = Maj::symbol(&mut state, "any");
                vector = maj_vec_coerce(
                    &mut state, any, vector);
                if maj_errorp(vector.clone()).to_bool() {
                    return vector;
                }
            }
            let res =
                maj_vec_push(&mut state, first, vector.clone());
            if maj_errorp(res.clone()).to_bool() {
                return res;
            }
            iter = maj_cdr(iter);
        }
        vector
    }
}

32.6. (vec-length vec)

pub fn maj_vec_length(vec: Gc<Maj>) -> Gc<Maj> {
    use crate::core::types::MajVector;
    if let Maj::Vector(v) = &*vec.clone() {
        Maj::integer(
            match v {
                MajVector::Integer(v) => v.borrow().len(),
                MajVector::Float(v) => v.borrow().len(),
                MajVector::Char(s) => s.borrow().chars().count(),
                MajVector::Any(v) => v.borrow().len(),
            } as i64)
    } else {
        maj_err(
            Maj::string("{} is not a vector"),
            maj_list!(vec))
    }
}

32.7. (vec-pop vec)

pub fn maj_vec_pop(vec: Gc<Maj>) -> Gc<Maj> {
    use crate::core::types::MajVector;
    if let Maj::Vector(vv) = &*vec.clone() {
        match vv {
            MajVector::Integer(v) => {
                match v.borrow_mut().pop() {
                    Some(val) => Maj::integer(val),
                    None => Maj::nil(),
                }
            },
            MajVector::Float(v) => {
                match v.borrow_mut().pop() {
                    Some(val) => Maj::float(val),
                    None => Maj::nil(),
                }
            },
            MajVector::Char(s) => {
                match s.borrow_mut().pop() {
                    Some(val) => Maj::character(val),
                    None => Maj::nil(),
                }
            },
            MajVector::Any(v) => {
                match v.borrow_mut().pop() {
                    Some(val) => val,
                    None => Maj::nil(),
                }
            }
        }
    } else {
        maj_err(
            Maj::string("{} is not a vector"),
            maj_list!(vec))
    }
}

32.8. (vec-deq vec)

pub fn maj_vec_deq(vec: Gc<Maj>) -> Gc<Maj> {
    use crate::core::types::MajVector;
    if let Maj::Vector(vv) = &*vec.clone() {
        match vv {
            MajVector::Integer(v) => {
                if v.borrow().is_empty() {
                    Maj::nil()
                } else {
                    Maj::integer(
                        v.borrow_mut().remove(0))
                }
            },
            MajVector::Float(v) => {
                if v.borrow().is_empty() {
                    Maj::nil()
                } else {
                    Maj::float(
                        v.borrow_mut().remove(0))
                }
            },
            MajVector::Char(s) => {
                if s.borrow().is_empty() {
                    Maj::nil()
                } else {
                    Maj::character(
                        s.borrow_mut().remove(0))
                }
            },
            MajVector::Any(v) => {
                if v.borrow().is_empty() {
                    Maj::nil()
                } else {
                    v.borrow_mut().remove(0)
                }
            }
        }
    } else {
        maj_err(
            Maj::string("{} is not a vector"),
            maj_list!(vec))
    }
}

32.9. (vec-at x vec)

pub fn maj_vec_at(x: Gc<Maj>, vec: Gc<Maj>) -> Gc<Maj> {
    use crate::core::types::MajVector;
    use crate::axioms::predicates::maj_integerp;

    if !maj_integerp(x.clone()).to_bool() {
        return maj_err(
            Maj::string("{} is not an integer"),
            maj_list!(x));
    }

    let index = x.to_integer().unwrap() as usize;

    if let Maj::Vector(vv) = &*vec.clone() {
        match vv {
            MajVector::Integer(v) => {
                match v.borrow().get(index) {
                    Some(val) => Maj::integer(*val),
                    None => maj_err(
                        Maj::string("Index {} is out of bounds in {}"),
                        maj_list!(x.clone(), vec)),
                }
            },
            MajVector::Float(v) => {
                match v.borrow().get(index) {
                    Some(val) => Maj::float(*val),
                    None => maj_err(
                        Maj::string("Index {} is out of bounds in {}"),
                        maj_list!(x.clone(), vec)),
                }
            },
            MajVector::Char(s) => {
                match s.borrow().chars().nth(index) {
                    Some(val) => Maj::character(val),
                    None => maj_err(
                        Maj::string("Index {} is out of bounds in {}"),
                        maj_list!(x.clone(), vec)),
                }
            },
            MajVector::Any(v) => {
                match v.borrow().get(index) {
                    Some(val) => val.clone(),
                    None => maj_err(
                        Maj::string("Index {} is out of bounds in {}"),
                        maj_list!(x.clone(), vec)),
                }
            }
        }
    } else {
        maj_err(
            Maj::string("{} is not a vector"),
            maj_list!(vec))
    }
}

32.10. (vec-set pos x vec)

fn replace_string_char(strn: &mut String, index: usize, rep: char) {
    let mut vec = strn.chars().collect::<Vec<_>>();
    vec[index] = rep;
    *strn = vec.iter().collect::<String>();
}
pub fn maj_vec_set(
    pos: Gc<Maj>, x: Gc<Maj>, vec: Gc<Maj>
) -> Gc<Maj> {
    use crate::core::types::MajVector;
    use crate::axioms::predicates::{
        maj_integerp,
        maj_floatp,
        maj_charp
    };
    let index =
        match pos.clone().to_integer() {
            Some(x) => {
                if x < 0 {
                    return maj_err(
                        Maj::string(
                            "Index {} is out of bounds in {}"),
                        maj_list!(pos, vec));
                }
                x as usize
            },
            None => {
                return maj_err(
                    Maj::string("{} is not an integer"),
                    maj_list!(pos));
            },
        };

    if let Maj::Vector(vv) = &*vec.clone() {
        match vv {
            MajVector::Integer(v) => {
                if !maj_integerp(x.clone()).to_bool() {
                    return maj_err(
                        Maj::string(
                            "{} is not type-compatible with vector {}"),
                        maj_list!(x.clone(), vec.clone()));
                }
                let x = x.to_integer().unwrap();
                let len = v.borrow().len();
                if index >= len {
                    maj_err(
                        Maj::string("Index {} is out of bounds in {}"),
                        maj_list!(pos, vec))
                } else {
                    v.borrow_mut()[index] = x;
                    vec
                }
            },
            MajVector::Float(v) => {
                if!maj_floatp(x.clone()).to_bool() {
                    return maj_err(
                        Maj::string(
                            "{} is not type-compatible with vector {}"),
                        maj_list!(x.clone(), vec.clone()));
                }
                let x = x.to_float().unwrap();
                let len = v.borrow().len();
                if index >= len {
                    maj_err(
                        Maj::string("Index {} is out of bounds in {}"),
                        maj_list!(pos, vec))
                } else {
                    v.borrow_mut()[index] = x;
                    vec
                }
            },
            MajVector::Char(s) => {
                if!maj_charp(x.clone()).to_bool() {
                    maj_err(
                        Maj::string(
                            "{} is not type-compatible with vector {}"),
                        maj_list!(x.clone(), vec.clone()));
                }
                let c = x.clone().to_char().unwrap();
                let len = s.borrow().len();
                if index >= len {
                    maj_err(
                        Maj::string("Index {} is out of bounds in {}"),
                        maj_list!(pos, vec))
                } else {
                    replace_string_char(&mut s.borrow_mut(), index, c);
                    vec
                }
            },
            MajVector::Any(v) => {
                let len = v.borrow().len();
                if index >= len {
                    return maj_err(
                        Maj::string("Index {} is out of bounds in {}"),
                        maj_list!(pos, vec))
                } else {
                    v.borrow_mut()[index] = x.clone();
                    vec
                }
            },
        }
    } else {
        maj_err(
            Maj::string("{} is not a vector"),
            maj_list!(vec))
    }
}

32.11. (vec-remove vec pos)

pub fn maj_vec_remove(pos: Gc<Maj>, vec: Gc<Maj>) -> Gc<Maj> {
    use crate::core::types::MajVector;
    let index =
        match pos.clone().to_integer() {
            Some(x) => {
                if x < 0 {
                    return maj_err(
                        Maj::string(
                            "Index {} is out of bounds in {}"),
                        maj_list!(pos, vec));
                }
                x as usize
            },
            None => {
                return maj_err(
                    Maj::string("{} is not an integer"),
                    maj_list!(pos));
            },
        };
    if let Maj::Vector(vv) = &*vec {
        match vv {
            MajVector::Integer(v) => {
                let len = v.borrow().len();
                if index >= len {
                    maj_err(
                        Maj::string(
                            "Index {} is out of bounds in {}"),
                        maj_list!(pos, vec))
                } else {
                    let value = v.borrow_mut().remove(index);
                    Maj::integer(value)
                }
            },
            MajVector::Float(v) => {
                let len = v.borrow().len();
                if index >= len {
                    maj_err(
                        Maj::string(
                            "Index {} is out of bounds in {}"),
                        maj_list!(pos, vec))
                } else {
                    let value = v.borrow_mut().remove(index);
                    Maj::float(value)
                }
            },
            MajVector::Char(s) => {
                let len = s.borrow().chars().count();
                if index >= len {
                    maj_err(
                        Maj::string(
                            "Index {} is out of bounds in {}"),
                        maj_list!(pos, vec))
                } else {
                    let value = s.borrow_mut().remove(index);
                    Maj::character(value)
                }
            },
            MajVector::Any(v) => {
                let len = v.borrow().len();
                if index >= len {
                    maj_err(
                        Maj::string(
                            "Index {} is out of bounds in {}"),
                        maj_list!(pos, vec))
                } else {
                    v.borrow_mut().remove(index)
                }
            },
        }
    } else {
        maj_err(Maj::string("{} is not a vector"),
                maj_list!(vec))
    }
}

33. Funções customizadas

33.1. (gc)

pub fn maj_gc() -> Gc<Maj> {
    use gc::force_collect;
    force_collect();
    Maj::nil()
}

33.2. (print-env type)

fn maj_print_env(
    mut state: &mut MajState,
    args: Gc<Maj>,
    env: Gc<Maj>
) -> Gc<Maj> {
    use crate::printing::maj_format_env;
    let is_global =
        maj_eq(maj_car(args.clone()),
               Maj::symbol(&mut state, "global"))
        .to_bool();
    let is_lexical =
        maj_eq(maj_car(args.clone()),
               Maj::symbol(&mut state, "lexical"))
        .to_bool();

    if is_global {
        println!("{}", state);
        Maj::nil()
    } else if is_lexical {
        println!("{}", maj_format_env(&state, env));
        Maj::nil()
    } else {
        maj_err(Maj::string("Unknown environment type {}"),
                maj_list!(maj_car(args)))
    }
}

33.3. (stack-state)

fn maj_stack_state() -> Gc<Maj> {
    match stacker::remaining_stack() {
        Some(n) => {
            println!("Remaining stack: {}B (roughly {}KB)",
                     n, n / 1024);
            use std::convert::TryInto;
            match n.try_into() {
                Ok(n) => Maj::integer(n),
                Err(_) => Maj::t()
            }
        },
        None => {
            println!("Couldn't query stack state");
            Maj::nil()
        }
    }
}

34. Registro em contexto global

pub fn maj_gen_primitives(state: &mut MajState) {
    use crate::axioms::{ MajPrimFn, MajPrimArgs };
    let primitives: Vec<(&str, MajPrimArgs, MajPrimFn)> = vec![
        ("cons", MajPrimArgs::Required(2), |_, args, _| {
            maj_destructure_args!(args, first, rest, second);
            maj_cons(first, second)
        }),
        ("car", MajPrimArgs::Required(1), |_, args, _| {
            maj_destructure_args!(args, first);
            maj_car(first)
        }),
        ("cdr", MajPrimArgs::Required(1), |_, args, _| {
            maj_destructure_args!(args, first);
            maj_cdr(first)
        }),
        ("copy", MajPrimArgs::Required(1), |_, args, _| {
            maj_destructure_args!(args, first);
            maj_copy(first)
        }),
        ("length", MajPrimArgs::Required(1), |_, args, _| {
            maj_destructure_args!(args, first);
            maj_length(first)
        }),
        ("depth", MajPrimArgs::Required(1), |_, args, _| {
            maj_destructure_args!(args, first);
            maj_depth(first)
        }),
        ("type", MajPrimArgs::Required(1), |mut state, args, _| {
            maj_destructure_args!(args, first);
            maj_type(&mut state, first)
        }),
        ("intern", MajPrimArgs::Required(1), |mut state, args, _| {
            maj_destructure_args!(args, first);
            maj_intern(&mut state, first)
        }),
        ("name", MajPrimArgs::Required(1), |mut state, args, _| {
            maj_destructure_args!(args, first);
            maj_name(&mut state, first)
        }),
        ("get-environment", MajPrimArgs::Required(1),
         |mut state, args, env| {
            maj_destructure_args!(args, first);
            maj_get_environment(&mut state, first, env)
        }),
        ("coin", MajPrimArgs::None, |_, _, _| maj_coin()),
        ("sys", MajPrimArgs::Variadic(1), |_, args, _| {
            maj_destructure_args!(args, first, rest);
            maj_sys(first, rest)
        }),
        ("format", MajPrimArgs::Variadic(1), |state, args, _| {
            maj_destructure_args!(args, first, rest);
            maj_format_prim(&state, first, rest)
        }),
        ("err", MajPrimArgs::Variadic(1), |_, args, _| {
            maj_destructure_args!(args, first, rest);
            maj_err(first, rest)
        }),
        ("warn", MajPrimArgs::Variadic(1), |mut state, args, env| {
            maj_destructure_args!(args, first, rest);
            maj_warn(&mut state, first, rest, env)
        }),
        ("list", MajPrimArgs::Variadic(0),
         |_, args, _| maj_list(args)),
        ("append", MajPrimArgs::Variadic(0),
         |_, args, _| maj_append(args)),
        ("last", MajPrimArgs::Required(1), |_, args, _| {
            maj_destructure_args!(args, first);
            maj_last(first)
        }),
        ("reverse", MajPrimArgs::Required(1), |_, args, _| {
            maj_destructure_args!(args, first);
            maj_reverse(first)
        }),
        ("nth", MajPrimArgs::Required(2), |_, args, _| {
            maj_destructure_args!(args, first, rest, second);
            maj_nth(first, second)
        }),
        ("nthcdr", MajPrimArgs::Required(2), |_, args, _| {
            maj_destructure_args!(args, first, rest, second);
            maj_nthcdr(first, second)
        }),
        ("macroexpand-1", MajPrimArgs::Required(1),
         |mut state, args, env| {
             maj_destructure_args!(args, first);
             let (result, _) =
                 maj_macroexpand_1(&mut state, first, env);
             result
         }),
        ("not", MajPrimArgs::Required(1), |_, args, _| {
            maj_destructure_args!(args, first);
            maj_not(first)
        }),
        ("gensym", MajPrimArgs::None,
         |mut state, _, _| maj_gensym(&mut state)),
        ("iota", MajPrimArgs::Required(1), |_, args, _| {
            maj_destructure_args!(args, first);
            maj_iota(first)
         }),

        // Number functions
        ("number-coerce", MajPrimArgs::Required(2),
         |mut state, args, _| {
             maj_destructure_args!(args, first, rest, second);
             maj_number_coerce(&mut state, first, second)
         }),
        ("real-part", MajPrimArgs::Required(1), |_, args, _| {
            maj_destructure_args!(args, first);
            maj_real_part(first)
        }),
        ("imag-part", MajPrimArgs::Required(1), |_, args, _| {
            maj_destructure_args!(args, first);
            maj_imag_part(first)
        }),
        ("numer", MajPrimArgs::Required(1), |_, args, _| {
            maj_destructure_args!(args, first);
            maj_numer(first)
        }),
        ("denom", MajPrimArgs::Required(1), |_, args, _| {
            maj_destructure_args!(args, first);
            maj_denom(first)
        }),
        ("richest-number-type", MajPrimArgs::Required(2),
         |mut state, args, _| {
            maj_destructure_args!(args, first, rest, second);
            maj_richest_number_type(&mut state, first, second)
        }),
        ("rich-number-coerce", MajPrimArgs::Required(2),
         |mut state, args, _| {
             maj_destructure_args!(args, first, rest, second);
             maj_rich_number_coerce(&mut state, first, second)
         }),
        ("=", MajPrimArgs::Variadic(2),
         |mut state, args, env| {
             maj_destructure_args!(args, first, r, second, rest);
             maj_arithm_eq(&mut state, env, first, second, rest)
         }),
        ("float=", MajPrimArgs::Required(2),
         |mut state, args, env| {
             maj_destructure_args!(args, first, r, second);
             maj_arithm_floateq(&mut state, env, first, second)
         }),
        (">", MajPrimArgs::Variadic(2),
         |mut state, args, _| {
             maj_destructure_args!(args, first, r, second, rest);
             maj_arithm_greater(&mut state, first, second, rest)
         }),
        ("<", MajPrimArgs::Variadic(2),
         |mut state, args, _| {
             maj_destructure_args!(args, first, r, second, rest);
             maj_arithm_lesser(&mut state, first, second, rest)
         }),
        (">=", MajPrimArgs::Variadic(2),
         |mut state, args, env| {
             maj_destructure_args!(args, first, r, second, rest);
             maj_arithm_geq(&mut state, env, first, second, rest)
         }),
        ("<=", MajPrimArgs::Variadic(2),
         |mut state, args, env| {
             maj_destructure_args!(args, first, r, second, rest);
             maj_arithm_leq(&mut state, env, first, second, rest)
         }),
        ("+", MajPrimArgs::Variadic(0),
         |mut state, args, env| {
             maj_arithm_plus(&mut state, env, args)
         }),
        ("-", MajPrimArgs::Variadic(0),
         |mut state, args, env| {
             maj_arithm_minus(&mut state, env, args)
         }),
        ("*", MajPrimArgs::Variadic(0),
         |mut state, args, env| {
             maj_arithm_times(&mut state, env, args)
         }),
        ("/", MajPrimArgs::Variadic(0),
         |mut state, args, env| {
             maj_arithm_divide(&mut state, env, args)
         }),

        // Stream functions
        ("open-stream", MajPrimArgs::Required(2), |mut state, args, _| {
            maj_destructure_args!(args, first, rest, second);
            maj_open_stream(&mut state, first, second)
        }),
        ("close-stream", MajPrimArgs::Required(1), |mut state, args, _| {
            maj_destructure_args!(args, first);
            maj_close_stream(&mut state, first)
        }),
        ("stat", MajPrimArgs::Required(1), |mut state, args, _| {
            maj_destructure_args!(args, first);
            maj_stat(&mut state, first)
        }),
        ("read-char", MajPrimArgs::Required(1), |mut state, args, _| {
            maj_destructure_args!(args, first);
            maj_read_char(&mut state, first)
        }),
        ("peek-char", MajPrimArgs::Required(1), |mut state, args, _| {
            maj_destructure_args!(args, first);
            maj_peek_char(&mut state, first)
        }),
        ("write-char", MajPrimArgs::Required(2), |mut state, args, _| {
            maj_destructure_args!(args, first, rest, second);
            maj_write_char(&mut state, first, second)
        }),
        ("write-string", MajPrimArgs::Required(2), |mut state, args, _| {
            maj_destructure_args!(args, first, rest, second);
            maj_write_string(&mut state, first, second)
        }),
        ("write", MajPrimArgs::Required(2), |mut state, args, _| {
            maj_destructure_args!(args, first, rest, second);
            maj_write(&mut state, first, second)
        }),
        ("terpri", MajPrimArgs::None, |mut state, _, env| {
            maj_terpri(&mut state, env)
        }),
        ("display", MajPrimArgs::Required(1), |mut state, args, env| {
            maj_destructure_args!(args, first);
            maj_display(&mut state, first, env)
        }),
        ("pretty-display", MajPrimArgs::Required(1),
         |mut state, args, env| {
             maj_destructure_args!(args, first);
             maj_pretty_display(&mut state, first, env)
         }),
        ("print", MajPrimArgs::Variadic(1), |mut state, args, env| {
            maj_destructure_args!(args, first, rest);
            maj_print(&mut state, first, rest, env)
        }),
        ("load", MajPrimArgs::Required(1), |mut state, args, env| {
            maj_destructure_args!(args, first);
            maj_load(&mut state, env, first)
        }),

        // Vector functions
        ("vec-type", MajPrimArgs::Required(1), |mut state, args, _| {
            maj_destructure_args!(args, first);
            maj_vec_type(&mut state, first)
        }),
        ("vec-push", MajPrimArgs::Required(2), |mut state, args, _| {
            maj_destructure_args!(args, first, rest, second);
            maj_vec_push(&mut state, first, second)
        }),
        ("vec-insert", MajPrimArgs::Required(3), |mut state, args, _| {
            maj_destructure_args!(args, first, rest, second,
                                  sndrst, third);
            maj_vec_insert(&mut state, first, second, third)
        }),
        ("vec-coerce", MajPrimArgs::Required(2), |mut state, args, _| {
            maj_destructure_args!(args, first, rest, second);
            maj_vec_coerce(&mut state, first, second)
        }),
        ("vector", MajPrimArgs::Variadic(0), |mut state, args, _| {
            maj_vector(&mut state, args)
        }),
        ("vec-length", MajPrimArgs::Required(1), |_, args, _| {
            maj_destructure_args!(args, first);
            maj_vec_length(first)
        }),
        ("vec-pop", MajPrimArgs::Required(1), |_, args, _| {
            maj_destructure_args!(args, first);
            maj_vec_pop(first)
        }),
        ("vec-deq", MajPrimArgs::Required(1), |_, args, _| {
            maj_destructure_args!(args, first);
            maj_vec_deq(first)
        }),
        ("vec-at", MajPrimArgs::Required(2), |_, args, _| {
            maj_destructure_args!(args, first, rest, second);
            maj_vec_at(first, second)
        }),
        ("vec-set", MajPrimArgs::Required(3), |_, args, _| {
            maj_destructure_args!(args, first, rest, second,
                                  snd_rest, third);
            maj_vec_set(first, second, third)
        }),
        ("vec-remove", MajPrimArgs::Required(2), |_, args, _| {
            maj_destructure_args!(args, first, rest, second);
            maj_vec_remove(first, second)
        }),

        // Non-standard functions
        ("gc", MajPrimArgs::None, |_, _, _| maj_gc()),
        ("print-env", MajPrimArgs::Required(1), maj_print_env),
        ("stack-state", MajPrimArgs::None, |_, _, _| maj_stack_state()),
    ];

    for primitive in primitives.iter() {
        let (name, arity, f) = primitive;
        state.register_primitive(name, *arity, *f);
    }
}

Author: Lucas S. Vieira

Created: 2022-10-24 seg 00:27

Validate