<commit_before>extern crate foxbox_taxonomy;
extern crate foxbox_thinkerbell;

extern crate transformable_channels;

use foxbox_thinkerbell::fake_env::*;
use foxbox_thinkerbell::parse::*;
use foxbox_thinkerbell::run::*;
use foxbox_thinkerbell::ast::*;

use foxbox_taxonomy::api::{ Error as APIError };
use foxbox_taxonomy::util::Id;
use foxbox_taxonomy::selector::*;
use foxbox_taxonomy::services::*;
use foxbox_taxonomy::values::{ OnOff, Range, Type, TypeError as APITypeError , Value };

use std::marker::PhantomData;
use std::thread;
use std::collections::{ HashMap, HashSet };

use transformable_channels::mpsc::*;

#[derive(Debug)]
enum Event {
    Env(FakeEnvEvent),
    Run(ExecutionEvent),
}

#[test]
fn test_compile() {
    let (tx, rx) : (_, Receiver<Event>)= channel();

    let tx_env = Box::new(tx.map(|event| Event::Env(event)));
    let tx_run = tx.map(|event| Event::Run(event));

    let env = FakeEnv::new(tx_env);
    let mut exec = Execution::<FakeEnv>::new();

    thread::spawn(move || {
        for msg in rx {
            println!("LOG: {:?}", msg)
        }
    });

    println!("* Attempting to parse an run an empty script will raise an error.");
    let script = Script::parse("{\"rules\": []}").unwrap();
    match exec.start(env, script, tx_run) {
        Err(Error::CompileError(CompileError::SourceError(SourceError::NoRule))) => {},
        other => panic!("Unexpected result {:?}", other)
    }

    println!("//FIXME: Attempting to parse a script with an empty condition will raise an error.");
    println!("//FIXME: Attempting to parse a script with an empty statement will raise an error.");
    println!("//FIXME: Attempting to parse a script with an empty source will raise an error.");
    println!("//FIXME: Attempting to parse a script with an empty destination will raise an error.");
    println!("//FIXME: Attempting to parse a script with a type error in a match will raise an error.");
    println!("//FIXME: Attempting to parse a script with a type error in a send will raise an error.");

    println!("");
}

#[test]
fn test_run() {
    let (tx, rx) : (_, Receiver<Event>)= channel();

    let tx_env = Box::new(tx.map(|event| Event::Env(event)));
    let tx_run = tx.map(|event| Event::Run(event));
    let (tx_done, rx_done) = channel();
    let (tx_send, rx_send) = channel();

    let env = FakeEnv::new(tx_env);
    let mut exec = Execution::<FakeEnv>::new();

    thread::spawn(move || {
        for msg in rx {
            if let Event::Env(FakeEnvEvent::Done) = msg {
                tx_done.send(()).unwrap();
            } else if let Event::Env(FakeEnvEvent::Send { id, value }) = msg {
                tx_send.send((id, value)).unwrap();
            } else {
                println!("LOG: {:?}", msg);
            }
        }
    });

    let script_1 = Script {
        rules: vec![
            Rule {
                conditions: vec![
                    Match {
                        source: vec![
                            GetterSelector::new()
                        ],
                        kind: ChannelKind::OnOff,
                        range: Range::Eq(Value::OnOff(OnOff::On)),
                        duration: None,
                        phantom: PhantomData
                    }
                ],
                execute: vec![
                    Statement {
                        destination: vec![
                            SetterSelector::new()
                        ],
                        value: Value::OnOff(OnOff::Off),
                        kind: ChannelKind::OnOff,
                        phantom: PhantomData,
                    }
                ],
                phantom: PhantomData
            }
        ],
        phantom: PhantomData,
    };

    let adapter_id_1 = Id::<AdapterId>::new("Adapter 1");
    let service_id_1 = Id::<ServiceId>::new("Service 1");
    let getter_id_1 = Id::<Getter>::new("Getter 1");
    let getter_id_2 = Id::<Getter>::new("Getter 2");
    let setter_id_1 = Id::<Setter>::new("Setter 1");

    println!("* We can start executing a trivial rule.");
    exec.start(env.clone(), script_1, tx_run).unwrap();

    println!("* Changing the structure of the network doesn't break the rule.");
    env.execute(Instruction::AddAdapters(vec![adapter_id_1.to_string()]));
    rx_done.recv().unwrap();

    env.execute(Instruction::AddServices(vec![
        Service {
            id: service_id_1.clone(),
            adapter: adapter_id_1.clone(),
            getters: HashMap::new(),
            setters: HashMap::new(),
            tags: HashSet::new(),
        }
    ]));
    rx_done.recv().unwrap();

    env.execute(Instruction::AddGetters(vec![
        Channel {
            id: getter_id_1.clone(),
            adapter: adapter_id_1.clone(),
            service: service_id_1.clone(),
            tags: HashSet::new(),
            last_seen: None,
            mechanism: Getter {
                watch: true,
                poll: None,
                updated: None,
                trigger: None,
                kind: ChannelKind::OnOff,
            }
        }
    ]));
    rx_done.recv().unwrap();

    env.execute(Instruction::AddSetters(vec![
        Channel {
            id: setter_id_1.clone(),
            adapter: adapter_id_1.clone(),
            service: service_id_1.clone(),
            last_seen: None,
            tags: HashSet::new(),
            mechanism: Setter {
                push: None,
                updated: None,
                kind: ChannelKind::OnOff,
            }
        }
    ]));
    rx_done.recv().unwrap();

    println!("* Injecting the expected value triggers the send.");
    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::On)))
    ]));

    rx_done.recv().unwrap();
    let (id, value) = rx_send.recv().unwrap();
    assert_eq!(id, setter_id_1);
    assert_eq!(value, Value::OnOff(OnOff::Off));

    println!("* Injecting an out-of-range value does not trigger the send.");
    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::Off)))
    ]));

    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    println!("* Injecting an error does not trigger the send.");
    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Err(APIError::TypeError(APITypeError {
            expected: Type::OnOff,
            got: Type::OpenClosed
        })))
    ]));

    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    println!("* Injecting the expected value again triggers the send again.");
    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::On)))
    ]));

    rx_done.recv().unwrap();
    let (id, value) = rx_send.recv().unwrap();
    assert_eq!(id, setter_id_1);
    assert_eq!(value, Value::OnOff(OnOff::Off));

    println!("* Adding a second getter doesn't break the world.");
    env.execute(Instruction::AddGetters(vec![
        Channel {
            id: getter_id_2.clone(),
            adapter: adapter_id_1.clone(),
            service: service_id_1.clone(),
            tags: HashSet::new(),
            last_seen: None,
            mechanism: Getter {
                watch: true,
                poll: None,
                updated: None,
                trigger: None,
                kind: ChannelKind::OnOff,
            }
        }
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    println!("* Changing the state of the second getter while the condition remains true with the second getter doesn't do anything.");
    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_2.clone(), Ok(Value::OnOff(OnOff::On)))
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_2.clone(), Ok(Value::OnOff(OnOff::Off)))
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_2.clone(), Ok(Value::OnOff(OnOff::On)))
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    println!("* Changing the state of the first getter while the condition remains true with the second getter doesn't do anything.");
    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::On)))
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::Off)))
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::On)))
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    println!("* If neither condition is met, the second getter can trigger the send.");

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::Off))),
        (getter_id_2.clone(), Ok(Value::OnOff(OnOff::Off)))
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_2.clone(), Ok(Value::OnOff(OnOff::On)))
    ]));
    rx_done.recv().unwrap();
    let (id, value) = rx_send.recv().unwrap();
    assert_eq!(id, setter_id_1);
    assert_eq!(value, Value::OnOff(OnOff::Off));

    println!("* If neither condition is met, the first getter can trigger the send.");

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::Off))),
        (getter_id_2.clone(), Ok(Value::OnOff(OnOff::Off)))
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::On)))
    ]));
    rx_done.recv().unwrap();
    let (id, value) = rx_send.recv().unwrap();
    assert_eq!(id, setter_id_1);
    assert_eq!(value, Value::OnOff(OnOff::Off));

    println!("");
}<commit_msg>[fxbox/thinkerbell] Testing with several Sender<commit_after>extern crate foxbox_taxonomy;
extern crate foxbox_thinkerbell;

extern crate transformable_channels;

use foxbox_thinkerbell::fake_env::*;
use foxbox_thinkerbell::parse::*;
use foxbox_thinkerbell::run::*;
use foxbox_thinkerbell::ast::*;

use foxbox_taxonomy::api::{ Error as APIError };
use foxbox_taxonomy::util::Id;
use foxbox_taxonomy::selector::*;
use foxbox_taxonomy::services::*;
use foxbox_taxonomy::values::{ OnOff, Range, Type, TypeError as APITypeError , Value };

use std::marker::PhantomData;
use std::thread;
use std::collections::{ HashMap, HashSet };

use transformable_channels::mpsc::*;

#[derive(Debug)]
enum Event {
    Env(FakeEnvEvent),
    Run(ExecutionEvent),
}

#[test]
fn test_compile() {
    let (tx, rx) : (_, Receiver<Event>)= channel();

    let tx_env = Box::new(tx.map(|event| Event::Env(event)));
    let tx_run = tx.map(|event| Event::Run(event));

    let env = FakeEnv::new(tx_env);
    let mut exec = Execution::<FakeEnv>::new();

    thread::spawn(move || {
        for msg in rx {
            println!("LOG: {:?}", msg)
        }
    });

    println!("* Attempting to parse an run an empty script will raise an error.");
    let script = Script::parse("{\"rules\": []}").unwrap();
    match exec.start(env, script, tx_run) {
        Err(Error::CompileError(CompileError::SourceError(SourceError::NoRule))) => {},
        other => panic!("Unexpected result {:?}", other)
    }

    println!("//FIXME: Attempting to parse a script with an empty condition will raise an error.");
    println!("//FIXME: Attempting to parse a script with an empty statement will raise an error.");
    println!("//FIXME: Attempting to parse a script with an empty source will raise an error.");
    println!("//FIXME: Attempting to parse a script with an empty destination will raise an error.");
    println!("//FIXME: Attempting to parse a script with a type error in a match will raise an error.");
    println!("//FIXME: Attempting to parse a script with a type error in a send will raise an error.");

    println!("");
}

