1use anyhow::{anyhow, bail, Context, Result};
2use id_arena::{Arena, Id};
3use pulldown_cmark::{CodeBlockKind, CowStr, Event, Options, Parser, Tag};
4use std::collections::{HashMap, HashSet};
5use std::fs;
6use std::path::{Path, PathBuf};
7
8pub mod abi;
9mod ast;
10pub mod mangle;
11mod sizealign;
12pub use sizealign::*;
13
14pub fn validate_id(s: &str) -> Result<()> {
16 ast::validate_id(0, s)?;
17 Ok(())
18}
19
20#[derive(Debug, Clone, Default, PartialEq)]
21pub struct Interface {
22 pub name: String,
23 pub module: Option<String>,
31 pub types: Arena<TypeDef>,
32 pub type_lookup: HashMap<String, TypeId>,
33 pub resources: Arena<Resource>,
34 pub resource_lookup: HashMap<String, ResourceId>,
35 pub interfaces: Arena<Interface>,
36 pub interface_lookup: HashMap<String, InterfaceId>,
37 pub functions: Vec<Function>,
38 pub globals: Vec<Global>,
39}
40
41pub type TypeId = Id<TypeDef>;
42pub type ResourceId = Id<Resource>;
43pub type InterfaceId = Id<Interface>;
44
45#[derive(Debug, Clone, PartialEq)]
46pub struct TypeDef {
47 pub docs: Docs,
48 pub kind: TypeDefKind,
49 pub name: Option<String>,
50 pub foreign_module: Option<String>,
53}
54
55#[derive(Debug, Clone, PartialEq)]
56pub enum TypeDefKind {
57 Record(Record),
58 Flags(Flags),
59 Tuple(Tuple),
60 Variant(Variant),
61 Enum(Enum),
62 Option(Type),
63 Result(Result_),
64 Union(Union),
65 List(Type),
66 Future(Type),
67 Stream(Stream),
68 Type(Type),
69}
70
71#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
72pub enum Type {
73 Unit,
74 Bool,
75 U8,
76 U16,
77 U32,
78 U64,
79 S8,
80 S16,
81 S32,
82 S64,
83 Float32,
84 Float64,
85 Char,
86 String,
87 Handle(ResourceId),
88 Id(TypeId),
89}
90
91#[derive(PartialEq, Debug, Copy, Clone)]
92pub enum Int {
93 U8,
94 U16,
95 U32,
96 U64,
97}
98
99#[derive(Debug, Clone, PartialEq)]
100pub struct Record {
101 pub fields: Vec<Field>,
102}
103
104#[derive(Debug, Clone, PartialEq)]
105pub struct Field {
106 pub docs: Docs,
107 pub name: String,
108 pub ty: Type,
109}
110
111#[derive(Debug, Clone, PartialEq)]
112pub struct Flags {
113 pub flags: Vec<Flag>,
114}
115
116#[derive(Debug, Clone, PartialEq)]
117pub struct Flag {
118 pub docs: Docs,
119 pub name: String,
120}
121
122#[derive(Debug)]
123pub enum FlagsRepr {
124 U8,
125 U16,
126 U32(usize),
127}
128
129impl Flags {
130 pub fn repr(&self) -> FlagsRepr {
131 match self.flags.len() {
132 n if n <= 8 => FlagsRepr::U8,
133 n if n <= 16 => FlagsRepr::U16,
134 n => FlagsRepr::U32(sizealign::align_to(n, 32) / 32),
135 }
136 }
137}
138
139impl FlagsRepr {
140 pub fn count(&self) -> usize {
141 match self {
142 FlagsRepr::U8 => 1,
143 FlagsRepr::U16 => 1,
144 FlagsRepr::U32(n) => *n,
145 }
146 }
147}
148
149#[derive(Debug, Clone, PartialEq)]
150pub struct Tuple {
151 pub types: Vec<Type>,
152}
153
154#[derive(Debug, Clone, PartialEq)]
155pub struct Variant {
156 pub cases: Vec<Case>,
157}
158
159#[derive(Debug, Clone, PartialEq)]
160pub struct Case {
161 pub docs: Docs,
162 pub name: String,
163 pub ty: Type,
164}
165
166impl Variant {
167 pub fn tag(&self) -> Int {
168 match self.cases.len() {
169 n if n <= u8::max_value() as usize => Int::U8,
170 n if n <= u16::max_value() as usize => Int::U16,
171 n if n <= u32::max_value() as usize => Int::U32,
172 _ => panic!("too many cases to fit in a repr"),
173 }
174 }
175}
176
177#[derive(Debug, Clone, PartialEq)]
178pub struct Enum {
179 pub cases: Vec<EnumCase>,
180}
181
182#[derive(Debug, Clone, PartialEq)]
183pub struct EnumCase {
184 pub docs: Docs,
185 pub name: String,
186}
187
188impl Enum {
189 pub fn tag(&self) -> Int {
190 match self.cases.len() {
191 n if n <= u8::max_value() as usize => Int::U8,
192 n if n <= u16::max_value() as usize => Int::U16,
193 n if n <= u32::max_value() as usize => Int::U32,
194 _ => panic!("too many cases to fit in a repr"),
195 }
196 }
197}
198
199#[derive(Debug, Clone, PartialEq)]
200pub struct Result_ {
201 pub ok: Type,
202 pub err: Type,
203}
204
205#[derive(Debug, Clone, PartialEq)]
206pub struct Union {
207 pub cases: Vec<UnionCase>,
208}
209
210#[derive(Debug, Clone, PartialEq)]
211pub struct UnionCase {
212 pub docs: Docs,
213 pub ty: Type,
214}
215
216impl Union {
217 pub fn tag(&self) -> Int {
218 match self.cases.len() {
219 n if n <= u8::max_value() as usize => Int::U8,
220 n if n <= u16::max_value() as usize => Int::U16,
221 n if n <= u32::max_value() as usize => Int::U32,
222 _ => panic!("too many cases to fit in a repr"),
223 }
224 }
225}
226
227#[derive(Debug, Clone, PartialEq)]
228pub struct Stream {
229 pub element: Type,
230 pub end: Type,
231}
232
233#[derive(Clone, Default, Debug, PartialEq)]
234pub struct Docs {
235 pub contents: Option<String>,
236}
237
238#[derive(Debug, Clone, PartialEq)]
239pub struct Resource {
240 pub docs: Docs,
241 pub name: String,
242 pub supertype: Option<String>,
243 pub foreign_module: Option<String>,
246}
247
248#[derive(Debug, Clone, PartialEq)]
249pub struct Global {
250 pub docs: Docs,
251 pub name: String,
252 pub ty: Type,
253}
254
255#[derive(Debug, Clone, PartialEq)]
256pub struct Function {
257 pub docs: Docs,
258 pub name: String,
259 pub kind: FunctionKind,
260 pub params: Vec<(String, Type)>,
261 pub result: Type,
262}
263
264#[derive(Debug, Clone, PartialEq)]
265pub enum FunctionKind {
266 Freestanding,
267 Static { resource: ResourceId, name: String },
268 Method { resource: ResourceId, name: String },
269}
270
271impl Function {
272 pub fn item_name(&self) -> &str {
273 match &self.kind {
274 FunctionKind::Freestanding => &self.name,
275 FunctionKind::Static { name, .. } => name,
276 FunctionKind::Method { name, .. } => name,
277 }
278 }
279}
280
281fn unwrap_md(contents: &str) -> String {
282 let mut wit = String::new();
283 let mut last_pos = 0;
284 let mut in_wit_code_block = false;
285 Parser::new_ext(contents, Options::empty())
286 .into_offset_iter()
287 .for_each(|(event, range)| match (event, range) {
288 (Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(CowStr::Borrowed("wit")))), _) => {
289 in_wit_code_block = true;
290 }
291 (Event::Text(text), range) if in_wit_code_block => {
292 for _ in contents[last_pos..range.start].lines() {
295 wit.push_str("\n");
296 }
297 wit.push_str(&text);
298 last_pos = range.end;
299 }
300 (Event::End(Tag::CodeBlock(CodeBlockKind::Fenced(CowStr::Borrowed("wit")))), _) => {
301 in_wit_code_block = false;
302 }
303 _ => {}
304 });
305 wit
306}
307
308impl Interface {
309 pub fn parse(name: &str, input: &str) -> Result<Interface> {
310 Interface::parse_with(name, input, |f| {
311 Err(anyhow!("cannot load submodule `{}`", f))
312 })
313 }
314
315 pub fn parse_file(path: impl AsRef<Path>) -> Result<Interface> {
316 let path = path.as_ref();
317 let parent = path.parent().unwrap();
318 let contents = std::fs::read_to_string(&path)
319 .with_context(|| format!("failed to read: {}", path.display()))?;
320 Interface::parse_with(path, &contents, |path| load_fs(parent, path))
321 }
322
323 pub fn parse_with(
324 filename: impl AsRef<Path>,
325 contents: &str,
326 mut load: impl FnMut(&str) -> Result<(PathBuf, String)>,
327 ) -> Result<Interface> {
328 Interface::_parse_with(
329 filename.as_ref(),
330 contents,
331 &mut load,
332 &mut HashSet::new(),
333 &mut HashMap::new(),
334 )
335 }
336
337 fn _parse_with(
338 filename: &Path,
339 contents: &str,
340 load: &mut dyn FnMut(&str) -> Result<(PathBuf, String)>,
341 visiting: &mut HashSet<PathBuf>,
342 map: &mut HashMap<String, Interface>,
343 ) -> Result<Interface> {
344 let name = filename
345 .file_name()
346 .context("wit path must end in a file name")?
347 .to_str()
348 .context("wit filename must be valid unicode")?
349 .split(".")
351 .next()
352 .unwrap();
353 let mut contents = contents;
354
355 let md_contents;
358 if filename.extension().and_then(|s| s.to_str()) == Some("md") {
359 md_contents = unwrap_md(contents);
360 contents = &md_contents[..];
361 }
362
363 let ast = match ast::Ast::parse(contents) {
365 Ok(ast) => ast,
366 Err(mut e) => {
367 let file = filename.display().to_string();
368 ast::rewrite_error(&mut e, &file, contents);
369 return Err(e);
370 }
371 };
372
373 if !visiting.insert(filename.to_path_buf()) {
375 bail!("file `{}` recursively imports itself", filename.display())
376 }
377 for item in ast.items.iter() {
378 let u = match item {
379 ast::Item::Use(u) => u,
380 _ => continue,
381 };
382 if map.contains_key(&*u.from[0].name) {
383 continue;
384 }
385 let (filename, contents) = load(&u.from[0].name)
386 ?;
388 let instance = Interface::_parse_with(&filename, &contents, load, visiting, map)?;
389 map.insert(u.from[0].name.to_string(), instance);
390 }
391 visiting.remove(filename);
392
393 match ast.resolve(name, map) {
395 Ok(i) => Ok(i),
396 Err(mut e) => {
397 let file = filename.display().to_string();
398 ast::rewrite_error(&mut e, &file, contents);
399 Err(e)
400 }
401 }
402 }
403
404 pub fn topological_types(&self) -> Vec<TypeId> {
405 let mut ret = Vec::new();
406 let mut visited = HashSet::new();
407 for (id, _) in self.types.iter() {
408 self.topo_visit(id, &mut ret, &mut visited);
409 }
410 ret
411 }
412
413 fn topo_visit(&self, id: TypeId, list: &mut Vec<TypeId>, visited: &mut HashSet<TypeId>) {
414 if !visited.insert(id) {
415 return;
416 }
417 match &self.types[id].kind {
418 TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => {}
419 TypeDefKind::Type(t) | TypeDefKind::List(t) => self.topo_visit_ty(t, list, visited),
420 TypeDefKind::Record(r) => {
421 for f in r.fields.iter() {
422 self.topo_visit_ty(&f.ty, list, visited);
423 }
424 }
425 TypeDefKind::Tuple(t) => {
426 for t in t.types.iter() {
427 self.topo_visit_ty(t, list, visited);
428 }
429 }
430 TypeDefKind::Variant(v) => {
431 for v in v.cases.iter() {
432 self.topo_visit_ty(&v.ty, list, visited);
433 }
434 }
435 TypeDefKind::Option(ty) => self.topo_visit_ty(ty, list, visited),
436 TypeDefKind::Result(r) => {
437 self.topo_visit_ty(&r.ok, list, visited);
438 self.topo_visit_ty(&r.err, list, visited);
439 }
440 TypeDefKind::Union(u) => {
441 for t in u.cases.iter() {
442 self.topo_visit_ty(&t.ty, list, visited);
443 }
444 }
445 TypeDefKind::Future(ty) => {
446 self.topo_visit_ty(ty, list, visited);
447 }
448 TypeDefKind::Stream(s) => {
449 self.topo_visit_ty(&s.element, list, visited);
450 self.topo_visit_ty(&s.end, list, visited);
451 }
452 }
453 list.push(id);
454 }
455
456 fn topo_visit_ty(&self, ty: &Type, list: &mut Vec<TypeId>, visited: &mut HashSet<TypeId>) {
457 if let Type::Id(id) = ty {
458 self.topo_visit(*id, list, visited);
459 }
460 }
461
462 pub fn all_bits_valid(&self, ty: &Type) -> bool {
463 match ty {
464 Type::Unit
465 | Type::U8
466 | Type::S8
467 | Type::U16
468 | Type::S16
469 | Type::U32
470 | Type::S32
471 | Type::U64
472 | Type::S64
473 | Type::Float32
474 | Type::Float64 => true,
475
476 Type::Bool | Type::Char | Type::Handle(_) | Type::String => false,
477
478 Type::Id(id) => match &self.types[*id].kind {
479 TypeDefKind::List(_)
480 | TypeDefKind::Variant(_)
481 | TypeDefKind::Enum(_)
482 | TypeDefKind::Option(_)
483 | TypeDefKind::Result(_)
484 | TypeDefKind::Future(_)
485 | TypeDefKind::Stream(_)
486 | TypeDefKind::Union(_) => false,
487 TypeDefKind::Type(t) => self.all_bits_valid(t),
488 TypeDefKind::Record(r) => r.fields.iter().all(|f| self.all_bits_valid(&f.ty)),
489 TypeDefKind::Tuple(t) => t.types.iter().all(|t| self.all_bits_valid(t)),
490
491 TypeDefKind::Flags(_) => false,
495 },
496 }
497 }
498
499 pub fn get_variant(&self, ty: &Type) -> Option<&Variant> {
500 if let Type::Id(id) = ty {
501 match &self.types[*id].kind {
502 TypeDefKind::Variant(v) => Some(v),
503 _ => None,
504 }
505 } else {
506 None
507 }
508 }
509}
510
511fn load_fs(root: &Path, name: &str) -> Result<(PathBuf, String)> {
512 let wit = root.join(name).with_extension("wit");
513
514 match fs::read_to_string(&wit) {
516 Ok(contents) => Ok((wit, contents)),
517
518 Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
520 let wit_md = wit.with_extension("wit.md");
521 match fs::read_to_string(&wit_md) {
522 Ok(contents) => Ok((wit_md, contents)),
523 Err(_err) => Err(err.into()),
524 }
525 }
526
527 Err(err) => return Err(err.into()),
528 }
529}