packages feed

hydra-0.15.0: src/main/haskell/Hydra/Sources/Wasm/Syntax.hs

-- | A WebAssembly text format (WAT) syntax model.
-- | See https://webassembly.github.io/spec/core/ (retrieved 2026-04-06)

module Hydra.Sources.Wasm.Syntax where

-- Standard imports for type-level sources outside of the kernel
import           Hydra.Kernel
import           Hydra.Dsl.Annotations
import           Hydra.Dsl.Bootstrap
import           Hydra.Dsl.Types                 ((>:))
import qualified Hydra.Dsl.Types                 as T
import qualified Hydra.Sources.Kernel.Types.Core as Core
import qualified Data.List                       as L
import qualified Data.Map                        as M
import qualified Data.Set                        as S
import qualified Data.Maybe                      as Y


ns :: Namespace
ns = Namespace "hydra.wasm.syntax"

define :: String -> Type -> Binding
define = datatype ns

wasm :: String -> Type
wasm = typeref ns

module_ :: Module
module_ = Module {
            moduleNamespace = ns,
            moduleDefinitions = (map toTypeDef definitions),
            moduleTermDependencies = [Core.ns],
            moduleTypeDependencies = [Core.ns],
            moduleDescription = Just ("A WebAssembly text format (WAT) syntax model, based on the WebAssembly spec"
      ++ " (https://webassembly.github.io/spec/core/), retrieved 2026-04-06")}
  where
    definitions = moduleLevel ++ types ++ functions ++ instructions ++ memory
      ++ globals ++ tables ++ imports ++ exports ++ literals

    -- Module-level constructs
    moduleLevel = [
      wasmModule,
      moduleField]

    -- Type system
    types = [
      valType,
      funcType,
      funcTypeRef,
      typeUse,
      typeDef]

    -- Function definitions
    functions = [
      func,
      funcLocal,
      param]

    -- Instructions
    instructions = [
      brTableArgs,
      instruction,
      blockInstruction,
      blockType,
      memArg,
      memoryInstruction,
      ifInstruction,
      numericOp]

    -- Memory
    memory = [
      memoryDef,
      dataSegment,
      dataMode,
      limits]

    -- Globals
    globals = [
      globalDef,
      globalType]

    -- Tables
    tables = [
      tableDef,
      elemActive,
      elemSegment,
      elemMode,
      refType]

    -- Imports and exports
    imports = [
      importDef,
      importDesc,
      importFunc,
      importGlobal,
      importMemory,
      importTable]

    exports = [
      exportDef,
      exportDesc]

    -- Literal values
    literals = [
      constValue]


-- ================================================================================================
-- Module-level constructs
-- ================================================================================================

wasmModule :: Binding
wasmModule = define "Module" $
  doc "A WebAssembly module, the top-level container" $
  T.record [
    "name">:
      doc "Optional module name" $
      T.maybe T.string,
    "fields">:
      doc "The module's fields (types, functions, memories, tables, globals, imports, exports, etc.)" $
      T.list moduleField]

moduleField :: Binding
moduleField = define "ModuleField" $
  doc "A field within a WebAssembly module" $
  T.union [
    "type">:
      doc "A type definition" $
      wasm "TypeDef",
    "func">:
      doc "A function definition" $
      wasm "Func",
    "memory">:
      doc "A memory definition" $
      wasm "MemoryDef",
    "table">:
      doc "A table definition" $
      wasm "TableDef",
    "global">:
      doc "A global variable definition" $
      wasm "GlobalDef",
    "import">:
      doc "An import declaration" $
      wasm "ImportDef",
    "export">:
      doc "An export declaration" $
      wasm "ExportDef",
    "data">:
      doc "A data segment" $
      wasm "DataSegment",
    "elem">:
      doc "An element segment" $
      wasm "ElemSegment",
    "start">:
      doc "A start function index" $
      T.string]


-- ================================================================================================
-- Type system
-- ================================================================================================

valType :: Binding
valType = define "ValType" $
  doc "A WebAssembly value type" $
  T.union [
    "i32">:
      doc "32-bit integer" $
      T.unit,
    "i64">:
      doc "64-bit integer" $
      T.unit,
    "f32">:
      doc "32-bit float" $
      T.unit,
    "f64">:
      doc "64-bit float" $
      T.unit,
    "funcref">:
      doc "Function reference" $
      T.unit,
    "externref">:
      doc "External reference" $
      T.unit]

funcType :: Binding
funcType = define "FuncType" $
  doc "A function type signature" $
  T.record [
    "params">:
      doc "Parameter types" $
      T.list (wasm "ValType"),
    "results">:
      doc "Result types" $
      T.list (wasm "ValType")]