#[test]
fn test_run() {
    let (tx, rx) : (_, Receiver<Event>)= channel();

    let tx_env = Box::new(tx.map(|event| Event::Env(event)));
    let tx_run = tx.map(|event| Event::Run(event));
    let (tx_done, rx_done) = channel();
    let (tx_send, rx_send) = channel();

    let env = FakeEnv::new(tx_env);
    let mut exec = Execution::<FakeEnv>::new();

    thread::spawn(move || {
        for msg in rx {
            if let Event::Env(FakeEnvEvent::Done) = msg {
                tx_done.send(()).unwrap();
            } else if let Event::Env(FakeEnvEvent::Send { id, value }) = msg {
                tx_send.send((id, value)).unwrap();
            } else {
                println!("LOG: {:?}", msg);
            }
        }
    });

    let script_1 = Script {
        rules: vec![
            Rule {
                conditions: vec![
                    Match {
                        source: vec![
                            GetterSelector::new()
                        ],
                        kind: ChannelKind::OnOff,
                        range: Range::Eq(Value::OnOff(OnOff::On)),
                        duration: None,
                        phantom: PhantomData
                    }
                ],
                execute: vec![
                    Statement {
                        destination: vec![
                            SetterSelector::new()
                        ],
                        value: Value::OnOff(OnOff::Off),
                        kind: ChannelKind::OnOff,
                        phantom: PhantomData,
                    }
                ],
                phantom: PhantomData
            }
        ],
        phantom: PhantomData,
    };

    let adapter_id_1 = Id::<AdapterId>::new("Adapter 1");
    let service_id_1 = Id::<ServiceId>::new("Service 1");
    let getter_id_1 = Id::<Getter>::new("Getter 1");
    let getter_id_2 = Id::<Getter>::new("Getter 2");
    let setter_id_1 = Id::<Setter>::new("Setter 1");
    let setter_id_2 = Id::<Setter>::new("Setter 2");
    let setter_id_3 = Id::<Setter>::new("Setter 3");

    println!("* We can start executing a trivial rule.");
    exec.start(env.clone(), script_1, tx_run).unwrap();

    println!("* Changing the structure of the network doesn't break the rule.");
    env.execute(Instruction::AddAdapters(vec![adapter_id_1.to_string()]));
    rx_done.recv().unwrap();

    env.execute(Instruction::AddServices(vec![
        Service {
            id: service_id_1.clone(),
            adapter: adapter_id_1.clone(),
            getters: HashMap::new(),
            setters: HashMap::new(),
            tags: HashSet::new(),
        }
    ]));
    rx_done.recv().unwrap();

    env.execute(Instruction::AddGetters(vec![
        Channel {
            id: getter_id_1.clone(),
            adapter: adapter_id_1.clone(),
            service: service_id_1.clone(),
            tags: HashSet::new(),
            last_seen: None,
            mechanism: Getter {
                watch: true,
                poll: None,
                updated: None,
                trigger: None,
                kind: ChannelKind::OnOff,
            }
        }
    ]));
    rx_done.recv().unwrap();

    env.execute(Instruction::AddSetters(vec![
        Channel {
            id: setter_id_1.clone(),
            adapter: adapter_id_1.clone(),
            service: service_id_1.clone(),
            last_seen: None,
            tags: HashSet::new(),
            mechanism: Setter {
                push: None,
                updated: None,
                kind: ChannelKind::OnOff,
            }
        }
    ]));
    rx_done.recv().unwrap();

    println!("* Injecting the expected value triggers the send.");
    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::On)))
    ]));

    rx_done.recv().unwrap();
    let (id, value) = rx_send.recv().unwrap();
    assert_eq!(id, setter_id_1);
    assert_eq!(value, Value::OnOff(OnOff::Off));

    println!("* Injecting an out-of-range value does not trigger the send.");
    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::Off)))
    ]));

    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    println!("* Injecting an error does not trigger the send.");
    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Err(APIError::TypeError(APITypeError {
            expected: Type::OnOff,
            got: Type::OpenClosed
        })))
    ]));

    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    println!("* Injecting the expected value again triggers the send again.");
    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::On)))
    ]));

    rx_done.recv().unwrap();
    let (id, value) = rx_send.recv().unwrap();
    assert_eq!(id, setter_id_1);
    assert_eq!(value, Value::OnOff(OnOff::Off));

    println!("* Adding a second getter doesn't break the world.");
    env.execute(Instruction::AddGetters(vec![
        Channel {
            id: getter_id_2.clone(),
            adapter: adapter_id_1.clone(),
            service: service_id_1.clone(),
            tags: HashSet::new(),
            last_seen: None,
            mechanism: Getter {
                watch: true,
                poll: None,
                updated: None,
                trigger: None,
                kind: ChannelKind::OnOff,
            }
        }
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    println!("* Changing the state of the second getter while the condition remains true with the second getter doesn't do anything.");
    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_2.clone(), Ok(Value::OnOff(OnOff::On)))
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_2.clone(), Ok(Value::OnOff(OnOff::Off)))
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_2.clone(), Ok(Value::OnOff(OnOff::On)))
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    println!("* Changing the state of the first getter while the condition remains true with the second getter doesn't do anything.");
    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::On)))
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::Off)))
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::On)))
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    println!("* If neither condition is met, the second getter can trigger the send.");

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::Off))),
        (getter_id_2.clone(), Ok(Value::OnOff(OnOff::Off)))
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_2.clone(), Ok(Value::OnOff(OnOff::On)))
    ]));
    rx_done.recv().unwrap();
    let (id, value) = rx_send.recv().unwrap();
    assert_eq!(id, setter_id_1);
    assert_eq!(value, Value::OnOff(OnOff::Off));

    println!("* If neither condition is met, the first getter can trigger the send.");

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::Off))),
        (getter_id_2.clone(), Ok(Value::OnOff(OnOff::Off)))
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::On)))
    ]));
    rx_done.recv().unwrap();
    let (id, value) = rx_send.recv().unwrap();
    assert_eq!(id, setter_id_1);
    assert_eq!(value, Value::OnOff(OnOff::Off));

    println!("* If we add a second setter, it also receives these sends.");
    env.execute(Instruction::AddSetters(vec![
        Channel {
            id: setter_id_2.clone(),
            adapter: adapter_id_1.clone(),
            service: service_id_1.clone(),
            last_seen: None,
            tags: HashSet::new(),
            mechanism: Setter {
                push: None,
                updated: None,
                kind: ChannelKind::OnOff,
            }
        }
    ]));
    rx_done.recv().unwrap();

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::Off))),
        (getter_id_2.clone(), Ok(Value::OnOff(OnOff::Off)))
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::On)))
    ]));
    rx_done.recv().unwrap();

    let events : HashMap<_, _> = (0..2).map(|_| {
        rx_send.recv().unwrap()
    }).collect();
    assert_eq!(events.len(), 2);
    assert_eq!(*events.get(&setter_id_1).unwrap(), Value::OnOff(OnOff::Off));
    assert_eq!(*events.get(&setter_id_2).unwrap(), Value::OnOff(OnOff::Off));
    assert!(rx_send.try_recv().is_err());

    println!("* If we add a setter of a mismatched type, it does not receive these sends.");
    env.execute(Instruction::AddSetters(vec![
        Channel {
            id: setter_id_3.clone(),
            adapter: adapter_id_1.clone(),
            service: service_id_1.clone(),
            last_seen: None,
            tags: HashSet::new(),
            mechanism: Setter {
                push: None,
                updated: None,
                kind: ChannelKind::Ready,
            }
        }
    ]));
    rx_done.recv().unwrap();

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::Off))),
        (getter_id_2.clone(), Ok(Value::OnOff(OnOff::Off)))
    ]));
    rx_done.recv().unwrap();
    assert!(rx_send.try_recv().is_err());

    env.execute(Instruction::InjectGetterValues(vec![
        (getter_id_1.clone(), Ok(Value::OnOff(OnOff::On)))
    ]));
    rx_done.recv().unwrap();

    let events : HashMap<_, _> = (0..2).map(|_| {
        rx_send.recv().unwrap()
    }).collect();
    assert_eq!(events.len(), 2);
    assert_eq!(*events.get(&setter_id_1).unwrap(), Value::OnOff(OnOff::Off));
    assert_eq!(*events.get(&setter_id_2).unwrap(), Value::OnOff(OnOff::Off));
    assert!(rx_send.try_recv().is_err());

    println!("");
}<|endoftext|>


<commit_before>use core::cmp;
use spin::Mutex;

use io::{Io, Pio, ReadOnly, WriteOnly};

pub static PS2: Mutex<Ps2> = Mutex::new(Ps2::new());

pub unsafe fn init() {
    PS2.lock().init();
}

bitflags! {
    flags StatusFlags: u8 {
        const OUTPUT_FULL = 1,
        const INPUT_FULL = 1 << 1,
        const SYSTEM = 1 << 2,
        const COMMAND = 1 << 3,
        // Chipset specific
        const KEYBOARD_LOCK = 1 << 4,
        // Chipset specific
        const SECOND_OUTPUT_FULL = 1 << 5,
        const TIME_OUT = 1 << 6,
        const PARITY = 1 << 7
    }
}

bitflags! {
    flags ConfigFlags: u8 {
        const FIRST_INTERRUPT = 1,
        const SECOND_INTERRUPT = 1 << 1,
        const POST_PASSED = 1 << 2,
        // 1 << 3 should be zero
        const CONFIG_RESERVED_3 = 1 << 3,
        const FIRST_DISABLED = 1 << 4,
        const SECOND_DISABLED = 1 << 5,
        const FIRST_TRANSLATE = 1 << 6,
        // 1 << 7 should be zero
        const CONFIG_RESERVED_7 = 1 << 7,
    }
}

