/ gbnf / src / lib.rs
lib.rs
  1  use std::{
  2      collections::HashMap,
  3      fmt::Display,
  4      ops::{Deref, DerefMut},
  5  };
  6  
  7  use crate::limits::convert::{Limited, LimitedGbnfComplex, LimitedGbnfField, LimitedGbnfPrimitive};
  8  use convert_case::{Case, Casing};
  9  use itertools::Itertools;
 10  use serde::de::DeserializeOwned;
 11  
 12  mod limits;
 13  
 14  pub mod limit {
 15      pub use crate::limits::definitions::*;
 16  }
 17  
 18  pub mod prelude {
 19      pub use crate::gbnf_field;
 20      pub use crate::gbnf_field_type;
 21      pub use crate::limit;
 22      pub use crate::limit::*;
 23      pub use crate::AsGbnf;
 24      pub use crate::AsGbnfComplex;
 25      pub use crate::AsGbnfLimit;
 26      pub use crate::AsGbnfPrimitive;
 27      pub use crate::AsGrammar;
 28      pub use crate::GbnfComplex;
 29      pub use crate::GbnfField;
 30      pub use crate::GbnfFieldType;
 31      pub use crate::GbnfLimit;
 32      pub use crate::GbnfPrimitive;
 33      pub use crate::GbnfRule;
 34      pub use crate::GbnfRuleLevel;
 35      pub use crate::GbnfToken;
 36      pub use std::collections::HashMap;
 37  }
 38  
 39  /// The GbnfLimitedField trait is capable of producing a GbnfLimitType
 40  /// enum value, which itself contains either a list of allowable
 41  /// values (for simple types), or a nested GbnfLimit object(for
 42  /// complex types). This allows the creation of a complex nested Limit
 43  /// struct which can hold the full structure of nested limits of
 44  /// multiple types that derive Gbnf and have limits.
 45  pub trait AsGbnfLimit {
 46      fn to_gbnf_limit(self) -> GbnfLimit;
 47  }
 48  
 49  /// A type of GBNF value limit, either simple (a list of values meant
 50  /// for a single field), or complex (contains nested GbnfLimit).
 51  #[derive(Debug)]
 52  pub enum GbnfLimit {
 53      /// The primitive type, and the allowed values.
 54      Simple(GbnfPrimitive, Vec<String>),
 55      /// Field name -> nested limit type.
 56      Complex(HashMap<&'static str, GbnfLimit>),
 57  }
 58  
 59  // Converts GBNF defintions (through the types below) into the grammar
 60  // rules.
 61  pub trait AsGrammar {
 62      /// Create the rule itself, along with its dependent rules.
 63      fn rule(&self, token: &str) -> GbnfRule;
 64  
 65      /// The basic token for the type, or type contained within a
 66      /// wrapper. A wrapper is something like GbnfField. This always
 67      /// returns the token for the base type of the rule (including
 68      /// option or list, for those types).
 69      fn base_type_token(&self) -> GbnfToken;
 70  
 71      /// Wraps this trait impl in a corresponding Limited GBNF rule
 72      /// creator.
 73      fn with_limit<'a>(&'a self, limit: Option<&'a GbnfLimit>) -> impl AsGrammar + 'a;
 74  }
 75  
 76  /// Trait for regular types to implement to convert themselves to a
 77  /// GBNF value.
 78  pub trait AsGbnf {
 79      fn to_gbnf() -> GbnfFieldType;
 80  }
 81  
 82  pub trait AsGbnfPrimitive {
 83      fn to_gbnf_primitive() -> GbnfPrimitive;
 84  }
 85  
 86  pub trait AsGbnfComplex {
 87      fn to_gbnf_complex() -> GbnfComplex;
 88  }
 89  
 90  macro_rules! define_field_type {
 91      ($type:ty, $gbnf_type:expr) => {
 92          impl AsGbnf for $type {
 93              fn to_gbnf() -> GbnfFieldType {
 94                  $gbnf_type
 95              }
 96          }
 97      };
 98  }
 99  
100  macro_rules! define_gbnf_primitive {
101      ($type:ty, $gbnf_primitive:expr) => {
102          impl AsGbnfPrimitive for $type {
103              fn to_gbnf_primitive() -> GbnfPrimitive {
104                  $gbnf_primitive
105              }
106          }
107      };
108  }
109  
110  macro_rules! define_primitive_field_type {
111      ($type:ty, $gbnf_primitive:expr) => {
112          define_gbnf_primitive!($type, $gbnf_primitive);
113          define_field_type!($type, GbnfFieldType::Primitive($gbnf_primitive));
114      };
115  }
116  
117  #[macro_export]
118  macro_rules! gbnf_field_type {
119      ($type:ty) => {
120          <$type as AsGbnf>::to_gbnf()
121      };
122  }
123  
124  #[macro_export]
125  macro_rules! gbnf_field {
126      ($field_name:literal, $field_type:ty) => {
127          GbnfField {
128              field_name: $field_name.to_string(),
129              field_type: gbnf_field_type!($field_type),
130              limited: false,
131          }
132      };
133  
134      ($field_name:literal, $field_type: ty, $field_limit:expr) => {
135          GbnfField {
136              field_name: $field_name.to_string(),
137              field_type: gbnf_field_type!($field_type),
138              limit: $field_limit,
139          }
140      };
141  }
142  
143  // Implemented field type mappings for common rust types.
144  define_primitive_field_type!(i16, GbnfPrimitive::Number);
145  define_primitive_field_type!(u16, GbnfPrimitive::Number);
146  define_primitive_field_type!(i32, GbnfPrimitive::Number);
147  define_primitive_field_type!(u32, GbnfPrimitive::Number);
148  define_primitive_field_type!(i64, GbnfPrimitive::Number);
149  define_primitive_field_type!(u64, GbnfPrimitive::Number);
150  define_primitive_field_type!(f32, GbnfPrimitive::Number);
151  define_primitive_field_type!(f64, GbnfPrimitive::Number);
152  define_primitive_field_type!(usize, GbnfPrimitive::Number);
153  
154  define_primitive_field_type!(bool, GbnfPrimitive::Boolean);
155  
156  define_primitive_field_type!(String, GbnfPrimitive::String);
157  define_primitive_field_type!(char, GbnfPrimitive::String);
158  
159  // Blanket implementations to cover more types
160  impl<T, const N: usize> AsGbnf for [T; N]
161  where
162      T: AsGbnf + DeserializeOwned,
163  {
164      fn to_gbnf() -> GbnfFieldType {
165          use GbnfFieldType::*;
166          match <T as AsGbnf>::to_gbnf() {
167              Primitive(primitive_type) => PrimitiveList(primitive_type),
168              OptionalPrimitive(primitive_type) => PrimitiveList(primitive_type),
169              Complex(complex_type) => ComplexList(complex_type),
170              OptionalComplex(complex_type) => ComplexList(complex_type),
171              ComplexList(_) | PrimitiveList(_) => panic!("nested lists not supported"),
172          }
173      }
174  }
175  
176  impl<T> AsGbnf for Vec<T>
177  where
178      T: AsGbnf + DeserializeOwned,
179  {
180      fn to_gbnf() -> GbnfFieldType {
181          use GbnfFieldType::*;
182          match <T as AsGbnf>::to_gbnf() {
183              Primitive(primitive_type) => PrimitiveList(primitive_type),
184              OptionalPrimitive(primitive_type) => PrimitiveList(primitive_type),
185              Complex(complex_type) => ComplexList(complex_type),
186              OptionalComplex(complex_type) => ComplexList(complex_type),
187              ComplexList(_) | PrimitiveList(_) => panic!("nested lists not supported"),
188          }
189      }
190  }
191  
192  impl<T> AsGbnf for Option<T>
193  where
194      T: AsGbnf + DeserializeOwned,
195  {
196      fn to_gbnf() -> GbnfFieldType {
197          use GbnfFieldType::*;
198          match <T as AsGbnf>::to_gbnf() {
199              Primitive(primitive_type) => OptionalPrimitive(primitive_type),
200              Complex(complex_type) => OptionalComplex(complex_type),
201              OptionalPrimitive(_) | OptionalComplex(_) => panic!("nested options are not allowed"),
202              _ => panic!("optional type cannot be a list"),
203          }
204      }
205  }
206  
207  #[derive(Debug, Clone, Eq, Hash, PartialEq, Ord, PartialOrd)]
208  pub enum GbnfRuleLevel {
209      Root,
210      Middle,
211      Leaf,
212  }
213  
214  // Actual GBNF rule itself. Holds rule text for dedup.
215  #[derive(Debug, Clone, Eq, Hash, PartialEq)]
216  pub struct GbnfRule {
217      name: GbnfToken,
218      text: String,
219      dependents: Vec<GbnfRule>,
220      level: GbnfRuleLevel,
221  }
222  
223  impl GbnfRule {
224      pub fn new(token: GbnfToken, rule_text: String, level: GbnfRuleLevel) -> GbnfRule {
225          GbnfRule {
226              name: token,
227              text: rule_text,
228              dependents: vec![],
229              level,
230          }
231      }
232  
233      pub fn space() -> GbnfRule {
234          GbnfRule::new(
235              GbnfToken::new("ws".to_string()),
236              r#"[ \t\n]*"#.to_string(),
237              GbnfRuleLevel::Leaf,
238          )
239      }
240  
241      /// Turn this rule into an option rule: main rule is an option type, and
242      /// dependent rule is the original rule itself.
243      pub fn to_optional(self) -> GbnfRule {
244          let option_token = self.name.option_token();
245          let option_rule_text = format!(r#"{}   |   "null""#, self.name);
246          let mut option_rule = GbnfRule::new(option_token, option_rule_text, GbnfRuleLevel::Middle);
247          option_rule.dependents = vec![self];
248          option_rule
249      }
250  
251      /// Turn this rule into a list rule: main rule is a list type, and
252      /// dependent rule is the original rule itself.
253      pub fn to_list(self) -> GbnfRule {
254          let list_name = self.name.list_token();
255          let list_rule_text =
256              r#""[]" | "["   {SPACE}   {TYPE_NAME}   (","   {SPACE}   {TYPE_NAME})*   "]""#
257                  .replace("{LIST_NAME}", &list_name.0)
258                  .replace("{SPACE}", &GbnfRule::space().name.0)
259                  .replace("{TYPE_NAME}", &self.name.0);
260  
261          let mut list_rule = GbnfRule::new(list_name, list_rule_text, GbnfRuleLevel::Middle);
262          list_rule.dependents = vec![self, GbnfRule::space()];
263          list_rule
264      }
265  
266      /// Consume this rule to produce an ordered list of rules consisting of
267      /// this rule and its dependents. The rules in the list have no
268      /// dependents. Useful for final output of a rules list.
269      fn flatten(mut self) -> Vec<GbnfRule> {
270          let dependents = self
271              .dependents
272              .drain(0..)
273              .flat_map(|rule| rule.flatten())
274              .collect_vec();
275  
276          // Self needs to be in front to get proper rule ordering.
277          [&[self][..], &dependents[..]].concat()
278      }
279  }
280  
281  /// Tokens in the GBNF rule.
282  #[repr(transparent)]
283  #[derive(Debug, Clone, Eq, Hash, PartialEq)]
284  pub struct GbnfToken(String);
285  
286  impl From<&str> for GbnfToken {
287      fn from(value: &str) -> Self {
288          GbnfToken(value.to_string().to_case(Case::Camel))
289      }
290  }
291  
292  impl Display for GbnfToken {
293      fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
294          write!(f, "{}", self.0)
295      }
296  }
297  
298  impl Deref for GbnfToken {
299      type Target = String;
300      fn deref(&self) -> &Self::Target {
301          &self.0
302      }
303  }
304  
305  impl DerefMut for GbnfToken {
306      fn deref_mut(&mut self) -> &mut Self::Target {
307          &mut self.0
308      }
309  }
310  
311  impl GbnfToken {
312      pub fn new<S: AsRef<str>>(value: S) -> GbnfToken {
313          GbnfToken::from(value.as_ref())
314      }
315  
316      fn option_token(&self) -> GbnfToken {
317          GbnfToken::new(format!("{}Option", self.0))
318      }
319  
320      fn list_token(&self) -> GbnfToken {
321          GbnfToken::new(format!("{}List", self.0))
322      }
323  }
324  
325  /// Represents a primitive value in the GBNF, the simplest possible
326  /// value a type can hold.
327  #[derive(Debug, Clone, Copy, PartialEq)]
328  pub enum GbnfPrimitive {
329      String,
330      Boolean,
331      Number,
332  }
333  
334  impl GbnfPrimitive {
335      pub(self) const STRING: &'static str = r#""\""   ([^"]*)   "\"""#;
336      pub(self) const BOOLEAN: &'static str = r#""true" | "false""#;
337      pub(self) const NUMBER: &'static str = r#"[0-9]+   "."?   [0-9]*"#;
338  }
339  
340  impl AsGrammar for GbnfPrimitive {
341      /// Output the raw GBNF rule of this primitive.
342      fn rule(&self, token: &str) -> GbnfRule {
343          let rule_text = match self {
344              Self::Boolean => Self::BOOLEAN,
345              Self::Number => Self::NUMBER,
346              Self::String => Self::STRING,
347          };
348  
349          GbnfRule::new(token.into(), rule_text.to_string(), GbnfRuleLevel::Leaf)
350      }
351  
352      /// Output the token name of the GBNF rule (to refer to in other
353      /// rules).
354      fn base_type_token(&self) -> GbnfToken {
355          GbnfToken::from(match self {
356              Self::Boolean => "boolean",
357              Self::Number => "number",
358              Self::String => "string",
359          })
360      }
361  
362      fn with_limit<'a>(&'a self, limit: Option<&'a GbnfLimit>) -> impl AsGrammar + 'a {
363          Limited(self, limit.and_then(|l| LimitedGbnfPrimitive::new(l)))
364      }
365  }
366  
367  /// Categorize all types of fields that the generated grammar can
368  /// handle.
369  #[derive(Debug, Clone)]
370  pub enum GbnfFieldType {
371      /// A single property on the type, e.g. myField: i32
372      Primitive(GbnfPrimitive),
373  
374      /// Can be a value or null.
375      OptionalPrimitive(GbnfPrimitive),
376  
377      /// A list/vec of primitive types.
378      PrimitiveList(GbnfPrimitive),
379  
380      /// A complex type, with its own properties.
381      Complex(GbnfComplex),
382  
383      /// Can be a value or null.
384      OptionalComplex(GbnfComplex),
385  
386      /// A list/vec of complex types.
387      ComplexList(GbnfComplex),
388  }
389  
390  impl GbnfFieldType {
391      pub fn as_primitive(self) -> GbnfPrimitive {
392          match self {
393              GbnfFieldType::Primitive(primitive) => primitive,
394              _ => panic!("not a GBNF primitive"),
395          }
396      }
397  
398      pub fn as_complex(self) -> GbnfComplex {
399          match self {
400              GbnfFieldType::Complex(complex) => complex,
401              _ => panic!("Not a GBNF complex type"),
402          }
403      }
404  }
405  
406  /// Connect a property name and a field type to generate a GBNF rule.
407  #[derive(Debug, Clone)]
408  pub struct GbnfField {
409      pub field_name: String,
410      pub field_type: GbnfFieldType,
411      pub limited: bool,
412  }
413  
414  impl AsGrammar for GbnfField {
415      fn base_type_token(&self) -> GbnfToken {
416          match &self.field_type {
417              GbnfFieldType::Primitive(f) => f.base_type_token(),
418              GbnfFieldType::Complex(f) => f.base_type_token(),
419              GbnfFieldType::OptionalPrimitive(f) => f.base_type_token(),
420              GbnfFieldType::OptionalComplex(f) => f.base_type_token(),
421              GbnfFieldType::PrimitiveList(f) => f.base_type_token(),
422              GbnfFieldType::ComplexList(f) => f.base_type_token(),
423          }
424      }
425  
426      fn rule(&self, token: &str) -> GbnfRule {
427          match &self.field_type {
428              GbnfFieldType::Complex(f) => f.rule(token),
429              GbnfFieldType::Primitive(f) => f.rule(token),
430              GbnfFieldType::OptionalComplex(f) => f.rule(token).to_optional(),
431              GbnfFieldType::OptionalPrimitive(f) => f.rule(token).to_optional(),
432              GbnfFieldType::ComplexList(f) => f.rule(token).to_list(),
433              GbnfFieldType::PrimitiveList(f) => f.rule(token).to_list(),
434          }
435      }
436  
437      fn with_limit<'a>(&'a self, limit: Option<&'a GbnfLimit>) -> impl AsGrammar + 'a {
438          Limited(
439              self,
440              limit.map(|l| LimitedGbnfField {
441                  field: self,
442                  limit: l,
443              }),
444          )
445      }
446  }
447  
448  /// The complex type is a direct mapping from a supported Rust struct,
449  /// and also used to generate the root of a GBNF grammar.
450  #[derive(Debug, Clone)]
451  pub struct GbnfComplex {
452      pub name: String,
453      pub fields: Vec<GbnfField>,
454  }
455  
456  impl GbnfComplex {
457      pub fn to_grammar(&self, limit: Option<GbnfLimit>) -> String {
458          // The root type cannot itself be limited.
459          let mut root = GbnfRule::new(
460              GbnfToken::new("root".to_string()),
461              self.base_type_token().0,
462              GbnfRuleLevel::Root,
463          );
464  
465          let root_type_rule = if let Some(_) = limit {
466              let limited_self = self.with_limit(limit.as_ref());
467              limited_self.rule(&root.text)
468          } else {
469              self.rule(&root.text)
470          };
471  
472          root.dependents = vec![root_type_rule];
473          let rules = vec![root];
474  
475          // Final output: flatten all rules into one giant list of
476          // rules with no dependents, sort according to "rule level",
477          // and then deduplicate.
478          let mut grammar = rules
479              .into_iter()
480              .flat_map(|rule| rule.flatten())
481              .sorted_by(|rule1, rule2| Ord::cmp(&rule1.level, &rule2.level))
482              .unique()
483              .map(|rule| format!("{} ::= {}", rule.name, rule.text))
484              .join("\n");
485  
486          grammar.push('\n');
487          grammar
488      }
489  
490      fn rule_for_field(&self, field: &GbnfField, limit: Option<&GbnfLimit>) -> GbnfRule {
491          if let Some(GbnfLimit::Complex(nested_limit)) = limit {
492              nested_limit
493                  .get(field.field_name.as_str())
494                  .map(|l| {
495                      // For complex type fields, we "namespace" the
496                      // field type name when limiting, to prevent
497                      // collisions.
498                      let limited = field.with_limit(Some(l));
499                      let token = format!("{}{}", self.name, limited.base_type_token());
500                      limited.rule(&token)
501                  })
502                  .unwrap_or_else(|| field.rule(&field.base_type_token()))
503          } else {
504              field.rule(&field.base_type_token())
505          }
506      }
507  
508      /// The GBNF rule for the complex type itself.
509      fn rule_for_self(&self, token: &str, limit: Option<&GbnfLimit>) -> GbnfRule {
510          let mut rule = String::new();
511  
512          rule.push_str(r#""{"  "#);
513  
514          let field_rules_text = self
515              .fields
516              .iter()
517              .map(|field| {
518                  let field_rule = self.rule_for_field(field, limit);
519                  let token = field_rule.name;
520  
521                  let mut text = String::new();
522                  text.push_str(&GbnfRule::space().name.0);
523                  text.push_str("   ");
524                  text.push_str(&format!(
525                      r#""\"{}\":"   {}  {}"#,
526                      field.field_name,
527                      GbnfRule::space().name.0,
528                      token,
529                  ));
530                  text
531              })
532              .join(r#"   ","   "#);
533  
534          rule.push_str(&field_rules_text);
535          rule.push_str(r#"   "}""#);
536  
537          GbnfRule::new(token.into(), rule, GbnfRuleLevel::Middle)
538      }
539  
540      /// The rules for the fields of the complex type.
541      fn rules_for_fields(&self, limit: Option<&GbnfLimit>) -> Vec<GbnfRule> {
542          let mut rules = vec![];
543          for field in &self.fields {
544              rules.push(self.rule_for_field(field, limit));
545          }
546  
547          rules
548      }
549  }
550  
551  impl AsGrammar for GbnfComplex {
552      fn rule(&self, token: &str) -> GbnfRule {
553          let mut main_rule = self.rule_for_self(token, None);
554          let field_rules = self.rules_for_fields(None);
555          main_rule.dependents = field_rules;
556          main_rule.dependents.push(GbnfRule::space());
557          main_rule
558      }
559  
560      fn base_type_token(&self) -> GbnfToken {
561          GbnfToken::new(self.name.clone())
562      }
563  
564      fn with_limit<'a>(&'a self, limit: Option<&'a GbnfLimit>) -> impl AsGrammar + 'a {
565          Limited(
566              self,
567              limit.map(|l| LimitedGbnfComplex {
568                  complex: self,
569                  limit: l,
570              }),
571          )
572      }
573  }