implement new() for componentcontainer

This commit is contained in:
Vivian Lim 2023-02-18 15:13:52 -08:00
parent e32b76857b
commit a86e196c42
17 changed files with 223 additions and 79 deletions

View File

@ -1,5 +1,5 @@
[build]
target = "wasm32-unknown-unknown"
#target = "wasm32-unknown-unknown"
[target.wasm32-unknown-unknown]
rustflags = [

8
Cargo.lock generated
View File

@ -275,6 +275,13 @@ dependencies = [
"syn",
]
[[package]]
name = "ecs"
version = "0.1.0"
dependencies = [
"thiserror",
]
[[package]]
name = "ecs_derive"
version = "0.1.0"
@ -292,6 +299,7 @@ dependencies = [
name = "ecs_derive_test"
version = "0.1.0"
dependencies = [
"ecs",
"ecs_derive",
]

View File

@ -3,4 +3,5 @@ members = [
"ecs",
"ecs_derive",
"ecs_derive_test",
"ecs_wasm4_app",
]

View File

@ -1,22 +1,12 @@
[package]
name = "cart"
name = "ecs"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib"]
path = "src/lib.rs"
[dependencies]
buddy-alloc = { version = "0.4.1", optional = true }
strum = { version = "0.24", features = ["derive"], default-features = false }
enum_dispatch = "0.3.11"
ecs_derive = { path = "../ecs_derive" }
[profile.release]
opt-level = "z"
lto = true
[features]
# use `--no-default-features` or comment out next line to disable allocator
default = ["buddy-alloc"]
thiserror = "*"

View File

@ -1,47 +1,8 @@
#![cfg_attr(not(test), no_std)]
#[cfg(feature = "buddy-alloc")]
mod my_alloc;
mod wasm4;
use core::panic::PanicInfo;
use thiserror::Error;
#[macro_use]
extern crate alloc;
#[derive(Debug, Error)]
pub enum EcsError {
#[error("ecs instantiation error {0}")]
InstantiationError(String)
use wasm4::*;
mod ecs;
#[rustfmt::skip]
const SMILEY: [u8; 8] = [
0b11000011,
0b10000001,
0b00100100,
0b00100100,
0b00000000,
0b00100100,
0b10011001,
0b11000011,
];
/*
todo strum seems to have pulled in std??
#[cfg_attr(not(test), panic_handler)]
fn panic(info: &PanicInfo) -> ! {
trace(format!("{}", info));
loop {}
} */
#[no_mangle]
fn update() {
unsafe { *DRAW_COLORS = 2 }
text("Hello from Rust!", 10, 10);
let gamepad = unsafe { *GAMEPAD1 };
if gamepad & BUTTON_1 != 0 {
unsafe { *DRAW_COLORS = 4 }
}
blit(&SMILEY, 76, 76, 8, 8, BLIT_1BPP);
text("Press X to blink", 16, 90);
}
}

View File

@ -1,12 +1,12 @@
use std::ops::Range;
use proc_macro2::Ident;
use proc_macro2::{Ident, TokenStream};
use quote::{quote_spanned, quote, format_ident};
use syn::{ExprRange, Type};
use crate::parsing::literals::get_bounds_range;
use super::{ComponentStrategy, ComponentImpl};
use super::{ComponentStrategy, ComponentImpl, ComponentImplField};
use syn::{parse::Parse, Token};
@ -21,6 +21,7 @@ pub struct ContiguousComponent {
fn_get_by_id: Ident,
field_entity_has_map: Ident,
fn_next_free_id: Ident,
fn_claim: Ident,
}
@ -35,9 +36,12 @@ impl Parse for ContiguousComponent {
input.parse::<Token![:]>()?;
let ty: Type = input.parse()?;
let fn_get_by_id = format_ident!("get_{}", name);
let field_entity_has_map = format_ident!("entity_has_{}", name);
let fn_next_free_id = format_ident!("next_free_id_{}", name);
let prefix = format!("component_{}_", name);
let fn_get_by_id = format_ident!("{}get_range", prefix);
let field_entity_has_map = format_ident!("{}entity_has", prefix);
let fn_next_free_id = format_ident!("{}next_free_id", prefix);
let fn_create = format_ident!("{}claim_id", prefix);
Ok(Self{
name,
@ -49,6 +53,7 @@ impl Parse for ContiguousComponent {
fn_get_by_id,
field_entity_has_map,
fn_next_free_id,
fn_claim: fn_create,
})
}
}
@ -64,6 +69,7 @@ impl ComponentStrategy for ContiguousComponent {
field_entity_has_map,
fn_get_by_id,
fn_next_free_id,
fn_claim,
..} = self;
@ -72,17 +78,32 @@ impl ComponentStrategy for ContiguousComponent {
// test hi > lo
imp.struct_items.push(quote! {
#name: [#ty; #capacity]
imp.struct_fields.push(ComponentImplField {
field_name: quote! {
#name
},
field_type: quote! {
[#ty; #capacity]
},
initializer: initialize_fixed_length_array(&quote!{ #ty::default() }, *capacity),
additional_args: vec![]
});
imp.struct_items.push(quote! {
#field_entity_has_map: [bool; #capacity]
imp.struct_fields.push(ComponentImplField {
field_name: quote! {
#field_entity_has_map
},
field_type: quote! {
[bool; #capacity]
},
initializer: initialize_fixed_length_array(&quote!{ false }, *capacity),
additional_args: vec![]
});
imp.struct_impl.push(quote_spanned! {self.kw_span.span=>
pub fn #fn_get_by_id(&self, ids: std::ops::Range<usize>) -> Option<&[#ty]> {
let count = ids.end - ids.start;
// need to test if the requested span is within the range
if count > #capacity {
return Option::None; // A result instead?
}
@ -99,9 +120,33 @@ impl ComponentStrategy for ContiguousComponent {
return Some(#lo + i)
}
}
None
}
});
imp.struct_impl.push(quote_spanned! {self.kw_span.span=>
fn #fn_claim(&mut self) -> Option<usize> {
match self.#fn_next_free_id() {
Some(free_id) => {
self.#field_entity_has_map[free_id] = true;
Some(free_id)
},
None => None
}
}
});
Ok(imp)
}
}
fn initialize_fixed_length_array(value: &TokenStream, count: usize) -> TokenStream {
let mut values = vec![];
for _ in 0..count {
values.push(value.clone());
}
quote! {
[#(#values),*]
}
}

View File

@ -15,10 +15,21 @@ pub trait ComponentStrategy {
#[derive(Default)]
pub struct ComponentImpl{
pub struct_items: Vec<TokenStream>,
pub struct_fields: Vec<ComponentImplField>,
pub struct_impl: Vec<TokenStream>,
}
pub struct ComponentImplField {
/// Name of the field.
pub field_name: TokenStream,
/// Name of the field.
pub field_type: TokenStream,
/// How to initialize the field. May use `?` to fail initialization and return a Err(EcsError)
pub initializer: TokenStream,
/// Arguments to add to new()
pub additional_args: Vec<TokenStream>,
}
pub mod kw {
syn::custom_keyword!(sparse);
syn::custom_keyword!(contiguous);

View File

@ -1,8 +1,8 @@
use proc_macro2::Ident;
use quote::quote_spanned;
use quote::{quote_spanned, quote};
use syn::{Type, parse::Parse, Token};
use super::{ComponentStrategy, ComponentImpl};
use super::{ComponentStrategy, ComponentImpl, ComponentImplField};
pub struct SparseComponent {
@ -34,8 +34,17 @@ impl ComponentStrategy for SparseComponent {
ty,
..
} = self;
imp.struct_items.push(quote_spanned! {self.kw_span.span=>
#name: Vec<#ty>
imp.struct_fields.push(ComponentImplField {
field_name: quote_spanned! {self.kw_span.span=>
#name
},
field_type: quote_spanned! {self.kw_span.span=>
Vec<#ty>
},
initializer: quote! {
vec![]
},
additional_args: vec![]
});
Ok(imp)
}

View File

@ -5,7 +5,7 @@ use syn::{parse::Parse, Token};
use crate::ComponentImplGenError;
use self::components::{ComponentStrategy, ComponentImpl, parse_component};
use self::components::{ComponentStrategy, ComponentImpl, parse_component, ComponentImplField};
use quote::quote;
pub mod components;
@ -16,18 +16,52 @@ pub struct ComponentContainer {
impl ComponentContainer {
pub fn codegen(&mut self) -> Result<TokenStream, ComponentImplGenError> {
let mut container_struct_items: Vec<TokenStream> = vec![];
let mut container_struct_impl: Vec<TokenStream> = vec![];
let mut container_struct_field_defs: Vec<TokenStream> = vec![];
let mut container_struct_field: Vec<ComponentImplField> = vec![];
for component in &mut self.components {
let ComponentImpl { mut struct_items, mut struct_impl } = component.core()?;
container_struct_items.append(&mut struct_items);
let ComponentImpl { struct_fields: struct_items, mut struct_impl } = component.core()?;
for mut field in struct_items.into_iter() {
container_struct_field_defs.push({
let name = field.field_name.clone();
let ty = field.field_type.clone();
quote! {#name: #ty}
});
container_struct_field.push(field);
}
container_struct_impl.append(&mut struct_impl);
}
// Build up the impl for new().
container_struct_impl.push({
let mut container_struct_initialize_args: Vec<TokenStream> = vec![];
let let_statements: Vec<TokenStream> = container_struct_field.iter().map(
|ComponentImplField {field_name, field_type, initializer, ..}| quote! {
let #field_name: #field_type = #initializer;
}
).collect();
let field_names: Vec<TokenStream> = container_struct_field.iter().map(
|ComponentImplField {field_name, ..}| quote! {
#field_name
}
).collect();
quote!{
pub fn new(#(#container_struct_initialize_args),*) -> Result<ComponentContainer, ecs::EcsError> {
#(#let_statements) *
Ok(ComponentContainer {
#(#field_names),*
})
}
}
});
Ok(quote!{
struct ComponentContainer {
#(#container_struct_items),*
#(#container_struct_field_defs),*
}
impl ComponentContainer {

View File

@ -6,4 +6,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
ecs = { path = "../ecs" }
ecs_derive = { path = "../ecs_derive" }

View File

@ -1,21 +1,36 @@
use ecs_derive::{component_container};
#[derive(Default)]
struct Name(&'static str);
#[derive(Default)]
struct Position {
x: u32,
y: u32
}
#[derive(Default)]
struct Physics {
ageia: u32,
}
component_container!{
sparse name: Name,
sparse title: String,
contiguous 0..69 pos: Position,
contiguous 8..32 physx: Physics,
}
fn main() {
}
#[cfg(test)]
mod tests {
use crate::ComponentContainer;
#[test]
fn instantiate() {
let mut cc: ComponentContainer = ComponentContainer::new().unwrap();
}
}

22
ecs_wasm4_app/Cargo.toml Normal file
View File

@ -0,0 +1,22 @@
[package]
name = "cart"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib"]
[dependencies]
buddy-alloc = { version = "0.4.1", optional = true }
strum = { version = "0.24", features = ["derive"], default-features = false }
enum_dispatch = "0.3.11"
ecs_derive = { path = "../ecs_derive" }
[profile.release]
opt-level = "z"
lto = true
[features]
# use `--no-default-features` or comment out next line to disable allocator
default = ["buddy-alloc"]

47
ecs_wasm4_app/src/lib.rs Normal file
View File

@ -0,0 +1,47 @@
#![cfg_attr(not(test), no_std)]
#[cfg(feature = "buddy-alloc")]
mod my_alloc;
mod wasm4;
use core::panic::PanicInfo;
#[macro_use]
extern crate alloc;
use wasm4::*;
mod ecs;
#[rustfmt::skip]
const SMILEY: [u8; 8] = [
0b11000011,
0b10000001,
0b00100100,
0b00100100,
0b00000000,
0b00100100,
0b10011001,
0b11000011,
];
/*
todo strum seems to have pulled in std??
#[cfg_attr(not(test), panic_handler)]
fn panic(info: &PanicInfo) -> ! {
trace(format!("{}", info));
loop {}
} */
#[no_mangle]
fn update() {
unsafe { *DRAW_COLORS = 2 }
text("Hello from Rust!", 10, 10);
let gamepad = unsafe { *GAMEPAD1 };
if gamepad & BUTTON_1 != 0 {
unsafe { *DRAW_COLORS = 4 }
}
blit(&SMILEY, 76, 76, 8, 8, BLIT_1BPP);
text("Press X to blink", 16, 90);
}