Skip to main content

wast/component/
import.rs

1use crate::component::*;
2use crate::kw;
3use crate::parser::{Cursor, Parse, Parser, Peek, Result};
4use crate::token::{Id, Index, LParen, NameAnnotation, Span};
5
6/// An `import` statement and entry in a WebAssembly component.
7#[derive(Debug)]
8pub struct ComponentImport<'a> {
9    /// Where this `import` was defined
10    pub span: Span,
11    /// The name of the item being imported.
12    pub name: ComponentExternName<'a>,
13    /// The item that's being imported.
14    pub item: ItemSig<'a>,
15}
16
17impl<'a> Parse<'a> for ComponentImport<'a> {
18    fn parse(parser: Parser<'a>) -> Result<Self> {
19        let span = parser.parse::<kw::import>()?.0;
20        let name = parser.parse()?;
21        let item = parser.parens(|p| p.parse())?;
22        Ok(ComponentImport { span, name, item })
23    }
24}
25
26/// Identifiers, and metadata, for component imports and exports.
27#[derive(Debug, Copy, Clone)]
28pub struct ComponentExternName<'a> {
29    /// The string name this is referring to.
30    pub name: &'a str,
31    /// For imports, an optional `(implements "...")` directive.
32    pub implements: Option<&'a str>,
33}
34
35impl<'a> Parse<'a> for ComponentExternName<'a> {
36    fn parse(parser: Parser<'a>) -> Result<Self> {
37        // Prior to WebAssembly/component-model#263 the syntactic form
38        // `(interface "...")` was supported for interface names. This is no
39        // longer part of the syntax of the binary format nor the text format,
40        // but continue to parse this as "sugar" for the current format. This
41        // is intended to avoid breaking folks and provide a smoother transition
42        // forward.
43        let name = if parser.peek::<LParen>()? {
44            parser.parens(|p| {
45                p.parse::<kw::interface>()?;
46                p.parse()
47            })?
48        } else {
49            parser.parse()?
50        };
51        let implements = if parser.peek2::<kw::implements>()? {
52            Some(parser.parens(|p| {
53                p.parse::<kw::implements>()?;
54                p.parse()
55            })?)
56        } else {
57            None
58        };
59        Ok(ComponentExternName { name, implements })
60    }
61}
62
63/// An item signature for imported items.
64#[derive(Debug)]
65pub struct ItemSig<'a> {
66    /// Where this item is defined in the source.
67    pub span: Span,
68    /// An optional identifier used during name resolution to refer to this item
69    /// from the rest of the component.
70    pub id: Option<Id<'a>>,
71    /// An optional name which, for functions, will be stored in the
72    /// custom `name` section.
73    pub name: Option<NameAnnotation<'a>>,
74    /// What kind of item this is.
75    pub kind: ItemSigKind<'a>,
76}
77
78impl<'a> Parse<'a> for ItemSig<'a> {
79    fn parse(parser: Parser<'a>) -> Result<Self> {
80        parse_item_sig(parser, true)
81    }
82}
83
84/// An item signature for imported items.
85#[derive(Debug)]
86pub struct ItemSigNoName<'a>(pub ItemSig<'a>);
87
88impl<'a> Parse<'a> for ItemSigNoName<'a> {
89    fn parse(parser: Parser<'a>) -> Result<Self> {
90        Ok(ItemSigNoName(parse_item_sig(parser, false)?))
91    }
92}
93
94fn parse_item_sig<'a>(parser: Parser<'a>, name: bool) -> Result<ItemSig<'a>> {
95    let mut l = parser.lookahead1();
96    let (span, parse_kind): (_, fn(Parser<'a>) -> Result<ItemSigKind<'a>>) =
97        if l.peek::<kw::core>()? {
98            let span = parser.parse::<kw::core>()?.0;
99            parser.parse::<kw::module>()?;
100            (span, |parser| Ok(ItemSigKind::CoreModule(parser.parse()?)))
101        } else if l.peek::<kw::func>()? {
102            let span = parser.parse::<kw::func>()?.0;
103            (span, |parser| Ok(ItemSigKind::Func(parser.parse()?)))
104        } else if l.peek::<kw::component>()? {
105            let span = parser.parse::<kw::component>()?.0;
106            (span, |parser| Ok(ItemSigKind::Component(parser.parse()?)))
107        } else if l.peek::<kw::instance>()? {
108            let span = parser.parse::<kw::instance>()?.0;
109            (span, |parser| Ok(ItemSigKind::Instance(parser.parse()?)))
110        } else if l.peek::<kw::value>()? {
111            let span = parser.parse::<kw::value>()?.0;
112            (span, |parser| Ok(ItemSigKind::Value(parser.parse()?)))
113        } else if l.peek::<kw::r#type>()? {
114            let span = parser.parse::<kw::r#type>()?.0;
115            (span, |parser| {
116                Ok(ItemSigKind::Type(parser.parens(|parser| parser.parse())?))
117            })
118        } else {
119            return Err(l.error());
120        };
121    Ok(ItemSig {
122        span,
123        id: if name { parser.parse()? } else { None },
124        name: if name { parser.parse()? } else { None },
125        kind: parse_kind(parser)?,
126    })
127}
128
129/// The kind of signatures for imported items.
130#[derive(Debug)]
131pub enum ItemSigKind<'a> {
132    /// The item signature is for a core module.
133    CoreModule(CoreTypeUse<'a, ModuleType<'a>>),
134    /// The item signature is for a function.
135    Func(ComponentTypeUse<'a, ComponentFunctionType<'a>>),
136    /// The item signature is for a component.
137    Component(ComponentTypeUse<'a, ComponentType<'a>>),
138    /// The item signature is for an instance.
139    Instance(ComponentTypeUse<'a, InstanceType<'a>>),
140    /// The item signature is for a value.
141    Value(ComponentValTypeUse<'a>),
142    /// The item signature is for a type.
143    Type(TypeBounds<'a>),
144}
145
146/// Represents the bounds applied to types being imported.
147#[derive(Debug)]
148pub enum TypeBounds<'a> {
149    /// The equality type bounds.
150    Eq(Index<'a>),
151    /// A resource type is imported/exported,
152    SubResource,
153}
154
155impl<'a> Parse<'a> for TypeBounds<'a> {
156    fn parse(parser: Parser<'a>) -> Result<Self> {
157        let mut l = parser.lookahead1();
158        if l.peek::<kw::eq>()? {
159            parser.parse::<kw::eq>()?;
160            Ok(Self::Eq(parser.parse()?))
161        } else if l.peek::<kw::sub>()? {
162            parser.parse::<kw::sub>()?;
163            parser.parse::<kw::resource>()?;
164            Ok(Self::SubResource)
165        } else {
166            Err(l.error())
167        }
168    }
169}
170
171/// A listing of a inline `(import "foo")` statement.
172///
173/// This is the same as `core::InlineImport` except only one string import is
174/// required.
175#[derive(Debug, Clone)]
176pub struct InlineImport<'a> {
177    /// The name of the item being imported.
178    pub name: ComponentExternName<'a>,
179}
180
181impl<'a> Parse<'a> for InlineImport<'a> {
182    fn parse(parser: Parser<'a>) -> Result<Self> {
183        parser.parens(|p| {
184            p.parse::<kw::import>()?;
185            Ok(InlineImport { name: p.parse()? })
186        })
187    }
188}
189
190impl Peek for InlineImport<'_> {
191    fn peek(cursor: Cursor<'_>) -> Result<bool> {
192        let cursor = match cursor.lparen()? {
193            Some(cursor) => cursor,
194            None => return Ok(false),
195        };
196        let cursor = match cursor.keyword()? {
197            Some(("import", cursor)) => cursor,
198            _ => return Ok(false),
199        };
200
201        // (import "foo")
202        if let Some((_, cursor)) = cursor.string()? {
203            return Ok(cursor.rparen()?.is_some());
204        }
205
206        // (import (interface "foo"))
207        let cursor = match cursor.lparen()? {
208            Some(cursor) => cursor,
209            None => return Ok(false),
210        };
211        let cursor = match cursor.keyword()? {
212            Some(("interface", cursor)) => cursor,
213            _ => return Ok(false),
214        };
215        let cursor = match cursor.string()? {
216            Some((_, cursor)) => cursor,
217            _ => return Ok(false),
218        };
219        let cursor = match cursor.rparen()? {
220            Some(cursor) => cursor,
221            _ => return Ok(false),
222        };
223        Ok(cursor.rparen()?.is_some())
224    }
225
226    fn display() -> &'static str {
227        "inline import"
228    }
229}