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}