#[repr(u8)]
#[allow(dead_code)]
enum Command {
    ReadConfig = 0x20,
    WriteConfig = 0x60,
    DisableSecond = 0xA7,
    EnableSecond = 0xA8,
    TestSecond = 0xA9,
    TestController = 0xAA,
    TestFirst = 0xAB,
    Diagnostic = 0xAC,
    DisableFirst = 0xAD,
    EnableFirst = 0xAE,
    WriteSecond = 0xD4
}

#[repr(u8)]
#[allow(dead_code)]
enum KeyboardCommand {
    EnableReporting = 0xF4,
    SetDefaults = 0xF6,
    Reset = 0xFF
}

#[repr(u8)]
#[allow(dead_code)]
enum MouseCommand {
    GetDeviceId = 0xF2,
    EnableReporting = 0xF4,
    SetDefaults = 0xF6,
    Reset = 0xFF
}

#[repr(u8)]
enum MouseCommandData {
    SetSampleRate = 0xF3,
}

bitflags! {
    flags MousePacketFlags: u8 {
        const LEFT_BUTTON = 1,
        const RIGHT_BUTTON = 1 << 1,
        const MIDDLE_BUTTON = 1 << 2,
        const ALWAYS_ON = 1 << 3,
        const X_SIGN = 1 << 4,
        const Y_SIGN = 1 << 5,
        const X_OVERFLOW = 1 << 6,
        const Y_OVERFLOW = 1 << 7
    }
}

pub struct Ps2 {
    data: Pio<u8>,
    status: ReadOnly<Pio<u8>>,
    command: WriteOnly<Pio<u8>>,
    mouse: [u8; 4],
    mouse_i: usize,
    mouse_extra: bool,
    mouse_x: usize,
    mouse_y: usize
}

impl Ps2 {
    const fn new() -> Ps2 {
        Ps2 {
            data: Pio::new(0x60),
            status: ReadOnly::new(Pio::new(0x64)),
            command: WriteOnly::new(Pio::new(0x64)),
            mouse: [0; 4],
            mouse_i: 0,
            mouse_extra: false,
            mouse_x: 0,
            mouse_y: 0
        }
    }

    fn status(&mut self) -> StatusFlags {
        StatusFlags::from_bits_truncate(self.status.read())
    }

    fn wait_write(&mut self) {
        while self.status().contains(INPUT_FULL) {}
    }

    fn wait_read(&mut self) {
        while ! self.status().contains(OUTPUT_FULL) {}
    }

    fn flush_read(&mut self) {
        while self.status().contains(OUTPUT_FULL) {
            print!("FLUSH: {:X}\n", self.data.read());
        }
    }

    fn command(&mut self, command: Command) {
        self.wait_write();
        self.command.write(command as u8);
    }

    fn read(&mut self) -> u8 {
        self.wait_read();
        self.data.read()
    }

    fn write(&mut self, data: u8) {
        self.wait_write();
        self.data.write(data);
    }

    fn config(&mut self) -> ConfigFlags {
        self.command(Command::ReadConfig);
        ConfigFlags::from_bits_truncate(self.read())
    }

    fn set_config(&mut self, config: ConfigFlags) {
        self.command(Command::WriteConfig);
        self.write(config.bits());
    }

    fn keyboard_command(&mut self, command: KeyboardCommand) -> u8 {
        self.write(command as u8);
        self.read()
    }

    fn mouse_command(&mut self, command: MouseCommand) -> u8 {
        self.command(Command::WriteSecond);
        self.write(command as u8);
        self.read()
    }

    fn mouse_command_data(&mut self, command: MouseCommandData, data: u8) -> u8 {
        self.command(Command::WriteSecond);
        self.write(command as u8);
        self.read();
        self.command(Command::WriteSecond);
        self.write(data as u8);
        self.read()
    }

    fn init(&mut self) {
        // Disable devices
        self.command(Command::DisableFirst);
        self.command(Command::DisableSecond);

        // Clear remaining data
        self.flush_read();

        // Disable clocks, disable interrupts, and disable translate
        {
            let mut config = self.config();
            config.insert(FIRST_DISABLED);
            config.insert(SECOND_DISABLED);
            config.remove(FIRST_TRANSLATE);
            config.remove(FIRST_INTERRUPT);
            config.remove(SECOND_INTERRUPT);
            self.set_config(config);
        }

        // Perform the self test
        self.command(Command::TestController);
        let self_test = self.read();
        if self_test != 0x55 {
            // TODO: Do reset on failure
            print!("PS/2 Self Test Failure: {:X}\n", self_test);
            return;
        }

        // Enable devices
        self.command(Command::EnableFirst);
        self.command(Command::EnableSecond);

        // Reset and enable scanning on keyboard
        // TODO: Check for ack
        print!("KEYBOARD RESET {:X}\n", self.keyboard_command(KeyboardCommand::Reset));
        print!("KEYBOARD RESET RESULT {:X} == 0xAA\n", self.read());
        self.flush_read();

        // Reset and enable scanning on mouse
        // TODO: Check for ack
        print!("MOUSE RESET {:X}\n", self.mouse_command(MouseCommand::Reset));
        print!("MOUSE RESET RESULT {:X} == 0xAA\n", self.read());
        print!("MOUSE RESET ID {:X} == 0x00\n", self.read());
        self.flush_read();

        // Enable extra packet on mouse
        print!("SAMPLE 200 {:X}\n", self.mouse_command_data(MouseCommandData::SetSampleRate, 200));
        print!("SAMPLE 100 {:X}\n", self.mouse_command_data(MouseCommandData::SetSampleRate, 100));
        print!("SAMPLE 80 {:X}\n", self.mouse_command_data(MouseCommandData::SetSampleRate, 80));
        print!("GET ID {:X}\n", self.mouse_command(MouseCommand::GetDeviceId));
        let mouse_id = self.read();
        print!("MOUSE ID: {:X} == 0x03\n", mouse_id);
        self.mouse_extra = mouse_id == 3;

        // Enable extra buttons, TODO
        /*
        if self.mouse_extra {
            print!("SAMPLE 200 {:X}\n", self.mouse_command_data(MouseCommandData::SetSampleRate, 200));
            print!("SAMPLE 200 {:X}\n", self.mouse_command_data(MouseCommandData::SetSampleRate, 200));
            print!("SAMPLE 80 {:X}\n", self.mouse_command_data(MouseCommandData::SetSampleRate, 80));
            print!("GET ID {:X}\n", self.mouse_command(MouseCommand::GetDeviceId));
            let mouse_id = self.read();
            print!("MOUSE ID: {:X} == 0x04\n", mouse_id);
        }
        */

        // Set sample rate to maximum
        print!("SAMPLE 200 {:X}\n", self.mouse_command_data(MouseCommandData::SetSampleRate, 200));

        // Enable data reporting
        print!("KEYBOARD ENABLE {:X}\n", self.keyboard_command(KeyboardCommand::EnableReporting));
        print!("MOUSE ENABLE {:X}\n", self.mouse_command(MouseCommand::EnableReporting));

        // Enable clocks and interrupts
        {
            let mut config = self.config();
            config.remove(FIRST_DISABLED);
            config.remove(SECOND_DISABLED);
            config.insert(FIRST_INTERRUPT);
            config.insert(SECOND_INTERRUPT);
            self.set_config(config);
        }
    }

    pub fn on_keyboard(&mut self) {
        let data = self.data.read();
        print!("KEY {:X}\n", data);
    }

    pub fn on_mouse(&mut self) {
        self.mouse[self.mouse_i] = self.data.read();
        self.mouse_i += 1;
        if self.mouse_i >= self.mouse.len() || (!self.mouse_extra && self.mouse_i >= 3) {
            self.mouse_i = 0;

            let flags = MousePacketFlags::from_bits_truncate(self.mouse[0]);

            let mut dx = self.mouse[1] as isize;
            if flags.contains(X_SIGN) {
                dx -= 0x100;
            }

            let mut dy = self.mouse[2] as isize;
            if flags.contains(Y_SIGN) {
                dy -= 0x100;
            }

            let _extra = if self.mouse_extra {
                self.mouse[3]
            } else {
                0
            };

            //print!("MOUSE {:?}, {}, {}, {}\n", flags, dx, dy, extra);

            if let Some(ref mut display) = *super::display::DISPLAY.lock() {
                self.mouse_x = cmp::max(0, cmp::min(display.width as isize - 1, self.mouse_x as isize + dx)) as usize;
                self.mouse_y = cmp::max(0, cmp::min(display.height as isize - 1, self.mouse_y as isize - dy)) as usize;
                let offset = self.mouse_y * display.width + self.mouse_x;
                display.onscreen[offset as usize] = 0xFF0000;
            }
        }
    }
}
<commit_msg>Assertions in PS/2 driver<commit_after>use core::cmp;
use spin::Mutex;

use io::{Io, Pio, ReadOnly, WriteOnly};

pub static PS2: Mutex<Ps2> = Mutex::new(Ps2::new());

pub unsafe fn init() {
    PS2.lock().init();
}

bitflags! {
    flags StatusFlags: u8 {
        const OUTPUT_FULL = 1,
        const INPUT_FULL = 1 << 1,
        const SYSTEM = 1 << 2,
        const COMMAND = 1 << 3,
        // Chipset specific
        const KEYBOARD_LOCK = 1 << 4,
        // Chipset specific
        const SECOND_OUTPUT_FULL = 1 << 5,
        const TIME_OUT = 1 << 6,
        const PARITY = 1 << 7
    }
}

bitflags! {
    flags ConfigFlags: u8 {
        const FIRST_INTERRUPT = 1,
        const SECOND_INTERRUPT = 1 << 1,
        const POST_PASSED = 1 << 2,
        // 1 << 3 should be zero
        const CONFIG_RESERVED_3 = 1 << 3,
        const FIRST_DISABLED = 1 << 4,
        const SECOND_DISABLED = 1 << 5,
        const FIRST_TRANSLATE = 1 << 6,
        // 1 << 7 should be zero
        const CONFIG_RESERVED_7 = 1 << 7,
    }
}

