Skip to main content

meta_language/grammar/
links.rs

1use std::char;
2
3use crate::grammar::interchange::{
4    parse_native_expression, parse_native_grammar, render_native_expression, render_native_grammar,
5};
6use crate::grammar::{
7    CharClassItem, Grammar, GrammarExpr, GrammarFormat, GrammarRule, RuleAttributes, RuleKind,
8};
9use crate::link_network::{Link, LinkId, LinkMetadata, LinkNetwork, LinkType};
10use crate::rust_codec::{FromLinks, LinksCodecError, LinksDecoder, LinksEncoder, ToLinks};
11
12const GRAMMAR: &str = "grammar::grammar";
13const RULE: &str = "grammar::rule";
14
15const EXPR_EMPTY: &str = "grammar::expression::empty-expression";
16const EXPR_TERMINAL: &str = "grammar::expression::terminal";
17const EXPR_TERMINAL_INSENSITIVE: &str = "grammar::expression::case-insensitive-terminal";
18const EXPR_CHAR_RANGE: &str = "grammar::expression::character-range";
19const EXPR_CHAR_CLASS: &str = "grammar::expression::character-class";
20const EXPR_ANY_CHAR: &str = "grammar::expression::any-character";
21const EXPR_NON_TERMINAL: &str = "grammar::expression::nonterminal";
22const EXPR_CHOICE: &str = "grammar::expression::choice";
23const EXPR_SEQUENCE: &str = "grammar::expression::sequence";
24const EXPR_OPTIONAL: &str = "grammar::expression::optional-expression";
25const EXPR_ZERO_OR_MORE: &str = "grammar::expression::zero-or-more-repetition";
26const EXPR_ONE_OR_MORE: &str = "grammar::expression::one-or-more-repetition";
27const EXPR_REPEAT: &str = "grammar::expression::counted-repetition";
28const EXPR_AND: &str = "grammar::expression::positive-predicate";
29const EXPR_NOT: &str = "grammar::expression::negative-predicate";
30const EXPR_CAPTURE: &str = "grammar::expression::capture";
31/// A grammar feature-union expression, held as its native listing spelling.
32const EXPR_FEATURE: &str = "grammar::expression::feature-union-form";
33
34const CHAR_CLASS_CHAR: &str = "grammar::character-class-item::character";
35const CHAR_CLASS_RANGE: &str = "grammar::character-class-item::character-range";
36
37const VALUE_NONE: &str = "grammar::value::absent-value";
38const VALUE_SOME: &str = "grammar::value::present-value";
39const VALUE_STRING_PREFIX: &str = "grammar::value::string::";
40const VALUE_CHAR_PREFIX: &str = "grammar::value::character::";
41const VALUE_BOOL_PREFIX: &str = "grammar::value::boolean-value::";
42const VALUE_USIZE_PREFIX: &str = "grammar::value::natural-number::";
43const VALUE_RULE_KIND_PREFIX: &str = "grammar::value::rule-kind::";
44const VALUE_FORMAT_PREFIX: &str = "grammar::value::format::";
45
46/// Grammar link tags written before the readable-name renames, and the tag
47/// each became. Decoding reads a network written with either.
48const FORMER_GRAMMAR_TAGS: &[(&str, &str)] = &[
49    ("grammar::expr::empty", EXPR_EMPTY),
50    ("grammar::expr::terminal", EXPR_TERMINAL),
51    (
52        "grammar::expr::terminal-insensitive",
53        EXPR_TERMINAL_INSENSITIVE,
54    ),
55    ("grammar::expr::char-range", EXPR_CHAR_RANGE),
56    ("grammar::expr::char-class", EXPR_CHAR_CLASS),
57    ("grammar::expr::any-char", EXPR_ANY_CHAR),
58    ("grammar::expr::non-terminal", EXPR_NON_TERMINAL),
59    ("grammar::expr::choice", EXPR_CHOICE),
60    ("grammar::expr::sequence", EXPR_SEQUENCE),
61    ("grammar::expr::optional", EXPR_OPTIONAL),
62    ("grammar::expr::zero-or-more", EXPR_ZERO_OR_MORE),
63    ("grammar::expr::one-or-more", EXPR_ONE_OR_MORE),
64    ("grammar::expr::repeat", EXPR_REPEAT),
65    ("grammar::expr::and", EXPR_AND),
66    ("grammar::expr::not", EXPR_NOT),
67    ("grammar::expr::capture", EXPR_CAPTURE),
68    ("grammar::char-class-item::char", CHAR_CLASS_CHAR),
69    ("grammar::char-class-item::range", CHAR_CLASS_RANGE),
70    ("grammar::value::none", VALUE_NONE),
71    ("grammar::value::some", VALUE_SOME),
72];
73
74/// Grammar value prefixes written before the readable-name renames, and the
75/// prefix each became.
76const FORMER_VALUE_PREFIXES: &[(&str, &str)] = &[
77    ("grammar::value::char::", VALUE_CHAR_PREFIX),
78    ("grammar::value::bool::", VALUE_BOOL_PREFIX),
79    ("grammar::value::usize::", VALUE_USIZE_PREFIX),
80];
81
82/// The current grammar link tag for `term`, reading a former tag as the tag it became.
83fn current_grammar_tag(term: &str) -> &str {
84    FORMER_GRAMMAR_TAGS
85        .iter()
86        .find(|(former, _)| *former == term)
87        .map_or(term, |(_, current)| current)
88}
89
90impl ToLinks for Grammar {
91    fn to_links(&self, encoder: &mut LinksEncoder) -> LinkId {
92        encode_grammar(encoder.network_mut(), self)
93    }
94}
95
96impl FromLinks for Grammar {
97    fn from_links(decoder: &mut LinksDecoder<'_>, link: LinkId) -> Result<Self, LinksCodecError> {
98        decode_grammar(decoder.network(), link)
99    }
100}
101
102fn encode_grammar(network: &mut LinkNetwork, grammar: &Grammar) -> LinkId {
103    let mut references = Vec::with_capacity(grammar.rules().len() + 2);
104    references.push(encode_option_format(network, grammar.source_format()));
105    references.push(encode_option_string(network, grammar.start()));
106    references.extend(
107        grammar
108            .rules()
109            .iter()
110            .map(|rule| encode_rule(network, rule)),
111    );
112    insert_grammar_node(network, GRAMMAR, &references)
113}
114
115fn decode_grammar(network: &LinkNetwork, link: LinkId) -> Result<Grammar, LinksCodecError> {
116    let references = expect_tag(network, link, GRAMMAR)?;
117    if references.len() < 2 {
118        return Err(malformed(
119            link,
120            "grammar links must contain source format and start references",
121        ));
122    }
123
124    let source_format = decode_option_format(network, references[0])?;
125    let start = decode_option_string(network, references[1])?;
126    let mut grammar = Grammar::new();
127    if let Some(source_format) = source_format {
128        grammar = grammar.with_source_format(source_format);
129    }
130    if let Some(start) = start {
131        grammar = grammar.with_start(start);
132    }
133
134    for rule in &references[2..] {
135        grammar.add_rule(decode_rule(network, *rule)?);
136    }
137    Ok(grammar)
138}
139
140fn encode_rule(network: &mut LinkNetwork, rule: &GrammarRule) -> LinkId {
141    let expr = encode_expr(network, rule.expr());
142    let mut references = vec![
143        encode_string_value(network, rule.name()),
144        expr,
145        encode_rule_kind_value(network, rule.kind()),
146        encode_option_string(network, rule.concept()),
147        encode_option_string(network, rule.doc()),
148    ];
149    // The feature union attributes, when the rule has any, as the native
150    // listing line of an empty rule with them.
151    if !rule.attributes.is_empty() {
152        let carrier = Grammar::new().with_start(ATTRIBUTE_CARRIER).with_rule(
153            GrammarRule::new(ATTRIBUTE_CARRIER, GrammarExpr::Empty)
154                .with_attributes(rule.attributes.clone()),
155        );
156        references.push(encode_string_value(
157            network,
158            &render_native_grammar(&carrier),
159        ));
160    }
161    insert_grammar_node(network, RULE, &references)
162}
163
164/// The name of the empty rule whose native listing carries rule attributes.
165const ATTRIBUTE_CARRIER: &str = "attributes";
166
167fn decode_rule_attributes(
168    network: &LinkNetwork,
169    link: LinkId,
170) -> Result<RuleAttributes, LinksCodecError> {
171    let listing = decode_string_value(network, link)?;
172    parse_native_grammar(&listing)
173        .ok()
174        .and_then(|carrier| carrier.rules().first().map(|rule| rule.attributes.clone()))
175        .ok_or_else(|| malformed(link, "rule attributes must be a native rule listing"))
176}
177
178fn decode_rule(network: &LinkNetwork, link: LinkId) -> Result<GrammarRule, LinksCodecError> {
179    let references = expect_tag(network, link, RULE)?;
180    let (fields, attributes) = match references {
181        [fields @ .., attributes] if references.len() == 6 => (fields, Some(*attributes)),
182        fields => (fields, None),
183    };
184    let [name, expr, kind, concept, doc] = fields else {
185        return Err(malformed(
186            link,
187            "rule links must contain name, expression, kind, concept, and doc references, and may add attributes",
188        ));
189    };
190    let mut rule = GrammarRule::new(
191        decode_string_value(network, *name)?,
192        decode_expr(network, *expr)?,
193    )
194    .with_kind(decode_rule_kind_value(network, *kind)?);
195    if let Some(concept) = decode_option_string(network, *concept)? {
196        rule = rule.with_concept(concept);
197    }
198    if let Some(doc) = decode_option_string(network, *doc)? {
199        rule = rule.with_doc(doc);
200    }
201    if let Some(attributes) = attributes {
202        rule = rule.with_attributes(decode_rule_attributes(network, attributes)?);
203    }
204    Ok(rule)
205}
206
207fn encode_expr(network: &mut LinkNetwork, expr: &GrammarExpr) -> LinkId {
208    match expr {
209        GrammarExpr::Empty => insert_grammar_node(network, EXPR_EMPTY, &[]),
210        GrammarExpr::Terminal(value) => {
211            let references = [encode_string_value(network, value)];
212            insert_grammar_node(network, EXPR_TERMINAL, &references)
213        }
214        GrammarExpr::TerminalInsensitive(value) => {
215            let references = [encode_string_value(network, value)];
216            insert_grammar_node(network, EXPR_TERMINAL_INSENSITIVE, &references)
217        }
218        GrammarExpr::CharRange(start, end) => {
219            let references = [
220                encode_char_value(network, *start),
221                encode_char_value(network, *end),
222            ];
223            insert_grammar_node(network, EXPR_CHAR_RANGE, &references)
224        }
225        GrammarExpr::CharClass { negated, items } => {
226            let mut references = Vec::with_capacity(items.len() + 1);
227            references.push(encode_bool_value(network, *negated));
228            references.extend(
229                items
230                    .iter()
231                    .map(|item| encode_char_class_item(network, item)),
232            );
233            insert_grammar_node(network, EXPR_CHAR_CLASS, &references)
234        }
235        GrammarExpr::AnyChar => insert_grammar_node(network, EXPR_ANY_CHAR, &[]),
236        GrammarExpr::NonTerminal(value) => {
237            let references = [encode_string_value(network, value)];
238            insert_grammar_node(network, EXPR_NON_TERMINAL, &references)
239        }
240        GrammarExpr::Choice {
241            ordered,
242            alternatives,
243        } => {
244            let mut references = Vec::with_capacity(alternatives.len() + 1);
245            references.push(encode_bool_value(network, *ordered));
246            references.extend(
247                alternatives
248                    .iter()
249                    .map(|alternative| encode_expr(network, alternative)),
250            );
251            insert_grammar_node(network, EXPR_CHOICE, &references)
252        }
253        GrammarExpr::Sequence(items) => {
254            let references = items
255                .iter()
256                .map(|item| encode_expr(network, item))
257                .collect::<Vec<_>>();
258            insert_grammar_node(network, EXPR_SEQUENCE, &references)
259        }
260        GrammarExpr::Optional(expr) => encode_unary_expr(network, EXPR_OPTIONAL, expr),
261        GrammarExpr::ZeroOrMore(expr) => encode_unary_expr(network, EXPR_ZERO_OR_MORE, expr),
262        GrammarExpr::OneOrMore(expr) => encode_unary_expr(network, EXPR_ONE_OR_MORE, expr),
263        GrammarExpr::Repeat { expr, min, max } => {
264            let references = [
265                encode_expr(network, expr),
266                encode_usize_value(network, *min),
267                encode_option_usize(network, *max),
268            ];
269            insert_grammar_node(network, EXPR_REPEAT, &references)
270        }
271        GrammarExpr::And(expr) => encode_unary_expr(network, EXPR_AND, expr),
272        GrammarExpr::Not(expr) => encode_unary_expr(network, EXPR_NOT, expr),
273        GrammarExpr::Capture { label, expr } => {
274            let references = [
275                encode_option_string(network, label.as_deref()),
276                encode_expr(network, expr),
277            ];
278            insert_grammar_node(network, EXPR_CAPTURE, &references)
279        }
280        GrammarExpr::Feature(_) => {
281            let references = [encode_string_value(
282                network,
283                &render_native_expression(expr),
284            )];
285            insert_grammar_node(network, EXPR_FEATURE, &references)
286        }
287    }
288}
289
290fn decode_expr(network: &LinkNetwork, link: LinkId) -> Result<GrammarExpr, LinksCodecError> {
291    let (term, references) = grammar_link(network, link)?;
292    match term {
293        EXPR_EMPTY => {
294            expect_count(link, references, 0)?;
295            Ok(GrammarExpr::Empty)
296        }
297        EXPR_TERMINAL => {
298            let [value] = references else {
299                return Err(expected_count(link, 1, references.len()));
300            };
301            Ok(GrammarExpr::Terminal(decode_string_value(network, *value)?))
302        }
303        EXPR_TERMINAL_INSENSITIVE => {
304            let [value] = references else {
305                return Err(expected_count(link, 1, references.len()));
306            };
307            Ok(GrammarExpr::TerminalInsensitive(decode_string_value(
308                network, *value,
309            )?))
310        }
311        EXPR_CHAR_RANGE => {
312            let [start, end] = references else {
313                return Err(expected_count(link, 2, references.len()));
314            };
315            Ok(GrammarExpr::CharRange(
316                decode_char_value(network, *start)?,
317                decode_char_value(network, *end)?,
318            ))
319        }
320        EXPR_CHAR_CLASS => {
321            let Some((negated, items)) = references.split_first() else {
322                return Err(malformed(
323                    link,
324                    "character class links must contain the negated flag",
325                ));
326            };
327            Ok(GrammarExpr::CharClass {
328                negated: decode_bool_value(network, *negated)?,
329                items: items
330                    .iter()
331                    .map(|item| decode_char_class_item(network, *item))
332                    .collect::<Result<Vec<_>, _>>()?,
333            })
334        }
335        EXPR_ANY_CHAR => {
336            expect_count(link, references, 0)?;
337            Ok(GrammarExpr::AnyChar)
338        }
339        EXPR_NON_TERMINAL => {
340            let [value] = references else {
341                return Err(expected_count(link, 1, references.len()));
342            };
343            Ok(GrammarExpr::NonTerminal(decode_string_value(
344                network, *value,
345            )?))
346        }
347        EXPR_CHOICE => {
348            let Some((ordered, alternatives)) = references.split_first() else {
349                return Err(malformed(
350                    link,
351                    "choice links must contain the ordered flag",
352                ));
353            };
354            Ok(GrammarExpr::Choice {
355                ordered: decode_bool_value(network, *ordered)?,
356                alternatives: alternatives
357                    .iter()
358                    .map(|alternative| decode_expr(network, *alternative))
359                    .collect::<Result<Vec<_>, _>>()?,
360            })
361        }
362        EXPR_SEQUENCE => Ok(GrammarExpr::Sequence(
363            references
364                .iter()
365                .map(|item| decode_expr(network, *item))
366                .collect::<Result<Vec<_>, _>>()?,
367        )),
368        EXPR_OPTIONAL => decode_unary_expr(network, link, references).map(GrammarExpr::optional),
369        EXPR_ZERO_OR_MORE => {
370            decode_unary_expr(network, link, references).map(GrammarExpr::zero_or_more)
371        }
372        EXPR_ONE_OR_MORE => {
373            decode_unary_expr(network, link, references).map(GrammarExpr::one_or_more)
374        }
375        EXPR_REPEAT => {
376            let [expr, min, max] = references else {
377                return Err(expected_count(link, 3, references.len()));
378            };
379            Ok(GrammarExpr::repeat(
380                decode_expr(network, *expr)?,
381                decode_usize_value(network, *min)?,
382                decode_option_usize(network, *max)?,
383            ))
384        }
385        EXPR_AND => decode_unary_expr(network, link, references).map(GrammarExpr::and),
386        EXPR_NOT => decode_unary_expr(network, link, references).map(GrammarExpr::not),
387        EXPR_CAPTURE => {
388            let [label, expr] = references else {
389                return Err(expected_count(link, 2, references.len()));
390            };
391            Ok(GrammarExpr::Capture {
392                label: decode_option_string(network, *label)?,
393                expr: Box::new(decode_expr(network, *expr)?),
394            })
395        }
396        EXPR_FEATURE => {
397            let [text] = references else {
398                return Err(expected_count(link, 1, references.len()));
399            };
400            parse_native_expression(&decode_string_value(network, *text)?)
401                .map_err(|error| malformed(link, error.to_string()))
402        }
403        _ => Err(malformed(
404            link,
405            format!("unexpected grammar expression tag {term:?}"),
406        )),
407    }
408}
409
410fn encode_unary_expr(network: &mut LinkNetwork, tag: &str, expr: &GrammarExpr) -> LinkId {
411    let references = [encode_expr(network, expr)];
412    insert_grammar_node(network, tag, &references)
413}
414
415fn decode_unary_expr(
416    network: &LinkNetwork,
417    link: LinkId,
418    references: &[LinkId],
419) -> Result<GrammarExpr, LinksCodecError> {
420    let [expr] = references else {
421        return Err(expected_count(link, 1, references.len()));
422    };
423    decode_expr(network, *expr)
424}
425
426fn encode_char_class_item(network: &mut LinkNetwork, item: &CharClassItem) -> LinkId {
427    match item {
428        CharClassItem::Char(value) => {
429            let references = [encode_char_value(network, *value)];
430            insert_grammar_node(network, CHAR_CLASS_CHAR, &references)
431        }
432        CharClassItem::Range(start, end) => {
433            let references = [
434                encode_char_value(network, *start),
435                encode_char_value(network, *end),
436            ];
437            insert_grammar_node(network, CHAR_CLASS_RANGE, &references)
438        }
439    }
440}
441
442fn decode_char_class_item(
443    network: &LinkNetwork,
444    link: LinkId,
445) -> Result<CharClassItem, LinksCodecError> {
446    let (term, references) = grammar_link(network, link)?;
447    match term {
448        CHAR_CLASS_CHAR => {
449            let [value] = references else {
450                return Err(expected_count(link, 1, references.len()));
451            };
452            Ok(CharClassItem::Char(decode_char_value(network, *value)?))
453        }
454        CHAR_CLASS_RANGE => {
455            let [start, end] = references else {
456                return Err(expected_count(link, 2, references.len()));
457            };
458            Ok(CharClassItem::Range(
459                decode_char_value(network, *start)?,
460                decode_char_value(network, *end)?,
461            ))
462        }
463        _ => Err(malformed(
464            link,
465            format!("unexpected character class item tag {term:?}"),
466        )),
467    }
468}
469
470fn encode_option_string(network: &mut LinkNetwork, value: Option<&str>) -> LinkId {
471    match value {
472        Some(value) => {
473            let references = [encode_string_value(network, value)];
474            insert_grammar_node(network, VALUE_SOME, &references)
475        }
476        None => insert_grammar_node(network, VALUE_NONE, &[]),
477    }
478}
479
480fn decode_option_string(
481    network: &LinkNetwork,
482    link: LinkId,
483) -> Result<Option<String>, LinksCodecError> {
484    decode_option(network, link, decode_string_value)
485}
486
487fn encode_option_usize(network: &mut LinkNetwork, value: Option<usize>) -> LinkId {
488    match value {
489        Some(value) => {
490            let references = [encode_usize_value(network, value)];
491            insert_grammar_node(network, VALUE_SOME, &references)
492        }
493        None => insert_grammar_node(network, VALUE_NONE, &[]),
494    }
495}
496
497fn decode_option_usize(
498    network: &LinkNetwork,
499    link: LinkId,
500) -> Result<Option<usize>, LinksCodecError> {
501    decode_option(network, link, decode_usize_value)
502}
503
504fn encode_option_format(network: &mut LinkNetwork, value: Option<GrammarFormat>) -> LinkId {
505    match value {
506        Some(value) => {
507            let references = [encode_format_value(network, value)];
508            insert_grammar_node(network, VALUE_SOME, &references)
509        }
510        None => insert_grammar_node(network, VALUE_NONE, &[]),
511    }
512}
513
514fn decode_option_format(
515    network: &LinkNetwork,
516    link: LinkId,
517) -> Result<Option<GrammarFormat>, LinksCodecError> {
518    decode_option(network, link, decode_format_value)
519}
520
521fn decode_option<T>(
522    network: &LinkNetwork,
523    link: LinkId,
524    decode_value: fn(&LinkNetwork, LinkId) -> Result<T, LinksCodecError>,
525) -> Result<Option<T>, LinksCodecError> {
526    let (term, references) = grammar_link(network, link)?;
527    match term {
528        VALUE_NONE => {
529            expect_count(link, references, 0)?;
530            Ok(None)
531        }
532        VALUE_SOME => {
533            let [value] = references else {
534                return Err(expected_count(link, 1, references.len()));
535            };
536            decode_value(network, *value).map(Some)
537        }
538        _ => Err(malformed(
539            link,
540            format!("expected optional value tag, found {term:?}"),
541        )),
542    }
543}
544
545fn encode_string_value(network: &mut LinkNetwork, value: &str) -> LinkId {
546    insert_grammar_value(
547        network,
548        &format!("{VALUE_STRING_PREFIX}{}", hex_encode(value)),
549    )
550}
551
552fn decode_string_value(network: &LinkNetwork, link: LinkId) -> Result<String, LinksCodecError> {
553    let value = prefixed_value(network, link, VALUE_STRING_PREFIX, "String")?;
554    hex_decode(link, "String", value)
555}
556
557fn encode_char_value(network: &mut LinkNetwork, value: char) -> LinkId {
558    insert_grammar_value(
559        network,
560        &format!("{VALUE_CHAR_PREFIX}{:x}", u32::from(value)),
561    )
562}
563
564fn decode_char_value(network: &LinkNetwork, link: LinkId) -> Result<char, LinksCodecError> {
565    let value = prefixed_value(network, link, VALUE_CHAR_PREFIX, "char")?;
566    let code = u32::from_str_radix(value, 16)
567        .map_err(|error| invalid_value(link, "char", Some(value), &error.to_string()))?;
568    char::from_u32(code).ok_or_else(|| invalid_value(link, "char", Some(value), "invalid char"))
569}
570
571fn encode_bool_value(network: &mut LinkNetwork, value: bool) -> LinkId {
572    insert_grammar_value(network, &format!("{VALUE_BOOL_PREFIX}{value}"))
573}
574
575fn decode_bool_value(network: &LinkNetwork, link: LinkId) -> Result<bool, LinksCodecError> {
576    let value = prefixed_value(network, link, VALUE_BOOL_PREFIX, "bool")?;
577    value.parse().map_err(|error: std::str::ParseBoolError| {
578        invalid_value(link, "bool", Some(value), &error.to_string())
579    })
580}
581
582fn encode_usize_value(network: &mut LinkNetwork, value: usize) -> LinkId {
583    insert_grammar_value(network, &format!("{VALUE_USIZE_PREFIX}{value}"))
584}
585
586fn decode_usize_value(network: &LinkNetwork, link: LinkId) -> Result<usize, LinksCodecError> {
587    let value = prefixed_value(network, link, VALUE_USIZE_PREFIX, "usize")?;
588    value.parse().map_err(|error: std::num::ParseIntError| {
589        invalid_value(link, "usize", Some(value), &error.to_string())
590    })
591}
592
593fn encode_rule_kind_value(network: &mut LinkNetwork, value: RuleKind) -> LinkId {
594    insert_grammar_value(
595        network,
596        &format!("{VALUE_RULE_KIND_PREFIX}{}", value.as_str()),
597    )
598}
599
600fn decode_rule_kind_value(
601    network: &LinkNetwork,
602    link: LinkId,
603) -> Result<RuleKind, LinksCodecError> {
604    let value = prefixed_value(network, link, VALUE_RULE_KIND_PREFIX, "RuleKind")?;
605    RuleKind::from_tag(value)
606        .ok_or_else(|| invalid_value(link, "RuleKind", Some(value), "unknown rule kind"))
607}
608
609fn encode_format_value(network: &mut LinkNetwork, value: GrammarFormat) -> LinkId {
610    insert_grammar_value(network, &format!("{VALUE_FORMAT_PREFIX}{}", value.as_str()))
611}
612
613fn decode_format_value(
614    network: &LinkNetwork,
615    link: LinkId,
616) -> Result<GrammarFormat, LinksCodecError> {
617    let value = prefixed_value(network, link, VALUE_FORMAT_PREFIX, "GrammarFormat")?;
618    GrammarFormat::from_tag(value)
619        .ok_or_else(|| invalid_value(link, "GrammarFormat", Some(value), "unknown grammar format"))
620}
621
622fn insert_grammar_node(network: &mut LinkNetwork, term: &str, references: &[LinkId]) -> LinkId {
623    network.insert_dynamic_link(
624        references,
625        LinkMetadata::new()
626            .with_link_type(LinkType::Grammar)
627            .with_term(term),
628    )
629}
630
631fn insert_grammar_value(network: &mut LinkNetwork, term: &str) -> LinkId {
632    insert_grammar_node(network, term, &[])
633}
634
635fn expect_tag<'network>(
636    network: &'network LinkNetwork,
637    link: LinkId,
638    expected: &str,
639) -> Result<&'network [LinkId], LinksCodecError> {
640    let (term, references) = grammar_link(network, link)?;
641    if term == expected {
642        Ok(references)
643    } else {
644        Err(malformed(
645            link,
646            format!("expected grammar tag {expected:?}, found {term:?}"),
647        ))
648    }
649}
650
651fn grammar_link(network: &LinkNetwork, link: LinkId) -> Result<(&str, &[LinkId]), LinksCodecError> {
652    let link = network
653        .link(link)
654        .ok_or(LinksCodecError::MissingLink(link))?;
655    expect_grammar_type(link)?;
656    let Some(term) = link.metadata().term() else {
657        return Err(malformed(link.id(), "grammar link is missing its term tag"));
658    };
659    Ok((current_grammar_tag(term), link.references()))
660}
661
662fn expect_grammar_type(link: &Link) -> Result<(), LinksCodecError> {
663    if link.metadata().link_type() == Some(LinkType::Grammar) {
664        return Ok(());
665    }
666    Err(malformed(
667        link.id(),
668        format!(
669            "expected LinkType::Grammar, found {:?}",
670            link.metadata().link_type()
671        ),
672    ))
673}
674
675fn prefixed_value<'network>(
676    network: &'network LinkNetwork,
677    link: LinkId,
678    prefix: &str,
679    type_name: &str,
680) -> Result<&'network str, LinksCodecError> {
681    let (term, _) = grammar_link(network, link)?;
682    term.strip_prefix(prefix)
683        .or_else(|| {
684            FORMER_VALUE_PREFIXES
685                .iter()
686                .filter(|(_, current)| *current == prefix)
687                .find_map(|(former, _)| term.strip_prefix(former))
688        })
689        .ok_or_else(|| invalid_value(link, type_name, Some(term), "wrong value prefix"))
690}
691
692fn expect_count(
693    link: LinkId,
694    references: &[LinkId],
695    expected: usize,
696) -> Result<(), LinksCodecError> {
697    if references.len() == expected {
698        Ok(())
699    } else {
700        Err(expected_count(link, expected, references.len()))
701    }
702}
703
704fn expected_count(link: LinkId, expected: usize, actual: usize) -> LinksCodecError {
705    malformed(
706        link,
707        format!("expected {expected} references, found {actual}"),
708    )
709}
710
711fn malformed(link: LinkId, reason: impl Into<String>) -> LinksCodecError {
712    LinksCodecError::MalformedObject {
713        object: link,
714        reason: reason.into(),
715    }
716}
717
718fn invalid_value(
719    link: LinkId,
720    type_name: &str,
721    value: Option<&str>,
722    reason: &str,
723) -> LinksCodecError {
724    LinksCodecError::InvalidLiteral {
725        object: link,
726        type_name: type_name.to_string(),
727        value: value.map(ToString::to_string),
728        reason: reason.to_string(),
729    }
730}
731
732fn hex_encode(value: &str) -> String {
733    const HEX: &[u8; 16] = b"0123456789abcdef";
734    let mut output = String::with_capacity(value.len() * 2);
735    for byte in value.as_bytes() {
736        output.push(HEX[(byte >> 4) as usize] as char);
737        output.push(HEX[(byte & 0x0f) as usize] as char);
738    }
739    output
740}
741
742fn hex_decode(link: LinkId, type_name: &str, value: &str) -> Result<String, LinksCodecError> {
743    if !value.len().is_multiple_of(2) {
744        return Err(invalid_value(
745            link,
746            type_name,
747            Some(value),
748            "hex payload has odd length",
749        ));
750    }
751
752    let mut bytes = Vec::with_capacity(value.len() / 2);
753    for pair in value.as_bytes().as_chunks::<2>().0 {
754        let high = hex_digit(pair[0])
755            .ok_or_else(|| invalid_value(link, type_name, Some(value), "invalid hex digit"))?;
756        let low = hex_digit(pair[1])
757            .ok_or_else(|| invalid_value(link, type_name, Some(value), "invalid hex digit"))?;
758        bytes.push((high << 4) | low);
759    }
760
761    String::from_utf8(bytes).map_err(|error| {
762        invalid_value(
763            link,
764            type_name,
765            Some(value),
766            &format!("invalid UTF-8: {error}"),
767        )
768    })
769}
770
771const fn hex_digit(value: u8) -> Option<u8> {
772    match value {
773        b'0'..=b'9' => Some(value - b'0'),
774        b'a'..=b'f' => Some(value - b'a' + 10),
775        b'A'..=b'F' => Some(value - b'A' + 10),
776        _ => None,
777    }
778}