Remove boxed dyn requirement on GBNF Limits.

This commit removes the use of dynamic dispatch for GBNF rule limits
by making use of some overly complicated generic trait bounds plus
associated types, along with two new types. Instead of wrapping GBNF
limits in a box dynamic trait object that produces GBNF limits, there
are two types: GbnfLimitedPrimitive and GbnfLimitedComplex.

These types contain the actual (not very complicated) logic to convert
their contained limit-values into the GBNF limits, which then get
converted to actual limits in the GBNF grammar. This removes heap
allocation and dynamic dispatch, as well as making creation of limit
objects more ergonomic.
This commit is contained in:
projectmoon 2024-03-10 14:39:32 +01:00
parent f2faf258c4
commit 001da3ca2f
8 changed files with 265 additions and 190 deletions

View File

@ -7,6 +7,7 @@ use crate::models::world::items::Item;
use crate::models::world::people::Person; use crate::models::world::people::Person;
use crate::models::world::scenes::{Exit, Prop, Scene, Stage}; use crate::models::world::scenes::{Exit, Prop, Scene, Stage};
use crate::models::Insertable; use crate::models::Insertable;
use gbnf::prelude::limit::{GbnfLimitedComplex, GbnfLimitedPrimitive};
use itertools::Itertools; use itertools::Itertools;
use strum::VariantNames; use strum::VariantNames;
use tabled::settings::Style; use tabled::settings::Style;
@ -75,19 +76,6 @@ impl<'a> From<&'a Exit> for ExitTableRow<'a> {
} }
} }
const COMMAND_EXECUTION_BNF: &'static str = r#"
root ::= CommandExecution
CommandEvent ::= "{" ws "\"eventName\":" ws string "," ws "\"appliesTo\":" ws string "," ws "\"parameter\":" ws string "}"
CommandExecution ::= "{" ws "\"valid\":" ws boolean "," ws "\"reason\":" ws string "," ws "\"narration\":" ws string "," ws "\"event\":" ws CommandEvent "}"
CommandExecutionlist ::= "[]" | "[" ws CommandExecution ("," ws CommandExecution)* "]"
string ::= "\"" ([^"]*) "\""
boolean ::= "true" | "false"
ws ::= [ \t\n]*
number ::= [0-9]+ "."? [0-9]*
stringlist ::= "[" ws "]" | "[" ws string ("," ws string)* ws "]"
numberlist ::= "[" ws "]" | "[" ws string ("," ws number)* ws "]"
"#;
const COMMAND_EXECUTION_PROMPT: &'static str = r#" const COMMAND_EXECUTION_PROMPT: &'static str = r#"
[INST] [INST]
You are running a text-based adventure game. You have been given a command to execute. Your response must be in JSON. You are running a text-based adventure game. You have been given a command to execute. Your response must be in JSON.
@ -248,41 +236,25 @@ fn stage_info(stage: &Stage) -> String {
} }
fn execution_gbnf_limit<'a>(stage: &'a Stage) -> RawCommandExecutionGbnfLimit { fn execution_gbnf_limit<'a>(stage: &'a Stage) -> RawCommandExecutionGbnfLimit {
// We will begin this implementation by simply setting the limits // First version of this implementation is simply setting the limits
// to all uuids in the scene. We might wind up getting rid of the // to all uuids in the scene. We might wind up getting rid of the
// appliesTo field, leaving only parameter behind. // appliesTo field, leaving only parameter behind.
let people_keys = stage let people_keys = stage.people.iter().map(|p| &p._key).cloned().flatten();
.people let item_keys = stage.items.iter().map(|i| &i._key).cloned().flatten();
.iter()
.map(|p| &p._key)
.cloned()
.flatten();
let item_keys = stage
.items
.iter()
.map(|i| &i._key)
.cloned()
.flatten();
let curr_scene_key = stage.scene._key.iter();
let exit_keys = stage.scene.exits.iter().map(|e| &e.scene_key).cloned(); let exit_keys = stage.scene.exits.iter().map(|e| &e.scene_key).cloned();
let all_uuids = people_keys let all_uuids = people_keys.chain(item_keys).chain(exit_keys).collect_vec();
.chain(item_keys) let mut applies_to = all_uuids.clone();
//.chain(curr_scene_key) applies_to.push("self".to_string());
.chain(exit_keys)
.collect_vec();
//let all_uuids = std::rc::Rc::new(all_uuids);
let event_limit = RawCommandEventGbnfLimit { let event_limit = RawCommandEventGbnfLimit {
applies_to: Box::new(all_uuids.clone()), applies_to: GbnfLimitedPrimitive::new(applies_to),
parameter: Box::new(all_uuids.clone()), parameter: GbnfLimitedPrimitive::new(all_uuids),
}; };
RawCommandExecutionGbnfLimit { RawCommandExecutionGbnfLimit {
event: Box::new(event_limit), event: GbnfLimitedComplex::new(event_limit),
} }
} }
@ -299,9 +271,6 @@ pub fn execution_prompt(original_cmd: &str, stage: &Stage, cmd: &ParsedCommand)
let limit = execution_gbnf_limit(stage); let limit = execution_gbnf_limit(stage);
let grammar = RawCommandExecution::to_grammar_with_limit(limit); let grammar = RawCommandExecution::to_grammar_with_limit(limit);
println!("{}", grammar);
//AiPrompt::new_with_grammar_and_size(&prompt, COMMAND_EXECUTION_BNF, 512)
AiPrompt::new_with_grammar_and_size(&prompt, &grammar, 512) AiPrompt::new_with_grammar_and_size(&prompt, &grammar, 512)
} }