#[repr(u8)]
#[allow(dead_code)]
enum Command {
    ReadConfig = 0x20,
    WriteConfig = 0x60,
    DisableSecond = 0xA7,
    EnableSecond = 0xA8,
    TestSecond = 0xA9,
    TestController = 0xAA,
    TestFirst = 0xAB,
    Diagnostic = 0xAC,
    DisableFirst = 0xAD,
    EnableFirst = 0xAE,
    WriteSecond = 0xD4
}

#[repr(u8)]
#[allow(dead_code)]
enum KeyboardCommand {
    EnableReporting = 0xF4,
    SetDefaults = 0xF6,
    Reset = 0xFF
}

#[repr(u8)]
#[allow(dead_code)]
enum MouseCommand {
    GetDeviceId = 0xF2,
    EnableReporting = 0xF4,
    SetDefaults = 0xF6,
    Reset = 0xFF
}

#[repr(u8)]
enum MouseCommandData {
    SetSampleRate = 0xF3,
}

bitflags! {
    flags MousePacketFlags: u8 {
        const LEFT_BUTTON = 1,
        const RIGHT_BUTTON = 1 << 1,
        const MIDDLE_BUTTON = 1 << 2,
        const ALWAYS_ON = 1 << 3,
        const X_SIGN = 1 << 4,
        const Y_SIGN = 1 << 5,
        const X_OVERFLOW = 1 << 6,
        const Y_OVERFLOW = 1 << 7
    }
}

pub struct Ps2 {
    data: Pio<u8>,
    status: ReadOnly<Pio<u8>>,
    command: WriteOnly<Pio<u8>>,
    mouse: [u8; 4],
    mouse_i: usize,
    mouse_extra: bool,
    mouse_x: usize,
    mouse_y: usize
}

impl Ps2 {
    const fn new() -> Ps2 {
        Ps2 {
            data: Pio::new(0x60),
            status: ReadOnly::new(Pio::new(0x64)),
            command: WriteOnly::new(Pio::new(0x64)),
            mouse: [0; 4],
            mouse_i: 0,
            mouse_extra: false,
            mouse_x: 0,
            mouse_y: 0
        }
    }

    fn status(&mut self) -> StatusFlags {
        StatusFlags::from_bits_truncate(self.status.read())
    }

    fn wait_write(&mut self) {
        while self.status().contains(INPUT_FULL) {}
    }

    fn wait_read(&mut self) {
        while ! self.status().contains(OUTPUT_FULL) {}
    }

    fn flush_read(&mut self) {
        while self.status().contains(OUTPUT_FULL) {
            print!("FLUSH: {:X}\n", self.data.read());
        }
    }

    fn command(&mut self, command: Command) {
        self.wait_write();
        self.command.write(command as u8);
    }

    fn read(&mut self) -> u8 {
        self.wait_read();
        self.data.read()
    }

    fn write(&mut self, data: u8) {
        self.wait_write();
        self.data.write(data);
    }

    fn config(&mut self) -> ConfigFlags {
        self.command(Command::ReadConfig);
        ConfigFlags::from_bits_truncate(self.read())
    }

    fn set_config(&mut self, config: ConfigFlags) {
        self.command(Command::WriteConfig);
        self.write(config.bits());
    }

    fn keyboard_command(&mut self, command: KeyboardCommand) -> u8 {
        self.write(command as u8);
        self.read()
    }

    fn mouse_command(&mut self, command: MouseCommand) -> u8 {
        self.command(Command::WriteSecond);
        self.write(command as u8);
        self.read()
    }

    fn mouse_command_data(&mut self, command: MouseCommandData, data: u8) -> u8 {
        self.command(Command::WriteSecond);
        self.write(command as u8);
        self.read();
        self.command(Command::WriteSecond);
        self.write(data as u8);
        self.read()
    }

    fn init(&mut self) {
        // Disable devices
        self.command(Command::DisableFirst);
        self.command(Command::DisableSecond);

        // Clear remaining data
        self.flush_read();

        // Disable clocks, disable interrupts, and disable translate
        {
            let mut config = self.config();
            config.insert(FIRST_DISABLED);
            config.insert(SECOND_DISABLED);
            config.remove(FIRST_TRANSLATE);
            config.remove(FIRST_INTERRUPT);
            config.remove(SECOND_INTERRUPT);
            self.set_config(config);
        }

        // Perform the self test
        self.command(Command::TestController);
        assert_eq!(self.read(), 0x55);

        // Enable devices
        self.command(Command::EnableFirst);
        self.command(Command::EnableSecond);

        // Reset and enable scanning on keyboard
        assert_eq!(self.keyboard_command(KeyboardCommand::Reset), 0xFA);
        assert_eq!(self.read(), 0xAA);
        self.flush_read();

        // Reset and enable scanning on mouse
        // TODO: Check for ack
        assert_eq!(self.mouse_command(MouseCommand::Reset), 0xFA);
        assert_eq!(self.read(), 0xAA);
        assert_eq!(self.read(), 0x00);
        self.flush_read();

        // Enable extra packet on mouse
        assert_eq!(self.mouse_command_data(MouseCommandData::SetSampleRate, 200), 0xFA);
        assert_eq!(self.mouse_command_data(MouseCommandData::SetSampleRate, 100), 0xFA);
        assert_eq!(self.mouse_command_data(MouseCommandData::SetSampleRate, 80), 0xFA);
        assert_eq!(self.mouse_command(MouseCommand::GetDeviceId), 0xFA);
        let mouse_id = self.read();
        self.mouse_extra = mouse_id == 3;

        // Enable extra buttons, TODO
        /*
        if self.mouse_extra {
            print!("SAMPLE 200 {:X}\n", self.mouse_command_data(MouseCommandData::SetSampleRate, 200));
            print!("SAMPLE 200 {:X}\n", self.mouse_command_data(MouseCommandData::SetSampleRate, 200));
            print!("SAMPLE 80 {:X}\n", self.mouse_command_data(MouseCommandData::SetSampleRate, 80));
            print!("GET ID {:X}\n", self.mouse_command(MouseCommand::GetDeviceId));
            let mouse_id = self.read();
            print!("MOUSE ID: {:X} == 0x04\n", mouse_id);
        }
        */

        // Set sample rate to maximum
        assert_eq!(self.mouse_command_data(MouseCommandData::SetSampleRate, 200), 0xFA);

        // Enable data reporting
        assert_eq!(self.keyboard_command(KeyboardCommand::EnableReporting), 0xFA);
        assert_eq!(self.mouse_command(MouseCommand::EnableReporting), 0xFA);

        // Enable clocks and interrupts
        {
            let mut config = self.config();
            config.remove(FIRST_DISABLED);
            config.remove(SECOND_DISABLED);
            config.insert(FIRST_INTERRUPT);
            config.insert(SECOND_INTERRUPT);
            self.set_config(config);
        }
    }

    pub fn on_keyboard(&mut self) {
        let data = self.data.read();
        print!("KEY {:X}\n", data);
    }

