1use crate::document::{Document, StageItem, Sub, SubTo};
25use crate::error::{Error, Result};
26use crate::item::Item;
27use crate::stdlib::Prim;
28use std::cell::RefCell;
29use std::collections::BTreeMap;
30use std::path::PathBuf;
31use std::rc::Rc;
32
33pub struct Runtime {
36 load_path: Vec<PathBuf>,
37 cache: RefCell<BTreeMap<String, Rc<Document>>>,
38 loading: RefCell<Vec<String>>,
40}
41
42impl Runtime {
43 pub fn new(load_path: Vec<PathBuf>) -> Runtime {
44 Runtime {
45 load_path,
46 cache: RefCell::new(BTreeMap::new()),
47 loading: RefCell::new(Vec::new()),
48 }
49 }
50
51 pub fn load(&self, map_id: &str) -> Result<Rc<Document>> {
53 if let Some(doc) = self.cache.borrow().get(map_id) {
54 return Ok(doc.clone());
55 }
56 {
57 let loading = self.loading.borrow();
58 if loading.iter().any(|m| m == map_id) {
59 let mut chain = loading.clone();
60 chain.push(map_id.to_string());
61 return Err(Error::DependencyCycle(chain));
62 }
63 }
64 self.loading.borrow_mut().push(map_id.to_string());
65 let result = self.load_uncached(map_id);
66 self.loading.borrow_mut().pop();
67 result
68 }
69
70 fn load_uncached(&self, map_id: &str) -> Result<Rc<Document>> {
71 let path = crate::locate::locate(&self.load_path, map_id)?;
72 let source = crate::locate::read_map(&path)?;
73 let stem = path
74 .file_stem()
75 .and_then(|s| s.to_str())
76 .unwrap_or(map_id)
77 .to_string();
78 let tree = crate::grammar::parse_isc(&source).map_err(|(offset, expected)| Error::Parse {
79 map: stem.clone(),
80 offset,
81 expected,
82 })?;
83
84 let targets = crate::builder::dependency_targets(&tree);
86 let mut imports: Vec<Rc<Document>> = Vec::new();
87 let mut dep_docs: BTreeMap<String, Rc<Document>> = BTreeMap::new();
88 for (target, alias) in targets {
89 let dep = self.load(&target)?;
90 match alias {
91 Some(name) => {
92 dep_docs.insert(name, dep);
93 }
94 None => imports.push(dep),
95 }
96 }
97
98 let doc = Rc::new(crate::builder::build(&tree, &stem, &imports, &dep_docs)?);
99 self.cache
100 .borrow_mut()
101 .insert(map_id.to_string(), doc.clone());
102 Ok(doc)
103 }
104
105 pub fn transliterate(&self, map_id: &str, input: &str) -> Result<String> {
107 self.transliterate_stage(map_id, "main", input)
108 }
109
110 pub fn transliterate_stage(&self, map_id: &str, stage: &str, input: &str) -> Result<String> {
111 let doc = self.load(map_id)?;
112 execute_stage(&doc, stage, input)
113 }
114
115 pub fn run_tests(&self, map_id: &str) -> Result<(usize, usize, Vec<String>)> {
117 let doc = self.load(map_id)?;
118 let mut passed = 0usize;
119 let mut failed = 0usize;
120 let mut failures = Vec::new();
121 for (input, expected) in &doc.tests.data {
122 match self.transliterate(map_id, input) {
123 Ok(actual) if &actual == expected => passed += 1,
124 Ok(actual) => {
125 failed += 1;
126 failures.push(format!("{input:?} -> {actual:?} (expected {expected:?})"));
127 }
128 Err(e) => {
129 failed += 1;
130 failures.push(format!("{input:?} -> ERROR {e}"));
131 }
132 }
133 }
134 Ok((passed, failed, failures))
135 }
136}
137
138pub fn execute_stage_pub(doc: &Document, stage_name: &str, input: &str) -> Result<String> {
144 execute_stage(doc, stage_name, input)
145}
146
147fn execute_stage(doc: &Document, stage_name: &str, input: &str) -> Result<String> {
148 let stage = doc
149 .imported_stages()
150 .remove(stage_name)
151 .ok_or_else(|| Error::MapLogic(format!("stage {stage_name:?} not found in {}", doc.name)))?;
152 if stage.reverse_run == Some(true) {
155 return Ok(input.to_string());
156 }
157 let mut s = input.to_string();
158 for item in &stage.items {
159 let skipped = match item {
161 StageItem::Funcall(f) => f.reverse_run == Some(true),
162 StageItem::Run { reverse_run, .. } => *reverse_run == Some(true),
163 _ => false,
164 };
165 if skipped {
166 continue;
167 }
168 s = match item {
169 StageItem::Parallel(rules) => apply_parallel(doc, rules, &s)?,
170 StageItem::Sequential(rules) => {
171 let mut out = s;
172 for rule in rules {
173 if rule.reverse_run == Some(true) {
174 continue;
175 }
176 out = apply_sub(doc, rule, &out)?;
177 }
178 out
179 }
180 StageItem::Run { dep, stage, .. } => match dep {
181 Some(alias) => {
182 let dep_doc = doc.dep_aliases.get(alias).ok_or_else(|| {
183 Error::MapLogic(format!("dep alias {alias:?} not found in {}", doc.name))
184 })?;
185 execute_stage(dep_doc, stage, &s)?
186 }
187 None => execute_stage(doc, stage, &s)?,
188 },
189 StageItem::Funcall(funcall) => funcall.name.apply(s, &funcall.kwargs)?,
190 };
191 }
192 Ok(s)
193}
194
195fn apply_parallel(doc: &Document, rules: &[Sub], s: &str) -> Result<String> {
205 let live: Vec<&Sub> = rules
208 .iter()
209 .filter(|r| r.reverse_run != Some(true))
210 .collect();
211 if let Some(tree) = build_tree(&live, doc)? {
212 return tree_replace(&tree, s);
213 }
214
215 let chars: Vec<char> = s.chars().collect();
216 let mut order: Vec<usize> = (0..live.len()).collect();
218 order.sort_by_key(|&i| (std::cmp::Reverse(live[i].max_length()), i));
219
220 let mut out = String::with_capacity(s.len());
221 let mut copied = 0usize; let mut pos = 0usize;
223 while pos < chars.len() {
224 let mut matched: Option<(usize, String)> = None;
225 for &i in &order {
226 let rule = live[i];
227 let m = Matcher::new(&chars, doc);
228 if let Some((end, replacement)) = m.match_rule(rule, pos)? {
229 matched = Some((end, replacement));
230 break;
231 }
232 }
233 match matched {
234 Some((end, replacement)) => {
235 out.extend(&chars[copied..pos]);
236 out.push_str(&replacement);
237 copied = end;
238 pos = std::cmp::max(end, pos + 1);
241 }
242 None => pos += 1,
243 }
244 }
245 out.extend(&chars[copied..]);
246 Ok(out)
247}
248
249type Trie = std::collections::BTreeMap<char, TrieNode>;
251#[derive(Default)]
252struct TrieNode {
253 children: Trie,
254 replacement: Option<String>,
255}
256
257fn build_tree(rules: &[&Sub], doc: &Document) -> Result<Option<TrieNode>> {
263 let m = Matcher::new(&[], doc);
264 let mut map: std::collections::BTreeMap<String, String> = std::collections::BTreeMap::new();
265 for rule in rules {
266 if rule.before.is_some()
267 || rule.not_before.is_some()
268 || rule.after.is_some()
269 || rule.not_after.is_some()
270 {
271 return Ok(None);
272 }
273 let to = match &rule.to {
274 SubTo::Item(item) => match m.build_par_str(item) {
275 Ok(s) => s,
276 Err(_) => return Ok(None),
277 },
278 SubTo::Upcase | SubTo::Downcase => String::new(),
283 };
284 let _ = to;
285 match m.build_par_from(&rule.from) {
286 Ok(keys) => {
287 for key in keys {
288 map.insert(key, to.clone());
289 }
290 }
291 Err(_) => return Ok(None),
292 }
293 }
294 let mut root = TrieNode::default();
295 for (from, to) in map {
296 let chars: Vec<char> = from.chars().collect();
297 if chars.is_empty() {
298 continue;
301 }
302 let mut node = &mut root;
303 for c in chars {
304 node = node.children.entry(c).or_default();
305 }
306 node.replacement = Some(to);
307 }
308 Ok(Some(root))
309}
310
311fn tree_replace(root: &TrieNode, s: &str) -> Result<String> {
313 let chars: Vec<char> = s.chars().collect();
314 let len = chars.len();
315 let mut out = String::with_capacity(s.len());
316 let mut i = 0usize;
317 while i < len {
318 let mut branch = &root.children;
319 let mut matched: Option<(usize, &String)> = None;
320 let mut j = 0usize;
321 while j < len - i {
322 let cc = chars[i + j];
323 match branch.get(&cc) {
324 Some(node) => {
325 branch = &node.children;
326 if let Some(repl) = &node.replacement {
327 matched = Some((j + 1, repl));
328 }
329 j += 1;
330 }
331 None => break,
332 }
333 }
334 match matched {
335 Some((mlen, repl)) => {
336 out.push_str(repl);
337 i += mlen;
338 }
339 None => {
340 out.push(chars[i]);
341 i += 1;
342 }
343 }
344 }
345 Ok(out)
346}
347
348fn apply_sub(doc: &Document, rule: &Sub, s: &str) -> Result<String> {
350 let chars: Vec<char> = s.chars().collect();
351 let mut out = String::with_capacity(s.len());
352 let mut copied = 0usize; let mut pos = 0usize;
354 while pos <= chars.len() {
357 let m = Matcher::new(&chars, doc);
358 match m.match_rule(rule, pos)? {
359 Some((end, replacement)) => {
360 out.extend(&chars[copied..pos]);
361 out.push_str(&replacement);
362 copied = end;
363 pos = std::cmp::max(end, pos + 1);
364 }
365 None => pos += 1,
366 }
367 }
368 out.extend(&chars[copied..]);
369 Ok(out)
370}
371
372struct Matcher<'a> {
379 chars: &'a [char],
380 doc: &'a Document,
381 groups: RefCell<Vec<Option<(usize, usize)>>>,
382 error: RefCell<Option<Error>>,
383}
384
385impl<'a> Matcher<'a> {
386 fn new(chars: &'a [char], doc: &'a Document) -> Matcher<'a> {
387 Matcher {
388 chars,
389 doc,
390 groups: RefCell::new(Vec::new()),
391 error: RefCell::new(None),
392 }
393 }
394
395 fn match_rule(&self, rule: &Sub, pos: usize) -> Result<Option<(usize, String)>> {
398 let slots = rule.group_count + 1;
399 self.groups.borrow_mut().resize_with(slots, || None);
400
401 if let Some(before) = &rule.before {
403 if !self.lookbehind(before, pos)? {
404 return Ok(None);
405 }
406 }
407 if let Some(nb) = &rule.not_before {
411 if self.lookbehind(nb, pos)? {
412 return Ok(None);
413 }
414 }
415
416 let mut end_slot: Option<usize> = None;
417 let matched = {
418 let slot_ref = &mut end_slot;
419 self.match_item(&rule.from, pos, &mut |m, end| {
420 if let Some(na) = &rule.not_after {
422 if m.lookahead(na, end)? {
423 return Ok(false);
424 }
425 }
426 if let Some(a) = &rule.after {
428 if !m.lookahead(a, end)? {
429 return Ok(false);
430 }
431 }
432 *slot_ref = Some(end);
433 Ok(true)
434 })
435 };
436
437 if let Some(e) = self.error.borrow_mut().take() {
438 return Err(e);
439 }
440 if !matched {
441 return Ok(None);
442 }
443 let end = end_slot.unwrap_or(pos);
444
445 let replacement = match &rule.to {
446 SubTo::Upcase => self.chars[pos..end].iter().collect::<String>().to_uppercase(),
447 SubTo::Downcase => self.chars[pos..end].iter().collect::<String>().to_lowercase(),
448 SubTo::Item(to) => {
449 let collapsed = to.first_string();
450 let built = self.build_replacement(&collapsed)?;
451 self.decode_replacement(&built, pos, end)
452 }
453 };
454 Ok(Some((end, replacement)))
455 }
456
457 fn lookbehind(&self, item: &Item, pos: usize) -> Result<bool> {
459 for start in (0..=pos).rev() {
460 let ok = self.match_item(item, start, &mut |_m, end| Ok(end == pos));
461 if let Some(e) = self.error.borrow_mut().take() {
462 return Err(e);
463 }
464 if ok {
465 return Ok(true);
466 }
467 }
468 Ok(false)
469 }
470
471 fn lookahead(&self, item: &Item, pos: usize) -> Result<bool> {
473 let ok = self.match_item(item, pos, &mut |_m, _end| Ok(true));
474 if let Some(e) = self.error.borrow_mut().take() {
475 return Err(e);
476 }
477 Ok(ok)
478 }
479
480 fn match_item(
483 &self,
484 item: &Item,
485 pos: usize,
486 k: &mut dyn FnMut(&Matcher, usize) -> Result<bool>,
487 ) -> bool {
488 match item {
489 Item::None => self.call(k, pos),
490 Item::Str(s) => {
491 let pat: Vec<char> = s.chars().collect();
492 if pos + pat.len() <= self.chars.len()
493 && self.chars[pos..pos + pat.len()] == pat[..]
494 {
495 self.call(k, pos + pat.len())
496 } else {
497 false
498 }
499 }
500 Item::Prim(prim) => match prim.matches_zero_width(self.chars, pos) {
501 Some(ok) => ok && self.call(k, pos),
502 None => {
503 if pos < self.chars.len() && prim.matches_char(self.chars[pos]).unwrap_or(false)
504 {
505 self.call(k, pos + 1)
506 } else {
507 false
508 }
509 }
510 },
511 Item::Concat(parts) => self.match_seq(parts, pos, k),
512 Item::Set(entries) => {
513 for entry in entries {
514 if self.match_item(entry, pos, k) {
515 return true;
516 }
517 }
518 false
519 }
520 Item::Range(lo, hi) => {
521 if pos < self.chars.len() && *lo <= self.chars[pos] && self.chars[pos] <= *hi {
522 self.call(k, pos + 1)
523 } else {
524 false
525 }
526 }
527 Item::Maybe(inner) => {
528 let extended = self.match_item(inner, pos, k);
529 if extended {
530 return true;
531 }
532 self.call(k, pos)
533 }
534 Item::Some(inner) => self.match_some(inner, pos, 1, k),
535 Item::CaptureGroup { id, inner } => {
536 let snapshot = self.groups.borrow().clone();
537 let id = *id;
538 let ok = self.match_item(inner, pos, &mut |m, end| {
539 m.groups.borrow_mut()[id] = Some((pos, end));
540 match k(m, end) {
541 Ok(true) => Ok(true),
542 failed => {
543 *m.groups.borrow_mut() = snapshot.clone();
544 failed
545 }
546 }
547 });
548 if !ok {
549 *self.groups.borrow_mut() = snapshot;
550 }
551 ok
552 }
553 Item::CaptureRef(n) => {
554 let text = self
555 .groups
556 .borrow()
557 .get(*n)
558 .and_then(|g| g.as_ref())
559 .map(|(s, e)| self.chars[*s..*e].iter().collect::<String>());
560 match text {
561 Some(t) => {
562 let pat: Vec<char> = t.chars().collect();
563 if pos + pat.len() <= self.chars.len()
564 && self.chars[pos..pos + pat.len()] == pat[..]
565 {
566 self.call(k, pos + pat.len())
567 } else {
568 false
569 }
570 }
571 None => false,
573 }
574 }
575 Item::Alias { map, name } => match self.resolve_alias(map.as_deref(), name) {
576 Ok(Some(resolved)) => self.match_item(&resolved, pos, k),
577 Ok(None) => false,
578 Err(e) => {
579 self.record_error(e);
580 false
581 }
582 },
583 Item::Func(_) => {
584 self.record_error(Error::MapLogic("function item in match position".into()));
585 false
586 }
587 }
588 }
589
590 fn match_seq(
591 &self,
592 parts: &[Item],
593 pos: usize,
594 k: &mut dyn FnMut(&Matcher, usize) -> Result<bool>,
595 ) -> bool {
596 match parts.split_first() {
597 None => self.call(k, pos),
598 Some((first, rest)) => {
599 let rest: Vec<Item> = rest.to_vec();
600 self.match_item(first, pos, &mut |m, end| Ok(m.match_seq(&rest, end, k)))
601 }
602 }
603 }
604
605 fn match_some(
606 &self,
607 inner: &Item,
608 pos: usize,
609 count: usize,
610 k: &mut dyn FnMut(&Matcher, usize) -> Result<bool>,
611 ) -> bool {
612 let extended = self.match_item(inner, pos, &mut |m, end| {
616 if end > pos {
617 Ok(m.match_some(inner, end, count + 1, k))
618 } else {
619 Ok(false)
620 }
621 });
622 if extended {
623 return true;
624 }
625 if count >= 1 {
626 self.call(k, pos)
627 } else {
628 false
629 }
630 }
631
632 fn call(&self, k: &mut dyn FnMut(&Matcher, usize) -> Result<bool>, pos: usize) -> bool {
633 match k(self, pos) {
634 Ok(true) => true,
635 Ok(false) => false,
636 Err(e) => {
637 self.record_error(e);
638 false
639 }
640 }
641 }
642
643 fn record_error(&self, e: Error) {
644 let mut slot = self.error.borrow_mut();
645 if slot.is_none() {
646 *slot = Some(e);
647 }
648 }
649
650 fn resolve_alias(&self, map: Option<&str>, name: &str) -> Result<Option<Item>> {
651 if map.is_none() {
654 if let Some(prim) = Prim::from_name(name) {
655 return Ok(Some(Item::Prim(prim)));
656 }
657 }
658 match map {
659 Some(alias) => {
660 let doc = self
661 .doc
662 .dep_aliases
663 .get(alias)
664 .ok_or_else(|| Error::MapLogic(format!("dep alias {alias:?} not found")))?;
665 doc.lookup_alias(name).map(Some).ok_or_else(|| {
666 Error::MapLogic(format!("alias {name:?} of dep {alias:?} not found"))
667 })
668 }
669 None => self.doc.lookup_alias(name).map(Some).ok_or_else(|| {
670 Error::MapLogic(format!("alias {name:?} not found in {}", self.doc.name))
671 }),
672 }
673 }
674
675 fn build_replacement(&self, item: &Item) -> Result<String> {
677 match item {
678 Item::Str(s) => Ok(s.clone()),
679 Item::None => Ok(String::new()),
680 Item::Concat(parts) => {
681 let mut out = String::new();
682 for p in parts {
683 out.push_str(&self.build_replacement(p)?);
684 }
685 Ok(out)
686 }
687 Item::CaptureRef(n) => Ok(format!("\\{n}")),
688 Item::Maybe(inner) => Ok(format!("{}?", self.build_replacement(inner)?)),
691 Item::Some(inner) => Ok(format!("{}+", self.build_replacement(inner)?)),
692 Item::CaptureGroup { inner, .. } => {
693 Ok(format!("({})", self.build_replacement(inner)?))
694 }
695 Item::Prim(prim) => match prim {
696 Prim::None_ => Ok(String::new()),
697 Prim::Space => Ok(" ".to_string()),
698 other => Err(Error::MapLogic(format!(
699 "can't use {} in a string context",
700 other.name()
701 ))),
702 },
703 Item::Alias { map, name } => {
704 if map.is_none() {
705 if let Some(prim) = Prim::from_name(name) {
706 return self.build_replacement(&Item::Prim(prim));
707 }
708 }
709 match self.resolve_alias(map.as_deref(), name)? {
710 Some(resolved) => self.build_replacement(&resolved),
711 None => Err(Error::MapLogic(format!("alias {name:?} not found"))),
712 }
713 }
714 Item::Set(_) | Item::Range(_, _) => {
718 Err(Error::MapLogic("can't use Any in a string context".into()))
719 }
720 Item::Func(_) => Err(Error::MapLogic(
721 "function item in replacement position".into(),
722 )),
723 }
724 }
725
726 fn decode_replacement(&self, built: &str, start: usize, end: usize) -> String { let chars: Vec<char> = built.chars().collect();
730 let mut out = String::with_capacity(built.len());
731 let mut i = 0usize;
732 while i < chars.len() {
733 if chars[i] == '\\' && i + 1 < chars.len() {
734 let next = chars[i + 1];
735 match next {
736 '0' | '1'..='9' => {
737 let n = next.to_digit(10).unwrap() as usize;
738 if n == 0 {
739 out.extend(&self.chars[start..end]);
740 } else if let Some(Some((gs, ge))) = self.groups.borrow().get(n) {
741 out.extend(&self.chars[*gs..*ge]);
742 }
743 i += 2;
744 continue;
745 }
746 '\\' => {
747 out.push('\\');
748 i += 2;
749 continue;
750 }
751 '&' => {
752 out.extend(&self.chars[start..end]);
753 i += 2;
754 continue;
755 }
756 _ => {
757 out.push(next);
759 i += 2;
760 continue;
761 }
762 }
763 }
764 out.push(chars[i]);
765 i += 1;
766 }
767 out
768 }
769}
770
771impl<'a> Matcher<'a> {
772 fn build_par_from(&self, item: &Item) -> Result<Vec<String>> {
782 self.build_par_from_depth(item, 0)
783 }
784
785 fn build_par_from_depth(&self, item: &Item, depth: usize) -> Result<Vec<String>> {
786 if depth > 64 {
787 return Err(Error::MapLogic("alias expansion too deep".into()));
788 }
789 match item {
790 Item::Str(s) => Ok(vec![s.clone()]),
791 Item::None => Ok(vec![String::new()]),
792 Item::Prim(prim) => match prim {
793 Prim::None_ => Ok(vec![String::new()]),
794 Prim::Space => Ok(vec![" ".to_string()]),
795 other => Err(Error::MapLogic(format!(
796 "can't use {} in a par context",
797 other.name()
798 ))),
799 },
800 Item::Range(lo, hi) => {
804 let mut keys = Vec::new();
805 let mut c = *lo;
806 loop {
807 keys.push(c.to_string());
808 if c == *hi {
809 break;
810 }
811 match char::from_u32(c as u32 + 1) {
812 Some(n) => c = n,
813 None => break,
814 }
815 }
816 Ok(keys)
817 }
818 Item::Set(entries) => {
819 let mut keys = Vec::new();
820 for entry in entries {
821 match entry {
822 Item::Str(s) => keys.push(s.clone()),
823 _ => {
826 return Err(Error::MapLogic(
827 "can't parallelize nested any() entries".into(),
828 ))
829 }
830 }
831 }
832 Ok(keys)
833 }
834 Item::Concat(parts) => {
836 let mut acc: Vec<String> = vec![String::new()];
837 for part in parts {
838 let next = self.build_par_from_depth(part, depth)?;
839 let mut joined = Vec::new();
840 for a in &acc {
841 for b in &next {
842 joined.push(format!("{a}{b}"));
843 }
844 }
845 acc = joined;
846 }
847 Ok(acc)
848 }
849 Item::Alias { map, name } => {
850 if map.is_none() {
851 if let Some(prim) = Prim::from_name(name) {
852 return self.build_par_from_depth(&Item::Prim(prim), depth);
853 }
854 }
855 match self.resolve_alias(map.as_deref(), name)? {
856 Some(resolved) => self.build_par_from_depth(&resolved, depth + 1),
857 None => Ok(vec![]),
858 }
859 }
860 Item::Maybe(_) | Item::Some(_) => Err(Error::MapLogic(
862 "can't use a MaybeSome in a par context".into(),
863 )),
864 Item::CaptureGroup { .. } => Err(Error::MapLogic(
865 "can't use a CaptureGroup in a par context".into(),
866 )),
867 Item::CaptureRef(_) => Err(Error::MapLogic(
868 "Can't use CaptureRef in parallel mode".into(),
869 )),
870 Item::Func(_) => Ok(vec![]),
872 }
873 }
874
875 fn build_par_str(&self, item: &Item) -> Result<String> {
878 let collapsed = item.first_string();
879 self.build_par_str_inner(&collapsed, 0)
880 }
881
882 fn build_par_str_inner(&self, item: &Item, depth: usize) -> Result<String> {
883 if depth > 64 {
884 return Err(Error::MapLogic("alias expansion too deep".into()));
885 }
886 match item {
887 Item::Str(s) => Ok(s.clone()),
888 Item::None => Ok(String::new()),
889 Item::Prim(prim) => match prim {
890 Prim::None_ => Ok(String::new()),
891 Prim::Space => Ok(" ".to_string()),
892 other => Err(Error::MapLogic(format!(
893 "can't use {} in a par context",
894 other.name()
895 ))),
896 },
897 Item::Concat(parts) => {
898 let mut out = String::new();
899 for p in parts {
900 out.push_str(&self.build_par_str_inner(p, depth)?);
901 }
902 Ok(out)
903 }
904 Item::Alias { map, name } => {
905 if map.is_none() {
906 if let Some(prim) = Prim::from_name(name) {
907 return self.build_par_str_inner(&Item::Prim(prim), depth);
908 }
909 }
910 match self.resolve_alias(map.as_deref(), name)? {
911 Some(resolved) => self.build_par_str_inner(&resolved, depth + 1),
912 None => Err(Error::MapLogic(format!("alias {name:?} not found"))),
913 }
914 }
915 Item::Set(_) | Item::Range(_, _) => Err(Error::MapLogic(
918 "parallel replacement is not a single string".into(),
919 )),
920 Item::Maybe(_) | Item::Some(_) => Err(Error::MapLogic(
921 "can't use a MaybeSome in a par context".into(),
922 )),
923 Item::CaptureGroup { .. } => Err(Error::MapLogic(
924 "can't use a CaptureGroup in a par context".into(),
925 )),
926 Item::CaptureRef(_) => Err(Error::MapLogic(
927 "Can't use CaptureRef in parallel mode".into(),
928 )),
929 Item::Func(_) => Err(Error::MapLogic(
930 "function item in a par context".into(),
931 )),
932 }
933 }
934}