Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions packages/bugc-react/src/examples.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* The curated example set is sourced from `@ethdebug/bugc/examples`
* (the canonical `.bug` files), sliced down and stripped for display.
* These guards ensure the selection stays valid and editor-clean: the
* strings ship verbatim into the docs playground editor.
*/
import { describe, it, expect } from "vitest";
import { compile } from "@ethdebug/bugc";
import { exampleSources } from "@ethdebug/bugc/examples";
import { bugExamples } from "./examples.js";

describe("bugExamples", () => {
it("is the curated trio (counter, functions, arrays)", () => {
expect(bugExamples.map((e) => e.name)).toEqual([
"counter",
"functions",
"arrays",
]);
});

it("gives every example a display name and non-empty source", () => {
for (const ex of bugExamples) {
expect(ex.displayName.trim().length).toBeGreaterThan(0);
expect(ex.code.trim().length).toBeGreaterThan(0);
}
});

it("draws its sources from the canonical bugc examples", () => {
// Sanity: the raw sources the curation selects really exist
// upstream, so the selection can't silently drift to empty.
expect(
Object.prototype.hasOwnProperty.call(
exampleSources,
"intermediate/owner-counter.bug",
),
).toBe(true);
expect(bugExamples.length).toBeGreaterThan(0);
});

it("strips bugc's inline test annotations for display", () => {
for (const ex of bugExamples) {
expect(ex.code).not.toContain("@test");
expect(ex.code).not.toContain("@expect");
}
});

// Each curated source must compile cleanly to bytecode — this is
// the guard that matters, since these ship straight to the editor.
for (const ex of bugExamples) {
it(`compiles ${ex.name} to bytecode without errors`, async () => {
const result = await compile({
to: "bytecode",
source: ex.code,
optimizer: { level: 0 },
});
expect(result.success).toBe(true);
});
}
});
52 changes: 52 additions & 0 deletions packages/bugc-react/src/examples.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Curated BUG examples for the docs playground's example selector.
*
* The sources are the canonical `.bug` files shipped by
* `@ethdebug/bugc/examples` — the same files bugc's behavioral tests
* compile — so the playground never drifts from a hand-maintained copy.
* We select a small subset, label it for display, and strip bugc's
* inline test annotations so the editor shows clean source.
*/

import { exampleSources, stripTestAnnotations } from "@ethdebug/bugc/examples";

/** A named, display-labelled BUG source for the example selector. */
export interface BugExample {
/** Stable identifier (used as the <option> value). */
name: string;
/** Human-readable label shown in the dropdown. */
displayName: string;
/** Display-ready BUG source (test annotations stripped). */
code: string;
}

/**
* The curated subset, in display order. `path` refers into
* `@ethdebug/bugc/examples`; the sources stay upstream, only the
* selection and labels live here.
*/
const curated: { path: string; name: string; displayName: string }[] = [
{
path: "intermediate/owner-counter.bug",
name: "counter",
displayName: "Owned counter",
},
{ path: "basic/functions.bug", name: "functions", displayName: "Functions" },
{
path: "intermediate/arrays.bug",
name: "arrays",
displayName: "Arrays & loops",
},
];

export const bugExamples: BugExample[] = curated.map(
({ path, name, displayName }) => {
const source = exampleSources[path];
if (source === undefined) {
throw new Error(
`Curated example "${path}" is not in @ethdebug/bugc/examples`,
);
}
return { name, displayName, code: stripTestAnnotations(source) };
},
);
3 changes: 3 additions & 0 deletions packages/bugc-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ export {
type EditorSourceRange,
} from "#components/Editor";

// Curated example programs (for playground selectors)
export { bugExamples, type BugExample } from "./examples.js";