    pub fn on_mouse(&mut self) {
        self.mouse[self.mouse_i] = self.data.read();
        self.mouse_i += 1;
        if self.mouse_i >= self.mouse.len() || (!self.mouse_extra && self.mouse_i >= 3) {
            self.mouse_i = 0;

            let flags = MousePacketFlags::from_bits_truncate(self.mouse[0]);

            assert!(flags.contains(ALWAYS_ON));

            if ! flags.contains(X_OVERFLOW) && ! flags.contains(Y_OVERFLOW) {
                let mut dx = self.mouse[1] as isize;
                if flags.contains(X_SIGN) {
                    dx -= 0x100;
                }

                let mut dy = self.mouse[2] as isize;
                if flags.contains(Y_SIGN) {
                    dy -= 0x100;
                }

                let _extra = if self.mouse_extra {
                    self.mouse[3]
                } else {
                    0
                };

                //print!("MOUSE {:?}, {}, {}, {}\n", flags, dx, dy, extra);

                if let Some(ref mut display) = *super::display::DISPLAY.lock() {
                    self.mouse_x = cmp::max(0, cmp::min(display.width as isize - 1, self.mouse_x as isize + dx)) as usize;
                    self.mouse_y = cmp::max(0, cmp::min(display.height as isize - 1, self.mouse_y as isize - dy)) as usize;
                    let offset = self.mouse_y * display.width + self.mouse_x;
                    display.onscreen[offset as usize] = 0xFF0000;
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>%-------------------------------------------------------------
% Nonlinear New Keynesian Model
% Reference: Foerster, Rubio-Ramirez, Waggoner and Zha (2013)
% Perturbation Methods for Markov Switching Models.
%-------------------------------------------------------------
endogenous PAI, "Inflation", Y, "Output gap", R, "Interest rate"

exogenous EPS_R	"Monetary policy shock"

parameters betta, eta, kappa, mu_bar, rhor sigr a_tp_1_2, a_tp_2_1

parameters(a,2) mu, psi

model
	1-betta*(1-.5*kappa*(PAI-1)^2)*Y*R/((1-.5*kappa*(PAI{+1}-1)^2)*Y{+1}*exp(mu)*PAI{+1});

	1-eta+eta*(1-.5*kappa*(PAI-1)^2)*Y+betta*kappa*(1-.5*kappa*(PAI-1)^2)*(PAI{+1}-1)*PAI{+1}/(1-.5*kappa*(PAI{+1}-1)^2)
	-kappa*(PAI-1)*PAI;

	(R{-1}/steady_state(R))^rhor*(PAI/steady_state(PAI))^((1-rhor)*psi)*exp(sigr*EPS_R)-R/steady_state(R);

steady_state_model(unique,imposed)
    PAI=1;
    Y=(eta-1)/eta;
    R=exp(mu_bar)/betta*PAI;

parameterization
	a_tp_1_2,1-.9;
	a_tp_2_1,1-.9;
	betta, .99;
	kappa, 161;
	eta, 10;
	rhor, .8;
	sigr, 0.0025;
	mu_bar,0.02;
	mu(a,1), 0.03;
	mu(a,2), 0.01;
	psi(a,1), 3.1;
	psi(a,2), 0.9;
<commit_msg>adapted sstate for new syntax<commit_after>%-------------------------------------------------------------
% Nonlinear New Keynesian Model
% Reference: Foerster, Rubio-Ramirez, Waggoner and Zha (2013)
% Perturbation Methods for Markov Switching Models.
%-------------------------------------------------------------
endogenous PAI, "Inflation", Y, "Output gap", R, "Interest rate"

exogenous EPS_R	"Monetary policy shock"

parameters betta, eta, kappa, mu_bar, rhor sigr a_tp_1_2, a_tp_2_1

parameters(a,2) mu, psi

model
	1-betta*(1-.5*kappa*(PAI-1)^2)*Y*R/((1-.5*kappa*(PAI{+1}-1)^2)*Y{+1}*exp(mu)*PAI{+1});

	1-eta+eta*(1-.5*kappa*(PAI-1)^2)*Y+betta*kappa*(1-.5*kappa*(PAI-1)^2)*(PAI{+1}-1)*PAI{+1}/(1-.5*kappa*(PAI{+1}-1)^2)
	-kappa*(PAI-1)*PAI;

	(R{-1}/steady_state(R))^rhor*(PAI/steady_state(PAI))^((1-rhor)*psi)*exp(sigr*EPS_R)-R/steady_state(R);

steady_state_model
    PAI=1;
    Y=(eta-1)/eta;
    R=exp(mu_bar)/betta*PAI;

parameterization
	a_tp_1_2,1-.9;
	a_tp_2_1,1-.9;
	betta, .99;
	kappa, 161;
	eta, 10;
	rhor, .8;
	sigr, 0.0025;
	mu_bar,0.02;
	mu(a,1), 0.03;
	mu(a,2), 0.01;
	psi(a,1), 3.1;
	psi(a,2), 0.9;
<|endoftext|>


<commit_before>use crate::ast;
use crate::evaluation::object;
use crate::evaluation::object::ObjectT;
use crate::lexer;
use crate::parser;
use crate::token;

// TODO: Consider this to be a hack.
//
// We have 3 types of untis now.
// 1. Program
// 2. Statement
// 3. Expression
//
// To generalize eval API we would wrap program in a special
// wrapper enum, which knows how to handle every unit.

#[derive(Debug)]
pub enum WrappedNode {
    P(ast::Program),
    S(token::Statements),
    E(token::Expression),
    B(token::BlockStatement), // special case for block statement
}
pub type WN = WrappedNode; // Just alias to avoid typing :)

// Option number 2.
// Write 3 different functions to work with 3
// different types of values.
//
// It's super bad design decision, cause I'll have to
// use different evalers in my REPL implementation,
// but I don't see the design clearly for now.

// For now we'll stick with design approach with WrappedNode.
// Perhaps use From/Into modification?

// To save some typing in creating basic object
// we will use constants.

pub const NIL: object::Object = object::Object::Nil(object::Nil {});
pub const TRUE: object::Object = object::Object::Boolean(object::Boolean { value: true });
pub const FALSE: object::Object = object::Object::Boolean(object::Boolean { value: false });

pub fn eval(node: WN) -> object::Object {
    match node {
        WN::P(program) => eval_program(program),
        WN::S(statement) => match statement {
            token::Statements::ExpressionStatement(expr) => eval(WN::E(expr.expression)),
            token::Statements::LetStatement(_) => panic!("don't know how to handle let statement"),
            token::Statements::ReturnStatement(rs) => {
                let val = eval(WN::E(rs.return_value));
                object::Object::ReturnValue(Box::new(object::ReturnValue { value: val }))
            }
        },
        WN::B(block) => eval_block_statement(block.statements),
        WN::E(expression) => match expression {
            token::Expression::IntegerLiteral(il) => {
                object::Object::Integer(object::Integer { value: il.value })
            }
            token::Expression::Identifier(_i) => panic!("don't how to handle identifier"),
            token::Expression::PrefixExpression(pe) => {
                let right = eval(WN::E(pe.right));
                match pe.operator.as_ref() {
                    "!" => match right {
                        TRUE => FALSE,
                        FALSE => TRUE,
                        NIL => TRUE,
                        _ => FALSE,
                    },
                    "-" => match right {
                        object::Object::Integer(int_obj) => {
                            object::Object::Integer(object::Integer {
                                value: -int_obj.value,
                            })
                        }
                        _ => new_error(format!("unknown operator: -{}", right.object_type()))
                    },
                    _ => new_error(format!("unknown operator: {} {}", pe.operator, right.object_type()))
                }
            }
            token::Expression::InfixExpression(ie) => {
                let left = eval(WN::E(ie.left));
                let right = eval(WN::E(ie.right));
                if let (object::Object::Integer(left_obj), object::Object::Integer(right_obj)) =
                    (left.clone(), right.clone())
                {
                    match ie.operator.as_ref() {
                        "+" => object::Object::Integer(object::Integer {
                            value: left_obj.value + right_obj.value,
                        }),
                        "-" => object::Object::Integer(object::Integer {
                            value: left_obj.value - right_obj.value,
                        }),
                        "*" => object::Object::Integer(object::Integer {
                            value: left_obj.value * right_obj.value,
                        }),
                        "/" => object::Object::Integer(object::Integer {
                            value: left_obj.value / right_obj.value,
                        }),
                        "<" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value < right_obj.value
                        }),
                        ">" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value > right_obj.value
                        }),
                        "==" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value == right_obj.value
                        }),
                        "!=" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value != right_obj.value
                        }),
                        _ => new_error(format!("unknown operator: INTEGER {} INTEGER", ie.operator))
                    }
                    // what if not boolean?
                    // TODO: maybe I should cover every option every time?
                } else if let (object::Object::Boolean(left_obj), object::Object::Boolean(right_obj)) =
                    (left.clone(), right.clone()) {
                        match ie.operator.as_ref() {
                            "==" => object::Object::Boolean(object::Boolean {
                                value: left_obj.value == right_obj.value,
                            }),
                            "!=" => object::Object::Boolean(object::Boolean {
                                value: left_obj.value != right_obj.value,
                            }),
                            _ => new_error(format!("unknown operator: BOOLEAN {} BOOLEAN", ie.operator))
                        }
                } else {
                        new_error(format!("type mismatch: {} {} {}", left.object_type(), ie.operator, right.object_type()))
                }
            }
            token::Expression::Boolean(b) => {
                // TODO: Check possible perf optimization? Needed?
                // Reuse TRUE and FALSE I mean
                object::Object::Boolean(object::Boolean { value: b.value })
            }
            token::Expression::IfExpression(ie) => {
                let condition = eval(WN::E(ie.condition));

                if is_truthy(condition) {
                    eval(WN::B(ie.consequence))
                } else {
                    match ie.alternative {
                        Some(alt) => eval(WN::B(alt)),
                        None => NIL,
                    }
                }
            },
            token::Expression::FunctionLiteral(_fl) => {
                panic!("don't how to handle function literal")
            }
            token::Expression::CallExpression(_ce) => panic!("don't how to handle call expression"),
        },
    }
}

// ************************************************
// ************************************************
// *********   HELPER FUNCTIONS   *****************
// ************************************************
// ************************************************

fn is_truthy(cond: object::Object) -> bool {
    match cond {
        NIL => false,
        TRUE => true,
        FALSE => false,
        _ => true,
    }
}

fn new_error(formated_string: String) -> object::Object {
    object::Object::Error(object::Error {
        message: formated_string,
    })
}

pub fn eval_program(program: ast::Program) -> object::Object {
    // TODO: wow, impressive, I see your skill
    let statements = program.statements;
    let mut statements = statements.into_iter();
    let mut size = statements.len();

    let result = loop {
        size = size - 1;
        let statement = match statements.next() {
            Some(statement) => statement,
            None => panic!("eval_statement is badly broken"),
        };

        let result = eval(WN::S(statement));

        // if statement is rendered into Return Value we have to
        // interupt the execution and return this value.
        match result.clone() {
            object::Object::ReturnValue(ret_val) => break ret_val.value,
            err @ object::Object::Error(_) => break err,
            otherwise => (),
        };

        if size == 0 {
            break result;
        };
    };

    return result;
}

pub fn eval_block_statement(statements: Vec<token::Statements>) -> object::Object {
    // TODO: wow, impressive, I see your skill
    let mut statements = statements.into_iter();
    let mut size = statements.len();

    let result = loop {
        size = size - 1;
        let statement = match statements.next() {
            Some(statement) => statement,
            None => panic!("eval_statement is badly broken"),
        };

        let result = eval(WN::S(statement));

        // if statement is rendered into Return Value we have to
        // interupt the execution and return this value.
        match result.clone() {
            // Do not unwrap return value. It will be unwraped at highest scope.
            val @ object::Object::ReturnValue(_) => break val,
            err @ object::Object::Error(_) => break err,
            otherwise => (),
        };

        if size == 0 {
            break result;
        };
    };

    return result;
}

#[cfg(test)]
mod tests {
    use crate::evaluation;
    use crate::lexer;
    use crate::parser;
    use std::collections::HashMap;

    #[test]
    fn test_eval_integer_expression() {
        let pairs = vec![("1".to_string(), 1), ("2".to_string(), 2)];

        pairs.into_iter().for_each(|(value, expected)| {
            let evaluated = run_eval(value);
            assert_integer_object(evaluated, expected);
        })
    }

    #[test]
    fn test_eval_boolean_expression() {
        let pairs = vec![
            ("true".to_string(), true),
            ("false".to_string(), false),
            ("1 < 2".to_string(), true),
            ("1 > 2".to_string(), false),
            ("1 < 1".to_string(), false),
            ("1 < 1".to_string(), false),
            ("1 == 1".to_string(), true),
            ("1 != 1".to_string(), false),
            ("1 == 2".to_string(), false),
            ("1 != 2".to_string(), true),
            ("true == true".to_string(), true),
            ("false == false".to_string(), true),
            ("true == false".to_string(), false),
        ];

        pairs.into_iter().for_each(|(value, expected)| {
            let evaluated = run_eval(value);
            assert_boolean_object(evaluated, expected);
        })
    }