funcTypeRef :: Binding
funcTypeRef = define "FuncTypeRef" $
  doc "A reference to a function type, either by index or inline" $
  T.union [
    "index">:
      doc "Reference by type index" $
      T.string,
    "inline">:
      doc "Inline function type" $
      wasm "FuncType"]

typeUse :: Binding
typeUse = define "TypeUse" $
  doc "A type use clause, referencing a type definition" $
  T.record [
    "index">:
      doc "Optional type index reference" $
      T.maybe T.string,
    "params">:
      doc "Explicit parameter declarations (may augment or override type index)" $
      T.list (wasm "Param"),
    "results">:
      doc "Explicit result types" $
      T.list (wasm "ValType")]

typeDef :: Binding
typeDef = define "TypeDef" $
  doc "A named type definition in the type section" $
  T.record [
    "name">:
      doc "Optional type name" $
      T.maybe T.string,
    "type">:
      doc "The function type" $
      wasm "FuncType"]


-- ================================================================================================
-- Function definitions
-- ================================================================================================

func :: Binding
func = define "Func" $
  doc "A function definition" $
  T.record [
    "name">:
      doc "Optional function name" $
      T.maybe T.string,
    "typeUse">:
      doc "The function's type signature" $
      wasm "TypeUse",
    "locals">:
      doc "Local variable declarations" $
      T.list (wasm "FuncLocal"),
    "body">:
      doc "The function body (a sequence of instructions)" $
      T.list (wasm "Instruction")]

funcLocal :: Binding
funcLocal = define "FuncLocal" $
  doc "A local variable declaration within a function" $
  T.record [
    "name">:
      doc "Optional local name" $
      T.maybe T.string,
    "type">:
      doc "The local's value type" $
      wasm "ValType"]

param :: Binding
param = define "Param" $
  doc "A function parameter declaration" $
  T.record [
    "name">:
      doc "Optional parameter name" $
      T.maybe T.string,
    "type">:
      doc "The parameter's value type" $
      wasm "ValType"]


-- ================================================================================================
-- Instructions
-- ================================================================================================

instruction :: Binding
instruction = define "Instruction" $
  doc "A WebAssembly instruction" $
  T.union [
    "const">:
      doc "Push a constant value" $
      wasm "ConstValue",
    "localGet">:
      doc "Get local variable by name or index" $
      T.string,
    "localSet">:
      doc "Set local variable by name or index" $
      T.string,
    "localTee">:
      doc "Set local variable and keep value on stack" $
      T.string,
    "globalGet">:
      doc "Get global variable by name or index" $
      T.string,
    "globalSet">:
      doc "Set global variable by name or index" $
      T.string,
    "load">:
      doc "Load from memory (e.g., i32.load, f64.load)" $
      wasm "MemoryInstruction",
    "store">:
      doc "Store to memory (e.g., i32.store, f64.store)" $
      wasm "MemoryInstruction",
    "unop">:
      doc "Unary numeric operation (e.g., i32.clz, f64.neg)" $
      wasm "NumericOp",
    "binop">:
      doc "Binary numeric operation (e.g., i32.add, f64.mul)" $
      wasm "NumericOp",
    "testop">:
      doc "Test operation (e.g., i32.eqz)" $
      wasm "NumericOp",
    "relop">:
      doc "Comparison operation (e.g., i32.eq, f64.lt)" $
      wasm "NumericOp",
    "convert">:
      doc "Type conversion (e.g., i32.wrap_i64, f64.convert_i32_s)" $
      T.string,
    "call">:
      doc "Call a function by name or index" $
      T.string,
    "callIndirect">:
      doc "Indirect function call through a table" $
      wasm "TypeUse",
    "block">:
      doc "A block instruction" $
      wasm "BlockInstruction",
    "loop">:
      doc "A loop instruction" $
      wasm "BlockInstruction",
    "if">:
      doc "An if-then-else instruction" $
      wasm "IfInstruction",
    "br">:
      doc "Branch to a label" $
      T.string,
    "brIf">:
      doc "Conditional branch to a label" $
      T.string,
    "brTable">:
      doc "Branch table" $
      wasm "BrTableArgs",
    "return">:
      doc "Return from the current function" $
      T.unit,
    "drop">:
      doc "Drop the top value from the stack" $
      T.unit,
    "select">:
      doc "Select one of two values based on a condition" $
      T.unit,
    "unreachable">:
      doc "Trap immediately" $
      T.unit,
    "nop">:
      doc "No operation" $
      T.unit,
    "memorySize">:
      doc "Get current memory size in pages" $
      T.unit,
    "memoryGrow">:
      doc "Grow memory by a number of pages" $
      T.unit,
    "refNull">:
      doc "Null reference of the given type" $
      wasm "RefType",
    "refIsNull">:
      doc "Test if a reference is null" $
      T.unit,
    "raw">:
      doc "A raw WAT instruction string (escape hatch)" $
      T.string]

