1use std::sync::Arc;
4
5use super::Span;
6use malachite::{Integer, base::num::conversion::traits::*, rational::Rational};
7use scheme_rs_macros::{maybe_async, maybe_await};
8use unicode_categories::UnicodeCategories;
9
10#[cfg(feature = "async")]
11use futures::future::BoxFuture;
12
13use crate::{
14 exceptions::Exception,
15 num::{self, SimpleNumber},
16 ports::{PortData, PortInfo},
17};
18
19pub struct Lexer<'a> {
20 port_data: &'a mut PortData,
21 port_info: &'a PortInfo,
22 pos: usize,
23 buff: Vec<char>,
24 curr_span: Span,
25}
26
27impl<'a> Lexer<'a> {
28 pub(crate) fn new(port_data: &'a mut PortData, port_info: &'a PortInfo, span: Span) -> Self {
29 Self {
30 port_data,
31 port_info,
32 pos: 0,
33 buff: Vec::new(),
34 curr_span: span,
35 }
36 }
37
38 pub(crate) fn curr_span(&self) -> Span {
39 Span {
40 line: self.curr_span.line,
41 column: self.curr_span.column,
42 offset: self.curr_span.offset + self.pos,
43 file: self.curr_span.file.clone(),
44 }
45 }
46
47 #[maybe_async]
48 fn peek(&mut self) -> Result<Option<char>, Exception> {
49 if self.buff.len() > self.pos {
50 return Ok(Some(self.buff[self.pos]));
51 }
52 while self.buff.len() < self.pos {
53 let Some(chr) = maybe_await!(self.port_data.read_char(self.port_info))? else {
54 return Ok(None);
55 };
56 self.buff.push(chr);
57 }
58 maybe_await!(self.port_data.peekn_chars(self.port_info, 0))
59 }
60
61 fn skip(&mut self) {
62 self.pos += 1;
63 }
64
65 #[maybe_async]
66 pub(crate) fn take(&mut self) -> Result<Option<char>, Exception> {
67 let Some(chr) = maybe_await!(self.peek())? else {
68 return Ok(None);
69 };
70 self.pos += 1;
71 Ok(Some(chr))
72 }
73
74 #[maybe_async]
75 fn match_char(&mut self, chr: char) -> Result<bool, Exception> {
76 Ok(maybe_await!(self.match_pred(|peek| peek == chr))?.is_some())
77 }
78
79 #[maybe_async]
80 fn match_pred(&mut self, pred: impl FnOnce(char) -> bool) -> Result<Option<char>, Exception> {
81 let chr = maybe_await!(self.peek())?;
82 if let Some(chr) = chr
83 && pred(chr)
84 {
85 if chr == '\n' {
86 self.curr_span.line += 1;
87 self.curr_span.column = 0;
88 } else {
89 self.curr_span.column += 1;
90 }
91 self.pos += 1;
92 Ok(Some(chr))
93 } else {
94 Ok(None)
95 }
96 }
97
98 #[maybe_async]
99 fn match_tag(&mut self, tag: &str) -> Result<bool, Exception> {
100 let pos = self.pos;
101 for chr in tag.chars() {
102 if !maybe_await!(self.match_char(chr))? {
103 self.pos = pos;
104 return Ok(false);
105 }
106 }
107 self.curr_span.column += self.pos;
109 Ok(true)
110 }
111
112 #[maybe_async]
113 fn consume_chars(&mut self) -> Result<(), Exception> {
114 if self.pos > self.buff.len() {
116 maybe_await!(
117 self.port_data
118 .consume_chars(self.port_info, self.pos - self.buff.len())
119 )?;
120 }
121 self.pos = 0;
122 self.buff.clear();
123 Ok(())
124 }
125
126 #[maybe_async]
127 pub fn next_token(&mut self) -> Result<Option<Token>, LexerError> {
128 maybe_await!(self.interlexeme_space())?;
132
133 let span = self.curr_span();
137
138 let lexeme = if let Some(number) = maybe_await!(self.number(10))? {
140 Lexeme::Number(number)
141 } else if let Some(identifier) = maybe_await!(self.identifier())? {
142 Lexeme::Identifier(identifier)
143 } else if let Some(chr) = maybe_await!(self.take())? {
144 match chr {
145 '.' => Lexeme::Period,
146 '\'' => Lexeme::Quote,
147 '`' => Lexeme::Backquote,
148 ',' if maybe_await!(self.match_tag("@"))? => Lexeme::CommaAt,
149 ',' => Lexeme::Comma,
150 '(' => Lexeme::LParen,
151 ')' => Lexeme::RParen,
152 '[' => Lexeme::LBracket,
153 ']' => Lexeme::RBracket,
154 '"' => Lexeme::String(maybe_await!(self.string())?),
155 '#' if maybe_await!(self.match_tag(";"))? => Lexeme::DatumComment,
156 '#' if maybe_await!(self.match_tag("\\"))? => {
157 Lexeme::Character(maybe_await!(self.character())?)
158 }
159 '#' if maybe_await!(self.match_tag("F"))? || maybe_await!(self.match_tag("f"))? => {
160 Lexeme::Boolean(false)
161 }
162 '#' if maybe_await!(self.match_tag("T"))? || maybe_await!(self.match_tag("t"))? => {
163 Lexeme::Boolean(true)
164 }
165 '#' if maybe_await!(self.match_tag("("))? => Lexeme::HashParen,
166 '#' if maybe_await!(self.match_tag("vu8("))? => Lexeme::Vu8Paren,
167 '#' if maybe_await!(self.match_tag("'"))? => Lexeme::HashQuote,
168 '#' if maybe_await!(self.match_tag("`"))? => Lexeme::HashBackquote,
169 '#' if maybe_await!(self.match_tag(",@"))? => Lexeme::HashCommaAt,
170 '#' if maybe_await!(self.match_tag(","))? => Lexeme::HashComma,
171 '#' => {
172 let next_chr = maybe_await!(self.take())?;
173 if let Some(chr) = next_chr {
174 return Err(LexerError::UnexpectedCharacter {
175 chr,
176 span: self.curr_span(),
177 });
178 } else {
179 return Err(LexerError::UnexpectedEof);
180 }
181 }
182 '\0' => return Ok(None),
183 chr => return Err(LexerError::UnexpectedCharacter { chr, span }),
184 }
185 } else {
186 return Ok(None);
187 };
188
189 maybe_await!(self.consume_chars())?;
190
191 Ok(Some(Token { lexeme, span }))
192 }
193
194 #[maybe_async]
195 fn interlexeme_space(&mut self) -> Result<(), Exception> {
196 loop {
197 if maybe_await!(self.match_char(';'))? {
198 maybe_await!(self.comment())?;
199 } else if maybe_await!(self.match_tag("#|"))? {
200 maybe_await!(self.nested_comment())?;
201 } else if !maybe_await!(self.match_tag("#!r6rs"))?
202 && maybe_await!(self.match_pred(is_whitespace))?.is_none()
203 {
204 break;
205 }
206 }
207 Ok(())
208 }
209
210 #[maybe_async]
211 fn comment(&mut self) -> Result<(), Exception> {
212 while maybe_await!(self.match_pred(|chr| chr != '\n'))?.is_some() {}
213 Ok(())
214 }
215
216 #[cfg(feature = "async")]
217 fn nested_comment(&mut self) -> BoxFuture<'_, Result<(), Exception>> {
218 Box::pin(self.nested_comment_inner())
219 }
220
221 #[cfg(not(feature = "async"))]
222 fn nested_comment(&mut self) -> Result<(), Exception> {
223 self.nested_comment_inner()
224 }
225
226 #[maybe_async]
227 fn nested_comment_inner(&mut self) -> Result<(), Exception> {
228 while !maybe_await!(self.match_tag("|#"))? {
229 if maybe_await!(self.match_tag("#|"))? {
230 maybe_await!(self.nested_comment())?;
231 } else {
232 self.skip();
233 }
234 }
235 Ok(())
236 }
237
238 #[maybe_async]
239 fn character(&mut self) -> Result<Character, LexerError> {
240 let chr = if maybe_await!(self.match_tag("alarm"))? {
241 Character::Escaped(EscapedCharacter::Alarm)
242 } else if maybe_await!(self.match_tag("backspace"))? {
243 Character::Escaped(EscapedCharacter::Backspace)
244 } else if maybe_await!(self.match_tag("delete"))? {
245 Character::Escaped(EscapedCharacter::Delete)
246 } else if maybe_await!(self.match_tag("esc"))? {
247 Character::Escaped(EscapedCharacter::Escape)
248 } else if maybe_await!(self.match_tag("newline"))?
249 || maybe_await!(self.match_tag("linefeed"))?
250 {
251 Character::Escaped(EscapedCharacter::Newline)
252 } else if maybe_await!(self.match_tag("nul"))? {
253 Character::Escaped(EscapedCharacter::Nul)
254 } else if maybe_await!(self.match_tag("return"))? {
255 Character::Escaped(EscapedCharacter::Return)
256 } else if maybe_await!(self.match_tag("space"))? {
257 Character::Escaped(EscapedCharacter::Space)
258 } else if maybe_await!(self.match_tag("tab"))? {
259 Character::Escaped(EscapedCharacter::Tab)
260 } else if maybe_await!(self.match_tag("vtab"))? {
261 Character::Escaped(EscapedCharacter::VTab)
262 } else if maybe_await!(self.match_tag("page"))? {
263 Character::Escaped(EscapedCharacter::Page)
264 } else if maybe_await!(self.match_char('x'))? {
265 if is_delimiter(maybe_await!(self.peek())?.ok_or(LexerError::UnexpectedEof)?) {
266 Character::Literal('x')
267 } else {
268 let mut unicode = String::new();
269 while let Some(chr) = maybe_await!(self.match_pred(|c| c.is_ascii_hexdigit()))? {
270 unicode.push(chr);
271 }
272 Character::Unicode(unicode)
273 }
274 } else {
275 Character::Literal(maybe_await!(self.take())?.ok_or(LexerError::UnexpectedEof)?)
276 };
277 let peeked = maybe_await!(self.peek())?;
278 if let Some(peeked) = peeked
279 && !is_delimiter(peeked)
280 {
281 let span = self.curr_span();
282 Err(LexerError::UnexpectedCharacter { chr: peeked, span })
283 } else {
284 Ok(chr)
285 }
286 }
287
288 #[maybe_async]
289 pub(crate) fn number(&mut self, default_radix: u32) -> Result<Option<Number>, Exception> {
290 let saved_pos = self.pos;
291
292 let (radix, exactness) = maybe_await!(self.radix_and_exactness())?;
293
294 let radix = radix.unwrap_or(default_radix);
295
296 let has_sign = {
298 let peeked = maybe_await!(self.peek())?;
299 peeked == Some('+') || peeked == Some('-')
300 };
301
302 let first_part = maybe_await!(self.part(radix))?;
303
304 if first_part.is_none() {
305 self.pos = saved_pos;
306 return Ok(None);
307 }
308
309 let number = if maybe_await!(self.match_char('i'))? {
310 if !has_sign {
311 self.pos = saved_pos;
312 return Ok(None);
313 }
314 Number {
315 radix,
316 exactness,
317 real_part: None,
318 imag_part: first_part,
319 is_polar: false,
320 }
321 } else {
322 let matched_at = maybe_await!(self.match_char('@'))?;
323 let imag_part = if matched_at || {
324 let peeked = maybe_await!(self.peek())?;
325 peeked == Some('+') || peeked == Some('-')
326 } {
327 let second_part = maybe_await!(self.part(radix))?;
328 if second_part.is_none() || !matched_at && !maybe_await!(self.match_char('i'))? {
329 self.pos = saved_pos;
330 return Ok(None);
331 }
332 second_part
333 } else {
334 None
335 };
336 Number {
337 radix,
338 exactness,
339 real_part: first_part,
340 imag_part,
341 is_polar: matched_at,
342 }
343 };
344
345 match maybe_await!(self.peek()) {
346 Ok(Some(chr)) if is_subsequent(chr) => {
347 self.pos = saved_pos;
348 return Ok(None);
349 }
350 Err(err) => return Err(err),
351 Ok(_) => (),
352 }
353
354 Ok(Some(number))
355 }
356
357 #[maybe_async]
358 fn part(&mut self, radix: u32) -> Result<Option<Part>, Exception> {
359 let neg = !maybe_await!(self.match_char('+'))? && maybe_await!(self.match_char('-'))?;
360 let mut mantissa_width = None;
361
362 let real = if maybe_await!(self.match_tag("nan.0"))? {
364 Real::Nan
365 } else if maybe_await!(self.match_tag("inf.0"))? {
366 Real::Inf
367 } else {
368 let mut num = String::new();
369 while let Some(ch) = maybe_await!(self.match_pred(|chr| chr.is_digit(radix)))? {
370 num.push(ch);
371 }
372 if !num.is_empty() && maybe_await!(self.match_char('/'))? {
373 let mut denom = String::new();
375 while let Some(ch) = maybe_await!(self.match_pred(|chr| chr.is_digit(radix)))? {
376 denom.push(ch);
377 }
378 if denom.is_empty() {
379 return Ok(None);
380 }
381 Real::Rational(num, denom)
382 } else if radix == 10 {
383 let mut fractional = String::new();
384 if maybe_await!(self.match_char('.'))? {
385 while let Some(ch) = maybe_await!(self.match_pred(|chr| chr.is_digit(radix)))? {
386 fractional.push(ch);
387 }
388 }
389 if num.is_empty() && fractional.is_empty() {
390 return Ok(None);
391 }
392 let suffix = maybe_await!(self.suffix())?;
393 if maybe_await!(self.match_char('|'))? {
394 let mut width = 0;
395 while let Some(chr) = maybe_await!(self.match_pred(|chr| chr.is_ascii_digit()))?
396 {
397 width = width * 10 + chr.to_digit(10).unwrap() as usize;
398 }
399 mantissa_width = Some(width);
400 }
401 Real::Decimal(num, fractional, suffix)
402 } else if num.is_empty() {
403 return Ok(None);
404 } else {
405 Real::Num(num)
406 }
407 };
408
409 Ok(Some(Part {
410 neg,
411 real,
412 mantissa_width,
413 }))
414 }
415
416 #[maybe_async]
417 fn exactness(&mut self) -> Result<Option<Exactness>, Exception> {
418 Ok(
419 if maybe_await!(self.match_tag("#i"))? || maybe_await!(self.match_tag("#I"))? {
420 Some(Exactness::Inexact)
421 } else if maybe_await!(self.match_tag("#e"))? || maybe_await!(self.match_tag("#E"))? {
422 Some(Exactness::Exact)
423 } else {
424 None
425 },
426 )
427 }
428
429 #[maybe_async]
430 fn radix(&mut self) -> Result<Option<u32>, Exception> {
431 Ok(
432 if maybe_await!(self.match_tag("#b"))? || maybe_await!(self.match_tag("#B"))? {
433 Some(2)
434 } else if maybe_await!(self.match_tag("#o"))? || maybe_await!(self.match_tag("#O"))? {
435 Some(8)
436 } else if maybe_await!(self.match_tag("#x"))? || maybe_await!(self.match_tag("#X"))? {
437 Some(16)
438 } else if maybe_await!(self.match_tag("#d"))? || maybe_await!(self.match_tag("#D"))? {
439 Some(10)
440 } else {
441 None
442 },
443 )
444 }
445
446 #[maybe_async]
447 fn radix_and_exactness(&mut self) -> Result<(Option<u32>, Option<Exactness>), Exception> {
448 let exactness = maybe_await!(self.exactness())?;
449 let radix = maybe_await!(self.radix())?;
450 if exactness.is_some() {
451 Ok((radix, exactness))
452 } else {
453 Ok((radix, maybe_await!(self.exactness())?))
454 }
455 }
456
457 #[maybe_async]
458 fn suffix(&mut self) -> Result<Option<isize>, Exception> {
459 let pos = self.pos;
460 if maybe_await!(
461 self.match_pred(|chr| matches!(chr.to_ascii_lowercase(), 'e' | 's' | 'f' | 'd' | 'l'))
462 )?
463 .is_some()
464 {
465 let neg = !maybe_await!(self.match_char('+'))? && maybe_await!(self.match_char('-'))?;
466 let mut suffix = String::new();
467 while let Some(chr) = maybe_await!(self.match_pred(|chr| chr.is_ascii_digit()))? {
468 suffix.push(chr);
469 }
470 if !suffix.is_empty() {
471 let val: isize = suffix.parse().unwrap();
472 if neg {
473 return Ok(Some(-val));
474 } else {
475 return Ok(Some(val));
476 }
477 }
478 }
479 self.pos = pos;
480 Ok(None)
481 }
482
483 #[maybe_async]
484 fn string(&mut self) -> Result<String, LexerError> {
485 let mut output = String::new();
486 while let Some(chr) = maybe_await!(self.match_pred(|chr| chr != '"'))? {
487 if chr == '\\' {
488 let escaped = match maybe_await!(self.take())?.ok_or(LexerError::UnexpectedEof)? {
489 'x' => {
490 let escaped = maybe_await!(self.inline_hex_escape())?;
491 output.push_str(&escaped);
492 continue;
493 }
494 'a' => '\u{07}',
495 'b' => '\u{08}',
496 't' => '\t',
497 'n' => '\n',
498 'r' => '\r',
499 'v' => '\u{0B}',
500 'f' => '\u{0C}',
501 '"' => '"',
502 '\\' => '\\',
503 '\n' => {
504 while maybe_await!(self.match_pred(is_intraline_whitespace))?.is_some() {}
505 continue;
506 }
507 chr if is_intraline_whitespace(chr) => {
508 while maybe_await!(
509 self.match_pred(|chr| chr != '\n' && is_intraline_whitespace(chr))
510 )?
511 .is_some()
512 {}
513 let chr = maybe_await!(self.take())?.ok_or(LexerError::UnexpectedEof)?;
514 if chr != '\n' {
515 let span = self.curr_span();
516 return Err(LexerError::UnexpectedCharacter { chr, span });
517 }
518 while maybe_await!(self.match_pred(is_intraline_whitespace))?.is_some() {}
519 continue;
520 }
521 chr => {
522 let span = self.curr_span();
523 return Err(LexerError::BadEscapeCharacter { chr, span });
524 }
525 };
526 output.push(escaped);
527 } else {
528 output.push(chr);
529 }
530 }
531 self.skip();
533 Ok(output)
534 }
535
536 #[maybe_async]
537 fn identifier(&mut self) -> Result<Option<String>, LexerError> {
538 let mut ident = if maybe_await!(self.match_tag("\\x"))? {
539 maybe_await!(self.inline_hex_escape())?
540 } else if maybe_await!(self.match_tag("..."))? {
541 String::from("...")
542 } else if maybe_await!(self.match_tag("->"))? {
543 String::from("->")
544 } else if let Some(initial) =
545 maybe_await!(self.match_pred(|chr| is_initial(chr) || is_peculiar_initial(chr)))?
546 {
547 String::from(initial)
548 } else {
549 return Ok(None);
550 };
551
552 loop {
553 if maybe_await!(self.match_tag("\\x"))? {
554 ident.push_str(&maybe_await!(self.inline_hex_escape())?);
555 } else if let Some(next) = maybe_await!(self.match_pred(is_subsequent))? {
556 ident.push(next);
557 } else {
558 break;
559 }
560 }
561
562 Ok(Some(ident))
563 }
564
565 #[maybe_async]
566 fn inline_hex_escape(&mut self) -> Result<String, LexerError> {
567 let mut escaped = String::new();
568 let mut buff = String::with_capacity(2);
569 while let Some(chr) = maybe_await!(self.match_pred(|chr| chr != ';'))? {
570 if !chr.is_ascii_hexdigit() {
571 return Err(LexerError::InvalidCharacterInHexEscape {
572 chr,
573 span: self.curr_span(),
574 });
575 }
576 buff.push(chr);
577 if buff.len() == 2 {
578 escaped.push(u8::from_str_radix(&buff, 16).unwrap() as char);
579 buff.clear();
580 }
581 }
582 if !buff.is_empty() {
583 escaped.push(u8::from_str_radix(&buff, 16).unwrap() as char);
584 }
585 maybe_await!(self.take())?;
586 Ok(escaped)
587 }
588}
589
590#[derive(Debug)]
591pub enum LexerError {
592 UnexpectedEof,
593 InvalidCharacterInHexEscape { chr: char, span: Span },
594 UnexpectedCharacter { chr: char, span: Span },
595 BadEscapeCharacter { chr: char, span: Span },
596 ReadError(Exception),
597}
598
599impl From<Exception> for LexerError {
600 fn from(error: Exception) -> Self {
601 Self::ReadError(error)
602 }
603}
604
605fn is_delimiter(chr: char) -> bool {
606 is_whitespace(chr) || matches!(chr, '(' | ')' | '[' | ']' | '"' | ';' | '#')
607}
608
609fn is_whitespace(chr: char) -> bool {
610 chr.is_separator() || matches!(chr, '\t' | '\n' | '\r')
611}
612
613fn is_intraline_whitespace(chr: char) -> bool {
614 chr == '\t' || chr.is_separator()
615}
616
617fn is_initial(chr: char) -> bool {
618 is_constituent(chr) || is_special_initial(chr)
619}
620
621fn is_constituent(c: char) -> bool {
622 c.is_ascii_alphabetic()
623 || (c as u32 > 127
624 && (c.is_letter()
625 || c.is_mark_nonspacing()
626 || c.is_number_letter()
627 || c.is_number_other()
628 || c.is_punctuation_dash()
629 || c.is_punctuation_connector()
630 || c.is_punctuation_other()
631 || c.is_symbol()
632 || c.is_other_private_use()))
633}
634
635fn is_special_initial(chr: char) -> bool {
636 matches!(
637 chr,
638 '!' | '$' | '%' | '&' | '*' | '/' | ':' | '<' | '=' | '>' | '?' | '^' | '_' | '~'
639 )
640}
641
642fn is_peculiar_initial(chr: char) -> bool {
643 matches!(chr, '+' | '-')
644}
645
646fn is_special_subsequent(chr: char) -> bool {
647 matches!(chr, '+' | '-' | '.' | '@')
648}
649
650fn is_subsequent(chr: char) -> bool {
651 is_initial(chr)
652 || chr.is_ascii_digit()
653 || chr.is_number_decimal_digit()
654 || chr.is_mark_spacing_combining()
655 || chr.is_mark_enclosing()
656 || is_special_subsequent(chr)
657}
658
659#[derive(Clone, Debug)]
660pub struct Token {
661 pub lexeme: Lexeme,
662 pub span: super::Span,
663}
664
665#[derive(Clone, Debug, PartialEq)]
666pub enum Lexeme {
667 Identifier(String),
668 Boolean(bool),
669 Number(Number),
670 Character(Character),
671 String(String),
672 LParen,
673 RParen,
674 LBracket,
675 RBracket,
676 HashParen,
677 Vu8Paren,
678 Quote,
679 Backquote,
680 Comma,
681 CommaAt,
682 Period,
683 HashQuote,
684 HashBackquote,
685 HashComma,
686 HashCommaAt,
687 DatumComment,
688}
689
690#[derive(Clone, Debug, PartialEq)]
691pub struct Number {
692 radix: u32,
693 exactness: Option<Exactness>,
694 real_part: Option<Part>,
695 imag_part: Option<Part>,
696 is_polar: bool,
697}
698
699impl TryFrom<(Part, u32)> for SimpleNumber {
700 type Error = ParseNumberError;
701
702 fn try_from((part, radix): (Part, u32)) -> Result<Self, Self::Error> {
703 part.try_into_i64(radix)
704 .map(SimpleNumber::FixedInteger)
705 .or_else(|| part.try_into_integer(radix).map(SimpleNumber::BigInteger))
706 .or_else(|| part.try_into_rational(radix).map(SimpleNumber::Rational))
707 .or_else(|| part.try_into_f64(radix).map(SimpleNumber::Real))
708 .ok_or(ParseNumberError::NoValidRepresentation)
709 }
710}
711
712impl TryFrom<Number> for num::Number {
713 type Error = ParseNumberError;
714
715 fn try_from(value: Number) -> Result<Self, Self::Error> {
716 if let Some(imag_part) = value.imag_part {
718 let imag_part: SimpleNumber = (imag_part, value.radix).try_into()?;
720 let real_part: SimpleNumber = if let Some(real_part) = value.real_part {
721 (real_part, value.radix).try_into()?
722 } else {
723 SimpleNumber::Real(0.0)
724 };
725 return Ok(num::Number(Arc::new(num::NumberInner::Complex(
726 if value.is_polar {
727 num::ComplexNumber::from_polar(real_part, imag_part)
728 } else {
729 num::ComplexNumber::new(real_part, imag_part)
730 },
731 ))));
732 }
733
734 let part = value
735 .real_part
736 .ok_or(ParseNumberError::NoValidRepresentation)?;
737
738 Ok(num::Number(Arc::new(num::NumberInner::Simple(
739 (part, value.radix).try_into()?,
740 ))))
741 }
742}
743
744#[derive(Debug)]
745pub enum ParseNumberError {
746 NoValidRepresentation,
747}
748
749#[derive(Clone, Debug, PartialEq)]
750struct Part {
751 neg: bool,
752 real: Real,
753 mantissa_width: Option<usize>,
754}
755
756impl Part {
757 fn try_into_i64(&self, radix: u32) -> Option<i64> {
758 let num = match &self.real {
759 Real::Num(num) => i64::from_str_radix(num, radix).ok()?,
760 Real::Decimal(base, fract, None) if fract.is_empty() => base.parse().ok()?,
761 Real::Decimal(base, fract, Some(exp)) if fract.is_empty() && !exp.is_negative() => {
762 let base: i64 = base.parse().ok()?;
763 let exp = 10_i64.checked_pow((*exp).try_into().ok()?)?;
764 base.checked_mul(exp)?
765 }
766 _ => return None,
767 };
768 Some(if self.neg { -num } else { num })
769 }
770
771 fn try_into_integer(&self, radix: u32) -> Option<Integer> {
772 let num = match &self.real {
773 Real::Num(num) => Integer::from_string_base(radix as u8, num)?,
774 Real::Decimal(base, fract, None) if fract.is_empty() => {
775 Integer::from_string_base(10, base)?
776 }
777 Real::Decimal(base, fract, Some(exp)) if fract.is_empty() && !exp.is_negative() => {
778 Integer::from_sci_string(&format!("{base}e{exp}"))?
779 }
780 _ => return None,
781 };
782 Some(if self.neg { -num } else { num })
783 }
784
785 fn try_into_rational(&self, radix: u32) -> Option<Rational> {
786 let num = match &self.real {
787 Real::Rational(num, denom) => {
788 let num = Integer::from_string_base(radix as u8, num)?;
789 let den = Integer::from_string_base(radix as u8, denom)?;
790 if den == 0 {
791 return None;
792 }
793 Rational::from_integers(num, den)
794 }
795 _ => return None,
796 };
797 Some(if self.neg { -num } else { num })
798 }
799
800 fn try_into_f64(&self, radix: u32) -> Option<f64> {
801 match &self.real {
802 Real::Nan => Some(f64::NAN),
803 Real::Inf if !self.neg => Some(f64::INFINITY),
804 Real::Inf if self.neg => Some(f64::NEG_INFINITY),
805 Real::Num(s) if radix == 10 => {
806 let num: f64 = s.parse().ok()?;
807 Some(if self.neg { -num } else { num })
808 }
809 Real::Rational(num, den) if radix == 10 => {
810 let num: f64 = num.parse().ok()?;
811 let den: f64 = den.parse().ok()?;
812 if den == 0.0 {
813 return None;
814 }
815 let num = num / den;
816 Some(if self.neg { -num } else { num })
817 }
818 Real::Decimal(base, fract, None) => {
819 let num: f64 = format!("{base}.{fract}").parse().ok()?;
820 Some(if self.neg { -num } else { num })
821 }
822 Real::Decimal(base, fract, Some(exp)) => {
823 let num: f64 = format!("{base}.{fract}e{exp}").parse().ok()?;
824 Some(if self.neg { -num } else { num })
825 }
826 _ => None,
827 }
828 }
829}
830
831#[derive(Clone, Debug, PartialEq)]
832enum Exactness {
833 Exact,
834 Inexact,
835}
836
837#[derive(Clone, Debug, PartialEq)]
838enum Real {
839 Nan,
840 Inf,
841 Num(String),
842 Rational(String, String),
843 Decimal(String, String, Option<isize>),
844}
845
846#[derive(Clone, Debug, PartialEq, Eq)]
847pub enum Character {
848 Literal(char),
850 Escaped(EscapedCharacter),
852 Unicode(String),
854}
855
856#[derive(Clone, Copy, Debug, PartialEq, Eq)]
857pub enum EscapedCharacter {
858 Nul,
859 Alarm,
860 Backspace,
861 Tab,
862 Newline,
863 VTab,
864 Page,
865 Return,
866 Escape,
867 Space,
868 Delete,
869}
870
871impl From<EscapedCharacter> for char {
872 fn from(c: EscapedCharacter) -> char {
873 match c {
875 EscapedCharacter::Nul => '\u{0000}',
876 EscapedCharacter::Alarm => '\u{0007}',
877 EscapedCharacter::Backspace => '\u{0008}',
878 EscapedCharacter::Tab => '\u{0009}',
879 EscapedCharacter::Newline => '\u{000A}',
880 EscapedCharacter::VTab => '\u{000B}',
881 EscapedCharacter::Page => '\u{000C}',
882 EscapedCharacter::Return => '\u{000D}',
883 EscapedCharacter::Escape => '\u{001B}',
884 EscapedCharacter::Space => ' ',
885 EscapedCharacter::Delete => '\u{007F}',
886 }
887 }
888}
889
890#[cfg(test)]
891mod test {
892 use super::*;
893
894 #[test]
895 fn is_hash_identifier_char() {
896 assert!(!is_initial('#') && !is_peculiar_initial('#'))
897 }
898}