    #[test]
    fn test_bang_operator() {
        let pairs = vec![
            ("!true".to_string(), false),
            ("!false".to_string(), true),
            ("!1".to_string(), false),
            ("!!true".to_string(), true),
            ("!!false".to_string(), false),
            ("!!1".to_string(), true),
        ];

        for (value, expected) in pairs {
            assert_boolean_object(run_eval(value), expected)
        }
    }

    #[test]
    fn test_prefix_negate_operator() {
        let pairs = vec![("-1".to_string(), -1), ("--1".to_string(), 1)];

        for (value, expected) in pairs {
            assert_integer_object(run_eval(value), expected)
        }
    }

    #[test]
    fn test_integer_arithmetic() {
        let pairs = vec![
            ("1 + 1 + 1".to_string(), 3),
            ("2 * 2 * 2".to_string(), 8),
            ("4 / 2 * 2".to_string(), 4),
            ("1 + 3 * 2".to_string(), 7),
        ];

        for (value, expected) in pairs {
            assert_integer_object(run_eval(value), expected)
        }
    }

    #[test]
    fn test_if_expression() {
        let pairs = vec![
            ("if (true) { 10 }".to_string(), Some(10)),
            ("if (false) { 10 }".to_string(), None),
            ("if (1) { 10 }".to_string(), Some(10)),
            ("if (1 < 2) { 10 }".to_string(), Some(10)),
            ("if (1 > 2) { 10 }".to_string(), None),
            ("if (1 < 2) { 10 } else { 20 }".to_string(), Some(10)),
            ("if (1 > 2) { 10 } else { 20 }".to_string(), Some(20)),
        ];

        for (expression, expected) in pairs {
            let evaluated = run_eval(expression);
            match expected {
                Some(val) => assert_integer_object(evaluated, val),
                None => assert_eq!(evaluated, evaluation::evaluator::NIL),
            }
        }
    }

    #[test]
    fn test_return_statement() {
        let pairs = vec![
            ("return 10;".to_string(), 10),
            ("return 10;".to_string(), 10),
            ("return 2 * 2; 69;".to_string(), 4),
            ("4; return 2 * 3; 8".to_string(), 6),
            (r###"
               if (2 > 1) {
                 if (2 > 1) {
                   return 999;
                 }

                 return 888;
               }
             "###.to_string(), 999)
        ];

        for (expression, expected) in pairs {
            let evaluated = run_eval(expression);
            assert_integer_object(evaluated, expected)
        }
    }

    #[test]
    fn test_error_handling() {
        let pairs = vec![
            ("2 + true;".to_string(), "type mismatch: INTEGER + BOOLEAN".to_string()),
            ("2 + true; 999".to_string(), "type mismatch: INTEGER + BOOLEAN".to_string()),
            ("-true;".to_string(), "unknown operator: -BOOLEAN".to_string()),
            ("false + true;".to_string(), "unknown operator: BOOLEAN + BOOLEAN".to_string()),
            (r###"
               if (2 > 1) {
                 if (2 > 1) {
                   return true + false;
                 }

                 return 888;
               }
             "###.to_string(), "unknown operator: BOOLEAN + BOOLEAN".to_string()),
        ];

        for (expression, expected) in pairs {
            let evaluated = run_eval(expression);
            match evaluated {
                evaluation::object::Object::Error(err) => assert_eq!(err.message, expected),
                _ => panic!("expected error message, got {:?}", evaluated),
            }
        }
    }

    fn run_eval(source_code: String) -> evaluation::object::Object {
        let lexer = lexer::Lexer::new(source_code);
        let mut parser = parser::Parser::new(lexer);

        let mut lambda_parsers = parser::LambdaParsers {
            prefix_parse_fns: HashMap::new(),
            infix_parse_fns: HashMap::new(),
        };
        lambda_parsers.register_parsers();

        let program = parser.parse_program(&lambda_parsers);

        evaluation::evaluator::eval(evaluation::evaluator::WN::P(program))
    }

    fn assert_integer_object(object: evaluation::object::Object, expected: i32) {
        let integer = match object {
            evaluation::object::Object::Integer(int) => int,
            otherwise => panic!("expected integer, got {:?}", otherwise),
        };

        assert_eq!(integer.value, expected)
    }

    fn assert_boolean_object(object: evaluation::object::Object, expected: bool) {
        let bool = match object {
            evaluation::object::Object::Boolean(b) => b,
            otherwise => panic!("expected boolean, got {:?}", otherwise),
        };

        assert_eq!(bool.value, expected)
    }
}
<commit_msg>add error early return in eval<commit_after>use crate::ast;
use crate::evaluation::object;
use crate::evaluation::object::ObjectT;
use crate::lexer;
use crate::parser;
use crate::token;

// TODO: Consider this to be a hack.
//
// We have 3 types of untis now.
// 1. Program
// 2. Statement
// 3. Expression
//
// To generalize eval API we would wrap program in a special
// wrapper enum, which knows how to handle every unit.

#[derive(Debug)]
pub enum WrappedNode {
    P(ast::Program),
    S(token::Statements),
    E(token::Expression),
    B(token::BlockStatement), // special case for block statement
}
pub type WN = WrappedNode; // Just alias to avoid typing :)

// Option number 2.
// Write 3 different functions to work with 3
// different types of values.
//
// It's super bad design decision, cause I'll have to
// use different evalers in my REPL implementation,
// but I don't see the design clearly for now.

// For now we'll stick with design approach with WrappedNode.
// Perhaps use From/Into modification?

// To save some typing in creating basic object
// we will use constants.

pub const NIL: object::Object = object::Object::Nil(object::Nil {});
pub const TRUE: object::Object = object::Object::Boolean(object::Boolean { value: true });
pub const FALSE: object::Object = object::Object::Boolean(object::Boolean { value: false });

pub fn eval(node: WN) -> object::Object {
    match node {
        WN::P(program) => eval_program(program),
        WN::S(statement) => match statement {
            token::Statements::ExpressionStatement(expr) => eval(WN::E(expr.expression)),
            token::Statements::LetStatement(_) => panic!("don't know how to handle let statement"),
            token::Statements::ReturnStatement(rs) => {
                let val = eval(WN::E(rs.return_value));
                // TODO: Do I really need this?
                // Maybe move this plumbing somewhere else?
                if is_error(&val) {
                    return val;
                }
                object::Object::ReturnValue(Box::new(object::ReturnValue { value: val }))
            }
        },
        WN::B(block) => eval_block_statement(block.statements),
        WN::E(expression) => match expression {
            token::Expression::IntegerLiteral(il) => {
                object::Object::Integer(object::Integer { value: il.value })
            }
            token::Expression::Identifier(_i) => panic!("don't how to handle identifier"),
            token::Expression::PrefixExpression(pe) => {
                let right = eval(WN::E(pe.right));
                if is_error(&right) {
                    return right;
                }
                match pe.operator.as_ref() {
                    "!" => match right {
                        TRUE => FALSE,
                        FALSE => TRUE,
                        NIL => TRUE,
                        _ => FALSE,
                    },
                    "-" => match right {
                        object::Object::Integer(int_obj) => {
                            object::Object::Integer(object::Integer {
                                value: -int_obj.value,
                            })
                        }
                        _ => new_error(format!("unknown operator: -{}", right.object_type()))
                    },
                    _ => new_error(format!("unknown operator: {} {}", pe.operator, right.object_type()))
                }
            }
            token::Expression::InfixExpression(ie) => {
                let left = eval(WN::E(ie.left));
                if is_error(&left) {
                    return left;
                }
                let right = eval(WN::E(ie.right));
                if is_error(&right) {
                    return right;
                }
                if let (object::Object::Integer(left_obj), object::Object::Integer(right_obj)) =
                    (left.clone(), right.clone())
                {
                    match ie.operator.as_ref() {
                        "+" => object::Object::Integer(object::Integer {
                            value: left_obj.value + right_obj.value,
                        }),
                        "-" => object::Object::Integer(object::Integer {
                            value: left_obj.value - right_obj.value,
                        }),
                        "*" => object::Object::Integer(object::Integer {
                            value: left_obj.value * right_obj.value,
                        }),
                        "/" => object::Object::Integer(object::Integer {
                            value: left_obj.value / right_obj.value,
                        }),
                        "<" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value < right_obj.value
                        }),
                        ">" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value > right_obj.value
                        }),
                        "==" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value == right_obj.value
                        }),
                        "!=" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value != right_obj.value
                        }),
                        _ => new_error(format!("unknown operator: INTEGER {} INTEGER", ie.operator))
                    }
                    // what if not boolean?
                    // TODO: maybe I should cover every option every time?
                } else if let (object::Object::Boolean(left_obj), object::Object::Boolean(right_obj)) =
                    (left.clone(), right.clone()) {
                        match ie.operator.as_ref() {
                            "==" => object::Object::Boolean(object::Boolean {
                                value: left_obj.value == right_obj.value,
                            }),
                            "!=" => object::Object::Boolean(object::Boolean {
                                value: left_obj.value != right_obj.value,
                            }),
                            _ => new_error(format!("unknown operator: BOOLEAN {} BOOLEAN", ie.operator))
                        }
                } else {
                        new_error(format!("type mismatch: {} {} {}", left.object_type(), ie.operator, right.object_type()))
                }
            }
            token::Expression::Boolean(b) => {
                // TODO: Check possible perf optimization? Needed?
                // Reuse TRUE and FALSE I mean
                object::Object::Boolean(object::Boolean { value: b.value })
            }
            token::Expression::IfExpression(ie) => {
                let condition = eval(WN::E(ie.condition));
                if is_error(&condition) {
                    return condition;
                }

                if is_truthy(condition) {
                    eval(WN::B(ie.consequence))
                } else {
                    match ie.alternative {
                        Some(alt) => eval(WN::B(alt)),
                        None => NIL,
                    }
                }
            },
            token::Expression::FunctionLiteral(_fl) => {
                panic!("don't how to handle function literal")
            }
            token::Expression::CallExpression(_ce) => panic!("don't how to handle call expression"),
        },
    }
}