brTableArgs :: Binding
brTableArgs = define "BrTableArgs" $
  doc "Arguments for a branch table instruction" $
  T.record [
    "labels">:
      doc "Branch target labels" $
      T.list T.string,
    "default">:
      doc "Default branch target" $
      T.string]

blockInstruction :: Binding
blockInstruction = define "BlockInstruction" $
  doc "A block or loop instruction with a label and body" $
  T.record [
    "label">:
      doc "Optional block label" $
      T.maybe T.string,
    "blockType">:
      doc "The block's result type" $
      wasm "BlockType",
    "body">:
      doc "The instructions in the block" $
      T.list (wasm "Instruction")]

blockType :: Binding
blockType = define "BlockType" $
  doc "The type of a block, loop, or if instruction" $
  T.union [
    "empty">:
      doc "No result" $
      T.unit,
    "value">:
      doc "Single result value type" $
      wasm "ValType",
    "typeUse">:
      doc "Full function type signature" $
      wasm "TypeUse"]

memoryInstruction :: Binding
memoryInstruction = define "MemoryInstruction" $
  doc "A memory load or store instruction" $
  T.record [
    "type">:
      doc "Value type to load or store" $
      wasm "ValType",
    "memArg">:
      doc "Memory argument (offset and alignment)" $
      wasm "MemArg"]

numericOp :: Binding
numericOp = define "NumericOp" $
  doc "A typed numeric operation (used for unop, binop, testop, relop)" $
  T.record [
    "type">:
      doc "Operand value type" $
      wasm "ValType",
    "name">:
      doc "Operation name (e.g., add, sub, mul, eqz, lt_s)" $
      T.string]

memArg :: Binding
memArg = define "MemArg" $
  doc "Memory access arguments" $
  T.record [
    "offset">:
      doc "Memory offset in bytes" $
      T.int32,
    "align">:
      doc "Alignment hint (power of 2)" $
      T.int32]

ifInstruction :: Binding
ifInstruction = define "IfInstruction" $
  doc "An if-then-else instruction" $
  T.record [
    "label">:
      doc "Optional label for the if block" $
      T.maybe T.string,
    "blockType">:
      doc "The result type of the if" $
      wasm "BlockType",
    "then">:
      doc "The 'then' branch instructions" $
      T.list (wasm "Instruction"),
    "else">:
      doc "The 'else' branch instructions (may be empty)" $
      T.list (wasm "Instruction")]


-- ================================================================================================
-- Memory
-- ================================================================================================

memoryDef :: Binding
memoryDef = define "MemoryDef" $
  doc "A memory definition" $
  T.record [
    "name">:
      doc "Optional memory name" $
      T.maybe T.string,
    "limits">:
      doc "Memory size limits (in pages of 64KB)" $
      wasm "Limits"]

dataSegment :: Binding
dataSegment = define "DataSegment" $
  doc "A data segment for initializing memory" $
  T.record [
    "name">:
      doc "Optional data segment name" $
      T.maybe T.string,
    "mode">:
      doc "Whether this is an active or passive data segment" $
      wasm "DataMode",
    "bytes">:
      doc "The data bytes (as a string of escaped bytes)" $
      T.string]

dataMode :: Binding
dataMode = define "DataMode" $
  doc "The mode of a data segment" $
  T.union [
    "active">:
      doc "Active segment: loaded at the given offset expression" $
      T.list (wasm "Instruction"),
    "passive">:
      doc "Passive segment: must be explicitly loaded via memory.init" $
      T.unit]

limits :: Binding
limits = define "Limits" $
  doc "Size limits for memories and tables" $
  T.record [
    "min">:
      doc "Minimum size" $
      T.int32,
    "max">:
      doc "Optional maximum size" $
      T.maybe T.int32]


-- ================================================================================================
-- Globals
-- ================================================================================================

globalDef :: Binding
globalDef = define "GlobalDef" $
  doc "A global variable definition" $
  T.record [
    "name">:
      doc "Optional global name" $
      T.maybe T.string,
    "type">:
      doc "The global's type (value type and mutability)" $
      wasm "GlobalType",
    "init">:
      doc "Initialization expression" $
      T.list (wasm "Instruction")]

globalType :: Binding
globalType = define "GlobalType" $
  doc "A global variable type" $
  T.record [
    "valType">:
      doc "The value type" $
      wasm "ValType",
    "mutable">:
      doc "Whether the global is mutable" $
      T.boolean]


-- ================================================================================================
-- Tables
-- ================================================================================================

tableDef :: Binding
tableDef = define "TableDef" $
  doc "A table definition" $
  T.record [
    "name">:
      doc "Optional table name" $
      T.maybe T.string,
    "refType">:
      doc "The reference type of table elements" $
      wasm "RefType",
    "limits">:
      doc "Table size limits" $
      wasm "Limits"]

