dyon 0.4.0

A rusty dynamically typed scripting language
Documentation
extern crate dyon;
extern crate piston_window;
extern crate current;

use std::sync::Arc;
use piston_window::*;
use current::{Current, CurrentGuard};
use dyon::{error, load, Lt, Module, PreludeFunction, Runtime, Variable};

fn main() {
    let mut window: PistonWindow =
        WindowSettings::new("dyon: piston_window", [512; 2])
        .exit_on_esc(true)
        .samples(4)
        .build()
        .unwrap();
    let dyon_module = match load_module() {
        None => return,
        Some(m) => m
    };
    let mut dyon_runtime = Runtime::new();

    let window_guard = CurrentGuard::new(&mut window);
    if error(dyon_runtime.run(&dyon_module)) {
        return;
    }
    drop(window_guard);
}

fn load_module() -> Option<Module> {
    let mut module = Module::new();
    module.add(Arc::new("render".into()), dyon_render, PreludeFunction {
        lts: vec![],
        returns: true
    });
    module.add(Arc::new("update".into()), dyon_update, PreludeFunction {
        lts: vec![],
        returns: true
    });
    module.add(Arc::new("press".into()), dyon_press, PreludeFunction {
        lts: vec![],
        returns: true
    });
    module.add(Arc::new("release".into()), dyon_release, PreludeFunction {
        lts: vec![],
        returns: true
    });
    module.add(Arc::new("focus".into()), dyon_focus, PreludeFunction {
        lts: vec![],
        returns: true,
    });
    module.add(Arc::new("focus_arg".into()), dyon_focus_arg, PreludeFunction {
        lts: vec![],
        returns: true,
    });
    module.add(Arc::new("clear".into()), dyon_clear, PreludeFunction {
        lts: vec![Lt::Default],
        returns: false
    });
    module.add(Arc::new("draw_color_rect".into()),
        dyon_draw_color_rect, PreludeFunction {
            lts: vec![Lt::Default; 2],
            returns: false
        });
    module.add(Arc::new("draw_color_radius_line".into()),
        dyon_draw_color_radius_line, PreludeFunction {
            lts: vec![Lt::Default; 3],
            returns: false
        });
    module.add(Arc::new("next_event".into()),
        dyon_next_event, PreludeFunction {
            lts: vec![],
            returns: true
        });
    module.add(Arc::new("set_title".into()),
        dyon_set_title, PreludeFunction {
            lts: vec![Lt::Default],
            returns: false
        });
    module.add(Arc::new("update_dt".into()),
        dyon_update_dt, PreludeFunction {
            lts: vec![],
            returns: true
        });
    module.add(Arc::new("press_keyboard_key".into()),
        dyon_press_keyboard_key, PreludeFunction {
            lts: vec![],
            returns: true
        });
    module.add(Arc::new("release_keyboard_key".into()),
        dyon_release_keyboard_key, PreludeFunction {
            lts: vec![],
            returns: true
        });
    if error(load("source/piston_window/loader.rs", &mut module)) {
        None
    } else {
        Some(module)
    }
}

fn dyon_render(rt: &mut Runtime) -> Result<(), String> {
    let e = unsafe { &*Current::<PistonWindow>::new() };
    push_bool(rt, e.render_args().is_some());
    Ok(())
}

fn dyon_update(rt: &mut Runtime) -> Result<(), String> {
    let e = unsafe { &*Current::<PistonWindow>::new() };
    push_bool(rt, e.update_args().is_some());
    Ok(())
}

fn dyon_press(rt: &mut Runtime) -> Result<(), String> {
    let e = unsafe { &*Current::<PistonWindow>::new() };
    push_bool(rt, e.press_args().is_some());
    Ok(())
}

fn dyon_release(rt: &mut Runtime) -> Result<(), String> {
    let e = unsafe { &*Current::<PistonWindow>::new() };
    push_bool(rt, e.release_args().is_some());
    Ok(())
}

fn dyon_focus(rt: &mut Runtime) -> Result<(), String> {
    let e = unsafe { &*Current::<PistonWindow>::new() };
    push_bool(rt, e.focus_args().is_some());
    Ok(())
}

fn dyon_focus_arg(rt: &mut Runtime) -> Result<(), String> {
    let e = unsafe { &*Current::<PistonWindow>::new() };
    push_opt_bool(rt, e.focus_args());
    Ok(())
}

fn push_opt_bool(rt: &mut Runtime, val: Option<bool>) {
    match val {
        None => {
            rt.stack.push(Variable::Option(None))
        }
        Some(b) => {
            rt.stack.push(Variable::Option(Some(Box::new(Variable::Bool(b)))))
        }
    }
}

fn push_bool(rt: &mut Runtime, val: bool) {
    rt.stack.push(Variable::Bool(val))
}

fn push_opt_num(rt: &mut Runtime, val: Option<f64>) {
    match val {
        None => {
            rt.stack.push(Variable::Option(None))
        }
        Some(n) => {
            rt.stack.push(Variable::Option(Some(Box::new(Variable::F64(n)))))
        }
    }
}

