aboutsummaryrefslogtreecommitdiff
path: root/rust/qemu-api-macros/src/bits.rs
blob: 5ba84757ee029e79c52dccf14335733e99d631aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
// SPDX-License-Identifier: MIT or Apache-2.0 or GPL-2.0-or-later

// shadowing is useful together with "if let"
#![allow(clippy::shadow_unrelated)]

use proc_macro2::{
    Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree, TokenTree as TT,
};

use crate::utils::MacroError;

pub struct BitsConstInternal {
    typ: TokenTree,
}

fn paren(ts: TokenStream) -> TokenTree {
    TT::Group(Group::new(Delimiter::Parenthesis, ts))
}

fn ident(s: &'static str) -> TokenTree {
    TT::Ident(Ident::new(s, Span::call_site()))
}

fn punct(ch: char) -> TokenTree {
    TT::Punct(Punct::new(ch, Spacing::Alone))
}

/// Implements a recursive-descent parser that translates Boolean expressions on
/// bitmasks to invocations of `const` functions defined by the `bits!` macro.
impl BitsConstInternal {
    // primary ::= '(' or ')'
    //           | ident
    //           | '!' ident
    fn parse_primary(
        &self,
        tok: TokenTree,
        it: &mut dyn Iterator<Item = TokenTree>,
        out: &mut TokenStream,
    ) -> Result<Option<TokenTree>, MacroError> {
        let next = match tok {
            TT::Group(ref g) => {
                if g.delimiter() != Delimiter::Parenthesis && g.delimiter() != Delimiter::None {
                    return Err(MacroError::Message("expected parenthesis".into(), g.span()));
                }
                let mut stream = g.stream().into_iter();
                let Some(first_tok) = stream.next() else {
                    return Err(MacroError::Message(
                        "expected operand, found ')'".into(),
                        g.span(),
                    ));
                };
                let mut output = TokenStream::new();
                // start from the lowest precedence
                let next = self.parse_or(first_tok, &mut stream, &mut output)?;
                if let Some(tok) = next {
                    return Err(MacroError::Message(
                        format!("unexpected token {tok}"),
                        tok.span(),
                    ));
                }
                out.extend(Some(paren(output)));
                it.next()
            }
            TT::Ident(_) => {
                let mut output = TokenStream::new();
                output.extend([
                    self.typ.clone(),
                    TT::Punct(Punct::new(':', Spacing::Joint)),
                    TT::Punct(Punct::new(':', Spacing::Joint)),
                    tok,
                ]);
                out.extend(Some(paren(output)));
                it.next()
            }
            TT::Punct(ref p) => {
                if p.as_char() != '!' {
                    return Err(MacroError::Message("expected operand".into(), p.span()));
                }
                let Some(rhs_tok) = it.next() else {
                    return Err(MacroError::Message(
                        "expected operand at end of input".into(),
                        p.span(),
                    ));
                };
                let next = self.parse_primary(rhs_tok, it, out)?;
                out.extend([punct('.'), ident("invert"), paren(TokenStream::new())]);
                next
            }
            _ => {
                return Err(MacroError::Message("unexpected literal".into(), tok.span()));
            }
        };
        Ok(next)
    }

    fn parse_binop<
        F: Fn(
            &Self,
            TokenTree,
            &mut dyn Iterator<Item = TokenTree>,
            &mut TokenStream,
        ) -> Result<Option<TokenTree>, MacroError>,
    >(
        &self,
        tok: TokenTree,
        it: &mut dyn Iterator<Item = TokenTree>,
        out: &mut TokenStream,
        ch: char,
        f: F,
        method: &'static str,
    ) -> Result<Option<TokenTree>, MacroError> {
        let mut next = f(self, tok, it, out)?;
        while next.is_some() {
            let op = next.as_ref().unwrap();
            let TT::Punct(ref p) = op else { break };
            if p.as_char() != ch {
                break;
            }

            let Some(rhs_tok) = it.next() else {
                return Err(MacroError::Message(
                    "expected operand at end of input".into(),
                    p.span(),
                ));
            };
            let mut rhs = TokenStream::new();
            next = f(self, rhs_tok, it, &mut rhs)?;
            out.extend([punct('.'), ident(method), paren(rhs)]);
        }
        Ok(next)
    }

    // sub ::= primary ('-' primary)*
    pub fn parse_sub(
        &self,
        tok: TokenTree,
        it: &mut dyn Iterator<Item = TokenTree>,
        out: &mut TokenStream,
    ) -> Result<Option<TokenTree>, MacroError> {
        self.parse_binop(tok, it, out, '-', Self::parse_primary, "difference")
    }

    // and ::= sub ('&' sub)*
    fn parse_and(
        &self,
        tok: TokenTree,
        it: &mut dyn Iterator<Item = TokenTree>,
        out: &mut TokenStream,
    ) -> Result<Option<TokenTree>, MacroError> {
        self.parse_binop(tok, it, out, '&', Self::parse_sub, "intersection")
    }

    // xor ::= and ('&' and)*
    fn parse_xor(
        &self,
        tok: TokenTree,
        it: &mut dyn Iterator<Item = TokenTree>,
        out: &mut TokenStream,
    ) -> Result<Option<TokenTree>, MacroError> {
        self.parse_binop(tok, it, out, '^', Self::parse_and, "symmetric_difference")
    }

    // or ::= xor ('|' xor)*
    pub fn parse_or(
        &self,
        tok: TokenTree,
        it: &mut dyn Iterator<Item = TokenTree>,
        out: &mut TokenStream,
    ) -> Result<Option<TokenTree>, MacroError> {
        self.parse_binop(tok, it, out, '|', Self::parse_xor, "union")
    }

    pub fn parse(
        it: &mut dyn Iterator<Item = TokenTree>,
    ) -> Result<proc_macro2::TokenStream, MacroError> {
        let mut pos = Span::call_site();
        let mut typ = proc_macro2::TokenStream::new();

        // Gobble everything up to an `@` sign, which is followed by a
        // parenthesized expression; that is, all token trees except the
        // last two form the type.
        let next = loop {
            let tok = it.next();
            if let Some(ref t) = tok {
                pos = t.span();
            }
            match tok {
                None => break None,
                Some(TT::Punct(ref p)) if p.as_char() == '@' => {
                    let tok = it.next();
                    if let Some(ref t) = tok {
                        pos = t.span();
                    }
                    break tok;
                }
                Some(x) => typ.extend(Some(x)),
            }
        };

        let Some(tok) = next else {
            return Err(MacroError::Message(
                "expected expression, do not call this macro directly".into(),
                pos,
            ));
        };
        let TT::Group(ref _group) = tok else {
            return Err(MacroError::Message(
                "expected parenthesis, do not call this macro directly".into(),
                tok.span(),
            ));
        };
        let mut out = TokenStream::new();
        let state = Self {
            typ: TT::Group(Group::new(Delimiter::None, typ)),
        };

        let next = state.parse_primary(tok, it, &mut out)?;

        // A parenthesized expression is a single production of the grammar,
        // so the input must have reached the last token.
        if let Some(tok) = next {
            return Err(MacroError::Message(
                format!("unexpected token {tok}"),
                tok.span(),
            ));
        }
        Ok(out)
    }
}