-
Notifications
You must be signed in to change notification settings - Fork 112
Spec for WIT component syntax
#627
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -20,6 +20,11 @@ bytes. Types can be imported between interfaces within a package using | |||||||||||||
| unqualified names and additionally from other packages through namespace- | ||||||||||||||
| and-package-qualified names. | ||||||||||||||
|
|
||||||||||||||
| Alternatively, a WIT file can contain a single unnamed | ||||||||||||||
| [`component`][components-wit] definition, which provides a lightweight way to | ||||||||||||||
| define the imports and exports of a component without requiring a `package` | ||||||||||||||
| declaration, namespace, package name, or world name. | ||||||||||||||
|
|
||||||||||||||
| This document will go through the purpose of the syntactic constructs of a WIT | ||||||||||||||
| document, a pseudo-formal [grammar specification][lexical-structure], and | ||||||||||||||
| additionally a specification of the [package format][package-format] of a WIT | ||||||||||||||
|
|
@@ -799,6 +804,78 @@ same syntax with the same restrictions as the first, but prefixed with '%': | |||||||||||||
|
|
||||||||||||||
| [kebab-case]: https://en.wikipedia.org/wiki/Letter_case#Kebab_case | ||||||||||||||
|
|
||||||||||||||
| ## WIT Components | ||||||||||||||
| [components-wit]: #wit-components | ||||||||||||||
|
|
||||||||||||||
| In addition to defining reusable, published packages with `package` and `world`, | ||||||||||||||
| WIT supports a lightweight `component` definition for cases where you are simply | ||||||||||||||
| defining the imports and exports of a single component and don't intend to | ||||||||||||||
| publish the WIT as a reusable package. | ||||||||||||||
|
|
||||||||||||||
| A `component` definition is an unnamed world: it has `import` and `export` | ||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
| statements just like a `world`, but it has no name, and a `package` declaration | ||||||||||||||
| is neither required nor allowed to be present in the same file. This avoids | ||||||||||||||
| forcing the author to invent a namespace, package name, and world name that | ||||||||||||||
| would not appear in the compiled `.wasm` component anyway. | ||||||||||||||
|
|
||||||||||||||
| For example, instead of writing: | ||||||||||||||
|
|
||||||||||||||
| ```wit | ||||||||||||||
| package my:test; | ||||||||||||||
|
|
||||||||||||||
| world my-component { | ||||||||||||||
| import wasi:http/outgoing-handler; | ||||||||||||||
| export frob: func(in: string) -> string; | ||||||||||||||
| } | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| where the namespace `my`, the package name `test`, and the world name | ||||||||||||||
| `my-component` are all arbitrary and unused in the final binary, you can write: | ||||||||||||||
|
|
||||||||||||||
| ```wit | ||||||||||||||
| component { | ||||||||||||||
| import wasi:http/outgoing-handler; | ||||||||||||||
| export frob: func(in: string) -> string; | ||||||||||||||
| } | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| ### Rules | ||||||||||||||
|
|
||||||||||||||
| * A WIT file must contain **either** a `package` declaration (with optional | ||||||||||||||
| `world` and `interface` definitions) **or** a single `component` definition, | ||||||||||||||
| but **not both**. This invariant is enforced by the parser. | ||||||||||||||
|
Comment on lines
+844
to
+846
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question: could there be multiple WIT files (fed into wasm-tools just like today), as long as none of the files contain a package declaration if any of the files contains a component definition? |
||||||||||||||
| * A `component` definition is always unnamed. No identifier follows the | ||||||||||||||
| `component` keyword. | ||||||||||||||
| * The body of a `component` is identical to the body of a `world`: it may | ||||||||||||||
| contain `import`, `export`, `use`, `include`, and type definition items. | ||||||||||||||
| * Dependencies on external packages (e.g. `wasi:http/outgoing-handler`) are | ||||||||||||||
| referenced with the same fully-qualified `use-path` syntax used in worlds. | ||||||||||||||
| These dependencies must be resolved through the same mechanisms used for | ||||||||||||||
| `world` definitions (inline `package … { … }` blocks, `deps/` directory, | ||||||||||||||
| or tooling-specific configuration). | ||||||||||||||
| * Because there is no package name or world name, a `component` definition does | ||||||||||||||
| not produce a named entry in a WIT package and cannot be referenced by other | ||||||||||||||
| WIT files via `include` or `use-path`. | ||||||||||||||
| * Tooling that accepts a "world" input (e.g. bindings generators) should | ||||||||||||||
| automatically select the unnamed component when the WIT file contains a | ||||||||||||||
| `component` definition, without requiring the user to specify a world name. | ||||||||||||||
|
|
||||||||||||||
| ### Motivation | ||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we perhaps reframe this section from "Motivation" to "Guidance for when to use a |
||||||||||||||
|
|
||||||||||||||
| The `component` syntax addresses two common sources of confusion for newcomers: | ||||||||||||||
|
|
||||||||||||||
| 1. **Superfluous names.** When defining a custom component that is not intended | ||||||||||||||
| for reuse, the `package` namespace, package name, and `world` name add | ||||||||||||||
| ceremony that must be explained away. | ||||||||||||||
| 2. **Naming.** New users often expect to see the word "component" in a file that | ||||||||||||||
| defines a component. Using `component { … }` rather than `world { … }` for | ||||||||||||||
| this use case aligns with that expectation and reduces the number of concepts | ||||||||||||||
| a tutorial must introduce up front. | ||||||||||||||
|
|
||||||||||||||
| The `world` keyword and `package` declaration remain the right choice when | ||||||||||||||
| defining reusable, published contracts between multiple hosts and components. | ||||||||||||||
| `component` is intended for the single-component, non-published case. | ||||||||||||||
|
|
||||||||||||||
| # Filesystem structure | ||||||||||||||
|
|
||||||||||||||
| WIT supports multiple `package`s and the ability to define a single package | ||||||||||||||
|
|
@@ -832,10 +909,13 @@ happening. | |||||||||||||
| wit_bindgen::generate!("./my.wit"); | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| To be a valid WIT file the file being parsed must have a leading `package ...;` | ||||||||||||||
| statement meaning that it's now the "root package". Dependencies of this package | ||||||||||||||
| must be specified inline with `package ... { ... }` blocks when using this | ||||||||||||||
| format. | ||||||||||||||
| To be a valid WIT file the file being parsed must have either a leading | ||||||||||||||
| `package ...;` statement (meaning that it's now the "root package") or a single | ||||||||||||||
| `component { ... }` definition (see [WIT Components][components-wit]). | ||||||||||||||
| When a `package` statement is present, dependencies of this package must be | ||||||||||||||
| specified inline with `package ... { ... }` blocks when using this format. | ||||||||||||||
| When a `component` definition is present, dependencies on external packages | ||||||||||||||
| are resolved with the same inline `package ... { ... }` blocks. | ||||||||||||||
|
|
||||||||||||||
| Some tooling may support the ability to load multiple "roots" which means that | ||||||||||||||
| the final root is used for `world` selection and the other roots are used to | ||||||||||||||
|
|
@@ -883,6 +963,12 @@ wit/ | |||||||||||||
| Here `types.wit`, `world.wit`, and `my-interface.wit` would all be parsed | ||||||||||||||
| together as a single package. | ||||||||||||||
|
|
||||||||||||||
| Note that `component` files and `package` files cannot be mixed within the same | ||||||||||||||
| directory-based root. All `*.wit` files in a directory must be either package | ||||||||||||||
| files or component files — they cannot be a mixture of both. This mutual | ||||||||||||||
| exclusivity is enforced symmetrically: pushing a package file into a resolver | ||||||||||||||
| that already contains a component (or vice versa) is an error. | ||||||||||||||
|
|
||||||||||||||
| Dependencies in the directory format of the filesystem are specified in a `deps` | ||||||||||||||
| folder within the root folder. Above for example dependencies would be specified | ||||||||||||||
| in `wit/deps`. Dependencies are specified in a flat format where each dependency | ||||||||||||||
|
|
@@ -921,14 +1007,17 @@ package which is duplicated across dependencies must have the same contents. | |||||||||||||
|
|
||||||||||||||
| ## Specifying a World | ||||||||||||||
|
|
||||||||||||||
| The primary unit of bindings generation for WIT tooling is a `world` which means | ||||||||||||||
| that various phases of the process must take a `world` input. For example when | ||||||||||||||
| generating guest bindings within a language you'll be specifying a `world` as | ||||||||||||||
| the unit of bindings generation. WIT tooling should follow these conventions for | ||||||||||||||
| selecting a world: | ||||||||||||||
|
|
||||||||||||||
| * Inputs to world selection are a "root package" (what was parsed from the WIT | ||||||||||||||
| path specified) as well as an optional world string. | ||||||||||||||
| The primary unit of bindings generation for WIT tooling is a `world` (or | ||||||||||||||
| `component`) which means that various phases of the process must take a `world` | ||||||||||||||
| input. For example when generating guest bindings within a language you'll be | ||||||||||||||
| specifying a `world` as the unit of bindings generation. WIT tooling should | ||||||||||||||
| follow these conventions for selecting a world: | ||||||||||||||
|
|
||||||||||||||
| * If the WIT file contains a `component` definition (see | ||||||||||||||
| [WIT Components][components-wit]), that definition is used directly as the | ||||||||||||||
| world for bindings generation. No world string is needed or accepted. | ||||||||||||||
| * Otherwise, inputs to world selection are a "root package" (what was parsed | ||||||||||||||
| from the WIT path specified) as well as an optional world string. | ||||||||||||||
| * If the world string is not present, then the root package must have a single | ||||||||||||||
| world in it. If so that world is used for bindings generation. | ||||||||||||||
| * If the world string is a WIT identifier, then it specifies the name of a world | ||||||||||||||
|
|
@@ -1008,6 +1097,7 @@ keyword ::= 'as' | |||||||||||||
| | 'bool' | ||||||||||||||
| | 'borrow' | ||||||||||||||
| | 'char' | ||||||||||||||
| | 'component' | ||||||||||||||
| | 'constructor' | ||||||||||||||
| | 'enum' | ||||||||||||||
| | 'export' | ||||||||||||||
|
|
@@ -1064,14 +1154,28 @@ readability but this isn't required. | |||||||||||||
| Concretely, the structure of a `wit` file is: | ||||||||||||||
|
|
||||||||||||||
| ```ebnf | ||||||||||||||
| wit-file ::= (package-decl ';')? (package-items | nested-package-definition)* | ||||||||||||||
| wit-file ::= package-file | component-file | ||||||||||||||
|
|
||||||||||||||
| package-file ::= (package-decl ';')? (package-items | nested-package-definition)* | ||||||||||||||
|
|
||||||||||||||
| component-file ::= (toplevel-use-item | nested-package-definition)* component-item | ||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we perhaps allow (at most one, enforced by validation, not grammar) |
||||||||||||||
|
|
||||||||||||||
| nested-package-definition ::= package-decl '{' package-items* '}' | ||||||||||||||
|
|
||||||||||||||
| package-items ::= toplevel-use-item | interface-item | world-item | ||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| Essentially, these top level items are [worlds], [interfaces], [use statements][use] and other package definitions. | ||||||||||||||
| A `wit-file` is **either** a package-based file (`package-file`) **or** a file | ||||||||||||||
| containing a single [`component`][components-wit] definition (`component-file`). | ||||||||||||||
| The two forms are mutually exclusive: a `component` keyword at the top level | ||||||||||||||
| means no `package` declaration or named `world`/`interface` items may appear | ||||||||||||||
| (other than inline `package … { … }` dependency blocks and top-level `use` | ||||||||||||||
| statements which are allowed before the `component`). | ||||||||||||||
|
|
||||||||||||||
| Essentially, `package-file` top level items are [worlds], [interfaces], | ||||||||||||||
| [use statements][use] and other package definitions, while `component-file` | ||||||||||||||
| consists of optional `use` imports and dependency blocks followed by a single | ||||||||||||||
| [`component`][components-wit] definition. | ||||||||||||||
|
|
||||||||||||||
| ### Feature Gates | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -1391,6 +1495,27 @@ to `interface` items. | |||||||||||||
|
|
||||||||||||||
| [`componenttype`]: Explainer.md#type-definitions | ||||||||||||||
|
|
||||||||||||||
| ## Item: `component` | ||||||||||||||
|
|
||||||||||||||
| A `component` definition is an unnamed world that can appear as the sole | ||||||||||||||
| top-level construct in a WIT file (see [WIT Components][components-wit]). Its | ||||||||||||||
| body is identical to a `world` body. | ||||||||||||||
|
|
||||||||||||||
|
Comment on lines
+1500
to
+1503
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
| ```ebnf | ||||||||||||||
| component-item ::= 'component' '{' world-items* '}' | ||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
| ``` | ||||||||||||||
|
|
||||||||||||||
| The `component` keyword must **not** be preceded by a `gate` or followed by an | ||||||||||||||
| identifier — components are always unnamed and ungated. The `world-items` | ||||||||||||||
| production is the same one used by `world-item`, so `import`, `export`, `use`, | ||||||||||||||
| `include`, and type definitions are all permitted inside a `component` body. | ||||||||||||||
|
|
||||||||||||||
| A file containing a `component-item` must not contain a `package` declaration | ||||||||||||||
| (i.e. `package ...;` heading) or any `world-item` or `interface-item` at the | ||||||||||||||
| top level. Inline `package ... { ... }` dependency blocks and `use` statements | ||||||||||||||
| *are* permitted before the `component-item`, as reflected in the | ||||||||||||||
| `component-file` grammar above. | ||||||||||||||
|
|
||||||||||||||
| ## Item: `include` | ||||||||||||||
|
|
||||||||||||||
| A `include` statement enables the union of the current world with another world. The structure of an `include` statement is: | ||||||||||||||
|
|
@@ -2180,4 +2305,4 @@ encoded as: | |||||||||||||
| ) | ||||||||||||||
| ``` | ||||||||||||||
| Thus, `@since` and `@unstable` gates are not part of the runtime semantics of | ||||||||||||||
| components, just part of the source-level tooling for producing components. | ||||||||||||||
| components, just part of the source-level tooling for producing components. | ||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.