// ************************************************
// ************************************************
// *********   HELPER FUNCTIONS   *****************
// ************************************************
// ************************************************

fn is_truthy(cond: object::Object) -> bool {
    match cond {
        NIL => false,
        TRUE => true,
        FALSE => false,
        _ => true,
    }
}

fn is_error(potential_error: &object::Object) -> bool {
    match potential_error {
        object::Object::Error(_) => true,
        _ => false,
    }
}

fn new_error(formated_string: String) -> object::Object {
    object::Object::Error(object::Error {
        message: formated_string,
    })
}

pub fn eval_program(program: ast::Program) -> object::Object {
    // TODO: wow, impressive, I see your skill
    let statements = program.statements;
    let mut statements = statements.into_iter();
    let mut size = statements.len();

    let result = loop {
        size = size - 1;
        let statement = match statements.next() {
            Some(statement) => statement,
            None => panic!("eval_statement is badly broken"),
        };

        let result = eval(WN::S(statement));

        // if statement is rendered into Return Value we have to
        // interupt the execution and return this value.
        match result.clone() {
            object::Object::ReturnValue(ret_val) => break ret_val.value,
            err @ object::Object::Error(_) => break err,
            otherwise => (),
        };

        if size == 0 {
            break result;
        };
    };

    return result;
}

pub fn eval_block_statement(statements: Vec<token::Statements>) -> object::Object {
    // TODO: wow, impressive, I see your skill
    let mut statements = statements.into_iter();
    let mut size = statements.len();

    let result = loop {
        size = size - 1;
        let statement = match statements.next() {
            Some(statement) => statement,
            None => panic!("eval_statement is badly broken"),
        };

        let result = eval(WN::S(statement));

        // if statement is rendered into Return Value we have to
        // interupt the execution and return this value.
        match result.clone() {
            // Do not unwrap return value. It will be unwraped at highest scope.
            val @ object::Object::ReturnValue(_) => break val,
            err @ object::Object::Error(_) => break err,
            otherwise => (),
        };

        if size == 0 {
            break result;
        };
    };

    return result;
}

#[cfg(test)]
mod tests {
    use crate::evaluation;
    use crate::lexer;
    use crate::parser;
    use std::collections::HashMap;

    #[test]
    fn test_eval_integer_expression() {
        let pairs = vec![("1".to_string(), 1), ("2".to_string(), 2)];

        pairs.into_iter().for_each(|(value, expected)| {
            let evaluated = run_eval(value);
            assert_integer_object(evaluated, expected);
        })
    }

    #[test]
    fn test_eval_boolean_expression() {
        let pairs = vec![
            ("true".to_string(), true),
            ("false".to_string(), false),
            ("1 < 2".to_string(), true),
            ("1 > 2".to_string(), false),
            ("1 < 1".to_string(), false),
            ("1 < 1".to_string(), false),
            ("1 == 1".to_string(), true),
            ("1 != 1".to_string(), false),
            ("1 == 2".to_string(), false),
            ("1 != 2".to_string(), true),
            ("true == true".to_string(), true),
            ("false == false".to_string(), true),
            ("true == false".to_string(), false),
        ];

        pairs.into_iter().for_each(|(value, expected)| {
            let evaluated = run_eval(value);
            assert_boolean_object(evaluated, expected);
        })
    }

    #[test]
    fn test_bang_operator() {
        let pairs = vec![
            ("!true".to_string(), false),
            ("!false".to_string(), true),
            ("!1".to_string(), false),
            ("!!true".to_string(), true),
            ("!!false".to_string(), false),
            ("!!1".to_string(), true),
        ];

        for (value, expected) in pairs {
            assert_boolean_object(run_eval(value), expected)
        }
    }

    #[test]
    fn test_prefix_negate_operator() {
        let pairs = vec![("-1".to_string(), -1), ("--1".to_string(), 1)];

        for (value, expected) in pairs {
            assert_integer_object(run_eval(value), expected)
        }
    }

    #[test]
    fn test_integer_arithmetic() {
        let pairs = vec![
            ("1 + 1 + 1".to_string(), 3),
            ("2 * 2 * 2".to_string(), 8),
            ("4 / 2 * 2".to_string(), 4),
            ("1 + 3 * 2".to_string(), 7),
        ];

        for (value, expected) in pairs {
            assert_integer_object(run_eval(value), expected)
        }
    }

    #[test]
    fn test_if_expression() {
        let pairs = vec![
            ("if (true) { 10 }".to_string(), Some(10)),
            ("if (false) { 10 }".to_string(), None),
            ("if (1) { 10 }".to_string(), Some(10)),
            ("if (1 < 2) { 10 }".to_string(), Some(10)),
            ("if (1 > 2) { 10 }".to_string(), None),
            ("if (1 < 2) { 10 } else { 20 }".to_string(), Some(10)),
            ("if (1 > 2) { 10 } else { 20 }".to_string(), Some(20)),
        ];

        for (expression, expected) in pairs {
            let evaluated = run_eval(expression);
            match expected {
                Some(val) => assert_integer_object(evaluated, val),
                None => assert_eq!(evaluated, evaluation::evaluator::NIL),
            }
        }
    }

