assembler/ast/
asteval.rs

1//! Evaluate (i.e. generate binary data from) parts of the AST.
2
3use super::super::eval::{Evaluate, ScopeIdentifier};
4use super::{
5    Address, ArithmeticExpression, Atom, CommaDelimitedFragment, Commas, ConfigValue, DEFER_BIT,
6    EqualityValue, EvaluationContext, EvaluationFailure, HereValue, HoldBit, InstructionFragment,
7    LiteralValue, Operator, Origin, RcUpdater, RegisterContaining, RegistersContaining, Script,
8    Shl, Shr, Signed36Bit, SignedAtom, SymbolOrLiteral, TaggedProgramInstruction, Unsigned36Bit,
9    UntaggedProgramInstruction, evaluate_elevated_symbol, u36,
10};
11
12impl Evaluate for LiteralValue {
13    fn evaluate<R: RcUpdater>(
14        &self,
15        _ctx: &mut EvaluationContext<R>,
16        _scope: ScopeIdentifier,
17    ) -> Result<Unsigned36Bit, EvaluationFailure> {
18        Ok(self.value())
19    }
20}
21
22impl Evaluate for SignedAtom {
23    fn evaluate<R: RcUpdater>(
24        &self,
25        ctx: &mut EvaluationContext<R>,
26        scope: ScopeIdentifier,
27    ) -> Result<Unsigned36Bit, EvaluationFailure> {
28        self.magnitude.evaluate(ctx, scope).map(|magnitude| {
29            if self.negated {
30                let s36 = magnitude.reinterpret_as_signed();
31                let signed_result = Signed36Bit::ZERO.wrapping_sub(s36);
32                signed_result.reinterpret_as_unsigned()
33            } else {
34                magnitude
35            }
36        })
37    }
38}
39
40impl Evaluate for ArithmeticExpression {
41    fn evaluate<R: RcUpdater>(
42        &self,
43        ctx: &mut EvaluationContext<R>,
44        scope: ScopeIdentifier,
45    ) -> Result<Unsigned36Bit, EvaluationFailure> {
46        fn fold_step<R: RcUpdater>(
47            acc: Unsigned36Bit,
48            (binop, right): &(Operator, SignedAtom),
49            ctx: &mut EvaluationContext<R>,
50            scope: ScopeIdentifier,
51        ) -> Result<Unsigned36Bit, EvaluationFailure> {
52            let right: Unsigned36Bit = right.evaluate(ctx, scope)?;
53            Ok(ArithmeticExpression::eval_binop(acc, *binop, right))
54        }
55
56        let first: Unsigned36Bit = self.first.evaluate(ctx, scope)?;
57        let result: Result<Unsigned36Bit, EvaluationFailure> = self
58            .tail
59            .iter()
60            .try_fold(first, |acc, curr| fold_step(acc, curr, ctx, scope));
61        result
62    }
63}
64
65impl Evaluate for ConfigValue {
66    fn evaluate<R: RcUpdater>(
67        &self,
68        ctx: &mut EvaluationContext<R>,
69        scope: ScopeIdentifier,
70    ) -> Result<Unsigned36Bit, EvaluationFailure> {
71        // The `expr` member was either originally in superscript (in
72        // which case the `evaluate` value will already have been
73        // shifted into the correct position in the word, or in normal
74        // script (in which case we need to shift it ourselves).
75        let shift = if self.already_superscript { 0 } else { 30u32 };
76        self.expr.evaluate(ctx, scope).map(|value| value.shl(shift))
77    }
78}
79
80impl Evaluate for RegistersContaining {
81    fn evaluate<R: RcUpdater>(
82        &self,
83        ctx: &mut EvaluationContext<R>,
84        scope: ScopeIdentifier,
85    ) -> Result<Unsigned36Bit, EvaluationFailure> {
86        let mut first_addr: Option<Unsigned36Bit> = None;
87        for rc_word in self.words() {
88            // Evaluation of the RegisterContaining value will compute
89            // a correct here-value, we don't need to pass it in.  But
90            // we can't pass None, and so instead we pass NotAllowed
91            // so that if a bug is introduced we will see a failure
92            // rather than an incorrect result.
93            let address: Unsigned36Bit = ctx
94                .for_target_address(HereValue::NotAllowed, |newctx| {
95                    rc_word.evaluate(newctx, scope)
96                })?;
97            if first_addr.is_none() {
98                first_addr = Some(address);
99            }
100        }
101        match first_addr {
102            Some(addr) => Ok(addr),
103            None => {
104                unreachable!("RC-references should not occupy zero words of storage");
105            }
106        }
107    }
108}
109
110impl Evaluate for RegisterContaining {
111    fn evaluate<R: RcUpdater>(
112        &self,
113        // We must not use the passed-in target address (in ctx.here) since inside
114        // an RC-word, `#` refers to the adddress of the RC-word, not
115        // the address of the instruction which refers to it.
116        ctx: &mut EvaluationContext<R>,
117        scope: ScopeIdentifier,
118    ) -> Result<Unsigned36Bit, EvaluationFailure> {
119        match self {
120            RegisterContaining::Unallocated(_) => {
121                unreachable!(
122                    "evaluate() called on RegisterContaining instance {self:?} before it was allocated"
123                );
124            }
125            RegisterContaining::Allocated(rc_word_addr, inst) => {
126                // Tags defined in RC-words may not be used for M4's
127                // editing instuctions, but nevertheless they are not
128                // locally-scoped.  This is demonstrated by the
129                // example in section 6-4.7 of the User's Handbook,
130                // which looks like this:
131                //
132                // ```
133                // ☛☛DEF TBS|α
134                //  α
135                //  α
136                //  α
137                //  α
138                //  α
139                // ☛☛EMD
140                // 100|
141                // USE->     LDA {TS->TBS|0}  ** 5 BLANK RC WORDS
142                //           LDA TOMM
143                //           STA TS+3
144                // ```
145                //
146                // You will see above that the definition of the tag
147                // `TS` is inside an RC-word, but _not_ inside a macro
148                // body.
149                //
150                // The example explains that the above code snippet expands to:
151                //
152                // ```
153                // 100|
154                // USE ->    LDA {TS-> TBS| 0}              |002400 000103|000100
155                //           LDA TOMM                       |002400 000110|   101
156                //           STA TS+3                       |003400 000106|   102
157                // TS ->     TBS 0
158                //           0                              |000000 000000|   103
159                //           0                              |000000 000000|   104
160                //           0                              |000000 000000|   105
161                //           0                              |000000 000000|   106
162                //           0                              |000000 000000|   107
163                // TOMM ->   0                              |000000 000000|000110
164                // ```
165                //
166                // Within the RC-word, # ("here") resolves to the
167                // address of the RC-word itself.  So before we
168                // evaluate the value to be placed in the RC-word, we
169                // need to know the value that # will take during the
170                // evaluation process.
171                let rc_word_value: Unsigned36Bit = ctx
172                    .for_target_address(HereValue::Address(*rc_word_addr), |newctx| {
173                        inst.evaluate(newctx, scope)
174                    })?;
175                ctx.rc_updater.update(*rc_word_addr, rc_word_value);
176                Ok(Unsigned36Bit::from(rc_word_addr))
177            }
178        }
179    }
180}
181
182impl Evaluate for Atom {
183    fn evaluate<R: RcUpdater>(
184        &self,
185        ctx: &mut EvaluationContext<R>,
186        scope: ScopeIdentifier,
187    ) -> Result<Unsigned36Bit, EvaluationFailure> {
188        match self {
189            Atom::SymbolOrLiteral(value) => value.evaluate(ctx, scope),
190            Atom::Parens(_span, _script, expr) => expr.evaluate(ctx, scope),
191            Atom::RcRef(_span, registers_containing) => registers_containing.evaluate(ctx, scope),
192        }
193    }
194}
195
196impl Evaluate for SymbolOrLiteral {
197    fn evaluate<R: RcUpdater>(
198        &self,
199        ctx: &mut EvaluationContext<R>,
200        scope: ScopeIdentifier,
201    ) -> Result<Unsigned36Bit, EvaluationFailure> {
202        match self {
203            SymbolOrLiteral::Symbol(script, symbol_name, span) => {
204                evaluate_elevated_symbol(symbol_name, *script, *span, ctx, scope)
205            }
206            SymbolOrLiteral::Literal(literal_value) => literal_value.evaluate(ctx, scope),
207            SymbolOrLiteral::Here(script, span) => ctx
208                .here
209                .get_address(span)
210                .map(|addr: Address| Unsigned36Bit::from(addr))
211                .map(|addr_value: Unsigned36Bit| addr_value.shl(script.shift())),
212        }
213    }
214}
215
216impl Evaluate for InstructionFragment {
217    fn evaluate<R: RcUpdater>(
218        &self,
219        ctx: &mut EvaluationContext<R>,
220        scope: ScopeIdentifier,
221    ) -> Result<Unsigned36Bit, EvaluationFailure> {
222        match self {
223            InstructionFragment::Null(_) => Ok(Unsigned36Bit::ZERO),
224            InstructionFragment::Arithmetic(expr) => expr.evaluate(ctx, scope),
225            InstructionFragment::DeferredAddressing(_) => Ok(DEFER_BIT),
226            InstructionFragment::Config(value) => value.evaluate(ctx, scope),
227            InstructionFragment::PipeConstruct {
228                index: p,
229                rc_word_span: _,
230                rc_word_value,
231            } => {
232                // The pipe construct is described in section 6-2.8
233                // "SPECIAL SYMBOLS" of the Users Handbook.
234                //
235                //
236                // "ADXₚ|ₜQ" should be equivalent to "ADXₚ{Qₜ}*".
237                // (Note that in the real pipe construct the "|" is
238                // itself in subscript).  During parsing, the values
239                // of Q and ₜ were combined into the two parts of the
240                // rc_word_value tuple.  We evaluate Qₜ as
241                // rc_word_val.
242                let p_value: Unsigned36Bit = p.item.evaluate(ctx, scope)?;
243                let rc_word_addr: Unsigned36Bit = rc_word_value.evaluate(ctx, scope)?;
244                Ok(combine_fragment_values(
245                    combine_fragment_values(p_value, rc_word_addr),
246                    DEFER_BIT,
247                ))
248            }
249        }
250    }
251}
252
253impl Evaluate for Origin {
254    fn evaluate<R: RcUpdater>(
255        &self,
256        ctx: &mut EvaluationContext<R>,
257        scope: ScopeIdentifier,
258    ) -> Result<Unsigned36Bit, EvaluationFailure> {
259        match self {
260            Origin::Deduced(_span, _, address) | Origin::Literal(_span, address) => {
261                Ok(address.into())
262            }
263            Origin::Symbolic(span, symbol_name) => {
264                evaluate_elevated_symbol(symbol_name, Script::Normal, *span, ctx, scope)
265            }
266        }
267    }
268}
269
270/// Implement the value transformation rules described in the table
271/// "COMMA CHART" in section 6-2.4, "NUMERICAL FORMAT - USE OF COMMAS"
272/// of the Users Handbook.
273///
274/// It's likely that the TX-2's original implementation of this in the
275/// M4 assembler used the system configuration feature to perform the
276/// word-quarter masking and mapping.  While we could do that it would
277/// introduce a dependency between the assembler and the siimulator's
278/// implementation of the exchange unit.  Generating the correct
279/// system configuration value would be more or less as complex as
280/// just implementing the logic here, so we just implement it here in
281/// order to avoid the dependency.
282fn comma_transformation(
283    leading_commas: Option<&Commas>,
284    value: Unsigned36Bit,
285    trailing_commas: Option<&Commas>,
286) -> Unsigned36Bit {
287    // Re-ordering these cases to bring similar bodies togather would
288    // make the code harder to read.
289    #[allow(clippy::match_same_arms)]
290    match (leading_commas, trailing_commas) {
291        (None, None) => value,
292        (None, Some(Commas::One(_))) => value.and(0o777).shl(27),
293        (None, Some(Commas::Two(_))) => value.and(0o777_777).shl(18),
294        (None, Some(Commas::Three(_))) => value.and(0o777_777_777).shl(9),
295
296        (Some(Commas::One(_)), None) => value.and(0o777),
297        (Some(Commas::One(_)), Some(Commas::One(_))) => value.and(0o777_777).shl(9),
298        (Some(Commas::One(_)), Some(Commas::Two(_))) => value.and(0o777).shl(18),
299        (Some(Commas::One(_)), Some(Commas::Three(_))) => value.and(0o777_777_777_000),
300
301        (Some(Commas::Two(_)), None) => value.and(0o777_777),
302        (Some(Commas::Two(_)), Some(Commas::One(_))) => value.and(0o777).shl(9),
303        (Some(Commas::Two(_)), Some(Commas::Two(_))) => {
304            let hi = value.and(0o000_000_777_777).shl(18);
305            let lo = value.and(0o777_777_000_000).shr(18);
306            hi | lo
307        }
308        (Some(Commas::Two(_)), Some(Commas::Three(_))) => value.and(0o777_777_000_000).shr(18),
309
310        (Some(Commas::Three(_)), None) => value.and(0o777),
311        (Some(Commas::Three(_)), Some(Commas::One(_))) => value.and(0o777_000_000_000).shr(27),
312        (Some(Commas::Three(_)), Some(Commas::Two(_))) => value.and(0o777_000_000_000).shr(9),
313        (Some(Commas::Three(_)), Some(Commas::Three(_))) => value.and(0o777_777_000_000).shr(18),
314    }
315}
316
317impl Evaluate for CommaDelimitedFragment {
318    fn evaluate<R: RcUpdater>(
319        &self,
320        ctx: &mut EvaluationContext<R>,
321        scope: ScopeIdentifier,
322    ) -> Result<Unsigned36Bit, EvaluationFailure> {
323        self.fragment
324            .evaluate(ctx, scope)
325            .map(|word| {
326                // TODO: issue a diagnostic if there are inconsistent
327                //  values for the hold bit.  We will need to decide
328                // whether something like ",h" sets the hold bit (i.e. whether
329                // the hold bit is supposed to be subject to the same
330                // comma rules that other values are).
331                const HELD_MASK: Unsigned36Bit = u36!(1 << 35);
332
333                match self.holdbit {
334                    HoldBit::Hold => word | HELD_MASK,
335                    HoldBit::NotHold => word & !HELD_MASK,
336                    HoldBit::Unspecified => word,
337                }
338            })
339            .map(|value| {
340                comma_transformation(
341                    self.leading_commas.as_ref(),
342                    value,
343                    self.trailing_commas.as_ref(),
344                )
345            })
346    }
347}
348
349/// Combine the fragments of an instruction into a final `Unsigned36bit` value.
350///
351/// Section 6-2.7 of the [Users Handbook](https://tx-2.github.io/documentation#UH) says:
352///
353/// > The 36 bit address syllable is united with (inclusive OR) with the others
354/// > (configuration, operation, index).  Extra syllables of the latter group
355/// > are also united into the word.  The one bit syllables are set last,
356/// > "not hold" being the last one.
357///
358/// So, the operation is definitely an OR-operation.
359///
360/// The Users Handbook says in section 6-2.3 "RULES FOR SYMEX FORMATION" Rule 5,
361///
362/// > "A TYPE" is equivalent to "377604 + TYPE"
363///
364/// While this seems to contradict section 6-2.7, I think we should
365/// assume section 6-2.7 takes precedence.
366fn combine_fragment_values(left: Unsigned36Bit, right: Unsigned36Bit) -> Unsigned36Bit {
367    left | right
368}
369
370impl Evaluate for UntaggedProgramInstruction {
371    fn evaluate<R: RcUpdater>(
372        &self,
373        ctx: &mut EvaluationContext<R>,
374        scope: ScopeIdentifier,
375    ) -> Result<Unsigned36Bit, EvaluationFailure> {
376        fn evaluate_and_combine_values<'a, R, E, I>(
377            mut items: I,
378            ctx: &mut EvaluationContext<R>,
379            scope: ScopeIdentifier,
380        ) -> Result<Unsigned36Bit, EvaluationFailure>
381        where
382            R: RcUpdater,
383            E: Evaluate + 'a,
384            I: Iterator<Item = &'a E>,
385        {
386            items.try_fold(Unsigned36Bit::ZERO, |acc, item| {
387                item.evaluate(ctx, scope)
388                    .map(|value| combine_fragment_values(acc, value))
389            })
390        }
391
392        // Comma delimited values are evaluated left-to-right, as stated in item
393        // (b) in section 6-2.4, "NUMERICAL FORMAT - USE OF COMMAS" of
394        // the Users Handbook.  The initial value is zero (as
395        // specified in item (a) in the same place).
396        evaluate_and_combine_values(self.fragments.iter(), ctx, scope)
397    }
398}
399
400impl Evaluate for EqualityValue {
401    fn evaluate<R: RcUpdater>(
402        &self,
403        ctx: &mut EvaluationContext<R>,
404        scope: ScopeIdentifier,
405    ) -> Result<Unsigned36Bit, EvaluationFailure> {
406        self.inner.evaluate(ctx, scope)
407    }
408}
409
410impl Evaluate for TaggedProgramInstruction {
411    fn evaluate<R: RcUpdater>(
412        &self,
413        ctx: &mut EvaluationContext<R>,
414        scope: ScopeIdentifier,
415    ) -> Result<Unsigned36Bit, EvaluationFailure> {
416        self.instruction.evaluate(ctx, scope)
417    }
418}
419
420#[cfg(test)]
421mod comma_tests {
422    use super::super::span;
423    use super::comma_transformation;
424    use super::*;
425    use base::u36;
426
427    // This test case is taken from the table "COMMA CHART" in section
428    // 6-2.4, "NUMERICAL FORMAT - USE OF COMMAS" of the Users
429    // Handbook.
430    #[test]
431    fn test_comma_transformation_0_0() {
432        assert_eq!(
433            comma_transformation(None, u36!(0o444_333_222_111), None),
434            u36!(0o444_333_222_111)
435        );
436    }
437
438    // This test case is taken from the table "COMMA CHART" in section
439    // 6-2.4, "NUMERICAL FORMAT - USE OF COMMAS" of the Users
440    // Handbook.
441    #[test]
442    fn test_comma_transformation_0_1() {
443        // For convenience, our comma test cases adopt a standard
444        // (imaginary) layout for the input.  We do this so that the
445        // input spans could conceivably have been generated by real
446        // input, so that our tests don't inadvertently require the
447        // implementation to allow inconsistent/invalid inputs.
448        //
449        // Span 0..3 is the initial commas (or spaces).
450        // Span 3..15 is the constant 444333222111.
451        // Span 15..18 is the trailing commas (or spaces).
452        assert_eq!(
453            comma_transformation(
454                None,
455                u36!(0o444_333_222_111),
456                Some(&Commas::One(span(15..16)))
457            ),
458            u36!(0o111_000_000_000)
459        );
460    }
461
462    // This test case is taken from the table "COMMA CHART" in section
463    // 6-2.4, "NUMERICAL FORMAT - USE OF COMMAS" of the Users
464    // Handbook.
465    #[test]
466    fn test_comma_transformation_0_2() {
467        // For convenience, our comma test cases adopt a standard
468        // (imaginary) layout for the input.  We do this so that the
469        // input spans could conceivably have been generated by real
470        // input, so that our tests don't inadvertently require the
471        // implementation to allow inconsistent/invalid inputs.
472        //
473        // Span 0..3 is the initial commas (or spaces).
474        // Span 3..15 is the constant 444333222111.
475        // Span 15..18 is the trailing commas (or spaces).
476        assert_eq!(
477            comma_transformation(
478                None,
479                u36!(0o444_333_222_111),
480                Some(&Commas::Two(span(15..17)))
481            ),
482            u36!(0o222_111_000_000)
483        );
484    }
485
486    // This test case is taken from the table "COMMA CHART" in section
487    // 6-2.4, "NUMERICAL FORMAT - USE OF COMMAS" of the Users
488    // Handbook.
489    #[test]
490    fn test_comma_transformation_0_3() {
491        // For convenience, our comma test cases adopt a standard
492        // (imaginary) layout for the input.  We do this so that the
493        // input spans could conceivably have been generated by real
494        // input, so that our tests don't inadvertently require the
495        // implementation to allow inconsistent/invalid inputs.
496        //
497        // Span 0..3 is the initial commas (or spaces).
498        // Span 3..15 is the constant 444333222111.
499        // Span 15..18 is the trailing commas (or spaces).
500        assert_eq!(
501            comma_transformation(
502                None,
503                u36!(0o444_333_222_111),
504                Some(&Commas::Three(span(15..18)))
505            ),
506            u36!(0o333_222_111_000)
507        );
508    }
509
510    // This test case is taken from the table "COMMA CHART" in section
511    // 6-2.4, "NUMERICAL FORMAT - USE OF COMMAS" of the Users
512    // Handbook.
513    #[test]
514    fn test_comma_transformation_1_0() {
515        // For convenience, our comma test cases adopt a standard
516        // (imaginary) layout for the input.  We do this so that the
517        // input spans could conceivably have been generated by real
518        // input, so that our tests don't inadvertently require the
519        // implementation to allow inconsistent/invalid inputs.
520        //
521        // Span 0..3 is the initial commas (or spaces).
522        // Span 3..15 is the constant 444333222111.
523        // Span 15..18 is the trailing commas (or spaces).
524        assert_eq!(
525            comma_transformation(
526                Some(&Commas::One(span(2..3))),
527                u36!(0o444_333_222_111),
528                None,
529            ),
530            u36!(0o000_000_000_111)
531        );
532    }
533
534    // This test case is taken from the table "COMMA CHART" in section
535    // 6-2.4, "NUMERICAL FORMAT - USE OF COMMAS" of the Users
536    // Handbook.
537    #[test]
538    fn test_comma_transformation_1_1() {
539        // For convenience, our comma test cases adopt a standard
540        // (imaginary) layout for the input.  We do this so that the
541        // input spans could conceivably have been generated by real
542        // input, so that our tests don't inadvertently require the
543        // implementation to allow inconsistent/invalid inputs.
544        //
545        // Span 0..3 is the initial commas (or spaces).
546        // Span 3..15 is the constant 444333222111.
547        // Span 15..18 is the trailing commas (or spaces).
548        assert_eq!(
549            comma_transformation(
550                Some(&Commas::One(span(2..3))),
551                u36!(0o444_333_222_111),
552                Some(&Commas::One(span(15..16))),
553            ),
554            u36!(0o000_222_111_000)
555        );
556    }
557
558    // This test case is taken from the table "COMMA CHART" in section
559    // 6-2.4, "NUMERICAL FORMAT - USE OF COMMAS" of the Users
560    // Handbook.
561    #[test]
562    fn test_comma_transformation_1_2() {
563        // For convenience, our comma test cases adopt a standard
564        // (imaginary) layout for the input.  We do this so that the
565        // input spans could conceivably have been generated by real
566        // input, so that our tests don't inadvertently require the
567        // implementation to allow inconsistent/invalid inputs.
568        //
569        // Span 0..3 is the initial commas (or spaces).
570        // Span 3..15 is the constant 444333222111.
571        // Span 15..18 is the trailing commas (or spaces).
572        assert_eq!(
573            comma_transformation(
574                Some(&Commas::One(span(2..3))),
575                u36!(0o444_333_222_111),
576                Some(&Commas::Two(span(15..17))),
577            ),
578            u36!(0o000_111_000_000)
579        );
580    }
581
582    // This test case is taken from the table "COMMA CHART" in section
583    // 6-2.4, "NUMERICAL FORMAT - USE OF COMMAS" of the Users
584    // Handbook.
585    #[test]
586    fn test_comma_transformation_1_3() {
587        // For convenience, our comma test cases adopt a standard
588        // (imaginary) layout for the input.  We do this so that the
589        // input spans could conceivably have been generated by real
590        // input, so that our tests don't inadvertently require the
591        // implementation to allow inconsistent/invalid inputs.
592        //
593        // Span 0..3 is the initial commas (or spaces).
594        // Span 3..15 is the constant 444333222111.
595        // Span 15..18 is the trailing commas (or spaces).
596        assert_eq!(
597            comma_transformation(
598                Some(&Commas::One(span(2..3))),
599                u36!(0o444_333_222_111),
600                Some(&Commas::Three(span(15..18))),
601            ),
602            u36!(0o444_333_222_000)
603        );
604    }
605
606    // This test case is taken from the table "COMMA CHART" in section
607    // 6-2.4, "NUMERICAL FORMAT - USE OF COMMAS" of the Users
608    // Handbook.
609    #[test]
610    fn test_comma_transformation_2_0() {
611        // For convenience, our comma test cases adopt a standard
612        // (imaginary) layout for the input.  We do this so that the
613        // input spans could conceivably have been generated by real
614        // input, so that our tests don't inadvertently require the
615        // implementation to allow inconsistent/invalid inputs.
616        //
617        // Span 0..3 is the initial commas (or spaces).
618        // Span 3..15 is the constant 444333222111.
619        // Span 15..18 is the trailing commas (or spaces).
620        assert_eq!(
621            comma_transformation(
622                Some(&Commas::Two(span(1..3))),
623                u36!(0o444_333_222_111),
624                None,
625            ),
626            u36!(0o000_000_222_111)
627        );
628    }
629
630    // This test case is taken from the table "COMMA CHART" in section
631    // 6-2.4, "NUMERICAL FORMAT - USE OF COMMAS" of the Users
632    // Handbook.
633    #[test]
634    fn test_comma_transformation_2_1() {
635        // For convenience, our comma test cases adopt a standard
636        // (imaginary) layout for the input.  We do this so that the
637        // input spans could conceivably have been generated by real
638        // input, so that our tests don't inadvertently require the
639        // implementation to allow inconsistent/invalid inputs.
640        //
641        // Span 0..3 is the initial commas (or spaces).
642        // Span 3..15 is the constant 444333222111.
643        // Span 15..18 is the trailing commas (or spaces).
644        assert_eq!(
645            comma_transformation(
646                Some(&Commas::Two(span(1..3))),
647                u36!(0o444_333_222_111),
648                Some(&Commas::One(span(15..16))),
649            ),
650            u36!(0o000_000_111_000)
651        );
652    }
653
654    // This test case is taken from the table "COMMA CHART" in section
655    // 6-2.4, "NUMERICAL FORMAT - USE OF COMMAS" of the Users
656    // Handbook.
657    #[test]
658    fn test_comma_transformation_2_2() {
659        // For convenience, our comma test cases adopt a standard
660        // (imaginary) layout for the input.  We do this so that the
661        // input spans could conceivably have been generated by real
662        // input, so that our tests don't inadvertently require the
663        // implementation to allow inconsistent/invalid inputs.
664        //
665        // Span 0..3 is the initial commas (or spaces).
666        // Span 3..15 is the constant 444333222111.
667        // Span 15..18 is the trailing commas (or spaces).
668        assert_eq!(
669            comma_transformation(
670                Some(&Commas::Two(span(1..3))),
671                u36!(0o444_333_222_111),
672                Some(&Commas::Two(span(15..17))),
673            ),
674            u36!(0o222_111_444_333)
675        );
676    }
677
678    // This test case is taken from the table "COMMA CHART" in section
679    // 6-2.4, "NUMERICAL FORMAT - USE OF COMMAS" of the Users
680    // Handbook.
681    #[test]
682    fn test_comma_transformation_2_3() {
683        // For convenience, our comma test cases adopt a standard
684        // (imaginary) layout for the input.  We do this so that the
685        // input spans could conceivably have been generated by real
686        // input, so that our tests don't inadvertently require the
687        // implementation to allow inconsistent/invalid inputs.
688        //
689        // Span 0..3 is the initial commas (or spaces).
690        // Span 3..15 is the constant 444333222111.
691        // Span 15..18 is the trailing commas (or spaces).
692        assert_eq!(
693            comma_transformation(
694                Some(&Commas::Two(span(1..3))),
695                u36!(0o444_333_222_111),
696                Some(&Commas::Three(span(15..18))),
697            ),
698            u36!(0o000_000_444_333)
699        );
700    }
701
702    // This test case is taken from the table "COMMA CHART" in section
703    // 6-2.4, "NUMERICAL FORMAT - USE OF COMMAS" of the Users
704    // Handbook.
705    #[test]
706    fn test_comma_transformation_3_0() {
707        // For convenience, our comma test cases adopt a standard
708        // (imaginary) layout for the input.  We do this so that the
709        // input spans could conceivably have been generated by real
710        // input, so that our tests don't inadvertently require the
711        // implementation to allow inconsistent/invalid inputs.
712        //
713        // Span 0..3 is the initial commas (or spaces).
714        // Span 3..15 is the constant 444333222111.
715        // Span 15..18 is the trailing commas (or spaces).
716        assert_eq!(
717            comma_transformation(
718                Some(&Commas::Three(span(0..3))),
719                u36!(0o444_333_222_111),
720                None,
721            ),
722            u36!(0o000_000_000_111)
723        );
724    }
725
726    // This test case is taken from the table "COMMA CHART" in section
727    // 6-2.4, "NUMERICAL FORMAT - USE OF COMMAS" of the Users
728    // Handbook.
729    #[test]
730    fn test_comma_transformation_3_1() {
731        // For convenience, our comma test cases adopt a standard
732        // (imaginary) layout for the input.  We do this so that the
733        // input spans could conceivably have been generated by real
734        // input, so that our tests don't inadvertently require the
735        // implementation to allow inconsistent/invalid inputs.
736        //
737        // Span 0..3 is the initial commas (or spaces).
738        // Span 3..15 is the constant 444333222111.
739        // Span 15..18 is the trailing commas (or spaces).
740        assert_eq!(
741            comma_transformation(
742                Some(&Commas::Three(span(0..3))),
743                u36!(0o444_333_222_111),
744                Some(&Commas::One(span(15..16))),
745            ),
746            u36!(0o000_000_000_444)
747        );
748    }
749
750    // This test case is taken from the table "COMMA CHART" in section
751    // 6-2.4, "NUMERICAL FORMAT - USE OF COMMAS" of the Users
752    // Handbook.
753    #[test]
754    fn test_comma_transformation_3_2() {
755        // For convenience, our comma test cases adopt a standard
756        // (imaginary) layout for the input.  We do this so that the
757        // input spans could conceivably have been generated by real
758        // input, so that our tests don't inadvertently require the
759        // implementation to allow inconsistent/invalid inputs.
760        //
761        // Span 0..3 is the initial commas (or spaces).
762        // Span 3..15 is the constant 444333222111.
763        // Span 15..18 is the trailing commas (or spaces).
764        assert_eq!(
765            comma_transformation(
766                Some(&Commas::Three(span(0..3))),
767                u36!(0o444_333_222_111),
768                Some(&Commas::Two(span(15..17))),
769            ),
770            u36!(0o000_444_000_000)
771        );
772    }
773
774    // This test case is taken from the table "COMMA CHART" in section
775    // 6-2.4, "NUMERICAL FORMAT - USE OF COMMAS" of the Users
776    // Handbook.
777    #[test]
778    fn test_comma_transformation_3_3() {
779        // For convenience, our comma test cases adopt a standard
780        // (imaginary) layout for the input.  We do this so that the
781        // input spans could conceivably have been generated by real
782        // input, so that our tests don't inadvertently require the
783        // implementation to allow inconsistent/invalid inputs.
784        //
785        // Span 0..3 is the initial commas (or spaces).
786        // Span 3..15 is the constant 444333222111.
787        // Span 15..18 is the trailing commas (or spaces).
788        assert_eq!(
789            comma_transformation(
790                Some(&Commas::Three(span(0..3))),
791                u36!(0o444_333_222_111),
792                Some(&Commas::Three(span(15..18))),
793            ),
794            u36!(0o000_000_444_333)
795        );
796    }
797}