/src/wasm-tools/crates/wit-parser/src/ast.rs
Line | Count | Source |
1 | | use crate::ast::error::ParseError; |
2 | | use crate::{ParseResult, UnresolvedPackage, UnresolvedPackageGroup}; |
3 | | use alloc::borrow::Cow; |
4 | | use alloc::boxed::Box; |
5 | | use alloc::format; |
6 | | use alloc::string::{String, ToString}; |
7 | | use alloc::vec::Vec; |
8 | | #[cfg(feature = "std")] |
9 | | use anyhow::Context as _; |
10 | | use core::fmt; |
11 | | use core::mem; |
12 | | use core::result::Result; |
13 | | use lex::{Span, Token, Tokenizer}; |
14 | | use semver::Version; |
15 | | #[cfg(feature = "std")] |
16 | | use std::path::Path; |
17 | | |
18 | | pub mod error; |
19 | | pub mod lex; |
20 | | |
21 | | pub use resolve::Resolver; |
22 | | mod resolve; |
23 | | pub mod toposort; |
24 | | |
25 | | pub use lex::validate_id; |
26 | | |
27 | | /// Representation of a single WIT `*.wit` file and nested packages. |
28 | | struct PackageFile<'a> { |
29 | | /// Optional `package foo:bar;` header |
30 | | package_id: Option<PackageName<'a>>, |
31 | | /// Other AST items. |
32 | | decl_list: DeclList<'a>, |
33 | | } |
34 | | |
35 | | impl<'a> PackageFile<'a> { |
36 | | /// Parse a standalone file represented by `tokens`. |
37 | | /// |
38 | | /// This will optionally start with `package foo:bar;` and then will have a |
39 | | /// list of ast items after it. |
40 | 21.7k | fn parse(tokens: &mut Tokenizer<'a>) -> ParseResult<Self> { |
41 | 21.7k | let mut package_name_tokens_peek = tokens.clone(); |
42 | 21.7k | let docs = parse_docs(&mut package_name_tokens_peek)?; |
43 | | |
44 | | // Parse `package foo:bar;` but throw it out if it's actually |
45 | | // `package foo:bar { ... }` since that's an ast item instead. |
46 | 21.7k | let package_id = if package_name_tokens_peek.eat(Token::Package)? { |
47 | 16.2k | let name = PackageName::parse(&mut package_name_tokens_peek, docs)?; |
48 | 16.2k | if package_name_tokens_peek.eat(Token::Semicolon)? { |
49 | 16.2k | *tokens = package_name_tokens_peek; |
50 | 16.2k | Some(name) |
51 | | } else { |
52 | 0 | None |
53 | | } |
54 | | } else { |
55 | 5.49k | None |
56 | | }; |
57 | 21.7k | let decl_list = DeclList::parse_until(tokens, None)?; |
58 | 21.7k | Ok(PackageFile { |
59 | 21.7k | package_id, |
60 | 21.7k | decl_list, |
61 | 21.7k | }) |
62 | 21.7k | } |
63 | | |
64 | | /// Parse a nested package of the form `package foo:bar { ... }` |
65 | 852 | fn parse_nested( |
66 | 852 | tokens: &mut Tokenizer<'a>, |
67 | 852 | docs: Docs<'a>, |
68 | 852 | attributes: Vec<Attribute<'a>>, |
69 | 852 | ) -> ParseResult<Self> { |
70 | 852 | let span = tokens.expect(Token::Package)?; |
71 | 852 | if !attributes.is_empty() { |
72 | 0 | return Err(ParseError::new_syntax( |
73 | 0 | span, |
74 | 0 | format!("cannot place attributes on nested packages"), |
75 | 0 | )); |
76 | 852 | } |
77 | 852 | let package_id = PackageName::parse(tokens, docs)?; |
78 | 852 | tokens.expect(Token::LeftBrace)?; |
79 | 852 | let decl_list = DeclList::parse_until(tokens, Some(Token::RightBrace))?; |
80 | 852 | Ok(PackageFile { |
81 | 852 | package_id: Some(package_id), |
82 | 852 | decl_list, |
83 | 852 | }) |
84 | 852 | } |
85 | | } |
86 | | |
87 | | /// Stores all of the declarations in a package's scope. In AST terms, this |
88 | | /// means everything except the `package` declaration that demarcates a package |
89 | | /// scope. In the traditional implicit format, these are all of the declarations |
90 | | /// non-`package` declarations in the file: |
91 | | /// |
92 | | /// ```wit |
93 | | /// package foo:name; |
94 | | /// |
95 | | /// /* START DECL LIST */ |
96 | | /// // Some comment... |
97 | | /// interface i {} |
98 | | /// world w {} |
99 | | /// /* END DECL LIST */ |
100 | | /// ``` |
101 | | /// |
102 | | /// In the nested package style, a [`DeclList`] is everything inside of each |
103 | | /// `package` element's brackets: |
104 | | /// |
105 | | /// ```wit |
106 | | /// package foo:name { |
107 | | /// /* START FIRST DECL LIST */ |
108 | | /// // Some comment... |
109 | | /// interface i {} |
110 | | /// world w {} |
111 | | /// /* END FIRST DECL LIST */ |
112 | | /// } |
113 | | /// |
114 | | /// package bar:name { |
115 | | /// /* START SECOND DECL LIST */ |
116 | | /// // Some comment... |
117 | | /// interface i {} |
118 | | /// world w {} |
119 | | /// /* END SECOND DECL LIST */ |
120 | | /// } |
121 | | /// ``` |
122 | | #[derive(Default)] |
123 | | pub struct DeclList<'a> { |
124 | | items: Vec<AstItem<'a>>, |
125 | | } |
126 | | |
127 | | impl<'a> DeclList<'a> { |
128 | 22.6k | fn parse_until(tokens: &mut Tokenizer<'a>, end: Option<Token>) -> ParseResult<DeclList<'a>> { |
129 | 22.6k | let mut items = Vec::new(); |
130 | 22.6k | let mut docs = parse_docs(tokens)?; |
131 | | loop { |
132 | 94.0k | match end { |
133 | 2.15k | Some(end) => { |
134 | 2.15k | if tokens.eat(end)? { |
135 | 852 | break; |
136 | 1.29k | } |
137 | | } |
138 | | None => { |
139 | 91.8k | if tokens.clone().next()?.is_none() { |
140 | 21.7k | break; |
141 | 70.0k | } |
142 | | } |
143 | | } |
144 | 71.3k | items.push(AstItem::parse(tokens, docs)?); |
145 | 71.3k | docs = parse_docs(tokens)?; |
146 | | } |
147 | 22.6k | Ok(DeclList { items }) |
148 | 22.6k | } |
149 | | |
150 | 67.9k | fn for_each_path<'b>( |
151 | 67.9k | &'b self, |
152 | 67.9k | f: &mut dyn FnMut( |
153 | 67.9k | Option<&'b Id<'a>>, |
154 | 67.9k | &'b [Attribute<'a>], |
155 | 67.9k | &'b UsePath<'a>, |
156 | 67.9k | Option<&'b [UseName<'a>]>, |
157 | 67.9k | WorldOrInterface, |
158 | 67.9k | ) -> ParseResult<()>, |
159 | 67.9k | ) -> ParseResult<()> { |
160 | 211k | for item in self.items.iter() { |
161 | 211k | match item { |
162 | 51.0k | AstItem::World(world) => { |
163 | | // Visit imports here first before exports to help preserve |
164 | | // round-tripping of documents because printing a world puts |
165 | | // imports first but textually they can be listed with |
166 | | // exports first. |
167 | 51.0k | let mut imports = Vec::new(); |
168 | 51.0k | let mut exports = Vec::new(); |
169 | 99.9k | for item in world.items.iter() { |
170 | 99.9k | match item { |
171 | 3.85k | WorldItem::Use(u) => f( |
172 | 3.85k | None, |
173 | 3.85k | &u.attributes, |
174 | 3.85k | &u.from, |
175 | 3.85k | Some(&u.names), |
176 | 3.85k | WorldOrInterface::Interface, |
177 | 3.85k | )?, |
178 | 0 | WorldItem::Include(i) => f( |
179 | 0 | Some(&world.name), |
180 | 0 | &i.attributes, |
181 | 0 | &i.from, |
182 | 0 | None, |
183 | 0 | WorldOrInterface::World, |
184 | 0 | )?, |
185 | 25.5k | WorldItem::Type(_) => {} |
186 | | WorldItem::Import(Import { |
187 | 16.5k | kind, attributes, .. |
188 | 16.5k | }) => imports.push((kind, attributes)), |
189 | | WorldItem::Export(Export { |
190 | 54.0k | kind, attributes, .. |
191 | 54.0k | }) => exports.push((kind, attributes)), |
192 | | } |
193 | | } |
194 | | |
195 | 51.0k | let mut visit_kind = |
196 | 70.6k | |kind: &'b ExternKind<'a>, attrs: &'b [Attribute<'a>]| match kind { |
197 | 5.64k | ExternKind::Interface(_, items) => { |
198 | 14.5k | for item in items { |
199 | 14.5k | match item { |
200 | 1.99k | InterfaceItem::Use(u) => f( |
201 | 1.99k | None, |
202 | 1.99k | &u.attributes, |
203 | 1.99k | &u.from, |
204 | 1.99k | Some(&u.names), |
205 | 1.99k | WorldOrInterface::Interface, |
206 | 1.99k | )?, |
207 | 12.5k | _ => {} |
208 | | } |
209 | | } |
210 | 5.64k | Ok(()) |
211 | | } |
212 | 48.5k | ExternKind::Path(path) | ExternKind::NamedPath(_, path) => { |
213 | 52.8k | f(None, attrs, path, None, WorldOrInterface::Interface) |
214 | | } |
215 | 12.1k | ExternKind::Func(..) => Ok(()), |
216 | 70.6k | }; |
217 | | |
218 | 51.0k | for (kind, attrs) in imports { |
219 | 16.5k | visit_kind(kind, attrs)?; |
220 | | } |
221 | 54.0k | for (kind, attrs) in exports { |
222 | 54.0k | visit_kind(kind, attrs)?; |
223 | | } |
224 | | } |
225 | 149k | AstItem::Interface(i) => { |
226 | 171k | for item in i.items.iter() { |
227 | 171k | match item { |
228 | 60.8k | InterfaceItem::Use(u) => f( |
229 | 60.8k | Some(&i.name), |
230 | 60.8k | &u.attributes, |
231 | 60.8k | &u.from, |
232 | 60.8k | Some(&u.names), |
233 | 60.8k | WorldOrInterface::Interface, |
234 | 60.8k | )?, |
235 | 111k | _ => {} |
236 | | } |
237 | | } |
238 | | } |
239 | 10.9k | AstItem::Use(u) => { |
240 | | // At the top-level, we don't know if this is a world or an interface |
241 | | // It is up to the resolver to decides how to handle this ambiguity. |
242 | 10.9k | f( |
243 | 10.9k | None, |
244 | 10.9k | &u.attributes, |
245 | 10.9k | &u.item, |
246 | 10.9k | None, |
247 | 10.9k | WorldOrInterface::Unknown, |
248 | 10.9k | )?; |
249 | | } |
250 | | |
251 | 0 | AstItem::Package(pkg) => pkg.decl_list.for_each_path(f)?, |
252 | | } |
253 | | } |
254 | 67.9k | Ok(()) |
255 | 67.9k | } |
256 | | } |
257 | | |
258 | | enum AstItem<'a> { |
259 | | Interface(Interface<'a>), |
260 | | World(World<'a>), |
261 | | Use(ToplevelUse<'a>), |
262 | | Package(PackageFile<'a>), |
263 | | } |
264 | | |
265 | | impl<'a> AstItem<'a> { |
266 | 71.3k | fn parse(tokens: &mut Tokenizer<'a>, docs: Docs<'a>) -> ParseResult<Self> { |
267 | 71.3k | let attributes = Attribute::parse_list(tokens)?; |
268 | 71.3k | match tokens.clone().next()? { |
269 | 49.8k | Some((_span, Token::Interface)) => { |
270 | 49.8k | Interface::parse(tokens, docs, attributes).map(Self::Interface) |
271 | | } |
272 | 17.0k | Some((_span, Token::World)) => World::parse(tokens, docs, attributes).map(Self::World), |
273 | 3.64k | Some((_span, Token::Use)) => ToplevelUse::parse(tokens, attributes).map(Self::Use), |
274 | 852 | Some((_span, Token::Package)) => { |
275 | 852 | PackageFile::parse_nested(tokens, docs, attributes).map(Self::Package) |
276 | | } |
277 | 0 | other => Err(err_expected(tokens, "`world`, `interface` or `use`", other).into()), |
278 | | } |
279 | 71.3k | } |
280 | | } |
281 | | |
282 | | #[derive(Debug, Clone)] |
283 | | struct PackageName<'a> { |
284 | | docs: Docs<'a>, |
285 | | span: Span, |
286 | | namespace: Id<'a>, |
287 | | name: Id<'a>, |
288 | | version: Option<(Span, Version)>, |
289 | | } |
290 | | |
291 | | impl<'a> PackageName<'a> { |
292 | 17.1k | fn parse(tokens: &mut Tokenizer<'a>, docs: Docs<'a>) -> ParseResult<Self> { |
293 | 17.1k | let namespace = parse_id(tokens)?; |
294 | 17.1k | tokens.expect(Token::Colon)?; |
295 | 17.1k | let name = parse_id(tokens)?; |
296 | 17.1k | let version = parse_opt_version(tokens)?; |
297 | | Ok(PackageName { |
298 | 17.1k | docs, |
299 | 17.1k | span: Span::new( |
300 | 17.1k | namespace.span.start(), |
301 | 17.1k | version |
302 | 17.1k | .as_ref() |
303 | 17.1k | .map(|(s, _)| s.end()) |
304 | 17.1k | .unwrap_or(name.span.end()), |
305 | | ), |
306 | 17.1k | namespace, |
307 | 17.1k | name, |
308 | 17.1k | version, |
309 | | }) |
310 | 17.1k | } |
311 | | |
312 | 88.5k | fn package_name(&self) -> crate::PackageName { |
313 | | crate::PackageName { |
314 | 88.5k | namespace: self.namespace.name.to_string(), |
315 | 88.5k | name: self.name.name.to_string(), |
316 | 88.5k | version: self.version.as_ref().map(|(_, v)| v.clone()), |
317 | | } |
318 | 88.5k | } |
319 | | } |
320 | | |
321 | | struct ToplevelUse<'a> { |
322 | | span: Span, |
323 | | attributes: Vec<Attribute<'a>>, |
324 | | item: UsePath<'a>, |
325 | | as_: Option<Id<'a>>, |
326 | | } |
327 | | |
328 | | impl<'a> ToplevelUse<'a> { |
329 | 3.64k | fn parse(tokens: &mut Tokenizer<'a>, attributes: Vec<Attribute<'a>>) -> ParseResult<Self> { |
330 | 3.64k | let span = tokens.expect(Token::Use)?; |
331 | 3.64k | let item = UsePath::parse(tokens)?; |
332 | 3.64k | let as_ = if tokens.eat(Token::As)? { |
333 | 3.25k | Some(parse_id(tokens)?) |
334 | | } else { |
335 | 395 | None |
336 | | }; |
337 | 3.64k | tokens.expect_semicolon()?; |
338 | 3.64k | Ok(ToplevelUse { |
339 | 3.64k | span, |
340 | 3.64k | attributes, |
341 | 3.64k | item, |
342 | 3.64k | as_, |
343 | 3.64k | }) |
344 | 3.64k | } |
345 | | } |
346 | | |
347 | | struct World<'a> { |
348 | | docs: Docs<'a>, |
349 | | attributes: Vec<Attribute<'a>>, |
350 | | name: Id<'a>, |
351 | | items: Vec<WorldItem<'a>>, |
352 | | } |
353 | | |
354 | | impl<'a> World<'a> { |
355 | 17.0k | fn parse( |
356 | 17.0k | tokens: &mut Tokenizer<'a>, |
357 | 17.0k | docs: Docs<'a>, |
358 | 17.0k | attributes: Vec<Attribute<'a>>, |
359 | 17.0k | ) -> ParseResult<Self> { |
360 | 17.0k | tokens.expect(Token::World)?; |
361 | 17.0k | let name = parse_id(tokens)?; |
362 | 17.0k | let items = Self::parse_items(tokens)?; |
363 | 17.0k | Ok(World { |
364 | 17.0k | docs, |
365 | 17.0k | attributes, |
366 | 17.0k | name, |
367 | 17.0k | items, |
368 | 17.0k | }) |
369 | 17.0k | } |
370 | | |
371 | 17.0k | fn parse_items(tokens: &mut Tokenizer<'a>) -> ParseResult<Vec<WorldItem<'a>>> { |
372 | 17.0k | tokens.expect(Token::LeftBrace)?; |
373 | 17.0k | let mut items = Vec::new(); |
374 | | loop { |
375 | 50.3k | let docs = parse_docs(tokens)?; |
376 | 50.3k | if tokens.eat(Token::RightBrace)? { |
377 | 17.0k | break; |
378 | 33.3k | } |
379 | 33.3k | let attributes = Attribute::parse_list(tokens)?; |
380 | 33.3k | items.push(WorldItem::parse(tokens, docs, attributes)?); |
381 | | } |
382 | 17.0k | Ok(items) |
383 | 17.0k | } |
384 | | } |
385 | | |
386 | | enum WorldItem<'a> { |
387 | | Import(Import<'a>), |
388 | | Export(Export<'a>), |
389 | | Use(Use<'a>), |
390 | | Type(TypeDef<'a>), |
391 | | Include(Include<'a>), |
392 | | } |
393 | | |
394 | | impl<'a> WorldItem<'a> { |
395 | 33.3k | fn parse( |
396 | 33.3k | tokens: &mut Tokenizer<'a>, |
397 | 33.3k | docs: Docs<'a>, |
398 | 33.3k | attributes: Vec<Attribute<'a>>, |
399 | 33.3k | ) -> ParseResult<WorldItem<'a>> { |
400 | 33.3k | match tokens.clone().next()? { |
401 | 5.51k | Some((_span, Token::Import)) => { |
402 | 5.51k | Import::parse(tokens, docs, attributes).map(WorldItem::Import) |
403 | | } |
404 | 18.0k | Some((_span, Token::Export)) => { |
405 | 18.0k | Export::parse(tokens, docs, attributes).map(WorldItem::Export) |
406 | | } |
407 | 1.28k | Some((_span, Token::Use)) => Use::parse(tokens, attributes).map(WorldItem::Use), |
408 | 1.57k | Some((_span, Token::Type)) => { |
409 | 1.57k | TypeDef::parse(tokens, docs, attributes).map(WorldItem::Type) |
410 | | } |
411 | 267 | Some((_span, Token::Flags)) => { |
412 | 267 | TypeDef::parse_flags(tokens, docs, attributes).map(WorldItem::Type) |
413 | | } |
414 | 603 | Some((_span, Token::Resource)) => { |
415 | 603 | TypeDef::parse_resource(tokens, docs, attributes).map(WorldItem::Type) |
416 | | } |
417 | 847 | Some((_span, Token::Record)) => { |
418 | 847 | TypeDef::parse_record(tokens, docs, attributes).map(WorldItem::Type) |
419 | | } |
420 | 720 | Some((_span, Token::Variant)) => { |
421 | 720 | TypeDef::parse_variant(tokens, docs, attributes).map(WorldItem::Type) |
422 | | } |
423 | 4.49k | Some((_span, Token::Enum)) => { |
424 | 4.49k | TypeDef::parse_enum(tokens, docs, attributes).map(WorldItem::Type) |
425 | | } |
426 | 0 | Some((_span, Token::Include)) => { |
427 | 0 | Include::parse(tokens, attributes).map(WorldItem::Include) |
428 | | } |
429 | 0 | other => Err(err_expected( |
430 | 0 | tokens, |
431 | 0 | "`import`, `export`, `include`, `use`, or type definition", |
432 | 0 | other, |
433 | 0 | ) |
434 | 0 | .into()), |
435 | | } |
436 | 33.3k | } |
437 | | } |
438 | | |
439 | | struct Import<'a> { |
440 | | docs: Docs<'a>, |
441 | | attributes: Vec<Attribute<'a>>, |
442 | | kind: ExternKind<'a>, |
443 | | } |
444 | | |
445 | | impl<'a> Import<'a> { |
446 | 5.51k | fn parse( |
447 | 5.51k | tokens: &mut Tokenizer<'a>, |
448 | 5.51k | docs: Docs<'a>, |
449 | 5.51k | attributes: Vec<Attribute<'a>>, |
450 | 5.51k | ) -> ParseResult<Import<'a>> { |
451 | 5.51k | tokens.expect(Token::Import)?; |
452 | 5.51k | let kind = ExternKind::parse(tokens)?; |
453 | 5.51k | Ok(Import { |
454 | 5.51k | docs, |
455 | 5.51k | attributes, |
456 | 5.51k | kind, |
457 | 5.51k | }) |
458 | 5.51k | } |
459 | | } |
460 | | |
461 | | struct Export<'a> { |
462 | | docs: Docs<'a>, |
463 | | attributes: Vec<Attribute<'a>>, |
464 | | kind: ExternKind<'a>, |
465 | | } |
466 | | |
467 | | impl<'a> Export<'a> { |
468 | 18.0k | fn parse( |
469 | 18.0k | tokens: &mut Tokenizer<'a>, |
470 | 18.0k | docs: Docs<'a>, |
471 | 18.0k | attributes: Vec<Attribute<'a>>, |
472 | 18.0k | ) -> ParseResult<Export<'a>> { |
473 | 18.0k | tokens.expect(Token::Export)?; |
474 | 18.0k | let kind = ExternKind::parse(tokens)?; |
475 | 18.0k | Ok(Export { |
476 | 18.0k | docs, |
477 | 18.0k | attributes, |
478 | 18.0k | kind, |
479 | 18.0k | }) |
480 | 18.0k | } |
481 | | } |
482 | | |
483 | | enum ExternKind<'a> { |
484 | | Interface(Id<'a>, Vec<InterfaceItem<'a>>), |
485 | | Path(UsePath<'a>), |
486 | | Func(Id<'a>, Func<'a>), |
487 | | /// `label: use-path` — a named import/export that implements an interface. |
488 | | NamedPath(Id<'a>, UsePath<'a>), |
489 | | } |
490 | | |
491 | | impl<'a> ExternKind<'a> { |
492 | 23.5k | fn parse(tokens: &mut Tokenizer<'a>) -> ParseResult<ExternKind<'a>> { |
493 | | // Create a copy of the token stream to test out if this is a function |
494 | | // or an interface import. In those situations the token stream gets |
495 | | // reset to the state of the clone and we continue down those paths. |
496 | | // |
497 | | // If neither a function nor an interface appears here though then the |
498 | | // clone is thrown away and the original token stream is parsed for an |
499 | | // interface. This will redo the original ID parse and the original |
500 | | // colon parse, but that shouldn't be too bad perf-wise. |
501 | 23.5k | let mut clone = tokens.clone(); |
502 | 23.5k | let id = parse_id(&mut clone)?; |
503 | 23.5k | if clone.eat(Token::Colon)? { |
504 | | // import foo: async? func(...) |
505 | 22.7k | if clone.clone().eat(Token::Func)? || clone.clone().eat(Token::Async)? { |
506 | 4.05k | *tokens = clone; |
507 | 4.05k | let ret = ExternKind::Func(id, Func::parse(tokens)?); |
508 | 4.05k | tokens.expect_semicolon()?; |
509 | 4.05k | return Ok(ret); |
510 | 18.6k | } |
511 | | |
512 | | // import foo: interface { ... } |
513 | 18.6k | if clone.eat(Token::Interface)? { |
514 | 1.88k | *tokens = clone; |
515 | 1.88k | return Ok(ExternKind::Interface(id, Interface::parse_items(tokens)?)); |
516 | 16.8k | } |
517 | | |
518 | | // import label: use-path |
519 | | // At this point we consumed `id:` on the clone but the next token |
520 | | // is not `func`, `async`, or `interface`. This could be either: |
521 | | // import label: local-iface; (NamedPath) |
522 | | // import label: pkg:name/iface; (NamedPath with package path) |
523 | | // import ns:pkg/iface; (regular fully-qualified Path) |
524 | | // |
525 | | // Disambiguate: if the next tokens are `id /`, then the colon was |
526 | | // part of a fully-qualified `namespace:package/interface` name, not |
527 | | // a label separator. Fall through to the Path parser in that case. |
528 | 16.8k | let mut peek = clone.clone(); |
529 | 16.8k | let is_qualified_path = |
530 | 16.8k | parse_id(&mut peek).is_ok() && peek.clone().eat(Token::Slash).unwrap_or(false); |
531 | 16.8k | if !is_qualified_path { |
532 | 16.1k | *tokens = clone; |
533 | 16.1k | let path = UsePath::parse(tokens)?; |
534 | 16.1k | tokens.expect_semicolon()?; |
535 | 16.1k | return Ok(ExternKind::NamedPath(id, path)); |
536 | 641 | } |
537 | 789 | } |
538 | | |
539 | | // import foo |
540 | | // import foo/bar |
541 | | // import foo:bar/baz |
542 | 1.43k | let ret = ExternKind::Path(UsePath::parse(tokens)?); |
543 | 1.43k | tokens.expect_semicolon()?; |
544 | 1.43k | Ok(ret) |
545 | 23.5k | } |
546 | | |
547 | 0 | fn span(&self) -> Span { |
548 | 0 | match self { |
549 | 0 | ExternKind::Interface(id, _) => id.span, |
550 | 0 | ExternKind::Path(UsePath::Id(id)) => id.span, |
551 | 0 | ExternKind::Path(UsePath::Package { name, .. }) => name.span, |
552 | 0 | ExternKind::Func(id, _) => id.span, |
553 | 0 | ExternKind::NamedPath(id, _) => id.span, |
554 | | } |
555 | 0 | } |
556 | | } |
557 | | |
558 | | struct Interface<'a> { |
559 | | docs: Docs<'a>, |
560 | | attributes: Vec<Attribute<'a>>, |
561 | | name: Id<'a>, |
562 | | items: Vec<InterfaceItem<'a>>, |
563 | | } |
564 | | |
565 | | impl<'a> Interface<'a> { |
566 | 49.8k | fn parse( |
567 | 49.8k | tokens: &mut Tokenizer<'a>, |
568 | 49.8k | docs: Docs<'a>, |
569 | 49.8k | attributes: Vec<Attribute<'a>>, |
570 | 49.8k | ) -> ParseResult<Self> { |
571 | 49.8k | tokens.expect(Token::Interface)?; |
572 | 49.8k | let name = parse_id(tokens)?; |
573 | 49.8k | let items = Self::parse_items(tokens)?; |
574 | 49.8k | Ok(Interface { |
575 | 49.8k | docs, |
576 | 49.8k | attributes, |
577 | 49.8k | name, |
578 | 49.8k | items, |
579 | 49.8k | }) |
580 | 49.8k | } |
581 | | |
582 | 51.7k | pub(super) fn parse_items(tokens: &mut Tokenizer<'a>) -> ParseResult<Vec<InterfaceItem<'a>>> { |
583 | 51.7k | tokens.expect(Token::LeftBrace)?; |
584 | 51.7k | let mut items = Vec::new(); |
585 | | loop { |
586 | 113k | let docs = parse_docs(tokens)?; |
587 | 113k | if tokens.eat(Token::RightBrace)? { |
588 | 51.7k | break; |
589 | 62.1k | } |
590 | 62.1k | let attributes = Attribute::parse_list(tokens)?; |
591 | 62.1k | items.push(InterfaceItem::parse(tokens, docs, attributes)?); |
592 | | } |
593 | 51.7k | Ok(items) |
594 | 51.7k | } |
595 | | } |
596 | | |
597 | | #[derive(Debug)] |
598 | | pub enum WorldOrInterface { |
599 | | World, |
600 | | Interface, |
601 | | Unknown, |
602 | | } |
603 | | |
604 | | enum InterfaceItem<'a> { |
605 | | TypeDef(TypeDef<'a>), |
606 | | Func(NamedFunc<'a>), |
607 | | Use(Use<'a>), |
608 | | } |
609 | | |
610 | | struct Use<'a> { |
611 | | attributes: Vec<Attribute<'a>>, |
612 | | from: UsePath<'a>, |
613 | | names: Vec<UseName<'a>>, |
614 | | } |
615 | | |
616 | | #[derive(Debug)] |
617 | | enum UsePath<'a> { |
618 | | Id(Id<'a>), |
619 | | Package { id: PackageName<'a>, name: Id<'a> }, |
620 | | } |
621 | | |
622 | | impl<'a> UsePath<'a> { |
623 | 43.4k | fn parse(tokens: &mut Tokenizer<'a>) -> ParseResult<Self> { |
624 | 43.4k | let id = parse_id(tokens)?; |
625 | 43.4k | if tokens.eat(Token::Colon)? { |
626 | | // `foo:bar/baz@1.0` |
627 | 27.5k | let namespace = id; |
628 | 27.5k | let pkg_name = parse_id(tokens)?; |
629 | 27.5k | tokens.expect(Token::Slash)?; |
630 | 27.5k | let name = parse_id(tokens)?; |
631 | 27.5k | let version = parse_opt_version(tokens)?; |
632 | 27.5k | Ok(UsePath::Package { |
633 | 27.5k | id: PackageName { |
634 | 27.5k | docs: Default::default(), |
635 | 27.5k | span: Span::new(namespace.span.start(), pkg_name.span.end()), |
636 | 27.5k | namespace, |
637 | 27.5k | name: pkg_name, |
638 | 27.5k | version, |
639 | 27.5k | }, |
640 | 27.5k | name, |
641 | 27.5k | }) |
642 | | } else { |
643 | | // `foo` |
644 | 15.9k | Ok(UsePath::Id(id)) |
645 | | } |
646 | 43.4k | } |
647 | | |
648 | 7.29k | fn name(&self) -> &Id<'a> { |
649 | 7.29k | match self { |
650 | 2.96k | UsePath::Id(id) => id, |
651 | 4.33k | UsePath::Package { name, .. } => name, |
652 | | } |
653 | 7.29k | } |
654 | | } |
655 | | |
656 | | struct UseName<'a> { |
657 | | name: Id<'a>, |
658 | | as_: Option<Id<'a>>, |
659 | | } |
660 | | |
661 | | impl<'a> Use<'a> { |
662 | 22.2k | fn parse(tokens: &mut Tokenizer<'a>, attributes: Vec<Attribute<'a>>) -> ParseResult<Self> { |
663 | 22.2k | tokens.expect(Token::Use)?; |
664 | 22.2k | let from = UsePath::parse(tokens)?; |
665 | 22.2k | tokens.expect(Token::Period)?; |
666 | 22.2k | tokens.expect(Token::LeftBrace)?; |
667 | | |
668 | 22.2k | let mut names = Vec::new(); |
669 | 25.9k | while !tokens.eat(Token::RightBrace)? { |
670 | 25.9k | let mut name = UseName { |
671 | 25.9k | name: parse_id(tokens)?, |
672 | 25.9k | as_: None, |
673 | | }; |
674 | 25.9k | if tokens.eat(Token::As)? { |
675 | 22.7k | name.as_ = Some(parse_id(tokens)?); |
676 | 3.25k | } |
677 | 25.9k | names.push(name); |
678 | 25.9k | if !tokens.eat(Token::Comma)? { |
679 | 22.2k | tokens.expect(Token::RightBrace)?; |
680 | 22.2k | break; |
681 | 3.72k | } |
682 | | } |
683 | 22.2k | tokens.expect_semicolon()?; |
684 | 22.2k | Ok(Use { |
685 | 22.2k | attributes, |
686 | 22.2k | from, |
687 | 22.2k | names, |
688 | 22.2k | }) |
689 | 22.2k | } |
690 | | } |
691 | | |
692 | | struct Include<'a> { |
693 | | from: UsePath<'a>, |
694 | | attributes: Vec<Attribute<'a>>, |
695 | | names: Vec<IncludeName<'a>>, |
696 | | } |
697 | | |
698 | | struct IncludeName<'a> { |
699 | | name: Id<'a>, |
700 | | as_: Id<'a>, |
701 | | } |
702 | | |
703 | | impl<'a> Include<'a> { |
704 | 0 | fn parse(tokens: &mut Tokenizer<'a>, attributes: Vec<Attribute<'a>>) -> ParseResult<Self> { |
705 | 0 | tokens.expect(Token::Include)?; |
706 | 0 | let from = UsePath::parse(tokens)?; |
707 | | |
708 | 0 | let names = if tokens.eat(Token::With)? { |
709 | 0 | parse_list( |
710 | 0 | tokens, |
711 | 0 | Token::LeftBrace, |
712 | 0 | Token::RightBrace, |
713 | 0 | |_docs, tokens| { |
714 | 0 | let name = parse_id(tokens)?; |
715 | 0 | tokens.expect(Token::As)?; |
716 | 0 | let as_ = parse_id(tokens)?; |
717 | 0 | Ok(IncludeName { name, as_ }) |
718 | 0 | }, |
719 | 0 | )? |
720 | | } else { |
721 | 0 | tokens.expect_semicolon()?; |
722 | 0 | Vec::new() |
723 | | }; |
724 | | |
725 | 0 | Ok(Include { |
726 | 0 | attributes, |
727 | 0 | from, |
728 | 0 | names, |
729 | 0 | }) |
730 | 0 | } |
731 | | } |
732 | | |
733 | | #[derive(Debug, Clone)] |
734 | | pub struct Id<'a> { |
735 | | name: &'a str, |
736 | | span: Span, |
737 | | } |
738 | | |
739 | | impl<'a> From<&'a str> for Id<'a> { |
740 | 0 | fn from(s: &'a str) -> Id<'a> { |
741 | 0 | Id { |
742 | 0 | name: s.into(), |
743 | 0 | span: Default::default(), |
744 | 0 | } |
745 | 0 | } |
746 | | } |
747 | | |
748 | | #[derive(Debug, Clone)] |
749 | | pub struct Docs<'a> { |
750 | | docs: Vec<Cow<'a, str>>, |
751 | | span: Span, |
752 | | } |
753 | | |
754 | | impl<'a> Default for Docs<'a> { |
755 | 610k | fn default() -> Self { |
756 | 610k | Self { |
757 | 610k | docs: Default::default(), |
758 | 610k | span: Default::default(), |
759 | 610k | } |
760 | 610k | } |
761 | | } |
762 | | |
763 | | struct TypeDef<'a> { |
764 | | docs: Docs<'a>, |
765 | | attributes: Vec<Attribute<'a>>, |
766 | | name: Id<'a>, |
767 | | ty: Type<'a>, |
768 | | } |
769 | | |
770 | | enum Type<'a> { |
771 | | Bool(Span), |
772 | | U8(Span), |
773 | | U16(Span), |
774 | | U32(Span), |
775 | | U64(Span), |
776 | | S8(Span), |
777 | | S16(Span), |
778 | | S32(Span), |
779 | | S64(Span), |
780 | | F32(Span), |
781 | | F64(Span), |
782 | | Char(Span), |
783 | | String(Span), |
784 | | Name(Id<'a>), |
785 | | List(List<'a>), |
786 | | Map(Map<'a>), |
787 | | FixedLengthList(FixedLengthList<'a>), |
788 | | Handle(Handle<'a>), |
789 | | Resource(Resource<'a>), |
790 | | Record(Record<'a>), |
791 | | Flags(Flags<'a>), |
792 | | Variant(Variant<'a>), |
793 | | Tuple(Tuple<'a>), |
794 | | Enum(Enum<'a>), |
795 | | Option(Option_<'a>), |
796 | | Result(Result_<'a>), |
797 | | Future(Future<'a>), |
798 | | Stream(Stream<'a>), |
799 | | ErrorContext(Span), |
800 | | } |
801 | | |
802 | | enum Handle<'a> { |
803 | | Own { resource: Id<'a> }, |
804 | | Borrow { resource: Id<'a> }, |
805 | | } |
806 | | |
807 | | impl Handle<'_> { |
808 | 295 | fn span(&self) -> Span { |
809 | 295 | match self { |
810 | 295 | Handle::Own { resource } | Handle::Borrow { resource } => resource.span, |
811 | | } |
812 | 295 | } |
813 | | } |
814 | | |
815 | | struct Resource<'a> { |
816 | | span: Span, |
817 | | funcs: Vec<ResourceFunc<'a>>, |
818 | | } |
819 | | |
820 | | enum ResourceFunc<'a> { |
821 | | Method(NamedFunc<'a>), |
822 | | Static(NamedFunc<'a>), |
823 | | Constructor(NamedFunc<'a>), |
824 | | } |
825 | | |
826 | | impl<'a> ResourceFunc<'a> { |
827 | 4.56k | fn parse( |
828 | 4.56k | docs: Docs<'a>, |
829 | 4.56k | attributes: Vec<Attribute<'a>>, |
830 | 4.56k | tokens: &mut Tokenizer<'a>, |
831 | 4.56k | ) -> ParseResult<Self> { |
832 | 4.56k | match tokens.clone().next()? { |
833 | 555 | Some((span, Token::Constructor)) => { |
834 | 555 | tokens.expect(Token::Constructor)?; |
835 | 555 | tokens.expect(Token::LeftParen)?; |
836 | 1.16k | let params = parse_list_trailer(tokens, Token::RightParen, |_docs, tokens| { |
837 | 1.16k | let name = parse_id(tokens)?; |
838 | 1.16k | tokens.expect(Token::Colon)?; |
839 | 1.16k | let ty = Type::parse(tokens)?; |
840 | 1.16k | Ok((name, ty)) |
841 | 1.16k | })?; |
842 | 555 | let result = if tokens.eat(Token::RArrow)? { |
843 | 0 | let ty = Type::parse(tokens)?; |
844 | 0 | Some(ty) |
845 | | } else { |
846 | 555 | None |
847 | | }; |
848 | 555 | tokens.expect_semicolon()?; |
849 | 555 | Ok(ResourceFunc::Constructor(NamedFunc { |
850 | 555 | docs, |
851 | 555 | attributes, |
852 | 555 | name: Id { |
853 | 555 | span, |
854 | 555 | name: "constructor", |
855 | 555 | }, |
856 | 555 | func: Func { |
857 | 555 | span, |
858 | 555 | async_: false, |
859 | 555 | params, |
860 | 555 | result, |
861 | 555 | }, |
862 | 555 | })) |
863 | | } |
864 | 4.00k | Some((_span, Token::Id | Token::ExplicitId)) => { |
865 | 4.00k | let name = parse_id(tokens)?; |
866 | 4.00k | tokens.expect(Token::Colon)?; |
867 | 4.00k | let ctor = if tokens.eat(Token::Static)? { |
868 | 1.76k | ResourceFunc::Static |
869 | 2.24k | } else { |
870 | 2.24k | ResourceFunc::Method |
871 | 2.24k | }; |
872 | 4.00k | let func = Func::parse(tokens)?; |
873 | 4.00k | tokens.expect_semicolon()?; |
874 | 4.00k | Ok(ctor(NamedFunc { |
875 | 4.00k | docs, |
876 | 4.00k | attributes, |
877 | 4.00k | name, |
878 | 4.00k | func, |
879 | 4.00k | })) |
880 | | } |
881 | 0 | other => Err(err_expected(tokens, "`constructor` or identifier", other).into()), |
882 | | } |
883 | 4.56k | } |
884 | | |
885 | 4.56k | fn named_func(&self) -> &NamedFunc<'a> { |
886 | | use ResourceFunc::*; |
887 | 4.56k | match self { |
888 | 4.56k | Method(f) | Static(f) | Constructor(f) => f, |
889 | | } |
890 | 4.56k | } |
891 | | } |
892 | | |
893 | | struct Record<'a> { |
894 | | span: Span, |
895 | | fields: Vec<Field<'a>>, |
896 | | } |
897 | | |
898 | | struct Field<'a> { |
899 | | docs: Docs<'a>, |
900 | | name: Id<'a>, |
901 | | ty: Type<'a>, |
902 | | } |
903 | | |
904 | | struct Flags<'a> { |
905 | | span: Span, |
906 | | flags: Vec<Flag<'a>>, |
907 | | } |
908 | | |
909 | | struct Flag<'a> { |
910 | | docs: Docs<'a>, |
911 | | name: Id<'a>, |
912 | | } |
913 | | |
914 | | struct Variant<'a> { |
915 | | span: Span, |
916 | | cases: Vec<Case<'a>>, |
917 | | } |
918 | | |
919 | | struct Case<'a> { |
920 | | docs: Docs<'a>, |
921 | | name: Id<'a>, |
922 | | ty: Option<Type<'a>>, |
923 | | } |
924 | | |
925 | | struct Enum<'a> { |
926 | | span: Span, |
927 | | cases: Vec<EnumCase<'a>>, |
928 | | } |
929 | | |
930 | | struct EnumCase<'a> { |
931 | | docs: Docs<'a>, |
932 | | name: Id<'a>, |
933 | | } |
934 | | |
935 | | struct Option_<'a> { |
936 | | span: Span, |
937 | | ty: Box<Type<'a>>, |
938 | | } |
939 | | |
940 | | struct List<'a> { |
941 | | span: Span, |
942 | | ty: Box<Type<'a>>, |
943 | | } |
944 | | |
945 | | struct Map<'a> { |
946 | | span: Span, |
947 | | key: Box<Type<'a>>, |
948 | | value: Box<Type<'a>>, |
949 | | } |
950 | | |
951 | | struct FixedLengthList<'a> { |
952 | | span: Span, |
953 | | ty: Box<Type<'a>>, |
954 | | size: u32, |
955 | | } |
956 | | |
957 | | struct Future<'a> { |
958 | | span: Span, |
959 | | ty: Option<Box<Type<'a>>>, |
960 | | } |
961 | | |
962 | | struct Tuple<'a> { |
963 | | span: Span, |
964 | | types: Vec<Type<'a>>, |
965 | | } |
966 | | |
967 | | struct Result_<'a> { |
968 | | span: Span, |
969 | | ok: Option<Box<Type<'a>>>, |
970 | | err: Option<Box<Type<'a>>>, |
971 | | } |
972 | | |
973 | | struct Stream<'a> { |
974 | | span: Span, |
975 | | ty: Option<Box<Type<'a>>>, |
976 | | } |
977 | | |
978 | | struct NamedFunc<'a> { |
979 | | docs: Docs<'a>, |
980 | | attributes: Vec<Attribute<'a>>, |
981 | | name: Id<'a>, |
982 | | func: Func<'a>, |
983 | | } |
984 | | |
985 | | type ParamList<'a> = Vec<(Id<'a>, Type<'a>)>; |
986 | | |
987 | | struct Func<'a> { |
988 | | span: Span, |
989 | | async_: bool, |
990 | | params: ParamList<'a>, |
991 | | result: Option<Type<'a>>, |
992 | | } |
993 | | |
994 | | impl<'a> Func<'a> { |
995 | 26.1k | fn parse(tokens: &mut Tokenizer<'a>) -> ParseResult<Func<'a>> { |
996 | 26.1k | fn parse_params<'a>( |
997 | 26.1k | tokens: &mut Tokenizer<'a>, |
998 | 26.1k | left_paren: bool, |
999 | 26.1k | ) -> ParseResult<ParamList<'a>> { |
1000 | 26.1k | if left_paren { |
1001 | 26.1k | tokens.expect(Token::LeftParen)?; |
1002 | 0 | }; |
1003 | 59.1k | parse_list_trailer(tokens, Token::RightParen, |_docs, tokens| { |
1004 | 59.1k | let name = parse_id(tokens)?; |
1005 | 59.1k | tokens.expect(Token::Colon)?; |
1006 | 59.1k | let ty = Type::parse(tokens)?; |
1007 | 59.1k | Ok((name, ty)) |
1008 | 59.1k | }) |
1009 | 26.1k | } |
1010 | | |
1011 | 26.1k | let async_ = tokens.eat(Token::Async)?; |
1012 | 26.1k | let span = tokens.expect(Token::Func)?; |
1013 | 26.1k | let params = parse_params(tokens, true)?; |
1014 | 26.1k | let result = if tokens.eat(Token::RArrow)? { |
1015 | 19.7k | let ty = Type::parse(tokens)?; |
1016 | 19.7k | Some(ty) |
1017 | | } else { |
1018 | 6.33k | None |
1019 | | }; |
1020 | 26.1k | Ok(Func { |
1021 | 26.1k | span, |
1022 | 26.1k | async_, |
1023 | 26.1k | params, |
1024 | 26.1k | result, |
1025 | 26.1k | }) |
1026 | 26.1k | } |
1027 | | } |
1028 | | |
1029 | | impl<'a> InterfaceItem<'a> { |
1030 | 62.1k | fn parse( |
1031 | 62.1k | tokens: &mut Tokenizer<'a>, |
1032 | 62.1k | docs: Docs<'a>, |
1033 | 62.1k | attributes: Vec<Attribute<'a>>, |
1034 | 62.1k | ) -> ParseResult<InterfaceItem<'a>> { |
1035 | 62.1k | match tokens.clone().next()? { |
1036 | 1.36k | Some((_span, Token::Type)) => { |
1037 | 1.36k | TypeDef::parse(tokens, docs, attributes).map(InterfaceItem::TypeDef) |
1038 | | } |
1039 | 1.11k | Some((_span, Token::Flags)) => { |
1040 | 1.11k | TypeDef::parse_flags(tokens, docs, attributes).map(InterfaceItem::TypeDef) |
1041 | | } |
1042 | 9.41k | Some((_span, Token::Enum)) => { |
1043 | 9.41k | TypeDef::parse_enum(tokens, docs, attributes).map(InterfaceItem::TypeDef) |
1044 | | } |
1045 | 7.30k | Some((_span, Token::Variant)) => { |
1046 | 7.30k | TypeDef::parse_variant(tokens, docs, attributes).map(InterfaceItem::TypeDef) |
1047 | | } |
1048 | 1.85k | Some((_span, Token::Resource)) => { |
1049 | 1.85k | TypeDef::parse_resource(tokens, docs, attributes).map(InterfaceItem::TypeDef) |
1050 | | } |
1051 | 2.03k | Some((_span, Token::Record)) => { |
1052 | 2.03k | TypeDef::parse_record(tokens, docs, attributes).map(InterfaceItem::TypeDef) |
1053 | | } |
1054 | 9.34k | Some((_span, Token::Id)) | Some((_span, Token::ExplicitId)) => { |
1055 | 18.0k | NamedFunc::parse(tokens, docs, attributes).map(InterfaceItem::Func) |
1056 | | } |
1057 | 20.9k | Some((_span, Token::Use)) => Use::parse(tokens, attributes).map(InterfaceItem::Use), |
1058 | 0 | other => Err(err_expected(tokens, "`type`, `resource` or `func`", other).into()), |
1059 | | } |
1060 | 62.1k | } |
1061 | | } |
1062 | | |
1063 | | impl<'a> TypeDef<'a> { |
1064 | 2.93k | fn parse( |
1065 | 2.93k | tokens: &mut Tokenizer<'a>, |
1066 | 2.93k | docs: Docs<'a>, |
1067 | 2.93k | attributes: Vec<Attribute<'a>>, |
1068 | 2.93k | ) -> ParseResult<Self> { |
1069 | 2.93k | tokens.expect(Token::Type)?; |
1070 | 2.93k | let name = parse_id(tokens)?; |
1071 | 2.93k | tokens.expect(Token::Equals)?; |
1072 | 2.93k | let ty = Type::parse(tokens)?; |
1073 | 2.93k | tokens.expect_semicolon()?; |
1074 | 2.93k | Ok(TypeDef { |
1075 | 2.93k | docs, |
1076 | 2.93k | attributes, |
1077 | 2.93k | name, |
1078 | 2.93k | ty, |
1079 | 2.93k | }) |
1080 | 2.93k | } |
1081 | | |
1082 | 1.38k | fn parse_flags( |
1083 | 1.38k | tokens: &mut Tokenizer<'a>, |
1084 | 1.38k | docs: Docs<'a>, |
1085 | 1.38k | attributes: Vec<Attribute<'a>>, |
1086 | 1.38k | ) -> ParseResult<Self> { |
1087 | 1.38k | tokens.expect(Token::Flags)?; |
1088 | 1.38k | let name = parse_id(tokens)?; |
1089 | 1.38k | let ty = Type::Flags(Flags { |
1090 | 1.38k | span: name.span, |
1091 | 1.38k | flags: parse_list( |
1092 | 1.38k | tokens, |
1093 | 1.38k | Token::LeftBrace, |
1094 | 1.38k | Token::RightBrace, |
1095 | 3.80k | |docs, tokens| { |
1096 | 3.80k | let name = parse_id(tokens)?; |
1097 | 3.80k | Ok(Flag { docs, name }) |
1098 | 3.80k | }, |
1099 | 0 | )?, |
1100 | | }); |
1101 | 1.38k | Ok(TypeDef { |
1102 | 1.38k | docs, |
1103 | 1.38k | attributes, |
1104 | 1.38k | name, |
1105 | 1.38k | ty, |
1106 | 1.38k | }) |
1107 | 1.38k | } |
1108 | | |
1109 | 2.46k | fn parse_resource( |
1110 | 2.46k | tokens: &mut Tokenizer<'a>, |
1111 | 2.46k | docs: Docs<'a>, |
1112 | 2.46k | attributes: Vec<Attribute<'a>>, |
1113 | 2.46k | ) -> ParseResult<Self> { |
1114 | 2.46k | tokens.expect(Token::Resource)?; |
1115 | 2.46k | let name = parse_id(tokens)?; |
1116 | 2.46k | let mut funcs = Vec::new(); |
1117 | 2.46k | if tokens.eat(Token::LeftBrace)? { |
1118 | 6.18k | while !tokens.eat(Token::RightBrace)? { |
1119 | 4.56k | let docs = parse_docs(tokens)?; |
1120 | 4.56k | let attributes = Attribute::parse_list(tokens)?; |
1121 | 4.56k | funcs.push(ResourceFunc::parse(docs, attributes, tokens)?); |
1122 | | } |
1123 | | } else { |
1124 | 840 | tokens.expect_semicolon()?; |
1125 | | } |
1126 | 2.46k | let ty = Type::Resource(Resource { |
1127 | 2.46k | span: name.span, |
1128 | 2.46k | funcs, |
1129 | 2.46k | }); |
1130 | 2.46k | Ok(TypeDef { |
1131 | 2.46k | docs, |
1132 | 2.46k | attributes, |
1133 | 2.46k | name, |
1134 | 2.46k | ty, |
1135 | 2.46k | }) |
1136 | 2.46k | } |
1137 | | |
1138 | 2.88k | fn parse_record( |
1139 | 2.88k | tokens: &mut Tokenizer<'a>, |
1140 | 2.88k | docs: Docs<'a>, |
1141 | 2.88k | attributes: Vec<Attribute<'a>>, |
1142 | 2.88k | ) -> ParseResult<Self> { |
1143 | 2.88k | tokens.expect(Token::Record)?; |
1144 | 2.88k | let name = parse_id(tokens)?; |
1145 | 2.88k | let ty = Type::Record(Record { |
1146 | 2.88k | span: name.span, |
1147 | 2.88k | fields: parse_list( |
1148 | 2.88k | tokens, |
1149 | 2.88k | Token::LeftBrace, |
1150 | 2.88k | Token::RightBrace, |
1151 | 8.21k | |docs, tokens| { |
1152 | 8.21k | let name = parse_id(tokens)?; |
1153 | 8.21k | tokens.expect(Token::Colon)?; |
1154 | 8.21k | let ty = Type::parse(tokens)?; |
1155 | 8.21k | Ok(Field { docs, name, ty }) |
1156 | 8.21k | }, |
1157 | 0 | )?, |
1158 | | }); |
1159 | 2.88k | Ok(TypeDef { |
1160 | 2.88k | docs, |
1161 | 2.88k | attributes, |
1162 | 2.88k | name, |
1163 | 2.88k | ty, |
1164 | 2.88k | }) |
1165 | 2.88k | } |
1166 | | |
1167 | 8.02k | fn parse_variant( |
1168 | 8.02k | tokens: &mut Tokenizer<'a>, |
1169 | 8.02k | docs: Docs<'a>, |
1170 | 8.02k | attributes: Vec<Attribute<'a>>, |
1171 | 8.02k | ) -> ParseResult<Self> { |
1172 | 8.02k | tokens.expect(Token::Variant)?; |
1173 | 8.02k | let name = parse_id(tokens)?; |
1174 | 8.02k | let ty = Type::Variant(Variant { |
1175 | 8.02k | span: name.span, |
1176 | 8.02k | cases: parse_list( |
1177 | 8.02k | tokens, |
1178 | 8.02k | Token::LeftBrace, |
1179 | 8.02k | Token::RightBrace, |
1180 | 27.5k | |docs, tokens| { |
1181 | 27.5k | let name = parse_id(tokens)?; |
1182 | 27.5k | let ty = if tokens.eat(Token::LeftParen)? { |
1183 | 24.3k | let ty = Type::parse(tokens)?; |
1184 | 24.3k | tokens.expect(Token::RightParen)?; |
1185 | 24.3k | Some(ty) |
1186 | | } else { |
1187 | 3.18k | None |
1188 | | }; |
1189 | 27.5k | Ok(Case { docs, name, ty }) |
1190 | 27.5k | }, |
1191 | 0 | )?, |
1192 | | }); |
1193 | 8.02k | Ok(TypeDef { |
1194 | 8.02k | docs, |
1195 | 8.02k | attributes, |
1196 | 8.02k | name, |
1197 | 8.02k | ty, |
1198 | 8.02k | }) |
1199 | 8.02k | } |
1200 | | |
1201 | 13.9k | fn parse_enum( |
1202 | 13.9k | tokens: &mut Tokenizer<'a>, |
1203 | 13.9k | docs: Docs<'a>, |
1204 | 13.9k | attributes: Vec<Attribute<'a>>, |
1205 | 13.9k | ) -> ParseResult<Self> { |
1206 | 13.9k | tokens.expect(Token::Enum)?; |
1207 | 13.9k | let name = parse_id(tokens)?; |
1208 | 13.9k | let ty = Type::Enum(Enum { |
1209 | 13.9k | span: name.span, |
1210 | 13.9k | cases: parse_list( |
1211 | 13.9k | tokens, |
1212 | 13.9k | Token::LeftBrace, |
1213 | 13.9k | Token::RightBrace, |
1214 | 68.5k | |docs, tokens| { |
1215 | 68.5k | let name = parse_id(tokens)?; |
1216 | 68.5k | Ok(EnumCase { docs, name }) |
1217 | 68.5k | }, |
1218 | 0 | )?, |
1219 | | }); |
1220 | 13.9k | Ok(TypeDef { |
1221 | 13.9k | docs, |
1222 | 13.9k | attributes, |
1223 | 13.9k | name, |
1224 | 13.9k | ty, |
1225 | 13.9k | }) |
1226 | 13.9k | } |
1227 | | } |
1228 | | |
1229 | | impl<'a> NamedFunc<'a> { |
1230 | 18.0k | fn parse( |
1231 | 18.0k | tokens: &mut Tokenizer<'a>, |
1232 | 18.0k | docs: Docs<'a>, |
1233 | 18.0k | attributes: Vec<Attribute<'a>>, |
1234 | 18.0k | ) -> ParseResult<Self> { |
1235 | 18.0k | let name = parse_id(tokens)?; |
1236 | 18.0k | tokens.expect(Token::Colon)?; |
1237 | 18.0k | let func = Func::parse(tokens)?; |
1238 | 18.0k | tokens.expect_semicolon()?; |
1239 | 18.0k | Ok(NamedFunc { |
1240 | 18.0k | docs, |
1241 | 18.0k | attributes, |
1242 | 18.0k | name, |
1243 | 18.0k | func, |
1244 | 18.0k | }) |
1245 | 18.0k | } |
1246 | | } |
1247 | | |
1248 | 527k | fn parse_id<'a>(tokens: &mut Tokenizer<'a>) -> ParseResult<Id<'a>> { |
1249 | 527k | match tokens.next()? { |
1250 | 195k | Some((span, Token::Id)) => Ok(Id { |
1251 | 195k | name: tokens.parse_id(span)?, |
1252 | 195k | span, |
1253 | | }), |
1254 | 331k | Some((span, Token::ExplicitId)) => Ok(Id { |
1255 | 331k | name: tokens.parse_explicit_id(span)?, |
1256 | 331k | span, |
1257 | | }), |
1258 | 0 | other => Err(err_expected(tokens, "an identifier or string", other)), |
1259 | | } |
1260 | 527k | } |
1261 | | |
1262 | 44.6k | fn parse_opt_version(tokens: &mut Tokenizer<'_>) -> ParseResult<Option<(Span, Version)>> { |
1263 | 44.6k | if tokens.eat(Token::At)? { |
1264 | 36.9k | parse_version(tokens).map(Some) |
1265 | | } else { |
1266 | 7.72k | Ok(None) |
1267 | | } |
1268 | 44.6k | } |
1269 | | |
1270 | 41.6k | fn parse_version(tokens: &mut Tokenizer<'_>) -> ParseResult<(Span, Version)> { |
1271 | 41.6k | let start = tokens.expect(Token::Integer)?.start(); |
1272 | 41.6k | tokens.expect(Token::Period)?; |
1273 | 41.6k | tokens.expect(Token::Integer)?; |
1274 | 41.6k | tokens.expect(Token::Period)?; |
1275 | 41.6k | let end = tokens.expect(Token::Integer)?.end(); |
1276 | 41.6k | let mut span = Span::new(start, end); |
1277 | 41.6k | eat_ids(tokens, Token::Minus, &mut span)?; |
1278 | 41.6k | eat_ids(tokens, Token::Plus, &mut span)?; |
1279 | 41.6k | let string = tokens.get_span(span); |
1280 | 41.6k | let version = |
1281 | 41.6k | Version::parse(string).map_err(|e| ParseError::new_syntax(span, e.to_string()))?; |
1282 | 41.6k | return Ok((span, version)); |
1283 | | |
1284 | | // According to `semver.org` this is what we're parsing: |
1285 | | // |
1286 | | // ```ebnf |
1287 | | // <pre-release> ::= <dot-separated pre-release identifiers> |
1288 | | // |
1289 | | // <dot-separated pre-release identifiers> ::= <pre-release identifier> |
1290 | | // | <pre-release identifier> "." <dot-separated pre-release identifiers> |
1291 | | // |
1292 | | // <build> ::= <dot-separated build identifiers> |
1293 | | // |
1294 | | // <dot-separated build identifiers> ::= <build identifier> |
1295 | | // | <build identifier> "." <dot-separated build identifiers> |
1296 | | // |
1297 | | // <pre-release identifier> ::= <alphanumeric identifier> |
1298 | | // | <numeric identifier> |
1299 | | // |
1300 | | // <build identifier> ::= <alphanumeric identifier> |
1301 | | // | <digits> |
1302 | | // |
1303 | | // <alphanumeric identifier> ::= <non-digit> |
1304 | | // | <non-digit> <identifier characters> |
1305 | | // | <identifier characters> <non-digit> |
1306 | | // | <identifier characters> <non-digit> <identifier characters> |
1307 | | // |
1308 | | // <numeric identifier> ::= "0" |
1309 | | // | <positive digit> |
1310 | | // | <positive digit> <digits> |
1311 | | // |
1312 | | // <identifier characters> ::= <identifier character> |
1313 | | // | <identifier character> <identifier characters> |
1314 | | // |
1315 | | // <identifier character> ::= <digit> |
1316 | | // | <non-digit> |
1317 | | // |
1318 | | // <non-digit> ::= <letter> |
1319 | | // | "-" |
1320 | | // |
1321 | | // <digits> ::= <digit> |
1322 | | // | <digit> <digits> |
1323 | | // ``` |
1324 | | // |
1325 | | // This is loosely based on WIT syntax and an approximation is parsed here: |
1326 | | // |
1327 | | // * This function starts by parsing the optional leading `-` and `+` which |
1328 | | // indicates pre-release and build metadata. |
1329 | | // * Afterwards all of $id, $integer, `-`, and `.` are chomped. The only |
1330 | | // exception here is that if `.` isn't followed by $id, $integer, or `-` |
1331 | | // then it's assumed that it's something like `use a:b@1.0.0-a.{...}` |
1332 | | // where the `.` is part of WIT syntax, not semver. |
1333 | | // |
1334 | | // Note that this additionally doesn't try to return any first-class errors. |
1335 | | // Instead this bails out on something unrecognized for something else in |
1336 | | // the system to return an error. |
1337 | 83.3k | fn eat_ids( |
1338 | 83.3k | tokens: &mut Tokenizer<'_>, |
1339 | 83.3k | prefix: Token, |
1340 | 83.3k | end: &mut Span, |
1341 | 83.3k | ) -> Result<(), lex::Error> { |
1342 | 83.3k | if !tokens.eat(prefix)? { |
1343 | 13.2k | return Ok(()); |
1344 | 70.1k | } |
1345 | | loop { |
1346 | 245k | let mut clone = tokens.clone(); |
1347 | 245k | match clone.next()? { |
1348 | 70.1k | Some((span, Token::Id | Token::Integer | Token::Minus)) => { |
1349 | 70.1k | end.set_end(span.end()); |
1350 | 70.1k | *tokens = clone; |
1351 | 70.1k | } |
1352 | 117k | Some((_span, Token::Period)) => match clone.next()? { |
1353 | 105k | Some((span, Token::Id | Token::Integer | Token::Minus)) => { |
1354 | 105k | end.set_end(span.end()); |
1355 | 105k | *tokens = clone; |
1356 | 105k | } |
1357 | 12.4k | _ => break Ok(()), |
1358 | | }, |
1359 | 57.6k | _ => break Ok(()), |
1360 | | } |
1361 | | } |
1362 | 83.3k | } |
1363 | 41.6k | } |
1364 | | |
1365 | 582k | fn parse_docs<'a>(tokens: &mut Tokenizer<'a>) -> Result<Docs<'a>, lex::Error> { |
1366 | 582k | let mut docs = Docs::default(); |
1367 | 582k | let mut clone = tokens.clone(); |
1368 | 582k | let mut started = false; |
1369 | 1.08M | while let Some((span, token)) = clone.next_raw()? { |
1370 | 1.05M | match token { |
1371 | 499k | Token::Whitespace => {} |
1372 | | Token::Comment => { |
1373 | 0 | let comment = tokens.get_span(span); |
1374 | 0 | if !started { |
1375 | 0 | docs.span.set_start(span.start()); |
1376 | 0 | started = true; |
1377 | 0 | } |
1378 | 0 | let trailing_ws = comment |
1379 | 0 | .bytes() |
1380 | 0 | .rev() |
1381 | 0 | .take_while(|ch| ch.is_ascii_whitespace()) |
1382 | 0 | .count(); |
1383 | 0 | docs.span.set_end(span.end() - (trailing_ws as u32)); |
1384 | 0 | docs.docs.push(comment.into()); |
1385 | | } |
1386 | 560k | _ => break, |
1387 | | }; |
1388 | 499k | *tokens = clone.clone(); |
1389 | | } |
1390 | 582k | Ok(docs) |
1391 | 582k | } |
1392 | | |
1393 | | impl<'a> Type<'a> { |
1394 | 444k | fn parse(tokens: &mut Tokenizer<'a>) -> ParseResult<Self> { |
1395 | 444k | match tokens.next()? { |
1396 | 7.30k | Some((span, Token::U8)) => Ok(Type::U8(span)), |
1397 | 2.32k | Some((span, Token::U16)) => Ok(Type::U16(span)), |
1398 | 3.84k | Some((span, Token::U32)) => Ok(Type::U32(span)), |
1399 | 2.59k | Some((span, Token::U64)) => Ok(Type::U64(span)), |
1400 | 3.30k | Some((span, Token::S8)) => Ok(Type::S8(span)), |
1401 | 2.46k | Some((span, Token::S16)) => Ok(Type::S16(span)), |
1402 | 3.29k | Some((span, Token::S32)) => Ok(Type::S32(span)), |
1403 | 13.7k | Some((span, Token::S64)) => Ok(Type::S64(span)), |
1404 | 5.93k | Some((span, Token::F32)) => Ok(Type::F32(span)), |
1405 | 10.7k | Some((span, Token::F64)) => Ok(Type::F64(span)), |
1406 | 14.7k | Some((span, Token::Char)) => Ok(Type::Char(span)), |
1407 | | |
1408 | | // tuple<T, U, ...> |
1409 | 29.3k | Some((span, Token::Tuple)) => { |
1410 | 29.3k | let types = parse_list( |
1411 | 29.3k | tokens, |
1412 | 29.3k | Token::LessThan, |
1413 | 29.3k | Token::GreaterThan, |
1414 | 98.6k | |_docs, tokens| Type::parse(tokens), |
1415 | 0 | )?; |
1416 | 29.3k | Ok(Type::Tuple(Tuple { span, types })) |
1417 | | } |
1418 | | |
1419 | 107k | Some((span, Token::Bool)) => Ok(Type::Bool(span)), |
1420 | 2.67k | Some((span, Token::String_)) => Ok(Type::String(span)), |
1421 | | |
1422 | | // list<T> |
1423 | | // list<T, N> |
1424 | 116k | Some((span, Token::List)) => { |
1425 | 116k | tokens.expect(Token::LessThan)?; |
1426 | 116k | let ty = Type::parse(tokens)?; |
1427 | 116k | let size = if tokens.eat(Token::Comma)? { |
1428 | 107k | let number = tokens.next()?; |
1429 | 107k | if let Some((span, Token::Integer)) = number { |
1430 | 107k | let size: u32 = tokens.get_span(span).parse().map_err(|e| { |
1431 | 0 | ParseError::new_syntax(span, format!("invalid list size: {e}")) |
1432 | 0 | })?; |
1433 | 107k | Some(size) |
1434 | | } else { |
1435 | 0 | return Err(err_expected(tokens, "fixed-length", number).into()); |
1436 | | } |
1437 | | } else { |
1438 | 9.20k | None |
1439 | | }; |
1440 | 116k | tokens.expect(Token::GreaterThan)?; |
1441 | 116k | if let Some(size) = size { |
1442 | 107k | Ok(Type::FixedLengthList(FixedLengthList { |
1443 | 107k | span, |
1444 | 107k | ty: Box::new(ty), |
1445 | 107k | size, |
1446 | 107k | })) |
1447 | | } else { |
1448 | 9.20k | Ok(Type::List(List { |
1449 | 9.20k | span, |
1450 | 9.20k | ty: Box::new(ty), |
1451 | 9.20k | })) |
1452 | | } |
1453 | | } |
1454 | | |
1455 | | // map<K, V> |
1456 | 0 | Some((span, Token::Map)) => { |
1457 | 0 | tokens.expect(Token::LessThan)?; |
1458 | 0 | let key = Type::parse(tokens)?; |
1459 | 0 | tokens.expect(Token::Comma)?; |
1460 | 0 | let value = Type::parse(tokens)?; |
1461 | 0 | tokens.expect(Token::GreaterThan)?; |
1462 | 0 | Ok(Type::Map(Map { |
1463 | 0 | span, |
1464 | 0 | key: Box::new(key), |
1465 | 0 | value: Box::new(value), |
1466 | 0 | })) |
1467 | | } |
1468 | | |
1469 | | // option<T> |
1470 | 26.7k | Some((span, Token::Option_)) => { |
1471 | 26.7k | tokens.expect(Token::LessThan)?; |
1472 | 26.7k | let ty = Type::parse(tokens)?; |
1473 | 26.7k | tokens.expect(Token::GreaterThan)?; |
1474 | 26.7k | Ok(Type::Option(Option_ { |
1475 | 26.7k | span, |
1476 | 26.7k | ty: Box::new(ty), |
1477 | 26.7k | })) |
1478 | | } |
1479 | | |
1480 | | // result<T, E> |
1481 | | // result<_, E> |
1482 | | // result<T> |
1483 | | // result |
1484 | 39.9k | Some((span, Token::Result_)) => { |
1485 | 39.9k | let mut ok = None; |
1486 | 39.9k | let mut err = None; |
1487 | | |
1488 | 39.9k | if tokens.eat(Token::LessThan)? { |
1489 | 39.3k | if tokens.eat(Token::Underscore)? { |
1490 | 3.20k | tokens.expect(Token::Comma)?; |
1491 | 3.20k | err = Some(Box::new(Type::parse(tokens)?)); |
1492 | | } else { |
1493 | 36.1k | ok = Some(Box::new(Type::parse(tokens)?)); |
1494 | 36.1k | if tokens.eat(Token::Comma)? { |
1495 | 30.6k | err = Some(Box::new(Type::parse(tokens)?)); |
1496 | 5.55k | } |
1497 | | }; |
1498 | 39.3k | tokens.expect(Token::GreaterThan)?; |
1499 | 547 | }; |
1500 | 39.9k | Ok(Type::Result(Result_ { span, ok, err })) |
1501 | | } |
1502 | | |
1503 | | // future<T> |
1504 | | // future |
1505 | 11.5k | Some((span, Token::Future)) => { |
1506 | 11.5k | let mut ty = None; |
1507 | | |
1508 | 11.5k | if tokens.eat(Token::LessThan)? { |
1509 | 7.28k | ty = Some(Box::new(Type::parse(tokens)?)); |
1510 | 7.28k | tokens.expect(Token::GreaterThan)?; |
1511 | 4.26k | }; |
1512 | 11.5k | Ok(Type::Future(Future { span, ty })) |
1513 | | } |
1514 | | |
1515 | | // stream<T> |
1516 | | // stream |
1517 | 10.1k | Some((span, Token::Stream)) => { |
1518 | 10.1k | let mut ty = None; |
1519 | | |
1520 | 10.1k | if tokens.eat(Token::LessThan)? { |
1521 | 10.1k | ty = Some(Box::new(Type::parse(tokens)?)); |
1522 | 10.1k | tokens.expect(Token::GreaterThan)?; |
1523 | 0 | }; |
1524 | 10.1k | Ok(Type::Stream(Stream { span, ty })) |
1525 | | } |
1526 | | |
1527 | | // error-context |
1528 | 27.1k | Some((span, Token::ErrorContext)) => Ok(Type::ErrorContext(span)), |
1529 | | |
1530 | | // own<T> |
1531 | 295 | Some((_span, Token::Own)) => { |
1532 | 295 | tokens.expect(Token::LessThan)?; |
1533 | 295 | let resource = parse_id(tokens)?; |
1534 | 295 | tokens.expect(Token::GreaterThan)?; |
1535 | 295 | Ok(Type::Handle(Handle::Own { resource })) |
1536 | | } |
1537 | | |
1538 | | // borrow<T> |
1539 | 0 | Some((_span, Token::Borrow)) => { |
1540 | 0 | tokens.expect(Token::LessThan)?; |
1541 | 0 | let resource = parse_id(tokens)?; |
1542 | 0 | tokens.expect(Token::GreaterThan)?; |
1543 | 0 | Ok(Type::Handle(Handle::Borrow { resource })) |
1544 | | } |
1545 | | |
1546 | | // `foo` |
1547 | 1.55k | Some((span, Token::Id)) => Ok(Type::Name(Id { |
1548 | 1.55k | name: tokens.parse_id(span)?.into(), |
1549 | 1.55k | span, |
1550 | | })), |
1551 | | // `%foo` |
1552 | 1.37k | Some((span, Token::ExplicitId)) => Ok(Type::Name(Id { |
1553 | 1.37k | name: tokens.parse_explicit_id(span)?.into(), |
1554 | 1.37k | span, |
1555 | | })), |
1556 | | |
1557 | 0 | other => Err(err_expected(tokens, "a type", other).into()), |
1558 | | } |
1559 | 444k | } |
1560 | | |
1561 | 441k | fn span(&self) -> Span { |
1562 | 441k | match self { |
1563 | 106k | Type::Bool(span) |
1564 | 7.29k | | Type::U8(span) |
1565 | 2.31k | | Type::U16(span) |
1566 | 3.81k | | Type::U32(span) |
1567 | 2.57k | | Type::U64(span) |
1568 | 3.29k | | Type::S8(span) |
1569 | 2.45k | | Type::S16(span) |
1570 | 3.23k | | Type::S32(span) |
1571 | 13.6k | | Type::S64(span) |
1572 | 5.82k | | Type::F32(span) |
1573 | 10.6k | | Type::F64(span) |
1574 | 14.6k | | Type::Char(span) |
1575 | 2.66k | | Type::String(span) |
1576 | 206k | | Type::ErrorContext(span) => *span, |
1577 | 2.83k | Type::Name(id) => id.span, |
1578 | 9.13k | Type::List(l) => l.span, |
1579 | 0 | Type::Map(m) => m.span, |
1580 | 107k | Type::FixedLengthList(l) => l.span, |
1581 | 295 | Type::Handle(h) => h.span(), |
1582 | 0 | Type::Resource(r) => r.span, |
1583 | 0 | Type::Record(r) => r.span, |
1584 | 0 | Type::Flags(f) => f.span, |
1585 | 0 | Type::Variant(v) => v.span, |
1586 | 28.8k | Type::Tuple(t) => t.span, |
1587 | 0 | Type::Enum(e) => e.span, |
1588 | 26.3k | Type::Option(o) => o.span, |
1589 | 39.4k | Type::Result(r) => r.span, |
1590 | 11.5k | Type::Future(f) => f.span, |
1591 | 10.0k | Type::Stream(s) => s.span, |
1592 | | } |
1593 | 441k | } |
1594 | | } |
1595 | | |
1596 | 55.5k | fn parse_list<'a, T>( |
1597 | 55.5k | tokens: &mut Tokenizer<'a>, |
1598 | 55.5k | start: Token, |
1599 | 55.5k | end: Token, |
1600 | 55.5k | parse: impl FnMut(Docs<'a>, &mut Tokenizer<'a>) -> ParseResult<T>, |
1601 | 55.5k | ) -> ParseResult<Vec<T>> { |
1602 | 55.5k | tokens.expect(start)?; |
1603 | 55.5k | parse_list_trailer(tokens, end, parse) |
1604 | 55.5k | } Unexecuted instantiation: wit_parser::ast::parse_list::<wit_parser::ast::IncludeName, <wit_parser::ast::Include>::parse::{closure#0}>wit_parser::ast::parse_list::<wit_parser::ast::Case, <wit_parser::ast::TypeDef>::parse_variant::{closure#0}>Line | Count | Source | 1596 | 8.02k | fn parse_list<'a, T>( | 1597 | 8.02k | tokens: &mut Tokenizer<'a>, | 1598 | 8.02k | start: Token, | 1599 | 8.02k | end: Token, | 1600 | 8.02k | parse: impl FnMut(Docs<'a>, &mut Tokenizer<'a>) -> ParseResult<T>, | 1601 | 8.02k | ) -> ParseResult<Vec<T>> { | 1602 | 8.02k | tokens.expect(start)?; | 1603 | 8.02k | parse_list_trailer(tokens, end, parse) | 1604 | 8.02k | } |
wit_parser::ast::parse_list::<wit_parser::ast::Flag, <wit_parser::ast::TypeDef>::parse_flags::{closure#0}>Line | Count | Source | 1596 | 1.38k | fn parse_list<'a, T>( | 1597 | 1.38k | tokens: &mut Tokenizer<'a>, | 1598 | 1.38k | start: Token, | 1599 | 1.38k | end: Token, | 1600 | 1.38k | parse: impl FnMut(Docs<'a>, &mut Tokenizer<'a>) -> ParseResult<T>, | 1601 | 1.38k | ) -> ParseResult<Vec<T>> { | 1602 | 1.38k | tokens.expect(start)?; | 1603 | 1.38k | parse_list_trailer(tokens, end, parse) | 1604 | 1.38k | } |
wit_parser::ast::parse_list::<wit_parser::ast::Type, <wit_parser::ast::Type>::parse::{closure#0}>Line | Count | Source | 1596 | 29.3k | fn parse_list<'a, T>( | 1597 | 29.3k | tokens: &mut Tokenizer<'a>, | 1598 | 29.3k | start: Token, | 1599 | 29.3k | end: Token, | 1600 | 29.3k | parse: impl FnMut(Docs<'a>, &mut Tokenizer<'a>) -> ParseResult<T>, | 1601 | 29.3k | ) -> ParseResult<Vec<T>> { | 1602 | 29.3k | tokens.expect(start)?; | 1603 | 29.3k | parse_list_trailer(tokens, end, parse) | 1604 | 29.3k | } |
wit_parser::ast::parse_list::<wit_parser::ast::Field, <wit_parser::ast::TypeDef>::parse_record::{closure#0}>Line | Count | Source | 1596 | 2.88k | fn parse_list<'a, T>( | 1597 | 2.88k | tokens: &mut Tokenizer<'a>, | 1598 | 2.88k | start: Token, | 1599 | 2.88k | end: Token, | 1600 | 2.88k | parse: impl FnMut(Docs<'a>, &mut Tokenizer<'a>) -> ParseResult<T>, | 1601 | 2.88k | ) -> ParseResult<Vec<T>> { | 1602 | 2.88k | tokens.expect(start)?; | 1603 | 2.88k | parse_list_trailer(tokens, end, parse) | 1604 | 2.88k | } |
wit_parser::ast::parse_list::<wit_parser::ast::EnumCase, <wit_parser::ast::TypeDef>::parse_enum::{closure#0}>Line | Count | Source | 1596 | 13.9k | fn parse_list<'a, T>( | 1597 | 13.9k | tokens: &mut Tokenizer<'a>, | 1598 | 13.9k | start: Token, | 1599 | 13.9k | end: Token, | 1600 | 13.9k | parse: impl FnMut(Docs<'a>, &mut Tokenizer<'a>) -> ParseResult<T>, | 1601 | 13.9k | ) -> ParseResult<Vec<T>> { | 1602 | 13.9k | tokens.expect(start)?; | 1603 | 13.9k | parse_list_trailer(tokens, end, parse) | 1604 | 13.9k | } |
|
1605 | | |
1606 | 82.2k | fn parse_list_trailer<'a, T>( |
1607 | 82.2k | tokens: &mut Tokenizer<'a>, |
1608 | 82.2k | end: Token, |
1609 | 82.2k | mut parse: impl FnMut(Docs<'a>, &mut Tokenizer<'a>) -> ParseResult<T>, |
1610 | 82.2k | ) -> ParseResult<Vec<T>> { |
1611 | 82.2k | let mut items = Vec::new(); |
1612 | | loop { |
1613 | | // get docs before we skip them to try to eat the end token |
1614 | 298k | let docs = parse_docs(tokens)?; |
1615 | | |
1616 | | // if we found an end token then we're done |
1617 | 298k | if tokens.eat(end)? { |
1618 | 30.9k | break; |
1619 | 267k | } |
1620 | | |
1621 | 267k | let item = parse(docs, tokens)?; |
1622 | 267k | items.push(item); |
1623 | | |
1624 | | // if there's no trailing comma then this is required to be the end, |
1625 | | // otherwise we go through the loop to try to get another item |
1626 | 267k | if !tokens.eat(Token::Comma)? { |
1627 | 51.2k | tokens.expect(end)?; |
1628 | 51.2k | break; |
1629 | 215k | } |
1630 | | } |
1631 | 82.2k | Ok(items) |
1632 | 82.2k | } Unexecuted instantiation: wit_parser::ast::parse_list_trailer::<wit_parser::ast::IncludeName, <wit_parser::ast::Include>::parse::{closure#0}>wit_parser::ast::parse_list_trailer::<wit_parser::ast::Case, <wit_parser::ast::TypeDef>::parse_variant::{closure#0}>Line | Count | Source | 1606 | 8.02k | fn parse_list_trailer<'a, T>( | 1607 | 8.02k | tokens: &mut Tokenizer<'a>, | 1608 | 8.02k | end: Token, | 1609 | 8.02k | mut parse: impl FnMut(Docs<'a>, &mut Tokenizer<'a>) -> ParseResult<T>, | 1610 | 8.02k | ) -> ParseResult<Vec<T>> { | 1611 | 8.02k | let mut items = Vec::new(); | 1612 | | loop { | 1613 | | // get docs before we skip them to try to eat the end token | 1614 | 35.5k | let docs = parse_docs(tokens)?; | 1615 | | | 1616 | | // if we found an end token then we're done | 1617 | 35.5k | if tokens.eat(end)? { | 1618 | 8.02k | break; | 1619 | 27.5k | } | 1620 | | | 1621 | 27.5k | let item = parse(docs, tokens)?; | 1622 | 27.5k | items.push(item); | 1623 | | | 1624 | | // if there's no trailing comma then this is required to be the end, | 1625 | | // otherwise we go through the loop to try to get another item | 1626 | 27.5k | if !tokens.eat(Token::Comma)? { | 1627 | 0 | tokens.expect(end)?; | 1628 | 0 | break; | 1629 | 27.5k | } | 1630 | | } | 1631 | 8.02k | Ok(items) | 1632 | 8.02k | } |
wit_parser::ast::parse_list_trailer::<wit_parser::ast::Flag, <wit_parser::ast::TypeDef>::parse_flags::{closure#0}>Line | Count | Source | 1606 | 1.38k | fn parse_list_trailer<'a, T>( | 1607 | 1.38k | tokens: &mut Tokenizer<'a>, | 1608 | 1.38k | end: Token, | 1609 | 1.38k | mut parse: impl FnMut(Docs<'a>, &mut Tokenizer<'a>) -> ParseResult<T>, | 1610 | 1.38k | ) -> ParseResult<Vec<T>> { | 1611 | 1.38k | let mut items = Vec::new(); | 1612 | | loop { | 1613 | | // get docs before we skip them to try to eat the end token | 1614 | 5.18k | let docs = parse_docs(tokens)?; | 1615 | | | 1616 | | // if we found an end token then we're done | 1617 | 5.18k | if tokens.eat(end)? { | 1618 | 1.38k | break; | 1619 | 3.80k | } | 1620 | | | 1621 | 3.80k | let item = parse(docs, tokens)?; | 1622 | 3.80k | items.push(item); | 1623 | | | 1624 | | // if there's no trailing comma then this is required to be the end, | 1625 | | // otherwise we go through the loop to try to get another item | 1626 | 3.80k | if !tokens.eat(Token::Comma)? { | 1627 | 0 | tokens.expect(end)?; | 1628 | 0 | break; | 1629 | 3.80k | } | 1630 | | } | 1631 | 1.38k | Ok(items) | 1632 | 1.38k | } |
wit_parser::ast::parse_list_trailer::<wit_parser::ast::Type, <wit_parser::ast::Type>::parse::{closure#0}>Line | Count | Source | 1606 | 29.3k | fn parse_list_trailer<'a, T>( | 1607 | 29.3k | tokens: &mut Tokenizer<'a>, | 1608 | 29.3k | end: Token, | 1609 | 29.3k | mut parse: impl FnMut(Docs<'a>, &mut Tokenizer<'a>) -> ParseResult<T>, | 1610 | 29.3k | ) -> ParseResult<Vec<T>> { | 1611 | 29.3k | let mut items = Vec::new(); | 1612 | | loop { | 1613 | | // get docs before we skip them to try to eat the end token | 1614 | 98.6k | let docs = parse_docs(tokens)?; | 1615 | | | 1616 | | // if we found an end token then we're done | 1617 | 98.6k | if tokens.eat(end)? { | 1618 | 0 | break; | 1619 | 98.6k | } | 1620 | | | 1621 | 98.6k | let item = parse(docs, tokens)?; | 1622 | 98.6k | items.push(item); | 1623 | | | 1624 | | // if there's no trailing comma then this is required to be the end, | 1625 | | // otherwise we go through the loop to try to get another item | 1626 | 98.6k | if !tokens.eat(Token::Comma)? { | 1627 | 29.3k | tokens.expect(end)?; | 1628 | 29.3k | break; | 1629 | 69.3k | } | 1630 | | } | 1631 | 29.3k | Ok(items) | 1632 | 29.3k | } |
wit_parser::ast::parse_list_trailer::<wit_parser::ast::Field, <wit_parser::ast::TypeDef>::parse_record::{closure#0}>Line | Count | Source | 1606 | 2.88k | fn parse_list_trailer<'a, T>( | 1607 | 2.88k | tokens: &mut Tokenizer<'a>, | 1608 | 2.88k | end: Token, | 1609 | 2.88k | mut parse: impl FnMut(Docs<'a>, &mut Tokenizer<'a>) -> ParseResult<T>, | 1610 | 2.88k | ) -> ParseResult<Vec<T>> { | 1611 | 2.88k | let mut items = Vec::new(); | 1612 | | loop { | 1613 | | // get docs before we skip them to try to eat the end token | 1614 | 11.1k | let docs = parse_docs(tokens)?; | 1615 | | | 1616 | | // if we found an end token then we're done | 1617 | 11.1k | if tokens.eat(end)? { | 1618 | 2.88k | break; | 1619 | 8.21k | } | 1620 | | | 1621 | 8.21k | let item = parse(docs, tokens)?; | 1622 | 8.21k | items.push(item); | 1623 | | | 1624 | | // if there's no trailing comma then this is required to be the end, | 1625 | | // otherwise we go through the loop to try to get another item | 1626 | 8.21k | if !tokens.eat(Token::Comma)? { | 1627 | 0 | tokens.expect(end)?; | 1628 | 0 | break; | 1629 | 8.21k | } | 1630 | | } | 1631 | 2.88k | Ok(items) | 1632 | 2.88k | } |
wit_parser::ast::parse_list_trailer::<wit_parser::ast::EnumCase, <wit_parser::ast::TypeDef>::parse_enum::{closure#0}>Line | Count | Source | 1606 | 13.9k | fn parse_list_trailer<'a, T>( | 1607 | 13.9k | tokens: &mut Tokenizer<'a>, | 1608 | 13.9k | end: Token, | 1609 | 13.9k | mut parse: impl FnMut(Docs<'a>, &mut Tokenizer<'a>) -> ParseResult<T>, | 1610 | 13.9k | ) -> ParseResult<Vec<T>> { | 1611 | 13.9k | let mut items = Vec::new(); | 1612 | | loop { | 1613 | | // get docs before we skip them to try to eat the end token | 1614 | 82.4k | let docs = parse_docs(tokens)?; | 1615 | | | 1616 | | // if we found an end token then we're done | 1617 | 82.4k | if tokens.eat(end)? { | 1618 | 13.9k | break; | 1619 | 68.5k | } | 1620 | | | 1621 | 68.5k | let item = parse(docs, tokens)?; | 1622 | 68.5k | items.push(item); | 1623 | | | 1624 | | // if there's no trailing comma then this is required to be the end, | 1625 | | // otherwise we go through the loop to try to get another item | 1626 | 68.5k | if !tokens.eat(Token::Comma)? { | 1627 | 0 | tokens.expect(end)?; | 1628 | 0 | break; | 1629 | 68.5k | } | 1630 | | } | 1631 | 13.9k | Ok(items) | 1632 | 13.9k | } |
wit_parser::ast::parse_list_trailer::<(wit_parser::ast::Id, wit_parser::ast::Type), <wit_parser::ast::ResourceFunc>::parse::{closure#0}>Line | Count | Source | 1606 | 555 | fn parse_list_trailer<'a, T>( | 1607 | 555 | tokens: &mut Tokenizer<'a>, | 1608 | 555 | end: Token, | 1609 | 555 | mut parse: impl FnMut(Docs<'a>, &mut Tokenizer<'a>) -> ParseResult<T>, | 1610 | 555 | ) -> ParseResult<Vec<T>> { | 1611 | 555 | let mut items = Vec::new(); | 1612 | | loop { | 1613 | | // get docs before we skip them to try to eat the end token | 1614 | 1.32k | let docs = parse_docs(tokens)?; | 1615 | | | 1616 | | // if we found an end token then we're done | 1617 | 1.32k | if tokens.eat(end)? { | 1618 | 163 | break; | 1619 | 1.16k | } | 1620 | | | 1621 | 1.16k | let item = parse(docs, tokens)?; | 1622 | 1.16k | items.push(item); | 1623 | | | 1624 | | // if there's no trailing comma then this is required to be the end, | 1625 | | // otherwise we go through the loop to try to get another item | 1626 | 1.16k | if !tokens.eat(Token::Comma)? { | 1627 | 392 | tokens.expect(end)?; | 1628 | 392 | break; | 1629 | 768 | } | 1630 | | } | 1631 | 555 | Ok(items) | 1632 | 555 | } |
wit_parser::ast::parse_list_trailer::<(wit_parser::ast::Id, wit_parser::ast::Type), <wit_parser::ast::Func>::parse::parse_params::{closure#0}>Line | Count | Source | 1606 | 26.1k | fn parse_list_trailer<'a, T>( | 1607 | 26.1k | tokens: &mut Tokenizer<'a>, | 1608 | 26.1k | end: Token, | 1609 | 26.1k | mut parse: impl FnMut(Docs<'a>, &mut Tokenizer<'a>) -> ParseResult<T>, | 1610 | 26.1k | ) -> ParseResult<Vec<T>> { | 1611 | 26.1k | let mut items = Vec::new(); | 1612 | | loop { | 1613 | | // get docs before we skip them to try to eat the end token | 1614 | 63.7k | let docs = parse_docs(tokens)?; | 1615 | | | 1616 | | // if we found an end token then we're done | 1617 | 63.7k | if tokens.eat(end)? { | 1618 | 4.60k | break; | 1619 | 59.1k | } | 1620 | | | 1621 | 59.1k | let item = parse(docs, tokens)?; | 1622 | 59.1k | items.push(item); | 1623 | | | 1624 | | // if there's no trailing comma then this is required to be the end, | 1625 | | // otherwise we go through the loop to try to get another item | 1626 | 59.1k | if !tokens.eat(Token::Comma)? { | 1627 | 21.5k | tokens.expect(end)?; | 1628 | 21.5k | break; | 1629 | 37.6k | } | 1630 | | } | 1631 | 26.1k | Ok(items) | 1632 | 26.1k | } |
|
1633 | | |
1634 | 0 | fn err_expected( |
1635 | 0 | tokens: &Tokenizer<'_>, |
1636 | 0 | expected: &'static str, |
1637 | 0 | found: Option<(Span, Token)>, |
1638 | 0 | ) -> ParseError { |
1639 | 0 | match found { |
1640 | 0 | Some((span, token)) => ParseError::new_syntax( |
1641 | 0 | span, |
1642 | 0 | format!("expected {}, found {}", expected, token.describe()), |
1643 | | ), |
1644 | | None => { |
1645 | 0 | ParseError::new_syntax(tokens.eof_span(), format!("expected {expected}, found eof")) |
1646 | | } |
1647 | | } |
1648 | 0 | } |
1649 | | |
1650 | | enum Attribute<'a> { |
1651 | | Since { span: Span, version: Version }, |
1652 | | Unstable { span: Span, feature: Id<'a> }, |
1653 | | Deprecated { span: Span, version: Version }, |
1654 | | } |
1655 | | |
1656 | | impl<'a> Attribute<'a> { |
1657 | 171k | fn parse_list(tokens: &mut Tokenizer<'a>) -> ParseResult<Vec<Attribute<'a>>> { |
1658 | 171k | let mut ret = Vec::new(); |
1659 | 177k | while tokens.eat(Token::At)? { |
1660 | 5.90k | let id = parse_id(tokens)?; |
1661 | 5.90k | let attr = match id.name { |
1662 | 5.90k | "since" => { |
1663 | 3.48k | tokens.expect(Token::LeftParen)?; |
1664 | 3.48k | eat_id(tokens, "version")?; |
1665 | 3.48k | tokens.expect(Token::Equals)?; |
1666 | 3.48k | let (_span, version) = parse_version(tokens)?; |
1667 | 3.48k | tokens.expect(Token::RightParen)?; |
1668 | 3.48k | Attribute::Since { |
1669 | 3.48k | span: id.span, |
1670 | 3.48k | version, |
1671 | 3.48k | } |
1672 | | } |
1673 | 2.41k | "unstable" => { |
1674 | 1.18k | tokens.expect(Token::LeftParen)?; |
1675 | 1.18k | eat_id(tokens, "feature")?; |
1676 | 1.18k | tokens.expect(Token::Equals)?; |
1677 | 1.18k | let feature = parse_id(tokens)?; |
1678 | 1.18k | tokens.expect(Token::RightParen)?; |
1679 | 1.18k | Attribute::Unstable { |
1680 | 1.18k | span: id.span, |
1681 | 1.18k | feature, |
1682 | 1.18k | } |
1683 | | } |
1684 | 1.23k | "deprecated" => { |
1685 | 1.23k | tokens.expect(Token::LeftParen)?; |
1686 | 1.23k | eat_id(tokens, "version")?; |
1687 | 1.23k | tokens.expect(Token::Equals)?; |
1688 | 1.23k | let (_span, version) = parse_version(tokens)?; |
1689 | 1.23k | tokens.expect(Token::RightParen)?; |
1690 | 1.23k | Attribute::Deprecated { |
1691 | 1.23k | span: id.span, |
1692 | 1.23k | version, |
1693 | 1.23k | } |
1694 | | } |
1695 | 0 | other => { |
1696 | 0 | return Err(ParseError::new_syntax( |
1697 | 0 | id.span, |
1698 | 0 | format!("unknown attribute `{other}`"), |
1699 | 0 | )); |
1700 | | } |
1701 | | }; |
1702 | 5.90k | ret.push(attr); |
1703 | | } |
1704 | 171k | Ok(ret) |
1705 | 171k | } |
1706 | | |
1707 | 0 | fn span(&self) -> Span { |
1708 | 0 | match self { |
1709 | 0 | Attribute::Since { span, .. } |
1710 | 0 | | Attribute::Unstable { span, .. } |
1711 | 0 | | Attribute::Deprecated { span, .. } => *span, |
1712 | | } |
1713 | 0 | } |
1714 | | } |
1715 | | |
1716 | 5.90k | fn eat_id(tokens: &mut Tokenizer<'_>, expected: &str) -> ParseResult<Span> { |
1717 | 5.90k | let id = parse_id(tokens)?; |
1718 | 5.90k | if id.name != expected { |
1719 | 0 | return Err(ParseError::new_syntax( |
1720 | 0 | id.span, |
1721 | 0 | format!("expected `{expected}`, found `{}`", id.name), |
1722 | 0 | )); |
1723 | 5.90k | } |
1724 | 5.90k | Ok(id.span) |
1725 | 5.90k | } |
1726 | | |
1727 | | /// A listing of source files which are used to get parsed into an |
1728 | | /// [`UnresolvedPackage`]. |
1729 | | /// |
1730 | | /// [`UnresolvedPackage`]: crate::UnresolvedPackage |
1731 | | #[derive(Clone, Default, Debug, PartialEq, Eq)] |
1732 | | pub struct SourceMap { |
1733 | | sources: Vec<Source>, |
1734 | | offset: u32, |
1735 | | } |
1736 | | |
1737 | | #[derive(Clone, Debug, PartialEq, Eq)] |
1738 | | struct Source { |
1739 | | offset: u32, |
1740 | | path: String, |
1741 | | contents: String, |
1742 | | } |
1743 | | |
1744 | | impl SourceMap { |
1745 | | /// Creates a new empty source map. |
1746 | 7.22k | pub fn new() -> SourceMap { |
1747 | 7.22k | SourceMap::default() |
1748 | 7.22k | } |
1749 | | |
1750 | | /// Reads the file `path` on the filesystem and appends its contents to this |
1751 | | /// [`SourceMap`]. |
1752 | | #[cfg(feature = "std")] |
1753 | 0 | pub fn push_file(&mut self, path: &Path) -> anyhow::Result<()> { |
1754 | 0 | let contents = std::fs::read_to_string(path) |
1755 | 0 | .with_context(|| format!("failed to read file {path:?}"))?; |
1756 | 0 | self.push(path, contents); |
1757 | 0 | Ok(()) |
1758 | 0 | } |
1759 | | |
1760 | | /// Appends the given contents with the given path into this source map. |
1761 | | /// |
1762 | | /// The `path` provided is not read from the filesystem and is instead only |
1763 | | /// used during error messages. Each file added to a [`SourceMap`] is |
1764 | | /// used to create the final parsed package namely by unioning all the |
1765 | | /// interfaces and worlds defined together. Note that each file has its own |
1766 | | /// personal namespace, however, for top-level `use` and such. |
1767 | | #[cfg(feature = "std")] |
1768 | 17.2k | pub fn push(&mut self, path: &Path, contents: impl Into<String>) { |
1769 | 17.2k | self.push_str(&path.display().to_string(), contents); |
1770 | 17.2k | } <wit_parser::ast::SourceMap>::push::<&alloc::string::String> Line | Count | Source | 1768 | 17.2k | pub fn push(&mut self, path: &Path, contents: impl Into<String>) { | 1769 | 17.2k | self.push_str(&path.display().to_string(), contents); | 1770 | 17.2k | } |
Unexecuted instantiation: <wit_parser::ast::SourceMap>::push::<alloc::string::String> |
1771 | | |
1772 | | /// Appends the given contents with the given source name into this source map. |
1773 | | /// |
1774 | | /// The `path` provided is not read from the filesystem and is instead only |
1775 | | /// used during error messages. Each file added to a [`SourceMap`] is |
1776 | | /// used to create the final parsed package namely by unioning all the |
1777 | | /// interfaces and worlds defined together. Note that each file has its own |
1778 | | /// personal namespace, however, for top-level `use` and such. |
1779 | 21.9k | pub fn push_str(&mut self, path: &str, contents: impl Into<String>) { |
1780 | 21.9k | let mut contents = contents.into(); |
1781 | | // Guarantee that there's at least one character in these contents by |
1782 | | // appending a single newline to the end. This is excluded from |
1783 | | // tokenization below so it's only here to ensure that spans which point |
1784 | | // one byte beyond the end of a file (eof) point to the same original |
1785 | | // file. |
1786 | 21.9k | contents.push('\n'); |
1787 | 21.9k | let new_offset = self.offset + u32::try_from(contents.len()).unwrap(); |
1788 | 21.9k | self.sources.push(Source { |
1789 | 21.9k | offset: self.offset, |
1790 | 21.9k | path: path.to_string(), |
1791 | 21.9k | contents, |
1792 | 21.9k | }); |
1793 | 21.9k | self.offset = new_offset; |
1794 | 21.9k | } <wit_parser::ast::SourceMap>::push_str::<&alloc::string::String> Line | Count | Source | 1779 | 17.2k | pub fn push_str(&mut self, path: &str, contents: impl Into<String>) { | 1780 | 17.2k | let mut contents = contents.into(); | 1781 | | // Guarantee that there's at least one character in these contents by | 1782 | | // appending a single newline to the end. This is excluded from | 1783 | | // tokenization below so it's only here to ensure that spans which point | 1784 | | // one byte beyond the end of a file (eof) point to the same original | 1785 | | // file. | 1786 | 17.2k | contents.push('\n'); | 1787 | 17.2k | let new_offset = self.offset + u32::try_from(contents.len()).unwrap(); | 1788 | 17.2k | self.sources.push(Source { | 1789 | 17.2k | offset: self.offset, | 1790 | 17.2k | path: path.to_string(), | 1791 | 17.2k | contents, | 1792 | 17.2k | }); | 1793 | 17.2k | self.offset = new_offset; | 1794 | 17.2k | } |
Unexecuted instantiation: <wit_parser::ast::SourceMap>::push_str::<alloc::string::String> <wit_parser::ast::SourceMap>::push_str::<&str> Line | Count | Source | 1779 | 4.67k | pub fn push_str(&mut self, path: &str, contents: impl Into<String>) { | 1780 | 4.67k | let mut contents = contents.into(); | 1781 | | // Guarantee that there's at least one character in these contents by | 1782 | | // appending a single newline to the end. This is excluded from | 1783 | | // tokenization below so it's only here to ensure that spans which point | 1784 | | // one byte beyond the end of a file (eof) point to the same original | 1785 | | // file. | 1786 | 4.67k | contents.push('\n'); | 1787 | 4.67k | let new_offset = self.offset + u32::try_from(contents.len()).unwrap(); | 1788 | 4.67k | self.sources.push(Source { | 1789 | 4.67k | offset: self.offset, | 1790 | 4.67k | path: path.to_string(), | 1791 | 4.67k | contents, | 1792 | 4.67k | }); | 1793 | 4.67k | self.offset = new_offset; | 1794 | 4.67k | } |
|
1795 | | |
1796 | | /// Appends all sources from another `SourceMap` into this one. |
1797 | | /// |
1798 | | /// Returns the byte offset that should be added to all `Span.start` and |
1799 | | /// `Span.end` values from the appended source map to make them valid |
1800 | | /// in the combined source map. |
1801 | 19.3k | pub fn append(&mut self, other: SourceMap) -> u32 { |
1802 | 19.3k | let base = self.offset; |
1803 | 21.7k | for mut source in other.sources { |
1804 | 21.7k | source.offset += base; |
1805 | 21.7k | self.sources.push(source); |
1806 | 21.7k | } |
1807 | 19.3k | self.offset += other.offset; |
1808 | 19.3k | base |
1809 | 19.3k | } |
1810 | | |
1811 | | /// Parses the files added to this source map into a |
1812 | | /// [`UnresolvedPackageGroup`]. |
1813 | | /// |
1814 | | /// On failure returns `Err((self, e))` so the caller can use the source |
1815 | | /// map for error formatting if needed. |
1816 | 11.8k | pub fn parse(self) -> Result<UnresolvedPackageGroup, (Self, ParseError)> { |
1817 | 11.8k | match self.parse_inner() { |
1818 | 11.8k | Ok((main, nested)) => Ok(UnresolvedPackageGroup { |
1819 | 11.8k | main, |
1820 | 11.8k | nested, |
1821 | 11.8k | source_map: self, |
1822 | 11.8k | }), |
1823 | 0 | Err(e) => Err((self, e)), |
1824 | | } |
1825 | 11.8k | } |
1826 | | |
1827 | 11.8k | fn parse_inner(&self) -> ParseResult<(UnresolvedPackage, Vec<UnresolvedPackage>)> { |
1828 | 11.8k | let mut nested = Vec::new(); |
1829 | 11.8k | let mut resolver = Resolver::default(); |
1830 | 11.8k | let mut srcs = self.sources.iter().collect::<Vec<_>>(); |
1831 | 11.8k | srcs.sort_by_key(|src| &src.path); |
1832 | | |
1833 | | // Parse each source file individually. A tokenizer is created here |
1834 | | // from settings and then `PackageFile` is used to parse the whole |
1835 | | // stream of tokens. |
1836 | 21.7k | for src in srcs { |
1837 | 21.7k | let mut tokens = Tokenizer::new( |
1838 | | // chop off the forcibly appended `\n` character when |
1839 | | // passing through the source to get tokenized. |
1840 | 21.7k | &src.contents[..src.contents.len() - 1], |
1841 | 21.7k | src.offset, |
1842 | 0 | )?; |
1843 | 21.7k | let mut file = PackageFile::parse(&mut tokens)?; |
1844 | | |
1845 | | // Filter out any nested packages and resolve them separately. |
1846 | | // Nested packages have only a single "file" so only one item |
1847 | | // is pushed into a `Resolver`. Note that a nested `Resolver` |
1848 | | // is used here, not the outer one. |
1849 | | // |
1850 | | // Note that filtering out `Package` items is required due to |
1851 | | // how the implementation of disallowing nested packages in |
1852 | | // nested packages currently works. |
1853 | 70.0k | for item in mem::take(&mut file.decl_list.items) { |
1854 | 70.0k | match item { |
1855 | 852 | AstItem::Package(nested_pkg) => { |
1856 | 852 | let mut resolve = Resolver::default(); |
1857 | 852 | resolve.push(nested_pkg)?; |
1858 | 852 | nested.push(resolve.resolve()?); |
1859 | | } |
1860 | 69.2k | other => file.decl_list.items.push(other), |
1861 | | } |
1862 | | } |
1863 | | |
1864 | | // With nested packages handled push this file into the resolver. |
1865 | 21.7k | resolver.push(file)?; |
1866 | | } |
1867 | | |
1868 | 11.8k | Ok((resolver.resolve()?, nested)) |
1869 | 11.8k | } |
1870 | | |
1871 | 0 | pub(crate) fn highlight_span(&self, span: Span, err: impl fmt::Display) -> Option<String> { |
1872 | 0 | if !span.is_known() { |
1873 | 0 | return None; |
1874 | 0 | } |
1875 | 0 | Some(self.highlight_err(span.start(), Some(span.end()), err)) |
1876 | 0 | } Unexecuted instantiation: <wit_parser::ast::SourceMap>::highlight_span::<&wit_parser::ast::error::ParseErrorKind> Unexecuted instantiation: <wit_parser::ast::SourceMap>::highlight_span::<&alloc::string::String> |
1877 | | |
1878 | 0 | fn highlight_err(&self, start: u32, end: Option<u32>, err: impl fmt::Display) -> String { |
1879 | 0 | let src = self.source_for_offset(start); |
1880 | 0 | let start = src.to_relative_offset(start); |
1881 | 0 | let end = end.map(|end| src.to_relative_offset(end)); Unexecuted instantiation: <wit_parser::ast::SourceMap>::highlight_err::<&wit_parser::ast::error::ParseErrorKind>::{closure#0}Unexecuted instantiation: <wit_parser::ast::SourceMap>::highlight_err::<&alloc::string::String>::{closure#0} |
1882 | 0 | let (line, col) = src.linecol(start); |
1883 | 0 | let snippet = src.contents.lines().nth(line).unwrap_or(""); |
1884 | 0 | let line = line + 1; |
1885 | 0 | let col = col + 1; |
1886 | | |
1887 | | // If the snippet is too large then don't overload output on a terminal |
1888 | | // for example and instead just print the error. This also sidesteps |
1889 | | // Rust's restriction that `>0$` below has to be less than `u16::MAX`. |
1890 | 0 | if snippet.len() > 500 { |
1891 | 0 | return format!("{}:{line}:{col}: {err}", src.path); |
1892 | 0 | } |
1893 | 0 | let mut msg = format!( |
1894 | | "\ |
1895 | | {err} |
1896 | | --> {file}:{line}:{col} |
1897 | | | |
1898 | | {line:4} | {snippet} |
1899 | | | {marker:>0$}", |
1900 | | col, |
1901 | | file = src.path, |
1902 | | marker = "^", |
1903 | | ); |
1904 | 0 | if let Some(end) = end { |
1905 | 0 | if let Some(s) = src.contents.get(start..end) { |
1906 | 0 | for _ in s.chars().skip(1) { |
1907 | 0 | msg.push('-'); |
1908 | 0 | } |
1909 | 0 | } |
1910 | 0 | } |
1911 | 0 | return msg; |
1912 | 0 | } Unexecuted instantiation: <wit_parser::ast::SourceMap>::highlight_err::<&wit_parser::ast::error::ParseErrorKind> Unexecuted instantiation: <wit_parser::ast::SourceMap>::highlight_err::<&alloc::string::String> |
1913 | | |
1914 | | /// Renders a span as a human-readable location string (e.g., "file.wit:10:5"). |
1915 | 0 | pub fn render_location(&self, span: Span) -> String { |
1916 | 0 | if !span.is_known() { |
1917 | 0 | return "<unknown>".to_string(); |
1918 | 0 | } |
1919 | 0 | let start = span.start(); |
1920 | 0 | let src = self.source_for_offset(start); |
1921 | 0 | let rel_start = src.to_relative_offset(start); |
1922 | 0 | let (line, col) = src.linecol(rel_start); |
1923 | 0 | format!( |
1924 | | "{file}:{line}:{col}", |
1925 | | file = src.path, |
1926 | 0 | line = line + 1, |
1927 | 0 | col = col + 1, |
1928 | | ) |
1929 | 0 | } |
1930 | | |
1931 | 0 | fn source_for_offset(&self, start: u32) -> &Source { |
1932 | 0 | let i = match self.sources.binary_search_by_key(&start, |src| src.offset) { |
1933 | 0 | Ok(i) => i, |
1934 | 0 | Err(i) => i - 1, |
1935 | | }; |
1936 | 0 | &self.sources[i] |
1937 | 0 | } |
1938 | | |
1939 | | /// Returns an iterator over all filenames added to this source map. |
1940 | | #[cfg(feature = "std")] |
1941 | 0 | pub fn source_files(&self) -> impl Iterator<Item = &Path> { |
1942 | 0 | self.sources.iter().map(|src| Path::new(&src.path)) |
1943 | 0 | } |
1944 | | |
1945 | | /// Returns an iterator over all source names added to this source map. |
1946 | 11.8k | pub fn source_names(&self) -> impl Iterator<Item = &str> { |
1947 | 21.7k | self.sources.iter().map(|src| src.path.as_str()) |
1948 | 11.8k | } |
1949 | | } |
1950 | | |
1951 | | impl Source { |
1952 | 0 | fn to_relative_offset(&self, offset: u32) -> usize { |
1953 | 0 | usize::try_from(offset - self.offset).unwrap() |
1954 | 0 | } |
1955 | | |
1956 | 0 | fn linecol(&self, relative_offset: usize) -> (usize, usize) { |
1957 | 0 | let mut cur = 0; |
1958 | | // Use split_terminator instead of lines so that if there is a `\r`, |
1959 | | // it is included in the offset calculation. The `+1` values below |
1960 | | // account for the `\n`. |
1961 | 0 | for (i, line) in self.contents.split_terminator('\n').enumerate() { |
1962 | 0 | if cur + line.len() + 1 > relative_offset { |
1963 | 0 | return (i, relative_offset - cur); |
1964 | 0 | } |
1965 | 0 | cur += line.len() + 1; |
1966 | | } |
1967 | 0 | (self.contents.lines().count(), 0) |
1968 | 0 | } |
1969 | | } |
1970 | | |
1971 | | pub enum ParsedUsePath { |
1972 | | Name(String), |
1973 | | Package(crate::PackageName, String), |
1974 | | } |
1975 | | |
1976 | 0 | pub fn parse_use_path(s: &str) -> anyhow::Result<ParsedUsePath> { |
1977 | 0 | let mut tokens = Tokenizer::new(s, 0)?; |
1978 | 0 | let path = UsePath::parse(&mut tokens)?; |
1979 | 0 | if tokens.next()?.is_some() { |
1980 | 0 | anyhow::bail!("trailing tokens in path specifier"); |
1981 | 0 | } |
1982 | 0 | Ok(match path { |
1983 | 0 | UsePath::Id(id) => ParsedUsePath::Name(id.name.to_string()), |
1984 | 0 | UsePath::Package { id, name } => { |
1985 | 0 | ParsedUsePath::Package(id.package_name(), name.name.to_string()) |
1986 | | } |
1987 | | }) |
1988 | 0 | } |
1989 | | |
1990 | | #[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] |
1991 | | #[cfg_attr( |
1992 | | feature = "serde", |
1993 | | derive(serde_derive::Serialize, serde_derive::Deserialize) |
1994 | | )] |
1995 | | #[cfg_attr(feature = "serde", serde(into = "String", try_from = "String"))] |
1996 | | /// The fully-qualified name of a Component Model item (function, |
1997 | | /// type, or resource) as can be defined by wit. An item is optionally in a |
1998 | | /// package, the package optionally has a version, and the item is optionally |
1999 | | /// inside a namespace. |
2000 | | /// |
2001 | | /// The syntax for an ItemName always uses the package version as a suffix, if |
2002 | | /// it is given. The following tests show examples of the syntax for ItemName: |
2003 | | /// ```rust |
2004 | | /// # use wit_parser::{ItemName, PackageName}; |
2005 | | /// assert_eq!( |
2006 | | /// "foo".parse::<ItemName>().unwrap(), |
2007 | | /// ItemName { |
2008 | | /// package: None, |
2009 | | /// interface: None, |
2010 | | /// name: "foo".to_owned() |
2011 | | /// } |
2012 | | /// ); |
2013 | | /// assert_eq!( |
2014 | | /// "foo.bar".parse::<ItemName>().unwrap(), |
2015 | | /// ItemName { |
2016 | | /// package: None, |
2017 | | /// interface: Some("foo".to_owned()), |
2018 | | /// name: "bar".to_owned() |
2019 | | /// } |
2020 | | /// ); |
2021 | | /// assert_eq!( |
2022 | | /// "foo:bar/baz".parse::<ItemName>().unwrap(), |
2023 | | /// ItemName { |
2024 | | /// package: Some(PackageName { |
2025 | | /// namespace: "foo".to_owned(), |
2026 | | /// name: "bar".to_owned(), |
2027 | | /// version: None |
2028 | | /// }), |
2029 | | /// interface: None, |
2030 | | /// name: "baz".to_owned() |
2031 | | /// } |
2032 | | /// ); |
2033 | | /// assert_eq!( |
2034 | | /// "foo:bar/baz@0.1.0".parse::<ItemName>().unwrap(), |
2035 | | /// ItemName { |
2036 | | /// package: Some(PackageName { |
2037 | | /// namespace: "foo".to_owned(), |
2038 | | /// name: "bar".to_owned(), |
2039 | | /// version: Some("0.1.0".parse().unwrap()) |
2040 | | /// }), |
2041 | | /// interface: None, |
2042 | | /// name: "baz".to_owned() |
2043 | | /// } |
2044 | | /// ); |
2045 | | /// assert_eq!( |
2046 | | /// "foo:bar/baz.bat".parse::<ItemName>().unwrap(), |
2047 | | /// ItemName { |
2048 | | /// package: Some(PackageName { |
2049 | | /// namespace: "foo".to_owned(), |
2050 | | /// name: "bar".to_owned(), |
2051 | | /// version: None |
2052 | | /// }), |
2053 | | /// interface: Some("baz".to_owned()), |
2054 | | /// name: "bat".to_owned() |
2055 | | /// } |
2056 | | /// ); |
2057 | | /// assert_eq!( |
2058 | | /// "foo:bar/baz.bat@0.1.0".parse::<ItemName>().unwrap(), |
2059 | | /// ItemName { |
2060 | | /// package: Some(PackageName { |
2061 | | /// namespace: "foo".to_owned(), |
2062 | | /// name: "bar".to_owned(), |
2063 | | /// version: Some("0.1.0".parse().unwrap()), |
2064 | | /// }), |
2065 | | /// interface: Some("baz".to_owned()), |
2066 | | /// name: "bat".to_owned() |
2067 | | /// } |
2068 | | /// ); |
2069 | | /// assert!("foo@0.1.0".parse::<ItemName>().is_err()); |
2070 | | /// assert!("foo:bar@0.1.0".parse::<ItemName>().is_err()); |
2071 | | /// assert!("foo:bar/baz@0.1.0.bat".parse::<ItemName>().is_err()); |
2072 | | /// assert!("foo.bar@0.1.0".parse::<ItemName>().is_err()); |
2073 | | /// assert!("foo@0.1.0.bar".parse::<ItemName>().is_err()); |
2074 | | /// ``` |
2075 | | /// |
2076 | | /// Parse this from a string using its [`FromStr`](core::str::FromStr) or |
2077 | | /// [`TryFrom<String>`](core::convert::TryFrom) impls. |
2078 | | /// [`Display`](core::fmt::Display) to render to the same string syntax. |
2079 | | pub struct ItemName { |
2080 | | pub package: Option<crate::PackageName>, |
2081 | | pub interface: Option<String>, |
2082 | | pub name: String, |
2083 | | } |
2084 | | impl ItemName { |
2085 | | /// Get the name of the component instance containing this item, if any. |
2086 | | /// The instance name will be formed like: |
2087 | | /// "namespace.packagename/interfacename@0.1.0" |
2088 | | /// ```rust |
2089 | | /// # use wit_parser::ItemName; |
2090 | | /// assert_eq!( |
2091 | | /// "foo".parse::<ItemName>().unwrap().instance_name(), |
2092 | | /// None, |
2093 | | /// ); |
2094 | | /// assert_eq!( |
2095 | | /// "foo.bar".parse::<ItemName>().unwrap().instance_name(), |
2096 | | /// Some("foo".to_owned()) |
2097 | | /// ); |
2098 | | /// assert_eq!( |
2099 | | /// "foo:bar/baz.bat@0.1.0".parse::<ItemName>().unwrap().instance_name(), |
2100 | | /// Some("foo:bar/baz@0.1.0".to_owned()) |
2101 | | /// ); |
2102 | | /// ``` |
2103 | 0 | pub fn instance_name(&self) -> Option<String> { |
2104 | 0 | if self.package.is_none() && self.interface.is_none() { |
2105 | 0 | return None; |
2106 | 0 | } |
2107 | 0 | let mut s = String::new(); |
2108 | | if let Some(crate::PackageName { |
2109 | 0 | namespace, name, .. |
2110 | 0 | }) = &self.package |
2111 | 0 | { |
2112 | 0 | s.push_str(&format!("{namespace}:{name}/")); |
2113 | 0 | } |
2114 | 0 | if let Some(name) = &self.interface { |
2115 | 0 | s.push_str(&format!("{name}")); |
2116 | 0 | } |
2117 | | if let Some(crate::PackageName { |
2118 | 0 | version: Some(version), |
2119 | | .. |
2120 | 0 | }) = &self.package |
2121 | 0 | { |
2122 | 0 | s.push_str(&format!("@{version}")); |
2123 | 0 | } |
2124 | 0 | Some(s) |
2125 | 0 | } |
2126 | | } |
2127 | | impl core::str::FromStr for ItemName { |
2128 | | type Err = anyhow::Error; |
2129 | 0 | fn from_str(s: &str) -> anyhow::Result<ItemName> { |
2130 | 0 | let mut tokens = Tokenizer::new(s, 0)?; |
2131 | | |
2132 | 0 | let id = parse_id(&mut tokens)?; |
2133 | 0 | let (package, name) = if tokens.eat(Token::Colon)? { |
2134 | 0 | let name = parse_id(&mut tokens)?; |
2135 | 0 | tokens.expect(Token::Slash)?; |
2136 | 0 | (Some((id, name)), parse_id(&mut tokens)?) |
2137 | | } else { |
2138 | 0 | (None, id) |
2139 | | }; |
2140 | | let interface; |
2141 | 0 | let name = if tokens.eat(Token::Period)? { |
2142 | 0 | interface = Some(name.name.to_string()); |
2143 | 0 | parse_id(&mut tokens)? |
2144 | | } else { |
2145 | 0 | interface = None; |
2146 | 0 | name |
2147 | | }; |
2148 | | |
2149 | 0 | let package = package |
2150 | 0 | .map(|(namespace, name)| -> anyhow::Result<crate::PackageName> { |
2151 | 0 | let version = parse_opt_version(&mut tokens)?; |
2152 | | Ok(PackageName { |
2153 | 0 | docs: Docs::default(), |
2154 | 0 | span: Span::new( |
2155 | 0 | namespace.span.start(), |
2156 | 0 | version |
2157 | 0 | .as_ref() |
2158 | 0 | .map(|(s, _)| s.end()) |
2159 | 0 | .unwrap_or(name.span.end()), |
2160 | | ), |
2161 | 0 | namespace, |
2162 | 0 | name, |
2163 | 0 | version, |
2164 | | } |
2165 | 0 | .package_name()) |
2166 | 0 | }) |
2167 | 0 | .transpose()?; |
2168 | | |
2169 | 0 | if tokens.next()?.is_some() { |
2170 | 0 | anyhow::bail!("trailing tokens in item name specifier"); |
2171 | 0 | } |
2172 | 0 | Ok(ItemName { |
2173 | 0 | package, |
2174 | 0 | interface, |
2175 | 0 | name: name.name.to_string(), |
2176 | 0 | }) |
2177 | 0 | } |
2178 | | } |
2179 | | impl core::convert::TryFrom<String> for ItemName { |
2180 | | type Error = anyhow::Error; |
2181 | 0 | fn try_from(s: String) -> anyhow::Result<ItemName> { |
2182 | 0 | s.parse() |
2183 | 0 | } |
2184 | | } |
2185 | | impl fmt::Display for ItemName { |
2186 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
2187 | | if let Some(crate::PackageName { |
2188 | 0 | namespace, name, .. |
2189 | 0 | }) = &self.package |
2190 | | { |
2191 | 0 | write!(f, "{namespace}:{name}/")?; |
2192 | 0 | } |
2193 | 0 | if let Some(int) = &self.interface { |
2194 | 0 | write!(f, "{int}.")?; |
2195 | 0 | } |
2196 | 0 | write!(f, "{}", self.name)?; |
2197 | | if let Some(crate::PackageName { |
2198 | 0 | version: Some(version), |
2199 | | .. |
2200 | 0 | }) = &self.package |
2201 | | { |
2202 | 0 | write!(f, "@{version}")?; |
2203 | 0 | } |
2204 | 0 | Ok(()) |
2205 | 0 | } |
2206 | | } |
2207 | | impl From<ItemName> for String { |
2208 | 0 | fn from(m: ItemName) -> String { |
2209 | 0 | m.to_string() |
2210 | 0 | } |
2211 | | } |
2212 | | |
2213 | | #[cfg(test)] |
2214 | | mod item_name_test { |
2215 | | use super::{ItemName, Version}; |
2216 | | use crate::PackageName; |
2217 | | use alloc::borrow::ToOwned; |
2218 | | |
2219 | | fn assert_round_trip(s: &str) { |
2220 | | use alloc::string::ToString; |
2221 | | let i = s.parse::<ItemName>().unwrap(); |
2222 | | assert_eq!(i.to_string(), s); |
2223 | | } |
2224 | | |
2225 | | #[test] |
2226 | | fn bare() { |
2227 | | assert_eq!( |
2228 | | "bare-kebab-name".parse::<ItemName>().unwrap(), |
2229 | | ItemName { |
2230 | | package: None, |
2231 | | interface: None, |
2232 | | name: "bare-kebab-name".to_owned() |
2233 | | } |
2234 | | ); |
2235 | | assert_round_trip("bare-kebab-name"); |
2236 | | // Invalid to have a version without a package name |
2237 | | assert!("bare-kebab-name@0.1.0".parse::<ItemName>().is_err()); |
2238 | | } |
2239 | | #[test] |
2240 | | fn in_interface() { |
2241 | | assert_eq!( |
2242 | | "foo.bar".parse::<ItemName>().unwrap(), |
2243 | | ItemName { |
2244 | | package: None, |
2245 | | interface: Some("foo".to_owned()), |
2246 | | name: "bar".to_owned() |
2247 | | } |
2248 | | ); |
2249 | | assert_round_trip("foo.bar"); |
2250 | | // Invalid to have a version without a package name |
2251 | | assert!("foo.bar@0.1.0".parse::<ItemName>().is_err()); |
2252 | | } |
2253 | | #[test] |
2254 | | fn in_package() { |
2255 | | assert_eq!( |
2256 | | "foo:bar/baz.bat".parse::<ItemName>().unwrap(), |
2257 | | ItemName { |
2258 | | package: Some(PackageName { |
2259 | | namespace: "foo".to_owned(), |
2260 | | name: "bar".to_owned(), |
2261 | | version: None |
2262 | | }), |
2263 | | interface: Some("baz".to_owned()), |
2264 | | name: "bat".to_owned() |
2265 | | } |
2266 | | ); |
2267 | | assert_round_trip("foo:bar/baz.bat"); |
2268 | | assert_eq!( |
2269 | | "foo:bar/baz.bat@0.1.0".parse::<ItemName>().unwrap(), |
2270 | | ItemName { |
2271 | | package: Some(PackageName { |
2272 | | namespace: "foo".to_owned(), |
2273 | | name: "bar".to_owned(), |
2274 | | version: Some(Version::parse("0.1.0").unwrap()), |
2275 | | }), |
2276 | | interface: Some("baz".to_owned()), |
2277 | | name: "bat".to_owned() |
2278 | | } |
2279 | | ); |
2280 | | assert_round_trip("foo:bar/baz.bat@0.1.0"); |
2281 | | assert_eq!( |
2282 | | "foo:bar/baz".parse::<ItemName>().unwrap(), |
2283 | | ItemName { |
2284 | | package: Some(PackageName { |
2285 | | namespace: "foo".to_owned(), |
2286 | | name: "bar".to_owned(), |
2287 | | version: None |
2288 | | }), |
2289 | | interface: None, |
2290 | | name: "baz".to_owned() |
2291 | | } |
2292 | | ); |
2293 | | assert_round_trip("foo:bar/baz@0.1.0"); |
2294 | | assert_eq!( |
2295 | | "foo:bar/baz@0.1.0".parse::<ItemName>().unwrap(), |
2296 | | ItemName { |
2297 | | package: Some(PackageName { |
2298 | | namespace: "foo".to_owned(), |
2299 | | name: "bar".to_owned(), |
2300 | | version: Some(Version::parse("0.1.0").unwrap()), |
2301 | | }), |
2302 | | interface: None, |
2303 | | name: "baz".to_owned() |
2304 | | } |
2305 | | ); |
2306 | | } |
2307 | | } |