gluon_codegen 0.18.3

Code generation macros for the gluon programming language
Documentation
#[macro_use]
extern crate gluon_codegen;
extern crate gluon;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate gluon_vm;

mod init;

use gluon::{
    RootedThread, Thread, ThreadExt, import,
    vm::{
        self, ExternModule,
        api::{self, OpaqueValue, generic::A},
    },
};
use init::new_vm;

#[derive(Pushable, VmType, Serialize, Deserialize)]
#[gluon(vm_type = "types.Struct")]
struct Struct {
    string: String,
    number: u32,
    vec: Vec<f64>,
}

fn load_struct_mod(vm: &Thread) -> vm::Result<ExternModule> {
    let module = record! {
        new_struct => primitive!(1, new_struct),
    };

    ExternModule::new(vm, module)
}

fn new_struct(_: ()) -> Struct {
    Struct {
        string: "hello".to_owned(),
        number: 1,
        vec: vec![1.0, 2.0, 3.0],
    }
}

#[test]
fn normal_struct() {
    let vm = new_vm();

    // must be generated by hand because of bug in make_source (see #542)
    let src = r#"
        type Struct = { string: String, number: Int, vec: Array Float }
        { Struct }
    "#;

    vm.load_script("types", &src).unwrap();
    import::add_extern_module(&vm, "functions", load_struct_mod);

    let script = r#"
        let { Struct } = import! types
        let { new_struct } = import! functions
        let { assert } = import! std.test
        let { index, len } = import! std.array

        let { string, number, vec } = new_struct ()

        let _ = assert (string == "hello")
        let _ = assert (number == 1)
        let _ = assert (len vec == 3)
        let _ = assert (index vec 0 == 1.0)
        let _ = assert (index vec 1 == 2.0)
        assert (index vec 2 == 3.0)
    "#;

    if let Err(why) = vm.run_expr::<()>("test", script) {
        panic!("{}", why);
    }
}

#[derive(Pushable, VmType)]
#[gluon(vm_type = "types.GenericStruct")]
struct GenericStruct<T> {
    generic: T,
    other: u32,
}

fn load_generic_struct_mod(vm: &Thread) -> vm::Result<ExternModule> {
    let module = record! {
        new_generic_struct => primitive!(1, new_generic_struct),
    };

    ExternModule::new(vm, module)
}

fn new_generic_struct(
    arg: OpaqueValue<RootedThread, A>,
) -> GenericStruct<OpaqueValue<RootedThread, A>> {
    GenericStruct {
        generic: arg,
        other: 2012,
    }
}

#[test]
fn generic_struct() {
    let vm = new_vm();

    let src = r#"
        type GenericStruct a = { generic: a, other: u32 }
        { GenericStruct }
    "#;

    vm.load_script("types", &src).unwrap();
    import::add_extern_module(&vm, "functions", load_generic_struct_mod);

    let script = r#"
        let { GenericStruct } = import! types
        let { new_generic_struct } = import! functions
        let { assert } = import! std.test

        let { generic, other } = new_generic_struct "hi rust"

        let _ = assert (generic == "hi rust")
        let _ = assert (other == 2012)

        let { generic, other } = new_generic_struct 3.14

        let _ = assert (generic == 3.14)
        assert (other == 2012)
    "#;

    if let Err(why) = vm.run_expr::<()>("test", script) {
        panic!("{}", why);
    }
}

#[derive(Pushable, VmType, Serialize, Deserialize)]
#[gluon(vm_type = "types.LifetimeStruct")]
struct LifetimeStruct<'a> {
    string: &'a str,
    other: f64,
}

fn load_lifetime_struct_mod(vm: &Thread) -> vm::Result<ExternModule> {
    let module = record! {
        new_lifetime_struct => primitive!(1, new_lifetime_struct),
    };

    ExternModule::new(vm, module)
}

fn new_lifetime_struct<'a>(_: ()) -> LifetimeStruct<'a> {
    LifetimeStruct {
        string: "I'm borrowed",
        other: 6.6,
    }
}

#[test]
fn lifetime_struct() {
    let vm = new_vm();

    // make_source doesn't work with borrowed strings
    let src = r#"
        type LifetimeStruct = { string: String, other: Float }
        { LifetimeStruct }
    "#;

    vm.load_script("types", &src).unwrap();
    import::add_extern_module(&vm, "functions", load_lifetime_struct_mod);

    let script = r#"
        let { LifetimeStruct } = import! types
        let { new_lifetime_struct } = import! functions
        let { assert } = import! std.test

        let { string, other } = new_lifetime_struct ()

        let _ = assert (string == "I'm borrowed")
        assert (other == 6.6)
    "#;

    if let Err(why) = vm.run_expr::<()>("test", script) {
        panic!("{}", why);
    }
}

#[derive(Pushable, VmType, Serialize, Deserialize)]
#[gluon(vm_type = "types.Enum")]
enum Enum {
    Nothing,
    Tuple(u32, u32),
    Struct { key: String, value: String },
}

fn load_enum_mod(vm: &Thread) -> vm::Result<ExternModule> {
    let module = record! {
        new_enum => primitive!(1, new_enum),
    };

    ExternModule::new(vm, module)
}

fn new_enum(tag: u32) -> Enum {
    match tag {
        0 => Enum::Nothing,
        1 => Enum::Tuple(1920, 1080),
        _ => Enum::Struct {
            key: "under the doormat".to_owned(),
            value: "lots of gold".to_owned(),
        },
    }
}

#[test]
fn normal_enum() {
    let vm = new_vm();

    let src = api::typ::make_source::<Enum>(&vm).unwrap();
    vm.load_script("types", &src).unwrap();
    import::add_extern_module(&vm, "functions", load_enum_mod);

    let script = r#"
        let { Enum } = import! types
        let { new_enum } = import! functions
        let { assert } = import! std.test

        let assert_enum enum tag =
            let actual_tag =
                match enum with
                | Nothing -> 0
                | Tuple x y ->
                    let _ = assert (x == 1920)
                    let _ = assert (y == 1080)
                    1
                | Struct { key, value } ->
                    let _ = assert (key == "under the doormat")
                    let _ = assert (value == "lots of gold")
                    2

            assert (tag == actual_tag)

        let _ = assert_enum (new_enum 0) 0
        let _ = assert_enum (new_enum 1) 1
        assert_enum (new_enum 2) 2
    "#;

    if let Err(why) = vm.run_expr::<()>("test", script) {
        panic!("{}", why);
    }
}