fn pop_num(rt: &mut Runtime) -> Result<f64, String> {
    let num = rt.stack.pop().expect("Expected number");
    match rt.resolve(&num) {
        &Variable::F64(n) => Ok(n),
        _ => Err("Expected number".into())
    }
}

fn pop_color(rt: &mut Runtime) -> Result<[f32; 4], String> {
    let color = rt.stack.pop().expect("Expected color");
    match rt.resolve(&color) {
        &Variable::Array(ref arr) => {
            let r = match rt.resolve(&arr[0]) {
                &Variable::F64(r) => r,
                _ => return Err("Expected number".into())
            };
            let g = match rt.resolve(&arr[1]) {
                &Variable::F64(r) => r,
                _ => return Err("Expected number".into())
            };
            let b = match rt.resolve(&arr[2]) {
                &Variable::F64(r) => r,
                _ => return Err("Expected number".into())
            };
            let a = match rt.resolve(&arr[3]) {
                &Variable::F64(r) => r,
                _ => return Err("Expected number".into())
            };
            Ok([r as f32, g as f32, b as f32, a as f32])
        }
        _ => return Err("Expected color".into())
    }
}

fn pop_rect(rt: &mut Runtime) -> Result<[f64; 4], String> {
    let v = rt.stack.pop().expect("Expected rect");
    match rt.resolve(&v) {
        &Variable::Array(ref arr) => {
            let x = match rt.resolve(&arr[0]) {
                &Variable::F64(x) => x,
                _ => return Err("Expected number".into())
            };
            let y = match rt.resolve(&arr[1]) {
                &Variable::F64(y) => y,
                _ => return Err("Expected number".into())
            };
            let w = match rt.resolve(&arr[2]) {
                &Variable::F64(w) => w,
                _ => return Err("Expected number".into())
            };
            let h = match rt.resolve(&arr[3]) {
                &Variable::F64(h) => h,
                _ => return Err("Expected number".into())
            };
            Ok([x, y, w, h])
        }
        _ => return Err("Expected rect".into())
    }
}

fn pop_string(rt: &mut Runtime) -> Result<Arc<String>, String> {
    let v = rt.stack.pop().expect("Expected string");
    match rt.resolve(&v) {
        &Variable::Text(ref s) => Ok(s.clone()),
        _ => Err("Expected string".into())
    }
}

fn dyon_clear(rt: &mut Runtime) -> Result<(), String> {
    let e = unsafe { &mut *Current::<PistonWindow>::new() };
    let color = try!(pop_color(rt));
    e.draw_2d(|_c, g| {
        clear(color, g);
    });
    Ok(())
}

fn dyon_draw_color_rect(rt: &mut Runtime) -> Result<(), String> {
    let e = unsafe { &mut *Current::<PistonWindow>::new() };
    let rect = try!(pop_rect(rt));
    let color = try!(pop_color(rt));
    e.draw_2d(|c, g| {
        rectangle(color, rect, c.transform, g);
    });
    Ok(())
}

fn dyon_draw_color_radius_line(rt: &mut Runtime) -> Result<(), String> {
    let e = unsafe { &mut *Current::<PistonWindow>::new() };
    let rect = try!(pop_rect(rt));
    let radius = try!(pop_num(rt));
    let color = try!(pop_color(rt));
    e.draw_2d(|c, g| {
        line(color, radius, rect, c.transform, g);
    });
    Ok(())
}

fn dyon_next_event(rt: &mut Runtime) -> Result<(), String> {
    let e = unsafe { &mut *Current::<PistonWindow>::new() };
    if let Some(new_e) = e.next() {
        *e = new_e;
        push_bool(rt, true);
    } else {
        push_bool(rt, false);
    }
    Ok(())
}

fn dyon_set_title(rt: &mut Runtime) -> Result<(), String> {
    let e = unsafe { &mut *Current::<PistonWindow>::new() };
    let title = try!(pop_string(rt));
    e.set_title((*title).clone());
    Ok(())
}

fn dyon_update_dt(rt: &mut Runtime) -> Result<(), String> {
    let e = unsafe { &mut *Current::<PistonWindow>::new() };
    push_opt_num(rt, e.update_args().map(|args| args.dt));
    Ok(())
}

fn dyon_press_keyboard_key(rt: &mut Runtime) -> Result<(), String> {
    let e = unsafe { &mut *Current::<PistonWindow>::new() };
    if let Some(Button::Keyboard(key)) = e.press_args() {
        push_opt_num(rt, Some(key as u64 as f64));
    } else {
        push_opt_num(rt, None);
    }
    Ok(())
}

fn dyon_release_keyboard_key(rt: &mut Runtime) -> Result<(), String> {
    let e = unsafe { &mut *Current::<PistonWindow>::new() };
    if let Some(Button::Keyboard(key)) = e.release_args() {
        push_opt_num(rt, Some(key as u64 as f64));
    } else {
        push_opt_num(rt, None);
    }
    Ok(())
}