ai-game/gbnf/src/limited.rs

150 lines
4.4 KiB
Rust

use crate::{
AsGrammar, GbnfComplex, GbnfField, GbnfFieldType, GbnfLimit, GbnfPrimitive, GbnfRule,
GbnfRuleLevel, GbnfToken,
};
use itertools::Itertools;
#[derive(Debug, Clone)]
pub struct LimitedGbnfComplex<'a> {
pub complex: &'a GbnfComplex,
pub limit: &'a GbnfLimit,
}
impl AsGrammar for LimitedGbnfComplex<'_> {
fn rule(&self, token: &str) -> GbnfRule {
let mut main_rule = self.complex.rule_for_self(token, Some(self.limit));
let field_rules = self.complex.rules_for_fields(Some(self.limit));
main_rule.dependents = field_rules;
main_rule.dependents.push(GbnfRule::space());
main_rule
}
fn base_type_token(&self) -> GbnfToken {
GbnfToken::new(format!("{}Limit", self.complex.base_type_token()))
}
fn with_limit<'a>(&'a self, _: Option<&'a GbnfLimit>) -> impl AsGrammar + 'a {
self.clone()
}
}
#[derive(Debug, Clone)]
pub struct LimitedGbnfPrimitive<'a> {
primitive: GbnfPrimitive,
values: &'a [String],
}
impl LimitedGbnfPrimitive<'_> {
pub fn new(limit: &GbnfLimit) -> Option<LimitedGbnfPrimitive> {
if let GbnfLimit::Simple(primitive, values) = limit {
Some(LimitedGbnfPrimitive {
primitive: *primitive,
values,
})
} else {
None
}
}
}
impl AsGrammar for LimitedGbnfPrimitive<'_> {
fn rule(&self, token: &str) -> GbnfRule {
let values = self
.values
.into_iter()
.map(|v| {
if self.primitive == GbnfPrimitive::String {
format!(r#""\"{}\"""#, v)
} else {
format!(r#""{}""#, v)
}
})
.join(" | ");
GbnfRule::new(token.into(), values, GbnfRuleLevel::Leaf)
}
fn base_type_token(&self) -> GbnfToken {
GbnfToken::new(format!("{}Limit", self.primitive.base_type_token()))
}
fn with_limit<'a>(&'a self, _: Option<&'a GbnfLimit>) -> impl AsGrammar + 'a {
self.clone()
}
}
#[derive(Debug, Clone)]
pub struct LimitedGbnfField<'a> {
pub field: &'a GbnfField,
pub limit: &'a GbnfLimit,
}
macro_rules! wrap_in_limit {
($field:expr, $limit:expr, $token:expr) => {
$field.with_limit($limit).rule($token)
};
}
impl AsGrammar for LimitedGbnfField<'_> {
fn rule(&self, token: &str) -> GbnfRule {
// This is always the limit for this specific field.
let limit = Some(self.limit);
match &self.field.field_type {
GbnfFieldType::Complex(f) => wrap_in_limit!(f, limit, token),
GbnfFieldType::Primitive(f) => wrap_in_limit!(f, limit, token),
GbnfFieldType::OptionalComplex(f) => wrap_in_limit!(f, limit, token).to_optional(),
GbnfFieldType::OptionalPrimitive(f) => wrap_in_limit!(f, limit, token).to_optional(),
GbnfFieldType::ComplexList(f) => wrap_in_limit!(f, limit, token).to_list(),
GbnfFieldType::PrimitiveList(f) => wrap_in_limit!(f, limit, token).to_list(),
}
}
fn base_type_token(&self) -> GbnfToken {
let token = match &self.field.field_type {
GbnfFieldType::Primitive(f) => f.base_type_token(),
GbnfFieldType::Complex(f) => f.base_type_token(),
GbnfFieldType::OptionalPrimitive(f) => f.base_type_token(),
GbnfFieldType::OptionalComplex(f) => f.base_type_token(),
GbnfFieldType::PrimitiveList(f) => f.base_type_token(),
GbnfFieldType::ComplexList(f) => f.base_type_token(),
};
GbnfToken::new(format!("{}{}Limit", self.field.field_name, token))
}
fn with_limit<'a>(&'a self, _: Option<&'a GbnfLimit>) -> impl AsGrammar + 'a {
self.clone()
}
}
#[derive(Debug, Clone)]
pub struct Limited<'a, T, L>(pub &'a T, pub Option<L>)
where
T: AsGrammar + Clone,
L: AsGrammar + Clone;
impl<T, L> AsGrammar for Limited<'_, T, L>
where
T: AsGrammar + Clone,
L: AsGrammar + Clone,
{
fn rule(&self, token: &str) -> GbnfRule {
self.1
.as_ref()
.map(|l| l.rule(token))
.unwrap_or_else(|| self.0.rule(token))
}
fn base_type_token(&self) -> GbnfToken {
self.1
.as_ref()
.map(|l| l.base_type_token())
.unwrap_or_else(|| self.0.base_type_token())
}
fn with_limit<'a>(&'a self, _: Option<&'a GbnfLimit>) -> impl AsGrammar + 'a {
self.clone()
}
}