Skip to main content

tabnas/
merge.rs

1// Copyright (c) 2013-2026 Richard Rodger, MIT License
2
3//! Commutative composition of two configured parser instances.
4//!
5//! Token numbers belong to one `Tabnas` instance.  Merge therefore moves
6//! grammar slots and token sets through token *names*, allocates a fresh
7//! deterministic token space, and only then materializes the combined rules.
8
9use crate::options::{
10    CommentDef, ConfigModifier, FixedToken, LexCheck, LexMatcher, MatchToken, MatchTokenMatcher,
11    MatchValue, TextModifier, ValueDef,
12};
13use crate::{
14    Action, ActionBinding, AltActionBinding, AltSpec, ContextAction, LexSubscriber, Options,
15    Plugin, PluginError, RuleDoneSubscriber, RuleSpec, RuleSubscriber, Tabnas, TokenSubscriber,
16    Value,
17};
18use indexmap::IndexMap;
19use std::collections::{BTreeSet, HashMap};
20use std::fmt;
21use std::sync::Arc;
22
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct MergeError(pub String);
25
26impl fmt::Display for MergeError {
27    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
28        formatter.write_str(&self.0)
29    }
30}
31
32impl std::error::Error for MergeError {}
33
34fn conflict(path: &str) -> MergeError {
35    MergeError(format!("merge: conflicting option values at {path}"))
36}
37
38fn pick<T: Clone>(
39    left: &T,
40    right: &T,
41    default: &T,
42    path: &str,
43    equal: &dyn Fn(&T, &T) -> bool,
44) -> Result<T, MergeError> {
45    if equal(left, right) {
46        Ok(left.clone())
47    } else if equal(left, default) {
48        Ok(right.clone())
49    } else if equal(right, default) {
50        Ok(left.clone())
51    } else {
52        Err(conflict(path))
53    }
54}
55
56fn pick_without_default<T: Clone>(
57    left: &T,
58    right: &T,
59    path: &str,
60    equal: &dyn Fn(&T, &T) -> bool,
61) -> Result<T, MergeError> {
62    equal(left, right)
63        .then(|| left.clone())
64        .ok_or_else(|| conflict(path))
65}
66
67fn eq_value(left: &Value, right: &Value) -> bool {
68    left.deep_equal(right)
69}
70
71fn eq_f64(left: &f64, right: &f64) -> bool {
72    (left.is_nan() && right.is_nan()) || left.to_bits() == right.to_bits()
73}
74
75fn eq_option_arc<T: ?Sized>(left: &Option<Arc<T>>, right: &Option<Arc<T>>) -> bool {
76    match (left, right) {
77        (None, None) => true,
78        (Some(left), Some(right)) => Arc::ptr_eq(left, right),
79        _ => false,
80    }
81}
82
83fn eq_arc_vec<T: ?Sized>(left: &[Arc<T>], right: &[Arc<T>]) -> bool {
84    left.len() == right.len()
85        && left
86            .iter()
87            .zip(right)
88            .all(|(left, right)| Arc::ptr_eq(left, right))
89}
90
91fn eq_text_modifier_vec(left: &[TextModifier], right: &[TextModifier]) -> bool {
92    left.len() == right.len()
93        && left
94            .iter()
95            .zip(right)
96            .all(|(left, right)| left.same_callback(right))
97}
98
99fn eq_prepare_vec(left: &[crate::ParsePrepare], right: &[crate::ParsePrepare]) -> bool {
100    left.len() == right.len()
101        && left
102            .iter()
103            .zip(right)
104            .all(|(left, right)| left.same_callback(right))
105}
106
107fn eq_option_check(left: &Option<LexCheck>, right: &Option<LexCheck>) -> bool {
108    match (left, right) {
109        (None, None) => true,
110        (Some(left), Some(right)) => left.same_callback(right),
111        _ => false,
112    }
113}
114
115fn ordered_keys<T>(left: &IndexMap<String, T>, right: &IndexMap<String, T>) -> Vec<String> {
116    let mut keys = left.keys().cloned().collect::<Vec<_>>();
117    keys.extend(right.keys().filter(|key| !left.contains_key(*key)).cloned());
118    keys
119}
120
121fn sorted_hash_keys<T>(left: &HashMap<String, T>, right: &HashMap<String, T>) -> Vec<String> {
122    let mut keys = left
123        .keys()
124        .chain(right.keys())
125        .cloned()
126        .collect::<BTreeSet<_>>()
127        .into_iter()
128        .collect::<Vec<_>>();
129    keys.shrink_to_fit();
130    keys
131}
132
133fn merge_index_map<T: Clone>(
134    left: &IndexMap<String, T>,
135    right: &IndexMap<String, T>,
136    default: &IndexMap<String, T>,
137    prefix: &str,
138    equal: &dyn Fn(&T, &T) -> bool,
139) -> Result<IndexMap<String, T>, MergeError> {
140    let mut out = IndexMap::new();
141    for key in ordered_keys(left, right) {
142        let value = match (left.get(&key), right.get(&key), default.get(&key)) {
143            (Some(left), Some(right), Some(default)) => Some(pick(
144                left,
145                right,
146                default,
147                &format!("{prefix}.{key}"),
148                equal,
149            )?),
150            (Some(left), Some(right), None) => Some(pick_without_default(
151                left,
152                right,
153                &format!("{prefix}.{key}"),
154                equal,
155            )?),
156            // A missing resolved default entry represents an explicit
157            // deletion. It wins against the unchanged default, while a
158            // deletion and a different replacement are both non-default.
159            (Some(value), None, Some(default)) | (None, Some(value), Some(default)) => {
160                if equal(value, default) {
161                    None
162                } else {
163                    return Err(conflict(&format!("{prefix}.{key}")));
164                }
165            }
166            (Some(value), None, None) | (None, Some(value), None) => Some(value.clone()),
167            (None, None, _) => unreachable!(),
168        };
169        if let Some(value) = value {
170            out.insert(key, value);
171        }
172    }
173    Ok(out)
174}
175
176fn merge_hash_map<T: Clone>(
177    left: &HashMap<String, T>,
178    right: &HashMap<String, T>,
179    default: &HashMap<String, T>,
180    prefix: &str,
181    equal: &dyn Fn(&T, &T) -> bool,
182) -> Result<HashMap<String, T>, MergeError> {
183    let mut out = HashMap::new();
184    for key in sorted_hash_keys(left, right) {
185        let value = match (left.get(&key), right.get(&key), default.get(&key)) {
186            (Some(left), Some(right), Some(default)) => Some(pick(
187                left,
188                right,
189                default,
190                &format!("{prefix}.{key}"),
191                equal,
192            )?),
193            (Some(left), Some(right), None) => Some(pick_without_default(
194                left,
195                right,
196                &format!("{prefix}.{key}"),
197                equal,
198            )?),
199            (Some(value), None, Some(default)) | (None, Some(value), Some(default)) => {
200                if equal(value, default) {
201                    None
202                } else {
203                    return Err(conflict(&format!("{prefix}.{key}")));
204                }
205            }
206            (Some(value), None, None) | (None, Some(value), None) => Some(value.clone()),
207            (None, None, _) => unreachable!(),
208        };
209        if let Some(value) = value {
210            out.insert(key, value);
211        }
212    }
213    Ok(out)
214}
215
216fn merge_char_map(
217    left: &HashMap<char, String>,
218    right: &HashMap<char, String>,
219    default: &HashMap<char, String>,
220    prefix: &str,
221) -> Result<HashMap<char, String>, MergeError> {
222    let keys = left
223        .keys()
224        .chain(right.keys())
225        .copied()
226        .collect::<BTreeSet<_>>();
227    let mut out = HashMap::new();
228    for key in keys {
229        let path = format!("{prefix}.{key}");
230        let value = match (left.get(&key), right.get(&key), default.get(&key)) {
231            (Some(left), Some(right), Some(default)) => {
232                Some(pick(left, right, default, &path, &PartialEq::eq)?)
233            }
234            (Some(left), Some(right), None) => {
235                Some(pick_without_default(left, right, &path, &PartialEq::eq)?)
236            }
237            (Some(value), None, Some(default)) | (None, Some(value), Some(default)) => {
238                if value == default {
239                    None
240                } else {
241                    return Err(conflict(&path));
242                }
243            }
244            (Some(value), None, None) | (None, Some(value), None) => Some(value.clone()),
245            (None, None, _) => unreachable!(),
246        };
247        if let Some(value) = value {
248            out.insert(key, value);
249        }
250    }
251    Ok(out)
252}
253
254fn eq_matcher(left: &MatchTokenMatcher, right: &MatchTokenMatcher) -> bool {
255    match (left, right) {
256        (MatchTokenMatcher::Regex(left), MatchTokenMatcher::Regex(right)) => {
257            left.as_str() == right.as_str()
258        }
259        (MatchTokenMatcher::Callback(left), MatchTokenMatcher::Callback(right)) => {
260            Arc::ptr_eq(left, right)
261        }
262        _ => false,
263    }
264}
265
266fn eq_fixed_token(left: &FixedToken, right: &FixedToken) -> bool {
267    left.name == right.name && left.source == right.source
268}
269
270fn eq_match_token(left: &MatchToken, right: &MatchToken) -> bool {
271    left.name == right.name
272        && left.eager == right.eager
273        && eq_matcher(&left.matcher, &right.matcher)
274}
275
276fn eq_match_value(left: &MatchValue, right: &MatchValue) -> bool {
277    left.name == right.name
278        && eq_matcher(&left.matcher, &right.matcher)
279        && match (&left.val, &right.val) {
280            (None, None) => true,
281            (Some(left), Some(right)) => left.deep_equal(right),
282            _ => false,
283        }
284        && eq_option_arc(&left.transform, &right.transform)
285}
286
287fn eq_value_def(left: &ValueDef, right: &ValueDef) -> bool {
288    match (&left.val, &right.val) {
289        (None, None) => {}
290        (Some(left), Some(right)) if left.deep_equal(right) => {}
291        _ => return false,
292    }
293    match (&left.matcher, &right.matcher) {
294        (None, None) => {}
295        (Some(left), Some(right)) if left.as_str() == right.as_str() => {}
296        _ => return false,
297    }
298    left.consume == right.consume && eq_option_arc(&left.transform, &right.transform)
299}
300
301fn eq_comment_def(left: &CommentDef, right: &CommentDef) -> bool {
302    left.line == right.line
303        && left.start == right.start
304        && left.end == right.end
305        && left.lex == right.lex
306        && left.suffixes == right.suffixes
307        && left.eat_line == right.eat_line
308        && match (&left.suffix_matcher, &right.suffix_matcher) {
309            (None, None) => true,
310            (Some(left), Some(right)) => left.same_callback(right),
311            _ => false,
312        }
313}
314
315fn eq_lex_matcher(left: &LexMatcher, right: &LexMatcher) -> bool {
316    left.name == right.name
317        && eq_f64(&left.order, &right.order)
318        && eq_option_arc(&left.matcher, &right.matcher)
319        && eq_option_arc(&left.imperative, &right.imperative)
320        && eq_option_arc(&left.factory, &right.factory)
321}
322
323fn merge_options(left: &Options, right: &Options) -> Result<Options, MergeError> {
324    let default = Options::default();
325    let mut out = default.clone();
326
327    out.safe.key = pick(
328        &left.safe.key,
329        &right.safe.key,
330        &default.safe.key,
331        "safe.key",
332        &PartialEq::eq,
333    )?;
334
335    out.fixed.lex = pick(
336        &left.fixed.lex,
337        &right.fixed.lex,
338        &default.fixed.lex,
339        "fixed.lex",
340        &PartialEq::eq,
341    )?;
342    out.fixed.check = pick(
343        &left.fixed.check,
344        &right.fixed.check,
345        &default.fixed.check,
346        "fixed.check",
347        &eq_option_check,
348    )?;
349    out.fixed.tokens = merge_index_map(
350        &left.fixed.tokens,
351        &right.fixed.tokens,
352        &default.fixed.tokens,
353        "fixed.token",
354        &eq_fixed_token,
355    )?;
356
357    out.space.lex = pick(
358        &left.space.lex,
359        &right.space.lex,
360        &default.space.lex,
361        "space.lex",
362        &PartialEq::eq,
363    )?;
364    out.space.chars = pick(
365        &left.space.chars,
366        &right.space.chars,
367        &default.space.chars,
368        "space.chars",
369        &PartialEq::eq,
370    )?;
371    out.space.check = pick(
372        &left.space.check,
373        &right.space.check,
374        &default.space.check,
375        "space.check",
376        &eq_option_check,
377    )?;
378
379    out.text.lex = pick(
380        &left.text.lex,
381        &right.text.lex,
382        &default.text.lex,
383        "text.lex",
384        &PartialEq::eq,
385    )?;
386    out.text.modify = pick(
387        &left.text.modify,
388        &right.text.modify,
389        &default.text.modify,
390        "text.modify",
391        &|left, right| eq_text_modifier_vec(left, right),
392    )?;
393    out.text.check = pick(
394        &left.text.check,
395        &right.text.check,
396        &default.text.check,
397        "text.check",
398        &eq_option_check,
399    )?;
400
401    out.number.lex = pick(
402        &left.number.lex,
403        &right.number.lex,
404        &default.number.lex,
405        "number.lex",
406        &PartialEq::eq,
407    )?;
408    out.number.hex = pick(
409        &left.number.hex,
410        &right.number.hex,
411        &default.number.hex,
412        "number.hex",
413        &PartialEq::eq,
414    )?;
415    out.number.oct = pick(
416        &left.number.oct,
417        &right.number.oct,
418        &default.number.oct,
419        "number.oct",
420        &PartialEq::eq,
421    )?;
422    out.number.bin = pick(
423        &left.number.bin,
424        &right.number.bin,
425        &default.number.bin,
426        "number.bin",
427        &PartialEq::eq,
428    )?;
429    out.number.sep = pick(
430        &left.number.sep,
431        &right.number.sep,
432        &default.number.sep,
433        "number.sep",
434        &PartialEq::eq,
435    )?;
436    out.number.exclude = pick(
437        &left.number.exclude,
438        &right.number.exclude,
439        &default.number.exclude,
440        "number.exclude",
441        &PartialEq::eq,
442    )?;
443    out.number.check = pick(
444        &left.number.check,
445        &right.number.check,
446        &default.number.check,
447        "number.check",
448        &eq_option_check,
449    )?;
450
451    out.string.lex = pick(
452        &left.string.lex,
453        &right.string.lex,
454        &default.string.lex,
455        "string.lex",
456        &PartialEq::eq,
457    )?;
458    out.string.chars = pick(
459        &left.string.chars,
460        &right.string.chars,
461        &default.string.chars,
462        "string.chars",
463        &PartialEq::eq,
464    )?;
465    out.string.multi_chars = pick(
466        &left.string.multi_chars,
467        &right.string.multi_chars,
468        &default.string.multi_chars,
469        "string.multiChars",
470        &PartialEq::eq,
471    )?;
472    out.string.escape_char = pick(
473        &left.string.escape_char,
474        &right.string.escape_char,
475        &default.string.escape_char,
476        "string.escapeChar",
477        &PartialEq::eq,
478    )?;
479    out.string.escape = merge_char_map(
480        &left.string.escape,
481        &right.string.escape,
482        &default.string.escape,
483        "string.escape",
484    )?;
485    out.string.replace = merge_char_map(
486        &left.string.replace,
487        &right.string.replace,
488        &default.string.replace,
489        "string.replace",
490    )?;
491    out.string.allow_unknown = pick(
492        &left.string.allow_unknown,
493        &right.string.allow_unknown,
494        &default.string.allow_unknown,
495        "string.allowUnknown",
496        &PartialEq::eq,
497    )?;
498    out.string.escape_strict = pick(
499        &left.string.escape_strict,
500        &right.string.escape_strict,
501        &default.string.escape_strict,
502        "string.escapeStrict",
503        &PartialEq::eq,
504    )?;
505    out.string.allow_control = pick(
506        &left.string.allow_control,
507        &right.string.allow_control,
508        &default.string.allow_control,
509        "string.allowControl",
510        &PartialEq::eq,
511    )?;
512    out.string.abandon = pick(
513        &left.string.abandon,
514        &right.string.abandon,
515        &default.string.abandon,
516        "string.abandon",
517        &PartialEq::eq,
518    )?;
519    out.string.check = pick(
520        &left.string.check,
521        &right.string.check,
522        &default.string.check,
523        "string.check",
524        &eq_option_check,
525    )?;
526
527    out.line.lex = pick(
528        &left.line.lex,
529        &right.line.lex,
530        &default.line.lex,
531        "line.lex",
532        &PartialEq::eq,
533    )?;
534    out.line.chars = pick(
535        &left.line.chars,
536        &right.line.chars,
537        &default.line.chars,
538        "line.chars",
539        &PartialEq::eq,
540    )?;
541    out.line.row_chars = pick(
542        &left.line.row_chars,
543        &right.line.row_chars,
544        &default.line.row_chars,
545        "line.rowChars",
546        &PartialEq::eq,
547    )?;
548    out.line.single = pick(
549        &left.line.single,
550        &right.line.single,
551        &default.line.single,
552        "line.single",
553        &PartialEq::eq,
554    )?;
555    out.line.fixed = pick(
556        &left.line.fixed,
557        &right.line.fixed,
558        &default.line.fixed,
559        "line.fixed",
560        &PartialEq::eq,
561    )?;
562    out.line.check = pick(
563        &left.line.check,
564        &right.line.check,
565        &default.line.check,
566        "line.check",
567        &eq_option_check,
568    )?;
569
570    out.comment.lex = pick(
571        &left.comment.lex,
572        &right.comment.lex,
573        &default.comment.lex,
574        "comment.lex",
575        &PartialEq::eq,
576    )?;
577    out.comment.check = pick(
578        &left.comment.check,
579        &right.comment.check,
580        &default.comment.check,
581        "comment.check",
582        &eq_option_check,
583    )?;
584    out.comment.definitions = merge_index_map(
585        &left.comment.definitions,
586        &right.comment.definitions,
587        &default.comment.definitions,
588        "comment.def",
589        &eq_comment_def,
590    )?;
591
592    out.value.lex = pick(
593        &left.value.lex,
594        &right.value.lex,
595        &default.value.lex,
596        "value.lex",
597        &PartialEq::eq,
598    )?;
599    out.value.definitions = merge_index_map(
600        &left.value.definitions,
601        &right.value.definitions,
602        &default.value.definitions,
603        "value.def",
604        &eq_value_def,
605    )?;
606    out.ender = pick(
607        &left.ender,
608        &right.ender,
609        &default.ender,
610        "ender",
611        &PartialEq::eq,
612    )?;
613
614    out.map.extend = pick(
615        &left.map.extend,
616        &right.map.extend,
617        &default.map.extend,
618        "map.extend",
619        &PartialEq::eq,
620    )?;
621    out.map.merge = pick(
622        &left.map.merge,
623        &right.map.merge,
624        &default.map.merge,
625        "map.merge",
626        &eq_option_arc,
627    )?;
628    out.map.child = pick(
629        &left.map.child,
630        &right.map.child,
631        &default.map.child,
632        "map.child",
633        &PartialEq::eq,
634    )?;
635    out.map.ordered = pick(
636        &left.map.ordered,
637        &right.map.ordered,
638        &default.map.ordered,
639        "map.ordered",
640        &PartialEq::eq,
641    )?;
642
643    out.list.property = pick(
644        &left.list.property,
645        &right.list.property,
646        &default.list.property,
647        "list.property",
648        &PartialEq::eq,
649    )?;
650    out.list.pair = pick(
651        &left.list.pair,
652        &right.list.pair,
653        &default.list.pair,
654        "list.pair",
655        &PartialEq::eq,
656    )?;
657    out.list.child = pick(
658        &left.list.child,
659        &right.list.child,
660        &default.list.child,
661        "list.child",
662        &PartialEq::eq,
663    )?;
664
665    out.info.map = pick(
666        &left.info.map,
667        &right.info.map,
668        &default.info.map,
669        "info.map",
670        &PartialEq::eq,
671    )?;
672    out.info.list = pick(
673        &left.info.list,
674        &right.info.list,
675        &default.info.list,
676        "info.list",
677        &PartialEq::eq,
678    )?;
679    out.info.text = pick(
680        &left.info.text,
681        &right.info.text,
682        &default.info.text,
683        "info.text",
684        &PartialEq::eq,
685    )?;
686    out.info.marker = pick(
687        &left.info.marker,
688        &right.info.marker,
689        &default.info.marker,
690        "info.marker",
691        &PartialEq::eq,
692    )?;
693
694    out.lex.empty = pick(
695        &left.lex.empty,
696        &right.lex.empty,
697        &default.lex.empty,
698        "lex.empty",
699        &PartialEq::eq,
700    )?;
701    out.lex.empty_result = pick(
702        &left.lex.empty_result,
703        &right.lex.empty_result,
704        &default.lex.empty_result,
705        "lex.emptyResult",
706        &eq_value,
707    )?;
708    out.lex.relex = pick(
709        &left.lex.relex,
710        &right.lex.relex,
711        &default.lex.relex,
712        "lex.relex",
713        &PartialEq::eq,
714    )?;
715    out.lex.matchers = merge_index_map(
716        &left.lex.matchers,
717        &right.lex.matchers,
718        &default.lex.matchers,
719        "lex.match",
720        &eq_lex_matcher,
721    )?;
722    out.lex.matchers.sort_by(|name_a, left, name_b, right| {
723        left.order
724            .total_cmp(&right.order)
725            .then_with(|| name_a.cmp(name_b))
726    });
727
728    out.rewind.history = pick(
729        &left.rewind.history,
730        &right.rewind.history,
731        &default.rewind.history,
732        "rewind.history",
733        &PartialEq::eq,
734    )?;
735    out.rule.finish = pick(
736        &left.rule.finish,
737        &right.rule.finish,
738        &default.rule.finish,
739        "rule.finish",
740        &PartialEq::eq,
741    )?;
742    // Each bound as the links read it (`effective_rule_history`): two
743    // values that parse alike, `Some(0)` and `Some(1)` or one past the
744    // cap and the cap, are no conflict.
745    out.rule.history = pick(
746        &crate::options::effective_rule_history(left.rule.history),
747        &crate::options::effective_rule_history(right.rule.history),
748        &default.rule.history,
749        "rule.history",
750        &PartialEq::eq,
751    )?;
752    out.rule.maxmul = pick(
753        &left.rule.maxmul,
754        &right.rule.maxmul,
755        &default.rule.maxmul,
756        "rule.maxmul",
757        &PartialEq::eq,
758    )?;
759    out.rule.include = pick(
760        &left.rule.include,
761        &right.rule.include,
762        &default.rule.include,
763        "rule.include",
764        &PartialEq::eq,
765    )?;
766    out.rule.exclude = pick(
767        &left.rule.exclude,
768        &right.rule.exclude,
769        &default.rule.exclude,
770        "rule.exclude",
771        &PartialEq::eq,
772    )?;
773    out.rule.start = pick(
774        &left.rule.start,
775        &right.rule.start,
776        &default.rule.start,
777        "rule.start",
778        &PartialEq::eq,
779    )?;
780    out.result.fail = pick(
781        &left.result.fail,
782        &right.result.fail,
783        &default.result.fail,
784        "result.fail",
785        &|left, right| {
786            left.len() == right.len()
787                && left
788                    .iter()
789                    .zip(right)
790                    .all(|(left, right)| left.deep_equal(right))
791        },
792    )?;
793    out.plugin = merge_index_map(
794        &left.plugin,
795        &right.plugin,
796        &default.plugin,
797        "plugin",
798        &Value::deep_equal,
799    )?;
800
801    out.parse.prepare = pick(
802        &left.parse.prepare,
803        &right.parse.prepare,
804        &default.parse.prepare,
805        "parse.prepare",
806        &|left, right| eq_prepare_vec(left, right),
807    )?;
808    out.parse.named_prepare = merge_index_map(
809        &left.parse.named_prepare,
810        &right.parse.named_prepare,
811        &default.parse.named_prepare,
812        "parse.prepare",
813        &crate::ParsePrepare::same_callback,
814    )?;
815    out.parse.budget.check_every_n = pick(
816        &left.parse.budget.check_every_n,
817        &right.parse.budget.check_every_n,
818        &default.parse.budget.check_every_n,
819        "parse.budget.checkEveryN",
820        &PartialEq::eq,
821    )?;
822    out.parse.budget.on_check = pick(
823        &left.parse.budget.on_check,
824        &right.parse.budget.on_check,
825        &default.parse.budget.on_check,
826        "parse.budget.onCheck",
827        &eq_option_arc,
828    )?;
829    out.parse.recover.enabled = pick(
830        &left.parse.recover.enabled,
831        &right.parse.recover.enabled,
832        &default.parse.recover.enabled,
833        "parse.recover.enabled",
834        &PartialEq::eq,
835    )?;
836    out.parse.recover.sync_groups = pick(
837        &left.parse.recover.sync_groups,
838        &right.parse.recover.sync_groups,
839        &default.parse.recover.sync_groups,
840        "parse.recover.syncGroups",
841        &PartialEq::eq,
842    )?;
843    out.parse.recover.sync_tokens = pick(
844        &left.parse.recover.sync_tokens,
845        &right.parse.recover.sync_tokens,
846        &default.parse.recover.sync_tokens,
847        "parse.recover.syncTokens",
848        &PartialEq::eq,
849    )?;
850    out.parse.recover.pop_until_valid = pick(
851        &left.parse.recover.pop_until_valid,
852        &right.parse.recover.pop_until_valid,
853        &default.parse.recover.pop_until_valid,
854        "parse.recover.popUntilValid",
855        &PartialEq::eq,
856    )?;
857    out.parse.recover.max_skip = pick(
858        &left.parse.recover.max_skip,
859        &right.parse.recover.max_skip,
860        &default.parse.recover.max_skip,
861        "parse.recover.maxSkip",
862        &PartialEq::eq,
863    )?;
864    out.parse.recover.max_recoveries = pick(
865        &left.parse.recover.max_recoveries,
866        &right.parse.recover.max_recoveries,
867        &default.parse.recover.max_recoveries,
868        "parse.recover.maxRecoveries",
869        &PartialEq::eq,
870    )?;
871    out.parse.recover.suppress = pick(
872        &left.parse.recover.suppress,
873        &right.parse.recover.suppress,
874        &default.parse.recover.suppress,
875        "parse.recover.suppress",
876        &PartialEq::eq,
877    )?;
878    out.parser.start = pick(
879        &left.parser.start,
880        &right.parser.start,
881        &default.parser.start,
882        "parser.start",
883        &eq_option_arc,
884    )?;
885    out.parser.start_with_instance = pick(
886        &left.parser.start_with_instance,
887        &right.parser.start_with_instance,
888        &default.parser.start_with_instance,
889        "parser.start",
890        &eq_option_arc,
891    )?;
892    out.parser.start_with_context = pick(
893        &left.parser.start_with_context,
894        &right.parser.start_with_context,
895        &default.parser.start_with_context,
896        "parser.start",
897        &eq_option_arc,
898    )?;
899    out.debug.maxlen = pick(
900        &left.debug.maxlen,
901        &right.debug.maxlen,
902        &default.debug.maxlen,
903        "debug.maxlen",
904        &PartialEq::eq,
905    )?;
906    out.debug.print.config = pick(
907        &left.debug.print.config,
908        &right.debug.print.config,
909        &default.debug.print.config,
910        "debug.print.config",
911        &PartialEq::eq,
912    )?;
913    out.debug.print.source = pick(
914        &left.debug.print.source,
915        &right.debug.print.source,
916        &default.debug.print.source,
917        "debug.print.src",
918        &eq_option_arc,
919    )?;
920    out.debug.output = pick(
921        &left.debug.output,
922        &right.debug.output,
923        &default.debug.output,
924        "debug.get_console",
925        &eq_option_arc,
926    )?;
927
928    out.match_lex = pick(
929        &left.match_lex,
930        &right.match_lex,
931        &default.match_lex,
932        "match.lex",
933        &PartialEq::eq,
934    )?;
935    out.match_check = pick(
936        &left.match_check,
937        &right.match_check,
938        &default.match_check,
939        "match.check",
940        &eq_option_check,
941    )?;
942    out.match_tokens = merge_index_map(
943        &left.match_tokens,
944        &right.match_tokens,
945        &default.match_tokens,
946        "match.token",
947        &eq_match_token,
948    )?;
949    out.match_values = merge_index_map(
950        &left.match_values,
951        &right.match_values,
952        &default.match_values,
953        "match.value",
954        &eq_match_value,
955    )?;
956    out.error = merge_hash_map(
957        &left.error,
958        &right.error,
959        &default.error,
960        "error",
961        &PartialEq::eq,
962    )?;
963    out.hint = merge_hash_map(
964        &left.hint,
965        &right.hint,
966        &default.hint,
967        "hint",
968        &PartialEq::eq,
969    )?;
970
971    out.errmsg.name = pick(
972        &left.errmsg.name,
973        &right.errmsg.name,
974        &default.errmsg.name,
975        "errmsg.name",
976        &PartialEq::eq,
977    )?;
978    out.errmsg.suffix = pick(
979        &left.errmsg.suffix,
980        &right.errmsg.suffix,
981        &default.errmsg.suffix,
982        "errmsg.suffix",
983        &PartialEq::eq,
984    )?;
985    out.errmsg.link = pick(
986        &left.errmsg.link,
987        &right.errmsg.link,
988        &default.errmsg.link,
989        "errmsg.link",
990        &PartialEq::eq,
991    )?;
992    out.color.active = pick(
993        &left.color.active,
994        &right.color.active,
995        &default.color.active,
996        "color.active",
997        &PartialEq::eq,
998    )?;
999    out.color.reset = pick(
1000        &left.color.reset,
1001        &right.color.reset,
1002        &default.color.reset,
1003        "color.reset",
1004        &PartialEq::eq,
1005    )?;
1006    out.color.hi = pick(
1007        &left.color.hi,
1008        &right.color.hi,
1009        &default.color.hi,
1010        "color.hi",
1011        &PartialEq::eq,
1012    )?;
1013    out.color.lo = pick(
1014        &left.color.lo,
1015        &right.color.lo,
1016        &default.color.lo,
1017        "color.lo",
1018        &PartialEq::eq,
1019    )?;
1020    out.color.line = pick(
1021        &left.color.line,
1022        &right.color.line,
1023        &default.color.line,
1024        "color.line",
1025        &PartialEq::eq,
1026    )?;
1027    out.config_modify = merge_index_map(
1028        &left.config_modify,
1029        &right.config_modify,
1030        &default.config_modify,
1031        "config.modify",
1032        &ConfigModifier::same_callback,
1033    )?;
1034
1035    merge_token_space(left, right, &default, &mut out)?;
1036    Ok(out)
1037}
1038
1039fn token_set_names(options: &Options) -> HashMap<String, Vec<String>> {
1040    options
1041        .token_set
1042        .iter()
1043        .map(|(name, members)| {
1044            (
1045                name.clone(),
1046                members.iter().map(|tin| options.token_name(*tin)).collect(),
1047            )
1048        })
1049        .collect()
1050}
1051
1052fn merge_token_space(
1053    left: &Options,
1054    right: &Options,
1055    default: &Options,
1056    out: &mut Options,
1057) -> Result<(), MergeError> {
1058    let left_sets = token_set_names(left);
1059    let right_sets = token_set_names(right);
1060    let default_sets = token_set_names(default);
1061    let sets = merge_hash_map(
1062        &left_sets,
1063        &right_sets,
1064        &default_sets,
1065        "tokenSet",
1066        &PartialEq::eq,
1067    )?;
1068
1069    let mut plain_names = left
1070        .tokens
1071        .keys()
1072        .chain(right.tokens.keys())
1073        .cloned()
1074        .collect::<BTreeSet<_>>();
1075    for name in out.fixed.tokens.keys().chain(out.match_tokens.keys()) {
1076        plain_names.insert(name.clone());
1077    }
1078    for members in sets.values() {
1079        plain_names.extend(members.iter().cloned());
1080    }
1081
1082    let mut next_tin = crate::token::TIN_MAX;
1083    let mut tins = HashMap::new();
1084    for name in &plain_names {
1085        let tin = crate::token::name_to_tin(name).unwrap_or_else(|| {
1086            let allocated = next_tin;
1087            next_tin += 1;
1088            allocated
1089        });
1090        tins.insert(name.clone(), tin);
1091    }
1092
1093    for (name, token) in &mut out.fixed.tokens {
1094        token.tin = *tins.get(name).expect("fixed token was allocated");
1095    }
1096    for (name, token) in &mut out.match_tokens {
1097        token.tin = *tins.get(name).expect("match token was allocated");
1098    }
1099    out.tokens = plain_names
1100        .iter()
1101        .map(|name| (name.clone(), tins[name]))
1102        .collect();
1103    out.token_set = sets
1104        .into_iter()
1105        .map(|(name, members)| {
1106            let members = members.into_iter().map(|member| tins[&member]).collect();
1107            (name, members)
1108        })
1109        .collect();
1110
1111    let mut by_source: HashMap<&str, &str> = HashMap::new();
1112    for (name, token) in &out.fixed.tokens {
1113        if let Some(existing) = by_source.insert(&token.source, name) {
1114            if existing != name {
1115                return Err(MergeError(format!(
1116                    "merge: fixed tokens {existing} and {name} both claim source {:?}",
1117                    token.source
1118                )));
1119            }
1120        }
1121    }
1122    Ok(())
1123}
1124
1125#[derive(Clone, Debug, PartialEq, Eq)]
1126enum ActionIdentity {
1127    Simple(usize),
1128    Context(usize),
1129    Matched(usize),
1130    State(usize),
1131    Builtin(String),
1132    Missing(String),
1133}
1134
1135fn erased_ptr<T: ?Sized>(value: &Arc<T>) -> usize {
1136    Arc::as_ptr(value) as *const () as usize
1137}
1138
1139fn action_identity(tabnas: &Tabnas, name: &str) -> ActionIdentity {
1140    if name.contains('$') {
1141        ActionIdentity::Builtin(name.to_string())
1142    } else if let Some(action) = tabnas.actions.get(name) {
1143        ActionIdentity::Simple(erased_ptr(action))
1144    } else if let Some(action) = tabnas.context_actions.get(name) {
1145        ActionIdentity::Context(erased_ptr(action))
1146    } else if let Some(action) = tabnas.matched_actions.get(name) {
1147        ActionIdentity::Matched(erased_ptr(action))
1148    } else if let Some(action) = tabnas.state_actions.get(name) {
1149        ActionIdentity::State(erased_ptr(action))
1150    } else {
1151        ActionIdentity::Missing(name.to_string())
1152    }
1153}
1154
1155fn rename_ref(name: &str, tag: &str) -> String {
1156    if name.contains('$') {
1157        name.to_string()
1158    } else if let Some(name) = name.strip_prefix('@') {
1159        format!("@{tag}:{name}")
1160    } else {
1161        format!("{tag}:{name}")
1162    }
1163}
1164
1165#[derive(Clone)]
1166struct PortableAlt {
1167    alt: AltSpec,
1168    slots: Vec<Vec<String>>,
1169    keys: Vec<String>,
1170    /// Per slot, the sorted names the slot re-resolves from on the merged
1171    /// instance, or its key when it has none. Two slots declared alike
1172    /// resolve alike under every later token-set override; two that only
1173    /// resolve alike now may not.
1174    declared: Vec<String>,
1175    complexity: [usize; 10],
1176    group: String,
1177    tag: String,
1178    action_identity: Vec<ActionIdentity>,
1179}
1180
1181fn portable_alt(tabnas: &Tabnas, alt: &AltSpec, tag: &str) -> PortableAlt {
1182    // A slot declared by name is what its names resolve to on the source
1183    // instance now, which is how that instance's parser reads it: the spec
1184    // keeps what they resolved to when the alternate was installed, and a
1185    // token set overridden since would otherwise key, order and carry the
1186    // old members.
1187    let slots = (0..alt.s.len())
1188        .map(|slot| {
1189            crate::parser::resolved_slot(alt, slot, &tabnas.options)
1190                .unwrap_or_else(|| alt.s[slot].clone())
1191                .iter()
1192                .map(|tin| tabnas.options.token_name(*tin))
1193                .collect::<Vec<_>>()
1194        })
1195        .collect::<Vec<_>>();
1196    let keys: Vec<String> = slots
1197        .iter()
1198        .map(|slot| {
1199            let mut names = slot.clone();
1200            names.sort();
1201            names.join(" ")
1202        })
1203        .collect();
1204    let mut cloned = alt.clone();
1205    let ordered_actions = crate::rule::resolved_alt_action_order(
1206        &alt.a,
1207        &alt.action_fns,
1208        &alt.matched_action_fns,
1209        &alt.action_order,
1210    );
1211    let action_identity = ordered_actions
1212        .iter()
1213        .map(|binding| match binding {
1214            AltActionBinding::Named(name) => action_identity(tabnas, name),
1215            AltActionBinding::Context(callback) => ActionIdentity::Context(erased_ptr(callback)),
1216            AltActionBinding::Matched(callback) => ActionIdentity::Matched(erased_ptr(callback)),
1217        })
1218        .collect();
1219    cloned.a = alt.a.iter().map(|name| rename_ref(name, tag)).collect();
1220    cloned.action_configs = alt
1221        .action_configs
1222        .iter()
1223        .map(|(name, value)| (rename_ref(name, tag), value.clone()))
1224        .collect();
1225    cloned.c_ref = alt.c_ref.as_ref().map(|name| rename_ref(name, tag));
1226    cloned.action_order = ordered_actions
1227        .into_iter()
1228        .map(|binding| match binding {
1229            AltActionBinding::Named(name) => AltActionBinding::Named(rename_ref(&name, tag)),
1230            AltActionBinding::Context(callback) => AltActionBinding::Context(callback),
1231            AltActionBinding::Matched(callback) => AltActionBinding::Matched(callback),
1232        })
1233        .collect();
1234    // A slot set by hand since its names last resolved (its `s` is no
1235    // longer `s_bound`) keeps its tins: its names are dropped here, so no
1236    // rebuild on the merged instance resolves them over the edit.
1237    for (slot, names) in cloned.s_names.iter_mut().enumerate() {
1238        if alt.s.get(slot) != alt.s_bound.get(slot) {
1239            names.clear();
1240        }
1241    }
1242    let declared = keys
1243        .iter()
1244        .enumerate()
1245        .map(|(slot, key)| match cloned.s_names.get(slot) {
1246            Some(names) if !names.is_empty() => {
1247                let mut names = names.clone();
1248                names.sort();
1249                names.join(" ")
1250            }
1251            _ => key.clone(),
1252        })
1253        .collect();
1254    cloned.s.clear();
1255    PortableAlt {
1256        alt: cloned,
1257        slots,
1258        keys,
1259        declared,
1260        complexity: [
1261            usize::from(
1262                !alt.c.is_empty()
1263                    || alt.c_fn.is_some()
1264                    || alt.c_match.is_some()
1265                    || alt.c_lex.is_some()
1266                    || alt.c_lex_match.is_some(),
1267            ),
1268            usize::from(alt.e.is_some() || alt.e_match.is_some()),
1269            usize::from(alt.h.is_some() || alt.h_match.is_some()),
1270            usize::from(alt.b != 0 || alt.b_fn.is_some() || alt.b_match.is_some()),
1271            alt.n.len(),
1272            usize::from(
1273                !alt.a.is_empty()
1274                    || !alt.action_fns.is_empty()
1275                    || !alt.matched_action_fns.is_empty(),
1276            ),
1277            usize::from(!alt.u.is_empty()),
1278            usize::from(!alt.k.is_empty()),
1279            usize::from(alt.p.is_some() || alt.p_fn.is_some() || alt.p_match.is_some()),
1280            usize::from(alt.r.is_some() || alt.r_fn.is_some() || alt.r_match.is_some()),
1281        ],
1282        group: alt.g.clone(),
1283        tag: tag.to_string(),
1284        action_identity,
1285    }
1286}
1287
1288fn compare_alts(left: &PortableAlt, right: &PortableAlt) -> std::cmp::Ordering {
1289    for (left, right) in left.keys.iter().zip(&right.keys) {
1290        let ordering = left.cmp(right);
1291        if !ordering.is_eq() {
1292            return ordering;
1293        }
1294    }
1295    if left.keys.len() != right.keys.len() {
1296        return right.keys.len().cmp(&left.keys.len());
1297    }
1298    for (left, right) in left.complexity.iter().zip(right.complexity) {
1299        let ordering = right.cmp(left);
1300        if !ordering.is_eq() {
1301            return ordering;
1302        }
1303    }
1304    left.group
1305        .cmp(&right.group)
1306        .then_with(|| left.tag.cmp(&right.tag))
1307}
1308
1309fn eq_value_hash(left: &HashMap<String, Value>, right: &HashMap<String, Value>) -> bool {
1310    left.len() == right.len()
1311        && left
1312            .iter()
1313            .all(|(key, value)| right.get(key).is_some_and(|other| value.deep_equal(other)))
1314}
1315
1316fn eq_conditions(left: &[crate::Condition], right: &[crate::Condition]) -> bool {
1317    left.len() == right.len()
1318        && left.iter().zip(right).all(|(left, right)| {
1319            left.path == right.path && left.op == right.op && left.value.deep_equal(&right.value)
1320        })
1321}
1322
1323fn identical_alts(left: &PortableAlt, right: &PortableAlt) -> bool {
1324    let left_alt = &left.alt;
1325    let right_alt = &right.alt;
1326    left.keys == right.keys
1327        && left.declared == right.declared
1328        && left.group == right.group
1329        && left.action_identity == right.action_identity
1330        && eq_arc_vec(&left_alt.action_fns, &right_alt.action_fns)
1331        && eq_arc_vec(&left_alt.matched_action_fns, &right_alt.matched_action_fns)
1332        && eq_conditions(&left_alt.c, &right_alt.c)
1333        && eq_option_arc(&left_alt.c_fn, &right_alt.c_fn)
1334        && eq_option_arc(&left_alt.c_match, &right_alt.c_match)
1335        && eq_option_arc(&left_alt.c_lex, &right_alt.c_lex)
1336        && eq_option_arc(&left_alt.c_lex_match, &right_alt.c_lex_match)
1337        && eq_option_arc(&left_alt.h, &right_alt.h)
1338        && eq_option_arc(&left_alt.h_match, &right_alt.h_match)
1339        && eq_option_arc(&left_alt.e, &right_alt.e)
1340        && eq_option_arc(&left_alt.e_match, &right_alt.e_match)
1341        && left_alt.b == right_alt.b
1342        && eq_option_arc(&left_alt.b_fn, &right_alt.b_fn)
1343        && eq_option_arc(&left_alt.b_match, &right_alt.b_match)
1344        && left_alt.p == right_alt.p
1345        && eq_option_arc(&left_alt.p_fn, &right_alt.p_fn)
1346        && eq_option_arc(&left_alt.p_match, &right_alt.p_match)
1347        && left_alt.r == right_alt.r
1348        && eq_option_arc(&left_alt.r_fn, &right_alt.r_fn)
1349        && eq_option_arc(&left_alt.r_match, &right_alt.r_match)
1350        && left_alt.n == right_alt.n
1351        && eq_value_hash(&left_alt.u, &right_alt.u)
1352        && eq_value_hash(&left_alt.k, &right_alt.k)
1353        && eq_value_hash(&left_alt.action_configs, &right_alt.action_configs)
1354}
1355
1356fn interleave(left: Vec<PortableAlt>, right: Vec<PortableAlt>) -> Vec<PortableAlt> {
1357    let right = right
1358        .into_iter()
1359        .filter(|candidate| {
1360            !left
1361                .iter()
1362                .any(|existing| identical_alts(existing, candidate))
1363        })
1364        .collect::<Vec<_>>();
1365    let mut out = Vec::with_capacity(left.len() + right.len());
1366    let (mut left_i, mut right_i) = (0, 0);
1367    while left_i < left.len() && right_i < right.len() {
1368        if compare_alts(&left[left_i], &right[right_i]).is_le() {
1369            out.push(left[left_i].clone());
1370            left_i += 1;
1371        } else {
1372            out.push(right[right_i].clone());
1373            right_i += 1;
1374        }
1375    }
1376    out.extend(left[left_i..].iter().cloned());
1377    out.extend(right[right_i..].iter().cloned());
1378    out
1379}
1380
1381fn append_callbacks<T: ?Sized>(left: &[Arc<T>], right: &[Arc<T>]) -> Vec<Arc<T>> {
1382    left.iter().chain(right).cloned().collect()
1383}
1384
1385#[derive(Clone)]
1386struct MergedRule {
1387    name: String,
1388    open: Vec<PortableAlt>,
1389    close: Vec<PortableAlt>,
1390    bo: Vec<String>,
1391    ao: Vec<String>,
1392    bc: Vec<String>,
1393    ac: Vec<String>,
1394    bo_fns: Vec<ContextAction>,
1395    ao_fns: Vec<ContextAction>,
1396    bc_fns: Vec<ContextAction>,
1397    ac_fns: Vec<ContextAction>,
1398    bo_state_fns: Vec<crate::StateAction>,
1399    ao_state_fns: Vec<crate::StateAction>,
1400    bc_state_fns: Vec<crate::StateAction>,
1401    ac_state_fns: Vec<crate::StateAction>,
1402    bo_order: Vec<ActionBinding>,
1403    ao_order: Vec<ActionBinding>,
1404    bc_order: Vec<ActionBinding>,
1405    ac_order: Vec<ActionBinding>,
1406}
1407
1408impl MergedRule {
1409    fn materialize(&self, options: &mut Options) -> RuleSpec {
1410        fn alts(alts: &[PortableAlt], options: &mut Options) -> Vec<AltSpec> {
1411            alts.iter()
1412                .map(|portable| {
1413                    let mut alt = portable.alt.clone();
1414                    alt.s = portable
1415                        .slots
1416                        .iter()
1417                        .map(|slot| {
1418                            slot.iter()
1419                                .map(|name| options.register_token(name))
1420                                .collect()
1421                        })
1422                        .collect();
1423                    // The tins are this instance's now, so what the names
1424                    // are compared with has to be too: left at the
1425                    // source's tins, every slot would read as set by hand
1426                    // and a set overridden here would never reach it.
1427                    alt.s_bound = alt.s.clone();
1428                    alt
1429                })
1430                .collect()
1431        }
1432        RuleSpec {
1433            name: self.name.clone(),
1434            open: alts(&self.open, options),
1435            close: alts(&self.close, options),
1436            bo: self.bo.clone(),
1437            ao: self.ao.clone(),
1438            bc: self.bc.clone(),
1439            ac: self.ac.clone(),
1440            bo_fns: self.bo_fns.clone(),
1441            ao_fns: self.ao_fns.clone(),
1442            bc_fns: self.bc_fns.clone(),
1443            ac_fns: self.ac_fns.clone(),
1444            bo_state_fns: self.bo_state_fns.clone(),
1445            ao_state_fns: self.ao_state_fns.clone(),
1446            bc_state_fns: self.bc_state_fns.clone(),
1447            ac_state_fns: self.ac_state_fns.clone(),
1448            bo_order: self.bo_order.clone(),
1449            ao_order: self.ao_order.clone(),
1450            bc_order: self.bc_order.clone(),
1451            ac_order: self.ac_order.clone(),
1452        }
1453    }
1454}
1455
1456struct PhaseSource<'a> {
1457    tabnas: &'a Tabnas,
1458    named: &'a [String],
1459    callbacks: &'a [ContextAction],
1460    states: &'a [crate::StateAction],
1461    order: &'a [ActionBinding],
1462    tag: &'a str,
1463}
1464
1465fn merge_phase(
1466    left: PhaseSource<'_>,
1467    right: PhaseSource<'_>,
1468) -> (
1469    Vec<String>,
1470    Vec<ContextAction>,
1471    Vec<crate::StateAction>,
1472    Vec<ActionBinding>,
1473) {
1474    fn portable(
1475        tabnas: &Tabnas,
1476        named: &[String],
1477        callbacks: &[ContextAction],
1478        states: &[crate::StateAction],
1479        order: &[ActionBinding],
1480        tag: &str,
1481    ) -> Vec<(ActionBinding, ActionIdentity)> {
1482        crate::rule::resolved_action_order(named, callbacks, states, order)
1483            .into_iter()
1484            .map(|binding| match binding {
1485                ActionBinding::Named(name) => (
1486                    ActionBinding::Named(rename_ref(&name, tag)),
1487                    action_identity(tabnas, &name),
1488                ),
1489                ActionBinding::Callback(callback) => {
1490                    let identity = ActionIdentity::Context(erased_ptr(&callback));
1491                    (ActionBinding::Callback(callback), identity)
1492                }
1493                ActionBinding::State(callback) => {
1494                    let identity = ActionIdentity::State(erased_ptr(&callback));
1495                    (ActionBinding::State(callback), identity)
1496                }
1497            })
1498            .collect()
1499    }
1500
1501    let mut order = portable(
1502        left.tabnas,
1503        left.named,
1504        left.callbacks,
1505        left.states,
1506        left.order,
1507        left.tag,
1508    );
1509    for candidate in portable(
1510        right.tabnas,
1511        right.named,
1512        right.callbacks,
1513        right.states,
1514        right.order,
1515        right.tag,
1516    ) {
1517        if !order.iter().any(|(_, identity)| *identity == candidate.1) {
1518            order.push(candidate);
1519        }
1520    }
1521    let order = order
1522        .into_iter()
1523        .map(|(binding, _)| binding)
1524        .collect::<Vec<_>>();
1525    let named = order
1526        .iter()
1527        .filter_map(|binding| match binding {
1528            ActionBinding::Named(name) => Some(name.clone()),
1529            ActionBinding::Callback(_) => None,
1530            ActionBinding::State(_) => None,
1531        })
1532        .collect();
1533    let callbacks = order
1534        .iter()
1535        .filter_map(|binding| match binding {
1536            ActionBinding::Named(_) => None,
1537            ActionBinding::Callback(callback) => Some(callback.clone()),
1538            ActionBinding::State(_) => None,
1539        })
1540        .collect();
1541    let states = order
1542        .iter()
1543        .filter_map(|binding| match binding {
1544            ActionBinding::State(callback) => Some(callback.clone()),
1545            ActionBinding::Named(_) | ActionBinding::Callback(_) => None,
1546        })
1547        .collect();
1548    (named, callbacks, states, order)
1549}
1550
1551fn merged_rules(left: &Tabnas, left_tag: &str, right: &Tabnas, right_tag: &str) -> Vec<MergedRule> {
1552    let names = left
1553        .rules
1554        .keys()
1555        .chain(right.rules.keys())
1556        .cloned()
1557        .collect::<BTreeSet<_>>();
1558    names
1559        .into_iter()
1560        .map(|name| {
1561            let left_rule = left.rules.get(&name);
1562            let right_rule = right.rules.get(&name);
1563            let empty_named = Vec::new();
1564            let empty_callbacks = Vec::new();
1565            let empty_states = Vec::new();
1566            let empty_order = Vec::new();
1567            let left_open = left_rule
1568                .map(|rule| {
1569                    rule.open
1570                        .iter()
1571                        .map(|alt| portable_alt(left, alt, left_tag))
1572                        .collect()
1573                })
1574                .unwrap_or_default();
1575            let right_open = right_rule
1576                .map(|rule| {
1577                    rule.open
1578                        .iter()
1579                        .map(|alt| portable_alt(right, alt, right_tag))
1580                        .collect()
1581                })
1582                .unwrap_or_default();
1583            let left_close = left_rule
1584                .map(|rule| {
1585                    rule.close
1586                        .iter()
1587                        .map(|alt| portable_alt(left, alt, left_tag))
1588                        .collect()
1589                })
1590                .unwrap_or_default();
1591            let right_close = right_rule
1592                .map(|rule| {
1593                    rule.close
1594                        .iter()
1595                        .map(|alt| portable_alt(right, alt, right_tag))
1596                        .collect()
1597                })
1598                .unwrap_or_default();
1599            let phase =
1600                |select_named: fn(&RuleSpec) -> &Vec<String>,
1601                 select_callbacks: fn(&RuleSpec) -> &Vec<ContextAction>,
1602                 select_states: fn(&RuleSpec) -> &Vec<crate::StateAction>,
1603                 select_order: fn(&RuleSpec) -> &Vec<ActionBinding>| {
1604                    merge_phase(
1605                        PhaseSource {
1606                            tabnas: left,
1607                            named: left_rule.map(select_named).unwrap_or(&empty_named),
1608                            callbacks: left_rule.map(select_callbacks).unwrap_or(&empty_callbacks),
1609                            states: left_rule.map(select_states).unwrap_or(&empty_states),
1610                            order: left_rule.map(select_order).unwrap_or(&empty_order),
1611                            tag: left_tag,
1612                        },
1613                        PhaseSource {
1614                            tabnas: right,
1615                            named: right_rule.map(select_named).unwrap_or(&empty_named),
1616                            callbacks: right_rule.map(select_callbacks).unwrap_or(&empty_callbacks),
1617                            states: right_rule.map(select_states).unwrap_or(&empty_states),
1618                            order: right_rule.map(select_order).unwrap_or(&empty_order),
1619                            tag: right_tag,
1620                        },
1621                    )
1622                };
1623            let (bo, bo_fns, bo_state_fns, bo_order) = phase(
1624                |rule| &rule.bo,
1625                |rule| &rule.bo_fns,
1626                |rule| &rule.bo_state_fns,
1627                |rule| &rule.bo_order,
1628            );
1629            let (ao, ao_fns, ao_state_fns, ao_order) = phase(
1630                |rule| &rule.ao,
1631                |rule| &rule.ao_fns,
1632                |rule| &rule.ao_state_fns,
1633                |rule| &rule.ao_order,
1634            );
1635            let (bc, bc_fns, bc_state_fns, bc_order) = phase(
1636                |rule| &rule.bc,
1637                |rule| &rule.bc_fns,
1638                |rule| &rule.bc_state_fns,
1639                |rule| &rule.bc_order,
1640            );
1641            let (ac, ac_fns, ac_state_fns, ac_order) = phase(
1642                |rule| &rule.ac,
1643                |rule| &rule.ac_fns,
1644                |rule| &rule.ac_state_fns,
1645                |rule| &rule.ac_order,
1646            );
1647            MergedRule {
1648                name,
1649                open: interleave(left_open, right_open),
1650                close: interleave(left_close, right_close),
1651                bo,
1652                ao,
1653                bc,
1654                ac,
1655                bo_fns,
1656                ao_fns,
1657                bc_fns,
1658                ac_fns,
1659                bo_state_fns,
1660                ao_state_fns,
1661                bc_state_fns,
1662                ac_state_fns,
1663                bo_order,
1664                ao_order,
1665                bc_order,
1666                ac_order,
1667            }
1668        })
1669        .collect()
1670}
1671
1672fn prefix_hash_map<T: Clone>(source: &HashMap<String, T>, tag: &str) -> HashMap<String, T> {
1673    source
1674        .iter()
1675        .map(|(name, value)| (rename_ref(name, tag), value.clone()))
1676        .collect()
1677}
1678
1679/// Both sides' guards, each under its side's tag, so that two grammars
1680/// that name a guard alike each keep theirs: a merged parser holds to the
1681/// bounds of both. Every name is prefixed, whatever it contains: a guard
1682/// name is an identity, not a reference, so `rename_ref`'s exemption for
1683/// builtin action names (anything with a `$`) must not apply, or two such
1684/// names would collide and one side's bound would be dropped.
1685fn combine_guards(
1686    left: &IndexMap<String, crate::ParseGuard>,
1687    left_tag: &str,
1688    right: &IndexMap<String, crate::ParseGuard>,
1689    right_tag: &str,
1690) -> IndexMap<String, crate::ParseGuard> {
1691    let side = |guards: &IndexMap<String, crate::ParseGuard>, tag: &str| {
1692        guards
1693            .iter()
1694            .map(|(name, guard)| (format!("{tag}:{name}"), guard.clone()))
1695            .collect::<Vec<_>>()
1696    };
1697    side(left, left_tag)
1698        .into_iter()
1699        .chain(side(right, right_tag))
1700        .collect()
1701}
1702
1703fn combine_prefixed<T: Clone>(
1704    left: &HashMap<String, T>,
1705    left_tag: &str,
1706    right: &HashMap<String, T>,
1707    right_tag: &str,
1708) -> HashMap<String, T> {
1709    let mut out = prefix_hash_map(left, left_tag);
1710    out.extend(prefix_hash_map(right, right_tag));
1711    out
1712}
1713
1714#[derive(Clone)]
1715struct MergedInstall {
1716    rules: Vec<MergedRule>,
1717    actions: HashMap<String, Action>,
1718    context_actions: HashMap<String, ContextAction>,
1719    matched_actions: HashMap<String, crate::AltAction>,
1720    state_actions: HashMap<String, crate::StateAction>,
1721    token_subscribers: Vec<TokenSubscriber>,
1722    lex_subscribers: Vec<LexSubscriber>,
1723    rule_subscribers: Vec<RuleSubscriber>,
1724    rule_done_subscribers: Vec<RuleDoneSubscriber>,
1725    parse_guards: IndexMap<String, crate::ParseGuard>,
1726    alt_conditions: HashMap<String, crate::AltCondition>,
1727    alt_match_conditions: HashMap<String, crate::AltConditionWithMatch>,
1728    alt_lexer_conditions: HashMap<String, crate::AltConditionWithLexer>,
1729    alt_lexer_match_conditions: HashMap<String, crate::AltConditionWithLexerAndMatch>,
1730    alt_modifiers: HashMap<String, crate::AltModifier>,
1731    alt_match_modifiers: HashMap<String, crate::AltModifierWithMatch>,
1732    alt_errors: HashMap<String, crate::AltError>,
1733    alt_match_errors: HashMap<String, crate::AltErrorWithMatch>,
1734    alt_pushes: HashMap<String, crate::AltNext>,
1735    alt_match_pushes: HashMap<String, crate::AltNextWithMatch>,
1736    alt_replaces: HashMap<String, crate::AltNext>,
1737    alt_match_replaces: HashMap<String, crate::AltNextWithMatch>,
1738    alt_backtracks: HashMap<String, crate::AltBack>,
1739    alt_match_backtracks: HashMap<String, crate::AltBackWithMatch>,
1740    match_token_refs: HashMap<String, (crate::MatchTokenCallback, bool)>,
1741    value_transform_refs: HashMap<String, crate::ValueTransform>,
1742    text_modifier_refs: HashMap<String, crate::TextModifier>,
1743    lex_check_refs: HashMap<String, crate::LexCheck>,
1744    comment_suffix_refs: HashMap<String, crate::CommentSuffixMatcher>,
1745    match_value_refs: HashMap<String, crate::MatchTokenCallback>,
1746    parse_prepare_refs: HashMap<String, crate::ParsePrepare>,
1747    budget_check_refs: HashMap<String, crate::BudgetCheck>,
1748    lex_match_refs: HashMap<String, crate::LexMatcherCallback>,
1749    imperative_lex_match_refs: HashMap<String, crate::ImperativeLexMatcher>,
1750    lex_match_factory_refs: HashMap<String, crate::LexMatcherFactory>,
1751    error_suffix_refs: HashMap<String, crate::ErrorSuffixCallback>,
1752    config_modifier_refs: HashMap<String, crate::ConfigModifier>,
1753    parser_start_refs: HashMap<String, crate::ParserStart>,
1754    parser_start_instance_refs: HashMap<String, crate::ParserStartWithInstance>,
1755    parser_start_context_refs: HashMap<String, crate::ParserStartWithContext>,
1756    map_merge_refs: HashMap<String, crate::MapMerge>,
1757}
1758
1759impl MergedInstall {
1760    fn install(&self, tabnas: &mut Tabnas) {
1761        tabnas.rules = self
1762            .rules
1763            .iter()
1764            .map(|rule| {
1765                let rule = rule.materialize(&mut tabnas.options);
1766                (rule.name.clone(), rule)
1767            })
1768            .collect::<indexmap::IndexMap<_, _>>()
1769            .into();
1770        tabnas.actions = self.actions.clone().into();
1771        tabnas.context_actions = (self.context_actions.clone()).into();
1772        tabnas.matched_actions = (self.matched_actions.clone()).into();
1773        tabnas.state_actions = (self.state_actions.clone()).into();
1774        tabnas.token_subscribers = (self.token_subscribers.clone()).into();
1775        tabnas.lex_subscribers = (self.lex_subscribers.clone()).into();
1776        tabnas.rule_subscribers = (self.rule_subscribers.clone()).into();
1777        tabnas.rule_done_subscribers = (self.rule_done_subscribers.clone()).into();
1778        // Added, not assigned: `derive` re-runs this install on a copy of
1779        // the merged parent's guards, and a guard installed on that parent
1780        // after the merge has to survive it.
1781        for (name, guard) in &self.parse_guards {
1782            tabnas.parse_guards.insert(name.clone(), guard.clone());
1783        }
1784        tabnas.alt_conditions = self.alt_conditions.clone();
1785        tabnas.alt_match_conditions = self.alt_match_conditions.clone();
1786        tabnas.alt_lexer_conditions = self.alt_lexer_conditions.clone();
1787        tabnas.alt_lexer_match_conditions = self.alt_lexer_match_conditions.clone();
1788        tabnas.alt_modifiers = self.alt_modifiers.clone();
1789        tabnas.alt_match_modifiers = self.alt_match_modifiers.clone();
1790        tabnas.alt_errors = self.alt_errors.clone();
1791        tabnas.alt_match_errors = self.alt_match_errors.clone();
1792        tabnas.alt_pushes = self.alt_pushes.clone();
1793        tabnas.alt_match_pushes = self.alt_match_pushes.clone();
1794        tabnas.alt_replaces = self.alt_replaces.clone();
1795        tabnas.alt_match_replaces = self.alt_match_replaces.clone();
1796        tabnas.alt_backtracks = self.alt_backtracks.clone();
1797        tabnas.alt_match_backtracks = self.alt_match_backtracks.clone();
1798        tabnas.match_token_refs = self.match_token_refs.clone();
1799        tabnas.value_transform_refs = self.value_transform_refs.clone();
1800        tabnas.text_modifier_refs = self.text_modifier_refs.clone();
1801        tabnas.lex_check_refs = self.lex_check_refs.clone();
1802        tabnas.comment_suffix_refs = self.comment_suffix_refs.clone();
1803        tabnas.match_value_refs = self.match_value_refs.clone();
1804        tabnas.parse_prepare_refs = self.parse_prepare_refs.clone();
1805        tabnas.budget_check_refs = self.budget_check_refs.clone();
1806        tabnas.lex_match_refs = self.lex_match_refs.clone();
1807        tabnas.imperative_lex_match_refs = self.imperative_lex_match_refs.clone();
1808        tabnas.lex_match_factory_refs = self.lex_match_factory_refs.clone();
1809        tabnas.error_suffix_refs = self.error_suffix_refs.clone();
1810        tabnas.config_modifier_refs = self.config_modifier_refs.clone();
1811        tabnas.parser_start_refs = self.parser_start_refs.clone();
1812        tabnas.parser_start_instance_refs = self.parser_start_instance_refs.clone();
1813        tabnas.parser_start_context_refs = self.parser_start_context_refs.clone();
1814        tabnas.map_merge_refs = self.map_merge_refs.clone();
1815    }
1816}
1817
1818fn merge_plugin_value(left: &Value, right: &Value, path: &str) -> Result<Value, MergeError> {
1819    match (left, right) {
1820        (Value::Undefined, value) | (value, Value::Undefined) => Ok(value.clone()),
1821        (Value::Object(left), Value::Object(right)) => {
1822            let mut out = IndexMap::new();
1823            for key in ordered_keys(left, right) {
1824                let value = match (left.get(&key), right.get(&key)) {
1825                    (Some(left), Some(right)) => {
1826                        merge_plugin_value(left, right, &format!("{path}.{key}"))?
1827                    }
1828                    (Some(value), None) | (None, Some(value)) => value.clone(),
1829                    (None, None) => unreachable!(),
1830                };
1831                out.insert(key, value);
1832            }
1833            Ok(Value::object(out))
1834        }
1835        _ if left.deep_equal(right) => Ok(left.clone()),
1836        _ => Err(conflict(path)),
1837    }
1838}
1839
1840fn merge_decorations(
1841    left: &IndexMap<String, crate::Decoration>,
1842    right: &IndexMap<String, crate::Decoration>,
1843) -> Result<IndexMap<String, crate::Decoration>, MergeError> {
1844    let mut out = left.clone();
1845    for (name, value) in right {
1846        if let Some(previous) = out.get(name) {
1847            if !previous.equivalent(value) {
1848                return Err(conflict(&format!("decoration.{name}")));
1849            }
1850        } else {
1851            out.insert(name.clone(), value.clone());
1852        }
1853    }
1854    Ok(out)
1855}
1856
1857pub(crate) fn merge(left: &Tabnas, right: &Tabnas) -> Result<Tabnas, MergeError> {
1858    fn tag<'a>(tabnas: &'a Tabnas, which: &str) -> Result<&'a str, MergeError> {
1859        let tag = tabnas.options.tag.as_str();
1860        if tag.is_empty() || tag == "-" {
1861            Err(MergeError(format!(
1862                "merge: the {which} instance needs a tag option (used to prefix its named actions)"
1863            )))
1864        } else {
1865            Ok(tag)
1866        }
1867    }
1868
1869    let left_tag = tag(left, "first")?;
1870    let right_tag = tag(right, "second")?;
1871    if left_tag == right_tag {
1872        return Err(MergeError(format!(
1873            "merge: instance tags must differ, both are {left_tag:?}"
1874        )));
1875    }
1876    let (left, left_tag, right, right_tag) = if left_tag < right_tag {
1877        (left, left_tag, right, right_tag)
1878    } else {
1879        (right, right_tag, left, left_tag)
1880    };
1881
1882    let mut options = merge_options(&left.options, &right.options)?;
1883    options.tag = format!("{left_tag}~{right_tag}");
1884    let plugin_options = merge_plugin_value(
1885        &Value::object(left.plugin_options.clone()),
1886        &Value::object(right.plugin_options.clone()),
1887        "plugin",
1888    )?;
1889    let Value::Object(plugin_options) = plugin_options else {
1890        unreachable!()
1891    };
1892    let decorations = merge_decorations(&left.decorations, &right.decorations)?;
1893
1894    let install = MergedInstall {
1895        rules: merged_rules(left, left_tag, right, right_tag),
1896        actions: combine_prefixed(&left.actions, left_tag, &right.actions, right_tag),
1897        context_actions: combine_prefixed(
1898            &left.context_actions,
1899            left_tag,
1900            &right.context_actions,
1901            right_tag,
1902        ),
1903        matched_actions: combine_prefixed(
1904            &left.matched_actions,
1905            left_tag,
1906            &right.matched_actions,
1907            right_tag,
1908        ),
1909        state_actions: combine_prefixed(
1910            &left.state_actions,
1911            left_tag,
1912            &right.state_actions,
1913            right_tag,
1914        ),
1915        token_subscribers: append_callbacks(&left.token_subscribers, &right.token_subscribers),
1916        lex_subscribers: append_callbacks(&left.lex_subscribers, &right.lex_subscribers),
1917        rule_subscribers: append_callbacks(&left.rule_subscribers, &right.rule_subscribers),
1918        rule_done_subscribers: append_callbacks(
1919            &left.rule_done_subscribers,
1920            &right.rule_done_subscribers,
1921        ),
1922        parse_guards: combine_guards(&left.parse_guards, left_tag, &right.parse_guards, right_tag),
1923        alt_conditions: combine_prefixed(
1924            &left.alt_conditions,
1925            left_tag,
1926            &right.alt_conditions,
1927            right_tag,
1928        ),
1929        alt_match_conditions: combine_prefixed(
1930            &left.alt_match_conditions,
1931            left_tag,
1932            &right.alt_match_conditions,
1933            right_tag,
1934        ),
1935        alt_lexer_conditions: combine_prefixed(
1936            &left.alt_lexer_conditions,
1937            left_tag,
1938            &right.alt_lexer_conditions,
1939            right_tag,
1940        ),
1941        alt_lexer_match_conditions: combine_prefixed(
1942            &left.alt_lexer_match_conditions,
1943            left_tag,
1944            &right.alt_lexer_match_conditions,
1945            right_tag,
1946        ),
1947        alt_modifiers: combine_prefixed(
1948            &left.alt_modifiers,
1949            left_tag,
1950            &right.alt_modifiers,
1951            right_tag,
1952        ),
1953        alt_match_modifiers: combine_prefixed(
1954            &left.alt_match_modifiers,
1955            left_tag,
1956            &right.alt_match_modifiers,
1957            right_tag,
1958        ),
1959        alt_errors: combine_prefixed(&left.alt_errors, left_tag, &right.alt_errors, right_tag),
1960        alt_match_errors: combine_prefixed(
1961            &left.alt_match_errors,
1962            left_tag,
1963            &right.alt_match_errors,
1964            right_tag,
1965        ),
1966        alt_pushes: combine_prefixed(&left.alt_pushes, left_tag, &right.alt_pushes, right_tag),
1967        alt_match_pushes: combine_prefixed(
1968            &left.alt_match_pushes,
1969            left_tag,
1970            &right.alt_match_pushes,
1971            right_tag,
1972        ),
1973        alt_replaces: combine_prefixed(
1974            &left.alt_replaces,
1975            left_tag,
1976            &right.alt_replaces,
1977            right_tag,
1978        ),
1979        alt_match_replaces: combine_prefixed(
1980            &left.alt_match_replaces,
1981            left_tag,
1982            &right.alt_match_replaces,
1983            right_tag,
1984        ),
1985        alt_backtracks: combine_prefixed(
1986            &left.alt_backtracks,
1987            left_tag,
1988            &right.alt_backtracks,
1989            right_tag,
1990        ),
1991        alt_match_backtracks: combine_prefixed(
1992            &left.alt_match_backtracks,
1993            left_tag,
1994            &right.alt_match_backtracks,
1995            right_tag,
1996        ),
1997        match_token_refs: combine_prefixed(
1998            &left.match_token_refs,
1999            left_tag,
2000            &right.match_token_refs,
2001            right_tag,
2002        ),
2003        value_transform_refs: combine_prefixed(
2004            &left.value_transform_refs,
2005            left_tag,
2006            &right.value_transform_refs,
2007            right_tag,
2008        ),
2009        text_modifier_refs: combine_prefixed(
2010            &left.text_modifier_refs,
2011            left_tag,
2012            &right.text_modifier_refs,
2013            right_tag,
2014        ),
2015        lex_check_refs: combine_prefixed(
2016            &left.lex_check_refs,
2017            left_tag,
2018            &right.lex_check_refs,
2019            right_tag,
2020        ),
2021        comment_suffix_refs: combine_prefixed(
2022            &left.comment_suffix_refs,
2023            left_tag,
2024            &right.comment_suffix_refs,
2025            right_tag,
2026        ),
2027        match_value_refs: combine_prefixed(
2028            &left.match_value_refs,
2029            left_tag,
2030            &right.match_value_refs,
2031            right_tag,
2032        ),
2033        parse_prepare_refs: combine_prefixed(
2034            &left.parse_prepare_refs,
2035            left_tag,
2036            &right.parse_prepare_refs,
2037            right_tag,
2038        ),
2039        budget_check_refs: combine_prefixed(
2040            &left.budget_check_refs,
2041            left_tag,
2042            &right.budget_check_refs,
2043            right_tag,
2044        ),
2045        lex_match_refs: combine_prefixed(
2046            &left.lex_match_refs,
2047            left_tag,
2048            &right.lex_match_refs,
2049            right_tag,
2050        ),
2051        imperative_lex_match_refs: combine_prefixed(
2052            &left.imperative_lex_match_refs,
2053            left_tag,
2054            &right.imperative_lex_match_refs,
2055            right_tag,
2056        ),
2057        lex_match_factory_refs: combine_prefixed(
2058            &left.lex_match_factory_refs,
2059            left_tag,
2060            &right.lex_match_factory_refs,
2061            right_tag,
2062        ),
2063        error_suffix_refs: combine_prefixed(
2064            &left.error_suffix_refs,
2065            left_tag,
2066            &right.error_suffix_refs,
2067            right_tag,
2068        ),
2069        config_modifier_refs: combine_prefixed(
2070            &left.config_modifier_refs,
2071            left_tag,
2072            &right.config_modifier_refs,
2073            right_tag,
2074        ),
2075        parser_start_refs: combine_prefixed(
2076            &left.parser_start_refs,
2077            left_tag,
2078            &right.parser_start_refs,
2079            right_tag,
2080        ),
2081        parser_start_instance_refs: combine_prefixed(
2082            &left.parser_start_instance_refs,
2083            left_tag,
2084            &right.parser_start_instance_refs,
2085            right_tag,
2086        ),
2087        parser_start_context_refs: combine_prefixed(
2088            &left.parser_start_context_refs,
2089            left_tag,
2090            &right.parser_start_context_refs,
2091            right_tag,
2092        ),
2093        map_merge_refs: combine_prefixed(
2094            &left.map_merge_refs,
2095            left_tag,
2096            &right.map_merge_refs,
2097            right_tag,
2098        ),
2099    };
2100
2101    let mut out = Tabnas::with_options(options);
2102    out.plugin_options = crate::value::unwrap_arc(plugin_options);
2103    out.decorations = decorations;
2104    let install_callback = install.clone();
2105    out.use_plugin(
2106        Plugin::new("merged", move |tabnas, _| {
2107            install_callback.install(tabnas);
2108            Ok(())
2109        }),
2110        None,
2111    )
2112    .map_err(|PluginError(message)| MergeError(format!("merge: {message}")))?;
2113    Ok(out)
2114}