# `rib-lang`
`rib-lang` implements **Rib**: a compact expression language aligned with the [WebAssembly Component Model](https://component-model.bytecodealliance.org/) and **WIT**-shaped types, with value text compatible with **[Wasm Wave](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasm-wave)** where applicable. The crate provides the full pipeline—**parse**, **type inference**, **checking against embedder-supplied export metadata**, **compile**, and **interpret**—so component hosts can offer typed scripting without maintaining a parallel type system.
**Language guide:** [golemcloud.github.io/rib/guide.html](https://golemcloud.github.io/rib/guide.html) — `instance()`, export calls, `match`, resources, comprehensions, and the teaching [`example.wit`](https://github.com/golemcloud/rib/blob/main/docs/example.wit).
**Familiarity** — Rib’s **syntax is deliberately Rust-like** (`let`, `match`, blocks, calls, records, string syntax). Authors comfortable with Rust typically write well-formed Rib quickly. **Runtime literals** (records, lists, scalars, `option`, `result`, etc.) follow **Wasm Wave** text rules, so experience with the Wasm **component / WIT / Wave** stack carries over directly.
---
## Audience
- **Wasm-time and other runtime maintainers** integrating a typed shell, diagnostics command, or test harness on top of `wasmtime::component::…` (or equivalent): analysed types and `Val` / resource tables already exist in the embedding; Rib centralises turning **user-authored text** into those calls with **static checking** first.
- **Tooling authors** standardising on **Wave-shaped literals** for records, variants, lists, and `option` / `result` instead of per-product JSON or ad-hoc parsers.
- **Rust embeddings that already load components** via `wasmtime::component` (or similar): Rib occupies the space between hand-written marshalling for every scenario and embedding a general-purpose scripting runtime.
---
## Design constraints
1. **Types mirror WIT** — Scalars (`s32`, `string`, …), `list<T>`, `option<T>`, `result`, records, variant `match`, and qualified **export paths** in the grammar.
2. **Wave integration** — `WitType` implements `wasm_wave::wasm::WasmType`; `ValueAndType` implements `WasmValue`. Parsing and printing use **`wasm-wave`**, consistent with other Bytecode Alliance tooling. Resource **handles** are not treated as arbitrary Wave-printable values; the API documents that boundary.
3. **Pluggable invocation** — On `call`, the interpreter defers to **`RibComponentFunctionInvoke`** (see `interpreter`). The crate does not assume Wasmtime; it requires analysed exports and an embedder capable of performing the call.
---
## Subsystem overview
| Subsystem | Role |
|-----------|------|
| **Parser** | Rib source → AST |
| **Type inference & checker** | Programs checked against the embedder’s registry / `WitExport` view |
| **Compiler** | Lowers to **Rib IR** consumed by the interpreter |
| **Interpreter** | Evaluation and invocation dispatch |
| **`wit_type`** | Structured representation of WIT-level types and exports |
| **`wave` / `wasm_wave_text`** | Wave bridge for `ValueAndType` |
| **`registry`** | Export and dependency metadata supplied to the compiler |
The semver-sensitive public API is defined by `rib-lang/src/lib.rs` (re-exports and modules).
---
## Embedding workflow
1. Obtain **analysed interface** data from the host stack (`wasmtime::component::…`, `wit-component`, etc.) and map it into Rib’s **`WitExport`** / **`WitType`** representation.
2. Construct a **registry** (and any instance or worker metadata required by the deployment model).
3. Run **parse → infer → check → compile**, then execute with an implementation of **`RibComponentFunctionInvoke`** that performs the actual cross-boundary call.
The **`rib-repl`** crate in this repository consumes the same pipeline for interactive input; CLIs and tests can call `rib-lang` directly without the REPL.
---
## Advanced usage (beyond the REPL)
Most people meet Rib in a **REPL**; **`rib-lang`** is also for **embedding** in your own Rust binary. There, Rib helps when components grow **many exports**, when you **revise WIT or ship new component versions often**, or when you want **short, typed programs** plugged into the **output** of component calls—post-process, reshape, validate—without hand-writing and re-hand-writing the same glue in Rust (or JSON shims) for every export and every shape change. You wire **analysed exports** into the registry once; Rib text is **checked against that surface** on each compile, so updating the component tends to surface mistakes in the script **before** a bad call reaches Wasm.
**Golem Cloud** used that **direct `rib-lang` embedding** pattern in production for **more than a year** (Rib in the path where worker/API behaviour met component definitions and return data). The product has **since shifted** so typical **end users** rely **less on raw WIT details** in day-to-day flows—but the embedding model is still the right tool for **advanced** hosts, gateways, tests, and automation that want a **small, statically checked** layer on top of components.
---
## Further reading
- [Rib language guide](https://golemcloud.github.io/rib/guide.html) — usage, examples, resources, `for` / `reduce`
- [WebAssembly Component Model — introduction](https://component-model.bytecodealliance.org/)
- [WIT](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md)
- [Wasm Wave](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasm-wave)
- Repository overview: [README.md](../README.md)
- REPL built on this crate: [rib-repl/README.md](../rib-repl/README.md)
---
## Formal grammar
The language syntax in EBNF-style form:
```
letter ::= ? Unicode letter ? ;
digit ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
ident_start ::= letter ;
ident_cont ::= letter | digit | "_" | "-" ;
IDENT ::= ident_start ident_cont* ;
(* trivia: whitespace; // /// line comments; /* */ /** */ block comments *)
program ::= rib_block ;
rib_block ::= rib_expr ( ";" rib_expr )* ;
rib_expr ::= simple_expr ( ":" type_name )? rib_suffix* ;
rib_suffix ::= index_suffix ( "." segment_suffix )* ( range_suffix )? ( bin_suffix )* ;
index_suffix ::= ( "[" rib_expr "]" )* ;
segment_suffix ::= ( simple_expr index_suffix ( ":" type_name )? )
| fraction_suffix ;
fraction_suffix ::= digit+ ( ( "e" | "E" ) ( "+" | "-" )? digit+ )? ;
range_suffix ::= ( ".." | "..=" ) simple_expr? index_suffix ( ":" type_name )? ;
bin_suffix ::= binary_op rib_expr index_suffix ;
binary_op ::= ">=" | "<=" | "==" | "<" | ">" | "&&" | "||" | "+" | "-" | "*" | "/" ;
simple_expr ::= list_comprehension
| list_aggregation
| pattern_match
| let_binding
| conditional
| multi_line_block
| flag_expr
| record_expr
| tuple_expr
| boolean_literal
| string_literal
| not_expr
| option_expr
| result_expr
| call_expr
| sequence_expr
| identifier_expr
| integer_literal ;
let_binding ::= "let" IDENT ( ":" type_name )? "=" rib_expr ;
conditional ::= "if" rib_expr "then" rib_expr "else" rib_expr ;
pattern_match ::= "match" rib_expr "{" match_arm ( "," match_arm )* "}" ;
match_arm ::= arm_pattern "=>" rib_expr ;
arm_pattern ::= ctor_pattern
| tuple_pat
| list_pat
| record_pat
| "_"
| pattern_alias
| arm_literal ;
pattern_alias ::= IDENT "@" arm_pattern ;
ctor_pattern ::= "none"
| ctor_name "(" ( arm_pattern ( "," arm_pattern )* )? ")" ;
ctor_name ::= ( letter | digit | "_" | "-" )+ ;
tuple_pat ::= "(" ( arm_pattern ( "," arm_pattern )* )? ")" ;
list_pat ::= "[" ( arm_pattern ( "," arm_pattern )* )? "]" ;
record_pat ::= "{" key_pat ( "," key_pat )+ "}" ;
key_pat ::= record_key ":" arm_pattern ;
record_key ::= letter ( letter | "_" | "-" )* ;
arm_literal ::= rib_expr ;
call_expr ::= function_name "(" ( rib_expr ( "," rib_expr )* )? ")" ;
function_name ::= IDENT
| interface_path ;
interface_path ::= ( ns_pkg "/" )? interface_name ( "@" semver )? "." "{" inner_function "}" ;
ns_pkg ::= ident_segment ":" ident_segment ;
ident_segment ::= ident_piece+ ;
ident_piece ::= ( letter | digit | "-" )+ ;
interface_name ::= ident_piece+ ;
semver ::= ? text until ".{" ? ;
inner_function ::= raw_ctor | raw_drop | raw_method | raw_static | plain_fn ;
raw_ctor ::= IDENT "." "new" | "[constructor]" IDENT ;
raw_drop ::= IDENT "." "drop" | "[drop]" IDENT ;
raw_method ::= IDENT "." IDENT | "[method]" IDENT "." IDENT ;
raw_static ::= "[static]" IDENT "." IDENT ;
plain_fn ::= IDENT ;
not_expr ::= "!" rib_expr ;
option_expr ::= "some" "(" rib_expr ")" | "none" ;
result_expr ::= "ok" "(" rib_expr ")" | "err" "(" rib_expr ")" ;
tuple_expr ::= "(" ( rib_expr ( "," rib_expr )* )? ")" ;
sequence_expr ::= "[" ( rib_expr ( "," rib_expr )* )? "]" ;
record_expr ::= "{" field ( "," field )+ "}" ;
field ::= field_key ":" rib_expr ;
field_key ::= letter ( letter | digit | "_" | "-" )* ;
flag_expr ::= "{" ( flag_name ( "," flag_name )* )? "}" ;
flag_name ::= ( letter | "_" | digit | "-" )+ ;
boolean_literal ::= "true" | "false" ;
string_literal ::= "\"" string_char* "\"" ;
string_char ::= ? any except "\" \"$" ? | escape | interpolation ;
escape ::= "\\" ( "n" | "t" | "r" | "\\" | "\"" | "$" | "{" | "}" | ?any? ) ;
interpolation ::= "${" rib_block "}" ;
integer_literal ::= "-"? digit+ ;
multi_line_block ::= "{" rib_block "}" ;
list_comprehension ::= "for" IDENT "in" rib_expr "{"
block_body?
"yield" rib_expr ";"
"}" ;
list_aggregation ::= "reduce" IDENT "," IDENT "in" rib_expr "from" rib_expr "{"
block_body?
"yield" rib_expr ";"
"}" ;
block_body ::= ( rib_expr ";" )* ;
identifier_expr ::= IDENT ;
type_name ::= basic_type | list_type | tuple_type | option_type | result_type ;
basic_type ::= "bool" | "s8" | "u8" | "s16" | "u16" | "s32" | "u32"
| "s64" | "u64" | "f32" | "f64" | "char" | "string" ;
list_type ::= "list" "<" type_name ">" ;
tuple_type ::= "tuple" "<" type_name ( "," type_name )* ">" ;
option_type ::= "option" "<" type_name ">" ;
result_type ::= "result"
| "result" "<" ( "_" | type_name ) ( "," type_name )? ">" ;
```