marianoguerra/wax/ast does not have a README file

    Attributes

    type Attributes = Array[Attribute]

    An Array is a collection of values that supports random access and can grow in size.

    Body

    A braced block of instructions together with the span of its braces.

    FieldType

    A type paired with its mutability. Used for both globals and struct fields, which is why it is generic in the thing being made mutable.

    HeapType

    A heap type: what a reference can point at.

    The No* cases are the bottom types of each hierarchy (nofunc is a subtype of every function type and has no values), and None_ is the bottom of the internal hierarchy. Type names a concrete defined type; Exact names one and forbids subtypes, which is the custom-descriptors proposal's &!t.

    Module

    A module: a list of spanned fields.

    There is no wrapper node -- a Wax file IS its field list.

    RecType

    A recursion group: types that may refer to each other.

    A single type t = ... is a one-member group, so there is no separate non-recursive form.

    RefType

    A reference type: a heap type plus whether null is allowed.

    StorageType

    What a struct field or array element stores.

    ValType

    The Wax instantiations of the shared type family.

    Wax refers to types by NAME, so every one of these is at Idx = Ident. Naming them here keeps [Ident] out of the ~90 AST constructors, and means the binary form's instantiation (at Idx = Int, for the code generator) will sit beside these rather than replace them.

    Note Limits is deliberately absent: the Wax AST stores memory and table bounds inline as (UInt64, UInt64?)?, exactly as the reference does, and the Limits record in wasm_types belongs to the binary form.

    BinOp

    Binary operators.

    Signedness is part of the operator rather than inferred, because WebAssembly has genuinely different instructions for signed and unsigned division, remainder, shift and comparison. It is optional on the operators where floats also apply (/, <, >, <=, >=): None means the spelling did not commit, and the typer resolves it from the operand type.

    CastType

    pub(all) enum CastType {
    Value(
    ValType
    [Ident])
    Func(nullable~ : Bool, sign~ : FuncType)
    Signed(typ~ : NumType, signage~ :
    Signage
    , strict~ : Bool)
    } derive(Eq,
    Debug
    )

    What an as cast targets.

    Catch

    pub(all) enum Catch {
    Catch(Ident, Ident)
    CatchRef(Ident, Ident)
    CatchAll(Ident)
    CatchAllRef(Ident)
    } derive(Eq,
    Debug
    )

    A try_table catch clause.

    The Ref variants deliver the exception reference itself alongside the payload, so a handler can rethrow.

    DataElem

    One element of a data segment's contents.

    Holds no instructions: every value is a literal. In a run the element type is stated once and the values are RAW literal strings, packed little-endian -- raw for the same round-tripping reason as numeric literals.

    DataMode

    pub(all) enum DataMode[Info] {
    Passive
    Active(Ident, Instr[Info])
    } derive(Eq,
    Debug
    )

    Whether a data segment is copied in at instantiation or only on demand.

    ElemMode

    pub(all) enum ElemMode[Info] {
    EPassive
    EActive(Ident, Instr[Info])
    } derive(Eq,
    Debug
    )

    FuncType

    A function signature.

    Parameters may be anonymous (fn(i32)) or named (fn(x: i32)), hence the optional name; results never are.

    FuncType::to_json

    fn FuncType::to_json(self : FuncType) -> Json

    Hint

    pub(all) struct Hint[T] {
    value : T
    loc :
    Location

    } derive(Eq,
    Debug
    )

    Advisory metadata.code.* metadata attached to an instruction.

    Written in Wax as an attribute prefixing the instruction (#[likely], #[freq = 16], #[targets(f: 0.73)]). These never affect behaviour: an engine may ignore them, and dropping one changes performance, never semantics.

    A hint carries the span of the attribute it was written as, so a diagnostic about a malformed hint blames the hint rather than the instruction it decorates.

    Hints

    pub(all) struct Hints {
    branch : Hint[Bool]?
    freq : Hint[Int]?
    targets : Hint[Array[(Ident, Int)]]?
    } derive(Eq,
    Debug
    )

    Hints::is_empty

    fn Hints::is_empty(self : Hints) -> Bool

    Ident

    #alias(Label)
    pub(all) struct Ident {
    name : String
    loc :
    Location

    } derive(Eq,
    Debug
    )

    An identifier with its span.

    This is the Idx the Wax AST instantiates the type family at. The reference spells it (string, location) annotated; a named struct is the same shape with readable field names.

    Ident::to_json

    fn Ident::to_json(self : Ident) -> Json

    ImportDecl

    pub(all) struct ImportDecl {
    id : Ident
    kind : ImportKind
    attributes : Array[Attribute]
    } derive(Eq,
    Debug
    )

    A single imported entity.

    It is imported under id unless a name-only #[import = "name"] attribute overrides it. attributes also carries e.g. #[export] to re-export it.

    ImportKind

    pub(all) enum ImportKind {
    Func(typ~ : Ident?, sign~ : FuncType?, exact~ : Bool)
    Global(mut_~ : Bool, typ~ :
    ValType
    [Ident])
    Tag(typ~ : Ident?, sign~ : FuncType?)
    Memory(address_type~ :
    AddressType
    , limits~ : (UInt64, UInt64?)?, page_size_log2~ : Int?, shared~ : Bool)
    Table(address_type~ :
    AddressType
    , reftype~ :
    RefType
    [Ident], limits~ : (UInt64, UInt64?)?)
    } derive(Eq,
    Debug
    )

    What an import "module" { ... } entry brings in.

    Imports have no body, so these carry only type-level information.

    Instr

    pub(all) struct Instr[Info] {
    desc : InstrDesc[Info]
    info : Info
    hints : Hints
    expected :
    ValType
    [Ident]?
    } derive(Eq,
    Debug
    )

    An instruction node.

    hints and expected are fields rather than wrapper nodes so the pervasive matches on desc neither see them nor have to see through them.

    Instr::iter_instr

    fn[I] Instr::iter_instr(self : Instr[I], f : (Instr[I]) -> Unit) -> Unit

    Apply f to this instruction and, recursively, to everything within it.

    Unlike map_info, f sees the whole node, so it can look at the desc.

    Instr::map_info

    fn[A, B] Instr::map_info(self : Instr[A], f : (A) -> B) -> Instr[B]

    Re-annotate every node, keeping the shape.

    The type checker's move: the parser builds Instr[Location] and typing rebuilds the same tree carrying inferred types.

    Instr::sub_instrs

    fn[I] Instr::sub_instrs(self : Instr[I]) -> Array[Instr[I]]

    The instructions immediately nested within this one, in no particular order.

    Instr::to_json

    fn[Info] Instr::to_json(self : Instr[Info]) -> Json

    The instruction tree.

    Deliberately shallow in places -- a node's own kind and its children, not every field -- because these snapshots exist to catch a wrong SHAPE, and a full dump would churn on every unrelated change.

    InstrDesc

    pub(all) enum InstrDesc[Info] {
    Block(label~ : Ident?, typ~ : FuncType, block~ :
    Annotated
    [Array[Instr[Info]],
    Location
    ])
    Loop(label~ : Ident?, typ~ : FuncType, block~ :
    Annotated
    [Array[Instr[Info]],
    Location
    ])
    While(label~ : Ident?, cond~ : Instr[Info], step~ : Instr[Info]?, block~ :
    Annotated
    [Array[Instr[Info]],
    Location
    ])
    If(label~ : Ident?, typ~ : FuncType, cond~ : Instr[Info], if_block~ :
    Annotated
    [Array[Instr[Info]],
    Location
    ], else_block~ :
    Annotated
    [Array[Instr[Info]],
    Location
    ]?)
    TryTable(label~ : Ident?, typ~ : FuncType, catches~ : Array[Catch], block~ :
    Annotated
    [Array[Instr[Info]],
    Location
    ])
    Try(label~ : Ident?, typ~ : FuncType, block~ :
    Annotated
    [Array[Instr[Info]],
    Location
    ], catches~ : Array[(Ident,
    Annotated
    [Array[Instr[Info]],
    Location
    ])], catch_all~ :
    Annotated
    [Array[Instr[Info]],
    Location
    ]?)
    TryCatch(label~ : Ident?, typ~ : FuncType, block~ :
    Annotated
    [Array[Instr[Info]],
    Location
    ], arms~ : Array[TryCatchArm[Info]])
    Unreachable
    Nop
    Hole
    Null
    Get(Ident)
    Path(Ident, Ident)
    Set(Ident,
    Annotated
    [BinOp,
    Location
    ]?, Instr[Info])
    Tee(Ident, Instr[Info])
    Call(Instr[Info], Array[Instr[Info]])
    TailCall(Instr[Info], Array[Instr[Info]])
    Labelled(Ident, Instr[Info])
    Char(Char)
    Str(Ident?, Bytes)
    Int(String)
    Float(String)
    Cast(Instr[Info], CastType)
    CastDesc(Instr[Info], Bool, Instr[Info])
    Test(Instr[Info],
    RefType
    [Ident])
    NonNull(Instr[Info])
    Struct(Ident?, Array[(Ident, Instr[Info]?)])
    StructDefault(Ident?)
    StructDesc(Instr[Info], Array[(Ident, Instr[Info]?)])
    StructDefaultDesc(Instr[Info])
    StructGet(Instr[Info], Ident)
    GetDescriptor(Instr[Info])
    StructSet(Instr[Info], Ident, Instr[Info])
    Array(Ident?, Instr[Info], Instr[Info])
    ArrayDefault(Ident?, Instr[Info])
    ArrayFixed(Ident?, Array[Instr[Info]])
    ArraySegment(Ident?, Ident, Instr[Info], Instr[Info])
    ArrayGet(Instr[Info], Instr[Info])
    ArraySet(Instr[Info], Instr[Info], Instr[Info])
    BinOpI(
    Annotated
    [BinOp,
    Location
    ], Instr[Info], Instr[Info])
    UnOpI(
    Annotated
    [UnOp,
    Location
    ], Instr[Info])
    Let(Array[(Ident?,
    ValType
    [Ident]?)], Instr[Info]?)
    Br(Ident, Instr[Info]?)
    BrIf(Ident, Instr[Info])
    BrTable(Array[Ident], Instr[Info])
    Dispatch(index~ : Instr[Info], cases~ : Array[Ident], default~ : Ident, arms~ : Array[(Ident,
    Annotated
    [Array[Instr[Info]],
    Location
    ])])
    Match(scrutinee~ : Instr[Info], arms~ : Array[(MatchPattern,
    Annotated
    [Array[Instr[Info]],
    Location
    ])], default~ :
    Annotated
    [Array[Instr[Info]],
    Location
    ])
    BrOnNull(Ident, Instr[Info])
    BrOnNonNull(Ident, Instr[Info])
    BrOnCast(Ident,
    RefType
    [Ident], Instr[Info])
    BrOnCastFail(Ident,
    RefType
    [Ident], Instr[Info])
    BrOnCastDescEq(Ident, Bool, Instr[Info], Instr[Info])
    BrOnCastDescEqFail(Ident, Bool, Instr[Info], Instr[Info])
    Throw(Ident, Array[Instr[Info]])
    ThrowRef(Instr[Info])
    ContNew(Ident, Instr[Info])
    ContBind(Ident, Ident, Array[Instr[Info]])
    Suspend(Ident, Array[Instr[Info]])
    Resume(Ident, Array[OnClause], Array[Instr[Info]])
    ResumeThrow(Ident, Ident, Array[OnClause], Array[Instr[Info]])
    ResumeThrowRef(Ident, Array[OnClause], Array[Instr[Info]])
    Switch(Ident, Ident, Array[Instr[Info]])
    On(Instr[Info], Array[OnClause])
    Return(Instr[Info]?)
    Sequence(Array[Instr[Info]])
    Select(Instr[Info], Instr[Info], Instr[Info])
    IfAnnotation(cond~ :
    Cond
    , then_body~ :
    Annotated
    [Array[Instr[Info]],
    Location
    ], else_body~ :
    Annotated
    [Array[Instr[Info]],
    Location
    ]?)
    } derive(Eq,
    Debug
    )

    A statement or expression. Wax is expression-oriented, so there is no separate statement type.

    InstrDesc::map_desc

    fn[A, B] InstrDesc::map_desc(self : InstrDesc[A], instr~ : (Instr[A]) -> Instr[B], block~ : (Array[Instr[A]]) -> Array[Instr[B]]) -> InstrDesc[B]

    Rebuild a desc, rewriting every nested operand and every nested body.

    The one exhaustive match over InstrDesc. Changing the type breaks this and nothing else, which is the point.

    InstrDesc::to_json

    fn[Info] InstrDesc::to_json(self : InstrDesc[Info]) -> Json

    MatchPattern

    pub(all) enum MatchPattern {
    MatchCast(Ident?,
    RefType
    [Ident])
    MatchNull
    } derive(Eq,
    Debug
    )

    A match arm pattern: an optionally-bound reference-type test, or a null test.

    MemData

    pub(all) struct MemData[Info] {
    data_name : Ident?
    offset : Instr[Info]
    init : Array[DataElem]
    } derive(Eq,
    Debug
    )

    A data segment attached to a memory definition.

    ModuleField

    pub(all) enum ModuleField[Info] {
    Type(Array[
    Annotated
    [(Ident, SubType),
    Location
    ]])
    Func(name~ : Ident, typ~ : Ident?, sign~ : FuncType?, body~ : (Ident?, Array[Instr[Info]]), attributes~ : Array[Attribute])
    Global(name~ : Ident, mut_~ : Bool, typ~ :
    ValType
    [Ident]?, def~ : Instr[Info], attributes~ : Array[Attribute])
    Tag(name~ : Ident, typ~ : Ident?, sign~ : FuncType?, attributes~ : Array[Attribute])
    Memory(name~ : Ident, address_type~ :
    AddressType
    , limits~ : (UInt64, UInt64?)?, page_size_log2~ : Int?, shared~ : Bool, data~ : Array[MemData[Info]], attributes~ : Array[Attribute])
    Data(name~ : Ident?, mode~ : DataMode[Info], init~ : Array[DataElem], attributes~ : Array[Attribute])
    Table(name~ : Ident, address_type~ :
    AddressType
    , reftype~ :
    RefType
    [Ident], limits~ : (UInt64, UInt64?)?, init~ : Instr[Info]?, attributes~ : Array[Attribute])
    Elem(name~ : Ident, reftype~ :
    RefType
    [Ident], mode~ : ElemMode[Info], init~ : Array[Instr[Info]], attributes~ : Array[Attribute])
    Import(module_~ :
    Annotated
    [Bytes,
    Location
    ], decl~ :
    Annotated
    [ImportDecl,
    Location
    ])
    ImportGroup(module_~ :
    Annotated
    [Bytes,
    Location
    ], decls~ : Array[
    Annotated
    [ImportDecl,
    Location
    ]])
    ModuleAnnotation(Array[Attribute])
    Conditional(cond~ :
    Cond
    , then_fields~ :
    Annotated
    [Array[
    Annotated
    [ModuleField[Info],
    Location
    ]],
    Location
    ], else_fields~ :
    Annotated
    [Array[
    Annotated
    [ModuleField[Info],
    Location
    ]],
    Location
    ]?)
    } derive(Eq,
    Debug
    )

    A top-level definition.

    ModuleField::to_json

    fn[Info] ModuleField::to_json(self : ModuleField[Info]) -> Json

    NumType

    pub(all) enum NumType {
    I32
    I64
    F32
    F64
    } derive(Eq,
    Debug
    )

    The four numeric types, where a construct admits only those.

    NumType::to_str

    fn NumType::to_str(self : NumType) -> String

    OnClause

    pub(all) enum OnClause {
    OnLabel(Ident, Ident)
    OnSwitch(Ident)
    } derive(Eq,
    Debug
    )

    A stack-switching handler clause, e on [tag -> 'label].

    SubType

    pub(all) struct SubType {
    typ : CompType
    supertype : Ident?
    final_ : Bool
    descriptor : Ident?
    describes : Ident?
    } derive(Eq,
    Debug
    )

    One member of a recursion group.

    descriptor/describes are the custom-descriptors proposal's pairing; both are present because the relation is navigable in each direction.

    TryCatchArm

    One arm of a structured try ... catch.

    UnOp

    pub(all) enum UnOp {
    Neg
    Pos
    Not
    } derive(Eq,
    Debug
    )

    Unary operators.

    format_signed_type

    fn format_signed_type(typ : NumType, signage :
    Signage
    , strict : Bool) -> String

    Render a signed numeric cast type, e.g. i32_s or i64_u_strict.

    heaptype_json

    import_name

    The name an imported entity is bound to in wasm.

    The name-only #[import = "name"] override if there is one, else the Wax name.

    BYTES, where the reference has a string: a wasm import name is a byte string, and this port keeps a string literal as the bytes the lexer decoded rather than re-encoding at every use. The Wax name is encoded here, which is lossless -- an identifier is UTF-8 by construction.

    lower_dispatch

    fn[I] lower_dispatch(block_info : I, index~ : Instr[I], cases~ : Array[Ident], default~ : Ident, arms~ : Array[(Ident,
    Annotated
    [Array[Instr[I]],
    Location
    ])]) -> Array[Instr[I]]

    Lower a dispatch to a br_table inside nested case blocks.

    One void block per case, the br_table innermost, each case body just after its own block. Branching to case c exits c's block, runs c's body, and falls through into the enclosing cases -- so the arms are listed in fall-through order, which is the REVERSE of the block nesting. The last arm is therefore outermost and its body trails the whole structure, which is why this returns a list rather than one instruction.

    lower_match

    Lower a match to the nested type-test ladder.

    The scrutinee is evaluated ONCE and threaded through a chain of br_on_cast (or br_on_null for a null arm) in the innermost block: each test, on success, branches out to its arm's block carrying the narrowed value; on failure it leaves the progressively narrowed value for the next test. The first arm is innermost, so arm i's body sits in arm i + 1's block and the last arm's body sits in the escape block. After every test fails the innermost block drops the value and branches to escape, past all the arm bodies, and the default follows the (void) escape block as trailing code.

    labels supplies n + 1 fresh labels: one per arm in order, then escape.

    lower_trycatch

    fn[I] lower_trycatch(block_info : I, join~ : Ident, arm_labels~ : Array[Ident], typ~ : FuncType, block~ :
    Annotated
    [Array[Instr[I]],
    Location
    ], arms~ : Array[TryCatchArm[I]]) -> Instr[I]

    Lower a structured try/catch to try_table plus a block ladder.

    One block per arm (the first innermost) inside the join block, the try_table innermost with one catch clause per arm, and each arm body as trailing code just after its block -- so an arm's completion falls into the next arm and the last arm's falls out of the join as the try's value. The body's normal completion escapes past all arms with the one implicit branch to join, carrying the value.

    arm_labels supplies one fresh label per arm; join is the try's own label when it has one. Each arm block's result type is the arm's entry stack (arm_types, which the typer fills).

    lower_while

    fn[I] lower_while(block_info : I, fresh_loop~ : Ident, label~ : Ident?, cond~ : Instr[I], step~ : Instr[I]?, block~ : Array[Instr[I]]) -> Array[Instr[I]]

    Lower a leading-test while C { B } to 'L: loop { if C { B; br 'L; } }.

    A continue-expression step has to run at the end of EVERY iteration, including one reached by continue (a branch to the loop label). When the loop is labelled -- so a continue can target it -- the body is wrapped in a block carrying the user's label, and the back-edge uses fresh_loop: br 'L then exits that block, runs the step, and takes the back-edge. An unlabelled stepped loop cannot be continued, so the step is simply appended to the body.

    module_to_json

    The whole module.

    no_hints

    let no_hints : Hints

    An instruction with no hints, which is what everything carries unless an attribute says otherwise.

    no_loc_instr

    A synthesized instruction: no source span, no hints.

    splice_field_name

    let splice_field_name : String

    The field name marking a .. splice at the head of a struct definition, which inherits the supertype's fields.

    .. is not a valid identifier, so it can never collide with a real field. The typer replaces it with the supertype's fields and the printer renders it back as ..; its field TYPE is a placeholder and is never inspected.

    synthetic_loop_label

    let synthetic_loop_label : String

    The label of the loop a label-less while lowers to.

    # is not a Wax identifier character, so this can never clash with a source label nor be the target of a user br. It only ever labels the lowering the type checker discards -- conversion to wasm picks a readable name instead, and that is what reaches emitted wat.

    valtype_json