elemSegment :: Binding
elemSegment = define "ElemSegment" $
  doc "An element segment for initializing tables" $
  T.record [
    "name">:
      doc "Optional element segment name" $
      T.maybe T.string,
    "mode">:
      doc "Whether this is an active or passive element segment" $
      wasm "ElemMode",
    "type">:
      doc "The reference type of elements" $
      wasm "RefType",
    "init">:
      doc "Initialization expressions (one per element)" $
      T.list (T.list (wasm "Instruction"))]

elemActive :: Binding
elemActive = define "ElemActive" $
  doc "Active element segment parameters" $
  T.record [
    "table">:
      doc "Table index" $
      T.string,
    "offset">:
      doc "Offset expression" $
      T.list (wasm "Instruction")]

elemMode :: Binding
elemMode = define "ElemMode" $
  doc "The mode of an element segment" $
  T.union [
    "active">:
      doc "Active segment: loaded at the given table index and offset" $
      wasm "ElemActive",
    "passive">:
      doc "Passive segment: must be explicitly loaded via table.init" $
      T.unit,
    "declarative">:
      doc "Declarative segment: declares function references for ref.func" $
      T.unit]

refType :: Binding
refType = define "RefType" $
  doc "A reference type for tables and reference instructions" $
  T.union [
    "funcref">:
      doc "Function reference" $
      T.unit,
    "externref">:
      doc "External reference" $
      T.unit]


-- ================================================================================================
-- Imports and exports
-- ================================================================================================

importDef :: Binding
importDef = define "ImportDef" $
  doc "An import declaration" $
  T.record [
    "module">:
      doc "The module name to import from" $
      T.string,
    "name">:
      doc "The name of the imported entity" $
      T.string,
    "desc">:
      doc "The import descriptor (what kind of entity is imported)" $
      wasm "ImportDesc"]

importDesc :: Binding
importDesc = define "ImportDesc" $
  doc "An import descriptor specifying the kind of imported entity" $
  T.union [
    "func">:
      doc "Import a function with the given type" $
      wasm "ImportFunc",
    "memory">:
      doc "Import a memory with the given limits" $
      wasm "ImportMemory",
    "table">:
      doc "Import a table" $
      wasm "ImportTable",
    "global">:
      doc "Import a global variable" $
      wasm "ImportGlobal"]

importFunc :: Binding
importFunc = define "ImportFunc" $
  doc "A function import descriptor" $
  T.record [
    "name">:
      doc "Optional local name for the imported function" $
      T.maybe T.string,
    "typeUse">:
      doc "The function's type" $
      wasm "TypeUse"]

importGlobal :: Binding
importGlobal = define "ImportGlobal" $
  doc "A global import descriptor" $
  T.record [
    "name">:
      doc "Optional local name" $
      T.maybe T.string,
    "type">:
      doc "Global type" $
      wasm "GlobalType"]

importMemory :: Binding
importMemory = define "ImportMemory" $
  doc "A memory import descriptor" $
  T.record [
    "name">:
      doc "Optional local name" $
      T.maybe T.string,
    "limits">:
      doc "Memory limits" $
      wasm "Limits"]

importTable :: Binding
importTable = define "ImportTable" $
  doc "A table import descriptor" $
  T.record [
    "name">:
      doc "Optional local name" $
      T.maybe T.string,
    "refType">:
      doc "Reference type" $
      wasm "RefType",
    "limits">:
      doc "Table limits" $
      wasm "Limits"]

exportDef :: Binding
exportDef = define "ExportDef" $
  doc "An export declaration" $
  T.record [
    "name">:
      doc "The exported name" $
      T.string,
    "desc">:
      doc "What is being exported" $
      wasm "ExportDesc"]

exportDesc :: Binding
exportDesc = define "ExportDesc" $
  doc "An export descriptor identifying what is being exported" $
  T.union [
    "func">:
      doc "Export a function by name or index" $
      T.string,
    "memory">:
      doc "Export a memory by name or index" $
      T.string,
    "table">:
      doc "Export a table by name or index" $
      T.string,
    "global">:
      doc "Export a global by name or index" $
      T.string]


-- ================================================================================================
-- Literal / constant values
-- ================================================================================================

constValue :: Binding
constValue = define "ConstValue" $
  doc "A constant value instruction" $
  T.union [
    "i32">:
      doc "A 32-bit integer constant" $
      T.int32,
    "i64">:
      doc "A 64-bit integer constant" $
      T.int64,
    "f32">:
      doc "A 32-bit float constant" $
      T.float32,
    "f64">:
      doc "A 64-bit float constant" $
      T.float64]