diff --git a/cspell.json b/cspell.json
index 4acf371999..ce76e68d9a 100644
--- a/cspell.json
+++ b/cspell.json
@@ -53,6 +53,7 @@
     "src/test/exit-codes/contracts/compute-phase-errors.tact",
     "src/test/e2e-slow/map-property-tests/map-properties-key-value-types.ts",
     "src/test/e2e-slow/map-property-tests/build",
+    "src/test/fuzzer/src/minimal-fc-stdlib/stdlib.fc",
     "/docs",
     "src/benchmarks/contracts/func/notcoin/stdlib-custom.fc",
     "src/benchmarks/contracts/func/notcoin/gas.fc",
diff --git a/package.json b/package.json
index 33ee33ee41..5cf49313f3 100644
--- a/package.json
+++ b/package.json
@@ -35,6 +35,7 @@
     "gen:contracts:test:map": "ts-node ./src/test/e2e-slow/map-property-tests/generate.ts",
     "gen:contracts:all": "yarn gen:contracts:fast && yarn gen:contracts:test:map",
     "gen": "yarn gen:grammar && yarn gen:stdlib && yarn gen:func-js && yarn gen:contracts:all",
+    "fuzz:expressions:stats": "ts-node src/test/fuzzer/test/expression-stats.ts",
     "clean": "rm -fr dist",
     "cleanall": "rm -fr dist node_modules",
     "copy:stdlib": "ts-node src/stdlib/copy.build.ts",
diff --git a/spell/cspell-list.txt b/spell/cspell-list.txt
index efb4f61f75..85fad4c8a4 100644
--- a/spell/cspell-list.txt
+++ b/spell/cspell-list.txt
@@ -119,6 +119,7 @@ mintable
 misparse
 misparsed
 mktemp
+mult
 multiformats
 nanoton
 nanotons
@@ -143,6 +144,7 @@ pinst
 POSIX
 postpack
 prando
+preorder
 quadtree
 quadtrees
 RANDU
diff --git a/src/func/funcCompile.ts b/src/func/funcCompile.ts
index 56801437bd..bd357e25d5 100644
--- a/src/func/funcCompile.ts
+++ b/src/func/funcCompile.ts
@@ -1,4 +1,5 @@
 import type { ILogger } from "@/context/logger";
+import { execSync } from "child_process";
 
 // Wasm Imports
 // eslint-disable-next-line @typescript-eslint/no-require-imports
@@ -60,6 +61,71 @@ export async function funcCompile(args: {
     entries: string[];
     sources: { path: string; content: string }[];
     logger: ILogger;
+}): Promise<FuncCompilationResult> {
+    const USE_NATIVE = process.env.USE_NATIVE === "true";
+    if (USE_NATIVE) {
+        return funcCompileNative(args);
+    }
+    return funcCompileWasm(args);
+}
+
+export function funcCompileNative(args: {
+    entries: string[];
+    sources: { path: string; content: string }[];
+    logger: ILogger;
+}): FuncCompilationResult {
+    const FC_STDLIB_PATH = process.env.FC_STDLIB_PATH;
+    if (typeof FC_STDLIB_PATH === "undefined") {
+        throw new Error("FC_STDLIB_PATH is not set");
+    }
+    const FIFT_LIBS_PATH = process.env.FIFT_LIBS_PATH;
+    if (typeof FIFT_LIBS_PATH === "undefined") {
+        throw new Error("FIFT_LIBS_PATH is not set");
+    }
+    const FUNC_FIFT_COMPILER_PATH = process.env.FUNC_FIFT_COMPILER_PATH;
+    if (typeof FUNC_FIFT_COMPILER_PATH === "undefined") {
+        throw new Error("FUNC_FIFT_COMPILER_PATH is not set");
+    }
+
+    const files: string[] = args.entries;
+    const configStr = JSON.stringify({
+        sources: files.map((f) => f.replace("@stdlib/", FC_STDLIB_PATH)),
+        optLevel: 2,
+        fiftPath: FIFT_LIBS_PATH,
+    });
+
+    const retJson = execSync(
+        [FUNC_FIFT_COMPILER_PATH, `'${configStr}'`].join(" "),
+    );
+
+    const result = JSON.parse(retJson.toString()) as CompileResult;
+
+    switch (result.status) {
+        case "error": {
+            return {
+                ok: false,
+                log: "",
+                fift: null,
+                output: null,
+            };
+        }
+        case "ok": {
+            return {
+                ok: true,
+                log: "",
+                fift: cutFirstLine(result.fiftCode.replaceAll("\\n", "\n")),
+                output: Buffer.from(result.codeBoc, "base64"),
+            };
+        }
+    }
+
+    throw Error("Unexpected compiler response");
+}
+
+export async function funcCompileWasm(args: {
+    entries: string[];
+    sources: { path: string; content: string }[];
+    logger: ILogger;
 }): Promise<FuncCompilationResult> {
     // Parameters
     const files: string[] = args.entries;
diff --git a/src/test/fuzzer/src/config.ts b/src/test/fuzzer/src/config.ts
new file mode 100644
index 0000000000..a62e5c6f77
--- /dev/null
+++ b/src/test/fuzzer/src/config.ts
@@ -0,0 +1,346 @@
+import * as os from "os";
+import { existsSync, mkdirSync } from "fs";
+import {
+    NonTerminal,
+    Terminal,
+} from "@/test/fuzzer/src/generators/uniform-expr-gen";
+import type {
+    NonTerminalEnum,
+    TerminalEnum,
+} from "@/test/fuzzer/src/generators/uniform-expr-gen";
+
+/**
+ * The default number of executions per test. Corresponds to fast-check defaults.
+ */
+const DEFAULT_NUM_RUNS: number = 100;
+
+/**
+ * Configuration handler for fuzz testing settings.
+ */
+export class FuzzConfig {
+    /**
+     * The number of samples to dump during fuzz testing.
+     * If `samplesNum` is not set, the fuzzer won't dump samples.
+     */
+    public samplesNum: number | undefined;
+
+    /**
+     * A format used to dump samples.
+     */
+    public samplesFormat: "ast" | "json" = "ast";
+
+    /**
+     * Explicitly specified fast-check seed.
+     */
+    public seed: number | undefined;
+
+    /**
+     * Number of AST generation cycles. POSITIVE_INFINITY means running in the continuous fuzzing mode.
+     */
+    public numRuns: number;
+
+    /**
+     * Directory to save contracts compiled during the compilation test.
+     */
+    public compileDir: string;
+
+    /**
+     * Maximum AST generation depth.
+     */
+    public maxDepth: number = 5;
+
+    /**
+     * Default generation parameters. Used by entity constructors.
+     */
+
+    /**
+     * -------------------------------------
+     * Parameters for contract generation
+     * -------------------------------------
+     */
+
+    /**
+     * Minimum number of receivers generated within a contract.
+     * @default 1
+     */
+    public static receiveMinNum: number = 1;
+
+    /**
+     * Maximum number of receivers generated within a contract.
+     * @default 5
+     */
+    public static receiveMaxNum: number = 5;
+
+    /**
+     * Minimum number of constants generated within a contract.
+     * @default 1
+     */
+    public static contractConstantMinNum: number = 1;
+
+    /**
+     * Maximum number of constants generated within a contract.
+     * @default 5
+     */
+    public static contractConstantMaxNum: number = 5;
+
+    /**
+     * Minimum number of fields generated within a contract.
+     * @default 1
+     */
+    public static contractFieldMinNum: number = 1;
+
+    /**
+     * Maximum number of fields generated within a contract.
+     * @default 5
+     */
+    public static contractFieldMaxNum: number = 5;
+
+    /**
+     * -------------------------------------
+     * Parameters for trait generation
+     * -------------------------------------
+     * TODO: make this parameters into min and max
+     */
+
+    /**
+     * Number of fields generated within a trait.
+     * @default 1
+     */
+    public static traitFieldNum: number = 1;
+
+    /**
+     * Number of method declarations generated within a trait.
+     * @default 1
+     */
+    public static traitMethodDeclarationsNum: number = 1;
+
+    /**
+     * Number of constant declarations generated within a trait.
+     * @default 1
+     */
+    public static traitConstantNum: number = 1;
+
+    /**
+     * -------------------------------------
+     * Parameters for expression generation
+     * -------------------------------------
+     */
+
+    /**
+     * Indicates whether generated expressions could use identifiers declared in the scope.
+     * @default true
+     */
+    public static useIdentifiersInExpressions: boolean = true;
+
+    /**
+     * The minimum expression size.
+     * @default 1
+     */
+    public static minExpressionSize: number = 1;
+
+    /**
+     * The maximum expression size.
+     * @default 5
+     */
+    public static maxExpressionSize: number = 5;
+
+    /**
+     * Non-terminals that the expression generator is allowed to use.
+     * @default Object.values(NonTerminal)
+     */
+    public static allowedNonTerminalsInExpressions: NonTerminalEnum[] =
+        Object.values(NonTerminal);
+
+    /**
+     * Terminals that the the expression generator is allowed to use.
+     * @default Object.values(Terminal);
+     */
+    public static allowedTerminalsInExpressions: TerminalEnum[] =
+        Object.values(Terminal);
+
+    /**
+     * -------------------------------------
+     * Parameters for function generation
+     * -------------------------------------
+     */
+
+    /**
+     * Minimum number of let statements at the start of function body.
+     * @default 1
+     */
+    public static letStatementsMinNum: number = 1;
+
+    /**
+     * Maximum number of let statements at the start of function body.
+     * @default 5
+     */
+    public static letStatementsMaxNum: number = 5;
+
+    /**
+     * Minimum number of sequential statements in the function body (not counting initial let statements and final return)
+     * @default 1
+     */
+    public static statementsMinLength: number = 1;
+
+    /**
+     * Maximum number of sequential statements in the function body (not counting initial let statements and final return)
+     * @default 5
+     */
+    public static statementsMaxLength: number = 5;
+
+    /**
+     * -------------------------------------
+     * Parameters for module generation
+     * -------------------------------------
+     */
+
+    /**
+     * Add definitions that mock stdlib ones to the generated program.
+     * @default false
+     */
+    public static addStdlib: boolean = false;
+
+    /**
+     * Minimum number of structures.
+     * @default 1
+     */
+    public static structsMinNum: number = 1;
+
+    /**
+     * Maximum number of structures.
+     * @default 4
+     */
+    public static structsMaxNum: number = 4;
+
+    /**
+     * Minimum number of messages.
+     * @default 1
+     */
+    public static messagesMinNum: number = 1;
+
+    /**
+     * Maximum number of messages.
+     * @default 4
+     */
+    public static messagesMaxNum: number = 4;
+
+    /**
+     * Minimum number of the generated traits. Some of them might be used by the generated contracts.
+     * @default 1
+     */
+    public static traitsMinNum: number = 1;
+
+    /**
+     * Maximum number of the generated traits. Some of them might be used by the generated contracts.
+     * @default 4
+     */
+    public static traitsMaxNum: number = 4;
+
+    /**
+     * Minimum number of generated contracts
+     * @default 1
+     */
+    public static contractsMinNum: number = 1;
+
+    /**
+     * Maximum number of generated contracts
+     * @default 4
+     */
+    public static contractsMaxNum: number = 4;
+
+    /**
+     * Minimum number of generated global functions
+     * @default 1
+     */
+    public static functionsMinNum: number = 1;
+
+    /**
+     * Maximum number of generated global functions
+     * @default 4
+     */
+    public static functionsMaxNum: number = 4;
+
+    /**
+     * Minimum number of global function arguments
+     * @default 0
+     */
+    public static functionArgsMinNum: number = 0;
+
+    /**
+     * Maximum number of global function arguments
+     * @default 6
+     */
+    public static functionArgsMaxNum: number = 6;
+
+    /**
+     * Minimum number of generated global constants
+     * @default 1
+     */
+    public static constantsMinNum: number = 1;
+
+    /**
+     * Maximum number of generated global constants
+     * @default 4
+     */
+    public static constantsMaxNum: number = 4;
+
+    /**
+     * -------------------------------------
+     * Parameters for general statement generation
+     * -------------------------------------
+     * TODO: Make these arguments into min and max parameters
+     */
+
+    /**
+     * Determines the maximum depth of nested statement blocks.
+     * @default 2
+     */
+    public static nestedBlocksNum: number = 2;
+
+    /**
+     * Number of statements in each block.
+     * @default 3
+     */
+    public static stmtsInBlock: number = 3;
+
+    constructor() {
+        this.samplesNum = process.env.SAMPLES_NUM
+            ? parseInt(process.env.SAMPLES_NUM)
+            : undefined;
+        if (process.env.SAMPLES_FORMAT) {
+            this.validateAndSetFormat(process.env.SAMPLES_FORMAT);
+        }
+        this.compileDir = process.env.COMPILE_DIR ?? os.tmpdir();
+        if (process.env.COMPILE_DIR && !existsSync(process.env.COMPILE_DIR)) {
+            mkdirSync(process.env.COMPILE_DIR, { recursive: true });
+        }
+        this.seed = process.env.SEED ? parseInt(process.env.SEED) : undefined;
+        this.numRuns =
+            process.env.FUZZ === "1"
+                ? Number.POSITIVE_INFINITY
+                : process.env.NUM_RUNS
+                  ? parseInt(process.env.NUM_RUNS)
+                  : DEFAULT_NUM_RUNS;
+        if (this.samplesNum && this.numRuns < this.samplesNum) {
+            console.warn(
+                `the requested number of SAMPLES_NUM=${this.samplesNum} is less than NUM_RUNS=${this.numRuns}`,
+            );
+        }
+    }
+
+    /**
+     * Validates and sets the sample format based on the provided format string.
+     * Throws an error if the format is not supported.
+     * @param fmt The format string to validate and set.
+     */
+    private validateAndSetFormat(fmt: string): void {
+        const supportedFormats: ("ast" | "json")[] = ["ast", "json"];
+        if (supportedFormats.includes(fmt as "ast" | "json")) {
+            this.samplesFormat = fmt as "ast" | "json";
+        } else {
+            throw new Error(
+                `unsupported SAMPLES_FMT: ${fmt} (supported options: "ast" and "json")`,
+            );
+        }
+    }
+}
diff --git a/src/test/fuzzer/src/context.ts b/src/test/fuzzer/src/context.ts
new file mode 100644
index 0000000000..622e46b77b
--- /dev/null
+++ b/src/test/fuzzer/src/context.ts
@@ -0,0 +1,102 @@
+import { prettyPrint } from "@/ast/ast-printer";
+import { FuzzConfig } from "@/test/fuzzer/src/config";
+import type { AstNode } from "@/ast/ast";
+import { stringify } from "@/test/fuzzer/src/util";
+import { getAstFactory } from "@/ast/ast-helpers";
+import type { FactoryAst } from "@/ast/ast-helpers";
+import { getMakeAst } from "@/ast/generated/make-factory";
+import type { MakeAstFactory } from "@/ast/generated/make-factory";
+
+/**
+ * FuzzContext represents a stateful context that handles AST generation.
+ * It keeps the global options used to control and configure the AST generation.
+ */
+export class FuzzContext {
+    /**
+     * Tracks the number of samples that have been printed.
+     */
+    private printedSamplesCount: number = 0;
+
+    /**
+     * Configuration of the fuzzer.
+     */
+    public config: FuzzConfig;
+
+    /**
+     * Current depth of AST expression generation, which limits recursive generation.
+     */
+    private currentDepth: number = 0;
+
+    /**
+     * The generic AST Factory
+     */
+    public astF: FactoryAst = getAstFactory();
+
+    /**
+     * The factory with the makeX methods
+     */
+    public makeF: MakeAstFactory = getMakeAst(this.astF);
+
+    constructor() {
+        this.config = new FuzzConfig();
+    }
+
+    public getDepth(): number {
+        return this.currentDepth;
+    }
+
+    public incDepth(): void {
+        this.currentDepth++;
+    }
+
+    public decDepth(): void {
+        this.currentDepth--;
+        if (this.currentDepth < 0) {
+            throw new Error("Reached negative recursion depth");
+        }
+    }
+
+    public resetDepth(): void {
+        this.currentDepth = 0;
+    }
+
+    /**
+     * Formats the given AST construction according to the current formatter configuration.
+     */
+    public format(value: AstNode, fmt = this.config.samplesFormat): string {
+        switch (fmt) {
+            case "json":
+                return stringify(value, 2);
+            case "ast":
+                return prettyPrint(value);
+            default:
+                throw new Error(
+                    `Unsupported sample format: ${this.config.samplesFormat}`,
+                );
+        }
+    }
+
+    /**
+     * Prints the given sample if the number of already printed samples is less than the configured limit.
+     * @param sample The sample to print.
+     */
+    public printSample(sample: AstNode): void {
+        if (
+            this.config.samplesNum === undefined ||
+            this.printedSamplesCount >= this.config.samplesNum
+        ) {
+            return;
+        }
+        console.log(
+            `Sample #${this.printedSamplesCount}:\n${this.format(sample)}`,
+        );
+        this.printedSamplesCount++;
+    }
+}
+
+/**
+ * A global context accessible and mutable throughout the generation process to
+ * reflect changes across the AST.
+ */
+// eslint-disable-next-line prefer-const
+export let GlobalContext: FuzzContext = new FuzzContext();
diff --git a/src/test/fuzzer/src/generators/constant.ts b/src/test/fuzzer/src/generators/constant.ts
new file mode 100644
index 0000000000..850afdcdd0
--- /dev/null
+++ b/src/test/fuzzer/src/generators/constant.ts
@@ -0,0 +1,122 @@
+import type * as Ast from "@/ast/ast";
+import {
+    generateAstIdFromName,
+    createSample,
+    generateAstId,
+    generateName,
+    dummySrcInfoPrintable,
+} from "@/test/fuzzer/src/util";
+import { tyToAstType } from "@/test/fuzzer/src/types";
+import type { Type } from "@/test/fuzzer/src/types";
+import type { Scope } from "@/test/fuzzer/src/scope";
+import { NamedGenerativeEntity } from "@/test/fuzzer/src/generators/generator";
+
+import fc from "fast-check";
+import { GlobalContext } from "@/test/fuzzer/src/context";
+
+/**
+ * An object that encapsulates a generated Ast.ConstantDecl.
+ */
+export class ConstantDecl extends NamedGenerativeEntity<Ast.ConstantDecl> {
+    constructor(scope: Scope, type: Type) {
+        super(type, createSample(generateAstId(scope)));
+    }
+
+    private getAttributes(
+        extraAttrs: Ast.ConstantAttribute[],
+    ): Ast.ConstantAttribute[] {
+        const attrs: Ast.ConstantAttribute[] = extraAttrs;
+        attrs.push({ type: "abstract", loc: dummySrcInfoPrintable });
+        return attrs;
+    }
+
+    private generateImpl(
+        extraAttrs: Ast.ConstantAttribute[],
+    ): fc.Arbitrary<Ast.ConstantDecl> {
+        return fc.constant(
+            GlobalContext.makeF.makeDummyConstantDecl(
+                this.getAttributes(extraAttrs),
+                this.name,
+                tyToAstType(this.type),
+            ),
+        );
+    }
+
+    /**
+     * Generates a constant declaration without extra attributes.
+     */
+    public generate(): fc.Arbitrary<Ast.ConstantDecl> {
+        return this.generateImpl([]);
+    }
+
+    /**
+     * Create definition for this constant destination.
+     * @param init An initializer evaluable in compile-time. // cspell:disable-line
+     */
+    public createDefinition(init: fc.Arbitrary<Ast.Expression>): ConstantDef {
+        return new ConstantDef(this.name.text, this.type, init);
+    }
+}
+
+/**
+ * An object that encapsulates a generated Ast.ConstantDef.
+ * @parentScope Scope this constant belongs to.
+ */
+export class ConstantDef extends NamedGenerativeEntity<Ast.ConstantDef> {
+    /**
+     * Create new constant definition from its name and type. Used to create definition from an existing declaration.
+     * @param init An initializer evaluable in compile-time. // cspell:disable-line
+     */
+    constructor(
+        name: string,
+        type: Type,
+        private init: fc.Arbitrary<Ast.Expression>,
+    ) {
+        super(type, generateAstIdFromName(name));
+    }
+    /**
+     * Create a new constant definition generation name from scope.
+     * @param scope Scope to generate constant name from.
+     * @param type Constant type.
+     * @param init An initializer evaluable in compile-time. // cspell:disable-line
+     */
+    public static fromScope(
+        scope: Scope,
+        type: Type,
+        init: fc.Arbitrary<Ast.Expression>,
+    ): ConstantDef {
+        return new ConstantDef(createSample(generateName(scope)), type, init);
+    }
+
+    private generateImpl(
+        extraAttrs: Ast.ConstantAttribute[],
+        init?: fc.Arbitrary<Ast.Expression>,
+    ): fc.Arbitrary<Ast.ConstantDef> {
+        const chosenInit = init ?? this.init;
+        return chosenInit.map((init) =>
+            GlobalContext.makeF.makeDummyConstantDef(
+                extraAttrs,
+                this.name,
+                tyToAstType(this.type),
+                init,
+            ),
+        );
+    }
+
+    /**
+     * Generates a constant definition without extra attributes.
+     */
+    public generate(): fc.Arbitrary<Ast.ConstantDef> {
+        return this.generateImpl([]);
+    }
+
+    /**
+     * Generates a constant definition with extra attributes and overridden init.
+     */
+    public generateWithAttrs(
+        extraAttrs: Ast.ConstantAttribute[] = [],
+        init?: fc.Arbitrary<Ast.Expression>,
+    ): fc.Arbitrary<Ast.ConstantDef> {
+        return this.generateImpl(extraAttrs, init);
+    }
+}
diff --git a/src/test/fuzzer/src/generators/contract.ts b/src/test/fuzzer/src/generators/contract.ts
new file mode 100644
index 0000000000..470cd70f49
--- /dev/null
+++ b/src/test/fuzzer/src/generators/contract.ts
@@ -0,0 +1,240 @@
+import type * as Ast from "@/ast/ast";
+import {
+    createSample,
+    dummySrcInfoPrintable,
+    generateAstId,
+    packArbitraries,
+} from "@/test/fuzzer/src/util";
+import { FunctionDef } from "@/test/fuzzer/src/generators/function";
+import type { Trait } from "@/test/fuzzer/src/generators/trait";
+import { Expression } from "@/test/fuzzer/src/generators/expression";
+import { Receive } from "@/test/fuzzer/src/generators/receiver";
+import { SUPPORTED_STDLIB_TYPES, UtilType } from "@/test/fuzzer/src/types";
+import type { FunctionType } from "@/test/fuzzer/src/types";
+import { Scope } from "@/test/fuzzer/src/scope";
+import { NamedGenerativeEntity } from "@/test/fuzzer/src/generators/generator";
+
+import fc from "fast-check";
+import { Field } from "@/test/fuzzer/src/generators/field";
+import { ConstantDef } from "@/test/fuzzer/src/generators/constant";
+import { FuzzConfig } from "@/test/fuzzer/src/config";
+import { GlobalContext } from "@/test/fuzzer/src/context";
+
+export interface ContractParameters {
+    /**
+     * Minimum number of receivers generated within a contract.
+     * @default FuzzConfig.receiveMinNum
+     */
+    receiveMinNum: number;
+
+    /**
+     * Maximum number of receivers generated within a contract.
+     * @default FuzzConfig.receiveMaxNum
+     */
+    receiveMaxNum: number;
+
+    /**
+     * Minimum number of constants generated within a contract.
+     * @default FuzzConfig.contractConstantMinNum
+     */
+    contractConstantMinNum: number;
+
+    /**
+     * Maximum number of constants generated within a contract.
+     * @default FuzzConfig.contractConstantMaxNum
+     */
+    contractConstantMaxNum: number;
+
+    /**
+     * Minimum number of fields generated within a contract.
+     * @default FuzzConfig.contractFieldMinNum
+     */
+    contractFieldMinNum: number;
+
+    /**
+     * Maximum number of fields generated within a contract.
+     * @default FuzzConfig.contractFieldMaxNum
+     */
+    contractFieldMaxNum: number;
+}
+
+/**
+ * An object that encapsulates a randomly generated Ast.Contract including extra information
+ * about its entries and their scopes.
+ */
+export class Contract extends NamedGenerativeEntity<Ast.Contract> {
+    /** Scope used within the generated contract. */
+    private scope: Scope;
+
+    private receiveMinNum: number;
+    private receiveMaxNum: number;
+    private contractConstantMinNum: number;
+    private contractConstantMaxNum: number;
+    private contractFieldMinNum: number;
+    private contractFieldMaxNum: number;
+
+    /**
+     * @param methodSignatures Signatures of methods to be generated in the contract.
+     * @param trait An optional trait that the generated contract must implement.
+     */
+    constructor(
+        parentScope: Scope,
+        private methodSignatures: FunctionType[],
+        private trait?: Trait,
+        params: Partial<ContractParameters> = {},
+    ) {
+        const scope = new Scope("contract", parentScope);
+        super(
+            { kind: "util", type: UtilType.Contract },
+            createSample(generateAstId(scope)),
+        );
+        this.scope = scope;
+
+        const {
+            receiveMinNum = FuzzConfig.receiveMinNum,
+            receiveMaxNum = FuzzConfig.receiveMaxNum,
+            contractConstantMinNum = FuzzConfig.contractConstantMinNum,
+            contractConstantMaxNum = FuzzConfig.contractConstantMaxNum,
+            contractFieldMinNum = FuzzConfig.contractFieldMinNum,
+            contractFieldMaxNum = FuzzConfig.contractFieldMaxNum,
+        } = params;
+        this.receiveMinNum = receiveMinNum;
+        this.receiveMaxNum = receiveMaxNum;
+        this.contractConstantMinNum = contractConstantMinNum;
+        this.contractConstantMaxNum = contractConstantMaxNum;
+        this.contractFieldMinNum = contractFieldMinNum;
+        this.contractFieldMaxNum = contractFieldMaxNum;
+    }
+
+    public generate(): fc.Arbitrary<Ast.Contract> {
+        // Implemented declarations from the trait
+        let traitFields: fc.Arbitrary<Ast.FieldDecl>[] = [];
+        let traitConstants: fc.Arbitrary<Ast.ConstantDef>[] = [];
+        let traitMethods: fc.Arbitrary<Ast.FunctionDef>[] = [];
+        if (this.trait !== undefined) {
+            traitFields = this.trait.fieldDeclarations.map(({ type, name }) => {
+                const init = new Expression(this.scope, type, {
+                    useIdentifiersInExpressions: false,
+                }).generate();
+                return new Field(this.scope, type, init, name).generate();
+            });
+            traitConstants = this.trait.constantDeclarations
+                .map((decl) => {
+                    const init = new Expression(this.scope, decl.type, {
+                        useIdentifiersInExpressions: false,
+                    }).generate();
+                    return decl
+                        .createDefinition(init)
+                        .generateWithAttrs([
+                            { type: "override", loc: dummySrcInfoPrintable },
+                        ]);
+                })
+                .concat(
+                    this.trait.constantDefinitions.map((def) =>
+                        def.generateWithAttrs([
+                            { type: "override", loc: dummySrcInfoPrintable },
+                        ]),
+                    ),
+                );
+            traitMethods = this.trait.methodDeclarations.map((m) => {
+                return m.generateDefinition("method", [
+                    {
+                        kind: "function_attribute",
+                        type: "override",
+                        loc: dummySrcInfoPrintable,
+                    },
+                    {
+                        kind: "function_attribute",
+                        type: "get",
+                        loc: dummySrcInfoPrintable,
+                        methodId: undefined,
+                    },
+                ]);
+            });
+        }
+
+        const requestedMethods = this.methodSignatures.map((signature) =>
+            new FunctionDef(this.scope, "method", signature).generate(),
+        );
+        //const generatedMethods = Array.from(
+        //    this.scope.getAllNamed("methodDef"),
+        //).map((m) => m.generate());
+
+        const requestedReceives = fc.array(new Receive(this.scope).generate(), {
+            minLength: this.receiveMinNum,
+            maxLength: this.receiveMaxNum,
+        });
+
+        // TODO: Augment the SUPPORTED_STDLIB_TYPES
+        const genConstantDefEntities = fc
+            .constantFrom(...SUPPORTED_STDLIB_TYPES)
+            .map((ty) =>
+                ConstantDef.fromScope(
+                    this.scope,
+                    { kind: "stdlib", type: ty },
+                    new Expression(
+                        this.scope,
+                        { kind: "stdlib", type: ty },
+                        { useIdentifiersInExpressions: false },
+                    ).generate(),
+                ),
+            )
+            .chain((d) => d.generate());
+
+        const generatedConstants = fc.array(genConstantDefEntities, {
+            minLength: this.contractConstantMinNum,
+            maxLength: this.contractConstantMaxNum,
+        });
+
+        //const generatedConstants = Array.from(
+        //    this.scope.getAllNamed("constantDef"),
+        //).map((c) => c.generate());
+
+        //const generatedFields = Array.from(this.scope.getAllNamed("field")).map(
+        //    (f) => f.generate(),
+        //);
+
+        // TODO: Augment the SUPPORTED_STDLIB_TYPES
+        const genFieldEntities = fc
+            .constantFrom(...SUPPORTED_STDLIB_TYPES)
+            .map(
+                (ty) =>
+                    new Field(
+                        this.scope,
+                        { kind: "stdlib", type: ty },
+                        new Expression(
+                            this.scope,
+                            { kind: "stdlib", type: ty },
+                            { useIdentifiersInExpressions: false },
+                        ).generate(),
+                    ),
+            )
+            .chain((f) => f.generate());
+
+        const generatedFields = fc.array(genFieldEntities, {
+            minLength: this.contractFieldMinNum,
+            maxLength: this.contractFieldMaxNum,
+        });
+
+        return fc
+            .tuple(
+                packArbitraries(traitConstants),
+                generatedConstants,
+                packArbitraries(traitFields),
+                generatedFields,
+                requestedReceives,
+                packArbitraries(traitMethods),
+                //...generatedMethods,
+                packArbitraries(requestedMethods),
+            )
+            .map((decls) =>
+                GlobalContext.makeF.makeDummyContract(
+                    this.name,
+                    this.trait === undefined ? [] : [this.trait.name],
+                    [],
+                    undefined,
+                    decls.flat(),
+                ),
+            );
+    }
+}
diff --git a/src/test/fuzzer/src/generators/expression.ts b/src/test/fuzzer/src/generators/expression.ts
new file mode 100644
index 0000000000..af015d124b
--- /dev/null
+++ b/src/test/fuzzer/src/generators/expression.ts
@@ -0,0 +1,265 @@
+import type * as Ast from "@/ast/ast";
+import type fc from "fast-check";
+import {
+    generateAstIdFromName,
+    packArbitraries,
+    stringify,
+} from "@/test/fuzzer/src/util";
+import {
+    GenerativeEntity,
+    NamedGenerativeEntity,
+} from "@/test/fuzzer/src/generators/generator";
+import { StdlibType } from "@/test/fuzzer/src/types";
+import type { Type } from "@/test/fuzzer/src/types";
+import type { Scope } from "@/test/fuzzer/src/scope";
+import {
+    initializeGenerator,
+    NonTerminal,
+} from "@/test/fuzzer/src/generators/uniform-expr-gen";
+import type {
+    GenInitConfig,
+    NonTerminalEnum,
+    TerminalEnum,
+} from "@/test/fuzzer/src/generators/uniform-expr-gen";
+import { GlobalContext } from "@/test/fuzzer/src/context";
+import { FuzzConfig } from "@/test/fuzzer/src/config";
+
+export type ExpressionParameters = {
+    /**
+     * Indicates whether the generated expression could use identifiers declared in the scope.
+     * @default FuzzConfig.useIdentifiersInExpressions
+     */
+    useIdentifiersInExpressions: boolean;
+
+    /**
+     * The minimum expression size.
+     * @default FuzzConfig.minExpressionSize
+     */
+    minExpressionSize: number;
+
+    /**
+     * The maximum expression size.
+     * @default FuzzConfig.maxExpressionSize
+     */
+    maxExpressionSize: number;
+
+    /**
+     * Lists the non-terminals that the generator is allowed to use.
+     * @default FuzzConfig.allowedNonTerminals
+     */
+    allowedNonTerminals: NonTerminalEnum[];
+
+    /**
+     * Lists the terminals that the generator is allowed to use.
+     * @default FuzzConfig.allowedTerminals
+     */
+    allowedTerminals: TerminalEnum[];
+};
+
+export function makeSelfID(): Ast.Id {
+    return GlobalContext.makeF.makeDummyId("self");
+}
+
+/**
+ * Generates expressions used in actual function call arguments.
+ * @param funTy Signature of the function.
+ * @param funScope Scope of the function.
+ */
+export function generateFunctionCallArgs(
+    funTy: Type,
+    funScope: Scope,
+): fc.Arbitrary<Ast.Expression>[] {
+    if (funTy.kind !== "function") {
+        throw new Error(`Incorrect type for function: ${stringify(funTy, 0)}`);
+    }
+    if (funTy.signature.length === 1) {
+        return [];
+    }
+    return funTy.signature
+        .slice(0, -1)
+        .map((argTy) => new Expression(funScope, argTy).generate());
+}
+
+/**
+ * Generates expressions used in actual method call arguments.
+ * @param methodTy Signature of the method.
+ * @param methodScope Scope of the method.
+ */
+export function generateMethodCallArgs(
+    methodTy: Type,
+    methodScope: Scope,
+): fc.Arbitrary<Ast.Expression>[] {
+    if (methodTy.kind !== "function") {
+        throw new Error(`Incorrect type for method: ${stringify(methodTy, 0)}`);
+    }
+    if (methodTy.signature.length === 2) {
+        return [];
+    }
+    return methodTy.signature
+        .slice(1, -1)
+        .map((argTy) => new Expression(methodScope, argTy).generate());
+}
+
+/**
+ * Generates method calls.
+ */
+export class MethodCall extends NamedGenerativeEntity<Ast.MethodCall> {
+    constructor(
+        type: Type,
+        name: string,
+        private src: Ast.Expression,
+        private args?: fc.Arbitrary<Ast.Expression>[],
+    ) {
+        super(type, generateAstIdFromName(name));
+    }
+    generate(): fc.Arbitrary<Ast.MethodCall> {
+        return packArbitraries(this.args).map((args) =>
+            GlobalContext.makeF.makeDummyMethodCall(this.src, this.name, args),
+        );
+    }
+}
+
+/**
+ * Generates free function calls.
+ */
+export class StaticCall extends NamedGenerativeEntity<Ast.StaticCall> {
+    constructor(
+        type: Type,
+        name: string,
+        private args?: fc.Arbitrary<Ast.Expression>[],
+    ) {
+        super(type, generateAstIdFromName(name));
+    }
+    generate(): fc.Arbitrary<Ast.StaticCall> {
+        return packArbitraries(this.args).map((args) =>
+            GlobalContext.makeF.makeDummyStaticCall(this.name, args),
+        );
+    }
+}
+
+/**
+ * Contains the logic to generate expressions based on their types.
+ */
+export class Expression extends GenerativeEntity<Ast.Expression> {
+    private static initializedGens: Map<
+        string,
+        (scope: Scope, type: NonTerminalEnum) => fc.Arbitrary<Ast.Expression>
+    > = new Map();
+    private parentScope: Scope;
+    private exprGen: (
+        scope: Scope,
+        type: NonTerminalEnum,
+    ) => fc.Arbitrary<Ast.Expression>;
+
+    /**
+     * @param parentScope Scope to extract declarations from.
+     * @param type Type of the generated expression.
+     * @param params Optional parameters for expression generation.
+     */
+    constructor(
+        parentScope: Scope,
+        type: Type,
+        params: Partial<ExpressionParameters> = {},
+    ) {
+        super(type);
+        this.parentScope = parentScope;
+
+        const {
+            useIdentifiersInExpressions = FuzzConfig.useIdentifiersInExpressions,
+            minExpressionSize = FuzzConfig.minExpressionSize,
+            maxExpressionSize = FuzzConfig.maxExpressionSize,
+            allowedNonTerminals = FuzzConfig.allowedNonTerminalsInExpressions,
+            allowedTerminals = FuzzConfig.allowedTerminalsInExpressions,
+        } = params;
+        const config: GenInitConfig = {
+            minSize: minExpressionSize,
+            maxSize: maxExpressionSize,
+            allowedNonTerminals,
+            allowedTerminals,
+            useIdentifiers: useIdentifiersInExpressions,
+        };
+        const configKey = JSON.stringify(config);
+        const initGen = Expression.initializedGens.get(configKey);
+        if (typeof initGen === "undefined") {
+            this.exprGen = initializeGenerator(config);
+            Expression.initializedGens.set(configKey, this.exprGen);
+        } else {
+            this.exprGen = initGen;
+        }
+    }
+
+    private getNonTerminalForType(
+        type: StdlibType,
+        optional: boolean,
+    ): NonTerminalEnum {
+        switch (type) {
+            case StdlibType.Int: {
+                return optional ? NonTerminal.OptInt : NonTerminal.Int;
+            }
+            case StdlibType.Bool: {
+                return optional ? NonTerminal.OptBool : NonTerminal.Bool;
+            }
+            case StdlibType.Cell: {
+                return optional ? NonTerminal.OptCell : NonTerminal.Cell;
+            }
+            case StdlibType.Address: {
+                return optional ? NonTerminal.OptAddress : NonTerminal.Address;
+            }
+            case StdlibType.Slice: {
+                return optional ? NonTerminal.OptSlice : NonTerminal.Slice;
+            }
+            case StdlibType.String: {
+                return optional ? NonTerminal.OptString : NonTerminal.String;
+            }
+            case StdlibType.Builder:
+            case StdlibType.StringBuilder:
+                throw new Error(
+                    `Generation of expressions of type ${stringify(type, 0)} is currently not supported.`,
+                );
+        }
+    }
+
+    /**
+     * Generates an AST expression of the specified type.
+     */
+    generate(): fc.Arbitrary<Ast.Expression> {
+        switch (this.type.kind) {
+            case "stdlib": {
+                const nonTerminal = this.getNonTerminalForType(
+                    this.type.type,
+                    false,
+                );
+                return this.exprGen(this.parentScope, nonTerminal);
+            }
+            case "optional": {
+                switch (this.type.type.kind) {
+                    case "stdlib": {
+                        const nonTerminal = this.getNonTerminalForType(
+                            this.type.type.type,
+                            true,
+                        );
+                        return this.exprGen(this.parentScope, nonTerminal);
+                    }
+                    case "optional":
+                    case "map":
+                    case "struct":
+                    case "message":
+                    case "util":
+                    case "function":
+                        throw new Error(
+                            `Generation of expressions of type ${stringify(this.type.type, 0)} is currently not supported.`,
+                        );
+                }
+                break;
+            }
+            case "map":
+            case "struct":
+            case "message":
+            case "util":
+            case "function":
+                throw new Error(
+                    `Generation of expressions of type ${stringify(this.type, 0)} is currently not supported.`,
+                );
+        }
+    }
+}
diff --git a/src/test/fuzzer/src/generators/field.ts b/src/test/fuzzer/src/generators/field.ts
new file mode 100644
index 0000000000..d90c5613b8
--- /dev/null
+++ b/src/test/fuzzer/src/generators/field.ts
@@ -0,0 +1,50 @@
+import type * as Ast from "@/ast/ast";
+import { createSample, generateAstId } from "@/test/fuzzer/src/util";
+import { tyToAstType } from "@/test/fuzzer/src/types";
+import type { Type } from "@/test/fuzzer/src/types";
+import type { Scope } from "@/test/fuzzer/src/scope";
+import { NamedGenerativeEntity } from "@/test/fuzzer/src/generators/generator";
+
+import fc from "fast-check";
+import { GlobalContext } from "@/test/fuzzer/src/context";
+
+/**
+ * An object that encapsulates a generated Ast.FieldDecl.
+ */
+export class Field extends NamedGenerativeEntity<Ast.FieldDecl> {
+    /**
+     * @param init An optional initializer evaluable in compile-time. // cspell:disable-line
+     * @param parentScope Scope this field belongs to. Could be a contract or program for struct fields.
+     */
+    constructor(
+        parentScope: Scope,
+        type: Type,
+        private init?: fc.Arbitrary<Ast.Expression>,
+        name?: Ast.Id,
+    ) {
+        if (
+            !parentScope.definedIn(
+                "contract",
+                "method",
+                "program" /* struct field */,
+                "trait",
+            )
+        ) {
+            throw new Error(
+                `Cannot define a field in a ${parentScope.kind} scope`,
+            );
+        }
+        super(type, name ?? createSample(generateAstId(parentScope)));
+    }
+
+    generate(): fc.Arbitrary<Ast.FieldDecl> {
+        return (this.init ?? fc.constant(undefined)).map((i) =>
+            GlobalContext.makeF.makeDummyFieldDecl(
+                this.name,
+                tyToAstType(this.type),
+                i,
+                undefined,
+            ),
+        );
+    }
+}
diff --git a/src/test/fuzzer/src/generators/function.ts b/src/test/fuzzer/src/generators/function.ts
new file mode 100644
index 0000000000..4508a8b1d9
--- /dev/null
+++ b/src/test/fuzzer/src/generators/function.ts
@@ -0,0 +1,311 @@
+import type * as Ast from "@/ast/ast";
+import {
+    tyToAstType,
+    StdlibType,
+    isThis,
+    getReturnType,
+    isUnit,
+    UtilType,
+    SUPPORTED_STDLIB_TYPES,
+} from "@/test/fuzzer/src/types";
+import type { FunctionType, Type } from "@/test/fuzzer/src/types";
+import { Let, Return, Statement } from "@/test/fuzzer/src/generators/statement";
+import { Parameter } from "@/test/fuzzer/src/generators/parameter";
+import { Scope } from "@/test/fuzzer/src/scope";
+import {
+    createSample,
+    dummySrcInfoPrintable,
+    generateAstId,
+    generateAstIdFromName,
+} from "@/test/fuzzer/src/util";
+import { NamedGenerativeEntity } from "@/test/fuzzer/src/generators/generator";
+
+import fc from "fast-check";
+import { GlobalContext } from "@/test/fuzzer/src/context";
+import { Expression } from "@/test/fuzzer/src/generators/expression";
+import { FuzzConfig } from "@/test/fuzzer/src/config";
+
+/**
+ * Utility type, used inside function definition and declaration classes and in shared functions.
+ */
+type FunctionKind = "function" | "method";
+
+export const SUPPORTED_RETURN_TYS = [
+    StdlibType.Int,
+    StdlibType.Bool,
+    StdlibType.String,
+];
+
+function notHaveArguments(kind: FunctionKind, type: FunctionType): boolean {
+    if (kind === "function") {
+        return type.signature.length === 1;
+    } else {
+        const firstArg = type.signature[0];
+        if (typeof firstArg === "undefined") {
+            throw new Error(`unexpected 'undefined'`);
+        }
+        return isThis(firstArg) && type.signature.length === 2;
+    }
+}
+
+/**
+ * Creates parameters entries saving them in the scope of the function or method as variables.
+ */
+function generateParameters(
+    kind: FunctionKind,
+    type: FunctionType,
+    scope: Scope,
+): Ast.TypedParameter[] {
+    if (notHaveArguments(kind, type)) {
+        return [];
+    }
+    const slice =
+        kind === "method"
+            ? type.signature.slice(1, -1)
+            : type.signature.slice(0, -1);
+    return slice.map((argType) => {
+        const param = new Parameter(scope, argType);
+        scope.addNamed("parameter", param);
+        return createSample(param.generate());
+    });
+}
+
+/**
+ * Prepares the final list of attributes based on function kind and the current implementation details.
+ */
+function getAttributes(
+    extraAttrs: Ast.FunctionAttribute[],
+    kind: FunctionKind,
+    onlyDeclaration: boolean,
+): Ast.FunctionAttribute[] {
+    const attrs: Ast.FunctionAttribute[] = extraAttrs;
+
+    // We are marking all the methods with the `get` attribute to ensure they
+    // will be compiled to func and tested by compilation tests.
+    // TODO: However, we cannot use `get` for abstract and overridden methods:
+    // https://github.com/tact-lang/tact/issues/490
+    if (kind === "method" && !extraAttrs.find((a) => a.type === "override")) {
+        attrs.push({
+            kind: "function_attribute",
+            type: "get",
+            loc: dummySrcInfoPrintable,
+            methodId: undefined,
+        });
+    }
+
+    if (onlyDeclaration) {
+        attrs.push({
+            kind: "function_attribute",
+            type: "abstract",
+            loc: dummySrcInfoPrintable,
+        });
+    }
+
+    return attrs;
+}
+
+export interface FunctionParameters {
+    /**
+     * Minimum number of let statements at the start of function body.
+     * @default FuzzConfig.letStatementsMinNum
+     */
+    letStatementsMinNum: number;
+
+    /**
+     * Maximum number of let statements at the start of function body.
+     * @default FuzzConfig.letStatementsMaxNum
+     */
+    letStatementsMaxNum: number;
+
+    /**
+     * Minimum number of statements in the function body (not counting initial let statements and final return)
+     * @default FuzzConfig.statementsMinLength
+     */
+    statementsMinLength: number;
+
+    /**
+     * Maximum number of statements in the function body (not counting initial let statements and final return)
+     * @default FuzzConfig.statementsMaxLength
+     */
+    statementsMaxLength: number;
+}
+
+/**
+ * An object that encapsulates the generated free function or contract method definition including
+ * its scope and nested elements.
+ */
+export class FunctionDef extends NamedGenerativeEntity<Ast.FunctionDef> {
+    /** Generated body items. */
+    private body: fc.Arbitrary<Ast.Statement>[] = [];
+
+    private letStatementsMinNum: number;
+    private letStatementsMaxNum: number;
+    private statementsMinLength: number;
+    private statementsMaxLength: number;
+
+    /** Scope used within the generated function. */
+    private scope: Scope;
+
+    private kind: FunctionKind;
+
+    constructor(
+        parentScope: Scope,
+        kind: FunctionKind,
+        type: FunctionType,
+        name?: string,
+        params: Partial<FunctionParameters> = {},
+    ) {
+        const scope = new Scope(kind, parentScope);
+        super(
+            type,
+            name
+                ? generateAstIdFromName(name)
+                : createSample(generateAstId(scope)),
+        );
+        this.scope = scope;
+        this.kind = kind;
+        const {
+            letStatementsMinNum = FuzzConfig.letStatementsMinNum,
+            letStatementsMaxNum = FuzzConfig.letStatementsMaxNum,
+            statementsMinLength = FuzzConfig.statementsMinLength,
+            statementsMaxLength = FuzzConfig.statementsMaxLength,
+        } = params;
+        this.letStatementsMinNum = letStatementsMinNum;
+        this.letStatementsMaxNum = letStatementsMaxNum;
+        this.statementsMinLength = statementsMinLength;
+        this.statementsMaxLength = statementsMaxLength;
+    }
+
+    /**
+     * Generates body of the function emitting return statement and statements generated from the bottom-up.
+     */
+    private generateBody(): fc.Arbitrary<Ast.Statement[]> {
+        const type = this.type as FunctionType;
+        const returnTy: Type =
+            type.signature.length > 0
+                ? getReturnType(type)
+                : { kind: "util", type: UtilType.Unit };
+        const returnStmt = new Return(this.scope, returnTy).generate();
+        //const generatedLetBindings = Array.from(
+        //    this.scope.getAllNamed("let"),
+        //).map((c) => c.generate());
+
+        // TODO: Augment the SUPPORTED_STDLIB_TYPES
+        const generatedLetEntities = fc
+            .constantFrom(...SUPPORTED_STDLIB_TYPES)
+            .map(
+                (ty) =>
+                    new Let(
+                        this.scope,
+                        { kind: "stdlib", type: ty },
+                        new Expression(this.scope, {
+                            kind: "stdlib",
+                            type: ty,
+                        }).generate(),
+                    ),
+            )
+            .chain((l) => l.generate());
+
+        const generatedLetBindings = fc.array(generatedLetEntities, {
+            minLength: this.letStatementsMinNum,
+            maxLength: this.letStatementsMaxNum,
+        });
+
+        const generatedStmts = fc.array(new Statement(this.scope).generate(), {
+            minLength: this.statementsMinLength,
+            maxLength: this.statementsMaxLength,
+        });
+
+        //const generatedStmts = Array.from(
+        //    this.scope.getAllUnnamed("statement"),
+        //).map((c) => c.generate());
+
+        return fc
+            .tuple(generatedLetBindings, generatedStmts, returnStmt)
+            .map((tup) => tup.flat());
+    }
+
+    public generateImpl(
+        extraAttrs: Ast.FunctionAttribute[],
+    ): fc.Arbitrary<Ast.FunctionDef> {
+        const returnTy = getReturnType(this.type as FunctionType);
+        return this.generateBody().map((stmt) =>
+            GlobalContext.makeF.makeDummyFunctionDef(
+                getAttributes(extraAttrs, this.kind, false),
+                this.name,
+                isUnit(returnTy) ? undefined : tyToAstType(returnTy),
+                generateParameters(
+                    this.kind,
+                    this.type as FunctionType,
+                    this.scope,
+                ),
+                stmt,
+            ),
+        );
+    }
+
+    /**
+     * Generates a function definition without extra attributes.
+     */
+    public generate(): fc.Arbitrary<Ast.FunctionDef> {
+        return this.generateImpl([]);
+    }
+}
+
+/**
+ * An object that encapsulates the generated free function or trait method declaration including
+ * its scope and nested elements.
+ */
+export class FunctionDecl extends NamedGenerativeEntity<Ast.FunctionDecl> {
+    /** Scope used within the generated function. */
+    private scope: Scope;
+
+    private kind: FunctionKind;
+
+    constructor(parentScope: Scope, kind: FunctionKind, type: FunctionType) {
+        const scope = new Scope(kind, parentScope);
+        super(type, createSample(generateAstId(scope)));
+        this.kind = "method";
+        this.scope = scope;
+    }
+
+    private generateImpl(
+        extraAttrs: Ast.FunctionAttribute[],
+    ): fc.Arbitrary<Ast.FunctionDecl> {
+        const returnTy = getReturnType(this.type as FunctionType);
+        return fc.constant(
+            GlobalContext.makeF.makeDummyFunctionDecl(
+                getAttributes(extraAttrs, this.kind, true),
+                this.name,
+                isUnit(returnTy) ? undefined : tyToAstType(returnTy),
+                generateParameters(
+                    this.kind,
+                    this.type as FunctionType,
+                    this.scope,
+                ),
+            ),
+        );
+    }
+
+    /**
+     * Generates a function definition without extra attributes.
+     */
+    public generate(): fc.Arbitrary<Ast.FunctionDecl> {
+        return this.generateImpl([]);
+    }
+
+    /**
+     * Generates a new function definition for this declaration.
+     */
+    public generateDefinition(
+        kind: FunctionKind,
+        attrs: Ast.FunctionAttribute[] = [],
+    ): fc.Arbitrary<Ast.FunctionDef> {
+        return new FunctionDef(
+            this.scope.parentScope!,
+            kind,
+            this.type as FunctionType,
+            this.name.text,
+        ).generateImpl(attrs);
+    }
+}
diff --git a/src/test/fuzzer/src/generators/generator.ts b/src/test/fuzzer/src/generators/generator.ts
new file mode 100644
index 0000000000..1f7e190693
--- /dev/null
+++ b/src/test/fuzzer/src/generators/generator.ts
@@ -0,0 +1,36 @@
+import type * as Ast from "@/ast/ast";
+import type { Type } from "@/test/fuzzer/src/types";
+
+import type fc from "fast-check";
+
+abstract class GenerativeEntityBase {
+    /** The type of the entity. */
+    public type: Type;
+
+    constructor(type: Type) {
+        this.type = type;
+    }
+}
+
+/**
+ * Abstract base class for entities that generate AST structures.
+ */
+export abstract class GenerativeEntity<T> extends GenerativeEntityBase {
+    abstract generate(): fc.Arbitrary<T>;
+}
+
+export abstract class NamedGenerativeEntity<T> extends GenerativeEntity<T> {
+    public name: Ast.Id;
+
+    constructor(type: Type, name: Ast.Id) {
+        super(type);
+        this.name = name;
+    }
+}
+
+/**
+ * A specialized version of GenerativeEntity that cannot generate AST entities in some cases.
+ */
+export abstract class GenerativeEntityOpt<T> extends GenerativeEntityBase {
+    abstract generate(): fc.Arbitrary<T> | undefined;
+}
diff --git a/src/test/fuzzer/src/generators/index.ts b/src/test/fuzzer/src/generators/index.ts
new file mode 100644
index 0000000000..8d3571a556
--- /dev/null
+++ b/src/test/fuzzer/src/generators/index.ts
@@ -0,0 +1,14 @@
+export {
+    ConstantDef,
+    ConstantDecl,
+} from "@/test/fuzzer/src/generators/constant";
+export { Field } from "@/test/fuzzer/src/generators/field";
+export { Contract } from "@/test/fuzzer/src/generators/contract";
+export {
+    FunctionDef,
+    FunctionDecl,
+} from "@/test/fuzzer/src/generators/function";
+export { Program } from "@/test/fuzzer/src/generators/program";
+export { Expression } from "@/test/fuzzer/src/generators/expression";
+export { Statement } from "@/test/fuzzer/src/generators/statement";
+export { GenerativeEntity } from "@/test/fuzzer/src/generators/generator";
diff --git a/src/test/fuzzer/src/generators/parameter.ts b/src/test/fuzzer/src/generators/parameter.ts
new file mode 100644
index 0000000000..1835183b57
--- /dev/null
+++ b/src/test/fuzzer/src/generators/parameter.ts
@@ -0,0 +1,40 @@
+import type * as Ast from "@/ast/ast";
+import { createSample, generateAstId } from "@/test/fuzzer/src/util";
+import { tyToAstType } from "@/test/fuzzer/src/types";
+import type { Type } from "@/test/fuzzer/src/types";
+import type { Scope } from "@/test/fuzzer/src/scope";
+import { NamedGenerativeEntity } from "@/test/fuzzer/src/generators/generator";
+
+import fc from "fast-check";
+import { GlobalContext } from "@/test/fuzzer/src/context";
+
+/**
+ * An object that encapsulates generated Ast.TypedParameter.
+ */
+export class Parameter extends NamedGenerativeEntity<Ast.TypedParameter> {
+    /**
+     * @param parentScope Scope of the function this argument belongs to.
+     * @param isBounced If the type of the argument should be wrapped in `bounced<>`
+     */
+    constructor(
+        parentScope: Scope,
+        type: Type,
+        private isBounced: boolean = false,
+    ) {
+        if (!parentScope.definedIn("receive", "method", "function")) {
+            throw new Error(
+                `Cannot define a function argument in the ${parentScope.kind} scope`,
+            );
+        }
+        super(type, createSample(generateAstId(parentScope)));
+    }
+
+    generate(): fc.Arbitrary<Ast.TypedParameter> {
+        return fc.constant(
+            GlobalContext.makeF.makeDummyTypedParameter(
+                this.name,
+                tyToAstType(this.type, this.isBounced),
+            ),
+        );
+    }
+}
diff --git a/src/test/fuzzer/src/generators/program.ts b/src/test/fuzzer/src/generators/program.ts
new file mode 100644
index 0000000000..dc390513c3
--- /dev/null
+++ b/src/test/fuzzer/src/generators/program.ts
@@ -0,0 +1,308 @@
+import type * as Ast from "@/ast/ast";
+import {
+    createSamplesArray,
+    createSample,
+    randomInt,
+    randomBool,
+    randomElement,
+    generateAstIdFromName,
+} from "@/test/fuzzer/src/util";
+import { TypeGen, UtilType, getStdlibTypes } from "@/test/fuzzer/src/types";
+import { Contract } from "@/test/fuzzer/src/generators/contract";
+import { Message, Struct } from "@/test/fuzzer/src/generators/struct";
+import { Trait } from "@/test/fuzzer/src/generators/trait";
+import { Scope } from "@/test/fuzzer/src/scope";
+import { NamedGenerativeEntity } from "@/test/fuzzer/src/generators/generator";
+import { getStdlibTraits } from "@/test/fuzzer/src/stdlib";
+import fc from "fast-check";
+import { GlobalContext } from "@/test/fuzzer/src/context";
+import { ConstantDef } from "@/test/fuzzer/src/generators/constant";
+import { Expression } from "@/test/fuzzer/src/generators/expression";
+import { FunctionDef } from "@/test/fuzzer/src/generators/function";
+import { FuzzConfig } from "@/test/fuzzer/src/config";
+
+export interface ProgramParameters {
+    /** Add definitions that mock stdlib ones to the generated program. */
+    addStdlib: boolean;
+
+    /**
+     * Minimum number of structures generated on the program level.
+     * @default FuzzConfig.structsMinNum
+     */
+    structsMinNum: number;
+
+    /**
+     * Maximum number of structures generated on the program level.
+     * @default FuzzConfig.structsMaxNum
+     */
+    structsMaxNum: number;
+
+    /**
+     * Minimum number of messages generated on the program level.
+     * @default FuzzConfig.messagesMinNum
+     */
+    messagesMinNum: number;
+
+    /**
+     * Maximum number of messages generated on the program level.
+     * @default FuzzConfig.messagesMaxNum
+     */
+    messagesMaxNum: number;
+
+    /**
+     * Minimum number of the generated traits. Some of them might be used by the generated contracts.
+     * @default FuzzConfig.traitsMinNum
+     */
+    traitsMinNum: number;
+
+    /**
+     * Maximum number of the generated traits. Some of them might be used by the generated contracts.
+     * @default FuzzConfig.traitsMaxNum
+     */
+    traitsMaxNum: number;
+
+    /**
+     * Minimum number of generated contracts
+     * @default FuzzConfig.contractsMinNum
+     */
+    contractsMinNum: number;
+
+    /**
+     * Maximum number of generated contracts
+     * @default FuzzConfig.contractsMaxNum
+     */
+    contractsMaxNum: number;
+
+    /**
+     * Minimum number of generated functions
+     * @default FuzzConfig.functionsMinNum
+     */
+    functionsMinNum: number;
+
+    /**
+     * Maximum number of generated functions
+     * @default FuzzConfig.functionsMaxNum
+     */
+    functionsMaxNum: number;
+
+    /**
+     * Minimum number of function arguments
+     * @default FuzzConfig.functionArgsMinNum
+     */
+    functionArgsMinNum: number;
+
+    /**
+     * Maximum number of function arguments
+     * @default FuzzConfig.functionArgsMaxNum
+     */
+    functionArgsMaxNum: number;
+
+    /**
+     * Minimum number of generated constants
+     * @default FuzzConfig.constantsMinNum
+     */
+    constantsMinNum: number;
+
+    /**
+     * Maximum number of generated constants
+     * @default FuzzConfig.constantsMaxNum
+     */
+    constantsMaxNum: number;
+}
+
+/**
+ * An object that encapsulates a randomly generated Ast.Module including extra information
+ * about its entries and their scopes.
+ */
+export class Program extends NamedGenerativeEntity<Ast.Module> {
+    /** Top-level global scope. */
+    private scope: Scope;
+
+    private addStdlib: boolean;
+
+    constructor(params: Partial<ProgramParameters> = {}) {
+        super(
+            { kind: "util", type: UtilType.Program },
+            generateAstIdFromName("program"),
+        );
+
+        const {
+            addStdlib = FuzzConfig.addStdlib,
+            structsMinNum = FuzzConfig.structsMinNum,
+            structsMaxNum = FuzzConfig.structsMaxNum,
+            messagesMinNum = FuzzConfig.messagesMinNum,
+            messagesMaxNum = FuzzConfig.messagesMaxNum,
+            traitsMinNum = FuzzConfig.traitsMinNum,
+            traitsMaxNum = FuzzConfig.traitsMaxNum,
+            contractsMinNum = FuzzConfig.contractsMinNum,
+            contractsMaxNum = FuzzConfig.constantsMaxNum,
+            functionsMinNum = FuzzConfig.functionsMinNum,
+            functionsMaxNum = FuzzConfig.functionsMaxNum,
+            functionArgsMinNum = FuzzConfig.functionArgsMinNum,
+            functionArgsMaxNum = FuzzConfig.functionArgsMaxNum,
+            constantsMinNum = FuzzConfig.constantsMinNum,
+            constantsMaxNum = FuzzConfig.constantsMaxNum,
+        } = params;
+        this.addStdlib = addStdlib;
+
+        this.scope = new Scope("program", undefined);
+
+        // NOTE: Structures and messages must be generated prior to contracts in order
+        // to add their entries to scopes for further reuse.
+        Array.from({ length: randomInt(structsMinNum, structsMaxNum) }).forEach(
+            (_) => {
+                this.scope.addNamed("struct", this.makeStruct());
+            },
+        );
+
+        Array.from({
+            length: randomInt(messagesMinNum, messagesMaxNum),
+        }).forEach((_) => {
+            this.scope.addNamed("message", this.makeMessage());
+        });
+
+        // NOTE: Traits must be generated prior to contracts to enable them implement them.
+        const traits: Trait[] = [];
+        Array.from({ length: randomInt(traitsMinNum, traitsMaxNum) }).forEach(
+            (_) => {
+                const trait = this.makeTrait();
+                traits.push(trait);
+                this.scope.addNamed("trait", trait);
+            },
+        );
+
+        // One of the traits could be implemented by the generated contracts.
+        Array.from({
+            length: randomInt(contractsMinNum, contractsMaxNum),
+        }).forEach((_) => {
+            const traitToImplement =
+                traits.length > 0 && randomBool()
+                    ? randomElement(traits)
+                    : undefined;
+            this.scope.addNamed(
+                "contract",
+                this.makeContract(traitToImplement),
+            );
+        });
+
+        Array.from({
+            length: randomInt(functionsMinNum, functionsMaxNum),
+        }).forEach((_) => {
+            this.scope.addNamed(
+                "functionDef",
+                this.makeFunction(functionArgsMinNum, functionArgsMaxNum),
+            );
+        });
+
+        Array.from({
+            length: randomInt(constantsMinNum, constantsMaxNum),
+        }).forEach((_) => {
+            this.scope.addNamed("constantDef", this.makeConstant());
+        });
+    }
+
+    /**
+     * Generates a Tact program.
+     *
+     * It always follows a structure that includes a single contract with a few methods
+     * which are considered as entry points of the random program generation. This means, the generation
+     * starts bottom-up from the return types of these methods and adds different AST entries, including
+     * constants, functions and contract fields. AST nodes inside the contract implementation may vary,
+     * as determined by fast-check.
+     */
+    public generate(): fc.Arbitrary<Ast.Module> {
+        const stdlibEntries = this.addStdlib
+            ? getStdlibTraits()
+                  .concat(getStdlibTypes())
+                  .map((entry) => fc.constant(entry))
+            : [];
+
+        const traits = Array.from(this.scope.getAllNamed("trait")).map((t) =>
+            t.generate(),
+        );
+        const contracts = Array.from(this.scope.getAllNamed("contract")).map(
+            (c) => c.generate(),
+        );
+        const structs = Array.from(this.scope.getAllNamed("struct")).map((s) =>
+            s.generate(),
+        );
+        const messages = Array.from(this.scope.getAllNamed("message")).map(
+            (m) => m.generate(),
+        );
+        const constants = Array.from(this.scope.getAllNamed("constantDef")).map(
+            (c) => c.generate(),
+        );
+        const functions = Array.from(this.scope.getAllNamed("functionDef")).map(
+            (f) => f.generate(),
+        );
+        return fc
+            .tuple(
+                ...stdlibEntries,
+                ...structs,
+                ...messages,
+                ...constants,
+                ...functions,
+                ...traits,
+                ...contracts,
+            )
+            .map((decls) => GlobalContext.makeF.makeModule([], decls));
+    }
+
+    /**
+     * Creates a contract object with the predefined structure which is an entry point of the generation.
+     * @param trait Trait the generated contract should implement
+     */
+    private makeContract(trait?: Trait): Contract {
+        const methodSignatures = createSamplesArray(
+            () => TypeGen.fromScope(this.scope).generateMethod(),
+            1,
+            3,
+        ).map((arb) => createSample(arb));
+        return new Contract(this.scope, methodSignatures, trait);
+    }
+
+    /**
+     * Creates a structure in the program scope.
+     */
+    private makeStruct(): Struct {
+        return new Struct(
+            this.scope,
+            createSample(TypeGen.fromScope(this.scope).generateStruct(false)),
+        );
+    }
+
+    /**
+     * Creates a message in the program scope.
+     */
+    private makeMessage(): Message {
+        return new Message(
+            this.scope,
+            createSample(TypeGen.fromScope(this.scope).generateStruct(true)),
+        );
+    }
+
+    /**
+     * Creates a trait in the program scope.
+     */
+    private makeTrait(): Trait {
+        return new Trait(this.scope);
+    }
+
+    private makeConstant(): ConstantDef {
+        const ty = createSample(TypeGen.fromScope(this.scope).generate());
+        return ConstantDef.fromScope(
+            this.scope,
+            ty,
+            new Expression(this.scope, ty, {
+                useIdentifiersInExpressions: false,
+            }).generate(),
+        );
+    }
+
+    private makeFunction(minArgsNum: number, maxArgsNum: number): FunctionDef {
+        const ty = createSample(
+            TypeGen.fromScope(this.scope).generateFun(minArgsNum, maxArgsNum),
+        );
+        return new FunctionDef(this.scope, "function", ty);
+    }
+}
diff --git a/src/test/fuzzer/src/generators/receiver.ts b/src/test/fuzzer/src/generators/receiver.ts
new file mode 100644
index 0000000000..d832c386bf
--- /dev/null
+++ b/src/test/fuzzer/src/generators/receiver.ts
@@ -0,0 +1,215 @@
+import type * as Ast from "@/ast/ast";
+import {
+    UtilType,
+    StdlibType,
+    isBouncedMessage,
+} from "@/test/fuzzer/src/types";
+import type { Type } from "@/test/fuzzer/src/types";
+import { Scope } from "@/test/fuzzer/src/scope";
+import { GenerativeEntity } from "@/test/fuzzer/src/generators/generator";
+import {
+    createSample,
+    randomBool,
+    randomElement,
+} from "@/test/fuzzer/src/util";
+import { Expression } from "@/test/fuzzer/src/generators/expression";
+import { Parameter } from "@/test/fuzzer/src/generators/parameter";
+import { Let, Statement } from "@/test/fuzzer/src/generators/statement";
+
+import fc from "fast-check";
+import { GlobalContext } from "@/test/fuzzer/src/context";
+import { generateString } from "@/test/fuzzer/src/generators/uniform-expr-gen";
+import { FuzzConfig } from "@/test/fuzzer/src/config";
+
+const RECEIVE_RETURN_TY: Type = { kind: "util", type: UtilType.Unit };
+
+function generateReceiverSimpleSubKind(
+    param: Parameter,
+): fc.Arbitrary<Ast.ReceiverSimple> {
+    return param
+        .generate()
+        .map((p) => GlobalContext.makeF.makeReceiverSimple(p));
+}
+
+function generateReceiverFallbackSubKind(): fc.Arbitrary<Ast.ReceiverFallback> {
+    return fc.constant(GlobalContext.makeF.makeReceiverFallback());
+}
+
+function generateReceiverCommentSubKind(): fc.Arbitrary<Ast.ReceiverComment> {
+    return generateString(/*nonEmpty=*/ true).map((s) =>
+        GlobalContext.makeF.makeReceiverComment(s),
+    );
+}
+
+function generateInternalReceiverKind(
+    subKind: fc.Arbitrary<Ast.ReceiverSubKind>,
+): fc.Arbitrary<Ast.ReceiverInternal> {
+    return subKind.map((k) => GlobalContext.makeF.makeDummyReceiverInternal(k));
+}
+
+function generateExternalReceiverKind(
+    subKind: fc.Arbitrary<Ast.ReceiverSubKind>,
+): fc.Arbitrary<Ast.ReceiverExternal> {
+    return subKind.map((k) => GlobalContext.makeF.makeDummyReceiverExternal(k));
+}
+
+export interface ReceiveParameters {
+    /**
+     * Minimum number of let statements at the start of function body.
+     * @default FuzzConfig.letStatementsMinNum
+     */
+    letStatementsMinNum: number;
+
+    /**
+     * Maximum number of let statements at the start of function body.
+     * @default FuzzConfig.letStatementsMaxNum
+     */
+    letStatementsMaxNum: number;
+
+    /**
+     * Minimum number of statements in the function body (not counting initial let statements and final return)
+     * @default FuzzConfig.statementsMinLength
+     */
+    statementsMinLength: number;
+
+    /**
+     * Maximum number of statements in the function body (not counting initial let statements and final return)
+     * @default FuzzConfig.statementsMaxLength
+     */
+    statementsMaxLength: number;
+}
+
+/**
+ * An object that encapsulates an Ast.Receiver.
+ */
+export class Receive extends GenerativeEntity<Ast.Receiver> {
+    /** Generated body items. */
+    private body: fc.Arbitrary<Ast.Statement>[] = [];
+
+    /** Scope used within the generated receive method. */
+    private scope: Scope;
+
+    private letStatementsMinNum: number;
+    private letStatementsMaxNum: number;
+    private statementsMinLength: number;
+    private statementsMaxLength: number;
+
+    constructor(parentScope: Scope, params: Partial<ReceiveParameters> = {}) {
+        super(RECEIVE_RETURN_TY);
+        this.scope = new Scope("receive", parentScope);
+        const {
+            letStatementsMinNum = FuzzConfig.letStatementsMinNum,
+            letStatementsMaxNum = FuzzConfig.letStatementsMaxNum,
+            statementsMinLength = FuzzConfig.statementsMinLength,
+            statementsMaxLength = FuzzConfig.statementsMaxLength,
+        } = params;
+        this.letStatementsMinNum = letStatementsMinNum;
+        this.letStatementsMaxNum = letStatementsMaxNum;
+        this.statementsMinLength = statementsMinLength;
+        this.statementsMaxLength = statementsMaxLength;
+    }
+
+    private generateSelector(): fc.Arbitrary<Ast.ReceiverKind> {
+        if (randomBool()) {
+            const ty = createSample(
+                fc.record<Type>({
+                    kind: fc.constant("stdlib"),
+                    type: fc.constantFrom(
+                        // TODO: Support Slice
+                        StdlibType.String,
+                    ),
+                }),
+            ) as Type;
+            const param = new Parameter(this.scope, ty);
+            this.scope.addNamed("parameter", param);
+            const internalSimple = generateInternalReceiverKind(
+                generateReceiverSimpleSubKind(param),
+            );
+            const externalSimple = generateExternalReceiverKind(
+                generateReceiverSimpleSubKind(param),
+            );
+            return fc.oneof(internalSimple, externalSimple);
+        }
+
+        // Choose a random message and create a bounced receiver using it.
+        const messages = this.scope.getProgramScope().getAllNamed("message");
+        if (messages.length > 0 && randomBool()) {
+            const msg = randomElement(messages);
+            const param = new Parameter(
+                this.scope,
+                msg.type,
+                isBouncedMessage(msg.type),
+            );
+            this.scope.addNamed("parameter", param);
+            return param
+                .generate()
+                .map((p) => GlobalContext.makeF.makeDummyReceiverBounce(p));
+        }
+
+        const internalFallback = generateInternalReceiverKind(
+            generateReceiverFallbackSubKind(),
+        );
+        const externalFallback = generateExternalReceiverKind(
+            generateReceiverFallbackSubKind(),
+        );
+        const internalComment = generateInternalReceiverKind(
+            generateReceiverCommentSubKind(),
+        );
+        const externalComment = generateExternalReceiverKind(
+            generateReceiverCommentSubKind(),
+        );
+
+        return fc.oneof(
+            internalFallback,
+            externalFallback,
+            internalComment,
+            externalComment,
+        ); // TODO: add bounce receiver generation
+    }
+
+    private generateBody(): fc.Arbitrary<Ast.Statement[]> {
+        // Create a dummy expression to execute the bottom-up AST generation.
+        //const expr = new Expression(this.scope, this.type).generate();
+        //const stmt = new StatementExpression(expr).generate();
+
+        //const generatedLetBindings = Array.from(
+        //    this.scope.getAllNamed("let"),
+        //).map((c) => c.generate());
+        //const generatedStmts = Array.from(
+        //    this.scope.getAllUnnamed("statement"),
+        //).map((c) => c.generate());
+
+        // TODO: Make it generate arbitrary types
+        const generatedLetBindings = fc.array(
+            new Let(
+                this.scope,
+                { kind: "stdlib", type: StdlibType.Int },
+                new Expression(this.scope, {
+                    kind: "stdlib",
+                    type: StdlibType.Int,
+                }).generate(),
+            ).generate(),
+            {
+                minLength: this.letStatementsMinNum,
+                maxLength: this.letStatementsMaxNum,
+            },
+        );
+
+        const generatedStmts = fc.array(new Statement(this.scope).generate(), {
+            minLength: this.statementsMinLength,
+            maxLength: this.statementsMaxLength,
+        });
+
+        return fc
+            .tuple(generatedLetBindings, generatedStmts)
+            .map((tup) => tup.flat());
+    }
+
+    public generate(): fc.Arbitrary<Ast.Receiver> {
+        return fc
+            .tuple(this.generateSelector(), this.generateBody())
+            .map(([sel, stmt]) =>
+                GlobalContext.makeF.makeDummyReceiver(sel, stmt),
+            );
+    }
+}
diff --git a/src/test/fuzzer/src/generators/statement.ts b/src/test/fuzzer/src/generators/statement.ts
new file mode 100644
index 0000000000..145641bae1
--- /dev/null
+++ b/src/test/fuzzer/src/generators/statement.ts
@@ -0,0 +1,622 @@
+import type * as Ast from "@/ast/ast";
+import fc from "fast-check";
+
+import {
+    Expression,
+    generateFunctionCallArgs,
+    generateMethodCallArgs,
+    makeSelfID,
+    MethodCall,
+    StaticCall,
+} from "@/test/fuzzer/src/generators/expression";
+import {
+    randomBool,
+    createSample,
+    generateName,
+    packArbitraries,
+    randomElement,
+    generateAstId,
+    generateAstIdFromName,
+} from "@/test/fuzzer/src/util";
+import {
+    GenerativeEntity,
+    NamedGenerativeEntity,
+} from "@/test/fuzzer/src/generators/generator";
+import { StdlibType, UtilType, tyToAstType } from "@/test/fuzzer/src/types";
+import type { Type } from "@/test/fuzzer/src/types";
+import { Scope } from "@/test/fuzzer/src/scope";
+import type { NamedScopeItemKind } from "@/test/fuzzer/src/scope";
+import { GlobalContext } from "@/test/fuzzer/src/context";
+import { FuzzConfig } from "@/test/fuzzer/src/config";
+
+/** Type all the imperative constructions have. */
+const STMT_TY: Type = { kind: "util", type: UtilType.Unit };
+
+/**
+ * Generates `return` statements.
+ */
+export class Return extends GenerativeEntity<Ast.Statement> {
+    /**
+     * @param parentScope Scope this statement belongs to.
+     */
+    constructor(
+        private parentScope: Scope,
+        type: Type,
+    ) {
+        super(type);
+    }
+    generate(): fc.Arbitrary<Ast.Statement> {
+        const gen =
+            this.type.kind === "util" && this.type.type === UtilType.Unit
+                ? fc.constant(undefined)
+                : new Expression(this.parentScope, this.type).generate();
+
+        return gen.map((expr) =>
+            GlobalContext.makeF.makeDummyStatementReturn(expr),
+        );
+    }
+}
+
+/**
+ * Let generator is the entry point of the bottom-up statement generation.
+ * It creates a variable binding and then adds additional statements that mutate the created binding and the global state.
+ */
+export class Let extends NamedGenerativeEntity<Ast.Statement> {
+    /**
+     * @param parentScope Scope this statement belongs to.
+     * @param type Type of the generated binding.
+     * @param expr Expression generator to initialize that binding.
+     */
+    constructor(
+        parentScope: Scope,
+        type: Type,
+        private expr: fc.Arbitrary<Ast.Expression>,
+    ) {
+        super(type, createSample(generateAstId(parentScope)));
+    }
+
+    generate(): fc.Arbitrary<Ast.Statement> {
+        return this.expr.map((expr) =>
+            GlobalContext.makeF.makeDummyStatementLet(
+                this.name,
+                tyToAstType(this.type),
+                expr,
+            ),
+        );
+    }
+}
+
+/**
+ * Creates assignments and augmented assignments to modify global or local variables.
+ */
+export class AssignStatement extends GenerativeEntity<Ast.Statement> {
+    /**
+     * @param path A qualified name of the lhs.
+     * @param rhs Expression to assign to.
+     * @param rhsTy Type of the rhs of the assignment.
+     * @param ty Type of the statement.
+     */
+    constructor(
+        private path: Ast.Expression,
+        private rhs: fc.Arbitrary<Ast.Expression>,
+        private rhsTy: Type,
+        ty = STMT_TY,
+    ) {
+        super(ty);
+    }
+
+    generate(): fc.Arbitrary<Ast.Statement> {
+        const assigns: fc.Arbitrary<Ast.Statement>[] = [
+            this.rhs.map((expr) =>
+                GlobalContext.makeF.makeDummyStatementAssign(this.path, expr),
+            ),
+        ];
+        // Only integer types in augmented assignments are supported.
+        // See: https://github.com/tact-lang/tact/issues/353.
+        if (
+            this.rhsTy.kind === "stdlib" &&
+            this.rhsTy.type === StdlibType.Int
+        ) {
+            assigns.push(
+                this.rhs.chain((expr) => {
+                    return fc
+                        .constantFrom<Ast.AugmentedAssignOperation>(
+                            "+=",
+                            "-=",
+                            "*=",
+                            "/=",
+                            "%=",
+                        )
+                        .map((op) =>
+                            GlobalContext.makeF.makeDummyStatementAugmentedAssign(
+                                op,
+                                this.path,
+                                expr,
+                            ),
+                        );
+                }),
+            );
+        }
+        return fc.oneof(...assigns);
+    }
+}
+
+/**
+ * Generates `while` and `until` loops.
+ */
+export class WhileUntilStatement extends GenerativeEntity<Ast.Statement> {
+    constructor(
+        private condition: fc.Arbitrary<Ast.Expression>,
+        private body: fc.Arbitrary<Ast.Statement>[],
+        private kind: "until" | "while",
+        type: Type = STMT_TY,
+    ) {
+        super(type);
+    }
+    generate(): fc.Arbitrary<Ast.Statement> {
+        return packArbitraries(this.body).chain((stmts) =>
+            this.condition.map((expr) => {
+                if (this.kind === "until") {
+                    return GlobalContext.makeF.makeDummyStatementUntil(
+                        expr,
+                        stmts,
+                    );
+                } else {
+                    return GlobalContext.makeF.makeDummyStatementWhile(
+                        expr,
+                        stmts,
+                    );
+                }
+            }),
+        );
+    }
+}
+
+/**
+ * Generates `repeat` loops.
+ */
+export class RepeatStatement extends GenerativeEntity<Ast.Statement> {
+    constructor(
+        private parentScope: Scope,
+        private body: fc.Arbitrary<Ast.Statement>[],
+        type: Type = STMT_TY,
+    ) {
+        super(type);
+    }
+    generate(): fc.Arbitrary<Ast.Statement> {
+        const iterations = new Expression(this.parentScope, {
+            kind: "stdlib",
+            type: StdlibType.Int,
+        }).generate();
+        return iterations.chain((iter) =>
+            packArbitraries(this.body).map((stmts) =>
+                GlobalContext.makeF.makeDummyStatementRepeat(iter, stmts),
+            ),
+        );
+    }
+}
+
+/**
+ * Generates `foreach` loops.
+ */
+export class ForeachStatement extends GenerativeEntity<Ast.Statement> {
+    constructor(
+        private map: fc.Arbitrary<Ast.Expression>,
+        private keyName: string,
+        private valueName: string,
+        private body: fc.Arbitrary<Ast.Statement>[],
+        type: Type = STMT_TY,
+    ) {
+        super(type);
+    }
+    generate(): fc.Arbitrary<Ast.Statement> {
+        return this.map.chain((map) =>
+            packArbitraries(this.body).map((stmts) =>
+                GlobalContext.makeF.makeDummyStatementForEach(
+                    generateAstIdFromName(this.keyName),
+                    generateAstIdFromName(this.valueName),
+                    map,
+                    stmts,
+                ),
+            ),
+        );
+    }
+}
+
+/**
+ * Generates conditional statements.
+ */
+export class ConditionStatement extends GenerativeEntity<Ast.StatementCondition> {
+    constructor(
+        private parentScope: Scope,
+        private trueStmts: fc.Arbitrary<Ast.Statement>[],
+        private falseStmts?: fc.Arbitrary<Ast.Statement>[],
+        type: Type = STMT_TY,
+    ) {
+        super(type);
+    }
+    generate(): fc.Arbitrary<Ast.StatementCondition> {
+        const condition = new Expression(this.parentScope, {
+            kind: "stdlib",
+            type: StdlibType.Bool,
+        }).generate();
+        const falseArb = this.falseStmts
+            ? packArbitraries(this.falseStmts)
+            : fc.constant(undefined);
+
+        return condition.chain((cond) =>
+            packArbitraries(this.trueStmts).chain((trueStmts) =>
+                falseArb.map((falseStmts) =>
+                    GlobalContext.makeF.makeDummyStatementCondition(
+                        cond,
+                        trueStmts,
+                        falseStmts,
+                    ),
+                ),
+            ),
+        );
+    }
+}
+
+/**
+ * Generates try-catch statements.
+ */
+export class TryCatch extends GenerativeEntity<Ast.Statement> {
+    constructor(
+        private tryStmts: fc.Arbitrary<Ast.Statement>[],
+        private catchBlock?: Ast.CatchBlock,
+        type: Type = STMT_TY,
+    ) {
+        super(type);
+    }
+    generate(): fc.Arbitrary<Ast.Statement> {
+        return packArbitraries(this.tryStmts).map((stmts) =>
+            GlobalContext.makeF.makeDummyStatementTry(stmts, this.catchBlock),
+        );
+    }
+}
+
+/**
+ * Generates expression statements.
+ * The return value of the function/method calls generated by this is never used.
+ */
+export class StatementExpression extends GenerativeEntity<Ast.Statement> {
+    constructor(
+        private expr: fc.Arbitrary<Ast.Expression>,
+        type: Type = STMT_TY,
+    ) {
+        super(type);
+    }
+    generate(): fc.Arbitrary<Ast.Statement> {
+        return this.expr.map((expr) =>
+            GlobalContext.makeF.makeDummyStatementExpression(expr),
+        );
+    }
+}
+
+export interface StatementParameters {
+    /**
+     * Determines the maximum depth of nested statement blocks.
+     * @default FuzzConfig.nestedBlocksNum
+     */
+    nestedBlocksNum: number;
+
+    /**
+     * Number of statements in each block.
+     * @default FuzzConfig.stmtsInBlock
+     */
+    stmtsInBlock: number;
+}
+
+/**
+ * The generator that creates statements in the given block which mutate global or local state.
+ */
+export class Statement extends GenerativeEntity<Ast.Statement> {
+    private nestedBlocksNum: number;
+    private stmtsInBlock: number;
+    private params: Partial<StatementParameters>;
+
+    /**
+     * @param parentScope Scope the generated statements belongs to.
+     * @param recursionLevel Used internally within Statement.
+     * @param params Optional parameters for statement generation.
+     * @param type Type of the generated statement.
+     */
+    constructor(
+        private parentScope: Scope,
+        private recursionLevel = 0,
+        params: Partial<StatementParameters> = {},
+        type: Type = STMT_TY,
+    ) {
+        if (parentScope.definedIn("program", "contract")) {
+            throw new Error(
+                `Cannot generate statements in the ${parentScope.kind} scope`,
+            );
+        }
+        super(type);
+
+        const {
+            nestedBlocksNum = FuzzConfig.nestedBlocksNum,
+            stmtsInBlock = FuzzConfig.stmtsInBlock,
+        } = params;
+        this.nestedBlocksNum = nestedBlocksNum;
+        this.stmtsInBlock = stmtsInBlock;
+
+        this.params = params;
+    }
+
+    generate(): fc.Arbitrary<Ast.Statement> {
+        const varAssign = this.makeVarAssign();
+        const fieldAssign = this.makeFieldAssign();
+        const loopStmt = randomBool()
+            ? this.makeWhileUntil()
+            : this.makeRepeat();
+        const foreachStmt = this.makeForEach();
+        const condStmt = this.makeCondition();
+        const tryCatch = this.makeTryCatch();
+        const callStmt = this.makeCall();
+        const generated = [
+            ...(varAssign ? [varAssign] : []),
+            ...(fieldAssign ? [fieldAssign] : []),
+            ...(loopStmt ? [loopStmt] : []),
+            ...(foreachStmt ? [foreachStmt] : []),
+            ...(condStmt ? [condStmt] : []),
+            ...(tryCatch ? [tryCatch] : []),
+            ...(callStmt ? [callStmt] : []),
+        ];
+        if (generated.length === 0) {
+            // No variables in local/global scopes are available: generate dummy statements.
+            generated.push(this.makeDummyStmt());
+        }
+        return fc.oneof(...generated);
+    }
+
+    /**
+     * Creates statements that mutate local variables, including assignments and augmented assignments.
+     */
+    private makeVarAssign(): fc.Arbitrary<Ast.Statement> | undefined {
+        const varEntries: [string, Type][] =
+            this.parentScope.getNamedEntriesRecursive("let");
+        if (varEntries.length === 0) {
+            return undefined;
+        }
+        const arbs = varEntries.map(([name, ty]) => {
+            const expr = new Expression(this.parentScope, ty).generate();
+            return new AssignStatement(
+                generateAstIdFromName(name),
+                expr,
+                ty,
+            ).generate();
+        });
+        return arbs.length > 0 ? fc.oneof(...arbs) : undefined;
+    }
+
+    /**
+     * Creates statements that mutate contract fields, including assignments and augmented assignments.
+     */
+    private makeFieldAssign(): fc.Arbitrary<Ast.Statement> | undefined {
+        if (!this.parentScope.definedIn("method")) {
+            return undefined;
+        }
+        const fieldEntries: [string, Type][] =
+            this.parentScope.getNamedEntriesRecursive("field");
+        if (fieldEntries.length === 0) {
+            return undefined;
+        }
+        const arbs = fieldEntries.map(([name, ty]) => {
+            const expr = new Expression(this.parentScope, ty).generate();
+            return new AssignStatement(
+                GlobalContext.makeF.makeDummyFieldAccess(
+                    makeSelfID(),
+                    GlobalContext.makeF.makeDummyId(name),
+                ),
+                expr,
+                ty,
+            ).generate();
+        });
+        return arbs.length > 0 ? fc.oneof(...arbs) : undefined;
+    }
+
+    /**
+     * Creates either while or until loops.
+     */
+    private makeWhileUntil(): fc.Arbitrary<Ast.Statement> | undefined {
+        if (this.recursionLevel >= this.nestedBlocksNum) {
+            return undefined;
+        }
+        const condition = new Expression(this.parentScope, {
+            kind: "stdlib",
+            type: StdlibType.Bool,
+        }).generate();
+        const body = this.makeStmtsBlock();
+        return new WhileUntilStatement(
+            condition,
+            body,
+            randomBool() ? "while" : "until",
+        ).generate();
+    }
+
+    /**
+     * Generates repeat loops.
+     */
+    private makeRepeat(): fc.Arbitrary<Ast.Statement> | undefined {
+        if (this.recursionLevel >= this.nestedBlocksNum) {
+            return undefined;
+        }
+        const body = this.makeStmtsBlock();
+        return new RepeatStatement(this.parentScope, body).generate();
+    }
+
+    /**
+     * Collects all local map Ast.Ids in parent scope.
+     */
+    private collectLocalMapIds(entryKinds: NamedScopeItemKind[]): Ast.Id[] {
+        return this.parentScope
+            .getNamedEntriesRecursive(...entryKinds)
+            .filter(([_, mapTy]: [string, Type]) => mapTy.kind === "map")
+            .map(([mapName, _]: [string, Type]) =>
+                generateAstIdFromName(mapName),
+            );
+    }
+
+    /**
+     * Collects all field map Ast.Ids in parent scope.
+     */
+    private collectFieldMapIds(
+        entryKinds: NamedScopeItemKind[],
+    ): Ast.FieldAccess[] {
+        return this.parentScope
+            .getNamedEntriesRecursive(...entryKinds)
+            .filter(([_, mapTy]: [string, Type]) => mapTy.kind === "map")
+            .map(([mapName, _]: [string, Type]) =>
+                GlobalContext.makeF.makeDummyFieldAccess(
+                    makeSelfID(),
+                    GlobalContext.makeF.makeDummyId(mapName),
+                ),
+            );
+    }
+
+    /**
+     * Generates foreach loops.
+     */
+    private makeForEach(): fc.Arbitrary<Ast.Statement> | undefined {
+        if (this.recursionLevel >= this.nestedBlocksNum) {
+            return undefined;
+        }
+        const scope = new Scope("block", this.parentScope);
+        const simpleMapIds = this.collectLocalMapIds([
+            "let",
+            "constantDecl",
+            "constantDef",
+        ]);
+        const fieldMapPaths = this.collectFieldMapIds(["field"]);
+        const mapIds = [
+            ...simpleMapIds.map(fc.constant),
+            ...fieldMapPaths.map(fc.constant),
+        ];
+        if (mapIds.length === 0) {
+            return undefined;
+        }
+        const map: fc.Arbitrary<Ast.Expression> = fc.oneof(...mapIds);
+        const keyVarName = createSample(generateName(scope));
+        const valueVarName = createSample(generateName(scope));
+        const body = this.makeStmtsBlock(scope);
+        return new ForeachStatement(
+            map,
+            keyVarName,
+            valueVarName,
+            body,
+        ).generate();
+    }
+
+    /**
+     * Generates conditional statements.
+     */
+    private makeCondition(): fc.Arbitrary<Ast.StatementCondition> | undefined {
+        if (this.recursionLevel >= this.nestedBlocksNum) {
+            return undefined;
+        }
+        const trueStatements = this.makeStmtsBlock();
+        const falseStatements = randomBool()
+            ? undefined
+            : this.makeStmtsBlock();
+        return new ConditionStatement(
+            this.parentScope,
+            trueStatements,
+            falseStatements,
+        ).generate();
+    }
+
+    /**
+     * Generates try and try-catch statements
+     */
+    private makeTryCatch(): fc.Arbitrary<Ast.Statement> | undefined {
+        if (this.recursionLevel >= this.nestedBlocksNum) {
+            return undefined;
+        }
+        const tryStmts = this.makeStmtsBlock();
+        if (randomBool()) {
+            const catchScope = new Scope("block", this.parentScope);
+            const catchName = createSample(generateName(catchScope));
+            const catchStmts = this.makeStmtsBlock(catchScope).map((stmt) =>
+                createSample(stmt),
+            );
+            return new TryCatch(tryStmts, {
+                catchName: generateAstIdFromName(catchName),
+                catchStatements: catchStmts,
+            }).generate();
+        } else {
+            return new TryCatch(tryStmts).generate();
+        }
+    }
+
+    /**
+     * Generates function or method calls without using the return value.
+     */
+    private makeCall(): fc.Arbitrary<Ast.Statement> | undefined {
+        if (this.recursionLevel >= this.nestedBlocksNum) {
+            return undefined;
+        }
+        if (this.parentScope.definedIn("method", "contract") && randomBool()) {
+            // Call a method
+            const methodEntries =
+                this.parentScope.getNamedEntriesRecursive("methodDef");
+            if (methodEntries.length === 0) {
+                return undefined;
+            }
+            const [funName, funTy] = randomElement(methodEntries);
+            return new StatementExpression(
+                new MethodCall(
+                    funTy,
+                    funName,
+                    makeSelfID(),
+                    generateMethodCallArgs(funTy, this.parentScope),
+                ).generate(),
+            ).generate();
+        } else {
+            // Call a function
+            const funEntries =
+                this.parentScope.getNamedEntriesRecursive("functionDef");
+            if (funEntries.length === 0) {
+                return undefined;
+            }
+            const [funName, funTy] = randomElement(funEntries);
+            return new StatementExpression(
+                new StaticCall(
+                    funTy,
+                    funName,
+                    generateFunctionCallArgs(funTy, this.parentScope),
+                ).generate(),
+            ).generate();
+        }
+    }
+
+    /**
+     * Creates a block of statements nested in curly braces in concrete syntax.
+     */
+    private makeStmtsBlock(blockScope?: Scope): fc.Arbitrary<Ast.Statement>[] {
+        const scope = blockScope ?? new Scope("block", this.parentScope);
+        const block: fc.Arbitrary<Ast.Statement>[] = [];
+        Array.from({ length: this.stmtsInBlock }).forEach(() => {
+            const stmt = new Statement(
+                scope,
+                this.recursionLevel + 1,
+                this.params,
+            ).generate();
+            block.push(stmt);
+        });
+        return block;
+    }
+
+    /**
+     * Generates a dummy statement which doesn't have affect on control-flow nor state:
+     * `while (false) { }`
+     */
+    private makeDummyStmt(): fc.Arbitrary<Ast.Statement> {
+        return new WhileUntilStatement(
+            fc.constant(GlobalContext.makeF.makeDummyBoolean(false)),
+            [],
+            "while",
+        ).generate();
+    }
+}
diff --git a/src/test/fuzzer/src/generators/struct.ts b/src/test/fuzzer/src/generators/struct.ts
new file mode 100644
index 0000000000..c934595b2c
--- /dev/null
+++ b/src/test/fuzzer/src/generators/struct.ts
@@ -0,0 +1,94 @@
+import type * as Ast from "@/ast/ast";
+import { tyToString, throwTyError } from "@/test/fuzzer/src/types";
+import type { Type, StructField } from "@/test/fuzzer/src/types";
+import type { Scope } from "@/test/fuzzer/src/scope";
+import { Field } from "@/test/fuzzer/src/generators/field";
+import { generateAstIdFromName, packArbitraries } from "@/test/fuzzer/src/util";
+import { NamedGenerativeEntity } from "@/test/fuzzer/src/generators/generator";
+
+import type fc from "fast-check";
+import { GlobalContext } from "@/test/fuzzer/src/context";
+
+/**
+ * An object that generates Ast.StructDecl object.
+ */
+export class Struct extends NamedGenerativeEntity<Ast.StructDecl> {
+    /**
+     * @param programScope A program scope the structure defined in.
+     */
+    constructor(
+        private programScope: Scope,
+        type: Type,
+    ) {
+        if (type.kind !== "struct") {
+            throw new Error(
+                `Cannot create a structure with the ${tyToString(type)} type`,
+            );
+        }
+        if (!programScope.definedIn("program")) {
+            throw new Error(
+                `Cannot define a struct out of the program scope (got ${programScope.kind})`,
+            );
+        }
+        super(type, generateAstIdFromName(type.name));
+    }
+
+    generate(): fc.Arbitrary<Ast.StructDecl> {
+        if (this.type.kind !== "struct") {
+            throwTyError(this.type);
+        }
+        const fields = this.type.fields.map((fieldTy: StructField) => {
+            return new Field(
+                this.programScope,
+                fieldTy.type,
+                fieldTy.default,
+                generateAstIdFromName(fieldTy.name),
+            ).generate();
+        });
+        return packArbitraries(fields).map((f) =>
+            GlobalContext.makeF.makeDummyStructDecl(this.name, f),
+        );
+    }
+}
+
+/**
+ * An object that generates Ast.MessageDecl object messages.
+ */
+export class Message extends NamedGenerativeEntity<Ast.MessageDecl> {
+    /**
+     * @param programScope A program scope the structure defined in.
+     */
+    constructor(
+        private programScope: Scope,
+        type: Type,
+    ) {
+        if (type.kind !== "message") {
+            throw new Error(
+                `Cannot create a message with the ${tyToString(type)} type`,
+            );
+        }
+        if (!programScope.definedIn("program")) {
+            throw new Error(
+                `Cannot define a struct out of the program scope (got ${programScope.kind})`,
+            );
+        }
+        super(type, generateAstIdFromName(type.name));
+    }
+
+    generate(): fc.Arbitrary<Ast.MessageDecl> {
+        if (this.type.kind !== "message") {
+            throwTyError(this.type);
+        }
+        const fields = this.type.fields.map((fieldTy: StructField) => {
+            return new Field(
+                this.programScope,
+                fieldTy.type,
+                fieldTy.default,
+                generateAstIdFromName(fieldTy.name),
+            ).generate();
+        });
+        return packArbitraries(fields).map((f) =>
+            GlobalContext.makeF.makeDummyMessageDecl(this.name, undefined, f),
+        );
+    }
+}
diff --git a/src/test/fuzzer/src/generators/trait.ts b/src/test/fuzzer/src/generators/trait.ts
new file mode 100644
index 0000000000..8cf7fb26b9
--- /dev/null
+++ b/src/test/fuzzer/src/generators/trait.ts
@@ -0,0 +1,143 @@
+import type * as Ast from "@/ast/ast";
+import {
+    createSample,
+    generateAstId,
+    randomBool,
+} from "@/test/fuzzer/src/util";
+import { FunctionDecl } from "@/test/fuzzer/src/generators/function";
+import { Field } from "@/test/fuzzer/src/generators/field";
+import {
+    ConstantDecl,
+    ConstantDef,
+} from "@/test/fuzzer/src/generators/constant";
+import { Expression } from "@/test/fuzzer/src/generators/expression";
+import { TypeGen, makeFunctionTy, UtilType } from "@/test/fuzzer/src/types";
+import type { Type } from "@/test/fuzzer/src/types";
+import { Scope } from "@/test/fuzzer/src/scope";
+import { NamedGenerativeEntity } from "@/test/fuzzer/src/generators/generator";
+
+import fc from "fast-check";
+import { GlobalContext } from "@/test/fuzzer/src/context";
+import { FuzzConfig } from "@/test/fuzzer/src/config";
+
+export interface TraitParameters {
+    /**
+     * Number of fields generated within a trait.
+     * @default FuzzConfig.traitFieldNum
+     */
+    traitFieldNum: number;
+
+    /**
+     * Number of method declarations generated within a trait.
+     * @default FuzzConfig.traitMethodDeclarationsNum
+     */
+    traitMethodDeclarationsNum: number;
+
+    /**
+     * Number of constant declarations generated within a trait.
+     * @default FuzzConfig.traitConstantNum
+     */
+    traitConstantNum: number;
+}
+
+/**
+ * An object that encapsulates a randomly generated Ast.Trait.
+ */
+export class Trait extends NamedGenerativeEntity<Ast.Trait> {
+    /** Trait scope. */
+    private scope: Scope;
+
+    // Configuration options
+    private fieldNum: number;
+    private methodDeclarationsNum: number;
+    private constantNum: number;
+
+    // Declarations to be defined within contracts/traits that implement this trait.
+    public fieldDeclarations: Field[] = [];
+    public constantDeclarations: ConstantDecl[] = [];
+    public constantDefinitions: ConstantDef[] = [];
+    public methodDeclarations: FunctionDecl[] = [];
+
+    constructor(programScope: Scope, params: Partial<TraitParameters> = {}) {
+        const scope = new Scope("trait", programScope);
+        super(
+            { kind: "util", type: UtilType.Trait },
+            createSample(generateAstId(scope)),
+        );
+        this.scope = scope;
+
+        const {
+            traitFieldNum = FuzzConfig.traitFieldNum,
+            traitMethodDeclarationsNum = FuzzConfig.traitMethodDeclarationsNum,
+            traitConstantNum = FuzzConfig.traitConstantNum,
+        } = params;
+        this.fieldNum = traitFieldNum;
+        this.methodDeclarationsNum = traitMethodDeclarationsNum;
+        this.constantNum = traitConstantNum;
+
+        this.prepareDeclarationTypes();
+    }
+
+    /**
+     * Randomly generates init expressions for constants.
+     */
+    private makeInit(ty: Type): fc.Arbitrary<Ast.Expression> | undefined {
+        return ty.kind === "map" || randomBool()
+            ? undefined
+            : new Expression(this.scope, ty, {
+                  useIdentifiersInExpressions: false,
+              }).generate();
+    }
+
+    /**
+     * Generates arbitrary types for fields, methods and constants that will be
+     * defined in the trait.
+     */
+    private prepareDeclarationTypes() {
+        this.fieldDeclarations = Array.from({ length: this.fieldNum }).map(
+            (_) => {
+                const ty = TypeGen.fromScope(this.scope).pick();
+                const field = new Field(this.scope, ty);
+                this.scope.addNamed("field", field);
+                return field;
+            },
+        );
+        this.methodDeclarations = Array.from({
+            length: this.methodDeclarationsNum,
+        }).map((_) => {
+            const returnTy = TypeGen.fromScope(this.scope).pick();
+            const funTy = makeFunctionTy("method", returnTy);
+            return new FunctionDecl(this.scope, "method", funTy);
+        });
+        this.constantDeclarations = [];
+        this.constantDefinitions = [];
+        Array.from({ length: this.constantNum }).forEach((_) => {
+            const ty = TypeGen.fromScope(this.scope).pick();
+            const init = this.makeInit(ty);
+            if (init)
+                this.constantDefinitions.push(
+                    ConstantDef.fromScope(this.scope, ty, init),
+                );
+            else
+                this.constantDeclarations.push(
+                    new ConstantDecl(this.scope, ty),
+                );
+        });
+    }
+
+    public generate(): fc.Arbitrary<Ast.Trait> {
+        // NOTE: It doesn't implement any receive functions, to don't clutter the top-level with them.
+        const constants = (
+            this.constantDeclarations as (ConstantDecl | ConstantDef)[]
+        )
+            .concat(this.constantDefinitions)
+            .map((c) => c.generate());
+        const fields = this.fieldDeclarations.map((f) => f.generate());
+        const methods = this.methodDeclarations.map((m) => m.generate());
+        return fc
+            .tuple(...constants, ...fields, ...methods)
+            .map((decl) =>
+                GlobalContext.makeF.makeDummyTrait(this.name, [], [], decl),
+            );
+    }
+}
diff --git a/src/test/fuzzer/src/generators/uniform-expr-gen.ts b/src/test/fuzzer/src/generators/uniform-expr-gen.ts
new file mode 100644
index 0000000000..cefd586577
--- /dev/null
+++ b/src/test/fuzzer/src/generators/uniform-expr-gen.ts
@@ -0,0 +1,1825 @@
+import type * as Ast from "@/ast/ast";
+import type { FactoryAst } from "@/ast/ast-helpers";
+import { getMakeAst } from "@/ast/generated/make-factory";
+import { getAstUtil } from "@/ast/util";
+import { Interpreter } from "@/optimizer/interpreter";
+import { GlobalContext } from "@/test/fuzzer/src/context";
+import type { Scope } from "@/test/fuzzer/src/scope";
+import { StdlibType } from "@/test/fuzzer/src/types";
+import type { Type } from "@/test/fuzzer/src/types";
+import { stringify } from "@/test/fuzzer/src/util";
+import { beginCell } from "@ton/core";
+import type { Address, Cell } from "@ton/core";
+import { sha256_sync } from "@ton/crypto";
+import { TreasuryContract } from "@ton/sandbox";
+import * as fc from "fast-check";
+
+/*export const AllowedType = {
+    Int: "Int",
+    OptInt: "Int?",
+    Bool: "Bool",
+    OptBool: "Bool?",
+    Cell: "Cell",
+    OptCell: "Cell?",
+    Slice: "Slice",
+    OptSlice: "Slice?",
+    Address: "Address",
+    OptAddress: "Address?",
+    String: "String",
+    OptString: "String?",
+} as const;
+
+export type AllowedTypeEnum = (typeof AllowedType)[keyof typeof AllowedType];
+*/
+
+export type GenInitConfig = {
+    // The minimum expression size
+    minSize: number;
+
+    // The maximum expression size
+    maxSize: number;
+
+    // The non-terminals to choose from. Non-terminals not listed here will
+    // be disallowed during generation
+    allowedNonTerminals: NonTerminalEnum[];
+
+    // The terminals to choose from. Terminals not listed here will
+    // be disallowed during generation
+    allowedTerminals: TerminalEnum[];
+
+    useIdentifiers: boolean;
+};
+
+export const NonTerminal = {
+    Initial: { terminal: false, literal: false, id: 0 },
+    Int: { terminal: false, literal: false, id: 1 },
+    OptInt: { terminal: false, literal: false, id: 2 },
+    LiteralInt: { terminal: false, literal: true, id: 3 },
+    // LiteralOptInt: { terminal: false, literal: true, id: 4 },
+    Bool: { terminal: false, literal: false, id: 4 },
+    OptBool: { terminal: false, literal: false, id: 5 },
+    LiteralBool: { terminal: false, literal: true, id: 6 },
+    // LiteralOptBool: { terminal: false, literal: true, id: 8 },
+    Cell: { terminal: false, literal: false, id: 7 },
+    OptCell: { terminal: false, literal: false, id: 8 },
+    LiteralCell: { terminal: false, literal: true, id: 9 },
+    // LiteralOptCell: { terminal: false, literal: true, id: 12 },
+    Slice: { terminal: false, literal: false, id: 10 },
+    OptSlice: { terminal: false, literal: false, id: 11 },
+    LiteralSlice: { terminal: false, literal: true, id: 12 },
+    // LiteralOptSlice: { terminal: false, literal: true, id: 16 },
+    Address: { terminal: false, literal: false, id: 13 },
+    OptAddress: { terminal: false, literal: false, id: 14 },
+    LiteralAddress: { terminal: false, literal: true, id: 15 },
+    // LiteralOptAddress: { terminal: false, literal: true, id: 20 },
+    String: { terminal: false, literal: false, id: 16 },
+    OptString: { terminal: false, literal: false, id: 17 },
+    LiteralString: { terminal: false, literal: true, id: 18 },
+    // LiteralOptString: { terminal: false, literal: true, id: 24 },
+} as const;
+
+export type NonTerminalEnum = (typeof NonTerminal)[keyof typeof NonTerminal];
+
+type GenericNonTerminal = {
+    id: number;
+    literal: boolean;
+    terminal: false;
+};
+
+export const Terminal = {
+    integer: { terminal: true, id: 1 },
+    add: { terminal: true, id: 2 },
+    minus: { terminal: true, id: 3 },
+    mult: { terminal: true, id: 4 },
+    div: { terminal: true, id: 5 },
+    mod: { terminal: true, id: 6 },
+    shift_r: { terminal: true, id: 7 },
+    shift_l: { terminal: true, id: 8 },
+    bit_and: { terminal: true, id: 9 },
+    bit_or: { terminal: true, id: 10 },
+    bit_xor: { terminal: true, id: 11 },
+    // unary_plus: { terminal: true, id: 12 },
+    unary_minus: { terminal: true, id: 12 },
+    bit_not: { terminal: true, id: 13 },
+
+    bool: { terminal: true, id: 14 },
+    eq: { terminal: true, id: 15 },
+    neq: { terminal: true, id: 16 },
+    lt: { terminal: true, id: 17 },
+    le: { terminal: true, id: 18 },
+    gt: { terminal: true, id: 19 },
+    ge: { terminal: true, id: 20 },
+    and: { terminal: true, id: 21 },
+    or: { terminal: true, id: 22 },
+    not: { terminal: true, id: 23 },
+
+    cell: { terminal: true, id: 24 },
+    //code_of: { terminal: true, id: 25 },
+
+    slice: { terminal: true, id: 25 },
+
+    address: { terminal: true, id: 26 },
+
+    string: { terminal: true, id: 27 },
+
+    // opt_inj: { terminal: true, id: 30 },
+    // null: { terminal: true, id: 30 },
+    non_null_assert: { terminal: true, id: 28 },
+
+    cond: { terminal: true, id: 29 },
+
+    id_int: { terminal: true, id: 30 },
+    id_opt_int: { terminal: true, id: 31 },
+    id_bool: { terminal: true, id: 32 },
+    id_opt_bool: { terminal: true, id: 33 },
+    id_cell: { terminal: true, id: 34 },
+    id_opt_cell: { terminal: true, id: 35 },
+    id_slice: { terminal: true, id: 36 },
+    id_opt_slice: { terminal: true, id: 37 },
+    id_address: { terminal: true, id: 38 },
+    id_opt_address: { terminal: true, id: 39 },
+    id_string: { terminal: true, id: 40 },
+    id_opt_string: { terminal: true, id: 41 },
+} as const;
+
+export type TerminalEnum = (typeof Terminal)[keyof typeof Terminal];
+
+type Token = TerminalEnum | GenericNonTerminal;
+
+type ExprProduction = {
+    tokens: Token[];
+    id: number;
+};
+
+const allProductions: ExprProduction[][] = [
+    [
+        // Productions for Initial
+        { id: 0, tokens: [NonTerminal.Int] },
+        { id: 1, tokens: [NonTerminal.OptInt] },
+        { id: 2, tokens: [NonTerminal.LiteralInt] },
+        // { id: 3, tokens: [NonTerminal.LiteralOptInt] },
+        { id: 3, tokens: [NonTerminal.Bool] },
+        { id: 4, tokens: [NonTerminal.OptBool] },
+        { id: 5, tokens: [NonTerminal.LiteralBool] },
+        // { id: 7, tokens: [NonTerminal.LiteralOptBool] },
+        { id: 6, tokens: [NonTerminal.Cell] },
+        { id: 7, tokens: [NonTerminal.OptCell] },
+        { id: 8, tokens: [NonTerminal.LiteralCell] },
+        // { id: 11, tokens: [NonTerminal.LiteralOptCell] },
+        { id: 9, tokens: [NonTerminal.Slice] },
+        { id: 10, tokens: [NonTerminal.OptSlice] },
+        { id: 11, tokens: [NonTerminal.LiteralSlice] },
+        // { id: 15, tokens: [NonTerminal.LiteralOptSlice] },
+        { id: 12, tokens: [NonTerminal.Address] },
+        { id: 13, tokens: [NonTerminal.OptAddress] },
+        { id: 14, tokens: [NonTerminal.LiteralAddress] },
+        // { id: 19, tokens: [NonTerminal.LiteralOptAddress] },
+        { id: 15, tokens: [NonTerminal.String] },
+        { id: 16, tokens: [NonTerminal.LiteralString] },
+        { id: 17, tokens: [NonTerminal.OptString] },
+        // { id: 23, tokens: [NonTerminal.LiteralOptString] },
+    ],
+    [
+        // Productions for Int
+        { id: 0, tokens: [Terminal.add, NonTerminal.Int, NonTerminal.Int] },
+        {
+            id: 1,
+            tokens: [Terminal.minus, NonTerminal.Int, NonTerminal.Int],
+        },
+        { id: 2, tokens: [Terminal.mult, NonTerminal.Int, NonTerminal.Int] },
+        { id: 3, tokens: [Terminal.div, NonTerminal.Int, NonTerminal.Int] },
+        { id: 4, tokens: [Terminal.mod, NonTerminal.Int, NonTerminal.Int] },
+        {
+            id: 5,
+            tokens: [Terminal.shift_r, NonTerminal.Int, NonTerminal.Int],
+        },
+        {
+            id: 6,
+            tokens: [Terminal.shift_l, NonTerminal.Int, NonTerminal.Int],
+        },
+        {
+            id: 7,
+            tokens: [Terminal.bit_and, NonTerminal.Int, NonTerminal.Int],
+        },
+        {
+            id: 8,
+            tokens: [Terminal.bit_or, NonTerminal.Int, NonTerminal.Int],
+        },
+        {
+            id: 9,
+            tokens: [Terminal.bit_xor, NonTerminal.Int, NonTerminal.Int],
+        },
+        // { id: 10, tokens: [Terminal.unary_plus, NonTerminal.Int] },
+        { id: 10, tokens: [Terminal.unary_minus, NonTerminal.Int] },
+        { id: 11, tokens: [Terminal.bit_not, NonTerminal.Int] },
+
+        { id: 12, tokens: [Terminal.non_null_assert, NonTerminal.OptInt] },
+        {
+            id: 13,
+            tokens: [
+                Terminal.cond,
+                NonTerminal.Bool,
+                NonTerminal.Int,
+                NonTerminal.Int,
+            ],
+        },
+        { id: 14, tokens: [Terminal.id_int] },
+        { id: 15, tokens: [NonTerminal.LiteralInt] },
+    ],
+    [
+        // Productions for OptInt
+        // { id: 0, tokens: [Terminal.opt_inj, NonTerminal.Int] },
+        {
+            id: 0,
+            tokens: [
+                Terminal.cond,
+                NonTerminal.Bool,
+                NonTerminal.OptInt,
+                NonTerminal.OptInt,
+            ],
+        },
+        { id: 1, tokens: [Terminal.id_opt_int] },
+        // { id: 2, tokens: [NonTerminal.LiteralOptInt] },
+    ],
+    [
+        // Productions for LiteralInt
+        { id: 0, tokens: [Terminal.integer] },
+    ],
+    // [
+    //     // Productions for LiteralOptInt
+    //     // { id: 0, tokens: [Terminal.null] },
+    //     // { id: 1, tokens: [Terminal.opt_inj, NonTerminal.LiteralInt] },
+    // ],
+    [
+        // Productions for Bool
+        { id: 0, tokens: [Terminal.eq, NonTerminal.Int, NonTerminal.Int] },
+        {
+            id: 1,
+            tokens: [Terminal.eq, NonTerminal.OptInt, NonTerminal.OptInt],
+        },
+        { id: 2, tokens: [Terminal.eq, NonTerminal.Bool, NonTerminal.Bool] },
+        {
+            id: 3,
+            tokens: [Terminal.eq, NonTerminal.OptBool, NonTerminal.OptBool],
+        },
+        {
+            id: 4,
+            tokens: [Terminal.eq, NonTerminal.Address, NonTerminal.Address],
+        },
+        {
+            id: 5,
+            tokens: [
+                Terminal.eq,
+                NonTerminal.OptAddress,
+                NonTerminal.OptAddress,
+            ],
+        },
+        { id: 6, tokens: [Terminal.eq, NonTerminal.Cell, NonTerminal.Cell] },
+        {
+            id: 7,
+            tokens: [Terminal.eq, NonTerminal.OptCell, NonTerminal.OptCell],
+        },
+        {
+            id: 8,
+            tokens: [Terminal.eq, NonTerminal.Slice, NonTerminal.Slice],
+        },
+        {
+            id: 9,
+            tokens: [Terminal.eq, NonTerminal.OptSlice, NonTerminal.OptSlice],
+        },
+        {
+            id: 10,
+            tokens: [Terminal.eq, NonTerminal.String, NonTerminal.String],
+        },
+        {
+            id: 11,
+            tokens: [Terminal.eq, NonTerminal.OptString, NonTerminal.OptString],
+        },
+
+        { id: 12, tokens: [Terminal.neq, NonTerminal.Int, NonTerminal.Int] },
+        {
+            id: 13,
+            tokens: [Terminal.neq, NonTerminal.OptInt, NonTerminal.OptInt],
+        },
+        {
+            id: 14,
+            tokens: [Terminal.neq, NonTerminal.Bool, NonTerminal.Bool],
+        },
+        {
+            id: 15,
+            tokens: [Terminal.neq, NonTerminal.OptBool, NonTerminal.OptBool],
+        },
+        {
+            id: 16,
+            tokens: [Terminal.neq, NonTerminal.Address, NonTerminal.Address],
+        },
+        {
+            id: 17,
+            tokens: [
+                Terminal.neq,
+                NonTerminal.OptAddress,
+                NonTerminal.OptAddress,
+            ],
+        },
+        {
+            id: 18,
+            tokens: [Terminal.neq, NonTerminal.Cell, NonTerminal.Cell],
+        },
+        {
+            id: 19,
+            tokens: [Terminal.neq, NonTerminal.OptCell, NonTerminal.OptCell],
+        },
+        {
+            id: 20,
+            tokens: [Terminal.neq, NonTerminal.Slice, NonTerminal.Slice],
+        },
+        {
+            id: 21,
+            tokens: [Terminal.neq, NonTerminal.OptSlice, NonTerminal.OptSlice],
+        },
+        {
+            id: 22,
+            tokens: [Terminal.neq, NonTerminal.String, NonTerminal.String],
+        },
+        {
+            id: 23,
+            tokens: [
+                Terminal.neq,
+                NonTerminal.OptString,
+                NonTerminal.OptString,
+            ],
+        },
+
+        { id: 24, tokens: [Terminal.lt, NonTerminal.Int, NonTerminal.Int] },
+        { id: 25, tokens: [Terminal.le, NonTerminal.Int, NonTerminal.Int] },
+        { id: 26, tokens: [Terminal.gt, NonTerminal.Int, NonTerminal.Int] },
+        { id: 27, tokens: [Terminal.ge, NonTerminal.Int, NonTerminal.Int] },
+        {
+            id: 28,
+            tokens: [Terminal.and, NonTerminal.Bool, NonTerminal.Bool],
+        },
+        {
+            id: 29,
+            tokens: [Terminal.or, NonTerminal.Bool, NonTerminal.Bool],
+        },
+        { id: 30, tokens: [Terminal.not, NonTerminal.Bool] },
+
+        { id: 31, tokens: [Terminal.non_null_assert, NonTerminal.OptBool] },
+        {
+            id: 32,
+            tokens: [
+                Terminal.cond,
+                NonTerminal.Bool,
+                NonTerminal.Bool,
+                NonTerminal.Bool,
+            ],
+        },
+        { id: 33, tokens: [Terminal.id_bool] },
+        { id: 34, tokens: [NonTerminal.LiteralBool] },
+    ],
+    [
+        // Productions for OptBool
+        // { id: 0, tokens: [Terminal.opt_inj, NonTerminal.Bool] },
+        {
+            id: 0,
+            tokens: [
+                Terminal.cond,
+                NonTerminal.Bool,
+                NonTerminal.OptBool,
+                NonTerminal.OptBool,
+            ],
+        },
+        { id: 1, tokens: [Terminal.id_opt_bool] },
+        // { id: 3, tokens: [NonTerminal.LiteralOptBool] },
+    ],
+    [
+        // Productions for LiteralBool
+        { id: 0, tokens: [Terminal.bool] },
+    ],
+    // [
+    //     // Productions for LiteralOptBool
+    //     // { id: 0, tokens: [Terminal.null] },
+    //     // { id: 1, tokens: [Terminal.opt_inj, NonTerminal.LiteralBool] },
+    // ],
+    [
+        // Productions for Cell
+        //{ id: 0, tokens: [Terminal.code_of] },
+        { id: 0, tokens: [Terminal.non_null_assert, NonTerminal.OptCell] },
+        {
+            id: 1,
+            tokens: [
+                Terminal.cond,
+                NonTerminal.Bool,
+                NonTerminal.Cell,
+                NonTerminal.Cell,
+            ],
+        },
+        { id: 2, tokens: [Terminal.id_cell] },
+        { id: 3, tokens: [NonTerminal.LiteralCell] },
+    ],
+    [
+        // Productions for OptCell
+        // { id: 0, tokens: [Terminal.opt_inj, NonTerminal.Cell] },
+        {
+            id: 0,
+            tokens: [
+                Terminal.cond,
+                NonTerminal.Bool,
+                NonTerminal.OptCell,
+                NonTerminal.OptCell,
+            ],
+        },
+        { id: 1, tokens: [Terminal.id_opt_cell] },
+        // { id: 2, tokens: [NonTerminal.LiteralOptCell] },
+    ],
+    [
+        // Productions for LiteralCell
+        { id: 0, tokens: [Terminal.cell] },
+    ],
+    // [
+    //     // Productions for LiteralOptCell
+    //     // { id: 0, tokens: [Terminal.null] },
+    //     // { id: 1, tokens: [Terminal.opt_inj, NonTerminal.LiteralCell] },
+    // ],
+    [
+        // Productions for Slice
+        { id: 0, tokens: [Terminal.non_null_assert, NonTerminal.OptSlice] },
+        {
+            id: 1,
+            tokens: [
+                Terminal.cond,
+                NonTerminal.Bool,
+                NonTerminal.Slice,
+                NonTerminal.Slice,
+            ],
+        },
+        { id: 2, tokens: [Terminal.id_slice] },
+        { id: 3, tokens: [NonTerminal.LiteralSlice] },
+    ],
+    [
+        // Productions for OptSlice
+        // { id: 0, tokens: [Terminal.opt_inj, NonTerminal.Slice] },
+        {
+            id: 0,
+            tokens: [
+                Terminal.cond,
+                NonTerminal.Bool,
+                NonTerminal.OptSlice,
+                NonTerminal.OptSlice,
+            ],
+        },
+        { id: 1, tokens: [Terminal.id_opt_slice] },
+        // { id: 2, tokens: [NonTerminal.LiteralOptSlice] },
+    ],
+    [
+        // Productions for LiteralSlice
+        { id: 0, tokens: [Terminal.slice] },
+    ],
+    // [
+    //     // Productions for LiteralOptSlice
+    //     // { id: 0, tokens: [Terminal.null] },
+    //     // { id: 1, tokens: [Terminal.opt_inj, NonTerminal.LiteralSlice] },
+    // ],
+    [
+        // Productions for Address
+        {
+            id: 0,
+            tokens: [Terminal.non_null_assert, NonTerminal.OptAddress],
+        },
+        {
+            id: 1,
+            tokens: [
+                Terminal.cond,
+                NonTerminal.Bool,
+                NonTerminal.Address,
+                NonTerminal.Address,
+            ],
+        },
+        { id: 2, tokens: [Terminal.id_address] },
+        { id: 3, tokens: [NonTerminal.LiteralAddress] },
+    ],
+    [
+        // Productions for OptAddress
+        // { id: 0, tokens: [Terminal.opt_inj, NonTerminal.Address] },
+        {
+            id: 0,
+            tokens: [
+                Terminal.cond,
+                NonTerminal.Bool,
+                NonTerminal.OptAddress,
+                NonTerminal.OptAddress,
+            ],
+        },
+        { id: 1, tokens: [Terminal.id_opt_address] },
+        // { id: 3, tokens: [NonTerminal.LiteralOptAddress] },
+    ],
+    [
+        // Productions for LiteralAddress
+        { id: 0, tokens: [Terminal.address] },
+    ],
+    // [
+    //     // Productions for LiteralOptAddress
+    //     // { id: 0, tokens: [Terminal.null] },
+    //     // { id: 1, tokens: [Terminal.opt_inj, NonTerminal.LiteralAddress] },
+    // ],
+    [
+        // Productions for String
+        { id: 0, tokens: [Terminal.non_null_assert, NonTerminal.OptString] },
+        {
+            id: 1,
+            tokens: [
+                Terminal.cond,
+                NonTerminal.Bool,
+                NonTerminal.String,
+                NonTerminal.String,
+            ],
+        },
+        { id: 2, tokens: [Terminal.id_string] },
+        { id: 3, tokens: [NonTerminal.LiteralString] },
+    ],
+    [
+        // Productions for OptString
+        // { id: 0, tokens: [Terminal.opt_inj, NonTerminal.String] },
+        {
+            id: 0,
+            tokens: [
+                Terminal.cond,
+                NonTerminal.Bool,
+                NonTerminal.OptString,
+                NonTerminal.OptString,
+            ],
+        },
+        { id: 1, tokens: [Terminal.id_opt_string] },
+        // { id: 2, tokens: [NonTerminal.LiteralOptString] },
+    ],
+    [
+        // Productions for LiteralString
+        { id: 0, tokens: [Terminal.string] },
+    ],
+    // [
+    //     // Productions for LiteralOptString
+    //     // { id: 0, tokens: [Terminal.null] },
+    //     // { id: 1, tokens: [Terminal.opt_inj, NonTerminal.LiteralString] },
+    // ],
+];
+
+function sum(counts: number[]): number {
+    // If at least one array element (which are counts represented as logarithms) is -Inf, it means that they represent the count 0.
+    // therefore, they can be filtered out from the array, because they will not affect the final sum
+    const filteredCounts = counts.filter((n) => n !== Number.NEGATIVE_INFINITY);
+
+    if (filteredCounts.length === 0) {
+        // The sum would be 0. So, return -Inf
+        return Number.NEGATIVE_INFINITY;
+    }
+    if (filteredCounts.length === 1) {
+        return filteredCounts[0]!;
+    }
+    const first = filteredCounts[0]!;
+    // For the general case, we reduce the array thanks to the following formula:
+    // log(x + y) = log x + log(1 + 2^(log y - log x))
+    // which tells us how we should add counts when they are represented as logarithms
+    return filteredCounts
+        .slice(1)
+        .reduce(
+            (prev, curr) => prev + Math.log2(1 + 2 ** (curr - prev)),
+            first,
+        );
+}
+
+function normalizeArray(counts: number[]): number[] {
+    // Any -Inf represents a count of 0, which means that such index should never get selected.
+    // So, it is enough to transform -Inf back to 0.
+    // Any 0 represents a count of 1. Since such index has a non-zero probability to be selected,
+    // we change the 0s to 1s.
+    // Also, 1 represents a count of 2. So, we transform 1s to 2s, to avoid
+    // squashing counts 1s and 2s together.
+    // The rest of numbers we take their ceil to transform them into integers.
+    return counts.map((n) => {
+        if (n === Number.NEGATIVE_INFINITY) {
+            return 0;
+        }
+        if (n === 0) {
+            return 1;
+        }
+        if (n === 1) {
+            return 2;
+        }
+        return Math.ceil(n);
+    });
+}
+
+function multiply(first: number, second: number): number {
+    // If at least one input (which are counts represented as logarithms) is -Inf, it means that they represent the count 0.
+    // therefore, their multiplication is also 0 (or -Inf in the logarithm representation)
+    if (
+        first === Number.NEGATIVE_INFINITY ||
+        second === Number.NEGATIVE_INFINITY
+    ) {
+        return Number.NEGATIVE_INFINITY;
+    } else {
+        // To multiply two counts represented as logarithms, it is enough to add their representations
+        // thanks to this formula:
+        // log(x * y) = log(x) + log(y)
+        return first + second;
+    }
+}
+
+function transform(n: number): number {
+    // We transform counts into their base-2 logarithmic representation
+    return Math.log2(n);
+}
+
+function filterProductions(initConfig: GenInitConfig): {
+    productions: ExprProduction[][];
+    nonTerminals: GenericNonTerminal[];
+    reindexMap: Map<number, number>;
+} {
+    const nonTerminalIdsToInclude: Set<number> = new Set(
+        initConfig.allowedNonTerminals.map((e) => e.id),
+    );
+    const terminalIdsToInclude: Set<number> = new Set(
+        initConfig.allowedTerminals.map((e) => e.id),
+    );
+
+    // Make a copy of all the productions
+    let productions: ExprProduction[][] = [];
+    for (let i = 0; i < allProductions.length; i++) {
+        productions[i] = allProductions[i]!.map((prod) => {
+            return { id: prod.id, tokens: prod.tokens };
+        });
+    }
+
+    // If flag useIdentifiers is off, remove generation of identifiers
+    if (!initConfig.useIdentifiers) {
+        [
+            Terminal.id_address,
+            Terminal.id_bool,
+            Terminal.id_cell,
+            Terminal.id_int,
+            Terminal.id_opt_address,
+            Terminal.id_opt_bool,
+            Terminal.id_opt_cell,
+            Terminal.id_opt_int,
+            Terminal.id_opt_slice,
+            Terminal.id_opt_string,
+            Terminal.id_slice,
+            Terminal.id_string,
+        ].forEach((terminal) => {
+            terminalIdsToInclude.delete(terminal.id);
+        });
+    }
+
+    // Remove productions that use terminals and non-terminals not listed in the allowed lists.
+    let initialNonTerminalsCount;
+    do {
+        initialNonTerminalsCount = nonTerminalIdsToInclude.size;
+
+        for (let i = 0; i < productions.length; i++) {
+            productions[i] = productions[i]!.filter((prod) =>
+                prod.tokens.every((t) => {
+                    if (t.terminal) {
+                        return terminalIdsToInclude.has(t.id);
+                    } else {
+                        return nonTerminalIdsToInclude.has(t.id);
+                    }
+                }),
+            );
+            // If non-terminal i has no productions at the end, we need
+            // to remove i from the final non-terminals
+            // and go again through the process of removing productions
+            if (productions[i]!.length === 0) {
+                nonTerminalIdsToInclude.delete(i);
+            }
+        }
+    } while (initialNonTerminalsCount !== nonTerminalIdsToInclude.size);
+
+    // Remove unused non-terminals, and reindex them
+    const reindexMap: Map<number, number> = new Map();
+    const nonTerminalsFiltered = Object.values(NonTerminal).filter((n) =>
+        nonTerminalIdsToInclude.has(n.id),
+    );
+    nonTerminalsFiltered.forEach((n, newIndex) => {
+        reindexMap.set(n.id, newIndex);
+    });
+    const nonTerminals = nonTerminalsFiltered.map((n, newIndex) => {
+        return { id: newIndex, literal: n.literal, terminal: n.terminal };
+    });
+
+    // Remove productions belonging to removed non-terminals.
+    productions = productions.filter((_, index) =>
+        nonTerminalIdsToInclude.has(index),
+    );
+
+    // Reindex all the productions, including non-terminal tokens occurring inside the production
+    for (let i = 0; i < productions.length; i++) {
+        productions[i] = productions[i]!.map((prod, newIndex) => {
+            return {
+                id: newIndex,
+                tokens: prod.tokens.map((t) => {
+                    if (t.terminal) {
+                        return t;
+                    } else {
+                        const newIndex = reindexMap.get(t.id);
+                        if (typeof newIndex === "undefined") {
+                            throw new Error(
+                                `Invalid old index ${t.id}: it does not have a re-indexing`,
+                            );
+                        }
+                        return {
+                            id: newIndex,
+                            literal: t.literal,
+                            terminal: t.terminal,
+                        };
+                    }
+                }),
+            };
+        });
+    }
+
+    return {
+        productions,
+        nonTerminals,
+        reindexMap,
+    };
+}
+
+function computeCountTables(
+    minSize: number,
+    maxSize: number,
+    finalProductions: ExprProduction[][],
+    nonTerminals: GenericNonTerminal[],
+): {
+    nonTerminalCounts: number[][][];
+    sizeSplitCounts: number[][][][][];
+    totalCounts: number[][];
+} {
+    const nonTerminalCounts: number[][][] = [];
+    const sizeSplitCounts: number[][][][][] = [];
+    const totalCounts: number[][] = [];
+
+    function updateTotalCounts(idx: number, size: number, count: number) {
+        if (typeof totalCounts[idx] === "undefined") {
+            totalCounts[idx] = Array(maxSize + 1);
+            totalCounts[idx][size] = count;
+        } else {
+            totalCounts[idx][size] = count;
+        }
+    }
+
+    function updateNonTerminalCounts(
+        idx: number,
+        size: number,
+        counts: number[],
+    ) {
+        if (typeof nonTerminalCounts[idx] === "undefined") {
+            nonTerminalCounts[idx] = Array(maxSize + 1);
+            nonTerminalCounts[idx][size] = counts;
+        } else {
+            nonTerminalCounts[idx][size] = counts;
+        }
+    }
+
+    function peekNonTerminalCounts(
+        idx: number,
+        size: number,
+    ): number[] | undefined {
+        if (typeof nonTerminalCounts[idx] !== "undefined") {
+            return nonTerminalCounts[idx][size];
+        } else {
+            return undefined;
+        }
+    }
+
+    function updateSizeSplitCounts(
+        nonTerminalIndex: number,
+        prodIndex: number,
+        tokenIndex: number,
+        size: number,
+        counts: number[],
+    ) {
+        const prods = getProductions(finalProductions, nonTerminalIndex);
+        const prod = getProductionAt(prods, prodIndex);
+
+        if (typeof sizeSplitCounts[nonTerminalIndex] === "undefined") {
+            sizeSplitCounts[nonTerminalIndex] = Array(prods.length);
+            sizeSplitCounts[nonTerminalIndex][prodIndex] = Array(
+                prod.tokens.length,
+            );
+            sizeSplitCounts[nonTerminalIndex][prodIndex][tokenIndex] = Array(
+                maxSize + 1,
+            );
+            sizeSplitCounts[nonTerminalIndex][prodIndex][tokenIndex][size] =
+                counts;
+            return;
+        } else {
+            if (
+                typeof sizeSplitCounts[nonTerminalIndex][prodIndex] ===
+                "undefined"
+            ) {
+                sizeSplitCounts[nonTerminalIndex][prodIndex] = Array(
+                    prod.tokens.length,
+                );
+                sizeSplitCounts[nonTerminalIndex][prodIndex][tokenIndex] =
+                    Array(maxSize + 1);
+                sizeSplitCounts[nonTerminalIndex][prodIndex][tokenIndex][size] =
+                    counts;
+                return;
+            }
+            if (
+                typeof sizeSplitCounts[nonTerminalIndex][prodIndex][
+                    tokenIndex
+                ] === "undefined"
+            ) {
+                sizeSplitCounts[nonTerminalIndex][prodIndex][tokenIndex] =
+                    Array(maxSize + 1);
+                sizeSplitCounts[nonTerminalIndex][prodIndex][tokenIndex][size] =
+                    counts;
+                return;
+            }
+            sizeSplitCounts[nonTerminalIndex][prodIndex][tokenIndex][size] =
+                counts;
+        }
+    }
+
+    function peekSizeSplitCounts(
+        nonTerminalIndex: number,
+        prodIndex: number,
+        tokenIndex: number,
+        size: number,
+    ): number[] | undefined {
+        return sizeSplitCounts[nonTerminalIndex]?.[prodIndex]?.[tokenIndex]?.[
+            size
+        ];
+    }
+
+    function countFromNonTerminal(id: number, size: number): number[] {
+        const peekedCounts = peekNonTerminalCounts(id, size);
+        if (typeof peekedCounts !== "undefined") {
+            return peekedCounts;
+        }
+        const prods = getProductions(finalProductions, id);
+        return prods.map((prod) => sum(countFromProduction(id, prod, 0, size)));
+    }
+
+    function countFromProduction(
+        nonTerminalIndex: number,
+        production: ExprProduction,
+        tokenIndex: number,
+        size: number,
+    ): number[] {
+        if (size === 0) {
+            return [];
+        }
+        const peekedCounts = peekSizeSplitCounts(
+            nonTerminalIndex,
+            production.id,
+            tokenIndex,
+            size,
+        );
+        if (typeof peekedCounts !== "undefined") {
+            return peekedCounts;
+        }
+        const head = getTokenAt(production.tokens, tokenIndex);
+        if (head.terminal) {
+            if (tokenIndex === production.tokens.length - 1) {
+                // i.e., the head is the last symbol in the production
+                // Transform the values 1 and 0 to whatever representation of counts we are currently using
+                return size === 1 ? [transform(1)] : [transform(0)];
+            } else {
+                return [
+                    sum(
+                        countFromProduction(
+                            nonTerminalIndex,
+                            production,
+                            tokenIndex + 1,
+                            size - 1,
+                        ),
+                    ),
+                ];
+            }
+        }
+        // head is not a terminal
+        if (tokenIndex === production.tokens.length - 1) {
+            // i.e., the head is the last symbol in the production
+            return [sum(countFromNonTerminal(head.id, size))];
+        } else {
+            const result: number[] = [];
+
+            for (
+                let l = 1;
+                l <= size - production.tokens.length + tokenIndex + 1;
+                l++
+            ) {
+                const partition1 = sum(countFromNonTerminal(head.id, l));
+                const partition2 = sum(
+                    countFromProduction(
+                        nonTerminalIndex,
+                        production,
+                        tokenIndex + 1,
+                        size - l,
+                    ),
+                );
+                result.push(multiply(partition1, partition2));
+            }
+            return result;
+        }
+    }
+
+    function doCountsForNonTerminals(nonTerminals: GenericNonTerminal[]) {
+        // First, compute the counts of all the non-terminals that produce literals
+
+        // The first step is to initialize the tables for size 0
+        for (const nonTerminal of nonTerminals) {
+            const nonTerminalIdx = nonTerminal.id;
+
+            const productions = getProductions(
+                finalProductions,
+                nonTerminalIdx,
+            );
+
+            // Transform count 0 to whatever representation of counts we are currently using
+            updateNonTerminalCounts(
+                nonTerminalIdx,
+                0,
+                productions.map((_) => transform(0)),
+            );
+
+            for (const prod of productions) {
+                for (
+                    let tokenIndx = 0;
+                    tokenIndx < prod.tokens.length;
+                    tokenIndx++
+                ) {
+                    updateSizeSplitCounts(
+                        nonTerminalIdx,
+                        prod.id,
+                        tokenIndx,
+                        0,
+                        [],
+                    );
+                }
+            }
+        }
+
+        // Now, for the rest of sizes
+        for (let size = 1; size <= maxSize; size++) {
+            for (const nonTerminal of nonTerminals) {
+                const nonTerminalIdx = nonTerminal.id;
+
+                const productions = getProductions(
+                    finalProductions,
+                    nonTerminalIdx,
+                );
+
+                for (const prod of productions) {
+                    for (
+                        let tokenIndx = 0;
+                        tokenIndx < prod.tokens.length;
+                        tokenIndx++
+                    ) {
+                        updateSizeSplitCounts(
+                            nonTerminalIdx,
+                            prod.id,
+                            tokenIndx,
+                            size,
+                            countFromProduction(
+                                nonTerminalIdx,
+                                prod,
+                                tokenIndx,
+                                size,
+                            ),
+                        );
+                    }
+                }
+
+                updateNonTerminalCounts(
+                    nonTerminalIdx,
+                    size,
+                    countFromNonTerminal(nonTerminalIdx, size),
+                );
+            }
+        }
+    }
+
+    function doTotalCounts() {
+        // From 0 to minSize-1, set counts to 0, since we are not going to choose those sizes
+        // Transform the count 0 to whatever representation we are currently using
+        for (let size = 0; size < minSize; size++) {
+            for (const nonTerminal of nonTerminals) {
+                updateTotalCounts(nonTerminal.id, size, transform(0));
+            }
+        }
+
+        for (let size = minSize; size <= maxSize; size++) {
+            for (const nonTerminal of nonTerminals) {
+                updateTotalCounts(
+                    nonTerminal.id,
+                    size,
+                    sum(
+                        lookupNonTerminalCounts(
+                            nonTerminalCounts,
+                            nonTerminal.id,
+                            size,
+                        ),
+                    ),
+                );
+            }
+        }
+    }
+
+    function normalizeCounts() {
+        // The total counts
+        for (const nonTerminal of nonTerminals) {
+            const counts = totalCounts[nonTerminal.id];
+            if (typeof counts === "undefined") {
+                throw new Error(`Index ${nonTerminal.id} out of bounds`);
+            }
+            const newCounts = normalizeArray(counts);
+            totalCounts[nonTerminal.id] = newCounts;
+        }
+
+        // The non-terminal counts
+        for (const nonTerminal of nonTerminals) {
+            for (let size = 0; size <= maxSize; size++) {
+                const counts = lookupNonTerminalCounts(
+                    nonTerminalCounts,
+                    nonTerminal.id,
+                    size,
+                );
+                const newCounts = normalizeArray(counts);
+                updateNonTerminalCounts(nonTerminal.id, size, newCounts);
+            }
+        }
+
+        // Split size counts
+        for (const nonTerminal of nonTerminals) {
+            const nonTerminalIdx = nonTerminal.id;
+
+            const productions = getProductions(
+                finalProductions,
+                nonTerminalIdx,
+            );
+
+            for (const prod of productions) {
+                for (
+                    let tokenIndx = 0;
+                    tokenIndx < prod.tokens.length;
+                    tokenIndx++
+                ) {
+                    for (let size = 0; size <= maxSize; size++) {
+                        const counts = lookupSizeSplitCounts(
+                            sizeSplitCounts,
+                            nonTerminal.id,
+                            prod.id,
+                            tokenIndx,
+                            size,
+                        );
+                        const newCounts = normalizeArray(counts);
+                        updateSizeSplitCounts(
+                            nonTerminal.id,
+                            prod.id,
+                            tokenIndx,
+                            size,
+                            newCounts,
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    // First, fill the non-terminals that compute literals
+    doCountsForNonTerminals(
+        nonTerminals.filter((nonTerminal) => nonTerminal.literal),
+    );
+
+    // Now the rest of non-terminals
+    doCountsForNonTerminals(
+        nonTerminals.filter((nonTerminal) => !nonTerminal.literal),
+    );
+
+    doTotalCounts();
+
+    // Now that the tables are filled, we need to normalize the counts
+    // into non-negative integers (because they may be currently encoded
+    // in a different way. For example, when using logarithms to represent
+    // counts, the numbers have fractional part, and may also include -Inf).
+    normalizeCounts();
+
+    return {
+        nonTerminalCounts,
+        sizeSplitCounts,
+        totalCounts,
+    };
+}
+
+function lookupSizeSplitCounts(
+    sizeSplitCounts: number[][][][][],
+    nonTerminalIndex: number,
+    prodIndex: number,
+    tokenIndex: number,
+    size: number,
+): number[] {
+    const nTCounts = sizeSplitCounts[nonTerminalIndex];
+    if (typeof nTCounts === "undefined") {
+        throw new Error(`Index ${nonTerminalIndex} out of bounds`);
+    }
+    const prodCounts = nTCounts[prodIndex];
+    if (typeof prodCounts === "undefined") {
+        throw new Error(`Index ${prodIndex} out of bounds`);
+    }
+    const tokenCounts = prodCounts[tokenIndex];
+    if (typeof tokenCounts === "undefined") {
+        throw new Error(`Index ${tokenIndex} out of bounds`);
+    }
+    const result = tokenCounts[size];
+    if (typeof result === "undefined") {
+        throw new Error(`Index ${size} out of bounds`);
+    }
+    return result;
+}
+
+function lookupNonTerminalCounts(
+    nonTerminalCounts: number[][][],
+    nonTerminalIndex: number,
+    size: number,
+): number[] {
+    const nTCounts = nonTerminalCounts[nonTerminalIndex];
+    if (typeof nTCounts === "undefined") {
+        throw new Error(`Index ${nonTerminalIndex} out of bounds`);
+    }
+    const result = nTCounts[size];
+    if (typeof result === "undefined") {
+        throw new Error(`Index ${size} out of bounds`);
+    }
+    return result;
+}
+
+function lookupTotalCounts(
+    totalCounts: number[][],
+    nonTerminalIndex: number,
+): number[] {
+    const nTCounts = totalCounts[nonTerminalIndex];
+    if (typeof nTCounts === "undefined") {
+        throw new Error(`Index ${nonTerminalIndex} out of bounds`);
+    }
+    return nTCounts;
+}
+
+function getProductions(
+    productions: ExprProduction[][],
+    idx: number,
+): ExprProduction[] {
+    const prods = productions[idx];
+    if (typeof prods === "undefined") {
+        throw new Error(`${idx} is not a valid id for a non-terminal`);
+    }
+    return prods;
+}
+
+function getTokenAt(tokens: Token[], id: number): Token {
+    const token = tokens[id];
+    if (typeof token === "undefined") {
+        throw new Error(`Index ${id} is out of bounds`);
+    }
+    return token;
+}
+
+function getNonTerminalAt(tokens: Token[], id: number): GenericNonTerminal {
+    const token = getTokenAt(tokens, id);
+    if (token.terminal) {
+        throw new Error(`Was expecting a non-terminal`);
+    }
+    return token;
+}
+
+function getProductionAt(prods: ExprProduction[], id: number): ExprProduction {
+    const prod = prods[id];
+    if (typeof prod === "undefined") {
+        throw new Error(`Index ${id} out of bounds in productions array`);
+    }
+    return prod;
+}
+
+function makeExpression(
+    astF: FactoryAst,
+    nonTerminalId: number,
+    scope: Scope,
+    nonTerminalCounts: number[][][],
+    sizeSplitCounts: number[][][][][],
+    finalProductions: ExprProduction[][],
+    size: number,
+): fc.Arbitrary<Ast.Expression> {
+    const makeF = getMakeAst(astF);
+    const interpreter = new Interpreter(getAstUtil(astF));
+
+    function genFromNonTerminal(
+        id: number,
+        size: number,
+    ): fc.Arbitrary<Ast.Expression> {
+        const prods = getProductions(finalProductions, id);
+        const nonTerminalOptions = lookupNonTerminalCounts(
+            nonTerminalCounts,
+            id,
+            size,
+        );
+
+        const weightedNonTerminalOptions: fc.WeightedArbitrary<number>[] =
+            nonTerminalOptions.map((w, i) => {
+                return { arbitrary: fc.constant(i), weight: w };
+            });
+        return fc
+            .oneof(...weightedNonTerminalOptions)
+            .chain((chosenProdIndex) => {
+                const production = getProductionAt(prods, chosenProdIndex);
+                return genFromProduction(id, production, size);
+            });
+    }
+
+    function genFromProduction(
+        nonTerminalIndex: number,
+        production: ExprProduction,
+        size: number,
+    ): fc.Arbitrary<Ast.Expression> {
+        const head = getTokenAt(production.tokens, 0);
+        if (head.terminal) {
+            // The production must have the form: N -> head list_of_non_terminals
+            // where head indicates the kind of tree we need to produce
+            return makeTree(
+                nonTerminalIndex,
+                production.id,
+                head,
+                production.tokens.slice(1),
+                size,
+            );
+        }
+        // head is not a terminal
+        // The production must have the form N -> head
+        return genFromNonTerminal(head.id, size);
+    }
+
+    function chooseSizeSplit(
+        nonTerminalIndex: number,
+        prodIndex: number,
+        tokenIndex: number,
+        size: number,
+    ): fc.Arbitrary<number> {
+        const sizeSplits = lookupSizeSplitCounts(
+            sizeSplitCounts,
+            nonTerminalIndex,
+            prodIndex,
+            tokenIndex,
+            size,
+        );
+        // We need to add 1 to the result because sizes
+        // in splits are always positive. So, index 0
+        // represents size 1, and so on.
+        const weightedSizeSplits: fc.WeightedArbitrary<number>[] =
+            sizeSplits.map((w, i) => {
+                return { arbitrary: fc.constant(i + 1), weight: w };
+            });
+        return fc.oneof(...weightedSizeSplits);
+    }
+
+    function handleShiftOperators(
+        op: Ast.BinaryOperation,
+        expr: Ast.Expression,
+    ): fc.Arbitrary<Ast.Expression> {
+        if (op !== "<<" && op !== ">>") {
+            return fc.constant(expr);
+        }
+        try {
+            const literal = interpreter.interpretExpression(expr);
+            if (literal.kind !== "number") {
+                // Generate an integer in range [0..256]
+                return fc
+                    .bigInt(0n, 256n)
+                    .map((n) => makeF.makeDummyNumber(10, n));
+            }
+            if (literal.value >= 0n && literal.value <= 256n) {
+                return fc.constant(expr);
+            } else {
+                // Generate an integer in range [0..256]
+                return fc
+                    .bigInt(0n, 256n)
+                    .map((n) => makeF.makeDummyNumber(10, n));
+            }
+        } catch (_) {
+            // Any kind of error, leave the expr as is
+            return fc.constant(expr);
+        }
+    }
+
+    function makeBinaryOperatorTree(
+        op: Ast.BinaryOperation,
+        nonTerminalIndex: number,
+        prodIndex: number,
+        rest: Token[],
+        size: number,
+    ): fc.Arbitrary<Ast.Expression> {
+        const currSize = size - 1;
+        // Choose a single split for the size
+        return chooseSizeSplit(nonTerminalIndex, prodIndex, 1, currSize).chain(
+            (sizeSplit) => {
+                const leftNonTerminal = getNonTerminalAt(rest, 0);
+                const rightNonTerminal = getNonTerminalAt(rest, 1);
+
+                return genFromNonTerminal(leftNonTerminal.id, sizeSplit).chain(
+                    (leftOperand) => {
+                        return genFromNonTerminal(
+                            rightNonTerminal.id,
+                            currSize - sizeSplit,
+                        ).chain((rightOperand) => {
+                            // We need special logic to handle the shift operators, because they check
+                            // at compile time if their right-hand side is within the range [0..256].
+                            return handleShiftOperators(op, rightOperand).map(
+                                (squashedRightOperand) =>
+                                    makeF.makeDummyOpBinary(
+                                        op,
+                                        leftOperand,
+                                        squashedRightOperand,
+                                    ),
+                            );
+                        });
+                    },
+                );
+            },
+        );
+    }
+
+    function makeUnaryOperatorTree(
+        op: Ast.UnaryOperation,
+        rest: Token[],
+        size: number,
+    ): fc.Arbitrary<Ast.Expression> {
+        const currSize = size - 1;
+
+        const operandNonTerminal = getNonTerminalAt(rest, 0);
+        return genFromNonTerminal(operandNonTerminal.id, currSize).map(
+            (operand) => makeF.makeDummyOpUnary(op, operand),
+        );
+    }
+
+    function makeIdentifier(ty: Type): fc.Arbitrary<Ast.Expression> {
+        const names = scope.getNamesRecursive("let", ty);
+        if (names.length === 0) {
+            throw new Error(
+                `There must exist at least one identifier for type ${stringify(ty, 0)}`,
+            );
+        }
+        return fc.constantFrom(...names).map((id) => makeF.makeDummyId(id));
+    }
+
+    function makeTree(
+        nonTerminalIndex: number,
+        prodIndex: number,
+        head: TerminalEnum,
+        rest: Token[],
+        size: number,
+    ): fc.Arbitrary<Ast.Expression> {
+        switch (head.id) {
+            case Terminal.integer.id: {
+                return _generateIntBitLength(257, true).map((i) =>
+                    makeF.makeDummyNumber(10, i),
+                );
+            }
+            case Terminal.add.id: {
+                return makeBinaryOperatorTree(
+                    "+",
+                    nonTerminalIndex,
+                    prodIndex,
+                    rest,
+                    size,
+                );
+            }
+            case Terminal.minus.id: {
+                return makeBinaryOperatorTree(
+                    "-",
+                    nonTerminalIndex,
+                    prodIndex,
+                    rest,
+                    size,
+                );
+            }
+            case Terminal.mult.id: {
+                return makeBinaryOperatorTree(
+                    "*",
+                    nonTerminalIndex,
+                    prodIndex,
+                    rest,
+                    size,
+                );
+            }
+            case Terminal.div.id: {
+                return makeBinaryOperatorTree(
+                    "/",
+                    nonTerminalIndex,
+                    prodIndex,
+                    rest,
+                    size,
+                );
+            }
+            case Terminal.mod.id: {
+                return makeBinaryOperatorTree(
+                    "%",
+                    nonTerminalIndex,
+                    prodIndex,
+                    rest,
+                    size,
+                );
+            }
+            case Terminal.shift_r.id: {
+                return makeBinaryOperatorTree(
+                    ">>",
+                    nonTerminalIndex,
+                    prodIndex,
+                    rest,
+                    size,
+                );
+            }
+            case Terminal.shift_l.id: {
+                return makeBinaryOperatorTree(
+                    "<<",
+                    nonTerminalIndex,
+                    prodIndex,
+                    rest,
+                    size,
+                );
+            }
+            case Terminal.bit_and.id: {
+                return makeBinaryOperatorTree(
+                    "&",
+                    nonTerminalIndex,
+                    prodIndex,
+                    rest,
+                    size,
+                );
+            }
+            case Terminal.bit_or.id: {
+                return makeBinaryOperatorTree(
+                    "|",
+                    nonTerminalIndex,
+                    prodIndex,
+                    rest,
+                    size,
+                );
+            }
+            case Terminal.bit_xor.id: {
+                return makeBinaryOperatorTree(
+                    "^",
+                    nonTerminalIndex,
+                    prodIndex,
+                    rest,
+                    size,
+                );
+            }
+            // case Terminal.unary_plus.id: {
+            //     return makeUnaryOperatorTree("+", rest, size);
+            // }
+            case Terminal.unary_minus.id: {
+                return makeUnaryOperatorTree("-", rest, size);
+            }
+            case Terminal.bit_not.id: {
+                return makeUnaryOperatorTree("~", rest, size);
+            }
+            case Terminal.bool.id: {
+                return fc.boolean().map((b) => makeF.makeDummyBoolean(b));
+            }
+            case Terminal.eq.id: {
+                return makeBinaryOperatorTree(
+                    "==",
+                    nonTerminalIndex,
+                    prodIndex,
+                    rest,
+                    size,
+                );
+            }
+            case Terminal.neq.id: {
+                return makeBinaryOperatorTree(
+                    "!=",
+                    nonTerminalIndex,
+                    prodIndex,
+                    rest,
+                    size,
+                );
+            }
+            case Terminal.lt.id: {
+                return makeBinaryOperatorTree(
+                    "<",
+                    nonTerminalIndex,
+                    prodIndex,
+                    rest,
+                    size,
+                );
+            }
+            case Terminal.le.id: {
+                return makeBinaryOperatorTree(
+                    "<=",
+                    nonTerminalIndex,
+                    prodIndex,
+                    rest,
+                    size,
+                );
+            }
+            case Terminal.gt.id: {
+                return makeBinaryOperatorTree(
+                    ">",
+                    nonTerminalIndex,
+                    prodIndex,
+                    rest,
+                    size,
+                );
+            }
+            case Terminal.ge.id: {
+                return makeBinaryOperatorTree(
+                    ">=",
+                    nonTerminalIndex,
+                    prodIndex,
+                    rest,
+                    size,
+                );
+            }
+            case Terminal.and.id: {
+                return makeBinaryOperatorTree(
+                    "&&",
+                    nonTerminalIndex,
+                    prodIndex,
+                    rest,
+                    size,
+                );
+            }
+            case Terminal.or.id: {
+                return makeBinaryOperatorTree(
+                    "||",
+                    nonTerminalIndex,
+                    prodIndex,
+                    rest,
+                    size,
+                );
+            }
+            case Terminal.not.id: {
+                return makeUnaryOperatorTree("!", rest, size);
+            }
+            case Terminal.cell.id: {
+                return _generateCell().map((c) => makeF.makeDummyCell(c));
+            }
+            //case Terminal.code_of.id: {
+            //    if (ctx.contractNames.length === 0) {
+            //        throw new Error(
+            //            "There must exist at least one contract name in generator context",
+            //        );
+            //    }
+            //    return fc
+            //        .constantFrom(...ctx.contractNames)
+            //        .map((name) =>
+            //            makeF.makeDummyCodeOf(makeF.makeDummyId(name)),
+            //        );
+            //}
+            case Terminal.slice.id: {
+                return _generateCell().map((c) =>
+                    makeF.makeDummySlice(c.asSlice()),
+                );
+            }
+            case Terminal.address.id: {
+                return _generateAddress().map((a) => makeF.makeDummyAddress(a));
+            }
+            case Terminal.string.id: {
+                return fc.string().map((s) => makeF.makeDummyString(s));
+            }
+            // case Terminal.opt_inj.id: {
+            //     const currSize = size - 1;
+            //     const operandNonTerminal = getNonTerminalAt(rest, 0);
+            //     return genFromNonTerminal(operandNonTerminal.id, currSize);
+            // }
+            // case Terminal.null.id: {
+            //     return fc.constant(makeF.makeDummyNull());
+            // }
+            case Terminal.non_null_assert.id: {
+                return makeUnaryOperatorTree("!!", rest, size);
+            }
+            case Terminal.cond.id: {
+                const currSize = size - 1;
+                // Choose two splits for the size
+                return chooseSizeSplit(
+                    nonTerminalIndex,
+                    prodIndex,
+                    1,
+                    currSize,
+                ).chain((sizeSplit1) => {
+                    return chooseSizeSplit(
+                        nonTerminalIndex,
+                        prodIndex,
+                        2,
+                        currSize - sizeSplit1,
+                    ).chain((sizeSplit2) => {
+                        const condNonTerminal = getNonTerminalAt(rest, 0);
+                        const thenNonTerminal = getNonTerminalAt(rest, 1);
+                        const elseNonTerminal = getNonTerminalAt(rest, 2);
+
+                        return genFromNonTerminal(
+                            condNonTerminal.id,
+                            sizeSplit1,
+                        ).chain((condOperand) => {
+                            return genFromNonTerminal(
+                                thenNonTerminal.id,
+                                sizeSplit2,
+                            ).chain((thenOperand) => {
+                                return genFromNonTerminal(
+                                    elseNonTerminal.id,
+                                    currSize - sizeSplit1 - sizeSplit2,
+                                ).map((elseOperand) =>
+                                    makeF.makeDummyConditional(
+                                        condOperand,
+                                        thenOperand,
+                                        elseOperand,
+                                    ),
+                                );
+                            });
+                        });
+                    });
+                });
+            }
+            case Terminal.id_int.id: {
+                return makeIdentifier({ kind: "stdlib", type: StdlibType.Int });
+            }
+            case Terminal.id_opt_int.id: {
+                return makeIdentifier({
+                    kind: "optional",
+                    type: { kind: "stdlib", type: StdlibType.Int },
+                });
+            }
+            case Terminal.id_bool.id: {
+                return makeIdentifier({
+                    kind: "stdlib",
+                    type: StdlibType.Bool,
+                });
+            }
+            case Terminal.id_opt_bool.id: {
+                return makeIdentifier({
+                    kind: "optional",
+                    type: { kind: "stdlib", type: StdlibType.Bool },
+                });
+            }
+            case Terminal.id_cell.id: {
+                return makeIdentifier({
+                    kind: "stdlib",
+                    type: StdlibType.Cell,
+                });
+            }
+            case Terminal.id_opt_cell.id: {
+                return makeIdentifier({
+                    kind: "optional",
+                    type: { kind: "stdlib", type: StdlibType.Cell },
+                });
+            }
+            case Terminal.id_slice.id: {
+                return makeIdentifier({
+                    kind: "stdlib",
+                    type: StdlibType.Slice,
+                });
+            }
+            case Terminal.id_opt_slice.id: {
+                return makeIdentifier({
+                    kind: "optional",
+                    type: { kind: "stdlib", type: StdlibType.Slice },
+                });
+            }
+            case Terminal.id_address.id: {
+                return makeIdentifier({
+                    kind: "stdlib",
+                    type: StdlibType.Address,
+                });
+            }
+            case Terminal.id_opt_address.id: {
+                return makeIdentifier({
+                    kind: "optional",
+                    type: { kind: "stdlib", type: StdlibType.Address },
+                });
+            }
+            case Terminal.id_string.id: {
+                return makeIdentifier({
+                    kind: "stdlib",
+                    type: StdlibType.String,
+                });
+            }
+            case Terminal.id_opt_string.id: {
+                return makeIdentifier({
+                    kind: "optional",
+                    type: { kind: "stdlib", type: StdlibType.String },
+                });
+            }
+        }
+    }
+
+    return genFromNonTerminal(nonTerminalId, size);
+}
+
+export function initializeGenerator(
+    initConfig: GenInitConfig,
+): (
+    scope: Scope,
+    nonTerminal: NonTerminalEnum,
+) => fc.Arbitrary<Ast.Expression> {
+    const { productions, nonTerminals, reindexMap } =
+        filterProductions(initConfig);
+
+    const { nonTerminalCounts, sizeSplitCounts, totalCounts } =
+        computeCountTables(
+            initConfig.minSize,
+            initConfig.maxSize,
+            productions,
+            nonTerminals,
+        );
+
+    return (scope: Scope, nonTerminal: NonTerminalEnum) => {
+        const nonTerminalId = reindexMap.get(nonTerminal.id);
+        if (typeof nonTerminalId === "undefined") {
+            throw new Error(
+                `Non-terminal ${nonTerminal.id} does not have a re-indexing`,
+            );
+        }
+
+        if (nonTerminals.every((n) => n.id !== nonTerminalId)) {
+            throw new Error(
+                `Non-terminal ${nonTerminalId} is not among the allowed non-terminals`,
+            );
+        }
+        const sizes = lookupTotalCounts(totalCounts, nonTerminalId);
+        if (sizes.every((s) => s === 0)) {
+            throw new Error(
+                `There are no trees for non-terminal ${nonTerminalId}`,
+            );
+        }
+        const weightedSizes: fc.WeightedArbitrary<number>[] = sizes.map(
+            (w, i) => {
+                return { arbitrary: fc.constant(i), weight: w };
+            },
+        );
+
+        return fc.oneof(...weightedSizes).chain((size) => {
+            return makeExpression(
+                GlobalContext.astF,
+                nonTerminalId,
+                scope,
+                nonTerminalCounts,
+                sizeSplitCounts,
+                productions,
+                size,
+            );
+        });
+    };
+}
+
+function testSubwalletId(seed: string): bigint {
+    return BigInt("0x" + sha256_sync("TEST_SEED" + seed).toString("hex"));
+}
+
+function _generateAddress(): fc.Arbitrary<Address> {
+    return fc.string().map((str) => {
+        const subwalletId = testSubwalletId(str);
+        const wallet = TreasuryContract.create(0, subwalletId);
+        return wallet.address;
+    });
+}
+
+function _generateCell(): fc.Arbitrary<Cell> {
+    return fc.int8Array().map((buf) => {
+        return beginCell().storeBuffer(Buffer.from(buf.buffer)).endCell();
+    });
+}
+
+function _generateIntBitLength(
+    bitLength: number,
+    signed: boolean,
+): fc.Arbitrary<bigint> {
+    const maxUnsigned = (1n << BigInt(bitLength)) - 1n;
+
+    if (signed) {
+        const minSigned = -maxUnsigned / 2n - 1n;
+        const maxSigned = maxUnsigned / 2n;
+        return fc.bigInt(minSigned, maxSigned);
+    } else {
+        return fc.bigInt(0n, maxUnsigned);
+    }
+}
+
+function generateStringValue(
+    nonEmpty: boolean,
+    constValue?: string,
+): fc.Arbitrary<string> {
+    return constValue === undefined
+        ? nonEmpty
+            ? fc.string({ minLength: 1 })
+            : fc.string()
+        : fc.constantFrom(constValue);
+}
+
+export function generateString(
+    nonEmpty: boolean,
+    constValue?: string,
+): fc.Arbitrary<Ast.String> {
+    return generateStringValue(nonEmpty, constValue).map((s) =>
+        GlobalContext.makeF.makeDummyString(s),
+    );
+}
diff --git a/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Asm.fif b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Asm.fif
new file mode 100644
index 0000000000..976093f809
--- /dev/null
+++ b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Asm.fif
@@ -0,0 +1,1661 @@
+library TVM_Asm
+// simple TVM Assembler
+namespace Asm
+Asm definitions
+"0.4.5" constant asm-fif-version
+
+variable @atend
+variable @was-split
+false @was-split !
+{ "not in asm context" abort } @atend !
+{ `normal eq? not abort"must be terminated by }>" } : @normal?
+{ context@ @atend @ 2 { @atend ! context! @normal? } does @atend ! } : @pushatend
+{ @pushatend Asm <b } : <{
+{ @atend @ execute } : @endblk
+{ false @was-split ! `normal @endblk } : }>
+{ }> b> } : }>c
+{ }>c <s } : }>s
+{ @atend @ 2 { true @was-split ! @atend ! rot b> ref, swap @endblk } does @atend ! <b } : @|
+{ @atend @ 3 { @atend ! 2swap rot execute } does @atend ! <b } : @doafter<{
+{ over brembits <= } : @havebits
+{ rot + -rot + swap } : pair+
+{ rot >= -rot <= and } : 2x<=
+{ 2 pick brembitrefs 1- 2x<= } : @havebitrefs
+{ @havebits ' @| ifnot } : @ensurebits
+{ @havebitrefs ' @| ifnot } : @ensurebitrefs
+{ rot over @ensurebits -rot u, } : @simpleuop
+{ tuck sbitrefs @ensurebitrefs swap s, } : @addop
+{ tuck bbitrefs @ensurebitrefs swap b+ } : @addopb
+' @addopb : @inline
+{ 1 ' @addop does create } : @Defop
+{ 1 { <b swap s, swap 8 u, @addopb } does create } : @Defop(8u)
+{ 1 { <b swap s, swap 8 i, @addopb } does create } : @Defop(8i)
+{ 1 { <b swap s, swap 1- 8 u, @addopb } does create } : @Defop(8u+1)
+{ 1 { <b swap s, swap 4 u, @addopb } does create } : @Defop(4u)
+{ 1 { <b swap s, swap 12 u, @addopb } does create } : @Defop(12u)
+{ 1 { <b swap s, rot 4 u, swap 4 u, @addopb } does create } : @Defop(4u,4u)
+{ 1 { <b swap s, swap ref, @addopb } does create } : @Defop(ref)
+{ 1 { <b swap s, rot ref, swap ref, @addopb } does create } : @Defop(ref*2)
+{ <b 0xef 8 u, swap 12 i, b> } : si()
+// x mi ma -- ?
+{ rot tuck >= -rot <= and } : @range
+{ rot tuck < -rot > or } : @-range
+{ @-range abort"Out of range" } : @rangechk
+{ dup 0 < over 255 > or abort"Invalid stack register number" si() } : s()
+{ si() constant } : @Sreg
+-2 @Sreg s(-2)
+-1 @Sreg s(-1)
+0 @Sreg s0
+1 @Sreg s1
+2 @Sreg s2
+3 @Sreg s3
+4 @Sreg s4
+5 @Sreg s5
+6 @Sreg s6
+7 @Sreg s7
+8 @Sreg s8
+9 @Sreg s9
+10 @Sreg s10
+11 @Sreg s11
+12 @Sreg s12
+13 @Sreg s13
+14 @Sreg s14
+15 @Sreg s15
+{ dup 0 < over 7 > or abort"Invalid control register number" <b 0xcc 8 u, swap 4 u, b> } : c()
+{ c() constant } : @Creg
+0 @Creg c0
+1 @Creg c1
+2 @Creg c2
+3 @Creg c3
+4 @Creg c4
+5 @Creg c5
+7 @Creg c7
+{ <s 8 u@+ swap 0xef <> abort"not a stack register" 12 i@+ s> } : @bigsridx
+{ @bigsridx dup 16 >= over 0< or abort"stack register s0..s15 expected" } : @sridx
+{ rot @bigsridx tuck < -rot tuck > rot or abort"stack register out of range" } : @sridxrange
+{ swap @bigsridx + dup 16 >= over 0< or abort"stack register out of range" } : @sridx+
+{ <s 8 u@+ 4 u@+ s> swap 0xcc <> over 7 > or over 6 = or abort"not a control register c0..c5 or c7" } : @cridx
+{ <s 8 u@ 0xcc = } : @iscr?
+{ <b swap s, 1 { swap @sridx 4 u, @addopb } does create } : @Defop(s)
+{ <b swap s, 1 { rot @sridx 4 u, swap @sridx 4 u, @addopb } does create } : @Defop(s,s)
+{ <b swap s, 1 { swap @cridx 4 u, @addopb } does create } : @Defop(c)
+//
+// stack manipulation primitives
+// (simple stack primitives)
+//
+x{00} @Defop NOP
+x{01} @Defop SWAP
+x{0} @Defop(s) XCHG0
+{ @bigsridx swap @bigsridx 2dup =
+  { 2drop <b }
+  { 2dup < { swap } if dup 0=
+    { drop dup 16 <
+      { <b x{0} s, swap 4 u, }
+      { <b x{11} s, swap 8 u, }
+      cond
+    }
+    { over 16 >=
+      { tuck 16 >=
+        { <b x{11} s, 2 pick 8 u, x{11} s, swap 8 u, x{11} s, swap 8 u, }
+	{ <b x{0} s, 2 pick 4 u, x{11} s, swap 8 u, x{0} s, swap 4 u, } cond
+      }
+      { dup 1 =
+        { drop <b x{1} s, swap 4 u, }
+        { <b x{10} s, swap 4 u, swap 4 u, }
+        cond
+      } cond
+    } cond
+  } cond
+  @addopb } : XCHG
+x{ED4} @Defop(c) PUSHCTR
+x{ED5} @Defop(c) POPCTR
+{ dup @iscr?
+  ' PUSHCTR
+  { @bigsridx dup 16 <
+    { <b x{2} s, swap 4 u, }
+    { <b x{56} s, swap 8 u,
+    } cond
+  @addopb
+  } cond
+} : PUSH
+x{20} @Defop DUP
+x{21} @Defop OVER
+{ dup @iscr?
+  ' POPCTR
+  { @bigsridx dup 16 <
+    { <b x{3} s, swap 4 u, }
+    { <b x{57} s, swap 8 u,
+    } cond
+  @addopb
+  } cond
+} : POP
+x{30} @Defop DROP
+x{31} @Defop NIP
+
+// compound stack primitives
+{ @sridx rot @sridx rot @sridx swap <b x{4} s, swap 4 u, swap 4 u, swap 4 u, @addopb } : XCHG3
+x{50} @Defop(s,s) XCHG2
+x{51} @Defop(s,s) XCPU
+{ <b x{52} s, rot @sridx 4 u, swap 1 @sridx+ 4 u, @addopb } : PUXC
+x{53} @Defop(s,s) PUSH2
+{ @sridx rot @sridx rot @sridx swap <b x{540} s, swap 4 u, swap 4 u, swap 4 u, @addopb } : XCHG3_l
+{ @sridx rot @sridx rot @sridx swap <b x{541} s, swap 4 u, swap 4 u, swap 4 u, @addopb } : XC2PU
+{ 1 @sridx+ rot @sridx rot @sridx swap <b x{542} s, swap 4 u, swap 4 u, swap 4 u, @addopb } : XCPUXC
+{ @sridx rot @sridx rot @sridx swap <b x{543} s, swap 4 u, swap 4 u, swap 4 u, @addopb } : XCPU2
+{ 1 @sridx+ rot @sridx rot 1 @sridx+ swap <b x{544} s, swap 4 u, swap 4 u, swap 4 u, @addopb } : PUXC2
+{ 1 @sridx+ rot @sridx rot 1 @sridx+ swap <b x{545} s, swap 4 u, swap 4 u, swap 4 u, @addopb } : PUXCPU
+{ 2 @sridx+ rot @sridx rot 1 @sridx+ swap <b x{546} s, swap 4 u, swap 4 u, swap 4 u, @addopb } : PU2XC
+{ @sridx rot @sridx rot @sridx swap <b x{547} s, swap 4 u, swap 4 u, swap 4 u, @addopb } : PUSH3
+{ <b x{55} s, rot 1- 4 u, swap 1- 4 u, @addopb } : BLKSWAP
+{ dup { 1 swap BLKSWAP } { drop } cond } : ROLL
+{ dup { 1 BLKSWAP } { drop } cond } dup : -ROLL : ROLLREV
+x{5513} dup @Defop 2ROT @Defop ROT2
+
+// exotic stack primitives
+x{58} @Defop ROT
+x{59} dup @Defop -ROT @Defop ROTREV
+x{5A} dup @Defop 2SWAP @Defop SWAP2
+x{5B} dup @Defop 2DROP @Defop DROP2
+x{5C} dup @Defop 2DUP @Defop DUP2
+x{5D} dup @Defop 2OVER @Defop OVER2
+{ <b x{5E} s, rot 2 - 4 u, swap 4 u, @addopb } : REVERSE
+{ <b x{5F0} s, swap 4 u, @addopb } : BLKDROP
+{ over 0= abort"first argument must be non-zero"
+  <b x{5F} s, rot 4 u, swap 4 u, @addopb } : BLKPUSH
+x{60} dup @Defop PICK @Defop PUSHX
+x{61} @Defop ROLLX
+x{62} dup @Defop -ROLLX @Defop ROLLREVX
+x{63} @Defop BLKSWX
+x{64} @Defop REVX
+x{65} @Defop DROPX
+x{66} @Defop TUCK
+x{67} @Defop XCHGX
+x{68} @Defop DEPTH
+x{69} @Defop CHKDEPTH
+x{6A} @Defop ONLYTOPX
+x{6B} @Defop ONLYX
+{ over 0= abort"first argument must be non-zero"
+  <b x{6C} s, rot 4 u, swap 4 u, @addopb } : BLKDROP2
+
+// null primitives
+x{6D} dup @Defop NULL @Defop PUSHNULL
+x{6E} @Defop ISNULL
+// tuple primitives
+x{6F0} @Defop(4u) TUPLE
+x{6F00} @Defop NIL
+x{6F01} @Defop SINGLE
+x{6F02} dup @Defop PAIR @Defop CONS 
+x{6F03} @Defop TRIPLE
+x{6F1} @Defop(4u) INDEX
+x{6F10} dup @Defop FIRST @Defop CAR
+x{6F11} dup @Defop SECOND @Defop CDR
+x{6F12} @Defop THIRD
+x{6F2} @Defop(4u) UNTUPLE
+x{6F21} @Defop UNSINGLE
+x{6F22} dup @Defop UNPAIR @Defop UNCONS
+x{6F23} @Defop UNTRIPLE
+x{6F3} @Defop(4u) UNPACKFIRST
+x{6F30} @Defop CHKTUPLE
+x{6F4} @Defop(4u) EXPLODE
+x{6F5} @Defop(4u) SETINDEX
+x{6F50} @Defop SETFIRST
+x{6F51} @Defop SETSECOND
+x{6F52} @Defop SETTHIRD
+x{6F6} @Defop(4u) INDEXQ
+x{6F60} dup @Defop FIRSTQ @Defop CARQ
+x{6F61} dup @Defop SECONDQ @Defop CDRQ
+x{6F62} @Defop THIRDQ
+x{6F7} @Defop(4u) SETINDEXQ
+x{6F70} @Defop SETFIRSTQ
+x{6F71} @Defop SETSECONDQ
+x{6F72} @Defop SETTHIRDQ
+x{6F80} @Defop TUPLEVAR
+x{6F81} @Defop INDEXVAR
+x{6F82} @Defop UNTUPLEVAR
+x{6F83} @Defop UNPACKFIRSTVAR
+x{6F84} @Defop EXPLODEVAR
+x{6F85} @Defop SETINDEXVAR
+x{6F86} @Defop INDEXVARQ
+x{6F87} @Defop SETINDEXVARQ
+x{6F88} @Defop TLEN
+x{6F89} @Defop QTLEN
+x{6F8A} @Defop ISTUPLE
+x{6F8B} @Defop LAST
+x{6F8C} dup @Defop TPUSH @Defop COMMA
+x{6F8D} @Defop TPOP
+x{6FA0} @Defop NULLSWAPIF
+x{6FA1} @Defop NULLSWAPIFNOT
+x{6FA2} @Defop NULLROTRIF
+x{6FA3} @Defop NULLROTRIFNOT
+x{6FA4} @Defop NULLSWAPIF2
+x{6FA5} @Defop NULLSWAPIFNOT2
+x{6FA6} @Defop NULLROTRIF2
+x{6FA7} @Defop NULLROTRIFNOT2
+{ <b x{6FB} s, rot 2 u, swap 2 u, @addopb } : INDEX2
+x{6FB4} @Defop CADR
+x{6FB5} @Defop CDDR
+{ <b x{6FE_} s, 3 roll 2 u, rot 2 u, swap 2 u, @addopb } : INDEX3
+x{6FD4} @Defop CADDR
+x{6FD5} @Defop CDDDR
+
+// integer constants
+x{70} dup @Defop ZERO @Defop FALSE
+x{71} @Defop ONE
+x{72} @Defop TWO
+x{7A} @Defop TEN
+x{7F} @Defop TRUE
+{ dup 10 <= over -5 >= and
+  { 15 and <b x{7} s, swap 4 u, }
+  { dup 8 fits
+    { <b x{80} s, swap 8 i, }
+    { dup 16 fits
+      { <b x{81} s, swap 16 i, }
+      { 11 { dup 259 > abort"integer too large" 8 + 2dup fits } until
+        <b x{82} s, over 3 >> 2- 5 u, -rot i, 
+      } cond
+    } cond
+  } cond
+  @addopb } dup : PUSHINT : INT
+{ dup 256 = abort"use PUSHNAN instead of 256 PUSHPOW2" <b x{83} s, swap 1- 8 u, @addopb } : PUSHPOW2
+x{83FF} @Defop PUSHNAN
+{ <b x{84} s, swap 1- 8 u, @addopb } : PUSHPOW2DEC
+{ <b x{85} s, swap 1- 8 u, @addopb } : PUSHNEGPOW2
+//
+// other constants
+x{88} @Defop(ref) PUSHREF
+x{89} @Defop(ref) PUSHREFSLICE
+x{8A} @Defop(ref) PUSHREFCONT
+{ 1- dup 0< over 8 >= or abort"invalid slice padding"
+  swap 1 1 u, 0 rot u, } : @scomplete
+{ tuck sbitrefs swap 26 + swap @havebitrefs not
+  { <b rot s, b> PUSHREFSLICE }
+  { over sbitrefs 2dup 123 0 2x<=
+    { drop tuck 4 + 3 >> swap x{8B} s, over 4 u, 3 roll s,
+      -rot 3 << 4 + swap - @scomplete }
+    { 2dup 1 >= swap 248 <= and
+      { rot x{8C} s, swap 1- 2 u, over 7 + 3 >> tuck 5 u, 3 roll s,
+        -rot 3 << 1 + swap - @scomplete }
+      { rot x{8D} s, swap 3 u, over 2 + 3 >> tuck 7 u, 3 roll s,
+        -rot 3 << 6 + swap - @scomplete
+      } cond
+    } cond
+  } cond
+} dup : PUSHSLICE : SLICE
+// ( b' -- ? )
+{ bbitrefs or 0= } : @cont-empty?
+{ bbits 7 and 0= } : @cont-aligned?
+// ( b b' -- ? )
+{ bbitrefs over 7 and { 2drop drop false } {
+  swap 16 + swap @havebitrefs nip
+  } cond
+} : @cont-fits?
+// ( b b' -- ? )
+{ bbitrefs over 7 and { 2drop drop false } {
+  32 1 pair+ @havebitrefs nip
+  } cond
+} : @cont-ref-fit?
+// ( b b' b'' -- ? )
+{ over @cont-aligned? over @cont-aligned? and not { 2drop drop false } {
+  bbitrefs rot bbitrefs pair+ swap 32 + swap @havebitrefs nip
+  } cond
+} : @two-cont-fit?
+{ 2dup @cont-fits? not
+  { b> PUSHREFCONT }
+  { swap over bbitrefs 2dup 120 0 2x<=
+    { drop swap x{9} s, swap 3 >> 4 u, swap b+ }
+    { rot x{8F_} s, swap 2 u, swap 3 >> 7 u, swap b+ } cond
+  } cond
+} dup : PUSHCONT : CONT
+{ }> PUSHCONT } : }>CONT
+{ { @normal? PUSHCONT } @doafter<{ } : CONT:<{
+
+// arithmetic operations
+{ 2 { rot dup 8 fits
+      { nip <b rot s, swap 8 i, }
+      { rot drop <b swap PUSHINT swap s, }
+      cond @addopb
+    } does create
+} : @Defop(8i,alt)
+x{A0} @Defop ADD
+x{A1} @Defop SUB
+x{A2} @Defop SUBR
+x{A3} @Defop NEGATE
+x{A4} @Defop INC
+x{A5} @Defop DEC
+x{A6} x{A0} @Defop(8i,alt) ADDCONST
+{ negate ADDCONST } : SUBCONST
+x{A7} x{A8} @Defop(8i,alt) MULCONST
+' ADDCONST : ADDINT
+' SUBCONST : SUBINT
+' MULCONST : MULINT
+x{A8} @Defop MUL
+
+x{A904} @Defop DIV
+x{A905} @Defop DIVR
+x{A906} @Defop DIVC
+x{A908} @Defop MOD
+x{A909} @Defop MODR
+x{A90A} @Defop MODC
+x{A90C} @Defop DIVMOD
+x{A90D} @Defop DIVMODR
+x{A90E} @Defop DIVMODC
+x{A900} @Defop ADDDIVMOD
+x{A901} @Defop ADDDIVMODR
+x{A902} @Defop ADDDIVMODC
+
+x{A925} @Defop RSHIFTR
+x{A926} @Defop RSHIFTC
+x{A928} @Defop MODPOW2
+x{A929} @Defop MODPOW2R
+x{A92A} @Defop MODPOW2C
+x{A92C} @Defop RSHIFTMOD
+x{A92D} @Defop RSHIFTMODR
+x{A92E} @Defop RSHIFTMODC
+x{A920} @Defop ADDRSHIFTMOD
+x{A921} @Defop ADDRSHIFTMODR
+x{A922} @Defop ADDRSHIFTMODC
+
+x{A935} @Defop(8u+1) RSHIFTR#
+x{A936} @Defop(8u+1) RSHIFTC#
+x{A938} @Defop(8u+1) MODPOW2#
+x{A939} @Defop(8u+1) MODPOW2R#
+x{A93A} @Defop(8u+1) MODPOW2C#
+x{A93C} @Defop(8u+1) RSHIFT#MOD
+x{A93D} @Defop(8u+1) RSHIFTR#MOD
+x{A93E} @Defop(8u+1) RSHIFTC#MOD
+x{A930} @Defop(8u+1) ADDRSHIFT#MOD
+x{A931} @Defop(8u+1) ADDRSHIFTR#MOD
+x{A932} @Defop(8u+1) ADDRSHIFTC#MOD
+
+x{A984} @Defop MULDIV
+x{A985} @Defop MULDIVR
+x{A986} @Defop MULDIVC
+x{A988} @Defop MULMOD
+x{A989} @Defop MULMODR
+x{A98A} @Defop MULMODC
+x{A98C} @Defop MULDIVMOD
+x{A98D} @Defop MULDIVMODR
+x{A98E} @Defop MULDIVMODC
+x{A980} @Defop MULADDDIVMOD
+x{A981} @Defop MULADDDIVMODR
+x{A982} @Defop MULADDDIVMODC
+
+x{A9A4} @Defop MULRSHIFT
+x{A9A5} @Defop MULRSHIFTR
+x{A9A6} @Defop MULRSHIFTC
+x{A9A8} @Defop MULMODPOW2
+x{A9A9} @Defop MULMODPOW2R
+x{A9AA} @Defop MULMODPOW2C
+x{A9AC} @Defop MULRSHIFTMOD
+x{A9AD} @Defop MULRSHIFTRMOD
+x{A9AE} @Defop MULRSHIFTCMOD
+x{A9A0} @Defop MULADDRSHIFTMOD
+x{A9A1} @Defop MULADDRSHIFTRMOD
+x{A9A2} @Defop MULADDRSHIFTCMOD
+
+x{A9B4} @Defop(8u+1) MULRSHIFT#
+x{A9B5} @Defop(8u+1) MULRSHIFTR#
+x{A9B6} @Defop(8u+1) MULRSHIFTC#
+x{A9B8} @Defop(8u+1) MULMODPOW2#
+x{A9B9} @Defop(8u+1) MULMODPOW2R#
+x{A9BA} @Defop(8u+1) MULMODPOW2C#
+x{A9BC} @Defop(8u+1) MULRSHIFT#MOD
+x{A9BD} @Defop(8u+1) MULRSHIFTR#MOD
+x{A9BE} @Defop(8u+1) MULRSHIFTC#MOD
+x{A9B0} @Defop(8u+1) MULADDRSHIFT#MOD
+x{A9B1} @Defop(8u+1) MULADDRSHIFTR#MOD
+x{A9B2} @Defop(8u+1) MULADDRSHIFTC#MOD
+
+x{A9C4} @Defop LSHIFTDIV
+x{A9C5} @Defop LSHIFTDIVR
+x{A9C6} @Defop LSHIFTDIVC
+x{A9C8} @Defop LSHIFTMOD
+x{A9C9} @Defop LSHIFTMODR
+x{A9CA} @Defop LSHIFTMODC
+x{A9CC} @Defop LSHIFTDIVMOD
+x{A9CD} @Defop LSHIFTDIVMODR
+x{A9CE} @Defop LSHIFTDIVMODC
+x{A9C0} @Defop LSHIFTADDDIVMOD
+x{A9C1} @Defop LSHIFTADDDIVMODR
+x{A9C2} @Defop LSHIFTADDDIVMODC
+
+x{A9D4} @Defop(8u+1) LSHIFT#DIV
+x{A9D5} @Defop(8u+1) LSHIFT#DIVR
+x{A9D6} @Defop(8u+1) LSHIFT#DIVC
+x{A9D8} @Defop(8u+1) LSHIFT#MOD
+x{A9D9} @Defop(8u+1) LSHIFT#MODR
+x{A9DA} @Defop(8u+1) LSHIFT#MODC
+x{A9DC} @Defop(8u+1) LSHIFT#DIVMOD
+x{A9DD} @Defop(8u+1) LSHIFT#DIVMODR
+x{A9DE} @Defop(8u+1) LSHIFT#DIVMODC
+x{A9D0} @Defop(8u+1) LSHIFT#ADDDIVMOD
+x{A9D1} @Defop(8u+1) LSHIFT#ADDDIVMODR
+x{A9D2} @Defop(8u+1) LSHIFT#ADDDIVMODC
+
+x{AA} @Defop(8u+1) LSHIFT#
+x{AB} @Defop(8u+1) RSHIFT#
+x{AC} @Defop LSHIFT
+x{AD} @Defop RSHIFT
+x{AE} @Defop POW2
+x{B0} @Defop AND
+x{B1} @Defop OR
+x{B2} @Defop XOR
+x{B3} @Defop NOT
+x{B4} @Defop(8u+1) FITS
+x{B400} @Defop CHKBOOL
+x{B5} @Defop(8u+1) UFITS
+x{B500} @Defop CHKBIT
+x{B600} @Defop FITSX
+x{B601} @Defop UFITSX
+x{B602} @Defop BITSIZE
+x{B603} @Defop UBITSIZE
+x{B608} @Defop MIN
+x{B609} @Defop MAX
+x{B60A} dup @Defop MINMAX @Defop INTSORT2
+x{B60B} @Defop ABS
+x{B7} @Defop QUIET
+x{B7A0} @Defop QADD
+x{B7A1} @Defop QSUB
+x{B7A2} @Defop QSUBR
+x{B7A3} @Defop QNEGATE
+x{B7A4} @Defop QINC
+x{B7A5} @Defop QDEC
+x{B7A8} @Defop QMUL
+
+x{B7A904} @Defop QDIV
+x{B7A905} @Defop QDIVR
+x{B7A906} @Defop QDIVC
+x{B7A908} @Defop QMOD
+x{B7A909} @Defop QMODR
+x{B7A90A} @Defop QMODC
+x{B7A90C} @Defop QDIVMOD
+x{B7A90D} @Defop QDIVMODR
+x{B7A90E} @Defop QDIVMODC
+x{B7A900} @Defop QADDDIVMOD
+x{B7A901} @Defop QADDDIVMODR
+x{B7A902} @Defop QADDDIVMODC
+
+x{B7A925} @Defop QRSHIFTR
+x{B7A926} @Defop QRSHIFTC
+x{B7A928} @Defop QMODPOW2
+x{B7A929} @Defop QMODPOW2R
+x{B7A92A} @Defop QMODPOW2C
+x{B7A92C} @Defop QRSHIFTMOD
+x{B7A92D} @Defop QRSHIFTMODR
+x{B7A92E} @Defop QRSHIFTMODC
+x{B7A920} @Defop QADDRSHIFTMOD
+x{B7A921} @Defop QADDRSHIFTMODR
+x{B7A922} @Defop QADDRSHIFTMODC
+
+x{B7A935} @Defop(8u+1) QRSHIFTR#
+x{B7A936} @Defop(8u+1) QRSHIFTC#
+x{B7A938} @Defop(8u+1) QMODPOW2#
+x{B7A939} @Defop(8u+1) QMODPOW2R#
+x{B7A93A} @Defop(8u+1) QMODPOW2C#
+x{B7A93C} @Defop(8u+1) QRSHIFT#MOD
+x{B7A93D} @Defop(8u+1) QRSHIFTR#MOD
+x{B7A93E} @Defop(8u+1) QRSHIFTC#MOD
+x{B7A930} @Defop(8u+1) QADDRSHIFT#MOD
+x{B7A931} @Defop(8u+1) QADDRSHIFTR#MOD
+x{B7A932} @Defop(8u+1) QADDRSHIFTC#MOD
+
+x{B7A984} @Defop QMULDIV
+x{B7A985} @Defop QMULDIVR
+x{B7A986} @Defop QMULDIVC
+x{B7A988} @Defop QMULMOD
+x{B7A989} @Defop QMULMODR
+x{B7A98A} @Defop QMULMODC
+x{B7A98C} @Defop QMULDIVMOD
+x{B7A98D} @Defop QMULDIVMODR
+x{B7A98E} @Defop QMULDIVMODC
+x{B7A980} @Defop QMULADDDIVMOD
+x{B7A981} @Defop QMULADDDIVMODR
+x{B7A982} @Defop QMULADDDIVMODC
+
+x{B7A9A4} @Defop QMULRSHIFT
+x{B7A9A5} @Defop QMULRSHIFTR
+x{B7A9A6} @Defop QMULRSHIFTC
+x{B7A9A8} @Defop QMULMODPOW2
+x{B7A9A9} @Defop QMULMODPOW2R
+x{B7A9AA} @Defop QMULMODPOW2C
+x{B7A9AC} @Defop QMULRSHIFTMOD
+x{B7A9AD} @Defop QMULRSHIFTRMOD
+x{B7A9AE} @Defop QMULRSHIFTCMOD
+x{B7A9A0} @Defop QMULADDRSHIFTMOD
+x{B7A9A1} @Defop QMULADDRSHIFTRMOD
+x{B7A9A2} @Defop QMULADDRSHIFTCMOD
+
+x{B7A9B4} @Defop(8u+1) QMULRSHIFT#
+x{B7A9B5} @Defop(8u+1) QMULRSHIFTR#
+x{B7A9B6} @Defop(8u+1) QMULRSHIFTC#
+x{B7A9B8} @Defop(8u+1) QMULMODPOW2#
+x{B7A9B9} @Defop(8u+1) QMULMODPOW2R#
+x{B7A9BA} @Defop(8u+1) QMULMODPOW2C#
+x{B7A9BC} @Defop(8u+1) QMULRSHIFT#MOD
+x{B7A9BD} @Defop(8u+1) QMULRSHIFTR#MOD
+x{B7A9BE} @Defop(8u+1) QMULRSHIFTC#MOD
+x{B7A9B0} @Defop(8u+1) QMULADDRSHIFT#MOD
+x{B7A9B1} @Defop(8u+1) QMULADDRSHIFTR#MOD
+x{B7A9B2} @Defop(8u+1) QMULADDRSHIFTC#MOD
+
+x{B7A9C4} @Defop QLSHIFTDIV
+x{B7A9C5} @Defop QLSHIFTDIVR
+x{B7A9C6} @Defop QLSHIFTDIVC
+x{B7A9C8} @Defop QLSHIFTMOD
+x{B7A9C9} @Defop QLSHIFTMODR
+x{B7A9CA} @Defop QLSHIFTMODC
+x{B7A9CC} @Defop QLSHIFTDIVMOD
+x{B7A9CD} @Defop QLSHIFTDIVMODR
+x{B7A9CE} @Defop QLSHIFTDIVMODC
+x{B7A9C0} @Defop QLSHIFTADDDIVMOD
+x{B7A9C1} @Defop QLSHIFTADDDIVMODR
+x{B7A9C2} @Defop QLSHIFTADDDIVMODC
+
+x{B7A9D4} @Defop(8u+1) QLSHIFT#DIV
+x{B7A9D5} @Defop(8u+1) QLSHIFT#DIVR
+x{B7A9D6} @Defop(8u+1) QLSHIFT#DIVC
+x{B7A9D8} @Defop(8u+1) QLSHIFT#MOD
+x{B7A9D9} @Defop(8u+1) QLSHIFT#MODR
+x{B7A9DA} @Defop(8u+1) QLSHIFT#MODC
+x{B7A9DC} @Defop(8u+1) QLSHIFT#DIVMOD
+x{B7A9DD} @Defop(8u+1) QLSHIFT#DIVMODR
+x{B7A9DE} @Defop(8u+1) QLSHIFT#DIVMODC
+x{B7A9D0} @Defop(8u+1) QLSHIFT#ADDDIVMOD
+x{B7A9D1} @Defop(8u+1) QLSHIFT#ADDDIVMODR
+x{B7A9D2} @Defop(8u+1) QLSHIFT#ADDDIVMODC
+
+x{B7AC} @Defop QLSHIFT
+x{B7AD} @Defop QRSHIFT
+x{B7AE} @Defop QPOW2
+x{B7B0} @Defop QAND
+x{B7B1} @Defop QOR
+x{B7B2} @Defop QXOR
+x{B7B3} @Defop QNOT
+x{B7B4} @Defop(8u+1) QFITS
+x{B7B5} @Defop(8u+1) QUFITS
+x{B7B600} @Defop QFITSX
+x{B7B601} @Defop QUFITSX
+
+// advanced integer constants
+{ 0 { over 1 and 0= } { 1+ swap 2/ swap } while } : pow2decomp
+{ dup 8 fits { PUSHINT } {
+  dup pow2decomp over 1 = { nip nip PUSHPOW2 } {
+  over -1 = { nip nip PUSHNEGPOW2 } {
+  dup 20 >= { rot drop -rot PUSHINT swap LSHIFT# } {
+  { drop PUSHINT } {
+  not pow2decomp swap -1 = { nip PUSHPOW2DEC } {
+  drop PUSHINT
+  } cond } cond } cond } cond } cond } cond
+} dup : PUSHINTX : INTX
+
+// integer comparison
+x{B8} @Defop SGN
+x{B9} @Defop LESS
+x{BA} @Defop EQUAL
+x{BB} @Defop LEQ
+x{BC} @Defop GREATER
+x{BD} @Defop NEQ
+x{BE} @Defop GEQ
+x{BF} @Defop CMP
+x{C0} x{BA} @Defop(8i,alt) EQINT
+x{C000} @Defop ISZERO
+x{C1} x{B9} @Defop(8i,alt) LESSINT
+{ 1+ LESSINT } : LEQINT
+x{C100} @Defop ISNEG
+x{C101} @Defop ISNPOS
+x{C2} x{BC} @Defop(8i,alt) GTINT
+{ 1- GTINT } : GEQINT
+x{C200} @Defop ISPOS
+x{C2FF} @Defop ISNNEG
+x{C3} x{BD} @Defop(8i,alt) NEQINT
+x{C300} @Defop ISNZERO
+x{C4} @Defop ISNAN
+x{C5} @Defop CHKNAN
+
+// other comparison
+x{C700} @Defop SEMPTY
+x{C701} @Defop SDEMPTY
+x{C702} @Defop SREMPTY
+x{C703} @Defop SDFIRST
+x{C704} @Defop SDLEXCMP
+x{C705} @Defop SDEQ
+x{C708} @Defop SDPFX
+x{C709} @Defop SDPFXREV
+x{C70A} @Defop SDPPFX
+x{C70B} @Defop SDPPFXREV
+x{C70C} @Defop SDSFX
+x{C70D} @Defop SDSFXREV
+x{C70E} @Defop SDPSFX
+x{C70F} @Defop SDPSFXREV
+x{C710} @Defop SDCNTLEAD0
+x{C711} @Defop SDCNTLEAD1
+x{C712} @Defop SDCNTTRAIL0
+x{C713} @Defop SDCNTTRAIL1
+
+// cell serialization (Builder manipulation primitives)
+x{C8} @Defop NEWC
+x{C9} @Defop ENDC
+x{CA} @Defop(8u+1) STI
+x{CB} @Defop(8u+1) STU
+x{CC} @Defop STREF
+x{CD} dup @Defop STBREFR @Defop ENDCST
+x{CE} @Defop STSLICE
+x{CF00} @Defop STIX
+x{CF01} @Defop STUX
+x{CF02} @Defop STIXR
+x{CF03} @Defop STUXR
+x{CF04} @Defop STIXQ
+x{CF05} @Defop STUXQ
+x{CF06} @Defop STIXRQ
+x{CF07} @Defop STUXRQ
+x{CF08} @Defop(8u+1) STI_l
+x{CF09} @Defop(8u+1) STU_l
+x{CF0A} @Defop(8u+1) STIR
+x{CF0B} @Defop(8u+1) STUR
+x{CF0C} @Defop(8u+1) STIQ
+x{CF0D} @Defop(8u+1) STUQ
+x{CF0E} @Defop(8u+1) STIRQ
+x{CF0F} @Defop(8u+1) STURQ
+x{CF10} @Defop STREF_l
+x{CF11} @Defop STBREF
+x{CF12} @Defop STSLICE_l
+x{CF13} @Defop STB
+x{CF14} @Defop STREFR
+x{CF15} @Defop STBREFR_l
+x{CF16} @Defop STSLICER
+x{CF17} dup @Defop STBR @Defop BCONCAT
+x{CF18} @Defop STREFQ
+x{CF19} @Defop STBREFQ
+x{CF1A} @Defop STSLICEQ
+x{CF1B} @Defop STBQ
+x{CF1C} @Defop STREFRQ
+x{CF1D} @Defop STBREFRQ
+x{CF1E} @Defop STSLICERQ
+x{CF1F} dup @Defop STBRQ @Defop BCONCATQ
+x{CF20} @Defop(ref) STREFCONST
+{ <b x{CF21} s, rot ref, swap ref, @addopb } : STREF2CONST
+x{CF23} @Defop ENDXC
+x{CF28} @Defop STILE4
+x{CF29} @Defop STULE4
+x{CF2A} @Defop STILE8
+x{CF2B} @Defop STULE8
+x{CF30} @Defop BDEPTH
+x{CF31} @Defop BBITS
+x{CF32} @Defop BREFS
+x{CF33} @Defop BBITREFS
+x{CF35} @Defop BREMBITS
+x{CF36} @Defop BREMREFS
+x{CF37} @Defop BREMBITREFS
+x{CF38} @Defop(8u+1) BCHKBITS#
+x{CF39} @Defop BCHKBITS
+x{CF3A} @Defop BCHKREFS
+x{CF3B} @Defop BCHKBITREFS
+x{CF3C} @Defop(8u+1) BCHKBITSQ#
+x{CF3D} @Defop BCHKBITSQ
+x{CF3E} @Defop BCHKREFSQ
+x{CF3F} @Defop BCHKBITREFSQ
+x{CF40} @Defop STZEROES
+x{CF41} @Defop STONES
+x{CF42} @Defop STSAME
+{ tuck sbitrefs swap 22 + swap @havebitrefs not
+  { swap PUSHSLICE STSLICER }
+  { over sbitrefs 2dup 57 3 2x<=
+    { rot x{CFC_} s, swap 2 u, over 6 + 3 >> tuck 3 u, 3 roll s,
+      -rot 3 << 2 + swap - @scomplete }
+    { 2drop swap PUSHSLICE STSLICER } cond
+  } cond
+} : STSLICECONST
+x{CF81} @Defop STZERO
+x{CF83} @Defop STONE
+
+// cell deserialization (CellSlice primitives)
+x{D0} @Defop CTOS
+x{D1} @Defop ENDS
+x{D2} @Defop(8u+1) LDI
+x{D3} @Defop(8u+1) LDU
+x{D4} @Defop LDREF
+x{D5} @Defop LDREFRTOS
+x{D6} @Defop(8u+1) LDSLICE
+x{D700} @Defop LDIX
+x{D701} @Defop LDUX
+x{D702} @Defop PLDIX
+x{D703} @Defop PLDUX
+x{D704} @Defop LDIXQ
+x{D705} @Defop LDUXQ
+x{D706} @Defop PLDIXQ
+x{D707} @Defop PLDUXQ
+x{D708} @Defop(8u+1) LDI_l
+x{D709} @Defop(8u+1) LDU_l
+x{D70A} @Defop(8u+1) PLDI
+x{D70B} @Defop(8u+1) PLDU
+x{D70C} @Defop(8u+1) LDIQ
+x{D70D} @Defop(8u+1) LDUQ
+x{D70E} @Defop(8u+1) PLDIQ
+x{D70F} @Defop(8u+1) PLDUQ
+{ dup 31 and abort"argument must be a multiple of 32" 5 >> 1-
+  <b x{D714_} s, swap 3 u, @addopb
+} : PLDUZ
+x{D718} @Defop LDSLICEX
+x{D719} @Defop PLDSLICEX
+x{D71A} @Defop LDSLICEXQ
+x{D71B} @Defop PLDSLICEXQ
+x{D71C} @Defop(8u+1) LDSLICE_l
+x{D71D} @Defop(8u+1) PLDSLICE
+x{D71E} @Defop(8u+1) LDSLICEQ
+x{D71F} @Defop(8u+1) PLDSLICEQ
+x{D720} @Defop SDCUTFIRST
+x{D721} @Defop SDSKIPFIRST
+x{D722} @Defop SDCUTLAST
+x{D723} @Defop SDSKIPLAST
+x{D724} @Defop SDSUBSTR
+x{D726} @Defop SDBEGINSX
+x{D727} @Defop SDBEGINSXQ
+{ tuck sbits tuck 5 + 3 >> swap x{D72A_} s, over 7 u, 3 roll s,
+  -rot 3 << 3 + swap - @scomplete } : SDBEGINS:imm
+{ tuck sbitrefs abort"no references allowed in slice" dup 26 <=
+  { drop <b rot SDBEGINS:imm @addopb }
+  { @havebits
+    { swap SDBEGINS:imm }
+    { swap PUSHSLICE SDBEGINSX
+    } cond
+  } cond
+} : SDBEGINS
+{ tuck sbits tuck 5 + 3 >> swap x{D72E_} s, over 7 u, 3 roll s,
+  -rot 3 << 3 + swap - @scomplete } : SDBEGINSQ:imm
+{ tuck sbitrefs abort"no references allowed in slice" dup 26 <=
+  { drop <b rot SDBEGINSQ:imm @addopb }
+  { @havebits
+    { swap SDBEGINSQ:imm }
+    { swap PUSHSLICE SDBEGINSXQ
+    } cond
+  } cond
+} : SDBEGINSQ
+x{D730} @Defop SCUTFIRST
+x{D731} @Defop SSKIPFIRST
+x{D732} @Defop SCUTLAST
+x{D733} @Defop SSKIPLAST
+x{D734} @Defop SUBSLICE
+x{D736} @Defop SPLIT
+x{D737} @Defop SPLITQ
+x{D739} @Defop XCTOS
+x{D73A} @Defop XLOAD
+x{D73B} @Defop XLOADQ
+x{D741} @Defop SCHKBITS
+x{D742} @Defop SCHKREFS
+x{D743} @Defop SCHKBITREFS
+x{D745} @Defop SCHKBITSQ
+x{D746} @Defop SCHKREFSQ
+x{D747} @Defop SCHKBITREFSQ
+x{D748} @Defop PLDREFVAR
+x{D749} @Defop SBITS
+x{D74A} @Defop SREFS
+x{D74B} @Defop SBITREFS
+{ <b x{D74E_} s, swap 2 u, @addopb } : PLDREFIDX
+x{D74C} @Defop PLDREF
+x{D750} @Defop LDILE4
+x{D751} @Defop LDULE4
+x{D752} @Defop LDILE8
+x{D753} @Defop LDULE8
+x{D754} @Defop PLDILE4
+x{D755} @Defop PLDULE4
+x{D756} @Defop PLDILE8
+x{D757} @Defop PLDULE8
+x{D758} @Defop LDILE4Q
+x{D759} @Defop LDULE4Q
+x{D75A} @Defop LDILE8Q
+x{D75B} @Defop LDULE8Q
+x{D75C} @Defop PLDILE4Q
+x{D75D} @Defop PLDULE4Q
+x{D75E} @Defop PLDILE8Q
+x{D75F} @Defop PLDULE8Q
+x{D760} @Defop LDZEROES
+x{D761} @Defop LDONES
+x{D762} @Defop LDSAME
+x{D764} @Defop SDEPTH
+x{D765} @Defop CDEPTH
+x{D766} @Defop CLEVEL
+x{D767} @Defop CLEVELMASK
+{ <b x{D76A_} s, swap 2 u, @addopb } : CHASHI
+{ <b x{D76E_} s, swap 2 u, @addopb } : CDEPTHI
+x{D770} @Defop CHASHIX
+x{D771} @Defop CDEPTHIX
+//
+// continuation / flow control primitives
+x{D8} dup @Defop EXECUTE @Defop CALLX
+x{D9} @Defop JMPX
+{ dup 1+
+  { <b x{DA} s, rot 4 u, swap 4 u, }
+  { drop <b x{DB0} s, swap 4 u,
+  } cond @addopb
+} : CALLXARGS
+x{DB1} @Defop(4u) JMPXARGS
+x{DB2} @Defop(4u) RETARGS
+x{DB30} dup @Defop RET @Defop RETTRUE
+x{DB31} dup @Defop RETALT @Defop RETFALSE
+x{DB32} dup @Defop BRANCH @Defop RETBOOL
+x{DB34} @Defop CALLCC
+x{DB35} @Defop JMPXDATA
+{ dup 1+ 0= { 16 + } if
+  <b x{DB36} s, rot 4 u, swap 4 u, @addopb
+} : CALLCCARGS
+x{DB38} @Defop CALLXVARARGS
+x{DB39} @Defop RETVARARGS
+x{DB3A} @Defop JMPXVARARGS
+x{DB3B} @Defop CALLCCVARARGS
+x{DB3C} @Defop(ref) CALLREF
+x{DB3D} @Defop(ref) JMPREF
+x{DB3E} @Defop(ref) JMPREFDATA
+x{DB3F} @Defop RETDATA
+x{DB4} @Defop(12u) RUNVM
+x{DB50} @Defop RUNVMX
+// conditional and iterated execution primitives
+x{DC} @Defop IFRET
+x{DD} @Defop IFNOTRET
+x{DE} @Defop IF
+x{DF} @Defop IFNOT
+' IFNOTRET : IF:
+' IFRET : IFNOT:
+x{E0} @Defop IFJMP
+x{E1} @Defop IFNOTJMP
+x{E2} @Defop IFELSE
+
+x{E300} @Defop(ref) IFREF
+x{E301} @Defop(ref) IFNOTREF
+x{E302} @Defop(ref) IFJMPREF
+x{E303} @Defop(ref) IFNOTJMPREF
+x{E30D} @Defop(ref) IFREFELSE
+x{E30E} @Defop(ref) IFELSEREF
+x{E30F} @Defop(ref*2) IFREFELSEREF
+
+{ 16 1 @havebitrefs nip } : @refop-fits?
+// b b1 [e0 e1 e2] -- b'
+{ -rot dup @cont-empty? { drop swap 0 } {
+  2dup @cont-fits? { rot 1 } {
+  over @refop-fits? { b> rot 2 } {
+  swap @| swap 2dup @cont-fits? { rot 1 } {
+  b> rot 2
+  } cond } cond } cond } cond
+  [] execute
+} : @run-cont-op
+{ triple 1 ' @run-cont-op does create } : @def-cont-op
+{ DROP } { PUSHCONT IF } { IFREF } @def-cont-op IF-cont
+{ IFRET } { PUSHCONT IFJMP } { IFJMPREF } @def-cont-op IFJMP-cont
+{ DROP } { PUSHCONT IFNOT } { IFNOTREF } @def-cont-op IFNOT-cont
+{ IFNOTRET } { PUSHCONT IFNOTJMP } { IFNOTJMPREF } @def-cont-op IFNOTJMP-cont
+{ dup 2over rot } : 3dup
+
+recursive IFELSE-cont2 {
+  dup @cont-empty? { drop IF-cont } {
+  over @cont-empty? { nip IFNOT-cont } {
+  3dup @two-cont-fit? { -rot PUSHCONT swap PUSHCONT IFELSE } {
+  3dup nip @cont-ref-fit? { rot swap PUSHCONT swap b> IFREFELSE } {
+  3dup drop @cont-ref-fit? { -rot PUSHCONT swap b> IFELSEREF } {
+  rot 32 2 @havebitrefs { rot b> rot b> IFREFELSEREF } {
+  @| -rot IFELSE-cont2
+  } cond } cond } cond } cond } cond } cond
+} swap !
+
+{ }> IF-cont } : }>IF
+{ }> IFNOT-cont } : }>IFNOT
+{ }> IFJMP-cont } : }>IFJMP
+{ }> IFNOTJMP-cont } : }>IFNOTJMP
+{ { @normal? IFJMP-cont } @doafter<{ } : IFJMP:<{
+{ { @normal? IFNOTJMP-cont } @doafter<{ } : IFNOTJMP:<{
+{ `else @endblk } : }>ELSE<{
+{ `else: @endblk } : }>ELSE:
+{ 1 { swap @normal? swap IFELSE-cont2 } does @doafter<{ } : @doifelse
+{ 1 { swap @normal? IFELSE-cont2 } does @doafter<{ } : @doifnotelse
+{
+  { dup `else eq? 
+    { drop @doifelse }
+    { dup `else: eq?
+      { drop IFJMP-cont }
+      { @normal? IF-cont
+      } cond 
+    } cond 
+  } @doafter<{ 
+} : IF:<{
+{
+  { dup `else eq? 
+    { drop @doifnotelse }
+    { dup `else: eq?
+      { drop IFNOTJMP-cont }
+      { @normal? IFNOT-cont
+      } cond 
+    } cond 
+  } @doafter<{ 
+} : IFNOT:<{
+
+x{E304} @Defop CONDSEL
+x{E305} @Defop CONDSELCHK
+x{E308} @Defop IFRETALT
+x{E309} @Defop IFNOTRETALT
+{ <b x{E39_} s, swap 5 u, @addopb } : IFBITJMP
+{ <b x{E3B_} s, swap 5 u, @addopb } : IFNBITJMP
+{ <b x{E3D_} s, swap 5 u, swap ref, @addopb } : IFBITJMPREF
+{ <b x{E3F_} s, swap 5 u, swap ref, @addopb } : IFNBITJMPREF
+
+x{E4} @Defop REPEAT
+x{E5} dup @Defop REPEATEND @Defop REPEAT:
+x{E6} @Defop UNTIL
+x{E7} dup @Defop UNTILEND @Defop UNTIL:
+x{E8} @Defop WHILE
+x{E9} @Defop WHILEEND
+x{EA} @Defop AGAIN
+x{EB} dup @Defop AGAINEND @Defop AGAIN:
+
+{ `do @endblk } : }>DO<{
+{ `do: @endblk } : }>DO:
+{ }> PUSHCONT REPEAT } : }>REPEAT
+{ { @normal? PUSHCONT REPEAT } @doafter<{ } : REPEAT:<{
+{ }> PUSHCONT UNTIL } : }>UNTIL
+{ { @normal? PUSHCONT UNTIL } @doafter<{ } : UNTIL:<{
+{ PUSHCONT { @normal? PUSHCONT WHILE } @doafter<{ } : @dowhile
+{ 
+  { dup `do eq? 
+    { drop @dowhile } 
+    { `do: eq? not abort"`}>DO<{` expected" PUSHCONT WHILEEND 
+    } cond 
+  } @doafter<{ 
+} : WHILE:<{
+{ }> PUSHCONT AGAIN } : }>AGAIN
+{ { @normal? PUSHCONT AGAIN } @doafter<{ } : AGAIN:<{
+
+x{E314} @Defop REPEATBRK
+x{E315} @Defop REPEATENDBRK
+x{E316} @Defop UNTILBRK
+x{E317} dup @Defop UNTILENDBRK @Defop UNTILBRK:
+x{E318} @Defop WHILEBRK
+x{E319} @Defop WHILEENDBRK
+x{E31A} @Defop AGAINBRK
+x{E31B} dup @Defop AGAINENDBRK @Defop AGAINBRK:
+
+{ }> PUSHCONT REPEATBRK } : }>REPEATBRK
+{ { @normal? PUSHCONT REPEATBRK } @doafter<{ } : REPEATBRK:<{
+{ }> PUSHCONT UNTILBRK } : }>UNTILBRK
+{ { @normal? PUSHCONT UNTILBRK } @doafter<{ } : UNTILBRK:<{
+{ PUSHCONT { @normal? PUSHCONT WHILEBRK } @doafter<{ } : @dowhile
+{
+  { dup `do eq? 
+    { drop @dowhile } 
+    { `do: eq? not abort"`}>DO<{` expected" PUSHCONT WHILEENDBRK
+    } cond 
+  } @doafter<{ 
+} : WHILEBRK:<{
+{ }> PUSHCONT AGAINBRK } : }>AGAINBRK
+{ { @normal? PUSHCONT AGAINBRK } @doafter<{ } : AGAINBRK:<{
+
+
+//
+// continuation stack manipulation and continuation creation
+//
+{ <b x{EC} s, rot 4 u, swap dup 1+ { 16 + } ifnot 4 u, @addopb } : SETCONTARGS
+{ 0 swap SETCONTARGS } : SETNUMARGS
+x{ED0} @Defop(4u) RETURNARGS
+x{ED10} @Defop RETURNVARARGS
+x{ED11} @Defop SETCONTVARARGS
+x{ED12} @Defop SETNUMVARARGS
+x{ED1E} @Defop BLESS
+x{ED1F} @Defop BLESSVARARGS
+{ <b x{EE} s, rot 4 u, swap dup 1+ { 16 + } ifnot 4 u, @addopb } : BLESSARGS
+{ 0 swap BLESSARGS } : BLESSNUMARGS
+//
+// control register and continuation savelist manipulation
+// x{ED4} Defop(c) PUSHCTR
+// x{ED5} Defop(c) POPCTR
+{ c4 PUSHCTR } : PUSHROOT
+{ c4 POPCTR } : POPROOT
+x{ED6} dup @Defop(c) SETCONTCTR @Defop(c) SETCONT
+x{ED7} @Defop(c) SETRETCTR 
+x{ED8} @Defop(c) SETALTCTR 
+x{ED9} dup @Defop(c) POPSAVE @Defop(c) POPCTRSAVE
+x{EDA} dup @Defop(c) SAVE @Defop(c) SAVECTR
+x{EDB} dup @Defop(c) SAVEALT @Defop(c) SAVEALTCTR
+x{EDC} dup @Defop(c) SAVEBOTH @Defop(c) SAVEBOTHCTR
+x{EDE0} @Defop PUSHCTRX
+x{EDE1} @Defop POPCTRX
+x{EDE2} @Defop SETCONTCTRX
+x{EDE3} @Defop(8u) SETCONTCTRMANY
+x{EDE3} @Defop(8u) SETCONTMANY
+x{EDE4} @Defop SETCONTCTRMANYX
+x{EDE4} @Defop SETCONTMANYX
+x{EDF0} dup @Defop BOOLAND @Defop COMPOS
+x{EDF1} dup @Defop BOOLOR @Defop COMPOSALT
+x{EDF2} @Defop COMPOSBOTH
+x{EDF3} @Defop ATEXIT
+{ }> PUSHCONT ATEXIT } : }>ATEXIT
+{ { @normal? PUSHCONT ATEXIT } @doafter<{ } : ATEXIT:<{
+x{EDF4} @Defop ATEXITALT
+{ }> PUSHCONT ATEXITALT } : }>ATEXITALT
+{ { @normal? PUSHCONT ATEXITALT } @doafter<{ } : ATEXITALT:<{
+x{EDF5} @Defop SETEXITALT
+{ }> PUSHCONT SETEXITALT } : }>SETEXITALT
+{ { @normal? PUSHCONT SETEXITALT } @doafter<{ } : SETEXITALT:<{
+x{EDF6} @Defop THENRET
+x{EDF7} @Defop THENRETALT
+x{EDF8} @Defop INVERT
+x{EDF9} @Defop BOOLEVAL
+x{EDFA} @Defop SAMEALT
+x{EDFB} @Defop SAMEALTSAVE
+// x{EE} is BLESSARGS
+//
+// dictionary subroutine call/jump primitives
+{ c3 PUSH EXECUTE } : CALLVAR
+{ c3 PUSH JMPX } : JMPVAR
+{ c3 PUSH } : PREPAREVAR
+{ dup 14 ufits {
+    dup 8 ufits {
+      <b x{F0} s, swap 8 u, } {
+      <b x{F12_} s, swap 14 u,
+    } cond @addopb } {
+    PUSHINT CALLVAR
+  } cond
+} dup : CALL : CALLDICT
+{ dup 14 ufits
+  { <b x{F16_} s, swap 14 u, @addopb }
+  { PUSHINT JMPVAR } cond
+} dup : JMP : JMPDICT
+{ dup 14 ufits
+  { <b x{F1A_} s, swap 14 u, @addopb }
+  { PUSHINT c3 PREPAREVAR } cond
+} dup : PREPARE : PREPAREDICT
+//
+// inline support
+{ dup sbits
+   { @addop }
+   {
+    dup srefs // 
+      { ref@ CALLREF }
+      { drop }
+      cond
+   }
+   cond
+} : INLINE
+//
+// throwing and handling exceptions
+{ dup 6 ufits
+  { <b x{F22_} s, swap 6 u, }
+  { <b x{F2C4_} s, swap 11 u,
+  } cond
+@addopb } : THROW
+{ dup 6 ufits
+  { <b x{F26_} s, swap 6 u, }
+  { <b x{F2D4_} s, swap 11 u,
+  } cond
+@addopb } : THROWIF
+{ dup 6 ufits
+  { <b x{F2A_} s, swap 6 u, }
+  { <b x{F2E4_} s, swap 11 u,
+  } cond
+@addopb } : THROWIFNOT
+{ <b x{F2CC_} s, swap 11 u, @addopb } : THROWARG
+{ <b x{F2DC_} s, swap 11 u, @addopb } : THROWARGIF
+{ <b x{F2EC_} s, swap 11 u, @addopb } : THROWARGIFNOT
+x{F2F0} @Defop THROWANY
+x{F2F1} @Defop THROWARGANY
+x{F2F2} @Defop THROWANYIF
+x{F2F3} @Defop THROWARGANYIF
+x{F2F4} @Defop THROWANYIFNOT
+x{F2F5} @Defop THROWARGANYIFNOT
+x{F2FF} @Defop TRY
+x{F3} @Defop(4u,4u) TRYARGS
+{ `catch @endblk } : }>CATCH<{
+{ PUSHCONT { @normal? PUSHCONT TRY } @doafter<{ } : @trycatch
+{ 
+  { `catch eq? not abort"`}>CATCH<{` expected" @trycatch
+  } @doafter<{ 
+} : TRY:<{
+//
+// dictionary manipulation
+' NULL : NEWDICT
+' ISNULL : DICTEMPTY
+' STSLICE : STDICTS
+x{F400} dup @Defop STDICT @Defop STOPTREF
+x{F401} dup @Defop SKIPDICT @Defop SKIPOPTREF
+x{F402} @Defop LDDICTS
+x{F403} @Defop PLDDICTS
+x{F404} dup @Defop LDDICT @Defop LDOPTREF
+x{F405} dup @Defop PLDDICT @Defop PLDOPTREF
+x{F406} @Defop LDDICTQ
+x{F407} @Defop PLDDICTQ
+
+x{F40A} @Defop DICTGET
+x{F40B} @Defop DICTGETREF
+x{F40C} @Defop DICTIGET
+x{F40D} @Defop DICTIGETREF
+x{F40E} @Defop DICTUGET
+x{F40F} @Defop DICTUGETREF
+
+x{F412} @Defop DICTSET
+x{F413} @Defop DICTSETREF
+x{F414} @Defop DICTISET
+x{F415} @Defop DICTISETREF
+x{F416} @Defop DICTUSET
+x{F417} @Defop DICTUSETREF
+x{F41A} @Defop DICTSETGET
+x{F41B} @Defop DICTSETGETREF
+x{F41C} @Defop DICTISETGET
+x{F41D} @Defop DICTISETGETREF
+x{F41E} @Defop DICTUSETGET
+x{F41F} @Defop DICTUSETGETREF
+
+x{F422} @Defop DICTREPLACE
+x{F423} @Defop DICTREPLACEREF
+x{F424} @Defop DICTIREPLACE
+x{F425} @Defop DICTIREPLACEREF
+x{F426} @Defop DICTUREPLACE
+x{F427} @Defop DICTUREPLACEREF
+x{F42A} @Defop DICTREPLACEGET
+x{F42B} @Defop DICTREPLACEGETREF
+x{F42C} @Defop DICTIREPLACEGET
+x{F42D} @Defop DICTIREPLACEGETREF
+x{F42E} @Defop DICTUREPLACEGET
+x{F42F} @Defop DICTUREPLACEGETREF
+
+x{F432} @Defop DICTADD
+x{F433} @Defop DICTADDREF
+x{F434} @Defop DICTIADD
+x{F435} @Defop DICTIADDREF
+x{F436} @Defop DICTUADD
+x{F437} @Defop DICTUADDREF
+x{F43A} @Defop DICTADDGET
+x{F43B} @Defop DICTADDGETREF
+x{F43C} @Defop DICTIADDGET
+x{F43D} @Defop DICTIADDGETREF
+x{F43E} @Defop DICTUADDGET
+x{F43F} @Defop DICTUADDGETREF
+
+x{F441} @Defop DICTSETB
+x{F442} @Defop DICTISETB
+x{F443} @Defop DICTUSETB
+x{F445} @Defop DICTSETGETB
+x{F446} @Defop DICTISETGETB
+x{F447} @Defop DICTUSETGETB
+
+x{F449} @Defop DICTREPLACEB
+x{F44A} @Defop DICTIREPLACEB
+x{F44B} @Defop DICTUREPLACEB
+x{F44D} @Defop DICTREPLACEGETB
+x{F44E} @Defop DICTIREPLACEGETB
+x{F44F} @Defop DICTUREPLACEGETB
+
+x{F451} @Defop DICTADDB
+x{F452} @Defop DICTIADDB
+x{F453} @Defop DICTUADDB
+x{F455} @Defop DICTADDGETB
+x{F456} @Defop DICTIADDGETB
+x{F457} @Defop DICTUADDGETB
+
+x{F459} @Defop DICTDEL
+x{F45A} @Defop DICTIDEL
+x{F45B} @Defop DICTUDEL
+
+x{F462} @Defop DICTDELGET
+x{F463} @Defop DICTDELGETREF
+x{F464} @Defop DICTIDELGET
+x{F465} @Defop DICTIDELGETREF
+x{F466} @Defop DICTUDELGET
+x{F467} @Defop DICTUDELGETREF
+
+x{F469} @Defop DICTGETOPTREF
+x{F46A} @Defop DICTIGETOPTREF
+x{F46B} @Defop DICTUGETOPTREF
+x{F46D} @Defop DICTSETGETOPTREF
+x{F46E} @Defop DICTISETGETOPTREF
+x{F46F} @Defop DICTUSETGETOPTREF
+
+x{F470} @Defop PFXDICTSET
+x{F471} @Defop PFXDICTREPLACE
+x{F472} @Defop PFXDICTADD
+x{F473} @Defop PFXDICTDEL
+
+x{F474} @Defop DICTGETNEXT
+x{F475} @Defop DICTGETNEXTEQ
+x{F476} @Defop DICTGETPREV
+x{F477} @Defop DICTGETPREVEQ
+x{F478} @Defop DICTIGETNEXT
+x{F479} @Defop DICTIGETNEXTEQ
+x{F47A} @Defop DICTIGETPREV
+x{F47B} @Defop DICTIGETPREVEQ
+x{F47C} @Defop DICTUGETNEXT
+x{F47D} @Defop DICTUGETNEXTEQ
+x{F47E} @Defop DICTUGETPREV
+x{F47F} @Defop DICTUGETPREVEQ
+
+x{F482} @Defop DICTMIN
+x{F483} @Defop DICTMINREF
+x{F484} @Defop DICTIMIN
+x{F485} @Defop DICTIMINREF
+x{F486} @Defop DICTUMIN
+x{F487} @Defop DICTUMINREF
+x{F48A} @Defop DICTMAX
+x{F48B} @Defop DICTMAXREF
+x{F48C} @Defop DICTIMAX
+x{F48D} @Defop DICTIMAXREF
+x{F48E} @Defop DICTUMAX
+x{F48F} @Defop DICTUMAXREF
+
+x{F492} @Defop DICTREMMIN
+x{F493} @Defop DICTREMMINREF
+x{F494} @Defop DICTIREMMIN
+x{F495} @Defop DICTIREMMINREF
+x{F496} @Defop DICTUREMMIN
+x{F497} @Defop DICTUREMMINREF
+x{F49A} @Defop DICTREMMAX
+x{F49B} @Defop DICTREMMAXREF
+x{F49C} @Defop DICTIREMMAX
+x{F49D} @Defop DICTIREMMAXREF
+x{F49E} @Defop DICTUREMMAX
+x{F49F} @Defop DICTUREMMAXREF
+
+x{F4A0} @Defop DICTIGETJMP
+x{F4A1} @Defop DICTUGETJMP
+x{F4A2} @Defop DICTIGETEXEC
+x{F4A3} @Defop DICTUGETEXEC
+{ dup sbitrefs tuck 1 > swap 1 <> or abort"not a dictionary" swap 1 u@ over <> abort"not a dictionary" } : @chkdicts
+{ dup null? tuck { <s } ifnot drop not } : @chkdict 
+{ over @chkdict
+  { swap <b x{F4A6_} s, swap ref, swap 10 u, @addopb }
+  { nip swap NEWDICT swap PUSHINT }
+  cond
+} : DICTPUSHCONST
+x{F4A8} @Defop PFXDICTGETQ
+x{F4A9} @Defop PFXDICTGET
+x{F4AA} @Defop PFXDICTGETJMP
+x{F4AB} @Defop PFXDICTGETEXEC
+{ over @chkdict
+  { swap <b x{F4AE_} s, swap ref, swap 10 u, @addopb
+  } if
+} dup : PFXDICTCONSTGETJMP : PFXDICTSWITCH
+
+x{F4B1} @Defop SUBDICTGET
+x{F4B2} @Defop SUBDICTIGET
+x{F4B3} @Defop SUBDICTUGET
+x{F4B5} @Defop SUBDICTRPGET
+x{F4B6} @Defop SUBDICTIRPGET
+x{F4B7} @Defop SUBDICTURPGET
+
+x{F4BC} @Defop DICTIGETJMPZ
+x{F4BD} @Defop DICTUGETJMPZ
+x{F4BE} @Defop DICTIGETEXECZ
+x{F4BF} @Defop DICTUGETEXECZ
+
+//
+// blockchain-specific primitives
+
+x{F800} @Defop ACCEPT
+x{F801} @Defop SETGASLIMIT
+x{F807} @Defop GASCONSUMED
+x{F80F} @Defop COMMIT
+
+x{F810} @Defop RANDU256
+x{F811} @Defop RAND
+x{F814} @Defop SETRAND
+x{F815} dup @Defop ADDRAND @Defop RANDOMIZE
+
+x{F82} @Defop(4u) GETPARAM
+x{F823} @Defop NOW
+x{F824} @Defop BLOCKLT
+x{F825} @Defop LTIME
+x{F826} @Defop RANDSEED
+x{F827} @Defop BALANCE
+x{F828} @Defop MYADDR
+x{F829} @Defop CONFIGROOT
+x{F82A} @Defop MYCODE
+x{F82B} @Defop INCOMINGVALUE
+x{F82C} @Defop STORAGEFEES
+x{F82D} @Defop PREVBLOCKSINFOTUPLE
+x{F82E} @Defop UNPACKEDCONFIGTUPLE
+x{F82F} @Defop DUEPAYMENT
+x{F830} @Defop CONFIGDICT
+x{F832} @Defop CONFIGPARAM
+x{F833} @Defop CONFIGOPTPARAM
+x{F83400} @Defop PREVMCBLOCKS
+x{F83401} @Defop PREVKEYBLOCK
+x{F83402} @Defop PREVMCBLOCKS_100
+x{F835} @Defop GLOBALID
+x{F836} @Defop GETGASFEE
+x{F837} @Defop GETSTORAGEFEE
+x{F838} @Defop GETFORWARDFEE
+x{F839} @Defop GETPRECOMPILEDGAS
+x{F83A} @Defop GETORIGINALFWDFEE
+x{F83B} @Defop GETGASFEESIMPLE
+x{F83C} @Defop GETFORWARDFEESIMPLE
+
+x{F840} @Defop GETGLOBVAR
+{ dup 1 31 @rangechk <b x{F85_} s, swap 5 u, @addopb } : GETGLOB
+x{F860} @Defop SETGLOBVAR
+{ dup 1 31 @rangechk <b x{F87_} s, swap 5 u, @addopb } : SETGLOB
+
+x{F900} @Defop HASHCU
+x{F901} @Defop HASHSU
+x{F902} @Defop SHA256U
+
+x{F904} @Defop(8u) HASHEXT
+x{F90400} @Defop HASHEXT_SHA256
+x{F90401} @Defop HASHEXT_SHA512
+x{F90402} @Defop HASHEXT_BLAKE2B
+x{F90403} @Defop HASHEXT_KECCAK256
+x{F90404} @Defop HASHEXT_KECCAK512
+x{F905} @Defop(8u) HASHEXTR
+x{F90500} @Defop HASHEXTR_SHA256
+x{F90501} @Defop HASHEXTR_SHA512
+x{F90502} @Defop HASHEXTR_BLAKE2B
+x{F90503} @Defop HASHEXTR_KECCAK256
+x{F90504} @Defop HASHEXTR_KECCAK512
+x{F906} @Defop(8u) HASHEXTA
+x{F90600} @Defop HASHEXTA_SHA256
+x{F90601} @Defop HASHEXTA_SHA512
+x{F90602} @Defop HASHEXTA_BLAKE2B
+x{F90603} @Defop HASHEXTA_KECCAK256
+x{F90604} @Defop HASHEXTA_KECCAK512
+x{F907} @Defop(8u) HASHEXTAR
+x{F90700} @Defop HASHEXTAR_SHA256
+x{F90701} @Defop HASHEXTAR_SHA512
+x{F90702} @Defop HASHEXTAR_BLAKE2B
+x{F90703} @Defop HASHEXTAR_KECCAK256
+x{F90704} @Defop HASHEXTAR_KECCAK512
+
+x{F910} @Defop CHKSIGNU
+x{F911} @Defop CHKSIGNS
+x{F912} @Defop ECRECOVER
+x{F913} @Defop SECP256K1_XONLY_PUBKEY_TWEAK_ADD
+x{F914} @Defop P256_CHKSIGNU
+x{F915} @Defop P256_CHKSIGNS
+
+x{F920} @Defop RIST255_FROMHASH
+x{F921} @Defop RIST255_VALIDATE
+x{F922} @Defop RIST255_ADD
+x{F923} @Defop RIST255_SUB
+x{F924} @Defop RIST255_MUL
+x{F925} @Defop RIST255_MULBASE
+x{F926} @Defop RIST255_PUSHL
+
+x{B7F921} @Defop RIST255_QVALIDATE
+x{B7F922} @Defop RIST255_QADD
+x{B7F923} @Defop RIST255_QSUB
+x{B7F924} @Defop RIST255_QMUL
+x{B7F925} @Defop RIST255_QMULBASE
+
+x{F93000} @Defop BLS_VERIFY
+x{F93001} @Defop BLS_AGGREGATE
+x{F93002} @Defop BLS_FASTAGGREGATEVERIFY
+x{F93003} @Defop BLS_AGGREGATEVERIFY
+
+x{F93010} @Defop BLS_G1_ADD
+x{F93011} @Defop BLS_G1_SUB
+x{F93012} @Defop BLS_G1_NEG
+x{F93013} @Defop BLS_G1_MUL
+x{F93014} @Defop BLS_G1_MULTIEXP
+x{F93015} @Defop BLS_G1_ZERO
+x{F93016} @Defop BLS_MAP_TO_G1
+x{F93017} @Defop BLS_G1_INGROUP
+x{F93018} @Defop BLS_G1_ISZERO
+
+x{F93020} @Defop BLS_G2_ADD
+x{F93021} @Defop BLS_G2_SUB
+x{F93022} @Defop BLS_G2_NEG
+x{F93023} @Defop BLS_G2_MUL
+x{F93024} @Defop BLS_G2_MULTIEXP
+x{F93025} @Defop BLS_G2_ZERO
+x{F93026} @Defop BLS_MAP_TO_G2
+x{F93027} @Defop BLS_G2_INGROUP
+x{F93028} @Defop BLS_G2_ISZERO
+
+x{F93030} @Defop BLS_PAIRING
+x{F93031} @Defop BLS_PUSHR
+
+x{F940} @Defop CDATASIZEQ
+x{F941} @Defop CDATASIZE
+x{F942} @Defop SDATASIZEQ
+x{F943} @Defop SDATASIZE
+
+x{FA00} dup @Defop LDGRAMS @Defop LDVARUINT16
+x{FA01} @Defop LDVARINT16
+x{FA02} dup @Defop STGRAMS @Defop STVARUINT16
+x{FA03} @Defop STVARINT16
+
+x{FA04} @Defop LDVARUINT32 // (s -- x s')
+x{FA05} @Defop LDVARINT32  // (s -- x s')
+x{FA06} @Defop STVARUINT32 // (b x -- b')
+x{FA07} @Defop STVARINT32  // (b x -- b')
+
+x{FA40} @Defop LDMSGADDR
+x{FA41} @Defop LDMSGADDRQ
+x{FA42} @Defop PARSEMSGADDR
+x{FA43} @Defop PARSEMSGADDRQ
+x{FA44} @Defop REWRITESTDADDR
+x{FA45} @Defop REWRITESTDADDRQ
+x{FA46} @Defop REWRITEVARADDR
+x{FA47} @Defop REWRITEVARADDRQ
+
+x{FB00} @Defop SENDRAWMSG
+x{FB02} @Defop RAWRESERVE
+x{FB03} @Defop RAWRESERVEX
+x{FB04} @Defop SETCODE
+x{FB06} @Defop SETLIBCODE
+x{FB07} @Defop CHANGELIB
+x{FB08} @Defop SENDMSG
+
+//
+// debug primitives
+
+{ dup 0 239 @-range abort"debug selector out of range"
+  <b x{FE} s, swap 8 u, @addopb
+} : DEBUG
+{ dup $len 1- <b x{FEF} s, swap 4 u, swap $, @addopb
+} : DEBUGSTR
+{ over $len <b x{FEF} s, swap 4 u, swap 8 u, swap $, @addopb
+} : DEBUGSTRI
+
+x{FE00} @Defop DUMPSTK
+{ 1 15 @rangechk <b x{FE0} s, swap 4 u, @addopb
+} : DUMPSTKTOP
+x{FE10} @Defop HEXDUMP
+x{FE11} @Defop HEXPRINT
+x{FE12} @Defop BINDUMP
+x{FE13} @Defop BINPRINT
+x{FE14} @Defop STRDUMP
+x{FE15} @Defop STRPRINT
+x{FE1E} @Defop DEBUGOFF
+x{FE1F} @Defop DEBUGON
+x{FE2} @Defop(s) DUMP
+x{FE3} @Defop(s) PRINT
+' DEBUGSTR : DUMPTOSFMT
+{ 0 DEBUGSTRI } : LOGSTR
+{ 1 DEBUGSTRI } : PRINTSTR
+x{FEF000} @Defop LOGFLUSH
+
+//
+// codepage primitives
+x{FF00} @Defop SETCP0
+x{FFF0} @Defop SETCPX
+{ dup -14 239 @-range abort"codepage out of range"
+  255 and <b x{FF} s, swap 8 u, @addopb
+} : SETCP
+
+' @addop : CUSTOMOP
+
+//
+// provisions for defining programs consisting of several mutually-recursive procedures
+//
+variable @proccnt
+variable @proclist
+variable @procdict
+variable @procinfo
+variable @gvarcnt
+variable @parent-state
+variable asm-mode  1 asm-mode !
+19 constant @procdictkeylen
+32 constant @zcount
+{ pair @proclist @ cons @proclist ! } : @proclistadd
+{ @procinfo @ @procdictkeylen idict@ { 16 i@ } { 0 } cond } : @procinfo@
+{ <b rot 16 i, swap @procinfo @ @procdictkeylen b>idict!
+  not abort"cannot add key to procedure info dictionary"
+  @procinfo !
+} : @procinfo!
+// ( x v1 v2 -- )
+{ not 2 pick @procinfo@ and xor swap @procinfo! } : @procinfo~!
+// ( s i f -- )
+{ over @procdictkeylen fits not abort"procedure index out of range"
+  over swap dup @procinfo~!  2dup @proclistadd
+  1 'nop does swap 0 (create)
+} : @declproc
+{ 1 'nop does swap 0 (create) } : @declglobvar
+{ @proccnt @ 1+ dup @proccnt ! 1 @declproc } : @newproc
+{ @gvarcnt @ 1+ dup @gvarcnt ! @declglobvar } : @newglobvar
+variable @oldcurrent  variable @oldctx
+Fift-wordlist dup @oldcurrent ! @oldctx !
+{ current@ @oldcurrent !  context@ @oldctx !  Asm definitions
+  @proccnt @ @proclist @ @procdict @ @procinfo @ @gvarcnt @ @parent-state @ current@ @oldcurrent @ @oldctx @
+  9 tuple @parent-state !
+  hole current!
+  0 =: main  @proclist null!  @proccnt 0!  @gvarcnt 0!
+  { bl word @newproc } : NEWPROC
+  { bl word dup (def?) ' drop ' @newproc cond } : DECLPROC
+  { bl word dup find
+    { nip execute <> abort"method redefined with different id" }
+    { swap 17 @declproc }
+  cond } : DECLMETHOD
+  { bl word @newglobvar } : DECLGLOBVAR
+  "main" 0 @proclistadd
+  dictnew dup @procdict !
+  @procinfo !  16 0 @procinfo!
+} : PROGRAM{
+{ over sbits < { s>c <b swap ref, b> <s } if } : @adj-long-proc
+{ // i s l
+  dup 0< {
+    negate
+    @was-split @ { drop 0 } if
+  } if
+  @adj-long-proc over @procdict @ @procdictkeylen
+  idict!+ not abort"cannot define procedure, redefined?"
+  @procdict !  2 2 @procinfo~!
+} : @def-proc
+{ @procinfo @ null? not } : @have-procinfo?
+{ @have-procinfo? { 4 4 @procinfo~! } { drop } cond } : @proc-inlined
+{ @have-procinfo? { 8 8 @procinfo~! } { drop } cond } : @proc-called
+{ 1000 @def-proc } : PROC
+{ 0 @def-proc } : PROCREF
+{ -1000 @def-proc } : PROCINLINE
+{ @procdict @ @procdictkeylen idict@ abort"procedure already defined"
+} : @fail-ifdef
+{ u@?+ { swap abort"first bits are not zeroes" } if } : @cut-zeroes
+{ over @fail-ifdef
+  2 { rot @normal? rot b> <s @zcount @cut-zeroes swap @def-proc drop } does
+  null swap @doafter<{ 0 @zcount u,
+} : @PROC:<{
+{ 1000 @PROC:<{ } : PROC:<{
+{ 0 @PROC:<{ } : PROCREF:<{
+{ -1000 @PROC:<{ } : PROCINLINE:<{
+{ dup @proc-called CALLDICT } dup : CALL : CALLDICT
+{ dup @proc-called JMPDICT } dup : JMP : JMPDICT
+{ dup @proc-called PREPAREDICT } dup : PREPARE : PREPAREDICT
+{ dup @procdict @ @procdictkeylen idict@
+  { swap @proc-inlined INLINE } { CALLDICT } cond
+} dup : INLINECALL : INLINECALLDICT
+{ 0 @procdict @ @procdictkeylen idict@ not abort"`main` procedure not defined" drop
+} : @chkmaindef
+{ @procdict @ @procdictkeylen idict- drop @procdict ! } : @remove-proc
+{ ."Procedure `" over type ."` index=" 2 pick . ." flags=0x" dup x. cr } : @showprocinfo
+// ( proc_name proc_idx f -- )  f:+1=declared, +2=defined, +4=inlined, +8=called, +16=method
+{ // @showprocinfo
+  dup 0x1a and 2 = asm-mode @ 3 and and ?dup {
+    2 and {
+      over ."Warning: removing (inlined) procedure `" type ."` from call dictionary" cr
+    } if
+    2 pick @remove-proc
+  } if  // remove unused procs
+  dup 0xc and 0xc = asm-mode @ 4 and and {
+    over ."Warning: inline procedure `" type ."` is not always inlined" cr
+  } if
+  dup 0x1e and 2 = asm-mode @ 8 and and {
+    over ."Warning: procedure `" type ."` defined but not used" cr
+  } if
+  drop 2drop
+} : @chkprocdef
+{ @chkmaindef
+  @proclist @ { dup null? not } {
+    uncons swap unpair over find not
+    { drop +": undefined procedure name in list" abort } if
+    drop tuck @procdict @ @procdictkeylen idict@ not
+    { +": procedure declared but left undefined" abort } if
+    drop swap 2dup @procinfo@ @chkprocdef (forget)
+  } while
+  drop @proclist null! @procinfo null! @proccnt 0!
+  @procdict dup @ swap null!
+  @parent-state @ dup null? { drop } {
+    9 untuple
+    @oldctx ! @oldcurrent ! current! @parent-state ! @gvarcnt ! @procinfo ! @procdict ! @proclist ! @proccnt !
+  } cond
+  @oldctx @ context!  @oldcurrent @ current!
+} : }END
+forget @proclist  forget @proccnt
+{ }END <{ SETCP0 swap @procdictkeylen DICTPUSHCONST DICTIGETJMPZ 11 THROWARG }> } : }END>
+{ }END> b> } : }END>c
+{ }END>c <s } : }END>s
+
+// This is the way how FunC assigns method_id for reserved functions.
+// Note, that Tolk entrypoints have other names (`onInternalMessage`, etc.),
+// but method_id is assigned not by Fift, but by Tolk code generation.
+0 constant recv_internal
+-1 constant recv_external
+-2 constant run_ticktock
+-3 constant split_prepare
+-4 constant split_install
+
+{ asm-mode 0 3 ~! } : asm-no-remove-unused
+{ asm-mode 1 1 ~! } : asm-remove-unused   // enabled by default
+{ asm-mode 3 3 ~! } : asm-warn-remove-unused
+{ asm-mode 4 4 ~! } : asm-warn-inline-mix
+{ asm-mode 0 4 ~! } : asm-no-warn-inline-mix  // disabled by default
+{ asm-mode 8 8 ~! } : asm-warn-unused
+{ asm-mode 0 8 ~! } : asm-no-warn-unused  // disabled by default
+
+// ( c -- )   add vm library for later use with runvmcode
+{ <b over ref, b> <s swap hash vmlibs @ 256 udict! not abort"cannot add library" vmlibs ! } : add-lib
+// ( x -- c ) make library reference cell
+{ <b 2 8 u, swap 256 u, b>spec } : hash>libref
+// ( c -- c' )
+{ hash hash>libref } : >libref
+
+{ dup "." $pos dup -1 =
+     { drop 0 }
+     { $| 1 $| nip swap (number) 1- abort"invalid version"
+       dup dup 0 < swap 999 > or abort"invalid version"
+     }
+     cond
+} : parse-version-level
+
+{
+  0 swap
+  "." $+
+  { swap 1000 * swap parse-version-level rot + swap } 3 times
+  "" $= not abort"invalid version"
+} : parse-asm-fif-version
+
+{
+  dup =: required-version parse-asm-fif-version
+  asm-fif-version parse-asm-fif-version
+  = 1+ {
+      "Required Asm.fif version: " @' required-version "; actual Asm.fif version: " asm-fif-version $+ $+ $+ abort
+    } if
+} : require-asm-fif-version
+
+{
+  dup =: required-version parse-asm-fif-version
+  asm-fif-version parse-asm-fif-version
+  swap
+  >= 1+ {
+      "Required Asm.fif version: " @' required-version "; actual Asm.fif version: " asm-fif-version $+ $+ $+ abort
+    } if
+} : require-asm-fif-version>=
+
+
+Fift definitions Asm
+' <{ : <{
+' PROGRAM{ : PROGRAM{
+' asm-fif-version : asm-fif-version
+' require-asm-fif-version : require-asm-fif-version
+' require-asm-fif-version>= : require-asm-fif-version>=
+Fift
diff --git a/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Color.fif b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Color.fif
new file mode 100644
index 0000000000..bd8ed7af6d
--- /dev/null
+++ b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Color.fif
@@ -0,0 +1,21 @@
+library Color
+{ 27 emit } : esc
+
{ char " word 27 chr swap $+ 1 ' type does create } :_ make-esc"
+
make-esc"[0m" ^reset
+
make-esc"[30m" ^black
+
make-esc"[31m" ^red
+
make-esc"[32m" ^green
+make-esc"[33m" ^yellow
+make-esc"[34m" ^blue
+make-esc"[35m" ^magenta
+make-esc"[36m" ^cyan
+make-esc"[37m" ^white
+
// bold
+make-esc"[30;1m" ^Black
+make-esc"[31;1m" ^Red
+make-esc"[32;1m" ^Green
+make-esc"[33;1m" ^Yellow
+make-esc"[34;1m" ^Blue
+make-esc"[35;1m" ^Magenta
+make-esc"[36;1m" ^Cyan
+make-esc"[37;1m" ^White
diff --git a/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Disasm.fif b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Disasm.fif
new file mode 100644
index 0000000000..a46eb5b277
--- /dev/null
+++ b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Disasm.fif
@@ -0,0 +1,141 @@
+library TVM_Disasm
+// simple TVM Disassembler
+"Lists.fif" include
+
+variable 'disasm
+{ 'disasm @ execute } : disasm  // disassemble a slice
+// usage: x{74B0} disasm
+
+variable @dismode  @dismode 0!
+{ rot over @ and rot xor swap ! } : andxor!
+{ -2 0 @dismode andxor! } : stack-disasm  // output 's1 s4 XCHG'
+{ -2 1 @dismode andxor! } : std-disasm    // output 'XCHG s1, s4'
+{ -3 2 @dismode andxor! } : show-vm-code
+{ -3 0 @dismode andxor! } : hide-vm-code
+{ @dismode @ 1 and 0= } : stack-disasm?
+
+variable @indent  @indent 0!
+{ ' space @indent @ 2* times } : .indent
+{ @indent 1+! } : +indent
+{ @indent 1-! } : -indent
+
+{ " " $pos } : spc-pos
+{ dup " " $pos swap "," $pos dup 0< { drop } {
+  over 0< { nip } { min } cond } cond
+} : spc-comma-pos
+{ { dup spc-pos 0= } { 1 $| nip } while } : -leading
+{ -leading -trailing dup spc-pos dup 0< {
+  drop dup $len { atom single } { drop nil } cond } {
+    $| swap atom swap -leading 2 { over spc-comma-pos dup 0>= } {
+      swap 1+ -rot $| 1 $| nip -leading rot
+    } while drop tuple
+  } cond
+} : parse-op
+{ dup "s-1" $= { drop "s(-1)" true } {
+  dup "s-2" $= { drop "s(-2)" true } {
+  dup 1 $| swap "x" $= { nip "x{" swap $+ +"}" true } {
+  2drop false } cond } cond } cond
+} : adj-op-arg
+{ over count over <= { drop } { 2dup [] adj-op-arg { swap []= } { drop } cond } cond } : adj-arg[]
+{ 1 adj-arg[] 2 adj-arg[] 3 adj-arg[]
+  dup first
+  dup `XCHG eq? {
+    drop dup count 2 = { tpop swap "s0" , swap , } if } {
+  dup `LSHIFT eq? {
+    drop dup count 2 = stack-disasm? and { second `LSHIFT# swap pair } if } {
+  dup `RSHIFT eq? {
+    drop dup count 2 = stack-disasm? and { second `RSHIFT# swap pair } if } {
+  drop 
+  } cond } cond } cond
+} : adjust-op
+
+variable @cp  @cp 0!
+variable @curop
+variable @contX  variable @contY  variable @cdict
+
+{ atom>$ type } : .atom
+{ dup first .atom dup count 1 > { space 0 over count 2- { 1+ 2dup [] type .", " } swap times 1+ [] type } { drop } cond } : std-show-op
+{ 0 over count 1- { 1+ 2dup [] type space } swap times drop first .atom } : stk-show-op
+{ @dismode @ 2 and { .indent ."// " @curop @ csr. } if } : .curop?
+{ .curop? .indent @dismode @ 1 and ' std-show-op ' stk-show-op cond cr
+} : show-simple-op
+{ dup 4 u@ 9 = { 8 u@+ swap 15 and 3 << s@ } {
+  dup 7 u@ 0x47 = { 7 u@+ nip 2 u@+ 7 u@+ -rot 3 << swap sr@ } {
+  dup 8 u@ 0x8A = { ref@ <s } {
+  abort"invalid PUSHCONT"
+  } cond } cond } cond
+} : get-cont-body
+{ 14 u@+ nip 10 u@+ ref@ dup rot pair swap <s empty? { drop null } if } : get-const-dict
+{ @contX @ @contY @ @contX ! @contY ! } : scont-swap
+{ .indent swap type type cr @contY @ @contY null! @contX @ @contX null!
+  +indent disasm -indent @contY !
+} : show-cont-bodyx
+{ ":<{" show-cont-bodyx .indent ."}>" cr } : show-cont-op
+{ swap scont-swap ":<{" show-cont-bodyx scont-swap
+  "" show-cont-bodyx .indent ."}>" cr } : show-cont2-op
+
+{ @contX @ null? { "CONT" show-cont-op } ifnot
+} : flush-contX
+{ @contY @ null? { scont-swap "CONT" show-cont-op scont-swap } ifnot
+} : flush-contY
+{ flush-contY flush-contX } : flush-cont
+{ @contX @ null? not } : have-cont?
+{ @contY @ null? not } : have-cont2?
+{ flush-contY @contY ! scont-swap } : save-cont-body
+
+{ @cdict ! } : save-const-dict
+{ @cdict null! } : flush-dict
+{ @cdict @ null? not } : have-dict?
+
+{ flush-cont .indent type .":<{" cr
+  @curop @ ref@ <s +indent disasm -indent .indent ."}>" cr
+} : show-ref-op
+{ flush-contY .indent rot type .":<{" cr
+  @curop @ ref@ <s @contX @ @contX null! rot ' swap if
+  +indent disasm -indent .indent swap type cr
+  +indent disasm -indent .indent ."}>" cr
+} : show-cont-ref-op
+{ flush-cont .indent swap type .":<{" cr
+  @curop @ ref@+ <s +indent disasm -indent .indent swap type cr
+  ref@ <s +indent disasm -indent .indent ."}>" cr
+} : show-ref2-op
+
+{ flush-cont first atom>$ dup 5 $| drop "DICTI" $= swap
+  .indent type ." {" cr +indent @cdict @ @cdict null! unpair
+  rot {
+    swap .indent . ."=> <{" cr +indent disasm -indent .indent ."}>" cr true
+  } swap ' idictforeach ' dictforeach cond drop 
+  -indent .indent ."}" cr
+} : show-const-dict-op
+
+( `PUSHCONT `PUSHREFCONT ) constant @PushContL
+( `REPEAT `UNTIL `IF `IFNOT `IFJMP `IFNOTJMP ) constant @CmdC1
+( `IFREF `IFNOTREF `IFJMPREF `IFNOTJMPREF `CALLREF `JMPREF ) constant @CmdR1
+( `DICTIGETJMP `DICTIGETJMPZ `DICTUGETJMP `DICTUGETJMPZ `DICTIGETEXEC `DICTUGETEXEC ) constant @JmpDictL
+{ dup first `DICTPUSHCONST eq? {
+    flush-cont @curop @ get-const-dict save-const-dict show-simple-op } {
+  dup first @JmpDictL list-member? have-dict? and {
+    flush-cont show-const-dict-op } {
+  flush-dict
+  dup first @PushContL list-member? {
+    drop @curop @ get-cont-body save-cont-body } {
+  dup first @CmdC1 list-member? have-cont? and {
+    flush-contY first atom>$ .curop? show-cont-op } {
+  dup first @CmdR1 list-member? {
+    flush-cont first atom>$ dup $len 3 - $| drop .curop? show-ref-op } {
+  dup first `WHILE eq? have-cont2? and {
+    drop "WHILE" "}>DO<{" .curop? show-cont2-op } {
+  dup first `IFELSE eq? have-cont2? and {
+    drop "IF" "}>ELSE<{" .curop? show-cont2-op } {
+  dup first dup `IFREFELSE eq? swap `IFELSEREF eq? or have-cont? and {
+    first `IFREFELSE eq? "IF" "}>ELSE<{" rot .curop? show-cont-ref-op } {
+  dup first `IFREFELSEREF eq? {
+    drop "IF" "}>ELSE<{" .curop? show-ref2-op } {
+    flush-cont show-simple-op
+  } cond } cond } cond } cond } cond } cond } cond } cond } cond
+} : show-op
+{ dup @cp @ (vmoplen) dup 0> { 65536 /mod swap sr@+ swap dup @cp @ (vmopdump) parse-op swap s> true } { drop false } cond } : fetch-one-op
+{ { fetch-one-op } { swap @curop ! adjust-op show-op } while } : disasm-slice
+{ { disasm-slice dup sbitrefs 1- or 0= } { ref@ <s } while flush-dict flush-cont } : disasm-chain
+{ @curop @ swap disasm-chain dup sbitrefs or { .indent ."Cannot disassemble: " csr. } { drop } cond @curop ! }
+'disasm !
diff --git a/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Fift.fif b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Fift.fif
new file mode 100644
index 0000000000..0fde5cb945
--- /dev/null
+++ b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Fift.fif
@@ -0,0 +1,142 @@
+{ 0 word drop 0 'nop } :: //
+{ char " word 1 { swap { abort } if drop } } ::_ abort"
+{ { bl word dup "" $= abort"comment extends after end of file" "*/" $= } until 0 'nop } :: /*
+// { bl word 1 2 ' (create) } "::" 1 (create)
+// { bl word 0 2 ' (create) } :: :
+// { bl word 2 2 ' (create) } :: :_
+// { bl word 3 2 ' (create) } :: ::_
+// { bl word 0 (create) } : create
+// { bl word (forget) } : forget
+{ bl word 1 ' (forget) } :: [forget]
+{ char " word 1 ' type } ::_ ."
+{ char } word x>B 1 'nop } ::_ B{
+{ swap ({) over 2+ -roll swap (compile) (}) } : does
+{ 1 'nop does create } : constant
+{ 2 'nop does create } : 2constant
+{ hole constant } : variable
+10 constant ten
+{ bl word 1 { find 0= abort"word not found" } } :: (')
+{ bl word find not abort"-?" 0 swap } :: [compile]
+{ bl word 1 { 
+  dup find { " -?" $+ abort } ifnot nip execute
+} } :: @'
+{ bl word 1 { swap 1 'nop does swap 0 (create) }
+} :: =:
+{ bl word 1 { -rot 2 'nop does swap 0 (create) }
+} :: 2=:
+{ <b swap s, b> } : s>c
+{ s>c hashB } : shash
+// to be more efficiently re-implemented in C++ in the future
+{ dup 0< ' negate if } : abs
+{ 2dup > ' swap if } : minmax
+{ minmax drop } : min
+{ minmax nip } : max
+"" constant <#
+' $reverse : #>
+{ swap 10 /mod char 0 + rot swap hold } : #
+{ { # over 0<= } until } : #s
+{ 0< { char - hold } if } : sign
+// { dup abs <# #s rot sign #> nip } : (.)
+// { (.) type } : ._
+// { ._ space } : .
+{ dup 10 < { 48 } { 55 } cond + } : Digit
+{ dup 10 < { 48 } { 87 } cond + } : digit
+// x s b -- x' s'
+{ rot swap /mod Digit rot swap hold } : B#
+{ rot swap /mod digit rot swap hold } : b#
+{ 16 B# } : X#
+{ 16 b# } : x#
+// x s b -- 0 s'
+{ -rot { 2 pick B# over 0<= } until rot drop } : B#s
+{ -rot { 2 pick b# over 0<= } until rot drop } : b#s
+{ 16 B#s } : X#s
+{ 16 b#s } : x#s
+variable base
+{ 10 base ! } : decimal
+{ 16 base ! } : hex
+{ 8 base ! } : octal
+{ 2 base ! } : binary
+{ base @ B# } : Base#
+{ base @ b# } : base#
+{ base @ B#s } : Base#s
+{ base @ b#s } : base#s
+// x w -- s
+{ over abs <# rot 1- ' X# swap times X#s rot sign #> nip } : (0X.)
+{ over abs <# rot 1- ' x# swap times x#s rot sign #> nip } : (0x.)
+{ (0X.) type } : 0X._
+{ 0X._ space } : 0X.
+{ (0x.) type } : 0x._
+{ 0x._ space } : 0x.
+{ bl (-trailing) } : -trailing
+{ char 0 (-trailing) } : -trailing0
+{ char " word 1 ' $+ } ::_ +"
+{ find 0<> dup ' nip if } : (def?)
+{ bl word 1 ' (def?) } :: def?
+{ bl word 1 { (def?) not } } :: undef?
+{ def? ' skip-to-eof if } : skip-ifdef
+{ bl word dup (def?) { drop skip-to-eof } { 'nop swap 0 (create) } cond } : library
+{ bl word dup (def?) { 2drop skip-to-eof } { swap 1 'nop does swap 0 (create) } cond } : library-version
+{ hole dup 1 'nop does swap 1 { context! } does bl word tuck 0 (create) +"-wordlist" 0 (create) } : namespace
+{ context@ current! } : definitions
+{ char ) word "$" swap $+ 1 { find 0= abort"undefined parameter" execute } } ::_ $(
+// b s -- ?
+{ sbitrefs rot brembitrefs rot >= -rot <= and } : s-fits?
+// b s x -- ?
+{ swap sbitrefs -rot + rot brembitrefs -rot <= -rot <= and } : s-fits-with?
+{ 0 swap ! } : 0!
+{ tuck @ + swap ! } : +!
+{ tuck @ swap - swap ! } : -!
+{ 1 swap +! } : 1+!
+{ -1 swap +! } : 1-!
+{ null swap ! } : null!
+{ not 2 pick @ and xor swap ! } : ~!
+0 tuple constant nil
+{ 1 tuple } : single
+{ 2 tuple } : pair
+{ 3 tuple } : triple
+{ 1 untuple } : unsingle
+{ 2 untuple } : unpair
+{ 3 untuple } : untriple
+{ over tuple? { swap count = } { 2drop false } cond } : tuple-len?
+{ 0 tuple-len? } : nil?
+{ 1 tuple-len? } : single?
+{ 2 tuple-len? } : pair?
+{ 3 tuple-len? } : triple?
+{ 0 [] } : first
+{ 1 [] } : second
+{ 2 [] } : third
+' pair : cons
+' unpair : uncons
+{ 0 [] } : car
+{ 1 [] } : cdr
+{ cdr car } : cadr
+{ cdr cdr } : cddr
+{ cdr cdr car } : caddr
+{ null ' cons rot times } : list
+{ -rot pair swap ! } : 2!
+{ @ unpair } : 2@
+{ true (atom) drop } : atom
+{ bl word atom 1 'nop } ::_ `
+{ hole dup 1 { @ execute } does create } : recursive
+{ 0 { 1+ dup 1 ' $() does over (.) "$" swap $+ 0 (create) } rot times drop } : :$1..n
+{ 10 hold } : +cr
+{ 9 hold } : +tab
+{ "" swap { 0 word 2dup $cmp } { rot swap $+ +cr swap } while 2drop } : scan-until-word
+{ 0 word -trailing scan-until-word 1 'nop } ::_ $<<
+{ 0x40 runvmx } : runvmcode
+{ 0x48 runvmx } : gasrunvmcode
+{ 0xc8 runvmx } : gas2runvmcode
+{ 0x43 runvmx } : runvmdict
+{ 0x4b runvmx } : gasrunvmdict
+{ 0xcb runvmx } : gas2runvmdict
+{ 0x45 runvmx } : runvm
+{ 0x4d runvmx } : gasrunvm
+{ 0xcd runvmx } : gas2runvm
+{ 0x55 runvmx } : runvmctx
+{ 0x5d runvmx } : gasrunvmctx
+{ 0xdd runvmx } : gas2runvmctx
+{ 0x75 runvmx } : runvmctxact
+{ 0x7d runvmx } : gasrunvmctxact
+{ 0xfd runvmx } : gas2runvmctxact
+{ 0x35 runvmx } : runvmctxactq
+{ 0x3d runvmx } : gasrunvmctxactq
diff --git a/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/FiftExt.fif b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/FiftExt.fif
new file mode 100644
index 0000000000..6ed677d7f2
--- /dev/null
+++ b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/FiftExt.fif
@@ -0,0 +1,118 @@
+{ ?dup { 1+ { execute } { 0 swap } cond } 
+       { (number) ?dup 0= abort"-?" 'nop } cond
+} : (interpret-prepare)
+{ { include-depth 0= (seekeof?) not } { 
+    (word-prefix-find) (interpret-prepare) (execute)
+  } while
+} : interpret
+{ ({)
+  { 0 (seekeof?) abort"no }" (word-prefix-find) (interpret-prepare) (compile) over atom? not } until
+  (}) swap execute
+} : begin-block
+{ swap 0 'nop } : end-block
+{ { 1 'nop } `{ begin-block } 
+{ { swap `{ eq? not abort"} without {" swap execute } end-block }
+:: } :: {
+
+// if{ ... }then{ ... }elseif{ ... }then{ ... }else{ ... }
+{ eq? not abort"unexpected" } : ?pairs
+{ dup `if eq? swap `ifnot eq? over or not abort"without if{" } : if-ifnot?
+// cond then ? -- exec
+{ { ' if } { ' ifnot } cond rot ({) 0 rot (compile) -rot 1 swap (compile) (})  
+} : (make-if)
+// cond then else -- exec
+{ rot ({) 0 rot (compile) -rot 2 ' cond (compile) (})
+} : (make-cond)
+{ `noelse `if begin-block } :: if{
+{ `noelse `ifnot begin-block } :: ifnot{
+{ 1 ' end-block does } : end-block-does
+{ { over `else eq? } {
+    nip rot if-ifnot? ' swap ifnot (make-cond)
+  } while
+  swap `noelse ?pairs 0 swap 
+} : finish-else-chain
+{ swap dup if-ifnot? drop `then {
+    swap `then ?pairs
+    swap if-ifnot? (make-if) finish-else-chain
+  } `{ begin-block
+} end-block-does :: }then{
+{ swap `{ ?pairs nip
+  swap `then eq? not abort"without }then{" `else
+} : ?else-ok
+{ ?else-ok { finish-else-chain } `{ begin-block } end-block-does :: }else{
+{ ?else-ok `if begin-block } end-block-does :: }elseif{
+{ ?else-ok `ifnot begin-block } end-block-does :: }elseifnot{
+
+// while{ ... }do{ ... }
+{ 2 ' while does } : (make-while)
+{ `while begin-block } :: while{
+{ swap `while eq? not abort"without while{" `while-do {
+    swap `while-do ?pairs (make-while) 0 swap
+  } `{ begin-block
+} end-block-does :: }do{
+
+// repeat{ ... }until{ ... }
+{ swap ({) 0 rot (compile) 0 rot (compile) (}) 1 ' until does } : (make-until) 
+{ `repeat begin-block } :: repeat{
+{ swap `repeat eq? not abort"without repeat{" `until {
+    swap `until ?pairs (make-until) 0 swap
+  } `{ begin-block
+} end-block-does :: }until{
+
+// def <name> { ... } instead of { ... } : <name>
+{ bl word swap bl word "{" $cmp abort"{ expected" `def {
+    swap `def ?pairs -rot 3 ' (create) 
+  } `{ begin-block
+} : (def)
+{ 0 (def) } :: def
+{ 1 (def) } :: def::
+
+// defrec <name> { ... } instead of recursive <name> { ... } swap !
+{ recursive bl word "{" $cmp abort"{ expected" `defrec {
+    swap `defrec ?pairs swap ! 0 'nop
+  } `{ begin-block
+} :: defrec
+
+def .sgn {
+  if{ ?dup 0= }then{
+    ."zero"
+  }elseif{ 0> }then{
+    ."positive" 
+  }else{
+    ."negative" 
+  } 
+  cr
+}
+// equivalent to: { ?dup 0= { ."zero" } { 0> { ."positive" } { ."negative" } cond } cond cr } : .sgn
+
+defrec fact {
+  if{ dup }then{
+    dup 1- fact *
+  }else{
+    drop 1
+  }
+}
+// equivalent to: recursive fact { dup { dup 1- fact * } { drop 1 } cond } swap !
+
+// [[ ... ]] computes arbitrary constants inside definitions
+//   { [[ 5 dup * ]] + } : add25
+// is equivalent to
+//   { 25 + } : add25
+{ "without [[" abort } box constant ']]
+{ ']] @ execute } : ]]
+{ { ']] @ 2 { ']] ! call/cc } does ']] !
+    interpret 'nop ']] ! "]] not found" abort
+  } call/cc
+  drop 1 'nop
+} :: [[
+
+{ { over @ swap 2 { call/cc } does swap !
+    interpret "literal to eof" abort
+  } call/cc
+  drop execute 1 'nop
+} : interpret-literal-to
+// use next line only if Lists.fif is loaded (or move it to Lists.fif if FiftExt.fif becomes part of Fift.fif)
+//   { ( ') interpret-literal-to } :: '(
+// then you can use list literals '( a b c ... ) inside definitions:
+//   { '( 1 2 3 ) } : test
+//   { '( ( `a { ."A" } ) ( `b { ."B" } ) ) assoc { cadr execute } { ."???" } cond } : test2
diff --git a/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/GetOpt.fif b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/GetOpt.fif
new file mode 100644
index 0000000000..442552b636
--- /dev/null
+++ b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/GetOpt.fif
@@ -0,0 +1,131 @@
+library GetOpt  // Simple command-line options parser
+"Lists.fif" include
+
+// May be used as follows:
+// begin-options
+//   "h" { ."Help Message" 0 halt } short-option
+//   "v" { parse-int =: verbosity } short-option-arg
+//   "i" "--interactive" { true =: interactive } short-long-option
+// parse-options
+
+// ( l -- l')  computes tail of list l if non-empty; else ()
+{ dup null? ' cdr ifnot } : safe-cdr
+// ( l c -- l')  deletes first c elements from list l
+{ ' safe-cdr swap times } : list-delete-first
+// ( l n c -- l' )  deletes c elements starting from n-th in list l
+recursive list-delete-range {
+  dup 0<= { 2drop } {
+  over 0<= { nip list-delete-first } {
+  swap 1- swap rot uncons 2swap list-delete-range cons
+  } cond } cond
+} swap !
+// ( n c -- )  deletes $n .. $(n+c-1) from the argument list $*
+{ swap 1- $* @ swap rot list-delete-range $* ! } : $*del.. 
+// ( s s' -- ? )  checks whether s' is a prefix of s
+{ tuck $len over $len over >= { $| drop $= } { 2drop drop false } cond
+} : $pfx?
+// ( s -- ? )  checks whether s is an option (a string beginning with '-')
+{ dup $len 1 > { "-" $pfx? } { drop false } cond } : is-opt?
+// ( s -- ? )  checks whether s is a digit option
+{ 2 $| drop 1 $| nip $>B 8 B>u@ dup 57 <= swap 48 >= and } : is-digit-opt?
+0 box constant disable-digit-opts
+// ( l -- s i or 0 )  finds first string in l beginning with '-'
+{ 0 { 1+ over null? { 2drop 0 true } {
+  swap uncons over is-opt?
+  { disable-digit-opts @ { over is-digit-opt? not } { true } cond } { false } cond
+  { drop swap true } { nip swap false } cond
+  } cond } until
+} : list-find-opt
+// ( -- s i or 0 )  finds first option in cmdline args
+{ $* @ list-find-opt } : first-opt
+' second : get-opt-flags
+' first : get-opt-exec
+// ( s t -- ? )  checks whether short/long option s matches description t
+{ third $= } : short-option-matches
+{ dup get-opt-flags 4 and 0= 3 + [] $=
+} : long-option-matches
+// ( t -- s -1 or 0 )  extracts help message from description
+{ dup get-opt-flags 4 and 0= 4 + over count over >
+  { [] true } { 2drop false } cond
+} : get-opt-help
+// ( s l -- t -1 or 0 )  finds short/long option s in list l
+{ swap 1 { swap short-option-matches } does assoc-gen
+} : lookup-short-option
+{ swap 1 { swap long-option-matches } does assoc-gen
+} : lookup-long-option
+// ( s -- s' null or s' s'' )  Splits long option --opt=arg at '='
+{ dup "=" $pos 1+ ?dup { tuck $| swap rot 1- $| drop swap } { null } cond
+} : split-longopt
+// ( l -- f or 0 )  Extracts global option flags from first entry of l
+{ dup null? { drop 0 } { car get-opt-flags -256 and } cond
+} : get-global-option-flags
+variable options-list
+// ( l -- i or 0 ) 
+// parses command line arguments according to option description list l
+// and returns index i of first incorrect option
+{ dup options-list ! get-global-option-flags
+  256 and disable-digit-opts !
+  { first-opt dup 0= { true } {
+    swap dup "--" $pfx? {  // i s
+      dup $len 2 = { drop dup 1 $*del.. 0 true } {
+      split-longopt swap options-list @
+      lookup-long-option not { drop true } { // i s' t f
+      dup get-opt-exec swap get-opt-flags 3 and // i s' e f'
+      2 pick null? { dup 1 = } { dup 0= negate } cond  // i s' e f' f''
+      dup 1 = { 2drop 2drop true } {
+      { drop nip over 1+ $() swap execute 2 $*del.. false } {
+      ' nip ifnot execute 1 $*del.. false
+    } cond } cond } cond } cond } { // i s
+      1 $| nip {
+        dup $len 0= { drop 1 $*del.. false true } {
+        1 $| swap options-list @  // i s' s l
+        lookup-short-option not { drop true true } { // i s' t
+        dup get-opt-exec swap get-opt-flags 3 and  // i s' e f'
+        ?dup 0= { execute false } {
+        2 pick $len { drop execute "" false } {
+        2 = { nip null swap execute "" false } {  // i e
+        nip over 1+ $() swap execute 2 $*del.. false true
+      } cond } cond } cond } cond } cond } until
+    } cond
+  } cond } until
+} : getopt
+// ( t -- )  Displays help message for one option
+{ dup get-opt-flags dup 4 and 2 pick third swap {
+    ."-" type ."/" over 3 [] type } {
+    dup $len { dup "--" $pfx? { ."-" } ifnot type } {
+    drop ."usage: " $0 type
+  } cond } cond
+  dup 3 and ?dup {
+    2 = { ."[=<optarg>]" } { ."=<optarg>" } cond
+  } if
+  8 and { 9 emit } ifnot
+  get-opt-help { type } { ."No help available" } cond cr
+} : show-opt-help
+// ( -- )  Displays options help message according to options-list
+{ options-list @ { dup null? not } {
+    uncons swap show-opt-help
+  } while drop
+} : show-options-help
+// ( l -- )  Parses options and throws an error on failure
+{ getopt ?dup {
+  $() "cannot parse command line options near `" swap $+ +"`"
+  show-options-help abort } if
+} : run-getopt
+anon constant opt-list-marker
+' opt-list-marker : begin-options
+{ opt-list-marker list-until-marker } : end-options
+{ end-options run-getopt } : parse-options
+// ( s e -- o )  Creates short/long option s with execution token e
+{ 0 rot triple } dup : short-option : long-option
+// ( s s' e -- o ) Creates a combined short option s and long option s' with execution token e
+{ 4 2swap 4 tuple } : short-long-option
+{ 1 rot triple } dup : short-option-arg : long-option-arg
+{ 2 rot triple } dup : short-option-?arg : long-option-?arg
+{ 5 2swap 4 tuple } : short-long-option-arg
+{ 6 2swap 4 tuple } : short-long-option-?arg
+// ( o s -- s' )  Adds help message to option
+' , : option-help
+// ( s f -- o )  Creates a generic help message
+{ swap 'nop rot "" 3 roll 4 tuple } : generic-help-setopt
+{ 0 generic-help-setopt } : generic-help
+256 constant disable-digit-options
diff --git a/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Lisp.fif b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Lisp.fif
new file mode 100644
index 0000000000..fb91408d33
--- /dev/null
+++ b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Lisp.fif
@@ -0,0 +1,436 @@
+library Lisp  // tiny Lisp (or rather Scheme) interpreter
+"Lists.fif" include
+variable lisp-dict
+{ hole dup 1 { @ execute } does create } : recursive
+{ atom>$ +" undefined" abort } : report-not-found
+// a l -- d -1 or a 0  Look up definition d of atom a in dictionary l
+{ { dup null? { drop false true }
+    { uncons -rot unpair -rot over eq?
+      { drop nip true true } { nip swap false } cond
+    } cond
+  } until
+} : lookup-in
+// a dict -- def
+{ lookup-in ' report-not-found ifnot } : lookup-or-fail
+{ lisp-dict @ lookup-or-fail } : lisp-dict-lookup
+// a d --  Defines a with definition d in dictionary lisp-dict
+{ pair lisp-dict @ cons lisp-dict ! } : lisp-dict-int-define
+{ box lisp-dict-int-define } : lisp-dict-define
+// a d -- Defines new a with defininition d
+{ over lisp-dict @ lookup-in { 2drop atom>$ +" already defined" abort }
+  { drop lisp-dict-int-define } cond
+} : lisp-dict-int-define-new
+{ box lisp-dict-int-define-new } : lisp-dict-define-new
+// a e -- Defines a with executable definition given by e
+{ single lisp-dict-define-new } : lisp-dict-define-exec
+// expr ctx def -- val
+{ dup first execute } : run-definition
+// expr ctx -- val
+recursive lisp-ctx-eval {
+  over tuple?
+  { over first over lisp-ctx-eval run-definition }
+  { over atom? { lookup-or-fail @ } { drop } cond }
+  cond
+} swap !
+// exp -- value
+{ lisp-dict @ lisp-ctx-eval } : lisp-eval
+// (exprs) ctx -- (vals)
+recursive lisp-ctx-eval-list
+{ over null? { drop } {
+  swap uncons -rot over lisp-ctx-eval -rot lisp-ctx-eval-list cons
+  } cond 
+} swap !
+// (exprs) ctx -- val
+{ null rot {
+  dup null? { drop nip true } {
+  nip uncons swap 2 pick lisp-ctx-eval swap false
+  } cond } until
+} : lisp-ctx-eval-list-last
+// l c -- (args)
+{ swap uncons nip swap lisp-ctx-eval-list } : extract-eval-arg-list
+{ drop uncons nip } : extract-arg-list
+// (x1 .. xn) e n -- x1 .. xn e
+{ { swap uncons rot } swap times
+  swap null? not abort"invalid number of arguments"
+} : unpack-list
+// l c n e -- v
+{ swap 2swap extract-eval-arg-list  // e n (args)
+  -rot unpack-list execute
+} : eval-exec-fixed
+// l c n e -- v
+{ 2 pick pair
+  swap 2swap extract-arg-list  // [e c] n (args)
+  -rot unpack-list unpair swap execute
+} : exec-fixed
+// l c e -- v
+{ -rot extract-eval-arg-list  // e (args)
+  swap execute
+} : eval-exec-list
+{ -rot tuck extract-arg-list  // e c (args)
+  swap rot execute
+} : exec-list
+// e a n --
+{ rot 2 {  // expr ctx def n e
+    rot drop eval-exec-fixed } does
+  lisp-dict-define-exec
+} : lisp-fixed-primitive
+{ rot 2 { rot drop exec-fixed } does lisp-dict-define-exec
+} : lisp-fixed-lazy-primitive
+// e a --
+{ swap 1 { nip eval-exec-list } does lisp-dict-define-exec
+} : lisp-primitive
+{ swap 1 { nip exec-list } does lisp-dict-define-exec
+} : lisp-lazy-primitive
+
+// Uncomment next line for Fift booleans
+// false constant #f  true constant #t  null constant no-answer
+// Uncomment next line for Scheme booleans
+`#f constant #f  `#t constant #t  #f constant no-answer
+{ #f eq? } : lisp-false?
+{ lisp-false? 0= } : lisp-true?
+{ ' #t ' #f cond } : lisp-bool
+
+// temp for defining a lot of primitives
+{ bl word atom lisp-primitive } : L:
+{ bl word atom swap lisp-dict-define } : L=:
+{ bl word atom swap lisp-fixed-primitive } : #L:
+{ 0 #L: } : 0L:
+{ 1 #L: } : 1L:
+{ 2 #L: } : 2L:
+
+// basic primitives
+{ sum-list } L: +
+{ - } 2L: -
+{ dup null? { drop 1 } { ' * foldl-ne } cond } L: *
+{ / } 2L: /
+{ mod } 2L: modulo
+{ abs } 1L: abs
+{ ' min foldl-ne } L: min
+{ ' max foldl-ne } L: max
+{ true ' and foldl } L: integer-and
+{ false ' or foldl } L: integer-or
+{ 0 ' xor foldl } L: integer-xor
+{ not } 1L: integer-not
+{ = lisp-bool } 2L: =
+{ <> lisp-bool } 2L: <>
+{ < lisp-bool } 2L: <
+{ <= lisp-bool } 2L: <=
+{ > lisp-bool } 2L: >
+{ >= lisp-bool } 2L: >=
+{ eq? lisp-bool } 2L: eq?
+{ eqv? lisp-bool } 2L: eqv?
+{ equal? lisp-bool } 2L: equal?
+{ cons } 2L: cons
+{ car } 1L: car
+{ cdr } 1L: cdr
+{ cadr } 1L: cadr
+{ cddr } 1L: cddr
+{ caddr } 1L: caddr
+{ cdr cddr } 1L: cdddr
+{ concat-list-lists } L: append
+{ list-reverse } 1L: reverse
+{ list-tail } 2L: list-tail
+{ list-ref } 2L: list-ref
+{ list-member-eq } 2L: memq
+{ list-member-eqv } 2L: memv
+{ list-member-equal } 2L: member
+{ assq ' #f ifnot } 2L: assq
+{ assv ' #f ifnot } 2L: assv
+{ assoc ' #f ifnot } 2L: assoc
+{ list? lisp-bool } 1L: list?
+{ pair? lisp-bool } 1L: pair?
+{ tuple? lisp-bool } 1L: tuple?
+{ string? lisp-bool } 1L: string?
+{ integer? lisp-bool } 1L: integer?
+{ integer? lisp-bool } 1L: number?
+{ count } 1L: width
+{ list-length } 1L: length
+{ [] } 2L: tuple-ref
+{ first } 1L: first
+{ second } 1L: second
+{ third } 1L: third
+{ 3 [] } 1L: fourth
+{ list>tuple } 1L: list->tuple
+{ explode list } 1L: tuple->list
+null L=: null
+{ atom? lisp-bool } 1L: symbol?
+{ atom } 1L: string->symbol
+{ atom>$ } 1L: symbol->string
+{ dup #f eq? swap #t eq? or lisp-bool } 1L: boolean?
+#t L=: else
+#f L=: #f
+#t L=: #t
+{ null? lisp-bool } 1L: null?
+{ 0= lisp-bool } 1L: zero?
+{ 0> lisp-bool } 1L: positive?
+{ 0< lisp-bool } 1L: negative?
+{ 1 and 0= lisp-bool } 1L: even?
+{ 1 and 0<> lisp-bool } 1L: odd?
+{ bye } 0L: exit
+{ .l null } 1L: write
+{ lisp-eval } 1L: eval
+{ drop } `quote 1 lisp-fixed-lazy-primitive
+'nop L: list
+{ list>tuple } L: tuple
+{ list-last } L: begin
+{ $len } 1L: string-length
+{ concat-string-list } L: string-append
+{ $= lisp-bool } 2L: string=?
+{ $cmp 0< lisp-bool } 2L: string<?
+{ $cmp 0<= lisp-bool } 2L: string<=?
+{ $cmp 0> lisp-bool } 2L: string>?
+{ $cmp 0>= lisp-bool } 2L: string>=?
+{ (number) dup 1 = { drop } { ' 2drop if no-answer } cond
+} 1L: string->number
+{ (.) } 1L: number->string
+{ box? lisp-bool } 1L: box?
+{ box } 1L: box
+{ hole } 0L: new-box
+{ @ } 1L: unbox
+{ tuck swap ! } 2L: set-box!
+{ abort } 1L: error
+{ dup find { nip execute } { +" -?" abort } cond } : find-execute
+{ explode-list 1- roll find-execute } L: fift-exec
+{ explode-list dup 1- swap roll find-execute } L: fift-exec-cnt
+{ uncons swap find-execute } L: fift-exec-list
+// end of basic primitives
+forget L:  forget #L:  forget L=:
+forget 0L:  forget 1L:  forget 2L:
+
+{ { dup tuple? ' do-quote if } list-map } : map-quote
+{ uncons ' cons foldr-ne map-quote
+  null swap cons lisp-dict @ rot run-definition
+} `apply lisp-primitive  // bad: should have preserved original context
+// e1 e2 e3 ctx
+{ 3 exch 3 pick lisp-ctx-eval lisp-true? ' swap if nip swap lisp-ctx-eval }
+`if 3 lisp-fixed-lazy-primitive
+// (e) ctx
+{ #t -rot 
+  { over null? { 2drop true } {
+    swap uncons swap 2 pick lisp-ctx-eval dup lisp-true? // v' c t v ?
+    { swap 2swap nip false } { -rot 2drop nip true } cond
+  } cond } until
+} `and lisp-lazy-primitive
+{ #f -rot
+  { over null? { 2drop true } {
+    swap uncons swap 2 pick lisp-ctx-eval dup lisp-false? // v' c t v ?
+    { swap 2swap nip false } { -rot 2drop nip true } cond
+  } cond } until
+} `or lisp-lazy-primitive
+{ lisp-false? lisp-bool } `not 1 lisp-fixed-primitive
+// cond-clause ctx -- v -1 or 0
+{ swap uncons -rot dup `else eq? {
+  drop lisp-ctx-eval-list-last true } {
+  over lisp-ctx-eval lisp-true? {
+  lisp-ctx-eval-list-last true } {
+  2drop false
+  } cond } cond
+} : eval-cond-clause
+// (clauses) ctx -- v
+{ { over null? { no-answer true } {
+    swap uncons -rot over eval-cond-clause } cond 
+  } until -rot 2drop
+} `cond lisp-lazy-primitive
+{ lisp-dict @ lookup-in { hole tuck lisp-dict-int-define } ifnot
+} : lisp-create-global-var
+// a e ctx  -- old (simple) define
+{ drop over atom? not abort"only a variable can be define'd"
+  over lisp-create-global-var swap lisp-eval swap ! 
+} drop // `define 2 lisp-fixed-lazy-primitive
+{ tuck lisp-ctx-eval rot dup atom? not abort"only a variable can be set"
+  rot lookup-or-fail dup @ -rot !
+} `set! 2 lisp-fixed-lazy-primitive
+// define lambda
+{ { dup null? { drop true true }
+    { uncons swap atom? { false } { drop false true } cond } cond
+  } until
+} : var-list?
+{ { dup null? over atom? or { drop true true }
+    { uncons swap atom? { false } { drop false true } cond } cond
+  } until
+} : lambda-var-list?
+// (quote x) -- x -1 ; else 0
+{ dup pair? { uncons swap `quote eq? { car true } { drop false } cond }
+  { drop false } cond
+} : is-quote?
+recursive match-arg-list-acc
+// l (vars) (args) -- ((var . arg) ...)+l -1 or ? 0
+{ over atom? { over `_ eq? { 2drop } { pair swap cons } cond true } { 
+  over null? { nip null? } { // (vars) (args)
+  over tuple? not { 2drop false } {
+  over is-quote? { eq? nip } { // (v) (a)
+  dup tuple? not { 2drop false } {
+  over count over count over <> { drop 2drop false } { // l [v] [a] n
+  3 roll 0 rot { // [v] [a] l i
+    dup 0< {
+      3 pick over [] swap  // [v] [a] l vi i
+      3 pick over [] 2swap rot // [v] [a] i l vi ai
+      match-arg-list-acc { // [v] [a] i l'
+        swap 1+ } { nip -1 } cond
+    } ifnot
+  } swap times
+  2swap 2drop 0>=
+  } cond } cond } cond } cond } cond } cond
+} swap !
+{ null -rot match-arg-list-acc } : match-arg-list
+// ((var . arg)...) ctx -- ctx'
+{ { over null? not }
+  { swap uncons swap unpair box pair rot cons } while
+  nip
+} : extend-ctx-by-list
+// ((vars) body) ctx
+{ swap uncons -rot 
+  dup lambda-var-list? not abort"invalid formal parameter list"
+  { // l-expr ctx' [_ body ctx (vars)]
+    -rot 2 pick 3 [] swap rot  // [_ body ...] (vars) ctx' l-expr
+    uncons nip swap lisp-ctx-eval-list  // [_ body ...] (vars) (arg-vals)
+    match-arg-list not abort"invalid arguments to lambda" // [_ body ...] ((var arg)...)
+    over third extend-ctx-by-list // [_ body ctx (vars)] ctx''
+    swap second swap lisp-ctx-eval-list-last
+  } 3 -roll 4 tuple
+} : make-lambda
+{ make-lambda } `lambda lisp-lazy-primitive
+// (a e) ctx -- more sophisticated (define a e)
+{ drop uncons swap dup atom? { // (e) a 
+    tuck lisp-create-global-var
+    swap lisp-dict @ lisp-ctx-eval-list-last swap !
+  } { // (e) (a v..)
+    uncons over atom? not abort"only variables can be define'd"  // (e) a (v..)
+    rot cons over lisp-create-global-var   // a ((v..) (e)) h
+    swap lisp-dict @ make-lambda swap !
+  } cond
+} `define lisp-lazy-primitive
+// ((x e) ..) ctx -- ((x.v) ..)
+recursive eval-assign-list
+{ over null? { drop } {
+  swap uncons swap uncons // ctx t x (e)
+  over atom? not abort"invalid variable name in assignment list"
+  3 pick lisp-ctx-eval-list-last  // ctx t x v
+  pair swap rot eval-assign-list cons
+  } cond
+} swap !
+// (((x v) ..) body) ctx  -- let construct
+{ swap uncons swap 2 pick eval-assign-list  // ctx body ((x v)...)
+  rot extend-ctx-by-list lisp-ctx-eval-list-last
+} `let lisp-lazy-primitive
+// ((x e) ..) ctx -- ctx'
+{ swap {
+  dup null? { drop true } {
+  uncons swap uncons  // ctx t x (e)
+  over atom? not abort"invalid variable name in assignment list"
+  3 pick lisp-ctx-eval-list-last  // ctx t x v
+  box pair rot cons swap false
+  } cond } until
+} : compute-let*-ctx
+// (((x v) ..) body) ctx  -- let* construct
+{ swap uncons swap rot compute-let*-ctx lisp-ctx-eval-list-last
+} `let* lisp-lazy-primitive
+// ((x e) ..) ctx -- ((h e) ..) ctx' , with x bound to h in ctx'
+recursive prepare-letrec-ctx {
+  over null? {
+  swap uncons swap uncons swap // ctx t (e) x
+  hole tuck pair swap rot cons // ctx t (x.h) (h e)
+  3 -roll rot cons prepare-letrec-ctx // (h e) t ctx'
+  -rot cons swap
+  } ifnot
+} swap !
+// (((x v) ..) body) ctx  -- letrec construct
+{ swap uncons swap rot prepare-letrec-ctx swap { // body ctx' ((h e)..)
+    dup null? { drop true } {
+    uncons -rot uncons 2 pick lisp-ctx-eval-list-last // body t ctx' h v
+    swap ! swap false
+  } cond } until
+  lisp-ctx-eval-list-last
+} `letrec lisp-lazy-primitive
+// (e (p e)...) ctx -- match construct
+{ swap uncons swap 2 pick lisp-ctx-eval swap { // ctx v ((p e)..)
+  dup null? { drop 2drop no-answer true } {
+  uncons swap uncons swap 3 pick // ctx v t e p v
+  match-arg-list { // ctx v t e ((x' . v')...)
+  2swap 2drop rot extend-ctx-by-list lisp-ctx-eval-list-last true } {
+  2drop false
+  } cond } cond } until
+} `match lisp-lazy-primitive
+//
+lisp-dict @ constant original-lisp-dict
+{ original-lisp-dict lisp-dict ! } : reset-lisp
+{ ' drop { lisp-eval .l cr } List-generic( } :_ LISP-EVAL-PRINT(
+// LISP-EVAL-PRINT((+ 3 4) (* 5 6)) computes and prints 12 and 30
+{ hole dup 1 { @ nip } does swap 
+  1 { swap lisp-eval swap ! } does 
+  List-generic(
+} :_ LISP-EVAL(
+// LISP-EVAL((+ 3 4) (* 5 6)) computes 12 and 30, returns only 30
+// /*
+LISP-EVAL-PRINT(
+  (define succ (lambda (x) (+ x 1)))
+  (define (twice f) (lambda (x) (f (f x))))
+  (define (fact n) (if (= n 0) 1 (* n (fact (- n 1)))))
+  (fact ((twice succ) 5))
+  (define compare (lambda (x y) (cond ((< x y) 'less) ((= x y) 'equal) (else 'greater))))
+  (compare 2 3)
+  (compare 7 (+ 2 3))
+  (define next (let ((cnt 0)) (lambda () (set! cnt (+ cnt 1)) cnt)))
+  (list (next) (next))
+  (define new-counter (lambda () (let ((x 0)) (lambda () (set! x (+ x 1)) x))))
+  (define c1 (new-counter))
+  (define c2 (new-counter))
+  (list (c1) (c1) (c2) (c1) (c2) (c1) (c1) (c2) (c2))
+  (let* ((x (+ 2 3)) (y (* x x)) (z (+ x y))) (list x y z))
+  (letrec ((even? (lambda (n) (if (= n 0) #t (odd? (- n 1)))))
+           (odd?  (lambda (n) (if (= n 0) #f (even? (- n 1))))))
+          (even? 88))
+  (define (len l) (if (null? l) 0 (+ 1 (len (cdr l)))))
+  (len '(2 3 9))
+  (define (len2 l) (match l (() 0) ((x . t) (+ 1 (len2 t)))))
+  (len2 '(2 3 9))
+  (define (foo x) (match x
+     (('zero) 0)
+     (('succ x) (+ (foo x) 1))
+     (('plus x y) (+ (foo x) (foo y)))
+     (('minus x y) (- (foo x) (foo y)))
+     (x x)))
+  (foo '(plus (succ (zero)) (minus (succ (succ 5)) 3)))
+  (define (bar x) (match x
+     (['zero] 0)
+     (['succ x] (+ (bar x) 1))
+     (['plus x y] (+ (bar x) (bar y)))
+     (['minus x y] (- (bar x) (bar y)))
+     (['const x] x)))
+  (bar '[plus [succ [zero]] [minus [succ [succ [const 5]]] [const 3]]])
+  (define (map f l) (letrec
+    ((map-f (lambda (l) (match l
+      (() ())
+      ((h . t) (cons (f h) (map-f t)))))))
+    (map-f l)))
+  (map (lambda (x) (* x (+ 2 x))) '(2 3 9))
+  (define (make-promise proc) (let ((result-ready? #f) (result #f))
+    (lambda ()
+      (if result-ready? result
+        (let ((x (proc)))
+	  (if result-ready? result
+	    (begin (set! result x) (set! result-ready? #t) result)))))))
+  (define (force promise) (promise))
+)
+// */
+// words for invoking Lisp definitions from Fift
+// (args) def -- val
+{ null rot map-quote cons lisp-dict @ rot run-definition
+} : invoke-lisp-definition
+{ atom lisp-dict-lookup 1 { @ invoke-lisp-definition }
+} : (invoke-lisp)
+{ bl word (invoke-lisp) } :: invoke-lisp
+// ( 2 3 ) invoke-lisp compare .l
+{ atom lisp-dict-lookup 2 { @ mklist-1 invoke-lisp-definition }
+} : (invoke-lisp-fixed)
+{ bl word (invoke-lisp-fixed) } :: invoke-lisp-fixed
+// 9 8  2 invoke-lisp-fixed compare .l
+{ bl word (invoke-lisp) does } : make-lisp-invoker
+{ bl word (invoke-lisp-fixed) does } : make-lisp-fixed-invoker
+// 2 make-lisp-fixed-invoker compare : compare
+// 3 9 compare
+// import Lisp definitions as Fift words
+{ bl word dup (invoke-lisp) does swap 0 (create) } : import-lisp
+{ bl word tuck (invoke-lisp-fixed) does swap 0 (create) } : import-lisp-fixed
+// 1 import-lisp-fixed fact
+// 7 fact .
diff --git a/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Lists.fif b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Lists.fif
new file mode 100644
index 0000000000..b59e40a0d9
--- /dev/null
+++ b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Lists.fif
@@ -0,0 +1,220 @@
+library Lists  // List utilities
+//
+{ hole dup 1 { @ execute } does create } : recursive
+// x x' -- ?  recursively compares two S-expressions
+recursive equal? {
+  dup tuple? {
+    over tuple? {
+      over count over count over = { // t t' l ?
+        0 { dup 0>= { 2dup [] 3 pick 2 pick [] equal? { 1+ } { drop -1 } cond
+            } if } rot times
+        nip nip 0>=
+      } { drop 2drop false } cond
+    } { 2drop false } cond
+  } { eqv? } cond
+} swap !
+// (a1 .. an) -- (an .. a1)
+{ null swap { dup null? not } { uncons swap rot cons swap } while drop } : list-reverse
+// (a1 .. an) -- an   Computes last element of non-empty list l
+{ { uncons dup null? { drop true } { nip false } cond } until } : list-last
+// l l' -- l++l'  Concatenates two lists
+recursive list+ {
+  over null? { nip } { swap uncons rot list+ cons } cond
+} swap !
+// l l' -- l'' -1 or 0, where l = l' ++ l''
+// Removes prefix from list
+{ { dup null? { drop true true } {
+  swap dup null? { 2drop false true } { // l' l
+  uncons swap rot uncons -rot equal? { false } {
+  2drop false true
+  } cond } cond } cond } until
+} : list-
+// (a1 .. an) -- a1 .. an n   Explodes a list
+{ 0 { over null? not } { swap uncons rot 1+ } while nip } : explode-list
+// (a1 .. an) x -- a1 .. an n x   Explodes a list under the topmost element
+{ swap explode-list dup 1+ roll } : explode-list-1
+// l -- t   Transforms a list into a tuple with the same elements
+{ explode-list tuple } : list>tuple
+// a1 ... an n x -- (a1 .. an) x
+{ null swap rot { -rot cons swap } swap times } : mklist-1
+// (s1 ... sn) -- s1+...+sn   Concatenates a list of strings
+{ "" { over null? not } { swap uncons -rot $+ } while nip
+} : concat-string-list
+// (x1 ... xn) -- x1+...+xn   Sums a list of integers
+{ 0 { over null? not } { swap uncons -rot + } while nip
+} : sum-list
+// (a1 ... an) a e -- e(...e(e(a,a1),a2),...),an)
+{ -rot { over null? not } { swap uncons -rot 3 pick execute } while nip nip
+} : foldl
+// (a1 ... an) e -- e(...e(e(a1,a2),a3),...),an)
+{ swap uncons swap rot foldl } : foldl-ne
+// (a1 ... an) a e -- e(a1,e(a2,...,e(an,a)...))
+recursive foldr {
+  rot dup null? { 2drop } {
+    uncons -rot 2swap swap 3 pick foldr rot execute
+  } cond
+} swap !
+// (a1 ... an) e -- e(a1,e(a2,...,e(a[n-1],an)...))
+recursive foldr-ne {
+  over cdr null? { drop car } {
+    swap uncons 2 pick foldr-ne rot execute
+  } cond
+} swap !
+// (l1 ... ln) -- l1++...++ln   Concatenates a list of lists
+{ dup null? { ' list+ foldr-ne } ifnot } : concat-list-lists
+// (a1 .. an . t) n -- t   Computes the n-th tail of a list
+{ ' cdr swap times } : list-tail
+// (a0 .. an ..) n -- an   Computes the n-th element of a list
+{ list-tail car } : list-ref
+// l -- ?
+{ { dup null? { drop true true } {
+    dup pair? { cdr false } {
+    drop false true
+  } cond } cond } until
+} : list?
+// l -- n
+{ 0 { over null? not } { 1+ swap uncons nip swap } while nip
+} : list-length
+// l e -- t  // returns tail of l after first member that satisfies e
+{ swap {
+  dup null? { nip true } {
+  tuck car over execute { drop true } {
+  swap cdr false
+  } cond } cond } until
+} : list-tail-from
+// a l -- t  // tail of l after first occurence of a using eq?
+{ swap 1 ' eq? does list-tail-from } : list-member-eq
+{ swap 1 ' eqv? does list-tail-from } : list-member-eqv
+{ swap 1 ' equal? does list-tail-from } : list-member-equal
+// a l -- ?
+{ list-member-eq null? not } : list-member?
+{ list-member-eqv null? not } : list-member-eqv?
+// l -- a -1 or 0 // returns car l if l is non-empty
+{ dup null? { drop false } { car true } cond
+} : safe-car
+{ dup null? { drop false } { car second true } cond
+} : get-first-value
+// l e -- v -1 or 0
+{ list-tail-from safe-car } : assoc-gen
+{ list-tail-from get-first-value } : assoc-gen-x
+// a l -- (a.v) -1 or 0 -- returns first entry (a . v) in l
+{ swap 1 { swap first eq? } does assoc-gen } : assq
+{ swap 1 { swap first eqv? } does assoc-gen } : assv
+{ swap 1 { swap first equal? } does assoc-gen } : assoc
+// a l -- v -1 or 0 -- returns v from first entry (a . v) in l
+{ swap 1 { swap first eq? } does assoc-gen-x } : assq-val
+{ swap 1 { swap first eqv? } does assoc-gen-x } : assv-val
+{ swap 1 { swap first equal? } does assoc-gen-x } : assoc-val
+// (a1 .. an) e -- (e(a1) .. e(an))
+recursive list-map {
+  over null? { drop } {
+  swap uncons -rot over execute -rot list-map cons
+  } cond
+} swap !
+
+variable ctxdump  variable curctx
+// (a1 .. an) e -- executes e for a1, ..., an
+{ ctxdump @ curctx @ ctxdump 2! curctx 2!
+  { curctx 2@ over null? not } { swap uncons rot tuck curctx 2! execute }
+  while 2drop ctxdump 2@ curctx ! ctxdump !
+} : list-foreach
+forget ctxdump  forget curctx
+
+//
+// Experimental implementation of `for` loops with index
+//
+variable loopdump  variable curloop
+{ curloop @ loopdump @ loopdump 2! } : push-loop-ctx
+{ loopdump 2@ loopdump ! curloop ! } : pop-loop-ctx
+// ilast i0 e -- executes e for i=i0,i0+1,...,ilast-1
+{ -rot 2dup > {
+    push-loop-ctx {
+      triple dup curloop ! first execute curloop @ untriple 1+ 2dup <=
+    } until pop-loop-ctx
+  } if 2drop drop
+} : for
+// ilast i0 e -- same as 'for', but pushes current index i before executing e
+{ -rot 2dup > {
+    push-loop-ctx {
+      triple dup curloop ! untriple nip swap execute curloop @ untriple 1+ 2dup <=
+    } until pop-loop-ctx
+  } if 2drop drop
+} : for-i
+// ( -- i )  Returns innermost loop index
+{ curloop @ third } : i
+// ( -- j )  Returns outer loop index
+{ loopdump @ car third } : j
+{ loopdump @ cadr third } : k
+forget curloop  forget loopdump
+
+//
+// create Lisp-style lists using words "(" and ")"
+//
+variable ')
+'nop box constant ',
+{ ") without (" abort } ') ! 
+{ ') @ execute } : )
+anon constant dot-marker
+// m x1 ... xn t m -- (x1 ... xn . t)
+{ swap
+  { -rot 2dup eq? not }
+  { over dot-marker eq? abort"invalid dotted list"
+    swap rot cons } while 2drop
+} : list-tail-until-marker
+// m x1 ... xn m -- (x1 ... xn)
+{ null swap list-tail-until-marker } : list-until-marker
+{ over dot-marker eq? { nip 2dup eq? abort"invalid dotted list" }
+  { null swap } cond
+  list-tail-until-marker
+} : list-until-marker-ext
+{ ') @ ', @ } : ops-get
+{ ', ! ') ! } : ops-set
+{ anon dup ops-get 3 { ops-set list-until-marker-ext } does ') ! 'nop ', !
+} : (
+// test of Lisp-style lists
+// ( 42 ( `+ 9 ( `* 3 4 ) ) "test" ) .l cr
+// ( `eq? ( `* 3 4 ) 3 4 * ) .l cr
+// `alpha ( `beta `gamma `delta ) cons .l cr
+// { ( `eq? ( `* 3 5 pick ) 3 4 roll * ) } : 3*sample
+// 17 3*sample .l cr
+
+// similar syntax _( x1 .. xn ) for tuples
+{ 2 { 1+ 2dup pick eq? } until 3 - nip } : count-to-marker
+{ count-to-marker tuple nip } : tuple-until-marker
+{ anon dup ops-get 3 { ops-set tuple-until-marker } does ') ! 'nop ', ! } : _(
+// test of tuples
+// _( _( 2 "two" ) _( 3 "three" ) _( 4 "four" ) ) .dump cr
+
+// pseudo-Lisp tokenizer
+"()[]'" 34 hold constant lisp-delims
+{ lisp-delims 11 (word) } : lisp-token
+{ null cons `quote swap cons } : do-quote
+{ 1 { ', @ 2 { 2 { ', ! execute ', @ execute } does ', ! }
+      does ', ! } does
+} : postpone-prefix
+{ ', @ 1 { ', ! } does ', ! } : postpone-',
+( `( ' ( pair
+  `) ' ) pair
+  `[ ' _( pair
+  `] ' ) pair
+  `' ' do-quote postpone-prefix pair
+  `. ' dot-marker postpone-prefix pair
+  `" { char " word } pair
+  `;; { 0 word drop postpone-', } pair
+) constant lisp-token-dict
+variable eol
+{ eol @ eol 0! anon dup ') @ 'nop 3
+  { ops-set list-until-marker-ext true eol ! } does ') ! rot ', !
+  { lisp-token dup (number) dup { roll drop } {
+      drop atom dup lisp-token-dict assq { nip second execute } if
+    } cond
+    ', @ execute
+    eol @
+  } until
+  -rot eol ! execute
+} :_ List-generic(
+{ 'nop 'nop List-generic( } :_ LIST(
+// LIST((lambda (x) (+ x 1)) (* 3 4))
+// LIST('(+ 3 4))
+// LIST(2 3 "test" . 9)
+// LIST((process '[plus 3 4]))
diff --git a/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Stack.fif b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Stack.fif
new file mode 100644
index 0000000000..c7ef6f8e58
--- /dev/null
+++ b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/Stack.fif
@@ -0,0 +1,266 @@
+library Stack  // advanced stack manupulation library
+"Lists.fif" include
+// S(a b c - a c 2 a b) would compile to code performing the requested stack manipulation
+
+// interface to low-level stack manipulation primitives
+{ (number) 1- abort"index expected" dup 0 < over 255 > or
+  abort"index 0..255 expected"
+} : (idx)
+// push(n) : a0 .. an - a0 .. an a0  equivalent to "n pick"
+// push(0) = dup, push(1) = over
+{ 0 char ) word (idx) <push> } ::_ push(
+// pop(n) : a0 a1 .. a(n-1) an - an a1 .. a(n-1)
+// pop(0) = drop, pop(1) = nip
+{ 0 char ) word (idx) <pop> } ::_ pop(
+// xchg(i,j) : equivalent to "i j exch2"
+{ 0 char , word (idx) char ) word (idx) <xchg> } ::_ xchg(
+// xchg0(i) : equivalent to "i exch" or "xchg(0,i)"
+// xchg0(1) = swap
+{ 0 char ) word (idx) 0 <xchg> } ::_ xchg0(
+forget (idx)
+
+// parser for stack notation expressions
+")" 34 hold +"  -" constant stk-delims
+anon constant stk-start
+anon constant stk-to
+variable stk-mode
+{ stk-delims 11 (word) } : stk-token
+'nop : mk-lit
+// stk-start vn ... v0 -- stk-start ... v0 i  where v[i]=v0
+{ 0 {
+  1+ 2dup 2+ pick dup stk-start eq? { 2drop drop 0 true } { eqv? } cond
+  } until
+} : stk-lookup
+// stk-start a1 .. an stk-to b1 .. bm -- [a1 .. an] [b1 .. bm]
+{ stk-mode @ 0= abort"identifier expected" } : chk-lit
+{ stk-to list-until-marker stk-mode !
+  stk-start list-until-marker stk-mode @
+  stk-mode 0!
+} : build-stk-effect
+{ stk-start stk-mode 0! {
+  stk-token dup ")" $= { drop true } {
+  dup "-" $= {
+    drop stk-mode @ abort"duplicate -" true stk-mode ! stk-to false } {
+  dup 34 chr $= { chk-lit drop char " word mk-lit false } {
+  dup (number) ?dup { chk-lit 1- { swap mk-lit -rot } if mk-lit nip false } {
+  atom dup `_ eq? { stk-mode @ abort"identifier expected" false } {
+  stk-lookup 0= stk-mode @ = {
+    stk-mode @ { atom>$ +" -?" } { atom>$ +" redefined" } cond abort } {
+  false
+  } cond } cond } cond } cond } cond } cond } until
+  stk-mode @ 0= abort"'-' expected"
+  build-stk-effect
+} :_ parse-stk-list(
+
+// stack operation list construction
+variable op-rlist
+{ op-rlist null! } : clear-op-list
+{ op-rlist @ list-reverse } : get-op-list
+{ op-rlist @ cons op-rlist ! } : issue-op
+{ minmax `xchg -rot triple } : op-xchg
+{ `push swap pair } : op-push
+{ `lit swap pair } : op-lit
+{ `pop swap pair } : op-pop
+0 op-pop constant op-drop
+{ 2dup <> { op-xchg issue-op } if } : issue-xchg
+{ op-push issue-op } : issue-push
+{ op-lit issue-op } : issue-lit
+{ op-pop issue-op } : issue-pop
+{ op-drop issue-op } : issue-drop
+{ ' issue-drop swap times } : issue-drop-#
+
+// emulated stack contents
+variable emul-stk
+{ emul-stk @ count } : emul-depth
+{ emul-depth 1- swap - } : adj-i
+{ emul-depth 1- tuck swap - swap rot - swap } : adj-ij
+// i j --
+{ adj-ij 2dup emul-stk @ tuck swap [] swap rot [] rot // i sj si j
+  emul-stk @ -rot []= swap rot []= emul-stk !
+} : emul-xchg
+{ emul-stk @ tpop drop emul-stk ! } : emul-drop
+// i --
+{ 0 emul-xchg emul-drop } : emul-pop
+// i -- s[i]
+{ emul-stk @ swap [] } : emul-stk[]
+// i -- si
+{ adj-i emul-stk[] } : emul-get
+{ 0 emul-get } : emul-tos
+// v i -- ?   Check whether s[i]=v
+{ dup emul-depth < { emul-stk[] eqv? } { 2drop false } cond } : emul[]-eq?
+// v -- i or -1   Returns maximum i with s[i]=v
+{ emul-stk @ dup count { // v s i
+  ?dup 0= { -1 true } { 1- 2dup [] 3 pick eqv? } cond  // v s i' ?
+  } until nip nip
+} : emul-stk-lookup-rev
+// i --
+{ emul-get emul-stk @ swap , emul-stk ! } : emul-push
+{ emul-stk @ swap , emul-stk ! } : emul-lit
+// show emulated stack contents similarly to .s
+{ emul-stk @ explode dup 1 reverse ' .l swap times cr } : .e
+
+// both issue an operation and emulate it
+{ 2dup issue-xchg emul-xchg } : issue-emul-xchg
+{ dup issue-push emul-push } : issue-emul-push
+{ dup issue-lit emul-lit } : issue-emul-lit
+{ dup issue-pop emul-pop } : issue-emul-pop
+{ issue-drop emul-drop } : issue-emul-drop
+{ ' issue-emul-drop swap times } : issue-emul-drop-#
+
+// b.. s -- b.. s   moves tos value to stk[s]
+{ dup emul-stk[] 2 pick cdr list-member-eqv? {
+  dup adj-i 0 issue-emul-xchg } { dup adj-i issue-emul-pop } cond
+} : move-tos-to
+
+// new s -- ops registered
+{ { over null? not } {
+  // .sl .e get-op-list .l cr 
+  // get-op-list list-length 100 > abort"too long"
+  emul-depth over >
+    { over emul-tos swap list-member-eqv? not } { false } cond {
+  // b.. s  tos unneeded
+  issue-emul-drop } {
+  over car // b.. s b1
+  2dup swap emul[]-eq? { drop swap cdr swap 1+ } {
+  dup emul-stk-lookup-rev // b.. s b1 i
+  dup 0< { // b.. s b1 i  not found, must be a literal
+    drop dup atom? abort"unavailable value"
+    issue-emul-lit } {
+  dup 3 pick < { // b.. s b1 i  found in bottom s stack values
+    nip adj-i issue-emul-push  // b.. s
+    dup emul-depth 1- < { move-tos-to } if
+  } {
+  emul-depth 1- over = { // b.. s b1 i  found in tos
+    2drop move-tos-to
+  } { // b.. s b1 i
+    nip over adj-ij issue-emul-xchg
+  } cond } cond } cond } cond } cond } while
+  nip emul-depth swap - issue-emul-drop-#
+} : generate-reorder-ops
+
+// old new -- op-list
+{ emul-stk @ op-rlist @ 2swap 
+  swap list>tuple emul-stk ! clear-op-list
+  0 generate-reorder-ops get-op-list
+  -rot op-rlist ! emul-stk !
+} : generate-reorder
+{ parse-stk-list( generate-reorder } :_ SG(
+
+// op-list rewriting according to a ruleset 
+// l f l1 l2 -- l' -1 or l f  with l' = l2 + (l - l1)
+{ push(3) rot list- { list+ nip nip true } { drop } cond
+} : try-rule
+// l f ll -- l' -1 or l f
+{ { dup null? not } { uncons 3 -roll unpair try-rule rot } while drop
+} : try-ruleset
+// l ll -- l'
+{ swap { over false swap try-ruleset 0= } until nip
+} : try-ruleset*
+// l ruleset -- l'
+recursive try-ruleset*-everywhere {
+  tuck try-ruleset* dup null? { nip } {
+    uncons rot try-ruleset*-everywhere cons } cond
+} swap !
+LIST(
+ [([xchg 0 1] [xchg 0 2]) ([rot])]
+ [([xchg 0 1] [xchg 1 2]) ([-rot])]
+ [([xchg 0 2] [xchg 1 2]) ([rot])]
+ [([xchg 0 2] [xchg 0 1]) ([-rot])]
+ [([xchg 1 2] [xchg 0 1]) ([rot])]
+ [([xchg 1 2] [xchg 0 2]) ([-rot])]
+ [([xchg 0 1] [rot]) ([xchg 0 2])]
+ [([-rot] [xchg 0 1]) ([xchg 0 2])]
+ [([xchg 0 2] [xchg 1 3]) ([2swap])]
+ [([xchg 1 3] [xchg 0 2]) ([2swap])]
+ [([push 1] [push 1]) ([2dup])]
+ [([push 3] [push 3]) ([2over])]
+ [([pop 0] [pop 0]) ([2drop])]
+ [([pop 1] [pop 0]) ([2drop])]
+ [([xchg 0 1] [push 1]) ([tuck])]
+ [([rot] [-rot]) ()]
+ [([-rot] [rot]) ()]
+) constant fift-stack-ruleset
+{ fift-stack-ruleset try-ruleset*-everywhere } : fift-ops-rewrite
+{ SG( fift-ops-rewrite } :_ SGF(
+
+// helpers for creating Fift source strings for one fift-op
+// i j -- s
+{ minmax over { "xchg(" rot (.) $+ +"," swap (.) $+ +")" }
+  { nip dup 1 = { drop "swap" } { 
+    ?dup { "xchg0(" swap (.) $+ +")" } { "" } cond
+  } cond } cond
+} : source-<xchg>
+// i -- s
+{ dup 1 = { drop "over" } {
+  ?dup { "push(" swap (.) $+ +")" } { "dup" } cond
+  } cond
+} : source-<push>
+// i -- s
+{ dup 1 = { drop "nip" } {
+  ?dup { "pop(" swap (.) $+ +")" } { "drop" } cond
+  } cond
+} : source-<pop>
+// lit -- s
+{ dup string? { char " chr swap $+ char " hold } { (.) } cond
+} : source-<lit> 
+
+// dictionary with all fift op compilation/source creation
+{ 0 swap (compile) } : fop-compile
+( _( `xchg 2 { <xchg> fop-compile } { source-<xchg> swap cons } )
+  _( `push 1 { <push> fop-compile } { source-<push> swap cons } )
+  _( `pop 1 { <pop> fop-compile } { source-<pop> swap cons } )
+  _( `lit 1 { 1 'nop (compile) } { source-<lit> swap cons } )
+  _( `rot 0 { ' rot fop-compile } { "rot" swap cons } )
+  _( `-rot 0 { ' -rot fop-compile } { "-rot" swap cons } )
+  _( `tuck 0 { ' tuck fop-compile } { "tuck" swap cons } )
+  _( `2swap 0 { ' 2swap fop-compile } { "2swap" swap cons } )
+  _( `2drop 0 { ' 2drop fop-compile } { "2drop" swap cons } )
+  _( `2dup 0 { ' 2dup fop-compile } { "2dup" swap cons } )
+  _( `2over 0 { ' 2over fop-compile } { "2over" swap cons } )
+) box constant fift-op-dict
+
+{ dup atom? { atom>$ } { drop "" } cond
+  "unknown operation " swap $+ abort
+} : report-unknown-op
+variable 'fop-entry-exec
+// process fift-op according to 'fop-entry-exec
+// ... op - ...
+{ dup first dup fift-op-dict @ assq { report-unknown-op } ifnot
+  dup second 1+ push(3) count <> abort"incorrect param count"
+  nip swap explode dup roll drop 1- roll // o2 .. on entry
+  'fop-entry-exec @ execute 
+} : process-fift-op
+
+// compile op-list into Fift wordlist
+// wl op-list -- wl'
+{ { third execute } 'fop-entry-exec !
+  swap ' process-fift-op foldl } : compile-fift-op*
+// op-list -- e
+{ fift-ops-rewrite ({) swap compile-fift-op* (}) } : ops>wdef
+
+// S(<orig-stack> - <new-stack>) compiles a "word" performing required action
+{ SG( ops>wdef 0 swap } ::_ S(
+// 1 2 3 S(a b c - c a b a) .s  would print 3 1 2 1
+
+// transform op-list into Fift source
+// ls op -- ls'
+{ fift-ops-rewrite 
+  { 3 [] execute } 'fop-entry-exec !
+  null ' process-fift-op foldl
+  dup null? { drop "" } { { +" " swap $+ } foldr-ne } cond
+} : ops>$
+{ SG( ops>$ 1 'nop } ::_ $S(
+{ SG( ops>$ type } :_ .$S(
+// $S(a b c - b c a c a c) => string "rot 2dup over"
+// S(a b c - b c a c a c) => compile/execute block { rot 2dup over }
+// $S(_ x y _ - y x) => string "drop pop(2)"
+// .$S(x1 x2 - 17 x1) => print string "drop 17 swap"
+
+// simplify/transform sequences of stack manipulation operations
+LIST(. [a b c d e f g h i j]) constant std-stack
+{ stk-start std-stack explode drop stk-to std-stack explode drop
+} : simplify<{
+{ build-stk-effect generate-reorder ops>$ } : }>stack
+// simplify<{ drop drop over over -13 }>stack => string "2drop 2dup -13"
+// simplify<{ 17 rot }>stack => string "swap 17 swap"
+// simplify<{ 5 1 reverse }>stack => string "xchg(1,5) xchg(2,4)"
diff --git a/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/TonUtil.fif b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/TonUtil.fif
new file mode 100644
index 0000000000..f31b591eb3
--- /dev/null
+++ b/src/test/fuzzer/src/minimal-fc-stdlib/fift-lib/TonUtil.fif
@@ -0,0 +1,381 @@
+library TonUtil // TON Blockchain Fift Library
+"Lists.fif" include
+
+-1 constant Masterchain
+0 constant Basechain
+
+// parse workchain id
+// ( S -- workchain )
+{ (number) 1- abort"workchain id must be an integer"
+  dup 32 fits not abort"workchain id must fit in 32 bits"
+} : parse-workchain-id
+
+{ (number) 1- abort"integer expected" } : parse-int
+
+{ over null? ' swap if drop } : replace-if-null
+
+// Private key load/generate
+// ( fname -- pubkey privkey )
+{ dup ."Loading private key from file " type cr 
+  file>B dup Blen 32 <> abort"Private key must be exactly 32 bytes long"
+  dup priv>pub swap 
+} : load-keypair
+// ( fname -- pubkey privkey )
+{ dup file-exists?
+  { load-keypair }
+  { dup newkeypair swap rot over swap B>file
+    rot ."Saved new private key to file " type cr 
+  } cond
+} : load-generate-keypair
+
+// Parse smart-contract address
+// ( S -- workchain addr bounce? )
+{ $>smca not abort"invalid smart-contract address"
+  1 and 0= 
+} : parse-smc-addr
+
+// ( x -- ) Displays a 64-digit hex number
+{ 64 0x. } : 64x.
+{ 64 0X. } : 64X.
+// ( wc addr -- )  Show address in <workchain>:<account> form
+{ swap ._ .":" 64x. } : .addr
+// ( wc addr flags -- )  Show address in base64url form
+{ smca>$ type } : .Addr
+// ( wc addr fname -- )  Save address to file in 36-byte format
+{ -rot 256 u>B swap 32 i>B B+ swap B>file } : save-address
+// ( wc addr fname -- )  Save address and print message
+{ dup ."(Saving address to file " type .")" cr save-address
+} : save-address-verbose
+
+// ( fname -- wc addr )  Load address from file
+{ file>B 32 B| 
+  dup Blen { 32 B>i@ } { drop Basechain } cond
+  swap 256 B>u@
+} : load-address
+// ( fname -- wc addr )  Load address from file and print message
+{ dup ."(Loading address from file " type .")" cr load-address
+} : load-address-verbose
+// Parse string as address or load address from file (if string is prefixed by @)
+// ( S default-bounce -- workchain addr bounce? )
+{ over $len 0= abort"empty smart-contract address"
+  swap dup 1 $| swap "@" $=
+  { nip load-address rot } { drop nip parse-smc-addr } cond
+} : parse-load-address
+
+// ( hex-str -- addr )  Parses ADNL address
+{ dup $len 64 <> abort"ADNL address must consist of exactly 64 hexadecimal characters"
+  (hex-number) 1 <> abort"ADNL address must consist of 64 hexadecimal characters"
+  dup 256 ufits not abort"invalid ADNL address"
+} : parse-adnl-address
+
+// ( b wc addr -- b' )  Serializes address into Builder b
+{ -rot 8 i, swap 256 u, } : addr, 
+{ over 8 fits { rot b{100} s, -rot addr, } {
+  rot b{110} s, 256 9 u, rot 32 i, swap 256 u, } cond
+} : Addr,
+
+// Gram utilities
+1000000000 constant Gram
+{ Gram swap */r } : Gram*/
+{ Gram * } : Gram*
+{ (number) dup { 1- ' Gram*/ ' Gram* cond true } if
+} : $>GR?
+// ( S -- nanograms )
+{ $>GR? not abort"not a valid Gram amount"
+} : $>GR
+{ bl word $>GR 1 'nop } ::_ GR$
+// ( nanograms -- S )
+{ dup abs <# ' # 9 times char . hold #s rot sign #>
+nip -trailing0 } : (.GR)
+{ (.GR) ."GR$" type } : .GR_
+{ .GR_ space } : .GR
+
+// b x -- b'  ( serializes a Gram amount )
+{ -1 { 1+ 2dup 8 * ufits } until
+  rot over 4 u, -rot 8 * u, } : Gram,
+// s -- x s'  ( deserializes a Gram amount )
+{ 4 u@+ swap 8 * u@+ } : Gram@+
+// s -- x
+{ 4 u@+ swap 8 * u@ } : Gram@
+
+// currency collections
+// b x --> b'  ( serializes a VarUInteger32 )
+{ -1 { 1+ 2dup 8 * ufits } until
+  rot over 5 u, -rot 8 * u, } : VarUInt32,
+// s --> x  ( deserializes a VarUInteger32 )
+{ 5 u@+ swap 8 * u@ } : VarUInt32@
+32 constant cc-key-bits
+' VarUInt32, : val,
+' VarUInt32@ : val@
+// d k v -- d'
+{ <b swap val, b> <s swap rot cc-key-bits idict!+ not abort"cannot add key-value to CurrencyCollection"
+} : +newccpair
+{ dup { -rot tuck swap cc-key-bits idict@- { val@ 2swap -rot + } { swap rot } cond +newccpair
+  } { 2drop } cond
+} : +ccpair
+dictnew constant cc0   // zero currency collection
+// ( v k -- d )  Creates currency collection representing v units of currency k
+{ cc0 swap rot +ccpair } : of-cc
+{ dictnew { over null? not } { swap uncons -rot unpair +ccpair } while nip } : list>cc
+{ dup null? { ."(null)" drop } { val@ ._ } cond } dup : .maybeVarUInt32 : .val
+{ swap cc-key-bits { rot { ."+" } if .val ."*$" ._ true true } idictforeach drop } : (.cc)
+{ false (.cc) { ."0" } ifnot } : .cc_
+{ .cc_ space } : .cc
+{ true (.cc) drop } : .+cc_
+{ .+cc_ space } : .+cc
+{ cc-key-bits { rot . ."-> " swap .val .val ."; " true } dictdiff drop cr } : show-cc-diff
+{ cc-key-bits { val@ swap val@ + val, true } dictmerge } : cc+
+{ null swap cc-key-bits { val@ pair swap cons true } idictforeach drop } : cc>list-rev
+{ cc>list-rev list-reverse } : cc>list
+forget val,  forget val@  forget .val
+
+// ( S -- x -1 or 0 )
+{ (number) dup 2 = { -rot 2drop } if 1 = } : int?
+{ int? dup { drop dup 0< { drop false } { true } cond } if } : pos-int?
+// ( S -- k v -1 or 0 )  Parses expression <value>*<currency> or <value>*$<currency>
+{ dup "*" $pos dup 0< { 2drop false } {
+  $| dup $len 2 < { 2drop false } {
+  1 $| nip dup 1 $| swap "$" $= { swap } if drop
+  int? dup { over 32 fits { 2drop false } ifnot } if
+  not { drop false } {
+  swap pos-int? not { drop false } {
+  true
+  } cond } cond } cond } cond
+} : cc-key-value?
+// ( S -- D -1 or 0 )  Parses an extra currency collection
+// e.g. "10000*$3+7777*$-11" means "10000 units of currency #3 and 7777 units of currency #-11"
+{ dictnew {  // S D
+    swap dup "+" $pos dup 0< { drop null -rot } { $| 1 $| nip -rot } cond
+    cc-key-value? { +ccpair over null? dup { rot drop true } if } { 2drop false true } cond
+  } until
+} : $>xcc?
+{ $>xcc? not abort"invalid extra currency collection" } : $>xcc
+{ char } word dup $len { $>xcc } { drop dictnew } cond 1 'nop } ::_ CX{
+
+// complete currency collections
+{ $>xcc? { true } { drop false } cond } : end-parse-cc
+// ( S -- x D -1 or 0 )  Parses a currency collection
+// e.g. "1.2+300*$2" means "1200000000ng plus 300 units of currency #2"
+{ 0 swap dup "+" $pos dup 0< { drop dup
+      $>GR? { nip nip dictnew true } { end-parse-cc } cond
+    } { over swap $| swap $>GR? { 2swap 2drop swap 1 $| nip } { drop
+  } cond end-parse-cc } cond
+} : $>cc?
+{ $>cc? not abort"invalid currency collection" } : $>cc
+{ char } word dup $len { $>cc } { drop 0 dictnew } cond 2 'nop } ::_ CC{
+// ( x D -- )
+{ swap ?dup { .GR_ .+cc_ } { .cc_ } cond } : .GR+cc_
+{ .GR+cc_ space } : .GR+cc
+{ -rot Gram, swap dict, } : Gram+cc,
+
+// Libraries
+// ( -- D )  New empty library collection
+' dictnew : Libs{
+// ( D -- D )  Return library collection as dictionary
+'nop : }Libs
+// ( D c x -- D' )  Add a public/private library c to collection D
+{ <b swap 1 u, over ref, b> <s swap hash rot 256 udict!+
+  0= abort"duplicate library in collection" } : lib+
+// ( D c -- D' )  Add private library c to collection D
+{ 0 lib+ } : private_lib
+// ( D c -- D' )  Add public library c to collection D
+{ 1 lib+ } : public_lib
+
+// serialize simple transfers with long comments
+// b B n -- b'
+recursive append-long-bytes {
+  over Blen over <= { drop B, } {
+    B| <b swap 127 append-long-bytes b> -rot B, swap ref,
+  } cond
+} swap !
+// b S n -- b'
+{ swap $>B swap append-long-bytes } : append-long-string
+// S -- c
+{ <b over $len { 0 32 u, swap 36 append-long-string } { nip } cond b>
+} : simple-transfer-body
+
+// ( S -- x )  parse public key
+{ dup $len 48 <> abort"public key must be 48 characters long"
+  base64url>B dup Blen 36 <> abort"public key must be 48 characters long"
+  34 B| 16 B>u@ over crc16 <> abort"crc16 mismatch in public key"
+  16 B>u@+ 0x3ee6 <> abort"invalid tag in public key"
+  256 B>u@
+} : parse-pubkey
+{ bl word parse-pubkey 1 'nop } ::_ PK'
+// ( x -- S )  serialize public key
+{ 256 u>B B{3ee6} swap B+ dup crc16 16 u>B B+ B>base64 } : pubkey>$
+{ pubkey>$ type } : .pubkey
+
+// ( S -- x )  parse validator-encoded public key
+{ base64>B dup Blen 36 <> abort"public key with magic must be 36 bytes long"
+  4 B| swap 32 B>u@ 0xC6B41348 <> abort"unknown magic for public key (not Ed25519)"
+} : parse-val-pubkey
+{ bl word parse-val-pubkey 1 'nop } ::_ VPK'
+{ char } word base64>B 1 'nop } ::_ B64{
+
+// adnl address parser
+{ 256 u>B B{2D} swap B+ dup crc16 16 u>B B+ } : adnl-preconv
+{ swap 32 /mod dup 26 < { 65 } { 24 } cond + rot swap hold } : Base32#
+{ <# ' Base32# 8 times #> } : Base32#*8
+{ "" over Blen 5 / { swap 40 B>u@+ Base32#*8 nip rot swap $+ } swap times nip } : B>Base32
+
+// ( x -- S )  Converts an adnl-address from a 256-bit integer to a string
+{ adnl-preconv B>Base32 1 $| nip } : adnl>$
+
+{ 65 - dup 0>= { -33 and dup 26 < } { 41 + dup 25 > over 32 < and } cond ?dup nip } : Base32-digit?
+{ Base32-digit? not abort"not a Base32 digit" } : Base32-digit
+{ 0 { over $len } { swap 1 $| -rot (char) Base32-digit swap 5 << + } while nip } : Base32-number
+{ B{} { over $len } { swap 8 $| -rot Base32-number 40 u>B B+ } while nip } : Base32>B
+
+// ( S -- x )  Converts an adnl address from a string to 256-bit integer
+{ dup $len 55 <> abort"not 55 alphanumeric characters" "F" swap $+ Base32>B
+  33 B| 16 B>u@ over crc16 <> abort"crc16 checksum mismatch"
+  8 B>u@+ 0x2D <> abort"not a valid adnl address" 256 B>u@ } : $>adnl
+
+{ 65 - dup 0>= { -33 and 10 + dup 16 < } { 17 + dup 0>= over 10 < and } cond ?dup nip } : hex-digit?
+// ( S -- x -1 or 0 )  Parses a hexadecimal integer
+{ dup $len {
+    0 {
+      4 << swap 1 $| -rot (char) hex-digit?  // S a d -1 or S a 0
+      { + over $len 0= } { drop -1 true } cond
+    } until
+    dup 0< { 2drop false } { nip true } cond
+  } { drop false } cond
+} : hex$>u?
+// ( S -- x )
+{ hex$>u? not abort"not a hexadecimal number" } : hex$>u
+
+{ dup $len 64 = { hex$>u } {
+  dup $len 55 = { $>adnl } {
+    true abort"invalid adnl address"
+  } cond } cond
+} : parse-adnl-addr
+{ adnl>$ type } : .adnl
+{ bl word parse-adnl-addr 1 'nop } ::_ adnl:
+
+// ( x a b -- a<=x<=b )
+{ 2 pick >= -rot >= and } : in-range?
+
+// ( c i -- ? )  Checks whether c is a valid value for config param #i
+def? config-valid? {
+  { nip 0>= { ."warning: cannot check validity of configuration parameter value, use create-state instead of fift to check validity" cr } if
+    true } : config-valid?
+} ifnot
+
+{ dup -1000 = { drop <s ref@ <s 12 u@ 0xFF0 = } {
+  dup -1001 = { drop <s ref@ <s 12 u@ 0xFF0 = } {
+  over null? { 2drop true } {
+  config-valid?
+  } cond } cond } cond
+} : is-valid-config?
+
+
+// Get anycast depth / rewrite_pfx or return 0
+// ( S -- x y S )
+{
+    // maybe
+    1 u@+ swap 0 >
+    {
+        // anycast_info$_ depth:(#<= 30) { depth >= 1 }
+        //    rewrite_pfx:(bits depth) = Anycast;
+        30 u@+ swap // get depth
+
+        dup 1 > {
+            dup 2 roll swap u@+ // get rewrite_pfx
+            // return depth, rewrite_pfx, slice
+        }
+        {
+            drop // drop depth (<=1)
+            0 0 2 roll // set anycast to none
+        } cond
+    }
+    {
+        0 0 2 roll // set anycast to none
+    } cond
+} : maybe-anycast
+
+// Rewrite first bits of addr with anycast info
+{ // input: anycast depth, rewrite_pfx, workchain, slice, address length
+    4 -roll
+    3 roll dup dup 0 = { 2drop 2 roll drop }
+    {
+        rot swap u@+ swap drop
+        3 roll
+        <b swap 3 roll u, b> <s swap |+
+    } cond // rewrite first bits of address with rewrite_pfx
+    2 roll
+    u@+ // get address
+} : parse-address-with-anycast
+
+// Parse Slice S and return:
+// 0 `addr_none S - if addr_none$00 is parsed
+// addr `addr_extern S - if addr_extern$01 is parsed
+// wc addr `addr_std S - if addr_std$10 is parsed
+// wc addr `addr_var S - if addr_var$11 is parsed
+// ( S -- 0 A S or addr A S or wc addr A S )
+{ 2 u@+ swap dup 0>  // Get addr: addr_none$00 / addr_extern$01 / addr_std$10 / addr_var$11
+    { // if greater that zero
+       dup 1 >
+       {
+            2 =
+            {
+                // if addr_std$10
+                // anycast:(Maybe Anycast)
+                // workchain_id:int8
+                // address:bits256  = MsgAddressInt;
+                maybe-anycast // get anycast depth, bits, slice
+                8 i@+ // get workchain
+                256 parse-address-with-anycast
+                `addr-std swap
+            }
+
+            {
+                // if addr_var$11
+                // anycast:(Maybe Anycast)
+                // addr_len:(## 9)
+                // workchain_id:int32
+                // address:(bits addr_len) = MsgAddressInt;
+                maybe-anycast // get anycast depth, bits, slice
+                9 u@+  // get addr_len
+                32 i@+ // get workchain
+                swap 2 -roll // move workchain to neede position
+                swap parse-address-with-anycast
+                `addr-var swap
+            } cond
+
+       }
+       {
+            drop // drop header (dup for statment upper)
+            // if addr_extern$01
+            // addr_extern$01 len:(## 9)
+            // external_address:(bits len)
+            9 u@+ swap  // bit len
+            u@+ // external_address
+            `addr-extern swap
+       } cond
+    }
+    {
+        swap
+        // if addr_none$00
+        `addr-none swap
+    } cond
+} : addr@+
+
+{ addr@+ drop } : addr@
+
+// User-friendly prints output of addr@
+// (0 A or addr A or wc addr A -- )
+{
+    dup `addr-none eq?
+    { 2drop ."addr_none" }
+    {
+        `addr-extern eq?
+        { (dump) type }
+        { (x.) swap (dump) ":" $+ swap $+ type }
+        cond
+    }
+    cond
+} : print-addr // print addr with workchain
+
+forget maybe-anycast
+forget parse-address-with-anycast
diff --git a/src/test/fuzzer/src/minimal-fc-stdlib/funcplusfift b/src/test/fuzzer/src/minimal-fc-stdlib/funcplusfift
new file mode 100755
index 0000000000..77e0449436
Binary files /dev/null and b/src/test/fuzzer/src/minimal-fc-stdlib/funcplusfift differ
diff --git a/src/test/fuzzer/src/minimal-fc-stdlib/stdlib.fc b/src/test/fuzzer/src/minimal-fc-stdlib/stdlib.fc
new file mode 100644
index 0000000000..29dfb07a09
--- /dev/null
+++ b/src/test/fuzzer/src/minimal-fc-stdlib/stdlib.fc
@@ -0,0 +1,71 @@
+cell get_data() asm "c4 PUSH";
+
+slice begin_parse(cell c) asm "CTOS";
+
+slice skip_bits(slice s, int len) asm "SDSKIPFIRST";
+(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST";
+
+int slice_bits(slice s) asm "SBITS";
+
+(slice, slice) load_msg_addr(slice s) asm(-> 1 0) "LDMSGADDR";
+
+forall X -> X null() asm "PUSHNULL";
+
+(slice, cell) load_ref(slice s) asm(-> 1 0) "LDREF";
+
+builder begin_cell() asm "NEWC";
+
+() set_data(cell c) impure asm "c4 POP";
+
+cell end_cell(builder b) asm "ENDC";
+
+builder store_dict(builder b, cell c) asm(c b) "STDICT";
+
+(slice, cell) load_dict(slice s) asm(-> 1 0) "LDDICT";
+
+(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL";
+(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT";
+
+cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
+(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
+
+cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
+(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
+
+(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF" "NULLSWAPIFNOT";
+
+cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
+(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
+cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
+(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
+
+(slice, int) load_coins(slice s) asm(-> 1 0) "LDVARUINT16";
+builder store_coins(builder b, int x) asm "STVARUINT16";
+
+(slice, int) load_varuint16(slice s) asm(-> 1 0) "LDVARUINT16";
+
+builder store_slice(builder b, slice s) asm "STSLICER";
+
+(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT";
+(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL";
+
+cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
+(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
+
+cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
+(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
+
+(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF" "NULLSWAPIFNOT";
+
+cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
+(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
+
+int cell_hash(cell c) asm "HASHCU";
+
+int slice_hash(slice s) asm "HASHSU";
+
+int string_hash(slice s) asm "SHA256U";
+
+int equal_slices_bits(slice a, slice b) asm "SDEQ";
+
+cell my_code() asm "MYCODE";
\ No newline at end of file
diff --git a/src/test/fuzzer/src/minimal-fc-stdlib/stdlib_ex.fc b/src/test/fuzzer/src/minimal-fc-stdlib/stdlib_ex.fc
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/src/test/fuzzer/src/minimal-fc-stdlib/stdlib_ex.fc
@@ -0,0 +1 @@
+
diff --git a/src/test/fuzzer/src/scope.ts b/src/test/fuzzer/src/scope.ts
new file mode 100644
index 0000000000..ab71e2a3fc
--- /dev/null
+++ b/src/test/fuzzer/src/scope.ts
@@ -0,0 +1,453 @@
+import type { Type } from "@/test/fuzzer/src/types";
+import { getReturnType, tyEq } from "@/test/fuzzer/src/types";
+import type { GenerativeEntity } from "@/test/fuzzer/src/generators";
+import type * as Ast from "@/ast/ast";
+import type { NamedGenerativeEntity } from "@/test/fuzzer/src/generators/generator";
+
+export type ScopeKind =
+    | "program"
+    | "trait"
+    | "contract"
+    | "function"
+    | "method"
+    | "receive"
+    | "block";
+
+const namedScopeItemKinds = [
+    "field",
+    "contract",
+    "trait",
+    "struct",
+    "message",
+    "constantDecl",
+    "constantDef",
+    "functionDecl",
+    "functionDef",
+    "methodDecl",
+    "methodDef",
+    "let",
+    "parameter",
+] as const;
+export type NamedScopeItemKind = (typeof namedScopeItemKinds)[number];
+
+/*
+function isNamedScopeItemKind(val: string): val is NamedScopeItemKind {
+    return namedScopeItemKinds.find((tpe) => tpe === val) ? true : false;
+}
+*/
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+//const unnamedScopeItemKinds = ["statement", "receive"] as const;
+//export type UnnamedScopeItemKind = (typeof unnamedScopeItemKinds)[number];
+
+export type ScopeItemKind = NamedScopeItemKind; //| UnnamedScopeItemKind;
+
+/** Maps each ScopeItemKind to its respective GenerativeEntity specialization. */
+type NamedGenerativeEntityMap = {
+    let: NamedGenerativeEntity<Ast.Statement>;
+    parameter: NamedGenerativeEntity<Ast.TypedParameter>;
+    struct: NamedGenerativeEntity<Ast.StructDecl>;
+    message: NamedGenerativeEntity<Ast.MessageDecl>;
+    constantDecl: NamedGenerativeEntity<Ast.ConstantDecl>;
+    constantDef: NamedGenerativeEntity<Ast.ConstantDef>;
+    functionDecl: NamedGenerativeEntity<Ast.FunctionDecl>;
+    functionDef: NamedGenerativeEntity<Ast.FunctionDef>;
+    methodDecl: NamedGenerativeEntity<Ast.FunctionDecl>;
+    methodDef: NamedGenerativeEntity<Ast.FunctionDef>;
+    field: NamedGenerativeEntity<Ast.FieldDecl>;
+    contract: NamedGenerativeEntity<Ast.Contract>;
+    trait: NamedGenerativeEntity<Ast.Trait>;
+};
+/*type GenerativeEntityMap = {
+    statement: GenerativeEntity<Ast.Statement>;
+    receive: GenerativeEntity<Ast.Receiver>;
+};*/
+
+/**
+ * Scope contains AST entries generated during the bottom-up AST generation and
+ * provides an information to access data in parent scopes.
+ */
+export class Scope {
+    kind: ScopeKind;
+
+    /** Reference to the parent scope. `undefined` for the top-level scope. */
+    readonly parentScope?: Scope;
+
+    /**
+     * Contains AST entries generated during the bottom-up AST generation.
+     *
+    private mapUnnamed: Map<
+        UnnamedScopeItemKind,
+        Map<number, GenerativeEntity<any>> // eslint-disable-line @typescript-eslint/no-explicit-any
+    > = new Map();
+     */
+
+    private mapNamed: Map<
+        NamedScopeItemKind,
+        Map<string, NamedGenerativeEntity<any>> // eslint-disable-line @typescript-eslint/no-explicit-any
+    > = new Map();
+
+    constructor(kind: ScopeKind, parentScope: Scope | undefined) {
+        this.kind = kind;
+        this.parentScope = parentScope;
+    }
+
+    public isProgramScope(): boolean {
+        return this.parentScope === undefined;
+    }
+
+    /**
+     * Returns the top-level scope.
+     */
+    public getProgramScope(): Scope {
+        return this.isProgramScope()
+            ? this
+            : this.parentScope!.getProgramScope();
+    }
+
+    /**
+     * Returns the contract-level scope or `undefined` if it is not possible to reach it from the current scope.
+     */
+    public getContractScope(): Scope | undefined {
+        if (this.isContractScope()) {
+            return this;
+        }
+        if (this.parentScope === undefined) {
+            return undefined;
+        }
+        return this.parentScope!.getContractScope();
+    }
+
+    public isContractScope(): boolean {
+        return (
+            this.parentScope !== undefined &&
+            this.parentScope.isProgramScope() &&
+            this.kind === "contract"
+        );
+    }
+
+    /**
+     * Determine the appropriate parent scope based on the kind of entity
+     */
+    private getTargetScopeToAdd(kind: ScopeItemKind) {
+        let targetScope: Scope | undefined;
+        switch (kind) {
+            case "let":
+            case "parameter":
+                //case "statement":
+                // eslint-disable-next-line @typescript-eslint/no-this-alias
+                targetScope = this;
+                break;
+            case "constantDecl":
+            case "constantDef":
+                targetScope = this.findParent("trait", "contract", "program");
+                break;
+            case "functionDecl":
+            case "functionDef":
+            case "trait":
+                targetScope = this.findParent("program");
+                break;
+            case "methodDecl":
+            case "methodDef":
+            case "field":
+                //case "receive":
+                targetScope = this.findParent("trait", "contract");
+                break;
+            case "contract":
+            case "struct":
+            case "message":
+                targetScope = this.findParent("program");
+                break;
+            default:
+                throw new Error("Unsupported kind for adding to scope.");
+        }
+        if (targetScope === undefined) {
+            throw new Error(`Cannot add "${kind}" to the "${this.kind}" scope`);
+        }
+        return targetScope;
+    }
+
+    /**
+     * Put a new entity in the scope according to the Tact semantics.
+     *
+    public addUnnamed<T extends UnnamedScopeItemKind>(
+        _kind: T,
+        _entity: GenerativeEntityMap[T],
+    ): void {
+        throw new Error("Currently not supported");
+        // const targetScope = this.getTargetScopeToAdd(kind);
+        // if (targetScope.mapUnnamed.has(kind)) {
+        //     targetScope.mapUnnamed.get(kind)!.set(entity.idx, entity);
+        // } else {
+        //     targetScope.mapUnnamed
+        //         .set(kind, new Map())
+        //         .get(kind)!
+        //         .set(entity.idx, entity);
+        // }
+    }*/
+
+    /**
+     * Put a new entity in the scope according to the Tact semantics.
+     */
+    public addNamed<T extends NamedScopeItemKind>(
+        kind: T,
+        entity: NamedGenerativeEntityMap[T],
+    ): void {
+        const targetScope = this.getTargetScopeToAdd(kind);
+
+        //if (isNamedScopeItemKind(kind)) {
+        if (targetScope.mapNamed.has(kind)) {
+            targetScope.mapNamed.get(kind)!.set(entity.name.text, entity);
+        } else {
+            targetScope.mapNamed
+                .set(kind, new Map())
+                .get(kind)!
+                .set(entity.name.text, entity);
+        }
+        //}
+    }
+
+    /*
+    public getAllUnnamed<T extends UnnamedScopeItemKind>(
+        _kind: T,
+    ): GenerativeEntityMap[T][] {
+        throw new Error("Currently not supported");
+        // const kindMap = this.mapUnnamed.get(kind);
+        // if (kindMap) {
+        //     return Array.from(kindMap.values());
+        // }
+        // return [];
+    }*/
+
+    public getAllNamed<T extends NamedScopeItemKind>(
+        kind: T,
+    ): NamedGenerativeEntityMap[T][] {
+        const kindMap = this.mapNamed.get(kind);
+        if (kindMap) {
+            return Array.from(kindMap.values());
+        }
+        return [];
+    }
+
+    /**
+     * Collects name-type tuples of all the entries with the given type defined within this scope.
+     */
+    public getNamedEntries(kind: NamedScopeItemKind): [string, Type][] {
+        const names = this.mapNamed.get(kind);
+        if (names === undefined) {
+            return [];
+        }
+        return Array.from(names)
+            .map(
+                ([_id, entry]) =>
+                    [entry.name.text, entry.type] as [string | undefined, Type],
+            )
+            .filter(
+                (nameType): nameType is [string, Type] =>
+                    nameType[0] !== undefined,
+            );
+    }
+
+    /**
+     * Collects name-type tuples of all the entries with the given type defined within scope
+     * and its parent scopes.
+     */
+    public getNamedEntriesRecursive(
+        ...kinds: NamedScopeItemKind[]
+    ): [string, Type][] {
+        const recursiveHelper = (
+            kinds: NamedScopeItemKind[],
+            acc: [string, Type][],
+            scope?: Scope,
+        ): [string, Type][] => {
+            if (scope === undefined) {
+                return acc;
+            }
+            const entries = kinds.flatMap((kind) =>
+                scope.getNamedEntries(kind),
+            );
+            if (scope.isProgramScope()) {
+                return acc.concat(entries);
+            } else {
+                return recursiveHelper(
+                    kinds,
+                    acc.concat(entries),
+                    scope.parentScope,
+                );
+            }
+        };
+        return recursiveHelper(kinds, [], this);
+    }
+
+    /**
+     * Collects names of all the entries with the given type defined within this scope.
+     */
+    public getNames(kind: NamedScopeItemKind, ty: Type): string[] {
+        return this.getNamedEntries(kind)
+            .filter(([_name, type]) => tyEq(type, ty))
+            .map(([name, _type]) => name);
+    }
+
+    /**
+     * Collects names of all the entries with the given type defined within scope
+     * and its parent scopes.
+     */
+    public getNamesRecursive(
+        kind: NamedScopeItemKind,
+        ty: Type,
+        acc: string[] = [],
+    ): string[] {
+        const names = this.getNames(kind, ty);
+        if (this.isProgramScope()) {
+            return acc.concat(names);
+        } else {
+            return acc.concat(
+                this.parentScope!.getNamesRecursive(kind, ty, names),
+            );
+        }
+    }
+
+    /**
+     * Collects all names of all entities in the scope.
+     */
+    public getAllNames(): string[] {
+        return Array.from(this.mapNamed.values()).flatMap((m) =>
+            Array.from(m.values()).map((entity) => entity.name.text),
+        );
+    }
+
+    /**
+     * Collects all names of all entities in the scope and it all parent scopes.
+     */
+    public getAllNamesRecursive(): string[] {
+        return this.getAllNames().concat(
+            this.parentScope?.getAllNamesRecursive() ?? [],
+        );
+    }
+
+    /**
+     * Returns all items of the given type defined within this scope.
+     */
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    public getItems(kind: ScopeItemKind): GenerativeEntity<any>[] {
+        //const result = isNamedScopeItemKind(kind)
+        //    ? this.mapNamed.get(kind)
+        //    : this.mapUnnamed.get(kind);
+        const result = this.mapNamed.get(kind);
+        return result === undefined ? [] : Array.from(result.values());
+    }
+
+    /**
+     * Returns all items of the given type defined within this scope and its parents.
+     */
+    public getItemsRecursive(
+        kind: NamedScopeItemKind,
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
+        acc?: GenerativeEntity<any>[], // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    ): NamedGenerativeEntity<any>[];
+    //public getItemsRecursive(
+    //    kind: UnnamedScopeItemKind,
+    //    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    //    acc?: GenerativeEntity<any>[], // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    //): GenerativeEntity<any>[];
+    public getItemsRecursive(
+        kind: ScopeItemKind,
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
+        acc: GenerativeEntity<any>[] = [],
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    ): GenerativeEntity<any>[] {
+        const currentItems = this.getItems(kind);
+        const accN = acc.concat(currentItems);
+        if (!this.isProgramScope() && this.parentScope)
+            //return isNamedScopeItemKind(kind)
+            //    ? this.parentScope.getItemsRecursive(kind, accN)
+            //    : this.parentScope.getItemsRecursive(kind, accN);
+            return this.parentScope.getItemsRecursive(kind, accN);
+        else {
+            return accN;
+        }
+    }
+
+    /**
+     * Recursively searches for functions or methods that return the specified type.
+     * @param kind The kind of callable to search for.
+     * @param returnTy The return type to match.
+     * @return An array of tuples containing the function/method names and their full signatures.
+     */
+    public findFunction(
+        kind: "methodDecl" | "methodDef" | "functionDecl" | "functionDef",
+        returnTy: Type,
+    ): [string, Type][] {
+        const functions = this.getItemsRecursive(kind);
+        return Array.from(functions.values()).reduce<[string, Type][]>(
+            (acc, entry) => {
+                if (
+                    entry.type.kind === "function" &&
+                    getReturnType(entry.type) === returnTy
+                ) {
+                    acc.push([entry.name.text, entry.type]);
+                }
+                return acc;
+            },
+            [],
+        );
+    }
+
+    /**
+     * Checks if the given scope defines an identifier.
+     */
+    public has(kind: NamedScopeItemKind, name: string): boolean {
+        return (
+            this.mapNamed.has(kind) &&
+            Array.from(this.mapNamed.get(kind)!).find(
+                ([_id, entry]) => entry.name.text === name,
+            ) !== undefined
+        );
+    }
+
+    /**
+     * Checks if the given scope or its parents define an identifier.
+     */
+    public hasRecursive(kind: NamedScopeItemKind, name: string): boolean {
+        if (this.has(kind, name)) {
+            return true;
+        } else if (this.isProgramScope()) {
+            return false;
+        } else {
+            return this.parentScope!.hasRecursive(kind, name);
+        }
+    }
+
+    /**
+     * Looks for a parent scope with one of the given kinds.
+     */
+    public findParent(...kinds: ScopeKind[]): Scope | undefined {
+        if (kinds.find((kind) => this.kind === kind)) {
+            return this;
+        } else if (this.parentScope === undefined) {
+            return undefined;
+        } else if (
+            kinds.find(
+                (kind) => this.parentScope && this.parentScope.kind === kind,
+            )
+        ) {
+            return this.parentScope;
+        } else {
+            return this.parentScope.findParent(...kinds);
+        }
+    }
+
+    /**
+     * Returns true if the given scope has one or more ancestors with the given kind.
+     */
+    public hasParent(...kinds: ScopeKind[]): boolean {
+        return this.findParent(...kinds) !== undefined;
+    }
+
+    /**
+     * Returns true if the given scope is defined inside one of the given kinds.
+     */
+    public definedIn(...kinds: ScopeKind[]): boolean {
+        return kinds.find((k) => this.kind == k) !== undefined;
+    }
+}
diff --git a/src/test/fuzzer/src/stdlib.ts b/src/test/fuzzer/src/stdlib.ts
new file mode 100644
index 0000000000..684c1317c6
--- /dev/null
+++ b/src/test/fuzzer/src/stdlib.ts
@@ -0,0 +1,35 @@
+import type * as Ast from "@/ast/ast";
+import * as path from "path";
+import { files } from "@/stdlib/stdlib";
+import { createVirtualFileSystem } from "@/vfs/createVirtualFileSystem";
+import { generateAstIdFromName } from "@/test/fuzzer/src/util";
+import { GlobalContext } from "@/test/fuzzer/src/context";
+
+const StdlibFilePath = path.join(
+    __dirname,
+    "..",
+    "..",
+    "src",
+    "stdlib",
+    "stdlib",
+);
+const StdlibVFS = createVirtualFileSystem(StdlibFilePath, files);
+export const StdlibPath = StdlibVFS.resolve("std/stdlib.fc");
+export const StdlibCode = StdlibVFS.readFile(StdlibPath).toString();
+// export const StdlibExPath = StdlibVFS.resolve("std/stdlib_ex.fc");
+// export const StdlibExCode = StdlibVFS.readFile(StdlibExPath).toString();
+
+/**
+ * Returns traits defined in stdlib.
+ * TODO: We should parse its sources instead
+ */
+export function getStdlibTraits(): Ast.TypeDecl[] {
+    return [
+        GlobalContext.makeF.makeDummyTrait(
+            generateAstIdFromName("BaseTrait"),
+            [],
+            [],
+            [],
+        ),
+    ];
+}
diff --git a/src/test/fuzzer/src/types.ts b/src/test/fuzzer/src/types.ts
new file mode 100644
index 0000000000..37ca2f5742
--- /dev/null
+++ b/src/test/fuzzer/src/types.ts
@@ -0,0 +1,447 @@
+import type * as Ast from "@/ast/ast";
+import {
+    createSample,
+    generateName,
+    randomInt,
+    randomBool,
+    generateAstIdFromName,
+    stringify,
+} from "@/test/fuzzer/src/util";
+import type { Scope } from "@/test/fuzzer/src/scope";
+import type { TypeRef } from "@/types/types";
+import fc from "fast-check";
+import { GlobalContext } from "@/test/fuzzer/src/context";
+
+/**
+ * Types from Tact stdlib.
+ */
+export enum StdlibType {
+    Int = "Int",
+    Bool = "Bool",
+    Builder = "Builder",
+    Slice = "Slice",
+    Cell = "Cell",
+    Address = "Address",
+    String = "String",
+    StringBuilder = "StringBuilder",
+}
+
+/** User-defined maps. */
+export type MapType = {
+    key: Type;
+    value: Type;
+};
+
+/** A single struct or message field. */
+export type StructField = {
+    name: string;
+    type: Type;
+    default?: fc.Arbitrary<Ast.Expression>;
+};
+
+/** Utility types used internally in the generator. */
+export enum UtilType {
+    Contract = "Contract",
+    Trait = "Trait",
+    Program = "Program",
+    This = "This",
+    /**
+     * Functional Unit type that refers to the `void` type in languages like C++.
+     * Typically used when returning nothing from functions/methods or in imperative
+     * constructions which only mutate the state.
+     */
+    Unit = "Unit",
+}
+
+export type OptionalType = {
+    kind: "optional";
+    type: Type;
+};
+
+/**
+ * Represents the signature of a function in a format typical for functional languages, such as Int -> Int -> Int.
+ * The last element of the list means the return type, previous elements are types of the arguments.
+ */
+export type FunctionType = {
+    kind: "function";
+    signature: Type[];
+};
+
+export type Type =
+    | {
+          kind: "stdlib";
+          type: StdlibType;
+      }
+    | {
+          kind: "map";
+          type: MapType;
+      }
+    | {
+          kind: "struct";
+          name: string;
+          fields: StructField[];
+      }
+    | {
+          kind: "message";
+          name: string;
+          fields: StructField[];
+      }
+    | {
+          kind: "util";
+          type: UtilType;
+      }
+    | FunctionType
+    | OptionalType;
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export function throwTyError(ty: any): never {
+    throw new Error(`Unsupported type: ${stringify(ty, 0)}`);
+}
+
+export function tyToString(ty: Type): string {
+    switch (ty.kind) {
+        case "stdlib":
+            return ty.type;
+        case "struct":
+        case "message":
+            return ty.name;
+        case "map":
+            return `map<${tyToString(ty.type.key)}, ${tyToString(ty.type.value)}>`;
+        case "optional":
+            return `${tyToString(ty.type)}?`;
+        default:
+            throwTyError(ty);
+    }
+}
+
+export function tyEq(lhs: Type, rhs: Type): boolean {
+    return tyToString(lhs) === tyToString(rhs);
+}
+
+/**
+ * A subset of supported Stdlib types that might be used in AST generation.
+ */
+export const SUPPORTED_STDLIB_TYPES: StdlibType[] = [
+    StdlibType.String,
+    StdlibType.Bool,
+    StdlibType.Int,
+];
+
+function makePrimitiveType(name: string): Ast.PrimitiveTypeDecl {
+    return GlobalContext.makeF.makeDummyPrimitiveTypeDecl(
+        generateAstIdFromName(name),
+    );
+}
+function makeASTTypeRef(name: string): Ast.TypeId {
+    return GlobalContext.makeF.makeDummyTypeId(name);
+}
+
+function makeTypeRef(name: string): TypeRef {
+    return {
+        kind: "ref",
+        name,
+        optional: false,
+    };
+}
+
+/**
+ * Cache for Stdlib types.
+ */
+const StdlibTypeCache: Map<StdlibType, [Ast.TypeDecl, Ast.Type, TypeRef]> =
+    new Map();
+Object.values(StdlibType).forEach((ty) => {
+    StdlibTypeCache.set(ty, [
+        transformTy<Ast.TypeDecl>(ty, makePrimitiveType),
+        transformTy<Ast.Type>(ty, makeASTTypeRef),
+        transformTy<TypeRef>(ty, makeTypeRef),
+    ]);
+});
+
+/**
+ * Creates a Tact type entry from the given tact-check type definition.
+ */
+function transformTy<T>(ty: StdlibType, transform: (type: StdlibType) => T): T {
+    if (!Object.values(StdlibType).includes(ty)) {
+        throwTyError(ty);
+    }
+    return transform(ty);
+}
+export function tyToAstTypeDecl(ty: Type): Ast.TypeDecl {
+    switch (ty.kind) {
+        case "stdlib": {
+            const result = StdlibTypeCache.get(ty.type);
+            if (!result) {
+                throwTyError(ty);
+            }
+            return result[0];
+        }
+        default:
+            throwTyError(ty);
+    }
+}
+export function tyToAstType(ty: Type, isBounced = false): Ast.Type {
+    const generateAstTypeId = (text: string) =>
+        GlobalContext.makeF.makeDummyTypeId(text);
+
+    switch (ty.kind) {
+        case "stdlib": {
+            const result = StdlibTypeCache.get(ty.type);
+            if (!result) {
+                throwTyError(ty);
+            }
+            return result[1];
+        }
+        case "struct":
+        case "message": {
+            const simpleType = GlobalContext.makeF.makeDummyTypeId(ty.name);
+            return isBounced
+                ? GlobalContext.makeF.makeDummyBouncedMessageType(simpleType)
+                : simpleType;
+        }
+        case "map":
+            return GlobalContext.makeF.makeDummyMapType(
+                generateAstTypeId(tyToString(ty.type.key)),
+                undefined,
+                generateAstTypeId(tyToString(ty.type.value)),
+                undefined,
+            );
+        case "optional":
+            return GlobalContext.makeF.makeDummyOptionalType(
+                tyToAstType(ty.type, isBounced),
+            );
+        default:
+            throwTyError(ty);
+    }
+}
+
+export function tyToTypeRef(ty: Type): TypeRef {
+    switch (ty.kind) {
+        case "stdlib": {
+            const result = StdlibTypeCache.get(ty.type);
+            if (!result) {
+                throwTyError(ty);
+            }
+            return result[2];
+        }
+        default:
+            throwTyError(ty);
+    }
+}
+
+/**
+ * Retrieves a return type from the function type.
+ */
+export function getReturnType(ty: FunctionType): Type {
+    if (ty.signature.length === 0) {
+        throw new Error("Empty function signature");
+    }
+    const result = ty.signature[ty.signature.length - 1];
+    if (typeof result === "undefined") {
+        throw new Error("Unexpected 'undefined'");
+    }
+    return result;
+}
+
+/**
+ * Returns mock AST entries for types defined in standard library.
+ */
+export function getStdlibTypes(): Ast.TypeDecl[] {
+    return [...Object.values(StdlibType)].map((type) =>
+        makePrimitiveType(type),
+    );
+}
+
+/**
+ * An utility class used to generate internal tact-check types.
+ */
+export class TypeGen {
+    private constructor(private scope: Scope) {}
+
+    public static fromScope(scope: Scope): TypeGen {
+        return new TypeGen(scope);
+    }
+
+    /** Arbitrary that generates stdlib types. */
+    public stdlibArbitrary: fc.Arbitrary<Type> = fc.record({
+        kind: fc.constant("stdlib"),
+        type: fc.constantFrom(...SUPPORTED_STDLIB_TYPES),
+    });
+
+    /** Arbitrary that generates map types. */
+    public mapArbitrary: fc.Arbitrary<Type> = fc.record({
+        kind: fc.constant("map"),
+        type: fc.record({
+            key: fc.record({
+                kind: fc.constant("stdlib"),
+                type: fc.constantFrom(
+                    // TODO: Support Address
+                    StdlibType.Int,
+                ),
+            }) as fc.Arbitrary<Type>,
+            value: fc.record({
+                kind: fc.constant("stdlib"),
+                type: fc.constantFrom(
+                    // TODO: Support Address, Cell, Struct, Message
+                    StdlibType.Int,
+                    StdlibType.Bool,
+                ),
+            }) as fc.Arbitrary<Type>,
+        }),
+    });
+
+    /**
+     * Picks an arbitrary type available within the scope.
+     * This doesn't generate new type definitions.
+     */
+    public pick(): Type {
+        const arb = fc.oneof(
+            this.stdlibArbitrary,
+            // this.mapArbitrary,
+            // ...this.getStructs(),
+        );
+        return createSample(arb);
+    }
+
+    /**
+     * Generates any of the supported types.
+     */
+    public generate(): fc.Arbitrary<Type> {
+        return fc.oneof(
+            this.stdlibArbitrary,
+            this.generateFun(),
+            this.generateStruct(randomBool()),
+        );
+    }
+
+    /**
+     * Generates an arbitrary function signature.
+     */
+    public generateFun(
+        minLength = 1,
+        maxLength = 3,
+    ): fc.Arbitrary<FunctionType> {
+        const structs = this.getStructs();
+        return fc.record<FunctionType>({
+            kind: fc.constant("function"),
+            signature: fc.array(
+                fc.oneof(this.stdlibArbitrary, this.mapArbitrary, ...structs),
+                {
+                    minLength,
+                    maxLength,
+                },
+            ),
+        });
+    }
+
+    /**
+     * Generates an arbitrary method signature that always starts with `this`.
+     */
+    public generateMethod(): fc.Arbitrary<FunctionType> {
+        return this.generateFun().map((funType) => ({
+            kind: "function",
+            signature: [
+                { kind: "util", type: UtilType.This },
+                ...funType.signature,
+            ],
+        }));
+    }
+
+    /**
+     * Generates an arbitrary struct or message signature.
+     */
+    public generateStruct(isMessage: boolean): fc.Arbitrary<Type> {
+        const structName = createSample(
+            generateName(this.scope, /*shadowing=*/ true, /*isType=*/ true),
+        );
+
+        // NOTE: It doesn't support nested structs/messages as they are not
+        const fields = fc
+            .array(
+                fc.record<StructField>({
+                    name: generateName(this.scope),
+                    type: this.stdlibArbitrary,
+                    default: fc.constantFrom(undefined),
+                }),
+                { minLength: 1, maxLength: 4 },
+            )
+            .filter((generatedFields) =>
+                generatedFields.every(
+                    (item, index) =>
+                        generatedFields.findIndex(
+                            (other) => other.name === item.name,
+                        ) === index,
+                ),
+            );
+        if (isMessage) {
+            return fc.record<Type>({
+                kind: fc.constant("message"),
+                name: fc.constant(structName),
+                fields: fields,
+            }) as fc.Arbitrary<Type>;
+        } else {
+            return fc.record<Type>({
+                kind: fc.constant("struct"),
+                name: fc.constant(structName),
+                fields: fields,
+            }) as fc.Arbitrary<Type>;
+        }
+    }
+
+    /**
+     * Returns arbitraries to generate structs available in the program scope.
+     */
+    private getStructs(): fc.Arbitrary<Type>[] {
+        const structs = this.scope.getItemsRecursive("struct");
+        if (structs.length === 0) {
+            return [];
+        }
+        return structs.map((s) => fc.constantFrom(s.type));
+    }
+}
+
+/**
+ * Creates an arbitrary function or method signature that returns the given type.
+ */
+export function makeFunctionTy(
+    kind: "function" | "method",
+    returnTy: Type,
+    minArgs = 1,
+    maxArgs = 3,
+): FunctionType {
+    const argsLength = randomInt(minArgs, maxArgs);
+    const thisArg: Type[] =
+        kind === "method" ? [{ kind: "util", type: UtilType.This }] : [];
+    const args: Type[] = Array.from({ length: argsLength }, () => {
+        const idx = randomInt(0, SUPPORTED_STDLIB_TYPES.length - 1);
+        const selectedType = SUPPORTED_STDLIB_TYPES[idx];
+        if (typeof selectedType === "undefined") {
+            throw new Error("Unexpected 'undefined'");
+        }
+        return { kind: "stdlib", type: selectedType };
+    });
+    return { kind: "function", signature: [...thisArg, ...args, returnTy] };
+}
+
+export function isUnit(ty: Type): boolean {
+    return ty.kind === "util" && ty.type === "Unit";
+}
+
+export function isThis(ty: Type): boolean {
+    return ty.kind === "util" && ty.type === "This";
+}
+
+/**
+ * An heuristic that replicates the `resolvePartialFields` logic in the compiler in order to
+ * detect if the message ought to be wrapped in `bounced<>`.
+ */
+export function isBouncedMessage(ty: Type): boolean {
+    if (ty.kind !== "message") {
+        throwTyError(ty);
+    }
+    for (const f of ty.fields) {
+        if (!(f.type.kind === "stdlib" && f.type.type === StdlibType.Bool)) {
+            return true; // too big; must be wrapped
+        }
+    }
+    return false;
+}
diff --git a/src/test/fuzzer/src/util.ts b/src/test/fuzzer/src/util.ts
new file mode 100644
index 0000000000..3b0f658813
--- /dev/null
+++ b/src/test/fuzzer/src/util.ts
@@ -0,0 +1,587 @@
+import os from "os";
+import { createNodeFileSystem } from "@/vfs/createNodeFileSystem";
+import type { VirtualFileSystem } from "@/vfs/VirtualFileSystem";
+import { mkdtemp } from "fs/promises";
+import * as fs from "fs";
+import * as path from "path";
+import fc from "fast-check";
+
+import type { NamedScopeItemKind, Scope } from "@/test/fuzzer/src/scope";
+import { GlobalContext } from "@/test/fuzzer/src/context";
+import type { Type } from "@/test/fuzzer/src/types";
+import type * as Ast from "@/ast/ast";
+import { getSrcInfo } from "@/grammar/src-info";
+import type { FactoryAst } from "@/ast/ast-helpers";
+import { idText } from "@/ast/ast-helpers";
+import { CompilerContext } from "@/context/context";
+import { getParser } from "@/grammar";
+import { createVirtualFileSystem } from "@/vfs/createVirtualFileSystem";
+import { files } from "@/stdlib/stdlib";
+import { resolveImports } from "@/imports/resolveImports";
+import { getRawAST, openContext, parseModules } from "@/context/store";
+import type { MakeAstFactory } from "@/ast/generated/make-factory";
+import { precompile } from "@/pipeline/precompile";
+import { getAllTypes } from "@/types/resolveDescriptors";
+import { topSortContracts } from "@/pipeline/utils";
+import { featureEnable } from "@/config/features";
+import { posixNormalize } from "@/utils/filePath";
+import { funcCompile } from "@/func/funcCompile";
+import { Logger } from "@/context/logger";
+import { beginCell, Cell, contractAddress, TupleBuilder } from "@ton/core";
+import type {
+    StateInit,
+    Address,
+    Contract,
+    ContractProvider,
+    Sender,
+    TupleItem,
+} from "@ton/core";
+import type { Blockchain, SandboxContract } from "@ton/sandbox";
+import { compileTact } from "@/pipeline/compile";
+import { enableFeatures, type BuildContext } from "@/pipeline/build";
+
+export const VALID_ID = /^[a-zA-Z_]+[a-zA-Z_0-9]$/;
+export const VALID_TYPE_ID = /^[A-Z]+[a-zA-Z_0-9]$/;
+
+/**
+ * Creates a temp node file system to use inside a property.
+ */
+export async function withNodeFS(f: (vfs: VirtualFileSystem) => Promise<void>) {
+    const tempDir = await mkdtemp(
+        path.join(GlobalContext.config.compileDir, "tact-check-"),
+    );
+    const vfs = createNodeFileSystem(tempDir, false);
+    try {
+        await f(vfs);
+    } finally {
+        if (GlobalContext.config.compileDir == os.tmpdir()) {
+            await fs.promises.rm(tempDir, { recursive: true });
+        }
+    }
+}
+
+/**
+ * Creates a new property that executes additional logic implemented in tact-check.
+ */
+export function createProperty<Ts extends [unknown, ...unknown[]]>(
+    ...args: [
+        ...arbitraries: { [K in keyof Ts]: fc.Arbitrary<Ts[K]> },
+        predicate: (...args: Ts) => boolean | void, // eslint-disable-line @typescript-eslint/no-invalid-void-type
+    ]
+): fc.IPropertyWithHooks<Ts> {
+    const arbitraries = args.slice(0, -1) as unknown as {
+        [K in keyof Ts]: fc.Arbitrary<Ts[K]>;
+    };
+    const originalPredicate = args[args.length - 1] as (
+        ...args: Ts
+    ) => boolean | void; // eslint-disable-line @typescript-eslint/no-invalid-void-type
+    // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
+    const enhancedPredicate = (...args: Ts): boolean | void => {
+        args.forEach((arg) => {
+            GlobalContext.printSample(arg as Ast.AstNode);
+        });
+        return originalPredicate(...args);
+    };
+    return fc.property(...arbitraries, enhancedPredicate);
+}
+
+/**
+ * Create parameters for custom property checking.
+ */
+function makeParams<T>(
+    counterexamplePrinter: (generated: T) => string,
+    numRuns: number | undefined,
+): fc.Parameters<T> {
+    return {
+        numRuns: numRuns ?? GlobalContext.config.numRuns,
+        seed: GlobalContext.config.seed,
+        reporter(out) {
+            if (out.failed) {
+                let errorSufffix = "";
+                if (out.counterexample !== null) {
+                    errorSufffix = counterexamplePrinter(out.counterexample);
+                }
+                throw new Error(fc.defaultReportMessage(out) + errorSufffix);
+            }
+        },
+    };
+}
+/**
+ * Create parameters for custom property checking.
+ */
+function makeAsyncParams<T>(
+    counterexamplePrinter: (generated: T) => string,
+    numRuns: number | undefined,
+): fc.Parameters<T> {
+    return {
+        numRuns: numRuns ?? GlobalContext.config.numRuns,
+        seed: GlobalContext.config.seed,
+        reporter: undefined,
+        async asyncReporter(out) {
+            if (out.failed) {
+                let errorSuffix = "";
+                if (out.counterexample !== null) {
+                    errorSuffix = counterexamplePrinter(out.counterexample);
+                }
+                throw new Error(
+                    (await fc.asyncDefaultReportMessage(out)) + errorSuffix,
+                );
+            }
+        },
+    };
+}
+
+/**
+ * Checks the given property enhancing `fc.assert` with additional functionality.
+ */
+export function checkProperty<T>(
+    property: fc.IPropertyWithHooks<T>,
+    counterexamplePrinter: (generated: T) => string,
+    numRuns: number | undefined = undefined,
+) {
+    fc.assert(property, makeParams(counterexamplePrinter, numRuns));
+}
+
+/**
+ * Checks the given async property enhancing `fc.assert` with additional functionality.
+ */
+export async function checkAsyncProperty<T>(
+    property: fc.IAsyncPropertyWithHooks<T>,
+    counterexamplePrinter: (generated: T) => string,
+    numRuns: number | undefined = undefined,
+) {
+    await fc.assert(property, makeAsyncParams(counterexamplePrinter, numRuns));
+}
+
+export function astNodeCounterexamplePrinter(
+    generated: Ast.AstNode | Ast.AstNode[],
+) {
+    const node = "kind" in generated ? generated : generated[0]!;
+    return `\n-----\nGenerated ${node.kind}:\n${GlobalContext.format(node)}\n-----\n`;
+}
+
+/**
+ * Creates a single fast-check sample with respect to the current global configuration.
+ * @param gen The arbitrary generator used to create the sample.
+ * @throws If the arbitrary cannot generate any elements.
+ */
+export function createSample<T>(gen: fc.Arbitrary<T>): T {
+    const result = fc.sample(gen, {
+        seed: GlobalContext.config.seed,
+        numRuns: 1,
+    })[0];
+    if (typeof result === "undefined") {
+        throw new Error("Unexpected 'undefined'");
+    }
+    return result;
+}
+
+/**
+ * Generates an array of items using the provided generator function, with a length determined by a sampled range.
+ * @param fn The generator function to create items.
+ * @param minLength The minimum length of the array.
+ * @param maxLength The maximum length of the array.
+ * @returns An array of generated items.
+ */
+export function createSamplesArray<T>(
+    fn: () => T,
+    minLength: number,
+    maxLength: number,
+): T[] {
+    const length = createSample(fc.integer({ min: minLength, max: maxLength }));
+    return Array.from({ length }, () => fn());
+}
+
+/**
+ * Generates a new valid identifier with a name unique within the current scope and with unique id.
+ * @param shadowing Allow shadowing (using names available in parent scopes)
+ */
+export function generateName(
+    scope: Scope,
+    shadowing: boolean = true,
+    isType: boolean = false,
+): fc.Arbitrary<string> {
+    const availableNames = shadowing
+        ? scope.getAllNames()
+        : scope.getAllNamesRecursive();
+
+    return fc
+        .stringMatching(isType ? VALID_TYPE_ID : VALID_ID)
+        .filter((generatedName) => {
+            if (availableNames.find(([name, _]) => name == generatedName)) {
+                return false;
+            }
+            return true;
+        });
+}
+
+/**
+ * Generates Ast.Id from string name and with new id.
+ */
+export function generateAstIdFromName(name: string): Ast.Id {
+    return GlobalContext.makeF.makeDummyId(name);
+}
+
+/**
+ * Generates Ast.Id.
+ * @param scope Current scope, from which Ast.Id.text will be generated.
+ * @param shadowing Allow shadowing (using names available in parent scopes)
+ */
+export function generateAstId(
+    scope: Scope,
+    shadowing: boolean = true,
+    isType: boolean = false,
+): fc.Arbitrary<Ast.Id> {
+    return generateName(scope, shadowing, isType).map((s) =>
+        GlobalContext.makeF.makeDummyId(s),
+    );
+}
+
+/**
+ * Chooses an arbitrary identifier available in the current scope.
+ * @returns Chosen identifier or `undefined` if there are no identifiers available with the given kind/type.
+ */
+export function choose(
+    scope: Scope,
+    kind: NamedScopeItemKind,
+    ty: Type,
+): string | undefined {
+    const availableNames = scope.getNamesRecursive(kind, ty);
+    if (availableNames.length === 0) {
+        return undefined;
+    }
+    return createSample(fc.constantFrom(...availableNames));
+}
+
+/**
+ * Randomly chooses a boolean value using wrt to SEED.
+ */
+export function randomBool(): boolean {
+    return createSample(fc.boolean());
+}
+
+/**
+ * Randomly chooses an integer value using wrt to SEED.
+ */
+export function randomInt(min: number, max: number): number {
+    return createSample(fc.integer({ min, max }));
+}
+
+/**
+ * Chooses a random list element wrt to SEED.
+ */
+export function randomElement<T>(list: T[]): T {
+    if (list.length === 0) {
+        throw new Error("Empty list");
+    }
+    if (list.length === 1) {
+        const result = list[0];
+        if (typeof result === "undefined") {
+            throw new Error("Unexpected 'undefined'");
+        }
+        return result;
+    }
+    const result = list[randomInt(1, list.length - 1)];
+    if (typeof result === "undefined") {
+        throw new Error("Unexpected 'undefined'");
+    }
+    return result;
+}
+
+export function packArbitraries<T>(
+    arbs?: fc.Arbitrary<T>[],
+): fc.Arbitrary<T[]> {
+    return arbs ? fc.tuple(...(arbs as [fc.Arbitrary<T>])) : fc.constant([]);
+}
+
+export const dummySrcInfoPrintable = getSrcInfo(" ", 0, 0, null, "user");
+
+export function stringify(obj: unknown, space: number): string {
+    return JSON.stringify(
+        obj,
+        (_, value) => (typeof value === "bigint" ? value.toString() : value),
+        space,
+    );
+}
+
+/***
+ * Utility functions for compiling contracts and sandbox them
+ */
+
+export function parseStandardLibrary(astF: FactoryAst): CompilerContext {
+    let ctx = new CompilerContext();
+    const parser = getParser(astF);
+    const fileSystem = {
+        [`contracts/empty.tact`]: "",
+    };
+    const project = createVirtualFileSystem("/", fileSystem, false);
+    const stdlib = createVirtualFileSystem("@stdlib", files);
+
+    const imported = resolveImports({
+        entrypoint: "contracts/empty.tact",
+        project,
+        stdlib,
+        parser,
+    });
+
+    // Add information about all the source code entries to the context
+    ctx = openContext(
+        ctx,
+        imported.tact,
+        imported.func,
+        parseModules(imported.tact, getParser(astF)),
+    );
+
+    return ctx;
+}
+
+export function filterStdlib(
+    ctx: CompilerContext,
+    mF: MakeAstFactory,
+    names: Set<string>,
+): CustomStdlib {
+    const result: Ast.ModuleItem[] = [];
+
+    const rawAst = getRawAST(ctx);
+
+    for (const c of rawAst.constants) {
+        if (names.has(idText(c.name))) {
+            result.push(c);
+        }
+    }
+
+    for (const f of rawAst.functions) {
+        if (names.has(idText(f.name))) {
+            result.push(f);
+        }
+    }
+
+    for (const t of rawAst.types) {
+        if (names.has(idText(t.name))) {
+            result.push(t);
+        }
+    }
+
+    const customTactStdlib = mF.makeModule([], result);
+    //const stdlib_fc = fs
+    //    .readFileSync(path.join(__dirname, "minimal-fc-stdlib", "stdlib.fc"))
+    //    .toString("base64");
+
+    return {
+        modules: [customTactStdlib],
+        stdlib_fc: "",
+        stdlib_ex_fc: "",
+    };
+}
+
+export type CustomStdlib = {
+    // Parsed modules of Tact stdlib
+    modules: Ast.Module[];
+    // Contents of the stdlib.fc file
+    stdlib_fc: string;
+    // Contents of the stdlib_ex.fc file
+    stdlib_ex_fc: string;
+};
+
+// If flag useCustomStdlib is false, it will parse the entire stdlib. Otherwise,
+// it will use the provided data in CustomStdlib.
+export async function buildModule(
+    astF: FactoryAst,
+    module: Ast.Module,
+    customStdlib: CustomStdlib,
+    blockchain: Blockchain,
+): Promise<Map<string, SandboxContract<ProxyContract>>> {
+    // We need an entrypoint for precompile, even if it is empty
+    const fileSystem = {
+        [`contracts/empty.tact`]: "",
+    };
+    const minimalStdlib = {
+        // Needed by precompile, but we set its contents to be empty
+        ["std/stdlib.tact"]: "",
+        // These two func files are needed during tvm compilation
+        ["stdlib_ex.fc"]: customStdlib.stdlib_ex_fc,
+        ["stdlib.fc"]: customStdlib.stdlib_fc,
+    };
+
+    const project = createVirtualFileSystem("/", fileSystem, false);
+    const stdlib = createVirtualFileSystem("@stdlib", minimalStdlib);
+
+    const config = {
+        name: "test",
+        path: "contracts/empty.tact",
+        output: ".",
+        options: {
+            debug: true,
+            external: true,
+            ipfsAbiGetter: false,
+            interfacesGetter: false,
+            safety: {
+                nullChecks: true,
+            },
+        },
+    };
+
+    const contractsToTest: Map<
+        string,
+        SandboxContract<ProxyContract>
+    > = new Map();
+
+    const logger = new Logger();
+
+    let ctx = new CompilerContext();
+    ctx = enableFeatures(ctx, logger, config);
+
+    ctx = precompile(ctx, project, stdlib, config.path, [
+        module,
+        ...customStdlib.modules,
+    ]);
+
+    const built: Record<
+        string,
+        | {
+              codeBoc: Buffer;
+              abi: string;
+          }
+        | undefined
+    > = {};
+
+    const compilerInfo: string = JSON.stringify({
+        entrypoint: posixNormalize(config.path),
+        options: config.options,
+    });
+
+    const bCtx: BuildContext = {
+        config,
+        logger,
+        project,
+        stdlib,
+        compilerInfo,
+        ctx,
+        built: {},
+        errorMessages: [],
+    };
+
+    const allContracts = getAllTypes(ctx).filter((v) => v.kind === "contract");
+
+    // Sort contracts in topological order
+    // If a cycle is found, return undefined
+    const sortedContracts = topSortContracts(allContracts);
+    if (sortedContracts !== undefined) {
+        ctx = featureEnable(ctx, "optimizedChildCode");
+    }
+    for (const contract of sortedContracts ?? allContracts) {
+        const contractName = contract.name;
+
+        // Compiling contract to func
+        const res = await compileTact(bCtx, contractName);
+        if (!res) {
+            throw new Error(
+                "Tact compilation failed. " +
+                    bCtx.errorMessages.map((error) => error.message).join("\n"),
+            );
+        }
+        const codeEntrypoint = res.entrypointPath;
+
+        // Compiling contract to TVM
+        const stdlibPath = stdlib.resolve("stdlib.fc");
+        //const stdlibCode = stdlib.readFile(stdlibPath).toString();
+        const stdlibExPath = stdlib.resolve("stdlib_ex.fc");
+        //const stdlibExCode = stdlib.readFile(stdlibExPath).toString();
+
+        process.env.USE_NATIVE = "true";
+        process.env.FC_STDLIB_PATH = path.join(
+            __dirname,
+            "/minimal-fc-stdlib/",
+        );
+        process.env.FUNC_FIFT_COMPILER_PATH = path.join(
+            __dirname,
+            "/minimal-fc-stdlib/funcplusfift",
+        );
+        process.env.FIFT_LIBS_PATH = path.join(
+            __dirname,
+            "/minimal-fc-stdlib/fift-lib/",
+        );
+
+        const contractFilePath = path.join(
+            __dirname,
+            "/minimal-fc-stdlib/",
+            codeEntrypoint,
+        );
+
+        fs.writeFileSync(contractFilePath, res.funcSource.content);
+
+        const c = await funcCompile({
+            entries: [stdlibPath, stdlibExPath, contractFilePath],
+            sources: [],
+            logger,
+        });
+
+        if (!c.ok) {
+            throw new Error(c.log);
+        }
+
+        // Add to built map
+        built[contractName] = {
+            codeBoc: c.output,
+            abi: "",
+        };
+
+        contractsToTest.set(
+            contractName,
+            blockchain.openContract(
+                new ProxyContract(getContractStateInit(c.output)),
+            ),
+        );
+    }
+
+    return contractsToTest;
+}
+
+function getContractStateInit(contractCode: Buffer): StateInit {
+    const data = beginCell().storeUint(0, 1).endCell();
+    const code = Cell.fromBoc(contractCode)[0];
+    if (typeof code === "undefined") {
+        throw new Error("Code cell expected");
+    }
+    return { code, data };
+}
+
+export class ProxyContract implements Contract {
+    address: Address;
+    init: StateInit;
+
+    constructor(stateInit: StateInit) {
+        this.address = contractAddress(0, stateInit);
+        this.init = stateInit;
+    }
+
+    async send(
+        provider: ContractProvider,
+        via: Sender,
+        args: { value: bigint; bounce?: boolean | null | undefined },
+        body?: Cell,
+    ) {
+        await provider.internal(via, { ...args, body: body });
+    }
+
+    async getInt(provider: ContractProvider) {
+        const builder = new TupleBuilder();
+        const result = (await provider.get("getInt", builder.build())).stack;
+        return result.readBigNumber();
+    }
+
+    async getIndexed(provider: ContractProvider, index: number) {
+        const builder = new TupleBuilder();
+        const result = (await provider.get(`getInt${index}`, builder.build()))
+            .stack;
+        return result.readBigNumber();
+    }
+
+    async getGeneric(
+        provider: ContractProvider,
+        getterName: string,
+        params: TupleItem[],
+    ) {
+        return (await provider.get(getterName, params)).stack;
+    }
+}
diff --git a/src/test/fuzzer/test/compilation.spec.ts b/src/test/fuzzer/test/compilation.spec.ts
new file mode 100644
index 0000000000..974f26a7ba
--- /dev/null
+++ b/src/test/fuzzer/test/compilation.spec.ts
@@ -0,0 +1,137 @@
+import { funcCompile } from "@/func/funcCompile";
+import { posixNormalize } from "@/utils/filePath";
+import type * as Ast from "@/ast/ast";
+import { writeFileSync } from "fs";
+import * as path from "path";
+import fc from "fast-check";
+
+import { Program } from "@/test/fuzzer/src/generators";
+import { StdlibCode, StdlibPath } from "@/test/fuzzer/src/stdlib";
+import {
+    withNodeFS,
+    checkAsyncProperty,
+    astNodeCounterexamplePrinter,
+} from "@/test/fuzzer/src/util";
+import {
+    compile,
+    precompile,
+    createContext,
+    enableFeatures,
+} from "@/test/fuzzer/test/testUtils";
+import { GlobalContext } from "@/test/fuzzer/src/context";
+import { getAstFactory } from "@/ast/ast-helpers";
+
+function getContract(program: Ast.Module): Ast.Contract | undefined {
+    for (const entry of program.items) {
+        if (entry.kind === "contract") {
+            return entry;
+        }
+    }
+    return undefined;
+}
+
+async function compileProgram(program: Ast.Module) {
+    throw new Error("Deprecated function");
+    // await withNodeFS(async (vfs) => {
+    //     const factoryAst = getAstFactory();
+    //     let ctx = createContext(program);
+    //     ctx = enableFeatures(ctx, "external");
+    //     ctx = precompile(ctx, factoryAst);
+    //     const compilationOutput = vfs.root;
+
+    //     const contract = getContract(program)!;
+
+    //     // Save the generated contract to a file
+    //     const contractCode = GlobalContext.format(contract, "ast");
+    //     writeFileSync(
+    //         path.join(compilationOutput, "contract.tact"),
+    //         contractCode,
+    //     );
+
+    //     // Compile contracts to FunC
+    //     const res = await compile(ctx, contract.name.text);
+    //     for (const files of res.output.files) {
+    //         const ffc = vfs.resolve(compilationOutput, files.name);
+    //         vfs.writeFile(ffc, files.code);
+    //     }
+
+    //     // Process compilation output
+    //     const codeFc = res.output.files.map((v) => ({
+    //         path: posixNormalize(vfs.resolve(compilationOutput, v.name)),
+    //         content: v.code,
+    //     }));
+    //     const codeEntrypoint = res.output.entrypoint;
+
+    //     // Compile the resulted FunC code
+    //     // NOTE: We intentionally disabled stdlibEx, since the generated
+    //     // contracts currently don't use it.
+    //     const c = await funcCompile({
+    //         entries: [
+    //             StdlibPath,
+    //             // stdlibExPath,
+    //             posixNormalize(vfs.resolve(compilationOutput, codeEntrypoint)),
+    //         ],
+    //         sources: [
+    //             { path: StdlibPath, content: StdlibCode },
+    //             // {
+    //             //     path: stdlibExPath,
+    //             //     content: stdlibExCode,
+    //             // },
+    //             ...codeFc,
+    //         ],
+    //         logger: {
+    //             info: (_) => {},
+    //             debug: (_) => {},
+    //             warn: (_) => {},
+    //             error: (_) => {},
+    //         },
+    //     });
+    //     try {
+    //         expect(c.ok).toBeTruthy();
+    //     } catch (_error) {
+    //         throw new Error(`FunC compilation failed:\n${c.log}`);
+    //     }
+
+    //     GlobalContext.resetDepth();
+    // });
+}
+
+describe("properties", () => {
+    it(
+        "compiles contracts",
+        async () => {
+            // The generated AST is compiled once on compilation tests.
+            // This approach is used to speed-up testing, since non-structural changes
+            // are not significant for this case.
+            //
+            // Instead, the original NUM_RUNS option is used to generate the requested number of
+            // programs with a different structure.
+            const numRuns = GlobalContext.config.numRuns;
+
+            const compileAndCheckProperty = async () => {
+                const property = fc.asyncProperty(
+                    new Program({ addStdlib: true }).generate(),
+                    compileProgram,
+                );
+                await checkAsyncProperty(
+                    property,
+                    astNodeCounterexamplePrinter,
+                    /*numRuns=*/ 1,
+                );
+            };
+
+            if (numRuns === Infinity) {
+                for (;;) {
+                    await compileAndCheckProperty();
+                }
+            } else {
+                await Promise.all(
+                    Array.from({ length: numRuns }).map(async () => {
+                        await compileAndCheckProperty();
+                    }),
+                );
+            }
+        },
+        /*timeout_ms=*/ 60 * 60 * 1000 /*1hr*/,
+    );
+});
diff --git a/src/test/fuzzer/test/expressions/expression-stats.ts b/src/test/fuzzer/test/expressions/expression-stats.ts
new file mode 100644
index 0000000000..58c62b9b93
--- /dev/null
+++ b/src/test/fuzzer/test/expressions/expression-stats.ts
@@ -0,0 +1,272 @@
+import type * as Ast from "@/ast/ast";
+import * as fc from "fast-check";
+import * as fs from "fs";
+import { Scope } from "@/test/fuzzer/src/scope";
+import { SUPPORTED_STDLIB_TYPES } from "@/test/fuzzer/src/types";
+import type { Type } from "@/test/fuzzer/src/types";
+import { Expression } from "@/test/fuzzer/src/generators";
+import path from "path";
+
+/**
+ * AST utility functions
+ */
+
+function getHeight(tree: Ast.Expression): number {
+    switch (tree.kind) {
+        case "address":
+        case "boolean":
+        case "cell":
+        case "id":
+        case "null":
+        case "number":
+        case "string":
+        case "slice":
+        case "code_of":
+            return 0;
+        case "init_of": {
+            const children = tree.args.map((arg) => getHeight(arg));
+            return children.length === 0 ? 0 : Math.max(...children) + 1;
+        }
+        case "field_access":
+            return getHeight(tree.aggregate) + 1;
+        case "conditional":
+            return (
+                Math.max(
+                    getHeight(tree.condition),
+                    getHeight(tree.thenBranch),
+                    getHeight(tree.elseBranch),
+                ) + 1
+            );
+        case "method_call":
+            return (
+                Math.max(
+                    getHeight(tree.self),
+                    ...tree.args.map((arg) => getHeight(arg)),
+                ) + 1
+            );
+        case "static_call": {
+            const children = tree.args.map((arg) => getHeight(arg));
+            return children.length === 0 ? 0 : Math.max(...children) + 1;
+        }
+        case "struct_instance": {
+            const children = tree.args.map((init) =>
+                getHeight(init.initializer),
+            );
+            return children.length === 0 ? 0 : Math.max(...children) + 1;
+        }
+        case "struct_value": {
+            const children = tree.args.map((init) =>
+                getHeight(init.initializer),
+            );
+            return children.length === 0 ? 0 : Math.max(...children) + 1;
+        }
+        case "op_unary":
+            return getHeight(tree.operand) + 1;
+        case "op_binary":
+            return Math.max(getHeight(tree.left), getHeight(tree.right)) + 1;
+    }
+}
+
+function countNodes(tree: Ast.Expression): number {
+    switch (tree.kind) {
+        case "address":
+        case "boolean":
+        case "cell":
+        case "id":
+        case "null":
+        case "number":
+        case "string":
+        case "slice":
+        case "code_of":
+            return 1;
+        case "init_of":
+            return sum(tree.args.map((arg) => countNodes(arg))) + 1;
+        case "field_access":
+            return countNodes(tree.aggregate) + 1;
+        case "conditional":
+            return (
+                countNodes(tree.condition) +
+                countNodes(tree.thenBranch) +
+                countNodes(tree.elseBranch) +
+                1
+            );
+        case "method_call":
+            return (
+                countNodes(tree.self) +
+                sum(tree.args.map((arg) => countNodes(arg))) +
+                1
+            );
+        case "static_call":
+            return sum(tree.args.map((arg) => countNodes(arg))) + 1;
+        case "struct_instance":
+            return (
+                sum(tree.args.map((init) => countNodes(init.initializer))) + 1
+            );
+        case "struct_value":
+            return (
+                sum(tree.args.map((init) => countNodes(init.initializer))) + 1
+            );
+        case "op_unary":
+            return countNodes(tree.operand) + 1;
+        case "op_binary":
+            return countNodes(tree.left) + countNodes(tree.right) + 1;
+    }
+}
+
+function sum(items: number[]): number {
+    return items.reduce((prev, curr) => prev + curr, 0);
+}
+
+function preorderTraversal(tree: Ast.Expression, accumulator: string[]) {
+    switch (tree.kind) {
+        case "address":
+        case "boolean":
+        case "cell":
+        case "id":
+        case "null":
+        case "number":
+        case "string":
+        case "slice":
+        case "code_of": {
+            accumulator.push(tree.kind);
+            break;
+        }
+        case "init_of": {
+            accumulator.push(tree.kind + "_" + tree.contract.text);
+            tree.args.forEach((arg) => {
+                preorderTraversal(arg, accumulator);
+            });
+            break;
+        }
+        case "field_access": {
+            accumulator.push(tree.kind);
+            preorderTraversal(tree.aggregate, accumulator);
+            break;
+        }
+        case "conditional": {
+            accumulator.push(tree.kind);
+            preorderTraversal(tree.condition, accumulator);
+            preorderTraversal(tree.thenBranch, accumulator);
+            preorderTraversal(tree.elseBranch, accumulator);
+            break;
+        }
+        case "method_call": {
+            accumulator.push(tree.kind + "_" + tree.method.text);
+            preorderTraversal(tree.self, accumulator);
+            tree.args.forEach((arg) => {
+                preorderTraversal(arg, accumulator);
+            });
+            break;
+        }
+        case "static_call": {
+            accumulator.push(tree.kind + "_" + tree.function.text);
+            tree.args.forEach((arg) => {
+                preorderTraversal(arg, accumulator);
+            });
+            break;
+        }
+        case "struct_instance": {
+            accumulator.push(
+                tree.kind + "_" + tree.args.length + "_" + tree.type.text,
+            );
+            tree.args.forEach((arg) => {
+                preorderTraversal(arg.initializer, accumulator);
+            });
+            break;
+        }
+        case "struct_value": {
+            accumulator.push(
+                tree.kind + "_" + tree.args.length + "_" + tree.type.text,
+            );
+            tree.args.forEach((arg) => {
+                preorderTraversal(arg.initializer, accumulator);
+            });
+            break;
+        }
+        case "op_unary": {
+            accumulator.push(tree.kind + "_" + tree.op);
+            preorderTraversal(tree.operand, accumulator);
+            break;
+        }
+        case "op_binary": {
+            accumulator.push(tree.kind + "_" + tree.op);
+            preorderTraversal(tree.left, accumulator);
+            preorderTraversal(tree.right, accumulator);
+            break;
+        }
+    }
+}
+
+function incrementKey<K>(
+    key: K,
+    map: Map<K, TreeStats>,
+    height: number,
+    size: number,
+) {
+    const value = map.get(key);
+    if (value) {
+        const newVal = {
+            count: value.count + 1,
+            height: value.height,
+            size: value.size,
+        };
+        map.set(key, newVal);
+    } else {
+        map.set(key, {
+            count: 1,
+            height,
+            size,
+        });
+    }
+}
+
+type TreeStats = {
+    count: number;
+    height: number;
+    size: number;
+};
+
+function getRows(dist: Map<string, TreeStats>): string[] {
+    const rows: string[] = [];
+    for (const [tree, stats] of dist) {
+        rows.push(`${tree} ${stats.count} ${stats.height} ${stats.size}`);
+    }
+    return rows;
+}
+
+export function statistics(
+    gen: fc.Arbitrary<Ast.Expression>,
+    numberOfSamples: number,
+    fileName: string,
+) {
+    const trees = fc.sample(gen, numberOfSamples);
+
+    const totalPreTraversals: Map<string, TreeStats> = new Map();
+
+    for (const tree of trees) {
+        const preTraversal: string[] = [];
+        preorderTraversal(tree, preTraversal);
+        const treeName = preTraversal.join("@");
+        const height = getHeight(tree);
+        const size = countNodes(tree);
+        incrementKey(treeName, totalPreTraversals, height, size);
+    }
+
+    fs.writeFileSync(
+        fileName,
+        `tree count height size\n${getRows(totalPreTraversals).join("\n")}`,
+    );
+}
+
+function main() {
+    const globalScope = new Scope("program", undefined);
+    const generator = fc
+        .constantFrom(...SUPPORTED_STDLIB_TYPES)
+        .chain((type) => {
+            const ty: Type = { kind: "stdlib", type };
+            return new Expression(globalScope, ty).generate();
+        });
+    statistics(generator, 50000, path.join(__dirname, "counts.txt"));
+}
+
+main();
diff --git a/src/test/fuzzer/test/expressions/expression.spec.ts b/src/test/fuzzer/test/expressions/expression.spec.ts
new file mode 100644
index 0000000000..8a5aabc444
--- /dev/null
+++ b/src/test/fuzzer/test/expressions/expression.spec.ts
@@ -0,0 +1,268 @@
+import type * as Ast from "@/ast/ast";
+import { CompilerContext } from "@/context/context";
+import { resolveExpression, getExpType } from "@/types/resolveExpression";
+import type { StatementContext } from "@/types/resolveStatements";
+import type { TypeRef } from "@/types/types";
+import assert from "assert";
+
+import { Expression } from "@/test/fuzzer/src/generators";
+import { Scope } from "@/test/fuzzer/src/scope";
+import { StdlibType, SUPPORTED_STDLIB_TYPES } from "@/test/fuzzer/src/types";
+import type { Type } from "@/test/fuzzer/src/types";
+import {
+    createProperty,
+    checkProperty,
+    dummySrcInfoPrintable,
+    checkAsyncProperty,
+    astNodeCounterexamplePrinter,
+    packArbitraries,
+} from "@/test/fuzzer/src/util";
+import fc from "fast-check";
+import {
+    initializeGenerator,
+    NonTerminal,
+    Terminal,
+} from "../../src/generators/uniform-expr-gen";
+import type { NonTerminalEnum } from "../../src/generators/uniform-expr-gen";
+import {
+    bindingsAndExpressionPrtinter,
+    compileExpression,
+    interpretExpression,
+    saveExpressionTest,
+    setupEnvironment,
+} from "./utils";
+import type { ExpressionTestingEnvironment } from "./utils";
+import { Let } from "@/test/fuzzer/src/generators/statement";
+import { GlobalContext } from "@/test/fuzzer/src/context";
+
+function emptyStatementContext(): StatementContext {
+    return {
+        root: dummySrcInfoPrintable,
+        returns: { kind: "void" },
+        vars: new Map<string, TypeRef>(),
+        requiredFields: [],
+        funName: null,
+    };
+}
+
+function setupContexts(): [CompilerContext, StatementContext] {
+    const ctx: CompilerContext = new CompilerContext();
+    const sctx = emptyStatementContext();
+    return [ctx, sctx];
+}
+
+// describe("generation properties", () => {
+//     it("generates well-typed expressions", () => {
+//         const results = setupContexts();
+//         let compilerCtx = results[0];
+//         const stmtCtx = results[1];
+//         const globalScope = new Scope("program", undefined);
+//         for (const type of SUPPORTED_STDLIB_TYPES) {
+//             const ty: Type = { kind: "stdlib", type };
+//             // NOTE: This test checks only pure expressions, without introducing new
+//             // entries to any scopes.
+//             const exprGen = new Expression(globalScope, ty).generate();
+//             const property = createProperty(exprGen, (expr) => {
+//                 compilerCtx = resolveExpression(expr, stmtCtx, compilerCtx);
+//                 const resolvedTy = getExpType(compilerCtx, expr);
+//                 if (resolvedTy.kind == "ref") {
+//                     assert.strictEqual(
+//                         resolvedTy.name,
+//                         ty.type,
+//                         `The resolved type ${resolvedTy.name} does not match the expected type ${ty.type}`,
+//                     );
+//                 } else {
+//                     assert.fail(`Unexpected type: ${resolvedTy.kind}`);
+//                 }
+//             });
+//             checkProperty(property, astNodeCounterexamplePrinter);
+//         }
+//     });
+// });
+
+describe("evaluation properties", () => {
+    let expressionTestingEnvironment: ExpressionTestingEnvironment;
+
+    beforeAll(async () => {
+        expressionTestingEnvironment = await setupEnvironment();
+    });
+
+    afterAll(() => {
+        expressionTestingEnvironment.outputStream.close();
+    });
+
+    test(
+        "compiler and interpreter evaluate generated expressions equally",
+        async () => {
+            /*const expressionGenerationIds: Map<AllowedTypeEnum, string[]> =
+                new Map();
+            expressionGenerationIds.set(AllowedType.Int, ["int1"]);
+            expressionGenerationIds.set(AllowedType.OptInt, ["int_null"]);
+            expressionGenerationIds.set(AllowedType.Bool, ["bool1"]);
+            expressionGenerationIds.set(AllowedType.OptBool, ["bool_null"]);
+            expressionGenerationIds.set(AllowedType.Cell, ["cell1"]);
+            expressionGenerationIds.set(AllowedType.OptCell, ["cell_null"]);
+            expressionGenerationIds.set(AllowedType.Slice, ["slice1"]);
+            expressionGenerationIds.set(AllowedType.OptSlice, ["slice_null"]);
+            expressionGenerationIds.set(AllowedType.Address, ["address1"]);
+            expressionGenerationIds.set(AllowedType.OptAddress, [
+                "address_null",
+            ]);
+            expressionGenerationIds.set(AllowedType.String, ["string1"]);
+            expressionGenerationIds.set(AllowedType.OptString, ["string_null"]);
+            */
+
+            const globalScope = new Scope("block", undefined);
+            const initializerCtx = {
+                minSize: 1,
+                maxSize: 1,
+                useIdentifiers: false,
+                allowedNonTerminals: Object.values(NonTerminal),
+                allowedTerminals: Object.values(Terminal),
+            };
+            const bindingsGenerator = initializeGenerator(initializerCtx);
+
+            const expressionGenerationCtx = {
+                minSize: 1,
+                maxSize: 10,
+                useIdentifiers: true,
+                allowedNonTerminals: Object.values(NonTerminal),
+                allowedTerminals: Object.values(Terminal),
+            };
+            const exprGenerator = new Expression(
+                globalScope,
+                { kind: "stdlib", type: StdlibType.Int },
+                expressionGenerationCtx,
+            );
+
+            const property = fc.asyncProperty(
+                generateBindings(globalScope, bindingsGenerator),
+                exprGenerator.generate(),
+                async (bindings, expr) => {
+                    const compilationResult = await compileExpression(
+                        expressionTestingEnvironment,
+                        bindings,
+                        expr,
+                    );
+
+                    const interpretationResult = interpretExpression(
+                        expressionTestingEnvironment,
+                        bindings,
+                        expr,
+                    );
+                    if (
+                        (compilationResult instanceof Error &&
+                            interpretationResult instanceof BigInt) ||
+                        (interpretationResult instanceof Error &&
+                            compilationResult instanceof BigInt)
+                    ) {
+                        expect(compilationResult).toEqual(interpretationResult);
+                    } else if (
+                        compilationResult instanceof Error &&
+                        interpretationResult instanceof Error
+                    ) {
+                        saveExpressionTest(
+                            bindings,
+                            expr,
+                            compilationResult,
+                            interpretationResult,
+                            expressionTestingEnvironment.outputStream,
+                        );
+                    } else {
+                        expect(compilationResult).toBe(interpretationResult);
+                    }
+                },
+            );
+            await checkAsyncProperty(property, bindingsAndExpressionPrtinter);
+        },
+        60 * 1000, // 1 minute
+    );
+});
+
+function generateBindings(
+    scope: Scope,
+    bindingsGenerator: (
+        scope: Scope,
+        nonTerminal: NonTerminalEnum,
+    ) => fc.Arbitrary<Ast.Expression>,
+): fc.Arbitrary<Ast.StatementLet[]> {
+    // For each of the types, we create a let generator
+    const types: Type[] = [
+        { kind: "stdlib", type: StdlibType.Int },
+        { kind: "stdlib", type: StdlibType.Bool },
+        { kind: "stdlib", type: StdlibType.Address },
+        { kind: "stdlib", type: StdlibType.Cell },
+        { kind: "stdlib", type: StdlibType.Slice },
+        { kind: "stdlib", type: StdlibType.String },
+        { kind: "optional", type: { kind: "stdlib", type: StdlibType.Int } },
+        { kind: "optional", type: { kind: "stdlib", type: StdlibType.Bool } },
+        {
+            kind: "optional",
+            type: { kind: "stdlib", type: StdlibType.Address },
+        },
+        { kind: "optional", type: { kind: "stdlib", type: StdlibType.Cell } },
+        { kind: "optional", type: { kind: "stdlib", type: StdlibType.Slice } },
+        { kind: "optional", type: { kind: "stdlib", type: StdlibType.String } },
+    ];
+    const result: fc.Arbitrary<Ast.StatementLet>[] = [];
+
+    for (const ty of types) {
+        const genBuilder = new Let(
+            scope,
+            ty,
+            bindingsGenerator(scope, typeToNonTerminal(ty)),
+        );
+        scope.addNamed("let", genBuilder);
+        result.push(genBuilder.generate() as fc.Arbitrary<Ast.StatementLet>);
+
+        if (ty.kind === "optional") {
+            const genBuilder = new Let(
+                scope,
+                ty,
+                fc.constant(GlobalContext.makeF.makeDummyNull()),
+            );
+            scope.addNamed("let", genBuilder);
+            result.push(
+                genBuilder.generate() as fc.Arbitrary<Ast.StatementLet>,
+            );
+        }
+    }
+
+    return packArbitraries(result);
+}
+
+function typeToNonTerminal(ty: Type): NonTerminalEnum {
+    switch (ty.kind) {
+        case "optional": {
+            // Treat them as if they were non-optionals
+            return typeToNonTerminal(ty.type);
+        }
+        case "stdlib": {
+            switch (ty.type) {
+                case StdlibType.Int:
+                    return NonTerminal.Int;
+                case StdlibType.Address:
+                    return NonTerminal.Address;
+                case StdlibType.Bool:
+                    return NonTerminal.Bool;
+                case StdlibType.Cell:
+                    return NonTerminal.Cell;
+                case StdlibType.Slice:
+                    return NonTerminal.Slice;
+                case StdlibType.String:
+                    return NonTerminal.String;
+                case StdlibType.Builder:
+                    throw new Error("Not supported");
+                case StdlibType.StringBuilder:
+                    throw new Error("Not supported");
+            }
+            break;
+        }
+        case "function":
+        case "map":
+        case "message":
+        case "struct":
+        case "util":
+            throw new Error("Not supported.");
+    }
+}
diff --git a/src/test/fuzzer/test/expressions/utils.ts b/src/test/fuzzer/test/expressions/utils.ts
new file mode 100644
index 0000000000..b867af71a4
--- /dev/null
+++ b/src/test/fuzzer/test/expressions/utils.ts
@@ -0,0 +1,268 @@
+import type * as Ast from "@/ast/ast";
+import type { MakeAstFactory } from "@/ast/generated/make-factory";
+import { NonTerminal } from "../../src/generators/uniform-expr-gen";
+import { GlobalContext } from "../../src/context";
+import { Interpreter } from "@/optimizer/interpreter";
+import { CompilerContext } from "@/context/context";
+import type { AstUtil } from "@/ast/util";
+import { getAstUtil } from "@/ast/util";
+import type { FactoryAst } from "@/ast/ast-helpers";
+import type { CustomStdlib } from "../../src/util";
+import {
+    buildModule,
+    filterStdlib,
+    parseStandardLibrary,
+} from "../../src/util";
+import { Blockchain } from "@ton/sandbox";
+import type { Sender } from "@ton/core";
+import { toNano } from "@ton/core";
+import * as fs from "node:fs";
+
+export function bindingsAndExpressionPrtinter([bindings, expr]: [
+    Ast.StatementLet[],
+    Ast.Expression,
+]) {
+    return (
+        `\n-----\nGenerated bindings:\n` +
+        bindings.map((bind) => GlobalContext.format(bind)).join("\n") +
+        `\nGenerated expression:\n` +
+        GlobalContext.format(expr) +
+        `\n-----\n`
+    );
+}
+
+export function makeExpressionGetter(
+    makeF: MakeAstFactory,
+    getterName: string,
+    bindings: Ast.Statement[],
+    returnExpr: Ast.Expression,
+): Ast.FunctionDef {
+    return makeF.makeDummyFunctionDef(
+        [makeF.makeDummyFunctionAttributeGet(undefined)],
+        makeF.makeDummyId(getterName),
+        makeF.makeDummyTypeId("Int"),
+        [],
+        bindings.concat([makeF.makeDummyStatementReturn(returnExpr)]),
+    );
+}
+
+export function createModuleWithExpressionGetter(
+    makeF: MakeAstFactory,
+    contractName: string,
+    getterName: string,
+    bindings: Ast.Statement[],
+    returnExpr: Ast.Expression,
+): Ast.Module {
+    // throw new Error("createContract is not implemented yet"); // TODO: implement, probably should place this function in a different file
+    return makeF.makeModule(
+        [],
+        [
+            makeF.makeDummyContract(
+                makeF.makeDummyId(contractName),
+                [],
+                [],
+                [],
+                [makeExpressionGetter(makeF, getterName, bindings, returnExpr)],
+            ),
+        ],
+    );
+}
+
+export const initializersMapping = {
+    Int: NonTerminal.LiteralInt,
+    "Int?": NonTerminal.LiteralInt,
+    Bool: NonTerminal.LiteralBool,
+    "Bool?": NonTerminal.LiteralBool,
+    Cell: NonTerminal.LiteralCell,
+    "Cell?": NonTerminal.LiteralCell,
+    Address: NonTerminal.LiteralAddress,
+    "Address?": NonTerminal.LiteralAddress,
+    Slice: NonTerminal.LiteralSlice,
+    "Slice?": NonTerminal.LiteralSlice,
+    String: NonTerminal.LiteralString,
+    "String?": NonTerminal.LiteralString,
+} as const;
+
+export type ExpressionTestingEnvironment = {
+    astF: FactoryAst;
+    makeF: MakeAstFactory;
+    customStdlib: CustomStdlib;
+    blockchain: Blockchain;
+    emptyCompilerContext: CompilerContext;
+    astUtil: AstUtil;
+    sender: Sender;
+    contractNameToCompile: string;
+    outputStream: fs.WriteStream;
+};
+
+export async function setupEnvironment(): Promise<ExpressionTestingEnvironment> {
+    const astF = GlobalContext.astF;
+    const makeF = GlobalContext.makeF;
+    const customStdlib = filterStdlib(
+        parseStandardLibrary(astF),
+        makeF,
+        new Set([
+            "Int",
+            "Bool",
+            "Address",
+            "Cell",
+            "Context",
+            "Slice",
+            "Slice?",
+            "String",
+            "String?",
+            "StateInit",
+            "SendParameters",
+            "BaseTrait",
+            "SendDefaultMode",
+            "SendRemainingValue",
+            "SendIgnoreErrors",
+            "SendRemainingBalance",
+            "ReserveExact",
+            "sender",
+            "context",
+            "myBalance",
+            "nativeReserve",
+            "toString",
+            "StringBuilder",
+            //"contractAddress",
+            //"contractAddressExt",
+            //"storeUint",
+            //"storeInt",
+            //"contractHash",
+            //"newAddress",
+            //"beginCell",
+            //"endCell",
+            "send",
+            //"asSlice",
+            //"asAddressUnsafe",
+            //"beginParse",
+        ]),
+    );
+    const blockchain = await Blockchain.create();
+    const astUtil = getAstUtil(astF);
+    const sender = (await blockchain.treasury("treasury")).getSender();
+    return {
+        astF,
+        makeF,
+        customStdlib,
+        blockchain,
+        astUtil,
+        sender,
+        emptyCompilerContext: new CompilerContext(),
+        contractNameToCompile: "ExpressionContract",
+        outputStream: fs.createWriteStream("interesting-failing-tests.txt", {
+            flags: "a",
+        }),
+    };
+}
+
+export function interpretExpression(
+    { astUtil, emptyCompilerContext }: ExpressionTestingEnvironment,
+    bindings: Ast.StatementLet[],
+    expr: Ast.Expression,
+): bigint | Error {
+    try {
+        const interpreter = new Interpreter(astUtil, emptyCompilerContext);
+        bindings.forEach((bind) => {
+            interpreter.interpretStatement(bind);
+        });
+        const result = interpreter.interpretExpression(expr);
+        expect(result.kind).toBe("number");
+        return (result as Ast.Number).value;
+    } catch (e: any) {
+        return e as Error;
+    }
+}
+
+export async function compileExpression(
+    {
+        makeF,
+        contractNameToCompile,
+        astF,
+        customStdlib,
+        blockchain,
+        sender,
+    }: ExpressionTestingEnvironment,
+    bindings: Ast.StatementLet[],
+    expr: Ast.Expression,
+): Promise<bigint | Error> {
+    const contractModule = createModuleWithExpressionGetter(
+        makeF,
+        contractNameToCompile,
+        "getInt",
+        bindings,
+        expr,
+    );
+
+    try {
+        const contractMapPromise = buildModule(
+            astF,
+            contractModule,
+            customStdlib,
+            blockchain,
+        );
+
+        const contractMap = await contractMapPromise;
+        const contract = contractMap.get(contractNameToCompile)!;
+        await contract.send(sender, { value: toNano(1) });
+        return await contract.getInt();
+    } catch (e: any) {
+        return e;
+    }
+}
+
+/*
+export function generateBindings(
+    expressionTestingEnvironment: ExpressionTestingEnvironment,
+    expressionGenerationIds: Map<AllowedTypeEnum, string[]>,
+    generator: (nonTerminalId: NonTerminalEnum) => fc.Arbitrary<Ast.Expression>,
+): fc.Arbitrary<Ast.StatementLet[]> {
+    return fc.tuple(
+        ...expressionGenerationIds
+            .entries()
+            .flatMap(([type, names]) =>
+                names.map((name) =>
+                    type.slice(-1) === "?"
+                        ? fc.constant(
+                              expressionTestingEnvironment.makeF.makeDummyStatementLet(
+                                  expressionTestingEnvironment.makeF.makeDummyId(
+                                      name,
+                                  ),
+                                  expressionTestingEnvironment.makeF.makeDummyOptionalType(
+                                      expressionTestingEnvironment.makeF.makeDummyTypeId(
+                                          type.slice(0, -1),
+                                      ),
+                                  ),
+                                  expressionTestingEnvironment.makeF.makeDummyNull(),
+                              ),
+                          )
+                        : generator(initializersMapping[type]).map((expr) =>
+                              expressionTestingEnvironment.makeF.makeDummyStatementLet(
+                                  expressionTestingEnvironment.makeF.makeDummyId(
+                                      name,
+                                  ),
+                                  expressionTestingEnvironment.makeF.makeDummyTypeId(
+                                      type,
+                                  ),
+                                  expr,
+                              ),
+                          ),
+                ),
+            ),
+    );
+}
+*/
+
+export function saveExpressionTest(
+    bindings: Ast.StatementLet[],
+    expr: Ast.Expression,
+    compilationResult: Error,
+    interpretationResult: Error,
+    outputStream: fs.WriteStream,
+): void {
+    outputStream.write(
+        bindingsAndExpressionPrtinter([bindings, expr]) +
+            `\nCompilation error: ${compilationResult}\nInterpretation error: ${interpretationResult}\n`,
+    );
+}
diff --git a/src/test/fuzzer/test/program.spec.ts b/src/test/fuzzer/test/program.spec.ts
new file mode 100644
index 0000000000..c2b996ab16
--- /dev/null
+++ b/src/test/fuzzer/test/program.spec.ts
@@ -0,0 +1,69 @@
+import { Program } from "@/test/fuzzer/src/generators";
+import assert from "assert";
+
+import {
+    precompile,
+    createContext,
+    enableFeatures,
+} from "@/test/fuzzer/test/testUtils";
+import {
+    createProperty,
+    checkProperty,
+    astNodeCounterexamplePrinter,
+} from "@/test/fuzzer/src/util";
+import { GlobalContext } from "@/test/fuzzer/src/context";
+import { getAstFactory } from "@/ast/ast-helpers";
+
+describe("properties", () => {
+    it("generates well-typed programs", () => {
+        const property = createProperty(
+            new Program({ addStdlib: true }).generate(),
+            (program) => {
+                const factoryAst = getAstFactory();
+                let ctx = createContext(program);
+                ctx = enableFeatures(ctx, "external");
+                precompile(ctx, factoryAst);
+                GlobalContext.resetDepth();
+            },
+        );
+        checkProperty(property, astNodeCounterexamplePrinter);
+    });
+
+    it("generates reproducible AST", () => {
+        // Setting a fixed seed for reproducibility
+        const originalSeed = GlobalContext.config.seed;
+        GlobalContext.config.seed = 42;
+
+        let program1: string | undefined;
+        let program2: string | undefined;
+
+        // Create a single property that generates two programs
+        const property = createProperty(new Program().generate(), (program) => {
+            if (program1 === undefined) {
+                program1 = GlobalContext.format(program, "ast");
+            } else {
+                program2 = GlobalContext.format(program, "ast");
+            }
+            GlobalContext.resetDepth();
+        });
+
+        // Execute property twice
+        checkProperty(property, astNodeCounterexamplePrinter, /*numRuns=*/ 1);
+        checkProperty(property, astNodeCounterexamplePrinter, /*numRuns=*/ 1);
+
+        assert.notEqual(
+            program1,
+            undefined,
+            "First program should not be undefined",
+        );
+        assert.notEqual(
+            program2,
+            undefined,
+            "Second program should not be undefined",
+        );
+        assert.equal(program1, program2, "Both programs should be identical");
+
+        // Restore the original seed
+        GlobalContext.config.seed = originalSeed;
+    });
+});
diff --git a/src/test/fuzzer/test/testUtils.ts b/src/test/fuzzer/test/testUtils.ts
new file mode 100644
index 0000000000..86e0d94fa1
--- /dev/null
+++ b/src/test/fuzzer/test/testUtils.ts
@@ -0,0 +1,68 @@
+import { CompilerContext } from "@/context/context";
+import { createABI } from "@/generator/createABI";
+import { writeProgram } from "@/generator/writeProgram";
+import type * as Ast from "@/ast/ast";
+import { openContext } from "@/context/store";
+import { resolveAllocations } from "@/storage/resolveAllocation";
+import { featureEnable } from "@/config/features";
+import { resolveDescriptors } from "@/types/resolveDescriptors";
+import { resolveSignatures } from "@/types/resolveSignatures";
+import { resolveStatements } from "@/types/resolveStatements";
+import { evalComptimeExpressions } from "@/types/evalComptimeExpressions";
+import { resolveErrors } from "@/types/resolveErrors";
+import type { FactoryAst } from "@/ast/ast-helpers";
+
+export function createContext(program: Ast.Module): CompilerContext {
+    let ctx = new CompilerContext();
+    ctx = openContext(
+        ctx,
+        /*sources=*/ [],
+        /*funcSources=*/ [],
+        //getParser(factoryAst),
+        [program],
+    );
+    return ctx;
+}
+
+/**
+ * Replicates the `precompile` pipeline.
+ */
+export function precompile(
+    ctx: CompilerContext,
+    factoryAst: FactoryAst,
+): CompilerContext {
+    ctx = resolveDescriptors(ctx, factoryAst);
+    ctx = resolveStatements(ctx);
+    evalComptimeExpressions(ctx, factoryAst);
+    ctx = resolveSignatures(ctx, factoryAst);
+    ctx = resolveErrors(ctx, factoryAst);
+    ctx = resolveAllocations(ctx);
+    return ctx;
+}
+
+/**
+ * Enables compiler's features.
+ */
+export function enableFeatures(
+    ctx: CompilerContext,
+    ...features: ["inline" | "debug" | "masterchain" | "external"]
+): CompilerContext {
+    return features.reduce((accCtx, feature) => {
+        return featureEnable(accCtx, feature);
+    }, ctx);
+}
+
+/**
+ * Replicates the `compile` pipeline.
+ */
+export async function compile(ctx: CompilerContext, contractName: string) {
+    const abi = createABI(ctx, contractName);
+    const output = await writeProgram(
+        ctx,
+        abi,
+        `tact_check_${contractName}`,
+        {}, //ContractCodes
+        false,
+    );
+    return { output, ctx };
+}