View File

@ -2,7 +2,10 @@ use ai::logic::AiLogic;
use anyhow::Result; use anyhow::Result;
use config::Config; use config::Config;
use game_loop::GameLoop; use game_loop::GameLoop;
use models::world::scenes::{root_scene_id, Stage}; use models::{
commands::RawCommandExecutionGbnfLimit,
world::scenes::{root_scene_id, Stage},
};
use state::GameState; use state::GameState;
use std::{io::stdout, rc::Rc, str::FromStr, time::Duration}; use std::{io::stdout, rc::Rc, str::FromStr, time::Duration};

View File

@ -1,7 +1,5 @@
use std::fmt::Display;
use gbnf::prelude::*; use gbnf::prelude::*;
use gbnf_derive::{Gbnf}; use gbnf_derive::Gbnf;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use strum::{EnumString, EnumVariantNames}; use strum::{EnumString, EnumVariantNames};
use thiserror::Error; use thiserror::Error;
@ -63,7 +61,7 @@ pub struct RawCommandExecution {
pub reason: Option<String>, pub reason: Option<String>,
pub narration: String, pub narration: String,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
#[gbnf_limit] #[gbnf_limit_complex]
pub event: Option<RawCommandEvent>, pub event: Option<RawCommandEvent>,
} }
@ -82,9 +80,9 @@ impl RawCommandExecution {
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub struct RawCommandEvent { pub struct RawCommandEvent {
pub event_name: String, pub event_name: String,
#[gbnf_limit] #[gbnf_limit_primitive]
pub applies_to: String, pub applies_to: String,
#[gbnf_limit] #[gbnf_limit_primitive]
pub parameter: String, pub parameter: String,
} }

View File

@ -4,24 +4,31 @@ use std::{
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
}; };
use itertools::Itertools; use crate::limits::convert::{Limited, LimitedGbnfComplex, LimitedGbnfField, LimitedGbnfPrimitive};
use limited::{Limited, LimitedGbnfComplex, LimitedGbnfField, LimitedGbnfPrimitive};
use serde::de::DeserializeOwned;
use convert_case::{Case, Casing}; use convert_case::{Case, Casing};
use itertools::Itertools;
use serde::de::DeserializeOwned;
mod limited; mod limits;
pub mod limit {
pub use crate::limits::definitions::*;
}
pub mod prelude { pub mod prelude {
pub use crate::gbnf_field; pub use crate::gbnf_field;
pub use crate::gbnf_field_type; pub use crate::gbnf_field_type;
pub use crate::limit;
pub use crate::limit::*;
pub use crate::AsGbnf; pub use crate::AsGbnf;
pub use crate::AsGbnfComplex;
pub use crate::AsGbnfLimit;
pub use crate::AsGbnfPrimitive; pub use crate::AsGbnfPrimitive;
pub use crate::AsGrammar; pub use crate::AsGrammar;
pub use crate::GbnfComplex; pub use crate::GbnfComplex;
pub use crate::GbnfField; pub use crate::GbnfField;
pub use crate::GbnfFieldType; pub use crate::GbnfFieldType;
pub use crate::GbnfLimit; pub use crate::GbnfLimit;
pub use crate::GbnfLimitedField;
pub use crate::GbnfPrimitive; pub use crate::GbnfPrimitive;
pub use crate::GbnfRule; pub use crate::GbnfRule;
pub use crate::GbnfRuleLevel; pub use crate::GbnfRuleLevel;
@ -35,106 +42,8 @@ pub mod prelude {
/// complex types). This allows the creation of a complex nested Limit /// complex types). This allows the creation of a complex nested Limit
/// struct which can hold the full structure of nested limits of /// struct which can hold the full structure of nested limits of
/// multiple types that derive Gbnf and have limits. /// multiple types that derive Gbnf and have limits.
pub trait GbnfLimitedField<T> { pub trait AsGbnfLimit {
fn limit(self: Box<Self>) -> GbnfLimit; fn to_gbnf_limit(self) -> GbnfLimit;
}
impl<T: GbnfLimitedField<T> + 'static> From<Box<T>> for Box<dyn GbnfLimitedField<T>> {
fn from(value: Box<T>) -> Self {
value
}
}
impl<T: ToString + AsGbnfPrimitive + 'static> From<Vec<T>> for Box<dyn GbnfLimitedField<T>> {
fn from(value: Vec<T>) -> Self {
Box::new(value)
}
}
impl<T, const N: usize> From<[T; N]> for Box<dyn GbnfLimitedField<T>>
where
T: ToString + AsGbnfPrimitive + 'static,
{
fn from(value: [T; N]) -> Self {
Box::new(value)
}
}
impl<T: ToString + AsGbnfPrimitive + 'static> From<Box<Vec<T>>> for Box<dyn GbnfLimitedField<T>> {
fn from(value: Box<Vec<T>>) -> Self {
value
}
}
// Single pritive field types that can only have specific values.
impl<T: ToString + AsGbnfPrimitive> GbnfLimitedField<T> for Vec<T> {
fn limit(self: Box<Self>) -> GbnfLimit {
GbnfLimit::Simple(
T::to_gbnf_primitive(),
self.into_iter().map(|v| v.to_string()).collect(),
)
}
}
impl<T: ToString + AsGbnfPrimitive> GbnfLimitedField<std::rc::Rc<T>> for Vec<T> {
fn limit(self: Box<Self>) -> GbnfLimit {
GbnfLimit::Simple(
T::to_gbnf_primitive(),
self.into_iter().map(|v| v.to_string()).collect(),
)
}
}
// List types that can only have specific values.
impl<T: ToString + AsGbnfPrimitive> GbnfLimitedField<Vec<T>> for Vec<T> {
fn limit(self: Box<Self>) -> GbnfLimit {
GbnfLimit::Simple(
T::to_gbnf_primitive(),
self.into_iter().map(|v| v.to_string()).collect(),
)
}
}
impl<T, const N: usize> GbnfLimitedField<T> for [T; N]
where
T: ToString + AsGbnfPrimitive,
{
fn limit(self: Box<Self>) -> GbnfLimit {
GbnfLimit::Simple(
T::to_gbnf_primitive(),
self.into_iter().map(|v| v.to_string()).collect(),
)
}
}
impl<T, const N: usize> GbnfLimitedField<Vec<T>> for [T; N]
where
T: ToString + AsGbnfPrimitive,
{
fn limit(self: Box<Self>) -> GbnfLimit {
GbnfLimit::Simple(
T::to_gbnf_primitive(),
self.into_iter().map(|v| v.to_string()).collect(),
)
}
}
impl<'a> GbnfLimitedField<String> for &'a [&'a str] {
fn limit(self: Box<Self>) -> GbnfLimit {
GbnfLimit::Simple(
String::to_gbnf_primitive(),
self.into_iter().map(|v| v.to_string()).collect(),
)
}
}
impl<'a> GbnfLimitedField<String> for std::rc::Rc<Vec<&'a str>> {
fn limit(self: Box<Self>) -> GbnfLimit {
GbnfLimit::Simple(
String::to_gbnf_primitive(),
self.iter().map(|v| v.to_string()).collect(),
)
}
} }
/// A type of GBNF value limit, either simple (a list of values meant /// A type of GBNF value limit, either simple (a list of values meant
@ -147,12 +56,6 @@ pub enum GbnfLimit {
Complex(HashMap<&'static str, GbnfLimit>), Complex(HashMap<&'static str, GbnfLimit>),
} }
impl<T> From<Box<dyn GbnfLimitedField<T>>> for GbnfLimit {
fn from(value: Box<dyn GbnfLimitedField<T>>) -> Self {
value.limit()
}
}
// Converts GBNF defintions (through the types below) into the grammar // Converts GBNF defintions (through the types below) into the grammar
// rules. // rules.
pub trait AsGrammar { pub trait AsGrammar {
@ -180,6 +83,10 @@ pub trait AsGbnfPrimitive {
fn to_gbnf_primitive() -> GbnfPrimitive; fn to_gbnf_primitive() -> GbnfPrimitive;
} }
pub trait AsGbnfComplex {
fn to_gbnf_complex() -> GbnfComplex;
}
macro_rules! define_field_type { macro_rules! define_field_type {
($type:ty, $gbnf_type:expr) => { ($type:ty, $gbnf_type:expr) => {
impl AsGbnf for $type { impl AsGbnf for $type {
@ -331,6 +238,8 @@ impl GbnfRule {
) )
} }
/// Turn this rule into an option rule: main rule is an option type, and
/// dependent rule is the original rule itself.
pub fn to_optional(self) -> GbnfRule { pub fn to_optional(self) -> GbnfRule {
let option_token = self.name.option_token(); let option_token = self.name.option_token();
let option_rule_text = format!(r#"{} | "null""#, self.name); let option_rule_text = format!(r#"{} | "null""#, self.name);
@ -340,7 +249,7 @@ impl GbnfRule {
} }
/// Turn this rule into a list rule: main rule is a list type, and /// Turn this rule into a list rule: main rule is a list type, and
/// dependent rule is the original type/rule itself. /// dependent rule is the original rule itself.
pub fn to_list(self) -> GbnfRule { pub fn to_list(self) -> GbnfRule {
let list_name = self.name.list_token(); let list_name = self.name.list_token();
let list_rule_text = let list_rule_text =
@ -354,7 +263,7 @@ impl GbnfRule {
list_rule list_rule
} }
/// Consume this rule to produce a list of rules consisting of /// Consume this rule to produce an ordered list of rules consisting of
/// this rule and its dependents. The rules in the list have no /// this rule and its dependents. The rules in the list have no
/// dependents. Useful for final output of a rules list. /// dependents. Useful for final output of a rules list.
fn flatten(mut self) -> Vec<GbnfRule> { fn flatten(mut self) -> Vec<GbnfRule> {

View File

@ -4,6 +4,7 @@ use crate::{
}; };
use itertools::Itertools; use itertools::Itertools;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct LimitedGbnfComplex<'a> { pub struct LimitedGbnfComplex<'a> {
pub complex: &'a GbnfComplex, pub complex: &'a GbnfComplex,

View File

@ -0,0 +1,157 @@
use crate::AsGbnfComplex;
use crate::AsGbnfLimit;
use crate::AsGbnfPrimitive;
use crate::GbnfLimit;
pub trait GbnfLimitType {
type Type;
}
pub trait GbnfLimitStructMarker {}
macro_rules! define_limit_marker {
($field_type:ty, $value_type:ty) => {
impl GbnfLimitType for $field_type {
type Type = $value_type;
}
};
}
define_limit_marker!(String, String);
define_limit_marker!(i32, i32);
impl<T> GbnfLimitType for Vec<T>
where
T: GbnfLimitType,
{
type Type = <T as GbnfLimitType>::Type;
}
impl<T> GbnfLimitType for Option<T> {
type Type = T;
}
pub trait GbnfLimitTypeContainer<T: GbnfLimitType> {
type ContainerType;
}
impl GbnfLimitTypeContainer<String> for String {
type ContainerType = Vec<String>;
}
impl GbnfLimitTypeContainer<i32> for i32 {
type ContainerType = Vec<i32>;
}
impl<T: GbnfLimitType> GbnfLimitTypeContainer<T> for Vec<T> {
type ContainerType = Vec<<T as GbnfLimitType>::Type>;
}
impl<T: GbnfLimitType + GbnfLimitTypeContainer<T>> GbnfLimitTypeContainer<Vec<T>> for Vec<T> {
type ContainerType = <T as GbnfLimitTypeContainer<T>>::ContainerType;
}
impl<T: GbnfLimitType> GbnfLimitTypeContainer<T> for Option<T> {
type ContainerType = <T as GbnfLimitType>::Type;
}
impl<T: GbnfLimitType> GbnfLimitTypeContainer<Option<T>> for Option<T> {
type ContainerType = <T as GbnfLimitType>::Type;
}
pub struct GbnfLimitedPrimitive<T>
where
T: GbnfLimitType + GbnfLimitTypeContainer<T>,
{
field_type: std::marker::PhantomData<T>,
values: <T as GbnfLimitTypeContainer<T>>::ContainerType,
}
impl<T> GbnfLimitedPrimitive<T>
where
T: GbnfLimitType + GbnfLimitTypeContainer<T>,
{
pub fn new(values: <T as GbnfLimitTypeContainer<T>>::ContainerType) -> GbnfLimitedPrimitive<T> {
GbnfLimitedPrimitive {
field_type: std::marker::PhantomData,
values,
}
}
}
impl<T> AsGbnfLimit for GbnfLimitedPrimitive<T>
where
T: AsGbnfPrimitive
+ ToString
+ GbnfLimitTypeContainer<T, ContainerType = Vec<T>>
+ GbnfLimitType<Type = T>,
{
fn to_gbnf_limit(self) -> GbnfLimit {
let vals = self.values.into_iter().map(|v| v.to_string());
GbnfLimit::Simple(<T as AsGbnfPrimitive>::to_gbnf_primitive(), vals.collect())
}
}
impl<T> AsGbnfLimit for GbnfLimitedPrimitive<Vec<T>>
where
T: AsGbnfPrimitive
+ ToString
+ GbnfLimitTypeContainer<T, ContainerType = Vec<T>>
+ GbnfLimitType<Type = T>,
Vec<T>: GbnfLimitType,
{
fn to_gbnf_limit(self) -> GbnfLimit {
let vals = self.values.into_iter().map(|v| v.to_string());
GbnfLimit::Simple(<T as AsGbnfPrimitive>::to_gbnf_primitive(), vals.collect())
}
}
pub struct GbnfLimitedComplex<T>
where
T: GbnfLimitType + GbnfLimitTypeContainer<T>,
{
field_type: std::marker::PhantomData<T>,
values: <T as GbnfLimitTypeContainer<T>>::ContainerType,
}
impl<T> GbnfLimitedComplex<T>
where
T: GbnfLimitType + GbnfLimitTypeContainer<T>,
{
pub fn new(values: <T as GbnfLimitTypeContainer<T>>::ContainerType) -> GbnfLimitedComplex<T> {
GbnfLimitedComplex {
field_type: std::marker::PhantomData,
values,
}
}
}
impl<T, U> AsGbnfLimit for GbnfLimitedComplex<T>
where
T: AsGbnfComplex + GbnfLimitType<Type = U> + GbnfLimitTypeContainer<T, ContainerType = U>,
U: AsGbnfLimit + GbnfLimitStructMarker,
{
fn to_gbnf_limit(self) -> GbnfLimit {
self.values.to_gbnf_limit()
}
}
impl<T, U> AsGbnfLimit for GbnfLimitedComplex<Vec<T>>
where
T: AsGbnfComplex + GbnfLimitType<Type = U> + GbnfLimitTypeContainer<T, ContainerType = U>,
U: AsGbnfLimit + GbnfLimitStructMarker,
{
fn to_gbnf_limit(self) -> GbnfLimit {
self.values.to_gbnf_limit()
}
}
impl<T, U> AsGbnfLimit for GbnfLimitedComplex<Option<T>>
where
T: GbnfLimitType<Type = U> + GbnfLimitTypeContainer<T, ContainerType = U>,
U: AsGbnfLimit + GbnfLimitStructMarker,
{
fn to_gbnf_limit(self) -> GbnfLimit {
self.values.to_gbnf_limit()
}
}

2
gbnf/src/limits/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub(crate) mod convert;
pub mod definitions;

View File

@ -3,11 +3,11 @@ use darling::{FromDeriveInput, FromField};
use proc_macro::{Span, TokenStream}; use proc_macro::{Span, TokenStream};
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote, ToTokens}; use quote::{format_ident, quote, ToTokens};
use syn::parse_macro_input; use syn::{parse_macro_input, Attribute};
use syn::{DeriveInput, Ident, LitStr}; use syn::{DeriveInput, Ident, LitStr};
#[derive(Clone, Debug, FromField)] #[derive(Clone, Debug, FromField)]
#[darling(forward_attrs(gbnf_limit))] #[darling(forward_attrs(gbnf_limit_primitive, gbnf_limit_complex))]
struct GbnfFieldDef { struct GbnfFieldDef {
ident: Option<syn::Ident>, ident: Option<syn::Ident>,
ty: syn::Type, ty: syn::Type,
@ -15,14 +15,47 @@ struct GbnfFieldDef {
attrs: Vec<syn::Attribute>, attrs: Vec<syn::Attribute>,
} }
enum LimitType {
LimitedPrimitive,
LimitedComplex,
NotLimited,
}
impl GbnfFieldDef {
fn limit_type(&self) -> LimitType {
let limit_priv = self
.attrs
.iter()
.find(|attr| attr.path().get_ident().unwrap().to_string() == "gbnf_limit_primitive");
let limit_complex = self
.attrs
.iter()
.find(|attr| attr.path().get_ident().unwrap().to_string() == "gbnf_limit_complex");
match (limit_priv, limit_complex) {
(Some(_), None) => LimitType::LimitedPrimitive,
(None, Some(_)) => LimitType::LimitedComplex,
(Some(_), Some(_)) => panic!("cannot be both a primitive and complex limit"),
(None, None) => LimitType::NotLimited,
}
}
}
impl ToTokens for GbnfFieldDef { impl ToTokens for GbnfFieldDef {
fn to_tokens(&self, tokens: &mut TokenStream2) { fn to_tokens(&self, tokens: &mut TokenStream2) {
let ident = &self.ident; let ident = &self.ident;
let ty = &self.ty; // TODO figure out how to get ident out let ty = &self.ty; // TODO figure out how to get ident out
let vis = &self.vis; let vis = &self.vis;
let wrapper_type = match self.limit_type() {
LimitType::LimitedPrimitive => quote! { GbnfLimitedPrimitive<#ty> },
LimitType::LimitedComplex => quote! { GbnfLimitedComplex<#ty> },
_ => panic!("somehow attempting to make a limited field without a helper attr"),
};
let output = quote! { let output = quote! {
#vis #ident: Box<dyn GbnfLimitedField<#ty>> #vis #ident: #wrapper_type
}; };
output.to_tokens(tokens); output.to_tokens(tokens);
@ -55,13 +88,18 @@ impl ToTokens for GbnfStructDef {
} }
} }
fn is_limited_field(attr: &Attribute) -> bool {
let ident = attr.path().get_ident().unwrap().to_string();
ident == "gbnf_limit_primitive" || ident == "gbnf_limit_complex"
}
/// Find fields in the struct with a #[gbnf_limit] attribute. /// Find fields in the struct with a #[gbnf_limit] attribute.
fn find_limited_fields(fields: &[GbnfFieldDef]) -> impl Iterator<Item = &GbnfFieldDef> + '_ { fn find_limited_fields(fields: &[GbnfFieldDef]) -> impl Iterator<Item = &GbnfFieldDef> + '_ {
fields.iter().filter(|field| { fields.iter().filter(|field| {
field field
.attrs .attrs
.iter() .iter()
.find(|attr| attr.path().get_ident().unwrap().to_string() == "gbnf_limit") .find(|attr| is_limited_field(attr))
.is_some() .is_some()
}) })
} }
@ -72,7 +110,7 @@ fn find_non_limited_fields(fields: &[GbnfFieldDef]) -> impl Iterator<Item = &Gbn
field field
.attrs .attrs
.iter() .iter()
.find(|attr| attr.path().get_ident().unwrap().to_string() == "gbnf_limit") .find(|attr| is_limited_field(attr))
.is_none() .is_none()
}) })
} }
@ -95,18 +133,28 @@ fn generate_to_grammar_impl(original_struct_name: &Ident, fields: &[GbnfFieldDef
.to_token_stream(); .to_token_stream();
let ident = &field.ident; let ident = &field.ident;
let value = quote! { self.#ident.into() }; let value = quote! { self.#ident.to_gbnf_limit() };
quote! { (#key, #value) } quote! { (#key, #value) }
}); });
let as_gbnf_complex_impl = quote! {
impl AsGbnfComplex for #original_struct_name {
fn to_gbnf_complex() -> GbnfComplex {
Self::to_gbnf().as_complex()
}
}
};
if limit_struct_fields.len() > 0 { if limit_struct_fields.len() > 0 {
quote! { quote! {
pub struct #limit_struct_name { pub struct #limit_struct_name {
#(#limit_struct_fields),* #(#limit_struct_fields),*
} }
impl #limit_struct_name { impl GbnfLimitStructMarker for #limit_struct_name {}
pub fn to_gbnf_limit(self) -> GbnfLimit {
impl AsGbnfLimit for #limit_struct_name {
fn to_gbnf_limit(self) -> GbnfLimit {
GbnfLimit::Complex( GbnfLimit::Complex(
HashMap::from([ HashMap::from([
#(#from_assignments),* #(#from_assignments),*
@ -115,28 +163,12 @@ fn generate_to_grammar_impl(original_struct_name: &Ident, fields: &[GbnfFieldDef
} }
} }
impl GbnfLimitedField<#original_struct_name> for #limit_struct_name { impl GbnfLimitType for #original_struct_name {
fn limit(self: Box<#limit_struct_name>) -> GbnfLimit { type Type = #limit_struct_name;
self.to_gbnf_limit()
}
} }
impl GbnfLimitedField<Option<#original_struct_name>> for #limit_struct_name { impl GbnfLimitTypeContainer<#original_struct_name> for #original_struct_name {
fn limit(self: Box<#limit_struct_name>) -> GbnfLimit { type ContainerType = #limit_struct_name;
self.to_gbnf_limit()
}
}
impl From<#limit_struct_name> for Box<dyn GbnfLimitedField<#original_struct_name>> {
fn from(limit: #limit_struct_name) -> Self {
Box::new(limit)
}
}
impl From<#limit_struct_name> for Box<dyn GbnfLimitedField<Option<#original_struct_name>>> {
fn from(limit: #limit_struct_name) -> Self {
Box::new(limit)
}
} }
impl #original_struct_name { impl #original_struct_name {
@ -145,6 +177,8 @@ fn generate_to_grammar_impl(original_struct_name: &Ident, fields: &[GbnfFieldDef
Self::to_gbnf().as_complex().to_grammar(Some(gbnf_limit)) Self::to_gbnf().as_complex().to_grammar(Some(gbnf_limit))
} }
} }
#as_gbnf_complex_impl
} }
} else { } else {
quote! { quote! {
@ -155,6 +189,8 @@ fn generate_to_grammar_impl(original_struct_name: &Ident, fields: &[GbnfFieldDef
GRAMMAR.get_or_init(|| Self::to_gbnf().as_complex().to_grammar(None)) GRAMMAR.get_or_init(|| Self::to_gbnf().as_complex().to_grammar(None))
} }
} }
#as_gbnf_complex_impl
} }
} }
} }
@ -241,7 +277,7 @@ fn map_limited_gbnfs(fields: &[GbnfFieldDef]) -> impl Iterator<Item = TokenStrea
} }
/// Convert a Rust type into a GBNF grammar. /// Convert a Rust type into a GBNF grammar.
#[proc_macro_derive(Gbnf, attributes(gbnf_limit))] #[proc_macro_derive(Gbnf, attributes(gbnf_limit_primitive, gbnf_limit_complex))]
pub fn gbnf(input: TokenStream) -> TokenStream { pub fn gbnf(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);
generate_gbnf(&input) generate_gbnf(&input)