1pub mod concepts;
27pub mod emit;
28pub mod fidelity;
29pub mod import;
30pub mod inference;
31mod links;
32pub mod runtime;
33pub mod surface;
34pub mod translate;
35pub mod validate;
36
37pub use concepts::{
38 annotate_grammar_concepts, grammar_expr_concept_id, rule_concept_id, GrammarConcept,
39 GRAMMAR_CONCEPTS,
40};
41pub use emit::{
42 emit_abnf, emit_bnf, emit_ebnf, emit_gbnf, emit_javascript_parser, emit_peggy, emit_pest,
43 emit_rust_parser, emit_tree_sitter_grammar_js, emit_tree_sitter_grammar_js_with_report,
44 render_rust_type, EmitReport, GrammarEmitError, JsParserArtifacts, RustParserArtifacts,
45};
46pub use fidelity::{
47 canonical_grammar_format, grammar_format_profile, GrammarFidelityLevel, GrammarFormatProfile,
48 GRAMMAR_CONSTRUCTS, GRAMMAR_FORMATS,
49};
50pub use import::{
51 import_abnf, import_antlr, import_bnf, import_ebnf, import_gbnf, import_lark, import_pest,
52 import_tree_sitter_json, GrammarImportError,
53};
54pub use inference::active::{
55 clean_structural_acceptance, learn_dfa, learn_grammar, ActiveLearningConfig,
56 ActiveLearningError, Dfa, GrammarAcceptorOracle, Oracle as ActiveLearningOracle,
57 ParserAcceptancePredicate, ParserMembershipOracle, SamplingEquivalenceOracle,
58 Symbol as ActiveSymbol,
59};
60pub use inference::advisor::{
61 AdviceDecision, AdviceDecisionKind, AdviceSource, ConceptNamingAdvisor, FallbackAdvisor,
62 MdlMergeAdvisor, MergeAdvisor, MergeCandidate, MergeRequest, MergeScore, NameCandidate,
63 NamingAdvisor, NamingRequest,
64};
65#[cfg(feature = "llm-assist")]
66pub use inference::advisor::{LlmClient, LlmError, LlmMergeAdvisor, LlmNamingAdvisor};
67pub use inference::cfg::{
68 infer_cfg, infer_cfg_with_advisors, InferenceOptions, InferenceReport, InferenceResult, Oracle,
69 PositiveOnlyOracle,
70};
71pub use inference::eval::{
72 evaluate, mdl, run_corpus, run_named_corpus, sample, size_symbols, BenchmarkReport, EvalError,
73 GoldenCorpus, GrammarOracle, MembershipOracle, MetricScores, SampleConfig, ScoringMode,
74 GOLDEN_CORPORA,
75};
76pub use inference::lexical::{
77 categorise, infer_lexical_classes, CharCategory, LexicalConfig, LexicalModel, Token,
78};
79pub use inference::minimize::{
80 mdl_cost, minimize, Mdl, MinimizeOptions, MinimizeReport, MinimizeResult,
81};
82pub use inference::prior::{
83 build_structural_prior, ByteSpan, Delimiter, LeafKind, PriorOptions, SeedNode, SeedTree,
84 StructuralPrior, WhitespacePolicy,
85};
86pub use inference::semantic::{
87 default_pattern_catalog, evaluate_atom, evaluate_clause, evaluate_constraint,
88 evaluate_probabilistic, mine_semantic_constraints, ConstraintAtom, ConstraintClause,
89 ConstraintPattern, LengthUnit, NonTerminalRef, SemanticConstraint, SemanticInferenceConfig,
90};
91pub use inference::sequitur::{run_sequitur, Symbol};
92pub use inference::state_merging::{infer_dfa, InferredAutomaton, MergeStrategy, Sample};
93pub use runtime::{register_grammar, with_grammar, GrammarParser};
94pub use surface::{
95 grammar_from_lino, grammar_to_lino, parse_grammar_surface, write_grammar_surface,
96 GrammarSurfaceError,
97};
98pub use translate::{
99 grammar_concept_translation_rules, translate_grammar_surface, GrammarTranslateError,
100};
101pub use validate::{validate, DiagnosticKind, GrammarDiagnostic, RuleSpan, Severity};
102
103use std::collections::BTreeSet;
104use std::fmt;
105
106#[derive(Clone, Debug, PartialEq, Eq)]
108pub enum GrammarExpr {
109 Empty,
111 Terminal(String),
113 TerminalInsensitive(String),
115 CharRange(char, char),
117 CharClass {
119 negated: bool,
121 items: Vec<CharClassItem>,
123 },
124 AnyChar,
126 NonTerminal(String),
128 Choice {
130 ordered: bool,
132 alternatives: Vec<Self>,
134 },
135 Sequence(Vec<Self>),
137 Optional(Box<Self>),
139 ZeroOrMore(Box<Self>),
141 OneOrMore(Box<Self>),
143 Repeat {
145 expr: Box<Self>,
147 min: usize,
149 max: Option<usize>,
151 },
152 And(Box<Self>),
154 Not(Box<Self>),
156 Capture {
158 label: Option<String>,
160 expr: Box<Self>,
162 },
163}
164
165impl GrammarExpr {
166 #[must_use]
168 pub const fn empty() -> Self {
169 Self::Empty
170 }
171
172 #[must_use]
174 pub fn terminal(value: impl Into<String>) -> Self {
175 Self::Terminal(value.into())
176 }
177
178 #[must_use]
180 pub fn terminal_insensitive(value: impl Into<String>) -> Self {
181 Self::TerminalInsensitive(value.into())
182 }
183
184 #[must_use]
186 pub const fn char_range(start: char, end: char) -> Self {
187 Self::CharRange(start, end)
188 }
189
190 #[must_use]
192 pub fn char_class<I>(negated: bool, items: I) -> Self
193 where
194 I: IntoIterator<Item = CharClassItem>,
195 {
196 Self::CharClass {
197 negated,
198 items: items.into_iter().collect(),
199 }
200 }
201
202 #[must_use]
204 pub const fn any_char() -> Self {
205 Self::AnyChar
206 }
207
208 #[must_use]
210 pub fn non_terminal(value: impl Into<String>) -> Self {
211 Self::NonTerminal(value.into())
212 }
213
214 #[must_use]
216 pub fn choice<I>(ordered: bool, alternatives: I) -> Self
217 where
218 I: IntoIterator<Item = Self>,
219 {
220 Self::Choice {
221 ordered,
222 alternatives: alternatives.into_iter().collect(),
223 }
224 }
225
226 #[must_use]
228 pub fn sequence<I>(items: I) -> Self
229 where
230 I: IntoIterator<Item = Self>,
231 {
232 Self::Sequence(items.into_iter().collect())
233 }
234
235 #[must_use]
237 pub fn optional(expr: Self) -> Self {
238 Self::Optional(Box::new(expr))
239 }
240
241 #[must_use]
243 pub fn zero_or_more(expr: Self) -> Self {
244 Self::ZeroOrMore(Box::new(expr))
245 }
246
247 #[must_use]
249 pub fn one_or_more(expr: Self) -> Self {
250 Self::OneOrMore(Box::new(expr))
251 }
252
253 #[must_use]
255 pub fn repeat(expr: Self, min: usize, max: Option<usize>) -> Self {
256 Self::Repeat {
257 expr: Box::new(expr),
258 min,
259 max,
260 }
261 }
262
263 #[must_use]
265 pub fn and(expr: Self) -> Self {
266 Self::And(Box::new(expr))
267 }
268
269 #[must_use]
271 #[allow(clippy::should_implement_trait)]
272 pub fn not(expr: Self) -> Self {
273 Self::Not(Box::new(expr))
274 }
275
276 #[must_use]
278 pub fn capture(label: impl Into<String>, expr: Self) -> Self {
279 Self::Capture {
280 label: Some(label.into()),
281 expr: Box::new(expr),
282 }
283 }
284
285 #[must_use]
287 pub fn capture_unlabeled(expr: Self) -> Self {
288 Self::Capture {
289 label: None,
290 expr: Box::new(expr),
291 }
292 }
293
294 fn collect_nonterminals(&self, names: &mut BTreeSet<String>) {
295 match self {
296 Self::NonTerminal(name) => {
297 names.insert(name.clone());
298 }
299 Self::Choice { alternatives, .. } => {
300 for alternative in alternatives {
301 alternative.collect_nonterminals(names);
302 }
303 }
304 Self::Sequence(items) => {
305 for item in items {
306 item.collect_nonterminals(names);
307 }
308 }
309 Self::Optional(expr)
310 | Self::ZeroOrMore(expr)
311 | Self::OneOrMore(expr)
312 | Self::And(expr)
313 | Self::Not(expr)
314 | Self::Capture { expr, .. }
315 | Self::Repeat { expr, .. } => expr.collect_nonterminals(names),
316 Self::Empty
317 | Self::Terminal(_)
318 | Self::TerminalInsensitive(_)
319 | Self::CharRange(_, _)
320 | Self::CharClass { .. }
321 | Self::AnyChar => {}
322 }
323 }
324}
325
326impl fmt::Display for GrammarExpr {
327 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
328 match self {
329 Self::Empty => formatter.write_str("empty"),
330 Self::Terminal(value) => write!(formatter, "{value:?}"),
331 Self::TerminalInsensitive(value) => write!(formatter, "i{value:?}"),
332 Self::CharRange(start, end) => write!(formatter, "{start:?}..={end:?}"),
333 Self::CharClass { negated, items } => {
334 let marker = if *negated { "^" } else { "" };
335 write!(formatter, "[{marker}")?;
336 for item in items {
337 write!(formatter, "{item}")?;
338 }
339 formatter.write_str("]")
340 }
341 Self::AnyChar => formatter.write_str("."),
342 Self::NonTerminal(name) => formatter.write_str(name),
343 Self::Choice {
344 ordered,
345 alternatives,
346 } => {
347 let separator = if *ordered { " / " } else { " | " };
348 write_joined(formatter, alternatives, separator)
349 }
350 Self::Sequence(items) => write_joined(formatter, items, " "),
351 Self::Optional(expr) => write!(formatter, "({expr})?"),
352 Self::ZeroOrMore(expr) => write!(formatter, "({expr})*"),
353 Self::OneOrMore(expr) => write!(formatter, "({expr})+"),
354 Self::Repeat { expr, min, max } => match max {
355 Some(max) => write!(formatter, "({expr}){{{min},{max}}}"),
356 None => write!(formatter, "({expr}){{{min},}}"),
357 },
358 Self::And(expr) => write!(formatter, "&({expr})"),
359 Self::Not(expr) => write!(formatter, "!({expr})"),
360 Self::Capture { label, expr } => match label {
361 Some(label) => write!(formatter, "{label}:({expr})"),
362 None => write!(formatter, "capture({expr})"),
363 },
364 }
365 }
366}
367
368#[derive(Clone, Debug, PartialEq, Eq)]
370pub enum CharClassItem {
371 Char(char),
373 Range(char, char),
375}
376
377impl CharClassItem {
378 #[must_use]
380 pub const fn char(value: char) -> Self {
381 Self::Char(value)
382 }
383
384 #[must_use]
386 pub const fn range(start: char, end: char) -> Self {
387 Self::Range(start, end)
388 }
389}
390
391impl fmt::Display for CharClassItem {
392 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
393 match self {
394 Self::Char(value) => write!(formatter, "{}", value.escape_default()),
395 Self::Range(start, end) => {
396 write!(
397 formatter,
398 "{}-{}",
399 start.escape_default(),
400 end.escape_default()
401 )
402 }
403 }
404 }
405}
406
407#[derive(Clone, Copy, Debug, PartialEq, Eq)]
409pub enum RuleKind {
410 Normal,
412 Atomic,
414 Silent,
416 Token,
418}
419
420impl RuleKind {
421 #[must_use]
423 pub const fn as_str(self) -> &'static str {
424 match self {
425 Self::Normal => "normal",
426 Self::Atomic => "atomic",
427 Self::Silent => "silent",
428 Self::Token => "token",
429 }
430 }
431
432 pub(crate) fn from_tag(value: &str) -> Option<Self> {
433 match value {
434 "normal" => Some(Self::Normal),
435 "atomic" => Some(Self::Atomic),
436 "silent" => Some(Self::Silent),
437 "token" => Some(Self::Token),
438 _ => None,
439 }
440 }
441}
442
443impl fmt::Display for RuleKind {
444 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
445 formatter.write_str(self.as_str())
446 }
447}
448
449#[derive(Clone, Debug, PartialEq, Eq)]
451pub struct GrammarRule {
452 pub name: String,
454 pub expr: GrammarExpr,
456 pub kind: RuleKind,
458 pub concept: Option<String>,
460 pub doc: Option<String>,
462}
463
464impl GrammarRule {
465 #[must_use]
467 pub fn new(name: impl Into<String>, expr: GrammarExpr) -> Self {
468 Self {
469 name: name.into(),
470 expr,
471 kind: RuleKind::Normal,
472 concept: None,
473 doc: None,
474 }
475 }
476
477 #[must_use]
479 pub const fn with_kind(mut self, kind: RuleKind) -> Self {
480 self.kind = kind;
481 self
482 }
483
484 #[must_use]
486 pub fn with_concept(mut self, concept: impl Into<String>) -> Self {
487 self.concept = Some(concept.into());
488 self
489 }
490
491 #[must_use]
493 pub fn with_doc(mut self, doc: impl Into<String>) -> Self {
494 self.doc = Some(doc.into());
495 self
496 }
497
498 #[must_use]
500 pub fn name(&self) -> &str {
501 &self.name
502 }
503
504 #[must_use]
506 pub const fn expr(&self) -> &GrammarExpr {
507 &self.expr
508 }
509
510 #[must_use]
512 pub const fn kind(&self) -> RuleKind {
513 self.kind
514 }
515
516 #[must_use]
518 pub fn concept(&self) -> Option<&str> {
519 self.concept.as_deref()
520 }
521
522 #[must_use]
524 pub fn doc(&self) -> Option<&str> {
525 self.doc.as_deref()
526 }
527}
528
529#[derive(Clone, Copy, Debug, PartialEq, Eq)]
531pub enum GrammarFormat {
532 MetaLanguage,
534 Bnf,
536 Ebnf,
538 Abnf,
540 Peg,
542 Antlr,
544 Lark,
546 Gbnf,
548 TreeSitter,
550 Inferred,
552}
553
554impl GrammarFormat {
555 #[must_use]
557 pub const fn as_str(self) -> &'static str {
558 match self {
559 Self::MetaLanguage => "meta-language",
560 Self::Bnf => "bnf",
561 Self::Ebnf => "ebnf",
562 Self::Abnf => "abnf",
563 Self::Peg => "peg",
564 Self::Antlr => "antlr",
565 Self::Lark => "lark",
566 Self::Gbnf => "gbnf",
567 Self::TreeSitter => "tree-sitter",
568 Self::Inferred => "inferred",
569 }
570 }
571
572 pub(crate) fn from_tag(value: &str) -> Option<Self> {
573 match value {
574 "meta-language" => Some(Self::MetaLanguage),
575 "bnf" => Some(Self::Bnf),
576 "ebnf" => Some(Self::Ebnf),
577 "abnf" => Some(Self::Abnf),
578 "peg" => Some(Self::Peg),
579 "antlr" => Some(Self::Antlr),
580 "lark" => Some(Self::Lark),
581 "gbnf" => Some(Self::Gbnf),
582 "tree-sitter" => Some(Self::TreeSitter),
583 "inferred" => Some(Self::Inferred),
584 _ => None,
585 }
586 }
587}
588
589impl fmt::Display for GrammarFormat {
590 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
591 formatter.write_str(self.as_str())
592 }
593}
594
595#[derive(Clone, Debug, Default, PartialEq, Eq)]
597pub struct Grammar {
598 rules: Vec<GrammarRule>,
599 start: Option<String>,
600 source_format: Option<GrammarFormat>,
601}
602
603impl Grammar {
604 #[must_use]
606 pub const fn new() -> Self {
607 Self {
608 rules: Vec::new(),
609 start: None,
610 source_format: None,
611 }
612 }
613
614 #[must_use]
616 pub const fn builder() -> GrammarBuilder {
617 GrammarBuilder::new()
618 }
619
620 #[must_use]
622 pub const fn expr() -> ExprBuilder {
623 ExprBuilder
624 }
625
626 #[must_use]
628 pub fn with_rule(mut self, rule: GrammarRule) -> Self {
629 self.rules.push(rule);
630 self
631 }
632
633 #[must_use]
635 pub fn with_start(mut self, start: impl Into<String>) -> Self {
636 self.start = Some(start.into());
637 self
638 }
639
640 #[must_use]
642 pub const fn with_source_format(mut self, source_format: GrammarFormat) -> Self {
643 self.source_format = Some(source_format);
644 self
645 }
646
647 pub fn add_rule(&mut self, rule: GrammarRule) {
649 self.rules.push(rule);
650 }
651
652 pub fn set_start(&mut self, start: impl Into<String>) {
654 self.start = Some(start.into());
655 }
656
657 pub fn clear_start(&mut self) {
659 self.start = None;
660 }
661
662 pub const fn set_source_format(&mut self, source_format: GrammarFormat) {
664 self.source_format = Some(source_format);
665 }
666
667 #[must_use]
669 pub fn rules(&self) -> &[GrammarRule] {
670 &self.rules
671 }
672
673 #[must_use]
675 pub fn rule(&self, name: &str) -> Option<&GrammarRule> {
676 self.rules.iter().find(|rule| rule.name == name)
677 }
678
679 #[must_use]
681 pub fn start(&self) -> Option<&str> {
682 self.start.as_deref()
683 }
684
685 #[must_use]
687 pub fn start_rule(&self) -> Option<&GrammarRule> {
688 self.start
689 .as_deref()
690 .map_or_else(|| self.rules.first(), |start| self.rule(start))
691 }
692
693 #[must_use]
695 pub const fn source_format(&self) -> Option<GrammarFormat> {
696 self.source_format
697 }
698
699 #[must_use]
701 pub fn rule_names(&self) -> Vec<&str> {
702 self.rules.iter().map(GrammarRule::name).collect()
703 }
704
705 #[must_use]
707 pub fn referenced_nonterminals(&self) -> BTreeSet<String> {
708 let mut names = BTreeSet::new();
709 for rule in &self.rules {
710 rule.expr.collect_nonterminals(&mut names);
711 }
712 names
713 }
714
715 #[must_use]
717 pub fn undefined_nonterminals(&self) -> BTreeSet<String> {
718 let defined = self
719 .rules
720 .iter()
721 .map(|rule| rule.name.clone())
722 .collect::<BTreeSet<_>>();
723 self.referenced_nonterminals()
724 .difference(&defined)
725 .cloned()
726 .collect()
727 }
728}
729
730#[derive(Clone, Debug, Default, PartialEq, Eq)]
732pub struct GrammarBuilder {
733 grammar: Grammar,
734}
735
736impl GrammarBuilder {
737 #[must_use]
739 pub const fn new() -> Self {
740 Self {
741 grammar: Grammar::new(),
742 }
743 }
744
745 #[must_use]
747 pub const fn source_format(mut self, source_format: GrammarFormat) -> Self {
748 self.grammar.source_format = Some(source_format);
749 self
750 }
751
752 #[must_use]
754 pub fn start(mut self, start: impl Into<String>) -> Self {
755 self.grammar.start = Some(start.into());
756 self
757 }
758
759 #[must_use]
761 pub fn rule(mut self, name: impl Into<String>, expr: GrammarExpr) -> Self {
762 self.grammar.rules.push(GrammarRule::new(name, expr));
763 self
764 }
765
766 #[must_use]
768 pub fn grammar_rule(mut self, rule: GrammarRule) -> Self {
769 self.grammar.rules.push(rule);
770 self
771 }
772
773 #[must_use]
775 pub fn rule_with_kind(
776 mut self,
777 name: impl Into<String>,
778 expr: GrammarExpr,
779 kind: RuleKind,
780 ) -> Self {
781 self.grammar
782 .rules
783 .push(GrammarRule::new(name, expr).with_kind(kind));
784 self
785 }
786
787 #[must_use]
789 pub fn build(self) -> Grammar {
790 self.grammar
791 }
792}
793
794#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
796pub struct ExprBuilder;
797
798impl ExprBuilder {
799 #[must_use]
801 pub const fn empty(self) -> GrammarExpr {
802 GrammarExpr::Empty
803 }
804
805 #[must_use]
807 pub fn term(self, value: impl Into<String>) -> GrammarExpr {
808 GrammarExpr::terminal(value)
809 }
810
811 #[must_use]
813 pub fn terminal(self, value: impl Into<String>) -> GrammarExpr {
814 GrammarExpr::terminal(value)
815 }
816
817 #[must_use]
819 pub fn terminal_insensitive(self, value: impl Into<String>) -> GrammarExpr {
820 GrammarExpr::terminal_insensitive(value)
821 }
822
823 #[must_use]
825 pub const fn char(self, value: char) -> GrammarExpr {
826 GrammarExpr::CharRange(value, value)
827 }
828
829 #[must_use]
831 pub const fn char_range(self, start: char, end: char) -> GrammarExpr {
832 GrammarExpr::CharRange(start, end)
833 }
834
835 #[must_use]
837 pub fn char_class<I>(self, negated: bool, items: I) -> GrammarExpr
838 where
839 I: IntoIterator<Item = CharClassItem>,
840 {
841 GrammarExpr::char_class(negated, items)
842 }
843
844 #[must_use]
846 pub const fn any(self) -> GrammarExpr {
847 GrammarExpr::AnyChar
848 }
849
850 #[must_use]
852 pub fn nt(self, value: impl Into<String>) -> GrammarExpr {
853 GrammarExpr::non_terminal(value)
854 }
855
856 #[must_use]
858 pub fn non_terminal(self, value: impl Into<String>) -> GrammarExpr {
859 GrammarExpr::non_terminal(value)
860 }
861
862 #[must_use]
864 pub fn choice<I>(self, ordered: bool, alternatives: I) -> GrammarExpr
865 where
866 I: IntoIterator<Item = GrammarExpr>,
867 {
868 GrammarExpr::choice(ordered, alternatives)
869 }
870
871 #[must_use]
873 pub fn choice_ordered<I>(self, alternatives: I) -> GrammarExpr
874 where
875 I: IntoIterator<Item = GrammarExpr>,
876 {
877 GrammarExpr::choice(true, alternatives)
878 }
879
880 #[must_use]
882 pub fn choice_unordered<I>(self, alternatives: I) -> GrammarExpr
883 where
884 I: IntoIterator<Item = GrammarExpr>,
885 {
886 GrammarExpr::choice(false, alternatives)
887 }
888
889 #[must_use]
891 pub fn seq<I>(self, items: I) -> GrammarExpr
892 where
893 I: IntoIterator<Item = GrammarExpr>,
894 {
895 GrammarExpr::sequence(items)
896 }
897
898 #[must_use]
900 pub fn opt(self, expr: GrammarExpr) -> GrammarExpr {
901 GrammarExpr::optional(expr)
902 }
903
904 #[must_use]
906 pub fn rep0(self, expr: GrammarExpr) -> GrammarExpr {
907 GrammarExpr::zero_or_more(expr)
908 }
909
910 #[must_use]
912 pub fn rep1(self, expr: GrammarExpr) -> GrammarExpr {
913 GrammarExpr::one_or_more(expr)
914 }
915
916 #[must_use]
918 pub fn repeat(self, expr: GrammarExpr, min: usize, max: Option<usize>) -> GrammarExpr {
919 GrammarExpr::repeat(expr, min, max)
920 }
921
922 #[must_use]
924 pub fn and(self, expr: GrammarExpr) -> GrammarExpr {
925 GrammarExpr::and(expr)
926 }
927
928 #[must_use]
930 pub fn not(self, expr: GrammarExpr) -> GrammarExpr {
931 GrammarExpr::not(expr)
932 }
933
934 #[must_use]
936 pub fn capture(self, label: Option<impl Into<String>>, expr: GrammarExpr) -> GrammarExpr {
937 match label {
938 Some(label) => GrammarExpr::capture(label, expr),
939 None => GrammarExpr::capture_unlabeled(expr),
940 }
941 }
942
943 #[must_use]
945 pub fn capture_unlabeled(self, expr: GrammarExpr) -> GrammarExpr {
946 GrammarExpr::capture_unlabeled(expr)
947 }
948}
949
950fn write_joined(
951 formatter: &mut fmt::Formatter<'_>,
952 expressions: &[GrammarExpr],
953 separator: &str,
954) -> fmt::Result {
955 if let Some((first, rest)) = expressions.split_first() {
956 write!(formatter, "{first}")?;
957 for expression in rest {
958 formatter.write_str(separator)?;
959 write!(formatter, "{expression}")?;
960 }
961 }
962 Ok(())
963}