Skip to content

Commit 9caf8ea

Browse files
committed
Add @ethdebug/programs
1 parent 5ebbc60 commit 9caf8ea

File tree

16 files changed

+371
-0
lines changed

16 files changed

+371
-0
lines changed

bin/start

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ concurrently --names=format,pointers,web,jest \
1313
"cd ./packages/materials && yarn watch" \
1414
"cd ./packages/pointers && yarn watch" \
1515
"cd ./packages/types && yarn watch" \
16+
"cd ./packages/programs && yarn watch" \
1617
"cd ./packages/web && yarn start $NO_OPEN_FLAG" \
1718
"yarn test --watchAll"
1819

packages/programs/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
dist

packages/programs/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# @ethdebug/programs
2+
3+
_This NPM package contains type definitions and type predicates for working
4+
with objects in the
5+
[**ethdebug/format/program** schema](https://ethdebug.github.io/format/spec/program/overview)._

packages/programs/bin/.gitkeep

Whitespace-only changes.

packages/programs/jest.config.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { Config } from "jest";
2+
3+
const config: Config = {
4+
displayName: "@ethdebug/programs",
5+
preset: "ts-jest",
6+
testEnvironment: "node",
7+
extensionsToTreatAsEsm: [".ts"],
8+
moduleFileExtensions: ["ts", "js"],
9+
moduleNameMapper: {
10+
'^(\\.{1,2}/.*)\\.js$': '$1',
11+
},
12+
modulePathIgnorePatterns: ["<rootDir>/dist/"],
13+
setupFilesAfterEnv: ["<rootDir>/../../jest.setup.ts"],
14+
transform: {
15+
// '^.+\\.[tj]sx?$' to process js/ts with `ts-jest`
16+
// '^.+\\.m?[tj]sx?$' to process js/ts/mjs/mts with `ts-jest`
17+
'^.+\\.tsx?$': [
18+
'ts-jest',
19+
{
20+
useESM: true,
21+
},
22+
],
23+
},
24+
};
25+
26+
export default config;

packages/programs/package.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "@ethdebug/programs",
3+
"version": "0.1.0-0",
4+
"description": "Reference implementation for ethdebug/format programs",
5+
"main": "dist/src/index.js",
6+
"type": "module",
7+
"license": "MIT",
8+
"scripts": {
9+
"prepare": "tsc",
10+
"watch": "yarn prepare --watch",
11+
"test": "node --experimental-vm-modules $(yarn bin jest)"
12+
},
13+
"devDependencies": {
14+
"@ethdebug/format": "^0.1.0-0",
15+
"@ethdebug/materials": "^0.1.0-0",
16+
"@ethdebug/pointers": "^0.1.0-0",
17+
"@ethdebug/types": "^0.1.0-0",
18+
"@jest/globals": "^29.7.0",
19+
"chalk": "^5.3.0",
20+
"cli-highlight": "^2.1.11",
21+
"ganache": "7.9.x",
22+
"jest": "^29.7.0",
23+
"solc": "^0.8.26",
24+
"ts-jest": "^29.1.1",
25+
"ts-node": "^10.9.2",
26+
"typescript": "^5.3.3"
27+
},
28+
"dependencies": {
29+
"ethereum-cryptography": "^2.1.3"
30+
},
31+
"publishConfig": {
32+
"access": "public"
33+
}
34+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/// <reference path="../../../jest.d.ts" />
2+
import { expect, describe, it } from "@jest/globals";
3+
4+
import { describeSchema } from "@ethdebug/format";
5+
6+
import { Context, isContext } from "./context.js";
7+
8+
describe("type guards", () => {
9+
const schemaGuards = [
10+
{
11+
schema: {
12+
id: "schema:ethdebug/format/program/context"
13+
},
14+
guard: isContext
15+
},
16+
{
17+
schema: {
18+
id: "schema:ethdebug/format/program/context/code"
19+
},
20+
guard: Context.isCode
21+
},
22+
{
23+
schema: {
24+
id: "schema:ethdebug/format/program/context/variables"
25+
},
26+
guard: Context.isVariables
27+
},
28+
{
29+
schema: {
30+
id: "schema:ethdebug/format/program/context/remark"
31+
},
32+
guard: Context.isRemark
33+
},
34+
] as const;
35+
36+
for (const { guard, ...describeSchemaOptions } of schemaGuards) {
37+
const { schema } = describeSchemaOptions;
38+
describe(schema.id.slice("schema:".length), () => {
39+
it("matches its examples", () => {
40+
const {
41+
schema: {
42+
examples = []
43+
}
44+
} = describeSchema(describeSchemaOptions);
45+
46+
expect(guard).toSatisfyAll(examples);
47+
});
48+
});
49+
}
50+
});

packages/programs/src/context.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { Materials } from "@ethdebug/materials";
2+
import { Type, isType } from "@ethdebug/types";
3+
import { Pointer, isPointer } from "@ethdebug/pointers";
4+
5+
export type Context =
6+
| Context.Code
7+
| Context.Variables
8+
| Context.Remark;
9+
10+
export const isContext = (value: unknown): value is Context => [
11+
Context.isCode,
12+
Context.isVariables,
13+
Context.isRemark,
14+
].some(guard => guard(value));
15+
16+
export namespace Context {
17+
export interface Code {
18+
code: Materials.SourceRange;
19+
}
20+
21+
export const isCode = (value: unknown): value is Code =>
22+
typeof value === "object" && !!value &&
23+
"code" in value && Materials.isSourceRange(value.code);
24+
25+
export interface Variables {
26+
variables: Variables.Variable[]
27+
}
28+
29+
export const isVariables = (value: unknown): value is Variables =>
30+
typeof value === "object" && !!value &&
31+
"variables" in value && value.variables instanceof Array &&
32+
value.variables.length > 0 &&
33+
value.variables.every(Variables.isVariable);
34+
35+
export namespace Variables {
36+
export interface Variable {
37+
identifier?: string;
38+
declaration?: Materials.SourceRange;
39+
type?: Type;
40+
pointer?: Pointer;
41+
}
42+
43+
const allowedKeys = new Set([
44+
"identifier",
45+
"declaration",
46+
"type",
47+
"pointer"
48+
]);
49+
50+
export const isVariable = (value: unknown): value is Variable =>
51+
typeof value === "object" && !!value &&
52+
Object.keys(value).length > 0 &&
53+
Object.keys(value).every(key => allowedKeys.has(key)) &&
54+
(
55+
!("identifier" in value) ||
56+
typeof value.identifier === "string"
57+
) &&
58+
(
59+
!("declaration" in value) ||
60+
Materials.isSourceRange(value.declaration)
61+
) &&
62+
(
63+
!("type" in value) ||
64+
isType(value.type)
65+
) &&
66+
(
67+
!("pointer" in value) ||
68+
isPointer(value.pointer)
69+
);
70+
}
71+
72+
export interface Remark {
73+
remark: string;
74+
}
75+
76+
export const isRemark = (value: unknown): value is Remark =>
77+
typeof value === "object" && !!value &&
78+
"remark" in value && typeof value.remark === "string";
79+
}

packages/programs/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { Program, isProgram } from "./program.js";
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/// <reference path="../../../jest.d.ts" />
2+
import { expect, describe, it } from "@jest/globals";
3+
4+
import { describeSchema } from "@ethdebug/format";
5+
6+
import { Instruction, isInstruction } from "./instruction.js";
7+
8+
describe("type guards", () => {
9+
const schemaGuards = [
10+
{
11+
schema: {
12+
id: "schema:ethdebug/format/program/instruction"
13+
},
14+
guard: isInstruction
15+
},
16+
] as const;
17+
18+
for (const { guard, ...describeSchemaOptions } of schemaGuards) {
19+
const { schema } = describeSchemaOptions;
20+
describe(schema.id.slice("schema:".length), () => {
21+
it("matches its examples", () => {
22+
const {
23+
schema: {
24+
examples = []
25+
}
26+
} = describeSchema(describeSchemaOptions);
27+
28+
expect(guard).toSatisfyAll(examples);
29+
});
30+
});
31+
}
32+
});

0 commit comments

Comments
 (0)