    #[test]
    fn test_return_statement() {
        let pairs = vec![
            ("return 10;".to_string(), 10),
            ("return 10;".to_string(), 10),
            ("return 2 * 2; 69;".to_string(), 4),
            ("4; return 2 * 3; 8".to_string(), 6),
            (r###"
               if (2 > 1) {
                 if (2 > 1) {
                   return 999;
                 }

                 return 888;
               }
             "###.to_string(), 999)
        ];

        for (expression, expected) in pairs {
            let evaluated = run_eval(expression);
            assert_integer_object(evaluated, expected)
        }
    }

    #[test]
    fn test_error_handling() {
        let pairs = vec![
            ("2 + true;".to_string(), "type mismatch: INTEGER + BOOLEAN".to_string()),
            ("2 + true; 999".to_string(), "type mismatch: INTEGER + BOOLEAN".to_string()),
            ("-true;".to_string(), "unknown operator: -BOOLEAN".to_string()),
            ("false + true;".to_string(), "unknown operator: BOOLEAN + BOOLEAN".to_string()),
            (r###"
               if (2 > 1) {
                 if (2 > 1) {
                   return true + false;
                 }

                 return 888;
               }
             "###.to_string(), "unknown operator: BOOLEAN + BOOLEAN".to_string()),
        ];

        for (expression, expected) in pairs {
            let evaluated = run_eval(expression);
            match evaluated {
                evaluation::object::Object::Error(err) => assert_eq!(err.message, expected),
                _ => panic!("expected error message, got {:?}", evaluated),
            }
        }
    }

    fn run_eval(source_code: String) -> evaluation::object::Object {
        let lexer = lexer::Lexer::new(source_code);
        let mut parser = parser::Parser::new(lexer);

        let mut lambda_parsers = parser::LambdaParsers {
            prefix_parse_fns: HashMap::new(),
            infix_parse_fns: HashMap::new(),
        };
        lambda_parsers.register_parsers();

        let program = parser.parse_program(&lambda_parsers);

        evaluation::evaluator::eval(evaluation::evaluator::WN::P(program))
    }

    fn assert_integer_object(object: evaluation::object::Object, expected: i32) {
        let integer = match object {
            evaluation::object::Object::Integer(int) => int,
            otherwise => panic!("expected integer, got {:?}", otherwise),
        };

        assert_eq!(integer.value, expected)
    }

    fn assert_boolean_object(object: evaluation::object::Object, expected: bool) {
        let bool = match object {
            evaluation::object::Object::Boolean(b) => b,
            otherwise => panic!("expected boolean, got {:?}", otherwise),
        };

        assert_eq!(bool.value, expected)
    }
}
<|endoftext|>


<commit_before>use parking_lot::Mutex;
use std::cmp::{max, min};
use std::sync::Arc;

use gc::swiper::large::LargeSpace;
use gc::swiper::old::OldGen;
use gc::swiper::young::YoungGen;
use gc::swiper::CollectionKind;
use gc::{formatted_size, GEN_SIZE, M};
use timer;

pub fn choose_collection_kind(config: &SharedHeapConfig, young: &YoungGen) -> CollectionKind {
    let (eden_size, semi_size) = young.committed_size();
    let young_size = eden_size + semi_size;

    let rest = {
        let config = config.lock();
        config.old_limit - config.old_size
    };

    if rest < young_size {
        CollectionKind::Full
    } else {
        CollectionKind::Minor
    }
}

const MAX_YOUNG_SIZE: usize = 20 * M;

const INIT_HEAP_SIZE_RATIO: usize = 2;
const INIT_YOUNG_RATIO: usize = 4;
const INIT_SEMI_RATIO: usize = 3;

pub fn init(config: &mut HeapConfig) {
    assert!(config.min_heap_size <= config.max_heap_size);

    let young_size = min(MAX_YOUNG_SIZE, config.max_heap_size / INIT_YOUNG_RATIO);
    let young_size = max(young_size, GEN_SIZE);

    let semi_size = max(young_size / INIT_SEMI_RATIO, GEN_SIZE);
    let eden_size = young_size - semi_size;

    config.eden_size = eden_size;
    config.semi_size = semi_size;

    let max_old_limit = config.max_heap_size - young_size;
    let min_old_limit = if config.min_heap_size > young_size {
        config.min_heap_size - young_size
    } else {
        0
    };

    let old_limit = min(max_old_limit, config.max_heap_size / INIT_HEAP_SIZE_RATIO);
    let old_limit = max(old_limit, min_old_limit);

    config.old_size = 0;
    config.old_limit = old_limit;
}

pub fn start(config: &SharedHeapConfig, young: &YoungGen, old: &OldGen, large: &LargeSpace) {
    let mut config = config.lock();

    config.gc_start = timer::timestamp();
    config.start_object_size = object_size(young, old, large);
    config.start_memory_size = memory_size(young, old, large);
}

pub fn stop(
    config: &SharedHeapConfig,
    kind: CollectionKind,
    young: &YoungGen,
    old: &OldGen,
    large: &LargeSpace,
    verbose: bool,
) {
    let mut config = config.lock();

    let gc_end = timer::timestamp();
    config.gc_duration = timer::in_ms(gc_end - config.gc_start);

    config.end_object_size = object_size(young, old, large);
    config.end_memory_size = memory_size(young, old, large);

    assert!(young.eden_active().empty());
    let (eden_size, semi_size) = young.committed_size();
    let young_size = eden_size + semi_size;

    let old_size = old.committed_size() + large.committed_size();
    config.old_size = old_size;

    let max_old_limit = config.max_heap_size - young_size;
    config.old_limit = max_old_limit;

    match kind {
        CollectionKind::Minor => {
            config.total_minor_collections += 1;
            config.total_minor_pause += config.gc_duration;
        }

        CollectionKind::Full => {
            config.total_full_collections += 1;
            config.total_full_pause += config.gc_duration;
        }
    }

    if verbose {
        print(&*config, kind);
    }
}

fn print(config: &HeapConfig, kind: CollectionKind) {
    match kind {
        CollectionKind::Minor => {
            println!(
                "GC: {} {}/{} -> {}/{}; {:.2} ms; {} promoted; {} copied",
                kind,
                formatted_size(config.start_object_size),
                formatted_size(config.start_memory_size),
                formatted_size(config.end_object_size),
                formatted_size(config.end_memory_size),
                config.gc_duration,
                formatted_size(config.minor_promoted),
                formatted_size(config.minor_copied),
            );
        }

        CollectionKind::Full => {
            println!(
                "GC: {} {}/{} -> {}/{}; {:.2} ms",
                kind,
                formatted_size(config.start_object_size),
                formatted_size(config.start_memory_size),
                formatted_size(config.end_object_size),
                formatted_size(config.end_memory_size),
                config.gc_duration,
            );
        }
    }
}

fn object_size(young: &YoungGen, old: &OldGen, large: &LargeSpace) -> usize {
    young.active_size() + old.active_size() + large.committed_size()
}

fn memory_size(young: &YoungGen, old: &OldGen, large: &LargeSpace) -> usize {
    let (eden_size, semi_size) = young.committed_size();
    let young_size = eden_size + semi_size;

    young_size + old.committed_size() + large.committed_size()
}

pub struct HeapConfig {
    min_heap_size: usize,
    max_heap_size: usize,

    pub eden_size: usize,
    pub semi_size: usize,
    pub old_size: usize,
    pub old_limit: usize,

    gc_start: u64,
    gc_duration: f32,

    start_object_size: usize,
    start_memory_size: usize,
    end_object_size: usize,
    end_memory_size: usize,

    pub minor_promoted: usize,
    pub minor_copied: usize,

    pub total_minor_collections: usize,
    pub total_minor_pause: f32,
    pub total_full_collections: usize,
    pub total_full_pause: f32,
}

impl HeapConfig {
    pub fn new(min_heap_size: usize, max_heap_size: usize) -> HeapConfig {
        assert!(min_heap_size <= max_heap_size);

        HeapConfig {
            min_heap_size: min_heap_size,
            max_heap_size: max_heap_size,

            eden_size: 0,
            semi_size: 0,
            old_size: 0,
            old_limit: 0,

            gc_start: 0,
            gc_duration: 0f32,

            start_object_size: 0,
            start_memory_size: 0,
            end_object_size: 0,
            end_memory_size: 0,

            minor_promoted: 0,
            minor_copied: 0,

            total_minor_collections: 0,
            total_minor_pause: 0f32,
            total_full_collections: 0,
            total_full_pause: 0f32,
        }
    }

    pub fn grow_old(&mut self, size: usize) -> bool {
        if self.old_size + size <= self.old_limit {
            self.old_size += size;
            true
        } else {
            false
        }
    }
}

pub type SharedHeapConfig = Arc<Mutex<HeapConfig>>;
<commit_msg>align young size to generation<commit_after>use parking_lot::Mutex;
use std::cmp::{max, min};
use std::sync::Arc;

use gc::swiper::large::LargeSpace;
use gc::swiper::old::OldGen;
use gc::swiper::young::YoungGen;
use gc::swiper::CollectionKind;
use gc::{align_gen, formatted_size, GEN_SIZE, M};
use timer;

pub fn choose_collection_kind(config: &SharedHeapConfig, young: &YoungGen) -> CollectionKind {
    let (eden_size, semi_size) = young.committed_size();
    let young_size = eden_size + semi_size;

    let rest = {
        let config = config.lock();
        config.old_limit - config.old_size
    };

    if rest < young_size {
        CollectionKind::Full
    } else {
        CollectionKind::Minor
    }
}

const MAX_YOUNG_SIZE: usize = 20 * M;

const INIT_HEAP_SIZE_RATIO: usize = 2;
const INIT_YOUNG_RATIO: usize = 4;
const INIT_SEMI_RATIO: usize = 3;

pub fn init(config: &mut HeapConfig) {
    assert!(config.min_heap_size <= config.max_heap_size);

    let young_size = align_gen(config.max_heap_size / INIT_YOUNG_RATIO);
    let young_size = min(MAX_YOUNG_SIZE, young_size);
    let young_size = max(young_size, GEN_SIZE);

    let semi_size = align_gen(young_size / INIT_SEMI_RATIO);
    let semi_size = max(semi_size, GEN_SIZE);
    let eden_size = young_size - semi_size;

    config.eden_size = eden_size;
    config.semi_size = semi_size;

    let max_old_limit = config.max_heap_size - young_size;
    let min_old_limit = if config.min_heap_size > young_size {
        config.min_heap_size - young_size
    } else {
        0
    };

    let old_limit = min(max_old_limit, config.max_heap_size / INIT_HEAP_SIZE_RATIO);
    let old_limit = max(old_limit, min_old_limit);

    config.old_size = 0;
    config.old_limit = old_limit;
}

pub fn start(config: &SharedHeapConfig, young: &YoungGen, old: &OldGen, large: &LargeSpace) {
    let mut config = config.lock();

    config.gc_start = timer::timestamp();
    config.start_object_size = object_size(young, old, large);
    config.start_memory_size = memory_size(young, old, large);
}

pub fn stop(
    config: &SharedHeapConfig,
    kind: CollectionKind,
    young: &YoungGen,
    old: &OldGen,
    large: &LargeSpace,
    verbose: bool,
) {
    let mut config = config.lock();

    let gc_end = timer::timestamp();
    config.gc_duration = timer::in_ms(gc_end - config.gc_start);

    config.end_object_size = object_size(young, old, large);
    config.end_memory_size = memory_size(young, old, large);

    assert!(young.eden_active().empty());
    let (eden_size, semi_size) = young.committed_size();
    let young_size = eden_size + semi_size;

    let old_size = old.committed_size() + large.committed_size();
    config.old_size = old_size;

    let max_old_limit = config.max_heap_size - young_size;
    config.old_limit = max_old_limit;

    match kind {
        CollectionKind::Minor => {
            config.total_minor_collections += 1;
            config.total_minor_pause += config.gc_duration;
        }

        CollectionKind::Full => {
            config.total_full_collections += 1;
            config.total_full_pause += config.gc_duration;
        }
    }

    if verbose {
        print(&*config, kind);
    }
}

fn print(config: &HeapConfig, kind: CollectionKind) {
    match kind {
        CollectionKind::Minor => {
            println!(
                "GC: {} {}/{} -> {}/{}; {:.2} ms; {} promoted; {} copied",
                kind,
                formatted_size(config.start_object_size),
                formatted_size(config.start_memory_size),
                formatted_size(config.end_object_size),
                formatted_size(config.end_memory_size),
                config.gc_duration,
                formatted_size(config.minor_promoted),
                formatted_size(config.minor_copied),
            );
        }

        CollectionKind::Full => {
            println!(
                "GC: {} {}/{} -> {}/{}; {:.2} ms",
                kind,
                formatted_size(config.start_object_size),
                formatted_size(config.start_memory_size),
                formatted_size(config.end_object_size),
                formatted_size(config.end_memory_size),
                config.gc_duration,
            );
        }
    }
}

fn object_size(young: &YoungGen, old: &OldGen, large: &LargeSpace) -> usize {
    young.active_size() + old.active_size() + large.committed_size()
}

fn memory_size(young: &YoungGen, old: &OldGen, large: &LargeSpace) -> usize {
    let (eden_size, semi_size) = young.committed_size();
    let young_size = eden_size + semi_size;

    young_size + old.committed_size() + large.committed_size()
}

pub struct HeapConfig {
    min_heap_size: usize,
    max_heap_size: usize,

    pub eden_size: usize,
    pub semi_size: usize,
    pub old_size: usize,
    pub old_limit: usize,

    gc_start: u64,
    gc_duration: f32,

    start_object_size: usize,
    start_memory_size: usize,
    end_object_size: usize,
    end_memory_size: usize,

    pub minor_promoted: usize,
    pub minor_copied: usize,

    pub total_minor_collections: usize,
    pub total_minor_pause: f32,
    pub total_full_collections: usize,
    pub total_full_pause: f32,
}

impl HeapConfig {
    pub fn new(min_heap_size: usize, max_heap_size: usize) -> HeapConfig {
        assert!(min_heap_size <= max_heap_size);

        HeapConfig {
            min_heap_size: min_heap_size,
            max_heap_size: max_heap_size,

            eden_size: 0,
            semi_size: 0,
            old_size: 0,
            old_limit: 0,

            gc_start: 0,
            gc_duration: 0f32,

            start_object_size: 0,
            start_memory_size: 0,
            end_object_size: 0,
            end_memory_size: 0,

            minor_promoted: 0,
            minor_copied: 0,

            total_minor_collections: 0,
            total_minor_pause: 0f32,
            total_full_collections: 0,
            total_full_pause: 0f32,
        }
    }

    pub fn grow_old(&mut self, size: usize) -> bool {
        if self.old_size + size <= self.old_limit {
            self.old_size += size;
            true
        } else {
            false
        }
    }
}

pub type SharedHeapConfig = Arc<Mutex<HeapConfig>>;
<|endoftext|>