cpclib_asm/implementation/
tokens.rs

1use std::fmt::Debug;
2use std::sync::Arc;
3
4use cpclib_common::itertools::Itertools;
5use cpclib_common::smallvec::SmallVec;
6use cpclib_tokens::symbols::*;
7use cpclib_tokens::tokens::*;
8
9use crate::assembler::{Env, Visited, assemble_defs_item};
10use crate::error::*;
11use crate::implementation::expression::ExprEvaluationExt;
12use crate::implementation::listing::ListingExt;
13use crate::{AssemblingOptions, EnvOptions};
14
15/// Needed methods for the Token defined in cpclib_tokens
16pub trait TokenExt: ListingElement + Debug + Visited {
17    fn estimated_duration(&self) -> Result<usize, AssemblerError>;
18    /// Unroll the tokens when it represents a loop
19    fn unroll(&self, env: &mut crate::Env) -> Option<Result<Vec<&Self>, AssemblerError>>;
20
21    /// Generate the listing of opcodes for directives that embed bytes
22    fn disassemble_data(&self) -> Result<Listing, String>;
23
24    fn to_bytes_with_options(&self, option: EnvOptions) -> Result<Vec<u8>, AssemblerError> {
25        let mut env = Env::new(option);
26        // we need several passes in case the token is a directive that contains code
27        loop {
28            env.start_new_pass();
29            // println!("[pass] {:?}", env.pass);
30
31            if env.pass().is_finished() {
32                break;
33            }
34
35            self.visited(&mut env)?;
36        }
37
38        Ok(env.produced_bytes())
39    }
40
41    /// Returns the number of bytes of the instructions by assembling it
42    fn number_of_bytes(&self) -> Result<usize, String> {
43        let bytes = self.to_bytes();
44        if bytes.is_ok() {
45            Ok(bytes.ok().unwrap().len())
46        }
47        else {
48            Err(format!("Unable to get the bytes of this token: {:?}", self))
49        }
50    }
51
52    /// returns the number of bytes without assembling it
53    fn fallback_number_of_bytes(&self) -> Result<usize, String>;
54
55    /// Return the number of bytes of the token given the provided context
56    fn number_of_bytes_with_context(
57        &self,
58        table: &mut SymbolsTableCaseDependent
59    ) -> Result<usize, String> {
60        let bytes = self.to_bytes_with_context(table);
61        if bytes.is_ok() {
62            Ok(bytes.ok().unwrap().len())
63        }
64        else {
65            eprintln!("{:?}", bytes);
66            Err(format!("Unable to get the bytes of this token: {:?}", self))
67        }
68    }
69
70    /// Dummy version that assemble without taking into account the context
71    /// TODO find a way to not build a symbol table each time
72    fn to_bytes(&self) -> Result<Vec<u8>, AssemblerError> {
73        let mut table = SymbolsTableCaseDependent::laxist();
74        let table = &mut table;
75        self.to_bytes_with_context(table)
76    }
77
78    /// Assemble the symbol taking into account some context, but never modify this context
79    #[allow(clippy::match_same_arms)]
80    fn to_bytes_with_context(
81        &self,
82        table: &mut SymbolsTableCaseDependent
83    ) -> Result<Vec<u8>, AssemblerError> {
84        let mut options = if table.is_case_sensitive() {
85            AssemblingOptions::new_case_sensitive()
86        }
87        else {
88            AssemblingOptions::new_case_insensitive()
89        };
90        options.set_symbols(table.table());
91
92        let options = EnvOptions::new(Default::default(), options, Arc::new(()));
93        self.to_bytes_with_options(options)
94    }
95
96    /// Check if the token is valid. We consider a token vlaid if it is possible to assemble it
97    fn is_valid(&self) -> bool {
98        self.to_bytes().is_ok()
99    }
100}
101
102// impl<'t> TokenExt for Cow<'t, Token> {
103// fn disassemble_data(&self) -> Result<Listing, String> {
104// self.deref().disassemble_data()
105// }
106//
107// fn estimated_duration(&self) -> Result<usize, AssemblerError> {
108// self.deref().estimated_duration()
109// }
110//
111// fn to_bytes_with_options(&self, option: &AssemblingOptions) -> Result<Vec<u8>, AssemblerError> {
112// self.deref().to_bytes_with_options(option)
113// }
114//
115// fn unroll(&self, _env: &crate::Env) -> Option<Result<Vec<&Self>, AssemblerError>> {
116// unimplemented!("signature issue. should be transformed/unused")
117// }
118// }
119
120impl TokenExt for Token {
121    /// Unroll the tokens when in a repetition loop
122    /// TODO return an iterator in order to not produce the vector each time
123    fn unroll(&self, env: &mut crate::Env) -> Option<Result<Vec<&Self>, AssemblerError>> {
124        if let Token::Repeat(expr, tokens, _counter_label, _counter_start) = self {
125            let count: Result<ExprResult, AssemblerError> = expr.resolve(env);
126            if count.is_err() {
127                Some(Err(count.err().unwrap()))
128            }
129            else {
130                let count = count.unwrap().int().unwrap();
131                let mut res = Vec::with_capacity(count as usize * tokens.len());
132                for _i in 0..count {
133                    // TODO add a specific token to control the loop counter (and change the return type)
134                    for t in tokens.iter() {
135                        res.push(t);
136                    }
137                }
138                Some(Ok(res))
139            }
140        }
141        else {
142            None
143        }
144    }
145
146    /// Generate the listing of opcodes for directives that contain data Defb/defw/Defs in order to have
147    /// mnemonics. Fails when some values are not opcodes
148    fn disassemble_data(&self) -> Result<Listing, String> {
149        // Disassemble the bytes and return the listing ONLY if it has no more defb/w/s directives
150        let wrap = |bytes: &[u8]| {
151            use crate::disass::disassemble;
152
153            let lst = disassemble(bytes);
154            for token in lst.listing() {
155                match token {
156                    Token::Defb(_) | Token::Defw(_) | Token::Defs(_) => {
157                        return Err(format!("{} as not been disassembled", token));
158                    },
159                    _ => {}
160                }
161            }
162
163            Ok(lst)
164        };
165
166        match self {
167            Token::Defs(l) => {
168                l.iter()
169                    .map(|(e, f)| {
170                        assemble_defs_item(e, f.as_ref(), &mut Env::default())
171                            .map_err(|err| format!("Unable to assemble {}: {:?}", self, err))
172                    })
173                    .fold_ok(SmallVec::<[u8; 4]>::new(), |mut acc, v| {
174                        acc.extend_from_slice(v.as_slice());
175                        acc
176                    })
177                    .and_then(|b| wrap(&b))
178            },
179
180            Token::Defb(e) | Token::Defw(e) | Token::Str(e) => {
181                let mut env = Env::default();
182                env.visit_db_or_dw_or_str(self.into(), e, 0.into())
183                    .map_err(|err| format!("Unable to assemble {}: {:?}", self, err))?;
184                wrap(&env.produced_bytes())
185            },
186
187            _ => {
188                let mut lst = Listing::new();
189                lst.push(self.clone());
190                Ok(lst)
191            }
192        }
193    }
194
195    /// Returns an estimation of the duration.
196    /// This estimation may be wrong for instruction having several states.
197    #[allow(clippy::match_same_arms)]
198    fn estimated_duration(&self) -> Result<usize, AssemblerError> {
199        let duration = match self {
200            Token::Assert(..)
201            | Token::Breakpoint { .. }
202            | Token::Comment(_)
203            | Token::Label(_)
204            | Token::Equ { .. }
205            | Token::Protect(..) => 0,
206
207            Token::Repeat(Expr::Value(count), lst, ..) if *count >= 0 => {
208                let lst_count = lst.estimated_duration()?;
209                lst_count * (*count as usize)
210            },
211
212            // Here, there is a strong limitation => it will works only if no symbols are used
213            Token::Defw(_) | Token::Defb(_) | Token::Defs(_) => {
214                self.disassemble_data()
215                    .map_err(|e| AssemblerError::DisassemblerError { msg: e })
216                    .and_then(|lst| lst.estimated_duration())?
217            },
218
219            Token::OpCode(mnemonic, arg1, arg2, _arg3) => {
220                match mnemonic {
221                    &Mnemonic::Add => {
222                        match arg1 {
223                            Some(DataAccess::Register8(_)) => {
224                                match arg2 {
225                                    Some(DataAccess::Register8(_)) => 1,
226                                    Some(DataAccess::IndexRegister16WithIndex(..)) => 5,
227                                    _ => 2
228                                }
229                            },
230                            Some(DataAccess::Register16(_)) => 4,
231                            Some(DataAccess::IndexRegister16(_)) => 5,
232                            _ => panic!("Impossible case {:?}, {:?}, {:?}", mnemonic, arg1, arg2)
233                        }
234                    },
235
236                    &Mnemonic::And | &Mnemonic::Or | &Mnemonic::Xor => {
237                        match arg1 {
238                            Some(DataAccess::Register8(_)) => 1,
239                            Some(DataAccess::IndexRegister8(_)) => 2,
240                            Some(DataAccess::Expression(_)) => 2,
241                            Some(DataAccess::MemoryRegister16(_)) => 2,
242                            Some(DataAccess::IndexRegister16WithIndex(..)) => 5,
243                            _ => unreachable!()
244                        }
245                    },
246
247                    Mnemonic::Call => {
248                        match arg1 {
249                            Some(_) => 3, // unstable (5 if jump)
250                            None => 5
251                        }
252                    },
253
254                    // XXX Not stable timing
255                    &Mnemonic::Djnz => 3, // or 4
256
257                    &Mnemonic::ExAf => 1,
258
259                    &Mnemonic::Inc | &Mnemonic::Dec => {
260                        match arg1 {
261                            Some(DataAccess::Register8(_)) => 1,
262                            Some(DataAccess::Register16(_)) => 2,
263                            _ => panic!("Impossible case {:?}, {:?}, {:?}", mnemonic, arg1, arg2)
264                        }
265                    },
266
267                    &Mnemonic::Jp => {
268                        match arg1 {
269                            &None => {
270                                match arg2 {
271                                    Some(DataAccess::Expression(_)) => 3,
272                                    Some(DataAccess::MemoryRegister16(Register16::Hl)) => 1,
273                                    Some(DataAccess::IndexRegister16(_)) => 2,
274                                    _ => {
275                                        panic!(
276                                            "Impossible case {:?}, {:?}, {:?}",
277                                            mnemonic, arg1, arg2
278                                        )
279                                    }
280                                }
281                            },
282
283                            Some(DataAccess::FlagTest(_)) => {
284                                match arg2 {
285                                    Some(DataAccess::Expression(_)) => 3,
286                                    _ => {
287                                        panic!(
288                                            "Impossible case {:?}, {:?}, {:?}",
289                                            mnemonic, arg1, arg2
290                                        )
291                                    }
292                                }
293                            },
294
295                            _ => panic!("Impossible case {:?}, {:?}, {:?}", mnemonic, arg1, arg2)
296                        }
297                    },
298
299                    // Always give the fastest
300                    &Mnemonic::Jr => {
301                        match arg1 {
302                            &None => {
303                                match arg2 {
304                                    Some(DataAccess::Expression(_)) => 3,
305                                    _ => {
306                                        panic!(
307                                            "Impossible case {:?}, {:?}, {:?}",
308                                            mnemonic, arg1, arg2
309                                        )
310                                    }
311                                }
312                            },
313
314                            Some(DataAccess::FlagTest(_)) => {
315                                match arg2 {
316                                    Some(DataAccess::Expression(_)) => 2, // or 3
317                                    _ => {
318                                        panic!(
319                                            "Impossible case {:?}, {:?}, {:?}",
320                                            mnemonic, arg1, arg2
321                                        )
322                                    }
323                                }
324                            },
325
326                            _ => panic!("Impossible case {:?}, {:?}, {:?}", mnemonic, arg1, arg2)
327                        }
328                    },
329
330                    &Mnemonic::Ld => {
331                        match arg1 {
332                            // Dest in memory pointed by register
333                            Some(DataAccess::MemoryRegister16(_)) => {
334                                match arg2 {
335                                    Some(DataAccess::Register8(_)) => 2,
336                                    Some(DataAccess::Expression(_)) => 3, // XXX Valid only for HL
337                                    _ => {
338                                        panic!(
339                                            "Impossible case {:?}, {:?}, {:?}",
340                                            mnemonic, arg1, arg2
341                                        )
342                                    }
343                                }
344                            },
345
346                            // Dest in 8bits reg
347                            Some(DataAccess::Register8(_dst)) => {
348                                match arg2 {
349                                    Some(DataAccess::Register8(_)) => 1,
350                                    Some(DataAccess::MemoryRegister16(Register16::Hl)) => 2,
351                                    Some(DataAccess::Expression(_)) => 2,
352                                    Some(DataAccess::Memory(_)) => 4,
353                                    Some(DataAccess::IndexRegister16WithIndex(..)) => 5,
354                                    _ => {
355                                        panic!(
356                                            "Impossible case {:?}, {:?}, {:?}",
357                                            mnemonic, arg1, arg2
358                                        )
359                                    }
360                                }
361                            },
362
363                            // Dest in 16bits reg
364                            Some(DataAccess::Register16(dst)) => {
365                                match arg2 {
366                                    Some(DataAccess::Expression(_)) => 3,
367                                    Some(DataAccess::Memory(_)) if dst == &Register16::Hl => 5,
368                                    Some(DataAccess::Memory(_)) => 6,
369                                    _ => {
370                                        panic!(
371                                            "Impossible case {:?}, {:?}, {:?}",
372                                            mnemonic, arg1, arg2
373                                        )
374                                    }
375                                }
376                            },
377
378                            Some(DataAccess::IndexRegister16(_)) => {
379                                match arg2 {
380                                    Some(DataAccess::Expression(_)) => 4,
381                                    _ => {
382                                        panic!(
383                                            "Impossible case {:?}, {:?}, {:?}",
384                                            mnemonic, arg1, arg2
385                                        )
386                                    }
387                                }
388                            },
389
390                            Some(DataAccess::Memory(_)) => {
391                                match arg2 {
392                                    Some(DataAccess::Register8(Register8::A)) => 4,
393                                    Some(DataAccess::Register16(Register16::Hl)) => 5,
394                                    Some(DataAccess::Register16(_)) => 6,
395                                    Some(DataAccess::IndexRegister16(_)) => 6,
396                                    _ => {
397                                        panic!(
398                                            "Impossible case {:?}, {:?}, {:?}",
399                                            mnemonic, arg1, arg2
400                                        )
401                                    }
402                                }
403                            },
404
405                            _ => panic!("Impossible case {:?}, {:?}, {:?}", mnemonic, arg1, arg2)
406                        }
407                    },
408
409                    &Mnemonic::Ldi | &Mnemonic::Ldd => 5,
410
411                    &Mnemonic::Nop | &Mnemonic::Exx | &Mnemonic::Di | &Mnemonic::ExHlDe => 1,
412                    &Mnemonic::Nop2 => 2,
413
414                    &Mnemonic::Out => {
415                        match arg1 {
416                            Some(DataAccess::PortC) => 4, // XXX Not sure for out (c), 0
417                            Some(DataAccess::Expression(_)) => 3,
418                            Some(DataAccess::PortN(_)) => 3,
419                            _ => panic!("Impossible case {:?}, {:?}, {:?}", mnemonic, arg1, arg2)
420                        }
421                    },
422
423                    Mnemonic::Outi | Mnemonic::Outd => 5,
424
425                    &Mnemonic::Pop => {
426                        match arg1 {
427                            Some(DataAccess::Register16(_)) => 3,
428                            Some(DataAccess::IndexRegister16(_)) => 4,
429                            _ => panic!("Impossible case {:?}, {:?}, {:?}", mnemonic, arg1, arg2)
430                        }
431                    },
432
433                    &Mnemonic::Push => {
434                        match arg1 {
435                            Some(DataAccess::Register16(_)) => 4,
436                            Some(DataAccess::IndexRegister16(_)) => 5,
437                            _ => panic!("Impossible case {:?}, {:?}, {:?}", mnemonic, arg1, arg2)
438                        }
439                    },
440
441                    &Mnemonic::Res | &Mnemonic::Set => {
442                        match arg2 {
443                            Some(DataAccess::Register8(_)) => 2,
444                            Some(DataAccess::MemoryRegister16(_)) => 3, // XXX only HL
445                            Some(DataAccess::IndexRegister16WithIndex(..)) => 7,
446                            _ => panic!("Impossible case {:?}, {:?}, {:?}", mnemonic, arg1, arg2)
447                        }
448                    },
449
450                    &Mnemonic::Ret => {
451                        match arg1 {
452                            None => 3,
453                            _ => panic!("Impossible case {:?}, {:?}, {:?}", mnemonic, arg1, arg2)
454                        }
455                    },
456
457                    &Mnemonic::Sub => {
458                        match arg1 {
459                            Some(DataAccess::Register8(_)) => 1,
460                            Some(DataAccess::IndexRegister8(_)) => 2,
461                            Some(DataAccess::Expression(_)) => 2,
462                            Some(DataAccess::MemoryRegister16(Register16::Hl)) => 2,
463                            Some(DataAccess::IndexRegister16WithIndex(..)) => 5,
464                            _ => panic!("Impossible case {:?}, {:?}, {:?}", mnemonic, arg1, arg2)
465                        }
466                    },
467
468                    _ => {
469                        panic!(
470                            "Duration not set for {:?}, {:?}, {:?}",
471                            mnemonic, arg1, arg2
472                        )
473                    }
474                }
475            },
476            _ => {
477                return Err(AssemblerError::BugInAssembler {
478                    file: file!(),
479                    line: line!(),
480                    msg: format!("Duration computation for {:?} not yet coded", self)
481                });
482            }
483        };
484        Ok(duration)
485    }
486
487    fn fallback_number_of_bytes(&self) -> Result<usize, String> {
488        match self {
489            Self::OpCode(mne, arg1, arg2, arg3) => {
490                let arg1 = arg1.as_ref().map(|arg| arg.replace_expressions_by_0());
491                let arg2 = arg2.as_ref().map(|arg| arg.replace_expressions_by_0());
492
493                Self::OpCode(*mne, arg1, arg2, *arg3).number_of_bytes()
494            },
495            Self::Comment(..) | Self::Label(..) | Self::Assert(..) => Ok(0),
496            _ => {
497                Result::Err(format!(
498                    "fallback_number_of_bytes not implemented for {self}"
499                ))
500            },
501        }
502    }
503}
504
505#[cfg(test)]
506#[allow(clippy::pedantic)]
507#[allow(warnings)]
508mod tests {
509    use crate::preamble::*;
510
511    #[test]
512    fn test_timing2() {
513        // We are only able to disassemble nop ...
514        assert_eq!(defs_expr_expr(10, 0).estimated_duration().unwrap(), 10);
515        assert_eq!(defw(0).estimated_duration().unwrap(), 2);
516        assert_eq!(defb(0).estimated_duration().unwrap(), 1);
517
518        assert_eq!(exx().estimated_duration().unwrap(), 1);
519
520        assert_eq!(pop_de().estimated_duration().unwrap(), 3);
521
522        assert_eq!(inc_l().estimated_duration().unwrap(), 1);
523
524        assert_eq!(jp_label("XX").estimated_duration().unwrap(), 3);
525
526        assert_eq!(ld_l_mem_ix(14.into()).estimated_duration().unwrap(), 5);
527
528        assert_eq!(ld_mem_hl_e().estimated_duration().unwrap(), 2);
529
530        assert_eq!(ld_e_mem_hl().estimated_duration().unwrap(), 2);
531
532        assert_eq!(ld_d_mem_hl().estimated_duration().unwrap(), 2);
533
534        assert_eq!(out_c_d().estimated_duration().unwrap(), 4);
535    }
536
537    #[test]
538    fn is_valid_ok() {
539        assert!(out_c_d().is_valid());
540    }
541
542    #[test]
543    fn is_valid_nok() {
544        assert!(
545            !Token::OpCode(
546                Mnemonic::Out,
547                Some(DataAccess::Register8(Register8::C)),
548                Some(DataAccess::Register8(Register8::A)),
549                None
550            )
551            .is_valid()
552        );
553    }
554
555    #[cfg(test)]
556    mod test {
557
558        use ParseToken;
559
560        use super::*;
561        #[test]
562        fn fixup_duration() {
563            assert_eq!(
564                Token::parse_token(" di")
565                    .unwrap()
566                    .estimated_duration()
567                    .unwrap(),
568                1
569            );
570            assert_eq!(
571                Token::parse_token(" add a,c ")
572                    .unwrap()
573                    .estimated_duration()
574                    .unwrap(),
575                1
576            );
577            assert_eq!(
578                Token::parse_token(" ld l, a")
579                    .unwrap()
580                    .estimated_duration()
581                    .unwrap(),
582                1
583            );
584            assert_eq!(
585                Token::parse_token(" ld b, e")
586                    .unwrap()
587                    .estimated_duration()
588                    .unwrap(),
589                1
590            );
591            assert_eq!(
592                Token::parse_token(" ld e, b")
593                    .unwrap()
594                    .estimated_duration()
595                    .unwrap(),
596                1
597            );
598            assert_eq!(
599                Token::parse_token(" exx")
600                    .unwrap()
601                    .estimated_duration()
602                    .unwrap(),
603                1
604            );
605            assert_eq!(
606                Token::parse_token(" push bc")
607                    .unwrap()
608                    .estimated_duration()
609                    .unwrap(),
610                4
611            );
612            assert_eq!(
613                Token::parse_token(" pop bc")
614                    .unwrap()
615                    .estimated_duration()
616                    .unwrap(),
617                3
618            );
619            assert_eq!(
620                Token::parse_token(" push ix")
621                    .unwrap()
622                    .estimated_duration()
623                    .unwrap(),
624                5
625            );
626            assert_eq!(
627                Token::parse_token(" pop ix")
628                    .unwrap()
629                    .estimated_duration()
630                    .unwrap(),
631                4
632            );
633            assert_eq!(
634                Token::parse_token(" ld b, nnn")
635                    .unwrap()
636                    .estimated_duration()
637                    .unwrap(),
638                2
639            );
640            assert_eq!(
641                Token::parse_token(" ld e, (hl)")
642                    .unwrap()
643                    .estimated_duration()
644                    .unwrap(),
645                2
646            );
647            assert_eq!(
648                Token::parse_token(" ld d, (hl)")
649                    .unwrap()
650                    .estimated_duration()
651                    .unwrap(),
652                2
653            );
654            assert_eq!(
655                Token::parse_token(" ld a, (hl)")
656                    .unwrap()
657                    .estimated_duration()
658                    .unwrap(),
659                2
660            );
661            assert_eq!(
662                dbg!(Token::parse_token(" ld a, (dd)").unwrap())
663                    .estimated_duration()
664                    .unwrap(),
665                4
666            );
667            assert_eq!(
668                Token::parse_token(" ld hl, (dd)")
669                    .unwrap()
670                    .estimated_duration()
671                    .unwrap(),
672                5
673            );
674            println!("{:?}", Token::parse_token(" ld de, (dd)").unwrap());
675            assert_eq!(
676                Token::parse_token(" ld de, (dd)")
677                    .unwrap()
678                    .estimated_duration()
679                    .unwrap(),
680                6
681            );
682            assert_eq!(
683                Token::parse_token(" ld a, (ix+0)")
684                    .unwrap()
685                    .estimated_duration()
686                    .unwrap(),
687                5
688            );
689            assert_eq!(
690                Token::parse_token(" ld l, (ix+0)")
691                    .unwrap()
692                    .estimated_duration()
693                    .unwrap(),
694                5
695            );
696            assert_eq!(
697                Token::parse_token(" ldi")
698                    .unwrap()
699                    .estimated_duration()
700                    .unwrap(),
701                5
702            );
703            assert_eq!(
704                Token::parse_token(" inc c")
705                    .unwrap()
706                    .estimated_duration()
707                    .unwrap(),
708                1
709            );
710            assert_eq!(
711                Token::parse_token(" inc l")
712                    .unwrap()
713                    .estimated_duration()
714                    .unwrap(),
715                1
716            );
717            assert_eq!(
718                Token::parse_token(" dec c")
719                    .unwrap()
720                    .estimated_duration()
721                    .unwrap(),
722                1
723            );
724            assert_eq!(
725                Token::parse_token(" out (c), d")
726                    .unwrap()
727                    .estimated_duration()
728                    .unwrap(),
729                4
730            );
731            assert_eq!(
732                Token::parse_token(" out (c), c")
733                    .unwrap()
734                    .estimated_duration()
735                    .unwrap(),
736                4
737            );
738            assert_eq!(
739                Token::parse_token(" out (c), e")
740                    .unwrap()
741                    .estimated_duration()
742                    .unwrap(),
743                4
744            );
745            assert_eq!(
746                Token::parse_token(" ld b, 0x7f")
747                    .unwrap()
748                    .estimated_duration()
749                    .unwrap(),
750                2
751            );
752
753            assert!(
754                Token::Basic(None, None, "".to_owned())
755                    .estimated_duration()
756                    .is_err()
757            );
758        }
759    }
760}