// Utilities
export {
// Debug utilities
Expand Down
13 changes: 13 additions & 0 deletions packages/bugc-react/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { defineConfig } from "vitest/config";

export default defineConfig({
test: {
globals: true,
environment: "jsdom",
coverage: {
provider: "v8",
reporter: ["text", "json", "html"],
exclude: ["node_modules/", "dist/", "**/*.test.ts", "**/*.test.tsx"],
},
},
});
68 changes: 40 additions & 28 deletions packages/playground/src/playground/examples.ts
Original file line number Diff line number Diff line change
@@ -1,118 +1,130 @@
import { exampleSources, stripTestAnnotations } from "@ethdebug/bugc/examples";

export interface Example {
name: string;
displayName: string;
category: "basic" | "intermediate" | "advanced" | "optimizations";
code: string;
}

// Import all .bug files from the bugc examples directory
// Vite will inline the file contents at build time
const exampleFiles = import.meta.glob("../../../bugc/examples/**/*.bug", {
query: "?raw",
import: "default",
eager: true,
}) as Record<string, string>;

// Map the actual example files to the Example interface
// Organized by category, showing only working examples
export const examples: Example[] = [
// Curated subset of the canonical `.bug` files shipped by
// `@ethdebug/bugc/examples`, organized by category. The sources stay
// upstream (bugc's behavioral tests compile the same files); only the
// selection, labels, and display order live here. Test annotations are
// stripped so the editor shows clean source.
const curated: Array<{
path: string;
name: string;
displayName: string;
category: Example["category"];
}> = [
// Basic examples
{
path: "basic/minimal.bug",
name: "minimal",
displayName: "Minimal",
category: "basic",
code: exampleFiles["../../../bugc/examples/basic/minimal.bug"] || "",
},
{
path: "basic/conditionals.bug",
name: "conditionals",
displayName: "Conditionals",
category: "basic",
code: exampleFiles["../../../bugc/examples/basic/conditionals.bug"],
},
{
path: "basic/functions.bug",
name: "functions",
displayName: "Functions",
category: "basic",
code: exampleFiles["../../../bugc/examples/basic/functions.bug"],
},
{
path: "basic/array-length.bug",
name: "array-length",
displayName: "Array Length",
category: "basic",
code: exampleFiles["../../../bugc/examples/basic/array-length.bug"],
},

// Intermediate examples
{
path: "intermediate/owner-counter.bug",
name: "owner-counter",
displayName: "Owner Counter",
category: "intermediate",
code: exampleFiles["../../../bugc/examples/intermediate/owner-counter.bug"],
},
{
path: "intermediate/arrays.bug",
name: "arrays",
displayName: "Arrays and Loops",
category: "intermediate",
code: exampleFiles["../../../bugc/examples/intermediate/arrays.bug"],
},
{
path: "intermediate/mappings.bug",
name: "mappings",
displayName: "Mappings",
category: "intermediate",
code: exampleFiles["../../../bugc/examples/intermediate/mappings.bug"],
},
{
path: "intermediate/scopes.bug",
name: "scopes",
displayName: "Variable Scopes",
category: "intermediate",
code: exampleFiles["../../../bugc/examples/intermediate/scopes.bug"],
},
{
path: "intermediate/slices.bug",
name: "slices",
displayName: "Byte Slices",
category: "intermediate",
code: exampleFiles["../../../bugc/examples/intermediate/slices.bug"],
},
{
path: "intermediate/calldata.bug",
name: "calldata",
displayName: "Calldata Access",
category: "intermediate",
code: exampleFiles["../../../bugc/examples/intermediate/calldata.bug"],
},

// Advanced examples
{
path: "advanced/nested-mappings.bug",
name: "nested-mappings",
displayName: "Nested Mappings",
category: "advanced",
code: exampleFiles["../../../bugc/examples/advanced/nested-mappings.bug"],
},
{
path: "advanced/nested-arrays.bug",
name: "nested-arrays",
displayName: "Nested Arrays",
category: "advanced",
code: exampleFiles["../../../bugc/examples/advanced/nested-arrays.bug"],
},
{
path: "advanced/nested-structs.bug",
name: "nested-structs",
displayName: "Nested Structs",
category: "advanced",
code: exampleFiles["../../../bugc/examples/advanced/nested-structs.bug"],
},

// Optimization demos
{
path: "optimizations/cse.bug",
name: "cse",
displayName: "CSE Demo",
category: "optimizations",
code: exampleFiles["../../../bugc/examples/optimizations/cse.bug"],
},
{
path: "optimizations/constant-folding.bug",
name: "constant-folding",
displayName: "Constant Folding",
category: "optimizations",
code: exampleFiles[
"../../../bugc/examples/optimizations/constant-folding.bug"
],
},
];

export const examples: Example[] = curated.map(
({ path, name, displayName, category }) => {
const source = exampleSources[path];
if (source === undefined) {
throw new Error(
`Curated example "${path}" is not in @ethdebug/bugc/examples`,
);
}
return { name, displayName, category, code: stripTestAnnotations(source) };
},
);
6 changes: 4 additions & 2 deletions packages/web/src/theme/BugcExample/BugPlayground.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,16 @@
align-items: center;
}

.bug-playground-opt-control {
.bug-playground-opt-control,
.bug-playground-example-control {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.875rem;
}

.bug-playground-opt-control select {
.bug-playground-opt-control select,
.bug-playground-example-control select {
padding: 0.25rem 0.5rem;
border-radius: var(--ifm-global-radius);
border: 1px solid var(--ifm-color-emphasis-300);
Expand Down
Loading
Loading