Skip to main content

scheme_rs/syntax/
lex.rs

1//! Lexical analysis of symbolic expressions
2
3use 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        // tag cannot contain newlines
108        self.curr_span.column += self.pos;
109        Ok(true)
110    }
111
112    #[maybe_async]
113    fn consume_chars(&mut self) -> Result<(), Exception> {
114        // Consume all the characters we need to
115        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        // TODO: Check if the port is empty
129
130        // Check for any interlexeme space:
131        maybe_await!(self.interlexeme_space())?;
132
133        // self.consume_chars()?;
134
135        // Get the current span:
136        let span = self.curr_span();
137
138        // Check for various special characters:
139        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        // Need this because "10i" is not a valid number.
297        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        // Check for special nan/inf
363        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                // Rational number
374                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        // Skip the terminating quote
532        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        // Ignore exactness for now
717        if let Some(imag_part) = value.imag_part {
718            // This is a complex number:
719            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    /// `#\a` characters
849    Literal(char),
850    /// `#\foo` characters
851    Escaped(EscapedCharacter),
852    /// `#\xcafe` characters
853    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        // from r7rs 6.6
874        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}