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