Skip to main content

wast/core/
import.rs

1use crate::core::*;
2use crate::kw;
3use crate::parser::{Cursor, Parse, Parser, Peek, Result};
4use crate::token::{Id, LParen, NameAnnotation, Span};
5
6/// An `import` statement and entry in a WebAssembly module.
7#[derive(Debug, Clone)]
8pub struct Imports<'a> {
9    /// Where this `import` statement was defined.
10    pub span: Span,
11    /// All items inside the `import` statement.
12    pub items: ImportItems<'a>,
13}
14
15/// The individual items inside an `import` statement, possibly in one of the
16/// "compact" forms.
17#[derive(Debug, Clone)]
18pub enum ImportItems<'a> {
19    /// A single import item:
20    ///
21    /// ```text
22    /// (import "mod" "name" <type>)
23    /// ```
24    Single {
25        /// The name of the module being importing from.
26        module: &'a str,
27        /// The name of the field in the module being imported from.
28        name: &'a str,
29        /// The type of the item being imported.
30        sig: ItemSig<'a>,
31    },
32
33    /// A group of import items with a common module name:
34    ///
35    /// ```text
36    /// (import "mod" (item "foo" <type>) (item "bar" <type>))
37    /// ```
38    Group1 {
39        /// The name of the module being imported from.
40        module: &'a str,
41        /// The individual items being imported (name/type).
42        items: Vec<ImportGroup1Item<'a>>,
43    },
44
45    /// A group of import items with a common module name and type:
46    ///
47    /// ```text
48    /// (import "mod" (item "foo") (item "bar") <type>)
49    /// ```
50    Group2 {
51        /// The name of the module being imported from.
52        module: &'a str,
53        /// The type of all the items being imported.
54        sig: ItemSig<'a>,
55        /// The individual items being imported (names only).
56        items: Vec<ImportGroup2Item<'a>>,
57    },
58}
59
60/// An item in a group of compact imports sharing a module name. Used with [`ImportItems::Group1`].
61///
62/// ```text
63/// (item "foo" <type>)
64/// ```
65#[derive(Debug, Clone)]
66pub struct ImportGroup1Item<'a> {
67    /// Where this `item` was defined.
68    pub span: Span,
69    /// The name of the item being imported.
70    pub name: &'a str,
71    /// The type of the item being imported.
72    pub sig: ItemSig<'a>,
73}
74
75/// An item in a group of compact imports sharing a module name and type. Used with [`ImportItems::Group2`].
76#[derive(Debug, Clone)]
77pub struct ImportGroup2Item<'a> {
78    /// Where this `item` was defined.
79    pub span: Span,
80    /// The name of the item being imported.
81    pub name: &'a str,
82}
83
84enum CompactImportEncoding {
85    Unknown,
86    Encoding1,
87    Encoding2,
88}
89
90impl<'a> Imports<'a> {
91    /// Constructs an Imports object for a single import item.
92    pub fn single(span: Span, module: &'a str, name: &'a str, sig: ItemSig<'a>) -> Self {
93        Self {
94            span,
95            items: ImportItems::Single { module, name, sig },
96        }
97    }
98
99    /// Returns the number of import items defined in the group.
100    pub fn num_items(&self) -> usize {
101        match &self.items {
102            ImportItems::Single {
103                module: _,
104                name: _,
105                sig: _,
106            } => 1,
107            ImportItems::Group1 { module: _, items } => items.len(),
108            ImportItems::Group2 {
109                module: _,
110                sig: _,
111                items,
112            } => items.len(),
113        }
114    }
115
116    /// Returns the ItemSig for each defined import in the group. Items using
117    /// compact encoding 2 will share an ItemSig.
118    pub fn item_sigs(&self) -> Vec<&ItemSig<'a>> {
119        let res = match &self.items {
120            ImportItems::Single {
121                module: _,
122                name: _,
123                sig,
124            } => vec![sig],
125            ImportItems::Group1 { module: _, items } => {
126                items.iter().map(|item| &item.sig).collect()
127            }
128            ImportItems::Group2 {
129                module: _,
130                sig,
131                items,
132            } => vec![sig; items.len()],
133        };
134        debug_assert!(res.len() == self.num_items());
135        res
136    }
137
138    /// Returns mutable references to each ItemSig defined in the group. This
139    /// may be less than the number of imports defined in the group, if items
140    /// share a sig.
141    pub fn unique_sigs_mut(&mut self) -> Vec<&mut ItemSig<'a>> {
142        match &mut self.items {
143            ImportItems::Single {
144                module: _,
145                name: _,
146                sig: item,
147            } => vec![item],
148            ImportItems::Group1 { module: _, items } => {
149                items.iter_mut().map(|item| &mut item.sig).collect()
150            }
151            ImportItems::Group2 {
152                module: _,
153                sig,
154                items: _,
155            } => vec![sig],
156        }
157    }
158}
159
160struct ImportGroupItemCommon<'a> {
161    span: Span,
162    name: &'a str,
163    sig: Option<ItemSig<'a>>,
164}
165
166impl<'a> Parse<'a> for Imports<'a> {
167    fn parse(parser: Parser<'a>) -> Result<Self> {
168        let span = parser.parse::<kw::import>()?.0;
169        let module = parser.parse()?;
170        if parser.peek::<LParen>()? {
171            let mut encoding = CompactImportEncoding::Unknown;
172            let mut items = Vec::new();
173            while parser.peek2::<kw::item>()? {
174                let item: ImportGroupItemCommon = parser.parens(|p| p.parse())?;
175                match item.sig {
176                    Some(_) => {
177                        // Compact encoding 1 (name / type pairs)
178                        match encoding {
179                            CompactImportEncoding::Unknown => {
180                                encoding = CompactImportEncoding::Encoding1
181                            }
182                            CompactImportEncoding::Encoding1 => {}
183                            CompactImportEncoding::Encoding2 => {
184                                return Err(parser.error("unexpected import type"));
185                            }
186                        }
187                    }
188                    None => {
189                        // Compact encoding 2 (names only)
190                        match encoding {
191                            CompactImportEncoding::Unknown => {
192                                encoding = CompactImportEncoding::Encoding2
193                            }
194                            CompactImportEncoding::Encoding1 => {
195                                return Err(parser.error("unexpected `)`"));
196                            }
197                            CompactImportEncoding::Encoding2 => {}
198                        }
199                    }
200                }
201                items.push(item);
202            }
203
204            match encoding {
205                CompactImportEncoding::Unknown => Err(parser.error("expected import items")),
206                CompactImportEncoding::Encoding1 => Ok(Imports {
207                    span,
208                    items: ImportItems::Group1 {
209                        module,
210                        items: items
211                            .into_iter()
212                            .map(|item| ImportGroup1Item {
213                                span: item.span,
214                                name: item.name,
215                                sig: item.sig.unwrap(),
216                            })
217                            .collect(),
218                    },
219                }),
220                CompactImportEncoding::Encoding2 => {
221                    let sig: ItemSig = parser.parens(|p| p.parse())?;
222                    if let Some(id) = sig.id {
223                        return Err(parser.error_at(id.span(), "identifier not allowed"));
224                    }
225                    Ok(Imports {
226                        span,
227                        items: ImportItems::Group2 {
228                            module,
229                            sig,
230                            items: items
231                                .into_iter()
232                                .map(|item| ImportGroup2Item {
233                                    span: item.span,
234                                    name: item.name,
235                                })
236                                .collect(),
237                        },
238                    })
239                }
240            }
241        } else {
242            // Single item
243            let field = parser.parse()?;
244            let sig = parser.parens(|p| p.parse())?;
245            Ok(Imports::single(span, module, field, sig))
246        }
247    }
248}
249
250impl<'a> Parse<'a> for ImportGroupItemCommon<'a> {
251    fn parse(parser: Parser<'a>) -> Result<Self> {
252        let span = parser.parse::<kw::item>()?.0;
253        Ok(ImportGroupItemCommon {
254            span,
255            name: parser.parse()?,
256            sig: if parser.is_empty() {
257                None
258            } else {
259                Some(parser.parens(|p| p.parse())?)
260            },
261        })
262    }
263}
264
265#[derive(Debug, Clone)]
266#[allow(missing_docs)]
267pub struct ItemSig<'a> {
268    /// Where this item is defined in the source.
269    pub span: Span,
270    /// An optional identifier used during name resolution to refer to this item
271    /// from the rest of the module.
272    pub id: Option<Id<'a>>,
273    /// An optional name which, for functions, will be stored in the
274    /// custom `name` section.
275    pub name: Option<NameAnnotation<'a>>,
276    /// What kind of item this is.
277    pub kind: ItemKind<'a>,
278}
279
280#[derive(Debug, Clone)]
281#[allow(missing_docs)]
282pub enum ItemKind<'a> {
283    Func(TypeUse<'a, FunctionType<'a>>),
284    Table(TableType<'a>),
285    Memory(MemoryType),
286    Global(GlobalType<'a>),
287    Tag(TagType<'a>),
288    FuncExact(TypeUse<'a, FunctionType<'a>>),
289}
290
291impl<'a> Parse<'a> for ItemSig<'a> {
292    fn parse(parser: Parser<'a>) -> Result<Self> {
293        let mut l = parser.lookahead1();
294        if l.peek::<kw::func>()? {
295            let span = parser.parse::<kw::func>()?.0;
296            let id = parser.parse()?;
297            let name = parser.parse()?;
298            let kind = if parser.peek2::<kw::exact>()? {
299                ItemKind::FuncExact(parser.parens(|p| {
300                    p.parse::<kw::exact>()?;
301                    p.parse()
302                })?)
303            } else {
304                ItemKind::Func(parser.parse()?)
305            };
306            Ok(ItemSig {
307                span,
308                id,
309                name,
310                kind,
311            })
312        } else if l.peek::<kw::table>()? {
313            let span = parser.parse::<kw::table>()?.0;
314            Ok(ItemSig {
315                span,
316                id: parser.parse()?,
317                name: None,
318                kind: ItemKind::Table(parser.parse()?),
319            })
320        } else if l.peek::<kw::memory>()? {
321            let span = parser.parse::<kw::memory>()?.0;
322            Ok(ItemSig {
323                span,
324                id: parser.parse()?,
325                name: None,
326                kind: ItemKind::Memory(parser.parse()?),
327            })
328        } else if l.peek::<kw::global>()? {
329            let span = parser.parse::<kw::global>()?.0;
330            Ok(ItemSig {
331                span,
332                id: parser.parse()?,
333                name: None,
334                kind: ItemKind::Global(parser.parse()?),
335            })
336        } else if l.peek::<kw::tag>()? {
337            let span = parser.parse::<kw::tag>()?.0;
338            Ok(ItemSig {
339                span,
340                id: parser.parse()?,
341                name: None,
342                kind: ItemKind::Tag(parser.parse()?),
343            })
344        } else {
345            Err(l.error())
346        }
347    }
348}
349
350/// A listing of a inline `(import "foo")` statement.
351///
352/// Note that when parsing this type it is somewhat unconventional that it
353/// parses its own surrounding parentheses. This is typically an optional type,
354/// so it's so far been a bit nicer to have the optionality handled through
355/// `Peek` rather than `Option<T>`.
356#[derive(Debug, Copy, Clone)]
357#[allow(missing_docs)]
358pub struct InlineImport<'a> {
359    pub module: &'a str,
360    pub field: &'a str,
361}
362
363impl<'a> Parse<'a> for InlineImport<'a> {
364    fn parse(parser: Parser<'a>) -> Result<Self> {
365        parser.parens(|p| {
366            p.parse::<kw::import>()?;
367            Ok(InlineImport {
368                module: p.parse()?,
369                field: p.parse()?,
370            })
371        })
372    }
373}
374
375impl Peek for InlineImport<'_> {
376    fn peek(cursor: Cursor<'_>) -> Result<bool> {
377        let cursor = match cursor.lparen()? {
378            Some(cursor) => cursor,
379            None => return Ok(false),
380        };
381        let cursor = match cursor.keyword()? {
382            Some(("import", cursor)) => cursor,
383            _ => return Ok(false),
384        };
385        let cursor = match cursor.string()? {
386            Some((_, cursor)) => cursor,
387            None => return Ok(false),
388        };
389        let cursor = match cursor.string()? {
390            Some((_, cursor)) => cursor,
391            None => return Ok(false),
392        };
393
394        Ok(cursor.rparen()?.is_some())
395    }
396
397    fn display() -> &'static str {
398        "inline import"
399    }
400}