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