narust_158/language/base/
making.rs

1//! 📄OpenNARS `nars.language.MakeTerm`
2//! * 🎯复刻原OpenNARS 1.5.8的`make`系列方法
3//! * 🚩构造词项前,
4//!   * 检查其合法性
5//!   * 简化其表达
6//! * 🎯用于「制作词项」
7//!   * 📝是NARS中「词项逻辑」的重要部分——非推理语义简化
8//! * 🚩【2024-09-07 16:09:46】从外部IO解析出一个新词项的流程:词法折叠→语义简化→创建结构体
9
10use super::{Term, TermComponents};
11use crate::{
12    language::{variable::MaximumVariableId, vec_utils, CompoundTermRef, StatementRef},
13    symbols::*,
14};
15
16impl Term {
17    /* Word */
18
19    /// 制作「词语」
20    #[inline]
21    pub fn make_word(name: impl Into<String>) -> Term {
22        Term::new_word(name)
23    }
24
25    /* 🆕Placeholder */
26
27    /// 制作「占位符」
28    #[inline]
29    pub fn make_placeholder() -> Term {
30        Term::new_placeholder()
31    }
32
33    /* Variable */
34
35    /// 制作「独立变量」
36    #[inline]
37    pub fn make_var_i(to_max: impl MaximumVariableId) -> Term {
38        Term::new_var_i(to_max.maximum_variable_id() + 1)
39    }
40
41    /// 制作「非独变量」
42    #[inline]
43    pub fn make_var_d(to_max: impl MaximumVariableId) -> Term {
44        Term::new_var_d(to_max.maximum_variable_id() + 1)
45    }
46
47    /// 制作「查询变量」
48    #[inline]
49    pub fn make_var_q(to_max: impl MaximumVariableId) -> Term {
50        Term::new_var_q(to_max.maximum_variable_id() + 1)
51    }
52
53    /// 制作「与现有变量类型相同」的变量
54    /// * 🚩类型相同但编号不同
55    /// * 🎯用于「变量推理」中的「重命名变量」
56    #[inline]
57    pub fn make_var_similar(from: &Term, id: impl Into<usize>) -> Term {
58        Term::from_var_similar(from.identifier(), id)
59    }
60
61    /* CompoundTerm */
62
63    /// 📄OpenNARS `public static Term makeCompoundTerm(CompoundTerm compound, ArrayList<Term> components)`
64    pub fn make_compound_term(template: CompoundTermRef, components: Vec<Term>) -> Option<Term> {
65        /* 📄OpenNARS
66        if (compound instanceof ImageExt)
67            // * 🚩外延像
68            return makeImageExt(components, ((ImageExt) compound).getRelationIndex());
69        else if (compound instanceof ImageInt)
70            // * 🚩内涵像
71            return makeImageInt(components, ((ImageInt) compound).getRelationIndex());
72        else
73            // * 🚩其它
74            return makeCompoundTerm(compound.operator(), components); */
75        let term = template.inner;
76        match term.identifier.as_str() {
77            IMAGE_EXT_OPERATOR => {
78                Self::make_image_ext_arg(components, template.get_placeholder_index())
79            }
80            IMAGE_INT_OPERATOR => {
81                Self::make_image_int_arg(components, template.get_placeholder_index())
82            }
83            identifier => Self::make_compound_term_from_identifier(identifier, components),
84        }
85    }
86
87    pub fn make_compound_term_or_statement(
88        template: CompoundTermRef,
89        mut components: Vec<Term>,
90    ) -> Option<Term> {
91        match template.as_statement() {
92            // * 🚩陈述模板
93            Some(statement) => match &components.as_slice() {
94                // * 🚩双元素
95                &[_, _] => {
96                    // * 🚩取出其中仅有的两个元素
97                    let predicate = components.pop().unwrap();
98                    let subject = components.pop().unwrap();
99                    Self::make_statement(&statement, subject, predicate)
100                }
101                // * 🚩其它⇒无
102                _ => None,
103            },
104            // * 🚩复合词项⇒继续
105            _ => Self::make_compound_term(template, components),
106        }
107    }
108
109    /// 📄OpenNARS `public static Term makeCompoundTerm(String op, ArrayList<Term> arg)`
110    pub fn make_compound_term_from_identifier(
111        identifier: impl AsRef<str>,
112        argument: Vec<Term>,
113    ) -> Option<Term> {
114        match identifier.as_ref() {
115            SET_EXT_OPERATOR => Self::make_set_ext_arg(argument),
116            SET_INT_OPERATOR => Self::make_set_int_arg(argument),
117            INTERSECTION_EXT_OPERATOR => Self::make_intersection_ext_arg(argument),
118            INTERSECTION_INT_OPERATOR => Self::make_intersection_int_arg(argument),
119            DIFFERENCE_EXT_OPERATOR => Self::make_difference_ext_arg(argument),
120            DIFFERENCE_INT_OPERATOR => Self::make_difference_int_arg(argument),
121            PRODUCT_OPERATOR => Self::make_product_arg(argument),
122            IMAGE_EXT_OPERATOR => Self::make_image_ext_vec(argument),
123            IMAGE_INT_OPERATOR => Self::make_image_int_vec(argument),
124            NEGATION_OPERATOR => Self::make_negation_arg(argument),
125            CONJUNCTION_OPERATOR => Self::make_conjunction_arg(argument),
126            DISJUNCTION_OPERATOR => Self::make_disjunction_arg(argument),
127            // * 🚩其它⇒未知/域外⇒空
128            _ => None,
129        }
130    }
131
132    // * ℹ️其它与「删改词项」有关的方法,均放在「复合词项引用」中
133
134    // * ✅无需复刻`arguments_to_list`:就是直接构造一个双词项数组,另外还可重定向构造函数
135    #[deprecated]
136    #[allow(unused)]
137    fn arguments_to_list(t1: Term, t2: Term) -> Vec<Term> {
138        /* 📄OpenNARS改版
139        final ArrayList<Term> list = new ArrayList<>(2);
140        list.add(t1);
141        list.add(t2);
142        return list; */
143        vec![t1, t2]
144    }
145
146    /* Set */
147
148    /// 制作一个 外延集/内涵集
149    /// * 🚩单个词项⇒视作一元数组构造
150    fn make_set(t: Term, make_set_arg: fn(Vec<Term>) -> Option<Term>) -> Option<Term> {
151        make_set_arg(vec![t])
152    }
153
154    /// 制作一个 外延集/内涵集
155    /// * 🚩数组⇒统一重排去重⇒构造
156    /// * ℹ️相对改版而言,综合「用集合构造」与「用数组构造」
157    fn make_set_arg(mut argument: Vec<Term>, new_set: fn(Vec<Term>) -> Term) -> Option<Term> {
158        // * 🚩不允许空集
159        if argument.is_empty() {
160            return None;
161        }
162        // * 🚩重排去重 | 📌只重排一层:OpenNARS原意如此,并且在外部构建的词项也经过了重排去重
163        TermComponents::sort_dedup_term_vec(&mut argument);
164        // * 🚩构造
165        Some(new_set(argument))
166    }
167
168    /* SetExt */
169
170    /// 制作一个外延集
171    pub fn make_set_ext(t: Term) -> Option<Term> {
172        Self::make_set(t, Self::make_set_ext_arg)
173    }
174
175    /// 制作一个外延集
176    pub fn make_set_ext_arg(argument: Vec<Term>) -> Option<Term> {
177        Self::make_set_arg(argument, Term::new_set_ext)
178    }
179
180    /* SetInt */
181
182    /// 制作一个内涵集
183    pub fn make_set_int(t: Term) -> Option<Term> {
184        Self::make_set(t, Self::make_set_int_arg)
185    }
186
187    /// 制作一个内涵集
188    pub fn make_set_int_arg(argument: Vec<Term>) -> Option<Term> {
189        Self::make_set_arg(argument, Term::new_set_int)
190    }
191
192    /* Intersection */
193
194    /// 统一的「外延交/内涵交」制作
195    /// * 🔧term1、term2:参与制作的两个词项
196    /// * 🚩统一的「外延/内涵」参数前缀:要么统一选左侧,要么统一选右侧
197    ///   * 左⇒构造**外延**交
198    ///   * 右⇒构造**内涵**交
199    #[allow(clippy::too_many_arguments)]
200    fn make_intersection(
201        term1: Term,
202        term2: Term,
203        // * 📌有关「同相」的参数:外延→外延,内涵→内涵
204        ex_in_set_operator: &str,
205        ex_in_intersection_operator: &str,
206        ex_in_make_set_arg: fn(Vec<Term>) -> Option<Term>,
207        ex_in_make_intersection_vec: fn(Vec<Term>) -> Option<Term>,
208        // * 📌有关「反相」的参数:外延→内涵,内涵→外延
209        in_ex_set_operator: &str,
210        in_ex_make_set_arg: fn(Vec<Term>) -> Option<Term>,
211    ) -> Option<Term> {
212        // * 🚩预置「词项列表」与「词项制作」
213        let mut terms = vec![];
214        let make: fn(Vec<Term>) -> Option<Term>;
215        // * 🚩两个内涵集取外延交 ⇒ 外延交=内涵并 ⇒ 取并集 | 两个外延集取内涵交 ⇒ 内涵交=外延并 ⇒ 取并集
216        // * 📄[A,B] & [C,D] = [A,B,C,D]
217        // * 📄{A,B} | {C,D} = {A,B,C,D}
218        if let [Some(s1), Some(s2)] = [
219            term1.as_compound_type(in_ex_set_operator),
220            term2.as_compound_type(in_ex_set_operator),
221        ] {
222            // * 🚩s1加入最终词项集 | s1加入最终词项集
223            terms.extend(s1.components.iter().cloned());
224            // * 🚩s2加入最终词项集 | s2加入最终词项集
225            terms.extend(s2.components.iter().cloned());
226            // * 🚩最终生成内涵集 | 最终生成外延集
227            make = in_ex_make_set_arg;
228        }
229        // * 🚩两个外延集取外延交 ⇒ 取交集 | 两个内涵集取内涵交 ⇒ 取交集
230        // * 📄{A,B} & {B,C} = {B}
231        // * 📄[A,B] | [B,C] = [B]
232        else if let [Some(s1), Some(s2)] = [
233            term1.as_compound_type(ex_in_set_operator),
234            term2.as_compound_type(ex_in_set_operator),
235        ] {
236            // * 🚩s1加入最终词项集 | s1加入最终词项集
237            terms.extend(s1.components.iter().cloned());
238            // * 🚩加入的词项集和s2取交集 | 加入的词项集和s2取交集
239            vec_utils::retain_all(&mut terms, s2.components);
240            // * 🚩最终生成外延集 | 最终生成内涵集
241            make = ex_in_make_set_arg;
242        } else {
243            // * 🚩均生成外延交 | 注意:在OpenNARS中是传入集合然后重载,此处即改为「直接传递类集合数组」 | 均生成内涵交
244            make = ex_in_make_intersection_vec;
245            match [
246                term1.as_compound_type(ex_in_intersection_operator),
247                term2.as_compound_type(ex_in_intersection_operator),
248            ] {
249                // * 🚩左右都是外延交 ⇒ 取交集 | 左右都是内涵交 ⇒ 取交集
250                // * 📄(&,P,Q) & (&,R,S) = (&,P,Q,R,S)
251                // * 📄(|,P,Q) | (|,R,S) = (|,P,Q,R,S)
252                [Some(s1), Some(s2)] => {
253                    terms.extend(s1.components.iter().cloned());
254                    terms.extend(s2.components.iter().cloned());
255                }
256                // * 🚩仅左边是外延交 ⇒ 右边加进左边 | 仅左边是内涵交 ⇒ 右边加进左边
257                // * 📄(&,P,Q) & R = (&,P,Q,R)
258                // * 📄(|,P,Q) | R = (|,P,Q,R)
259                [Some(s1), None] => {
260                    terms.extend(s1.components.iter().cloned());
261                    terms.push(term2);
262                }
263                // * 🚩仅右边是外延交 ⇒ 左边加进右边 | 仅右边是内涵交 ⇒ 左边加进右边
264                // * 📄R & (&,P,Q) = (&,P,Q,R)
265                // * 📄R | (|,P,Q) = (|,P,Q,R)
266                [None, Some(s2)] => {
267                    terms.extend(s2.components.iter().cloned());
268                    terms.push(term1);
269                }
270                // * 🚩纯默认 ⇒ 直接添加
271                // * 📄P & Q = (&,P,Q)
272                // * 📄P | Q = (|,P,Q)
273                _ => {
274                    terms.push(term1);
275                    terms.push(term2);
276                }
277            }
278        }
279
280        // * 🚩将「最终词项集」视作「集合」重排去重,然后加入「制作」
281        TermComponents::sort_dedup_term_vec(&mut terms);
282        make(terms)
283    }
284
285    /// * 📝同时包括「用户输入」与「从参数构造」两种来源
286    /// * 📄来源1:结构规则「structuralCompose2」
287    /// * 🆕现在构造时也会用reduce逻辑尝试合并
288    fn make_intersection_arg(
289        mut argument: Vec<Term>,
290        make_arg: fn(Term, Term) -> Option<Term>,
291    ) -> Option<Term> {
292        if argument.is_empty() {
293            return None;
294        }
295        // * 🆕🚩做一个reduce的操作
296        // ! ❌【2024-06-17 23:52:45】不能「从尾到头」:先后顺序不一样
297        let mut term = argument.remove(0);
298        // * 🚩取出剩下的
299        let mut argument = argument.into_iter();
300        while let Some(t) = argument.next() {
301            // * 🚩尝试做交集
302            term = match make_arg(term, t) {
303                // * 🚩成功⇒更新
304                Some(new_term) => new_term,
305                // * 🚩失败⇒空集⇒跳到下一个
306                None => argument.next()?,
307            };
308        }
309        // * 🚩返回
310        Some(term)
311    }
312
313    /// * 🚩只依照集合数量进行化简
314    fn make_intersection_vec(
315        mut terms: Vec<Term>,
316        new_intersection: fn(Vec<Term>) -> Term,
317    ) -> Option<Term> {
318        // * 🚩重排去重 | 📌只重排一层:OpenNARS原意如此,并且在外部构建的词项也经过了重排去重
319        TermComponents::sort_dedup_term_vec(&mut terms);
320        // * 🚩再按照重排后的集合大小分派
321        match terms.len() {
322            // * 🚩空集⇒空
323            0 => None,
324            // * 🚩单个元素⇒直接取元素
325            1 => terms.pop(),
326            // * 🚩其它⇒新建词项
327            _ => Some(new_intersection(terms)),
328        }
329    }
330
331    /* IntersectionExt */
332
333    pub fn make_intersection_ext(term1: Term, term2: Term) -> Option<Term> {
334        Self::make_intersection(
335            term1,
336            term2,
337            SET_EXT_OPERATOR,
338            INTERSECTION_EXT_OPERATOR,
339            Self::make_set_ext_arg,
340            Self::make_intersection_ext_vec,
341            SET_INT_OPERATOR,
342            Self::make_set_int_arg,
343        )
344    }
345
346    /// * 📝同时包括「用户输入」与「从参数构造」两种来源
347    /// * 📄来源1:结构规则「structuralCompose2」
348    /// * 🆕现在构造时也会用reduce逻辑尝试合并
349    pub fn make_intersection_ext_arg(argument: Vec<Term>) -> Option<Term> {
350        Self::make_intersection_arg(argument, Self::make_intersection_ext)
351    }
352
353    /// * 🚩只依照集合数量进行化简
354    pub fn make_intersection_ext_vec(terms: Vec<Term>) -> Option<Term> {
355        Self::make_intersection_vec(terms, Term::new_intersection_ext)
356    }
357
358    /* IntersectionInt */
359
360    pub fn make_intersection_int(term1: Term, term2: Term) -> Option<Term> {
361        Self::make_intersection(
362            term1,
363            term2,
364            SET_INT_OPERATOR,
365            INTERSECTION_INT_OPERATOR,
366            Self::make_set_int_arg,
367            Self::make_intersection_int_vec,
368            SET_EXT_OPERATOR,
369            Self::make_set_ext_arg,
370        )
371    }
372
373    /// * 📝同时包括「用户输入」与「从参数构造」两种来源
374    /// * 📄来源1:结构规则「structuralCompose2」
375    /// * 🆕现在构造时也会用reduce逻辑尝试合并
376    pub fn make_intersection_int_arg(argument: Vec<Term>) -> Option<Term> {
377        Self::make_intersection_arg(argument, Self::make_intersection_int)
378    }
379
380    /// * 🚩只依照集合数量进行化简
381    pub fn make_intersection_int_vec(terms: Vec<Term>) -> Option<Term> {
382        Self::make_intersection_vec(terms, Term::new_intersection_int)
383    }
384
385    /* Difference */
386
387    fn make_difference(
388        left: Term,
389        right: Term,
390        set_operator: &str,
391        make_set_arg: fn(Vec<Term>) -> Option<Term>,
392        new_diff: fn(Term, Term) -> Term,
393    ) -> Option<Term> {
394        // * 🚩自己减自己 ⇒ 空集 ⇒ 空
395        if left == right {
396            return None;
397        }
398        match [
399            left.as_compound_type(set_operator),
400            right.as_compound_type(set_operator),
401        ] {
402            // * 🚩外延集的差:求差,构造外延集 | {A, B} - {A} = {B}
403            // * 🚩内涵集的差:求差,构造内涵集 | [A, B] - [A] = [B]
404            [Some(..), Some(..)] => {
405                // * 🚩先解包出内部元素(开始丢弃左右所有权)
406                let [left, right] = [
407                    left.unwrap_compound_components().unwrap(), // ! 先前已假设过复合词项 |
408                    right.unwrap_compound_components().unwrap(), // ! 先前已假设过复合词项 |
409                ];
410                // * 🚩left加入最终词项集 |
411                // * 📝to_vec会拷贝元素,故不用之 |
412                let mut terms = left.into();
413                // * 🚩加入的词项集和right取差集 // set difference |
414                vec_utils::remove_all(&mut terms, &right);
415                // * 🚩最终生成外延集 |
416                make_set_arg(terms)
417            }
418            // * 🚩否则:直接构造差集
419            // * 📄A - B = (-,A,B)
420            // * 📄A ~ B = (~,A,B)
421            _ => Some(new_diff(left, right)),
422        }
423    }
424
425    fn make_difference_arg(
426        mut argument: Vec<Term>,
427        make_difference: fn(Term, Term) -> Option<Term>,
428    ) -> Option<Term> {
429        match argument.len() {
430            // * 🚩单个元素:约简为内部元素(仅在「约简元素」reduceComponent时使用)
431            // * 📄(-,A) = A
432            // * 📄(~,A) = A
433            1 => argument.pop(), // special case from CompoundTerm.reduceComponent
434            // * 🚩两个元素⇒进一步判断
435            2 => {
436                let right = argument.pop().unwrap();
437                let left = argument.pop().unwrap();
438                make_difference(left, right)
439            }
440            // * 🚩其它⇒空
441            _ => None,
442        }
443    }
444
445    /* DifferenceExt */
446
447    pub fn make_difference_ext(left: Term, right: Term) -> Option<Term> {
448        Self::make_difference(
449            left,
450            right,
451            SET_EXT_OPERATOR,
452            Self::make_set_ext_arg,
453            Term::new_diff_ext,
454        )
455    }
456
457    fn make_difference_ext_arg(argument: Vec<Term>) -> Option<Term> {
458        Self::make_difference_arg(argument, Self::make_difference_ext)
459    }
460
461    /* DifferenceInt */
462
463    pub fn make_difference_int(left: Term, right: Term) -> Option<Term> {
464        Self::make_difference(
465            left,
466            right,
467            SET_INT_OPERATOR,
468            Self::make_set_int_arg,
469            Term::new_diff_int,
470        )
471    }
472
473    fn make_difference_int_arg(argument: Vec<Term>) -> Option<Term> {
474        Self::make_difference_arg(argument, Self::make_difference_int)
475    }
476
477    /* Product */
478
479    pub fn make_product_arg(argument: Vec<Term>) -> Option<Term> {
480        Some(Term::new_product(argument))
481    }
482
483    /// * 🚩从「外延像/内涵像」构造,用某个词项替换掉占位符处的元素,并返回新的关系词项
484    ///   * 📄`(/, R, _, b)`, `a` => [`(*, a, b)`, `R`]
485    /// * 📝`<a --> (/, R, _, b)>` => `<(*, a, b) --> R>`,其中就要用 a 替换 [R,b] 中的R
486    /// * ⚠️【2024-06-16 16:29:18】后续要留意其中与OpenNARS「占位符不作词项」逻辑的不同
487    pub fn make_product(image: CompoundTermRef, component: &Term) -> Option<[Term; 2]> {
488        let mut terms = vec![];
489        let mut image_components = image.components.iter();
490        let relation = image_components.next()?.clone();
491        for term in image_components {
492            // * 🚩占位符⇒跳过
493            if term.is_placeholder() {
494                // ! ⚠️不递增索引:相当于「先移除占位符,再添加元素」
495                terms.push(component.clone());
496                continue;
497            }
498            // * 🚩模拟「替换词项」,但使用「惰性复制」的方式(被替换处的词项不会被复制)
499            terms.push(term.clone());
500        }
501        // * 🚩制作 & 返回
502        Self::make_product_arg(terms).map(|product| [product, relation])
503    }
504
505    /* Image */
506
507    /// * 📌作为模板的「像」提供「占位符位置」,但作为「组分」的`argument`可能没有占位符
508    ///   * ⚠️时刻注意OpenNARS内部存储方式的不同
509    ///     * 📄"`(/,neutralization,_,base)` => `[neutralization,base]`+relation_index=0"
510    ///     * 📄"`(/,reaction,acid,_)` => `[acid,reaction]`+relation_index=1"
511    ///   * ❓【2024-08-05 22:59:21】后续是否要完全革新,不按照OpenNARS的构造方式来
512    /// * 📄"(/,num,_)", ["0"] => "(/,0,_)"
513    /// * 📄"(/,neutralization,_,base)", ["reaction", "base"] => "(/,reaction,_,base)"
514    /// * 📄"(/,reaction,acid,_)", ["acid", "neutralization"] => "(/,neutralization,acid,_)"
515    /// * 📄"(/,(*,tim,tom),tom,_)", ["tom", "uncle"] => "(/,uncle,tom,_)";
516    fn make_image_arg(
517        mut argument: Vec<Term>,
518        placeholder_index: usize,
519        make_image_vec: fn(Vec<Term>) -> Option<Term>,
520    ) -> Option<Term> {
521        // * 🚩按占位符位置找到「关系词项」并放在最前边(占位符位置>0)
522        debug_assert!(placeholder_index > 0);
523        // * 🚩【2024-08-05 22:57:53】补丁:若参数表中有占位符,先移除占位符
524        if let Some(old_placeholder_index) = argument.iter().position(|term| term.is_placeholder())
525        {
526            // * 🚩先移除旧位置的占位符
527            argument.remove(old_placeholder_index);
528        } else {
529            // * 🚩OpenNARS旧情况:先将对应位置的词项当作「关系词项」挪到最开头
530            let relation = argument.remove(placeholder_index - 1);
531            argument.insert(0, relation);
532        }
533        // * 🚩再插入占位符
534        match placeholder_index >= argument.len() {
535            // * 🎯处理edge case: "(/,num,_)", ["0"] => "(/,0,_)"
536            true => argument.push(Term::new_placeholder()),
537            // * 🚩否则⇒插入
538            false => argument.insert(placeholder_index, Term::new_placeholder()),
539        }
540        // * 🚩制作词项
541        make_image_vec(argument)
542    }
543
544    fn make_image_vec(
545        argument: Vec<Term>,
546        new_image: fn(Vec<Term>) -> anyhow::Result<Term>,
547    ) -> Option<Term> {
548        // * 🚩拒绝元素过少的词项 | 第一个词项需要是「关系」,除此之外必须含有至少一个元素 & 占位符
549        if argument.len() < 2 {
550            return None;
551        }
552        // ! 📌【2024-08-05 22:08:05】断言:构造的「像」中只能有正好一个占位符
553        debug_assert!(argument.iter().filter(|term| term.is_placeholder()).count() == 1);
554        // * 🚩因为「词项中自带占位符」所以无需「特别决定索引」
555        new_image(argument).ok()
556    }
557
558    /// 共用的「从乘积构造像」逻辑
559    /// * ⚠️有关「像」的机制跟OpenNARS实现不一致,将作调整
560    ///   * 💭但在效果上是可以一致的
561    /// * 🚩整体过程:关系词项插入到最前头,然后在指定的占位符处替换
562    ///   * 📌应用「惰性复制」思路
563    fn make_image_from_product(
564        product: CompoundTermRef,
565        relation: &Term,
566        index: usize, // * 📝这个指的是「乘积里头挖空」的索引
567        make_image_vec: fn(Vec<Term>) -> Option<Term>,
568    ) -> Option<Term> {
569        // * 🚩关系词项是「乘积」⇒可能可以简化
570        if let Some(p2) = relation.as_compound_type(PRODUCT_OPERATOR) {
571            // * 🚩对「二元像」作特别的「取索引」简化
572            if product.size() == 2 && p2.size() == 2 {
573                if index == 0 && product.components[1] == p2.components[1] {
574                    // (/,(*,a,b),_,b) with [(*,a,b),b]#0
575                    // is reduced to self[0][0] = (*,a,b)[0] = a
576                    return Some(p2.components[0].clone());
577                }
578                if index == 1 && product.components[0] == p2.components[0] {
579                    // (/,(*,a,b),a,_) with [a,(*,a,b)]#1
580                    // is reduced to self[1][1] = (*,a,b)[1] = b
581                    return Some(p2.components[1].clone());
582                }
583            }
584        }
585        // * 🚩通过「前插关系词项」与「占位符挖空」构造像
586        let mut argument = vec![relation.clone()];
587        for (i, term) in product.components.iter().enumerate() {
588            let term = match i == index {
589                // * 🚩要替换的位置⇒占位符
590                true => Term::new_placeholder(),
591                // * 🚩其它位置⇒惰性拷贝
592                false => term.clone(),
593            };
594            // * 🚩推送元素
595            argument.push(term);
596        }
597        // * 🚩最终从「装填好的参数」中构造词项
598        make_image_vec(argument)
599    }
600
601    /// 共用的「从像构造像」逻辑
602    /// * 📌从一个已知的外延像中构造新外延像,并切换占位符的位置
603    /// * 🚩关系词项位置不变,后头词项改变位置,原占位符填充词项
604    ///   * ℹ️输出`[新像, 被换出来的词项]`
605    /// * ⚠️`index`的语义:除了第一个「关系词项」外,新的占位符要处在的相对位置
606    ///   * 📌最大值:长度-1
607    ///   * 📄`(/, R, a, _)`中`index = 0` => 指代位置`a`(而非`R`)
608    ///   * 📄`(/, R, a, _)`中`index = 1` => 指代位置`_`(而非`a`,index最大值)
609    ///   * 📄`(/, R, a, _)`中`index = 2` => 超出位置
610    ///   * ℹ️【2024-08-01 15:53:40】此设定旨在与OpenNARS方案对齐
611    /// * 📝本质上是个「替换&插入」的过程
612    ///   * 🚩总体过程:
613    ///     * `component`插入到原来占位符的位置上
614    ///     * `index + 1`处替换为占位符
615    ///   * ⚠️index不会指向占位符位置上:
616    ///     * 为兼容考虑,此情况将返回`[拷贝的原像, component]`
617    fn make_image_from_image(
618        old_image: CompoundTermRef,
619        component: &Term,
620        index: usize,
621        make_image_vec: fn(Vec<Term>) -> Option<Term>,
622    ) -> Option<[Term; 2]> {
623        // * 🚩提取信息 | `old_placeholder_index`算入了「关系词项」
624        let old_placeholder_index = old_image.get_placeholder_index();
625        // * 🚩判断index是否指向了占位符:若为占位符,直接弹出
626        if index + 1 == old_placeholder_index {
627            return Some([old_image.inner.clone(), component.clone()]);
628        }
629        // ! ⚠️【2024-08-08 15:32:34】防御性代码:索引越界⇒驳回
630        /* from:
631        println!("old_image = {old_image}, component = {component}, index = {index}");
632        old_image = /(open _ {}(lock1)), component = $1, index = 2
633            at .\src\language\term_impl\term_making.rs:614
634            at .\src\language\term_impl\term_making.rs:692
635            at .\src\inference\rules\transform_rules.rs:304
636            at .\src\inference\rules\transform_rules.rs:154
637            at .\src\inference\rules\transform_rules.rs:75
638        TODO: 彻查如上bug | 💭思路:可能是在构建「二层转换索引」时出现了问题
639         */
640        if index + 1 >= old_image.components.len() {
641            return None;
642        }
643        // * 🚩开始选择性添加词项(关系词项也算在内)
644        let mut argument = vec![];
645        let outer = old_image.components[index + 1].clone();
646        for (i, term) in old_image.components.iter().enumerate() {
647            let term = if i == index + 1 {
648                // * 🚩要替换的位置(要相对「关系词项」后移)⇒占位符
649                Term::new_placeholder()
650            } else if i == old_placeholder_index {
651                // * 🚩原先占位符的位置⇒新元素
652                component.clone()
653            } else {
654                // * 🚩其它位置⇒原词项
655                term.clone()
656            };
657            argument.push(term);
658        }
659        // * 🚩构造出新词项
660        make_image_vec(argument).map(|image| [image, outer])
661    }
662
663    /* ImageExt */
664
665    fn make_image_ext_arg(argument: Vec<Term>, placeholder_index: usize) -> Option<Term> {
666        Self::make_image_arg(argument, placeholder_index, Self::make_image_ext_vec)
667    }
668
669    /// * 🚩从解析器构造外延像
670    /// * ⚠️参数argument中含有「占位符」词项
671    ///   * ✅这点和OpenNARS相同
672    ///
673    /// ## 📄OpenNARS中的例子
674    ///
675    /// * 📄argList=[reaction, _, base] => argument=[reaction, base], index=0
676    /// * * => "(/,reaction,_,base)"
677    /// * 📄argList=[reaction, acid, _] => argument=[acid, reaction], index=1
678    /// * * => "(/,reaction,acid,_)"
679    /// * 📄argList=[neutralization, _, base] => argument=[neutralization, base], index=0
680    /// * * => "(/,neutralization,_,base)"
681    /// * 📄argList=[open, $120, _] => argument=[$120, open], index=1
682    /// * * => "(/,open,$120,_)"
683    pub fn make_image_ext_vec(argument: impl Into<Vec<Term>>) -> Option<Term> {
684        Self::make_image_vec(argument.into(), Term::new_image_ext)
685    }
686
687    /// 从一个「乘积」构造外延像
688    ///
689    /// ## 📄OpenNARS中的例子
690    ///
691    /// * 📄product="(*,$1,sunglasses)", relation="own",  index=1 => "(/,own,$1,_)"
692    /// * 📄product="(*,bird,plant)",    relation="?1",   index=0 => "(/,?1,_,plant)"
693    /// * 📄product="(*,bird,plant)",    relation="?1",   index=1 => "(/,?1,bird,_)"
694    /// * 📄product="(*,robin,worms)",   relation="food", index=1 => "(/,food,robin,_)"
695    /// * 📄product="(*,CAT,eat,fish)",  relation="R",    index=0 => "(/,R,_,eat,fish)"
696    /// * 📄product="(*,CAT,eat,fish)",  relation="R",    index=1 => "(/,R,CAT,_,fish)"
697    /// * 📄product="(*,CAT,eat,fish)",  relation="R",    index=2 => "(/,R,CAT,eat,_)"
698    /// * 📄product="(*,b,a)", relation="(*,b,(/,like,b,_))", index=1 => "(/,like,b,_)"
699    /// * 📄product="(*,a,b)", relation="(*,(/,like,b,_),b)", index=0 => "(/,like,b,_)"
700    pub fn make_image_ext_from_product(
701        product: CompoundTermRef,
702        relation: &Term,
703        index: usize, // * 📝这个指的是「乘积里头挖空」的索引
704    ) -> Option<Term> {
705        // * 🚩现在统一在一个「『像』构造」逻辑中
706        Self::make_image_from_product(product, relation, index, Self::make_image_ext_vec)
707    }
708
709    /// ## 📄OpenNARS中的例子
710    ///
711    /// * 📄oldImage="(/,open,{key1},_)",   component="lock",   index=0 => "(/,open,_,lock)"
712    /// * 📄oldImage="(/,uncle,_,tom)",     component="tim",    index=1 => "(/,uncle,tim,_)"
713    /// * 📄oldImage="(/,open,{key1},_)",   component="$2",     index=0 => "(/,open,_,$2)"
714    /// * 📄oldImage="(/,open,{key1},_)",   component="#1",     index=0 => "(/,open,_,#1)"
715    /// * 📄oldImage="(/,like,_,a)",        component="b",      index=1 => "(/,like,b,_)"
716    /// * 📄oldImage="(/,like,b,_)",        component="a",      index=0 => "(/,like,_,a)"
717    pub fn make_image_ext_from_image(
718        old_image: CompoundTermRef,
719        component: &Term,
720        index: usize,
721    ) -> Option<[Term; 2]> {
722        // * 🚩现在统一在一个「『像』构造」逻辑中
723        Self::make_image_from_image(old_image, component, index, Self::make_image_ext_vec)
724    }
725
726    /* ImageInt */
727
728    fn make_image_int_arg(argument: Vec<Term>, placeholder_index: usize) -> Option<Term> {
729        Self::make_image_arg(argument, placeholder_index, Self::make_image_int_vec)
730    }
731
732    pub fn make_image_int_vec(argument: impl Into<Vec<Term>>) -> Option<Term> {
733        Self::make_image_vec(argument.into(), Term::new_image_int)
734    }
735
736    pub fn make_image_int_from_product(
737        product: CompoundTermRef,
738        relation: &Term,
739        index: usize, // * 📝这个指的是「乘积里头挖空」的索引
740    ) -> Option<Term> {
741        // * 🚩现在统一在一个「『像』构造」逻辑中
742        Self::make_image_from_product(product, relation, index, Self::make_image_int_vec)
743    }
744
745    /// ## 📄OpenNARS中的例子
746    ///
747    /// * 📄oldImage=`(\,X,_,eat,fish)`,          component=`cat`,  index=`2` => `(\,X,cat,eat,_)`
748    /// * 📄oldImage=`(\,reaction,acid,_)`,       component=`soda`, index=`0` => `(\,reaction,_,soda)`
749    /// * 📄oldImage=`(\,X,_,eat,fish)`,          component=`Y`,    index=`2` => `(\,X,Y,eat,_)`
750    /// * 📄oldImage=`(\,neutralization,_,soda)`, component=`acid`, index=`1` => `(\,neutralization,acid,_)`
751    /// * 📄oldImage=`(\,neutralization,acid,_)`, component=`$1`,   index=`0` => `(\,neutralization,_,$1)`
752    /// * 📄oldImage=`(\,REPRESENT,_,$1)`,        component=`Y`,    index=`1` => `(\,REPRESENT,Y,_)`
753    ///
754    /// ℹ️更多例子详见单元测试用例
755    pub fn make_image_int_from_image(
756        old_image: CompoundTermRef,
757        component: &Term,
758        index: usize,
759    ) -> Option<[Term; 2]> {
760        // * 🚩现在统一在一个「『像』构造」逻辑中
761        Self::make_image_from_image(old_image, component, index, Self::make_image_int_vec)
762    }
763
764    /* Junction */
765
766    /// 同时代表「从数组」与「从集合」
767    fn make_junction_arg(
768        mut argument: Vec<Term>,
769        new_junction: fn(Vec<Term>) -> Term,
770    ) -> Option<Term> {
771        // * 🚩重排去重 | 📌只重排一层:OpenNARS原意如此,并且在外部构建的词项也经过了重排去重
772        TermComponents::sort_dedup_term_vec(&mut argument);
773        // * 🚩再根据参数数目分派
774        match argument.len() {
775            // * 🚩不允许空集
776            0 => None,
777            // * 🚩单元素⇒直接用元素(可提取)
778            // special case: single component
779            1 => argument.pop(),
780            // * 🚩多元素⇒构造新的词项
781            _ => Some(new_junction(argument)),
782        }
783    }
784
785    /// 从推理规则中构建
786    fn make_junction(
787        term1: Term,
788        term2: Term,
789        junction_operator: &str,
790        make_junction_arg: fn(Vec<Term>) -> Option<Term>,
791    ) -> Option<Term> {
792        let mut terms: Vec<Term> = vec![];
793        match term1.as_compound_type(junction_operator) {
794            // * 🚩同类⇒合并
795            Some(..) => terms.extend(
796                term1
797                    .unwrap_compound_components()
798                    .expect("已判断是复合词项")
799                    .into_vec(),
800            ),
801            // * 🚩异类⇒加入
802            _ => terms.push(term1),
803        }
804        match term2.as_compound_type(junction_operator) {
805            // * 🚩同类⇒合并
806            Some(..) => terms.extend(
807                term2
808                    .unwrap_compound_components()
809                    .expect("已判断是复合词项")
810                    .into_vec(),
811            ),
812            // * 🚩异类⇒加入
813            _ => terms.push(term2),
814        }
815        make_junction_arg(terms)
816    }
817
818    /* Conjunction */
819    // ? 【2024-06-17 23:24:39】单独的单元测试
820
821    pub fn make_conjunction_arg(argument: Vec<Term>) -> Option<Term> {
822        Self::make_junction_arg(argument, Term::new_conjunction)
823    }
824
825    pub fn make_conjunction(term1: Term, term2: Term) -> Option<Term> {
826        Self::make_junction(
827            term1,
828            term2,
829            CONJUNCTION_OPERATOR,
830            Self::make_conjunction_arg,
831        )
832    }
833
834    /* Disjunction */
835    // ? 【2024-06-17 23:24:39】单独的单元测试
836
837    pub fn make_disjunction_arg(argument: Vec<Term>) -> Option<Term> {
838        Self::make_junction_arg(argument, Term::new_disjunction)
839    }
840
841    pub fn make_disjunction(term1: Term, term2: Term) -> Option<Term> {
842        Self::make_junction(
843            term1,
844            term2,
845            DISJUNCTION_OPERATOR,
846            Self::make_disjunction_arg,
847        )
848    }
849
850    /* Negation */
851    // ? 【2024-06-17 23:24:39】单独的单元测试
852
853    pub fn make_negation(t: Term) -> Option<Term> {
854        match t.as_compound_type(NEGATION_OPERATOR) {
855            // * 🚩双重否定⇒肯定
856            // * 📄-- (--,P) = P
857            Some(..) => t
858                .unwrap_compound_components()
859                .expect("已经假定是复合词项")
860                .into_vec()
861                .pop(), // * 📌只能使用pop来安全取出元素。。
862            // * 🚩其它⇒只有一个参数的「否定」词项
863            None => Self::make_negation_arg(vec![t]),
864        }
865    }
866
867    fn make_negation_arg(mut argument: Vec<Term>) -> Option<Term> {
868        match argument.len() {
869            // * 🚩仅有一个⇒构造
870            1 => Some(Term::new_negation(argument.pop().unwrap())),
871            // * 🚩其它⇒空(失败)
872            _ => None,
873        }
874    }
875
876    /* Statement */
877
878    /// 从一个「陈述系词」中构造
879    pub fn make_statement_relation(
880        copula: impl AsRef<str>,
881        subject: Term,
882        predicate: Term,
883    ) -> Option<Term> {
884        // * 🚩无效⇒制作失败
885        if StatementRef::invalid_statement(&subject, &predicate) {
886            return None;
887        }
888        // * 🚩按照「陈述系词」分派
889        match copula.as_ref() {
890            INHERITANCE_RELATION => Self::make_inheritance(subject, predicate),
891            SIMILARITY_RELATION => Self::make_similarity(subject, predicate),
892            INSTANCE_RELATION => Self::make_instance(subject, predicate),
893            PROPERTY_RELATION => Self::make_property(subject, predicate),
894            INSTANCE_PROPERTY_RELATION => Self::make_instance_property(subject, predicate),
895            IMPLICATION_RELATION => Self::make_implication(subject, predicate),
896            EQUIVALENCE_RELATION => Self::make_equivalence(subject, predicate),
897            _ => None,
898        }
899    }
900
901    /// 从模板中制作「陈述」
902    /// * 🎯推理规则
903    /// * 🚩【2024-07-08 11:45:32】放宽对「词项类型」的限制
904    ///   * 📌实际上只需识别标识符
905    /// * ♻️【2024-08-05 00:58:29】直接使用[`Self::make_statement_relation`]
906    ///   * 📌目前保持「依照『模板词项』的标识符制作陈述」的语义
907    ///   * ✅由此也兼容了「实例/属性/实例属性」等外部系词
908    pub fn make_statement(template: &Term, subject: Term, predicate: Term) -> Option<Term> {
909        // * 🚩直接是`make_statement_relation`的链接
910        Term::make_statement_relation(template.identifier(), subject, predicate)
911    }
912
913    /// 📄OpenNARS `Statement.makeSym`
914    /// * 🚩通过使用「标识符映射」将「非对称版本」映射到「对称版本」
915    /// * ⚠️目前只支持「继承」和「蕴含」,其它均会`panic`
916    /// * 🚩【2024-07-23 15:35:41】实际上并不需要「复合词项引用」:只是对标识符做分派
917    ///
918    /// # 📄OpenNARS
919    /// Make a symmetric Statement from given components and temporal information,
920    /// called by the rules
921    pub fn make_statement_symmetric(
922        template: &Term,
923        subject: Term,
924        predicate: Term,
925    ) -> Option<Term> {
926        match template.identifier() {
927            // 继承⇒相似
928            INHERITANCE_RELATION => Self::make_similarity(subject, predicate),
929            // 蕴含⇒等价
930            IMPLICATION_RELATION => Self::make_equivalence(subject, predicate),
931            // 其它⇒panic
932            identifier => unimplemented!("不支持的标识符:{identifier:?}"),
933        }
934    }
935
936    /* Inheritance */
937
938    pub fn make_inheritance(subject: Term, predicate: Term) -> Option<Term> {
939        // * 🚩检查有效性
940        match StatementRef::invalid_statement(&subject, &predicate) {
941            true => None,
942            false => Some(Term::new_inheritance(subject, predicate)),
943        }
944    }
945
946    /* Instance */
947
948    /// * 🚩转发 ⇒ 继承 + 外延集
949    pub fn make_instance(subject: Term, predicate: Term) -> Option<Term> {
950        Self::make_inheritance(Self::make_set_ext(subject)?, predicate)
951    }
952
953    /* Property */
954
955    /// * 🚩转发 ⇒ 继承 + 内涵集
956    pub fn make_property(subject: Term, predicate: Term) -> Option<Term> {
957        Self::make_inheritance(subject, Self::make_set_int(predicate)?)
958    }
959
960    /* InstanceProperty */
961
962    /// * 🚩转发 ⇒ 继承 + 外延集 + 内涵集
963    pub fn make_instance_property(subject: Term, predicate: Term) -> Option<Term> {
964        Self::make_inheritance(Self::make_set_ext(subject)?, Self::make_set_int(predicate)?)
965    }
966
967    /* Similarity */
968
969    pub fn make_similarity(subject: Term, predicate: Term) -> Option<Term> {
970        // * 🚩检查有效性
971        match StatementRef::invalid_statement(&subject, &predicate) {
972            true => None,
973            // * ✅在创建时自动排序
974            false => Some(Term::new_similarity(subject, predicate)),
975        }
976    }
977
978    /* Implication */
979
980    pub fn make_implication(subject: Term, predicate: Term) -> Option<Term> {
981        // TODO: 🚧【2024-09-07 15:28:30】有待继续提取至独立的「检查是否合法」方法
982        //   * 🏗️后续继续为「变量替换后检查有效性」做准备
983        // * 🚩检查有效性
984        if StatementRef::invalid_statement(&subject, &predicate) {
985            return None;
986        }
987        // * 🚩检查主词类型
988        if subject.instanceof_implication() || subject.instanceof_equivalence() {
989            return None;
990        }
991        if predicate.instanceof_equivalence() {
992            return None;
993        }
994        // B in <A ==> <B ==> C>>
995        if predicate.as_compound_type(IMPLICATION_RELATION).is_some() {
996            let [old_condition, predicate_predicate] = predicate
997                .unwrap_statement_components()
998                .expect("已经假定是复合词项");
999            // ! ❌ <A ==> <(&&, A, B) ==> C>>
1000            // ? ❓为何不能合并:实际上A && (&&, A, B) = (&&, A, B)
1001            if let Some(conjunction) = old_condition.as_compound_type(CONJUNCTION_OPERATOR) {
1002                if conjunction.contain_component(&subject) {
1003                    return None;
1004                }
1005            }
1006            // * ♻️ <A ==> <B ==> C>> ⇒ <(&&, A, B) ==> C>
1007            let new_condition = Self::make_conjunction(subject, old_condition)?;
1008            Self::make_implication(new_condition, predicate_predicate)
1009        } else {
1010            Some(Term::new_implication(subject, predicate))
1011        }
1012    }
1013
1014    /* Equivalence */
1015
1016    pub fn make_equivalence(subject: Term, predicate: Term) -> Option<Term> {
1017        // to be extended to check if subject is Conjunction
1018        // * 🚩检查非法主谓组合
1019        // ! <<A ==> B> <=> C> or <<A <=> B> <=> C>
1020        if subject.instanceof_implication() || subject.instanceof_equivalence() {
1021            return None;
1022        }
1023        // ! <C <=> <C ==> D>> or <C <=> <C <=> D>>
1024        if subject.instanceof_implication() || subject.instanceof_equivalence() {
1025            return None;
1026        }
1027        // ! <A <=> A>, <<A --> B> <=> <B --> A>>
1028        // * 🚩检查有效性
1029        match StatementRef::invalid_statement(&subject, &predicate) {
1030            true => None,
1031            // * ✅在创建时自动排序
1032            false => Some(Term::new_equivalence(subject, predicate)),
1033        }
1034    }
1035}
1036
1037#[cfg(test)]
1038mod tests {
1039    use super::super::test_term::*;
1040    use super::*;
1041    use crate::{ok, option_term, test_term as term, util::AResult};
1042    use nar_dev_utils::macro_once;
1043
1044    /// 具体的词项构造
1045    /// * 📄外延集、内涵集……
1046    mod concrete_type {
1047        use super::*;
1048
1049        fn test_make_one(term: Term, expected: Option<Term>, make: fn(Term) -> Option<Term>) {
1050            // * 🚩格式化字符串,以备后用
1051            let term_str = term.to_string();
1052            // * 🚩传入两个词项所有权,制作新词项
1053            let out = make(term);
1054            // * 🚩检验
1055            assert_eq!(
1056                out,
1057                expected,
1058                "{term_str:?} => {} != {}",
1059                format_option_term(&out),
1060                format_option_term(&expected)
1061            );
1062        }
1063
1064        fn test_make_one_f(make: fn(Term) -> Option<Term>) -> impl Fn(Term, Option<Term>) {
1065            move |term, expected| test_make_one(term, expected, make)
1066        }
1067
1068        fn test_make_two(
1069            term1: Term,
1070            term2: Term,
1071            expected: Option<Term>,
1072            make: fn(Term, Term) -> Option<Term>,
1073        ) {
1074            // * 🚩格式化字符串,以备后用
1075            let term1_str = term1.to_string();
1076            let term2_str = term2.to_string();
1077            // * 🚩传入两个词项所有权,制作新词项
1078            let out = make(term1, term2);
1079            // * 🚩检验
1080            assert_eq!(
1081                out,
1082                expected,
1083                "{term1_str:?}, {term2_str:?} => {} != {}",
1084                format_option_term(&out),
1085                format_option_term(&expected)
1086            );
1087        }
1088
1089        fn test_make_two_f(
1090            make: fn(Term, Term) -> Option<Term>,
1091        ) -> impl Fn(Term, Term, Option<Term>) {
1092            move |t1, t2, expected| test_make_two(t1, t2, expected, make)
1093        }
1094
1095        fn test_make_arg(
1096            terms: Vec<Term>,
1097            expected: Option<Term>,
1098            make: fn(Vec<Term>) -> Option<Term>,
1099        ) {
1100            // * 🚩格式化字符串,以备后用
1101            let terms_str = format!("{terms:?}");
1102            // * 🚩传入两个词项所有权,制作新词项
1103            let out = make(terms);
1104            // * 🚩检验
1105            assert_eq!(
1106                out,
1107                expected,
1108                "{terms_str:?} => {} != {}",
1109                format_option_term(&out),
1110                format_option_term(&expected)
1111            );
1112        }
1113
1114        fn test_make_arg_f(
1115            make: fn(Vec<Term>) -> Option<Term>,
1116        ) -> impl Fn(Vec<Term>, Option<Term>) {
1117            move |argument, expected| test_make_arg(argument, expected, make)
1118        }
1119
1120        fn test_make_image_from_product_f(
1121            make: fn(CompoundTermRef, &Term, usize) -> Option<Term>,
1122        ) -> impl Fn(Term, Term, usize, Term) {
1123            move |p, relation, index, expected| {
1124                let product = p.as_compound().expect("解析出的不是复合词项!");
1125                let image = make(product, &relation, index).expect("词项制作失败!");
1126                assert_eq!(
1127                    image, expected,
1128                    "{product}, {relation}, {index} => {image} != {expected}"
1129                );
1130            }
1131        }
1132
1133        fn test_make_image_from_image_f(
1134            make: fn(CompoundTermRef, &Term, usize) -> Option<[Term; 2]>,
1135        ) -> impl Fn(Term, Term, usize, Term, Term) {
1136            move |i, component, index, expected, expected_outer| {
1137                let old_image = i.as_compound().expect("解析出的不是复合词项!");
1138                let [image, outer] = make(old_image, &component, index).expect("词项制作失败!");
1139                assert_eq!(
1140                    image, expected,
1141                    "{old_image}, {component}, {index} => {image} != {expected}"
1142                );
1143                assert_eq!(
1144                    outer, expected_outer,
1145                    "{old_image}, {component}, {index} => {outer} != {expected_outer}"
1146                );
1147            }
1148        }
1149
1150        /* SetExt */
1151
1152        #[test]
1153        fn make_set_ext() -> AResult {
1154            let test = test_make_one_f(Term::make_set_ext);
1155            macro_once! {
1156                // * 🚩模式:参数列表 ⇒ 预期词项
1157                macro test($($t:tt => $expected:tt;)*) {
1158                    $( test(term!($t) ,option_term!($expected)); )*
1159                }
1160                "tom" => "{tom}";
1161                "Tweety" => "{Tweety}";
1162                "Saturn" => "{Saturn}";
1163                "Venus" => "{Venus}";
1164                "tim" => "{tim}";
1165                "Birdie" => "{Birdie}";
1166                "Pluto" => "{Pluto}";
1167            }
1168            ok!()
1169        }
1170
1171        #[test]
1172        fn make_set_ext_arg() -> AResult {
1173            let test = test_make_arg_f(Term::make_set_ext_arg);
1174            macro_once! {
1175                // * 🚩模式:参数列表 ⇒ 预期词项
1176                macro test($($argument:tt => $expected:tt;)*) {
1177                    $( test(term!($argument).into(), option_term!($expected)); )*
1178                }
1179                [] => None;
1180                ["?49"] => "{?49}";
1181                ["Mars", "Pluto", "Venus"] => "{Mars,Pluto,Venus}";
1182                ["Birdie"] => "{Birdie}";
1183                ["lock"] => "{lock}";
1184                ["#1"] => "{#1}";
1185                ["key1"] => "{key1}";
1186                ["Pluto", "Saturn"] => "{Pluto,Saturn}";
1187                ["Mars", "Venus"] => "{Mars,Venus}";
1188                ["lock1"] => "{lock1}";
1189                ["Tweety"] => "{Tweety}";
1190            }
1191            ok!()
1192        }
1193
1194        /* SetInt */
1195
1196        #[test]
1197        fn make_set_int() -> AResult {
1198            let test = test_make_one_f(Term::make_set_int);
1199            macro_once! {
1200                // * 🚩模式:参数列表 ⇒ 预期词项
1201                macro test($($t:tt => $expected:tt;)*) {
1202                    $( test(term!($t) ,option_term!($expected)); )*
1203                }
1204                "[1]" => "[[1]]";
1205                "[{1}]" => "[[{1}]]";
1206                "{[<[1] --> {1}>]}" => "[{[<[1] --> {1}>]}]";
1207                // * ℹ️以下用例源自OpenNARS实际运行
1208                "black" => "[black]";
1209                "yellow" => "[yellow]";
1210            }
1211            ok!()
1212        }
1213
1214        #[test]
1215        fn make_set_int_arg() -> AResult {
1216            let test = test_make_arg_f(Term::make_set_int_arg);
1217            macro_once! {
1218                // * 🚩模式:参数列表 ⇒ 预期词项
1219                macro test($($argument:tt => $expected:tt;)*) {
1220                    $( test(term!($argument).into(), option_term!($expected)); )*
1221                }
1222                [] => None;
1223                ["1", "2"] => "[1, 2]";
1224                ["1", "2", "[1]", "[2]"] => "[1, 2, [1], [2]]";
1225                ["1", "2", "<1 --> 2>", "<1 --> 2>"] => "[1, 2, <1 --> 2>]"; // 去重
1226                // * ℹ️以下用例源自OpenNARS实际运行
1227                ["flying"]     => "[flying]";
1228                ["unscrewing"] => "[unscrewing]";
1229                ["with_wings"] => "[with_wings]";
1230                ["smart"]      => "[smart]";
1231                ["bright"]     => "[bright]";
1232                ["strong"]     => "[strong]";
1233                ["living"]     => "[living]";
1234                ["chirping"]   => "[chirping]";
1235                ["aggressive"] => "[aggressive]";
1236                ["black"]      => "[black]";
1237                ["bendable"]   => "[bendable]";
1238                ["hurt"]       => "[hurt]";
1239                ["with_beak"]  => "[with_beak]";
1240            }
1241            ok!()
1242        }
1243
1244        /* IntersectionExt */
1245
1246        #[test]
1247        fn make_intersection_ext() -> AResult {
1248            let test = test_make_two_f(Term::make_intersection_ext);
1249            macro_once! {
1250                // * 🚩模式:函数参数 ⇒ 预期词项
1251                macro test($($term1:tt, $term2:tt => $expected:tt;)*) {
1252                    $( test(term!($term1), term!($term2), option_term!($expected)); )*
1253                }
1254                // * ℹ️用例均源自OpenNARS实际运行
1255                // 集合之间的交集
1256                "[with_wings]", "[with_wings,yellow]" => "[with_wings,with_wings,yellow]";
1257                "[with_wings]", "[with_wings]" => "[with_wings,with_wings]";
1258                "{Mars,Pluto,Venus}", "{Pluto,Saturn}" => "{Pluto}";
1259                "{Mars,Venus}", "{Pluto,Saturn}" => None;
1260                "{Pluto,Saturn}", "{Mars,Pluto,Venus}" => "{Pluto}";
1261                "{Tweety}", "{Birdie}" => None;
1262                // 其它情形
1263                "#1", "bird" => "(&,#1,bird)";
1264                "#1", "{Birdie}" => "(&,#1,{Birdie})";
1265                "(&,bird,{Birdie})", "[yellow]" => "(&,bird,[yellow],{Birdie})";
1266                "(&,bird,{Birdie})", "flyer" => "(&,bird,flyer,{Birdie})";
1267                "(&,flyer,{Birdie})", "(&,bird,[yellow])" => "(&,bird,flyer,[yellow],{Birdie})";
1268                "(|,bird,flyer)", "#1" => "(&,#1,(|,bird,flyer))";
1269                "(|,bird,flyer)", "(|,bird,{Birdie})" => "(&,(|,bird,flyer),(|,bird,{Birdie}))";
1270                "(|,flyer,{Birdie})", "(|,#1,flyer)" => "(&,(|,#1,flyer),(|,flyer,{Birdie}))";
1271                "(|,flyer,{Birdie})", "[with-wings]" => "(&,[with-wings],(|,flyer,{Birdie}))";
1272                "<{Tweety} --> bird>", "<bird --> fly>" => "(&,<bird --> fly>,<{Tweety} --> bird>)";
1273                "[strong]", "(~,youth,girl)" => "(&,[strong],(~,youth,girl))";
1274                "[yellow]", "bird" => "(&,bird,[yellow])";
1275                "animal", "bird" => "(&,animal,bird)";
1276                "bird", "#1" => "(&,#1,bird)";
1277                "bird", "(|,#1,flyer)" => "(&,bird,(|,#1,flyer))";
1278                "bird", "[with-wings]" => "(&,bird,[with-wings])";
1279                "bird", "[yellow]" => "(&,bird,[yellow])";
1280                "bird", "{Birdie}" => "(&,bird,{Birdie})";
1281                "flyer", "(&,bird,[yellow])" => "(&,bird,flyer,[yellow])";
1282                "flyer", "(&,bird,{Birdie})" => "(&,bird,flyer,{Birdie})";
1283                "{Birdie}", "[with-wings]" => "(&,[with-wings],{Birdie})";
1284                "{Birdie}", "[with_wings]" => "(&,[with_wings],{Birdie})";
1285                "{Birdie}", "bird" => "(&,bird,{Birdie})";
1286                "{Tweety}", "#1" => "(&,#1,{Tweety})";
1287            }
1288            ok!()
1289        }
1290
1291        /* IntersectionInt */
1292        #[test]
1293        fn make_intersection_int() -> AResult {
1294            let test = test_make_two_f(Term::make_intersection_int);
1295            macro_once! {
1296                // * 🚩模式:函数参数 ⇒ 预期词项
1297                macro test($($term1:tt, $term2:tt => $expected:tt;)*) {
1298                    $( test(term!($term1), term!($term2), option_term!($expected)); )*
1299                }
1300                // * ℹ️用例均源自OpenNARS实际运行
1301                "#1", "(&,bird,{Birdie})" => "(|,#1,(&,bird,{Birdie}))";
1302                "#1", "bird" => "(|,#1,bird)";
1303                "#1", "{Birdie}" => "(|,#1,{Birdie})";
1304                "(&,#1,{lock1})", "lock1" => "(|,lock1,(&,#1,{lock1}))";
1305                "(&,[with-wings],{Birdie})", "(&,bird,flyer)" => "(|,(&,bird,flyer),(&,[with-wings],{Birdie}))";
1306                "(&,bird,{Birdie})", "[yellow]" => "(|,[yellow],(&,bird,{Birdie}))";
1307                "(&,bird,{Birdie})", "flyer" => "(|,flyer,(&,bird,{Birdie}))";
1308                "(&,flyer,{Birdie})", "(&,bird,[yellow])" => "(|,(&,bird,[yellow]),(&,flyer,{Birdie}))";
1309                "(&,flyer,{Birdie})", "(&,bird,{Birdie})" => "(|,(&,bird,{Birdie}),(&,flyer,{Birdie}))";
1310                "(|,#1,bird)", "{Birdie}" => "(|,#1,bird,{Birdie})";
1311                "(|,[with-wings],(&,bird,[yellow]))", "flyer" => "(|,flyer,[with-wings],(&,bird,[yellow]))";
1312                "(|,bird,flyer)", "#1" => "(|,#1,bird,flyer)";
1313                "(|,bird,flyer)", "(|,bird,{Birdie})" => "(|,bird,flyer,{Birdie})";
1314                "(|,bird,flyer)", "{Birdie}" => "(|,bird,flyer,{Birdie})";
1315                "(|,flyer,[with_wings])", "{Birdie}" => "(|,flyer,[with_wings],{Birdie})";
1316                "(|,flyer,{Birdie})", "(|,#1,flyer)" => "(|,#1,flyer,{Birdie})";
1317                "(|,flyer,{Birdie})", "[with-wings]" => "(|,flyer,[with-wings],{Birdie})";
1318                "(|,flyer,{Tweety})", "{Birdie}" => "(|,flyer,{Birdie},{Tweety})";
1319                "(~,boy,girl)", "(~,youth,girl)" => "(|,(~,boy,girl),(~,youth,girl))";
1320                "[strong]", "(~,youth,girl)" => "(|,[strong],(~,youth,girl))";
1321                "[with-wings]", "#1" => "(|,#1,[with-wings])";
1322                "[with-wings]", "(&,bird,[yellow])" => "(|,[with-wings],(&,bird,[yellow]))";
1323                "[with-wings]", "(&,bird,flyer)" => "(|,[with-wings],(&,bird,flyer))";
1324                "[with-wings]", "(&,bird,{Birdie})" => "(|,[with-wings],(&,bird,{Birdie}))";
1325                "[with-wings]", "(|,bird,flyer)" => "(|,bird,flyer,[with-wings])";
1326                "[with-wings]", "[with_wings,yellow]" => None;
1327                "[with-wings]", "{Birdie}" => "(|,[with-wings],{Birdie})";
1328                "[with_wings]", "(&,bird,[with-wings])" => "(|,[with_wings],(&,bird,[with-wings]))";
1329                "[with_wings]", "(&,bird,{Birdie})" => "(|,[with_wings],(&,bird,{Birdie}))";
1330                "[with_wings]", "(|,bird,{Birdie})" => "(|,bird,[with_wings],{Birdie})";
1331                "[with_wings]", "[with-wings]" => None;
1332                "[with_wings]", "[yellow]" => None;
1333                "[with_wings]", "bird" => "(|,bird,[with_wings])";
1334                "[with_wings]", "{Birdie}" => "(|,[with_wings],{Birdie})";
1335                "animal", "bird" => "(|,animal,bird)";
1336                "bird", "#1" => "(|,#1,bird)";
1337                "bird", "(&,bird,{Birdie})" => "(|,bird,(&,bird,{Birdie}))";
1338                "bird", "(|,#1,flyer)" => "(|,#1,bird,flyer)";
1339                "bird", "(|,bird,flyer)" => "(|,bird,flyer)";
1340                "bird", "(|,flyer,[with-wings])" => "(|,bird,flyer,[with-wings])";
1341                "bird", "[with-wings]" => "(|,bird,[with-wings])";
1342                "bird", "[yellow]" => "(|,bird,[yellow])";
1343                "bird", "{Birdie}" => "(|,bird,{Birdie})";
1344                "boy", "(~,youth,girl)" => "(|,boy,(~,youth,girl))";
1345                "flyer", "(&,bird,[with-wings])" => "(|,flyer,(&,bird,[with-wings]))";
1346                "flyer", "(&,bird,[yellow])" => "(|,flyer,(&,bird,[yellow]))";
1347                "robin", "(|,#1,{Birdie})" => "(|,#1,robin,{Birdie})";
1348                "{Birdie}", "(|,[with_wings],(&,bird,[with-wings]))" => "(|,[with_wings],{Birdie},(&,bird,[with-wings]))";
1349                "{Birdie}", "[with-wings]" => "(|,[with-wings],{Birdie})";
1350                "{Birdie}", "[with_wings]" => "(|,[with_wings],{Birdie})";
1351                "{Birdie}", "bird" => "(|,bird,{Birdie})";
1352                "{Mars,Pluto,Venus}", "{Pluto,Saturn}" => "{Mars,Pluto,Saturn,Venus}";
1353                "{Mars,Venus}", "{Pluto,Saturn}" => "{Mars,Pluto,Saturn,Venus}";
1354                "{Pluto,Saturn}", "{Mars,Pluto,Venus}" => "{Mars,Pluto,Saturn,Venus}";
1355                "{Tweety}", "#1" => "(|,#1,{Tweety})";
1356                "{Tweety}", "{Birdie}" => "{Birdie,Tweety}";
1357            }
1358            ok!()
1359        }
1360
1361        /* DifferenceExt */
1362
1363        #[test]
1364        fn make_difference_ext_arg() -> AResult {
1365            let test = test_make_arg_f(Term::make_difference_ext_arg);
1366            macro_once! {
1367                // * 🚩模式:参数列表 ⇒ 预期词项
1368                macro test($($arg_list:tt => $expected:expr;)*) {
1369                    $( test(term!($arg_list).into(), option_term!($expected)); )*
1370                }
1371                // * ℹ️用例均源自OpenNARS实际运行
1372                ["swimmer", "bird"] => "(-,swimmer,bird)";
1373                ["mammal", "swimmer"] => "(-,mammal,swimmer)";
1374                ["bird", "swimmer"] => "(-,bird,swimmer)";
1375                ["swimmer", "animal"] => "(-,swimmer,animal)";
1376            }
1377            ok!()
1378        }
1379
1380        #[test]
1381        fn make_difference_ext() -> AResult {
1382            let test = test_make_two_f(Term::make_difference_ext);
1383            macro_once! {
1384                // * 🚩模式:参数列表 ⇒ 预期词项
1385                macro test($($term1:tt, $term2:tt => $expected:expr;)*) {
1386                    $( test(term!($term1), term!($term2), option_term!($expected)); )*
1387                }
1388                // * ℹ️用例均源自OpenNARS实际运行
1389                "(&,bird,(|,[yellow],{Birdie}))", "[with_wings]" => "(-,(&,bird,(|,[yellow],{Birdie})),[with_wings])";
1390                "(&,bird,flyer)", "[with_wings]" => "(-,(&,bird,flyer),[with_wings])";
1391                "(&,flyer,[yellow])", "[with_wings]" => "(-,(&,flyer,[yellow]),[with_wings])";
1392                "(&,flyer,{Birdie})", "[with_wings]" => "(-,(&,flyer,{Birdie}),[with_wings])";
1393                "(/,open,_,#1)", "(/,open,_,{lock1})" => "(-,(/,open,_,#1),(/,open,_,{lock1}))";
1394                "(|,[yellow],{Birdie})", "[with_wings]" => "(-,(|,[yellow],{Birdie}),[with_wings])";
1395                "(|,[yellow],{Birdie})", "bird" => "(-,(|,[yellow],{Birdie}),bird)";
1396                "(|,bird,flyer)", "[with_wings]" => "(-,(|,bird,flyer),[with_wings])";
1397                "(|,bird,swimmer)", "animal" => "(-,(|,bird,swimmer),animal)";
1398                "(|,bird,{Birdie})", "[with_wings]" => "(-,(|,bird,{Birdie}),[with_wings])";
1399                "(|,chess,competition)", "(|,competition,sport)" => "(-,(|,chess,competition),(|,competition,sport))";
1400                "(|,flyer,[with_wings])", "[yellow]" => "(-,(|,flyer,[with_wings]),[yellow])";
1401                "(|,flyer,[yellow])", "{Birdie}" => "(-,(|,flyer,[yellow]),{Birdie})";
1402                "[yellow]", "[with_wings]" => "(-,[yellow],[with_wings])";
1403                "[yellow]", "bird" => "(-,[yellow],bird)";
1404                "[yellow]", "{Birdie}" => "(-,[yellow],{Birdie})";
1405                "animal", "swimmer" => "(-,animal,swimmer)";
1406                "bird", "[with_wings]" => "(-,bird,[with_wings])";
1407                "{Birdie}", "[with_wings]" => "(-,{Birdie},[with_wings])";
1408                "{Birdie}", "flyer" => "(-,{Birdie},flyer)";
1409                "{Mars,Pluto,Venus}", "{Pluto,Saturn}" => "{Mars,Venus}";
1410            }
1411            ok!()
1412        }
1413
1414        /* DifferenceInt */
1415
1416        #[test]
1417        fn make_difference_int_arg() -> AResult {
1418            let test = test_make_arg_f(Term::make_difference_int_arg);
1419            macro_once! {
1420                // * 🚩模式:参数列表 ⇒ 预期词项
1421                macro test($($arg_list:tt => $expected:expr;)*) {
1422                    $( test(term!($arg_list).into(), option_term!($expected)); )*
1423                }
1424                // * ℹ️用例均源自OpenNARS实际运行
1425                ["(~,boy,girl)", "girl"] => "(~,(~,boy,girl),girl)";
1426                ["swimmer", "swan"] => "(~,swimmer,swan)";
1427                ["youth", "girl"] => "(~,youth,girl)";
1428                ["(|,boy,girl)", "girl"] => "(~,(|,boy,girl),girl)";
1429                ["boy", "girl"] => "(~,boy,girl)";
1430                ["(/,(*,tim,tom),tom,_)", "(/,uncle,tom,_)"] => "(~,(/,(*,tim,tom),tom,_),(/,uncle,tom,_))";
1431                ["[strong]", "girl"] => "(~,[strong],girl)";
1432            }
1433            ok!()
1434        }
1435
1436        #[test]
1437        fn make_difference_int() -> AResult {
1438            let test = test_make_two_f(Term::make_difference_int);
1439            macro_once! {
1440                // * 🚩模式:参数列表 ⇒ 预期词项
1441                macro test($($term1:tt, $term2:tt => $expected:expr;)*) {
1442                    $( test(term!($term1), term!($term2), option_term!($expected)); )*
1443                }
1444                // * ℹ️用例均源自OpenNARS实际运行
1445                "(&,bird,robin)", "tiger" => "(~,(&,bird,robin),tiger)";
1446                "(&,flyer,{Birdie})", "(&,flyer,robin)" => "(~,(&,flyer,{Birdie}),(&,flyer,robin))";
1447                "(&,flyer,{Birdie})", "robin" => "(~,(&,flyer,{Birdie}),robin)";
1448                "(/,(*,tim,tom),tom,_)", "(/,uncle,tom,_)" => "(~,(/,(*,tim,tom),tom,_),(/,uncle,tom,_))";
1449                "(/,(*,tim,tom),tom,_)", "tim" => "(~,(/,(*,tim,tom),tom,_),tim)";
1450                "(/,open,_,lock)", "{key1}" => "(~,(/,open,_,lock),{key1})";
1451                "(|,bird,robin)", "tiger" => "(~,(|,bird,robin),tiger)";
1452                "(|,flyer,[with_wings],{Birdie})", "robin" => "(~,(|,flyer,[with_wings],{Birdie}),robin)";
1453                "(|,flyer,{Birdie})", "robin" => "(~,(|,flyer,{Birdie}),robin)";
1454                "(~,boy,girl)", "girl" => "(~,(~,boy,girl),girl)";
1455                "[strong]", "girl" => "(~,[strong],girl)";
1456                "animal", "bird" => "(~,animal,bird)";
1457                "bird", "#1" => "(~,bird,#1)";
1458                "bird", "(|,robin,tiger)" => "(~,bird,(|,robin,tiger))";
1459                "{Birdie}", "(|,flyer,robin)" => "(~,{Birdie},(|,flyer,robin))";
1460                "{Birdie}", "robin" => "(~,{Birdie},robin)";
1461                "{Tweety}", "(&,flyer,robin)" => "(~,{Tweety},(&,flyer,robin))";
1462                "{Tweety}", "(|,robin,[yellow],{Birdie})" => "(~,{Tweety},(|,robin,[yellow],{Birdie}))";
1463                "{lock1}", "#1" => "(~,{lock1},#1)";
1464            }
1465            ok!()
1466        }
1467
1468        /* ImageExt */
1469
1470        #[test]
1471        fn make_image_ext_vec() -> AResult {
1472            let test = test_make_arg_f(Term::make_image_ext_vec);
1473            macro_once! {
1474                // * 🚩模式:参数列表 ⇒ 预期词项
1475                macro test($($arg_list:tt => $expected:expr;)*) {
1476                    $( test(term!($arg_list).into(), option_term!($expected)); )*
1477                }
1478                ["reaction", "_", "base"] => "(/,reaction,_,base)";
1479                ["reaction", "acid", "_"] => "(/,reaction,acid,_)";
1480                ["neutralization", "_", "base"] => "(/,neutralization,_,base)";
1481                ["open", "$120", "_"] => "(/,open,$120,_)";
1482            }
1483            ok!()
1484        }
1485
1486        #[test]
1487        fn make_image_ext_from_product() -> AResult {
1488            let test = test_make_image_from_product_f(Term::make_image_ext_from_product);
1489            macro_once! {
1490                // * 🚩模式:参数列表 ⇒ 预期词项
1491                macro test($($product:tt, $relation:tt, $index:tt => $expected:expr;)*) {
1492                    $( test( term!($product), term!($relation), $index, term!($expected) ); )*
1493                }
1494                // * ℹ️用例均源自OpenNARS实际运行
1495                "(*,$1,sunglasses)", "own",                1 => "(/,own,$1,_)";
1496                "(*,bird,plant)",    "?1",                 0 => "(/,?1,_,plant)";
1497                "(*,bird,plant)",    "?1",                 1 => "(/,?1,bird,_)";
1498                "(*,robin,worms)",   "food",               1 => "(/,food,robin,_)";
1499                "(*,CAT,eat,fish)",  "R",                  0 => "(/,R,_,eat,fish)";
1500                "(*,CAT,eat,fish)",  "R",                  1 => "(/,R,CAT,_,fish)";
1501                "(*,CAT,eat,fish)",  "R",                  2 => "(/,R,CAT,eat,_)";
1502                "(*,b,a)",           "(*,b,(/,like,b,_))", 1 => "(/,like,b,_)";
1503                "(*,a,b)",           "(*,(/,like,b,_),b)", 0 => "(/,like,b,_)";
1504                // 特别替换
1505                r"(*,(/,like,b,_),b)",                   r"(*,a,b)",                            0 => r"a";
1506                r"(*,(&,key,(/,open,_,{lock1})),lock1)", r"(*,{key1},lock1)",                   0 => r"{key1}";
1507                r"(*,(\,reaction,_,soda),base)",         r"(*,(\,neutralization,_,soda),base)", 0 => r"(\,neutralization,_,soda)";
1508                r"(*,(&,key,(/,open,_,{lock1})),lock)",  r"(*,{key1},lock)",                    0 => r"{key1}";
1509                r"(*,b,(/,like,b,_))",                   r"(*,b,a)",                            1 => r"a";
1510                r"(*,(/,like,_,a),a)",                   r"(*,b,a)",                            0 => r"b";
1511            }
1512            ok!()
1513        }
1514
1515        #[test]
1516        fn make_image_ext_from_image() -> AResult {
1517            let test = test_make_image_from_image_f(Term::make_image_ext_from_image);
1518            macro_once! {
1519                // * 🚩模式:参数列表 ⇒ 预期词项
1520                macro test($($image:tt, $component:tt, $index:tt => [$expected:expr, $expected_outer:expr];)*) {
1521                    $( test( term!($image), term!($component), $index, term!($expected), term!($expected_outer) ); )*
1522                }
1523                // * 📌特殊用例:误打误撞占位符
1524                "(/,open,{key1},_)",   "lock",   1 => ["(/,open,{key1},_)", "lock"];
1525                // * ℹ️用例均源自OpenNARS实际运行
1526                "(/,open,{key1},_)",   "lock",   0 => ["(/,open,_,lock)", "{key1}"];
1527                "(/,uncle,_,tom)",     "tim",    1 => ["(/,uncle,tim,_)", "tom"];
1528                "(/,open,{key1},_)",   "$2",     0 => ["(/,open,_,$2)", "{key1}"];
1529                "(/,open,{key1},_)",   "#1",     0 => ["(/,open,_,#1)", "{key1}"];
1530                "(/,like,_,a)",        "b",      1 => ["(/,like,b,_)", "a"];
1531                "(/,like,b,_)",        "a",      0 => ["(/,like,_,a)", "b"];
1532            }
1533            ok!()
1534        }
1535
1536        /* ImageInt */
1537
1538        #[test]
1539        fn make_image_int_vec() -> AResult {
1540            let test = test_make_arg_f(Term::make_image_int_vec);
1541            macro_once! {
1542                // * 🚩模式:参数列表 ⇒ 预期词项
1543                macro test($($arg_list:tt => $expected:expr;)*) {
1544                    $( test(term!($arg_list).into(), option_term!($expected)); )*
1545                }
1546                // * ℹ️用例均源自OpenNARS实际运行
1547                ["reaction", "_", "base"]       => r"(\,reaction,_,base)";
1548                ["reaction", "acid", "_"]       => r"(\,reaction,acid,_)";
1549                ["neutralization", "_", "base"] => r"(\,neutralization,_,base)";
1550                ["open", "$120", "_"]           => r"(\,open,$120,_)";
1551            }
1552            ok!()
1553        }
1554
1555        #[test]
1556        fn make_image_int_from_product() -> AResult {
1557            let test = test_make_image_from_product_f(Term::make_image_int_from_product);
1558            macro_once! {
1559                // * 🚩模式:参数列表 ⇒ 预期词项
1560                macro test($($product:tt, $relation:tt, $index:tt => $expected:expr;)*) {
1561                    $( test( term!($product), term!($relation), $index, term!($expected) ); )*
1562                }
1563                // * ℹ️用例均源自OpenNARS实际运行
1564                r"(*,(/,num,_))",                       "#1",                0 => r"(\,#1,_)";
1565                r"(*,(\,reaction,_,soda),base)",        "neutralization",    1 => r"(\,neutralization,(\,reaction,_,soda),_)";
1566                r"(*,(\,reaction,_,soda),base)",        "neutralization",    0 => r"(\,neutralization,_,base)";
1567                r"(*,(/,num,_))",                       "(*,num)",           0 => r"(\,(*,num),_)";
1568                r"(*,acid,soda)",                       "reaction",          0 => r"(\,reaction,_,soda)";
1569                r"(*,(*,num))",                         "(*,(*,(/,num,_)))", 0 => r"(\,(*,(*,(/,num,_))),_)";
1570                r"(*,(*,(*,num)))",                     "(*,(*,(*,0)))",     0 => r"(\,(*,(*,(*,0))),_)";
1571                r"(*,(\,reaction,_,soda),base)",        "#1",                1 => r"(\,#1,(\,reaction,_,soda),_)";
1572                r"(*,(*,num))",                         "(*,(*,0))",         0 => r"(\,(*,(*,0)),_)";
1573                r"(*,acid,base)",                       "reaction",          0 => r"(\,reaction,_,base)";
1574                r"(*,b,(/,like,b,_))",                  "(*,b,a)",           0 => r"(\,(*,b,a),_,(/,like,b,_))";
1575                r"(*,(\,reaction,_,soda),base)",        "#1",                0 => r"(\,#1,_,base)";
1576                r"(*,(*,(/,num,_)))",                   "(*,(*,0))",         0 => r"(\,(*,(*,0)),_)";
1577                r"(*,(/,num,_))",                       "(*,0)",             0 => r"(\,(*,0),_)";
1578                r"(*,(/,num,_))",                       "$1",                0 => r"(\,$1,_)";
1579                r"(*,num)",                             "(*,0)",             0 => r"(\,(*,0),_)";
1580                r"(*,acid,soda)",                       "reaction",          1 => r"(\,reaction,acid,_)";
1581                r"(*,(/,like,_,a),a)",                  "(*,b,a)",           1 => r"(\,(*,b,a),(/,like,_,a),_)";
1582                r"(*,acid,base)",                       "reaction",          1 => r"(\,reaction,acid,_)";
1583                r"(*,(&,key,(/,open,_,{lock1})),lock)", "(*,{key1},lock)",   1 => r"(\,(*,{key1},lock),(&,key,(/,open,_,{lock1})),_)";
1584                r"(*,(/,like,b,_),b)",                  "(*,a,b)",           1 => r"(\,(*,a,b),(/,like,b,_),_)";
1585                // 特别替换
1586                r"(*,(\,reaction,_,soda),base)",         r"(*,(\,reaction,_,soda),soda)",       1 => r"soda";
1587                r"(*,(\,reaction,_,soda),base)",         r"(*,acid,base)",                      0 => r"acid";
1588                r"(*,acid,(\,neutralization,acid,_))",   r"(*,acid,(\,reaction,acid,_))",       1 => r"(\,reaction,acid,_)";
1589                r"(*,(&,key,(/,open,_,{lock1})),lock)",  r"(*,{key1},lock)",                    0 => r"{key1}";
1590                r"(*,(\,neutralization,_,soda),base)",   r"(*,(\,reaction,_,soda),base)",       0 => r"(\,reaction,_,soda)";
1591                r"(*,(/,open,_,#1),{lock1})",            r"(*,{key1},{lock1})",                 0 => r"{key1}";
1592                r"(*,key,lock)",                         r"(*,{key1},lock)",                    0 => r"{key1}";
1593                r"(*,acid,(\,reaction,acid,_))",         r"(*,acid,soda)",                      1 => r"soda";
1594                r"(*,(|,key,(/,open,_,{lock1})),lock1)", r"(*,{key1},lock1)",                   0 => r"{key1}";
1595                r"(*,(&,key,(/,open,_,{lock1})),lock1)", r"(*,{key1},lock1)",                   0 => r"{key1}";
1596            }
1597            ok!()
1598        }
1599
1600        #[test]
1601        fn make_image_int_from_image() -> AResult {
1602            let test = test_make_image_from_image_f(Term::make_image_int_from_image);
1603            macro_once! {
1604                // * 🚩模式:参数列表 ⇒ 预期词项
1605                macro test($($image:tt, $component:tt, $index:tt => [$expected:expr, $expected_outer:expr];)*) {
1606                    $( test( term!($image), term!($component), $index, term!($expected), term!($expected_outer) ); )*
1607                }
1608                // * 📌特殊用例:误打误撞占位符
1609                r"(\,R,_,eat,fish)",           "cat",                       0 => [r"(\,R,_,eat,fish)", "cat"];
1610                // * ℹ️用例均源自OpenNARS实际运行
1611                r"(\,R,_,eat,fish)",           "cat",                       2 => [r"(\,R,cat,eat,_)", "fish"];
1612                r"(\,reaction,acid,_)",        "soda",                      0 => [r"(\,reaction,_,soda)", "acid"];
1613                r"(\,R,_,eat,fish)",          r"(\,REPRESENT,_,$1)",        2 => [r"(\,R,(\,REPRESENT,_,$1),eat,_)", "fish"];
1614                r"(\,neutralization,_,soda)",  "acid",                      1 => [r"(\,neutralization,acid,_)", "soda"];
1615                r"(\,neutralization,acid,_)",  "$1",                        0 => [r"(\,neutralization,_,$1)", "acid"];
1616                r"(\,REPRESENT,_,$1)",        r"(\,R,_,eat,fish)",          1 => [r"(\,REPRESENT,(\,R,_,eat,fish),_)", "$1"];
1617                r"(\,neutralization,acid,_)",  "soda",                      0 => [r"(\,neutralization,_,soda)", "acid"];
1618                r"(\,neutralization,acid,_)",  "?1",                        0 => [r"(\,neutralization,_,?1)", "acid"];
1619                r"(\,reaction,acid,_)",       r"(\,neutralization,acid,_)", 0 => [r"(\,reaction,_,(\,neutralization,acid,_))", "acid"];
1620                r"(\,REPRESENT,_,CAT)",        "(/,R,_,eat,fish)",          1 => [r"(\,REPRESENT,(/,R,_,eat,fish),_)", "CAT"];
1621                r"(\,R,_,eat,fish)",          r"(\,REPRESENT,_,$1)",        1 => [r"(\,R,(\,REPRESENT,_,$1),_,fish)", "eat"];
1622                r"(\,R,_,eat,fish)",           "cat",                       1 => [r"(\,R,cat,_,fish)", "eat"];
1623                r"(\,reaction,_,soda)",        "acid",                      1 => [r"(\,reaction,acid,_)", "soda"];
1624                r"(\,reaction,_,base)",       r"(\,reaction,_,soda)",       1 => [r"(\,reaction,(\,reaction,_,soda),_)", "base"];
1625                r"(\,neutralization,acid,_)",  "#1",                        0 => [r"(\,neutralization,_,#1)", "acid"];
1626                r"(\,neutralization,acid,_)",  "base",                      0 => [r"(\,neutralization,_,base)", "acid"];
1627                r"(\,reaction,_,base)",        "acid",                      1 => [r"(\,reaction,acid,_)", "base"];
1628                r"(\,neutralization,acid,_)",  "(/,reaction,acid,_)",       0 => [r"(\,neutralization,_,(/,reaction,acid,_))", "acid"];
1629            }
1630            ok!()
1631        }
1632    }
1633
1634    mod compound {
1635        use super::*;
1636
1637        fn test_make_term_with_identifier_f(
1638            make: fn(&str, Vec<Term>) -> Option<Term>,
1639        ) -> impl Fn(&str, Vec<Term>, Option<Term>) {
1640            move |identifier, terms, expected| {
1641                let terms_str = terms
1642                    .iter()
1643                    .map(|t| format!("\"{t}\""))
1644                    .collect::<Vec<_>>()
1645                    .join(", ");
1646                let out = make(identifier, terms);
1647                assert_eq!(
1648                    out,
1649                    expected,
1650                    "{identifier:?}, {terms_str} => {} != {}",
1651                    format_option_term(&out),
1652                    format_option_term(&expected),
1653                );
1654            }
1655        }
1656
1657        #[test]
1658        fn make_compound_term_from_identifier() -> AResult {
1659            fn make(identifier: &str, terms: Vec<Term>) -> Option<Term> {
1660                Term::make_compound_term_from_identifier(identifier, terms)
1661            }
1662            let test = test_make_term_with_identifier_f(make);
1663            macro_once! {
1664                // * 🚩模式:参数列表 ⇒ 预期词项
1665                macro test($($identifier:tt, $terms:tt => $expected:tt;)*) {
1666                    $( test($identifier, term!($terms).into(), option_term!($expected)); )*
1667                }
1668                // * ℹ️用例均源自OpenNARS实际运行
1669                "&", ["(&,robin,{Tweety})", "{Birdie}"] => "(&,robin,{Birdie},{Tweety})";
1670                "&", ["(/,neutralization,_,(\\,neutralization,acid,_))", "acid"] => "(&,acid,(/,neutralization,_,(\\,neutralization,acid,_)))";
1671                "&", ["(/,neutralization,_,base)", "(/,reaction,_,base)"] => "(&,(/,neutralization,_,base),(/,reaction,_,base))";
1672                "&", ["(/,neutralization,_,base)", "acid"] => "(&,acid,(/,neutralization,_,base))";
1673                "&", ["(/,open,_,{lock1})", "(/,open,_,lock)"] => "(&,(/,open,_,lock),(/,open,_,{lock1}))";
1674                "&", ["(\\,REPRESENT,_,CAT)", "(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)"] => "(&,(\\,REPRESENT,_,CAT),(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))";
1675                "&", ["(\\,reaction,_,soda)", "(\\,neutralization,_,base)"] => "(&,(\\,neutralization,_,base),(\\,reaction,_,soda))";
1676                "&", ["(|,(/,open,_,lock1),(/,open,_,{lock1}))", "(/,open,_,lock)"] => "(&,(/,open,_,lock),(|,(/,open,_,lock1),(/,open,_,{lock1})))";
1677                "&", ["(|,bird,{Tweety})", "(|,bird,{Birdie})"] => "(&,(|,bird,{Birdie}),(|,bird,{Tweety}))";
1678                "&", ["(|,key,(/,open,_,{lock1}))", "(/,open,_,lock)"] => "(&,(/,open,_,lock),(|,key,(/,open,_,{lock1})))";
1679                "&", ["acid", "(/,reaction,_,base)"] => "(&,acid,(/,reaction,_,base))";
1680                "&", ["acid", "(\\,neutralization,_,base)"] => "(&,acid,(\\,neutralization,_,base))";
1681                "&", ["animal", "(&,robin,swan)"] => "(&,animal,robin,swan)";
1682                "&", ["animal", "(|,animal,swimmer)"] => "(&,animal,(|,animal,swimmer))";
1683                "&", ["animal", "gull"] => "(&,animal,gull)";
1684                "&", ["bird", "robin", "{Birdie}", "(|,[yellow],{Birdie})"] => "(&,bird,robin,{Birdie},(|,[yellow],{Birdie}))";
1685                "&", ["flyer", "[with_wings]"] => "(&,flyer,[with_wings])";
1686                "&", ["flyer", "{Birdie}", "(|,[with_wings],{Birdie})"] => "(&,flyer,{Birdie},(|,[with_wings],{Birdie}))";
1687                "&", ["flyer", "{Birdie}"] => "(&,flyer,{Birdie})";
1688                "&", ["key", "(/,open,_,{lock1})"] => "(&,key,(/,open,_,{lock1}))";
1689                "&", ["neutralization", "(*,(\\,neutralization,_,base),base)"] => "(&,neutralization,(*,(\\,neutralization,_,base),base))";
1690                "&", ["neutralization", "(*,acid,(/,reaction,acid,_))"] => "(&,neutralization,(*,acid,(/,reaction,acid,_)))";
1691                "&", ["neutralization", "(*,acid,base)"] => "(&,neutralization,(*,acid,base))";
1692                "&", ["num", "(/,num,_)"] => "(&,num,(/,num,_))";
1693                "&", ["{Birdie}", "(|,flyer,{Tweety})"] => "(&,{Birdie},(|,flyer,{Tweety}))";
1694                "&", ["{Birdie}", "{Tweety}"] => None;
1695                "&&", ["<robin --> [chirping]>", "<robin --> [flying]>"] => "(&&,<robin --> [chirping]>,<robin --> [flying]>)";
1696                "&&", ["<robin --> [chirping]>"] => "<robin --> [chirping]>";
1697                "&&", ["<robin --> bird>", "(||,(&&,<robin --> [flying]>,<robin --> [with_wings]>),<robin --> bird>)"] => "(&&,<robin --> bird>,(||,(&&,<robin --> [flying]>,<robin --> [with_wings]>),<robin --> bird>))";
1698                "&&", ["<robin --> bird>", "<robin --> [flying]>", "<robin --> [with_wings]>"] => "(&&,<robin --> bird>,<robin --> [flying]>,<robin --> [with_wings]>)";
1699                "&&", ["<robin --> bird>", "<robin --> [flying]>"] => "(&&,<robin --> bird>,<robin --> [flying]>)";
1700                "&&", ["<robin --> bird>"] => "<robin --> bird>";
1701                "&&", ["<robin --> flyer>", "<(*,robin,worms) --> food>"] => "(&&,<robin --> flyer>,<(*,robin,worms) --> food>)";
1702                "&&", ["<robin --> flyer>", "<robin --> bird>", "<(*,robin,worms) --> food>"] => "(&&,<robin --> bird>,<robin --> flyer>,<(*,robin,worms) --> food>)";
1703                "&&", ["<robin --> flyer>", "<robin --> bird>", "<worms --> (/,food,robin,_)>"] => "(&&,<robin --> bird>,<robin --> flyer>,<worms --> (/,food,robin,_)>)";
1704                "&&", ["<robin --> flyer>", "<robin --> bird>"] => "(&&,<robin --> bird>,<robin --> flyer>)";
1705                "&&", ["<robin --> flyer>", "<worms --> (/,food,robin,_)>"] => "(&&,<robin --> flyer>,<worms --> (/,food,robin,_)>)";
1706                "*", ["(&,key,(/,open,_,{lock1}))", "lock"] => "(*,(&,key,(/,open,_,{lock1})),lock)";
1707                "*", ["(&,num,(/,(*,(/,num,_)),_))"] => "(*,(&,num,(/,(*,(/,num,_)),_)))";
1708                "*", ["(*,num)"] => "(*,(*,num))";
1709                "*", ["(/,(*,(/,num,_)),_)"] => "(*,(/,(*,(/,num,_)),_))";
1710                "*", ["(/,(/,num,_),_)"] => "(*,(/,(/,num,_),_))";
1711                "*", ["(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>)", "<(*,CAT,FISH) --> FOOD>"] => "(*,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),<(*,CAT,FISH) --> FOOD>)";
1712                "*", ["(/,num,_)"] => "(*,(/,num,_))";
1713                "*", ["(/,open,_,lock)", "lock"] => "(*,(/,open,_,lock),lock)";
1714                "*", ["(/,open,_,lock)", "{lock1}"] => "(*,(/,open,_,lock),{lock1})";
1715                "*", ["(/,open,_,{lock1})", "lock"] => "(*,(/,open,_,{lock1}),lock)";
1716                "*", ["(/,open,_,{lock1})", "{lock1}"] => "(*,(/,open,_,{lock1}),{lock1})";
1717                "*", ["(\\,neutralization,_,base)", "base"] => "(*,(\\,neutralization,_,base),base)";
1718                "*", ["(|,(/,open,_,lock1),(/,open,_,{lock1}))", "lock1"] => "(*,(|,(/,open,_,lock1),(/,open,_,{lock1})),lock1)";
1719                "*", ["(|,key,(/,open,_,{lock1}))", "lock"] => "(*,(|,key,(/,open,_,{lock1})),lock)";
1720                "*", ["0"] => "(*,0)";
1721                "*", ["a", "b"] => "(*,a,b)";
1722                "*", ["acid", "(&,soda,(/,neutralization,acid,_))"] => "(*,acid,(&,soda,(/,neutralization,acid,_)))";
1723                "*", ["acid", "(/,neutralization,acid,_)"] => "(*,acid,(/,neutralization,acid,_))";
1724                "*", ["acid", "(\\,neutralization,acid,_)"] => "(*,acid,(\\,neutralization,acid,_))";
1725                "*", ["acid", "(|,base,(\\,reaction,acid,_))"] => "(*,acid,(|,base,(\\,reaction,acid,_)))";
1726                "*", ["key", "{lock1}"] => "(*,key,{lock1})";
1727                "*", ["{key1}", "lock1"] => "(*,{key1},lock1)";
1728                "[]", ["bright"] => "[bright]";
1729                "{}", ["Birdie"] => "{Birdie}";
1730                "{}", ["Mars", "Venus"] => "{Mars,Venus}";
1731                "|", ["(&,animal,gull)", "swimmer"] => "(|,swimmer,(&,animal,gull))";
1732                "|", ["(&,flyer,{Birdie})", "(|,[yellow],{Birdie})"] => "(|,[yellow],{Birdie},(&,flyer,{Birdie}))";
1733                "|", ["(&,flyer,{Birdie})", "{Birdie}"] => "(|,{Birdie},(&,flyer,{Birdie}))";
1734                "|", ["(/,neutralization,_,base)", "(/,reaction,_,(\\,neutralization,acid,_))"] => "(|,(/,neutralization,_,base),(/,reaction,_,(\\,neutralization,acid,_)))";
1735                "|", ["(/,neutralization,_,base)", "(/,reaction,_,base)"] => "(|,(/,neutralization,_,base),(/,reaction,_,base))";
1736                "|", ["(/,neutralization,_,base)", "acid"] => "(|,acid,(/,neutralization,_,base))";
1737                "|", ["(/,neutralization,acid,_)", "(\\,neutralization,acid,_)"] => "(|,(/,neutralization,acid,_),(\\,neutralization,acid,_))";
1738                "|", ["(/,num,_)", "0"] => "(|,0,(/,num,_))";
1739                "|", ["(/,open,_,{lock1})", "(/,open,_,lock)"] => "(|,(/,open,_,lock),(/,open,_,{lock1}))";
1740                "|", ["(\\,REPRESENT,_,CAT)", "(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)"] => "(|,(\\,REPRESENT,_,CAT),(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))";
1741                "|", ["(|,key,(/,open,_,{lock1}))", "(/,open,_,lock)"] => "(|,key,(/,open,_,lock),(/,open,_,{lock1}))";
1742                "|", ["(~,boy,girl)", "(~,youth,girl)"] => "(|,(~,boy,girl),(~,youth,girl))";
1743                "|", ["[with_wings]", "(|,flyer,{Tweety})", "{Birdie}"] => "(|,flyer,[with_wings],{Birdie},{Tweety})";
1744                "|", ["[with_wings]", "flyer", "{Birdie}"] => "(|,flyer,[with_wings],{Birdie})";
1745                "|", ["[with_wings]", "{Birdie}", "(|,[with_wings],{Birdie})"] => "(|,[with_wings],{Birdie})";
1746                "|", ["[with_wings]", "{Tweety}", "{Birdie}"] => "(|,[with_wings],{Birdie},{Tweety})";
1747                "|", ["[yellow]", "[with_wings]"] => None;
1748                "|", ["[yellow]", "bird"] => "(|,bird,[yellow])";
1749                "|", ["[yellow]", "{Tweety}"] => "(|,[yellow],{Tweety})";
1750                "|", ["acid", "(/,reaction,_,base)"] => "(|,acid,(/,reaction,_,base))";
1751                "|", ["acid", "(\\,neutralization,_,base)"] => "(|,acid,(\\,neutralization,_,base))";
1752                "|", ["animal", "robin"] => "(|,animal,robin)";
1753                "|", ["bird", "[with_wings]"] => "(|,bird,[with_wings])";
1754                "|", ["bird", "flyer", "{Birdie}"] => "(|,bird,flyer,{Birdie})";
1755                "|", ["bird", "{Birdie}"] => "(|,bird,{Birdie})";
1756                "|", ["bird", "{Tweety}", "{Birdie}"] => "(|,bird,{Birdie},{Tweety})";
1757                "|", ["boy", "(~,youth,girl)"] => "(|,boy,(~,youth,girl))";
1758                "|", ["chess", "(|,chess,sport)"] => "(|,chess,sport)";
1759                "|", ["flyer", "(&,flyer,{Birdie})", "{Birdie}"] => "(|,flyer,{Birdie},(&,flyer,{Birdie}))";
1760                "|", ["flyer", "(&,flyer,{Birdie})"] => "(|,flyer,(&,flyer,{Birdie}))";
1761                "|", ["flyer", "(|,flyer,{Tweety})", "{Birdie}"] => "(|,flyer,{Birdie},{Tweety})";
1762                "|", ["flyer", "[yellow]", "{Birdie}"] => "(|,flyer,[yellow],{Birdie})";
1763                "|", ["flyer", "{Birdie}", "(&,bird,(|,[yellow],{Birdie}))"] => "(|,flyer,{Birdie},(&,bird,(|,[yellow],{Birdie})))";
1764                "|", ["flyer", "{Birdie}", "(&,flyer,{Birdie})"] => "(|,flyer,{Birdie},(&,flyer,{Birdie}))";
1765                "|", ["key", "(/,open,_,{lock1})"] => "(|,key,(/,open,_,{lock1}))";
1766                "|", ["neutralization", "(*,acid,(\\,neutralization,acid,_))"] => "(|,neutralization,(*,acid,(\\,neutralization,acid,_)))";
1767                "|", ["neutralization", "(*,acid,base)"] => "(|,neutralization,(*,acid,base))";
1768                "|", ["robin", "(|,flyer,{Tweety})", "{Birdie}"] => "(|,flyer,robin,{Birdie},{Tweety})";
1769                "|", ["tiger", "(|,animal,swimmer)"] => "(|,animal,swimmer,tiger)";
1770                "|", ["{Birdie}", "{Tweety}"] => "{Birdie,Tweety}";
1771                "|", ["{Tweety}", "{Birdie}", "(&,flyer,{Birdie})"] => "(|,(&,flyer,{Birdie}),{Birdie,Tweety})";
1772                "~", ["(/,(*,tim,tom),tom,_)", "(/,uncle,tom,_)"] => "(~,(/,(*,tim,tom),tom,_),(/,uncle,tom,_))";
1773                "~", ["(|,boy,girl)", "girl"] => "(~,(|,boy,girl),girl)";
1774                "~", ["(~,boy,girl)", "girl"] => "(~,(~,boy,girl),girl)";
1775                "~", ["[strong]", "girl"] => "(~,[strong],girl)";
1776                "~", ["boy", "girl"] => "(~,boy,girl)";
1777            }
1778            ok!()
1779        }
1780
1781        #[test]
1782        fn make_compound_term() -> AResult {
1783            fn test(template: Term, terms: Vec<Term>, expected: Option<Term>) {
1784                let terms_str = terms
1785                    .iter()
1786                    .map(|t| format!("\"{t}\""))
1787                    .collect::<Vec<_>>()
1788                    .join(", ");
1789                let out = Term::make_compound_term(
1790                    template.as_compound().expect("模板不是复合词项!"),
1791                    terms,
1792                );
1793                assert_eq!(
1794                    out,
1795                    expected,
1796                    "\"{template}\", {terms_str} => {} != {}",
1797                    format_option_term(&out),
1798                    format_option_term(&expected),
1799                );
1800            }
1801            macro_once! {
1802                // * 🚩模式:参数列表 ⇒ 预期词项
1803                macro test($($template:tt, $terms:tt => $expected:tt;)*) {
1804                    $(
1805                        test(
1806                            term!($template),
1807                            term!($terms).into(),
1808                            option_term!($expected),
1809                        );
1810                    )*
1811                }
1812                // * ℹ️用例均源自OpenNARS实际运行
1813                "(&&,<robin --> [chirping]>,<robin --> [flying]>)", ["<robin --> [chirping]>"] => "<robin --> [chirping]>";
1814                "(&&,<robin --> [chirping]>,<robin --> [flying]>)", ["<robin --> bird>", "<robin --> [flying]>"] => "(&&,<robin --> bird>,<robin --> [flying]>)";
1815                "(&&,<robin --> [chirping]>,<robin --> [flying]>,<robin --> [with_wings]>)", ["<robin --> [chirping]>", "<robin --> [flying]>"] => "(&&,<robin --> [chirping]>,<robin --> [flying]>)";
1816                "(&&,<robin --> [chirping]>,<robin --> [flying]>,<robin --> [with_wings]>)", ["<robin --> bird>", "<robin --> [flying]>", "<robin --> [with_wings]>"] => "(&&,<robin --> bird>,<robin --> [flying]>,<robin --> [with_wings]>)";
1817                "(&&,<robin --> [chirping]>,<robin --> [with_wings]>)", ["<robin --> [chirping]>", "<robin --> bird>"] => "(&&,<robin --> bird>,<robin --> [chirping]>)";
1818                "(&&,<robin --> bird>,<robin --> [flying]>)", ["<robin --> [flying]>"] => "<robin --> [flying]>";
1819                "(&&,<robin --> bird>,<robin --> [flying]>)", ["<robin --> bird>"] => "<robin --> bird>";
1820                "(&&,<robin --> bird>,<robin --> [flying]>,<robin --> [with_wings]>)", ["<robin --> [flying]>", "<robin --> [with_wings]>"] => "(&&,<robin --> [flying]>,<robin --> [with_wings]>)";
1821                "(&&,<robin --> bird>,<robin --> [flying]>,<robin --> [with_wings]>)", ["<robin --> bird>", "<robin --> [flying]>", "<robin --> bird>"] => "(&&,<robin --> bird>,<robin --> [flying]>)";
1822                "(&&,<robin --> bird>,<robin --> [flying]>,<robin --> [with_wings]>)", ["<robin --> bird>", "<robin --> [flying]>"] => "(&&,<robin --> bird>,<robin --> [flying]>)";
1823                "(&&,<robin --> bird>,<robin --> [living]>)", ["<robin --> bird>", "(||,(&&,<robin --> [flying]>,<robin --> [with_wings]>),<robin --> bird>)"] => "(&&,<robin --> bird>,(||,(&&,<robin --> [flying]>,<robin --> [with_wings]>),<robin --> bird>))";
1824                "(&&,<robin --> bird>,<robin --> [living]>)", ["<robin --> bird>", "<robin --> [flying]>", "<robin --> [with_wings]>"] => "(&&,<robin --> bird>,<robin --> [flying]>,<robin --> [with_wings]>)";
1825                "(&&,<robin --> bird>,<robin --> [living]>)", ["<robin --> bird>", "<robin --> [flying]>"] => "(&&,<robin --> bird>,<robin --> [flying]>)";
1826                "(&&,<robin --> bird>,<robin --> [living]>)", ["<robin --> bird>", "<robin --> bird>", "<robin --> [flying]>"] => "(&&,<robin --> bird>,<robin --> [flying]>)";
1827                "(&&,<robin --> flyer>,<(*,robin,worms) --> food>)", ["<robin --> flyer>", "<worms --> (/,food,robin,_)>"] => "(&&,<robin --> flyer>,<worms --> (/,food,robin,_)>)";
1828                "(&&,<robin --> flyer>,<robin --> [chirping]>)", ["<robin --> flyer>", "<robin --> bird>"] => "(&&,<robin --> bird>,<robin --> flyer>)";
1829                "(&&,<robin --> flyer>,<robin --> [chirping]>,<(*,robin,worms) --> food>)", ["<robin --> flyer>", "<(*,robin,worms) --> food>"] => "(&&,<robin --> flyer>,<(*,robin,worms) --> food>)";
1830                "(&&,<robin --> flyer>,<robin --> [chirping]>,<(*,robin,worms) --> food>)", ["<robin --> flyer>", "<robin --> bird>", "<(*,robin,worms) --> food>"] => "(&&,<robin --> bird>,<robin --> flyer>,<(*,robin,worms) --> food>)";
1831                "(&&,<robin --> flyer>,<robin --> [chirping]>,<worms --> (/,food,robin,_)>)", ["<robin --> flyer>", "<robin --> bird>", "<worms --> (/,food,robin,_)>"] => "(&&,<robin --> bird>,<robin --> flyer>,<worms --> (/,food,robin,_)>)";
1832                "(&&,<robin --> flyer>,<robin --> [chirping]>,<worms --> (/,food,robin,_)>)", ["<robin --> flyer>", "<worms --> (/,food,robin,_)>"] => "(&&,<robin --> flyer>,<worms --> (/,food,robin,_)>)";
1833                "(&&,<robin --> flyer>,<worms --> (/,food,robin,_)>)", ["<robin --> flyer>", "<(*,robin,worms) --> food>"] => "(&&,<robin --> flyer>,<(*,robin,worms) --> food>)";
1834                "(&,(/,neutralization,_,(\\,neutralization,acid,_)),(/,reaction,_,base))", ["(/,neutralization,_,(\\,neutralization,acid,_))", "acid"] => "(&,acid,(/,neutralization,_,(\\,neutralization,acid,_)))";
1835                "(&,(/,neutralization,_,(\\,neutralization,acid,_)),(/,reaction,_,base))", ["acid", "(/,reaction,_,base)"] => "(&,acid,(/,reaction,_,base))";
1836                "(&,(/,neutralization,_,base),(/,reaction,_,soda))", ["(/,neutralization,_,base)", "(/,reaction,_,base)"] => "(&,(/,neutralization,_,base),(/,reaction,_,base))";
1837                "(&,(/,neutralization,_,base),(/,reaction,_,soda))", ["(/,neutralization,_,base)", "acid"] => "(&,acid,(/,neutralization,_,base))";
1838                "(&,(/,neutralization,_,soda),(/,reaction,_,base))", ["acid", "(/,reaction,_,base)"] => "(&,acid,(/,reaction,_,base))";
1839                "(&,(/,open,_,lock),(/,open,_,{lock1}))", ["(/,open,_,lock)", "key"] => "(&,key,(/,open,_,lock))";
1840                "(&,(\\,REPRESENT,_,CAT),(\\,(\\,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))", ["(\\,REPRESENT,_,CAT)", "(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)"] => "(&,(\\,REPRESENT,_,CAT),(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))";
1841                "(&,(\\,REPRESENT,_,CAT),(\\,(\\,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))", ["cat", "(\\,(\\,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)"] => "(&,cat,(\\,(\\,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))";
1842                "(&,(\\,reaction,_,soda),(|,acid,(\\,reaction,_,base)))", ["(\\,reaction,_,soda)", "(\\,neutralization,_,base)"] => "(&,(\\,neutralization,_,base),(\\,reaction,_,soda))";
1843                "(&,(|,bird,flyer),(|,bird,{Birdie}))", ["(|,bird,{Tweety})", "(|,bird,{Birdie})"] => "(&,(|,bird,{Birdie}),(|,bird,{Tweety}))";
1844                "(&,(|,bird,flyer),(|,bird,{Birdie}))", ["{Tweety}", "(|,bird,{Birdie})"] => "(&,{Tweety},(|,bird,{Birdie}))";
1845                "(&,[with_wings],{Birdie})", ["(&,robin,{Tweety})", "{Birdie}"] => "(&,robin,{Birdie},{Tweety})";
1846                "(&,[with_wings],{Birdie})", ["flyer", "{Birdie}"] => "(&,flyer,{Birdie})";
1847                "(&,[with_wings],{Birdie})", ["{Tweety}", "{Birdie}"] => None;
1848                "(&,acid,(/,neutralization,_,soda))", ["acid", "(/,reaction,_,base)"] => "(&,acid,(/,reaction,_,base))";
1849                "(&,acid,(\\,reaction,_,base))", ["acid", "(\\,neutralization,_,base)"] => "(&,acid,(\\,neutralization,_,base))";
1850                "(&,animal,(|,animal,swimmer))", ["animal", "gull"] => "(&,animal,gull)";
1851                "(&,animal,(|,bird,swimmer))", ["animal", "(&,robin,swan)"] => "(&,animal,robin,swan)";
1852                "(&,animal,gull)", ["animal", "(|,animal,swimmer)"] => "(&,animal,(|,animal,swimmer))";
1853                "(&,animal,gull)", ["animal", "swan"] => "(&,animal,swan)";
1854                "(&,base,(\\,reaction,acid,_))", ["base", "(/,reaction,acid,_)"] => "(&,base,(/,reaction,acid,_))";
1855                "(&,base,(\\,reaction,acid,_))", ["base", "soda"] => "(&,base,soda)";
1856                "(&,bird,[with_wings],{Birdie},(|,[yellow],{Birdie}))", ["bird", "robin", "{Birdie}", "(|,[yellow],{Birdie})"] => "(&,bird,robin,{Birdie},(|,[yellow],{Birdie}))";
1857                "(&,flyer,[with_wings])", ["flyer", "(&,robin,{Tweety})"] => "(&,flyer,robin,{Tweety})";
1858                "(&,flyer,[with_wings])", ["flyer", "robin"] => "(&,flyer,robin)";
1859                "(&,flyer,[with_wings])", ["flyer", "{Birdie}"] => "(&,flyer,{Birdie})";
1860                "(&,flyer,[yellow],(|,[with_wings],{Birdie}))", ["flyer", "{Birdie}", "(|,[with_wings],{Birdie})"] => "(&,flyer,{Birdie},(|,[with_wings],{Birdie}))";
1861                "(&,flyer,{Birdie})", ["flyer", "[with_wings]"] => "(&,flyer,[with_wings])";
1862                "(&,flyer,{Birdie})", ["flyer", "bird"] => "(&,bird,flyer)";
1863                "(&,flyer,{Birdie})", ["flyer", "{Tweety}"] => "(&,flyer,{Tweety})";
1864                "(&,key,(/,open,_,lock))", ["key", "(/,open,_,{lock1})"] => "(&,key,(/,open,_,{lock1}))";
1865                "(&,key,(/,open,_,lock))", ["key", "{key1}"] => "(&,key,{key1})";
1866                "(&,neutralization,(*,(\\,reaction,_,soda),base))", ["neutralization", "(*,(\\,neutralization,_,base),base)"] => "(&,neutralization,(*,(\\,neutralization,_,base),base))";
1867                "(&,neutralization,(*,(\\,reaction,_,soda),base))", ["neutralization", "reaction"] => "(&,neutralization,reaction)";
1868                "(&,neutralization,(*,acid,(\\,neutralization,acid,_)))", ["neutralization", "(*,acid,(/,reaction,acid,_))"] => "(&,neutralization,(*,acid,(/,reaction,acid,_)))";
1869                "(&,neutralization,(*,acid,(\\,neutralization,acid,_)))", ["neutralization", "(*,acid,soda)"] => "(&,neutralization,(*,acid,soda))";
1870                "(&,neutralization,(*,acid,soda))", ["neutralization", "(*,acid,base)"] => "(&,neutralization,(*,acid,base))";
1871                "(&,neutralization,(*,acid,soda))", ["neutralization", "reaction"] => "(&,neutralization,reaction)";
1872                "(&,num,(/,(*,0),_))", ["num", "(/,num,_)"] => "(&,num,(/,num,_))";
1873                "(&,tiger,(|,bird,robin))", ["bird", "(|,bird,robin)"] => "(&,bird,(|,bird,robin))";
1874                "(&,{Birdie},(|,flyer,[yellow]))", ["{Birdie}", "(|,flyer,{Tweety})"] => "(&,{Birdie},(|,flyer,{Tweety}))";
1875                "(&,{Birdie},(|,flyer,[yellow]))", ["{Birdie}", "{Tweety}"] => None;
1876                "(&,{key1},(/,open,_,lock))", ["(/,open,_,{lock1})", "(/,open,_,lock)"] => "(&,(/,open,_,lock),(/,open,_,{lock1}))";
1877                "(&,{key1},(/,open,_,lock))", ["(|,(/,open,_,lock1),(/,open,_,{lock1}))", "(/,open,_,lock)"] => "(&,(/,open,_,lock),(|,(/,open,_,lock1),(/,open,_,{lock1})))";
1878                "(&,{key1},(/,open,_,lock))", ["(|,key,(/,open,_,{lock1}))", "(/,open,_,lock)"] => "(&,(/,open,_,lock),(|,key,(/,open,_,{lock1})))";
1879                "(&,{key1},(/,open,_,lock))", ["key", "(/,open,_,lock)"] => "(&,key,(/,open,_,lock))";
1880                "(*,(*,(*,0)))", ["(*,(*,(/,num,_)))"] => "(*,(*,(*,(/,num,_))))";
1881                "(*,(*,0))", ["(*,(/,num,_))"] => "(*,(*,(/,num,_)))";
1882                "(*,(*,0))", ["(*,num)"] => "(*,(*,num))";
1883                "(*,(*,CAT,eat,fish),<(*,CAT,FISH) --> FOOD>)", ["(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>)", "<(*,CAT,FISH) --> FOOD>"] => "(*,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),<(*,CAT,FISH) --> FOOD>)";
1884                "(*,(/,(*,0),_))", ["(/,num,_)"] => "(*,(/,num,_))";
1885                "(*,(/,(/,num,_),_))", ["(/,num,_)"] => "(*,(/,num,_))";
1886                "(*,(/,num,_))", ["(/,(/,num,_),_)"] => "(*,(/,(/,num,_),_))";
1887                "(*,(/,num,_))", ["0"] => "(*,0)";
1888                "(*,(/,open,_,lock1),lock1)", ["{key1}", "lock1"] => "(*,{key1},lock1)";
1889                "(*,(\\,reaction,_,base),base)", ["(\\,neutralization,_,base)", "base"] => "(*,(\\,neutralization,_,base),base)";
1890                "(*,(\\,reaction,_,soda),base)", ["acid", "base"] => "(*,acid,base)";
1891                "(*,(|,key,(/,open,_,{lock1})),lock)", ["(/,open,_,lock)", "lock"] => "(*,(/,open,_,lock),lock)";
1892                "(*,0)", ["(&,num,(/,(*,(/,num,_)),_))"] => "(*,(&,num,(/,(*,(/,num,_)),_)))";
1893                "(*,0)", ["(/,(*,(/,num,_)),_)"] => "(*,(/,(*,(/,num,_)),_))";
1894                "(*,0)", ["(/,num,_)"] => "(*,(/,num,_))";
1895                "(*,0)", ["num"] => "(*,num)";
1896                "(*,a,(/,like,_,a))", ["a", "b"] => "(*,a,b)";
1897                "(*,a,b)", ["(/,like,b,_)", "b"] => "(*,(/,like,b,_),b)";
1898                "(*,a,b)", ["a", "(/,like,_,a)"] => "(*,a,(/,like,_,a))";
1899                "(*,acid,(&,soda,(/,neutralization,acid,_)))", ["acid", "(/,reaction,acid,_)"] => "(*,acid,(/,reaction,acid,_))";
1900                "(*,acid,(/,reaction,acid,_))", ["acid", "(&,soda,(/,neutralization,acid,_))"] => "(*,acid,(&,soda,(/,neutralization,acid,_)))";
1901                "(*,acid,(/,reaction,acid,_))", ["acid", "(/,neutralization,acid,_)"] => "(*,acid,(/,neutralization,acid,_))";
1902                "(*,acid,(/,reaction,acid,_))", ["acid", "(\\,neutralization,acid,_)"] => "(*,acid,(\\,neutralization,acid,_))";
1903                "(*,acid,(/,reaction,acid,_))", ["acid", "(|,base,(\\,reaction,acid,_))"] => "(*,acid,(|,base,(\\,reaction,acid,_)))";
1904                "(*,acid,base)", ["acid", "(\\,neutralization,acid,_)"] => "(*,acid,(\\,neutralization,acid,_))";
1905                "(*,acid,base)", ["acid", "soda"] => "(*,acid,soda)";
1906                "(*,{key1},lock)", ["(&,key,(/,open,_,{lock1}))", "lock"] => "(*,(&,key,(/,open,_,{lock1})),lock)";
1907                "(*,{key1},lock)", ["(/,open,_,{lock1})", "lock"] => "(*,(/,open,_,{lock1}),lock)";
1908                "(*,{key1},lock)", ["(|,key,(/,open,_,{lock1}))", "lock"] => "(*,(|,key,(/,open,_,{lock1})),lock)";
1909                "(*,{key1},lock)", ["key", "lock"] => "(*,key,lock)";
1910                "(*,{key1},lock1)", ["(/,open,_,lock)", "lock1"] => "(*,(/,open,_,lock),lock1)";
1911                "(*,{key1},lock1)", ["(|,(/,open,_,lock1),(/,open,_,{lock1}))", "lock1"] => "(*,(|,(/,open,_,lock1),(/,open,_,{lock1})),lock1)";
1912                "(*,{key1},{lock1})", ["(/,open,_,lock)", "{lock1}"] => "(*,(/,open,_,lock),{lock1})";
1913                "(*,{key1},{lock1})", ["(/,open,_,{lock1})", "{lock1}"] => "(*,(/,open,_,{lock1}),{lock1})";
1914                "(*,{key1},{lock1})", ["key", "{lock1}"] => "(*,key,{lock1})";
1915                "(/,(*,(/,num,_)),_)", ["(*,num)"] => "(/,(*,num),_)";
1916                "(/,(*,b,(/,like,b,_)),_,a)", ["(*,b,a)", "a"] => "(/,(*,b,a),_,a)";
1917                "(/,(*,num),_)", ["(*,0)"] => "(/,(*,0),_)";
1918                "(/,(*,tim,tom),tom,_)", ["tom", "uncle"] => "(/,uncle,tom,_)";
1919                "(/,(/,num,_),_)", ["0"] => "(/,0,_)";
1920                "(/,0,_)", ["(&,num,(/,(*,(/,num,_)),_))"] => "(/,(&,num,(/,(*,(/,num,_)),_)),_)";
1921                "(/,0,_)", ["(/,num,_)"] => "(/,(/,num,_),_)";
1922                "(/,0,_)", ["num"] => "(/,num,_)";
1923                "(/,like,_,a)", ["like", "(/,like,b,_)"] => "(/,like,_,(/,like,b,_))";
1924                "(/,like,b,_)", ["(/,like,_,a)", "like"] => "(/,like,(/,like,_,a),_)";
1925                "(/,neutralization,_,base)", ["neutralization", "(\\,neutralization,acid,_)"] => "(/,neutralization,_,(\\,neutralization,acid,_))";
1926                "(/,neutralization,_,base)", ["neutralization", "soda"] => "(/,neutralization,_,soda)";
1927                "(/,num,_)", ["(*,0)"] => "(/,(*,0),_)";
1928                "(/,open,_,(|,lock,(/,open,{key1},_)))", ["open", "{lock1}"] => "(/,open,_,{lock1})";
1929                "(/,open,_,{lock1})", ["open", "(|,lock,(/,open,{key1},_))"] => "(/,open,_,(|,lock,(/,open,{key1},_)))";
1930                "(/,open,_,{lock1})", ["open", "lock"] => "(/,open,_,lock)";
1931                "(/,reaction,_,base)", ["(*,acid,soda)", "base"] => "(/,(*,acid,soda),_,base)";
1932                "(/,reaction,acid,_)", ["acid", "(*,acid,soda)"] => "(/,(*,acid,soda),acid,_)";
1933                "(\\,(*,b,a),_,(/,like,b,_))", ["like", "(/,like,b,_)"] => "(\\,like,_,(/,like,b,_))";
1934                "(\\,REPRESENT,_,CAT)", ["REPRESENT", "(\\,REPRESENT,_,CAT)"] => "(\\,REPRESENT,_,(\\,REPRESENT,_,CAT))";
1935                "(\\,neutralization,_,(/,neutralization,acid,_))", ["neutralization", "soda"] => "(\\,neutralization,_,soda)";
1936                "(\\,neutralization,_,(/,reaction,acid,_))", ["neutralization", "(/,neutralization,acid,_)"] => "(\\,neutralization,_,(/,neutralization,acid,_))";
1937                "(\\,neutralization,_,(/,reaction,acid,_))", ["neutralization", "(\\,neutralization,acid,_)"] => "(\\,neutralization,_,(\\,neutralization,acid,_))";
1938                "(\\,neutralization,_,(/,reaction,acid,_))", ["neutralization", "(|,base,(\\,reaction,acid,_))"] => "(\\,neutralization,_,(|,base,(\\,reaction,acid,_)))";
1939                "(\\,neutralization,_,base)", ["neutralization", "(/,neutralization,acid,_)"] => "(\\,neutralization,_,(/,neutralization,acid,_))";
1940                "(\\,neutralization,_,base)", ["neutralization", "soda"] => "(\\,neutralization,_,soda)";
1941                "(\\,neutralization,acid,_)", ["(\\,reaction,_,base)", "neutralization"] => "(\\,neutralization,(\\,reaction,_,base),_)";
1942                "(\\,reaction,(\\,reaction,_,soda),_)", ["(\\,reaction,_,base)", "reaction"] => "(\\,reaction,(\\,reaction,_,base),_)";
1943                "(\\,reaction,_,base)", ["(*,acid,soda)", "base"] => "(\\,(*,acid,soda),_,base)";
1944                "(\\,reaction,acid,_)", ["acid", "(*,acid,soda)"] => "(\\,(*,acid,soda),acid,_)";
1945                "(|,(&,animal,gull),(&,bird,robin))", ["(&,animal,gull)", "swimmer"] => "(|,swimmer,(&,animal,gull))";
1946                "(|,(&,flyer,{Birdie}),{Birdie,Tweety})", ["(&,flyer,{Birdie})", "(|,[yellow],{Birdie})"] => "(|,[yellow],{Birdie},(&,flyer,{Birdie}))";
1947                "(|,(/,neutralization,_,(\\,neutralization,acid,_)),(/,reaction,_,base))", ["(/,neutralization,_,base)", "(/,reaction,_,base)"] => "(|,(/,neutralization,_,base),(/,reaction,_,base))";
1948                "(|,(/,neutralization,_,(\\,neutralization,acid,_)),(/,reaction,_,base))", ["acid", "(/,reaction,_,base)"] => "(|,acid,(/,reaction,_,base))";
1949                "(|,(/,neutralization,_,base),(/,reaction,_,base))", ["(/,neutralization,_,base)", "acid"] => "(|,acid,(/,neutralization,_,base))";
1950                "(|,(/,neutralization,_,base),(/,reaction,_,soda))", ["(/,neutralization,_,base)", "(/,neutralization,_,(\\,neutralization,acid,_))"] => "(|,(/,neutralization,_,base),(/,neutralization,_,(\\,neutralization,acid,_)))";
1951                "(|,(/,neutralization,_,base),(/,reaction,_,soda))", ["(/,neutralization,_,base)", "(/,reaction,_,base)"] => "(|,(/,neutralization,_,base),(/,reaction,_,base))";
1952                "(|,(/,neutralization,_,soda),(/,reaction,_,base))", ["acid", "(/,reaction,_,base)"] => "(|,acid,(/,reaction,_,base))";
1953                "(|,(/,num,_),(/,(*,num),_))", ["(/,num,_)", "0"] => "(|,0,(/,num,_))";
1954                "(|,(\\,REPRESENT,_,CAT),(\\,(\\,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))", ["(\\,REPRESENT,_,CAT)", "(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)"] => "(|,(\\,REPRESENT,_,CAT),(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))";
1955                "(|,(\\,REPRESENT,_,CAT),(\\,(\\,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))", ["cat", "(\\,(\\,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)"] => "(|,cat,(\\,(\\,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))";
1956                "(|,CAT,(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))", ["(\\,(\\,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)", "(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)"] => "(|,(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish),(\\,(\\,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))";
1957                "(|,[strong],(~,youth,girl))", ["(~,boy,girl)", "(~,youth,girl)"] => "(|,(~,boy,girl),(~,youth,girl))";
1958                "(|,[strong],(~,youth,girl))", ["boy", "(~,youth,girl)"] => "(|,boy,(~,youth,girl))";
1959                "(|,X,Y)", ["[with_wings]", "(|,flyer,{Tweety})", "{Birdie}"] => "(|,flyer,[with_wings],{Birdie},{Tweety})"; // ! 📌【2024-09-07 14:17:33】为避免左侧词项被自动约简,将「模板词项」简化
1960                "(|,X,Y)", ["[with_wings]", "flyer", "{Birdie}"] => "(|,flyer,[with_wings],{Birdie})";                       // ! 📌【2024-09-07 14:17:33】为避免左侧词项被自动约简,将「模板词项」简化
1961                "(|,X,Y)", ["[with_wings]", "{Tweety}", "{Birdie}"] => "(|,[with_wings],{Birdie},{Tweety})";                 // ! 📌【2024-09-07 14:17:33】为避免左侧词项被自动约简,将「模板词项」简化
1962                "(|,X,Y)", ["flyer", "[yellow]", "{Birdie}"] => "(|,flyer,[yellow],{Birdie})";                               // ! 📌【2024-09-07 14:17:33】为避免左侧词项被自动约简,将「模板词项」简化
1963                "(|,[with_wings],{Birdie})", ["flyer", "{Birdie}"] => "(|,flyer,{Birdie})";
1964                "(|,[with_wings],{Birdie})", ["{Tweety}", "{Birdie}"] => "{Birdie,Tweety}";
1965                "(|,[with_wings],{Birdie},(&,bird,(|,[yellow],{Birdie})))", ["flyer", "{Birdie}", "(&,bird,(|,[yellow],{Birdie}))"] => "(|,flyer,{Birdie},(&,bird,(|,[yellow],{Birdie})))";
1966                "(|,[with_wings],{Birdie},(&,flyer,[yellow]))", ["[with_wings]", "{Birdie}", "(|,[with_wings],{Birdie})"] => "(|,[with_wings],{Birdie})";
1967                "(|,[yellow],{Birdie})", ["(&,flyer,{Birdie})", "{Birdie}"] => "(|,{Birdie},(&,flyer,{Birdie}))";
1968                "(|,[yellow],{Birdie})", ["[yellow]", "[with_wings]"] => None;
1969                "(|,[yellow],{Birdie})", ["[yellow]", "bird"] => "(|,bird,[yellow])";
1970                "(|,[yellow],{Birdie})", ["[yellow]", "{Tweety}"] => "(|,[yellow],{Tweety})";
1971                "(|,[yellow],{Birdie},(&,flyer,{Birdie}))", ["flyer", "{Birdie}", "(&,flyer,{Birdie})"] => "(|,flyer,{Birdie},(&,flyer,{Birdie}))";
1972                "(|,[yellow],{Birdie},(&,flyer,{Birdie}))", ["{Tweety}", "{Birdie}", "(&,flyer,{Birdie})"] => "(|,(&,flyer,{Birdie}),{Birdie,Tweety})";
1973                "(|,acid,(/,neutralization,_,soda))", ["acid", "(/,reaction,_,base)"] => "(|,acid,(/,reaction,_,base))";
1974                "(|,acid,(\\,reaction,_,base))", ["acid", "(\\,neutralization,_,base)"] => "(|,acid,(\\,neutralization,_,base))";
1975                "(|,animal,gull)", ["animal", "robin"] => "(|,animal,robin)";
1976                "(|,base,(\\,reaction,acid,_))", ["base", "(/,reaction,acid,_)"] => "(|,base,(/,reaction,acid,_))";
1977                "(|,base,(\\,reaction,acid,_))", ["base", "soda"] => "(|,base,soda)";
1978                "(|,bird,(&,robin,tiger))", ["bird", "animal"] => "(|,animal,bird)";
1979                "(|,bird,[yellow])", ["bird", "flyer"] => "(|,bird,flyer)";
1980                "(|,bird,[yellow])", ["bird", "{Birdie}"] => "(|,bird,{Birdie})";
1981                "(|,bird,[yellow],{Birdie})", ["bird", "flyer", "{Birdie}"] => "(|,bird,flyer,{Birdie})";
1982                "(|,bird,[yellow],{Birdie})", ["bird", "{Tweety}", "{Birdie}"] => "(|,bird,{Birdie},{Tweety})";
1983                "(|,bird,{Birdie})", ["bird", "[with_wings]"] => "(|,bird,[with_wings])";
1984                "(|,bird,{Birdie})", ["bird", "flyer"] => "(|,bird,flyer)";
1985                "(|,bird,{Birdie})", ["bird", "{Tweety}"] => "(|,bird,{Tweety})";
1986                "(|,bird,{Tweety})", ["bird", "(|,bird,flyer)"] => "(|,bird,flyer)";
1987                "(|,chess,competition)", ["chess", "(|,chess,sport)"] => "(|,chess,sport)";
1988                "(|,flyer,[yellow])", ["flyer", "(&,flyer,{Birdie})"] => "(|,flyer,(&,flyer,{Birdie}))";
1989                "(|,flyer,[yellow],(&,flyer,{Birdie}))", ["flyer", "{Birdie}", "(&,flyer,{Birdie})"] => "(|,flyer,{Birdie},(&,flyer,{Birdie}))";
1990                "(|,flyer,[yellow],{Birdie})", ["flyer", "(&,flyer,{Birdie})", "{Birdie}"] => "(|,flyer,{Birdie},(&,flyer,{Birdie}))";
1991                "(|,flyer,[yellow],{Birdie})", ["flyer", "(|,flyer,{Tweety})", "{Birdie}"] => "(|,flyer,{Birdie},{Tweety})";
1992                "(|,key,(/,open,_,lock))", ["key", "(/,open,_,{lock1})"] => "(|,key,(/,open,_,{lock1}))";
1993                "(|,key,(/,open,_,lock))", ["key", "{key1}"] => "(|,key,{key1})";
1994                "(|,neutralization,(*,(\\,reaction,_,soda),base))", ["neutralization", "reaction"] => "(|,neutralization,reaction)";
1995                "(|,neutralization,(*,acid,soda))", ["neutralization", "(*,acid,(\\,neutralization,acid,_))"] => "(|,neutralization,(*,acid,(\\,neutralization,acid,_)))";
1996                "(|,neutralization,(*,acid,soda))", ["neutralization", "(*,acid,base)"] => "(|,neutralization,(*,acid,base))";
1997                "(|,neutralization,(*,acid,soda))", ["neutralization", "reaction"] => "(|,neutralization,reaction)";
1998                "(|,robin,[yellow],{Birdie})", ["robin", "(|,flyer,{Tweety})", "{Birdie}"] => "(|,flyer,robin,{Birdie},{Tweety})";
1999                "(|,soda,(\\,neutralization,acid,_))", ["(/,neutralization,acid,_)", "(\\,neutralization,acid,_)"] => "(|,(/,neutralization,acid,_),(\\,neutralization,acid,_))";
2000                "(|,tiger,(&,bird,robin))", ["tiger", "(|,animal,swimmer)"] => "(|,animal,swimmer,tiger)";
2001                "(|,{key1},(/,open,_,lock))", ["(/,open,_,{lock1})", "(/,open,_,lock)"] => "(|,(/,open,_,lock),(/,open,_,{lock1}))";
2002                "(|,{key1},(/,open,_,lock))", ["(|,key,(/,open,_,{lock1}))", "(/,open,_,lock)"] => "(|,key,(/,open,_,lock),(/,open,_,{lock1}))";
2003                "(|,{key1},(/,open,_,lock))", ["key", "(/,open,_,lock)"] => "(|,key,(/,open,_,lock))";
2004                "(~,(/,(*,tim,tom),tom,_),tim)", ["(/,(*,tim,tom),tom,_)", "(/,uncle,tom,_)"] => "(~,(/,(*,tim,tom),tom,_),(/,uncle,tom,_))";
2005                "(~,[strong],girl)", ["(~,boy,girl)", "girl"] => "(~,(~,boy,girl),girl)";
2006                "(~,[strong],girl)", ["boy", "girl"] => "(~,boy,girl)";
2007                "(~,boy,girl)", ["[strong]", "girl"] => "(~,[strong],girl)";
2008                "(~,boy,girl)", ["youth", "girl"] => "(~,youth,girl)";
2009                "(~,youth,girl)", ["(|,boy,girl)", "girl"] => "(~,(|,boy,girl),girl)";
2010                "[bright]", ["smart"] => "[smart]";
2011                "{Birdie}", ["Tweety"] => "{Tweety}";
2012                "{Mars,Pluto,Saturn,Venus}", ["Mars", "Venus"] => "{Mars,Venus}";
2013            }
2014            ok!()
2015        }
2016    }
2017
2018    mod statement {
2019        use super::*;
2020
2021        #[test]
2022        fn make_statement_relation() -> AResult {
2023            fn test(relation: &str, subject: Term, predicate: Term, expected: Option<Term>) {
2024                let out =
2025                    Term::make_statement_relation(relation, subject.clone(), predicate.clone());
2026                assert_eq!(
2027                    out,
2028                    expected,
2029                    "\"{relation}\", \"{subject}\", \"{predicate}\" => {} != {}",
2030                    format_option_term(&out),
2031                    format_option_term(&expected),
2032                );
2033            }
2034            macro_once! {
2035                // * 🚩模式:参数列表 ⇒ 预期词项
2036                macro test($($relation:tt, $subject:tt, $predicate:tt => $expected:tt;)*) {
2037                    $( test($relation, term!($subject), term!($predicate), option_term!($expected)); )*
2038                }
2039                // * ℹ️用例均源自OpenNARS实际运行
2040                "-->", "(&,<bird --> fly>,<{Tweety} --> bird>)", "claimedByBob" => "<(&,<bird --> fly>,<{Tweety} --> bird>) --> claimedByBob>";
2041                "-->", "(&,bird,swimmer)", "(&,animal,swimmer)" => "<(&,bird,swimmer) --> (&,animal,swimmer)>";
2042                "-->", "(&,swan,swimmer)", "bird" => "<(&,swan,swimmer) --> bird>";
2043                "-->", "(*,(*,(*,0)))", "num" => "<(*,(*,(*,0))) --> num>";
2044                "-->", "(*,CAT,FISH)", "FOOD" => "<(*,CAT,FISH) --> FOOD>";
2045                "-->", "(*,bird,plant)", "?120" => "<(*,bird,plant) --> ?120>";
2046                "-->", "(-,swimmer,animal)", "(-,swimmer,bird)" => "<(-,swimmer,animal) --> (-,swimmer,bird)>";
2047                "-->", "(/,neutralization,_,base)", "?120" => "<(/,neutralization,_,base) --> ?120>";
2048                "-->", "(|,boy,girl)", "youth" => "<(|,boy,girl) --> youth>";
2049                "-->", "(~,boy,girl)", "[strong]" => "<(~,boy,girl) --> [strong]>";
2050                "-->", "(~,swimmer,swan)", "bird" => "<(~,swimmer,swan) --> bird>";
2051                "-->", "0", "(/,num,_)" => "<0 --> (/,num,_)>";
2052                "-->", "0", "num" => "<0 --> num>";
2053                "-->", "?120", "claimedByBob" => "<?120 --> claimedByBob>";
2054                "-->", "[smart]", "[bright]" => "<[smart] --> [bright]>";
2055                "-->", "acid", "(/,reaction,_,base)" => "<acid --> (/,reaction,_,base)>";
2056                "-->", "cat", "(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)" => "<cat --> (/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)>";
2057                "-->", "neutralization", "(*,acid,base)" => "<neutralization --> (*,acid,base)>";
2058                "-->", "planetX", "{Mars,Pluto,Venus}" => "<planetX --> {Mars,Pluto,Venus}>";
2059                "-->", "planetX", "{Pluto,Saturn}" => "<planetX --> {Pluto,Saturn}>";
2060                "-->", "robin", "(&,bird,swimmer)" => "<robin --> (&,bird,swimmer)>";
2061                "-->", "robin", "(-,bird,swimmer)" => "<robin --> (-,bird,swimmer)>";
2062                "-->", "robin", "(|,bird,swimmer)" => "<robin --> (|,bird,swimmer)>";
2063                "-->", "robin", "[chirping]" => "<robin --> [chirping]>";
2064                "-->", "{?49}", "swimmer" => "<{?49} --> swimmer>";
2065                "-->", "{Tweety}", "[with_wings]" => "<{Tweety} --> [with_wings]>";
2066                "-->", "{Tweety}", "bird" => "<{Tweety} --> bird>";
2067                "-->", "{Tweety}", "{Birdie}" => "<{Tweety} --> {Birdie}>";
2068                "-->", "{key1}", "(/,open,_,{lock1})" => "<{key1} --> (/,open,_,{lock1})>";
2069                "--]", "raven", "black" => "<raven --> [black]>";
2070                "<->", "Birdie", "Tweety" => "<Birdie <-> Tweety>";
2071                "<->", "[bright]", "[smart]" => "<[bright] <-> [smart]>";
2072                "<->", "{Birdie}", "{Tweety}" => "<{Birdie} <-> {Tweety}>";
2073                "<=>", "<robin --> animal>", "<robin --> bird>" => "<<robin --> animal> <=> <robin --> bird>>";
2074                "<=>", "<robin --> bird>", "<robin --> [flying]>" => "<<robin --> bird> <=> <robin --> [flying]>>";
2075                "==>", "(&&,<robin --> [chirping]>,<robin --> [flying]>)", "<robin --> bird>" => "<(&&,<robin --> [chirping]>,<robin --> [flying]>) ==> <robin --> bird>>";
2076                "==>", "(&&,<robin --> [chirping]>,<robin --> [flying]>,<robin --> [with_wings]>)", "<robin --> bird>" => "<(&&,<robin --> [chirping]>,<robin --> [flying]>,<robin --> [with_wings]>) ==> <robin --> bird>>";
2077                "==>", "(&&,<robin --> [flying]>,<robin --> [with_wings]>)", "<robin --> [living]>" => "<(&&,<robin --> [flying]>,<robin --> [with_wings]>) ==> <robin --> [living]>>";
2078                "==>", "(&&,<robin --> bird>,<robin --> [flying]>)", "<robin --> [living]>" => "<(&&,<robin --> bird>,<robin --> [flying]>) ==> <robin --> [living]>>";
2079                "==>", "(&&,<robin --> bird>,<robin --> [living]>)", "<robin --> animal>" => "<(&&,<robin --> bird>,<robin --> [living]>) ==> <robin --> animal>>";
2080                "==>", "(--,<robin --> [flying]>)", "<robin --> bird>" => "<(--,<robin --> [flying]>) ==> <robin --> bird>>";
2081                "==>", "(--,<robin --> bird>)", "<robin --> [flying]>" => "<(--,<robin --> bird>) ==> <robin --> [flying]>>";
2082                "==>", "<robin --> [flying]>", "<robin --> [with_beak]>" => "<<robin --> [flying]> ==> <robin --> [with_beak]>>";
2083                "==>", "<robin --> [flying]>", "<robin --> animal>" => "<<robin --> [flying]> ==> <robin --> animal>>";
2084                "==>", "<robin --> bird>", "(&&,<robin --> animal>,<robin --> [flying]>)" => "<<robin --> bird> ==> (&&,<robin --> animal>,<robin --> [flying]>)>";
2085                "==>", "<robin --> bird>", "<robin --> [flying]>" => "<<robin --> bird> ==> <robin --> [flying]>>";
2086                "==>", "<robin --> bird>", "<robin --> animal>" => "<<robin --> bird> ==> <robin --> animal>>";
2087                "{--", "Tweety", "bird" => "<{Tweety} --> bird>";
2088                "{-]", "Tweety", "yellow" => "<{Tweety} --> [yellow]>";
2089            }
2090            ok!()
2091        }
2092
2093        #[test]
2094        fn make_statement() -> AResult {
2095            fn test(template: Term, subject: Term, predicate: Term, expected: Option<Term>) {
2096                let out = Term::make_statement(&template, subject.clone(), predicate.clone());
2097                assert_eq!(
2098                    out,
2099                    expected,
2100                    "\"{template}\", \"{subject}\", \"{predicate}\" => {} != {}",
2101                    format_option_term(&out),
2102                    format_option_term(&expected),
2103                );
2104            }
2105            macro_once! {
2106                // * 🚩模式:参数列表 ⇒ 预期词项
2107                macro test($($template:tt, $subject:tt, $predicate:tt => $expected:tt;)*) {
2108                    $( test(term!($template), term!($subject), term!($predicate), option_term!($expected)); )*
2109                }
2110                // * ℹ️用例均源自OpenNARS实际运行
2111                "<(&&,<robin --> [chirping]>,<robin --> [flying]>) ==> <robin --> bird>>", "(&&,<robin --> bird>,<robin --> [flying]>)", "<robin --> bird>" => None;"<(&&,<robin --> [chirping]>,<robin --> [flying]>) ==> <robin --> bird>>", "<robin --> [chirping]>", "<robin --> bird>" => "<<robin --> [chirping]> ==> <robin --> bird>>";
2112                "<(&&,<robin --> [chirping]>,<robin --> [flying]>,<robin --> [with_wings]>) ==> <robin --> bird>>", "(&&,<robin --> [chirping]>,<robin --> [flying]>)", "<robin --> bird>" => "<(&&,<robin --> [chirping]>,<robin --> [flying]>) ==> <robin --> bird>>";
2113                "<(&&,<robin --> [chirping]>,<robin --> [flying]>,<robin --> [with_wings]>) ==> <robin --> bird>>", "(&&,<robin --> bird>,<robin --> [flying]>,<robin --> [with_wings]>)", "<robin --> bird>" => None;
2114                "<(&&,<robin --> [chirping]>,<robin --> [with_wings]>) ==> <robin --> bird>>", "(&&,<robin --> bird>,<robin --> [chirping]>)", "<robin --> bird>" => None;
2115                "<(&&,<robin --> [flying]>,<robin --> [with_wings]>) ==> <robin --> [living]>>", "<robin --> [flying]>", "<robin --> [living]>" => "<<robin --> [flying]> ==> <robin --> [living]>>";
2116                "<(&&,<robin --> [flying]>,<robin --> [with_wings]>) ==> <robin --> [living]>>", "<robin --> [with_wings]>", "<robin --> bird>" => "<<robin --> [with_wings]> ==> <robin --> bird>>";
2117                "<(&&,<robin --> [flying]>,<robin --> [with_wings]>) ==> <robin --> animal>>", "(&&,<robin --> [flying]>,<robin --> [with_wings]>)", "(&&,<robin --> animal>,<robin --> bird>)" => "<(&&,<robin --> [flying]>,<robin --> [with_wings]>) ==> (&&,<robin --> animal>,<robin --> bird>)>";
2118                "<(&&,<robin --> [flying]>,<robin --> [with_wings]>) ==> <robin --> animal>>", "(&&,<robin --> [flying]>,<robin --> [with_wings]>)", "(||,<robin --> animal>,<robin --> bird>)" => "<(&&,<robin --> [flying]>,<robin --> [with_wings]>) ==> (||,<robin --> animal>,<robin --> bird>)>";
2119                "<(&&,<robin --> [flying]>,<robin --> [with_wings]>) ==> <robin --> animal>>", "<robin --> animal>", "<robin --> bird>" => "<<robin --> animal> ==> <robin --> bird>>";
2120                "<(&&,<robin --> bird>,<robin --> [flying]>) ==> <robin --> [living]>>", "<robin --> [flying]>", "<robin --> [living]>" => "<<robin --> [flying]> ==> <robin --> [living]>>";
2121                "<(&&,<robin --> bird>,<robin --> [flying]>) ==> <robin --> [living]>>", "<robin --> bird>", "<robin --> [living]>" => "<<robin --> bird> ==> <robin --> [living]>>";
2122                "<(&&,<robin --> bird>,<robin --> [flying]>) ==> <robin --> animal>>", "<robin --> [flying]>", "<robin --> animal>" => "<<robin --> [flying]> ==> <robin --> animal>>";
2123                "<(&&,<robin --> bird>,<robin --> [flying]>) ==> <robin --> animal>>", "<robin --> bird>", "<robin --> animal>" => "<<robin --> bird> ==> <robin --> animal>>";
2124                "<(&&,<robin --> bird>,<robin --> [flying]>,<robin --> [with_wings]>) ==> <robin --> animal>>", "(&&,<robin --> [flying]>,<robin --> [with_wings]>)", "<robin --> animal>" => "<(&&,<robin --> [flying]>,<robin --> [with_wings]>) ==> <robin --> animal>>";
2125                "<(&&,<robin --> bird>,<robin --> [flying]>,<robin --> [with_wings]>) ==> <robin --> animal>>", "(&&,<robin --> bird>,<robin --> [flying]>)", "<robin --> animal>" => "<(&&,<robin --> bird>,<robin --> [flying]>) ==> <robin --> animal>>";
2126                "<(&&,<robin --> bird>,<robin --> [living]>) ==> <robin --> animal>>", "(&&,<robin --> bird>,<robin --> [flying]>)", "<robin --> animal>" => "<(&&,<robin --> bird>,<robin --> [flying]>) ==> <robin --> animal>>";
2127                "<(&&,<robin --> bird>,<robin --> [living]>) ==> <robin --> animal>>", "(&&,<robin --> bird>,<robin --> [flying]>,<robin --> [with_wings]>)", "<robin --> animal>" => "<(&&,<robin --> bird>,<robin --> [flying]>,<robin --> [with_wings]>) ==> <robin --> animal>>";
2128                "<(&&,<robin --> flyer>,<robin --> [chirping]>) ==> <robin --> bird>>", "(&&,<robin --> bird>,<robin --> flyer>)", "<robin --> bird>" => None;
2129                "<(&&,<robin --> flyer>,<robin --> [chirping]>,<(*,robin,worms) --> food>) ==> <robin --> bird>>", "(&&,<robin --> bird>,<robin --> flyer>,<(*,robin,worms) --> food>)", "<robin --> bird>" => None;
2130                "<(&&,<robin --> flyer>,<robin --> [chirping]>,<(*,robin,worms) --> food>) ==> <robin --> bird>>", "(&&,<robin --> flyer>,<(*,robin,worms) --> food>)", "<robin --> bird>" => "<(&&,<robin --> flyer>,<(*,robin,worms) --> food>) ==> <robin --> bird>>";
2131                "<(&&,<robin --> flyer>,<robin --> [chirping]>,<worms --> (/,food,robin,_)>) ==> <robin --> bird>>", "(&&,<robin --> bird>,<robin --> flyer>,<worms --> (/,food,robin,_)>)", "<robin --> bird>" => None;
2132                "<(&,bird,swimmer) --> (&,animal,swimmer)>", "bird", "animal" => "<bird --> animal>";
2133                "<(&,bird,swimmer) --> (&,animal,swimmer)>", "swimmer", "swimmer" => None;
2134                "<(&,chess,sport) --> competition>", "chess", "competition" => "<chess --> competition>";
2135                "<(&,robin,swan) --> (&,bird,swimmer)>", "(&,robin,swan)", "bird" => "<(&,robin,swan) --> bird>";
2136                "<(&,robin,swimmer) --> animal>", "(&,robin,swimmer)", "(&,animal,bird)" => "<(&,robin,swimmer) --> (&,animal,bird)>";
2137                "<(&,robin,swimmer) --> animal>", "(&,robin,swimmer)", "(|,animal,bird)" => "<(&,robin,swimmer) --> (|,animal,bird)>";
2138                "<(&,robin,{Tweety}) --> [with_wings]>", "(&,flyer,robin,{Tweety})", "(&,flyer,[with_wings])" => "<(&,flyer,robin,{Tweety}) --> (&,flyer,[with_wings])>";
2139                "<(&,robin,{Tweety}) --> [with_wings]>", "(&,robin,{Birdie},{Tweety})", "(&,[with_wings],{Birdie})" => "<(&,robin,{Birdie},{Tweety}) --> (&,[with_wings],{Birdie})>";
2140                "<(*,(*,(*,0))) --> num>", "(*,(*,(*,(/,num,_))))", "num" => "<(*,(*,(*,(/,num,_)))) --> num>";
2141                "<(*,(*,(*,0))) --> num>", "num", "(*,(*,(*,(/,num,_))))" => "<num --> (*,(*,(*,(/,num,_))))>";
2142                "<(*,(*,0)) --> (*,(*,(/,num,_)))>", "(*,(*,(*,0)))", "(*,(*,(*,(/,num,_))))" => "<(*,(*,(*,0))) --> (*,(*,(*,(/,num,_))))>";
2143                "<(*,(*,0)) --> (*,(*,(/,num,_)))>", "(*,(*,(/,num,_)))", "(*,(*,num))" => "<(*,(*,(/,num,_))) --> (*,(*,num))>";
2144                "<(*,(*,0)) --> (*,(*,(/,num,_)))>", "(*,(*,0))", "(&,(*,(*,num)),(*,(*,(/,num,_))))" => "<(*,(*,0)) --> (&,(*,(*,num)),(*,(*,(/,num,_))))>";
2145                "<(*,(*,0)) --> (*,(*,(/,num,_)))>", "(*,(*,0))", "(|,(*,(*,num)),(*,(*,(/,num,_))))" => "<(*,(*,0)) --> (|,(*,(*,num)),(*,(*,(/,num,_))))>";
2146                "<(*,(*,0)) --> (*,(*,(/,num,_)))>", "(*,(*,num))", "(*,(*,(/,num,_)))" => "<(*,(*,num)) --> (*,(*,(/,num,_)))>";
2147                "<(*,(*,0)) --> (*,(*,(/,num,_)))>", "(*,0)", "(*,(/,num,_))" => "<(*,0) --> (*,(/,num,_))>";
2148                "<(*,0) --> (*,(/,num,_))>", "(*,(*,0))", "(*,(*,(/,num,_)))" => "<(*,(*,0)) --> (*,(*,(/,num,_)))>";
2149                "<(*,0) --> (*,(/,num,_))>", "(*,(/,num,_))", "(*,num)" => "<(*,(/,num,_)) --> (*,num)>";
2150                "<(*,0) --> (*,(/,num,_))>", "(*,0)", "(&,(*,num),(*,(/,num,_)))" => "<(*,0) --> (&,(*,num),(*,(/,num,_)))>";
2151                "<(*,0) --> (*,(/,num,_))>", "(*,0)", "(|,(*,num),(*,(/,num,_)))" => "<(*,0) --> (|,(*,num),(*,(/,num,_)))>";
2152                "<(*,0) --> (*,(/,num,_))>", "(*,num)", "(*,(/,num,_))" => "<(*,num) --> (*,(/,num,_))>";
2153                "<(*,0) --> (*,(/,num,_))>", "0", "(/,num,_)" => "<0 --> (/,num,_)>";
2154                "<(*,0) --> (*,num)>", "(*,(*,0))", "(*,(*,num))" => "<(*,(*,0)) --> (*,(*,num))>";
2155                "<(*,0) --> (*,num)>", "(*,(/,num,_))", "(*,num)" => "<(*,(/,num,_)) --> (*,num)>";
2156                "<(*,0) --> (*,num)>", "(*,0)", "(&,(*,num),(*,(/,num,_)))" => "<(*,0) --> (&,(*,num),(*,(/,num,_)))>";
2157                "<(*,0) --> (*,num)>", "(*,0)", "(|,(*,num),(*,(/,num,_)))" => "<(*,0) --> (|,(*,num),(*,(/,num,_)))>";
2158                "<(*,0) --> (*,num)>", "(*,num)", "(*,(/,num,_))" => "<(*,num) --> (*,(/,num,_))>";
2159                "<(*,0) --> (*,num)>", "0", "num" => "<0 --> num>";
2160                "<(*,0) --> num>", "(/,(*,0),_)", "(/,num,_)" => "<(/,(*,0),_) --> (/,num,_)>";
2161                "<(*,a,b) --> (&,like,(*,(/,like,b,_),b))>", "(*,a,b)", "(&,like,(*,(/,like,b,_),b))" => "<(*,a,b) --> (&,like,(*,(/,like,b,_),b))>";
2162                "<(*,a,b) --> like>", "(*,(/,like,b,_),b)", "like" => "<(*,(/,like,b,_),b) --> like>";
2163                "<(*,a,b) --> like>", "(*,a,b)", "(&,like,(*,(/,like,b,_),b))" => "<(*,a,b) --> (&,like,(*,(/,like,b,_),b))>";
2164                "<(*,a,b) --> like>", "(*,a,b)", "(|,like,(*,(/,like,b,_),b))" => "<(*,a,b) --> (|,like,(*,(/,like,b,_),b))>";
2165                "<(*,a,b) --> like>", "like", "(*,(/,like,b,_),b)" => "<like --> (*,(/,like,b,_),b)>";
2166                "<(*,acid,base) --> reaction>", "neutralization", "reaction" => "<neutralization --> reaction>";
2167                "<(*,b,a) --> (*,b,(/,like,b,_))>", "a", "(/,like,b,_)" => "<a --> (/,like,b,_)>";
2168                "<(*,b,a) --> (*,b,(/,like,b,_))>", "b", "b" => None;
2169                "<(*,num) <-> (*,(/,num,_))>", "num", "(/,num,_)" => "<num <-> (/,num,_)>";
2170                "<(*,tim,tom) --> uncle>", "(/,(*,tim,tom),_,tom)", "(/,uncle,_,tom)" => "<(/,(*,tim,tom),_,tom) --> (/,uncle,_,tom)>";
2171                "<(-,swimmer,animal) --> (-,swimmer,bird)>", "bird", "animal" => "<bird --> animal>";
2172                "<(-,swimmer,animal) --> (-,swimmer,bird)>", "swimmer", "swimmer" => None;
2173                "<(--,<robin --> [flying]>) ==> <robin --> bird>>", "(--,<robin --> bird>)", "<robin --> [flying]>" => "<(--,<robin --> bird>) ==> <robin --> [flying]>>";
2174                "<(--,<robin --> bird>) ==> <robin --> [flying]>>", "(--,<robin --> [flying]>)", "<robin --> bird>" => "<(--,<robin --> [flying]>) ==> <robin --> bird>>";
2175                "<(/,(*,0),_) --> (/,num,_)>", "(*,(/,(*,0),_))", "(*,(/,num,_))" => "<(*,(/,(*,0),_)) --> (*,(/,num,_))>";
2176                "<(/,(*,tim,tom),_,tom) --> (/,uncle,_,tom)>", "(*,tim,tom)", "uncle" => "<(*,tim,tom) --> uncle>";
2177                "<(/,(*,tim,tom),_,tom) --> (/,uncle,_,tom)>", "tom", "tom" => None;
2178                "<(/,(*,tim,tom),tom,_) --> (/,uncle,tom,_)>", "(&,tim,(/,(*,tim,tom),tom,_))", "(/,uncle,tom,_)" => "<(&,tim,(/,(*,tim,tom),tom,_)) --> (/,uncle,tom,_)>";
2179                "<(/,(*,tim,tom),tom,_) --> (/,uncle,tom,_)>", "(/,(*,tim,tom),tom,_)", "tim" => "<(/,(*,tim,tom),tom,_) --> tim>";
2180                "<(/,(*,tim,tom),tom,_) --> (/,uncle,tom,_)>", "(|,tim,(/,(*,tim,tom),tom,_))", "(/,uncle,tom,_)" => "<(|,tim,(/,(*,tim,tom),tom,_)) --> (/,uncle,tom,_)>";
2181                "<(/,(*,tim,tom),tom,_) --> (/,uncle,tom,_)>", "(~,(/,(*,tim,tom),tom,_),tim)", "(/,uncle,tom,_)" => "<(~,(/,(*,tim,tom),tom,_),tim) --> (/,uncle,tom,_)>";
2182                "<(/,(*,tim,tom),tom,_) --> (/,uncle,tom,_)>", "tim", "(/,(*,tim,tom),tom,_)" => "<tim --> (/,(*,tim,tom),tom,_)>";
2183                "<(/,neutralization,_,base) --> (/,reaction,_,base)>", "(&,acid,(/,neutralization,_,base))", "(/,reaction,_,base)" => "<(&,acid,(/,neutralization,_,base)) --> (/,reaction,_,base)>";
2184                "<(/,neutralization,_,base) --> (/,reaction,_,base)>", "(/,neutralization,_,base)", "acid" => "<(/,neutralization,_,base) --> acid>";
2185                "<(/,neutralization,_,base) --> (/,reaction,_,base)>", "(|,acid,(/,neutralization,_,base))", "(/,reaction,_,base)" => "<(|,acid,(/,neutralization,_,base)) --> (/,reaction,_,base)>";
2186                "<(/,neutralization,_,base) --> (/,reaction,_,base)>", "acid", "(/,neutralization,_,base)" => "<acid --> (/,neutralization,_,base)>";
2187                "<(/,neutralization,_,base) --> (/,reaction,_,base)>", "base", "base" => None;
2188                "<(/,neutralization,_,base) --> (/,reaction,_,base)>", "neutralization", "reaction" => "<neutralization --> reaction>";
2189                "<(/,neutralization,_,base) --> ?1>", "(/,neutralization,_,base)", "(/,reaction,_,base)" => "<(/,neutralization,_,base) --> (/,reaction,_,base)>";
2190                "<(/,neutralization,_,base) --> ?1>", "(/,reaction,_,base)", "?1" => "<(/,reaction,_,base) --> ?1>";
2191                "<(/,neutralization,_,base) --> ?1>", "?1", "(/,reaction,_,base)" => "<?1 --> (/,reaction,_,base)>";
2192                "<(/,neutralization,acid,_) <-> (/,reaction,acid,_)>", "acid", "acid" => None;
2193                "<(/,num,_) --> num>", "(*,(/,num,_))", "(*,num)" => "<(*,(/,num,_)) --> (*,num)>";
2194                "<(/,open,_,lock) --> (&,key,(/,open,_,{lock1}))>", "(/,open,_,lock)", "(/,open,_,{lock1})" => "<(/,open,_,lock) --> (/,open,_,{lock1})>";
2195                "<(/,open,_,lock) --> (&,key,(/,open,_,{lock1}))>", "(/,open,_,lock)", "key" => "<(/,open,_,lock) --> key>";
2196                "<(/,open,_,lock) --> (/,open,_,{lock1})>", "(/,open,_,lock)", "(&,key,(/,open,_,{lock1}))" => "<(/,open,_,lock) --> (&,key,(/,open,_,{lock1}))>";
2197                "<(/,open,_,lock) --> (/,open,_,{lock1})>", "(/,open,_,lock)", "(|,key,(/,open,_,{lock1}))" => "<(/,open,_,lock) --> (|,key,(/,open,_,{lock1}))>";
2198                "<(/,open,_,lock) --> (/,open,_,{lock1})>", "(/,open,_,{lock1})", "key" => "<(/,open,_,{lock1}) --> key>";
2199                "<(/,open,_,lock) --> (/,open,_,{lock1})>", "key", "(/,open,_,{lock1})" => "<key --> (/,open,_,{lock1})>";
2200                "<(/,open,_,lock) --> (/,open,_,{lock1})>", "open", "open" => None;
2201                "<(/,open,_,lock) --> (/,open,_,{lock1})>", "{lock1}", "lock" => "<{lock1} --> lock>";
2202                "<(/,open,_,lock) --> key>", "(/,open,_,lock)", "(&,key,(/,open,_,{lock1}))" => "<(/,open,_,lock) --> (&,key,(/,open,_,{lock1}))>";
2203                "<(/,open,_,lock) --> key>", "(/,open,_,lock)", "(|,key,(/,open,_,{lock1}))" => "<(/,open,_,lock) --> (|,key,(/,open,_,{lock1}))>";
2204                "<(/,open,_,lock) --> key>", "(/,open,_,{lock1})", "key" => "<(/,open,_,{lock1}) --> key>";
2205                "<(/,open,_,lock) --> key>", "key", "(/,open,_,{lock1})" => "<key --> (/,open,_,{lock1})>";
2206                "<(/,reaction,acid,_) --> soda>", "(/,neutralization,acid,_)", "soda" => "<(/,neutralization,acid,_) --> soda>";
2207                "<(/,reaction,acid,_) --> soda>", "(/,reaction,acid,_)", "(&,soda,(/,neutralization,acid,_))" => "<(/,reaction,acid,_) --> (&,soda,(/,neutralization,acid,_))>";
2208                "<(/,reaction,acid,_) --> soda>", "(/,reaction,acid,_)", "(|,soda,(/,neutralization,acid,_))" => "<(/,reaction,acid,_) --> (|,soda,(/,neutralization,acid,_))>";
2209                "<(/,reaction,acid,_) --> soda>", "soda", "(/,neutralization,acid,_)" => "<soda --> (/,neutralization,acid,_)>";
2210                "<(|,acid,(/,neutralization,_,base)) --> (/,reaction,_,base)>", "(/,neutralization,_,base)", "(/,reaction,_,base)" => "<(/,neutralization,_,base) --> (/,reaction,_,base)>";
2211                "<(|,acid,(/,neutralization,_,base)) --> (/,reaction,_,base)>", "acid", "(/,reaction,_,base)" => "<acid --> (/,reaction,_,base)>";
2212                "<(|,bird,robin) --> animal>", "bird", "animal" => "<bird --> animal>";
2213                "<(|,bird,{Tweety}) --> (|,bird,flyer)>", "bird", "bird" => None;
2214                "<(|,bird,{Tweety}) --> (|,bird,flyer)>", "{Tweety}", "flyer" => "<{Tweety} --> flyer>";
2215                "<(|,bird,{Tweety}) --> (|,bird,{Birdie})>", "bird", "bird" => None;
2216                "<(|,bird,{Tweety}) --> (|,bird,{Birdie})>", "{Tweety}", "{Birdie}" => "<{Tweety} --> {Birdie}>";
2217                "<(|,boy,girl) --> (|,girl,youth)>", "(|,boy,girl)", "(|,girl,youth)" => "<(|,boy,girl) --> (|,girl,youth)>";
2218                "<(|,boy,girl) --> (|,girl,youth)>", "boy", "girl" => "<boy --> girl>";
2219                "<(|,boy,girl) --> (~,youth,girl)>", "(~,youth,girl)", "(|,boy,girl)" => "<(~,youth,girl) --> (|,boy,girl)>";
2220                "<(|,boy,girl) --> youth>", "(|,boy,girl)", "(~,youth,girl)" => "<(|,boy,girl) --> (~,youth,girl)>";
2221                "<(|,boy,girl) --> youth>", "(|,boy,girl)", "youth" => "<(|,boy,girl) --> youth>";
2222                "<(|,boy,girl) --> youth>", "(~,(|,boy,girl),girl)", "(~,youth,girl)" => "<(~,(|,boy,girl),girl) --> (~,youth,girl)>";
2223                "<(|,boy,girl) --> youth>", "youth", "(|,boy,girl)" => "<youth --> (|,boy,girl)>";
2224                "<(|,chess,sport) --> (|,chess,competition)>", "chess", "chess" => None;
2225                "<(|,chess,sport) --> competition>", "(|,chess,sport)", "(|,chess,competition)" => "<(|,chess,sport) --> (|,chess,competition)>";
2226                "<(|,robin,swan) --> (&,bird,swimmer)>", "(|,robin,swan)", "bird" => "<(|,robin,swan) --> bird>";
2227                "<(|,robin,swan) --> (|,bird,swimmer)>", "robin", "(|,bird,swimmer)" => "<robin --> (|,bird,swimmer)>";
2228                "<(|,robin,swimmer) --> bird>", "(|,robin,swimmer)", "(&,animal,bird)" => "<(|,robin,swimmer) --> (&,animal,bird)>";
2229                "<(|,robin,{Tweety}) --> [with_wings]>", "robin", "[with_wings]" => "<robin --> [with_wings]>";
2230                "<(|,robin,{Tweety}) --> [with_wings]>", "{Tweety}", "[with_wings]" => "<{Tweety} --> [with_wings]>";
2231                "<(~,boy,girl) --> (&,[strong],(~,youth,girl))>", "(~,boy,girl)", "(&,[strong],(~,youth,girl))" => "<(~,boy,girl) --> (&,[strong],(~,youth,girl))>";
2232                "<(~,boy,girl) --> (~,youth,girl)>", "boy", "(~,youth,girl)" => "<boy --> (~,youth,girl)>";
2233                "<(~,boy,girl) --> (~,youth,girl)>", "boy", "youth" => "<boy --> youth>";
2234                "<(~,boy,girl) --> (~,youth,girl)>", "girl", "(~,youth,girl)" => None;
2235                "<(~,boy,girl) --> (~,youth,girl)>", "girl", "girl" => None;
2236                "<(~,boy,girl) --> [strong]>", "(~,boy,girl)", "(&,[strong],(~,youth,girl))" => "<(~,boy,girl) --> (&,[strong],(~,youth,girl))>";
2237                "<(~,boy,girl) --> [strong]>", "(~,boy,girl)", "(|,[strong],(~,youth,girl))" => "<(~,boy,girl) --> (|,[strong],(~,youth,girl))>";
2238                "<(~,boy,girl) --> [strong]>", "(~,boy,girl)", "[strong]" => "<(~,boy,girl) --> [strong]>";
2239                "<(~,boy,girl) --> [strong]>", "[strong]", "(~,youth,girl)" => "<[strong] --> (~,youth,girl)>";
2240                "<(~,boy,girl) --> [strong]>", "boy", "[strong]" => "<boy --> [strong]>";
2241                "<0 --> (/,num,_)>", "(*,0)", "(*,(/,num,_))" => "<(*,0) --> (*,(/,num,_))>";
2242                "<0 --> (/,num,_)>", "(/,num,_)", "num" => "<(/,num,_) --> num>";
2243                "<0 --> (/,num,_)>", "0", "num" => "<0 --> num>";
2244                "<0 --> (/,num,_)>", "num", "(/,num,_)" => "<num --> (/,num,_)>";
2245                "<0 --> num>", "(*,0)", "(*,num)" => "<(*,0) --> (*,num)>";
2246                "<0 --> num>", "(/,num,_)", "num" => "<(/,num,_) --> num>";
2247                "<0 --> num>", "num", "(/,num,_)" => "<num --> (/,num,_)>";
2248                "<<robin --> [with_wings]> ==> <robin --> [living]>>", "<robin --> flyer>", "<robin --> [living]>" => "<<robin --> flyer> ==> <robin --> [living]>>";
2249                "<<robin --> [with_wings]> ==> <robin --> bird>>", "<robin --> [with_wings]>", "(&&,<robin --> bird>,<robin --> [living]>)" => "<<robin --> [with_wings]> ==> (&&,<robin --> bird>,<robin --> [living]>)>";
2250                "<<robin --> [with_wings]> ==> <robin --> bird>>", "<robin --> [with_wings]>", "(||,<robin --> bird>,<robin --> [living]>)" => "<<robin --> [with_wings]> ==> (||,<robin --> bird>,<robin --> [living]>)>";
2251                "<<robin --> [with_wings]> ==> <robin --> bird>>", "<robin --> flyer>", "<robin --> bird>" => "<<robin --> flyer> ==> <robin --> bird>>";
2252                "<?1 --> claimedByBob>", "(&,<bird --> fly>,<{Tweety} --> bird>)", "?1" => "<(&,<bird --> fly>,<{Tweety} --> bird>) --> ?1>";
2253                "<?1 --> claimedByBob>", "?1", "(&,<bird --> fly>,<{Tweety} --> bird>)" => "<?1 --> (&,<bird --> fly>,<{Tweety} --> bird>)>";
2254                "<?1 --> swimmer>", "?1", "animal" => "<?1 --> animal>";
2255                "<?1 --> swimmer>", "animal", "?1" => "<animal --> ?1>";
2256                "<?1 --> swimmer>", "animal", "swimmer" => "<animal --> swimmer>";
2257                "<Birdie <-> Tweety>", "Birdie", "Tweety" => "<Birdie <-> Tweety>";
2258                "<Birdie <-> Tweety>", "{Birdie}", "{Tweety}" => "<{Birdie} <-> {Tweety}>";
2259                "<CAT --> (/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)>", "CAT", "(|,CAT,(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))" => None;
2260                "<[bright] --> [smart]>", "[smart]", "[bright]" => "<[smart] --> [bright]>";
2261                "<[bright] <-> [smart]>", "bright", "smart" => "<bright <-> smart>";
2262                "<[with_wings] --> {Birdie}>", "[with_wings]", "{Tweety}" => "<[with_wings] --> {Tweety}>";
2263                "<[yellow] --> {Birdie}>", "(|,flyer,[yellow])", "(|,flyer,{Birdie})" => "<(|,flyer,[yellow]) --> (|,flyer,{Birdie})>";
2264                "<[yellow] <-> {Birdie}>", "(|,flyer,[yellow])", "(|,flyer,{Birdie})" => "<(|,flyer,[yellow]) <-> (|,flyer,{Birdie})>";
2265                "<[yellow] <-> {Birdie}>", "[yellow]", "{Tweety}" => "<[yellow] <-> {Tweety}>";
2266                "<a --> (/,like,b,_)>", "(*,a,b)", "(*,(/,like,b,_),b)" => "<(*,a,b) --> (*,(/,like,b,_),b)>";
2267                "<a --> (/,like,b,_)>", "(*,b,a)", "(*,b,(/,like,b,_))" => "<(*,b,a) --> (*,b,(/,like,b,_))>";
2268                "<a --> (/,like,b,_)>", "(/,like,_,(/,like,b,_))", "(/,like,_,a)" => "<(/,like,_,(/,like,b,_)) --> (/,like,_,a)>";
2269                "<acid --> (/,reaction,_,base)>", "(&,acid,(/,neutralization,_,base))", "(/,reaction,_,base)" => "<(&,acid,(/,neutralization,_,base)) --> (/,reaction,_,base)>";
2270                "<acid --> (/,reaction,_,base)>", "(/,neutralization,_,base)", "acid" => "<(/,neutralization,_,base) --> acid>";
2271                "<acid --> (/,reaction,_,base)>", "(|,acid,(/,neutralization,_,base))", "(/,reaction,_,base)" => "<(|,acid,(/,neutralization,_,base)) --> (/,reaction,_,base)>";
2272                "<acid --> (/,reaction,_,base)>", "acid", "(/,neutralization,_,base)" => "<acid --> (/,neutralization,_,base)>";
2273                "<b --> (/,like,_,a)>", "(/,like,(/,like,_,a),_)", "(/,like,b,_)" => "<(/,like,(/,like,_,a),_) --> (/,like,b,_)>";
2274                "<bird --> (&,animal,swimmer)>", "bird", "animal" => "<bird --> animal>";
2275                "<bird --> animal>", "(&,bird,robin)", "animal" => "<(&,bird,robin) --> animal>";
2276                "<bird --> animal>", "(|,bird,robin)", "animal" => "<(|,bird,robin) --> animal>";
2277                "<bird --> animal>", "bird", "robin" => "<bird --> robin>";
2278                "<bird --> swimmer>", "bird", "(&,animal,swimmer)" => "<bird --> (&,animal,swimmer)>";
2279                "<bird --> swimmer>", "bird", "(|,animal,swimmer)" => "<bird --> (|,animal,swimmer)>";
2280                "<bird --> {Birdie}>", "bird", "(|,bird,{Birdie})" => None;
2281                "<boy --> [strong]>", "(~,boy,girl)", "(~,[strong],girl)" => "<(~,boy,girl) --> (~,[strong],girl)>";
2282                "<boy --> youth>", "(|,boy,girl)", "(|,girl,youth)" => "<(|,boy,girl) --> (|,girl,youth)>";
2283                "<boy --> youth>", "(~,boy,girl)", "(~,youth,girl)" => "<(~,boy,girl) --> (~,youth,girl)>";
2284                "<bright <-> smart>", "[bright]", "[smart]" => "<[bright] <-> [smart]>";
2285                "<cat --> (&,CAT,(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))>", "cat", "(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)" => "<cat --> (/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)>";
2286                "<cat --> (&,CAT,(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))>", "cat", "CAT" => "<cat --> CAT>";
2287                "<cat --> CAT>", "(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)", "CAT" => "<(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish) --> CAT>";
2288                "<cat --> CAT>", "CAT", "(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)" => "<CAT --> (/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)>";
2289                "<cat --> CAT>", "cat", "(&,CAT,(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))" => "<cat --> (&,CAT,(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))>";
2290                "<cat --> CAT>", "cat", "(|,CAT,(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))" => "<cat --> (|,CAT,(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))>";
2291                "<chess --> competition>", "(~,sport,chess)", "competition" => "<(~,sport,chess) --> competition>";
2292                "<chess --> competition>", "chess", "(|,chess,competition)" => None;
2293                "<flyer <-> [with_wings]>", "(|,flyer,{Birdie})", "(|,[with_wings],{Birdie})" => "<(|,flyer,{Birdie}) <-> (|,[with_wings],{Birdie})>";
2294                "<neutralization --> (*,acid,base)>", "neutralization", "reaction" => "<neutralization --> reaction>";
2295                "<neutralization --> reaction>", "(/,neutralization,_,base)", "(/,reaction,_,base)" => "<(/,neutralization,_,base) --> (/,reaction,_,base)>";
2296                "<neutralization <-> reaction>", "(/,neutralization,_,base)", "(/,reaction,_,base)" => "<(/,neutralization,_,base) <-> (/,reaction,_,base)>";
2297                "<num <-> (/,num,_)>", "(*,num)", "(*,(/,num,_))" => "<(*,num) <-> (*,(/,num,_))>";
2298                "<num <-> (/,num,_)>", "(/,num,_)", "(/,(/,num,_),_)" => "<(/,num,_) <-> (/,(/,num,_),_)>";
2299                "<planetX --> {Mars,Pluto,Saturn,Venus}>", "{Mars,Pluto,Saturn,Venus}", "{Mars,Pluto,Venus}" => "<{Mars,Pluto,Saturn,Venus} --> {Mars,Pluto,Venus}>";
2300                "<planetX --> {Mars,Pluto,Saturn,Venus}>", "{Mars,Pluto,Venus}", "{Mars,Pluto,Saturn,Venus}" => "<{Mars,Pluto,Venus} --> {Mars,Pluto,Saturn,Venus}>";
2301                "<planetX --> {Mars,Pluto,Venus}>", "planetX", "{Mars,Pluto,Saturn,Venus}" => "<planetX --> {Mars,Pluto,Saturn,Venus}>";
2302                "<planetX --> {Mars,Pluto,Venus}>", "planetX", "{Mars,Venus}" => "<planetX --> {Mars,Venus}>";
2303                "<planetX --> {Mars,Pluto,Venus}>", "planetX", "{Pluto}" => "<planetX --> {Pluto}>";
2304                "<planetX --> {Mars,Pluto,Venus}>", "{Mars,Pluto,Venus}", "{Pluto,Saturn}" => "<{Mars,Pluto,Venus} --> {Pluto,Saturn}>";
2305                "<planetX --> {Mars,Pluto,Venus}>", "{Pluto,Saturn}", "{Mars,Pluto,Venus}" => "<{Pluto,Saturn} --> {Mars,Pluto,Venus}>";
2306                "<planetX --> {Mars,Venus}>", "planetX", "{Mars,Pluto,Saturn,Venus}" => "<planetX --> {Mars,Pluto,Saturn,Venus}>";
2307                "<planetX --> {Mars,Venus}>", "{Mars,Venus}", "{Pluto,Saturn}" => "<{Mars,Venus} --> {Pluto,Saturn}>";
2308                "<planetX --> {Pluto,Saturn}>", "planetX", "{Mars,Venus}" => "<planetX --> {Mars,Venus}>";
2309                "<planetX --> {Pluto,Saturn}>", "planetX", "{Pluto}" => "<planetX --> {Pluto}>";
2310                "<planetX --> {Pluto,Saturn}>", "{Mars,Pluto,Saturn,Venus}", "{Pluto,Saturn}" => "<{Mars,Pluto,Saturn,Venus} --> {Pluto,Saturn}>";
2311                "<planetX --> {Pluto,Saturn}>", "{Mars,Pluto,Venus}", "{Pluto,Saturn}" => "<{Mars,Pluto,Venus} --> {Pluto,Saturn}>";
2312                "<planetX --> {Pluto,Saturn}>", "{Pluto,Saturn}", "{Mars,Pluto,Saturn,Venus}" => "<{Pluto,Saturn} --> {Mars,Pluto,Saturn,Venus}>";
2313                "<planetX --> {Pluto,Saturn}>", "{Pluto,Saturn}", "{Mars,Pluto,Venus}" => "<{Pluto,Saturn} --> {Mars,Pluto,Venus}>";
2314                "<robin --> (-,bird,swimmer)>", "robin", "bird" => "<robin --> bird>";
2315                "<robin --> (|,bird,swimmer)>", "(&,robin,swan)", "(|,bird,swimmer)" => "<(&,robin,swan) --> (|,bird,swimmer)>";
2316                "<robin --> (|,bird,swimmer)>", "(|,robin,swan)", "(|,bird,swimmer)" => "<(|,robin,swan) --> (|,bird,swimmer)>";
2317                "<robin --> (|,bird,swimmer)>", "robin", "swan" => "<robin --> swan>";
2318                "<robin --> [with_wings]>", "(&,flyer,robin)", "(&,flyer,[with_wings])" => "<(&,flyer,robin) --> (&,flyer,[with_wings])>";
2319                "<robin --> [with_wings]>", "(&,robin,{Birdie})", "[with_wings]" => "<(&,robin,{Birdie}) --> [with_wings]>";
2320                "<robin --> [with_wings]>", "(|,flyer,robin)", "(|,flyer,[with_wings])" => "<(|,flyer,robin) --> (|,flyer,[with_wings])>";
2321                "<robin --> [with_wings]>", "(|,robin,{Birdie})", "(|,[with_wings],{Birdie})" => "<(|,robin,{Birdie}) --> (|,[with_wings],{Birdie})>";
2322                "<robin --> [with_wings]>", "(|,robin,{Birdie})", "[with_wings]" => "<(|,robin,{Birdie}) --> [with_wings]>";
2323                "<robin --> [with_wings]>", "robin", "(|,[with_wings],{Birdie})" => "<robin --> (|,[with_wings],{Birdie})>";
2324                "<robin --> [with_wings]>", "robin", "(|,flyer,[with_wings])" => "<robin --> (|,flyer,[with_wings])>";
2325                "<robin --> [with_wings]>", "robin", "flyer" => "<robin --> flyer>";
2326                "<robin --> [with_wings]>", "robin", "{Birdie}" => "<robin --> {Birdie}>";
2327                "<robin --> [with_wings]>", "{Birdie}", "robin" => "<{Birdie} --> robin>";
2328                "<soda --> base>", "(/,reaction,acid,_)", "soda" => "<(/,reaction,acid,_) --> soda>";
2329                "<soda --> base>", "soda", "(/,reaction,acid,_)" => "<soda --> (/,reaction,acid,_)>";
2330                "<swan --> (&,bird,swimmer)>", "(&,robin,swan)", "(&,bird,swimmer)" => "<(&,robin,swan) --> (&,bird,swimmer)>";
2331                "<swan --> (&,bird,swimmer)>", "(|,robin,swan)", "(&,bird,swimmer)" => "<(|,robin,swan) --> (&,bird,swimmer)>";
2332                "<swan --> swimmer>", "(&,swan,swimmer)", "swimmer" => None;
2333                "<swan --> swimmer>", "(~,swimmer,swan)", "swimmer" => None;
2334                "<tiger --> animal>", "(&,robin,tiger)", "(&,animal,robin)" => "<(&,robin,tiger) --> (&,animal,robin)>";
2335                "<tim --> (/,uncle,_,tom)>", "(/,uncle,_,tom)", "(/,uncle,tom,_)" => "<(/,uncle,_,tom) --> (/,uncle,tom,_)>";
2336                "<tim --> (/,uncle,tom,_)>", "(&,tim,(/,(*,tim,tom),tom,_))", "(/,uncle,tom,_)" => "<(&,tim,(/,(*,tim,tom),tom,_)) --> (/,uncle,tom,_)>";
2337                "<tim --> (/,uncle,tom,_)>", "(/,(*,tim,tom),tom,_)", "tim" => "<(/,(*,tim,tom),tom,_) --> tim>";
2338                "<tim --> (/,uncle,tom,_)>", "(|,tim,(/,(*,tim,tom),tom,_))", "(/,uncle,tom,_)" => "<(|,tim,(/,(*,tim,tom),tom,_)) --> (/,uncle,tom,_)>";
2339                "<tim --> (/,uncle,tom,_)>", "(~,(/,(*,tim,tom),tom,_),tim)", "(/,uncle,tom,_)" => "<(~,(/,(*,tim,tom),tom,_),tim) --> (/,uncle,tom,_)>";
2340                "<tim --> (/,uncle,tom,_)>", "tim", "(/,(*,tim,tom),tom,_)" => "<tim --> (/,(*,tim,tom),tom,_)>";
2341                "<{?1} --> swimmer>", "robin", "{?1}" => "<robin --> {?1}>";
2342                "<{?1} --> swimmer>", "{?1}", "bird" => "<{?1} --> bird>";
2343                "<{Birdie} --> [with_wings]>", "{Tweety}", "[with_wings]" => "<{Tweety} --> [with_wings]>";
2344                "<{Birdie} --> [yellow]>", "(&,flyer,{Birdie})", "(&,flyer,[yellow])" => "<(&,flyer,{Birdie}) --> (&,flyer,[yellow])>";
2345                "<{Birdie} --> [yellow]>", "(|,flyer,{Birdie})", "(|,flyer,[yellow])" => "<(|,flyer,{Birdie}) --> (|,flyer,[yellow])>";
2346                "<{Birdie} --> [yellow]>", "{Birdie}", "(|,[yellow],{Birdie})" => None;
2347                "<{Birdie} --> [yellow]>", "{Birdie}", "(|,flyer,[yellow])" => "<{Birdie} --> (|,flyer,[yellow])>";
2348                "<{Birdie} --> flyer>", "(&,flyer,{Birdie})", "flyer" => None;
2349                "<{Birdie} --> flyer>", "{Tweety}", "flyer" => "<{Tweety} --> flyer>";
2350                "<{Birdie} <-> {Tweety}>", "Birdie", "Tweety" => "<Birdie <-> Tweety>";
2351                "<{Birdie} <-> {Tweety}>", "{Birdie}", "{Tweety}" => "<{Birdie} <-> {Tweety}>";
2352                "<{Birdie} <-> {Tweety}>", "{Tweety}", "bird" => "<bird <-> {Tweety}>";
2353                "<{Mars,Pluto,Saturn,Venus} --> {Mars,Pluto,Venus}>", "{Pluto}", "{Mars,Pluto,Venus}" => "<{Pluto} --> {Mars,Pluto,Venus}>";
2354                "<{Tweety} --> (&,[with_wings],{Birdie})>", "{Tweety}", "[with_wings]" => "<{Tweety} --> [with_wings]>";
2355                "<{Tweety} --> (&,[with_wings],{Birdie})>", "{Tweety}", "{Birdie}" => "<{Tweety} --> {Birdie}>";
2356                "<{Tweety} --> (&,bird,flyer)>", "{Tweety}", "bird" => "<{Tweety} --> bird>";
2357                "<{Tweety} --> (&,bird,{Birdie})>", "{Tweety}", "bird" => "<{Tweety} --> bird>";
2358                "<{Tweety} --> (&,bird,{Birdie})>", "{Tweety}", "{Birdie}" => "<{Tweety} --> {Birdie}>";
2359                "<{Tweety} --> (&,flyer,(|,[yellow],{Birdie}))>", "{Tweety}", "(|,[yellow],{Birdie})" => "<{Tweety} --> (|,[yellow],{Birdie})>";
2360                "<{Tweety} --> (&,flyer,(|,[yellow],{Birdie}))>", "{Tweety}", "flyer" => "<{Tweety} --> flyer>";
2361                "<{Tweety} --> (&,flyer,[with_wings])>", "{Tweety}", "[with_wings]" => "<{Tweety} --> [with_wings]>";
2362                "<{Tweety} --> (&,flyer,[with_wings])>", "{Tweety}", "flyer" => "<{Tweety} --> flyer>";
2363                "<{Tweety} --> (|,[with_wings],{Birdie})>", "(&,flyer,[yellow])", "(|,[with_wings],{Birdie})" => "<(&,flyer,[yellow]) --> (|,[with_wings],{Birdie})>";
2364                "<{Tweety} --> (|,[with_wings],{Birdie})>", "(|,[with_wings],{Birdie})", "(&,flyer,[yellow])" => "<(|,[with_wings],{Birdie}) --> (&,flyer,[yellow])>";
2365                "<{Tweety} --> (|,[with_wings],{Birdie})>", "{Tweety}", "(&,flyer,[yellow],(|,[with_wings],{Birdie}))" => "<{Tweety} --> (&,flyer,[yellow],(|,[with_wings],{Birdie}))>";
2366                "<{Tweety} --> (|,[with_wings],{Birdie})>", "{Tweety}", "(|,[with_wings],{Birdie},(&,flyer,[yellow]))" => "<{Tweety} --> (|,[with_wings],{Birdie},(&,flyer,[yellow]))>";
2367                "<{Tweety} --> (|,bird,flyer)>", "(|,bird,flyer)", "(|,bird,{Birdie})" => "<(|,bird,flyer) --> (|,bird,{Birdie})>";
2368                "<{Tweety} --> (|,bird,flyer)>", "(|,bird,{Birdie})", "(|,bird,flyer)" => "<(|,bird,{Birdie}) --> (|,bird,flyer)>";
2369                "<{Tweety} --> (|,bird,flyer)>", "{Tweety}", "(&,(|,bird,flyer),(|,bird,{Birdie}))" => "<{Tweety} --> (&,(|,bird,flyer),(|,bird,{Birdie}))>";
2370                "<{Tweety} --> (|,bird,flyer)>", "{Tweety}", "(|,bird,flyer,{Birdie})" => "<{Tweety} --> (|,bird,flyer,{Birdie})>";
2371                "<{Tweety} --> (|,flyer,[yellow])>", "bird", "(|,flyer,[yellow])" => "<bird --> (|,flyer,[yellow])>";
2372                "<{Tweety} --> [with_wings]>", "(&,flyer,{Birdie})", "[with_wings]" => "<(&,flyer,{Birdie}) --> [with_wings]>";
2373                "<{Tweety} --> [with_wings]>", "(|,flyer,{Birdie})", "[with_wings]" => "<(|,flyer,{Birdie}) --> [with_wings]>";
2374                "<{Tweety} --> [with_wings]>", "[with_wings]", "(&,flyer,{Birdie})" => "<[with_wings] --> (&,flyer,{Birdie})>";
2375                "<{Tweety} --> [with_wings]>", "[with_wings]", "(|,flyer,{Birdie})" => "<[with_wings] --> (|,flyer,{Birdie})>";
2376                "<{Tweety} --> [with_wings]>", "[with_wings]", "flyer" => "<[with_wings] --> flyer>";
2377                "<{Tweety} --> [with_wings]>", "flyer", "[with_wings]" => "<flyer --> [with_wings]>";
2378                "<{Tweety} --> [with_wings]>", "robin", "{Tweety}" => "<robin --> {Tweety}>";
2379                "<{Tweety} --> [with_wings]>", "{Birdie,Tweety}", "(|,[with_wings],{Birdie})" => "<{Birdie,Tweety} --> (|,[with_wings],{Birdie})>";
2380                "<{Tweety} --> [with_wings]>", "{Tweety}", "(&,[with_wings],(|,flyer,{Birdie}))" => "<{Tweety} --> (&,[with_wings],(|,flyer,{Birdie}))>";
2381                "<{Tweety} --> [with_wings]>", "{Tweety}", "(&,flyer,[with_wings])" => "<{Tweety} --> (&,flyer,[with_wings])>";
2382                "<{Tweety} --> [with_wings]>", "{Tweety}", "(&,flyer,[with_wings],{Birdie})" => "<{Tweety} --> (&,flyer,[with_wings],{Birdie})>";
2383                "<{Tweety} --> [with_wings]>", "{Tweety}", "(|,[with_wings],(&,flyer,{Birdie}))" => "<{Tweety} --> (|,[with_wings],(&,flyer,{Birdie}))>";
2384                "<{Tweety} --> [with_wings]>", "{Tweety}", "(|,[with_wings],{Birdie})" => "<{Tweety} --> (|,[with_wings],{Birdie})>";
2385                "<{Tweety} --> [with_wings]>", "{Tweety}", "(|,flyer,[with_wings],{Birdie})" => "<{Tweety} --> (|,flyer,[with_wings],{Birdie})>";
2386                "<{Tweety} --> [with_wings]>", "{Tweety}", "robin" => "<{Tweety} --> robin>";
2387                "<{Tweety} --> bird>", "bird", "flyer" => "<bird --> flyer>";
2388                "<{Tweety} --> bird>", "bird", "{Birdie}" => "<bird --> {Birdie}>";
2389                "<{Tweety} --> bird>", "{Tweety}", "(&,bird,flyer)" => "<{Tweety} --> (&,bird,flyer)>";
2390                "<{Tweety} --> bird>", "{Tweety}", "(&,bird,{Birdie})" => "<{Tweety} --> (&,bird,{Birdie})>";
2391                "<{Tweety} --> bird>", "{Tweety}", "(|,bird,flyer)" => "<{Tweety} --> (|,bird,flyer)>";
2392                "<{Tweety} --> bird>", "{Tweety}", "(|,bird,{Birdie})" => "<{Tweety} --> (|,bird,{Birdie})>";
2393                "<{Tweety} --> flyer>", "(&,[with_wings],{Birdie})", "flyer" => "<(&,[with_wings],{Birdie}) --> flyer>";
2394                "<{Tweety} --> flyer>", "(|,[with_wings],{Birdie})", "flyer" => "<(|,[with_wings],{Birdie}) --> flyer>";
2395                "<{Tweety} --> flyer>", "[with_wings]", "flyer" => "<[with_wings] --> flyer>";
2396                "<{Tweety} --> flyer>", "flyer", "(&,[with_wings],{Birdie})" => "<flyer --> (&,[with_wings],{Birdie})>";
2397                "<{Tweety} --> flyer>", "flyer", "(|,[with_wings],{Birdie})" => "<flyer --> (|,[with_wings],{Birdie})>";
2398                "<{Tweety} --> flyer>", "flyer", "[with_wings]" => "<flyer --> [with_wings]>";
2399                "<{Tweety} --> flyer>", "{Tweety}", "(&,flyer,(|,[with_wings],{Birdie}))" => "<{Tweety} --> (&,flyer,(|,[with_wings],{Birdie}))>";
2400                "<{Tweety} --> flyer>", "{Tweety}", "(&,flyer,[with_wings])" => "<{Tweety} --> (&,flyer,[with_wings])>";
2401                "<{Tweety} --> flyer>", "{Tweety}", "(&,flyer,[with_wings],{Birdie})" => "<{Tweety} --> (&,flyer,[with_wings],{Birdie})>";
2402                "<{Tweety} --> flyer>", "{Tweety}", "(|,flyer,(&,[with_wings],{Birdie}))" => "<{Tweety} --> (|,flyer,(&,[with_wings],{Birdie}))>";
2403                "<{Tweety} --> flyer>", "{Tweety}", "(|,flyer,[with_wings])" => "<{Tweety} --> (|,flyer,[with_wings])>";
2404                "<{Tweety} --> flyer>", "{Tweety}", "(|,flyer,[with_wings],{Birdie})" => "<{Tweety} --> (|,flyer,[with_wings],{Birdie})>";
2405                "<{Tweety} --> {Birdie}>", "(|,bird,{Tweety})", "(|,bird,{Birdie})" => "<(|,bird,{Tweety}) --> (|,bird,{Birdie})>";
2406                "<{Tweety} --> {Birdie}>", "[with_wings]", "{Birdie}" => "<[with_wings] --> {Birdie}>";
2407                "<{Tweety} --> {Birdie}>", "bird", "{Birdie}" => "<bird --> {Birdie}>";
2408                "<{Tweety} --> {Birdie}>", "{Birdie}", "[with_wings]" => "<{Birdie} --> [with_wings]>";
2409                "<{Tweety} --> {Birdie}>", "{Birdie}", "bird" => "<{Birdie} --> bird>";
2410                "<{Tweety} --> {Birdie}>", "{Tweety}", "(&,[with_wings],{Birdie})" => "<{Tweety} --> (&,[with_wings],{Birdie})>";
2411                "<{Tweety} --> {Birdie}>", "{Tweety}", "(&,bird,{Birdie})" => "<{Tweety} --> (&,bird,{Birdie})>";
2412                "<{Tweety} --> {Birdie}>", "{Tweety}", "(|,[with_wings],{Birdie})" => "<{Tweety} --> (|,[with_wings],{Birdie})>";
2413                "<{Tweety} --> {Birdie}>", "{Tweety}", "(|,bird,{Birdie})" => "<{Tweety} --> (|,bird,{Birdie})>";
2414                "<{key1} --> (&,key,(/,open,_,{lock1}))>", "{key1}", "(/,open,_,{lock1})" => "<{key1} --> (/,open,_,{lock1})>";
2415                "<{key1} --> (&,key,(/,open,_,{lock1}))>", "{key1}", "key" => "<{key1} --> key>";
2416                "<{key1} --> (/,open,_,{lock1})>", "(/,open,_,{lock1})", "key" => "<(/,open,_,{lock1}) --> key>";
2417                "<{key1} --> (/,open,_,{lock1})>", "key", "(/,open,_,{lock1})" => "<key --> (/,open,_,{lock1})>";
2418                "<{key1} --> (/,open,_,{lock1})>", "{key1}", "(&,key,(/,open,_,{lock1}))" => "<{key1} --> (&,key,(/,open,_,{lock1}))>";
2419                "<{key1} --> (/,open,_,{lock1})>", "{key1}", "(|,key,(/,open,_,{lock1}))" => "<{key1} --> (|,key,(/,open,_,{lock1}))>";
2420                "<{key1} --> (|,key,(/,open,_,{lock1}))>", "{key1}", "(/,open,_,{lock1})" => "<{key1} --> (/,open,_,{lock1})>";
2421                "<{key1} --> (|,key,(/,open,_,{lock1}))>", "{key1}", "(|,key,(/,open,_,{lock1}))" => "<{key1} --> (|,key,(/,open,_,{lock1}))>";
2422                "<{key1} --> key>", "(/,open,_,{lock1})", "key" => "<(/,open,_,{lock1}) --> key>";
2423                "<{key1} --> key>", "key", "(/,open,_,{lock1})" => "<key --> (/,open,_,{lock1})>";
2424                "<{key1} --> key>", "{key1}", "(&,key,(/,open,_,{lock1}))" => "<{key1} --> (&,key,(/,open,_,{lock1}))>";
2425                "<{key1} --> key>", "{key1}", "(/,open,_,{lock1})" => "<{key1} --> (/,open,_,{lock1})>";
2426                "<{key1} --> key>", "{key1}", "(|,key,(/,open,_,{lock1}))" => "<{key1} --> (|,key,(/,open,_,{lock1}))>";
2427                "<{lock1} --> (&,lock,(/,open,{key1},_))>", "{lock1}", "lock" => "<{lock1} --> lock>";
2428                "<{lock1} --> (|,lock,(/,open,{key1},_))>", "(/,open,_,(|,lock,(/,open,{key1},_)))", "(/,open,_,{lock1})" => "<(/,open,_,(|,lock,(/,open,{key1},_))) --> (/,open,_,{lock1})>";
2429                "<{lock1} --> lock>", "(/,open,_,lock)", "(/,open,_,{lock1})" => "<(/,open,_,lock) --> (/,open,_,{lock1})>";
2430                "<{lock1} --> lock>", "(/,open,{key1},_)", "lock" => "<(/,open,{key1},_) --> lock>";
2431                "<{lock1} --> lock>", "lock", "(/,open,{key1},_)" => "<lock --> (/,open,{key1},_)>";
2432                "<{lock1} --> lock>", "{lock1}", "(&,lock,(/,open,{key1},_))" => "<{lock1} --> (&,lock,(/,open,{key1},_))>";
2433                "<{lock1} --> lock>", "{lock1}", "(|,lock,(/,open,{key1},_))" => "<{lock1} --> (|,lock,(/,open,{key1},_))>";
2434            }
2435            ok!()
2436        }
2437
2438        #[test]
2439        fn make_statement_symmetric() -> AResult {
2440            fn test(template: Term, subject: Term, predicate: Term, expected: Option<Term>) {
2441                let out =
2442                    Term::make_statement_symmetric(&template, subject.clone(), predicate.clone());
2443                assert_eq!(
2444                    out,
2445                    expected,
2446                    "\"{template}\", \"{subject}\", \"{predicate}\" => {} != {}",
2447                    format_option_term(&out),
2448                    format_option_term(&expected),
2449                );
2450            }
2451            macro_once! {
2452                // * 🚩模式:参数列表 ⇒ 预期词项
2453                macro test($($template:tt, $subject:tt, $predicate:tt => $expected:tt;)*) {
2454                    $( test(term!($template), term!($subject), term!($predicate), option_term!($expected)); )*
2455                }
2456                "<(&&,<robin --> [flying]>,<robin --> [with_wings]>) ==> <robin --> bird>>", "<robin --> [living]>", "<robin --> bird>" => "<<robin --> bird> <=> <robin --> [living]>>";
2457                "<(&,bird,[yellow]) --> (&,bird,{Birdie})>", "(&,bird,[yellow])", "{Tweety}" => "<{Tweety} <-> (&,bird,[yellow])>";
2458                "<(&,robin,swan) --> (&,robin,(|,bird,swimmer))>", "bird", "(&,robin,(|,bird,swimmer))" => "<bird <-> (&,robin,(|,bird,swimmer))>";
2459                "<(&,robin,swan) --> bird>", "swimmer", "bird" => "<bird <-> swimmer>";
2460                "<(&,swan,swimmer) --> bird>", "(&,swimmer,(|,bird,robin))", "bird" => "<bird <-> (&,swimmer,(|,bird,robin))>";
2461                "<(*,(*,(*,0))) --> num>", "(*,(*,(*,0)))", "0" => "<0 <-> (*,(*,(*,0)))>";
2462                "<(*,b,a) --> like>", "(*,b,(/,like,_,b))", "like" => "<like <-> (*,b,(/,like,_,b))>";
2463                "<(/,neutralization,_,(/,reaction,acid,_)) --> (/,neutralization,_,base)>", "(/,reaction,_,(/,reaction,acid,_))", "(/,neutralization,_,base)" => "<(/,neutralization,_,base) <-> (/,reaction,_,(/,reaction,acid,_))>";
2464                "<(/,neutralization,_,base) --> (/,(*,acid,base),_,base)>", "(/,neutralization,_,(/,reaction,acid,_))", "(/,(*,acid,base),_,base)" => "<(/,neutralization,_,(/,reaction,acid,_)) <-> (/,(*,acid,base),_,base)>";
2465                "<(/,neutralization,_,base) --> (/,reaction,_,base)>", "(/,neutralization,_,base)", "acid" => "<acid <-> (/,neutralization,_,base)>";
2466                "<(/,neutralization,_,base) --> ?1>", "(/,(*,acid,base),_,base)", "?1" => "<?1 <-> (/,(*,acid,base),_,base)>";
2467                "<(/,neutralization,_,base) --> ?1>", "(/,neutralization,_,(/,reaction,acid,_))", "?1" => "<?1 <-> (/,neutralization,_,(/,reaction,acid,_))>";
2468                "<(/,neutralization,_,base) --> ?1>", "(/,reaction,_,base)", "?1" => "<?1 <-> (/,reaction,_,base)>";
2469                "<(/,open,_,lock) --> (/,open,_,{lock1})>", "(/,open,_,lock)", "{key1}" => "<{key1} <-> (/,open,_,lock)>";
2470                "<(/,reaction,(/,reaction,_,base),_) --> (/,reaction,acid,_)>", "(/,reaction,(/,reaction,_,base),_)", "base" => "<base <-> (/,reaction,(/,reaction,_,base),_)>";
2471                "<(\\,neutralization,_,base) --> acid>", "(/,reaction,_,base)", "acid" => "<acid <-> (/,reaction,_,base)>";
2472                "<(\\,neutralization,acid,_) --> (/,reaction,(/,reaction,_,base),_)>", "base", "(/,reaction,(/,reaction,_,base),_)" => "<base <-> (/,reaction,(/,reaction,_,base),_)>";
2473                "<(\\,neutralization,acid,_) --> (\\,reaction,acid,_)>", "(\\,neutralization,acid,_)", "(\\,reaction,acid,_)" => "<(\\,neutralization,acid,_) <-> (\\,reaction,acid,_)>";
2474                "<(\\,neutralization,acid,_) --> ?1>", "(/,reaction,(/,reaction,_,base),_)", "?1" => "<?1 <-> (/,reaction,(/,reaction,_,base),_)>";
2475                "<(\\,neutralization,acid,_) --> ?1>", "base", "?1" => "<?1 <-> base>";
2476                "<(\\,neutralization,acid,_) --> base>", "(/,reaction,(/,reaction,_,base),_)", "base" => "<base <-> (/,reaction,(/,reaction,_,base),_)>";
2477                "<(|,boy,girl) --> youth>", "(|,girl,[strong])", "youth" => "<youth <-> (|,girl,[strong])>";
2478                "<(|,robin,swan) --> (|,animal,robin)>", "(&,bird,swimmer)", "(|,animal,robin)" => "<(&,bird,swimmer) <-> (|,animal,robin)>";
2479                "<(|,robin,swan) --> (|,animal,robin)>", "(|,bird,robin)", "(|,animal,robin)" => "<(|,animal,robin) <-> (|,bird,robin)>";
2480                "<0 --> num>", "(/,num,_)", "num" => "<num <-> (/,num,_)>";
2481                "<?1 --> claimedByBob>", "?1", "(&,<bird --> fly>,<{Tweety} --> bird>)" => "<?1 <-> (&,<bird --> fly>,<{Tweety} --> bird>)>";
2482                "<?1 --> swimmer>", "?1", "animal" => "<?1 <-> animal>";
2483                "<[bright] --> [smart]>", "[bright]", "[smart]" => "<[bright] <-> [smart]>";
2484                "<bird --> (|,robin,swimmer)>", "bird", "(|,robin,swan)" => "<bird <-> (|,robin,swan)>";
2485                "<bird --> animal>", "bird", "robin" => "<bird <-> robin>";
2486                "<bird --> {Birdie}>", "bird", "[yellow]" => "<bird <-> [yellow]>";
2487                "<bird --> {Birdie}>", "bird", "{Tweety}" => "<bird <-> {Tweety}>";
2488                "<cat --> CAT>", "(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)", "CAT" => "<CAT <-> (/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)>";
2489                "<planetX --> {Mars,Pluto,Saturn,Venus}>", "{Mars,Pluto,Venus}", "{Mars,Pluto,Saturn,Venus}" => "<{Mars,Pluto,Venus} <-> {Mars,Pluto,Saturn,Venus}>";
2490                "<planetX --> {Mars,Pluto,Saturn,Venus}>", "{Pluto,Saturn}", "{Mars,Pluto,Saturn,Venus}" => "<{Pluto,Saturn} <-> {Mars,Pluto,Saturn,Venus}>";
2491                "<planetX --> {Mars,Pluto,Venus}>", "{Mars,Venus}", "{Mars,Pluto,Venus}" => "<{Mars,Venus} <-> {Mars,Pluto,Venus}>";
2492                "<planetX --> {Mars,Venus}>", "{Mars,Pluto,Venus}", "{Mars,Venus}" => "<{Mars,Venus} <-> {Mars,Pluto,Venus}>";
2493                "<planetX --> {Mars,Venus}>", "{Pluto,Saturn}", "{Mars,Venus}" => "<{Mars,Venus} <-> {Pluto,Saturn}>";
2494                "<planetX --> {Pluto,Saturn}>", "{Mars,Pluto,Saturn,Venus}", "{Pluto,Saturn}" => "<{Pluto,Saturn} <-> {Mars,Pluto,Saturn,Venus}>";
2495                "<robin --> (&,bird,swimmer)>", "robin", "swan" => "<robin <-> swan>";
2496                "<robin --> (|,bird,swimmer)>", "robin", "swan" => "<robin <-> swan>";
2497                "<robin --> [chirping]>", "robin", "{Tweety}" => "<robin <-> {Tweety}>";
2498                "<robin --> [with_wings]>", "robin", "bird" => "<bird <-> robin>";
2499                "<swan --> animal>", "(|,bird,robin)", "animal" => "<animal <-> (|,bird,robin)>";
2500                "<tim --> (/,uncle,_,tom)>", "(/,uncle,tom,_)", "(/,uncle,_,tom)" => "<(/,uncle,tom,_) <-> (/,uncle,_,tom)>";
2501                "<tim --> (/,uncle,tom,_)>", "tim", "(/,(*,tim,tom),tom,_)" => "<tim <-> (/,(*,tim,tom),tom,_)>";
2502                "<{Birdie} --> [yellow]>", "bird", "[yellow]" => "<bird <-> [yellow]>";
2503                "<{Birdie} --> {Tweety}>", "{Birdie}", "{Tweety}" => "<{Birdie} <-> {Tweety}>";
2504                "<{Tweety} --> (&,[yellow],{Birdie})>", "bird", "(&,[yellow],{Birdie})" => "<bird <-> (&,[yellow],{Birdie})>";
2505                "<{Tweety} --> (&,bird,[yellow])>", "{Birdie}", "(&,bird,[yellow])" => "<{Birdie} <-> (&,bird,[yellow])>";
2506                "<{Tweety} --> (&,bird,{Birdie})>", "(|,bird,[yellow])", "(&,bird,{Birdie})" => "<(&,bird,{Birdie}) <-> (|,bird,[yellow])>";
2507                "<{Tweety} --> (|,bird,[yellow])>", "{Birdie}", "(|,bird,[yellow])" => "<{Birdie} <-> (|,bird,[yellow])>";
2508                "<{Tweety} --> [chirping]>", "{Tweety}", "robin" => "<robin <-> {Tweety}>";
2509                "<{Tweety} --> [yellow]>", "{Birdie}", "[yellow]" => "<[yellow] <-> {Birdie}>";
2510                "<{Tweety} --> bird>", "[with_wings]", "bird" => "<bird <-> [with_wings]>";
2511                "<{Tweety} --> bird>", "flyer", "bird" => "<bird <-> flyer>";
2512                "<{Tweety} --> bird>", "{Tweety}", "bird" => "<bird <-> {Tweety}>";
2513                "<{Tweety} --> {Birdie}>", "(&,bird,[yellow])", "{Birdie}" => "<{Birdie} <-> (&,bird,[yellow])>";
2514                "<{Tweety} --> {Birdie}>", "bird", "{Birdie}" => "<bird <-> {Birdie}>";
2515                "<{Tweety} --> {Birdie}>", "{Tweety}", "[yellow]" => "<[yellow] <-> {Tweety}>";
2516                "<{Tweety} --> {Birdie}>", "{Tweety}", "bird" => "<bird <-> {Tweety}>";
2517                "<{key1} --> key>", "(/,open,_,{lock1})", "key" => "<key <-> (/,open,_,{lock1})>";
2518                "<{lock1} --> (/,open,{key1},_)>", "lock", "(/,open,{key1},_)" => "<lock <-> (/,open,{key1},_)>";
2519            }
2520            ok!()
2521        }
2522    }
2523}