wasm_wave/
lex.rs

1//! Lexing types
2
3use std::fmt::Display;
4
5pub use logos::Span;
6
7/// A WAVE `logos::Lexer`
8pub type Lexer<'source> = logos::Lexer<'source, Token>;
9
10/// A WAVE token
11#[derive(Clone, Copy, Debug, PartialEq, Eq, logos::Logos)]
12#[logos(error = Option<Span>)]
13#[logos(skip r"[ \t\n\r]+")]
14#[logos(skip r"//[^\n]*")]
15#[logos(subpattern label_word = r"[a-z][a-z0-9]*|[A-Z][A-Z0-9]*")]
16#[logos(subpattern char_escape = r#"\\['"tnr\\]|\\u\{[0-9a-fA-F]{1,6}\}"#)]
17pub enum Token {
18    /// The `{` symbol
19    #[token("{")]
20    BraceOpen,
21    /// The `}` symbol
22    #[token("}")]
23    BraceClose,
24
25    /// The `(` symbol
26    #[token("(")]
27    ParenOpen,
28    /// The `)` symbol
29    #[token(")")]
30    ParenClose,
31
32    /// The `[` symbol
33    #[token("[")]
34    BracketOpen,
35    /// The `]` symbol
36    #[token("]")]
37    BracketClose,
38
39    /// The `:` symbol
40    #[token(":")]
41    Colon,
42
43    /// The `,` symbol
44    #[token(",")]
45    Comma,
46
47    /// A number literal
48    #[regex(r"-?(0|([1-9][0-9]*))(\.[0-9]+)?([eE][-+]?[0-9]+)?")]
49    #[token("-inf")]
50    Number,
51
52    /// A label or keyword
53    #[regex(r"%?(?&label_word)(-(?&label_word))*")]
54    LabelOrKeyword,
55
56    /// A char literal
57    #[regex(r#"'([^\\'\n]{1,4}|(?&char_escape))'"#, validate_char)]
58    Char,
59
60    /// A string literal
61    #[regex(r#""([^\\"\n]|(?&char_escape))*""#)]
62    String,
63
64    /// A multi-line string literal
65    #[token(r#"""""#, lex_multiline_string)]
66    MultilineString,
67}
68
69impl Display for Token {
70    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71        write!(f, "{self:?}")
72    }
73}
74
75fn validate_char(lex: &mut Lexer) -> Result<(), Option<Span>> {
76    let s = &lex.slice()[1..lex.slice().len() - 1];
77    if s.starts_with('\\') || s.chars().count() == 1 {
78        Ok(())
79    } else {
80        Err(Some(lex.span()))
81    }
82}
83
84fn lex_multiline_string(lex: &mut Lexer) -> bool {
85    if let Some(end) = lex.remainder().find(r#"""""#) {
86        lex.bump(end + 3);
87        true
88    } else {
89        false
90    }
91}
92
93/// A WAVE keyword
94#[derive(Clone, Copy, Debug, PartialEq)]
95#[allow(missing_docs)]
96pub enum Keyword {
97    True,
98    False,
99    Some,
100    None,
101    Ok,
102    Err,
103    Inf,
104    Nan,
105}
106
107impl Keyword {
108    /// Returns any keyword exactly matching the given string.
109    pub fn decode(raw_label: &str) -> Option<Self> {
110        Some(match raw_label {
111            "true" => Self::True,
112            "false" => Self::False,
113            "some" => Self::Some,
114            "none" => Self::None,
115            "ok" => Self::Ok,
116            "err" => Self::Err,
117            "inf" => Self::Inf,
118            "nan" => Self::Nan,
119            _ => return None,
120        })
121    }
122}
123
124impl Display for Keyword {
125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126        f.write_str(match self {
127            Keyword::True => "true",
128            Keyword::False => "false",
129            Keyword::Some => "some",
130            Keyword::None => "none",
131            Keyword::Ok => "ok",
132            Keyword::Err => "err",
133            Keyword::Inf => "inf",
134            Keyword::Nan => "nan",
135        })
136    }
137}